Code

Merge branch 'cb/ignored-paths-are-precious' into pu
authorJunio C Hamano <gitster@pobox.com>
Mon, 31 Jan 2011 03:03:19 +0000 (19:03 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 31 Jan 2011 03:03:19 +0000 (19:03 -0800)
* cb/ignored-paths-are-precious:
  checkout/merge: optionally fail operation when ignored files need to be overwritten

Conflicts:
Documentation/config.txt
builtin/checkout.c

934 files changed:
.gitignore
.mailmap
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/RelNotes-1.5.0.1.txt [deleted file]
Documentation/RelNotes-1.5.0.2.txt [deleted file]
Documentation/RelNotes-1.5.0.3.txt [deleted file]
Documentation/RelNotes-1.5.0.4.txt [deleted file]
Documentation/RelNotes-1.5.0.5.txt [deleted file]
Documentation/RelNotes-1.5.0.6.txt [deleted file]
Documentation/RelNotes-1.5.0.7.txt [deleted file]
Documentation/RelNotes-1.5.0.txt [deleted file]
Documentation/RelNotes-1.5.1.1.txt [deleted file]
Documentation/RelNotes-1.5.1.2.txt [deleted file]
Documentation/RelNotes-1.5.1.3.txt [deleted file]
Documentation/RelNotes-1.5.1.4.txt [deleted file]
Documentation/RelNotes-1.5.1.5.txt [deleted file]
Documentation/RelNotes-1.5.1.6.txt [deleted file]
Documentation/RelNotes-1.5.1.txt [deleted file]
Documentation/RelNotes-1.5.2.1.txt [deleted file]
Documentation/RelNotes-1.5.2.2.txt [deleted file]
Documentation/RelNotes-1.5.2.3.txt [deleted file]
Documentation/RelNotes-1.5.2.4.txt [deleted file]
Documentation/RelNotes-1.5.2.5.txt [deleted file]
Documentation/RelNotes-1.5.2.txt [deleted file]
Documentation/RelNotes-1.5.3.1.txt [deleted file]
Documentation/RelNotes-1.5.3.2.txt [deleted file]
Documentation/RelNotes-1.5.3.3.txt [deleted file]
Documentation/RelNotes-1.5.3.4.txt [deleted file]
Documentation/RelNotes-1.5.3.5.txt [deleted file]
Documentation/RelNotes-1.5.3.6.txt [deleted file]
Documentation/RelNotes-1.5.3.7.txt [deleted file]
Documentation/RelNotes-1.5.3.8.txt [deleted file]
Documentation/RelNotes-1.5.3.txt [deleted file]
Documentation/RelNotes-1.5.4.1.txt [deleted file]
Documentation/RelNotes-1.5.4.2.txt [deleted file]
Documentation/RelNotes-1.5.4.3.txt [deleted file]
Documentation/RelNotes-1.5.4.4.txt [deleted file]
Documentation/RelNotes-1.5.4.5.txt [deleted file]
Documentation/RelNotes-1.5.4.6.txt [deleted file]
Documentation/RelNotes-1.5.4.7.txt [deleted file]
Documentation/RelNotes-1.5.4.txt [deleted file]
Documentation/RelNotes-1.5.5.1.txt [deleted file]
Documentation/RelNotes-1.5.5.2.txt [deleted file]
Documentation/RelNotes-1.5.5.3.txt [deleted file]
Documentation/RelNotes-1.5.5.4.txt [deleted file]
Documentation/RelNotes-1.5.5.5.txt [deleted file]
Documentation/RelNotes-1.5.5.6.txt [deleted file]
Documentation/RelNotes-1.5.5.txt [deleted file]
Documentation/RelNotes-1.5.6.1.txt [deleted file]
Documentation/RelNotes-1.5.6.2.txt [deleted file]
Documentation/RelNotes-1.5.6.3.txt [deleted file]
Documentation/RelNotes-1.5.6.4.txt [deleted file]
Documentation/RelNotes-1.5.6.5.txt [deleted file]
Documentation/RelNotes-1.5.6.6.txt [deleted file]
Documentation/RelNotes-1.5.6.txt [deleted file]
Documentation/RelNotes-1.6.0.1.txt [deleted file]
Documentation/RelNotes-1.6.0.2.txt [deleted file]
Documentation/RelNotes-1.6.0.3.txt [deleted file]
Documentation/RelNotes-1.6.0.4.txt [deleted file]
Documentation/RelNotes-1.6.0.5.txt [deleted file]
Documentation/RelNotes-1.6.0.6.txt [deleted file]
Documentation/RelNotes-1.6.0.txt [deleted file]
Documentation/RelNotes-1.6.1.1.txt [deleted file]
Documentation/RelNotes-1.6.1.2.txt [deleted file]
Documentation/RelNotes-1.6.1.3.txt [deleted file]
Documentation/RelNotes-1.6.1.4.txt [deleted file]
Documentation/RelNotes-1.6.1.txt [deleted file]
Documentation/RelNotes-1.6.2.1.txt [deleted file]
Documentation/RelNotes-1.6.2.2.txt [deleted file]
Documentation/RelNotes-1.6.2.3.txt [deleted file]
Documentation/RelNotes-1.6.2.4.txt [deleted file]
Documentation/RelNotes-1.6.2.5.txt [deleted file]
Documentation/RelNotes-1.6.2.txt [deleted file]
Documentation/RelNotes-1.6.3.1.txt [deleted file]
Documentation/RelNotes-1.6.3.2.txt [deleted file]
Documentation/RelNotes-1.6.3.3.txt [deleted file]
Documentation/RelNotes-1.6.3.4.txt [deleted file]
Documentation/RelNotes-1.6.3.txt [deleted file]
Documentation/RelNotes-1.6.4.1.txt [deleted file]
Documentation/RelNotes-1.6.4.2.txt [deleted file]
Documentation/RelNotes-1.6.4.3.txt [deleted file]
Documentation/RelNotes-1.6.4.4.txt [deleted file]
Documentation/RelNotes-1.6.4.txt [deleted file]
Documentation/RelNotes-1.6.5.1.txt [deleted file]
Documentation/RelNotes-1.6.5.2.txt [deleted file]
Documentation/RelNotes-1.6.5.3.txt [deleted file]
Documentation/RelNotes-1.6.5.4.txt [deleted file]
Documentation/RelNotes-1.6.5.5.txt [deleted file]
Documentation/RelNotes-1.6.5.6.txt [deleted file]
Documentation/RelNotes-1.6.5.7.txt [deleted file]
Documentation/RelNotes-1.6.5.8.txt [deleted file]
Documentation/RelNotes-1.6.5.txt [deleted file]
Documentation/RelNotes-1.6.6.1.txt [deleted file]
Documentation/RelNotes-1.6.6.2.txt [deleted file]
Documentation/RelNotes-1.6.6.txt [deleted file]
Documentation/RelNotes-1.7.0.1.txt [deleted file]
Documentation/RelNotes-1.7.0.2.txt [deleted file]
Documentation/RelNotes-1.7.0.3.txt [deleted file]
Documentation/RelNotes-1.7.0.4.txt [deleted file]
Documentation/RelNotes-1.7.0.5.txt [deleted file]
Documentation/RelNotes-1.7.0.6.txt [deleted file]
Documentation/RelNotes-1.7.0.7.txt [deleted file]
Documentation/RelNotes-1.7.0.txt [deleted file]
Documentation/RelNotes-1.7.1.1.txt [deleted file]
Documentation/RelNotes-1.7.1.2.txt [deleted file]
Documentation/RelNotes-1.7.1.txt [deleted file]
Documentation/RelNotes-1.7.2.1.txt [deleted file]
Documentation/RelNotes-1.7.2.2.txt [deleted file]
Documentation/RelNotes-1.7.2.txt [deleted file]
Documentation/RelNotes/1.5.0.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.0.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.0.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.0.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.0.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.0.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.0.7.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.0.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.1.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.1.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.1.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.1.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.1.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.1.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.2.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.2.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.2.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.2.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.2.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.3.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.3.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.3.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.3.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.3.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.3.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.3.7.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.3.8.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.4.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.4.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.4.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.4.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.4.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.4.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.4.7.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.5.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.5.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.5.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.5.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.5.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.5.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.6.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.6.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.6.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.6.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.6.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.6.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.5.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.0.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.0.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.0.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.0.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.0.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.0.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.0.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.1.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.1.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.1.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.1.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.2.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.2.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.2.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.2.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.2.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.2.txt [new file with mode: 0644]
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.3.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.5.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.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.5.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.5.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.5.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.5.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.5.7.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.5.8.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.5.9.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.6.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.6.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.6.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.6.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.0.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.0.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.0.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.0.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.0.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.0.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.0.7.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.0.8.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.0.9.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.0.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.1.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.1.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.1.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.1.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.2.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.2.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.2.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.2.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.2.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.3.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.3.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.3.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.3.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.3.5.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.4.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/asciidoc.conf
Documentation/config.txt
Documentation/diff-generate-patch.txt
Documentation/diff-options.txt
Documentation/docbook.xsl
Documentation/everyday.txt
Documentation/fetch-options.txt
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-apply.txt
Documentation/git-archimport.txt
Documentation/git-archive.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-blame.txt
Documentation/git-branch.txt
Documentation/git-bundle.txt
Documentation/git-cat-file.txt
Documentation/git-check-ref-format.txt
Documentation/git-checkout-index.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-clone.txt
Documentation/git-commit-tree.txt
Documentation/git-commit.txt
Documentation/git-cvsexportcommit.txt
Documentation/git-cvsserver.txt
Documentation/git-daemon.txt
Documentation/git-describe.txt
Documentation/git-diff.txt
Documentation/git-difftool.txt
Documentation/git-fast-export.txt
Documentation/git-fast-import.txt
Documentation/git-fetch.txt
Documentation/git-filter-branch.txt
Documentation/git-fmt-merge-msg.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-fsck.txt
Documentation/git-gc.txt
Documentation/git-grep.txt
Documentation/git-help.txt
Documentation/git-index-pack.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-mailsplit.txt
Documentation/git-merge-base.txt
Documentation/git-merge-index.txt
Documentation/git-merge.txt
Documentation/git-mergetool--lib.txt
Documentation/git-mergetool.txt
Documentation/git-notes.txt
Documentation/git-pack-objects.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-reflog.txt
Documentation/git-relink.txt
Documentation/git-remote-ext.txt [new file with mode: 0644]
Documentation/git-remote-fd.txt [new file with mode: 0644]
Documentation/git-remote.txt
Documentation/git-repack.txt
Documentation/git-reset.txt
Documentation/git-rev-list.txt
Documentation/git-rev-parse.txt
Documentation/git-revert.txt
Documentation/git-rm.txt
Documentation/git-send-email.txt
Documentation/git-shell.txt
Documentation/git-shortlog.txt
Documentation/git-show-branch.txt
Documentation/git-show-ref.txt
Documentation/git-show.txt
Documentation/git-stash.txt
Documentation/git-svn.txt
Documentation/git-tag.txt
Documentation/git-update-index.txt
Documentation/git-verify-tag.txt
Documentation/git-web--browse.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitcore-tutorial.txt
Documentation/gitdiffcore.txt
Documentation/githooks.txt
Documentation/gitignore.txt
Documentation/gitk.txt
Documentation/gitmodules.txt
Documentation/gittutorial-2.txt
Documentation/gittutorial.txt
Documentation/glossary-content.txt
Documentation/howto/revert-a-faulty-merge.txt
Documentation/howto/revert-branch-rebase.txt
Documentation/install-webdoc.sh
Documentation/merge-config.txt
Documentation/merge-options.txt
Documentation/merge-strategies.txt
Documentation/pretty-options.txt
Documentation/rev-list-options.txt
Documentation/revisions.txt
Documentation/technical/api-merge.txt [new file with mode: 0644]
Documentation/technical/api-parse-options.txt
Documentation/technical/api-sigchain.txt [new file with mode: 0644]
Documentation/technical/api-tree-walking.txt
Documentation/technical/index-format.txt [new file with mode: 0644]
Documentation/user-manual.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
abspath.c
alias.c
archive.c
attr.c
branch.h
builtin.h
builtin/add.c
builtin/apply.c
builtin/blame.c
builtin/branch.c
builtin/bundle.c
builtin/cat-file.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-files.c
builtin/diff.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/index-pack.c
builtin/init-db.c
builtin/log.c
builtin/ls-files.c
builtin/ls-remote.c
builtin/ls-tree.c
builtin/mailinfo.c
builtin/mailsplit.c
builtin/merge-base.c
builtin/merge-file.c
builtin/merge-recursive.c
builtin/merge.c
builtin/mv.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/prune.c
builtin/read-tree.c
builtin/remote-ext.c [new file with mode: 0644]
builtin/remote-fd.c [new file with mode: 0644]
builtin/remote.c
builtin/rerere.c
builtin/reset.c
builtin/rev-list.c
builtin/revert.c
builtin/rm.c
builtin/send-pack.c
builtin/shortlog.c
builtin/show-branch.c
builtin/show-ref.c
builtin/skew.c [new file with mode: 0644]
builtin/symbolic-ref.c
builtin/tag.c
builtin/unpack-objects.c
builtin/update-index.c
builtin/update-server-info.c
builtin/var.c
builtin/verify-tag.c
bundle.c
cache-tree.c
cache.h
color.c
color.h
commit.c
commit.h
compat/inet_ntop.c
compat/inet_pton.c
compat/mingw.c
compat/mingw.h
compat/msvc.c
compat/nedmalloc/malloc.c.h
compat/regex/regcomp.c [new file with mode: 0644]
compat/regex/regex.c
compat/regex/regex.h
compat/regex/regex_internal.c [new file with mode: 0644]
compat/regex/regex_internal.h [new file with mode: 0644]
compat/regex/regexec.c [new file with mode: 0644]
compat/strtok_r.c [new file with mode: 0644]
compat/vcbuild/include/dirent.h [deleted file]
compat/vcbuild/include/unistd.h
compat/win32/dirent.c [new file with mode: 0644]
compat/win32/dirent.h [new file with mode: 0644]
compat/win32/sys/poll.c [new file with mode: 0644]
compat/win32/sys/poll.h [new file with mode: 0644]
compat/win32/syslog.c [new file with mode: 0644]
compat/win32/syslog.h [new file with mode: 0644]
config.c
config.mak.in
configure.ac
connect.c
contrib/ciabot/ciabot.py
contrib/completion/git-completion.bash
contrib/emacs/git-blame.el
contrib/examples/builtin-fetch--tool.c
contrib/examples/git-merge.sh
contrib/examples/git-revert.sh
contrib/examples/git-svnimport.perl
contrib/fast-import/git-p4
contrib/fast-import/import-directories.perl
contrib/git-shell-commands/README [new file with mode: 0644]
contrib/git-shell-commands/help [new file with mode: 0755]
contrib/git-shell-commands/list [new file with mode: 0755]
contrib/hooks/post-receive-email
contrib/svn-fe/svn-fe.c
contrib/svn-fe/svn-fe.txt
contrib/workdir/git-new-workdir
convert.c
daemon.c
diff-delta.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-pickaxe.c
diffcore.h
dir.c
dir.h
entry.c
environment.c
exec_cmd.c
fast-import.c
git-add--interactive.perl
git-am.sh
git-archimport.perl
git-bisect.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-difftool--helper.sh
git-difftool.perl
git-filter-branch.sh
git-gui/GIT-VERSION-GEN
git-gui/Makefile
git-gui/git-gui--askpass
git-gui/git-gui.sh
git-gui/lib/blame.tcl
git-gui/lib/branch_rename.tcl
git-gui/lib/diff.tcl
git-gui/po/sv.po
git-instaweb.sh
git-merge-octopus.sh
git-mergetool--lib.sh
git-mergetool.sh
git-parse-remote.sh
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-relink.perl
git-repack.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_remote_helpers/setup.cfg [new file with mode: 0644]
gitk-git/gitk [changed mode: 0644->0755]
gitk-git/po/pt_br.po [new file with mode: 0644]
gitk-git/po/sv.po
gitweb/Makefile
gitweb/README
gitweb/gitweb.perl
gitweb/static/gitweb.css
graph.c
grep.c
grep.h
help.c
help.h
http-backend.c
http-fetch.c
http-push.c
http.c
http.h
ident.c
list-objects.c
ll-merge.c
ll-merge.h
mailmap.c
merge-file.c
merge-recursive.c
merge-recursive.h
name-hash.c
notes-cache.c
notes-merge.c [new file with mode: 0644]
notes-merge.h [new file with mode: 0644]
notes.c
notes.h
object.c
object.h
pack-check.c
parse-options.c
parse-options.h
path.c
perl/Git.pm
perl/Makefile
preload-index.c
pretty.c
reachable.c
read-cache.c
reflog-walk.c
refs.c
refs.h
remote.c
rerere.c
revision.c
revision.h
send-pack.h
setup.c
sha1_file.c
sha1_name.c
shallow.c
shell.c
strbuf.c
strbuf.h
submodule.c
submodule.h
symlinks.c
t/Makefile
t/README
t/annotate-tests.sh
t/gitweb-lib.sh
t/harness [new file with mode: 0755]
t/lib-cvs.sh
t/lib-git-svn.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/lib-httpd/passwd [new file with mode: 0644]
t/lib-patch-mode.sh
t/lib-prereq-FILEMODE.sh [new file with mode: 0644]
t/lib-rebase.sh
t/lib-terminal.sh [new file with mode: 0644]
t/t0000-basic.sh
t/t0001-init.sh
t/t0003-attributes.sh
t/t0004-unwritable.sh
t/t0020-crlf.sh
t/t0021-conversion.sh
t/t0024-crlf-archive.sh
t/t0026-eol-config.sh
t/t0050-filesystem.sh
t/t0070-fundamental.sh
t/t0080-vcs-svn.sh [new file with mode: 0755]
t/t1000-read-tree-m-3way.sh
t/t1001-read-tree-m-2way.sh
t/t1002-read-tree-m-u-2way.sh
t/t1004-read-tree-m-u-wf.sh
t/t1011-read-tree-sparse-checkout.sh
t/t1020-subdirectory.sh
t/t1200-tutorial.sh
t/t1300-repo-config.sh
t/t1302-repo-version.sh
t/t1304-default-acl.sh
t/t1400-update-ref.sh
t/t1401-symbolic-ref.sh
t/t1402-check-ref-format.sh
t/t1410-reflog.sh
t/t1412-reflog-loop.sh [new file with mode: 0755]
t/t1450-fsck.sh
t/t1501-worktree.sh
t/t1502-rev-parse-parseopt.sh
t/t1503-rev-parse-verify.sh
t/t1504-ceiling-dirs.sh
t/t1506-rev-parse-diagnosis.sh
t/t1507-rev-parse-upstream.sh
t/t1509-root-worktree.sh
t/t1510-repo-setup.sh [new file with mode: 0755]
t/t1511-rev-parse-caret.sh [new file with mode: 0755]
t/t2006-checkout-index-basic.sh [new file with mode: 0755]
t/t2007-checkout-symlink.sh
t/t2013-checkout-submodule.sh
t/t2016-checkout-patch.sh
t/t2017-checkout-orphan.sh
t/t2019-checkout-amiguous-ref.sh [new file with mode: 0755]
t/t2050-git-dir-relative.sh
t/t2101-update-index-reupdate.sh
t/t2105-update-index-gitfile.sh
t/t2107-update-index-basic.sh [new file with mode: 0755]
t/t2200-add-update.sh
t/t3001-ls-files-others-exclude.sh
t/t3004-ls-files-basic.sh [new file with mode: 0755]
t/t3020-ls-files-error-unmatch.sh
t/t3030-merge-recursive.sh
t/t3032-merge-recursive-options.sh [new file with mode: 0755]
t/t3050-subprojects-fetch.sh
t/t3060-ls-files-with-tree.sh
t/t3100-ls-tree-restrict.sh
t/t3101-ls-tree-dirname.sh
t/t3200-branch.sh
t/t3203-branch-output.sh
t/t3300-funny-names.sh
t/t3301-notes.sh
t/t3302-notes-index-expensive.sh
t/t3303-notes-subtrees.sh
t/t3307-notes-man.sh
t/t3308-notes-merge.sh [new file with mode: 0755]
t/t3309-notes-merge-auto-resolve.sh [new file with mode: 0755]
t/t3310-notes-merge-manual-resolve.sh [new file with mode: 0755]
t/t3311-notes-merge-fanout.sh [new file with mode: 0755]
t/t3400-rebase.sh
t/t3402-rebase-merge.sh
t/t3404-rebase-interactive.sh
t/t3406-rebase-message.sh
t/t3407-rebase-abort.sh
t/t3408-rebase-multi-line.sh
t/t3409-rebase-preserve-merges.sh
t/t3412-rebase-root.sh
t/t3415-rebase-autosquash.sh
t/t3417-rebase-whitespace-fix.sh
t/t3419-rebase-patch-id.sh [new file with mode: 0755]
t/t3501-revert-cherry-pick.sh
t/t3504-cherry-pick-rerere.sh
t/t3506-cherry-pick-ff.sh
t/t3507-cherry-pick-conflict.sh
t/t3508-cherry-pick-many-commits.sh
t/t3509-cherry-pick-merge-df.sh [new file with mode: 0755]
t/t3600-rm.sh
t/t3700-add.sh
t/t3701-add-interactive.sh
t/t3900-i18n-commit.sh
t/t3902-quoted.sh
t/t3903-stash.sh
t/t3904-stash-patch.sh
t/t4002-diff-basic.sh
t/t4004-diff-rename-symlink.sh
t/t4008-diff-break-rewrite.sh
t/t4010-diff-pathspec.sh
t/t4011-diff-symlink.sh
t/t4012-diff-binary.sh
t/t4013-diff-various.sh
t/t4013/diff.log_-GF_-p_--pickaxe-all_master [new file with mode: 0644]
t/t4013/diff.log_-GF_-p_master [new file with mode: 0644]
t/t4013/diff.log_-GF_master [new file with mode: 0644]
t/t4013/diff.log_-S_F_master [new file with mode: 0644]
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4016-diff-quote.sh
t/t4017-diff-retval.sh
t/t4018-diff-funcname.sh
t/t4019-diff-wserror.sh
t/t4021-format-patch-numbered.sh
t/t4023-diff-rename-typechange.sh
t/t4026-color.sh
t/t4027-diff-submodule.sh
t/t4034-diff-words.sh
t/t4041-diff-submodule-option.sh
t/t4102-apply-rename.sh
t/t4103-apply-binary.sh
t/t4111-apply-subdir.sh [new file with mode: 0755]
t/t4114-apply-typechange.sh
t/t4115-apply-symlink.sh
t/t4119-apply-config.sh
t/t4120-apply-popt.sh
t/t4122-apply-symlink-inside.sh
t/t4124-apply-ws-rule.sh
t/t4127-apply-same-fn.sh
t/t4129-apply-samemode.sh
t/t4130-apply-criss-cross-rename.sh
t/t4132-apply-removal.sh
t/t4133-apply-filenames.sh
t/t4134-apply-submodule.sh
t/t4135-apply-weird-filenames.sh [new file with mode: 0755]
t/t4135/.gitignore [new file with mode: 0644]
t/t4135/add-plain.diff [new file with mode: 0644]
t/t4135/add-with backslash.diff [new file with mode: 0644]
t/t4135/add-with quote.diff [new file with mode: 0644]
t/t4135/add-with spaces.diff [new file with mode: 0644]
t/t4135/add-with tab.diff [new file with mode: 0644]
t/t4135/damaged-tz.diff [new file with mode: 0644]
t/t4135/damaged.diff [new file with mode: 0644]
t/t4135/diff-plain.diff [new file with mode: 0644]
t/t4135/diff-with backslash.diff [new file with mode: 0644]
t/t4135/diff-with quote.diff [new file with mode: 0644]
t/t4135/diff-with spaces.diff [new file with mode: 0644]
t/t4135/diff-with tab.diff [new file with mode: 0644]
t/t4135/funny-tz.diff [new file with mode: 0644]
t/t4135/git-plain.diff [new file with mode: 0644]
t/t4135/git-with backslash.diff [new file with mode: 0644]
t/t4135/git-with quote.diff [new file with mode: 0644]
t/t4135/git-with spaces.diff [new file with mode: 0644]
t/t4135/git-with tab.diff [new file with mode: 0644]
t/t4135/make-patches [new file with mode: 0755]
t/t4150-am.sh
t/t4151-am-abort.sh
t/t4200-rerere.sh
t/t4201-shortlog.sh
t/t4202-log.sh
t/t4203-mailmap.sh
t/t4252-am-options.sh
t/t5000-tar-tree.sh
t/t5300-pack-object.sh
t/t5301-sliding-window.sh
t/t5302-pack-index.sh
t/t5400-send-pack.sh
t/t5407-post-rewrite-hook.sh
t/t5500-fetch-pack.sh
t/t5502-quickfetch.sh
t/t5503-tagfollow.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5513-fetch-track.sh
t/t5514-fetch-multiple.sh
t/t5516-fetch-push.sh
t/t5519-push-alternates.sh
t/t5520-pull.sh
t/t5522-pull-symlink.sh
t/t5523-push-upstream.sh
t/t5525-fetch-tagopt.sh [new file with mode: 0755]
t/t5526-fetch-submodules.sh [new file with mode: 0755]
t/t5531-deep-submodule-push.sh
t/t5550-http-fetch.sh
t/t5551-http-fetch.sh
t/t5560-http-backend-noserver.sh
t/t556x_common
t/t5601-clone.sh
t/t5602-clone-remote-exec.sh
t/t5701-clone-local.sh
t/t5704-bundle.sh
t/t5705-clone-2gb.sh
t/t5800-remote-helpers.sh
t/t6000-rev-list-misc.sh [new file with mode: 0755]
t/t6001-rev-list-graft.sh
t/t6006-rev-list-format.sh
t/t6009-rev-list-parent.sh
t/t6010-merge-base.sh
t/t6016-rev-list-graph-simplify-history.sh
t/t6018-rev-list-glob.sh
t/t6020-merge-df.sh
t/t6022-merge-rename.sh
t/t6023-merge-file.sh
t/t6024-recursive-merge.sh
t/t6029-merge-subtree.sh
t/t6030-bisect-porcelain.sh
t/t6031-merge-recursive.sh
t/t6032-merge-large-rename.sh
t/t6035-merge-dir-to-symlink.sh
t/t6036-recursive-corner-cases.sh
t/t6038-merge-text-auto.sh [new file with mode: 0755]
t/t6040-tracking-info.sh
t/t6050-replace.sh
t/t6200-fmt-merge-msg.sh
t/t6500-gc.sh [new file with mode: 0755]
t/t7001-mv.sh
t/t7003-filter-branch.sh
t/t7004-tag.sh
t/t7005-editor.sh
t/t7006-pager.sh
t/t7006/test-terminal.perl [deleted file]
t/t7008-grep-binary.sh
t/t7105-reset-patch.sh
t/t7300-clean.sh
t/t7400-submodule-basic.sh
t/t7401-submodule-summary.sh
t/t7403-submodule-sync.sh
t/t7405-submodule-merge.sh
t/t7406-submodule-update.sh
t/t7407-submodule-foreach.sh
t/t7500-commit.sh
t/t7500/edit-content [new file with mode: 0755]
t/t7501-commit.sh
t/t7502-commit.sh
t/t7508-status.sh
t/t7509-commit.sh
t/t7600-merge.sh
t/t7601-merge-pull-config.sh
t/t7602-merge-octopus-many.sh
t/t7606-merge-custom.sh
t/t7607-merge-overwrite.sh
t/t7608-merge-messages.sh
t/t7609-merge-co-error-msgs.sh [new file with mode: 0755]
t/t7610-mergetool.sh
t/t7611-merge-abort.sh [new file with mode: 0755]
t/t7700-repack.sh
t/t7800-difftool.sh
t/t7810-grep.sh
t/t8002-blame.sh
t/t8003-blame-corner-cases.sh [new file with mode: 0755]
t/t8003-blame.sh [deleted file]
t/t8004-blame-with-conflicts.sh [new file with mode: 0755]
t/t8004-blame.sh [deleted file]
t/t8006-blame-textconv.sh
t/t8007-cat-file-textconv.sh
t/t9001-send-email.sh
t/t9010-svn-fe.sh [new file with mode: 0755]
t/t9100-git-svn-basic.sh
t/t9101-git-svn-props.sh
t/t9102-git-svn-deep-rmdir.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/t9114-git-svn-dcommit-merge.sh
t/t9115-git-svn-dcommit-funky-renames.sh
t/t9116-git-svn-log.sh
t/t9119-git-svn-info.sh
t/t9120-git-svn-clone-with-percent-escapes.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/t9130-git-svn-authors-file.sh
t/t9131-git-svn-empty-symlink.sh
t/t9137-git-svn-dcommit-clobber-series.sh
t/t9142-git-svn-shallow-clone.sh
t/t9143-git-svn-gc.sh
t/t9146-git-svn-empty-dirs.sh
t/t9151-svn-mergeinfo.sh
t/t9157-git-svn-fetch-merge.sh [new file with mode: 0755]
t/t9158-git-svn-mergeinfo.sh [new file with mode: 0755]
t/t9200-git-cvsexportcommit.sh
t/t9300-fast-import.sh
t/t9301-fast-import-notes.sh
t/t9350-fast-export.sh
t/t9400-git-cvsserver-server.sh
t/t9401-git-cvsserver-crlf.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9501-gitweb-standalone-http-status.sh
t/t9600-cvsimport.sh
t/t9601-cvsimport-vendor-branch.sh
t/t9602-cvsimport-branches-tags.sh
t/t9603-cvsimport-patchsets.sh
t/t9700/test.pl
t/test-lib.sh
t/test-terminal.perl [new file with mode: 0755]
tag.c
tag.h
test-line-buffer.c [new file with mode: 0644]
test-mktemp.c [new file with mode: 0644]
test-obj-pool.c [new file with mode: 0644]
test-parse-options.c
test-string-pool.c [new file with mode: 0644]
test-subprocess.c [new file with mode: 0644]
test-svn-fe.c [new file with mode: 0644]
test-treap.c [new file with mode: 0644]
thread-utils.h
trace.c
transport-helper.c
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
unpack-trees.c
unpack-trees.h
upload-pack.c
url.c
url.h
userdiff.c
vcs-svn/LICENSE [new file with mode: 0644]
vcs-svn/fast_export.c [new file with mode: 0644]
vcs-svn/fast_export.h [new file with mode: 0644]
vcs-svn/line_buffer.c [new file with mode: 0644]
vcs-svn/line_buffer.h [new file with mode: 0644]
vcs-svn/line_buffer.txt [new file with mode: 0644]
vcs-svn/obj_pool.h [new file with mode: 0644]
vcs-svn/repo_tree.c [new file with mode: 0644]
vcs-svn/repo_tree.h [new file with mode: 0644]
vcs-svn/string_pool.c [new file with mode: 0644]
vcs-svn/string_pool.h [new file with mode: 0644]
vcs-svn/string_pool.txt [new file with mode: 0644]
vcs-svn/svndump.c [new file with mode: 0644]
vcs-svn/svndump.h [new file with mode: 0644]
vcs-svn/trp.h [new file with mode: 0644]
vcs-svn/trp.txt [new file with mode: 0644]
walker.c
wrapper.c
ws.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff/xemit.c
xdiff/xmacros.h
xdiff/xmerge.c
xdiff/xutils.c
zlib.c [new file with mode: 0644]

index 14e2b6bde9bef55d678da8ba44dc180b039cd3ac..3aa0834ead79d84deb2c9013932b07a93a38c0e0 100644 (file)
 /git-remote-https
 /git-remote-ftp
 /git-remote-ftps
+/git-remote-fd
+/git-remote-ext
 /git-remote-testgit
 /git-repack
 /git-replace
 /git-show-branch
 /git-show-index
 /git-show-ref
+/git-skew
 /git-stage
 /git-stash
 /git-status
 /gitk-git/gitk-wish
 /gitweb/GITWEB-BUILD-OPTIONS
 /gitweb/gitweb.cgi
-/gitweb/gitweb.min.*
+/gitweb/static/gitweb.min.*
 /test-chmtime
 /test-ctype
 /test-date
 /test-dump-cache-tree
 /test-genrandom
 /test-index-version
+/test-line-buffer
 /test-match-trees
+/test-obj-pool
 /test-parse-options
 /test-path-utils
 /test-run-command
 /test-sha1
 /test-sigchain
+/test-string-pool
+/test-subprocess
+/test-svn-fe
+/test-treap
 /common-cmds.h
 *.tar.gz
 *.dsc
 *.[aos]
 *.py[co]
 .depend/
+*.gcda
+*.gcno
+*.gcov
+/coverage-untested-functions
+/cover_db/
+/cover_db_html/
 *+
 /config.mak
 /autom4te.cache
index a8091eb5dfa430bf1b0537da47a31e7cf88d8622..19c87262322cf5acc9d4fd5fa06f96eb3e988049 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -36,7 +36,7 @@ Lars Doelle <lars.doelle@on-line ! de>
 Lars Doelle <lars.doelle@on-line.de>
 Li Hong <leehong@pku.edu.cn>
 Lukas Sandström <lukass@etek.chalmers.se>
-Martin Langhoff <martin@catalyst.net.nz>
+Martin Langhoff <martin@laptop.org>
 Michael Coleman <tutufan@gmail.com>
 Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm>
 Michael W. Olson <mwolson@gnu.org>
index b8bf618a30fd32a014e41e1ba9914f5e652bdefd..ba2006d892ec09d606f8846588c7727396b1ee28 100644 (file)
@@ -31,25 +31,36 @@ But if you must have a list of rules, here they are.
 
 For shell scripts specifically (not exhaustive):
 
+ - We use tabs for indentation.
+
+ - Case arms are indented at the same depth as case and esac lines.
+
  - We prefer $( ... ) for command substitution; unlike ``, it
    properly nests.  It should have been the way Bourne spelled
    it from day one, but unfortunately isn't.
 
- - We use ${parameter-word} and its [-=?+] siblings, and their
-   colon'ed "unset or null" form.
+ - We use POSIX compliant parameter substitutions and avoid bashisms;
+   namely:
 
- We use ${parameter#word} and its [#%] siblings, and their
-   doubled "longest matching" form.
  - We use ${parameter-word} and its [-=?+] siblings, and their
+     colon'ed "unset or null" form.
 
- - We use Arithmetic Expansion $(( ... )).
+   - We use ${parameter#word} and its [#%] siblings, and their
+     doubled "longest matching" form.
 
- - No "Substring Expansion" ${parameter:offset:length}.
  - No "Substring Expansion" ${parameter:offset:length}.
 
- - No shell arrays.
  - No shell arrays.
 
- - No strlen ${#parameter}.
  - No strlen ${#parameter}.
 
- - No regexp ${parameter/pattern/string}.
+   - No pattern replacement ${parameter/pattern/string}.
+
+ - We use Arithmetic Expansion $(( ... )).
+
+ - Inside Arithmetic Expansion, spell shell variables with $ in front
+   of them, as some shells do not grok $((x)) while accepting $(($x))
+   just fine (e.g. dash older than 0.5.4).
 
  - We do not use Process Substitution <(list) or >(list).
 
@@ -132,3 +143,55 @@ For C programs:
 
  - When we pass <string, length> pair to functions, we should try to
    pass them in that order.
+
+Writing Documentation:
+
+ Every user-visible change should be reflected in the documentation.
+ The same general rule as for code applies -- imitate the existing
+ conventions.  A few commented examples follow to provide reference
+ when writing or modifying command usage strings and synopsis sections
+ in the manual pages:
+
+ Placeholders are enclosed in angle brackets:
+   <file>
+   --sort=<key>
+   --abbrev[=<n>]
+
+ Possibility of multiple occurrences is indicated by three dots:
+   <file>...
+   (One or more of <file>.)
+
+ Optional parts are enclosed in square brackets:
+   [<extra>]
+   (Zero or one <extra>.)
+
+   --exec-path[=<path>]
+   (Option with an optional argument.  Note that the "=" is inside the
+   brackets.)
+
+   [<patch>...]
+   (Zero or more of <patch>.  Note that the dots are inside, not
+   outside the brackets.)
+
+ Multiple alternatives are indicated with vertical bar:
+   [-q | --quiet]
+   [--utf8 | --no-utf8]
+
+ Parentheses are used for grouping:
+   [(<rev>|<range>)...]
+   (Any number of either <rev> or <range>.  Parens are needed to make
+   it clear that "..." pertains to both <rev> and <range>.)
+
+   [(-p <parent>)...]
+   (Any number of option -p, each with one <parent> argument.)
+
+   git remote set-head <name> (-a | -d | <branch>)
+   (One and only one of "-a", "-d" or "<branch>" _must_ (no square
+   brackets) be provided.)
+
+ And a somewhat more contrived example:
+   --diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]
+   Here "=" is outside the brackets, because "--diff-filter=" is a
+   valid usage.  "*" has its own pair of brackets, because it can
+   (optionally) be specified only when one or more of the letters is
+   also provided.
index a4c4063e50d3eb3bd36d94b399f790e56fe4b050..36989b7f6541cb8444385b64d2c955ae2a1f6d1b 100644 (file)
@@ -63,35 +63,28 @@ endif
 
 #
 # For asciidoc ...
-#      -7.1.2, no extra settings are needed.
-#      8.0-,   set ASCIIDOC8.
+#      -7.1.2, set ASCIIDOC7
+#      8.0-,   no extra settings are needed
 #
 
 #
 # For docbook-xsl ...
-#      -1.68.1,        set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0)
-#      1.69.0,         no extra settings are needed?
+#      -1.68.1,        no extra settings are needed?
+#      1.69.0,         set ASCIIDOC_ROFF?
 #      1.69.1-1.71.0,  set DOCBOOK_SUPPRESS_SP?
-#      1.71.1,         no extra settings are needed?
+#      1.71.1,         set ASCIIDOC_ROFF?
 #      1.72.0,         set DOCBOOK_XSL_172.
-#      1.73.0-,        set ASCIIDOC_NO_ROFF
+#      1.73.0-,        no extra settings are needed
 #
 
-#
-# If you had been using DOCBOOK_XSL_172 in an attempt to get rid
-# of 'the ".ft C" problem' in your generated manpages, and you
-# instead ended up with weird characters around callouts, try
-# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8).
-#
-
-ifdef ASCIIDOC8
+ifndef ASCIIDOC7
 ASCIIDOC_EXTRA += -a asciidoc7compatible -a no-inline-literal
 endif
 ifdef DOCBOOK_XSL_172
 ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
 MANPAGE_XSL = manpage-1.72.xsl
 else
-       ifdef ASCIIDOC_NO_ROFF
+       ifndef ASCIIDOC_ROFF
        # docbook-xsl after 1.72 needs the regular XSL, but will not
        # pass-thru raw roff codes from asciidoc.conf, so turn them off.
        ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
@@ -279,7 +272,7 @@ $(patsubst %,%.html,$(API_DOCS) technical/api-index): %.html : %.txt
 XSLT = docbook.xsl
 XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
 
-user-manual.html: user-manual.xml
+user-manual.html: user-manual.xml $(XSLT)
        $(QUIET_XSLTPROC)$(RM) $@+ $@ && \
        xsltproc $(XSLTOPTS) -o $@+ $(XSLT) $< && \
        mv $@+ $@
diff --git a/Documentation/RelNotes-1.5.0.1.txt b/Documentation/RelNotes-1.5.0.1.txt
deleted file mode 100644 (file)
index fea3f99..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-GIT v1.5.0.1 Release Notes
-==========================
-
-Fixes since v1.5.0
-------------------
-
-* Documentation updates
-
-  - Clarifications and corrections to 1.5.0 release notes.
-
-  - The main documentation did not link to git-remote documentation.
-
-  - Clarified introductory text of git-rebase documentation.
-
-  - Converted remaining mentions of update-index on Porcelain
-    documents to git-add/git-rm.
-
-  - Some i18n.* configuration variables were incorrectly
-    described as core.*; fixed.
-
-* Bugfixes
-
-  - git-add and git-update-index on a filesystem on which
-    executable bits are unreliable incorrectly reused st_mode
-    bits even when the path changed between symlink and regular
-    file.
-
-  - git-daemon marks the listening sockets with FD_CLOEXEC so
-    that it won't be leaked into the children.
-
-  - segfault from git-blame when the mandatory pathname
-    parameter was missing was fixed; usage() message is given
-    instead.
-
-  - git-rev-list did not read $GIT_DIR/config file, which means
-    that did not honor i18n.logoutputencoding correctly.
-
-* Tweaks
-
-  - sliding mmap() inefficiently mmaped the same region of a
-    packfile with an access pattern that used objects in the
-    reverse order.  This has been made more efficient.
diff --git a/Documentation/RelNotes-1.5.0.2.txt b/Documentation/RelNotes-1.5.0.2.txt
deleted file mode 100644 (file)
index b061e50..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-GIT v1.5.0.2 Release Notes
-==========================
-
-Fixes since v1.5.0.1
---------------------
-
-* Bugfixes
-
-  - Automated merge conflict handling when changes to symbolic
-    links conflicted were completely broken.  The merge-resolve
-    strategy created a regular file with conflict markers in it
-    in place of the symbolic link.  The default strategy,
-    merge-recursive was even more broken.  It removed the path
-    that was pointed at by the symbolic link.  Both of these
-    problems have been fixed.
-
-  - 'git diff maint master next' did not correctly give combined
-    diff across three trees.
-
-  - 'git fast-import' portability fix for Solaris.
-
-  - 'git show-ref --verify' without arguments did not error out
-    but segfaulted.
-
-  - 'git diff :tracked-file `pwd`/an-untracked-file' gave an extra
-    slashes after a/ and b/.
-
-  - 'git format-patch' produced too long filenames if the commit
-    message had too long line at the beginning.
-
-  - Running 'make all' and then without changing anything
-    running 'make install' still rebuilt some files.  This
-    was inconvenient when building as yourself and then
-    installing as root (especially problematic when the source
-    directory is on NFS and root is mapped to nobody).
-
-  - 'git-rerere' failed to deal with two unconflicted paths that
-    sorted next to each other.
-
-  - 'git-rerere' attempted to open(2) a symlink and failed if
-    there was a conflict.  Since a conflicting change to a
-    symlink would not benefit from rerere anyway, the command
-    now ignores conflicting changes to symlinks.
-
-  - 'git-repack' did not like to pass more than 64 arguments
-    internally to underlying 'rev-list' logic, which made it
-    impossible to repack after accumulating many (small) packs
-    in the repository.
-
-  - 'git-diff' to review the combined diff during a conflicted
-    merge were not reading the working tree version correctly
-    when changes to a symbolic link conflicted.  It should have
-    read the data using readlink(2) but read from the regular
-    file the symbolic link pointed at.
-
-  - 'git-remote' did not like period in a remote's name.
-
-* Documentation updates
-
-  - added and clarified core.bare, core.legacyheaders configurations.
-
-  - updated "git-clone --depth" documentation.
-
-
-* Assorted git-gui fixes.
diff --git a/Documentation/RelNotes-1.5.0.3.txt b/Documentation/RelNotes-1.5.0.3.txt
deleted file mode 100644 (file)
index cd500f9..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-GIT v1.5.0.3 Release Notes
-==========================
-
-Fixes since v1.5.0.2
---------------------
-
-* Bugfixes
-
-  - 'git.el' honors the commit coding system from the configuration.
-
-  - 'blameview' in contrib/ correctly digs deeper when a line is
-    clicked.
-
-  - 'http-push' correctly makes sure the remote side has leading
-    path.  Earlier it started in the middle of the path, and
-    incorrectly.
-
-  - 'git-merge' did not exit with non-zero status when the
-    working tree was dirty and cannot fast forward.  It does
-    now.
-
-  - 'cvsexportcommit' does not lose yet-to-be-used message file.
-
-  - int-vs-size_t typefix when running combined diff on files
-    over 2GB long.
-
-  - 'git apply --whitespace=strip' should not touch unmodified
-    lines.
-
-  - 'git-mailinfo' choke when a logical header line was too long.
-
-  - 'git show A..B' did not error out.  Negative ref ("not A" in
-    this example) does not make sense for the purpose of the
-    command, so now it errors out.
-
-  - 'git fmt-merge-msg --file' without file parameter did not
-    correctly error out.
-
-  - 'git archimport' barfed upon encountering a commit without
-    summary.
-
-  - 'git index-pack' did not protect itself from getting a short
-    read out of pread(2).
-
-  - 'git http-push' had a few buffer overruns.
-
-  - Build dependency fixes to rebuild fetch.o when other headers
-    change.
-
-* Documentation updates
-
-  - user-manual updates.
-
-  - Options to 'git remote add' were described insufficiently.
-
-  - Configuration format.suffix was not documented.
-
-  - Other formatting and spelling fixes.
diff --git a/Documentation/RelNotes-1.5.0.4.txt b/Documentation/RelNotes-1.5.0.4.txt
deleted file mode 100644 (file)
index feefa5d..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-GIT v1.5.0.4 Release Notes
-==========================
-
-Fixes since v1.5.0.3
---------------------
-
-* Bugfixes
-
-  - git.el does not add duplicate sign-off lines.
-
-  - git-commit shows the full stat of the resulting commit, not
-    just about the files in the current directory, when run from
-    a subdirectory.
-
-  - "git-checkout -m '@{8 hours ago}'" had a funny failure from
-    eval; fixed.
-
-  - git-gui updates.
-
-* Documentation updates
-
-* User manual updates
diff --git a/Documentation/RelNotes-1.5.0.5.txt b/Documentation/RelNotes-1.5.0.5.txt
deleted file mode 100644 (file)
index eeec3d7..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-GIT v1.5.0.5 Release Notes
-==========================
-
-Fixes since v1.5.0.3
---------------------
-
-* Bugfixes
-
-  - git-merge (hence git-pull) did not refuse fast-forwarding
-    when the working tree had local changes that would have
-    conflicted with it.
-
-  - git.el does not add duplicate sign-off lines.
-
-  - git-commit shows the full stat of the resulting commit, not
-    just about the files in the current directory, when run from
-    a subdirectory.
-
-  - "git-checkout -m '@{8 hours ago}'" had a funny failure from
-    eval; fixed.
-
-  - git-gui updates.
-
-* Documentation updates
-
-* User manual updates
diff --git a/Documentation/RelNotes-1.5.0.6.txt b/Documentation/RelNotes-1.5.0.6.txt
deleted file mode 100644 (file)
index c02015a..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-GIT v1.5.0.6 Release Notes
-==========================
-
-Fixes since v1.5.0.5
---------------------
-
-* Bugfixes
-
-  - a handful small fixes to gitweb.
-
-  - build procedure for user-manual is fixed not to require locally
-    installed stylesheets.
-
-  - "git commit $paths" on paths whose earlier contents were
-    already updated in the index were failing out.
-
-* Documentation
-
-  - user-manual has better cross references.
-
-  - gitweb installation/deployment procedure is now documented.
diff --git a/Documentation/RelNotes-1.5.0.7.txt b/Documentation/RelNotes-1.5.0.7.txt
deleted file mode 100644 (file)
index 670ad32..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-GIT v1.5.0.7 Release Notes
-==========================
-
-Fixes since v1.5.0.6
---------------------
-
-* Bugfixes
-
-  - git-upload-pack failed to close unused pipe ends, resulting
-    in many zombies to hang around.
-
-  - git-rerere was recording the contents of earlier hunks
-    duplicated in later hunks.  This prevented resolving the same
-    conflict when performing the same merge the other way around.
-
-* Documentation
-
-  - a few documentation fixes from Debian package maintainer.
diff --git a/Documentation/RelNotes-1.5.0.txt b/Documentation/RelNotes-1.5.0.txt
deleted file mode 100644 (file)
index daf4bdb..0000000
+++ /dev/null
@@ -1,469 +0,0 @@
-GIT v1.5.0 Release Notes
-========================
-
-Old news
---------
-
-This section is for people who are upgrading from ancient
-versions of git.  Although all of the changes in this section
-happened before the current v1.4.4 release, they are summarized
-here in the v1.5.0 release notes for people who skipped earlier
-versions.
-
-As of git v1.5.0 there are some optional features that changes
-the repository to allow data to be stored and transferred more
-efficiently.  These features are not enabled by default, as they
-will make the repository unusable with older versions of git.
-Specifically, the available options are:
-
- - There is a configuration variable core.legacyheaders that
-   changes the format of loose objects so that they are more
-   efficient to pack and to send out of the repository over git
-   native protocol, since v1.4.2.  However, loose objects
-   written in the new format cannot be read by git older than
-   that version; people fetching from your repository using
-   older clients over dumb transports (e.g. http) using older
-   versions of git will also be affected.
-
-   To let git use the new loose object format, you have to
-   set core.legacyheaders to false.
-
- - Since v1.4.3, configuration repack.usedeltabaseoffset allows
-   packfile to be created in more space efficient format, which
-   cannot be read by git older than that version.
-
-   To let git use the new format for packfiles, you have to
-   set repack.usedeltabaseoffset to true.
-
-The above two new features are not enabled by default and you
-have to explicitly ask for them, because they make repositories
-unreadable by older versions of git, and in v1.5.0 we still do
-not enable them by default for the same reason.  We will change
-this default probably 1 year after 1.4.2's release, when it is
-reasonable to expect everybody to have new enough version of
-git.
-
- - 'git pack-refs' appeared in v1.4.4; this command allows tags
-   to be accessed much more efficiently than the traditional
-   'one-file-per-tag' format.  Older git-native clients can
-   still fetch from a repository that packed and pruned refs
-   (the server side needs to run the up-to-date version of git),
-   but older dumb transports cannot.  Packing of refs is done by
-   an explicit user action, either by use of "git pack-refs
-   --prune" command or by use of "git gc" command.
-
- - 'git -p' to paginate anything -- many commands do pagination
-   by default on a tty.  Introduced between v1.4.1 and v1.4.2;
-   this may surprise old timers.
-
- - 'git archive' superseded 'git tar-tree' in v1.4.3;
-
- - 'git cvsserver' was new invention in v1.3.0;
-
- - 'git repo-config', 'git grep', 'git rebase' and 'gitk' were
-   seriously enhanced during v1.4.0 timeperiod.
-
- - 'gitweb' became part of git.git during v1.4.0 timeperiod and
-   seriously modified since then.
-
- - reflog is an v1.4.0 invention.  This allows you to name a
-   revision that a branch used to be at (e.g. "git diff
-   master@{yesterday} master" allows you to see changes since
-   yesterday's tip of the branch).
-
-
-Updates in v1.5.0 since v1.4.4 series
--------------------------------------
-
-* Index manipulation
-
- - git-add is to add contents to the index (aka "staging area"
-   for the next commit), whether the file the contents happen to
-   be is an existing one or a newly created one.
-
- - git-add without any argument does not add everything
-   anymore.  Use 'git-add .' instead.  Also you can add
-   otherwise ignored files with an -f option.
-
- - git-add tries to be more friendly to users by offering an
-   interactive mode ("git-add -i").
-
- - git-commit <path> used to refuse to commit if <path> was
-   different between HEAD and the index (i.e. update-index was
-   used on it earlier).  This check was removed.
-
- - git-rm is much saner and safer.  It is used to remove paths
-   from both the index file and the working tree, and makes sure
-   you are not losing any local modification before doing so.
-
- - git-reset <tree> <paths>... can be used to revert index
-   entries for selected paths.
-
- - git-update-index is much less visible.  Many suggestions to
-   use the command in git output and documentation have now been
-   replaced by simpler commands such as "git add" or "git rm".
-
-
-* Repository layout and objects transfer
-
- - The data for origin repository is stored in the configuration
-   file $GIT_DIR/config, not in $GIT_DIR/remotes/, for newly
-   created clones.  The latter is still supported and there is
-   no need to convert your existing repository if you are
-   already comfortable with your workflow with the layout.
-
- - git-clone always uses what is known as "separate remote"
-   layout for a newly created repository with a working tree.
-
-   A repository with the separate remote layout starts with only
-   one default branch, 'master', to be used for your own
-   development.  Unlike the traditional layout that copied all
-   the upstream branches into your branch namespace (while
-   renaming their 'master' to your 'origin'), the new layout
-   puts upstream branches into local "remote-tracking branches"
-   with their own namespace. These can be referenced with names
-   such as "origin/$upstream_branch_name" and are stored in
-   .git/refs/remotes rather than .git/refs/heads where normal
-   branches are stored.
-
-   This layout keeps your own branch namespace less cluttered,
-   avoids name collision with your upstream, makes it possible
-   to automatically track new branches created at the remote
-   after you clone from it, and makes it easier to interact with
-   more than one remote repository (you can use "git remote" to
-   add other repositories to track).  There might be some
-   surprises:
-
-   * 'git branch' does not show the remote tracking branches.
-     It only lists your own branches.  Use '-r' option to view
-     the tracking branches.
-
-   * If you are forking off of a branch obtained from the
-     upstream, you would have done something like 'git branch
-     my-next next', because traditional layout dropped the
-     tracking branch 'next' into your own branch namespace.
-     With the separate remote layout, you say 'git branch next
-     origin/next', which allows you to use the matching name
-     'next' for your own branch.  It also allows you to track a
-     remote other than 'origin' (i.e. where you initially cloned
-     from) and fork off of a branch from there the same way
-     (e.g. "git branch mingw j6t/master").
-
-   Repositories initialized with the traditional layout continue
-   to work.
-
- - New branches that appear on the origin side after a clone is
-   made are also tracked automatically.  This is done with an
-   wildcard refspec "refs/heads/*:refs/remotes/origin/*", which
-   older git does not understand, so if you clone with 1.5.0,
-   you would need to downgrade remote.*.fetch in the
-   configuration file to specify each branch you are interested
-   in individually if you plan to fetch into the repository with
-   older versions of git (but why would you?).
-
- - Similarly, wildcard refspec "refs/heads/*:refs/remotes/me/*"
-   can be given to "git-push" command to update the tracking
-   branches that is used to track the repository you are pushing
-   from on the remote side.
-
- - git-branch and git-show-branch know remote tracking branches
-   (use the command line switch "-r" to list only tracked branches).
-
- - git-push can now be used to delete a remote branch or a tag.
-   This requires the updated git on the remote side (use "git
-   push <remote> :refs/heads/<branch>" to delete "branch").
-
- - git-push more aggressively keeps the transferred objects
-   packed.  Earlier we recommended to monitor amount of loose
-   objects and repack regularly, but you should repack when you
-   accumulated too many small packs this way as well.  Updated
-   git-count-objects helps you with this.
-
- - git-fetch also more aggressively keeps the transferred objects
-   packed.  This behavior of git-push and git-fetch can be
-   tweaked with a single configuration transfer.unpacklimit (but
-   usually there should not be any need for a user to tweak it).
-
- - A new command, git-remote, can help you manage your remote
-   tracking branch definitions.
-
- - You may need to specify explicit paths for upload-pack and/or
-   receive-pack due to your ssh daemon configuration on the
-   other end.  This can now be done via remote.*.uploadpack and
-   remote.*.receivepack configuration.
-
-
-* Bare repositories
-
- - Certain commands change their behavior in a bare repository
-   (i.e. a repository without associated working tree).  We use
-   a fairly conservative heuristic (if $GIT_DIR is ".git", or
-   ends with "/.git", the repository is not bare) to decide if a
-   repository is bare, but "core.bare" configuration variable
-   can be used to override the heuristic when it misidentifies
-   your repository.
-
- - git-fetch used to complain updating the current branch but
-   this is now allowed for a bare repository.  So is the use of
-   'git-branch -f' to update the current branch.
-
- - Porcelain-ish commands that require a working tree refuses to
-   work in a bare repository.
-
-
-* Reflog
-
- - Reflog records the history from the view point of the local
-   repository. In other words, regardless of the real history,
-   the reflog shows the history as seen by one particular
-   repository (this enables you to ask "what was the current
-   revision in _this_ repository, yesterday at 1pm?").  This
-   facility is enabled by default for repositories with working
-   trees, and can be accessed with the "branch@{time}" and
-   "branch@{Nth}" notation.
-
- - "git show-branch" learned showing the reflog data with the
-   new -g option.  "git log" has -g option to view reflog
-   entries in a more verbose manner.
-
- - git-branch knows how to rename branches and moves existing
-   reflog data from the old branch to the new one.
-
- - In addition to the reflog support in v1.4.4 series, HEAD
-   reference maintains its own log.  "HEAD@{5.minutes.ago}"
-   means the commit you were at 5 minutes ago, which takes
-   branch switching into account.  If you want to know where the
-   tip of your current branch was at 5 minutes ago, you need to
-   explicitly say its name (e.g. "master@{5.minutes.ago}") or
-   omit the refname altogether i.e. "@{5.minutes.ago}".
-
- - The commits referred to by reflog entries are now protected
-   against pruning.  The new command "git reflog expire" can be
-   used to truncate older reflog entries and entries that refer
-   to commits that have been pruned away previously with older
-   versions of git.
-
-   Existing repositories that have been using reflog may get
-   complaints from fsck-objects and may not be able to run
-   git-repack, if you had run git-prune from older git; please
-   run "git reflog expire --stale-fix --all" first to remove
-   reflog entries that refer to commits that are no longer in
-   the repository when that happens.
-
-
-* Crufts removal
-
- - We used to say "old commits are retrievable using reflog and
-   'master@{yesterday}' syntax as long as you haven't run
-   git-prune".  We no longer have to say the latter half of the
-   above sentence, as git-prune does not remove things reachable
-   from reflog entries.
-
- - There is a toplevel garbage collector script, 'git-gc', that
-   runs periodic cleanup functions, including 'git-repack -a -d',
-   'git-reflog expire', 'git-pack-refs --prune', and 'git-rerere
-   gc'.
-
- - The output from fsck ("fsck-objects" is called just "fsck"
-   now, but the old name continues to work) was needlessly
-   alarming in that it warned missing objects that are reachable
-   only from dangling objects.  This has been corrected and the
-   output is much more useful.
-
-
-* Detached HEAD
-
- - You can use 'git-checkout' to check out an arbitrary revision
-   or a tag as well, instead of named branches.  This will
-   dissociate your HEAD from the branch you are currently on.
-
-   A typical use of this feature is to "look around".  E.g.
-
-       $ git checkout v2.6.16
-       ... compile, test, etc.
-       $ git checkout v2.6.17
-       ... compile, test, etc.
-
- - After detaching your HEAD, you can go back to an existing
-   branch with usual "git checkout $branch".  Also you can
-   start a new branch using "git checkout -b $newbranch" to
-   start a new branch at that commit.
-
- - You can even pull from other repositories, make merges and
-   commits while your HEAD is detached.  Also you can use "git
-   reset" to jump to arbitrary commit, while still keeping your
-   HEAD detached.
-
-   Remember that a detached state is volatile, i.e. it will be forgotten
-   as soon as you move away from it with the checkout or reset command,
-   unless a branch is created from it as mentioned above.  It is also
-   possible to rescue a lost detached state from the HEAD reflog.
-
-
-* Packed refs
-
- - Repositories with hundreds of tags have been paying large
-   overhead, both in storage and in runtime, due to the
-   traditional one-ref-per-file format.  A new command,
-   git-pack-refs, can be used to "pack" them in more efficient
-   representation (you can let git-gc do this for you).
-
- - Clones and fetches over dumb transports are now aware of
-   packed refs and can download from repositories that use
-   them.
-
-
-* Configuration
-
- - configuration related to color setting are consolidated under
-   color.* namespace (older diff.color.*, status.color.* are
-   still supported).
-
- - 'git-repo-config' command is accessible as 'git-config' now.
-
-
-* Updated features
-
- - git-describe uses better criteria to pick a base ref.  It
-   used to pick the one with the newest timestamp, but now it
-   picks the one that is topologically the closest (that is,
-   among ancestors of commit C, the ref T that has the shortest
-   output from "git-rev-list T..C" is chosen).
-
- - git-describe gives the number of commits since the base ref
-   between the refname and the hash suffix.  E.g. the commit one
-   before v2.6.20-rc6 in the kernel repository is:
-
-       v2.6.20-rc5-306-ga21b069
-
-   which tells you that its object name begins with a21b069,
-   v2.6.20-rc5 is an ancestor of it (meaning, the commit
-   contains everything -rc5 has), and there are 306 commits
-   since v2.6.20-rc5.
-
- - git-describe with --abbrev=0 can be used to show only the
-   name of the base ref.
-
- - git-blame learned a new option, --incremental, that tells it
-   to output the blames as they are assigned.  A sample script
-   to use it is also included as contrib/blameview.
-
- - git-blame starts annotating from the working tree by default.
-
-
-* Less external dependency
-
- - We no longer require the "merge" program from the RCS suite.
-   All 3-way file-level merges are now done internally.
-
- - The original implementation of git-merge-recursive which was
-   in Python has been removed; we have a C implementation of it
-   now.
-
- - git-shortlog is no longer a Perl script.  It no longer
-   requires output piped from git-log; it can accept revision
-   parameters directly on the command line.
-
-
-* I18n
-
- - We have always encouraged the commit message to be encoded in
-   UTF-8, but the users are allowed to use legacy encoding as
-   appropriate for their projects.  This will continue to be the
-   case.  However, a non UTF-8 commit encoding _must_ be
-   explicitly set with i18n.commitencoding in the repository
-   where a commit is made; otherwise git-commit-tree will
-   complain if the log message does not look like a valid UTF-8
-   string.
-
- - The value of i18n.commitencoding in the originating
-   repository is recorded in the commit object on the "encoding"
-   header, if it is not UTF-8.  git-log and friends notice this,
-   and reencodes the message to the log output encoding when
-   displaying, if they are different.  The log output encoding
-   is determined by "git log --encoding=<encoding>",
-   i18n.logoutputencoding configuration, or i18n.commitencoding
-   configuration, in the decreasing order of preference, and
-   defaults to UTF-8.
-
- - Tools for e-mailed patch application now default to -u
-   behavior; i.e. it always re-codes from the e-mailed encoding
-   to the encoding specified with i18n.commitencoding.  This
-   unfortunately forces projects that have happily been using a
-   legacy encoding without setting i18n.commitencoding to set
-   the configuration, but taken with other improvement, please
-   excuse us for this very minor one-time inconvenience.
-
-
-* e-mailed patches
-
- - See the above I18n section.
-
- - git-format-patch now enables --binary without being asked.
-   git-am does _not_ default to it, as sending binary patch via
-   e-mail is unusual and is harder to review than textual
-   patches and it is prudent to require the person who is
-   applying the patch to explicitly ask for it.
-
- - The default suffix for git-format-patch output is now ".patch",
-   not ".txt".  This can be changed with --suffix=.txt option,
-   or setting the config variable "format.suffix" to ".txt".
-
-
-* Foreign SCM interfaces
-
- - git-svn now requires the Perl SVN:: libraries, the
-   command-line backend was too slow and limited.
-
- - the 'commit' subcommand of git-svn has been renamed to
-   'set-tree', and 'dcommit' is the recommended replacement for
-   day-to-day work.
-
- - git fast-import backend.
-
-
-* User support
-
- - Quite a lot of documentation updates.
-
- - Bash completion scripts have been updated heavily.
-
- - Better error messages for often used Porcelainish commands.
-
- - Git GUI.  This is a simple Tk based graphical interface for
-   common Git operations.
-
-
-* Sliding mmap
-
- - We used to assume that we can mmap the whole packfile while
-   in use, but with a large project this consumes huge virtual
-   memory space and truly huge ones would not fit in the
-   userland address space on 32-bit platforms.  We now mmap huge
-   packfile in pieces to avoid this problem.
-
-
-* Shallow clones
-
- - There is a partial support for 'shallow' repositories that
-   keeps only recent history.  A 'shallow clone' is created by
-   specifying how deep that truncated history should be
-   (e.g. "git clone --depth 5 git://some.where/repo.git").
-
-   Currently a shallow repository has number of limitations:
-
-   - Cloning and fetching _from_ a shallow clone are not
-     supported (nor tested -- so they might work by accident but
-     they are not expected to).
-
-   - Pushing from nor into a shallow clone are not expected to
-     work.
-
-   - Merging inside a shallow repository would work as long as a
-     merge base is found in the recent history, but otherwise it
-     will be like merging unrelated histories and may result in
-     huge conflicts.
-
-   but this would be more than adequate for people who want to
-   look at near the tip of a big project with a deep history and
-   send patches in e-mail format.
diff --git a/Documentation/RelNotes-1.5.1.1.txt b/Documentation/RelNotes-1.5.1.1.txt
deleted file mode 100644 (file)
index 9147121..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-GIT v1.5.1.1 Release Notes
-==========================
-
-Fixes since v1.5.1
-------------------
-
-* Documentation updates
-
-  - The --left-right option of rev-list and friends is documented.
-
-  - The documentation for cvsimport has been majorly improved.
-
-  - "git-show-ref --exclude-existing" was documented.
-
-* Bugfixes
-
-  - The implementation of -p option in "git cvsexportcommit" had
-    the meaning of -C (context reduction) option wrong, and
-    loosened the context requirements when it was told to be
-    strict.
-
-  - "git cvsserver" did not behave like the real cvsserver when
-    client side removed a file from the working tree without
-    doing anything else on the path.  In such a case, it should
-    restore it from the checked out revision.
-
-  - "git fsck" issued an alarming error message on detached
-    HEAD.  It is not an error since at least 1.5.0.
-
-  - "git send-email" produced of References header of unbounded length;
-    fixed this with line-folding.
-
-  - "git archive" to download from remote site should not
-    require you to be in a git repository, but it incorrectly
-    did.
-
-  - "git apply" ignored -p<n> for "diff --git" formatted
-    patches.
-
-  - "git rerere" recorded a conflict that had one side empty
-    (the other side adds) incorrectly; this made merging in the
-    other direction fail to use previously recorded resolution.
-
-  - t4200 test was broken where "wc -l" pads its output with
-    spaces.
-
-  - "git branch -m old new" to rename branch did not work
-    without a configuration file in ".git/config".
-
-  - The sample hook for notification e-mail was misnamed.
-
-  - gitweb did not show type-changing patch correctly in the
-    blobdiff view.
-
-  - git-svn did not error out with incorrect command line options.
-
-  - git-svn fell into an infinite loop when insanely long commit
-    message was found.
-
-  - git-svn dcommit and rebase was confused by patches that were
-    merged from another branch that is managed by git-svn.
-
-  - git-svn used to get confused when globbing remote branch/tag
-    spec (e.g. "branches = proj/branches/*:refs/remotes/origin/*")
-    is used and there was a plain file that matched the glob.
diff --git a/Documentation/RelNotes-1.5.1.2.txt b/Documentation/RelNotes-1.5.1.2.txt
deleted file mode 100644 (file)
index d884563..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-GIT v1.5.1.2 Release Notes
-==========================
-
-Fixes since v1.5.1.1
---------------------
-
-* Bugfixes
-
-  - "git clone" over http from a repository that has lost the
-    loose refs by running "git pack-refs" were broken (a code to
-    deal with this was added to "git fetch" in v1.5.0, but it
-    was missing from "git clone").
-
-  - "git diff a/ b/" incorrectly fell in "diff between two
-    filesystem objects" codepath, when the user most likely
-    wanted to limit the extent of output to two tracked
-    directories.
-
-  - git-quiltimport had the same bug as we fixed for
-    git-applymbox in v1.5.1.1 -- it gave an alarming "did not
-    have any patch" message (but did not actually fail and was
-    harmless).
-
-  - various git-svn fixes.
-
-  - Sample update hook incorrectly always refused requests to
-    delete branches through push.
-
-  - git-blame on a very long working tree path had buffer
-    overrun problem.
-
-  - git-apply did not like to be fed two patches in a row that created
-    and then modified the same file.
-
-  - git-svn was confused when a non-project was stored directly under
-    trunk/, branches/ and tags/.
-
-  - git-svn wants the Error.pm module that was at least as new
-    as what we ship as part of git; install ours in our private
-    installation location if the one on the system is older.
-
-  - An earlier update to command line integer parameter parser was
-    botched and made 'update-index --cacheinfo' completely useless.
-
-
-* Documentation updates
-
-  - Various documentation updates from J. Bruce Fields, Frank
-    Lichtenheld, Alex Riesen and others.  Andrew Ruder started a
-    war on undocumented options.
diff --git a/Documentation/RelNotes-1.5.1.3.txt b/Documentation/RelNotes-1.5.1.3.txt
deleted file mode 100644 (file)
index 876408b..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-GIT v1.5.1.3 Release Notes
-==========================
-
-Fixes since v1.5.1.2
---------------------
-
-* Bugfixes
-
-  - git-add tried to optimize by finding common leading
-    directories across its arguments but botched, causing very
-    confused behaviour.
-
-  - unofficial rpm.spec file shipped with git was letting
-    ETC_GITCONFIG set to /usr/etc/gitconfig.  Tweak the official
-    Makefile to make it harder for distro people to make the
-    same mistake, by setting the variable to /etc/gitconfig if
-    prefix is set to /usr.
-
-  - git-svn inconsistently stripped away username from the URL
-    only when svnsync_props was in use.
-
-  - git-svn got confused when handling symlinks on Mac OS.
-
-  - git-send-email was not quoting recipient names that have
-    period '.' in them.  Also it did not allow overriding
-    envelope sender, which made it impossible to send patches to
-    certain subscriber-only lists.
-
-  - built-in write_tree() routine had a sequence that renamed a
-    file that is still open, which some systems did not like.
-
-  - when memory is very tight, sliding mmap code to read
-    packfiles incorrectly closed the fd that was still being
-    used to read the pack.
-
-  - import-tars contributed front-end for fastimport was passing
-    wrong directory modes without checking.
-
-  - git-fastimport trusted its input too much and allowed to
-    create corrupt tree objects with entries without a name.
-
-  - git-fetch needlessly barfed when too long reflog action
-    description was given by the caller.
-
-Also contains various documentation updates.
diff --git a/Documentation/RelNotes-1.5.1.4.txt b/Documentation/RelNotes-1.5.1.4.txt
deleted file mode 100644 (file)
index df2f66c..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-GIT v1.5.1.4 Release Notes
-==========================
-
-Fixes since v1.5.1.3
---------------------
-
-* Bugfixes
-
-  - "git-http-fetch" did not work around a bug in libcurl
-    earlier than 7.16 (curl_multi_remove_handle() was broken).
-
-  - "git cvsserver" handles a file that was once removed and
-    then added again correctly.
-
-  - import-tars script (in contrib/) handles GNU tar archives
-    that contain pathnames longer than 100 bytes (long-link
-    extension) correctly.
-
-  - xdelta test program did not build correctly.
-
-  - gitweb sometimes tried incorrectly to apply function to
-    decode utf8 twice, resulting in corrupt output.
-
-  - "git blame -C" mishandled text at the end of a group of
-    lines.
-
-  - "git log/rev-list --boundary" did not produce output
-    correctly without --left-right option.
-
-  - Many documentation updates.
diff --git a/Documentation/RelNotes-1.5.1.5.txt b/Documentation/RelNotes-1.5.1.5.txt
deleted file mode 100644 (file)
index b0ab8eb..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-GIT v1.5.1.5 Release Notes
-==========================
-
-Fixes since v1.5.1.4
---------------------
-
-* Bugfixes
-
-  - git-send-email did not understand aliases file for mutt, which
-    allows leading whitespaces.
-
-  - git-format-patch emitted Content-Type and Content-Transfer-Encoding
-    headers for non ASCII contents, but failed to add MIME-Version.
-
-  - git-name-rev had a buffer overrun with a deep history.
-
-  - contributed script import-tars did not get the directory in
-    tar archives interpreted correctly.
-
-  - git-svn was reported to segfault for many people on list and
-    #git; hopefully this has been fixed.
-
-  - "git-svn clone" does not try to minimize the URL
-    (i.e. connect to higher level hierarchy) by default, as this
-    can prevent clone to fail if only part of the repository
-    (e.g. 'trunk') is open to public.
-
-  - "git checkout branch^0" did not detach the head when you are
-    already on 'branch'; backported the fix from the 'master'.
-
-  - "git-config section.var" did not correctly work when
-    existing configuration file had both [section] and [section "name"]
-    next to each other.
-
-  - "git clone ../other-directory" was fooled if the current
-    directory $PWD points at is a symbolic link.
-
-  - (build) tree_entry_extract() function was both static inline
-    and extern, which caused trouble compiling with Forte12
-    compilers on Sun.
-
-  - Many many documentation fixes and updates.
diff --git a/Documentation/RelNotes-1.5.1.6.txt b/Documentation/RelNotes-1.5.1.6.txt
deleted file mode 100644 (file)
index 55f3ac1..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-GIT v1.5.1.6 Release Notes
-==========================
-
-Fixes since v1.5.1.4
---------------------
-
-* Bugfixes
-
-  - git-send-email did not understand aliases file for mutt, which
-    allows leading whitespaces.
-
-  - git-format-patch emitted Content-Type and Content-Transfer-Encoding
-    headers for non ASCII contents, but failed to add MIME-Version.
-
-  - git-name-rev had a buffer overrun with a deep history.
-
-  - contributed script import-tars did not get the directory in
-    tar archives interpreted correctly.
-
-  - git-svn was reported to segfault for many people on list and
-    #git; hopefully this has been fixed.
-
-  - git-svn also had a bug to crash svnserve by sending a bad
-    sequence of requests.
-
-  - "git-svn clone" does not try to minimize the URL
-    (i.e. connect to higher level hierarchy) by default, as this
-    can prevent clone to fail if only part of the repository
-    (e.g. 'trunk') is open to public.
-
-  - "git checkout branch^0" did not detach the head when you are
-    already on 'branch'; backported the fix from the 'master'.
-
-  - "git-config section.var" did not correctly work when
-    existing configuration file had both [section] and [section "name"]
-    next to each other.
-
-  - "git clone ../other-directory" was fooled if the current
-    directory $PWD points at is a symbolic link.
-
-  - (build) tree_entry_extract() function was both static inline
-    and extern, which caused trouble compiling with Forte12
-    compilers on Sun.
-
-  - Many many documentation fixes and updates.
diff --git a/Documentation/RelNotes-1.5.1.txt b/Documentation/RelNotes-1.5.1.txt
deleted file mode 100644 (file)
index daed367..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-GIT v1.5.1 Release Notes
-========================
-
-Updates since v1.5.0
---------------------
-
-* Deprecated commands and options.
-
-  - git-diff-stages and git-resolve have been removed.
-
-* New commands and options.
-
-  - "git log" and friends take --reverse, which instructs them
-    to give their output in the order opposite from their usual.
-    They typically output from new to old, but with this option
-    their output would read from old to new.  "git shortlog"
-    usually lists older commits first, but with this option,
-    they are shown from new to old.
-
-  - "git log --pretty=format:<string>" to allow more flexible
-    custom log output.
-
-  - "git diff" learned --ignore-space-at-eol.  This is a weaker
-    form of --ignore-space-change.
-
-  - "git diff --no-index pathA pathB" can be used as diff
-    replacement with git specific enhancements.
-
-  - "git diff --no-index" can read from '-' (standard input).
-
-  - "git diff" also learned --exit-code to exit with non-zero
-    status when it found differences.  In the future we might
-    want to make this the default but that would be a rather big
-    backward incompatible change; it will stay as an option for
-    now.
-
-  - "git diff --quiet" is --exit-code with output turned off,
-    meant for scripted use to quickly determine if there is any
-    tree-level difference.
-
-  - Textual patch generation with "git diff" without -w/-b
-    option has been significantly optimized.  "git blame" got
-    faster because of the same change.
-
-  - "git log" and "git rev-list" has been optimized
-    significantly when they are used with pathspecs.
-
-  - "git branch --track" can be used to set up configuration
-    variables to help it easier to base your work on branches
-    you track from a remote site.
-
-  - "git format-patch --attach" now emits attachments.  Use
-    --inline to get an inlined multipart/mixed.
-
-  - "git name-rev" learned --refs=<pattern>, to limit the tags
-    used for naming the given revisions only to the ones
-    matching the given pattern.
-
-  - "git remote update" is to run "git fetch" for defined remotes
-    to update tracking branches.
-
-  - "git cvsimport" can now take '-d' to talk with a CVS
-    repository different from what are recorded in CVS/Root
-    (overriding it with environment CVSROOT does not work).
-
-  - "git bundle" can help sneaker-netting your changes between
-    repositories.
-
-  - "git mergetool" can help 3-way file-level conflict
-    resolution with your favorite graphical merge tools.
-
-  - A new configuration "core.symlinks" can be used to disable
-    symlinks on filesystems that do not support them; they are
-    checked out as regular files instead.
-
-  - You can name a commit object with its first line of the
-    message.  The syntax to use is ':/message text'.  E.g.
-
-    $ git show ":/object name: introduce ':/<oneline prefix>' notation"
-
-    means the same thing as:
-
-    $ git show 28a4d940443806412effa246ecc7768a21553ec7
-
-  - "git bisect" learned a new command "run" that takes a script
-    to run after each revision is checked out to determine if it
-    is good or bad, to automate the bisection process.
-
-  - "git log" family learned a new traversal option --first-parent,
-    which does what the name suggests.
-
-
-* Updated behavior of existing commands.
-
-  - "git-merge-recursive" used to barf when there are more than
-    one common ancestors for the merge, and merging them had a
-    rename/rename conflict.  This has been fixed.
-
-  - "git fsck" does not barf on corrupt loose objects.
-
-  - "git rm" does not remove newly added files without -f.
-
-  - "git archimport" allows remapping when coming up with git
-    branch names from arch names.
-
-  - git-svn got almost a rewrite.
-
-  - core.autocrlf configuration, when set to 'true', makes git
-    to convert CRLF at the end of lines in text files to LF when
-    reading from the filesystem, and convert in reverse when
-    writing to the filesystem.  The variable can be set to
-    'input', in which case the conversion happens only while
-    reading from the filesystem but files are written out with
-    LF at the end of lines.  Currently, which paths to consider
-    'text' (i.e. be subjected to the autocrlf mechanism) is
-    decided purely based on the contents, but the plan is to
-    allow users to explicitly override this heuristic based on
-    paths.
-
-  - The behavior of 'git-apply', when run in a subdirectory,
-    without --index nor --cached were inconsistent with that of
-    the command with these options.  This was fixed to match the
-    behavior with --index.  A patch that is meant to be applied
-    with -p1 from the toplevel of the project tree can be
-    applied with any custom -p<n> option.  A patch that is not
-    relative to the toplevel needs to be applied with -p<n>
-    option with or without --index (or --cached).
-
-  - "git diff" outputs a trailing HT when pathnames have embedded
-    SP on +++/--- header lines, in order to help "GNU patch" to
-    parse its output.  "git apply" was already updated to accept
-    this modified output format since ce74618d (Sep 22, 2006).
-
-  - "git cvsserver" runs hooks/update and honors its exit status.
-
-  - "git cvsserver" can be told to send everything with -kb.
-
-  - "git diff --check" also honors the --color output option.
-
-  - "git name-rev" used to stress the fact that a ref is a tag too
-    much, by saying something like "v1.2.3^0~22".  It now says
-    "v1.2.3~22" in such a case (it still says "v1.2.3^0" if it does
-    not talk about an ancestor of the commit that is tagged, which
-    makes sense).
-
-  - "git rev-list --boundary" now shows boundary markers for the
-    commits omitted by --max-age and --max-count condition.
-
-  - The configuration mechanism now reads $(prefix)/etc/gitconfig.
-
-  - "git apply --verbose" shows what preimage lines were wanted
-    when it couldn't find them.
-
-  - "git status" in a read-only repository got a bit saner.
-
-  - "git fetch" (hence "git clone" and "git pull") are less
-    noisy when the output does not go to tty.
-
-  - "git fetch" between repositories with many refs were slow
-    even when there are not many changes that needed
-    transferring.  This has been sped up by partially rewriting
-    the heaviest parts in C.
-
-  - "git mailinfo" which splits an e-mail into a patch and the
-    meta-information was rewritten, thanks to Don Zickus.  It
-    handles nested multipart better.  The command was broken for
-    a brief period on 'master' branch since 1.5.0 but the
-    breakage is fixed now.
-
-  - send-email learned configurable bcc and chain-reply-to.
-
-  - "git remote show $remote" also talks about branches that
-    would be pushed if you run "git push remote".
-
-  - Using objects from packs is now seriously optimized by clever
-    use of a cache.  This should be most noticeable in git-log
-    family of commands that involve reading many tree objects.
-    In addition, traversing revisions while filtering changes
-    with pathspecs is made faster by terminating the comparison
-    between the trees as early as possible.
-
-
-* Hooks
-
-  - The part to send out notification e-mails was removed from
-    the sample update hook, as it was not an appropriate place
-    to do so.  The proper place to do this is the new post-receive
-    hook.  An example hook has been added to contrib/hooks/.
-
-
-* Others
-
-  - git-revert, git-gc and git-cherry-pick are now built-ins.
-
-Fixes since v1.5.0
-------------------
-
-These are all in v1.5.0.x series.
-
-* Documentation updates
-
-  - Clarifications and corrections to 1.5.0 release notes.
-
-  - The main documentation did not link to git-remote documentation.
-
-  - Clarified introductory text of git-rebase documentation.
-
-  - Converted remaining mentions of update-index on Porcelain
-    documents to git-add/git-rm.
-
-  - Some i18n.* configuration variables were incorrectly
-    described as core.*; fixed.
-
-  - added and clarified core.bare, core.legacyheaders configurations.
-
-  - updated "git-clone --depth" documentation.
-
-  - user-manual updates.
-
-  - Options to 'git remote add' were described insufficiently.
-
-  - Configuration format.suffix was not documented.
-
-  - Other formatting and spelling fixes.
-
-  - user-manual has better cross references.
-
-  - gitweb installation/deployment procedure is now documented.
-
-
-* Bugfixes
-
-  - git-upload-pack closes unused pipe ends; earlier this caused
-    many zombies to hang around.
-
-  - git-rerere was recording the contents of earlier hunks
-    duplicated in later hunks.  This prevented resolving the same
-    conflict when performing the same merge the other way around.
-
-  - git-add and git-update-index on a filesystem on which
-    executable bits are unreliable incorrectly reused st_mode
-    bits even when the path changed between symlink and regular
-    file.
-
-  - git-daemon marks the listening sockets with FD_CLOEXEC so
-    that it won't be leaked into the children.
-
-  - segfault from git-blame when the mandatory pathname
-    parameter was missing was fixed; usage() message is given
-    instead.
-
-  - git-rev-list did not read $GIT_DIR/config file, which means
-    that did not honor i18n.logoutputencoding correctly.
-
-  - Automated merge conflict handling when changes to symbolic
-    links conflicted were completely broken.  The merge-resolve
-    strategy created a regular file with conflict markers in it
-    in place of the symbolic link.  The default strategy,
-    merge-recursive was even more broken.  It removed the path
-    that was pointed at by the symbolic link.  Both of these
-    problems have been fixed.
-
-  - 'git diff maint master next' did not correctly give combined
-    diff across three trees.
-
-  - 'git fast-import' portability fix for Solaris.
-
-  - 'git show-ref --verify' without arguments did not error out
-    but segfaulted.
-
-  - 'git diff :tracked-file `pwd`/an-untracked-file' gave an extra
-    slashes after a/ and b/.
-
-  - 'git format-patch' produced too long filenames if the commit
-    message had too long line at the beginning.
-
-  - Running 'make all' and then without changing anything
-    running 'make install' still rebuilt some files.  This
-    was inconvenient when building as yourself and then
-    installing as root (especially problematic when the source
-    directory is on NFS and root is mapped to nobody).
-
-  - 'git-rerere' failed to deal with two unconflicted paths that
-    sorted next to each other.
-
-  - 'git-rerere' attempted to open(2) a symlink and failed if
-    there was a conflict.  Since a conflicting change to a
-    symlink would not benefit from rerere anyway, the command
-    now ignores conflicting changes to symlinks.
-
-  - 'git-repack' did not like to pass more than 64 arguments
-    internally to underlying 'rev-list' logic, which made it
-    impossible to repack after accumulating many (small) packs
-    in the repository.
-
-  - 'git-diff' to review the combined diff during a conflicted
-    merge were not reading the working tree version correctly
-    when changes to a symbolic link conflicted.  It should have
-    read the data using readlink(2) but read from the regular
-    file the symbolic link pointed at.
-
-  - 'git-remote' did not like period in a remote's name.
-
-  - 'git.el' honors the commit coding system from the configuration.
-
-  - 'blameview' in contrib/ correctly digs deeper when a line is
-    clicked.
-
-  - 'http-push' correctly makes sure the remote side has leading
-    path.  Earlier it started in the middle of the path, and
-    incorrectly.
-
-  - 'git-merge' did not exit with non-zero status when the
-    working tree was dirty and cannot fast forward.  It does
-    now.
-
-  - 'cvsexportcommit' does not lose yet-to-be-used message file.
-
-  - int-vs-size_t typefix when running combined diff on files
-    over 2GB long.
-
-  - 'git apply --whitespace=strip' should not touch unmodified
-    lines.
-
-  - 'git-mailinfo' choke when a logical header line was too long.
-
-  - 'git show A..B' did not error out.  Negative ref ("not A" in
-    this example) does not make sense for the purpose of the
-    command, so now it errors out.
-
-  - 'git fmt-merge-msg --file' without file parameter did not
-    correctly error out.
-
-  - 'git archimport' barfed upon encountering a commit without
-    summary.
-
-  - 'git index-pack' did not protect itself from getting a short
-    read out of pread(2).
-
-  - 'git http-push' had a few buffer overruns.
-
-  - Build dependency fixes to rebuild fetch.o when other headers
-    change.
-
-  - git.el does not add duplicate sign-off lines.
-
-  - git-commit shows the full stat of the resulting commit, not
-    just about the files in the current directory, when run from
-    a subdirectory.
-
-  - "git-checkout -m '@{8 hours ago}'" had a funny failure from
-    eval; fixed.
-
-  - git-merge (hence git-pull) did not refuse fast-forwarding
-    when the working tree had local changes that would have
-    conflicted with it.
-
-  - a handful small fixes to gitweb.
-
-  - build procedure for user-manual is fixed not to require locally
-    installed stylesheets.
-
-  - "git commit $paths" on paths whose earlier contents were
-    already updated in the index were failing out.
-
-
-* Tweaks
-
-  - sliding mmap() inefficiently mmaped the same region of a
-    packfile with an access pattern that used objects in the
-    reverse order.  This has been made more efficient.
diff --git a/Documentation/RelNotes-1.5.2.1.txt b/Documentation/RelNotes-1.5.2.1.txt
deleted file mode 100644 (file)
index ebf20e2..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-GIT v1.5.2.1 Release Notes
-==========================
-
-Fixes since v1.5.2
-------------------
-
-* Bugfixes
-
-  - Temporary files that are used when invoking external diff
-    programs did not tolerate a long TMPDIR.
-
-  - git-daemon did not notice when it could not write into its
-    pid file.
-
-  - git-status did not honor core.excludesFile configuration like
-    git-add did.
-
-  - git-annotate did not work from a subdirectory while
-    git-blame did.
-
-  - git-cvsserver should have disabled access to a repository
-    with "gitcvs.pserver.enabled = false" set even when
-    "gitcvs.enabled = true" was set at the same time.  It
-    didn't.
-
-  - git-cvsimport did not work correctly in a repository with
-    its branch heads were packed with pack-refs.
-
-  - ident unexpansion to squash "$Id: xxx $" that is in the
-    repository copy removed incorrect number of bytes.
-
-  - git-svn misbehaved when the subversion repository did not
-    provide MD5 checksums for files.
-
-  - git rebase (and git am) misbehaved on commits that have '\n'
-    (literally backslash and en, not a linefeed) in the title.
-
-  - code to decode base85 used in binary patches had one error
-    return codepath wrong.
-
-  - RFC2047 Q encoding output by git-format-patch used '_' for a
-    space, which is not understood by some programs.  It uses =20
-    which is safer.
-
-  - git-fastimport --import-marks was broken; fixed.
-
-  - A lot of documentation updates, clarifications and fixes.
-
---
-exec >/var/tmp/1
-O=v1.5.2-65-g996e2d6
-echo O=`git describe refs/heads/maint`
-git shortlog --no-merges $O..refs/heads/maint
diff --git a/Documentation/RelNotes-1.5.2.2.txt b/Documentation/RelNotes-1.5.2.2.txt
deleted file mode 100644 (file)
index 7bfa341..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-GIT v1.5.2.2 Release Notes
-==========================
-
-Fixes since v1.5.2.1
---------------------
-
-* Usability fix
-
-  - git-gui is shipped with its updated blame interface.  It is
-    rumored that the older one was not just unusable but was
-    active health hazard, but this one is actually pretty.
-    Please see for yourself.
-
-* Bugfixes
-
-  - "git checkout fubar" was utterly confused when there is a
-    branch fubar and a tag fubar at the same time.  It correctly
-    checks out the branch fubar now.
-
-  - "git clone /path/foo" to clone a local /path/foo.git
-    repository left an incorrect configuration.
-
-  - "git send-email" correctly unquotes RFC 2047 quoted names in
-    the patch-email before using their values.
-
-  - We did not accept number of seconds since epoch older than
-    year 2000 as a valid timestamp.  We now interpret positive
-    integers more than 8 digits as such, which allows us to
-    express timestamps more recent than March 1973.
-
-  - git-cvsimport did not work when you have GIT_DIR to point
-    your repository at a nonstandard location.
-
-  - Some systems (notably, Solaris) lack hstrerror() to make
-    h_errno human readable; prepare a replacement
-    implementation.
-
-  - .gitignore file listed git-core.spec but what we generate is
-    git.spec, and nobody noticed for a long time.
-
-  - "git-merge-recursive" does not try to run file level merge
-    on binary files.
-
-  - "git-branch --track" did not create tracking configuration
-    correctly when the branch name had slash in it.
-
-  - The email address of the user specified with user.email
-    configuration was overridden by EMAIL environment variable.
-
-  - The tree parser did not warn about tree entries with
-    nonsense file modes, and assumed they must be blobs.
-
-  - "git log -z" without any other request to generate diff still
-    invoked the diff machinery, wasting cycles.
-
-* Documentation
-
-  - Many updates to fix stale or missing documentation.
-
-  - Although our documentation was primarily meant to be formatted
-    with AsciiDoc7, formatting with AsciiDoc8 is supported better.
diff --git a/Documentation/RelNotes-1.5.2.3.txt b/Documentation/RelNotes-1.5.2.3.txt
deleted file mode 100644 (file)
index addb229..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-GIT v1.5.2.3 Release Notes
-==========================
-
-Fixes since v1.5.2.2
---------------------
-
- * Bugfixes
-
-   - Version 2 pack index format was introduced in version 1.5.2
-     to support pack files that has offset that cannot be
-     represented in 32-bit.  The runtime code to validate such
-     an index mishandled such an index for an empty pack.
-
-   - Commit walkers (most notably, fetch over http protocol)
-     tried to traverse commit objects contained in trees (aka
-     subproject); they shouldn't.
-
-   - A build option NO_R_TO_GCC_LINKER was not explained in Makefile
-     comment correctly.
-
- * Documentation Fixes and Updates
-
-   - git-config --regexp was not documented properly.
-
-   - git-repack -a was not documented properly.
-
-   - git-remote -n was not documented properly.
diff --git a/Documentation/RelNotes-1.5.2.4.txt b/Documentation/RelNotes-1.5.2.4.txt
deleted file mode 100644 (file)
index 75cff47..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-GIT v1.5.2.4 Release Notes
-==========================
-
-Fixes since v1.5.2.3
---------------------
-
- * Bugfixes
-
-   - "git-gui" bugfixes, including a handful fixes to run it
-     better on Cygwin/MSYS.
-
-   - "git checkout" failed to switch back and forth between
-     branches, one of which has "frotz -> xyzzy" symlink and
-     file "xyzzy/filfre", while the other one has a file
-     "frotz/filfre".
-
-   - "git prune" used to segfault upon seeing a commit that is
-     referred to by a tree object (aka "subproject").
-
-   - "git diff --name-status --no-index" mishandled an added file.
-
-   - "git apply --reverse --whitespace=warn" still complained
-     about whitespaces that a forward application would have
-     introduced.
-
- * Documentation Fixes and Updates
-
-   - A handful documentation updates.
diff --git a/Documentation/RelNotes-1.5.2.5.txt b/Documentation/RelNotes-1.5.2.5.txt
deleted file mode 100644 (file)
index e8281c7..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-GIT v1.5.2.5 Release Notes
-==========================
-
-Fixes since v1.5.2.4
---------------------
-
- * Bugfixes
-
-   - "git add -u" had a serious data corruption problem in one
-     special case (when the changes to a subdirectory's files
-     consist only deletion of files).
-
-   - "git add -u <path>" did not work from a subdirectory.
-
-   - "git apply" left an empty directory after all its files are
-     renamed away.
-
-   - "git $anycmd foo/bar", when there is a file 'foo' in the
-     working tree, complained that "git $anycmd foo/bar --" form
-     should be used to disambiguate between revs and files,
-     which was completely bogus.
-
-   - "git checkout-index" and other commands that checks out
-     files to the work tree tried unlink(2) on directories,
-     which is a sane thing to do on sane systems, but not on
-     Solaris when you are root.
-
- * Documentation Fixes and Updates
-
-   - A handful documentation fixes.
diff --git a/Documentation/RelNotes-1.5.2.txt b/Documentation/RelNotes-1.5.2.txt
deleted file mode 100644 (file)
index e8328d0..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-GIT v1.5.2 Release Notes
-========================
-
-Updates since v1.5.1
---------------------
-
-* Plumbing level superproject support.
-
-  You can include a subdirectory that has an independent git
-  repository in your index and tree objects of your project
-  ("superproject").  This plumbing (i.e. "core") level
-  superproject support explicitly excludes recursive behaviour.
-
-  The "subproject" entries in the index and trees of a superproject
-  are incompatible with older versions of git.  Experimenting with
-  the plumbing level support is encouraged, but be warned that
-  unless everybody in your project updates to this release or
-  later, using this feature would make your project
-  inaccessible by people with older versions of git.
-
-* Plumbing level gitattributes support.
-
-  The gitattributes mechanism allows you to add 'attributes' to
-  paths in your project, and affect the way certain git
-  operations work.  Currently you can influence if a path is
-  considered a binary or text (the former would be treated by
-  'git diff' not to produce textual output; the latter can go
-  through the line endings conversion process in repositories
-  with core.autocrlf set), expand and unexpand '$Id$' keyword
-  with blob object name, specify a custom 3-way merge driver,
-  and specify a custom diff driver.  You can also apply
-  arbitrary filter to contents on check-in/check-out codepath
-  but this feature is an extremely sharp-edged razor and needs
-  to be handled with caution (do not use it unless you
-  understand the earlier mailing list discussion on keyword
-  expansion).  These conversions apply when checking files in
-  or out, and exporting via git-archive.
-
-* The packfile format now optionally supports 64-bit index.
-
-  This release supports the "version 2" format of the .idx
-  file.  This is automatically enabled when a huge packfile
-  needs more than 32-bit to express offsets of objects in the
-  pack.
-
-* Comes with an updated git-gui 0.7.1
-
-* Updated gitweb:
-
-  - can show combined diff for merges;
-  - uses font size of user's preference, not hardcoded in pixels;
-  - can now 'grep';
-
-* New commands and options.
-
-  - "git bisect start" can optionally take a single bad commit and
-    zero or more good commits on the command line.
-
-  - "git shortlog" can optionally be told to wrap its output.
-
-  - "subtree" merge strategy allows another project to be merged in as
-    your subdirectory.
-
-  - "git format-patch" learned a new --subject-prefix=<string>
-    option, to override the built-in "[PATCH]".
-
-  - "git add -u" is a quick way to do the first stage of "git
-    commit -a" (i.e. update the index to match the working
-    tree); it obviously does not make a commit.
-
-  - "git clean" honors a new configuration, "clean.requireforce".  When
-    set to true, this makes "git clean" a no-op, preventing you
-    from losing files by typing "git clean" when you meant to
-    say "make clean".  You can still say "git clean -f" to
-    override this.
-
-  - "git log" family of commands learned --date={local,relative,default}
-    option.  --date=relative is synonym to the --relative-date.
-    --date=local gives the timestamp in local timezone.
-
-* Updated behavior of existing commands.
-
-  - When $GIT_COMMITTER_EMAIL or $GIT_AUTHOR_EMAIL is not set
-    but $EMAIL is set, the latter is used as a substitute.
-
-  - "git diff --stat" shows size of preimage and postimage blobs
-    for binary contents.  Earlier it only said "Bin".
-
-  - "git lost-found" shows stuff that are unreachable except
-    from reflogs.
-
-  - "git checkout branch^0" now detaches HEAD at the tip commit
-    on the named branch, instead of just switching to the
-    branch (use "git checkout branch" to switch to the branch,
-    as before).
-
-  - "git bisect next" can be used after giving only a bad commit
-    without giving a good one (this starts bisection half-way to
-    the root commit).  We used to refuse to operate without a
-    good and a bad commit.
-
-  - "git push", when pushing into more than one repository, does
-    not stop at the first error.
-
-  - "git archive" does not insist you to give --format parameter
-    anymore; it defaults to "tar".
-
-  - "git cvsserver" can use backends other than sqlite.
-
-  - "gitview" (in contrib/ section) learned to better support
-    "git-annotate".
-
-  - "git diff $commit1:$path2 $commit2:$path2" can now report
-    mode changes between the two blobs.
-
-  - Local "git fetch" from a repository whose object store is
-    one of the alternates (e.g. fetching from the origin in a
-    repository created with "git clone -l -s") avoids
-    downloading objects unnecessarily.
-
-  - "git blame" uses .mailmap to canonicalize the author name
-    just like "git shortlog" does.
-
-  - "git pack-objects" pays attention to pack.depth
-    configuration variable.
-
-  - "git cherry-pick" and "git revert" does not use .msg file in
-    the working tree to prepare commit message; instead it uses
-    $GIT_DIR/MERGE_MSG as other commands do.
-
-* Builds
-
-  - git-p4import has never been installed; now there is an
-    installation option to do so.
-
-  - gitk and git-gui can be configured out.
-
-  - Generated documentation pages automatically get version
-    information from GIT_VERSION.
-
-  - Parallel build with "make -j" descending into subdirectory
-    was fixed.
-
-* Performance Tweaks
-
-  - Optimized "git-rev-list --bisect" (hence "git-bisect").
-
-  - Optimized "git-add $path" in a large directory, most of
-    whose contents are ignored.
-
-  - Optimized "git-diff-tree" for reduced memory footprint.
-
-  - The recursive merge strategy updated a worktree file that
-    was changed identically in two branches, when one of them
-    renamed it.  We do not do that when there is no rename, so
-    match that behaviour.  This avoids excessive rebuilds.
-
-  - The default pack depth has been increased to 50, as the
-    recent addition of delta_base_cache makes deeper delta chains
-    much less expensive to access.  Depending on the project, it was
-    reported that this reduces the resulting pack file by 10%
-    or so.
-
-
-Fixes since v1.5.1
-------------------
-
-All of the fixes in v1.5.1 maintenance series are included in
-this release, unless otherwise noted.
-
-* Bugfixes
-
-  - Switching branches with "git checkout" refused to work when
-    a path changes from a file to a directory between the
-    current branch and the new branch, in order not to lose
-    possible local changes in the directory that is being turned
-    into a file with the switch.  We now allow such a branch
-    switch after making sure that there is no locally modified
-    file nor un-ignored file in the directory.  This has not
-    been backported to 1.5.1.x series, as it is rather an
-    intrusive change.
-
-  - Merging branches that have a file in one and a directory in
-    another at the same path used to get quite confused.  We
-    handle such a case a bit more carefully, even though that is
-    still left as a conflict for the user to sort out.  This
-    will not be backported to 1.5.1.x series, as it is rather an
-    intrusive change.
-
-  - git-fetch had trouble with a remote with insanely large number
-    of refs.
-
-  - "git clean -d -X" now does not remove non-excluded directories.
-
-  - rebasing (without -m) a series that changes a symlink to a directory
-    in the middle of a path confused git-apply greatly and refused to
-    operate.
diff --git a/Documentation/RelNotes-1.5.3.1.txt b/Documentation/RelNotes-1.5.3.1.txt
deleted file mode 100644 (file)
index 7ff546c..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-GIT v1.5.3.1 Release Notes
-==========================
-
-Fixes since v1.5.3
-------------------
-
-This is solely to fix the generated RPM's dependencies.  We used
-to have git-p4 package but we do not anymore.  As suggested on
-the mailing list, this release makes git-core "Obsolete" git-p4,
-so that yum update would not complain.
diff --git a/Documentation/RelNotes-1.5.3.2.txt b/Documentation/RelNotes-1.5.3.2.txt
deleted file mode 100644 (file)
index 4bbde3c..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-GIT v1.5.3.2 Release Notes
-==========================
-
-Fixes since v1.5.3.1
---------------------
-
- * git-push sent thin packs by default, which was not good for
-   the public distribution server (no point in saving transfer
-   while pushing; no point in making the resulting pack less
-   optimum).
-
- * git-svn sometimes terminated with "Malformed network data" when
-   talking over svn:// protocol.
-
- * git-send-email re-issued the same message-id about 10% of the
-   time if you fired off 30 messages within a single second.
-
- * git-stash was not terminating the log message of commits it
-   internally creates with LF.
-
- * git-apply failed to check the size of the patch hunk when its
-   beginning part matched the remainder of the preimage exactly,
-   even though the preimage recorded in the hunk was much larger
-   (therefore the patch should not have applied), leading to a
-   segfault.
-
- * "git rm foo && git commit foo" complained that 'foo' needs to
-   be added first, instead of committing the removal, which was a
-   nonsense.
-
- * git grep -c said "/dev/null: 0".
-
- * git-add -u failed to recognize a blob whose type changed
-   between the index and the work tree.
-
- * The limit to rename detection has been tightened a lot to
-   reduce performance problems with a huge change.
-
- * cvsimport and svnimport barfed when the input tried to move
-   a tag.
-
- * "git apply -pN" did not chop the right number of directories.
-
- * "git svnimport" did not like SVN tags with funny characters in them.
-
- * git-gui 0.8.3, with assorted fixes, including:
-
-   - font-chooser on X11 was unusable with large number of fonts;
-   - a diff that contained a deleted symlink made it barf;
-   - an untracked symbolic link to a directory made it fart;
-   - a file with % in its name made it vomit;
-
-
-Documentation updates
----------------------
-
-User manual has been somewhat restructured.  I think the new
-organization is much easier to read.
diff --git a/Documentation/RelNotes-1.5.3.3.txt b/Documentation/RelNotes-1.5.3.3.txt
deleted file mode 100644 (file)
index d213846..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-GIT v1.5.3.3 Release Notes
-==========================
-
-Fixes since v1.5.3.2
---------------------
-
- * git-quiltimport did not like it when a patch described in the
-   series file does not exist.
-
- * p4 importer missed executable bit in some cases.
-
- * The default shell on some FreeBSD did not execute the
-   argument parsing code correctly and made git unusable.
-
- * git-svn incorrectly spawned pager even when the user
-   explicitly asked not to.
-
- * sample post-receive hook overquoted the envelope sender
-   value.
-
- * git-am got confused when the patch contained a change that is
-   only about type and not contents.
-
- * git-mergetool did not show our and their version of the
-   conflicted file when started from a subdirectory of the
-   project.
-
- * git-mergetool did not pass correct options when invoking diff3.
-
- * git-log sometimes invoked underlying "diff" machinery
-   unnecessarily.
diff --git a/Documentation/RelNotes-1.5.3.4.txt b/Documentation/RelNotes-1.5.3.4.txt
deleted file mode 100644 (file)
index b04b3a4..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-GIT v1.5.3.4 Release Notes
-==========================
-
-Fixes since v1.5.3.3
---------------------
-
- * Change to "git-ls-files" in v1.5.3.3 that was introduced to support
-   partial commit of removal better had a segfaulting bug, which was
-   diagnosed and fixed by Keith and Carl.
-
- * Performance improvements for rename detection has been backported
-   from the 'master' branch.
-
- * "git-for-each-ref --format='%(numparent)'" was not working
-   correctly at all, and --format='%(parent)' was not working for
-   merge commits.
-
- * Sample "post-receive-hook" incorrectly sent out push
-   notification e-mails marked as "From: " the committer of the
-   commit that happened to be at the tip of the branch that was
-   pushed, not from the person who pushed.
-
- * "git-remote" did not exit non-zero status upon error.
-
- * "git-add -i" did not respond very well to EOF from tty nor
-   bogus input.
-
- * "git-rebase -i" squash subcommand incorrectly made the
-   author of later commit the author of resulting commit,
-   instead of taking from the first one in the squashed series.
-
- * "git-stash apply --index" was not documented.
-
- * autoconfiguration learned that "ar" command is found as "gas" on
-   some systems.
diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt
deleted file mode 100644 (file)
index 7ff1d5d..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-GIT v1.5.3.5 Release Notes
-==========================
-
-Fixes since v1.5.3.4
---------------------
-
- * Comes with git-gui 0.8.4.
-
- * "git-config" silently ignored options after --list; now it will
-   error out with a usage message.
-
- * "git-config --file" failed if the argument used a relative path
-   as it changed directories before opening the file.
-
- * "git-config --file" now displays a proper error message if it
-   cannot read the file specified on the command line.
-
- * "git-config", "git-diff", "git-apply" failed if run from a
-   subdirectory with relative GIT_DIR and GIT_WORK_TREE set.
-
- * "git-blame" crashed if run during a merge conflict.
-
- * "git-add -i" did not handle single line hunks correctly.
-
- * "git-rebase -i" and "git-stash apply" failed if external diff
-   drivers were used for one or more files in a commit.  They now
-   avoid calling the external diff drivers.
-
- * "git-log --follow" did not work unless diff generation (e.g. -p)
-   was also requested.
-
- * "git-log --follow -B" did not work at all.  Fixed.
-
- * "git-log -M -B" did not correctly handle cases of very large files
-   being renamed and replaced by very small files in the same commit.
-
- * "git-log" printed extra newlines between commits when a diff
-   was generated internally (e.g. -S or --follow) but not displayed.
-
- * "git-push" error message is more helpful when pushing to a
-   repository with no matching refs and none specified.
-
- * "git-push" now respects + (force push) on wildcard refspecs,
-   matching the behavior of git-fetch.
-
- * "git-filter-branch" now updates the working directory when it
-   has finished filtering the current branch.
-
- * "git-instaweb" no longer fails on Mac OS X.
-
- * "git-cvsexportcommit" didn't always create new parent directories
-   before trying to create new child directories.  Fixed.
-
- * "git-fetch" printed a scary (but bogus) error message while
-   fetching a tag that pointed to a tree or blob.  The error did
-   not impact correctness, only user perception.  The bogus error
-   is no longer printed.
-
- * "git-ls-files --ignored" did not properly descend into non-ignored
-   directories that themselves contained ignored files if d_type
-   was not supported by the filesystem.  This bug impacted systems
-   such as AFS.  Fixed.
-
- * Git segfaulted when reading an invalid .gitattributes file.  Fixed.
-
- * post-receive-email example hook was fixed for non-fast-forward
-   updates.
-
- * Documentation updates for supported (but previously undocumented)
-   options of "git-archive" and "git-reflog".
-
- * "make clean" no longer deletes the configure script that ships
-   with the git tarball, making multiple architecture builds easier.
-
- * "git-remote show origin" spewed a warning message from Perl
-   when no remote is defined for the current branch via
-   branch.<name>.remote configuration settings.
-
- * Building with NO_PERL_MAKEMAKER excessively rebuilt contents
-   of perl/ subdirectory by rewriting perl.mak.
-
- * http.sslVerify configuration settings were not used in scripted
-   Porcelains.
-
- * "git-add" leaked a bit of memory while scanning for files to add.
-
- * A few workarounds to squelch false warnings from recent gcc have
-   been added.
-
- * "git-send-pack $remote frotz" segfaulted when there is nothing
-   named 'frotz' on the local end.
-
- * "git-rebase --interactive" did not handle its "--strategy" option
-   properly.
diff --git a/Documentation/RelNotes-1.5.3.6.txt b/Documentation/RelNotes-1.5.3.6.txt
deleted file mode 100644 (file)
index 069a2b2..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-GIT v1.5.3.6 Release Notes
-==========================
-
-Fixes since v1.5.3.5
---------------------
-
- * git-cvsexportcommit handles root commits better.
-
- * git-svn dcommit used to clobber when sending a series of
-   patches.
-
- * git-svn dcommit failed after attempting to rebase when
-   started with a dirty index; now it stops upfront.
-
- * git-grep sometimes refused to work when your index was
-   unmerged.
-
- * "git-grep -A1 -B2" acted as if it was told to run "git -A1 -B21".
-
- * git-hash-object did not honor configuration variables, such as
-   core.compression.
-
- * git-index-pack choked on a huge pack on 32-bit machines, even when
-   large file offsets are supported.
-
- * atom feeds from git-web said "10" for the month of November.
-
- * a memory leak in commit walker was plugged.
-
- * When git-send-email inserted the original author's From:
-   address in body, it did not mark the message with
-   Content-type: as needed.
-
- * git-revert and git-cherry-pick incorrectly refused to start
-   when the work tree was dirty.
-
- * git-clean did not honor core.excludesfile configuration.
-
- * git-add mishandled ".gitignore" files when applying them to
-   subdirectories.
-
- * While importing a too branchy history, git-fastimport did not
-   honor delta depth limit properly.
-
- * Support for zlib implementations that lack ZLIB_VERNUM and definition
-   of deflateBound() has been added.
-
- * Quite a lot of documentation clarifications.
diff --git a/Documentation/RelNotes-1.5.3.7.txt b/Documentation/RelNotes-1.5.3.7.txt
deleted file mode 100644 (file)
index 2f69061..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-GIT v1.5.3.7 Release Notes
-==========================
-
-Fixes since v1.5.3.6
---------------------
-
- * git-send-email added 8-bit contents to the payload without
-   marking it as 8-bit in a CTE header.
-
- * "git-bundle create a.bndl HEAD" dereferenced the symref and
-   did not record the ref as 'HEAD'; this prevented a bundle
-   from being used as a normal source of git-clone.
-
- * The code to reject nonsense command line of the form
-   "git-commit -a paths..." and "git-commit --interactive
-   paths..." were broken.
-
- * Adding a signature that is not ASCII-only to an original
-   commit that is ASCII-only would make the result non-ASCII.
-   "git-format-patch -s" did not mark such a message correctly
-   with MIME encoding header.
-
- * git-add sometimes did not mark the resulting index entry
-   stat-clean.  This affected only cases when adding the
-   contents with the same length as the previously staged
-   contents, and the previous staging made the index entry
-   "racily clean".
-
- * git-commit did not honor GIT_INDEX_FILE the user had in the
-   environment.
-
- * When checking out a revision, git-checkout did not report where the
-   updated HEAD is if you happened to have a file called HEAD in the
-   work tree.
-
- * "git-rev-list --objects" mishandled a tree that points at a
-   submodule.
-
- * "git cvsimport" was not ready for packed refs that "git gc" can
-   produce and gave incorrect results.
-
- * Many scripted Porcelains were confused when you happened to have a
-   file called "HEAD" in your work tree.
-
-Also it contains updates to the user manual and documentation.
diff --git a/Documentation/RelNotes-1.5.3.8.txt b/Documentation/RelNotes-1.5.3.8.txt
deleted file mode 100644 (file)
index 0e3ff58..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-GIT v1.5.3.8 Release Notes
-==========================
-
-Fixes since v1.5.3.7
---------------------
-
- * Some documentation used "email.com" as an example domain.
-
- * git-svn fix to handle funky branch and project names going over
-   http/https correctly.
-
- * git-svn fix to tone down a needlessly alarming warning message.
-
- * git-clone did not correctly report errors while fetching over http.
-
- * git-send-email added redundant Message-Id: header to the outgoing
-   e-mail when the patch text already had one.
-
- * a read-beyond-end-of-buffer bug in configuration file updater was fixed.
-
- * git-grep used to show the same hit repeatedly for unmerged paths.
-
- * After amending the patch title in "git-am -i", the command did not
-   report the patch it applied with the updated title.
-
diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt
deleted file mode 100644 (file)
index 0668d3c..0000000
+++ /dev/null
@@ -1,366 +0,0 @@
-GIT v1.5.3 Release Notes
-========================
-
-Updates since v1.5.2
---------------------
-
-* The commit walkers other than http are officially deprecated,
-  but still supported for now.
-
-* The submodule support has Porcelain layer.
-
-  Note that the current submodule support is minimal and this is
-  deliberately so.  A design decision we made is that operations
-  at the supermodule level do not recurse into submodules by
-  default.  The expectation is that later we would add a
-  mechanism to tell git which submodules the user is interested
-  in, and this information might be used to determine the
-  recursive behaviour of certain commands (e.g. "git checkout"
-  and "git diff"), but currently we haven't agreed on what that
-  mechanism should look like.  Therefore, if you use submodules,
-  you would probably need "git submodule update" on the
-  submodules you care about after running a "git checkout" at
-  the supermodule level.
-
-* There are a handful pack-objects changes to help you cope better
-  with repositories with pathologically large blobs in them.
-
-* For people who need to import from Perforce, a front-end for
-  fast-import is in contrib/fast-import/.
-
-* Comes with git-gui 0.8.2.
-
-* Comes with updated gitk.
-
-* New commands and options.
-
-  - "git log --date=<format>" can use more formats: iso8601, rfc2822.
-
-  - The hunk header output from "git diff" family can be customized
-    with the attributes mechanism.  See gitattributes(5) for details.
-
-  - "git stash" allows you to quickly save away your work in
-    progress and replay it later on an updated state.
-
-  - "git rebase" learned an "interactive" mode that let you
-    pick and reorder which commits to rebuild.
-
-  - "git fsck" can save its findings in $GIT_DIR/lost-found, without a
-    separate invocation of "git lost-found" command.  The blobs stored by
-    lost-found are stored in plain format to allow you to grep in them.
-
-  - $GIT_WORK_TREE environment variable can be used together with
-    $GIT_DIR to work in a subdirectory of a working tree that is
-    not located at "$GIT_DIR/..".
-
-  - Giving "--file=<file>" option to "git config" is the same as
-    running the command with GIT_CONFIG=<file> environment.
-
-  - "git log" learned a new option "--follow", to follow
-    renaming history of a single file.
-
-  - "git filter-branch" lets you rewrite the revision history of
-    specified branches. You can specify a number of filters to
-    modify the commits, files and trees.
-
-  - "git cvsserver" learned new options (--base-path, --export-all,
-    --strict-paths) inspired by "git daemon".
-
-  - "git daemon --base-path-relaxed" can help migrating a repository URL
-    that did not use to use --base-path to use --base-path.
-
-  - "git commit" can use "-t templatefile" option and commit.template
-    configuration variable to prime the commit message given to you in the
-    editor.
-
-  - "git submodule" command helps you manage the projects from
-    the superproject that contain them.
-
-  - In addition to core.compression configuration option,
-    core.loosecompression and pack.compression options can
-    independently tweak zlib compression levels used for loose
-    and packed objects.
-
-  - "git ls-tree -l" shows size of blobs pointed at by the
-    tree entries, similar to "/bin/ls -l".
-
-  - "git rev-list" learned --regexp-ignore-case and
-    --extended-regexp options to tweak its matching logic used
-    for --grep filtering.
-
-  - "git describe --contains" is a handier way to call more
-    obscure command "git name-rev --tags".
-
-  - "git gc --aggressive" tells the command to spend more cycles
-    to optimize the repository harder.
-
-  - "git repack" learned a "window-memory" limit which
-    dynamically reduces the window size to stay within the
-    specified memory usage.
-
-  - "git repack" can be told to split resulting packs to avoid
-    exceeding limit specified with "--max-pack-size".
-
-  - "git fsck" gained --verbose option.  This is really really
-    verbose but it might help you identify exact commit that is
-    corrupt in your repository.
-
-  - "git format-patch" learned --numbered-files option.  This
-    may be useful for MH users.
-
-  - "git format-patch" learned format.subjectprefix configuration
-    variable, which serves the same purpose as "--subject-prefix"
-    option.
-
-  - "git tag -n -l" shows tag annotations while listing tags.
-
-  - "git cvsimport" can optionally use the separate-remote layout.
-
-  - "git blame" can be told to see through commits that change
-    whitespaces and indentation levels with "-w" option.
-
-  - "git send-email" can be told not to thread the messages when
-    sending out more than one patches.
-
-  - "git send-email" can also be told how to find whom to cc the
-    message to for each message via --cc-cmd.
-
-  - "git config" learned NUL terminated output format via -z to
-    help scripts.
-
-  - "git add" learned "--refresh <paths>..." option to selectively refresh
-    the cached stat information.
-
-  - "git init -q" makes the command quieter.
-
-  - "git -p command" now has a cousin of opposite sex, "git --no-pager
-    command".
-
-* Updated behavior of existing commands.
-
-  - "gitweb" can offer multiple snapshot formats.
-
-    ***NOTE*** Unfortunately, this changes the format of the
-    $feature{snapshot}{default} entry in the per-site
-    configuration file 'gitweb_config.perl'.  It used to be a
-    three-element tuple that describe a single format; with the
-    new configuration item format, you only have to say the name
-    of the format ('tgz', 'tbz2' or 'zip').  Please update the
-    your configuration file accordingly.
-
-  - "git clone" uses -l (hardlink files under .git) by default when
-    cloning locally.
-
-  - URL used for "git clone" and friends can specify nonstandard SSH port
-    by using ssh://host:port/path/to/repo syntax.
-
-  - "git bundle create" can now create a bundle without negative refs,
-    i.e. "everything since the beginning up to certain points".
-
-  - "git diff" (but not the plumbing level "git diff-tree") now
-    recursively descends into trees by default.
-
-  - "git diff" does not show differences that come only from
-    stat-dirtiness in the form of "diff --git" header anymore.
-    It runs "update-index --refresh" silently as needed.
-
-  - "git tag -l" used to match tags by globbing its parameter as if it
-    has wildcard '*' on both ends, which made "git tag -l gui" to match
-    tag 'gitgui-0.7.0'; this was very annoying.  You now have to add
-    asterisk on the sides you want to wildcard yourself.
-
-  - The editor to use with many interactive commands can be
-    overridden with GIT_EDITOR environment variable, or if it
-    does not exist, with core.editor configuration variable.  As
-    before, if you have neither, environment variables VISUAL
-    and EDITOR are consulted in this order, and then finally we
-    fall back on "vi".
-
-  - "git rm --cached" does not complain when removing a newly
-    added file from the index anymore.
-
-  - Options to "git log" to affect how --grep/--author options look for
-    given strings now have shorter abbreviations.  -i is for ignore case,
-    and -E is for extended regexp.
-
-  - "git log" learned --log-size to show the number of bytes in
-    the log message part of the output to help qgit.
-
-  - "git log --name-status" does not require you to give "-r" anymore.
-    As a general rule, Porcelain commands should recurse when showing
-    diff.
-
-  - "git format-patch --root A" can be used to format everything
-    since the beginning up to A.  This was supported with
-    "git format-patch --root A A" for a long time, but was not
-    properly documented.
-
-  - "git svn dcommit" retains local merge information.
-
-  - "git svnimport" allows an empty string to be specified as the
-    trunk/ directory.  This is necessary to suck data from a SVN
-    repository that doe not have trunk/ branches/ and tags/ organization
-    at all.
-
-  - "git config" to set values also honors type flags like --bool
-    and --int.
-
-  - core.quotepath configuration can be used to make textual git
-    output to emit most of the characters in the path literally.
-
-  - "git mergetool" chooses its backend more wisely, taking
-    notice of its environment such as use of X, Gnome/KDE, etc.
-
-  - "gitweb" shows merge commits a lot nicer than before.  The
-    default view uses more compact --cc format, while the UI
-    allows to choose normal diff with any parent.
-
-  - snapshot files "gitweb" creates from a repository at
-    $path/$project/.git are more useful.  We use $project part
-    in the filename, which we used to discard.
-
-  - "git cvsimport" creates lightweight tags; there is no
-    interesting information we can record in an annotated tag,
-    and the handcrafted ones the old code created was not
-    properly formed anyway.
-
-  - "git push" pretends that you immediately fetched back from
-    the remote by updating corresponding remote tracking
-    branches if you have any.
-
-  - The diffstat given after a merge (or a pull) honors the
-    color.diff configuration.
-
-  - "git commit --amend" is now compatible with various message source
-    options such as -m/-C/-c/-F.
-
-  - "git apply --whitespace=strip" removes blank lines added at
-    the end of the file.
-
-  - "git fetch" over git native protocols with "-v" option shows
-    connection status, and the IP address of the other end, to
-    help diagnosing problems.
-
-  - We used to have core.legacyheaders configuration, when
-    set to false, allowed git to write loose objects in a format
-    that mimics the format used by objects stored in packs.  It
-    turns out that this was not so useful.  Although we will
-    continue to read objects written in that format, we do not
-    honor that configuration anymore and create loose objects in
-    the legacy/traditional format.
-
-  - "--find-copies-harder" option to diff family can now be
-    spelled as "-C -C" for brevity.
-
-  - "git mailsplit" (hence "git am") can read from Maildir
-    formatted mailboxes.
-
-  - "git cvsserver" does not barf upon seeing "cvs login"
-    request.
-
-  - "pack-objects" honors "delta" attribute set in
-    .gitattributes.  It does not attempt to deltify blobs that
-    come from paths with delta attribute set to false.
-
-  - "new-workdir" script (in contrib) can now be used with a
-    bare repository.
-
-  - "git mergetool" learned to use gvimdiff.
-
-  - "gitview" (in contrib) has a better blame interface.
-
-  - "git log" and friends did not handle a commit log message
-    that is larger than 16kB; they do now.
-
-  - "--pretty=oneline" output format for "git log" and friends
-    deals with "malformed" commit log messages that have more
-    than one lines in the first paragraph better.  We used to
-    show the first line, cutting the title at mid-sentence; we
-    concatenate them into a single line and treat the result as
-    "oneline".
-
-  - "git p4import" has been demoted to contrib status.  For
-    a superior option, checkout the "git p4" front end to
-    "git fast-import" (also in contrib).  The man page and p4
-    rpm have been removed as well.
-
-  - "git mailinfo" (hence "am") now tries to see if the message
-    is in utf-8 first, instead of assuming iso-8859-1, if
-    incoming e-mail does not say what encoding it is in.
-
-* Builds
-
-  - old-style function definitions (most notably, a function
-    without parameter defined with "func()", not "func(void)")
-    have been eradicated.
-
-  - "git tag" and "git verify-tag" have been rewritten in C.
-
-* Performance Tweaks
-
-  - "git pack-objects" avoids re-deltification cost by caching
-    small enough delta results it creates while looking for the
-    best delta candidates.
-
-  - "git pack-objects" learned a new heuristic to prefer delta
-    that is shallower in depth over the smallest delta
-    possible.  This improves both overall packfile access
-    performance and packfile density.
-
-  - diff-delta code that is used for packing has been improved
-    to work better on big files.
-
-  - when there are more than one pack files in the repository,
-    the runtime used to try finding an object always from the
-    newest packfile; it now tries the same packfile as we found
-    the object requested the last time, which exploits the
-    locality of references.
-
-  - verifying pack contents done by "git fsck --full" got boost
-    by carefully choosing the order to verify objects in them.
-
-  - "git read-tree -m" to read into an already populated index
-    has been optimized vastly.  The effect of this can be seen
-    when switching branches that have differences in only a
-    handful paths.
-
-  - "git add paths..." and "git commit paths..." has also been
-    heavily optimized.
-
-Fixes since v1.5.2
-------------------
-
-All of the fixes in v1.5.2 maintenance series are included in
-this release, unless otherwise noted.
-
-* Bugfixes
-
-  - "gitweb" had trouble handling non UTF-8 text with older
-    Encode.pm Perl module.
-
-  - "git svn" misparsed the data from the commits in the repository when
-    the user had "color.diff = true" in the configuration.  This has been
-    fixed.
-
-  - There was a case where "git svn dcommit" clobbered changes made on the
-    SVN side while committing multiple changes.
-
-  - "git-write-tree" had a bad interaction with racy-git avoidance and
-    gitattributes mechanisms.
-
-  - "git --bare command" overrode existing GIT_DIR setting and always
-    made it treat the current working directory as GIT_DIR.
-
-  - "git ls-files --error-unmatch" does not complain if you give the
-    same path pattern twice by mistake.
-
-  - "git init" autodetected core.filemode but not core.symlinks, which
-    made a new directory created automatically by "git clone" cumbersome
-    to use on filesystems that require these configurations to be set.
-
-  - "git log" family of commands behaved differently when run as "git
-    log" (no pathspec) and as "git log --" (again, no pathspec).  This
-    inconsistency was introduced somewhere in v1.3.0 series but now has
-    been corrected.
-
-  - "git rebase -m" incorrectly displayed commits that were skipped.
diff --git a/Documentation/RelNotes-1.5.4.1.txt b/Documentation/RelNotes-1.5.4.1.txt
deleted file mode 100644 (file)
index d4e44b8..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-GIT v1.5.4.1 Release Notes
-==========================
-
-Fixes since v1.5.4
-------------------
-
- * "git-commit -C $tag" used to work but rewrite in C done in
-   1.5.4 broke it.
-
- * An entry in the .gitattributes file that names a pattern in a
-   subdirectory of the directory it is in did not match
-   correctly (e.g. pattern "b/*.c" in "a/.gitattributes" should
-   match "a/b/foo.c" but it didn't).
-
- * Customized color specification was parsed incorrectly when
-   numeric color values are used.  This was fixed in 1.5.4.1.
-
diff --git a/Documentation/RelNotes-1.5.4.2.txt b/Documentation/RelNotes-1.5.4.2.txt
deleted file mode 100644 (file)
index 21d0df5..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-GIT v1.5.4.2 Release Notes
-==========================
-
-Fixes since v1.5.4
-------------------
-
- * The configuration parser was not prepared to see string
-   valued variables misspelled as boolean and segfaulted.
-
- * Temporary files left behind due to interrupted object
-   transfers were not cleaned up with "git prune".
-
- * "git config --unset" was confused when the unset variables
-   were spelled with continuation lines in the config file.
-
- * The merge message detection in "git cvsimport" did not catch
-   a message that began with "Merge...".
-
- * "git status" suggests "git rm --cached" for unstaging the
-   earlier "git add" before the initial commit.
-
- * "git status" output was incorrect during a partial commit.
-
- * "git bisect" refused to start when the HEAD was detached.
-
- * "git bisect" allowed a wildcard character in the commit
-   message expanded while writing its log file.
-
- * Manual pages were not formatted correctly with docbook xsl
-   1.72; added a workaround.
-
- * "git-commit -C $tag" used to work but rewrite in C done in
-   1.5.4 broke it.  This was fixed in 1.5.4.1.
-
- * An entry in the .gitattributes file that names a pattern in a
-   subdirectory of the directory it is in did not match
-   correctly (e.g. pattern "b/*.c" in "a/.gitattributes" should
-   match "a/b/foo.c" but it didn't).  This was fixed in 1.5.4.1.
-
- * Customized color specification was parsed incorrectly when
-   numeric color values are used.  This was fixed in 1.5.4.1.
-
- * http transport misbehaved when linked with curl-gnutls.
diff --git a/Documentation/RelNotes-1.5.4.3.txt b/Documentation/RelNotes-1.5.4.3.txt
deleted file mode 100644 (file)
index b0fc67f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-GIT v1.5.4.3 Release Notes
-==========================
-
-Fixes since v1.5.4.2
---------------------
-
- * RPM spec used to pull in everything with 'git'.  This has been
-   changed so that 'git' package contains just the core parts,
-   and we now supply 'git-all' metapackage to slurp in everything.
-   This should match end user's expectation better.
-
- * When some refs failed to update, git-push reported "failure"
-   which was unclear if some other refs were updated or all of
-   them failed atomically (the answer is the former).  Reworded
-   the message to clarify this.
-
- * "git clone" from a repository whose HEAD was misconfigured
-   did not set up the remote properly.  Now it tries to do
-   better.
-
- * Updated git-push documentation to clarify what "matching"
-   means, in order to reduce user confusion.
-
- * Updated git-add documentation to clarify "add -u" operates in
-   the current subdirectory you are in, just like other commands.
-
- * git-gui updates to work on OSX and Windows better.
diff --git a/Documentation/RelNotes-1.5.4.4.txt b/Documentation/RelNotes-1.5.4.4.txt
deleted file mode 100644 (file)
index 323c1a8..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-GIT v1.5.4.4 Release Notes
-==========================
-
-Fixes since v1.5.4.3
---------------------
-
- * Building and installing with an overtight umask such as 077 made
-   installed templates unreadable by others, while the rest of the install
-   are done in a way that is friendly to umask 022.
-
- * "git cvsexportcommit -w $cvsdir" misbehaved when GIT_DIR is set to a
-   relative directory.
-
- * "git http-push" had an invalid memory access that could lead it to
-   segfault.
-
- * When "git rebase -i" gave control back to the user for a commit that is
-   marked to be edited, it just said "modify it with commit --amend",
-   without saying what to do to continue after modifying it.  Give an
-   explicit instruction to run "rebase --continue" to be more helpful.
-
- * "git send-email" in 1.5.4.3 issued a bogus empty In-Reply-To: header.
-
- * "git bisect" showed mysterious "won't bisect on seeked tree" error message.
-   This was leftover from Cogito days to prevent "bisect" starting from a
-   cg-seeked state.  We still keep the Cogito safety, but running "git bisect
-   start" when another bisect was in effect will clean up and start over.
-
- * "git push" with an explicit PATH to receive-pack did not quite work if
-   receive-pack was not on usual PATH.  We earlier fixed the same issue
-   with "git fetch" and upload-pack, but somehow forgot to do so in the
-   other direction.
-
- * git-gui's info dialog was not displayed correctly when the user tries
-   to commit nothing (i.e. without staging anything).
-
- * "git revert" did not properly fail when attempting to run with a
-   dirty index.
-
- * "git merge --no-commit --no-ff <other>" incorrectly made commits.
-
- * "git merge --squash --no-ff <other>", which is a nonsense combination
-   of options, was not rejected.
-
- * "git ls-remote" and "git remote show" against an empty repository
-   failed, instead of just giving an empty result (regression).
-
- * "git fast-import" did not handle a renamed path whose name needs to be
-   quoted, due to a bug in unquote_c_style() function.
-
- * "git cvsexportcommit" was confused when multiple files with the same
-   basename needed to be pushed out in the same commit.
-
- * "git daemon" did not send early errors to syslog.
-
- * "git log --merge" did not work well with --left-right option.
-
- * "git svn" prompted for client cert password every time it accessed the
-   server.
-
- * The reset command in "git fast-import" data stream was documented to
-   end with an optional LF, but it actually required one.
-
- * "git svn dcommit/rebase" did not honor --rewrite-root option.
-
-Also included are a handful documentation updates.
diff --git a/Documentation/RelNotes-1.5.4.5.txt b/Documentation/RelNotes-1.5.4.5.txt
deleted file mode 100644 (file)
index bbd130e..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-GIT v1.5.4.5 Release Notes
-==========================
-
-Fixes since v1.5.4.4
---------------------
-
- * "git fetch there" when the URL information came from the Cogito style
-   branches/there file did not update refs/heads/there (regression in
-   1.5.4).
-
- * Bogus refspec configuration such as "remote.there.fetch = =" were not
-   detected as errors (regression in 1.5.4).
-
- * You couldn't specify a custom editor whose path contains a whitespace
-   via GIT_EDITOR (and core.editor).
-
- * The subdirectory filter to "git filter-branch" mishandled a history
-   where the subdirectory becomes empty and then later becomes non-empty.
-
- * "git shortlog" gave an empty line if the original commit message was
-   malformed (e.g. a botched import from foreign SCM).  Now it finds the
-   first non-empty line and uses it for better information.
-
- * When the user fails to give a revision parameter to "git svn", an error
-   from the Perl interpreter was issued because the script lacked proper
-   error checking.
-
- * After "git rebase" stopped due to conflicts, if the user played with
-   "git reset" and friends, "git rebase --abort" failed to go back to the
-   correct commit.
-
- * Additional work trees prepared with git-new-workdir (in contrib/) did
-   not share git-svn metadata directory .git/svn with the original.
-
- * "git-merge-recursive" did not mark addition of the same path with
-   different filemodes correctly as a conflict.
-
- * "gitweb" gave malformed URL when pathinfo stype paths are in use.
-
- * "-n" stands for "--no-tags" again for "git fetch".
-
- * "git format-patch" did not detect the need to add 8-bit MIME header
-   when the user used format.header configuration.
-
- * "rev~" revision specifier used to mean "rev", which was inconsistent
-   with how "rev^" worked.  Now "rev~" is the same as "rev~1" (hence it
-   also is the same as "rev^1"), and "rev~0" is the same as "rev^0"
-   (i.e. it has to be a commit).
-
- * "git quiltimport" did not grok empty lines, lines in "file -pNNN"
-   format to specify the prefix levels and lines with trailing comments.
-
- * "git rebase -m" triggered pre-commit verification, which made
-   "rebase --continue" impossible.
-
-As usual, it also comes with many documentation fixes and clarifications.
diff --git a/Documentation/RelNotes-1.5.4.6.txt b/Documentation/RelNotes-1.5.4.6.txt
deleted file mode 100644 (file)
index 3e3c3e5..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-GIT v1.5.4.6 Release Notes
-==========================
-
-I personally do not think there is any reason anybody should want to
-run v1.5.4.X series these days, because 'master' version is always
-more stable than any tagged released version of git.
-
-This is primarily to futureproof "git-shell" to accept requests
-without a dash between "git" and subcommand name (e.g. "git
-upload-pack") which the newer client will start to make sometime in
-the future.
-
-Fixes since v1.5.4.5
---------------------
-
- * Command line option "-n" to "git-repack" was not correctly parsed.
-
- * Error messages from "git-apply" when the patchfile cannot be opened
-   have been improved.
-
- * Error messages from "git-bisect" when given nonsense revisions have
-   been improved.
-
- * reflog syntax that uses time e.g. "HEAD@{10 seconds ago}:path" did not
-   stop parsing at the closing "}".
-
- * "git rev-parse --symbolic-full-name ^master^2" printed solitary "^",
-   but it should print nothing.
-
- * "git apply" did not enforce "match at the beginning" correctly.
-
- * a path specification "a/b" in .gitattributes file should not match
-   "sub/a/b", but it did.
-
- * "git log --date-order --topo-order" did not override the earlier
-   date-order with topo-order as expected.
-
- * "git fast-export" did not export octopus merges correctly.
-
- * "git archive --prefix=$path/" mishandled gitattributes.
-
-As usual, it also comes with many documentation fixes and clarifications.
-
diff --git a/Documentation/RelNotes-1.5.4.7.txt b/Documentation/RelNotes-1.5.4.7.txt
deleted file mode 100644 (file)
index 9065a0e..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-GIT v1.5.4.7 Release Notes
-==========================
-
-Fixes since 1.5.4.7
--------------------
-
- * Removed support for an obsolete gitweb request URI, whose
-   implementation ran "git diff" Porcelain, instead of using plumbing,
-   which would have run an external diff command specified in the
-   repository configuration as the gitweb user.
diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt
deleted file mode 100644 (file)
index f1323b6..0000000
+++ /dev/null
@@ -1,377 +0,0 @@
-GIT v1.5.4 Release Notes
-========================
-
-Removal
--------
-
- * "git svnimport" was removed in favor of "git svn".  It is still there
-   in the source tree (contrib/examples) but unsupported.
-
- * As git-commit and git-status have been rewritten, "git runstatus"
-   helper script lost all its users and has been removed.
-
-
-Temporarily disabled
---------------------
-
- * "git http-push" is known not to work well with cURL library older
-   than 7.16, and we had reports of repository corruption.  It is
-   disabled on such platforms for now.  Unfortunately, 1.5.3.8 shares
-   the same issue.  In other words, this does not mean you will be
-   fine if you stick to an older git release.  For now, please do not
-   use http-push from older git with cURL older than 7.16 if you
-   value your data. A proper fix will hopefully materialize in
-   later versions.
-
-
-Deprecation notices
--------------------
-
- * From v1.6.0, git will by default install dashed form of commands
-   (e.g. "git-commit") outside of users' normal $PATH, and will install
-   only selected commands ("git" itself, and "gitk") in $PATH.  This
-   implies:
-
-   - Using dashed forms of git commands (e.g. "git-commit") from the
-     command line has been informally deprecated since early 2006, but
-     now it officially is, and will be removed in the future.  Use
-     dash-less forms (e.g. "git commit") instead.
-
-   - Using dashed forms from your scripts, without first prepending the
-     return value from "git --exec-path" to the scripts' PATH, has been
-     informally deprecated since early 2006, but now it officially is.
-
-   - Use of dashed forms with "PATH=$(git --exec-path):$PATH; export
-     PATH" early in your script is not deprecated with this change.
-
-   Users are strongly encouraged to adjust their habits and scripts now
-   to prepare for this change.
-
- * The post-receive hook was introduced in March 2007 to supersede
-   the post-update hook, primarily to overcome the command line length
-   limitation of the latter.  Use of post-update hook will be deprecated
-   in future versions of git, starting from v1.6.0.
-
- * "git lost-found" was deprecated in favor of "git fsck"'s --lost-found
-   option, and will be removed in the future.
-
- * "git peek-remote" is deprecated, as "git ls-remote" was written in C
-   and works for all transports; "git peek-remote" will be removed in
-   the future.
-
- * "git repo-config" which was an old name for "git config" command
-   has been supported without being advertised for a long time.  The
-   next feature release will remove it.
-
- * From v1.6.0, the repack.usedeltabaseoffset config option will default
-   to true, which will give denser packfiles (i.e. more efficient storage).
-   The downside is that git older than version 1.4.4 will not be able
-   to directly use a repository packed using this setting.
-
- * From v1.6.0, the pack.indexversion config option will default to 2,
-   which is slightly more efficient, and makes repacking more immune to
-   data corruptions.  Git older than version 1.5.2 may revert to version 1
-   of the pack index with a manual "git index-pack" to be able to directly
-   access corresponding pack files.
-
-
-Updates since v1.5.3
---------------------
-
- * Comes with much improved gitk, with i18n.
-
- * Comes with git-gui 0.9.2 with i18n.
-
- * gitk is now merged as a subdirectory of git.git project, in
-   preparation for its i18n.
-
- * progress displays from many commands are a lot nicer to the eye.
-   Transfer commands show throughput data.
-
- * many commands that pay attention to per-directory .gitignore now do
-   so lazily, which makes the usual case go much faster.
-
- * Output processing for '--pretty=format:<user format>' has been
-   optimized.
-
- * Rename detection of diff family while detecting exact matches has
-   been greatly optimized.
-
- * Rename detection of diff family tries to make more natural looking
-   pairing.  Earlier, if multiple identical rename sources were
-   found in the preimage, the source used was picked pretty much at random.
-
- * Value "true" for color.diff and color.status configuration used to
-   mean "always" (even when the output is not going to a terminal).
-   This has been corrected to mean the same thing as "auto".
-
- * "git diff" Porcelain now respects diff.external configuration, which
-   is another way to specify GIT_EXTERNAL_DIFF.
-
- * "git diff" can be told to use different prefixes other than
-   "a/" and "b/" e.g. "git diff --src-prefix=l/ --dst-prefix=k/".
-
- * "git diff" sometimes did not quote paths with funny
-   characters properly.
-
- * "git log" (and any revision traversal commands) misbehaved
-   when --diff-filter is given but was not asked to actually
-   produce diff.
-
- * HTTP proxy can be specified per remote repository using
-   remote.*.httpproxy configuration, or global http.proxy configuration
-   variable.
-
- * Various Perforce importer updates.
-
- * Example update and post-receive hooks have been improved.
-
- * Any command that wants to take a commit object name can now use
-   ":/string" syntax to name a commit.
-
- * "git reset" is now built-in and its output can be squelched with -q.
-
- * "git reset --hard" does not make any sense in a bare
-   repository, but did not error out; fixed.
-
- * "git send-email" can optionally talk over ssmtp and use SMTP-AUTH.
-
- * "git rebase" learned --whitespace option.
-
- * In "git rebase", when you decide not to replay a particular change
-   after the command stopped with a conflict, you can say "git rebase
-   --skip" without first running "git reset --hard", as the command now
-   runs it for you.
-
- * "git rebase --interactive" mode can now work on detached HEAD.
-
- * Other minor to serious bugs in "git rebase -i" have been fixed.
-
- * "git rebase" now detaches head during its operation, so after a
-   successful "git rebase" operation, the reflog entry branch@{1} for
-   the current branch points at the commit before the rebase was
-   started.
-
- * "git rebase -i" also triggers rerere to help your repeated merges.
-
- * "git merge" can call the "post-merge" hook.
-
- * "git pack-objects" can optionally run deltification with multiple
-   threads.
-
- * "git archive" can optionally substitute keywords in files marked with
-   export-subst attribute.
-
- * "git cherry-pick" made a misguided attempt to repeat the original
-   command line in the generated log message, when told to cherry-pick a
-   commit by naming a tag that points at it.  It does not anymore.
-
- * "git for-each-ref" learned %(xxxdate:<date-format>) syntax to show the
-   various date fields in different formats.
-
- * "git gc --auto" is a low-impact way to automatically run a variant of
-   "git repack" that does not lose unreferenced objects (read: safer
-   than the usual one) after the user accumulates too many loose
-   objects.
-
- * "git clean" has been rewritten in C.
-
- * You need to explicitly set clean.requireForce to "false" to allow
-   "git clean" without -f to do any damage (lack of the configuration
-   variable used to mean "do not require -f option to lose untracked
-   files", but we now use the safer default).
-
- * The kinds of whitespace errors "git diff" and "git apply" notice (and
-   fix) can be controlled via 'core.whitespace' configuration variable
-   and 'whitespace' attribute in .gitattributes file.
-
- * "git push" learned --dry-run option to show what would happen if a
-   push is run.
-
- * "git push" does not update a tracking ref on the local side when the
-   remote refused to update the corresponding ref.
-
- * "git push" learned --mirror option.  This is to push the local refs
-   one-to-one to the remote, and deletes refs from the remote that do
-   not exist anymore in the repository on the pushing side.
-
- * "git push" can remove a corrupt ref at the remote site with the usual
-   ":ref" refspec.
-
- * "git remote" knows --mirror mode.  This is to set up configuration to
-   push into a remote repository to store local branch heads to the same
-   branch on the remote side, and remove branch heads locally removed
-   from local repository at the same time.  Suitable for pushing into a
-   back-up repository.
-
- * "git remote" learned "rm" subcommand.
-
- * "git cvsserver" can be run via "git shell".  Also, "cvs" is
-   recognized as a synonym for "git cvsserver", so that CVS users
-   can be switched to git just by changing their login shell.
-
- * "git cvsserver" acts more like receive-pack by running post-receive
-   and post-update hooks.
-
- * "git am" and "git rebase" are far less verbose.
-
- * "git pull" learned to pass --[no-]ff option to underlying "git
-   merge".
-
- * "git pull --rebase" is a different way to integrate what you fetched
-   into your current branch.
-
- * "git fast-export" produces data-stream that can be fed to fast-import
-   to reproduce the history recorded in a git repository.
-
- * "git add -i" takes pathspecs to limit the set of files to work on.
-
- * "git add -p" is a short-hand to go directly to the selective patch
-   subcommand in the interactive command loop and to exit when done.
-
- * "git add -i" UI has been colorized.  The interactive prompt
-   and menu can be colored by setting color.interactive
-   configuration.  The diff output (including the hunk picker)
-   are colored with color.diff configuration.
-
- * "git commit --allow-empty" allows you to create a single-parent
-   commit that records the same tree as its parent, overriding the usual
-   safety valve.
-
- * "git commit --amend" can amend a merge that does not change the tree
-   from its first parent.
-
- * "git commit" used to unconditionally strip comment lines that
-   began with '#' and removed excess blank lines.  This behavior has
-   been made configurable.
-
- * "git commit" has been rewritten in C.
-
- * "git stash random-text" does not create a new stash anymore.  It was
-   a UI mistake.  Use "git stash save random-text", or "git stash"
-   (without extra args) for that.
-
- * "git stash clear extra-text" does not clear the whole stash
-   anymore.  It is tempting to expect "git stash clear stash@{2}"
-   to drop only a single named stash entry, and it is rude to
-   discard everything when that is asked (but not provided).
-
- * "git prune --expire <time>" can exempt young loose objects from
-   getting pruned.
-
- * "git branch --contains <commit>" can list branches that are
-   descendants of a given commit.
-
- * "git log" learned --early-output option to help interactive GUI
-   implementations.
-
- * "git bisect" learned "skip" action to mark untestable commits.
-
- * "git bisect visualize" learned a shorter synonym "git bisect view".
-
- * "git bisect visualize" runs "git log" in a non-windowed
-   environments.  It also can be told what command to run (e.g. "git
-   bisect visualize tig").
-
- * "git format-patch" learned "format.numbered" configuration variable
-   to automatically turn --numbered option on when more than one commits
-   are formatted.
-
- * "git ls-files" learned "--exclude-standard" to use the canned set of
-   exclude files.
-
- * "git tag -a -f existing" begins the editor session using the existing
-   annotation message.
-
- * "git tag -m one -m bar" (multiple -m options) behaves similarly to
-   "git commit"; the parameters to -m options are formatted as separate
-   paragraphs.
-
- * The format "git show" outputs an annotated tag has been updated to
-   include "Tagger: " and "Date: " lines from the tag itself.  Strictly
-   speaking this is a backward incompatible change, but this is a
-   reasonable usability fix and people's scripts shouldn't have been
-   relying on the exact output from "git show" Porcelain anyway.
-
- * "git cvsimport" did not notice errors from underlying "cvsps"
-   and produced a corrupt import silently.
-
- * "git cvsexportcommit" learned -w option to specify and switch to the
-   CVS working directory.
-
- * "git checkout" from a subdirectory learned to use "../path" to allow
-   checking out a path outside the current directory without cd'ing up.
-
- * "git checkout" from and to detached HEAD leaves a bit more
-   information in the reflog.
-
- * "git send-email --dry-run" shows full headers for easier diagnosis.
-
- * "git merge-ours" is now built-in.
-
- * "git svn" learned "info" and "show-externals" subcommands.
-
- * "git svn" run from a subdirectory failed to read settings from the
-   .git/config.
-
- * "git svn" learned --use-log-author option, which picks up more
-   descriptive name from From: and Signed-off-by: lines in the commit
-   message.
-
- * "git svn" wasted way too much disk to record revision mappings
-   between svn and git; a new representation that is much more compact
-   for this information has been introduced to correct this.
-
- * "git svn" left temporary index files it used without cleaning them
-   up; this was corrected.
-
- * "git status" from a subdirectory now shows relative paths, which
-   makes copy-and-pasting for git-checkout/git-add/git-rm easier.  The
-   traditional behavior to show the full path relative to the top of
-   the work tree can be had by setting status.relativepaths
-   configuration variable to false.
-
- * "git blame" kept text for each annotated revision in core needlessly;
-   this has been corrected.
-
- * "git shortlog" learned to default to HEAD when the standard input is
-   a terminal and the user did not give any revision parameter.
-
- * "git shortlog" learned "-e" option to show e-mail addresses as well as
-   authors' names.
-
- * "git help" learned "-w" option to show documentation in browsers.
-
- * In addition there are quite a few internal clean-ups. Notably:
-
-   - many fork/exec have been replaced with run-command API,
-     brought from the msysgit effort.
-
-   - introduction and more use of the option parser API.
-
-   - enhancement and more use of the strbuf API.
-
- * Makefile tweaks to support HP-UX is in.
-
-Fixes since v1.5.3
-------------------
-
-All of the fixes in v1.5.3 maintenance series are included in
-this release, unless otherwise noted.
-
-These fixes are only in v1.5.4 and not backported to v1.5.3 maintenance
-series.
-
- * The way "git diff --check" behaves is much more consistent with the way
-   "git apply --whitespace=warn" works.
-
- * "git svn" talking with the SVN over HTTP will correctly quote branch
-   and project names.
-
- * "git config" did not work correctly on platforms that define
-   REG_NOMATCH to an even number.
-
- * Recent versions of AsciiDoc 8 has a change to break our
-   documentation; a workaround has been implemented.
-
- * "git diff --color-words" colored context lines in a wrong color.
diff --git a/Documentation/RelNotes-1.5.5.1.txt b/Documentation/RelNotes-1.5.5.1.txt
deleted file mode 100644 (file)
index 7de4197..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-GIT v1.5.5.1 Release Notes
-==========================
-
-Fixes since v1.5.5
-------------------
-
- * "git archive --prefix=$path/" mishandled gitattributes.
-
- * "git fetch -v" that fetches into FETCH_HEAD did not report the summary
-   the same way as done for updating the tracking refs.
-
- * "git svn" misbehaved when the configuration file customized the "git
-   log" output format using format.pretty.
-
- * "git submodule status" leaked an unnecessary error message.
-
- * "git log --date-order --topo-order" did not override the earlier
-   date-order with topo-order as expected.
-
- * "git bisect good $this" did not check the validity of the revision
-   given properly.
-
- * "url.<there>.insteadOf" did not work correctly.
-
- * "git clean" ran inside subdirectory behaved as if the directory was
-   explicitly specified for removal by the end user from the top level.
-
- * "git bisect" from a detached head leaked an unnecessary error message.
-
- * "git bisect good $a $b" when $a is Ok but $b is bogus should have
-   atomically failed before marking $a as good.
-
- * "git fmt-merge-msg" did not clean up leading empty lines from commit
-   log messages like "git log" family does.
-
- * "git am" recorded a commit with empty Subject: line without
-   complaining.
-
- * when given a commit log message whose first paragraph consists of
-   multiple lines, "git rebase" squashed it into a single line.
-
- * "git remote add $bogus_name $url" did not complain properly.
-
-Also comes with various documentation updates.
diff --git a/Documentation/RelNotes-1.5.5.2.txt b/Documentation/RelNotes-1.5.5.2.txt
deleted file mode 100644 (file)
index 391a7b0..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-GIT v1.5.5.2 Release Notes
-==========================
-
-Fixes since v1.5.5.1
---------------------
-
- * "git repack -n" was mistakenly made no-op earlier.
-
- * "git imap-send" wanted to always have imap.host even when use of
-   imap.tunnel made it unnecessary.
-
- * reflog syntax that uses time e.g. "HEAD@{10 seconds ago}:path" did not
-   stop parsing at the closing "}".
-
- * "git rev-parse --symbolic-full-name ^master^2" printed solitary "^",
-   but it should print nothing.
-
- * "git commit" did not detect when it failed to write tree objects.
-
- * "git fetch" sometimes transferred too many objects unnecessarily.
-
- * a path specification "a/b" in .gitattributes file should not match
-   "sub/a/b".
-
- * various gitweb fixes.
-
-Also comes with various documentation updates.
diff --git a/Documentation/RelNotes-1.5.5.3.txt b/Documentation/RelNotes-1.5.5.3.txt
deleted file mode 100644 (file)
index f22f98b..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-GIT v1.5.5.3 Release Notes
-==========================
-
-Fixes since v1.5.5.2
---------------------
-
- * "git send-email --compose" did not notice that non-ascii contents
-   needed some MIME magic.
-
- * "git fast-export" did not export octopus merges correctly.
-
-Also comes with various documentation updates.
diff --git a/Documentation/RelNotes-1.5.5.4.txt b/Documentation/RelNotes-1.5.5.4.txt
deleted file mode 100644 (file)
index 2d0279e..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-GIT v1.5.5.4 Release Notes
-==========================
-
-Fixes since v1.5.5.4
---------------------
-
- * "git name-rev --all" used to segfault.
diff --git a/Documentation/RelNotes-1.5.5.5.txt b/Documentation/RelNotes-1.5.5.5.txt
deleted file mode 100644 (file)
index 30fa361..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-GIT v1.5.5.5 Release Notes
-==========================
-
-I personally do not think there is any reason anybody should want to
-run v1.5.5.X series these days, because 'master' version is always
-more stable than any tagged released version of git.
-
-This is primarily to futureproof "git-shell" to accept requests
-without a dash between "git" and subcommand name (e.g. "git
-upload-pack") which the newer client will start to make sometime in
-the future.
diff --git a/Documentation/RelNotes-1.5.5.6.txt b/Documentation/RelNotes-1.5.5.6.txt
deleted file mode 100644 (file)
index d5e85cb..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-GIT v1.5.5.6 Release Notes
-==========================
-
-Fixes since 1.5.5.5
--------------------
-
- * Removed support for an obsolete gitweb request URI, whose
-   implementation ran "git diff" Porcelain, instead of using plumbing,
-   which would have run an external diff command specified in the
-   repository configuration as the gitweb user.
diff --git a/Documentation/RelNotes-1.5.5.txt b/Documentation/RelNotes-1.5.5.txt
deleted file mode 100644 (file)
index 2932212..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-GIT v1.5.5 Release Notes
-========================
-
-Updates since v1.5.4
---------------------
-
-(subsystems)
-
- * Comes with git-gui 0.10.1
-
-(portability)
-
- * We shouldn't ask for BSD group ownership semantics by setting g+s bit
-   on directories on older BSD systems that refuses chmod() by non root
-   users.  BSD semantics is the default there anyway.
-
- * Bunch of portability improvement patches coming from an effort to port
-   to Solaris has been applied.
-
-(performance)
-
- * On platforms with suboptimal qsort(3) implementation, there
-   is an option to use more reasonable substitute we ship with
-   our software.
-
- * New configuration variable "pack.packsizelimit" can be used
-   in place of command line option --max-pack-size.
-
- * "git fetch" over the native git protocol used to make a
-   connection to find out the set of current remote refs and
-   another to actually download the pack data.  We now use only
-   one connection for these tasks.
-
- * "git commit" does not run lstat(2) more than necessary
-   anymore.
-
-(usability, bells and whistles)
-
- * Bash completion script (in contrib) are aware of more commands and
-   options.
-
- * You can be warned when core.autocrlf conversion is applied in
-   such a way that results in an irreversible conversion.
-
- * A catch-all "color.ui" configuration variable can be used to
-   enable coloring of all color-capable commands, instead of
-   individual ones such as "color.status" and "color.branch".
-
- * The commands refused to take absolute pathnames where they
-   require pathnames relative to the work tree or the current
-   subdirectory.  They now can take absolute pathnames in such a
-   case as long as the pathnames do not refer outside of the
-   work tree.  E.g. "git add $(pwd)/foo" now works.
-
- * Error messages used to be sent to stderr, only to get hidden,
-   when $PAGER was in use.  They now are sent to stdout along
-   with the command output to be shown in the $PAGER.
-
- * A pattern "foo/" in .gitignore file now matches a directory
-   "foo".  Pattern "foo" also matches as before.
-
- * bash completion's prompt helper function can talk about
-   operation in-progress (e.g. merge, rebase, etc.).
-
- * Configuration variables "url.<usethis>.insteadof = <otherurl>" can be
-   used to tell "git-fetch" and "git-push" to use different URL than what
-   is given from the command line.
-
- * "git add -i" behaves better even before you make an initial commit.
-
- * "git am" refused to run from a subdirectory without a good reason.
-
- * After "git apply --whitespace=fix" fixes whitespace errors in a patch,
-   a line before the fix can appear as a context or preimage line in a
-   later patch, causing the patch not to apply.  The command now knows to
-   see through whitespace fixes done to context lines to successfully
-   apply such a patch series.
-
- * "git branch" (and "git checkout -b") to branch from a local branch can
-   optionally set "branch.<name>.merge" to mark the new branch to build on
-   the other local branch, when "branch.autosetupmerge" is set to
-   "always", or when passing the command line option "--track" (this option
-   was ignored when branching from local branches).  By default, this does
-   not happen when branching from a local branch.
-
- * "git checkout" to switch to a branch that has "branch.<name>.merge" set
-   (i.e. marked to build on another branch) reports how much the branch
-   and the other branch diverged.
-
- * When "git checkout" has to update a lot of paths, it used to be silent
-   for 4 seconds before it showed any progress report.  It is now a bit
-   more impatient and starts showing progress report early.
-
- * "git commit" learned a new hook "prepare-commit-msg" that can
-   inspect what is going to be committed and prepare the commit
-   log message template to be edited.
-
- * "git cvsimport" can now take more than one -M options.
-
- * "git describe" learned to limit the tags to be used for
-   naming with --match option.
-
- * "git describe --contains" now barfs when the named commit
-   cannot be described.
-
- * "git describe --exact-match" describes only commits that are tagged.
-
- * "git describe --long" describes a tagged commit as $tag-0-$sha1,
-   instead of just showing the exact tagname.
-
- * "git describe" warns when using a tag whose name and path contradict
-   with each other.
-
- * "git diff" learned "--relative" option to limit and output paths
-   relative to the current directory when working in a subdirectory.
-
- * "git diff" learned "--dirstat" option to show birds-eye-summary of
-   changes more concisely than "--diffstat".
-
- * "git format-patch" learned --cover-letter option to generate a cover
-   letter template.
-
- * "git gc" learned --quiet option.
-
- * "git gc" now automatically prunes unreachable objects that are two
-   weeks old or older.
-
- * "git gc --auto" can be disabled more easily by just setting gc.auto
-   to zero.  It also tolerates more packfiles by default.
-
- * "git grep" now knows "--name-only" is a synonym for the "-l" option.
-
- * "git help <alias>" now reports "'git <alias>' is alias to <what>",
-   instead of saying "No manual entry for git-<alias>".
-
- * "git help" can use different backends to show manual pages and this can
-   be configured using "man.viewer" configuration.
-
- * "gitk" does not restore window position from $HOME/.gitk anymore (it
-   still restores the size).
-
- * "git log --grep=<what>" learned "--fixed-strings" option to look for
-   <what> without treating it as a regular expression.
-
- * "git gui" learned an auto-spell checking.
-
- * "git push <somewhere> HEAD" and "git push <somewhere> +HEAD" works as
-   expected; they push the current branch (and only the current branch).
-   In addition, HEAD can be written as the value of "remote.<there>.push"
-   configuration variable.
-
- * When the configuration variable "pack.threads" is set to 0, "git
-   repack" auto detects the number of CPUs and uses that many threads.
-
- * "git send-email" learned to prompt for passwords
-   interactively.
-
- * "git send-email" learned an easier way to suppress CC
-   recipients.
-
- * "git stash" learned "pop" command, that applies the latest stash and
-   removes it from the stash, and "drop" command to discard the named
-   stash entry.
-
- * "git submodule" learned a new subcommand "summary" to show the
-   symmetric difference between the HEAD version and the work tree version
-   of the submodule commits.
-
- * Various "git cvsimport", "git cvsexportcommit", "git cvsserver",
-   "git svn" and "git p4" improvements.
-
-(internal)
-
- * Duplicated code between git-help and git-instaweb that
-   launches user's preferred browser has been refactored.
-
- * It is now easier to write test scripts that records known
-   breakages.
-
- * "git checkout" is rewritten in C.
-
- * "git remote" is rewritten in C.
-
- * Two conflict hunks that are separated by a very short span of common
-   lines are now coalesced into one larger hunk, to make the result easier
-   to read.
-
- * Run-command API's use of file descriptors is documented clearer and
-   is more consistent now.
-
- * diff output can be sent to FILE * that is different from stdout.  This
-   will help reimplementing more things in C.
-
-Fixes since v1.5.4
-------------------
-
-All of the fixes in v1.5.4 maintenance series are included in
-this release, unless otherwise noted.
-
- * "git-http-push" did not allow deletion of remote ref with the usual
-   "push <remote> :<branch>" syntax.
-
- * "git-rebase --abort" did not go back to the right location if
-   "git-reset" was run during the "git-rebase" session.
-
- * "git imap-send" without setting imap.host did not error out but
-   segfaulted.
diff --git a/Documentation/RelNotes-1.5.6.1.txt b/Documentation/RelNotes-1.5.6.1.txt
deleted file mode 100644 (file)
index 4864b16..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-GIT v1.5.6.1 Release Notes
-==========================
-
-Fixes since v1.5.6
-------------------
-
-* Last minute change broke loose object creation on AIX.
-
-* (performance fix) We used to make $GIT_DIR absolute path early in the
-  programs but keeping it relative to the current directory internally
-  gives 1-3 per-cent performance boost.
-
-* bash completion knows the new --graph option to git-log family.
-
-
-* git-diff -c/--cc showed unnecessary "deletion" lines at the context
-  boundary.
-
-* git-for-each-ref ignored %(object) and %(type) requests for tag
-  objects.
-
-* git-merge usage had a typo.
-
-* Rebuilding of git-svn metainfo database did not take rewriteRoot
-  option into account.
-
-* Running "git-rebase --continue/--skip/--abort" before starting a
-  rebase gave nonsense error messages.
diff --git a/Documentation/RelNotes-1.5.6.2.txt b/Documentation/RelNotes-1.5.6.2.txt
deleted file mode 100644 (file)
index 5902a85..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-GIT v1.5.6.2 Release Notes
-==========================
-
-Futureproof
------------
-
- * "git-shell" accepts requests without a dash between "git" and
-   subcommand name (e.g. "git upload-pack") which the newer client will
-   start to make sometime in the future.
-
-Fixes since v1.5.6.1
---------------------
-
-* "git clone" from a remote that is named with url.insteadOf setting in
-  $HOME/.gitconfig did not work well.
-
-* "git describe --long --tags" segfaulted when the described revision was
-  tagged with a lightweight tag.
-
-* "git diff --check" did not report the result via its exit status
-  reliably.
-
-* When remote side used to have branch 'foo' and git-fetch finds that now
-  it has branch 'foo/bar', it refuses to lose the existing remote tracking
-  branch and its reflog.  The error message has been improved to suggest
-  pruning the remote if the user wants to proceed and get the latest set
-  of branches from the remote, including such 'foo/bar'.
-
-* "git reset file" should mean the same thing as "git reset HEAD file",
-  but we required disambiguating -- even when "file" is not ambiguous.
-
-* "git show" segfaulted when an annotated tag that points at another
-  annotated tag was given to it.
-
-* Optimization for a large import via "git-svn" introduced in v1.5.6 had a
-  serious memory and temporary file leak, which made it unusable for
-  moderately large import.
-
-* "git-svn" mangled remote nickname used in the configuration file
-  unnecessarily.
diff --git a/Documentation/RelNotes-1.5.6.3.txt b/Documentation/RelNotes-1.5.6.3.txt
deleted file mode 100644 (file)
index 9426112..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-GIT v1.5.6.3 Release Notes
-==========================
-
-Fixes since v1.5.6.2
---------------------
-
-* Setting core.sharerepository to traditional "true" value was supposed to make
-  the repository group writable but should not affect permission for others.
-  However, since 1.5.6, it was broken to drop permission for others when umask is
-  022, making the repository unreadable by others.
-
-* Setting GIT_TRACE will report spawning of external process via run_command().
-
-* Using an object with very deep delta chain pinned memory needed for extracting
-  intermediate base objects unnecessarily long, leading to excess memory usage.
-
-* Bash completion script did not notice '--' marker on the command
-  line and tried the relatively slow "ref completion" even when
-  completing arguments after one.
-
-* Registering a non-empty blob racily and then truncating the working
-  tree file for it confused "racy-git avoidance" logic into thinking
-  that the path is now unchanged.
-
-* The section that describes attributes related to git-archive were placed
-  in a wrong place in the gitattributes(5) manual page.
-
-* "git am" was not helpful to the users when it detected that the committer
-  information is not set up properly yet.
-
-* "git clone" had a leftover debugging fprintf().
-
-* "git clone -q" was not quiet enough as it used to and gave object count
-  and progress reports.
-
-* "git clone" marked downloaded packfile with .keep; this could be a
-  good thing if the remote side is well packed but otherwise not,
-  especially for a project that is not really big.
-
-* "git daemon" used to call syslog() from a signal handler, which
-  could raise signals of its own but generally is not reentrant.  This
-  was fixed by restructuring the code to report syslog() after the handler
-  returns.
-
-* When "git push" tries to remove a remote ref, and corresponding
-  tracking ref is missing, we used to report error (i.e. failure to
-  remove something that does not exist).
-
-* "git mailinfo" (hence "git am") did not handle commit log messages in a
-  MIME multipart mail correctly.
-
-Contains other various documentation fixes.
diff --git a/Documentation/RelNotes-1.5.6.4.txt b/Documentation/RelNotes-1.5.6.4.txt
deleted file mode 100644 (file)
index d8968f1..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-GIT v1.5.6.4 Release Notes
-==========================
-
-Fixes since v1.5.6.3
---------------------
-
-* Various commands could overflow its internal buffer on a platform
-  with small PATH_MAX value in a repository that has contents with
-  long pathnames.
-
-* There wasn't a way to make --pretty=format:%<> specifiers to honor
-  .mailmap name rewriting for authors and committers.  Now you can with
-  %aN and %cN.
-
-* Bash completion wasted too many cycles; this has been optimized to be
-  usable again.
-
-* Bash completion lost ref part when completing something like "git show
-  pu:Makefile".
-
-* "git-cvsserver" did not clean up its temporary working area after annotate
-  request.
-
-* "git-daemon" called syslog() from its signal handler, which was a
-  no-no.
-
-* "git-fetch" into an empty repository used to remind that the fetch will
-   be huge by saying "no common commits", but this was an unnecessary
-   noise; it is already known by the user anyway.
-
-* "git-http-fetch" would have segfaulted when pack idx file retrieved
-  from the other side was corrupt.
-
-* "git-index-pack" used too much memory when dealing with a deep delta chain.
-
-* "git-mailinfo" (hence "git-am") did not correctly handle in-body [PATCH]
-  line to override the commit title taken from the mail Subject header.
-
-* "git-rebase -i -p" lost parents that are not involved in the history
-  being rewritten.
-
-* "git-rm" lost track of where the index file was when GIT_DIR was
-  specified as a relative path.
-
-* "git-rev-list --quiet" was not quiet as advertised.
-
-Contains other various documentation fixes.
diff --git a/Documentation/RelNotes-1.5.6.5.txt b/Documentation/RelNotes-1.5.6.5.txt
deleted file mode 100644 (file)
index 47ca172..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-GIT v1.5.6.5 Release Notes
-==========================
-
-Fixes since v1.5.6.4
---------------------
-
-* "git cvsimport" used to spit out "UNKNOWN LINE..." diagnostics to stdout.
-
-* "git commit -F filename" and "git tag -F filename" run from subdirectories
-  did not read the right file.
-
-* "git init --template=" with blank "template" parameter linked files
-  under root directories to .git, which was a total nonsense.  Instead, it
-  means "I do not want to use anything from the template directory".
-
-* "git diff-tree" and other diff plumbing ignored diff.renamelimit configuration
-  variable when the user explicitly asked for rename detection.
-
-* "git name-rev --name-only" did not work when "--stdin" option was in effect.
-
-* "git show-branch" mishandled its 8th branch.
-
-* Addition of "git update-index --ignore-submodules" that happened during
-  1.5.6 cycle broke "git update-index --ignore-missing".
-
-* "git send-email" did not parse charset from an existing Content-type:
-  header properly.
-
-Contains other various documentation fixes.
diff --git a/Documentation/RelNotes-1.5.6.6.txt b/Documentation/RelNotes-1.5.6.6.txt
deleted file mode 100644 (file)
index 79da23d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-GIT v1.5.6.6 Release Notes
-==========================
-
-Fixes since 1.5.6.5
--------------------
-
- * Removed support for an obsolete gitweb request URI, whose
-   implementation ran "git diff" Porcelain, instead of using plumbing,
-   which would have run an external diff command specified in the
-   repository configuration as the gitweb user.
diff --git a/Documentation/RelNotes-1.5.6.txt b/Documentation/RelNotes-1.5.6.txt
deleted file mode 100644 (file)
index e143d8d..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-GIT v1.5.6 Release Notes
-========================
-
-Updates since v1.5.5
---------------------
-
-(subsystems)
-
-* Comes with updated gitk and git-gui.
-
-(portability)
-
-* git will build on AIX better than before now.
-
-* core.ignorecase configuration variable can be used to work better on
-  filesystems that are not case sensitive.
-
-* "git init" now autodetects the case sensitivity of the filesystem and
-  sets core.ignorecase accordingly.
-
-* cpio is no longer used; neither "curl" binary (libcurl is still used).
-
-(documentation)
-
-* Many freestanding documentation pages have been converted and made
-  available to "git help" (aka "man git<something>") as section 7 of
-  the manual pages. This means bookmarks to some HTML documentation
-  files may need to be updated (eg "tutorial.html" became
-  "gittutorial.html").
-
-(performance)
-
-* "git clone" was rewritten in C.  This will hopefully help cloning a
-  repository with insane number of refs.
-
-* "git rebase --onto $there $from $branch" used to switch to the tip of
-  $branch only to immediately reset back to $from, smudging work tree
-  files unnecessarily.  This has been optimized.
-
-* Object creation codepath in "git-svn" has been optimized by enhancing
-  plumbing commands git-cat-file and git-hash-object.
-
-(usability, bells and whistles)
-
-* "git add -p" (and the "patch" subcommand of "git add -i") can choose to
-  apply (or not apply) mode changes independently from contents changes.
-
-* "git bisect help" gives longer and more helpful usage information.
-
-* "git bisect" does not use a special branch "bisect" anymore; instead, it
-  does its work on a detached HEAD.
-
-* "git branch" (and "git checkout -b") can be told to set up
-  branch.<name>.rebase automatically, so that later you can say "git pull"
-  and magically cause "git pull --rebase" to happen.
-
-* "git branch --merged" and "git branch --no-merged" can be used to list
-  branches that have already been merged (or not yet merged) to the
-  current branch.
-
-* "git cherry-pick" and "git revert" can add a sign-off.
-
-* "git commit" mentions the author identity when you are committing
-  somebody else's changes.
-
-* "git diff/log --dirstat" output is consistent between binary and textual
-  changes.
-
-* "git filter-branch" rewrites signed tags by demoting them to annotated.
-
-* "git format-patch --no-binary" can produce a patch that lack binary
-  changes (i.e. cannot be used to propagate the whole changes) meant only
-  for reviewing.
-
-* "git init --bare" is a synonym for "git --bare init" now.
-
-* "git gc --auto" honors a new pre-auto-gc hook to temporarily disable it.
-
-* "git log --pretty=tformat:<custom format>" gives a LF after each entry,
-  instead of giving a LF between each pair of entries which is how
-  "git log --pretty=format:<custom format>" works.
-
-* "git log" and friends learned the "--graph" option to show the ancestry
-  graph at the left margin of the output.
-
-* "git log" and friends can be told to use date format that is different
-  from the default via 'log.date' configuration variable.
-
-* "git send-email" now can send out messages outside a git repository.
-
-* "git send-email --compose" was made aware of rfc2047 quoting.
-
-* "git status" can optionally include output from "git submodule
-  summary".
-
-* "git svn" learned --add-author-from option to propagate the authorship
-  by munging the commit log message.
-
-* new object creation and looking up in "git svn" has been optimized.
-
-* "gitweb" can read from a system-wide configuration file.
-
-(internal)
-
-* "git unpack-objects" and "git receive-pack" is now more strict about
-  detecting breakage in the objects they receive over the wire.
-
-
-Fixes since v1.5.5
-------------------
-
-All of the fixes in v1.5.5 maintenance series are included in
-this release, unless otherwise noted.
-
-And there are too numerous small fixes to otherwise note here ;-)
diff --git a/Documentation/RelNotes-1.6.0.1.txt b/Documentation/RelNotes-1.6.0.1.txt
deleted file mode 100644 (file)
index 49d7a1c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-GIT v1.6.0.1 Release Notes
-==========================
-
-Fixes since v1.6.0
-------------------
-
-* "git diff --cc" did not honor content mangling specified by
-  gitattributes and core.autocrlf when reading from the work tree.
-
-* "git diff --check" incorrectly detected new trailing blank lines when
-  whitespace check was in effect.
-
-* "git for-each-ref" tried to dereference NULL when asked for '%(body)" on
-  a tag with a single incomplete line as its payload.
-
-* "git format-patch" peeked before the beginning of a string when
-  "format.headers" variable is empty (a misconfiguration).
-
-* "git help help" did not work correctly.
-
-* "git mailinfo" (hence "git am") was unhappy when MIME multipart message
-  contained garbage after the finishing boundary.
-
-* "git mailinfo" also was unhappy when the "From: " line only had a bare
-  e-mail address.
-
-* "git merge" did not refresh the index correctly when a merge resulted in
-  a fast-forward.
-
-* "git merge" did not resolve a truly trivial merges that can be done
-  without content level merges.
-
-* "git svn dcommit" to a repository with URL that has embedded usernames
-  did not work correctly.
-
-Contains other various documentation fixes.
diff --git a/Documentation/RelNotes-1.6.0.2.txt b/Documentation/RelNotes-1.6.0.2.txt
deleted file mode 100644 (file)
index 51b32f5..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-GIT v1.6.0.2 Release Notes
-==========================
-
-Fixes since v1.6.0.1
---------------------
-
-* Installation on platforms that needs .exe suffix to git-* programs were
-  broken in 1.6.0.1.
-
-* Installation on filesystems without symbolic links support did not
-  work well.
-
-* In-tree documentations and test scripts now use "git foo" form to set a
-  better example, instead of the "git-foo" form (which is an acceptable
-  form if you have "PATH=$(git --exec-path):$PATH" in your script)
-
-* Many commands did not use the correct working tree location when used
-  with GIT_WORK_TREE environment settings.
-
-* Some systems needs to use compatibility fnmach and regex libraries
-  independent from each other; the compat/ area has been reorganized to
-  allow this.
-
-
-* "git apply --unidiff-zero" incorrectly applied a -U0 patch that inserts
-  a new line before the second line.
-
-* "git blame -c" did not exactly work like "git annotate" when range
-  boundaries are involved.
-
-* "git checkout file" when file is still unmerged checked out contents from
-  a random high order stage, which was confusing.
-
-* "git clone $there $here/" with extra trailing slashes after explicit
-  local directory name $here did not work as expected.
-
-* "git diff" on tracked contents with CRLF line endings did not drive "less"
-  intelligently when showing added or removed lines.
-
-* "git diff --dirstat -M" did not add changes in subdirectories up
-  correctly for renamed paths.
-
-* "git diff --cumulative" did not imply "--dirstat".
-
-* "git for-each-ref refs/heads/" did not work as expected.
-
-* "git gui" allowed users to feed patch without any context to be applied.
-
-* "git gui" botched parsing "diff" output when a line that begins with two
-  dashes and a space gets removed or a line that begins with two pluses
-  and a space gets added.
-
-* "git gui" translation updates and i18n fixes.
-
-* "git index-pack" is more careful against disk corruption while completing
-  a thin pack.
-
-* "git log -i --grep=pattern" did not ignore case; neither "git log -E
-  --grep=pattern" triggered extended regexp.
-
-* "git log --pretty="%ad" --date=short" did not use short format when
-  showing the timestamp.
-
-* "git log --author=author" match incorrectly matched with the
-  timestamp part of "author " line in commit objects.
-
-* "git log -F --author=author" did not work at all.
-
-* Build procedure for "git shell" that used stub versions of some
-  functions and globals was not understood by linkers on some platforms.
-
-* "git stash" was fooled by a stat-dirty but otherwise unmodified paths
-  and refused to work until the user refreshed the index.
-
-* "git svn" was broken on Perl before 5.8 with recent fixes to reduce
-  use of temporary files.
-
-* "git verify-pack -v" did not work correctly when given more than one
-  packfile.
-
-Also contains many documentation updates.
-
---
-exec >/var/tmp/1
-O=v1.6.0.1-78-g3632cfc
-echo O=$(git describe maint)
-git shortlog --no-merges $O..maint
diff --git a/Documentation/RelNotes-1.6.0.3.txt b/Documentation/RelNotes-1.6.0.3.txt
deleted file mode 100644 (file)
index ae05778..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-GIT v1.6.0.3 Release Notes
-==========================
-
-Fixes since v1.6.0.2
---------------------
-
-* "git archive --format=zip" did not honor core.autocrlf while
-  --format=tar did.
-
-* Continuing "git rebase -i" was very confused when the user left modified
-  files in the working tree while resolving conflicts.
-
-* Continuing "git rebase -i" was also very confused when the user left
-  some staged changes in the index after "edit".
-
-* "git rebase -i" now honors the pre-rebase hook, just like the
-  other rebase implementations "git rebase" and "git rebase -m".
-
-* "git rebase -i" incorrectly aborted when there is no commit to replay.
-
-* Behaviour of "git diff --quiet" was inconsistent with "diff --exit-code"
-  with the output redirected to /dev/null.
-
-* "git diff --no-index" on binary files no longer outputs a bogus
-  "diff --git" header line.
-
-* "git diff" hunk header patterns with multiple elements separated by LF
-  were not used correctly.
-
-* Hunk headers in "git diff" default to using extended regular
-  expressions, fixing some of the internal patterns on non-GNU
-  platforms.
-
-* New config "diff.*.xfuncname" exposes extended regular expressions
-  for user specified hunk header patterns.
-
-* "git gc" when ejecting otherwise unreachable objects from packfiles into
-  loose form leaked memory.
-
-* "git index-pack" was recently broken and mishandled objects added by
-  thin-pack completion processing under memory pressure.
-
-* "git index-pack" was recently broken and misbehaved when run from inside
-  .git/objects/pack/ directory.
-
-* "git stash apply sash@{1}" was fixed to error out.  Prior versions
-  would have applied stash@{0} incorrectly.
-
-* "git stash apply" now offers a better suggestion on how to continue
-  if the working tree is currently dirty.
-
-* "git for-each-ref --format=%(subject)" fixed for commits with no
-  no newline in the message body.
-
-* "git remote" fixed to protect printf from user input.
-
-* "git remote show -v" now displays all URLs of a remote.
-
-* "git checkout -b branch" was confused when branch already existed.
-
-* "git checkout -q" once again suppresses the locally modified file list.
-
-* "git clone -q", "git fetch -q" asks remote side to not send
-  progress messages, actually making their output quiet.
-
-* Cross-directory renames are no longer used when creating packs.  This
-  allows more graceful behavior on filesystems like sshfs.
-
-* Stale temporary files under $GIT_DIR/objects/pack are now cleaned up
-  automatically by "git prune".
-
-* "git merge" once again removes directories after the last file has
-  been removed from it during the merge.
-
-* "git merge" did not allocate enough memory for the structure itself when
-  enumerating the parents of the resulting commit.
-
-* "git blame -C -C" no longer segfaults while trying to pass blame if
-   it encounters a submodule reference.
-
-* "git rm" incorrectly claimed that you have local modifications when a
-  path was merely stat-dirty.
-
-* "git svn" fixed to display an error message when 'set-tree' failed,
-   instead of a Perl compile error.
-
-* "git submodule" fixed to handle checking out a different commit
-  than HEAD after initializing the submodule.
-
-* The "git commit" error message when there are still unmerged
-  files present was clarified to match "git write-tree".
-
-* "git init" was confused when core.bare or core.sharedRepository are set
-  in system or user global configuration file by mistake.  When --bare or
-  --shared is given from the command line, these now override such
-  settings made outside the repositories.
-
-* Some segfaults due to uncaught NULL pointers were fixed in multiple
-  tools such as apply, reset, update-index.
-
-* Solaris builds now default to OLD_ICONV=1 to avoid compile warnings;
-  Solaris 8 does not define NEEDS_LIBICONV by default.
-
-* "Git.pm" tests relied on unnecessarily more recent version of Perl.
-
-* "gitweb" triggered undef warning on commits without log messages.
-
-* "gitweb" triggered undef warnings on missing trees.
-
-* "gitweb" now removes PATH_INFO from its URLs so users don't have
-  to manually set the URL in the gitweb configuration.
-
-* Bash completion removed support for legacy "git-fetch", "git-push"
-  and "git-pull" as these are no longer installed.  Dashless form
-  ("git fetch") is still however supported.
-
-Many other documentation updates.
diff --git a/Documentation/RelNotes-1.6.0.4.txt b/Documentation/RelNotes-1.6.0.4.txt
deleted file mode 100644 (file)
index d522661..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-GIT v1.6.0.4 Release Notes
-==========================
-
-Fixes since v1.6.0.3
---------------------
-
-* 'git add -p' said "No changes" when only binary files were changed.
-
-* 'git archive' did not work correctly in bare repositories.
-
-* 'git checkout -t -b newbranch' when you are on detached HEAD was broken.
-
-* when we refuse to detect renames because there are too many new or
-  deleted files, 'git diff' did not say how many there are.
-
-* 'git push --mirror' tried and failed to push the stash; there is no
-  point in sending it to begin with.
-
-* 'git push' did not update the remote tracking reference if the corresponding
-  ref on the remote end happened to be already up to date.
-
-* 'git pull $there $branch:$current_branch' did not work when you were on
-  a branch yet to be born.
-
-* when giving up resolving a conflicted merge, 'git reset --hard' failed
-  to remove new paths from the working tree.
-
-* 'git send-email' had a small fd leak while scanning directory.
-
-* 'git status' incorrectly reported a submodule directory as an untracked
-  directory.
-
-* 'git svn' used deprecated 'git-foo' form of subcommand invocation.
-
-* 'git update-ref -d' to remove a reference did not honor --no-deref option.
-
-* Plugged small memleaks here and there.
-
-* Also contains many documentation updates.
diff --git a/Documentation/RelNotes-1.6.0.5.txt b/Documentation/RelNotes-1.6.0.5.txt
deleted file mode 100644 (file)
index a08bb96..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-GIT v1.6.0.5 Release Notes
-==========================
-
-Fixes since v1.6.0.4
---------------------
-
-* "git checkout" used to crash when your HEAD was pointing at a deleted
-  branch.
-
-* "git checkout" from an un-checked-out state did not allow switching out
-  of the current branch.
-
-* "git diff" always allowed GIT_EXTERNAL_DIFF and --no-ext-diff was no-op for
-  the command.
-
-* Giving 3 or more tree-ish to "git diff" is supposed to show the combined
-  diff from second and subsequent trees to the first one, but the order was
-  screwed up.
-
-* "git fast-export" did not export all tags.
-
-* "git ls-files --with-tree=<tree>" did not work with options other
-  than -c, most notably with -m.
-
-* "git pack-objects" did not make its best effort to honor --max-pack-size
-  option when a single first object already busted the given limit and
-  placed many objects in a single pack.
-
-* "git-p4" fast import frontend was too eager to trigger its keyword expansion
-  logic, even on a keyword-looking string that does not have closing '$' on the
-  same line.
-
-* "git push $there" when the remote $there is defined in $GIT_DIR/branches/$there
-  behaves more like what cg-push from Cogito used to work.
-
-* when giving up resolving a conflicted merge, "git reset --hard" failed
-  to remove new paths from the working tree.
-
-* "git tag" did not complain when given mutually incompatible set of options.
-
-* The message constructed in the internal editor was discarded when "git
-  tag -s" failed to sign the message, which was often caused by the user
-  not configuring GPG correctly.
-
-* "make check" cannot be run without sparse; people may have meant to say
-  "make test" instead, so suggest that.
-
-* Internal diff machinery had a corner case performance bug that choked on
-  a large file with many repeated contents.
-
-* "git repack" used to grab objects out of packs marked with .keep
-  into a new pack.
-
-* Many unsafe call to sprintf() style varargs functions are corrected.
-
-* Also contains quite a few documentation updates.
diff --git a/Documentation/RelNotes-1.6.0.6.txt b/Documentation/RelNotes-1.6.0.6.txt
deleted file mode 100644 (file)
index 64ece1f..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-GIT v1.6.0.6 Release Notes
-==========================
-
-Fixes since 1.6.0.5
--------------------
-
- * "git fsck" had a deep recursion that wasted stack space.
-
- * "git fast-export" and "git fast-import" choked on an old style
-   annotated tag that lack the tagger information.
-
- * "git mergetool -- file" did not correctly skip "--" marker that
-   signals the end of options list.
-
- * "git show $tag" segfaulted when an annotated $tag pointed at a
-   nonexistent object.
-
- * "git show 2>error" when the standard output is automatically redirected
-   to the pager redirected the standard error to the pager as well; there
-   was no need to.
-
- * "git send-email" did not correctly handle list of addresses when
-   they had quoted comma (e.g. "Lastname, Givenname" <mail@addre.ss>).
-
- * Logic to discover branch ancestry in "git svn" was unreliable when
-   the process to fetch history was interrupted.
-
- * Removed support for an obsolete gitweb request URI, whose
-   implementation ran "git diff" Porcelain, instead of using plumbing,
-   which would have run an external diff command specified in the
-   repository configuration as the gitweb user.
-
-Also contains numerous documentation typofixes.
diff --git a/Documentation/RelNotes-1.6.0.txt b/Documentation/RelNotes-1.6.0.txt
deleted file mode 100644 (file)
index de7ef16..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-GIT v1.6.0 Release Notes
-========================
-
-User visible changes
---------------------
-
-With the default Makefile settings, most of the programs are now
-installed outside your $PATH, except for "git", "gitk" and
-some server side programs that need to be accessible for technical
-reasons.  Invoking a git subcommand as "git-xyzzy" from the command
-line has been deprecated since early 2006 (and officially announced in
-1.5.4 release notes); use of them from your scripts after adding
-output from "git --exec-path" to the $PATH is still supported in this
-release, but users are again strongly encouraged to adjust their
-scripts to use "git xyzzy" form, as we will stop installing
-"git-xyzzy" hardlinks for built-in commands in later releases.
-
-An earlier change to page "git status" output was overwhelmingly unpopular
-and has been reverted.
-
-Source changes needed for porting to MinGW environment are now all in the
-main git.git codebase.
-
-By default, packfiles created with this version uses delta-base-offset
-encoding introduced in v1.4.4.  Pack idx files are using version 2 that
-allows larger packs and added robustness thanks to its CRC checking,
-introduced in v1.5.2 and v1.4.4.5.  If you want to keep your repositories
-backwards compatible past these versions, set repack.useDeltaBaseOffset
-to false or pack.indexVersion to 1, respectively.
-
-We used to prevent sample hook scripts shipped in templates/ from
-triggering by default by relying on the fact that we install them as
-unexecutable, but on some filesystems, this approach does not work.
-They are now shipped with ".sample" suffix.  If you want to activate
-any of these samples as-is, rename them to drop the ".sample" suffix,
-instead of running "chmod +x" on them.  For example, you can rename
-hooks/post-update.sample to hooks/post-update to enable the sample
-hook that runs update-server-info, in order to make repositories
-friendly to dumb protocols (i.e. HTTP).
-
-GIT_CONFIG, which was only documented as affecting "git config", but
-actually affected all git commands, now only affects "git config".
-GIT_LOCAL_CONFIG, also only documented as affecting "git config" and
-not different from GIT_CONFIG in a useful way, is removed.
-
-The ".dotest" temporary area "git am" and "git rebase" use is now moved
-inside the $GIT_DIR, to avoid mistakes of adding it to the project by
-accident.
-
-An ancient merge strategy "stupid" has been removed.
-
-
-Updates since v1.5.6
---------------------
-
-(subsystems)
-
-* git-p4 in contrib learned "allowSubmit" configuration to control on
-  which branch to allow "submit" subcommand.
-
-* git-gui learned to stage changes per-line.
-
-(portability)
-
-* Changes for MinGW port have been merged, thanks to Johannes Sixt and
-  gangs.
-
-* Sample hook scripts shipped in templates/ are now suffixed with
-  *.sample.
-
-* perl's in-place edit (-i) does not work well without backup files on Windows;
-  some tests are rewritten to cope with this.
-
-(documentation)
-
-* Updated howto/update-hook-example
-
-* Got rid of usage of "git-foo" from the tutorial and made typography
-  more consistent.
-
-* Disambiguating "--" between revs and paths is finally documented.
-
-(performance, robustness, sanity etc.)
-
-* index-pack used too much memory when dealing with a deep delta chain.
-  This has been optimized.
-
-* reduced excessive inlining to shrink size of the "git" binary.
-
-* verify-pack checks the object CRC when using version 2 idx files.
-
-* When an object is corrupt in a pack, the object became unusable even
-  when the same object is available in a loose form,  We now try harder to
-  fall back to these redundant objects when able.  In particular, "git
-  repack -a -f" can be used to fix such a corruption as long as necessary
-  objects are available.
-
-* Performance of "git-blame -C -C" operation is vastly improved.
-
-* git-clone does not create refs in loose form anymore (it behaves as
-  if you immediately ran git-pack-refs after cloning).  This will help
-  repositories with insanely large number of refs.
-
-* core.fsyncobjectfiles configuration can be used to ensure that the loose
-  objects created will be fsync'ed (this is only useful on filesystems
-  that does not order data writes properly).
-
-* "git commit-tree" plumbing can make Octopus with more than 16 parents.
-  "git commit" has been capable of this for quite some time.
-
-(usability, bells and whistles)
-
-* even more documentation pages are now accessible via "man" and "git help".
-
-* A new environment variable GIT_CEILING_DIRECTORIES can be used to stop
-  the discovery process of the toplevel of working tree; this may be useful
-  when you are working in a slow network disk and are outside any working tree,
-  as bash-completion and "git help" may still need to run in these places.
-
-* By default, stash entries never expire.  Set reflogexpire in [gc
-  "refs/stash"] to a reasonable value to get traditional auto-expiration
-  behaviour back
-
-* Longstanding latency issue with bash completion script has been
-  addressed.  This will need to be backmerged to 'maint' later.
-
-* pager.<cmd> configuration variable can be used to enable/disable the
-  default paging behaviour per command.
-
-* "git-add -i" has a new action 'e/dit' to allow you edit the patch hunk
-  manually.
-
-* git-am records the original tip of the branch in ORIG_HEAD before it
-  starts applying patches.
-
-* git-apply can handle a patch that touches the same path more than once
-  much better than before.
-
-* git-apply can be told not to trust the line counts recorded in the input
-  patch but recount, with the new --recount option.
-
-* git-apply can be told to apply a patch to a path deeper than what the
-  patch records with --directory option.
-
-* git-archive can be told to omit certain paths from its output using
-  export-ignore attributes.
-
-* git-archive uses the zlib default compression level when creating
-  zip archive.
-
-* git-archive's command line options --exec and --remote can take their
-  parameters as separate command line arguments, similar to other commands.
-  IOW, both "--exec=path" and "--exec path" are now supported.
-
-* With -v option, git-branch describes the remote tracking statistics
-  similar to the way git-checkout reports by how many commits your branch
-  is ahead/behind.
-
-* git-branch's --contains option used to always require a commit parameter
-  to limit the branches with; it now defaults to list branches that
-  contains HEAD if this parameter is omitted.
-
-* git-branch's --merged and --no-merged option used to always limit the
-  branches relative to the HEAD, but they can now take an optional commit
-  argument that is used in place of HEAD.
-
-* git-bundle can read the revision arguments from the standard input.
-
-* git-cherry-pick can replay a root commit now.
-
-* git-clone can clone from a remote whose URL would be rewritten by
-  configuration stored in $HOME/.gitconfig now.
-
-* "git-clone --mirror" is a handy way to set up a bare mirror repository.
-
-* git-cvsserver learned to respond to "cvs co -c".
-
-* git-diff --check now checks leftover merge conflict markers.
-
-* "git-diff -p" learned to grab a better hunk header lines in
-  BibTex, Pascal/Delphi, and Ruby files and also pays attention to
-  chapter and part boundary in TeX documents.
-
-* When remote side used to have branch 'foo' and git-fetch finds that now
-  it has branch 'foo/bar', it refuses to lose the existing remote tracking
-  branch and its reflog.  The error message has been improved to suggest
-  pruning the remote if the user wants to proceed and get the latest set
-  of branches from the remote, including such 'foo/bar'.
-
-* fast-export learned to export and import marks file; this can be used to
-  interface with fast-import incrementally.
-
-* fast-import and fast-export learned to export and import gitlinks.
-
-* "gitk" left background process behind after being asked to dig very deep
-  history and the user killed the UI; the process is killed when the UI goes
-  away now.
-
-* git-rebase records the original tip of branch in ORIG_HEAD before it is
-  rewound.
-
-* "git rerere" can be told to update the index with auto-reused resolution
-  with rerere.autoupdate configuration variable.
-
-* git-rev-parse learned $commit^! and $commit^@ notations used in "log"
-  family.  These notations are available in gitk as well, because the gitk
-  command internally uses rev-parse to interpret its arguments.
-
-* git-rev-list learned --children option to show child commits it
-  encountered during the traversal, instead of showing parent commits.
-
-* git-send-mail can talk not just over SSL but over TLS now.
-
-* git-shortlog honors custom output format specified with "--pretty=format:".
-
-* "git-stash save" learned --keep-index option.  This lets you stash away the
-  local changes and bring the changes staged in the index to your working
-  tree for examination and testing.
-
-* git-stash also learned branch subcommand to create a new branch out of
-  stashed changes.
-
-* git-status gives the remote tracking statistics similar to the way
-  git-checkout reports by how many commits your branch is ahead/behind.
-
-* "git-svn dcommit" is now aware of auto-props setting the subversion user
-  has.
-
-* You can tell "git status -u" to even more aggressively omit checking
-  untracked files with --untracked-files=no.
-
-* Original SHA-1 value for "update-ref -d" is optional now.
-
-* Error codes from gitweb are made more descriptive where possible, rather
-  than "403 forbidden" as we used to issue everywhere.
-
-(internal)
-
-* git-merge has been reimplemented in C.
-
-
-Fixes since v1.5.6
-------------------
-
-All of the fixes in v1.5.6 maintenance series are included in
-this release, unless otherwise noted.
-
- * git-clone ignored its -u option; the fix needs to be backported to
-   'maint';
-
- * git-mv used to lose the distinction between changes that are staged
-   and that are only in the working tree, by staging both in the index
-   after moving such a path.
-
- * "git-rebase -i -p" rewrote the parents to wrong ones when amending
-   (either edit or squash) was involved, and did not work correctly
-   when fast forwarding.
-
diff --git a/Documentation/RelNotes-1.6.1.1.txt b/Documentation/RelNotes-1.6.1.1.txt
deleted file mode 100644 (file)
index 8c594ba..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-GIT v1.6.1.1 Release Notes
-==========================
-
-Fixes since v1.6.1
-------------------
-
-* "git add frotz/nitfol" when "frotz" is a submodule should have errored
-  out, but it didn't.
-
-* "git apply" took file modes from the patch text and updated the mode
-  bits of the target tree even when the patch was not about mode changes.
-
-* "git bisect view" on Cygwin did not launch gitk
-
-* "git checkout $tree" did not trigger an error.
-
-* "git commit" tried to remove COMMIT_EDITMSG from the work tree by mistake.
-
-* "git describe --all" complained when a commit is described with a tag,
-  which was nonsense.
-
-* "git diff --no-index --" did not trigger no-index (aka "use git-diff as
-  a replacement of diff on untracked files") behaviour.
-
-* "git format-patch -1 HEAD" on a root commit failed to produce patch
-  text.
-
-* "git fsck branch" did not work as advertised; instead it behaved the same
-  way as "git fsck".
-
-* "git log --pretty=format:%s" did not handle a multi-line subject the
-  same way as built-in log listers (i.e. shortlog, --pretty=oneline, etc.)
-
-* "git daemon", and "git merge-file" are more careful when freopen fails
-  and barf, instead of going on and writing to unopened filehandle.
-
-* "git http-push" did not like some RFC 4918 compliant DAV server
-  responses.
-
-* "git merge -s recursive" mistakenly overwritten an untracked file in the
-  work tree upon delete/modify conflict.
-
-* "git merge -s recursive" didn't leave the index unmerged for entries with
-  rename/delete conflicts.
-
-* "git merge -s recursive" clobbered untracked files in the work tree.
-
-* "git mv -k" with more than one erroneous paths misbehaved.
-
-* "git read-tree -m -u" hence branch switching incorrectly lost a
-  subdirectory in rare cases.
-
-* "git rebase -i" issued an unnecessary error message upon a user error of
-  marking the first commit to be "squash"ed.
-
-* "git shortlog" did not format a commit message with multi-line
-  subject correctly.
-
-Many documentation updates.
diff --git a/Documentation/RelNotes-1.6.1.2.txt b/Documentation/RelNotes-1.6.1.2.txt
deleted file mode 100644 (file)
index be37cbb..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-GIT v1.6.1.2 Release Notes
-==========================
-
-Fixes since v1.6.1.1
---------------------
-
-* The logic for rename detection in internal diff used by commands like
-  "git diff" and "git blame" has been optimized to avoid loading the same
-  blob repeatedly.
-
-* We did not allow writing out a blob that is larger than 2GB for no good
-  reason.
-
-* "git format-patch -o $dir", when $dir is a relative directory, used it
-  as relative to the root of the work tree, not relative to the current
-  directory.
-
-* v1.6.1 introduced an optimization for "git push" into a repository (A)
-  that borrows its objects from another repository (B) to avoid sending
-  objects that are available in repository B, when they are not yet used
-  by repository A.  However the code on the "git push" sender side was
-  buggy and did not work when repository B had new objects that are not
-  known by the sender.  This caused pushing into a "forked" repository
-  served by v1.6.1 software using "git push" from v1.6.1 sometimes did not
-  work.  The bug was purely on the "git push" sender side, and has been
-  corrected.
-
-* "git status -v" did not paint its diff output in colour even when
-  color.ui configuration was set.
-
-* "git ls-tree" learned --full-tree option to help Porcelain scripts that
-  want to always see the full path regardless of the current working
-  directory.
-
-* "git grep" incorrectly searched in work tree paths even when they are
-  marked as assume-unchanged.  It now searches in the index entries.
-
-* "git gc" with no grace period needlessly ejected packed but unreachable
-  objects in their loose form, only to delete them right away.
diff --git a/Documentation/RelNotes-1.6.1.3.txt b/Documentation/RelNotes-1.6.1.3.txt
deleted file mode 100644 (file)
index 6f0bde1..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-GIT v1.6.1.3 Release Notes
-==========================
-
-Fixes since v1.6.1.2
---------------------
-
-* "git diff --binary | git apply" pipeline did not work well when
-  a binary blob is changed to a symbolic link.
-
-* Some combinations of -b/-w/--ignore-space-at-eol to "git diff" did
-  not work as expected.
-
-* "git grep" did not pass the -I (ignore binary) option when
-  calling out an external grep program.
-
-* "git log" and friends include HEAD to the set of starting points
-  when --all is given.  This makes a difference when you are not
-  on any branch.
-
-* "git mv" to move an untracked file to overwrite a tracked
-  contents misbehaved.
-
-* "git merge -s octopus" with many potential merge bases did not
-  work correctly.
-
-* RPM binary package installed the html manpages in a wrong place.
-
-Also includes minor documentation fixes and updates.
-
-
---
-git shortlog --no-merges v1.6.1.2-33-gc789350..
diff --git a/Documentation/RelNotes-1.6.1.4.txt b/Documentation/RelNotes-1.6.1.4.txt
deleted file mode 100644 (file)
index 0ce6316..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-GIT v1.6.1.4 Release Notes
-==========================
-
-Fixes since v1.6.1.3
---------------------
-
-* .gitignore learned to handle backslash as a quoting mechanism for
-  comment introduction character "#".
-  This fix was first merged to 1.6.2.1.
-
-* "git fast-export" produced wrong output with some parents missing from
-  commits, when the history is clock-skewed.
-
-* "git fast-import" sometimes failed to read back objects it just wrote
-  out and aborted, because it failed to flush stale cached data.
-
-* "git-ls-tree" and "git-diff-tree" used a pathspec correctly when
-  deciding to descend into a subdirectory but they did not match the
-  individual paths correctly.  This caused pathspecs "abc/d ab" to match
-  "abc/0" ("abc/d" made them decide to descend into the directory "abc/",
-  and then "ab" incorrectly matched "abc/0" when it shouldn't).
-  This fix was first merged to 1.6.2.3.
-
-* import-zips script (in contrib) did not compute the common directory
-  prefix correctly.
-  This fix was first merged to 1.6.2.2.
-
-* "git init" segfaulted when given an overlong template location via
-  the --template= option.
-  This fix was first merged to 1.6.2.4.
-
-* "git repack" did not error out when necessary object was missing in the
-  repository.
-
-* git-repack (invoked from git-gc) did not work as nicely as it should in
-  a repository that borrows objects from neighbours via alternates
-  mechanism especially when some packs are marked with the ".keep" flag
-  to prevent them from being repacked.
-  This fix was first merged to 1.6.2.3.
-
-Also includes minor documentation fixes and updates.
-
---
-git shortlog --no-merges v1.6.1.3..
diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes-1.6.1.txt
deleted file mode 100644 (file)
index adb7cca..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-GIT v1.6.1 Release Notes
-========================
-
-Updates since v1.6.0
---------------------
-
-When some commands (e.g. "git log", "git diff") spawn pager internally, we
-used to make the pager the parent process of the git command that produces
-output.  This meant that the exit status of the whole thing comes from the
-pager, not the underlying git command.  We swapped the order of the
-processes around and you will see the exit code from the command from now
-on.
-
-(subsystems)
-
-* gitk can call out to git-gui to view "git blame" output; git-gui in turn
-  can run gitk from its blame view.
-
-* Various git-gui updates including updated translations.
-
-* Various gitweb updates from repo.or.cz installation.
-
-* Updates to emacs bindings.
-
-(portability)
-
-* A few test scripts used nonportable "grep" that did not work well on
-  some platforms, e.g. Solaris.
-
-* Sample pre-auto-gc script has OS X support.
-
-* Makefile has support for (ancient) FreeBSD 4.9.
-
-(performance)
-
-* Many operations that are lstat(3) heavy can be told to pre-execute
-  necessary lstat(3) in parallel before their main operations, which
-  potentially gives much improved performance for cold-cache cases or in
-  environments with weak metadata caching (e.g. NFS).
-
-* The underlying diff machinery to produce textual output has been
-  optimized, which would result in faster "git blame" processing.
-
-* Most of the test scripts (but not the ones that try to run servers)
-  can be run in parallel.
-
-* Bash completion of refnames in a repository with massive number of
-  refs has been optimized.
-
-* Cygwin port uses native stat/lstat implementations when applicable,
-  which leads to improved performance.
-
-* "git push" pays attention to alternate repositories to avoid sending
-  unnecessary objects.
-
-* "git svn" can rebuild an out-of-date rev_map file.
-
-(usability, bells and whistles)
-
-* When you mistype a command name, git helpfully suggests what it guesses
-  you might have meant to say.  help.autocorrect configuration can be set
-  to a non-zero value to accept the suggestion when git can uniquely
-  guess.
-
-* The packfile machinery hopefully is more robust when dealing with
-  corrupt packs if redundant objects involved in the corruption are
-  available elsewhere.
-
-* "git add -N path..." adds the named paths as an empty blob, so that
-  subsequent "git diff" will show a diff as if they are creation events.
-
-* "git add" gained a built-in synonym for people who want to say "stage
-  changes" instead of "add contents to the staging area" which amounts
-  to the same thing.
-
-* "git apply" learned --include=paths option, similar to the existing
-  --exclude=paths option.
-
-* "git bisect" is careful about a user mistake and suggests testing of
-  merge base first when good is not a strict ancestor of bad.
-
-* "git bisect skip" can take a range of commits.
-
-* "git blame" re-encodes the commit metainfo to UTF-8 from i18n.commitEncoding
-  by default.
-
-* "git check-attr --stdin" can check attributes for multiple paths.
-
-* "git checkout --track origin/hack" used to be a syntax error.  It now
-  DWIMs to create a corresponding local branch "hack", i.e. acts as if you
-  said "git checkout --track -b hack origin/hack".
-
-* "git checkout --ours/--theirs" can be used to check out one side of a
-  conflicting merge during conflict resolution.
-
-* "git checkout -m" can be used to recreate the initial conflicted state
-  during conflict resolution.
-
-* "git cherry-pick" can also utilize rerere for conflict resolution.
-
-* "git clone" learned to be verbose with -v
-
-* "git commit --author=$name" can look up author name from existing
-  commits.
-
-* output from "git commit" has been reworded in a more concise and yet
-  more informative way.
-
-* "git count-objects" reports the on-disk footprint for packfiles and
-  their corresponding idx files.
-
-* "git daemon" learned --max-connections=<count> option.
-
-* "git daemon" exports REMOTE_ADDR to record client address, so that
-  spawned programs can act differently on it.
-
-* "git describe --tags" favours closer lightweight tags than farther
-  annotated tags now.
-
-* "git diff" learned to mimic --suppress-blank-empty from GNU diff via a
-  configuration option.
-
-* "git diff" learned to put more sensible hunk headers for Python,
-  HTML and ObjC contents.
-
-* "git diff" learned to vary the a/ vs b/ prefix depending on what are
-  being compared, controlled by diff.mnemonicprefix configuration.
-
-* "git diff" learned --dirstat-by-file to count changed files, not number
-  of lines, when summarizing the global picture.
-
-* "git diff" learned "textconv" filters --- a binary or hard-to-read
-  contents can be munged into human readable form and the difference
-  between the results of the conversion can be viewed (obviously this
-  cannot produce a patch that can be applied, so this is disabled in
-  format-patch among other things).
-
-* "--cached" option to "git diff has an easier to remember synonym "--staged",
-  to ask "what is the difference between the given commit and the
-  contents staged in the index?"
-
-* "git for-each-ref" learned "refname:short" token that gives an
-  unambiguously abbreviated refname.
-
-* Auto-numbering of the subject lines is the default for "git
-  format-patch" now.
-
-* "git grep" learned to accept -z similar to GNU grep.
-
-* "git help" learned to use GIT_MAN_VIEWER environment variable before
-  using "man" program.
-
-* "git imap-send" can optionally talk SSL.
-
-* "git index-pack" is more careful against disk corruption while
-  completing a thin pack.
-
-* "git log --check" and "git log --exit-code" passes their underlying diff
-  status with their exit status code.
-
-* "git log" learned --simplify-merges, a milder variant of --full-history;
-  "gitk --simplify-merges" is easier to view than with --full-history.
-
-* "git log" learned "--source" to show what ref each commit was reached
-  from.
-
-* "git log" also learned "--simplify-by-decoration" to show the
-  birds-eye-view of the topology of the history.
-
-* "git log --pretty=format:" learned "%d" format element that inserts
-  names of tags that point at the commit.
-
-* "git merge --squash" and "git merge --no-ff" into an unborn branch are
-  noticed as user errors.
-
-* "git merge -s $strategy" can use a custom built strategy if you have a
-  command "git-merge-$strategy" on your $PATH.
-
-* "git pull" (and "git fetch") can be told to operate "-v"erbosely or
-  "-q"uietly.
-
-* "git push" can be told to reject deletion of refs with receive.denyDeletes
-  configuration.
-
-* "git rebase" honours pre-rebase hook; use --no-verify to bypass it.
-
-* "git rebase -p" uses interactive rebase machinery now to preserve the merges.
-
-* "git reflog expire branch" can be used in place of "git reflog expire
-  refs/heads/branch".
-
-* "git remote show $remote" lists remote branches one-per-line now.
-
-* "git send-email" can be given revision range instead of files and
-  maildirs on the command line, and automatically runs format-patch to
-  generate patches for the given revision range.
-
-* "git submodule foreach" subcommand allows you to iterate over checked
-  out submodules.
-
-* "git submodule sync" subcommands allows you to update the origin URL
-  recorded in submodule directories from the toplevel .gitmodules file.
-
-* "git svn branch" can create new branches on the other end.
-
-* "gitweb" can use more saner PATH_INFO based URL.
-
-(internal)
-
-* "git hash-object" learned to lie about the path being hashed, so that
-  correct gitattributes processing can be done while hashing contents
-  stored in a temporary file.
-
-* various callers of git-merge-recursive avoid forking it as an external
-  process.
-
-* Git class defined in "Git.pm" can be subclasses a bit more easily.
-
-* We used to link GNU regex library as a compatibility layer for some
-  platforms, but it turns out it is not necessary on most of them.
-
-* Some path handling routines used fixed number of buffers used alternately
-  but depending on the call depth, this arrangement led to hard to track
-  bugs.  This issue is being addressed.
-
-
-Fixes since v1.6.0
-------------------
-
-All of the fixes in v1.6.0.X maintenance series are included in this
-release, unless otherwise noted.
-
-* Porcelains implemented as shell scripts were utterly confused when you
-  entered to a subdirectory of a work tree from sideways, following a
-  symbolic link (this may need to be backported to older releases later).
-
-* Tracking symbolic links would work better on filesystems whose lstat()
-  returns incorrect st_size value for them.
-
-* "git add" and "git update-index" incorrectly allowed adding S/F when S
-  is a tracked symlink that points at a directory D that has a path F in
-  it (we still need to fix a similar nonsense when S is a submodule and F
-  is a path in it).
-
-* "git am" after stopping at a broken patch lost --whitespace, -C, -p and
-  --3way options given from the command line initially.
-
-* "git diff --stdin" used to take two trees on a line and compared them,
-  but we dropped support for such a use case long time ago.  This has
-  been resurrected.
-
-* "git filter-branch" failed to rewrite a tag name with slashes in it.
-
-* "git http-push" did not understand URI scheme other than opaquelocktoken
-  when acquiring a lock from the server (this may need to be backported to
-  older releases later).
-
-* After "git rebase -p" stopped with conflicts while replaying a merge,
- "git rebase --continue" did not work (may need to be backported to older
-  releases).
-
-* "git revert" records relative to which parent a revert was made when
-  reverting a merge.  Together with new documentation that explains issues
-  around reverting a merge and merging from the updated branch later, this
-  hopefully will reduce user confusion (this may need to be backported to
-  older releases later).
-
-* "git rm --cached" used to allow an empty blob that was added earlier to
-  be removed without --force, even when the file in the work tree has
-  since been modified.
-
-* "git push --tags --all $there" failed with generic usage message without
-  telling saying these two options are incompatible.
-
-* "git log --author/--committer" match used to potentially match the
-  timestamp part, exposing internal implementation detail.  Also these did
-  not work with --fixed-strings match at all.
-
-* "gitweb" did not mark non-ASCII characters imported from external HTML fragments
-  correctly.
-
---
-exec >/var/tmp/1
-O=v1.6.1-rc3-74-gf66bc5f
-echo O=$(git describe master)
-git shortlog --no-merges $O..master ^maint
diff --git a/Documentation/RelNotes-1.6.2.1.txt b/Documentation/RelNotes-1.6.2.1.txt
deleted file mode 100644 (file)
index dfa3641..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-GIT v1.6.2.1 Release Notes
-==========================
-
-Fixes since v1.6.2
-------------------
-
-* .gitignore learned to handle backslash as a quoting mechanism for
-  comment introduction character "#".
-
-* timestamp output in --date=relative mode used to display timestamps that
-  are long time ago in the default mode; it now uses "N years M months
-  ago", and "N years ago".
-
-* git-add -i/-p now works with non-ASCII pathnames.
-
-* "git hash-object -w" did not read from the configuration file from the
-  correct .git directory.
-
-* git-send-email learned to correctly handle multiple Cc: addresses.
diff --git a/Documentation/RelNotes-1.6.2.2.txt b/Documentation/RelNotes-1.6.2.2.txt
deleted file mode 100644 (file)
index fafa998..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-GIT v1.6.2.2 Release Notes
-==========================
-
-Fixes since v1.6.2.1
---------------------
-
-* A longstanding confusing description of what --pickaxe option of
-  git-diff does has been clarified in the documentation.
-
-* "git-blame -S" did not quite work near the commits that were given
-  on the command line correctly.
-
-* "git diff --pickaxe-regexp" did not count overlapping matches
-  correctly.
-
-* "git diff" did not feed files in work-tree representation to external
-  diff and textconv.
-
-* "git-fetch" in a repository that was not cloned from anywhere said
-  it cannot find 'origin', which was hard to understand for new people.
-
-* "git-format-patch --numbered-files --stdout" did not have to die of
-  incompatible options; it now simply ignores --numbered-files as no files
-  are produced anyway.
-
-* "git-ls-files --deleted" did not work well with GIT_DIR&GIT_WORK_TREE.
-
-* "git-read-tree A B C..." without -m option has been broken for a long
-  time.
-
-* git-send-email ignored --in-reply-to when --no-thread was given.
-
-* 'git-submodule add' did not tolerate extra slashes and ./ in the path it
-  accepted from the command line; it now is more lenient.
-
-* git-svn misbehaved when the project contained a path that began with
-  two dashes.
-
-* import-zips script (in contrib) did not compute the common directory
-  prefix correctly.
-
-* miscompilation of negated enum constants by old gcc (2.9) affected the
-  codepaths to spawn subprocesses.
-
-Many small documentation updates are included as well.
diff --git a/Documentation/RelNotes-1.6.2.3.txt b/Documentation/RelNotes-1.6.2.3.txt
deleted file mode 100644 (file)
index 4d3c1ac..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-GIT v1.6.2.3 Release Notes
-==========================
-
-Fixes since v1.6.2.2
---------------------
-
-* Setting an octal mode value to core.sharedrepository configuration to
-  restrict access to the repository to group members did not work as
-  advertised.
-
-* A fairly large and trivial memory leak while rev-list shows list of
-  reachable objects has been identified and plugged.
-
-* "git-commit --interactive" did not abort when underlying "git-add -i"
-  signaled a failure.
-
-* git-repack (invoked from git-gc) did not work as nicely as it should in
-  a repository that borrows objects from neighbours via alternates
-  mechanism especially when some packs are marked with the ".keep" flag
-  to prevent them from being repacked.
-
-Many small documentation updates are included as well.
diff --git a/Documentation/RelNotes-1.6.2.4.txt b/Documentation/RelNotes-1.6.2.4.txt
deleted file mode 100644 (file)
index f4bf1d0..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-GIT v1.6.2.4 Release Notes
-==========================
-
-Fixes since v1.6.2.3
---------------------
-
-* The configuration parser had a buffer overflow while parsing an overlong
-  value.
-
-* pruning reflog entries that are unreachable from the tip of the ref
-  during "git reflog prune" (hence "git gc") was very inefficient.
-
-* "git-add -p" lacked a way to say "q"uit to refuse staging any hunks for
-  the remaining paths.  You had to say "d" and then ^C.
-
-* "git-checkout <tree-ish> <submodule>" did not update the index entry at
-  the named path; it now does.
-
-* "git-fast-export" choked when seeing a tag that does not point at commit.
-
-* "git init" segfaulted when given an overlong template location via
-  the --template= option.
-
-* "git-ls-tree" and "git-diff-tree" used a pathspec correctly when
-  deciding to descend into a subdirectory but they did not match the
-  individual paths correctly.  This caused pathspecs "abc/d ab" to match
-  "abc/0" ("abc/d" made them decide to descend into the directory "abc/",
-  and then "ab" incorrectly matched "abc/0" when it shouldn't).
-
-* "git-merge-recursive" was broken when a submodule entry was involved in
-  a criss-cross merge situation.
-
-Many small documentation updates are included as well.
-
----
-exec >/var/tmp/1
-echo O=$(git describe maint)
-O=v1.6.2.3-38-g318b847
-git shortlog --no-merges $O..maint
diff --git a/Documentation/RelNotes-1.6.2.5.txt b/Documentation/RelNotes-1.6.2.5.txt
deleted file mode 100644 (file)
index b23f9e9..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-GIT v1.6.2.5 Release Notes
-==========================
-
-Fixes since v1.6.2.4
---------------------
-
-* "git apply" mishandled if you fed a git generated patch that renames
-  file A to B and file B to A at the same time.
-
-* "git diff -c -p" (and "diff --cc") did not expect to see submodule
-  differences and instead refused to work.
-
-* "git grep -e '('" segfaulted, instead of diagnosing a mismatched
-  parentheses error.
-
-* "git fetch" generated packs with offset-delta encoding when both ends of
-  the connection are capable of producing one; this cannot be read by
-  ancient git and the user should be able to disable this by setting
-  repack.usedeltabaseoffset configuration to false.
-
-
diff --git a/Documentation/RelNotes-1.6.2.txt b/Documentation/RelNotes-1.6.2.txt
deleted file mode 100644 (file)
index ad060f4..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-GIT v1.6.2 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.1
---------------------
-
-(subsystems)
-
-* git-svn updates.
-
-* gitweb updates, including a new patch view and RSS/Atom feed
-  improvements.
-
-* (contrib/emacs) git.el now has commands for checking out a branch,
-  creating a branch, cherry-picking and reverting commits; vc-git.el
-  is not shipped with git anymore (it is part of official Emacs).
-
-(performance)
-
-* pack-objects autodetects the number of CPUs available and uses threaded
-  version.
-
-(usability, bells and whistles)
-
-* automatic typo correction works on aliases as well
-
-* @{-1} is a way to refer to the last branch you were on.  This is
-  accepted not only where an object name is expected, but anywhere
-  a branch name is expected and acts as if you typed the branch name.
-  E.g. "git branch --track mybranch @{-1}", "git merge @{-1}", and
-  "git rev-parse --symbolic-full-name @{-1}" would work as expected.
-
-* When refs/remotes/origin/HEAD points at a remote tracking branch that
-  has been pruned away, many git operations issued warning when they
-  internally enumerated the refs.  We now warn only when you say "origin"
-  to refer to that pruned branch.
-
-* The location of .mailmap file can be configured, and its file format was
-  enhanced to allow mapping an incorrect e-mail field as well.
-
-* "git add -p" learned 'g'oto action to jump directly to a hunk.
-
-* "git add -p" learned to find a hunk with given text with '/'.
-
-* "git add -p" optionally can be told to work with just the command letter
-  without Enter.
-
-* when "git am" stops upon a patch that does not apply, it shows the
-  title of the offending patch.
-
-* "git am --directory=<dir>" and "git am --reject" passes these options
-  to underlying "git apply".
-
-* "git am" learned --ignore-date option.
-
-* "git blame" aligns author names better when they are spelled in
-  non US-ASCII encoding.
-
-* "git clone" now makes its best effort when cloning from an empty
-  repository to set up configuration variables to refer to the remote
-  repository.
-
-* "git checkout -" is a shorthand for "git checkout @{-1}".
-
-* "git cherry" defaults to whatever the current branch is tracking (if
-  exists) when the <upstream> argument is not given.
-
-* "git cvsserver" can be told not to add extra "via git-CVS emulator" to
-  the commit log message it serves via gitcvs.commitmsgannotation
-  configuration.
-
-* "git cvsserver" learned to handle 'noop' command some CVS clients seem
-  to expect to work.
-
-* "git diff" learned a new option --inter-hunk-context to coalesce close
-  hunks together and show context between them.
-
-* The definition of what constitutes a word for "git diff --color-words"
-  can be customized via gitattributes, command line or a configuration.
-
-* "git diff" learned --patience to run "patience diff" algorithm.
-
-* "git filter-branch" learned --prune-empty option that discards commits
-  that do not change the contents.
-
-* "git fsck" now checks loose objects in alternate object stores, instead
-  of misreporting them as missing.
-
-* "git gc --prune" was resurrected to allow "git gc --no-prune" and
-  giving non-default expiration period e.g. "git gc --prune=now".
-
-* "git grep -w" and "git grep" for fixed strings have been optimized.
-
-* "git mergetool" learned -y(--no-prompt) option to disable prompting.
-
-* "git rebase -i" can transplant a history down to root to elsewhere
-  with --root option.
-
-* "git reset --merge" is a new mode that works similar to the way
-  "git checkout" switches branches, taking the local changes while
-  switching to another commit.
-
-* "git submodule update" learned --no-fetch option.
-
-* "git tag" learned --contains that works the same way as the same option
-  from "git branch".
-
-
-Fixes since v1.6.1
-------------------
-
-All of the fixes in v1.6.1.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.1.X series.
-
-* "git-add sub/file" when sub is a submodule incorrectly added the path to
-  the superproject.
-
-* "git bundle" did not exclude annotated tags even when a range given
-  from the command line wanted to.
-
-* "git filter-branch" unnecessarily refused to work when you had
-  checked out a different commit from what is recorded in the superproject
-  index in a submodule.
-
-* "git filter-branch" incorrectly tried to update a nonexistent work tree
-  at the end when it is run in a bare repository.
-
-* "git gc" did not work if your repository was created with an ancient git
-  and never had any pack files in it before.
-
-* "git mergetool" used to ignore autocrlf and other attributes
-  based content rewriting.
-
-* branch switching and merges had a silly bug that did not validate
-  the correct directory when making sure an existing subdirectory is
-  clean.
-
-* "git -p cmd" when cmd is not a built-in one left the display in funny state
-  when killed in the middle.
diff --git a/Documentation/RelNotes-1.6.3.1.txt b/Documentation/RelNotes-1.6.3.1.txt
deleted file mode 100644 (file)
index 2400b72..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-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
deleted file mode 100644 (file)
index b2f3f02..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-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
deleted file mode 100644 (file)
index 1c28398..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-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
deleted file mode 100644 (file)
index cad461b..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-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.3.txt b/Documentation/RelNotes-1.6.3.txt
deleted file mode 100644 (file)
index 418c685..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-GIT v1.6.3 Release Notes
-========================
-
-With the next major release, "git push" into a branch that is
-currently checked out will be refused by default.  You can choose
-what should happen upon such a push by setting the configuration
-variable receive.denyCurrentBranch in the receiving repository.
-
-To ease the transition plan, the receiving repository of such a
-push running this release will issue a big warning when the
-configuration variable is missing.  Please refer to:
-
-  http://git.or.cz/gitwiki/GitFaq#non-bare
-  http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
-
-for more details on the reason why this change is needed and the
-transition plan.
-
-For a similar reason, "git push $there :$killed" to delete the branch
-$killed in a remote repository $there, if $killed branch is the current
-branch pointed at by its HEAD, gets a large warning.  You can choose what
-should happen upon such a push by setting the configuration variable
-receive.denyDeleteCurrent in the receiving repository.
-
-When the user does not tell "git push" what to push, it has always
-pushed matching refs.  For some people it is unexpected, and a new
-configuration variable push.default has been introduced to allow
-changing a different default behaviour.  To advertise the new feature,
-a big warning is issued if this is not configured and a git push without
-arguments is attempted.
-
-
-Updates since v1.6.2
---------------------
-
-(subsystems)
-
-* various git-svn updates.
-
-* git-gui updates, including an update to Russian translation, and a
-  fix to an infinite loop when showing an empty diff.
-
-* gitk updates, including an update to Russian translation and improved Windows
-  support.
-
-(performance)
-
-* many uses of lstat(2) in the codepath for "git checkout" have been
-  optimized out.
-
-(usability, bells and whistles)
-
-* Boolean configuration variable yes/no can be written as on/off.
-
-* rsync:/path/to/repo can be used to run git over rsync for local
-  repositories.  It may not be useful in practice; meant primarily for
-  testing.
-
-* http transport learned to prompt and use password when fetching from or
-  pushing to http://user@host.xz/ URL.
-
-* (msysgit) progress output that is sent over the sideband protocol can
-  be handled appropriately in Windows console.
-
-* "--pretty=<style>" option to the log family of commands can now be
-  spelled as "--format=<style>".  In addition, --format=%formatstring
-  is a short-hand for --pretty=tformat:%formatstring.
-
-* "--oneline" is a synonym for "--pretty=oneline --abbrev-commit".
-
-* "--graph" to the "git log" family can draw the commit ancestry graph
-  in colors.
-
-* If you realize that you botched the patch when you are editing hunks
-  with the 'edit' action in git-add -i/-p, you can abort the editor to
-  tell git not to apply it.
-
-* @{-1} is a new way to refer to the last branch you were on introduced in
-  1.6.2, but the initial implementation did not teach this to a few
-  commands.  Now the syntax works with "branch -m @{-1} newname".
-
-* git-archive learned --output=<file> option.
-
-* git-archive takes attributes from the tree being archived; strictly
-  speaking, this is an incompatible behaviour change, but is a good one.
-  Use --worktree-attributes option to allow it to read attributes from
-  the work tree as before (deprecated git-tar tree command always reads
-  attributes from the work tree).
-
-* git-bisect shows not just the number of remaining commits whose goodness
-  is unknown, but also shows the estimated number of remaining rounds.
-
-* You can give --date=<format> option to git-blame.
-
-* "git-branch -r" shows HEAD symref that points at a remote branch in
-  interest of each tracked remote repository.
-
-* "git-branch -v -v" is a new way to get list of names for branches and the
-  "upstream" branch for them.
-
-* git-config learned -e option to open an editor to edit the config file
-  directly.
-
-* git-clone runs post-checkout hook when run without --no-checkout.
-
-* git-difftool is now part of the officially supported command, primarily
-  maintained by David Aguilar.
-
-* git-for-each-ref learned a new "upstream" token.
-
-* git-format-patch can be told to use attachment with a new configuration,
-  format.attach.
-
-* git-format-patch can be told to produce deep or shallow message threads.
-
-* git-format-patch can be told to always add sign-off with a configuration
-  variable.
-
-* git-format-patch learned format.headers configuration to add extra
-  header fields to the output.  This behaviour is similar to the existing
-  --add-header=<header> option of the command.
-
-* git-format-patch gives human readable names to the attached files, when
-  told to send patches as attachments.
-
-* git-grep learned to highlight the found substrings in color.
-
-* git-imap-send learned to work around Thunderbird's inability to easily
-  disable format=flowed with a new configuration, imap.preformattedHTML.
-
-* git-rebase can be told to rebase the series even if your branch is a
-  descendant of the commit you are rebasing onto with --force-rebase
-  option.
-
-* git-rebase can be told to report diffstat with the --stat option.
-
-* Output from git-remote command has been vastly improved.
-
-* "git remote update --prune $remote" updates from the named remote and
-  then prunes stale tracking branches.
-
-* git-send-email learned --confirm option to review the Cc: list before
-  sending the messages out.
-
-(developers)
-
-* Test scripts can be run under valgrind.
-
-* Test scripts can be run with installed git.
-
-* Makefile learned 'coverage' option to run the test suites with
-  coverage tracking enabled.
-
-* Building the manpages with docbook-xsl between 1.69.1 and 1.71.1 now
-  requires setting DOCBOOK_SUPPRESS_SP to work around a docbook-xsl bug.
-  This workaround used to be enabled by default, but causes problems
-  with newer versions of docbook-xsl.  In addition, there are a few more
-  knobs you can tweak to work around issues with various versions of the
-  docbook-xsl package.  See comments in Documentation/Makefile for details.
-
-* Support for building and testing a subset of git on a system without a
-  working perl has been improved.
-
-
-Fixes since v1.6.2
-------------------
-
-All of the fixes in v1.6.2.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.2.X series.
-
-* "git-apply" rejected a patch that swaps two files (i.e. renames A to B
-  and B to A at the same time).  May need to be backported by cherry
-  picking d8c81df and then 7fac0ee).
-
-* The initial checkout did not read the attributes from the .gitattribute
-  file that is being checked out.
-
-* git-gc spent excessive amount of time to decide if an object appears
-  in a locally existing pack (if needed, backport by merging 69e020a).
diff --git a/Documentation/RelNotes-1.6.4.1.txt b/Documentation/RelNotes-1.6.4.1.txt
deleted file mode 100644 (file)
index e439e45..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-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
deleted file mode 100644 (file)
index c11ec01..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-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
deleted file mode 100644 (file)
index 4f29bab..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-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
deleted file mode 100644 (file)
index 0ead45f..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-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
deleted file mode 100644 (file)
index 7a90441..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-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
deleted file mode 100644 (file)
index 309ba18..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-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.2.txt b/Documentation/RelNotes-1.6.5.2.txt
deleted file mode 100644 (file)
index aa7ccce..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-GIT v1.6.5.2 Release Notes
-==========================
-
-Fixes since v1.6.5.1
---------------------
-
- * Installation of templates triggered a bug in busybox when using tar
-   implementation from it.
-
- * "git add -i" incorrectly ignored paths that are already in the index
-   if they matched .gitignore patterns.
-
- * "git describe --always" should have produced some output even there
-   were no tags in the repository, but it didn't.
-
- * "git ls-files" when showing tracked files incorrectly paid attention
-   to the exclude patterns.
-
-Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.5.3.txt b/Documentation/RelNotes-1.6.5.3.txt
deleted file mode 100644 (file)
index b2fad1b..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-Git v1.6.5.3 Release Notes
-==========================
-
-Fixes since v1.6.5.2
---------------------
-
- * info/grafts file didn't ignore trailing CR at the end of lines.
-
- * Packages generated on newer FC were unreadable by older versions of
-   RPM as the new default is to use stronger hash.
-
- * output from "git blame" was unreadable when the file ended in an
-   incomplete line.
-
- * "git add -i/-p" didn't handle deletion of empty files correctly.
-
- * "git clone" takes up to two parameters, but did not complain when
-   given more arguments than necessary and silently ignored them.
-
- * "git cvsimport" did not read files given as command line arguments
-   correctly when it is run from a subdirectory.
-
- * "git diff --color-words -U0" didn't work correctly.
-
- * The handling of blank lines at the end of file by "git diff/apply
-   --whitespace" was inconsistent with the other kinds of errors.
-   They are now colored, warned against, and fixed the same way as others.
-
- * There was no way to allow blank lines at the end of file without
-   allowing extra blanks at the end of lines.  You can use blank-at-eof
-   and blank-at-eol whitespace error class to specify them separately.
-   The old trailing-space error class is now a short-hand to set both.
-
- * "-p" option to "git format-patch" was supposed to suppress diffstat
-   generation, but it was broken since 1.6.1.
-
- * "git imap-send" did not compile cleanly with newer OpenSSL.
-
- * "git help -a" outside of a git repository was broken.
-
- * "git ls-files -i" was supposed to be inverse of "git ls-files" without -i
-   with respect to exclude patterns, but it was broken since 1.6.5.2.
-
- * "git ls-remote" outside of a git repository over http was broken.
-
- * "git rebase -i" gave bogus error message when the command word was
-   misspelled.
-
- * "git receive-pack" that is run in response to "git push" did not run
-   garbage collection nor update-server-info, but in larger hosting sites,
-   these almost always need to be run.  To help site administrators, the
-   command now runs "gc --auto" and "u-s-i" by setting receive.autogc
-   and receive.updateserverinfo configuration variables, respectively.
-
- * Release notes spelled the package name with incorrect capitalization.
-
- * "gitweb" did not escape non-ascii characters correctly in the URL.
-
- * "gitweb" showed "patch" link even for merge commits.
-
- * "gitweb" showed incorrect links for blob line numbers in pathinfo mode.
-
-Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.5.4.txt b/Documentation/RelNotes-1.6.5.4.txt
deleted file mode 100644 (file)
index e42f8b2..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-Git v1.6.5.4 Release Notes
-==========================
-
-Fixes since v1.6.5.3
---------------------
-
- * "git help" (without argument) used to check if you are in a directory
-   under git control. There was no breakage in behaviour per-se, but this
-   was unnecessary.
-
- * "git prune-packed" gave progress output even when its standard error is
-   not connected to a terminal; this caused cron jobs that run it to
-   produce crufts.
-
- * "git pack-objects --all-progress" is an option to ask progress output
-   from write-object phase _if_ progress output were to be produced, and
-   shouldn't have forced the progress output.
-
- * "git apply -p<n> --directory=<elsewhere>" did not work well for a
-   non-default value of n.
-
- * "git merge foo HEAD" was misparsed as an old-style invocation of the
-   command and produced a confusing error message.  As it does not specify
-   any other branch to merge, it shouldn't be mistaken as such.  We will
-   remove the old style "git merge <message> HEAD <commit>..."  syntax in
-   future versions, but not in this release,
-
- * "git merge -m <message> <branch>..." added the standard merge message
-   on its own after user-supplied message, which should have overrided the
-   standard one.
-
-Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.5.5.txt b/Documentation/RelNotes-1.6.5.5.txt
deleted file mode 100644 (file)
index ecfc57d..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-Git v1.6.5.5 Release Notes
-==========================
-
-Fixes since v1.6.5.4
---------------------
-
- * Manual pages can be formatted with older xmlto again.
-
- * GREP_OPTIONS exported from user's environment could have broken
-   our scripted commands.
-
- * In configuration files, a few variables that name paths can begin with
-   ~/ and ~username/ and they are expanded as expected.  This is not a
-   bugfix but 1.6.6 will have this and without backporting users cannot
-   easily use the same ~/.gitconfig across versions.
-
- * "git diff -B -M" did the same computation to hash lines of contents
-   twice, and held onto memory after it has used the data in it
-   unnecessarily before it freed.
-
- * "git diff -B" and "git diff --dirstat" was not counting newly added
-   contents correctly.
-
- * "git format-patch revisions... -- path" issued an incorrect error
-   message that suggested to use "--" on the command line when path
-   does not exist in the current work tree (it is a separate matter if
-   it makes sense to limit format-patch with pathspecs like that
-   without using the --full-diff option).
-
- * "git grep -F -i StRiNg" did not work as expected.
-
- * Enumeration of available merge strategies iterated over the list of
-   commands in a wrong way, sometimes producing an incorrect result.
-
- * "git shortlog" did not honor the "encoding" header embedded in the
-   commit object like "git log" did.
-
- * Reading progress messages that come from the remote side while running
-   "git pull" is given precedence over reading the actual pack data to
-   prevent garbled progress message on the user's terminal.
-
- * "git rebase" got confused when the log message began with certain
-   strings that looked like Subject:, Date: or From: header.
-
- * "git reset" accidentally run in .git/ directory checked out the
-   work tree contents in there.
-
-
-Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.5.6.txt b/Documentation/RelNotes-1.6.5.6.txt
deleted file mode 100644 (file)
index a9eaf76..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-Git v1.6.5.6 Release Notes
-==========================
-
-Fixes since v1.6.5.5
---------------------
-
- * "git add -p" had a regression since v1.6.5.3 that broke deletion of
-   non-empty files.
-
- * "git archive -o o.zip -- Makefile" produced an archive in o.zip
-   but in POSIX tar format.
-
- * Error message given to "git pull --rebase" when the user didn't give
-   enough clue as to what branch to integrate with still talked about
-   "merging with" the branch.
-
- * Error messages given by "git merge" when the merge resulted in a
-   fast-forward still were in plumbing lingo, even though in v1.6.5
-   we reworded messages in other cases.
-
- * The post-upload-hook run by upload-pack in response to "git fetch" has
-   been removed, due to security concerns (the hook first appeared in
-   1.6.5).
diff --git a/Documentation/RelNotes-1.6.5.7.txt b/Documentation/RelNotes-1.6.5.7.txt
deleted file mode 100644 (file)
index 5b49ea5..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-Git v1.6.5.7 Release Notes
-==========================
-
-Fixes since v1.6.5.6
---------------------
-
-* If a user specifies a color for a <slot> (i.e. a class of things to show
-  in a particular color) that is known only by newer versions of git
-  (e.g. "color.diff.func" was recently added for upcoming 1.6.6 release),
-  an older version of git should just ignore them.  Instead we diagnosed
-  it as an error.
-
-* With help.autocorrect set to non-zero value, the logic to guess typoes
-  in the subcommand name misfired and ran a random nonsense command.
-
-* If a command is run with an absolute path as a pathspec inside a bare
-  repository, e.g. "rev-list HEAD -- /home", the code tried to run
-  strlen() on NULL, which is the result of get_git_work_tree(), and
-  segfaulted.
diff --git a/Documentation/RelNotes-1.6.5.8.txt b/Documentation/RelNotes-1.6.5.8.txt
deleted file mode 100644 (file)
index 8b24beb..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-Git v1.6.5.8 Release Notes
-==========================
-
-Fixes since v1.6.5.7
---------------------
-
-* "git count-objects" did not handle packfiles that are bigger than 4G on
-  platforms with 32-bit off_t.
-
-* "git rebase -i" did not abort cleanly if it failed to launch the editor.
-
-* "git blame" did not work well when commit lacked the author name.
-
-* "git fast-import" choked when handling a tag that points at an object
-  that is not a commit.
-
-* "git reset --hard" did not work correctly when GIT_WORK_TREE environment
-  variable is used to point at the root of the true work tree.
-
-* "git grep" fed a buffer that is not NUL-terminated to underlying
-  regexec().
-
-* "git checkout -m other" while on a branch that does not have any commit
-  segfaulted, instead of failing.
-
-* "git branch -a other" should have diagnosed the command as an error.
-
-Other minor documentation updates are also included.
diff --git a/Documentation/RelNotes-1.6.5.txt b/Documentation/RelNotes-1.6.5.txt
deleted file mode 100644 (file)
index ee141c1..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-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.1.txt b/Documentation/RelNotes-1.6.6.1.txt
deleted file mode 100644 (file)
index f1d0a4a..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-Git v1.6.6.1 Release Notes
-==========================
-
-Fixes since v1.6.6
-------------------
-
- * "git blame" did not work well when commit lacked the author name.
-
- * "git branch -a name" wasn't diagnosed as an error.
-
- * "git count-objects" did not handle packfiles that are bigger than 4G on
-   platforms with 32-bit off_t.
-
- * "git checkout -m other" while on a branch that does not have any commit
-   segfaulted, instead of failing.
-
- * "git fast-import" choked when fed a tag that do not point at a
-   commit.
-
- * "git grep" finding from work tree files could have fed garbage to
-   the underlying regexec(3).
-
- * "git grep -L" didn't show empty files (they should never match, and
-   they should always appear in -L output as unmatching).
-
- * "git rebase -i" did not abort cleanly if it failed to launch the editor.
-
- * "git reset --hard" did not work correctly when GIT_WORK_TREE environment
-   variable is used to point at the root of the true work tree.
-
- * http-backend was not listed in the command list in the documentation.
-
- * Building on FreeBSD (both 7 and 8) needs OLD_ICONV set in the Makefile
-
- * "git checkout -m some-branch" while on an unborn branch crashed.
-
-Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.6.2.txt b/Documentation/RelNotes-1.6.6.2.txt
deleted file mode 100644 (file)
index 4eaddc0..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-Git v1.6.6.2 Release Notes
-==========================
-
-Fixes since v1.6.6.1
---------------------
-
- * recursive merge didn't correctly diagnose its own programming errors,
-   and instead caused the caller to segfault.
-
- * The new "smart http" aware clients probed the web servers to see if
-   they support smart http, but did not fall back to dumb http transport
-   correctly with some servers.
-
- * Time based reflog syntax e.g. "@{yesterday}" didn't diagnose a misspelled
-   time specification and instead assumed "@{now}".
-
- * "git archive HEAD -- no-such-directory" produced an empty archive
-   without complaining.
-
- * "git blame -L start,end -- file" misbehaved when given a start that is
-   larger than the number of lines in the file.
-
- * "git checkout -m" didn't correctly call custom merge backend supplied
-   by the end user.
-
- * "git config -f <file>" misbehaved when run from a subdirectory.
-
- * "git cvsserver" didn't like having regex metacharacters (e.g. '+') in
-   CVSROOT environment.
-
- * "git fast-import" did not correctly handle large blobs that may
-   bust the pack size limit.
-
- * "git gui" is supposed to work even when launched from inside a .git
-   directory.
-
- * "git gui" misbehaved when applying a hunk that ends with deletion.
-
- * "git imap-send" did not honor imap.preformattedHTML as documented.
-
- * "git log" family incorrectly showed the commit notes unconditionally by
-   mistake, which was especially irritating when running "git log --oneline".
-
- * "git status" shouldn't require an write access to the repository.
-
-Other minor documentation updates are included.
diff --git a/Documentation/RelNotes-1.6.6.txt b/Documentation/RelNotes-1.6.6.txt
deleted file mode 100644 (file)
index 04e205c..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-Git v1.6.6 Release Notes
-========================
-
-Notes on behaviour change
--------------------------
-
- * In this release, "git fsck" defaults to "git fsck --full" and
-   checks packfiles, and because of this it will take much longer to
-   complete than before.  If you prefer a quicker check only on loose
-   objects (the old default), you can say "git fsck --no-full".  This
-   has been supported by 1.5.4 and newer versions of git, so it is
-   safe to write it in your script even if you use slightly older git
-   on some of your machines.
-
-Preparing yourselves for compatibility issues in 1.7.0
-------------------------------------------------------
-
-In git 1.7.0, which is planned to be the release after 1.6.6, there will
-be a handful of behaviour changes that will break backward compatibility.
-
-These changes were discussed long time ago and existing behaviours have
-been identified as more problematic to the userbase than keeping them for
-the sake of backward compatibility.
-
-When necessary, a transition strategy for existing users has been designed
-not to force them running around setting configuration variables and
-updating their scripts in order to either keep the traditional behaviour
-or adjust to the new behaviour, on the day their sysadmin decides to install
-the new version of git.  When we switched from "git-foo" to "git foo" in
-1.6.0, even though the change had been advertised and the transition
-guide had been provided for a very long time, the users procrastinated
-during the entire transtion period, and ended up panicking on the day
-their sysadmins updated their git installation.  We are trying to avoid
-repeating that unpleasantness in the 1.7.0 release.
-
-For changes decided to be in 1.7.0, commands that will be affected
-have been much louder to strongly discourage such procrastination, and
-they continue to be in this release.  If you have been using recent
-versions of git, you would have seen warnings issued when you used
-features whose behaviour will change, with a clear instruction on how
-to keep the existing behaviour if you want to.  You hopefully are
-already well prepared.
-
-Of course, we have also been giving "this and that will change in
-1.7.0; prepare yourselves" warnings in the release notes and
-announcement messages for the past few releases.  Let's see how well
-users will fare this time.
-
- * "git push" into a branch that is currently checked out (i.e. pointed by
-   HEAD in a repository that is not bare) will be refused by default.
-
-   Similarly, "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.
-
-   Setting the configuration variables receive.denyCurrentBranch and
-   receive.denyDeleteCurrent to 'ignore' in the receiving repository
-   can be used to override these safety features.  Versions of git
-   since 1.6.2 have issued a loud warning when you tried to do these
-   operations without setting the configuration, so repositories of
-   people who still need to be able to perform such a push should
-   already have been future proofed.
-
-   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 process that already took place so far.
-
- * "git send-email" will not make deep threads by default when sending a
-   patch series with more than two messages.  All messages will be sent
-   as a reply to the first message, i.e. cover letter.  Git 1.6.6 (this
-   release) will issue a warning about the upcoming default change, when
-   it uses the traditional "deep threading" behaviour as the built-in
-   default.  To squelch the warning but still use the "deep threading"
-   behaviour, give --chain-reply-to option or set sendemail.chainreplyto
-   to true.
-
-   It has been possible to configure send-email to send "shallow thread"
-   by setting sendemail.chainreplyto configuration variable to false.
-   The only thing 1.7.0 release will do is to change the default when
-   you haven't configured that variable.
-
- * "git status" will not be "git commit --dry-run".  This change does not
-   affect you if you run the command without pathspec.
-
-   Nobody sane found the current behaviour of "git status Makefile" useful
-   nor meaningful, and it confused users.  "git commit --dry-run" has been
-   provided as a way to get the current behaviour of this command since
-   1.6.5.
-
- * "git diff" traditionally treated various "ignore whitespace" options
-   only as a way to filter the patch output.  "git diff --exit-code -b"
-   exited with non-zero status even if all changes were about changing the
-   ammount of whitespace and nothing else.  and "git diff -b" showed the
-   "diff --git" header line for such a change without patch text.
-
-   In 1.7.0, the "ignore whitespaces" will affect the semantics of the
-   diff operation itself.  A change that does not affect anything but
-   whitespaces will be reported with zero exit status when run with
-   --exit-code, and there will not be "diff --git" header for such a
-   change.
-
-
-Updates since v1.6.5
---------------------
-
-(subsystems)
-
- * various gitk updates including use of themed widgets under Tk 8.5,
-   Japanese translation, a fix to a bug when running "gui blame" from
-   a subdirectory, etc.
-
- * various git-gui updates including new translations, wm states fixes,
-   Tk bug workaround after quitting, improved heuristics to trigger gc,
-   etc.
-
- * various git-svn updates.
-
- * "git fetch" over http learned a new mode that is different from the
-   traditional "dumb commit walker".
-
-(portability)
-
- * imap-send can be built on mingw port.
-
-(performance)
-
- * "git diff -B" has smaller memory footprint.
-
-(usability, bells and whistles)
-
- * The object replace mechanism can be bypassed with --no-replace-objects
-   global option given to the "git" program.
-
- * In configuration files, a few variables that name paths can begin with ~/
-   and ~username/ and they are expanded as expected.
-
- * "git subcmd -h" now shows short usage help for many more subcommands.
-
- * "git bisect reset" can reset to an arbitrary commit.
-
- * "git checkout frotz" when there is no local branch "frotz" but there
-   is only one remote tracking branch "frotz" is taken as a request to
-   start the named branch at the corresponding remote tracking branch.
-
- * "git commit -c/-C/--amend" can be told with a new "--reset-author" option
-   to ignore authorship information in the commit it is taking the message
-   from.
-
- * "git describe" can be told to add "-dirty" suffix with "--dirty" option.
-
- * "git diff" learned --submodule option to show a list of one-line logs
-   instead of differences between the commit object names.
-
- * "git diff" learned to honor diff.color.func configuration to paint
-   function name hint printed on the hunk header "@@ -j,k +l,m @@" line
-   in the specified color.
-
- * "git fetch" learned --all and --multiple options, to run fetch from
-   many repositories, and --prune option to remove remote tracking
-   branches that went stale.  These make "git remote update" and "git
-   remote prune" less necessary (there is no plan to remove "remote
-   update" nor "remote prune", though).
-
- * "git fsck" by default checks the packfiles (i.e. "--full" is the
-   default); you can turn it off with "git fsck --no-full".
-
- * "git grep" can use -F (fixed strings) and -i (ignore case) together.
-
- * import-tars contributed fast-import frontend learned more types of
-   compressed tarballs.
-
- * "git instaweb" knows how to talk with mod_cgid to apache2.
-
- * "git log --decorate" shows the location of HEAD as well.
-
- * "git log" and "git rev-list" learned to take revs and pathspecs from
-   the standard input with the new "--stdin" option.
-
- * "--pretty=format" option to "log" family of commands learned:
-
-   . to wrap text with the "%w()" specifier.
-   . to show reflog information with "%g[sdD]" specifier.
-
- * "git notes" command to annotate existing commits.
-
- * "git merge" (and "git pull") learned --ff-only option to make it fail
-   if the merge does not result in a fast-forward.
-
- * "git mergetool" learned to use p4merge.
-
- * "git rebase -i" learned "reword" that acts like "edit" but immediately
-   starts an editor to tweak the log message without returning control to
-   the shell, which is done by "edit" to give an opportunity to tweak the
-   contents.
-
- * "git send-email" can be told with "--envelope-sender=auto" to use the
-   same address as "From:" address as the envelope sender address.
-
- * "git send-email" will issue a warning when it defaults to the
-   --chain-reply-to behaviour without being told by the user and
-   instructs to prepare for the change of the default in 1.7.0 release.
-
- * In "git submodule add <repository> <path>", <path> is now optional and
-   inferred from <repository> the same way "git clone <repository>" does.
-
- * "git svn" learned to read SVN 1.5+ and SVK merge tickets.
-
- * "git svn" learned to recreate empty directories tracked only by SVN.
-
- * "gitweb" can optionally render its "blame" output incrementally (this
-   requires JavaScript on the client side).
-
- * Author names shown in gitweb output are links to search commits by the
-   author.
-
-Fixes since v1.6.5
-------------------
-
-All of the fixes in v1.6.5.X maintenance series are included in this
-release, unless otherwise noted.
diff --git a/Documentation/RelNotes-1.7.0.1.txt b/Documentation/RelNotes-1.7.0.1.txt
deleted file mode 100644 (file)
index 8ff5bca..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-Git v1.7.0.1 Release Notes
-==========================
-
-Fixes since v1.7.0
-------------------
-
- * In a freshly created repository "rev-parse HEAD^0" complained that
-   it is dangling symref, even though "rev-parse HEAD" didn't.
-
- * "git show :no-such-name" tried to access the index without bounds
-   check, leading to a potential segfault.
-
- * Message from "git cherry-pick" was harder to read and use than necessary
-   when it stopped due to conflicting changes.
-
- * We referred to ".git/refs/" throughout the documentation when we
-   meant to talk about abstract notion of "ref namespace".  Because
-   people's repositories often have packed refs these days, this was
-   confusing.
-
- * "git diff --output=/path/that/cannot/be/written" did not correctly
-   error out.
-
- * "git grep -e -pattern-that-begin-with-dash paths..." could not be
-   spelled as "git grep -- -pattern-that-begin-with-dash paths..." which
-   would be a GNU way to use "--" as "end of options".
-
- * "git grep" compiled with threading support tried to access an
-   uninitialized mutex on boxes with a single CPU.
-
- * "git stash pop -q --index" failed because the unnecessary --index
-   option was propagated to "git stash drop" that is internally run at the
-   end.
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.0.2.txt b/Documentation/RelNotes-1.7.0.2.txt
deleted file mode 100644 (file)
index fcb46ca..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-Git v1.7.0.2 Release Notes
-==========================
-
-Fixes since v1.7.0.1
---------------------
-
- * GIT_PAGER was not honored consistently by some scripted Porcelains, most
-   notably "git am".
-
- * updating working tree files after telling git to add them to the
-   index and while it is still working created garbage object files in
-   the repository without diagnosing it as an error.
-
- * "git bisect -- pathspec..." did not diagnose an error condition properly when
-   the simplification with given pathspec made the history empty.
-
- * "git rev-list --cherry-pick A...B" now has an obvious optimization when the
-   histories haven't diverged (i.e. when one end is an ancestor of the other).
-
- * "git diff --quiet -w" did not work as expected.
-
- * "git fast-import" didn't work with a large input, as it lacked support
-   for producing the pack index in v2 format.
-
- * "git imap-send" didn't use CRLF line endings over the imap protocol
-   when storing its payload to the draft box, violating RFC 3501.
-
- * "git log --format='%w(x,y,z)%b'" and friends that rewrap message
-   has been optimized for utf-8 payload.
-
- * Error messages generated on the receiving end did not come back to "git
-   push".
-
- * "git status" in 1.7.0 lacked the optimization we used to have in 1.6.X series
-   to speed up scanning of large working tree.
-
- * "gitweb" did not diagnose parsing errors properly while reading tis configuration
-   file.
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.0.3.txt b/Documentation/RelNotes-1.7.0.3.txt
deleted file mode 100644 (file)
index 3b35573..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-Git v1.7.0.3 Release Notes
-==========================
-
-Fixes since v1.7.0.2
---------------------
-
- * Object files are created in a more ACL friendly way in repositories
-   where group permission is ACL controlled.
-
- * "git add -i" didn't handle a deleted path very well.
-
- * "git blame" padded line numbers with one extra SP when the total number
-   of lines was one less than multiple of ten due to an off-by-one error.
-
- * "git fetch --all/--multi" used to discard information for remotes that
-   are fetched earlier.
-
- * "git log --author=me --grep=it" tried to find commits that have "it"
-   or are written by "me", instead of the ones that have "it" _and_ are
-   written by "me".
-
- * "git log -g branch" misbehaved when there was no entries in the reflog
-   for the named branch.
-
- * "git mailinfo" (hence "git am") incorrectly removed initial indent from
-   paragraphs.
-
- * "git prune" and "git reflog" (hence "git gc" as well) didn't honor
-   an instruction never to expire by setting gc.reflogexpire to never.
-
- * "git push" misbehaved when branch.<name>.merge was configured without
-   matching branch.<name>.remote.
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.0.4.txt b/Documentation/RelNotes-1.7.0.4.txt
deleted file mode 100644 (file)
index cf7f60e..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-Git v1.7.0.4 Release Notes
-==========================
-
-Fixes since v1.7.0.3
---------------------
-
- * Optimized ntohl/htonl on big-endian machines were broken.
-
- * Color values given to "color.<cmd>.<slot>" configuration can now have
-   more than one attributes (e.g. "bold ul").
-
- * "git add -u nonexistent-path" did not complain.
-
- * "git apply --whitespace=fix" didn't work well when an early patch in
-   a patch series adds trailing blank lines and a later one depended on
-   such a block of blank lines at the end.
-
- * "git fast-export" didn't check error status and stop when marks file
-   cannot be opened.
-
- * "git format-patch --ignore-if-in-upstream" gave unwarranted errors
-   when the range was empty, instead of silently finishing.
-
- * "git remote prune" did not detect remote tracking refs that became
-   dangling correctly.
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.0.5.txt b/Documentation/RelNotes-1.7.0.5.txt
deleted file mode 100644 (file)
index 3149c91..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-Git v1.7.0.5 Release Notes
-==========================
-
-Fixes since v1.7.0.4
---------------------
-
- * "git daemon" failed to compile on platforms without sockaddr_storage type.
-
- * Output from "git rev-list --pretty=oneline" was unparsable when a
-   commit did not have any message, which is abnormal but possible in a
-   repository converted from foreign scm.
-
- * "git stash show <commit-that-is-not-a-stash>" gave an error message
-   that was not so useful.  Reworded the message to "<it> is not a
-   stash".
-
- * Python scripts in contrib/ area now start with "#!/usr/bin/env python"
-   to honor user's PATH.
-
- * "git imap-send" used to mistake any line that begins with "From " as a
-   message separator in format-patch output.
-
- * Smart http server backend failed to report an internal server error and
-   infinitely looped instead after output pipe was closed.
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.0.6.txt b/Documentation/RelNotes-1.7.0.6.txt
deleted file mode 100644 (file)
index b2852b6..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-Git v1.7.0.6 Release Notes
-==========================
-
-Fixes since v1.7.0.5
---------------------
-
- * "git diff --stat" used "int" to count the size of differences,
-   which could result in overflowing.
-
- * "git rev-list --abbrev-commit" defaulted to 40-byte abbreviations, unlike
-   newer tools in the git toolset.
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.0.7.txt b/Documentation/RelNotes-1.7.0.7.txt
deleted file mode 100644 (file)
index d0cb7ca..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-Git v1.7.0.7 Release Notes
-==========================
-
-Fixes since v1.7.0.6
---------------------
-
- * "make NO_CURL=NoThanks install" was broken.
-
- * An overlong line after ".gitdir: " in a git file caused out of bounds
-   access to an array on the stack.
-
- * "git config --path conf.var" to attempt to expand a variable conf.var
-   that uses "~/" short-hand segfaulted when $HOME environment variable
-   was not set.
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.0.txt b/Documentation/RelNotes-1.7.0.txt
deleted file mode 100644 (file)
index 43e3f33..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-Git v1.7.0 Release Notes
-========================
-
-Notes on behaviour change
--------------------------
-
- * "git push" into a branch that is currently checked out (i.e. pointed at by
-   HEAD in a repository that is not bare) is refused by default.
-
-   Similarly, "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.
-
-   Setting the configuration variables receive.denyCurrentBranch and
-   receive.denyDeleteCurrent to 'ignore' in the receiving repository
-   can be used to override these safety features.
-
- * "git send-email" does not make deep threads by default when sending a
-   patch series with more than two messages.  All messages will be sent
-   as a reply to the first message, i.e. cover letter.
-
-   It has been possible already to configure send-email to send "shallow thread"
-   by setting sendemail.chainreplyto configuration variable to false.  The
-   only thing this release does is to change the default when you haven't
-   configured that variable.
-
- * "git status" is not "git commit --dry-run" anymore.  This change does
-   not affect you if you run the command without argument.
-
- * "git diff" traditionally treated various "ignore whitespace" options
-   only as a way to filter the patch output.  "git diff --exit-code -b"
-   exited with non-zero status even if all changes were about changing the
-   amount of whitespace and nothing else;  and "git diff -b" showed the
-   "diff --git" header line for such a change without patch text.
-
-   In this release, the "ignore whitespaces" options affect the semantics
-   of the diff operation.  A change that does not affect anything but
-   whitespaces is reported with zero exit status when run with
-   --exit-code, and there is no "diff --git" header for such a change.
-
- * External diff and textconv helpers are now executed using the shell.
-   This makes them consistent with other programs executed by git, and
-   allows you to pass command-line parameters to the helpers. Any helper
-   paths containing spaces or other metacharacters now need to be
-   shell-quoted.  The affected helpers are GIT_EXTERNAL_DIFF in the
-   environment, and diff.*.command and diff.*.textconv in the config
-   file.
-
- * The --max-pack-size argument to 'git repack', 'git pack-objects', and
-   'git fast-import' was assuming the provided size to be expressed in MiB,
-   unlike the corresponding config variable and other similar options accepting
-   a size value.  It is now expecting a size expressed in bytes, with a possible
-   unit suffix of 'k', 'm', or 'g'.
-
-Updates since v1.6.6
---------------------
-
-(subsystems)
-
- * "git fast-import" updates; adds "option" and "feature" to detect the
-   mismatch between fast-import and the frontends that produce the input
-   stream.
-
- * "git svn" support of subversion "merge tickets" and miscellaneous fixes.
-
- * "gitk" and "git gui" translation updates.
-
- * "gitweb" updates (code clean-up, load checking etc.)
-
-(portability)
-
- * Some more MSVC portability patches for msysgit port.
-
- * Minimum Pthreads emulation for msysgit port.
-
-(performance)
-
- * More performance improvement patches for msysgit port.
-
-(usability, bells and whistles)
-
- * More commands learned "--quiet" and "--[no-]progress" options.
-
- * Various commands given by the end user (e.g. diff.type.textconv,
-   and GIT_EDITOR) can be specified with command line arguments.  E.g. it
-   is now possible to say "[diff "utf8doc"] textconv = nkf -w".
-
- * "sparse checkout" feature allows only part of the work tree to be
-   checked out.
-
- * HTTP transfer can use authentication scheme other than basic
-   (i.e./e.g. digest).
-
- * Switching from a version of superproject that used to have a submodule
-   to another version of superproject that no longer has it did not remove
-   the submodule directory when it should (namely, when you are not
-   interested in the submodule at all and didn't clone/checkout).
-
- * A new attribute conflict-marker-size can be used to change the size of
-   the conflict markers from the default 7; this is useful when tracked
-   contents (e.g. git-merge documentation) have strings that resemble the
-   conflict markers.
-
- * A new syntax "<branch>@{upstream}" can be used on the command line to
-   substitute the name of the "upstream" of the branch.  Missing branch
-   defaults to the current branch, so "git fetch && git merge @{upstream}"
-   will be equivalent to "git pull".
-
- * "git am --resolved" has a synonym "git am --continue".
-
- * "git branch --set-upstream" can be used to update the (surprise!) upstream,
-   i.e. where the branch is supposed to pull and merge from (or rebase onto).
-
- * "git checkout A...B" is a way to detach HEAD at the merge base between
-   A and B.
-
- * "git checkout -m path" to reset the work tree file back into the
-   conflicted state works even when you already ran "git add path" and
-   resolved the conflicts.
-
- * "git commit --date='<date>'" can be used to override the author date
-   just like "git commit --author='<name> <email>'" can be used to
-   override the author identity.
-
- * "git commit --no-status" can be used to omit the listing of the index
-   and the work tree status in the editor used to prepare the log message.
-
- * "git commit" warns a bit more aggressively until you configure user.email,
-   whose default value almost always is not (and fundamentally cannot be)
-   what you want.
-
- * "git difftool" has been extended to make it easier to integrate it
-   with gitk.
-
- * "git fetch --all" can now be used in place of "git remote update".
-
- * "git grep" does not rely on external grep anymore.  It can use more than
-   one thread to accelerate the operation.
-
- * "git grep" learned "--quiet" option.
-
- * "git log" and friends learned "--glob=heads/*" syntax that is a more
-   flexible way to complement "--branches/--tags/--remotes".
-
- * "git merge" learned to pass options specific to strategy-backends.  E.g.
-
-    - "git merge -Xsubtree=path/to/directory" can be used to tell the subtree
-      strategy how much to shift the trees explicitly.
-
-    - "git merge -Xtheirs" can be used to auto-merge as much as possible,
-      while discarding your own changes and taking merged version in
-      conflicted regions.
-
- * "git push" learned "git push origin --delete branch", a syntactic sugar
-   for "git push origin :branch".
-
- * "git push" learned "git push --set-upstream origin forker:forkee" that
-   lets you configure your "forker" branch to later pull from "forkee"
-   branch at "origin".
-
- * "git rebase --onto A...B" means the history is replayed on top of the
-   merge base between A and B.
-
- * "git rebase -i" learned new action "fixup" that squashes the change
-   but does not affect existing log message.
-
- * "git rebase -i" also learned --autosquash option that is useful
-   together with the new "fixup" action.
-
- * "git remote" learned set-url subcommand that updates (surprise!) url
-   for an existing remote nickname.
-
- * "git rerere" learned "forget path" subcommand.  Together with "git
-   checkout -m path" it will be useful when you recorded a wrong
-   resolution.
-
- * Use of "git reset --merge" has become easier when resetting away a
-   conflicted mess left in the work tree.
-
- * "git rerere" had rerere.autoupdate configuration but there was no way
-   to countermand it from the command line; --no-rerere-autoupdate option
-   given to "merge", "revert", etc. fixes this.
-
- * "git status" learned "-s(hort)" output format.
-
-(developers)
-
- * The infrastructure to build foreign SCM interface has been updated.
-
- * Many more commands are now built-in.
-
- * THREADED_DELTA_SEARCH is no more.  If you build with threads, delta
-   compression will always take advantage of it.
-
-Fixes since v1.6.6
-------------------
-
-All of the fixes in v1.6.6.X maintenance series are included in this
-release, unless otherwise noted.
-
- * "git branch -d branch" used to refuse deleting the branch even when
-   the branch is fully merged to its upstream branch if it is not merged
-   to the current branch.  It now deletes it in such a case.
-
- * "fiter-branch" command incorrectly said --prune-empty and --filter-commit
-   were incompatible; the latter should be read as --commit-filter.
-
- * When using "git status" or asking "git diff" to compare the work tree
-   with something, they used to consider that a checked-out submodule with
-   uncommitted changes is not modified; this could cause people to forget
-   committing these changes in the submodule before committing in the
-   superproject. They now consider such a change as a modification and
-   "git diff" will append a "-dirty" to the work tree side when generating
-   patch output or when used with the --submodule option.
diff --git a/Documentation/RelNotes-1.7.1.1.txt b/Documentation/RelNotes-1.7.1.1.txt
deleted file mode 100644 (file)
index 3f6b314..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-Git v1.7.1.1 Release Notes
-==========================
-
-Fixes since v1.7.1
-------------------
-
- * Authentication over http transport can now be made lazily, in that the
-   request can first go to a URL without username, get a 401 response and
-   then the client will ask for the username to use.
-
- * We used to mistakenly think "../work" is a subdirectory of the current
-   directory when we are in "../work-xyz".
-
- * The attribute mechanism now allows an entry that uses an attribute
-   macro that set/unset one attribute, immediately followed by an
-   overriding setting; this makes attribute macros much easier to use.
-
- * We didn't recognize timezone "Z" as a synonym for "UTC" (75b37e70).
-
- * In 1.7.0, read-tree and user commands that use the mechanism such as
-   checkout and merge were fixed to handle switching between branches one
-   of which has a file while the other has a directory at the same path
-   correctly even when there are some "confusing" pathnames in them.  But
-   the algorithm used for this fix was suboptimal and had a terrible
-   performance degradation especially in larger trees.
-
- * "git am -3" did not show diagnosis when the patch in the message was corrupt.
-
- * After "git apply --whitespace=fix" removed trailing blank lines in an
-   patch in a patch series, it failed to apply later patches that depend
-   on the presence of such blank lines.
-
- * "git bundle --stdin" segfaulted.
-
- * "git checkout" and "git rebase" overwrote paths that are marked "assume
-   unchanged".
-
- * "git commit --amend" on a commit with an invalid author-name line that
-   lacks the display name didn't work.
-
- * "git describe" did not tie-break tags that point at the same commit
-   correctly; newer ones are preferred by paying attention to the
-   tagger date now.
-
- * "git diff" used to tell underlying xdiff machinery to work very hard to
-   minimize the output, but this often was spending too many extra cycles
-   for very little gain.
-
- * "git diff --color" did not paint extended diff headers per line
-   (i.e. the coloring escape sequence didn't end at the end of line),
-   which confused "less -R".
-
- * "git fetch" over HTTP verifies the downloaded packfiles more robustly.
-
- * The memory usage by "git index-pack" (run during "git fetch" and "git
-   push") got leaner.
-
- * "GIT_DIR=foo.git git init --bare bar.git" created foo.git instead of bar.git.
-
- * "git log --abbrev=$num --format='%h' ignored --abbrev=$num.
-
- * "git ls-files ../out/side/cwd" refused to work.
-
- * "git merge --log" used to replace the custom message given by "-m" with
-   the shortlog, instead of appending to it.
-
- * "git notes copy" without any other argument segfaulted.
-
- * "git pull" accepted "--dry-run", gave it to underlying "git fetch" but
-   ignored the option itself, resulting in a bogus attempt to merge
-   unrelated commit.
-
- * "git rebase" did not faithfully reproduce a malformed author ident, that
-   is often seen in a repository converted from foreign SCMs.
-
- * "git reset --hard" started from a wrong directory and a working tree in
-   a nonstandard location is in use got confused.
-
- * "git send-email" lacked a way to specify the domainname used in the
-   EHLO/HELO exchange, causing rejected connection from picky servers.
-   It learned --smtp-domain option to solve this issue.
-
- * "git send-email" did not declare a content-transfer-encoding and
-   content-type even when its payload needs to be sent in 8-bit.
-
- * "git show -C -C" and other corner cases lost diff metainfo output
-   in 1.7.0.
-
- * "git stash" incorrectly lost paths in the working tree that were
-   previously removed from the index.
-
- * "git status" stopped refreshing the index by mistake in 1.7.1.
-
- * "git status" showed excess "hints" even when advice.statusHints is set to false.
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.1.2.txt b/Documentation/RelNotes-1.7.1.2.txt
deleted file mode 100644 (file)
index 61ba14e..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-Git v1.7.1.2 Release Notes
-==========================
-
-Fixes since v1.7.1.1
---------------------
-
- * "git commit" did not honor GIT_REFLOG_ACTION environment variable, resulting
-   reflog messages for cherry-pick and revert actions to be recorded as "commit".
-
- * "git clone/fetch/pull" issued an incorrect error message when a ref and
-   a symref that points to the ref were updated at the same time.  This
-   obviously would update them to the same value, and should not result in
-   an error condition.
-
- * "git diff" inside a tree with many pathnames that have certain
-   characters has become very slow in 1.7.0 by mistake.
-
- * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
-   when --keep-dashdash was in effect.
-
- * An overlong line after ".gitdir: " in a git file caused out of bounds
-   access to an array on the stack.
-
- * "git config --path conf.var" to attempt to expand a variable conf.var
-   that uses "~/" short-hand segfaulted when $HOME environment variable
-   was not set.
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.1.txt b/Documentation/RelNotes-1.7.1.txt
deleted file mode 100644 (file)
index 9d89fed..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-Git v1.7.1 Release Notes
-========================
-
-Updates since v1.7.0
---------------------
-
- * Eric Raymond is the maintainer of updated CIAbot scripts, in contrib/.
-
- * gitk updates.
-
- * Some commands (e.g. svn and http interfaces) that interactively ask
-   for a password can be told to use an external program given via
-   GIT_ASKPASS.
-
- * Conflict markers that lead the common ancestor in diff3-style output
-   now have a label, which hopefully would help third-party tools that
-   expect one.
-
- * Comes with an updated bash-completion script.
-
- * "git am" learned "--keep-cr" option to handle inputs that are
-   a mixture of changes to files with and without CRLF line endings.
-
- * "git cvsimport" learned -R option to leave revision mapping between
-   CVS revisions and resulting git commits.
-
- * "git diff --submodule" notices and describes dirty submodules.
-
- * "git for-each-ref" learned %(symref), %(symref:short) and %(flag)
-   tokens.
-
- * "git hash-object --stdin-paths" can take "--no-filters" option now.
-
- * "git init" can be told to look at init.templatedir configuration
-   variable (obviously that has to come from either /etc/gitconfig or
-   $HOME/.gitconfig).
-
- * "git grep" learned "--no-index" option, to search inside contents that
-   are not managed by git.
-
- * "git grep" learned --color=auto/always/never.
-
- * "git grep" learned to paint filename and line-number in colors.
-
- * "git log -p --first-parent -m" shows one-parent diff for merge
-   commits, instead of showing combined diff.
-
- * "git merge-file" learned to use custom conflict marker size and also
-   to use the "union merge" behaviour.
-
- * "git notes" command has been rewritten in C and learned many commands
-   and features to help you carry notes forward across rebases and amends.
-
- * "git request-pull" identifies the commit the request is relative to in
-   a more readable way.
-
- * "git reset" learned "--keep" option that lets you discard commits
-   near the tip while preserving your local changes in a way similar
-   to how "git checkout branch" does.
-
- * "git status" notices and describes dirty submodules.
-
- * "git svn" should work better when interacting with repositories
-   with CRLF line endings.
-
- * "git imap-send" learned to support CRAM-MD5 authentication.
-
- * "gitweb" installation procedure can use "minified" js/css files
-   better.
-
- * Various documentation updates.
-
-Fixes since v1.7.0
-------------------
-
-All of the fixes in v1.7.0.X maintenance series are included in this
-release, unless otherwise noted.
-
- * "git add frotz/nitfol" did not complain when the entire frotz/ directory
-   was ignored.
-
- * "git diff --stat" used "int" to count the size of differences,
-   which could result in overflowing.
-
- * "git rev-list --pretty=oneline" didn't terminate a record with LF for
-   commits without any message.
-
- * "git rev-list --abbrev-commit" defaulted to 40-byte abbreviations, unlike
-   newer tools in the git toolset.
diff --git a/Documentation/RelNotes-1.7.2.1.txt b/Documentation/RelNotes-1.7.2.1.txt
deleted file mode 100644 (file)
index 1103c47..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-Git v1.7.2.1 Release Notes
-==========================
-
-Fixes since v1.7.2
-------------------
-
- * "git instaweb" wasn't useful when your Apache was installed under a
-   name other than apache2 (e.g. "httpd").
-
- * Similarly, "git web--browse" (invoked by "git help -w") learned that
-   chrome browser is sometimes called google-chrome.
-
- * An overlong line after ".gitdir: " in a git file caused out of bounds
-   access to an array on the stack.
-
- * "git config --path conf.var" to attempt to expand a variable conf.var
-   that uses "~/" short-hand segfaulted when $HOME environment variable
-   was not set.
-
- * Documentation on Cygwin failed to build.
-
- * The error message from "git pull blarg" when 'blarg' is an unknown
-   remote name has been improved.
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.2.2.txt b/Documentation/RelNotes-1.7.2.2.txt
deleted file mode 100644 (file)
index 71eb6a8..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-Git v1.7.2.2 Release Notes
-==========================
-
-Fixes since v1.7.2.1
---------------------
-
- * Object transfer over smart http transport deadlocked the client when
-   the remote HTTP server returned a failure, instead of erroring it out.
-
- * git-gui honors custom textconv filters when showing diff and blame;
-
- * git diff --relative=subdir (without the necessary trailing /) did not
-   work well;
-
- * "git diff-files -p --submodule" was recently broken;
-
- * "git checkout -b n ':/token'" did not work;
-
- * "git index-pack" (hence "git fetch/clone/pull/push") enabled the object
-   replacement machinery by mistake (it never should have);
-
-And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.2.txt b/Documentation/RelNotes-1.7.2.txt
deleted file mode 100644 (file)
index 15cf011..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-Git v1.7.2 Release Notes
-========================
-
-Updates since v1.7.1
---------------------
-
- * core.eol configuration and text/eol attributes are the new way to control
-   the end of line conventions for files in the working tree.
-
- * core.autocrlf has been made safer - it will now only handle line
-   endings for new files and files that are LF-only in the
-   repository. To normalize content that has been checked in with
-   CRLF, use the new eol/text attributes.
-
- * The whitespace rules used in "git apply --whitespace" and "git diff"
-   gained a new member in the family (tab-in-indent) to help projects with
-   policy to indent only with spaces.
-
- * When working from a subdirectory, by default, git does not look for its
-   metadirectory ".git" across filesystems, primarily to help people who
-   have invocations of git in their custom PS1 prompts, as being outside
-   of a git repository would look for ".git" all the way up to the root
-   directory, and NFS mounts are often slow.  DISCOVERY_ACROSS_FILESYSTEM
-   environment variable can be used to tell git not to stop at a
-   filesystem boundary.
-
- * Usage help messages generated by parse-options library (i.e. most
-   of the Porcelain commands) are sent to the standard output now.
-
- * ':/<string>' notation to look for a commit now takes regular expression
-   and it is not anchored at the beginning of the commit log message
-   anymore (this is a backward incompatible change).
-
- * "git" wrapper learned "-c name=value" option to override configuration
-   variable from the command line.
-
- * Improved portability for various platforms including older SunOS,
-   HP-UX 10/11, AIX, Tru64, etc. and platforms with Python 2.4.
-
- * The message from "git am -3" has been improved when conflict
-   resolution ended up making the patch a no-op.
-
- * "git blame" applies the textconv filter to the contents it works
-   on, when available.
-
- * "git checkout --orphan newbranch" is similar to "-b newbranch" but
-   prepares to create a root commit that is not connected to any existing
-   commit.
-
- * "git cherry-pick" learned to pick a range of commits
-   (e.g. "cherry-pick A..B" and "cherry-pick --stdin"), so did "git
-   revert"; these do not support the nicer sequencing control "rebase
-   [-i]" has, though.
-
- * "git cherry-pick" and "git revert" learned --strategy option to specify
-   the merge strategy to be used when performing three-way merges.
-
- * "git cvsserver" can be told to use pserver; its password file can be
-   stored outside the repository.
-
- * The output from the textconv filter used by "git diff" can be cached to
-   speed up their reuse.
-
- * "git diff --word-diff=<mode>" extends the existing "--color-words"
-   option, making it more useful in color-challenged environments.
-
- * The regexp to detect function headers used by "git diff" for PHP has
-   been enhanced for visibility modifiers (public, protected, etc.) to
-   better support PHP5.
-
- * "diff.noprefix" configuration variable can be used to implicitly
-   ask for "diff --no-prefix" behaviour.
-
- * "git for-each-ref" learned "%(objectname:short)" that gives the object
-   name abbreviated.
-
- * "git format-patch" learned --signature option and format.signature
-   configuration variable to customize the e-mail signature used in the
-   output.
-
- * Various options to "git grep" (e.g. --count, --name-only) work better
-   with binary files.
-
- * "git grep" learned "-Ovi" to open the files with hits in your editor.
-
- * "git help -w" learned "chrome" and "chromium" browsers.
-
- * "git log --decorate" shows commit decorations in various colours.
-
- * "git log --follow <path>" follows across copies (it used to only follow
-   renames).  This may make the processing more expensive.
-
- * "git log --pretty=format:<template>" specifier learned "% <something>"
-   magic that inserts a space only when %<something> expands to a
-   non-empty string; this is similar to "%+<something>" magic, but is
-   useful in a context to generate a single line output.
-
- * "git notes prune" learned "-n" (dry-run) and "-v" options, similar to
-   what "git prune" has.
-
- * "git patch-id" can be fed a mbox without getting confused by the
-   signature line in the format-patch output.
-
- * "git remote" learned "set-branches" subcommand.
-
- * "git rev-list A..B" learned --ancestry-path option to further limit
-   the result to the commits that are on the ancestry chain between A and
-   B (i.e. commits that are not descendants of A are excluded).
-
- * "git show -5" is equivalent to "git show --do-walk 5"; this is similar
-   to the update to make "git show master..next" walk the history,
-   introduced in 1.6.4.
-
- * "git status [-s] --ignored" can be used to list ignored paths.
-
- * "git status -s -b" shows the current branch in the output.
-
- * "git status" learned "--ignore-submodules" option.
-
- * Various "gitweb" enhancements and clean-ups, including syntax
-   highlighting, "plackup" support for instaweb, .fcgi suffix to run
-   it as FastCGI script, etc.
-
- * The test harness has been updated to produce TAP-friendly output.
-
- * Many documentation improvement patches are also included.
-
-
-Fixes since v1.7.1
-------------------
-
-All of the fixes in v1.7.1.X maintenance series are included in this
-release, unless otherwise noted.
-
- * We didn't URL decode "file:///path/to/repo" correctly when path/to/repo
-   had percent-encoded characters (638794c, 9d2e942, ce83eda, 3c73a1d).
-
- * "git clone" did not configure remote.origin.url correctly for bare
-   clones (df61c889).
-
- * "git diff --graph" works better with "--color-words" and other options
-   (81fa024..4297c0a).
-
- * "git diff" could show ambiguous abbreviation of blob object names on
-   its "index" line (3e5a188).
-
- * "git reset --hard" started from a wrong directory and a working tree in
-   a nonstandard location is in use got confused (560fb6a1).
-
- * "git read-tree -m A B" used to switch to branch B while retaining
-   local changes added an incorrect cache-tree information (b1f47514).
diff --git a/Documentation/RelNotes/1.5.0.1.txt b/Documentation/RelNotes/1.5.0.1.txt
new file mode 100644 (file)
index 0000000..fea3f99
--- /dev/null
@@ -0,0 +1,42 @@
+GIT v1.5.0.1 Release Notes
+==========================
+
+Fixes since v1.5.0
+------------------
+
+* Documentation updates
+
+  - Clarifications and corrections to 1.5.0 release notes.
+
+  - The main documentation did not link to git-remote documentation.
+
+  - Clarified introductory text of git-rebase documentation.
+
+  - Converted remaining mentions of update-index on Porcelain
+    documents to git-add/git-rm.
+
+  - Some i18n.* configuration variables were incorrectly
+    described as core.*; fixed.
+
+* Bugfixes
+
+  - git-add and git-update-index on a filesystem on which
+    executable bits are unreliable incorrectly reused st_mode
+    bits even when the path changed between symlink and regular
+    file.
+
+  - git-daemon marks the listening sockets with FD_CLOEXEC so
+    that it won't be leaked into the children.
+
+  - segfault from git-blame when the mandatory pathname
+    parameter was missing was fixed; usage() message is given
+    instead.
+
+  - git-rev-list did not read $GIT_DIR/config file, which means
+    that did not honor i18n.logoutputencoding correctly.
+
+* Tweaks
+
+  - sliding mmap() inefficiently mmaped the same region of a
+    packfile with an access pattern that used objects in the
+    reverse order.  This has been made more efficient.
diff --git a/Documentation/RelNotes/1.5.0.2.txt b/Documentation/RelNotes/1.5.0.2.txt
new file mode 100644 (file)
index 0000000..b061e50
--- /dev/null
@@ -0,0 +1,65 @@
+GIT v1.5.0.2 Release Notes
+==========================
+
+Fixes since v1.5.0.1
+--------------------
+
+* Bugfixes
+
+  - Automated merge conflict handling when changes to symbolic
+    links conflicted were completely broken.  The merge-resolve
+    strategy created a regular file with conflict markers in it
+    in place of the symbolic link.  The default strategy,
+    merge-recursive was even more broken.  It removed the path
+    that was pointed at by the symbolic link.  Both of these
+    problems have been fixed.
+
+  - 'git diff maint master next' did not correctly give combined
+    diff across three trees.
+
+  - 'git fast-import' portability fix for Solaris.
+
+  - 'git show-ref --verify' without arguments did not error out
+    but segfaulted.
+
+  - 'git diff :tracked-file `pwd`/an-untracked-file' gave an extra
+    slashes after a/ and b/.
+
+  - 'git format-patch' produced too long filenames if the commit
+    message had too long line at the beginning.
+
+  - Running 'make all' and then without changing anything
+    running 'make install' still rebuilt some files.  This
+    was inconvenient when building as yourself and then
+    installing as root (especially problematic when the source
+    directory is on NFS and root is mapped to nobody).
+
+  - 'git-rerere' failed to deal with two unconflicted paths that
+    sorted next to each other.
+
+  - 'git-rerere' attempted to open(2) a symlink and failed if
+    there was a conflict.  Since a conflicting change to a
+    symlink would not benefit from rerere anyway, the command
+    now ignores conflicting changes to symlinks.
+
+  - 'git-repack' did not like to pass more than 64 arguments
+    internally to underlying 'rev-list' logic, which made it
+    impossible to repack after accumulating many (small) packs
+    in the repository.
+
+  - 'git-diff' to review the combined diff during a conflicted
+    merge were not reading the working tree version correctly
+    when changes to a symbolic link conflicted.  It should have
+    read the data using readlink(2) but read from the regular
+    file the symbolic link pointed at.
+
+  - 'git-remote' did not like period in a remote's name.
+
+* Documentation updates
+
+  - added and clarified core.bare, core.legacyheaders configurations.
+
+  - updated "git-clone --depth" documentation.
+
+
+* Assorted git-gui fixes.
diff --git a/Documentation/RelNotes/1.5.0.3.txt b/Documentation/RelNotes/1.5.0.3.txt
new file mode 100644 (file)
index 0000000..cd500f9
--- /dev/null
@@ -0,0 +1,58 @@
+GIT v1.5.0.3 Release Notes
+==========================
+
+Fixes since v1.5.0.2
+--------------------
+
+* Bugfixes
+
+  - 'git.el' honors the commit coding system from the configuration.
+
+  - 'blameview' in contrib/ correctly digs deeper when a line is
+    clicked.
+
+  - 'http-push' correctly makes sure the remote side has leading
+    path.  Earlier it started in the middle of the path, and
+    incorrectly.
+
+  - 'git-merge' did not exit with non-zero status when the
+    working tree was dirty and cannot fast forward.  It does
+    now.
+
+  - 'cvsexportcommit' does not lose yet-to-be-used message file.
+
+  - int-vs-size_t typefix when running combined diff on files
+    over 2GB long.
+
+  - 'git apply --whitespace=strip' should not touch unmodified
+    lines.
+
+  - 'git-mailinfo' choke when a logical header line was too long.
+
+  - 'git show A..B' did not error out.  Negative ref ("not A" in
+    this example) does not make sense for the purpose of the
+    command, so now it errors out.
+
+  - 'git fmt-merge-msg --file' without file parameter did not
+    correctly error out.
+
+  - 'git archimport' barfed upon encountering a commit without
+    summary.
+
+  - 'git index-pack' did not protect itself from getting a short
+    read out of pread(2).
+
+  - 'git http-push' had a few buffer overruns.
+
+  - Build dependency fixes to rebuild fetch.o when other headers
+    change.
+
+* Documentation updates
+
+  - user-manual updates.
+
+  - Options to 'git remote add' were described insufficiently.
+
+  - Configuration format.suffix was not documented.
+
+  - Other formatting and spelling fixes.
diff --git a/Documentation/RelNotes/1.5.0.4.txt b/Documentation/RelNotes/1.5.0.4.txt
new file mode 100644 (file)
index 0000000..feefa5d
--- /dev/null
@@ -0,0 +1,22 @@
+GIT v1.5.0.4 Release Notes
+==========================
+
+Fixes since v1.5.0.3
+--------------------
+
+* Bugfixes
+
+  - git.el does not add duplicate sign-off lines.
+
+  - git-commit shows the full stat of the resulting commit, not
+    just about the files in the current directory, when run from
+    a subdirectory.
+
+  - "git-checkout -m '@{8 hours ago}'" had a funny failure from
+    eval; fixed.
+
+  - git-gui updates.
+
+* Documentation updates
+
+* User manual updates
diff --git a/Documentation/RelNotes/1.5.0.5.txt b/Documentation/RelNotes/1.5.0.5.txt
new file mode 100644 (file)
index 0000000..eeec3d7
--- /dev/null
@@ -0,0 +1,26 @@
+GIT v1.5.0.5 Release Notes
+==========================
+
+Fixes since v1.5.0.3
+--------------------
+
+* Bugfixes
+
+  - git-merge (hence git-pull) did not refuse fast-forwarding
+    when the working tree had local changes that would have
+    conflicted with it.
+
+  - git.el does not add duplicate sign-off lines.
+
+  - git-commit shows the full stat of the resulting commit, not
+    just about the files in the current directory, when run from
+    a subdirectory.
+
+  - "git-checkout -m '@{8 hours ago}'" had a funny failure from
+    eval; fixed.
+
+  - git-gui updates.
+
+* Documentation updates
+
+* User manual updates
diff --git a/Documentation/RelNotes/1.5.0.6.txt b/Documentation/RelNotes/1.5.0.6.txt
new file mode 100644 (file)
index 0000000..c02015a
--- /dev/null
@@ -0,0 +1,21 @@
+GIT v1.5.0.6 Release Notes
+==========================
+
+Fixes since v1.5.0.5
+--------------------
+
+* Bugfixes
+
+  - a handful small fixes to gitweb.
+
+  - build procedure for user-manual is fixed not to require locally
+    installed stylesheets.
+
+  - "git commit $paths" on paths whose earlier contents were
+    already updated in the index were failing out.
+
+* Documentation
+
+  - user-manual has better cross references.
+
+  - gitweb installation/deployment procedure is now documented.
diff --git a/Documentation/RelNotes/1.5.0.7.txt b/Documentation/RelNotes/1.5.0.7.txt
new file mode 100644 (file)
index 0000000..670ad32
--- /dev/null
@@ -0,0 +1,18 @@
+GIT v1.5.0.7 Release Notes
+==========================
+
+Fixes since v1.5.0.6
+--------------------
+
+* Bugfixes
+
+  - git-upload-pack failed to close unused pipe ends, resulting
+    in many zombies to hang around.
+
+  - git-rerere was recording the contents of earlier hunks
+    duplicated in later hunks.  This prevented resolving the same
+    conflict when performing the same merge the other way around.
+
+* Documentation
+
+  - a few documentation fixes from Debian package maintainer.
diff --git a/Documentation/RelNotes/1.5.0.txt b/Documentation/RelNotes/1.5.0.txt
new file mode 100644 (file)
index 0000000..daf4bdb
--- /dev/null
@@ -0,0 +1,469 @@
+GIT v1.5.0 Release Notes
+========================
+
+Old news
+--------
+
+This section is for people who are upgrading from ancient
+versions of git.  Although all of the changes in this section
+happened before the current v1.4.4 release, they are summarized
+here in the v1.5.0 release notes for people who skipped earlier
+versions.
+
+As of git v1.5.0 there are some optional features that changes
+the repository to allow data to be stored and transferred more
+efficiently.  These features are not enabled by default, as they
+will make the repository unusable with older versions of git.
+Specifically, the available options are:
+
+ - There is a configuration variable core.legacyheaders that
+   changes the format of loose objects so that they are more
+   efficient to pack and to send out of the repository over git
+   native protocol, since v1.4.2.  However, loose objects
+   written in the new format cannot be read by git older than
+   that version; people fetching from your repository using
+   older clients over dumb transports (e.g. http) using older
+   versions of git will also be affected.
+
+   To let git use the new loose object format, you have to
+   set core.legacyheaders to false.
+
+ - Since v1.4.3, configuration repack.usedeltabaseoffset allows
+   packfile to be created in more space efficient format, which
+   cannot be read by git older than that version.
+
+   To let git use the new format for packfiles, you have to
+   set repack.usedeltabaseoffset to true.
+
+The above two new features are not enabled by default and you
+have to explicitly ask for them, because they make repositories
+unreadable by older versions of git, and in v1.5.0 we still do
+not enable them by default for the same reason.  We will change
+this default probably 1 year after 1.4.2's release, when it is
+reasonable to expect everybody to have new enough version of
+git.
+
+ - 'git pack-refs' appeared in v1.4.4; this command allows tags
+   to be accessed much more efficiently than the traditional
+   'one-file-per-tag' format.  Older git-native clients can
+   still fetch from a repository that packed and pruned refs
+   (the server side needs to run the up-to-date version of git),
+   but older dumb transports cannot.  Packing of refs is done by
+   an explicit user action, either by use of "git pack-refs
+   --prune" command or by use of "git gc" command.
+
+ - 'git -p' to paginate anything -- many commands do pagination
+   by default on a tty.  Introduced between v1.4.1 and v1.4.2;
+   this may surprise old timers.
+
+ - 'git archive' superseded 'git tar-tree' in v1.4.3;
+
+ - 'git cvsserver' was new invention in v1.3.0;
+
+ - 'git repo-config', 'git grep', 'git rebase' and 'gitk' were
+   seriously enhanced during v1.4.0 timeperiod.
+
+ - 'gitweb' became part of git.git during v1.4.0 timeperiod and
+   seriously modified since then.
+
+ - reflog is an v1.4.0 invention.  This allows you to name a
+   revision that a branch used to be at (e.g. "git diff
+   master@{yesterday} master" allows you to see changes since
+   yesterday's tip of the branch).
+
+
+Updates in v1.5.0 since v1.4.4 series
+-------------------------------------
+
+* Index manipulation
+
+ - git-add is to add contents to the index (aka "staging area"
+   for the next commit), whether the file the contents happen to
+   be is an existing one or a newly created one.
+
+ - git-add without any argument does not add everything
+   anymore.  Use 'git-add .' instead.  Also you can add
+   otherwise ignored files with an -f option.
+
+ - git-add tries to be more friendly to users by offering an
+   interactive mode ("git-add -i").
+
+ - git-commit <path> used to refuse to commit if <path> was
+   different between HEAD and the index (i.e. update-index was
+   used on it earlier).  This check was removed.
+
+ - git-rm is much saner and safer.  It is used to remove paths
+   from both the index file and the working tree, and makes sure
+   you are not losing any local modification before doing so.
+
+ - git-reset <tree> <paths>... can be used to revert index
+   entries for selected paths.
+
+ - git-update-index is much less visible.  Many suggestions to
+   use the command in git output and documentation have now been
+   replaced by simpler commands such as "git add" or "git rm".
+
+
+* Repository layout and objects transfer
+
+ - The data for origin repository is stored in the configuration
+   file $GIT_DIR/config, not in $GIT_DIR/remotes/, for newly
+   created clones.  The latter is still supported and there is
+   no need to convert your existing repository if you are
+   already comfortable with your workflow with the layout.
+
+ - git-clone always uses what is known as "separate remote"
+   layout for a newly created repository with a working tree.
+
+   A repository with the separate remote layout starts with only
+   one default branch, 'master', to be used for your own
+   development.  Unlike the traditional layout that copied all
+   the upstream branches into your branch namespace (while
+   renaming their 'master' to your 'origin'), the new layout
+   puts upstream branches into local "remote-tracking branches"
+   with their own namespace. These can be referenced with names
+   such as "origin/$upstream_branch_name" and are stored in
+   .git/refs/remotes rather than .git/refs/heads where normal
+   branches are stored.
+
+   This layout keeps your own branch namespace less cluttered,
+   avoids name collision with your upstream, makes it possible
+   to automatically track new branches created at the remote
+   after you clone from it, and makes it easier to interact with
+   more than one remote repository (you can use "git remote" to
+   add other repositories to track).  There might be some
+   surprises:
+
+   * 'git branch' does not show the remote tracking branches.
+     It only lists your own branches.  Use '-r' option to view
+     the tracking branches.
+
+   * If you are forking off of a branch obtained from the
+     upstream, you would have done something like 'git branch
+     my-next next', because traditional layout dropped the
+     tracking branch 'next' into your own branch namespace.
+     With the separate remote layout, you say 'git branch next
+     origin/next', which allows you to use the matching name
+     'next' for your own branch.  It also allows you to track a
+     remote other than 'origin' (i.e. where you initially cloned
+     from) and fork off of a branch from there the same way
+     (e.g. "git branch mingw j6t/master").
+
+   Repositories initialized with the traditional layout continue
+   to work.
+
+ - New branches that appear on the origin side after a clone is
+   made are also tracked automatically.  This is done with an
+   wildcard refspec "refs/heads/*:refs/remotes/origin/*", which
+   older git does not understand, so if you clone with 1.5.0,
+   you would need to downgrade remote.*.fetch in the
+   configuration file to specify each branch you are interested
+   in individually if you plan to fetch into the repository with
+   older versions of git (but why would you?).
+
+ - Similarly, wildcard refspec "refs/heads/*:refs/remotes/me/*"
+   can be given to "git-push" command to update the tracking
+   branches that is used to track the repository you are pushing
+   from on the remote side.
+
+ - git-branch and git-show-branch know remote tracking branches
+   (use the command line switch "-r" to list only tracked branches).
+
+ - git-push can now be used to delete a remote branch or a tag.
+   This requires the updated git on the remote side (use "git
+   push <remote> :refs/heads/<branch>" to delete "branch").
+
+ - git-push more aggressively keeps the transferred objects
+   packed.  Earlier we recommended to monitor amount of loose
+   objects and repack regularly, but you should repack when you
+   accumulated too many small packs this way as well.  Updated
+   git-count-objects helps you with this.
+
+ - git-fetch also more aggressively keeps the transferred objects
+   packed.  This behavior of git-push and git-fetch can be
+   tweaked with a single configuration transfer.unpacklimit (but
+   usually there should not be any need for a user to tweak it).
+
+ - A new command, git-remote, can help you manage your remote
+   tracking branch definitions.
+
+ - You may need to specify explicit paths for upload-pack and/or
+   receive-pack due to your ssh daemon configuration on the
+   other end.  This can now be done via remote.*.uploadpack and
+   remote.*.receivepack configuration.
+
+
+* Bare repositories
+
+ - Certain commands change their behavior in a bare repository
+   (i.e. a repository without associated working tree).  We use
+   a fairly conservative heuristic (if $GIT_DIR is ".git", or
+   ends with "/.git", the repository is not bare) to decide if a
+   repository is bare, but "core.bare" configuration variable
+   can be used to override the heuristic when it misidentifies
+   your repository.
+
+ - git-fetch used to complain updating the current branch but
+   this is now allowed for a bare repository.  So is the use of
+   'git-branch -f' to update the current branch.
+
+ - Porcelain-ish commands that require a working tree refuses to
+   work in a bare repository.
+
+
+* Reflog
+
+ - Reflog records the history from the view point of the local
+   repository. In other words, regardless of the real history,
+   the reflog shows the history as seen by one particular
+   repository (this enables you to ask "what was the current
+   revision in _this_ repository, yesterday at 1pm?").  This
+   facility is enabled by default for repositories with working
+   trees, and can be accessed with the "branch@{time}" and
+   "branch@{Nth}" notation.
+
+ - "git show-branch" learned showing the reflog data with the
+   new -g option.  "git log" has -g option to view reflog
+   entries in a more verbose manner.
+
+ - git-branch knows how to rename branches and moves existing
+   reflog data from the old branch to the new one.
+
+ - In addition to the reflog support in v1.4.4 series, HEAD
+   reference maintains its own log.  "HEAD@{5.minutes.ago}"
+   means the commit you were at 5 minutes ago, which takes
+   branch switching into account.  If you want to know where the
+   tip of your current branch was at 5 minutes ago, you need to
+   explicitly say its name (e.g. "master@{5.minutes.ago}") or
+   omit the refname altogether i.e. "@{5.minutes.ago}".
+
+ - The commits referred to by reflog entries are now protected
+   against pruning.  The new command "git reflog expire" can be
+   used to truncate older reflog entries and entries that refer
+   to commits that have been pruned away previously with older
+   versions of git.
+
+   Existing repositories that have been using reflog may get
+   complaints from fsck-objects and may not be able to run
+   git-repack, if you had run git-prune from older git; please
+   run "git reflog expire --stale-fix --all" first to remove
+   reflog entries that refer to commits that are no longer in
+   the repository when that happens.
+
+
+* Crufts removal
+
+ - We used to say "old commits are retrievable using reflog and
+   'master@{yesterday}' syntax as long as you haven't run
+   git-prune".  We no longer have to say the latter half of the
+   above sentence, as git-prune does not remove things reachable
+   from reflog entries.
+
+ - There is a toplevel garbage collector script, 'git-gc', that
+   runs periodic cleanup functions, including 'git-repack -a -d',
+   'git-reflog expire', 'git-pack-refs --prune', and 'git-rerere
+   gc'.
+
+ - The output from fsck ("fsck-objects" is called just "fsck"
+   now, but the old name continues to work) was needlessly
+   alarming in that it warned missing objects that are reachable
+   only from dangling objects.  This has been corrected and the
+   output is much more useful.
+
+
+* Detached HEAD
+
+ - You can use 'git-checkout' to check out an arbitrary revision
+   or a tag as well, instead of named branches.  This will
+   dissociate your HEAD from the branch you are currently on.
+
+   A typical use of this feature is to "look around".  E.g.
+
+       $ git checkout v2.6.16
+       ... compile, test, etc.
+       $ git checkout v2.6.17
+       ... compile, test, etc.
+
+ - After detaching your HEAD, you can go back to an existing
+   branch with usual "git checkout $branch".  Also you can
+   start a new branch using "git checkout -b $newbranch" to
+   start a new branch at that commit.
+
+ - You can even pull from other repositories, make merges and
+   commits while your HEAD is detached.  Also you can use "git
+   reset" to jump to arbitrary commit, while still keeping your
+   HEAD detached.
+
+   Remember that a detached state is volatile, i.e. it will be forgotten
+   as soon as you move away from it with the checkout or reset command,
+   unless a branch is created from it as mentioned above.  It is also
+   possible to rescue a lost detached state from the HEAD reflog.
+
+
+* Packed refs
+
+ - Repositories with hundreds of tags have been paying large
+   overhead, both in storage and in runtime, due to the
+   traditional one-ref-per-file format.  A new command,
+   git-pack-refs, can be used to "pack" them in more efficient
+   representation (you can let git-gc do this for you).
+
+ - Clones and fetches over dumb transports are now aware of
+   packed refs and can download from repositories that use
+   them.
+
+
+* Configuration
+
+ - configuration related to color setting are consolidated under
+   color.* namespace (older diff.color.*, status.color.* are
+   still supported).
+
+ - 'git-repo-config' command is accessible as 'git-config' now.
+
+
+* Updated features
+
+ - git-describe uses better criteria to pick a base ref.  It
+   used to pick the one with the newest timestamp, but now it
+   picks the one that is topologically the closest (that is,
+   among ancestors of commit C, the ref T that has the shortest
+   output from "git-rev-list T..C" is chosen).
+
+ - git-describe gives the number of commits since the base ref
+   between the refname and the hash suffix.  E.g. the commit one
+   before v2.6.20-rc6 in the kernel repository is:
+
+       v2.6.20-rc5-306-ga21b069
+
+   which tells you that its object name begins with a21b069,
+   v2.6.20-rc5 is an ancestor of it (meaning, the commit
+   contains everything -rc5 has), and there are 306 commits
+   since v2.6.20-rc5.
+
+ - git-describe with --abbrev=0 can be used to show only the
+   name of the base ref.
+
+ - git-blame learned a new option, --incremental, that tells it
+   to output the blames as they are assigned.  A sample script
+   to use it is also included as contrib/blameview.
+
+ - git-blame starts annotating from the working tree by default.
+
+
+* Less external dependency
+
+ - We no longer require the "merge" program from the RCS suite.
+   All 3-way file-level merges are now done internally.
+
+ - The original implementation of git-merge-recursive which was
+   in Python has been removed; we have a C implementation of it
+   now.
+
+ - git-shortlog is no longer a Perl script.  It no longer
+   requires output piped from git-log; it can accept revision
+   parameters directly on the command line.
+
+
+* I18n
+
+ - We have always encouraged the commit message to be encoded in
+   UTF-8, but the users are allowed to use legacy encoding as
+   appropriate for their projects.  This will continue to be the
+   case.  However, a non UTF-8 commit encoding _must_ be
+   explicitly set with i18n.commitencoding in the repository
+   where a commit is made; otherwise git-commit-tree will
+   complain if the log message does not look like a valid UTF-8
+   string.
+
+ - The value of i18n.commitencoding in the originating
+   repository is recorded in the commit object on the "encoding"
+   header, if it is not UTF-8.  git-log and friends notice this,
+   and reencodes the message to the log output encoding when
+   displaying, if they are different.  The log output encoding
+   is determined by "git log --encoding=<encoding>",
+   i18n.logoutputencoding configuration, or i18n.commitencoding
+   configuration, in the decreasing order of preference, and
+   defaults to UTF-8.
+
+ - Tools for e-mailed patch application now default to -u
+   behavior; i.e. it always re-codes from the e-mailed encoding
+   to the encoding specified with i18n.commitencoding.  This
+   unfortunately forces projects that have happily been using a
+   legacy encoding without setting i18n.commitencoding to set
+   the configuration, but taken with other improvement, please
+   excuse us for this very minor one-time inconvenience.
+
+
+* e-mailed patches
+
+ - See the above I18n section.
+
+ - git-format-patch now enables --binary without being asked.
+   git-am does _not_ default to it, as sending binary patch via
+   e-mail is unusual and is harder to review than textual
+   patches and it is prudent to require the person who is
+   applying the patch to explicitly ask for it.
+
+ - The default suffix for git-format-patch output is now ".patch",
+   not ".txt".  This can be changed with --suffix=.txt option,
+   or setting the config variable "format.suffix" to ".txt".
+
+
+* Foreign SCM interfaces
+
+ - git-svn now requires the Perl SVN:: libraries, the
+   command-line backend was too slow and limited.
+
+ - the 'commit' subcommand of git-svn has been renamed to
+   'set-tree', and 'dcommit' is the recommended replacement for
+   day-to-day work.
+
+ - git fast-import backend.
+
+
+* User support
+
+ - Quite a lot of documentation updates.
+
+ - Bash completion scripts have been updated heavily.
+
+ - Better error messages for often used Porcelainish commands.
+
+ - Git GUI.  This is a simple Tk based graphical interface for
+   common Git operations.
+
+
+* Sliding mmap
+
+ - We used to assume that we can mmap the whole packfile while
+   in use, but with a large project this consumes huge virtual
+   memory space and truly huge ones would not fit in the
+   userland address space on 32-bit platforms.  We now mmap huge
+   packfile in pieces to avoid this problem.
+
+
+* Shallow clones
+
+ - There is a partial support for 'shallow' repositories that
+   keeps only recent history.  A 'shallow clone' is created by
+   specifying how deep that truncated history should be
+   (e.g. "git clone --depth 5 git://some.where/repo.git").
+
+   Currently a shallow repository has number of limitations:
+
+   - Cloning and fetching _from_ a shallow clone are not
+     supported (nor tested -- so they might work by accident but
+     they are not expected to).
+
+   - Pushing from nor into a shallow clone are not expected to
+     work.
+
+   - Merging inside a shallow repository would work as long as a
+     merge base is found in the recent history, but otherwise it
+     will be like merging unrelated histories and may result in
+     huge conflicts.
+
+   but this would be more than adequate for people who want to
+   look at near the tip of a big project with a deep history and
+   send patches in e-mail format.
diff --git a/Documentation/RelNotes/1.5.1.1.txt b/Documentation/RelNotes/1.5.1.1.txt
new file mode 100644 (file)
index 0000000..9147121
--- /dev/null
@@ -0,0 +1,65 @@
+GIT v1.5.1.1 Release Notes
+==========================
+
+Fixes since v1.5.1
+------------------
+
+* Documentation updates
+
+  - The --left-right option of rev-list and friends is documented.
+
+  - The documentation for cvsimport has been majorly improved.
+
+  - "git-show-ref --exclude-existing" was documented.
+
+* Bugfixes
+
+  - The implementation of -p option in "git cvsexportcommit" had
+    the meaning of -C (context reduction) option wrong, and
+    loosened the context requirements when it was told to be
+    strict.
+
+  - "git cvsserver" did not behave like the real cvsserver when
+    client side removed a file from the working tree without
+    doing anything else on the path.  In such a case, it should
+    restore it from the checked out revision.
+
+  - "git fsck" issued an alarming error message on detached
+    HEAD.  It is not an error since at least 1.5.0.
+
+  - "git send-email" produced of References header of unbounded length;
+    fixed this with line-folding.
+
+  - "git archive" to download from remote site should not
+    require you to be in a git repository, but it incorrectly
+    did.
+
+  - "git apply" ignored -p<n> for "diff --git" formatted
+    patches.
+
+  - "git rerere" recorded a conflict that had one side empty
+    (the other side adds) incorrectly; this made merging in the
+    other direction fail to use previously recorded resolution.
+
+  - t4200 test was broken where "wc -l" pads its output with
+    spaces.
+
+  - "git branch -m old new" to rename branch did not work
+    without a configuration file in ".git/config".
+
+  - The sample hook for notification e-mail was misnamed.
+
+  - gitweb did not show type-changing patch correctly in the
+    blobdiff view.
+
+  - git-svn did not error out with incorrect command line options.
+
+  - git-svn fell into an infinite loop when insanely long commit
+    message was found.
+
+  - git-svn dcommit and rebase was confused by patches that were
+    merged from another branch that is managed by git-svn.
+
+  - git-svn used to get confused when globbing remote branch/tag
+    spec (e.g. "branches = proj/branches/*:refs/remotes/origin/*")
+    is used and there was a plain file that matched the glob.
diff --git a/Documentation/RelNotes/1.5.1.2.txt b/Documentation/RelNotes/1.5.1.2.txt
new file mode 100644 (file)
index 0000000..d884563
--- /dev/null
@@ -0,0 +1,50 @@
+GIT v1.5.1.2 Release Notes
+==========================
+
+Fixes since v1.5.1.1
+--------------------
+
+* Bugfixes
+
+  - "git clone" over http from a repository that has lost the
+    loose refs by running "git pack-refs" were broken (a code to
+    deal with this was added to "git fetch" in v1.5.0, but it
+    was missing from "git clone").
+
+  - "git diff a/ b/" incorrectly fell in "diff between two
+    filesystem objects" codepath, when the user most likely
+    wanted to limit the extent of output to two tracked
+    directories.
+
+  - git-quiltimport had the same bug as we fixed for
+    git-applymbox in v1.5.1.1 -- it gave an alarming "did not
+    have any patch" message (but did not actually fail and was
+    harmless).
+
+  - various git-svn fixes.
+
+  - Sample update hook incorrectly always refused requests to
+    delete branches through push.
+
+  - git-blame on a very long working tree path had buffer
+    overrun problem.
+
+  - git-apply did not like to be fed two patches in a row that created
+    and then modified the same file.
+
+  - git-svn was confused when a non-project was stored directly under
+    trunk/, branches/ and tags/.
+
+  - git-svn wants the Error.pm module that was at least as new
+    as what we ship as part of git; install ours in our private
+    installation location if the one on the system is older.
+
+  - An earlier update to command line integer parameter parser was
+    botched and made 'update-index --cacheinfo' completely useless.
+
+
+* Documentation updates
+
+  - Various documentation updates from J. Bruce Fields, Frank
+    Lichtenheld, Alex Riesen and others.  Andrew Ruder started a
+    war on undocumented options.
diff --git a/Documentation/RelNotes/1.5.1.3.txt b/Documentation/RelNotes/1.5.1.3.txt
new file mode 100644 (file)
index 0000000..876408b
--- /dev/null
@@ -0,0 +1,45 @@
+GIT v1.5.1.3 Release Notes
+==========================
+
+Fixes since v1.5.1.2
+--------------------
+
+* Bugfixes
+
+  - git-add tried to optimize by finding common leading
+    directories across its arguments but botched, causing very
+    confused behaviour.
+
+  - unofficial rpm.spec file shipped with git was letting
+    ETC_GITCONFIG set to /usr/etc/gitconfig.  Tweak the official
+    Makefile to make it harder for distro people to make the
+    same mistake, by setting the variable to /etc/gitconfig if
+    prefix is set to /usr.
+
+  - git-svn inconsistently stripped away username from the URL
+    only when svnsync_props was in use.
+
+  - git-svn got confused when handling symlinks on Mac OS.
+
+  - git-send-email was not quoting recipient names that have
+    period '.' in them.  Also it did not allow overriding
+    envelope sender, which made it impossible to send patches to
+    certain subscriber-only lists.
+
+  - built-in write_tree() routine had a sequence that renamed a
+    file that is still open, which some systems did not like.
+
+  - when memory is very tight, sliding mmap code to read
+    packfiles incorrectly closed the fd that was still being
+    used to read the pack.
+
+  - import-tars contributed front-end for fastimport was passing
+    wrong directory modes without checking.
+
+  - git-fastimport trusted its input too much and allowed to
+    create corrupt tree objects with entries without a name.
+
+  - git-fetch needlessly barfed when too long reflog action
+    description was given by the caller.
+
+Also contains various documentation updates.
diff --git a/Documentation/RelNotes/1.5.1.4.txt b/Documentation/RelNotes/1.5.1.4.txt
new file mode 100644 (file)
index 0000000..df2f66c
--- /dev/null
@@ -0,0 +1,30 @@
+GIT v1.5.1.4 Release Notes
+==========================
+
+Fixes since v1.5.1.3
+--------------------
+
+* Bugfixes
+
+  - "git-http-fetch" did not work around a bug in libcurl
+    earlier than 7.16 (curl_multi_remove_handle() was broken).
+
+  - "git cvsserver" handles a file that was once removed and
+    then added again correctly.
+
+  - import-tars script (in contrib/) handles GNU tar archives
+    that contain pathnames longer than 100 bytes (long-link
+    extension) correctly.
+
+  - xdelta test program did not build correctly.
+
+  - gitweb sometimes tried incorrectly to apply function to
+    decode utf8 twice, resulting in corrupt output.
+
+  - "git blame -C" mishandled text at the end of a group of
+    lines.
+
+  - "git log/rev-list --boundary" did not produce output
+    correctly without --left-right option.
+
+  - Many documentation updates.
diff --git a/Documentation/RelNotes/1.5.1.5.txt b/Documentation/RelNotes/1.5.1.5.txt
new file mode 100644 (file)
index 0000000..b0ab8eb
--- /dev/null
@@ -0,0 +1,42 @@
+GIT v1.5.1.5 Release Notes
+==========================
+
+Fixes since v1.5.1.4
+--------------------
+
+* Bugfixes
+
+  - git-send-email did not understand aliases file for mutt, which
+    allows leading whitespaces.
+
+  - git-format-patch emitted Content-Type and Content-Transfer-Encoding
+    headers for non ASCII contents, but failed to add MIME-Version.
+
+  - git-name-rev had a buffer overrun with a deep history.
+
+  - contributed script import-tars did not get the directory in
+    tar archives interpreted correctly.
+
+  - git-svn was reported to segfault for many people on list and
+    #git; hopefully this has been fixed.
+
+  - "git-svn clone" does not try to minimize the URL
+    (i.e. connect to higher level hierarchy) by default, as this
+    can prevent clone to fail if only part of the repository
+    (e.g. 'trunk') is open to public.
+
+  - "git checkout branch^0" did not detach the head when you are
+    already on 'branch'; backported the fix from the 'master'.
+
+  - "git-config section.var" did not correctly work when
+    existing configuration file had both [section] and [section "name"]
+    next to each other.
+
+  - "git clone ../other-directory" was fooled if the current
+    directory $PWD points at is a symbolic link.
+
+  - (build) tree_entry_extract() function was both static inline
+    and extern, which caused trouble compiling with Forte12
+    compilers on Sun.
+
+  - Many many documentation fixes and updates.
diff --git a/Documentation/RelNotes/1.5.1.6.txt b/Documentation/RelNotes/1.5.1.6.txt
new file mode 100644 (file)
index 0000000..55f3ac1
--- /dev/null
@@ -0,0 +1,45 @@
+GIT v1.5.1.6 Release Notes
+==========================
+
+Fixes since v1.5.1.4
+--------------------
+
+* Bugfixes
+
+  - git-send-email did not understand aliases file for mutt, which
+    allows leading whitespaces.
+
+  - git-format-patch emitted Content-Type and Content-Transfer-Encoding
+    headers for non ASCII contents, but failed to add MIME-Version.
+
+  - git-name-rev had a buffer overrun with a deep history.
+
+  - contributed script import-tars did not get the directory in
+    tar archives interpreted correctly.
+
+  - git-svn was reported to segfault for many people on list and
+    #git; hopefully this has been fixed.
+
+  - git-svn also had a bug to crash svnserve by sending a bad
+    sequence of requests.
+
+  - "git-svn clone" does not try to minimize the URL
+    (i.e. connect to higher level hierarchy) by default, as this
+    can prevent clone to fail if only part of the repository
+    (e.g. 'trunk') is open to public.
+
+  - "git checkout branch^0" did not detach the head when you are
+    already on 'branch'; backported the fix from the 'master'.
+
+  - "git-config section.var" did not correctly work when
+    existing configuration file had both [section] and [section "name"]
+    next to each other.
+
+  - "git clone ../other-directory" was fooled if the current
+    directory $PWD points at is a symbolic link.
+
+  - (build) tree_entry_extract() function was both static inline
+    and extern, which caused trouble compiling with Forte12
+    compilers on Sun.
+
+  - Many many documentation fixes and updates.
diff --git a/Documentation/RelNotes/1.5.1.txt b/Documentation/RelNotes/1.5.1.txt
new file mode 100644 (file)
index 0000000..daed367
--- /dev/null
@@ -0,0 +1,371 @@
+GIT v1.5.1 Release Notes
+========================
+
+Updates since v1.5.0
+--------------------
+
+* Deprecated commands and options.
+
+  - git-diff-stages and git-resolve have been removed.
+
+* New commands and options.
+
+  - "git log" and friends take --reverse, which instructs them
+    to give their output in the order opposite from their usual.
+    They typically output from new to old, but with this option
+    their output would read from old to new.  "git shortlog"
+    usually lists older commits first, but with this option,
+    they are shown from new to old.
+
+  - "git log --pretty=format:<string>" to allow more flexible
+    custom log output.
+
+  - "git diff" learned --ignore-space-at-eol.  This is a weaker
+    form of --ignore-space-change.
+
+  - "git diff --no-index pathA pathB" can be used as diff
+    replacement with git specific enhancements.
+
+  - "git diff --no-index" can read from '-' (standard input).
+
+  - "git diff" also learned --exit-code to exit with non-zero
+    status when it found differences.  In the future we might
+    want to make this the default but that would be a rather big
+    backward incompatible change; it will stay as an option for
+    now.
+
+  - "git diff --quiet" is --exit-code with output turned off,
+    meant for scripted use to quickly determine if there is any
+    tree-level difference.
+
+  - Textual patch generation with "git diff" without -w/-b
+    option has been significantly optimized.  "git blame" got
+    faster because of the same change.
+
+  - "git log" and "git rev-list" has been optimized
+    significantly when they are used with pathspecs.
+
+  - "git branch --track" can be used to set up configuration
+    variables to help it easier to base your work on branches
+    you track from a remote site.
+
+  - "git format-patch --attach" now emits attachments.  Use
+    --inline to get an inlined multipart/mixed.
+
+  - "git name-rev" learned --refs=<pattern>, to limit the tags
+    used for naming the given revisions only to the ones
+    matching the given pattern.
+
+  - "git remote update" is to run "git fetch" for defined remotes
+    to update tracking branches.
+
+  - "git cvsimport" can now take '-d' to talk with a CVS
+    repository different from what are recorded in CVS/Root
+    (overriding it with environment CVSROOT does not work).
+
+  - "git bundle" can help sneaker-netting your changes between
+    repositories.
+
+  - "git mergetool" can help 3-way file-level conflict
+    resolution with your favorite graphical merge tools.
+
+  - A new configuration "core.symlinks" can be used to disable
+    symlinks on filesystems that do not support them; they are
+    checked out as regular files instead.
+
+  - You can name a commit object with its first line of the
+    message.  The syntax to use is ':/message text'.  E.g.
+
+    $ git show ":/object name: introduce ':/<oneline prefix>' notation"
+
+    means the same thing as:
+
+    $ git show 28a4d940443806412effa246ecc7768a21553ec7
+
+  - "git bisect" learned a new command "run" that takes a script
+    to run after each revision is checked out to determine if it
+    is good or bad, to automate the bisection process.
+
+  - "git log" family learned a new traversal option --first-parent,
+    which does what the name suggests.
+
+
+* Updated behavior of existing commands.
+
+  - "git-merge-recursive" used to barf when there are more than
+    one common ancestors for the merge, and merging them had a
+    rename/rename conflict.  This has been fixed.
+
+  - "git fsck" does not barf on corrupt loose objects.
+
+  - "git rm" does not remove newly added files without -f.
+
+  - "git archimport" allows remapping when coming up with git
+    branch names from arch names.
+
+  - git-svn got almost a rewrite.
+
+  - core.autocrlf configuration, when set to 'true', makes git
+    to convert CRLF at the end of lines in text files to LF when
+    reading from the filesystem, and convert in reverse when
+    writing to the filesystem.  The variable can be set to
+    'input', in which case the conversion happens only while
+    reading from the filesystem but files are written out with
+    LF at the end of lines.  Currently, which paths to consider
+    'text' (i.e. be subjected to the autocrlf mechanism) is
+    decided purely based on the contents, but the plan is to
+    allow users to explicitly override this heuristic based on
+    paths.
+
+  - The behavior of 'git-apply', when run in a subdirectory,
+    without --index nor --cached were inconsistent with that of
+    the command with these options.  This was fixed to match the
+    behavior with --index.  A patch that is meant to be applied
+    with -p1 from the toplevel of the project tree can be
+    applied with any custom -p<n> option.  A patch that is not
+    relative to the toplevel needs to be applied with -p<n>
+    option with or without --index (or --cached).
+
+  - "git diff" outputs a trailing HT when pathnames have embedded
+    SP on +++/--- header lines, in order to help "GNU patch" to
+    parse its output.  "git apply" was already updated to accept
+    this modified output format since ce74618d (Sep 22, 2006).
+
+  - "git cvsserver" runs hooks/update and honors its exit status.
+
+  - "git cvsserver" can be told to send everything with -kb.
+
+  - "git diff --check" also honors the --color output option.
+
+  - "git name-rev" used to stress the fact that a ref is a tag too
+    much, by saying something like "v1.2.3^0~22".  It now says
+    "v1.2.3~22" in such a case (it still says "v1.2.3^0" if it does
+    not talk about an ancestor of the commit that is tagged, which
+    makes sense).
+
+  - "git rev-list --boundary" now shows boundary markers for the
+    commits omitted by --max-age and --max-count condition.
+
+  - The configuration mechanism now reads $(prefix)/etc/gitconfig.
+
+  - "git apply --verbose" shows what preimage lines were wanted
+    when it couldn't find them.
+
+  - "git status" in a read-only repository got a bit saner.
+
+  - "git fetch" (hence "git clone" and "git pull") are less
+    noisy when the output does not go to tty.
+
+  - "git fetch" between repositories with many refs were slow
+    even when there are not many changes that needed
+    transferring.  This has been sped up by partially rewriting
+    the heaviest parts in C.
+
+  - "git mailinfo" which splits an e-mail into a patch and the
+    meta-information was rewritten, thanks to Don Zickus.  It
+    handles nested multipart better.  The command was broken for
+    a brief period on 'master' branch since 1.5.0 but the
+    breakage is fixed now.
+
+  - send-email learned configurable bcc and chain-reply-to.
+
+  - "git remote show $remote" also talks about branches that
+    would be pushed if you run "git push remote".
+
+  - Using objects from packs is now seriously optimized by clever
+    use of a cache.  This should be most noticeable in git-log
+    family of commands that involve reading many tree objects.
+    In addition, traversing revisions while filtering changes
+    with pathspecs is made faster by terminating the comparison
+    between the trees as early as possible.
+
+
+* Hooks
+
+  - The part to send out notification e-mails was removed from
+    the sample update hook, as it was not an appropriate place
+    to do so.  The proper place to do this is the new post-receive
+    hook.  An example hook has been added to contrib/hooks/.
+
+
+* Others
+
+  - git-revert, git-gc and git-cherry-pick are now built-ins.
+
+Fixes since v1.5.0
+------------------
+
+These are all in v1.5.0.x series.
+
+* Documentation updates
+
+  - Clarifications and corrections to 1.5.0 release notes.
+
+  - The main documentation did not link to git-remote documentation.
+
+  - Clarified introductory text of git-rebase documentation.
+
+  - Converted remaining mentions of update-index on Porcelain
+    documents to git-add/git-rm.
+
+  - Some i18n.* configuration variables were incorrectly
+    described as core.*; fixed.
+
+  - added and clarified core.bare, core.legacyheaders configurations.
+
+  - updated "git-clone --depth" documentation.
+
+  - user-manual updates.
+
+  - Options to 'git remote add' were described insufficiently.
+
+  - Configuration format.suffix was not documented.
+
+  - Other formatting and spelling fixes.
+
+  - user-manual has better cross references.
+
+  - gitweb installation/deployment procedure is now documented.
+
+
+* Bugfixes
+
+  - git-upload-pack closes unused pipe ends; earlier this caused
+    many zombies to hang around.
+
+  - git-rerere was recording the contents of earlier hunks
+    duplicated in later hunks.  This prevented resolving the same
+    conflict when performing the same merge the other way around.
+
+  - git-add and git-update-index on a filesystem on which
+    executable bits are unreliable incorrectly reused st_mode
+    bits even when the path changed between symlink and regular
+    file.
+
+  - git-daemon marks the listening sockets with FD_CLOEXEC so
+    that it won't be leaked into the children.
+
+  - segfault from git-blame when the mandatory pathname
+    parameter was missing was fixed; usage() message is given
+    instead.
+
+  - git-rev-list did not read $GIT_DIR/config file, which means
+    that did not honor i18n.logoutputencoding correctly.
+
+  - Automated merge conflict handling when changes to symbolic
+    links conflicted were completely broken.  The merge-resolve
+    strategy created a regular file with conflict markers in it
+    in place of the symbolic link.  The default strategy,
+    merge-recursive was even more broken.  It removed the path
+    that was pointed at by the symbolic link.  Both of these
+    problems have been fixed.
+
+  - 'git diff maint master next' did not correctly give combined
+    diff across three trees.
+
+  - 'git fast-import' portability fix for Solaris.
+
+  - 'git show-ref --verify' without arguments did not error out
+    but segfaulted.
+
+  - 'git diff :tracked-file `pwd`/an-untracked-file' gave an extra
+    slashes after a/ and b/.
+
+  - 'git format-patch' produced too long filenames if the commit
+    message had too long line at the beginning.
+
+  - Running 'make all' and then without changing anything
+    running 'make install' still rebuilt some files.  This
+    was inconvenient when building as yourself and then
+    installing as root (especially problematic when the source
+    directory is on NFS and root is mapped to nobody).
+
+  - 'git-rerere' failed to deal with two unconflicted paths that
+    sorted next to each other.
+
+  - 'git-rerere' attempted to open(2) a symlink and failed if
+    there was a conflict.  Since a conflicting change to a
+    symlink would not benefit from rerere anyway, the command
+    now ignores conflicting changes to symlinks.
+
+  - 'git-repack' did not like to pass more than 64 arguments
+    internally to underlying 'rev-list' logic, which made it
+    impossible to repack after accumulating many (small) packs
+    in the repository.
+
+  - 'git-diff' to review the combined diff during a conflicted
+    merge were not reading the working tree version correctly
+    when changes to a symbolic link conflicted.  It should have
+    read the data using readlink(2) but read from the regular
+    file the symbolic link pointed at.
+
+  - 'git-remote' did not like period in a remote's name.
+
+  - 'git.el' honors the commit coding system from the configuration.
+
+  - 'blameview' in contrib/ correctly digs deeper when a line is
+    clicked.
+
+  - 'http-push' correctly makes sure the remote side has leading
+    path.  Earlier it started in the middle of the path, and
+    incorrectly.
+
+  - 'git-merge' did not exit with non-zero status when the
+    working tree was dirty and cannot fast forward.  It does
+    now.
+
+  - 'cvsexportcommit' does not lose yet-to-be-used message file.
+
+  - int-vs-size_t typefix when running combined diff on files
+    over 2GB long.
+
+  - 'git apply --whitespace=strip' should not touch unmodified
+    lines.
+
+  - 'git-mailinfo' choke when a logical header line was too long.
+
+  - 'git show A..B' did not error out.  Negative ref ("not A" in
+    this example) does not make sense for the purpose of the
+    command, so now it errors out.
+
+  - 'git fmt-merge-msg --file' without file parameter did not
+    correctly error out.
+
+  - 'git archimport' barfed upon encountering a commit without
+    summary.
+
+  - 'git index-pack' did not protect itself from getting a short
+    read out of pread(2).
+
+  - 'git http-push' had a few buffer overruns.
+
+  - Build dependency fixes to rebuild fetch.o when other headers
+    change.
+
+  - git.el does not add duplicate sign-off lines.
+
+  - git-commit shows the full stat of the resulting commit, not
+    just about the files in the current directory, when run from
+    a subdirectory.
+
+  - "git-checkout -m '@{8 hours ago}'" had a funny failure from
+    eval; fixed.
+
+  - git-merge (hence git-pull) did not refuse fast-forwarding
+    when the working tree had local changes that would have
+    conflicted with it.
+
+  - a handful small fixes to gitweb.
+
+  - build procedure for user-manual is fixed not to require locally
+    installed stylesheets.
+
+  - "git commit $paths" on paths whose earlier contents were
+    already updated in the index were failing out.
+
+
+* Tweaks
+
+  - sliding mmap() inefficiently mmaped the same region of a
+    packfile with an access pattern that used objects in the
+    reverse order.  This has been made more efficient.
diff --git a/Documentation/RelNotes/1.5.2.1.txt b/Documentation/RelNotes/1.5.2.1.txt
new file mode 100644 (file)
index 0000000..ebf20e2
--- /dev/null
@@ -0,0 +1,53 @@
+GIT v1.5.2.1 Release Notes
+==========================
+
+Fixes since v1.5.2
+------------------
+
+* Bugfixes
+
+  - Temporary files that are used when invoking external diff
+    programs did not tolerate a long TMPDIR.
+
+  - git-daemon did not notice when it could not write into its
+    pid file.
+
+  - git-status did not honor core.excludesFile configuration like
+    git-add did.
+
+  - git-annotate did not work from a subdirectory while
+    git-blame did.
+
+  - git-cvsserver should have disabled access to a repository
+    with "gitcvs.pserver.enabled = false" set even when
+    "gitcvs.enabled = true" was set at the same time.  It
+    didn't.
+
+  - git-cvsimport did not work correctly in a repository with
+    its branch heads were packed with pack-refs.
+
+  - ident unexpansion to squash "$Id: xxx $" that is in the
+    repository copy removed incorrect number of bytes.
+
+  - git-svn misbehaved when the subversion repository did not
+    provide MD5 checksums for files.
+
+  - git rebase (and git am) misbehaved on commits that have '\n'
+    (literally backslash and en, not a linefeed) in the title.
+
+  - code to decode base85 used in binary patches had one error
+    return codepath wrong.
+
+  - RFC2047 Q encoding output by git-format-patch used '_' for a
+    space, which is not understood by some programs.  It uses =20
+    which is safer.
+
+  - git-fastimport --import-marks was broken; fixed.
+
+  - A lot of documentation updates, clarifications and fixes.
+
+--
+exec >/var/tmp/1
+O=v1.5.2-65-g996e2d6
+echo O=`git describe refs/heads/maint`
+git shortlog --no-merges $O..refs/heads/maint
diff --git a/Documentation/RelNotes/1.5.2.2.txt b/Documentation/RelNotes/1.5.2.2.txt
new file mode 100644 (file)
index 0000000..7bfa341
--- /dev/null
@@ -0,0 +1,61 @@
+GIT v1.5.2.2 Release Notes
+==========================
+
+Fixes since v1.5.2.1
+--------------------
+
+* Usability fix
+
+  - git-gui is shipped with its updated blame interface.  It is
+    rumored that the older one was not just unusable but was
+    active health hazard, but this one is actually pretty.
+    Please see for yourself.
+
+* Bugfixes
+
+  - "git checkout fubar" was utterly confused when there is a
+    branch fubar and a tag fubar at the same time.  It correctly
+    checks out the branch fubar now.
+
+  - "git clone /path/foo" to clone a local /path/foo.git
+    repository left an incorrect configuration.
+
+  - "git send-email" correctly unquotes RFC 2047 quoted names in
+    the patch-email before using their values.
+
+  - We did not accept number of seconds since epoch older than
+    year 2000 as a valid timestamp.  We now interpret positive
+    integers more than 8 digits as such, which allows us to
+    express timestamps more recent than March 1973.
+
+  - git-cvsimport did not work when you have GIT_DIR to point
+    your repository at a nonstandard location.
+
+  - Some systems (notably, Solaris) lack hstrerror() to make
+    h_errno human readable; prepare a replacement
+    implementation.
+
+  - .gitignore file listed git-core.spec but what we generate is
+    git.spec, and nobody noticed for a long time.
+
+  - "git-merge-recursive" does not try to run file level merge
+    on binary files.
+
+  - "git-branch --track" did not create tracking configuration
+    correctly when the branch name had slash in it.
+
+  - The email address of the user specified with user.email
+    configuration was overridden by EMAIL environment variable.
+
+  - The tree parser did not warn about tree entries with
+    nonsense file modes, and assumed they must be blobs.
+
+  - "git log -z" without any other request to generate diff still
+    invoked the diff machinery, wasting cycles.
+
+* Documentation
+
+  - Many updates to fix stale or missing documentation.
+
+  - Although our documentation was primarily meant to be formatted
+    with AsciiDoc7, formatting with AsciiDoc8 is supported better.
diff --git a/Documentation/RelNotes/1.5.2.3.txt b/Documentation/RelNotes/1.5.2.3.txt
new file mode 100644 (file)
index 0000000..addb229
--- /dev/null
@@ -0,0 +1,27 @@
+GIT v1.5.2.3 Release Notes
+==========================
+
+Fixes since v1.5.2.2
+--------------------
+
+ * Bugfixes
+
+   - Version 2 pack index format was introduced in version 1.5.2
+     to support pack files that has offset that cannot be
+     represented in 32-bit.  The runtime code to validate such
+     an index mishandled such an index for an empty pack.
+
+   - Commit walkers (most notably, fetch over http protocol)
+     tried to traverse commit objects contained in trees (aka
+     subproject); they shouldn't.
+
+   - A build option NO_R_TO_GCC_LINKER was not explained in Makefile
+     comment correctly.
+
+ * Documentation Fixes and Updates
+
+   - git-config --regexp was not documented properly.
+
+   - git-repack -a was not documented properly.
+
+   - git-remote -n was not documented properly.
diff --git a/Documentation/RelNotes/1.5.2.4.txt b/Documentation/RelNotes/1.5.2.4.txt
new file mode 100644 (file)
index 0000000..75cff47
--- /dev/null
@@ -0,0 +1,28 @@
+GIT v1.5.2.4 Release Notes
+==========================
+
+Fixes since v1.5.2.3
+--------------------
+
+ * Bugfixes
+
+   - "git-gui" bugfixes, including a handful fixes to run it
+     better on Cygwin/MSYS.
+
+   - "git checkout" failed to switch back and forth between
+     branches, one of which has "frotz -> xyzzy" symlink and
+     file "xyzzy/filfre", while the other one has a file
+     "frotz/filfre".
+
+   - "git prune" used to segfault upon seeing a commit that is
+     referred to by a tree object (aka "subproject").
+
+   - "git diff --name-status --no-index" mishandled an added file.
+
+   - "git apply --reverse --whitespace=warn" still complained
+     about whitespaces that a forward application would have
+     introduced.
+
+ * Documentation Fixes and Updates
+
+   - A handful documentation updates.
diff --git a/Documentation/RelNotes/1.5.2.5.txt b/Documentation/RelNotes/1.5.2.5.txt
new file mode 100644 (file)
index 0000000..e8281c7
--- /dev/null
@@ -0,0 +1,30 @@
+GIT v1.5.2.5 Release Notes
+==========================
+
+Fixes since v1.5.2.4
+--------------------
+
+ * Bugfixes
+
+   - "git add -u" had a serious data corruption problem in one
+     special case (when the changes to a subdirectory's files
+     consist only deletion of files).
+
+   - "git add -u <path>" did not work from a subdirectory.
+
+   - "git apply" left an empty directory after all its files are
+     renamed away.
+
+   - "git $anycmd foo/bar", when there is a file 'foo' in the
+     working tree, complained that "git $anycmd foo/bar --" form
+     should be used to disambiguate between revs and files,
+     which was completely bogus.
+
+   - "git checkout-index" and other commands that checks out
+     files to the work tree tried unlink(2) on directories,
+     which is a sane thing to do on sane systems, but not on
+     Solaris when you are root.
+
+ * Documentation Fixes and Updates
+
+   - A handful documentation fixes.
diff --git a/Documentation/RelNotes/1.5.2.txt b/Documentation/RelNotes/1.5.2.txt
new file mode 100644 (file)
index 0000000..e8328d0
--- /dev/null
@@ -0,0 +1,197 @@
+GIT v1.5.2 Release Notes
+========================
+
+Updates since v1.5.1
+--------------------
+
+* Plumbing level superproject support.
+
+  You can include a subdirectory that has an independent git
+  repository in your index and tree objects of your project
+  ("superproject").  This plumbing (i.e. "core") level
+  superproject support explicitly excludes recursive behaviour.
+
+  The "subproject" entries in the index and trees of a superproject
+  are incompatible with older versions of git.  Experimenting with
+  the plumbing level support is encouraged, but be warned that
+  unless everybody in your project updates to this release or
+  later, using this feature would make your project
+  inaccessible by people with older versions of git.
+
+* Plumbing level gitattributes support.
+
+  The gitattributes mechanism allows you to add 'attributes' to
+  paths in your project, and affect the way certain git
+  operations work.  Currently you can influence if a path is
+  considered a binary or text (the former would be treated by
+  'git diff' not to produce textual output; the latter can go
+  through the line endings conversion process in repositories
+  with core.autocrlf set), expand and unexpand '$Id$' keyword
+  with blob object name, specify a custom 3-way merge driver,
+  and specify a custom diff driver.  You can also apply
+  arbitrary filter to contents on check-in/check-out codepath
+  but this feature is an extremely sharp-edged razor and needs
+  to be handled with caution (do not use it unless you
+  understand the earlier mailing list discussion on keyword
+  expansion).  These conversions apply when checking files in
+  or out, and exporting via git-archive.
+
+* The packfile format now optionally supports 64-bit index.
+
+  This release supports the "version 2" format of the .idx
+  file.  This is automatically enabled when a huge packfile
+  needs more than 32-bit to express offsets of objects in the
+  pack.
+
+* Comes with an updated git-gui 0.7.1
+
+* Updated gitweb:
+
+  - can show combined diff for merges;
+  - uses font size of user's preference, not hardcoded in pixels;
+  - can now 'grep';
+
+* New commands and options.
+
+  - "git bisect start" can optionally take a single bad commit and
+    zero or more good commits on the command line.
+
+  - "git shortlog" can optionally be told to wrap its output.
+
+  - "subtree" merge strategy allows another project to be merged in as
+    your subdirectory.
+
+  - "git format-patch" learned a new --subject-prefix=<string>
+    option, to override the built-in "[PATCH]".
+
+  - "git add -u" is a quick way to do the first stage of "git
+    commit -a" (i.e. update the index to match the working
+    tree); it obviously does not make a commit.
+
+  - "git clean" honors a new configuration, "clean.requireforce".  When
+    set to true, this makes "git clean" a no-op, preventing you
+    from losing files by typing "git clean" when you meant to
+    say "make clean".  You can still say "git clean -f" to
+    override this.
+
+  - "git log" family of commands learned --date={local,relative,default}
+    option.  --date=relative is synonym to the --relative-date.
+    --date=local gives the timestamp in local timezone.
+
+* Updated behavior of existing commands.
+
+  - When $GIT_COMMITTER_EMAIL or $GIT_AUTHOR_EMAIL is not set
+    but $EMAIL is set, the latter is used as a substitute.
+
+  - "git diff --stat" shows size of preimage and postimage blobs
+    for binary contents.  Earlier it only said "Bin".
+
+  - "git lost-found" shows stuff that are unreachable except
+    from reflogs.
+
+  - "git checkout branch^0" now detaches HEAD at the tip commit
+    on the named branch, instead of just switching to the
+    branch (use "git checkout branch" to switch to the branch,
+    as before).
+
+  - "git bisect next" can be used after giving only a bad commit
+    without giving a good one (this starts bisection half-way to
+    the root commit).  We used to refuse to operate without a
+    good and a bad commit.
+
+  - "git push", when pushing into more than one repository, does
+    not stop at the first error.
+
+  - "git archive" does not insist you to give --format parameter
+    anymore; it defaults to "tar".
+
+  - "git cvsserver" can use backends other than sqlite.
+
+  - "gitview" (in contrib/ section) learned to better support
+    "git-annotate".
+
+  - "git diff $commit1:$path2 $commit2:$path2" can now report
+    mode changes between the two blobs.
+
+  - Local "git fetch" from a repository whose object store is
+    one of the alternates (e.g. fetching from the origin in a
+    repository created with "git clone -l -s") avoids
+    downloading objects unnecessarily.
+
+  - "git blame" uses .mailmap to canonicalize the author name
+    just like "git shortlog" does.
+
+  - "git pack-objects" pays attention to pack.depth
+    configuration variable.
+
+  - "git cherry-pick" and "git revert" does not use .msg file in
+    the working tree to prepare commit message; instead it uses
+    $GIT_DIR/MERGE_MSG as other commands do.
+
+* Builds
+
+  - git-p4import has never been installed; now there is an
+    installation option to do so.
+
+  - gitk and git-gui can be configured out.
+
+  - Generated documentation pages automatically get version
+    information from GIT_VERSION.
+
+  - Parallel build with "make -j" descending into subdirectory
+    was fixed.
+
+* Performance Tweaks
+
+  - Optimized "git-rev-list --bisect" (hence "git-bisect").
+
+  - Optimized "git-add $path" in a large directory, most of
+    whose contents are ignored.
+
+  - Optimized "git-diff-tree" for reduced memory footprint.
+
+  - The recursive merge strategy updated a worktree file that
+    was changed identically in two branches, when one of them
+    renamed it.  We do not do that when there is no rename, so
+    match that behaviour.  This avoids excessive rebuilds.
+
+  - The default pack depth has been increased to 50, as the
+    recent addition of delta_base_cache makes deeper delta chains
+    much less expensive to access.  Depending on the project, it was
+    reported that this reduces the resulting pack file by 10%
+    or so.
+
+
+Fixes since v1.5.1
+------------------
+
+All of the fixes in v1.5.1 maintenance series are included in
+this release, unless otherwise noted.
+
+* Bugfixes
+
+  - Switching branches with "git checkout" refused to work when
+    a path changes from a file to a directory between the
+    current branch and the new branch, in order not to lose
+    possible local changes in the directory that is being turned
+    into a file with the switch.  We now allow such a branch
+    switch after making sure that there is no locally modified
+    file nor un-ignored file in the directory.  This has not
+    been backported to 1.5.1.x series, as it is rather an
+    intrusive change.
+
+  - Merging branches that have a file in one and a directory in
+    another at the same path used to get quite confused.  We
+    handle such a case a bit more carefully, even though that is
+    still left as a conflict for the user to sort out.  This
+    will not be backported to 1.5.1.x series, as it is rather an
+    intrusive change.
+
+  - git-fetch had trouble with a remote with insanely large number
+    of refs.
+
+  - "git clean -d -X" now does not remove non-excluded directories.
+
+  - rebasing (without -m) a series that changes a symlink to a directory
+    in the middle of a path confused git-apply greatly and refused to
+    operate.
diff --git a/Documentation/RelNotes/1.5.3.1.txt b/Documentation/RelNotes/1.5.3.1.txt
new file mode 100644 (file)
index 0000000..7ff546c
--- /dev/null
@@ -0,0 +1,10 @@
+GIT v1.5.3.1 Release Notes
+==========================
+
+Fixes since v1.5.3
+------------------
+
+This is solely to fix the generated RPM's dependencies.  We used
+to have git-p4 package but we do not anymore.  As suggested on
+the mailing list, this release makes git-core "Obsolete" git-p4,
+so that yum update would not complain.
diff --git a/Documentation/RelNotes/1.5.3.2.txt b/Documentation/RelNotes/1.5.3.2.txt
new file mode 100644 (file)
index 0000000..4bbde3c
--- /dev/null
@@ -0,0 +1,58 @@
+GIT v1.5.3.2 Release Notes
+==========================
+
+Fixes since v1.5.3.1
+--------------------
+
+ * git-push sent thin packs by default, which was not good for
+   the public distribution server (no point in saving transfer
+   while pushing; no point in making the resulting pack less
+   optimum).
+
+ * git-svn sometimes terminated with "Malformed network data" when
+   talking over svn:// protocol.
+
+ * git-send-email re-issued the same message-id about 10% of the
+   time if you fired off 30 messages within a single second.
+
+ * git-stash was not terminating the log message of commits it
+   internally creates with LF.
+
+ * git-apply failed to check the size of the patch hunk when its
+   beginning part matched the remainder of the preimage exactly,
+   even though the preimage recorded in the hunk was much larger
+   (therefore the patch should not have applied), leading to a
+   segfault.
+
+ * "git rm foo && git commit foo" complained that 'foo' needs to
+   be added first, instead of committing the removal, which was a
+   nonsense.
+
+ * git grep -c said "/dev/null: 0".
+
+ * git-add -u failed to recognize a blob whose type changed
+   between the index and the work tree.
+
+ * The limit to rename detection has been tightened a lot to
+   reduce performance problems with a huge change.
+
+ * cvsimport and svnimport barfed when the input tried to move
+   a tag.
+
+ * "git apply -pN" did not chop the right number of directories.
+
+ * "git svnimport" did not like SVN tags with funny characters in them.
+
+ * git-gui 0.8.3, with assorted fixes, including:
+
+   - font-chooser on X11 was unusable with large number of fonts;
+   - a diff that contained a deleted symlink made it barf;
+   - an untracked symbolic link to a directory made it fart;
+   - a file with % in its name made it vomit;
+
+
+Documentation updates
+---------------------
+
+User manual has been somewhat restructured.  I think the new
+organization is much easier to read.
diff --git a/Documentation/RelNotes/1.5.3.3.txt b/Documentation/RelNotes/1.5.3.3.txt
new file mode 100644 (file)
index 0000000..d213846
--- /dev/null
@@ -0,0 +1,31 @@
+GIT v1.5.3.3 Release Notes
+==========================
+
+Fixes since v1.5.3.2
+--------------------
+
+ * git-quiltimport did not like it when a patch described in the
+   series file does not exist.
+
+ * p4 importer missed executable bit in some cases.
+
+ * The default shell on some FreeBSD did not execute the
+   argument parsing code correctly and made git unusable.
+
+ * git-svn incorrectly spawned pager even when the user
+   explicitly asked not to.
+
+ * sample post-receive hook overquoted the envelope sender
+   value.
+
+ * git-am got confused when the patch contained a change that is
+   only about type and not contents.
+
+ * git-mergetool did not show our and their version of the
+   conflicted file when started from a subdirectory of the
+   project.
+
+ * git-mergetool did not pass correct options when invoking diff3.
+
+ * git-log sometimes invoked underlying "diff" machinery
+   unnecessarily.
diff --git a/Documentation/RelNotes/1.5.3.4.txt b/Documentation/RelNotes/1.5.3.4.txt
new file mode 100644 (file)
index 0000000..b04b3a4
--- /dev/null
@@ -0,0 +1,35 @@
+GIT v1.5.3.4 Release Notes
+==========================
+
+Fixes since v1.5.3.3
+--------------------
+
+ * Change to "git-ls-files" in v1.5.3.3 that was introduced to support
+   partial commit of removal better had a segfaulting bug, which was
+   diagnosed and fixed by Keith and Carl.
+
+ * Performance improvements for rename detection has been backported
+   from the 'master' branch.
+
+ * "git-for-each-ref --format='%(numparent)'" was not working
+   correctly at all, and --format='%(parent)' was not working for
+   merge commits.
+
+ * Sample "post-receive-hook" incorrectly sent out push
+   notification e-mails marked as "From: " the committer of the
+   commit that happened to be at the tip of the branch that was
+   pushed, not from the person who pushed.
+
+ * "git-remote" did not exit non-zero status upon error.
+
+ * "git-add -i" did not respond very well to EOF from tty nor
+   bogus input.
+
+ * "git-rebase -i" squash subcommand incorrectly made the
+   author of later commit the author of resulting commit,
+   instead of taking from the first one in the squashed series.
+
+ * "git-stash apply --index" was not documented.
+
+ * autoconfiguration learned that "ar" command is found as "gas" on
+   some systems.
diff --git a/Documentation/RelNotes/1.5.3.5.txt b/Documentation/RelNotes/1.5.3.5.txt
new file mode 100644 (file)
index 0000000..7ff1d5d
--- /dev/null
@@ -0,0 +1,94 @@
+GIT v1.5.3.5 Release Notes
+==========================
+
+Fixes since v1.5.3.4
+--------------------
+
+ * Comes with git-gui 0.8.4.
+
+ * "git-config" silently ignored options after --list; now it will
+   error out with a usage message.
+
+ * "git-config --file" failed if the argument used a relative path
+   as it changed directories before opening the file.
+
+ * "git-config --file" now displays a proper error message if it
+   cannot read the file specified on the command line.
+
+ * "git-config", "git-diff", "git-apply" failed if run from a
+   subdirectory with relative GIT_DIR and GIT_WORK_TREE set.
+
+ * "git-blame" crashed if run during a merge conflict.
+
+ * "git-add -i" did not handle single line hunks correctly.
+
+ * "git-rebase -i" and "git-stash apply" failed if external diff
+   drivers were used for one or more files in a commit.  They now
+   avoid calling the external diff drivers.
+
+ * "git-log --follow" did not work unless diff generation (e.g. -p)
+   was also requested.
+
+ * "git-log --follow -B" did not work at all.  Fixed.
+
+ * "git-log -M -B" did not correctly handle cases of very large files
+   being renamed and replaced by very small files in the same commit.
+
+ * "git-log" printed extra newlines between commits when a diff
+   was generated internally (e.g. -S or --follow) but not displayed.
+
+ * "git-push" error message is more helpful when pushing to a
+   repository with no matching refs and none specified.
+
+ * "git-push" now respects + (force push) on wildcard refspecs,
+   matching the behavior of git-fetch.
+
+ * "git-filter-branch" now updates the working directory when it
+   has finished filtering the current branch.
+
+ * "git-instaweb" no longer fails on Mac OS X.
+
+ * "git-cvsexportcommit" didn't always create new parent directories
+   before trying to create new child directories.  Fixed.
+
+ * "git-fetch" printed a scary (but bogus) error message while
+   fetching a tag that pointed to a tree or blob.  The error did
+   not impact correctness, only user perception.  The bogus error
+   is no longer printed.
+
+ * "git-ls-files --ignored" did not properly descend into non-ignored
+   directories that themselves contained ignored files if d_type
+   was not supported by the filesystem.  This bug impacted systems
+   such as AFS.  Fixed.
+
+ * Git segfaulted when reading an invalid .gitattributes file.  Fixed.
+
+ * post-receive-email example hook was fixed for non-fast-forward
+   updates.
+
+ * Documentation updates for supported (but previously undocumented)
+   options of "git-archive" and "git-reflog".
+
+ * "make clean" no longer deletes the configure script that ships
+   with the git tarball, making multiple architecture builds easier.
+
+ * "git-remote show origin" spewed a warning message from Perl
+   when no remote is defined for the current branch via
+   branch.<name>.remote configuration settings.
+
+ * Building with NO_PERL_MAKEMAKER excessively rebuilt contents
+   of perl/ subdirectory by rewriting perl.mak.
+
+ * http.sslVerify configuration settings were not used in scripted
+   Porcelains.
+
+ * "git-add" leaked a bit of memory while scanning for files to add.
+
+ * A few workarounds to squelch false warnings from recent gcc have
+   been added.
+
+ * "git-send-pack $remote frotz" segfaulted when there is nothing
+   named 'frotz' on the local end.
+
+ * "git-rebase --interactive" did not handle its "--strategy" option
+   properly.
diff --git a/Documentation/RelNotes/1.5.3.6.txt b/Documentation/RelNotes/1.5.3.6.txt
new file mode 100644 (file)
index 0000000..069a2b2
--- /dev/null
@@ -0,0 +1,48 @@
+GIT v1.5.3.6 Release Notes
+==========================
+
+Fixes since v1.5.3.5
+--------------------
+
+ * git-cvsexportcommit handles root commits better.
+
+ * git-svn dcommit used to clobber when sending a series of
+   patches.
+
+ * git-svn dcommit failed after attempting to rebase when
+   started with a dirty index; now it stops upfront.
+
+ * git-grep sometimes refused to work when your index was
+   unmerged.
+
+ * "git-grep -A1 -B2" acted as if it was told to run "git -A1 -B21".
+
+ * git-hash-object did not honor configuration variables, such as
+   core.compression.
+
+ * git-index-pack choked on a huge pack on 32-bit machines, even when
+   large file offsets are supported.
+
+ * atom feeds from git-web said "10" for the month of November.
+
+ * a memory leak in commit walker was plugged.
+
+ * When git-send-email inserted the original author's From:
+   address in body, it did not mark the message with
+   Content-type: as needed.
+
+ * git-revert and git-cherry-pick incorrectly refused to start
+   when the work tree was dirty.
+
+ * git-clean did not honor core.excludesfile configuration.
+
+ * git-add mishandled ".gitignore" files when applying them to
+   subdirectories.
+
+ * While importing a too branchy history, git-fastimport did not
+   honor delta depth limit properly.
+
+ * Support for zlib implementations that lack ZLIB_VERNUM and definition
+   of deflateBound() has been added.
+
+ * Quite a lot of documentation clarifications.
diff --git a/Documentation/RelNotes/1.5.3.7.txt b/Documentation/RelNotes/1.5.3.7.txt
new file mode 100644 (file)
index 0000000..2f69061
--- /dev/null
@@ -0,0 +1,45 @@
+GIT v1.5.3.7 Release Notes
+==========================
+
+Fixes since v1.5.3.6
+--------------------
+
+ * git-send-email added 8-bit contents to the payload without
+   marking it as 8-bit in a CTE header.
+
+ * "git-bundle create a.bndl HEAD" dereferenced the symref and
+   did not record the ref as 'HEAD'; this prevented a bundle
+   from being used as a normal source of git-clone.
+
+ * The code to reject nonsense command line of the form
+   "git-commit -a paths..." and "git-commit --interactive
+   paths..." were broken.
+
+ * Adding a signature that is not ASCII-only to an original
+   commit that is ASCII-only would make the result non-ASCII.
+   "git-format-patch -s" did not mark such a message correctly
+   with MIME encoding header.
+
+ * git-add sometimes did not mark the resulting index entry
+   stat-clean.  This affected only cases when adding the
+   contents with the same length as the previously staged
+   contents, and the previous staging made the index entry
+   "racily clean".
+
+ * git-commit did not honor GIT_INDEX_FILE the user had in the
+   environment.
+
+ * When checking out a revision, git-checkout did not report where the
+   updated HEAD is if you happened to have a file called HEAD in the
+   work tree.
+
+ * "git-rev-list --objects" mishandled a tree that points at a
+   submodule.
+
+ * "git cvsimport" was not ready for packed refs that "git gc" can
+   produce and gave incorrect results.
+
+ * Many scripted Porcelains were confused when you happened to have a
+   file called "HEAD" in your work tree.
+
+Also it contains updates to the user manual and documentation.
diff --git a/Documentation/RelNotes/1.5.3.8.txt b/Documentation/RelNotes/1.5.3.8.txt
new file mode 100644 (file)
index 0000000..0e3ff58
--- /dev/null
@@ -0,0 +1,25 @@
+GIT v1.5.3.8 Release Notes
+==========================
+
+Fixes since v1.5.3.7
+--------------------
+
+ * Some documentation used "email.com" as an example domain.
+
+ * git-svn fix to handle funky branch and project names going over
+   http/https correctly.
+
+ * git-svn fix to tone down a needlessly alarming warning message.
+
+ * git-clone did not correctly report errors while fetching over http.
+
+ * git-send-email added redundant Message-Id: header to the outgoing
+   e-mail when the patch text already had one.
+
+ * a read-beyond-end-of-buffer bug in configuration file updater was fixed.
+
+ * git-grep used to show the same hit repeatedly for unmerged paths.
+
+ * After amending the patch title in "git-am -i", the command did not
+   report the patch it applied with the updated title.
+
diff --git a/Documentation/RelNotes/1.5.3.txt b/Documentation/RelNotes/1.5.3.txt
new file mode 100644 (file)
index 0000000..0668d3c
--- /dev/null
@@ -0,0 +1,366 @@
+GIT v1.5.3 Release Notes
+========================
+
+Updates since v1.5.2
+--------------------
+
+* The commit walkers other than http are officially deprecated,
+  but still supported for now.
+
+* The submodule support has Porcelain layer.
+
+  Note that the current submodule support is minimal and this is
+  deliberately so.  A design decision we made is that operations
+  at the supermodule level do not recurse into submodules by
+  default.  The expectation is that later we would add a
+  mechanism to tell git which submodules the user is interested
+  in, and this information might be used to determine the
+  recursive behaviour of certain commands (e.g. "git checkout"
+  and "git diff"), but currently we haven't agreed on what that
+  mechanism should look like.  Therefore, if you use submodules,
+  you would probably need "git submodule update" on the
+  submodules you care about after running a "git checkout" at
+  the supermodule level.
+
+* There are a handful pack-objects changes to help you cope better
+  with repositories with pathologically large blobs in them.
+
+* For people who need to import from Perforce, a front-end for
+  fast-import is in contrib/fast-import/.
+
+* Comes with git-gui 0.8.2.
+
+* Comes with updated gitk.
+
+* New commands and options.
+
+  - "git log --date=<format>" can use more formats: iso8601, rfc2822.
+
+  - The hunk header output from "git diff" family can be customized
+    with the attributes mechanism.  See gitattributes(5) for details.
+
+  - "git stash" allows you to quickly save away your work in
+    progress and replay it later on an updated state.
+
+  - "git rebase" learned an "interactive" mode that let you
+    pick and reorder which commits to rebuild.
+
+  - "git fsck" can save its findings in $GIT_DIR/lost-found, without a
+    separate invocation of "git lost-found" command.  The blobs stored by
+    lost-found are stored in plain format to allow you to grep in them.
+
+  - $GIT_WORK_TREE environment variable can be used together with
+    $GIT_DIR to work in a subdirectory of a working tree that is
+    not located at "$GIT_DIR/..".
+
+  - Giving "--file=<file>" option to "git config" is the same as
+    running the command with GIT_CONFIG=<file> environment.
+
+  - "git log" learned a new option "--follow", to follow
+    renaming history of a single file.
+
+  - "git filter-branch" lets you rewrite the revision history of
+    specified branches. You can specify a number of filters to
+    modify the commits, files and trees.
+
+  - "git cvsserver" learned new options (--base-path, --export-all,
+    --strict-paths) inspired by "git daemon".
+
+  - "git daemon --base-path-relaxed" can help migrating a repository URL
+    that did not use to use --base-path to use --base-path.
+
+  - "git commit" can use "-t templatefile" option and commit.template
+    configuration variable to prime the commit message given to you in the
+    editor.
+
+  - "git submodule" command helps you manage the projects from
+    the superproject that contain them.
+
+  - In addition to core.compression configuration option,
+    core.loosecompression and pack.compression options can
+    independently tweak zlib compression levels used for loose
+    and packed objects.
+
+  - "git ls-tree -l" shows size of blobs pointed at by the
+    tree entries, similar to "/bin/ls -l".
+
+  - "git rev-list" learned --regexp-ignore-case and
+    --extended-regexp options to tweak its matching logic used
+    for --grep filtering.
+
+  - "git describe --contains" is a handier way to call more
+    obscure command "git name-rev --tags".
+
+  - "git gc --aggressive" tells the command to spend more cycles
+    to optimize the repository harder.
+
+  - "git repack" learned a "window-memory" limit which
+    dynamically reduces the window size to stay within the
+    specified memory usage.
+
+  - "git repack" can be told to split resulting packs to avoid
+    exceeding limit specified with "--max-pack-size".
+
+  - "git fsck" gained --verbose option.  This is really really
+    verbose but it might help you identify exact commit that is
+    corrupt in your repository.
+
+  - "git format-patch" learned --numbered-files option.  This
+    may be useful for MH users.
+
+  - "git format-patch" learned format.subjectprefix configuration
+    variable, which serves the same purpose as "--subject-prefix"
+    option.
+
+  - "git tag -n -l" shows tag annotations while listing tags.
+
+  - "git cvsimport" can optionally use the separate-remote layout.
+
+  - "git blame" can be told to see through commits that change
+    whitespaces and indentation levels with "-w" option.
+
+  - "git send-email" can be told not to thread the messages when
+    sending out more than one patches.
+
+  - "git send-email" can also be told how to find whom to cc the
+    message to for each message via --cc-cmd.
+
+  - "git config" learned NUL terminated output format via -z to
+    help scripts.
+
+  - "git add" learned "--refresh <paths>..." option to selectively refresh
+    the cached stat information.
+
+  - "git init -q" makes the command quieter.
+
+  - "git -p command" now has a cousin of opposite sex, "git --no-pager
+    command".
+
+* Updated behavior of existing commands.
+
+  - "gitweb" can offer multiple snapshot formats.
+
+    ***NOTE*** Unfortunately, this changes the format of the
+    $feature{snapshot}{default} entry in the per-site
+    configuration file 'gitweb_config.perl'.  It used to be a
+    three-element tuple that describe a single format; with the
+    new configuration item format, you only have to say the name
+    of the format ('tgz', 'tbz2' or 'zip').  Please update the
+    your configuration file accordingly.
+
+  - "git clone" uses -l (hardlink files under .git) by default when
+    cloning locally.
+
+  - URL used for "git clone" and friends can specify nonstandard SSH port
+    by using ssh://host:port/path/to/repo syntax.
+
+  - "git bundle create" can now create a bundle without negative refs,
+    i.e. "everything since the beginning up to certain points".
+
+  - "git diff" (but not the plumbing level "git diff-tree") now
+    recursively descends into trees by default.
+
+  - "git diff" does not show differences that come only from
+    stat-dirtiness in the form of "diff --git" header anymore.
+    It runs "update-index --refresh" silently as needed.
+
+  - "git tag -l" used to match tags by globbing its parameter as if it
+    has wildcard '*' on both ends, which made "git tag -l gui" to match
+    tag 'gitgui-0.7.0'; this was very annoying.  You now have to add
+    asterisk on the sides you want to wildcard yourself.
+
+  - The editor to use with many interactive commands can be
+    overridden with GIT_EDITOR environment variable, or if it
+    does not exist, with core.editor configuration variable.  As
+    before, if you have neither, environment variables VISUAL
+    and EDITOR are consulted in this order, and then finally we
+    fall back on "vi".
+
+  - "git rm --cached" does not complain when removing a newly
+    added file from the index anymore.
+
+  - Options to "git log" to affect how --grep/--author options look for
+    given strings now have shorter abbreviations.  -i is for ignore case,
+    and -E is for extended regexp.
+
+  - "git log" learned --log-size to show the number of bytes in
+    the log message part of the output to help qgit.
+
+  - "git log --name-status" does not require you to give "-r" anymore.
+    As a general rule, Porcelain commands should recurse when showing
+    diff.
+
+  - "git format-patch --root A" can be used to format everything
+    since the beginning up to A.  This was supported with
+    "git format-patch --root A A" for a long time, but was not
+    properly documented.
+
+  - "git svn dcommit" retains local merge information.
+
+  - "git svnimport" allows an empty string to be specified as the
+    trunk/ directory.  This is necessary to suck data from a SVN
+    repository that doe not have trunk/ branches/ and tags/ organization
+    at all.
+
+  - "git config" to set values also honors type flags like --bool
+    and --int.
+
+  - core.quotepath configuration can be used to make textual git
+    output to emit most of the characters in the path literally.
+
+  - "git mergetool" chooses its backend more wisely, taking
+    notice of its environment such as use of X, Gnome/KDE, etc.
+
+  - "gitweb" shows merge commits a lot nicer than before.  The
+    default view uses more compact --cc format, while the UI
+    allows to choose normal diff with any parent.
+
+  - snapshot files "gitweb" creates from a repository at
+    $path/$project/.git are more useful.  We use $project part
+    in the filename, which we used to discard.
+
+  - "git cvsimport" creates lightweight tags; there is no
+    interesting information we can record in an annotated tag,
+    and the handcrafted ones the old code created was not
+    properly formed anyway.
+
+  - "git push" pretends that you immediately fetched back from
+    the remote by updating corresponding remote tracking
+    branches if you have any.
+
+  - The diffstat given after a merge (or a pull) honors the
+    color.diff configuration.
+
+  - "git commit --amend" is now compatible with various message source
+    options such as -m/-C/-c/-F.
+
+  - "git apply --whitespace=strip" removes blank lines added at
+    the end of the file.
+
+  - "git fetch" over git native protocols with "-v" option shows
+    connection status, and the IP address of the other end, to
+    help diagnosing problems.
+
+  - We used to have core.legacyheaders configuration, when
+    set to false, allowed git to write loose objects in a format
+    that mimics the format used by objects stored in packs.  It
+    turns out that this was not so useful.  Although we will
+    continue to read objects written in that format, we do not
+    honor that configuration anymore and create loose objects in
+    the legacy/traditional format.
+
+  - "--find-copies-harder" option to diff family can now be
+    spelled as "-C -C" for brevity.
+
+  - "git mailsplit" (hence "git am") can read from Maildir
+    formatted mailboxes.
+
+  - "git cvsserver" does not barf upon seeing "cvs login"
+    request.
+
+  - "pack-objects" honors "delta" attribute set in
+    .gitattributes.  It does not attempt to deltify blobs that
+    come from paths with delta attribute set to false.
+
+  - "new-workdir" script (in contrib) can now be used with a
+    bare repository.
+
+  - "git mergetool" learned to use gvimdiff.
+
+  - "gitview" (in contrib) has a better blame interface.
+
+  - "git log" and friends did not handle a commit log message
+    that is larger than 16kB; they do now.
+
+  - "--pretty=oneline" output format for "git log" and friends
+    deals with "malformed" commit log messages that have more
+    than one lines in the first paragraph better.  We used to
+    show the first line, cutting the title at mid-sentence; we
+    concatenate them into a single line and treat the result as
+    "oneline".
+
+  - "git p4import" has been demoted to contrib status.  For
+    a superior option, checkout the "git p4" front end to
+    "git fast-import" (also in contrib).  The man page and p4
+    rpm have been removed as well.
+
+  - "git mailinfo" (hence "am") now tries to see if the message
+    is in utf-8 first, instead of assuming iso-8859-1, if
+    incoming e-mail does not say what encoding it is in.
+
+* Builds
+
+  - old-style function definitions (most notably, a function
+    without parameter defined with "func()", not "func(void)")
+    have been eradicated.
+
+  - "git tag" and "git verify-tag" have been rewritten in C.
+
+* Performance Tweaks
+
+  - "git pack-objects" avoids re-deltification cost by caching
+    small enough delta results it creates while looking for the
+    best delta candidates.
+
+  - "git pack-objects" learned a new heuristic to prefer delta
+    that is shallower in depth over the smallest delta
+    possible.  This improves both overall packfile access
+    performance and packfile density.
+
+  - diff-delta code that is used for packing has been improved
+    to work better on big files.
+
+  - when there are more than one pack files in the repository,
+    the runtime used to try finding an object always from the
+    newest packfile; it now tries the same packfile as we found
+    the object requested the last time, which exploits the
+    locality of references.
+
+  - verifying pack contents done by "git fsck --full" got boost
+    by carefully choosing the order to verify objects in them.
+
+  - "git read-tree -m" to read into an already populated index
+    has been optimized vastly.  The effect of this can be seen
+    when switching branches that have differences in only a
+    handful paths.
+
+  - "git add paths..." and "git commit paths..." has also been
+    heavily optimized.
+
+Fixes since v1.5.2
+------------------
+
+All of the fixes in v1.5.2 maintenance series are included in
+this release, unless otherwise noted.
+
+* Bugfixes
+
+  - "gitweb" had trouble handling non UTF-8 text with older
+    Encode.pm Perl module.
+
+  - "git svn" misparsed the data from the commits in the repository when
+    the user had "color.diff = true" in the configuration.  This has been
+    fixed.
+
+  - There was a case where "git svn dcommit" clobbered changes made on the
+    SVN side while committing multiple changes.
+
+  - "git-write-tree" had a bad interaction with racy-git avoidance and
+    gitattributes mechanisms.
+
+  - "git --bare command" overrode existing GIT_DIR setting and always
+    made it treat the current working directory as GIT_DIR.
+
+  - "git ls-files --error-unmatch" does not complain if you give the
+    same path pattern twice by mistake.
+
+  - "git init" autodetected core.filemode but not core.symlinks, which
+    made a new directory created automatically by "git clone" cumbersome
+    to use on filesystems that require these configurations to be set.
+
+  - "git log" family of commands behaved differently when run as "git
+    log" (no pathspec) and as "git log --" (again, no pathspec).  This
+    inconsistency was introduced somewhere in v1.3.0 series but now has
+    been corrected.
+
+  - "git rebase -m" incorrectly displayed commits that were skipped.
diff --git a/Documentation/RelNotes/1.5.4.1.txt b/Documentation/RelNotes/1.5.4.1.txt
new file mode 100644 (file)
index 0000000..d4e44b8
--- /dev/null
@@ -0,0 +1,17 @@
+GIT v1.5.4.1 Release Notes
+==========================
+
+Fixes since v1.5.4
+------------------
+
+ * "git-commit -C $tag" used to work but rewrite in C done in
+   1.5.4 broke it.
+
+ * An entry in the .gitattributes file that names a pattern in a
+   subdirectory of the directory it is in did not match
+   correctly (e.g. pattern "b/*.c" in "a/.gitattributes" should
+   match "a/b/foo.c" but it didn't).
+
+ * Customized color specification was parsed incorrectly when
+   numeric color values are used.  This was fixed in 1.5.4.1.
+
diff --git a/Documentation/RelNotes/1.5.4.2.txt b/Documentation/RelNotes/1.5.4.2.txt
new file mode 100644 (file)
index 0000000..21d0df5
--- /dev/null
@@ -0,0 +1,43 @@
+GIT v1.5.4.2 Release Notes
+==========================
+
+Fixes since v1.5.4
+------------------
+
+ * The configuration parser was not prepared to see string
+   valued variables misspelled as boolean and segfaulted.
+
+ * Temporary files left behind due to interrupted object
+   transfers were not cleaned up with "git prune".
+
+ * "git config --unset" was confused when the unset variables
+   were spelled with continuation lines in the config file.
+
+ * The merge message detection in "git cvsimport" did not catch
+   a message that began with "Merge...".
+
+ * "git status" suggests "git rm --cached" for unstaging the
+   earlier "git add" before the initial commit.
+
+ * "git status" output was incorrect during a partial commit.
+
+ * "git bisect" refused to start when the HEAD was detached.
+
+ * "git bisect" allowed a wildcard character in the commit
+   message expanded while writing its log file.
+
+ * Manual pages were not formatted correctly with docbook xsl
+   1.72; added a workaround.
+
+ * "git-commit -C $tag" used to work but rewrite in C done in
+   1.5.4 broke it.  This was fixed in 1.5.4.1.
+
+ * An entry in the .gitattributes file that names a pattern in a
+   subdirectory of the directory it is in did not match
+   correctly (e.g. pattern "b/*.c" in "a/.gitattributes" should
+   match "a/b/foo.c" but it didn't).  This was fixed in 1.5.4.1.
+
+ * Customized color specification was parsed incorrectly when
+   numeric color values are used.  This was fixed in 1.5.4.1.
+
+ * http transport misbehaved when linked with curl-gnutls.
diff --git a/Documentation/RelNotes/1.5.4.3.txt b/Documentation/RelNotes/1.5.4.3.txt
new file mode 100644 (file)
index 0000000..b0fc67f
--- /dev/null
@@ -0,0 +1,27 @@
+GIT v1.5.4.3 Release Notes
+==========================
+
+Fixes since v1.5.4.2
+--------------------
+
+ * RPM spec used to pull in everything with 'git'.  This has been
+   changed so that 'git' package contains just the core parts,
+   and we now supply 'git-all' metapackage to slurp in everything.
+   This should match end user's expectation better.
+
+ * When some refs failed to update, git-push reported "failure"
+   which was unclear if some other refs were updated or all of
+   them failed atomically (the answer is the former).  Reworded
+   the message to clarify this.
+
+ * "git clone" from a repository whose HEAD was misconfigured
+   did not set up the remote properly.  Now it tries to do
+   better.
+
+ * Updated git-push documentation to clarify what "matching"
+   means, in order to reduce user confusion.
+
+ * Updated git-add documentation to clarify "add -u" operates in
+   the current subdirectory you are in, just like other commands.
+
+ * git-gui updates to work on OSX and Windows better.
diff --git a/Documentation/RelNotes/1.5.4.4.txt b/Documentation/RelNotes/1.5.4.4.txt
new file mode 100644 (file)
index 0000000..323c1a8
--- /dev/null
@@ -0,0 +1,66 @@
+GIT v1.5.4.4 Release Notes
+==========================
+
+Fixes since v1.5.4.3
+--------------------
+
+ * Building and installing with an overtight umask such as 077 made
+   installed templates unreadable by others, while the rest of the install
+   are done in a way that is friendly to umask 022.
+
+ * "git cvsexportcommit -w $cvsdir" misbehaved when GIT_DIR is set to a
+   relative directory.
+
+ * "git http-push" had an invalid memory access that could lead it to
+   segfault.
+
+ * When "git rebase -i" gave control back to the user for a commit that is
+   marked to be edited, it just said "modify it with commit --amend",
+   without saying what to do to continue after modifying it.  Give an
+   explicit instruction to run "rebase --continue" to be more helpful.
+
+ * "git send-email" in 1.5.4.3 issued a bogus empty In-Reply-To: header.
+
+ * "git bisect" showed mysterious "won't bisect on seeked tree" error message.
+   This was leftover from Cogito days to prevent "bisect" starting from a
+   cg-seeked state.  We still keep the Cogito safety, but running "git bisect
+   start" when another bisect was in effect will clean up and start over.
+
+ * "git push" with an explicit PATH to receive-pack did not quite work if
+   receive-pack was not on usual PATH.  We earlier fixed the same issue
+   with "git fetch" and upload-pack, but somehow forgot to do so in the
+   other direction.
+
+ * git-gui's info dialog was not displayed correctly when the user tries
+   to commit nothing (i.e. without staging anything).
+
+ * "git revert" did not properly fail when attempting to run with a
+   dirty index.
+
+ * "git merge --no-commit --no-ff <other>" incorrectly made commits.
+
+ * "git merge --squash --no-ff <other>", which is a nonsense combination
+   of options, was not rejected.
+
+ * "git ls-remote" and "git remote show" against an empty repository
+   failed, instead of just giving an empty result (regression).
+
+ * "git fast-import" did not handle a renamed path whose name needs to be
+   quoted, due to a bug in unquote_c_style() function.
+
+ * "git cvsexportcommit" was confused when multiple files with the same
+   basename needed to be pushed out in the same commit.
+
+ * "git daemon" did not send early errors to syslog.
+
+ * "git log --merge" did not work well with --left-right option.
+
+ * "git svn" prompted for client cert password every time it accessed the
+   server.
+
+ * The reset command in "git fast-import" data stream was documented to
+   end with an optional LF, but it actually required one.
+
+ * "git svn dcommit/rebase" did not honor --rewrite-root option.
+
+Also included are a handful documentation updates.
diff --git a/Documentation/RelNotes/1.5.4.5.txt b/Documentation/RelNotes/1.5.4.5.txt
new file mode 100644 (file)
index 0000000..bbd130e
--- /dev/null
@@ -0,0 +1,56 @@
+GIT v1.5.4.5 Release Notes
+==========================
+
+Fixes since v1.5.4.4
+--------------------
+
+ * "git fetch there" when the URL information came from the Cogito style
+   branches/there file did not update refs/heads/there (regression in
+   1.5.4).
+
+ * Bogus refspec configuration such as "remote.there.fetch = =" were not
+   detected as errors (regression in 1.5.4).
+
+ * You couldn't specify a custom editor whose path contains a whitespace
+   via GIT_EDITOR (and core.editor).
+
+ * The subdirectory filter to "git filter-branch" mishandled a history
+   where the subdirectory becomes empty and then later becomes non-empty.
+
+ * "git shortlog" gave an empty line if the original commit message was
+   malformed (e.g. a botched import from foreign SCM).  Now it finds the
+   first non-empty line and uses it for better information.
+
+ * When the user fails to give a revision parameter to "git svn", an error
+   from the Perl interpreter was issued because the script lacked proper
+   error checking.
+
+ * After "git rebase" stopped due to conflicts, if the user played with
+   "git reset" and friends, "git rebase --abort" failed to go back to the
+   correct commit.
+
+ * Additional work trees prepared with git-new-workdir (in contrib/) did
+   not share git-svn metadata directory .git/svn with the original.
+
+ * "git-merge-recursive" did not mark addition of the same path with
+   different filemodes correctly as a conflict.
+
+ * "gitweb" gave malformed URL when pathinfo stype paths are in use.
+
+ * "-n" stands for "--no-tags" again for "git fetch".
+
+ * "git format-patch" did not detect the need to add 8-bit MIME header
+   when the user used format.header configuration.
+
+ * "rev~" revision specifier used to mean "rev", which was inconsistent
+   with how "rev^" worked.  Now "rev~" is the same as "rev~1" (hence it
+   also is the same as "rev^1"), and "rev~0" is the same as "rev^0"
+   (i.e. it has to be a commit).
+
+ * "git quiltimport" did not grok empty lines, lines in "file -pNNN"
+   format to specify the prefix levels and lines with trailing comments.
+
+ * "git rebase -m" triggered pre-commit verification, which made
+   "rebase --continue" impossible.
+
+As usual, it also comes with many documentation fixes and clarifications.
diff --git a/Documentation/RelNotes/1.5.4.6.txt b/Documentation/RelNotes/1.5.4.6.txt
new file mode 100644 (file)
index 0000000..3e3c3e5
--- /dev/null
@@ -0,0 +1,43 @@
+GIT v1.5.4.6 Release Notes
+==========================
+
+I personally do not think there is any reason anybody should want to
+run v1.5.4.X series these days, because 'master' version is always
+more stable than any tagged released version of git.
+
+This is primarily to futureproof "git-shell" to accept requests
+without a dash between "git" and subcommand name (e.g. "git
+upload-pack") which the newer client will start to make sometime in
+the future.
+
+Fixes since v1.5.4.5
+--------------------
+
+ * Command line option "-n" to "git-repack" was not correctly parsed.
+
+ * Error messages from "git-apply" when the patchfile cannot be opened
+   have been improved.
+
+ * Error messages from "git-bisect" when given nonsense revisions have
+   been improved.
+
+ * reflog syntax that uses time e.g. "HEAD@{10 seconds ago}:path" did not
+   stop parsing at the closing "}".
+
+ * "git rev-parse --symbolic-full-name ^master^2" printed solitary "^",
+   but it should print nothing.
+
+ * "git apply" did not enforce "match at the beginning" correctly.
+
+ * a path specification "a/b" in .gitattributes file should not match
+   "sub/a/b", but it did.
+
+ * "git log --date-order --topo-order" did not override the earlier
+   date-order with topo-order as expected.
+
+ * "git fast-export" did not export octopus merges correctly.
+
+ * "git archive --prefix=$path/" mishandled gitattributes.
+
+As usual, it also comes with many documentation fixes and clarifications.
+
diff --git a/Documentation/RelNotes/1.5.4.7.txt b/Documentation/RelNotes/1.5.4.7.txt
new file mode 100644 (file)
index 0000000..9065a0e
--- /dev/null
@@ -0,0 +1,10 @@
+GIT v1.5.4.7 Release Notes
+==========================
+
+Fixes since 1.5.4.7
+-------------------
+
+ * Removed support for an obsolete gitweb request URI, whose
+   implementation ran "git diff" Porcelain, instead of using plumbing,
+   which would have run an external diff command specified in the
+   repository configuration as the gitweb user.
diff --git a/Documentation/RelNotes/1.5.4.txt b/Documentation/RelNotes/1.5.4.txt
new file mode 100644 (file)
index 0000000..f1323b6
--- /dev/null
@@ -0,0 +1,377 @@
+GIT v1.5.4 Release Notes
+========================
+
+Removal
+-------
+
+ * "git svnimport" was removed in favor of "git svn".  It is still there
+   in the source tree (contrib/examples) but unsupported.
+
+ * As git-commit and git-status have been rewritten, "git runstatus"
+   helper script lost all its users and has been removed.
+
+
+Temporarily disabled
+--------------------
+
+ * "git http-push" is known not to work well with cURL library older
+   than 7.16, and we had reports of repository corruption.  It is
+   disabled on such platforms for now.  Unfortunately, 1.5.3.8 shares
+   the same issue.  In other words, this does not mean you will be
+   fine if you stick to an older git release.  For now, please do not
+   use http-push from older git with cURL older than 7.16 if you
+   value your data. A proper fix will hopefully materialize in
+   later versions.
+
+
+Deprecation notices
+-------------------
+
+ * From v1.6.0, git will by default install dashed form of commands
+   (e.g. "git-commit") outside of users' normal $PATH, and will install
+   only selected commands ("git" itself, and "gitk") in $PATH.  This
+   implies:
+
+   - Using dashed forms of git commands (e.g. "git-commit") from the
+     command line has been informally deprecated since early 2006, but
+     now it officially is, and will be removed in the future.  Use
+     dash-less forms (e.g. "git commit") instead.
+
+   - Using dashed forms from your scripts, without first prepending the
+     return value from "git --exec-path" to the scripts' PATH, has been
+     informally deprecated since early 2006, but now it officially is.
+
+   - Use of dashed forms with "PATH=$(git --exec-path):$PATH; export
+     PATH" early in your script is not deprecated with this change.
+
+   Users are strongly encouraged to adjust their habits and scripts now
+   to prepare for this change.
+
+ * The post-receive hook was introduced in March 2007 to supersede
+   the post-update hook, primarily to overcome the command line length
+   limitation of the latter.  Use of post-update hook will be deprecated
+   in future versions of git, starting from v1.6.0.
+
+ * "git lost-found" was deprecated in favor of "git fsck"'s --lost-found
+   option, and will be removed in the future.
+
+ * "git peek-remote" is deprecated, as "git ls-remote" was written in C
+   and works for all transports; "git peek-remote" will be removed in
+   the future.
+
+ * "git repo-config" which was an old name for "git config" command
+   has been supported without being advertised for a long time.  The
+   next feature release will remove it.
+
+ * From v1.6.0, the repack.usedeltabaseoffset config option will default
+   to true, which will give denser packfiles (i.e. more efficient storage).
+   The downside is that git older than version 1.4.4 will not be able
+   to directly use a repository packed using this setting.
+
+ * From v1.6.0, the pack.indexversion config option will default to 2,
+   which is slightly more efficient, and makes repacking more immune to
+   data corruptions.  Git older than version 1.5.2 may revert to version 1
+   of the pack index with a manual "git index-pack" to be able to directly
+   access corresponding pack files.
+
+
+Updates since v1.5.3
+--------------------
+
+ * Comes with much improved gitk, with i18n.
+
+ * Comes with git-gui 0.9.2 with i18n.
+
+ * gitk is now merged as a subdirectory of git.git project, in
+   preparation for its i18n.
+
+ * progress displays from many commands are a lot nicer to the eye.
+   Transfer commands show throughput data.
+
+ * many commands that pay attention to per-directory .gitignore now do
+   so lazily, which makes the usual case go much faster.
+
+ * Output processing for '--pretty=format:<user format>' has been
+   optimized.
+
+ * Rename detection of diff family while detecting exact matches has
+   been greatly optimized.
+
+ * Rename detection of diff family tries to make more natural looking
+   pairing.  Earlier, if multiple identical rename sources were
+   found in the preimage, the source used was picked pretty much at random.
+
+ * Value "true" for color.diff and color.status configuration used to
+   mean "always" (even when the output is not going to a terminal).
+   This has been corrected to mean the same thing as "auto".
+
+ * "git diff" Porcelain now respects diff.external configuration, which
+   is another way to specify GIT_EXTERNAL_DIFF.
+
+ * "git diff" can be told to use different prefixes other than
+   "a/" and "b/" e.g. "git diff --src-prefix=l/ --dst-prefix=k/".
+
+ * "git diff" sometimes did not quote paths with funny
+   characters properly.
+
+ * "git log" (and any revision traversal commands) misbehaved
+   when --diff-filter is given but was not asked to actually
+   produce diff.
+
+ * HTTP proxy can be specified per remote repository using
+   remote.*.httpproxy configuration, or global http.proxy configuration
+   variable.
+
+ * Various Perforce importer updates.
+
+ * Example update and post-receive hooks have been improved.
+
+ * Any command that wants to take a commit object name can now use
+   ":/string" syntax to name a commit.
+
+ * "git reset" is now built-in and its output can be squelched with -q.
+
+ * "git reset --hard" does not make any sense in a bare
+   repository, but did not error out; fixed.
+
+ * "git send-email" can optionally talk over ssmtp and use SMTP-AUTH.
+
+ * "git rebase" learned --whitespace option.
+
+ * In "git rebase", when you decide not to replay a particular change
+   after the command stopped with a conflict, you can say "git rebase
+   --skip" without first running "git reset --hard", as the command now
+   runs it for you.
+
+ * "git rebase --interactive" mode can now work on detached HEAD.
+
+ * Other minor to serious bugs in "git rebase -i" have been fixed.
+
+ * "git rebase" now detaches head during its operation, so after a
+   successful "git rebase" operation, the reflog entry branch@{1} for
+   the current branch points at the commit before the rebase was
+   started.
+
+ * "git rebase -i" also triggers rerere to help your repeated merges.
+
+ * "git merge" can call the "post-merge" hook.
+
+ * "git pack-objects" can optionally run deltification with multiple
+   threads.
+
+ * "git archive" can optionally substitute keywords in files marked with
+   export-subst attribute.
+
+ * "git cherry-pick" made a misguided attempt to repeat the original
+   command line in the generated log message, when told to cherry-pick a
+   commit by naming a tag that points at it.  It does not anymore.
+
+ * "git for-each-ref" learned %(xxxdate:<date-format>) syntax to show the
+   various date fields in different formats.
+
+ * "git gc --auto" is a low-impact way to automatically run a variant of
+   "git repack" that does not lose unreferenced objects (read: safer
+   than the usual one) after the user accumulates too many loose
+   objects.
+
+ * "git clean" has been rewritten in C.
+
+ * You need to explicitly set clean.requireForce to "false" to allow
+   "git clean" without -f to do any damage (lack of the configuration
+   variable used to mean "do not require -f option to lose untracked
+   files", but we now use the safer default).
+
+ * The kinds of whitespace errors "git diff" and "git apply" notice (and
+   fix) can be controlled via 'core.whitespace' configuration variable
+   and 'whitespace' attribute in .gitattributes file.
+
+ * "git push" learned --dry-run option to show what would happen if a
+   push is run.
+
+ * "git push" does not update a tracking ref on the local side when the
+   remote refused to update the corresponding ref.
+
+ * "git push" learned --mirror option.  This is to push the local refs
+   one-to-one to the remote, and deletes refs from the remote that do
+   not exist anymore in the repository on the pushing side.
+
+ * "git push" can remove a corrupt ref at the remote site with the usual
+   ":ref" refspec.
+
+ * "git remote" knows --mirror mode.  This is to set up configuration to
+   push into a remote repository to store local branch heads to the same
+   branch on the remote side, and remove branch heads locally removed
+   from local repository at the same time.  Suitable for pushing into a
+   back-up repository.
+
+ * "git remote" learned "rm" subcommand.
+
+ * "git cvsserver" can be run via "git shell".  Also, "cvs" is
+   recognized as a synonym for "git cvsserver", so that CVS users
+   can be switched to git just by changing their login shell.
+
+ * "git cvsserver" acts more like receive-pack by running post-receive
+   and post-update hooks.
+
+ * "git am" and "git rebase" are far less verbose.
+
+ * "git pull" learned to pass --[no-]ff option to underlying "git
+   merge".
+
+ * "git pull --rebase" is a different way to integrate what you fetched
+   into your current branch.
+
+ * "git fast-export" produces data-stream that can be fed to fast-import
+   to reproduce the history recorded in a git repository.
+
+ * "git add -i" takes pathspecs to limit the set of files to work on.
+
+ * "git add -p" is a short-hand to go directly to the selective patch
+   subcommand in the interactive command loop and to exit when done.
+
+ * "git add -i" UI has been colorized.  The interactive prompt
+   and menu can be colored by setting color.interactive
+   configuration.  The diff output (including the hunk picker)
+   are colored with color.diff configuration.
+
+ * "git commit --allow-empty" allows you to create a single-parent
+   commit that records the same tree as its parent, overriding the usual
+   safety valve.
+
+ * "git commit --amend" can amend a merge that does not change the tree
+   from its first parent.
+
+ * "git commit" used to unconditionally strip comment lines that
+   began with '#' and removed excess blank lines.  This behavior has
+   been made configurable.
+
+ * "git commit" has been rewritten in C.
+
+ * "git stash random-text" does not create a new stash anymore.  It was
+   a UI mistake.  Use "git stash save random-text", or "git stash"
+   (without extra args) for that.
+
+ * "git stash clear extra-text" does not clear the whole stash
+   anymore.  It is tempting to expect "git stash clear stash@{2}"
+   to drop only a single named stash entry, and it is rude to
+   discard everything when that is asked (but not provided).
+
+ * "git prune --expire <time>" can exempt young loose objects from
+   getting pruned.
+
+ * "git branch --contains <commit>" can list branches that are
+   descendants of a given commit.
+
+ * "git log" learned --early-output option to help interactive GUI
+   implementations.
+
+ * "git bisect" learned "skip" action to mark untestable commits.
+
+ * "git bisect visualize" learned a shorter synonym "git bisect view".
+
+ * "git bisect visualize" runs "git log" in a non-windowed
+   environments.  It also can be told what command to run (e.g. "git
+   bisect visualize tig").
+
+ * "git format-patch" learned "format.numbered" configuration variable
+   to automatically turn --numbered option on when more than one commits
+   are formatted.
+
+ * "git ls-files" learned "--exclude-standard" to use the canned set of
+   exclude files.
+
+ * "git tag -a -f existing" begins the editor session using the existing
+   annotation message.
+
+ * "git tag -m one -m bar" (multiple -m options) behaves similarly to
+   "git commit"; the parameters to -m options are formatted as separate
+   paragraphs.
+
+ * The format "git show" outputs an annotated tag has been updated to
+   include "Tagger: " and "Date: " lines from the tag itself.  Strictly
+   speaking this is a backward incompatible change, but this is a
+   reasonable usability fix and people's scripts shouldn't have been
+   relying on the exact output from "git show" Porcelain anyway.
+
+ * "git cvsimport" did not notice errors from underlying "cvsps"
+   and produced a corrupt import silently.
+
+ * "git cvsexportcommit" learned -w option to specify and switch to the
+   CVS working directory.
+
+ * "git checkout" from a subdirectory learned to use "../path" to allow
+   checking out a path outside the current directory without cd'ing up.
+
+ * "git checkout" from and to detached HEAD leaves a bit more
+   information in the reflog.
+
+ * "git send-email --dry-run" shows full headers for easier diagnosis.
+
+ * "git merge-ours" is now built-in.
+
+ * "git svn" learned "info" and "show-externals" subcommands.
+
+ * "git svn" run from a subdirectory failed to read settings from the
+   .git/config.
+
+ * "git svn" learned --use-log-author option, which picks up more
+   descriptive name from From: and Signed-off-by: lines in the commit
+   message.
+
+ * "git svn" wasted way too much disk to record revision mappings
+   between svn and git; a new representation that is much more compact
+   for this information has been introduced to correct this.
+
+ * "git svn" left temporary index files it used without cleaning them
+   up; this was corrected.
+
+ * "git status" from a subdirectory now shows relative paths, which
+   makes copy-and-pasting for git-checkout/git-add/git-rm easier.  The
+   traditional behavior to show the full path relative to the top of
+   the work tree can be had by setting status.relativepaths
+   configuration variable to false.
+
+ * "git blame" kept text for each annotated revision in core needlessly;
+   this has been corrected.
+
+ * "git shortlog" learned to default to HEAD when the standard input is
+   a terminal and the user did not give any revision parameter.
+
+ * "git shortlog" learned "-e" option to show e-mail addresses as well as
+   authors' names.
+
+ * "git help" learned "-w" option to show documentation in browsers.
+
+ * In addition there are quite a few internal clean-ups. Notably:
+
+   - many fork/exec have been replaced with run-command API,
+     brought from the msysgit effort.
+
+   - introduction and more use of the option parser API.
+
+   - enhancement and more use of the strbuf API.
+
+ * Makefile tweaks to support HP-UX is in.
+
+Fixes since v1.5.3
+------------------
+
+All of the fixes in v1.5.3 maintenance series are included in
+this release, unless otherwise noted.
+
+These fixes are only in v1.5.4 and not backported to v1.5.3 maintenance
+series.
+
+ * The way "git diff --check" behaves is much more consistent with the way
+   "git apply --whitespace=warn" works.
+
+ * "git svn" talking with the SVN over HTTP will correctly quote branch
+   and project names.
+
+ * "git config" did not work correctly on platforms that define
+   REG_NOMATCH to an even number.
+
+ * Recent versions of AsciiDoc 8 has a change to break our
+   documentation; a workaround has been implemented.
+
+ * "git diff --color-words" colored context lines in a wrong color.
diff --git a/Documentation/RelNotes/1.5.5.1.txt b/Documentation/RelNotes/1.5.5.1.txt
new file mode 100644 (file)
index 0000000..7de4197
--- /dev/null
@@ -0,0 +1,44 @@
+GIT v1.5.5.1 Release Notes
+==========================
+
+Fixes since v1.5.5
+------------------
+
+ * "git archive --prefix=$path/" mishandled gitattributes.
+
+ * "git fetch -v" that fetches into FETCH_HEAD did not report the summary
+   the same way as done for updating the tracking refs.
+
+ * "git svn" misbehaved when the configuration file customized the "git
+   log" output format using format.pretty.
+
+ * "git submodule status" leaked an unnecessary error message.
+
+ * "git log --date-order --topo-order" did not override the earlier
+   date-order with topo-order as expected.
+
+ * "git bisect good $this" did not check the validity of the revision
+   given properly.
+
+ * "url.<there>.insteadOf" did not work correctly.
+
+ * "git clean" ran inside subdirectory behaved as if the directory was
+   explicitly specified for removal by the end user from the top level.
+
+ * "git bisect" from a detached head leaked an unnecessary error message.
+
+ * "git bisect good $a $b" when $a is Ok but $b is bogus should have
+   atomically failed before marking $a as good.
+
+ * "git fmt-merge-msg" did not clean up leading empty lines from commit
+   log messages like "git log" family does.
+
+ * "git am" recorded a commit with empty Subject: line without
+   complaining.
+
+ * when given a commit log message whose first paragraph consists of
+   multiple lines, "git rebase" squashed it into a single line.
+
+ * "git remote add $bogus_name $url" did not complain properly.
+
+Also comes with various documentation updates.
diff --git a/Documentation/RelNotes/1.5.5.2.txt b/Documentation/RelNotes/1.5.5.2.txt
new file mode 100644 (file)
index 0000000..391a7b0
--- /dev/null
@@ -0,0 +1,27 @@
+GIT v1.5.5.2 Release Notes
+==========================
+
+Fixes since v1.5.5.1
+--------------------
+
+ * "git repack -n" was mistakenly made no-op earlier.
+
+ * "git imap-send" wanted to always have imap.host even when use of
+   imap.tunnel made it unnecessary.
+
+ * reflog syntax that uses time e.g. "HEAD@{10 seconds ago}:path" did not
+   stop parsing at the closing "}".
+
+ * "git rev-parse --symbolic-full-name ^master^2" printed solitary "^",
+   but it should print nothing.
+
+ * "git commit" did not detect when it failed to write tree objects.
+
+ * "git fetch" sometimes transferred too many objects unnecessarily.
+
+ * a path specification "a/b" in .gitattributes file should not match
+   "sub/a/b".
+
+ * various gitweb fixes.
+
+Also comes with various documentation updates.
diff --git a/Documentation/RelNotes/1.5.5.3.txt b/Documentation/RelNotes/1.5.5.3.txt
new file mode 100644 (file)
index 0000000..f22f98b
--- /dev/null
@@ -0,0 +1,12 @@
+GIT v1.5.5.3 Release Notes
+==========================
+
+Fixes since v1.5.5.2
+--------------------
+
+ * "git send-email --compose" did not notice that non-ascii contents
+   needed some MIME magic.
+
+ * "git fast-export" did not export octopus merges correctly.
+
+Also comes with various documentation updates.
diff --git a/Documentation/RelNotes/1.5.5.4.txt b/Documentation/RelNotes/1.5.5.4.txt
new file mode 100644 (file)
index 0000000..2d0279e
--- /dev/null
@@ -0,0 +1,7 @@
+GIT v1.5.5.4 Release Notes
+==========================
+
+Fixes since v1.5.5.4
+--------------------
+
+ * "git name-rev --all" used to segfault.
diff --git a/Documentation/RelNotes/1.5.5.5.txt b/Documentation/RelNotes/1.5.5.5.txt
new file mode 100644 (file)
index 0000000..30fa361
--- /dev/null
@@ -0,0 +1,11 @@
+GIT v1.5.5.5 Release Notes
+==========================
+
+I personally do not think there is any reason anybody should want to
+run v1.5.5.X series these days, because 'master' version is always
+more stable than any tagged released version of git.
+
+This is primarily to futureproof "git-shell" to accept requests
+without a dash between "git" and subcommand name (e.g. "git
+upload-pack") which the newer client will start to make sometime in
+the future.
diff --git a/Documentation/RelNotes/1.5.5.6.txt b/Documentation/RelNotes/1.5.5.6.txt
new file mode 100644 (file)
index 0000000..d5e85cb
--- /dev/null
@@ -0,0 +1,10 @@
+GIT v1.5.5.6 Release Notes
+==========================
+
+Fixes since 1.5.5.5
+-------------------
+
+ * Removed support for an obsolete gitweb request URI, whose
+   implementation ran "git diff" Porcelain, instead of using plumbing,
+   which would have run an external diff command specified in the
+   repository configuration as the gitweb user.
diff --git a/Documentation/RelNotes/1.5.5.txt b/Documentation/RelNotes/1.5.5.txt
new file mode 100644 (file)
index 0000000..2932212
--- /dev/null
@@ -0,0 +1,207 @@
+GIT v1.5.5 Release Notes
+========================
+
+Updates since v1.5.4
+--------------------
+
+(subsystems)
+
+ * Comes with git-gui 0.10.1
+
+(portability)
+
+ * We shouldn't ask for BSD group ownership semantics by setting g+s bit
+   on directories on older BSD systems that refuses chmod() by non root
+   users.  BSD semantics is the default there anyway.
+
+ * Bunch of portability improvement patches coming from an effort to port
+   to Solaris has been applied.
+
+(performance)
+
+ * On platforms with suboptimal qsort(3) implementation, there
+   is an option to use more reasonable substitute we ship with
+   our software.
+
+ * New configuration variable "pack.packsizelimit" can be used
+   in place of command line option --max-pack-size.
+
+ * "git fetch" over the native git protocol used to make a
+   connection to find out the set of current remote refs and
+   another to actually download the pack data.  We now use only
+   one connection for these tasks.
+
+ * "git commit" does not run lstat(2) more than necessary
+   anymore.
+
+(usability, bells and whistles)
+
+ * Bash completion script (in contrib) are aware of more commands and
+   options.
+
+ * You can be warned when core.autocrlf conversion is applied in
+   such a way that results in an irreversible conversion.
+
+ * A catch-all "color.ui" configuration variable can be used to
+   enable coloring of all color-capable commands, instead of
+   individual ones such as "color.status" and "color.branch".
+
+ * The commands refused to take absolute pathnames where they
+   require pathnames relative to the work tree or the current
+   subdirectory.  They now can take absolute pathnames in such a
+   case as long as the pathnames do not refer outside of the
+   work tree.  E.g. "git add $(pwd)/foo" now works.
+
+ * Error messages used to be sent to stderr, only to get hidden,
+   when $PAGER was in use.  They now are sent to stdout along
+   with the command output to be shown in the $PAGER.
+
+ * A pattern "foo/" in .gitignore file now matches a directory
+   "foo".  Pattern "foo" also matches as before.
+
+ * bash completion's prompt helper function can talk about
+   operation in-progress (e.g. merge, rebase, etc.).
+
+ * Configuration variables "url.<usethis>.insteadof = <otherurl>" can be
+   used to tell "git-fetch" and "git-push" to use different URL than what
+   is given from the command line.
+
+ * "git add -i" behaves better even before you make an initial commit.
+
+ * "git am" refused to run from a subdirectory without a good reason.
+
+ * After "git apply --whitespace=fix" fixes whitespace errors in a patch,
+   a line before the fix can appear as a context or preimage line in a
+   later patch, causing the patch not to apply.  The command now knows to
+   see through whitespace fixes done to context lines to successfully
+   apply such a patch series.
+
+ * "git branch" (and "git checkout -b") to branch from a local branch can
+   optionally set "branch.<name>.merge" to mark the new branch to build on
+   the other local branch, when "branch.autosetupmerge" is set to
+   "always", or when passing the command line option "--track" (this option
+   was ignored when branching from local branches).  By default, this does
+   not happen when branching from a local branch.
+
+ * "git checkout" to switch to a branch that has "branch.<name>.merge" set
+   (i.e. marked to build on another branch) reports how much the branch
+   and the other branch diverged.
+
+ * When "git checkout" has to update a lot of paths, it used to be silent
+   for 4 seconds before it showed any progress report.  It is now a bit
+   more impatient and starts showing progress report early.
+
+ * "git commit" learned a new hook "prepare-commit-msg" that can
+   inspect what is going to be committed and prepare the commit
+   log message template to be edited.
+
+ * "git cvsimport" can now take more than one -M options.
+
+ * "git describe" learned to limit the tags to be used for
+   naming with --match option.
+
+ * "git describe --contains" now barfs when the named commit
+   cannot be described.
+
+ * "git describe --exact-match" describes only commits that are tagged.
+
+ * "git describe --long" describes a tagged commit as $tag-0-$sha1,
+   instead of just showing the exact tagname.
+
+ * "git describe" warns when using a tag whose name and path contradict
+   with each other.
+
+ * "git diff" learned "--relative" option to limit and output paths
+   relative to the current directory when working in a subdirectory.
+
+ * "git diff" learned "--dirstat" option to show birds-eye-summary of
+   changes more concisely than "--diffstat".
+
+ * "git format-patch" learned --cover-letter option to generate a cover
+   letter template.
+
+ * "git gc" learned --quiet option.
+
+ * "git gc" now automatically prunes unreachable objects that are two
+   weeks old or older.
+
+ * "git gc --auto" can be disabled more easily by just setting gc.auto
+   to zero.  It also tolerates more packfiles by default.
+
+ * "git grep" now knows "--name-only" is a synonym for the "-l" option.
+
+ * "git help <alias>" now reports "'git <alias>' is alias to <what>",
+   instead of saying "No manual entry for git-<alias>".
+
+ * "git help" can use different backends to show manual pages and this can
+   be configured using "man.viewer" configuration.
+
+ * "gitk" does not restore window position from $HOME/.gitk anymore (it
+   still restores the size).
+
+ * "git log --grep=<what>" learned "--fixed-strings" option to look for
+   <what> without treating it as a regular expression.
+
+ * "git gui" learned an auto-spell checking.
+
+ * "git push <somewhere> HEAD" and "git push <somewhere> +HEAD" works as
+   expected; they push the current branch (and only the current branch).
+   In addition, HEAD can be written as the value of "remote.<there>.push"
+   configuration variable.
+
+ * When the configuration variable "pack.threads" is set to 0, "git
+   repack" auto detects the number of CPUs and uses that many threads.
+
+ * "git send-email" learned to prompt for passwords
+   interactively.
+
+ * "git send-email" learned an easier way to suppress CC
+   recipients.
+
+ * "git stash" learned "pop" command, that applies the latest stash and
+   removes it from the stash, and "drop" command to discard the named
+   stash entry.
+
+ * "git submodule" learned a new subcommand "summary" to show the
+   symmetric difference between the HEAD version and the work tree version
+   of the submodule commits.
+
+ * Various "git cvsimport", "git cvsexportcommit", "git cvsserver",
+   "git svn" and "git p4" improvements.
+
+(internal)
+
+ * Duplicated code between git-help and git-instaweb that
+   launches user's preferred browser has been refactored.
+
+ * It is now easier to write test scripts that records known
+   breakages.
+
+ * "git checkout" is rewritten in C.
+
+ * "git remote" is rewritten in C.
+
+ * Two conflict hunks that are separated by a very short span of common
+   lines are now coalesced into one larger hunk, to make the result easier
+   to read.
+
+ * Run-command API's use of file descriptors is documented clearer and
+   is more consistent now.
+
+ * diff output can be sent to FILE * that is different from stdout.  This
+   will help reimplementing more things in C.
+
+Fixes since v1.5.4
+------------------
+
+All of the fixes in v1.5.4 maintenance series are included in
+this release, unless otherwise noted.
+
+ * "git-http-push" did not allow deletion of remote ref with the usual
+   "push <remote> :<branch>" syntax.
+
+ * "git-rebase --abort" did not go back to the right location if
+   "git-reset" was run during the "git-rebase" session.
+
+ * "git imap-send" without setting imap.host did not error out but
+   segfaulted.
diff --git a/Documentation/RelNotes/1.5.6.1.txt b/Documentation/RelNotes/1.5.6.1.txt
new file mode 100644 (file)
index 0000000..4864b16
--- /dev/null
@@ -0,0 +1,28 @@
+GIT v1.5.6.1 Release Notes
+==========================
+
+Fixes since v1.5.6
+------------------
+
+* Last minute change broke loose object creation on AIX.
+
+* (performance fix) We used to make $GIT_DIR absolute path early in the
+  programs but keeping it relative to the current directory internally
+  gives 1-3 per-cent performance boost.
+
+* bash completion knows the new --graph option to git-log family.
+
+
+* git-diff -c/--cc showed unnecessary "deletion" lines at the context
+  boundary.
+
+* git-for-each-ref ignored %(object) and %(type) requests for tag
+  objects.
+
+* git-merge usage had a typo.
+
+* Rebuilding of git-svn metainfo database did not take rewriteRoot
+  option into account.
+
+* Running "git-rebase --continue/--skip/--abort" before starting a
+  rebase gave nonsense error messages.
diff --git a/Documentation/RelNotes/1.5.6.2.txt b/Documentation/RelNotes/1.5.6.2.txt
new file mode 100644 (file)
index 0000000..5902a85
--- /dev/null
@@ -0,0 +1,40 @@
+GIT v1.5.6.2 Release Notes
+==========================
+
+Futureproof
+-----------
+
+ * "git-shell" accepts requests without a dash between "git" and
+   subcommand name (e.g. "git upload-pack") which the newer client will
+   start to make sometime in the future.
+
+Fixes since v1.5.6.1
+--------------------
+
+* "git clone" from a remote that is named with url.insteadOf setting in
+  $HOME/.gitconfig did not work well.
+
+* "git describe --long --tags" segfaulted when the described revision was
+  tagged with a lightweight tag.
+
+* "git diff --check" did not report the result via its exit status
+  reliably.
+
+* When remote side used to have branch 'foo' and git-fetch finds that now
+  it has branch 'foo/bar', it refuses to lose the existing remote tracking
+  branch and its reflog.  The error message has been improved to suggest
+  pruning the remote if the user wants to proceed and get the latest set
+  of branches from the remote, including such 'foo/bar'.
+
+* "git reset file" should mean the same thing as "git reset HEAD file",
+  but we required disambiguating -- even when "file" is not ambiguous.
+
+* "git show" segfaulted when an annotated tag that points at another
+  annotated tag was given to it.
+
+* Optimization for a large import via "git-svn" introduced in v1.5.6 had a
+  serious memory and temporary file leak, which made it unusable for
+  moderately large import.
+
+* "git-svn" mangled remote nickname used in the configuration file
+  unnecessarily.
diff --git a/Documentation/RelNotes/1.5.6.3.txt b/Documentation/RelNotes/1.5.6.3.txt
new file mode 100644 (file)
index 0000000..f61dd35
--- /dev/null
@@ -0,0 +1,52 @@
+GIT v1.5.6.3 Release Notes
+==========================
+
+Fixes since v1.5.6.2
+--------------------
+
+* Setting core.sharedrepository to traditional "true" value was supposed to make
+  the repository group writable but should not affect permission for others.
+  However, since 1.5.6, it was broken to drop permission for others when umask is
+  022, making the repository unreadable by others.
+
+* Setting GIT_TRACE will report spawning of external process via run_command().
+
+* Using an object with very deep delta chain pinned memory needed for extracting
+  intermediate base objects unnecessarily long, leading to excess memory usage.
+
+* Bash completion script did not notice '--' marker on the command
+  line and tried the relatively slow "ref completion" even when
+  completing arguments after one.
+
+* Registering a non-empty blob racily and then truncating the working
+  tree file for it confused "racy-git avoidance" logic into thinking
+  that the path is now unchanged.
+
+* The section that describes attributes related to git-archive were placed
+  in a wrong place in the gitattributes(5) manual page.
+
+* "git am" was not helpful to the users when it detected that the committer
+  information is not set up properly yet.
+
+* "git clone" had a leftover debugging fprintf().
+
+* "git clone -q" was not quiet enough as it used to and gave object count
+  and progress reports.
+
+* "git clone" marked downloaded packfile with .keep; this could be a
+  good thing if the remote side is well packed but otherwise not,
+  especially for a project that is not really big.
+
+* "git daemon" used to call syslog() from a signal handler, which
+  could raise signals of its own but generally is not reentrant.  This
+  was fixed by restructuring the code to report syslog() after the handler
+  returns.
+
+* When "git push" tries to remove a remote ref, and corresponding
+  tracking ref is missing, we used to report error (i.e. failure to
+  remove something that does not exist).
+
+* "git mailinfo" (hence "git am") did not handle commit log messages in a
+  MIME multipart mail correctly.
+
+Contains other various documentation fixes.
diff --git a/Documentation/RelNotes/1.5.6.4.txt b/Documentation/RelNotes/1.5.6.4.txt
new file mode 100644 (file)
index 0000000..d8968f1
--- /dev/null
@@ -0,0 +1,47 @@
+GIT v1.5.6.4 Release Notes
+==========================
+
+Fixes since v1.5.6.3
+--------------------
+
+* Various commands could overflow its internal buffer on a platform
+  with small PATH_MAX value in a repository that has contents with
+  long pathnames.
+
+* There wasn't a way to make --pretty=format:%<> specifiers to honor
+  .mailmap name rewriting for authors and committers.  Now you can with
+  %aN and %cN.
+
+* Bash completion wasted too many cycles; this has been optimized to be
+  usable again.
+
+* Bash completion lost ref part when completing something like "git show
+  pu:Makefile".
+
+* "git-cvsserver" did not clean up its temporary working area after annotate
+  request.
+
+* "git-daemon" called syslog() from its signal handler, which was a
+  no-no.
+
+* "git-fetch" into an empty repository used to remind that the fetch will
+   be huge by saying "no common commits", but this was an unnecessary
+   noise; it is already known by the user anyway.
+
+* "git-http-fetch" would have segfaulted when pack idx file retrieved
+  from the other side was corrupt.
+
+* "git-index-pack" used too much memory when dealing with a deep delta chain.
+
+* "git-mailinfo" (hence "git-am") did not correctly handle in-body [PATCH]
+  line to override the commit title taken from the mail Subject header.
+
+* "git-rebase -i -p" lost parents that are not involved in the history
+  being rewritten.
+
+* "git-rm" lost track of where the index file was when GIT_DIR was
+  specified as a relative path.
+
+* "git-rev-list --quiet" was not quiet as advertised.
+
+Contains other various documentation fixes.
diff --git a/Documentation/RelNotes/1.5.6.5.txt b/Documentation/RelNotes/1.5.6.5.txt
new file mode 100644 (file)
index 0000000..47ca172
--- /dev/null
@@ -0,0 +1,29 @@
+GIT v1.5.6.5 Release Notes
+==========================
+
+Fixes since v1.5.6.4
+--------------------
+
+* "git cvsimport" used to spit out "UNKNOWN LINE..." diagnostics to stdout.
+
+* "git commit -F filename" and "git tag -F filename" run from subdirectories
+  did not read the right file.
+
+* "git init --template=" with blank "template" parameter linked files
+  under root directories to .git, which was a total nonsense.  Instead, it
+  means "I do not want to use anything from the template directory".
+
+* "git diff-tree" and other diff plumbing ignored diff.renamelimit configuration
+  variable when the user explicitly asked for rename detection.
+
+* "git name-rev --name-only" did not work when "--stdin" option was in effect.
+
+* "git show-branch" mishandled its 8th branch.
+
+* Addition of "git update-index --ignore-submodules" that happened during
+  1.5.6 cycle broke "git update-index --ignore-missing".
+
+* "git send-email" did not parse charset from an existing Content-type:
+  header properly.
+
+Contains other various documentation fixes.
diff --git a/Documentation/RelNotes/1.5.6.6.txt b/Documentation/RelNotes/1.5.6.6.txt
new file mode 100644 (file)
index 0000000..79da23d
--- /dev/null
@@ -0,0 +1,10 @@
+GIT v1.5.6.6 Release Notes
+==========================
+
+Fixes since 1.5.6.5
+-------------------
+
+ * Removed support for an obsolete gitweb request URI, whose
+   implementation ran "git diff" Porcelain, instead of using plumbing,
+   which would have run an external diff command specified in the
+   repository configuration as the gitweb user.
diff --git a/Documentation/RelNotes/1.5.6.txt b/Documentation/RelNotes/1.5.6.txt
new file mode 100644 (file)
index 0000000..e143d8d
--- /dev/null
@@ -0,0 +1,115 @@
+GIT v1.5.6 Release Notes
+========================
+
+Updates since v1.5.5
+--------------------
+
+(subsystems)
+
+* Comes with updated gitk and git-gui.
+
+(portability)
+
+* git will build on AIX better than before now.
+
+* core.ignorecase configuration variable can be used to work better on
+  filesystems that are not case sensitive.
+
+* "git init" now autodetects the case sensitivity of the filesystem and
+  sets core.ignorecase accordingly.
+
+* cpio is no longer used; neither "curl" binary (libcurl is still used).
+
+(documentation)
+
+* Many freestanding documentation pages have been converted and made
+  available to "git help" (aka "man git<something>") as section 7 of
+  the manual pages. This means bookmarks to some HTML documentation
+  files may need to be updated (eg "tutorial.html" became
+  "gittutorial.html").
+
+(performance)
+
+* "git clone" was rewritten in C.  This will hopefully help cloning a
+  repository with insane number of refs.
+
+* "git rebase --onto $there $from $branch" used to switch to the tip of
+  $branch only to immediately reset back to $from, smudging work tree
+  files unnecessarily.  This has been optimized.
+
+* Object creation codepath in "git-svn" has been optimized by enhancing
+  plumbing commands git-cat-file and git-hash-object.
+
+(usability, bells and whistles)
+
+* "git add -p" (and the "patch" subcommand of "git add -i") can choose to
+  apply (or not apply) mode changes independently from contents changes.
+
+* "git bisect help" gives longer and more helpful usage information.
+
+* "git bisect" does not use a special branch "bisect" anymore; instead, it
+  does its work on a detached HEAD.
+
+* "git branch" (and "git checkout -b") can be told to set up
+  branch.<name>.rebase automatically, so that later you can say "git pull"
+  and magically cause "git pull --rebase" to happen.
+
+* "git branch --merged" and "git branch --no-merged" can be used to list
+  branches that have already been merged (or not yet merged) to the
+  current branch.
+
+* "git cherry-pick" and "git revert" can add a sign-off.
+
+* "git commit" mentions the author identity when you are committing
+  somebody else's changes.
+
+* "git diff/log --dirstat" output is consistent between binary and textual
+  changes.
+
+* "git filter-branch" rewrites signed tags by demoting them to annotated.
+
+* "git format-patch --no-binary" can produce a patch that lack binary
+  changes (i.e. cannot be used to propagate the whole changes) meant only
+  for reviewing.
+
+* "git init --bare" is a synonym for "git --bare init" now.
+
+* "git gc --auto" honors a new pre-auto-gc hook to temporarily disable it.
+
+* "git log --pretty=tformat:<custom format>" gives a LF after each entry,
+  instead of giving a LF between each pair of entries which is how
+  "git log --pretty=format:<custom format>" works.
+
+* "git log" and friends learned the "--graph" option to show the ancestry
+  graph at the left margin of the output.
+
+* "git log" and friends can be told to use date format that is different
+  from the default via 'log.date' configuration variable.
+
+* "git send-email" now can send out messages outside a git repository.
+
+* "git send-email --compose" was made aware of rfc2047 quoting.
+
+* "git status" can optionally include output from "git submodule
+  summary".
+
+* "git svn" learned --add-author-from option to propagate the authorship
+  by munging the commit log message.
+
+* new object creation and looking up in "git svn" has been optimized.
+
+* "gitweb" can read from a system-wide configuration file.
+
+(internal)
+
+* "git unpack-objects" and "git receive-pack" is now more strict about
+  detecting breakage in the objects they receive over the wire.
+
+
+Fixes since v1.5.5
+------------------
+
+All of the fixes in v1.5.5 maintenance series are included in
+this release, unless otherwise noted.
+
+And there are too numerous small fixes to otherwise note here ;-)
diff --git a/Documentation/RelNotes/1.6.0.1.txt b/Documentation/RelNotes/1.6.0.1.txt
new file mode 100644 (file)
index 0000000..49d7a1c
--- /dev/null
@@ -0,0 +1,36 @@
+GIT v1.6.0.1 Release Notes
+==========================
+
+Fixes since v1.6.0
+------------------
+
+* "git diff --cc" did not honor content mangling specified by
+  gitattributes and core.autocrlf when reading from the work tree.
+
+* "git diff --check" incorrectly detected new trailing blank lines when
+  whitespace check was in effect.
+
+* "git for-each-ref" tried to dereference NULL when asked for '%(body)" on
+  a tag with a single incomplete line as its payload.
+
+* "git format-patch" peeked before the beginning of a string when
+  "format.headers" variable is empty (a misconfiguration).
+
+* "git help help" did not work correctly.
+
+* "git mailinfo" (hence "git am") was unhappy when MIME multipart message
+  contained garbage after the finishing boundary.
+
+* "git mailinfo" also was unhappy when the "From: " line only had a bare
+  e-mail address.
+
+* "git merge" did not refresh the index correctly when a merge resulted in
+  a fast-forward.
+
+* "git merge" did not resolve a truly trivial merges that can be done
+  without content level merges.
+
+* "git svn dcommit" to a repository with URL that has embedded usernames
+  did not work correctly.
+
+Contains other various documentation fixes.
diff --git a/Documentation/RelNotes/1.6.0.2.txt b/Documentation/RelNotes/1.6.0.2.txt
new file mode 100644 (file)
index 0000000..e1e24b3
--- /dev/null
@@ -0,0 +1,87 @@
+GIT v1.6.0.2 Release Notes
+==========================
+
+Fixes since v1.6.0.1
+--------------------
+
+* Installation on platforms that needs .exe suffix to git-* programs were
+  broken in 1.6.0.1.
+
+* Installation on filesystems without symbolic links support did not
+  work well.
+
+* In-tree documentations and test scripts now use "git foo" form to set a
+  better example, instead of the "git-foo" form (which is an acceptable
+  form if you have "PATH=$(git --exec-path):$PATH" in your script)
+
+* Many commands did not use the correct working tree location when used
+  with GIT_WORK_TREE environment settings.
+
+* Some systems need to use compatibility fnmatch and regex libraries
+  independent from each other; the compat/ area has been reorganized to
+  allow this.
+
+
+* "git apply --unidiff-zero" incorrectly applied a -U0 patch that inserts
+  a new line before the second line.
+
+* "git blame -c" did not exactly work like "git annotate" when range
+  boundaries are involved.
+
+* "git checkout file" when file is still unmerged checked out contents from
+  a random high order stage, which was confusing.
+
+* "git clone $there $here/" with extra trailing slashes after explicit
+  local directory name $here did not work as expected.
+
+* "git diff" on tracked contents with CRLF line endings did not drive "less"
+  intelligently when showing added or removed lines.
+
+* "git diff --dirstat -M" did not add changes in subdirectories up
+  correctly for renamed paths.
+
+* "git diff --cumulative" did not imply "--dirstat".
+
+* "git for-each-ref refs/heads/" did not work as expected.
+
+* "git gui" allowed users to feed patch without any context to be applied.
+
+* "git gui" botched parsing "diff" output when a line that begins with two
+  dashes and a space gets removed or a line that begins with two pluses
+  and a space gets added.
+
+* "git gui" translation updates and i18n fixes.
+
+* "git index-pack" is more careful against disk corruption while completing
+  a thin pack.
+
+* "git log -i --grep=pattern" did not ignore case; neither "git log -E
+  --grep=pattern" triggered extended regexp.
+
+* "git log --pretty="%ad" --date=short" did not use short format when
+  showing the timestamp.
+
+* "git log --author=author" match incorrectly matched with the
+  timestamp part of "author " line in commit objects.
+
+* "git log -F --author=author" did not work at all.
+
+* Build procedure for "git shell" that used stub versions of some
+  functions and globals was not understood by linkers on some platforms.
+
+* "git stash" was fooled by a stat-dirty but otherwise unmodified paths
+  and refused to work until the user refreshed the index.
+
+* "git svn" was broken on Perl before 5.8 with recent fixes to reduce
+  use of temporary files.
+
+* "git verify-pack -v" did not work correctly when given more than one
+  packfile.
+
+Also contains many documentation updates.
+
+--
+exec >/var/tmp/1
+O=v1.6.0.1-78-g3632cfc
+echo O=$(git describe maint)
+git shortlog --no-merges $O..maint
diff --git a/Documentation/RelNotes/1.6.0.3.txt b/Documentation/RelNotes/1.6.0.3.txt
new file mode 100644 (file)
index 0000000..ae05778
--- /dev/null
@@ -0,0 +1,117 @@
+GIT v1.6.0.3 Release Notes
+==========================
+
+Fixes since v1.6.0.2
+--------------------
+
+* "git archive --format=zip" did not honor core.autocrlf while
+  --format=tar did.
+
+* Continuing "git rebase -i" was very confused when the user left modified
+  files in the working tree while resolving conflicts.
+
+* Continuing "git rebase -i" was also very confused when the user left
+  some staged changes in the index after "edit".
+
+* "git rebase -i" now honors the pre-rebase hook, just like the
+  other rebase implementations "git rebase" and "git rebase -m".
+
+* "git rebase -i" incorrectly aborted when there is no commit to replay.
+
+* Behaviour of "git diff --quiet" was inconsistent with "diff --exit-code"
+  with the output redirected to /dev/null.
+
+* "git diff --no-index" on binary files no longer outputs a bogus
+  "diff --git" header line.
+
+* "git diff" hunk header patterns with multiple elements separated by LF
+  were not used correctly.
+
+* Hunk headers in "git diff" default to using extended regular
+  expressions, fixing some of the internal patterns on non-GNU
+  platforms.
+
+* New config "diff.*.xfuncname" exposes extended regular expressions
+  for user specified hunk header patterns.
+
+* "git gc" when ejecting otherwise unreachable objects from packfiles into
+  loose form leaked memory.
+
+* "git index-pack" was recently broken and mishandled objects added by
+  thin-pack completion processing under memory pressure.
+
+* "git index-pack" was recently broken and misbehaved when run from inside
+  .git/objects/pack/ directory.
+
+* "git stash apply sash@{1}" was fixed to error out.  Prior versions
+  would have applied stash@{0} incorrectly.
+
+* "git stash apply" now offers a better suggestion on how to continue
+  if the working tree is currently dirty.
+
+* "git for-each-ref --format=%(subject)" fixed for commits with no
+  no newline in the message body.
+
+* "git remote" fixed to protect printf from user input.
+
+* "git remote show -v" now displays all URLs of a remote.
+
+* "git checkout -b branch" was confused when branch already existed.
+
+* "git checkout -q" once again suppresses the locally modified file list.
+
+* "git clone -q", "git fetch -q" asks remote side to not send
+  progress messages, actually making their output quiet.
+
+* Cross-directory renames are no longer used when creating packs.  This
+  allows more graceful behavior on filesystems like sshfs.
+
+* Stale temporary files under $GIT_DIR/objects/pack are now cleaned up
+  automatically by "git prune".
+
+* "git merge" once again removes directories after the last file has
+  been removed from it during the merge.
+
+* "git merge" did not allocate enough memory for the structure itself when
+  enumerating the parents of the resulting commit.
+
+* "git blame -C -C" no longer segfaults while trying to pass blame if
+   it encounters a submodule reference.
+
+* "git rm" incorrectly claimed that you have local modifications when a
+  path was merely stat-dirty.
+
+* "git svn" fixed to display an error message when 'set-tree' failed,
+   instead of a Perl compile error.
+
+* "git submodule" fixed to handle checking out a different commit
+  than HEAD after initializing the submodule.
+
+* The "git commit" error message when there are still unmerged
+  files present was clarified to match "git write-tree".
+
+* "git init" was confused when core.bare or core.sharedRepository are set
+  in system or user global configuration file by mistake.  When --bare or
+  --shared is given from the command line, these now override such
+  settings made outside the repositories.
+
+* Some segfaults due to uncaught NULL pointers were fixed in multiple
+  tools such as apply, reset, update-index.
+
+* Solaris builds now default to OLD_ICONV=1 to avoid compile warnings;
+  Solaris 8 does not define NEEDS_LIBICONV by default.
+
+* "Git.pm" tests relied on unnecessarily more recent version of Perl.
+
+* "gitweb" triggered undef warning on commits without log messages.
+
+* "gitweb" triggered undef warnings on missing trees.
+
+* "gitweb" now removes PATH_INFO from its URLs so users don't have
+  to manually set the URL in the gitweb configuration.
+
+* Bash completion removed support for legacy "git-fetch", "git-push"
+  and "git-pull" as these are no longer installed.  Dashless form
+  ("git fetch") is still however supported.
+
+Many other documentation updates.
diff --git a/Documentation/RelNotes/1.6.0.4.txt b/Documentation/RelNotes/1.6.0.4.txt
new file mode 100644 (file)
index 0000000..d522661
--- /dev/null
@@ -0,0 +1,39 @@
+GIT v1.6.0.4 Release Notes
+==========================
+
+Fixes since v1.6.0.3
+--------------------
+
+* 'git add -p' said "No changes" when only binary files were changed.
+
+* 'git archive' did not work correctly in bare repositories.
+
+* 'git checkout -t -b newbranch' when you are on detached HEAD was broken.
+
+* when we refuse to detect renames because there are too many new or
+  deleted files, 'git diff' did not say how many there are.
+
+* 'git push --mirror' tried and failed to push the stash; there is no
+  point in sending it to begin with.
+
+* 'git push' did not update the remote tracking reference if the corresponding
+  ref on the remote end happened to be already up to date.
+
+* 'git pull $there $branch:$current_branch' did not work when you were on
+  a branch yet to be born.
+
+* when giving up resolving a conflicted merge, 'git reset --hard' failed
+  to remove new paths from the working tree.
+
+* 'git send-email' had a small fd leak while scanning directory.
+
+* 'git status' incorrectly reported a submodule directory as an untracked
+  directory.
+
+* 'git svn' used deprecated 'git-foo' form of subcommand invocation.
+
+* 'git update-ref -d' to remove a reference did not honor --no-deref option.
+
+* Plugged small memleaks here and there.
+
+* Also contains many documentation updates.
diff --git a/Documentation/RelNotes/1.6.0.5.txt b/Documentation/RelNotes/1.6.0.5.txt
new file mode 100644 (file)
index 0000000..a08bb96
--- /dev/null
@@ -0,0 +1,56 @@
+GIT v1.6.0.5 Release Notes
+==========================
+
+Fixes since v1.6.0.4
+--------------------
+
+* "git checkout" used to crash when your HEAD was pointing at a deleted
+  branch.
+
+* "git checkout" from an un-checked-out state did not allow switching out
+  of the current branch.
+
+* "git diff" always allowed GIT_EXTERNAL_DIFF and --no-ext-diff was no-op for
+  the command.
+
+* Giving 3 or more tree-ish to "git diff" is supposed to show the combined
+  diff from second and subsequent trees to the first one, but the order was
+  screwed up.
+
+* "git fast-export" did not export all tags.
+
+* "git ls-files --with-tree=<tree>" did not work with options other
+  than -c, most notably with -m.
+
+* "git pack-objects" did not make its best effort to honor --max-pack-size
+  option when a single first object already busted the given limit and
+  placed many objects in a single pack.
+
+* "git-p4" fast import frontend was too eager to trigger its keyword expansion
+  logic, even on a keyword-looking string that does not have closing '$' on the
+  same line.
+
+* "git push $there" when the remote $there is defined in $GIT_DIR/branches/$there
+  behaves more like what cg-push from Cogito used to work.
+
+* when giving up resolving a conflicted merge, "git reset --hard" failed
+  to remove new paths from the working tree.
+
+* "git tag" did not complain when given mutually incompatible set of options.
+
+* The message constructed in the internal editor was discarded when "git
+  tag -s" failed to sign the message, which was often caused by the user
+  not configuring GPG correctly.
+
+* "make check" cannot be run without sparse; people may have meant to say
+  "make test" instead, so suggest that.
+
+* Internal diff machinery had a corner case performance bug that choked on
+  a large file with many repeated contents.
+
+* "git repack" used to grab objects out of packs marked with .keep
+  into a new pack.
+
+* Many unsafe call to sprintf() style varargs functions are corrected.
+
+* Also contains quite a few documentation updates.
diff --git a/Documentation/RelNotes/1.6.0.6.txt b/Documentation/RelNotes/1.6.0.6.txt
new file mode 100644 (file)
index 0000000..64ece1f
--- /dev/null
@@ -0,0 +1,33 @@
+GIT v1.6.0.6 Release Notes
+==========================
+
+Fixes since 1.6.0.5
+-------------------
+
+ * "git fsck" had a deep recursion that wasted stack space.
+
+ * "git fast-export" and "git fast-import" choked on an old style
+   annotated tag that lack the tagger information.
+
+ * "git mergetool -- file" did not correctly skip "--" marker that
+   signals the end of options list.
+
+ * "git show $tag" segfaulted when an annotated $tag pointed at a
+   nonexistent object.
+
+ * "git show 2>error" when the standard output is automatically redirected
+   to the pager redirected the standard error to the pager as well; there
+   was no need to.
+
+ * "git send-email" did not correctly handle list of addresses when
+   they had quoted comma (e.g. "Lastname, Givenname" <mail@addre.ss>).
+
+ * Logic to discover branch ancestry in "git svn" was unreliable when
+   the process to fetch history was interrupted.
+
+ * Removed support for an obsolete gitweb request URI, whose
+   implementation ran "git diff" Porcelain, instead of using plumbing,
+   which would have run an external diff command specified in the
+   repository configuration as the gitweb user.
+
+Also contains numerous documentation typofixes.
diff --git a/Documentation/RelNotes/1.6.0.txt b/Documentation/RelNotes/1.6.0.txt
new file mode 100644 (file)
index 0000000..de7ef16
--- /dev/null
@@ -0,0 +1,258 @@
+GIT v1.6.0 Release Notes
+========================
+
+User visible changes
+--------------------
+
+With the default Makefile settings, most of the programs are now
+installed outside your $PATH, except for "git", "gitk" and
+some server side programs that need to be accessible for technical
+reasons.  Invoking a git subcommand as "git-xyzzy" from the command
+line has been deprecated since early 2006 (and officially announced in
+1.5.4 release notes); use of them from your scripts after adding
+output from "git --exec-path" to the $PATH is still supported in this
+release, but users are again strongly encouraged to adjust their
+scripts to use "git xyzzy" form, as we will stop installing
+"git-xyzzy" hardlinks for built-in commands in later releases.
+
+An earlier change to page "git status" output was overwhelmingly unpopular
+and has been reverted.
+
+Source changes needed for porting to MinGW environment are now all in the
+main git.git codebase.
+
+By default, packfiles created with this version uses delta-base-offset
+encoding introduced in v1.4.4.  Pack idx files are using version 2 that
+allows larger packs and added robustness thanks to its CRC checking,
+introduced in v1.5.2 and v1.4.4.5.  If you want to keep your repositories
+backwards compatible past these versions, set repack.useDeltaBaseOffset
+to false or pack.indexVersion to 1, respectively.
+
+We used to prevent sample hook scripts shipped in templates/ from
+triggering by default by relying on the fact that we install them as
+unexecutable, but on some filesystems, this approach does not work.
+They are now shipped with ".sample" suffix.  If you want to activate
+any of these samples as-is, rename them to drop the ".sample" suffix,
+instead of running "chmod +x" on them.  For example, you can rename
+hooks/post-update.sample to hooks/post-update to enable the sample
+hook that runs update-server-info, in order to make repositories
+friendly to dumb protocols (i.e. HTTP).
+
+GIT_CONFIG, which was only documented as affecting "git config", but
+actually affected all git commands, now only affects "git config".
+GIT_LOCAL_CONFIG, also only documented as affecting "git config" and
+not different from GIT_CONFIG in a useful way, is removed.
+
+The ".dotest" temporary area "git am" and "git rebase" use is now moved
+inside the $GIT_DIR, to avoid mistakes of adding it to the project by
+accident.
+
+An ancient merge strategy "stupid" has been removed.
+
+
+Updates since v1.5.6
+--------------------
+
+(subsystems)
+
+* git-p4 in contrib learned "allowSubmit" configuration to control on
+  which branch to allow "submit" subcommand.
+
+* git-gui learned to stage changes per-line.
+
+(portability)
+
+* Changes for MinGW port have been merged, thanks to Johannes Sixt and
+  gangs.
+
+* Sample hook scripts shipped in templates/ are now suffixed with
+  *.sample.
+
+* perl's in-place edit (-i) does not work well without backup files on Windows;
+  some tests are rewritten to cope with this.
+
+(documentation)
+
+* Updated howto/update-hook-example
+
+* Got rid of usage of "git-foo" from the tutorial and made typography
+  more consistent.
+
+* Disambiguating "--" between revs and paths is finally documented.
+
+(performance, robustness, sanity etc.)
+
+* index-pack used too much memory when dealing with a deep delta chain.
+  This has been optimized.
+
+* reduced excessive inlining to shrink size of the "git" binary.
+
+* verify-pack checks the object CRC when using version 2 idx files.
+
+* When an object is corrupt in a pack, the object became unusable even
+  when the same object is available in a loose form,  We now try harder to
+  fall back to these redundant objects when able.  In particular, "git
+  repack -a -f" can be used to fix such a corruption as long as necessary
+  objects are available.
+
+* Performance of "git-blame -C -C" operation is vastly improved.
+
+* git-clone does not create refs in loose form anymore (it behaves as
+  if you immediately ran git-pack-refs after cloning).  This will help
+  repositories with insanely large number of refs.
+
+* core.fsyncobjectfiles configuration can be used to ensure that the loose
+  objects created will be fsync'ed (this is only useful on filesystems
+  that does not order data writes properly).
+
+* "git commit-tree" plumbing can make Octopus with more than 16 parents.
+  "git commit" has been capable of this for quite some time.
+
+(usability, bells and whistles)
+
+* even more documentation pages are now accessible via "man" and "git help".
+
+* A new environment variable GIT_CEILING_DIRECTORIES can be used to stop
+  the discovery process of the toplevel of working tree; this may be useful
+  when you are working in a slow network disk and are outside any working tree,
+  as bash-completion and "git help" may still need to run in these places.
+
+* By default, stash entries never expire.  Set reflogexpire in [gc
+  "refs/stash"] to a reasonable value to get traditional auto-expiration
+  behaviour back
+
+* Longstanding latency issue with bash completion script has been
+  addressed.  This will need to be backmerged to 'maint' later.
+
+* pager.<cmd> configuration variable can be used to enable/disable the
+  default paging behaviour per command.
+
+* "git-add -i" has a new action 'e/dit' to allow you edit the patch hunk
+  manually.
+
+* git-am records the original tip of the branch in ORIG_HEAD before it
+  starts applying patches.
+
+* git-apply can handle a patch that touches the same path more than once
+  much better than before.
+
+* git-apply can be told not to trust the line counts recorded in the input
+  patch but recount, with the new --recount option.
+
+* git-apply can be told to apply a patch to a path deeper than what the
+  patch records with --directory option.
+
+* git-archive can be told to omit certain paths from its output using
+  export-ignore attributes.
+
+* git-archive uses the zlib default compression level when creating
+  zip archive.
+
+* git-archive's command line options --exec and --remote can take their
+  parameters as separate command line arguments, similar to other commands.
+  IOW, both "--exec=path" and "--exec path" are now supported.
+
+* With -v option, git-branch describes the remote tracking statistics
+  similar to the way git-checkout reports by how many commits your branch
+  is ahead/behind.
+
+* git-branch's --contains option used to always require a commit parameter
+  to limit the branches with; it now defaults to list branches that
+  contains HEAD if this parameter is omitted.
+
+* git-branch's --merged and --no-merged option used to always limit the
+  branches relative to the HEAD, but they can now take an optional commit
+  argument that is used in place of HEAD.
+
+* git-bundle can read the revision arguments from the standard input.
+
+* git-cherry-pick can replay a root commit now.
+
+* git-clone can clone from a remote whose URL would be rewritten by
+  configuration stored in $HOME/.gitconfig now.
+
+* "git-clone --mirror" is a handy way to set up a bare mirror repository.
+
+* git-cvsserver learned to respond to "cvs co -c".
+
+* git-diff --check now checks leftover merge conflict markers.
+
+* "git-diff -p" learned to grab a better hunk header lines in
+  BibTex, Pascal/Delphi, and Ruby files and also pays attention to
+  chapter and part boundary in TeX documents.
+
+* When remote side used to have branch 'foo' and git-fetch finds that now
+  it has branch 'foo/bar', it refuses to lose the existing remote tracking
+  branch and its reflog.  The error message has been improved to suggest
+  pruning the remote if the user wants to proceed and get the latest set
+  of branches from the remote, including such 'foo/bar'.
+
+* fast-export learned to export and import marks file; this can be used to
+  interface with fast-import incrementally.
+
+* fast-import and fast-export learned to export and import gitlinks.
+
+* "gitk" left background process behind after being asked to dig very deep
+  history and the user killed the UI; the process is killed when the UI goes
+  away now.
+
+* git-rebase records the original tip of branch in ORIG_HEAD before it is
+  rewound.
+
+* "git rerere" can be told to update the index with auto-reused resolution
+  with rerere.autoupdate configuration variable.
+
+* git-rev-parse learned $commit^! and $commit^@ notations used in "log"
+  family.  These notations are available in gitk as well, because the gitk
+  command internally uses rev-parse to interpret its arguments.
+
+* git-rev-list learned --children option to show child commits it
+  encountered during the traversal, instead of showing parent commits.
+
+* git-send-mail can talk not just over SSL but over TLS now.
+
+* git-shortlog honors custom output format specified with "--pretty=format:".
+
+* "git-stash save" learned --keep-index option.  This lets you stash away the
+  local changes and bring the changes staged in the index to your working
+  tree for examination and testing.
+
+* git-stash also learned branch subcommand to create a new branch out of
+  stashed changes.
+
+* git-status gives the remote tracking statistics similar to the way
+  git-checkout reports by how many commits your branch is ahead/behind.
+
+* "git-svn dcommit" is now aware of auto-props setting the subversion user
+  has.
+
+* You can tell "git status -u" to even more aggressively omit checking
+  untracked files with --untracked-files=no.
+
+* Original SHA-1 value for "update-ref -d" is optional now.
+
+* Error codes from gitweb are made more descriptive where possible, rather
+  than "403 forbidden" as we used to issue everywhere.
+
+(internal)
+
+* git-merge has been reimplemented in C.
+
+
+Fixes since v1.5.6
+------------------
+
+All of the fixes in v1.5.6 maintenance series are included in
+this release, unless otherwise noted.
+
+ * git-clone ignored its -u option; the fix needs to be backported to
+   'maint';
+
+ * git-mv used to lose the distinction between changes that are staged
+   and that are only in the working tree, by staging both in the index
+   after moving such a path.
+
+ * "git-rebase -i -p" rewrote the parents to wrong ones when amending
+   (either edit or squash) was involved, and did not work correctly
+   when fast forwarding.
+
diff --git a/Documentation/RelNotes/1.6.1.1.txt b/Documentation/RelNotes/1.6.1.1.txt
new file mode 100644 (file)
index 0000000..8c594ba
--- /dev/null
@@ -0,0 +1,59 @@
+GIT v1.6.1.1 Release Notes
+==========================
+
+Fixes since v1.6.1
+------------------
+
+* "git add frotz/nitfol" when "frotz" is a submodule should have errored
+  out, but it didn't.
+
+* "git apply" took file modes from the patch text and updated the mode
+  bits of the target tree even when the patch was not about mode changes.
+
+* "git bisect view" on Cygwin did not launch gitk
+
+* "git checkout $tree" did not trigger an error.
+
+* "git commit" tried to remove COMMIT_EDITMSG from the work tree by mistake.
+
+* "git describe --all" complained when a commit is described with a tag,
+  which was nonsense.
+
+* "git diff --no-index --" did not trigger no-index (aka "use git-diff as
+  a replacement of diff on untracked files") behaviour.
+
+* "git format-patch -1 HEAD" on a root commit failed to produce patch
+  text.
+
+* "git fsck branch" did not work as advertised; instead it behaved the same
+  way as "git fsck".
+
+* "git log --pretty=format:%s" did not handle a multi-line subject the
+  same way as built-in log listers (i.e. shortlog, --pretty=oneline, etc.)
+
+* "git daemon", and "git merge-file" are more careful when freopen fails
+  and barf, instead of going on and writing to unopened filehandle.
+
+* "git http-push" did not like some RFC 4918 compliant DAV server
+  responses.
+
+* "git merge -s recursive" mistakenly overwritten an untracked file in the
+  work tree upon delete/modify conflict.
+
+* "git merge -s recursive" didn't leave the index unmerged for entries with
+  rename/delete conflicts.
+
+* "git merge -s recursive" clobbered untracked files in the work tree.
+
+* "git mv -k" with more than one erroneous paths misbehaved.
+
+* "git read-tree -m -u" hence branch switching incorrectly lost a
+  subdirectory in rare cases.
+
+* "git rebase -i" issued an unnecessary error message upon a user error of
+  marking the first commit to be "squash"ed.
+
+* "git shortlog" did not format a commit message with multi-line
+  subject correctly.
+
+Many documentation updates.
diff --git a/Documentation/RelNotes/1.6.1.2.txt b/Documentation/RelNotes/1.6.1.2.txt
new file mode 100644 (file)
index 0000000..be37cbb
--- /dev/null
@@ -0,0 +1,39 @@
+GIT v1.6.1.2 Release Notes
+==========================
+
+Fixes since v1.6.1.1
+--------------------
+
+* The logic for rename detection in internal diff used by commands like
+  "git diff" and "git blame" has been optimized to avoid loading the same
+  blob repeatedly.
+
+* We did not allow writing out a blob that is larger than 2GB for no good
+  reason.
+
+* "git format-patch -o $dir", when $dir is a relative directory, used it
+  as relative to the root of the work tree, not relative to the current
+  directory.
+
+* v1.6.1 introduced an optimization for "git push" into a repository (A)
+  that borrows its objects from another repository (B) to avoid sending
+  objects that are available in repository B, when they are not yet used
+  by repository A.  However the code on the "git push" sender side was
+  buggy and did not work when repository B had new objects that are not
+  known by the sender.  This caused pushing into a "forked" repository
+  served by v1.6.1 software using "git push" from v1.6.1 sometimes did not
+  work.  The bug was purely on the "git push" sender side, and has been
+  corrected.
+
+* "git status -v" did not paint its diff output in colour even when
+  color.ui configuration was set.
+
+* "git ls-tree" learned --full-tree option to help Porcelain scripts that
+  want to always see the full path regardless of the current working
+  directory.
+
+* "git grep" incorrectly searched in work tree paths even when they are
+  marked as assume-unchanged.  It now searches in the index entries.
+
+* "git gc" with no grace period needlessly ejected packed but unreachable
+  objects in their loose form, only to delete them right away.
diff --git a/Documentation/RelNotes/1.6.1.3.txt b/Documentation/RelNotes/1.6.1.3.txt
new file mode 100644 (file)
index 0000000..6f0bde1
--- /dev/null
@@ -0,0 +1,32 @@
+GIT v1.6.1.3 Release Notes
+==========================
+
+Fixes since v1.6.1.2
+--------------------
+
+* "git diff --binary | git apply" pipeline did not work well when
+  a binary blob is changed to a symbolic link.
+
+* Some combinations of -b/-w/--ignore-space-at-eol to "git diff" did
+  not work as expected.
+
+* "git grep" did not pass the -I (ignore binary) option when
+  calling out an external grep program.
+
+* "git log" and friends include HEAD to the set of starting points
+  when --all is given.  This makes a difference when you are not
+  on any branch.
+
+* "git mv" to move an untracked file to overwrite a tracked
+  contents misbehaved.
+
+* "git merge -s octopus" with many potential merge bases did not
+  work correctly.
+
+* RPM binary package installed the html manpages in a wrong place.
+
+Also includes minor documentation fixes and updates.
+
+
+--
+git shortlog --no-merges v1.6.1.2-33-gc789350..
diff --git a/Documentation/RelNotes/1.6.1.4.txt b/Documentation/RelNotes/1.6.1.4.txt
new file mode 100644 (file)
index 0000000..0ce6316
--- /dev/null
@@ -0,0 +1,44 @@
+GIT v1.6.1.4 Release Notes
+==========================
+
+Fixes since v1.6.1.3
+--------------------
+
+* .gitignore learned to handle backslash as a quoting mechanism for
+  comment introduction character "#".
+  This fix was first merged to 1.6.2.1.
+
+* "git fast-export" produced wrong output with some parents missing from
+  commits, when the history is clock-skewed.
+
+* "git fast-import" sometimes failed to read back objects it just wrote
+  out and aborted, because it failed to flush stale cached data.
+
+* "git-ls-tree" and "git-diff-tree" used a pathspec correctly when
+  deciding to descend into a subdirectory but they did not match the
+  individual paths correctly.  This caused pathspecs "abc/d ab" to match
+  "abc/0" ("abc/d" made them decide to descend into the directory "abc/",
+  and then "ab" incorrectly matched "abc/0" when it shouldn't).
+  This fix was first merged to 1.6.2.3.
+
+* import-zips script (in contrib) did not compute the common directory
+  prefix correctly.
+  This fix was first merged to 1.6.2.2.
+
+* "git init" segfaulted when given an overlong template location via
+  the --template= option.
+  This fix was first merged to 1.6.2.4.
+
+* "git repack" did not error out when necessary object was missing in the
+  repository.
+
+* git-repack (invoked from git-gc) did not work as nicely as it should in
+  a repository that borrows objects from neighbours via alternates
+  mechanism especially when some packs are marked with the ".keep" flag
+  to prevent them from being repacked.
+  This fix was first merged to 1.6.2.3.
+
+Also includes minor documentation fixes and updates.
+
+--
+git shortlog --no-merges v1.6.1.3..
diff --git a/Documentation/RelNotes/1.6.1.txt b/Documentation/RelNotes/1.6.1.txt
new file mode 100644 (file)
index 0000000..adb7cca
--- /dev/null
@@ -0,0 +1,286 @@
+GIT v1.6.1 Release Notes
+========================
+
+Updates since v1.6.0
+--------------------
+
+When some commands (e.g. "git log", "git diff") spawn pager internally, we
+used to make the pager the parent process of the git command that produces
+output.  This meant that the exit status of the whole thing comes from the
+pager, not the underlying git command.  We swapped the order of the
+processes around and you will see the exit code from the command from now
+on.
+
+(subsystems)
+
+* gitk can call out to git-gui to view "git blame" output; git-gui in turn
+  can run gitk from its blame view.
+
+* Various git-gui updates including updated translations.
+
+* Various gitweb updates from repo.or.cz installation.
+
+* Updates to emacs bindings.
+
+(portability)
+
+* A few test scripts used nonportable "grep" that did not work well on
+  some platforms, e.g. Solaris.
+
+* Sample pre-auto-gc script has OS X support.
+
+* Makefile has support for (ancient) FreeBSD 4.9.
+
+(performance)
+
+* Many operations that are lstat(3) heavy can be told to pre-execute
+  necessary lstat(3) in parallel before their main operations, which
+  potentially gives much improved performance for cold-cache cases or in
+  environments with weak metadata caching (e.g. NFS).
+
+* The underlying diff machinery to produce textual output has been
+  optimized, which would result in faster "git blame" processing.
+
+* Most of the test scripts (but not the ones that try to run servers)
+  can be run in parallel.
+
+* Bash completion of refnames in a repository with massive number of
+  refs has been optimized.
+
+* Cygwin port uses native stat/lstat implementations when applicable,
+  which leads to improved performance.
+
+* "git push" pays attention to alternate repositories to avoid sending
+  unnecessary objects.
+
+* "git svn" can rebuild an out-of-date rev_map file.
+
+(usability, bells and whistles)
+
+* When you mistype a command name, git helpfully suggests what it guesses
+  you might have meant to say.  help.autocorrect configuration can be set
+  to a non-zero value to accept the suggestion when git can uniquely
+  guess.
+
+* The packfile machinery hopefully is more robust when dealing with
+  corrupt packs if redundant objects involved in the corruption are
+  available elsewhere.
+
+* "git add -N path..." adds the named paths as an empty blob, so that
+  subsequent "git diff" will show a diff as if they are creation events.
+
+* "git add" gained a built-in synonym for people who want to say "stage
+  changes" instead of "add contents to the staging area" which amounts
+  to the same thing.
+
+* "git apply" learned --include=paths option, similar to the existing
+  --exclude=paths option.
+
+* "git bisect" is careful about a user mistake and suggests testing of
+  merge base first when good is not a strict ancestor of bad.
+
+* "git bisect skip" can take a range of commits.
+
+* "git blame" re-encodes the commit metainfo to UTF-8 from i18n.commitEncoding
+  by default.
+
+* "git check-attr --stdin" can check attributes for multiple paths.
+
+* "git checkout --track origin/hack" used to be a syntax error.  It now
+  DWIMs to create a corresponding local branch "hack", i.e. acts as if you
+  said "git checkout --track -b hack origin/hack".
+
+* "git checkout --ours/--theirs" can be used to check out one side of a
+  conflicting merge during conflict resolution.
+
+* "git checkout -m" can be used to recreate the initial conflicted state
+  during conflict resolution.
+
+* "git cherry-pick" can also utilize rerere for conflict resolution.
+
+* "git clone" learned to be verbose with -v
+
+* "git commit --author=$name" can look up author name from existing
+  commits.
+
+* output from "git commit" has been reworded in a more concise and yet
+  more informative way.
+
+* "git count-objects" reports the on-disk footprint for packfiles and
+  their corresponding idx files.
+
+* "git daemon" learned --max-connections=<count> option.
+
+* "git daemon" exports REMOTE_ADDR to record client address, so that
+  spawned programs can act differently on it.
+
+* "git describe --tags" favours closer lightweight tags than farther
+  annotated tags now.
+
+* "git diff" learned to mimic --suppress-blank-empty from GNU diff via a
+  configuration option.
+
+* "git diff" learned to put more sensible hunk headers for Python,
+  HTML and ObjC contents.
+
+* "git diff" learned to vary the a/ vs b/ prefix depending on what are
+  being compared, controlled by diff.mnemonicprefix configuration.
+
+* "git diff" learned --dirstat-by-file to count changed files, not number
+  of lines, when summarizing the global picture.
+
+* "git diff" learned "textconv" filters --- a binary or hard-to-read
+  contents can be munged into human readable form and the difference
+  between the results of the conversion can be viewed (obviously this
+  cannot produce a patch that can be applied, so this is disabled in
+  format-patch among other things).
+
+* "--cached" option to "git diff has an easier to remember synonym "--staged",
+  to ask "what is the difference between the given commit and the
+  contents staged in the index?"
+
+* "git for-each-ref" learned "refname:short" token that gives an
+  unambiguously abbreviated refname.
+
+* Auto-numbering of the subject lines is the default for "git
+  format-patch" now.
+
+* "git grep" learned to accept -z similar to GNU grep.
+
+* "git help" learned to use GIT_MAN_VIEWER environment variable before
+  using "man" program.
+
+* "git imap-send" can optionally talk SSL.
+
+* "git index-pack" is more careful against disk corruption while
+  completing a thin pack.
+
+* "git log --check" and "git log --exit-code" passes their underlying diff
+  status with their exit status code.
+
+* "git log" learned --simplify-merges, a milder variant of --full-history;
+  "gitk --simplify-merges" is easier to view than with --full-history.
+
+* "git log" learned "--source" to show what ref each commit was reached
+  from.
+
+* "git log" also learned "--simplify-by-decoration" to show the
+  birds-eye-view of the topology of the history.
+
+* "git log --pretty=format:" learned "%d" format element that inserts
+  names of tags that point at the commit.
+
+* "git merge --squash" and "git merge --no-ff" into an unborn branch are
+  noticed as user errors.
+
+* "git merge -s $strategy" can use a custom built strategy if you have a
+  command "git-merge-$strategy" on your $PATH.
+
+* "git pull" (and "git fetch") can be told to operate "-v"erbosely or
+  "-q"uietly.
+
+* "git push" can be told to reject deletion of refs with receive.denyDeletes
+  configuration.
+
+* "git rebase" honours pre-rebase hook; use --no-verify to bypass it.
+
+* "git rebase -p" uses interactive rebase machinery now to preserve the merges.
+
+* "git reflog expire branch" can be used in place of "git reflog expire
+  refs/heads/branch".
+
+* "git remote show $remote" lists remote branches one-per-line now.
+
+* "git send-email" can be given revision range instead of files and
+  maildirs on the command line, and automatically runs format-patch to
+  generate patches for the given revision range.
+
+* "git submodule foreach" subcommand allows you to iterate over checked
+  out submodules.
+
+* "git submodule sync" subcommands allows you to update the origin URL
+  recorded in submodule directories from the toplevel .gitmodules file.
+
+* "git svn branch" can create new branches on the other end.
+
+* "gitweb" can use more saner PATH_INFO based URL.
+
+(internal)
+
+* "git hash-object" learned to lie about the path being hashed, so that
+  correct gitattributes processing can be done while hashing contents
+  stored in a temporary file.
+
+* various callers of git-merge-recursive avoid forking it as an external
+  process.
+
+* Git class defined in "Git.pm" can be subclasses a bit more easily.
+
+* We used to link GNU regex library as a compatibility layer for some
+  platforms, but it turns out it is not necessary on most of them.
+
+* Some path handling routines used fixed number of buffers used alternately
+  but depending on the call depth, this arrangement led to hard to track
+  bugs.  This issue is being addressed.
+
+
+Fixes since v1.6.0
+------------------
+
+All of the fixes in v1.6.0.X maintenance series are included in this
+release, unless otherwise noted.
+
+* Porcelains implemented as shell scripts were utterly confused when you
+  entered to a subdirectory of a work tree from sideways, following a
+  symbolic link (this may need to be backported to older releases later).
+
+* Tracking symbolic links would work better on filesystems whose lstat()
+  returns incorrect st_size value for them.
+
+* "git add" and "git update-index" incorrectly allowed adding S/F when S
+  is a tracked symlink that points at a directory D that has a path F in
+  it (we still need to fix a similar nonsense when S is a submodule and F
+  is a path in it).
+
+* "git am" after stopping at a broken patch lost --whitespace, -C, -p and
+  --3way options given from the command line initially.
+
+* "git diff --stdin" used to take two trees on a line and compared them,
+  but we dropped support for such a use case long time ago.  This has
+  been resurrected.
+
+* "git filter-branch" failed to rewrite a tag name with slashes in it.
+
+* "git http-push" did not understand URI scheme other than opaquelocktoken
+  when acquiring a lock from the server (this may need to be backported to
+  older releases later).
+
+* After "git rebase -p" stopped with conflicts while replaying a merge,
+ "git rebase --continue" did not work (may need to be backported to older
+  releases).
+
+* "git revert" records relative to which parent a revert was made when
+  reverting a merge.  Together with new documentation that explains issues
+  around reverting a merge and merging from the updated branch later, this
+  hopefully will reduce user confusion (this may need to be backported to
+  older releases later).
+
+* "git rm --cached" used to allow an empty blob that was added earlier to
+  be removed without --force, even when the file in the work tree has
+  since been modified.
+
+* "git push --tags --all $there" failed with generic usage message without
+  telling saying these two options are incompatible.
+
+* "git log --author/--committer" match used to potentially match the
+  timestamp part, exposing internal implementation detail.  Also these did
+  not work with --fixed-strings match at all.
+
+* "gitweb" did not mark non-ASCII characters imported from external HTML fragments
+  correctly.
+
+--
+exec >/var/tmp/1
+O=v1.6.1-rc3-74-gf66bc5f
+echo O=$(git describe master)
+git shortlog --no-merges $O..master ^maint
diff --git a/Documentation/RelNotes/1.6.2.1.txt b/Documentation/RelNotes/1.6.2.1.txt
new file mode 100644 (file)
index 0000000..dfa3641
--- /dev/null
@@ -0,0 +1,19 @@
+GIT v1.6.2.1 Release Notes
+==========================
+
+Fixes since v1.6.2
+------------------
+
+* .gitignore learned to handle backslash as a quoting mechanism for
+  comment introduction character "#".
+
+* timestamp output in --date=relative mode used to display timestamps that
+  are long time ago in the default mode; it now uses "N years M months
+  ago", and "N years ago".
+
+* git-add -i/-p now works with non-ASCII pathnames.
+
+* "git hash-object -w" did not read from the configuration file from the
+  correct .git directory.
+
+* git-send-email learned to correctly handle multiple Cc: addresses.
diff --git a/Documentation/RelNotes/1.6.2.2.txt b/Documentation/RelNotes/1.6.2.2.txt
new file mode 100644 (file)
index 0000000..fafa998
--- /dev/null
@@ -0,0 +1,45 @@
+GIT v1.6.2.2 Release Notes
+==========================
+
+Fixes since v1.6.2.1
+--------------------
+
+* A longstanding confusing description of what --pickaxe option of
+  git-diff does has been clarified in the documentation.
+
+* "git-blame -S" did not quite work near the commits that were given
+  on the command line correctly.
+
+* "git diff --pickaxe-regexp" did not count overlapping matches
+  correctly.
+
+* "git diff" did not feed files in work-tree representation to external
+  diff and textconv.
+
+* "git-fetch" in a repository that was not cloned from anywhere said
+  it cannot find 'origin', which was hard to understand for new people.
+
+* "git-format-patch --numbered-files --stdout" did not have to die of
+  incompatible options; it now simply ignores --numbered-files as no files
+  are produced anyway.
+
+* "git-ls-files --deleted" did not work well with GIT_DIR&GIT_WORK_TREE.
+
+* "git-read-tree A B C..." without -m option has been broken for a long
+  time.
+
+* git-send-email ignored --in-reply-to when --no-thread was given.
+
+* 'git-submodule add' did not tolerate extra slashes and ./ in the path it
+  accepted from the command line; it now is more lenient.
+
+* git-svn misbehaved when the project contained a path that began with
+  two dashes.
+
+* import-zips script (in contrib) did not compute the common directory
+  prefix correctly.
+
+* miscompilation of negated enum constants by old gcc (2.9) affected the
+  codepaths to spawn subprocesses.
+
+Many small documentation updates are included as well.
diff --git a/Documentation/RelNotes/1.6.2.3.txt b/Documentation/RelNotes/1.6.2.3.txt
new file mode 100644 (file)
index 0000000..4d3c1ac
--- /dev/null
@@ -0,0 +1,22 @@
+GIT v1.6.2.3 Release Notes
+==========================
+
+Fixes since v1.6.2.2
+--------------------
+
+* Setting an octal mode value to core.sharedrepository configuration to
+  restrict access to the repository to group members did not work as
+  advertised.
+
+* A fairly large and trivial memory leak while rev-list shows list of
+  reachable objects has been identified and plugged.
+
+* "git-commit --interactive" did not abort when underlying "git-add -i"
+  signaled a failure.
+
+* git-repack (invoked from git-gc) did not work as nicely as it should in
+  a repository that borrows objects from neighbours via alternates
+  mechanism especially when some packs are marked with the ".keep" flag
+  to prevent them from being repacked.
+
+Many small documentation updates are included as well.
diff --git a/Documentation/RelNotes/1.6.2.4.txt b/Documentation/RelNotes/1.6.2.4.txt
new file mode 100644 (file)
index 0000000..f4bf1d0
--- /dev/null
@@ -0,0 +1,39 @@
+GIT v1.6.2.4 Release Notes
+==========================
+
+Fixes since v1.6.2.3
+--------------------
+
+* The configuration parser had a buffer overflow while parsing an overlong
+  value.
+
+* pruning reflog entries that are unreachable from the tip of the ref
+  during "git reflog prune" (hence "git gc") was very inefficient.
+
+* "git-add -p" lacked a way to say "q"uit to refuse staging any hunks for
+  the remaining paths.  You had to say "d" and then ^C.
+
+* "git-checkout <tree-ish> <submodule>" did not update the index entry at
+  the named path; it now does.
+
+* "git-fast-export" choked when seeing a tag that does not point at commit.
+
+* "git init" segfaulted when given an overlong template location via
+  the --template= option.
+
+* "git-ls-tree" and "git-diff-tree" used a pathspec correctly when
+  deciding to descend into a subdirectory but they did not match the
+  individual paths correctly.  This caused pathspecs "abc/d ab" to match
+  "abc/0" ("abc/d" made them decide to descend into the directory "abc/",
+  and then "ab" incorrectly matched "abc/0" when it shouldn't).
+
+* "git-merge-recursive" was broken when a submodule entry was involved in
+  a criss-cross merge situation.
+
+Many small documentation updates are included as well.
+
+---
+exec >/var/tmp/1
+echo O=$(git describe maint)
+O=v1.6.2.3-38-g318b847
+git shortlog --no-merges $O..maint
diff --git a/Documentation/RelNotes/1.6.2.5.txt b/Documentation/RelNotes/1.6.2.5.txt
new file mode 100644 (file)
index 0000000..b23f9e9
--- /dev/null
@@ -0,0 +1,21 @@
+GIT v1.6.2.5 Release Notes
+==========================
+
+Fixes since v1.6.2.4
+--------------------
+
+* "git apply" mishandled if you fed a git generated patch that renames
+  file A to B and file B to A at the same time.
+
+* "git diff -c -p" (and "diff --cc") did not expect to see submodule
+  differences and instead refused to work.
+
+* "git grep -e '('" segfaulted, instead of diagnosing a mismatched
+  parentheses error.
+
+* "git fetch" generated packs with offset-delta encoding when both ends of
+  the connection are capable of producing one; this cannot be read by
+  ancient git and the user should be able to disable this by setting
+  repack.usedeltabaseoffset configuration to false.
+
+
diff --git a/Documentation/RelNotes/1.6.2.txt b/Documentation/RelNotes/1.6.2.txt
new file mode 100644 (file)
index 0000000..ad060f4
--- /dev/null
@@ -0,0 +1,164 @@
+GIT v1.6.2 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.1
+--------------------
+
+(subsystems)
+
+* git-svn updates.
+
+* gitweb updates, including a new patch view and RSS/Atom feed
+  improvements.
+
+* (contrib/emacs) git.el now has commands for checking out a branch,
+  creating a branch, cherry-picking and reverting commits; vc-git.el
+  is not shipped with git anymore (it is part of official Emacs).
+
+(performance)
+
+* pack-objects autodetects the number of CPUs available and uses threaded
+  version.
+
+(usability, bells and whistles)
+
+* automatic typo correction works on aliases as well
+
+* @{-1} is a way to refer to the last branch you were on.  This is
+  accepted not only where an object name is expected, but anywhere
+  a branch name is expected and acts as if you typed the branch name.
+  E.g. "git branch --track mybranch @{-1}", "git merge @{-1}", and
+  "git rev-parse --symbolic-full-name @{-1}" would work as expected.
+
+* When refs/remotes/origin/HEAD points at a remote tracking branch that
+  has been pruned away, many git operations issued warning when they
+  internally enumerated the refs.  We now warn only when you say "origin"
+  to refer to that pruned branch.
+
+* The location of .mailmap file can be configured, and its file format was
+  enhanced to allow mapping an incorrect e-mail field as well.
+
+* "git add -p" learned 'g'oto action to jump directly to a hunk.
+
+* "git add -p" learned to find a hunk with given text with '/'.
+
+* "git add -p" optionally can be told to work with just the command letter
+  without Enter.
+
+* when "git am" stops upon a patch that does not apply, it shows the
+  title of the offending patch.
+
+* "git am --directory=<dir>" and "git am --reject" passes these options
+  to underlying "git apply".
+
+* "git am" learned --ignore-date option.
+
+* "git blame" aligns author names better when they are spelled in
+  non US-ASCII encoding.
+
+* "git clone" now makes its best effort when cloning from an empty
+  repository to set up configuration variables to refer to the remote
+  repository.
+
+* "git checkout -" is a shorthand for "git checkout @{-1}".
+
+* "git cherry" defaults to whatever the current branch is tracking (if
+  exists) when the <upstream> argument is not given.
+
+* "git cvsserver" can be told not to add extra "via git-CVS emulator" to
+  the commit log message it serves via gitcvs.commitmsgannotation
+  configuration.
+
+* "git cvsserver" learned to handle 'noop' command some CVS clients seem
+  to expect to work.
+
+* "git diff" learned a new option --inter-hunk-context to coalesce close
+  hunks together and show context between them.
+
+* The definition of what constitutes a word for "git diff --color-words"
+  can be customized via gitattributes, command line or a configuration.
+
+* "git diff" learned --patience to run "patience diff" algorithm.
+
+* "git filter-branch" learned --prune-empty option that discards commits
+  that do not change the contents.
+
+* "git fsck" now checks loose objects in alternate object stores, instead
+  of misreporting them as missing.
+
+* "git gc --prune" was resurrected to allow "git gc --no-prune" and
+  giving non-default expiration period e.g. "git gc --prune=now".
+
+* "git grep -w" and "git grep" for fixed strings have been optimized.
+
+* "git mergetool" learned -y(--no-prompt) option to disable prompting.
+
+* "git rebase -i" can transplant a history down to root to elsewhere
+  with --root option.
+
+* "git reset --merge" is a new mode that works similar to the way
+  "git checkout" switches branches, taking the local changes while
+  switching to another commit.
+
+* "git submodule update" learned --no-fetch option.
+
+* "git tag" learned --contains that works the same way as the same option
+  from "git branch".
+
+
+Fixes since v1.6.1
+------------------
+
+All of the fixes in v1.6.1.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.1.X series.
+
+* "git-add sub/file" when sub is a submodule incorrectly added the path to
+  the superproject.
+
+* "git bundle" did not exclude annotated tags even when a range given
+  from the command line wanted to.
+
+* "git filter-branch" unnecessarily refused to work when you had
+  checked out a different commit from what is recorded in the superproject
+  index in a submodule.
+
+* "git filter-branch" incorrectly tried to update a nonexistent work tree
+  at the end when it is run in a bare repository.
+
+* "git gc" did not work if your repository was created with an ancient git
+  and never had any pack files in it before.
+
+* "git mergetool" used to ignore autocrlf and other attributes
+  based content rewriting.
+
+* branch switching and merges had a silly bug that did not validate
+  the correct directory when making sure an existing subdirectory is
+  clean.
+
+* "git -p cmd" when cmd is not a built-in one left the display in funny state
+  when killed in the middle.
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.3.txt b/Documentation/RelNotes/1.6.3.txt
new file mode 100644 (file)
index 0000000..418c685
--- /dev/null
@@ -0,0 +1,182 @@
+GIT v1.6.3 Release Notes
+========================
+
+With the next major release, "git push" into a branch that is
+currently checked out will be refused by default.  You can choose
+what should happen upon such a push by setting the configuration
+variable receive.denyCurrentBranch in the receiving repository.
+
+To ease the transition plan, the receiving repository of such a
+push running this release will issue a big warning when the
+configuration variable is missing.  Please refer to:
+
+  http://git.or.cz/gitwiki/GitFaq#non-bare
+  http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+for more details on the reason why this change is needed and the
+transition plan.
+
+For a similar reason, "git push $there :$killed" to delete the branch
+$killed in a remote repository $there, if $killed branch is the current
+branch pointed at by its HEAD, gets a large warning.  You can choose what
+should happen upon such a push by setting the configuration variable
+receive.denyDeleteCurrent in the receiving repository.
+
+When the user does not tell "git push" what to push, it has always
+pushed matching refs.  For some people it is unexpected, and a new
+configuration variable push.default has been introduced to allow
+changing a different default behaviour.  To advertise the new feature,
+a big warning is issued if this is not configured and a git push without
+arguments is attempted.
+
+
+Updates since v1.6.2
+--------------------
+
+(subsystems)
+
+* various git-svn updates.
+
+* git-gui updates, including an update to Russian translation, and a
+  fix to an infinite loop when showing an empty diff.
+
+* gitk updates, including an update to Russian translation and improved Windows
+  support.
+
+(performance)
+
+* many uses of lstat(2) in the codepath for "git checkout" have been
+  optimized out.
+
+(usability, bells and whistles)
+
+* Boolean configuration variable yes/no can be written as on/off.
+
+* rsync:/path/to/repo can be used to run git over rsync for local
+  repositories.  It may not be useful in practice; meant primarily for
+  testing.
+
+* http transport learned to prompt and use password when fetching from or
+  pushing to http://user@host.xz/ URL.
+
+* (msysgit) progress output that is sent over the sideband protocol can
+  be handled appropriately in Windows console.
+
+* "--pretty=<style>" option to the log family of commands can now be
+  spelled as "--format=<style>".  In addition, --format=%formatstring
+  is a short-hand for --pretty=tformat:%formatstring.
+
+* "--oneline" is a synonym for "--pretty=oneline --abbrev-commit".
+
+* "--graph" to the "git log" family can draw the commit ancestry graph
+  in colors.
+
+* If you realize that you botched the patch when you are editing hunks
+  with the 'edit' action in git-add -i/-p, you can abort the editor to
+  tell git not to apply it.
+
+* @{-1} is a new way to refer to the last branch you were on introduced in
+  1.6.2, but the initial implementation did not teach this to a few
+  commands.  Now the syntax works with "branch -m @{-1} newname".
+
+* git-archive learned --output=<file> option.
+
+* git-archive takes attributes from the tree being archived; strictly
+  speaking, this is an incompatible behaviour change, but is a good one.
+  Use --worktree-attributes option to allow it to read attributes from
+  the work tree as before (deprecated git-tar tree command always reads
+  attributes from the work tree).
+
+* git-bisect shows not just the number of remaining commits whose goodness
+  is unknown, but also shows the estimated number of remaining rounds.
+
+* You can give --date=<format> option to git-blame.
+
+* "git-branch -r" shows HEAD symref that points at a remote branch in
+  interest of each tracked remote repository.
+
+* "git-branch -v -v" is a new way to get list of names for branches and the
+  "upstream" branch for them.
+
+* git-config learned -e option to open an editor to edit the config file
+  directly.
+
+* git-clone runs post-checkout hook when run without --no-checkout.
+
+* git-difftool is now part of the officially supported command, primarily
+  maintained by David Aguilar.
+
+* git-for-each-ref learned a new "upstream" token.
+
+* git-format-patch can be told to use attachment with a new configuration,
+  format.attach.
+
+* git-format-patch can be told to produce deep or shallow message threads.
+
+* git-format-patch can be told to always add sign-off with a configuration
+  variable.
+
+* git-format-patch learned format.headers configuration to add extra
+  header fields to the output.  This behaviour is similar to the existing
+  --add-header=<header> option of the command.
+
+* git-format-patch gives human readable names to the attached files, when
+  told to send patches as attachments.
+
+* git-grep learned to highlight the found substrings in color.
+
+* git-imap-send learned to work around Thunderbird's inability to easily
+  disable format=flowed with a new configuration, imap.preformattedHTML.
+
+* git-rebase can be told to rebase the series even if your branch is a
+  descendant of the commit you are rebasing onto with --force-rebase
+  option.
+
+* git-rebase can be told to report diffstat with the --stat option.
+
+* Output from git-remote command has been vastly improved.
+
+* "git remote update --prune $remote" updates from the named remote and
+  then prunes stale tracking branches.
+
+* git-send-email learned --confirm option to review the Cc: list before
+  sending the messages out.
+
+(developers)
+
+* Test scripts can be run under valgrind.
+
+* Test scripts can be run with installed git.
+
+* Makefile learned 'coverage' option to run the test suites with
+  coverage tracking enabled.
+
+* Building the manpages with docbook-xsl between 1.69.1 and 1.71.1 now
+  requires setting DOCBOOK_SUPPRESS_SP to work around a docbook-xsl bug.
+  This workaround used to be enabled by default, but causes problems
+  with newer versions of docbook-xsl.  In addition, there are a few more
+  knobs you can tweak to work around issues with various versions of the
+  docbook-xsl package.  See comments in Documentation/Makefile for details.
+
+* Support for building and testing a subset of git on a system without a
+  working perl has been improved.
+
+
+Fixes since v1.6.2
+------------------
+
+All of the fixes in v1.6.2.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.2.X series.
+
+* "git-apply" rejected a patch that swaps two files (i.e. renames A to B
+  and B to A at the same time).  May need to be backported by cherry
+  picking d8c81df and then 7fac0ee).
+
+* The initial checkout did not read the attributes from the .gitattribute
+  file that is being checked out.
+
+* git-gc spent excessive amount of time to decide if an object appears
+  in a locally existing pack (if needed, backport by merging 69e020a).
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..5643e65
--- /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
+  presence 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.5.txt b/Documentation/RelNotes/1.6.4.5.txt
new file mode 100644 (file)
index 0000000..eb6307d
--- /dev/null
@@ -0,0 +1,20 @@
+Git v1.6.4.5 Release Notes
+==========================
+
+Fixes since v1.6.4.4
+--------------------
+
+ * Simplified base85 implementation.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git count-objects" did not handle packs larger than 4G.
+
+ * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
+   when --keep-dashdash was in effect.
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
+
+Other minor fixes and 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.2.txt b/Documentation/RelNotes/1.6.5.2.txt
new file mode 100644 (file)
index 0000000..aa7ccce
--- /dev/null
@@ -0,0 +1,19 @@
+GIT v1.6.5.2 Release Notes
+==========================
+
+Fixes since v1.6.5.1
+--------------------
+
+ * Installation of templates triggered a bug in busybox when using tar
+   implementation from it.
+
+ * "git add -i" incorrectly ignored paths that are already in the index
+   if they matched .gitignore patterns.
+
+ * "git describe --always" should have produced some output even there
+   were no tags in the repository, but it didn't.
+
+ * "git ls-files" when showing tracked files incorrectly paid attention
+   to the exclude patterns.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.5.3.txt b/Documentation/RelNotes/1.6.5.3.txt
new file mode 100644 (file)
index 0000000..b2fad1b
--- /dev/null
@@ -0,0 +1,63 @@
+Git v1.6.5.3 Release Notes
+==========================
+
+Fixes since v1.6.5.2
+--------------------
+
+ * info/grafts file didn't ignore trailing CR at the end of lines.
+
+ * Packages generated on newer FC were unreadable by older versions of
+   RPM as the new default is to use stronger hash.
+
+ * output from "git blame" was unreadable when the file ended in an
+   incomplete line.
+
+ * "git add -i/-p" didn't handle deletion of empty files correctly.
+
+ * "git clone" takes up to two parameters, but did not complain when
+   given more arguments than necessary and silently ignored them.
+
+ * "git cvsimport" did not read files given as command line arguments
+   correctly when it is run from a subdirectory.
+
+ * "git diff --color-words -U0" didn't work correctly.
+
+ * The handling of blank lines at the end of file by "git diff/apply
+   --whitespace" was inconsistent with the other kinds of errors.
+   They are now colored, warned against, and fixed the same way as others.
+
+ * There was no way to allow blank lines at the end of file without
+   allowing extra blanks at the end of lines.  You can use blank-at-eof
+   and blank-at-eol whitespace error class to specify them separately.
+   The old trailing-space error class is now a short-hand to set both.
+
+ * "-p" option to "git format-patch" was supposed to suppress diffstat
+   generation, but it was broken since 1.6.1.
+
+ * "git imap-send" did not compile cleanly with newer OpenSSL.
+
+ * "git help -a" outside of a git repository was broken.
+
+ * "git ls-files -i" was supposed to be inverse of "git ls-files" without -i
+   with respect to exclude patterns, but it was broken since 1.6.5.2.
+
+ * "git ls-remote" outside of a git repository over http was broken.
+
+ * "git rebase -i" gave bogus error message when the command word was
+   misspelled.
+
+ * "git receive-pack" that is run in response to "git push" did not run
+   garbage collection nor update-server-info, but in larger hosting sites,
+   these almost always need to be run.  To help site administrators, the
+   command now runs "gc --auto" and "u-s-i" by setting receive.autogc
+   and receive.updateserverinfo configuration variables, respectively.
+
+ * Release notes spelled the package name with incorrect capitalization.
+
+ * "gitweb" did not escape non-ascii characters correctly in the URL.
+
+ * "gitweb" showed "patch" link even for merge commits.
+
+ * "gitweb" showed incorrect links for blob line numbers in pathinfo mode.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.5.4.txt b/Documentation/RelNotes/1.6.5.4.txt
new file mode 100644 (file)
index 0000000..d3a2a3e
--- /dev/null
@@ -0,0 +1,32 @@
+Git v1.6.5.4 Release Notes
+==========================
+
+Fixes since v1.6.5.3
+--------------------
+
+ * "git help" (without argument) used to check if you are in a directory
+   under git control. There was no breakage in behaviour per-se, but this
+   was unnecessary.
+
+ * "git prune-packed" gave progress output even when its standard error is
+   not connected to a terminal; this caused cron jobs that run it to
+   produce crufts.
+
+ * "git pack-objects --all-progress" is an option to ask progress output
+   from write-object phase _if_ progress output were to be produced, and
+   shouldn't have forced the progress output.
+
+ * "git apply -p<n> --directory=<elsewhere>" did not work well for a
+   non-default value of n.
+
+ * "git merge foo HEAD" was misparsed as an old-style invocation of the
+   command and produced a confusing error message.  As it does not specify
+   any other branch to merge, it shouldn't be mistaken as such.  We will
+   remove the old style "git merge <message> HEAD <commit>..."  syntax in
+   future versions, but not in this release,
+
+ * "git merge -m <message> <branch>..." added the standard merge message
+   on its own after user-supplied message, which should have overridden the
+   standard one.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.5.5.txt b/Documentation/RelNotes/1.6.5.5.txt
new file mode 100644 (file)
index 0000000..ecfc57d
--- /dev/null
@@ -0,0 +1,49 @@
+Git v1.6.5.5 Release Notes
+==========================
+
+Fixes since v1.6.5.4
+--------------------
+
+ * Manual pages can be formatted with older xmlto again.
+
+ * GREP_OPTIONS exported from user's environment could have broken
+   our scripted commands.
+
+ * In configuration files, a few variables that name paths can begin with
+   ~/ and ~username/ and they are expanded as expected.  This is not a
+   bugfix but 1.6.6 will have this and without backporting users cannot
+   easily use the same ~/.gitconfig across versions.
+
+ * "git diff -B -M" did the same computation to hash lines of contents
+   twice, and held onto memory after it has used the data in it
+   unnecessarily before it freed.
+
+ * "git diff -B" and "git diff --dirstat" was not counting newly added
+   contents correctly.
+
+ * "git format-patch revisions... -- path" issued an incorrect error
+   message that suggested to use "--" on the command line when path
+   does not exist in the current work tree (it is a separate matter if
+   it makes sense to limit format-patch with pathspecs like that
+   without using the --full-diff option).
+
+ * "git grep -F -i StRiNg" did not work as expected.
+
+ * Enumeration of available merge strategies iterated over the list of
+   commands in a wrong way, sometimes producing an incorrect result.
+
+ * "git shortlog" did not honor the "encoding" header embedded in the
+   commit object like "git log" did.
+
+ * Reading progress messages that come from the remote side while running
+   "git pull" is given precedence over reading the actual pack data to
+   prevent garbled progress message on the user's terminal.
+
+ * "git rebase" got confused when the log message began with certain
+   strings that looked like Subject:, Date: or From: header.
+
+ * "git reset" accidentally run in .git/ directory checked out the
+   work tree contents in there.
+
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.5.6.txt b/Documentation/RelNotes/1.6.5.6.txt
new file mode 100644 (file)
index 0000000..a9eaf76
--- /dev/null
@@ -0,0 +1,23 @@
+Git v1.6.5.6 Release Notes
+==========================
+
+Fixes since v1.6.5.5
+--------------------
+
+ * "git add -p" had a regression since v1.6.5.3 that broke deletion of
+   non-empty files.
+
+ * "git archive -o o.zip -- Makefile" produced an archive in o.zip
+   but in POSIX tar format.
+
+ * Error message given to "git pull --rebase" when the user didn't give
+   enough clue as to what branch to integrate with still talked about
+   "merging with" the branch.
+
+ * Error messages given by "git merge" when the merge resulted in a
+   fast-forward still were in plumbing lingo, even though in v1.6.5
+   we reworded messages in other cases.
+
+ * The post-upload-hook run by upload-pack in response to "git fetch" has
+   been removed, due to security concerns (the hook first appeared in
+   1.6.5).
diff --git a/Documentation/RelNotes/1.6.5.7.txt b/Documentation/RelNotes/1.6.5.7.txt
new file mode 100644 (file)
index 0000000..dc5302c
--- /dev/null
@@ -0,0 +1,19 @@
+Git v1.6.5.7 Release Notes
+==========================
+
+Fixes since v1.6.5.6
+--------------------
+
+* If a user specifies a color for a <slot> (i.e. a class of things to show
+  in a particular color) that is known only by newer versions of git
+  (e.g. "color.diff.func" was recently added for upcoming 1.6.6 release),
+  an older version of git should just ignore them.  Instead we diagnosed
+  it as an error.
+
+* With help.autocorrect set to non-zero value, the logic to guess typos
+  in the subcommand name misfired and ran a random nonsense command.
+
+* If a command is run with an absolute path as a pathspec inside a bare
+  repository, e.g. "rev-list HEAD -- /home", the code tried to run
+  strlen() on NULL, which is the result of get_git_work_tree(), and
+  segfaulted.
diff --git a/Documentation/RelNotes/1.6.5.8.txt b/Documentation/RelNotes/1.6.5.8.txt
new file mode 100644 (file)
index 0000000..8b24beb
--- /dev/null
@@ -0,0 +1,28 @@
+Git v1.6.5.8 Release Notes
+==========================
+
+Fixes since v1.6.5.7
+--------------------
+
+* "git count-objects" did not handle packfiles that are bigger than 4G on
+  platforms with 32-bit off_t.
+
+* "git rebase -i" did not abort cleanly if it failed to launch the editor.
+
+* "git blame" did not work well when commit lacked the author name.
+
+* "git fast-import" choked when handling a tag that points at an object
+  that is not a commit.
+
+* "git reset --hard" did not work correctly when GIT_WORK_TREE environment
+  variable is used to point at the root of the true work tree.
+
+* "git grep" fed a buffer that is not NUL-terminated to underlying
+  regexec().
+
+* "git checkout -m other" while on a branch that does not have any commit
+  segfaulted, instead of failing.
+
+* "git branch -a other" should have diagnosed the command as an error.
+
+Other minor documentation updates are also included.
diff --git a/Documentation/RelNotes/1.6.5.9.txt b/Documentation/RelNotes/1.6.5.9.txt
new file mode 100644 (file)
index 0000000..bb469dd
--- /dev/null
@@ -0,0 +1,18 @@
+Git v1.6.5.9 Release Notes
+==========================
+
+Fixes since v1.6.5.8
+--------------------
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git blame -L $start,$end" segfaulted when too large $start was given.
+
+ * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
+   when --keep-dashdash was in effect.
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
+
+Other minor fixes and 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.1.txt b/Documentation/RelNotes/1.6.6.1.txt
new file mode 100644 (file)
index 0000000..f1d0a4a
--- /dev/null
@@ -0,0 +1,37 @@
+Git v1.6.6.1 Release Notes
+==========================
+
+Fixes since v1.6.6
+------------------
+
+ * "git blame" did not work well when commit lacked the author name.
+
+ * "git branch -a name" wasn't diagnosed as an error.
+
+ * "git count-objects" did not handle packfiles that are bigger than 4G on
+   platforms with 32-bit off_t.
+
+ * "git checkout -m other" while on a branch that does not have any commit
+   segfaulted, instead of failing.
+
+ * "git fast-import" choked when fed a tag that do not point at a
+   commit.
+
+ * "git grep" finding from work tree files could have fed garbage to
+   the underlying regexec(3).
+
+ * "git grep -L" didn't show empty files (they should never match, and
+   they should always appear in -L output as unmatching).
+
+ * "git rebase -i" did not abort cleanly if it failed to launch the editor.
+
+ * "git reset --hard" did not work correctly when GIT_WORK_TREE environment
+   variable is used to point at the root of the true work tree.
+
+ * http-backend was not listed in the command list in the documentation.
+
+ * Building on FreeBSD (both 7 and 8) needs OLD_ICONV set in the Makefile
+
+ * "git checkout -m some-branch" while on an unborn branch crashed.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.6.2.txt b/Documentation/RelNotes/1.6.6.2.txt
new file mode 100644 (file)
index 0000000..4eaddc0
--- /dev/null
@@ -0,0 +1,46 @@
+Git v1.6.6.2 Release Notes
+==========================
+
+Fixes since v1.6.6.1
+--------------------
+
+ * recursive merge didn't correctly diagnose its own programming errors,
+   and instead caused the caller to segfault.
+
+ * The new "smart http" aware clients probed the web servers to see if
+   they support smart http, but did not fall back to dumb http transport
+   correctly with some servers.
+
+ * Time based reflog syntax e.g. "@{yesterday}" didn't diagnose a misspelled
+   time specification and instead assumed "@{now}".
+
+ * "git archive HEAD -- no-such-directory" produced an empty archive
+   without complaining.
+
+ * "git blame -L start,end -- file" misbehaved when given a start that is
+   larger than the number of lines in the file.
+
+ * "git checkout -m" didn't correctly call custom merge backend supplied
+   by the end user.
+
+ * "git config -f <file>" misbehaved when run from a subdirectory.
+
+ * "git cvsserver" didn't like having regex metacharacters (e.g. '+') in
+   CVSROOT environment.
+
+ * "git fast-import" did not correctly handle large blobs that may
+   bust the pack size limit.
+
+ * "git gui" is supposed to work even when launched from inside a .git
+   directory.
+
+ * "git gui" misbehaved when applying a hunk that ends with deletion.
+
+ * "git imap-send" did not honor imap.preformattedHTML as documented.
+
+ * "git log" family incorrectly showed the commit notes unconditionally by
+   mistake, which was especially irritating when running "git log --oneline".
+
+ * "git status" shouldn't require an write access to the repository.
+
+Other minor documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.6.3.txt b/Documentation/RelNotes/1.6.6.3.txt
new file mode 100644 (file)
index 0000000..11483ac
--- /dev/null
@@ -0,0 +1,23 @@
+Git v1.6.6.3 Release Notes
+==========================
+
+Fixes since v1.6.6.2
+--------------------
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git bisect $path" did not correctly diagnose an error when given a
+   non-existent path.
+
+ * "git blame -L $start,$end" segfaulted when too large $start was given.
+
+ * "git imap-send" did not write draft box with CRLF line endings per RFC.
+
+ * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
+   when --keep-dashdash was in effect.
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
+
+Other minor fixes and documentation updates are included.
diff --git a/Documentation/RelNotes/1.6.6.txt b/Documentation/RelNotes/1.6.6.txt
new file mode 100644 (file)
index 0000000..c50b59c
--- /dev/null
@@ -0,0 +1,224 @@
+Git v1.6.6 Release Notes
+========================
+
+Notes on behaviour change
+-------------------------
+
+ * In this release, "git fsck" defaults to "git fsck --full" and
+   checks packfiles, and because of this it will take much longer to
+   complete than before.  If you prefer a quicker check only on loose
+   objects (the old default), you can say "git fsck --no-full".  This
+   has been supported by 1.5.4 and newer versions of git, so it is
+   safe to write it in your script even if you use slightly older git
+   on some of your machines.
+
+Preparing yourselves for compatibility issues in 1.7.0
+------------------------------------------------------
+
+In git 1.7.0, which is planned to be the release after 1.6.6, there will
+be a handful of behaviour changes that will break backward compatibility.
+
+These changes were discussed long time ago and existing behaviours have
+been identified as more problematic to the userbase than keeping them for
+the sake of backward compatibility.
+
+When necessary, a transition strategy for existing users has been designed
+not to force them running around setting configuration variables and
+updating their scripts in order to either keep the traditional behaviour
+or adjust to the new behaviour, on the day their sysadmin decides to install
+the new version of git.  When we switched from "git-foo" to "git foo" in
+1.6.0, even though the change had been advertised and the transition
+guide had been provided for a very long time, the users procrastinated
+during the entire transition period, and ended up panicking on the day
+their sysadmins updated their git installation.  We are trying to avoid
+repeating that unpleasantness in the 1.7.0 release.
+
+For changes decided to be in 1.7.0, commands that will be affected
+have been much louder to strongly discourage such procrastination, and
+they continue to be in this release.  If you have been using recent
+versions of git, you would have seen warnings issued when you used
+features whose behaviour will change, with a clear instruction on how
+to keep the existing behaviour if you want to.  You hopefully are
+already well prepared.
+
+Of course, we have also been giving "this and that will change in
+1.7.0; prepare yourselves" warnings in the release notes and
+announcement messages for the past few releases.  Let's see how well
+users will fare this time.
+
+ * "git push" into a branch that is currently checked out (i.e. pointed by
+   HEAD in a repository that is not bare) will be refused by default.
+
+   Similarly, "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.
+
+   Setting the configuration variables receive.denyCurrentBranch and
+   receive.denyDeleteCurrent to 'ignore' in the receiving repository
+   can be used to override these safety features.  Versions of git
+   since 1.6.2 have issued a loud warning when you tried to do these
+   operations without setting the configuration, so repositories of
+   people who still need to be able to perform such a push should
+   already have been future proofed.
+
+   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 process that already took place so far.
+
+ * "git send-email" will not make deep threads by default when sending a
+   patch series with more than two messages.  All messages will be sent
+   as a reply to the first message, i.e. cover letter.  Git 1.6.6 (this
+   release) will issue a warning about the upcoming default change, when
+   it uses the traditional "deep threading" behaviour as the built-in
+   default.  To squelch the warning but still use the "deep threading"
+   behaviour, give --chain-reply-to option or set sendemail.chainreplyto
+   to true.
+
+   It has been possible to configure send-email to send "shallow thread"
+   by setting sendemail.chainreplyto configuration variable to false.
+   The only thing 1.7.0 release will do is to change the default when
+   you haven't configured that variable.
+
+ * "git status" will not be "git commit --dry-run".  This change does not
+   affect you if you run the command without pathspec.
+
+   Nobody sane found the current behaviour of "git status Makefile" useful
+   nor meaningful, and it confused users.  "git commit --dry-run" has been
+   provided as a way to get the current behaviour of this command since
+   1.6.5.
+
+ * "git diff" traditionally treated various "ignore whitespace" options
+   only as a way to filter the patch output.  "git diff --exit-code -b"
+   exited with non-zero status even if all changes were about changing the
+   amount of whitespace and nothing else.  and "git diff -b" showed the
+   "diff --git" header line for such a change without patch text.
+
+   In 1.7.0, the "ignore whitespaces" will affect the semantics of the
+   diff operation itself.  A change that does not affect anything but
+   whitespaces will be reported with zero exit status when run with
+   --exit-code, and there will not be "diff --git" header for such a
+   change.
+
+
+Updates since v1.6.5
+--------------------
+
+(subsystems)
+
+ * various gitk updates including use of themed widgets under Tk 8.5,
+   Japanese translation, a fix to a bug when running "gui blame" from
+   a subdirectory, etc.
+
+ * various git-gui updates including new translations, wm states fixes,
+   Tk bug workaround after quitting, improved heuristics to trigger gc,
+   etc.
+
+ * various git-svn updates.
+
+ * "git fetch" over http learned a new mode that is different from the
+   traditional "dumb commit walker".
+
+(portability)
+
+ * imap-send can be built on mingw port.
+
+(performance)
+
+ * "git diff -B" has smaller memory footprint.
+
+(usability, bells and whistles)
+
+ * The object replace mechanism can be bypassed with --no-replace-objects
+   global option given to the "git" program.
+
+ * In configuration files, a few variables that name paths can begin with ~/
+   and ~username/ and they are expanded as expected.
+
+ * "git subcmd -h" now shows short usage help for many more subcommands.
+
+ * "git bisect reset" can reset to an arbitrary commit.
+
+ * "git checkout frotz" when there is no local branch "frotz" but there
+   is only one remote tracking branch "frotz" is taken as a request to
+   start the named branch at the corresponding remote tracking branch.
+
+ * "git commit -c/-C/--amend" can be told with a new "--reset-author" option
+   to ignore authorship information in the commit it is taking the message
+   from.
+
+ * "git describe" can be told to add "-dirty" suffix with "--dirty" option.
+
+ * "git diff" learned --submodule option to show a list of one-line logs
+   instead of differences between the commit object names.
+
+ * "git diff" learned to honor diff.color.func configuration to paint
+   function name hint printed on the hunk header "@@ -j,k +l,m @@" line
+   in the specified color.
+
+ * "git fetch" learned --all and --multiple options, to run fetch from
+   many repositories, and --prune option to remove remote tracking
+   branches that went stale.  These make "git remote update" and "git
+   remote prune" less necessary (there is no plan to remove "remote
+   update" nor "remote prune", though).
+
+ * "git fsck" by default checks the packfiles (i.e. "--full" is the
+   default); you can turn it off with "git fsck --no-full".
+
+ * "git grep" can use -F (fixed strings) and -i (ignore case) together.
+
+ * import-tars contributed fast-import frontend learned more types of
+   compressed tarballs.
+
+ * "git instaweb" knows how to talk with mod_cgid to apache2.
+
+ * "git log --decorate" shows the location of HEAD as well.
+
+ * "git log" and "git rev-list" learned to take revs and pathspecs from
+   the standard input with the new "--stdin" option.
+
+ * "--pretty=format" option to "log" family of commands learned:
+
+   . to wrap text with the "%w()" specifier.
+   . to show reflog information with "%g[sdD]" specifier.
+
+ * "git notes" command to annotate existing commits.
+
+ * "git merge" (and "git pull") learned --ff-only option to make it fail
+   if the merge does not result in a fast-forward.
+
+ * "git mergetool" learned to use p4merge.
+
+ * "git rebase -i" learned "reword" that acts like "edit" but immediately
+   starts an editor to tweak the log message without returning control to
+   the shell, which is done by "edit" to give an opportunity to tweak the
+   contents.
+
+ * "git send-email" can be told with "--envelope-sender=auto" to use the
+   same address as "From:" address as the envelope sender address.
+
+ * "git send-email" will issue a warning when it defaults to the
+   --chain-reply-to behaviour without being told by the user and
+   instructs to prepare for the change of the default in 1.7.0 release.
+
+ * In "git submodule add <repository> <path>", <path> is now optional and
+   inferred from <repository> the same way "git clone <repository>" does.
+
+ * "git svn" learned to read SVN 1.5+ and SVK merge tickets.
+
+ * "git svn" learned to recreate empty directories tracked only by SVN.
+
+ * "gitweb" can optionally render its "blame" output incrementally (this
+   requires JavaScript on the client side).
+
+ * Author names shown in gitweb output are links to search commits by the
+   author.
+
+Fixes since v1.6.5
+------------------
+
+All of the fixes in v1.6.5.X maintenance series are included in this
+release, unless otherwise noted.
diff --git a/Documentation/RelNotes/1.7.0.1.txt b/Documentation/RelNotes/1.7.0.1.txt
new file mode 100644 (file)
index 0000000..8ff5bca
--- /dev/null
@@ -0,0 +1,35 @@
+Git v1.7.0.1 Release Notes
+==========================
+
+Fixes since v1.7.0
+------------------
+
+ * In a freshly created repository "rev-parse HEAD^0" complained that
+   it is dangling symref, even though "rev-parse HEAD" didn't.
+
+ * "git show :no-such-name" tried to access the index without bounds
+   check, leading to a potential segfault.
+
+ * Message from "git cherry-pick" was harder to read and use than necessary
+   when it stopped due to conflicting changes.
+
+ * We referred to ".git/refs/" throughout the documentation when we
+   meant to talk about abstract notion of "ref namespace".  Because
+   people's repositories often have packed refs these days, this was
+   confusing.
+
+ * "git diff --output=/path/that/cannot/be/written" did not correctly
+   error out.
+
+ * "git grep -e -pattern-that-begin-with-dash paths..." could not be
+   spelled as "git grep -- -pattern-that-begin-with-dash paths..." which
+   would be a GNU way to use "--" as "end of options".
+
+ * "git grep" compiled with threading support tried to access an
+   uninitialized mutex on boxes with a single CPU.
+
+ * "git stash pop -q --index" failed because the unnecessary --index
+   option was propagated to "git stash drop" that is internally run at the
+   end.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.2.txt b/Documentation/RelNotes/1.7.0.2.txt
new file mode 100644 (file)
index 0000000..fcb46ca
--- /dev/null
@@ -0,0 +1,40 @@
+Git v1.7.0.2 Release Notes
+==========================
+
+Fixes since v1.7.0.1
+--------------------
+
+ * GIT_PAGER was not honored consistently by some scripted Porcelains, most
+   notably "git am".
+
+ * updating working tree files after telling git to add them to the
+   index and while it is still working created garbage object files in
+   the repository without diagnosing it as an error.
+
+ * "git bisect -- pathspec..." did not diagnose an error condition properly when
+   the simplification with given pathspec made the history empty.
+
+ * "git rev-list --cherry-pick A...B" now has an obvious optimization when the
+   histories haven't diverged (i.e. when one end is an ancestor of the other).
+
+ * "git diff --quiet -w" did not work as expected.
+
+ * "git fast-import" didn't work with a large input, as it lacked support
+   for producing the pack index in v2 format.
+
+ * "git imap-send" didn't use CRLF line endings over the imap protocol
+   when storing its payload to the draft box, violating RFC 3501.
+
+ * "git log --format='%w(x,y,z)%b'" and friends that rewrap message
+   has been optimized for utf-8 payload.
+
+ * Error messages generated on the receiving end did not come back to "git
+   push".
+
+ * "git status" in 1.7.0 lacked the optimization we used to have in 1.6.X series
+   to speed up scanning of large working tree.
+
+ * "gitweb" did not diagnose parsing errors properly while reading tis configuration
+   file.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.3.txt b/Documentation/RelNotes/1.7.0.3.txt
new file mode 100644 (file)
index 0000000..3b35573
--- /dev/null
@@ -0,0 +1,34 @@
+Git v1.7.0.3 Release Notes
+==========================
+
+Fixes since v1.7.0.2
+--------------------
+
+ * Object files are created in a more ACL friendly way in repositories
+   where group permission is ACL controlled.
+
+ * "git add -i" didn't handle a deleted path very well.
+
+ * "git blame" padded line numbers with one extra SP when the total number
+   of lines was one less than multiple of ten due to an off-by-one error.
+
+ * "git fetch --all/--multi" used to discard information for remotes that
+   are fetched earlier.
+
+ * "git log --author=me --grep=it" tried to find commits that have "it"
+   or are written by "me", instead of the ones that have "it" _and_ are
+   written by "me".
+
+ * "git log -g branch" misbehaved when there was no entries in the reflog
+   for the named branch.
+
+ * "git mailinfo" (hence "git am") incorrectly removed initial indent from
+   paragraphs.
+
+ * "git prune" and "git reflog" (hence "git gc" as well) didn't honor
+   an instruction never to expire by setting gc.reflogexpire to never.
+
+ * "git push" misbehaved when branch.<name>.merge was configured without
+   matching branch.<name>.remote.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.4.txt b/Documentation/RelNotes/1.7.0.4.txt
new file mode 100644 (file)
index 0000000..cf7f60e
--- /dev/null
@@ -0,0 +1,27 @@
+Git v1.7.0.4 Release Notes
+==========================
+
+Fixes since v1.7.0.3
+--------------------
+
+ * Optimized ntohl/htonl on big-endian machines were broken.
+
+ * Color values given to "color.<cmd>.<slot>" configuration can now have
+   more than one attributes (e.g. "bold ul").
+
+ * "git add -u nonexistent-path" did not complain.
+
+ * "git apply --whitespace=fix" didn't work well when an early patch in
+   a patch series adds trailing blank lines and a later one depended on
+   such a block of blank lines at the end.
+
+ * "git fast-export" didn't check error status and stop when marks file
+   cannot be opened.
+
+ * "git format-patch --ignore-if-in-upstream" gave unwarranted errors
+   when the range was empty, instead of silently finishing.
+
+ * "git remote prune" did not detect remote tracking refs that became
+   dangling correctly.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.5.txt b/Documentation/RelNotes/1.7.0.5.txt
new file mode 100644 (file)
index 0000000..3149c91
--- /dev/null
@@ -0,0 +1,26 @@
+Git v1.7.0.5 Release Notes
+==========================
+
+Fixes since v1.7.0.4
+--------------------
+
+ * "git daemon" failed to compile on platforms without sockaddr_storage type.
+
+ * Output from "git rev-list --pretty=oneline" was unparsable when a
+   commit did not have any message, which is abnormal but possible in a
+   repository converted from foreign scm.
+
+ * "git stash show <commit-that-is-not-a-stash>" gave an error message
+   that was not so useful.  Reworded the message to "<it> is not a
+   stash".
+
+ * Python scripts in contrib/ area now start with "#!/usr/bin/env python"
+   to honor user's PATH.
+
+ * "git imap-send" used to mistake any line that begins with "From " as a
+   message separator in format-patch output.
+
+ * Smart http server backend failed to report an internal server error and
+   infinitely looped instead after output pipe was closed.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.6.txt b/Documentation/RelNotes/1.7.0.6.txt
new file mode 100644 (file)
index 0000000..b2852b6
--- /dev/null
@@ -0,0 +1,13 @@
+Git v1.7.0.6 Release Notes
+==========================
+
+Fixes since v1.7.0.5
+--------------------
+
+ * "git diff --stat" used "int" to count the size of differences,
+   which could result in overflowing.
+
+ * "git rev-list --abbrev-commit" defaulted to 40-byte abbreviations, unlike
+   newer tools in the git toolset.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.7.txt b/Documentation/RelNotes/1.7.0.7.txt
new file mode 100644 (file)
index 0000000..d0cb7ca
--- /dev/null
@@ -0,0 +1,16 @@
+Git v1.7.0.7 Release Notes
+==========================
+
+Fixes since v1.7.0.6
+--------------------
+
+ * "make NO_CURL=NoThanks install" was broken.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+   that uses "~/" short-hand segfaulted when $HOME environment variable
+   was not set.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.0.8.txt b/Documentation/RelNotes/1.7.0.8.txt
new file mode 100644 (file)
index 0000000..7f05b48
--- /dev/null
@@ -0,0 +1,10 @@
+Git v1.7.0.8 Release Notes
+==========================
+
+This is primarily to backport support for the new "add.ignoreErrors"
+name given to the existing "add.ignore-errors" configuration variable.
+
+The next version, Git 1.7.4, and future versions, will support both
+old and incorrect name and the new corrected name, but without this
+backport, users who want to use the new name "add.ignoreErrors" in
+their repositories cannot use older versions of Git.
diff --git a/Documentation/RelNotes/1.7.0.9.txt b/Documentation/RelNotes/1.7.0.9.txt
new file mode 100644 (file)
index 0000000..bfb3166
--- /dev/null
@@ -0,0 +1,8 @@
+Git v1.7.0.9 Release Notes
+==========================
+
+Fixes since v1.7.0.8
+--------------------
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
diff --git a/Documentation/RelNotes/1.7.0.txt b/Documentation/RelNotes/1.7.0.txt
new file mode 100644 (file)
index 0000000..0bb8c0b
--- /dev/null
@@ -0,0 +1,214 @@
+Git v1.7.0 Release Notes
+========================
+
+Notes on behaviour change
+-------------------------
+
+ * "git push" into a branch that is currently checked out (i.e. pointed at by
+   HEAD in a repository that is not bare) is refused by default.
+
+   Similarly, "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.
+
+   Setting the configuration variables receive.denyCurrentBranch and
+   receive.denyDeleteCurrent to 'ignore' in the receiving repository
+   can be used to override these safety features.
+
+ * "git send-email" does not make deep threads by default when sending a
+   patch series with more than two messages.  All messages will be sent
+   as a reply to the first message, i.e. cover letter.
+
+   It has been possible already to configure send-email to send "shallow thread"
+   by setting sendemail.chainreplyto configuration variable to false.  The
+   only thing this release does is to change the default when you haven't
+   configured that variable.
+
+ * "git status" is not "git commit --dry-run" anymore.  This change does
+   not affect you if you run the command without argument.
+
+ * "git diff" traditionally treated various "ignore whitespace" options
+   only as a way to filter the patch output.  "git diff --exit-code -b"
+   exited with non-zero status even if all changes were about changing the
+   amount of whitespace and nothing else;  and "git diff -b" showed the
+   "diff --git" header line for such a change without patch text.
+
+   In this release, the "ignore whitespaces" options affect the semantics
+   of the diff operation.  A change that does not affect anything but
+   whitespaces is reported with zero exit status when run with
+   --exit-code, and there is no "diff --git" header for such a change.
+
+ * External diff and textconv helpers are now executed using the shell.
+   This makes them consistent with other programs executed by git, and
+   allows you to pass command-line parameters to the helpers. Any helper
+   paths containing spaces or other metacharacters now need to be
+   shell-quoted.  The affected helpers are GIT_EXTERNAL_DIFF in the
+   environment, and diff.*.command and diff.*.textconv in the config
+   file.
+
+ * The --max-pack-size argument to 'git repack', 'git pack-objects', and
+   'git fast-import' was assuming the provided size to be expressed in MiB,
+   unlike the corresponding config variable and other similar options accepting
+   a size value.  It is now expecting a size expressed in bytes, with a possible
+   unit suffix of 'k', 'm', or 'g'.
+
+Updates since v1.6.6
+--------------------
+
+(subsystems)
+
+ * "git fast-import" updates; adds "option" and "feature" to detect the
+   mismatch between fast-import and the frontends that produce the input
+   stream.
+
+ * "git svn" support of subversion "merge tickets" and miscellaneous fixes.
+
+ * "gitk" and "git gui" translation updates.
+
+ * "gitweb" updates (code clean-up, load checking etc.)
+
+(portability)
+
+ * Some more MSVC portability patches for msysgit port.
+
+ * Minimum Pthreads emulation for msysgit port.
+
+(performance)
+
+ * More performance improvement patches for msysgit port.
+
+(usability, bells and whistles)
+
+ * More commands learned "--quiet" and "--[no-]progress" options.
+
+ * Various commands given by the end user (e.g. diff.type.textconv,
+   and GIT_EDITOR) can be specified with command line arguments.  E.g. it
+   is now possible to say "[diff "utf8doc"] textconv = nkf -w".
+
+ * "sparse checkout" feature allows only part of the work tree to be
+   checked out.
+
+ * HTTP transfer can use authentication scheme other than basic
+   (i.e./e.g. digest).
+
+ * Switching from a version of superproject that used to have a submodule
+   to another version of superproject that no longer has it did not remove
+   the submodule directory when it should (namely, when you are not
+   interested in the submodule at all and didn't clone/checkout).
+
+ * A new attribute conflict-marker-size can be used to change the size of
+   the conflict markers from the default 7; this is useful when tracked
+   contents (e.g. git-merge documentation) have strings that resemble the
+   conflict markers.
+
+ * A new syntax "<branch>@{upstream}" can be used on the command line to
+   substitute the name of the "upstream" of the branch.  Missing branch
+   defaults to the current branch, so "git fetch && git merge @{upstream}"
+   will be equivalent to "git pull".
+
+ * "git am --resolved" has a synonym "git am --continue".
+
+ * "git branch --set-upstream" can be used to update the (surprise!) upstream,
+   i.e. where the branch is supposed to pull and merge from (or rebase onto).
+
+ * "git checkout A...B" is a way to detach HEAD at the merge base between
+   A and B.
+
+ * "git checkout -m path" to reset the work tree file back into the
+   conflicted state works even when you already ran "git add path" and
+   resolved the conflicts.
+
+ * "git commit --date='<date>'" can be used to override the author date
+   just like "git commit --author='<name> <email>'" can be used to
+   override the author identity.
+
+ * "git commit --no-status" can be used to omit the listing of the index
+   and the work tree status in the editor used to prepare the log message.
+
+ * "git commit" warns a bit more aggressively until you configure user.email,
+   whose default value almost always is not (and fundamentally cannot be)
+   what you want.
+
+ * "git difftool" has been extended to make it easier to integrate it
+   with gitk.
+
+ * "git fetch --all" can now be used in place of "git remote update".
+
+ * "git grep" does not rely on external grep anymore.  It can use more than
+   one thread to accelerate the operation.
+
+ * "git grep" learned "--quiet" option.
+
+ * "git log" and friends learned "--glob=heads/*" syntax that is a more
+   flexible way to complement "--branches/--tags/--remotes".
+
+ * "git merge" learned to pass options specific to strategy-backends.  E.g.
+
+    - "git merge -Xsubtree=path/to/directory" can be used to tell the subtree
+      strategy how much to shift the trees explicitly.
+
+    - "git merge -Xtheirs" can be used to auto-merge as much as possible,
+      while discarding your own changes and taking merged version in
+      conflicted regions.
+
+ * "git push" learned "git push origin --delete branch", a syntactic sugar
+   for "git push origin :branch".
+
+ * "git push" learned "git push --set-upstream origin forker:forkee" that
+   lets you configure your "forker" branch to later pull from "forkee"
+   branch at "origin".
+
+ * "git rebase --onto A...B" means the history is replayed on top of the
+   merge base between A and B.
+
+ * "git rebase -i" learned new action "fixup" that squashes the change
+   but does not affect existing log message.
+
+ * "git rebase -i" also learned --autosquash option that is useful
+   together with the new "fixup" action.
+
+ * "git remote" learned set-url subcommand that updates (surprise!) url
+   for an existing remote nickname.
+
+ * "git rerere" learned "forget path" subcommand.  Together with "git
+   checkout -m path" it will be useful when you recorded a wrong
+   resolution.
+
+ * Use of "git reset --merge" has become easier when resetting away a
+   conflicted mess left in the work tree.
+
+ * "git rerere" had rerere.autoupdate configuration but there was no way
+   to countermand it from the command line; --no-rerere-autoupdate option
+   given to "merge", "revert", etc. fixes this.
+
+ * "git status" learned "-s(hort)" output format.
+
+(developers)
+
+ * The infrastructure to build foreign SCM interface has been updated.
+
+ * Many more commands are now built-in.
+
+ * THREADED_DELTA_SEARCH is no more.  If you build with threads, delta
+   compression will always take advantage of it.
+
+Fixes since v1.6.6
+------------------
+
+All of the fixes in v1.6.6.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git branch -d branch" used to refuse deleting the branch even when
+   the branch is fully merged to its upstream branch if it is not merged
+   to the current branch.  It now deletes it in such a case.
+
+ * "filter-branch" command incorrectly said --prune-empty and --filter-commit
+   were incompatible; the latter should be read as --commit-filter.
+
+ * When using "git status" or asking "git diff" to compare the work tree
+   with something, they used to consider that a checked-out submodule with
+   uncommitted changes is not modified; this could cause people to forget
+   committing these changes in the submodule before committing in the
+   superproject. They now consider such a change as a modification and
+   "git diff" will append a "-dirty" to the work tree side when generating
+   patch output or when used with the --submodule option.
diff --git a/Documentation/RelNotes/1.7.1.1.txt b/Documentation/RelNotes/1.7.1.1.txt
new file mode 100644 (file)
index 0000000..3f6b314
--- /dev/null
@@ -0,0 +1,96 @@
+Git v1.7.1.1 Release Notes
+==========================
+
+Fixes since v1.7.1
+------------------
+
+ * Authentication over http transport can now be made lazily, in that the
+   request can first go to a URL without username, get a 401 response and
+   then the client will ask for the username to use.
+
+ * We used to mistakenly think "../work" is a subdirectory of the current
+   directory when we are in "../work-xyz".
+
+ * The attribute mechanism now allows an entry that uses an attribute
+   macro that set/unset one attribute, immediately followed by an
+   overriding setting; this makes attribute macros much easier to use.
+
+ * We didn't recognize timezone "Z" as a synonym for "UTC" (75b37e70).
+
+ * In 1.7.0, read-tree and user commands that use the mechanism such as
+   checkout and merge were fixed to handle switching between branches one
+   of which has a file while the other has a directory at the same path
+   correctly even when there are some "confusing" pathnames in them.  But
+   the algorithm used for this fix was suboptimal and had a terrible
+   performance degradation especially in larger trees.
+
+ * "git am -3" did not show diagnosis when the patch in the message was corrupt.
+
+ * After "git apply --whitespace=fix" removed trailing blank lines in an
+   patch in a patch series, it failed to apply later patches that depend
+   on the presence of such blank lines.
+
+ * "git bundle --stdin" segfaulted.
+
+ * "git checkout" and "git rebase" overwrote paths that are marked "assume
+   unchanged".
+
+ * "git commit --amend" on a commit with an invalid author-name line that
+   lacks the display name didn't work.
+
+ * "git describe" did not tie-break tags that point at the same commit
+   correctly; newer ones are preferred by paying attention to the
+   tagger date now.
+
+ * "git diff" used to tell underlying xdiff machinery to work very hard to
+   minimize the output, but this often was spending too many extra cycles
+   for very little gain.
+
+ * "git diff --color" did not paint extended diff headers per line
+   (i.e. the coloring escape sequence didn't end at the end of line),
+   which confused "less -R".
+
+ * "git fetch" over HTTP verifies the downloaded packfiles more robustly.
+
+ * The memory usage by "git index-pack" (run during "git fetch" and "git
+   push") got leaner.
+
+ * "GIT_DIR=foo.git git init --bare bar.git" created foo.git instead of bar.git.
+
+ * "git log --abbrev=$num --format='%h' ignored --abbrev=$num.
+
+ * "git ls-files ../out/side/cwd" refused to work.
+
+ * "git merge --log" used to replace the custom message given by "-m" with
+   the shortlog, instead of appending to it.
+
+ * "git notes copy" without any other argument segfaulted.
+
+ * "git pull" accepted "--dry-run", gave it to underlying "git fetch" but
+   ignored the option itself, resulting in a bogus attempt to merge
+   unrelated commit.
+
+ * "git rebase" did not faithfully reproduce a malformed author ident, that
+   is often seen in a repository converted from foreign SCMs.
+
+ * "git reset --hard" started from a wrong directory and a working tree in
+   a nonstandard location is in use got confused.
+
+ * "git send-email" lacked a way to specify the domainname used in the
+   EHLO/HELO exchange, causing rejected connection from picky servers.
+   It learned --smtp-domain option to solve this issue.
+
+ * "git send-email" did not declare a content-transfer-encoding and
+   content-type even when its payload needs to be sent in 8-bit.
+
+ * "git show -C -C" and other corner cases lost diff metainfo output
+   in 1.7.0.
+
+ * "git stash" incorrectly lost paths in the working tree that were
+   previously removed from the index.
+
+ * "git status" stopped refreshing the index by mistake in 1.7.1.
+
+ * "git status" showed excess "hints" even when advice.statusHints is set to false.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.1.2.txt b/Documentation/RelNotes/1.7.1.2.txt
new file mode 100644 (file)
index 0000000..61ba14e
--- /dev/null
@@ -0,0 +1,28 @@
+Git v1.7.1.2 Release Notes
+==========================
+
+Fixes since v1.7.1.1
+--------------------
+
+ * "git commit" did not honor GIT_REFLOG_ACTION environment variable, resulting
+   reflog messages for cherry-pick and revert actions to be recorded as "commit".
+
+ * "git clone/fetch/pull" issued an incorrect error message when a ref and
+   a symref that points to the ref were updated at the same time.  This
+   obviously would update them to the same value, and should not result in
+   an error condition.
+
+ * "git diff" inside a tree with many pathnames that have certain
+   characters has become very slow in 1.7.0 by mistake.
+
+ * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
+   when --keep-dashdash was in effect.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+   that uses "~/" short-hand segfaulted when $HOME environment variable
+   was not set.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.1.3.txt b/Documentation/RelNotes/1.7.1.3.txt
new file mode 100644 (file)
index 0000000..5b18518
--- /dev/null
@@ -0,0 +1,10 @@
+Git v1.7.1.3 Release Notes
+==========================
+
+This is primarily to backport support for the new "add.ignoreErrors"
+name given to the existing "add.ignore-errors" configuration variable.
+
+The next version, Git 1.7.4, and future versions, will support both
+old and incorrect name and the new corrected name, but without this
+backport, users who want to use the new name "add.ignoreErrors" in
+their repositories cannot use older versions of Git.
diff --git a/Documentation/RelNotes/1.7.1.4.txt b/Documentation/RelNotes/1.7.1.4.txt
new file mode 100644 (file)
index 0000000..7c734b4
--- /dev/null
@@ -0,0 +1,8 @@
+Git v1.7.1.4 Release Notes
+==========================
+
+Fixes since v1.7.1.3
+--------------------
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
diff --git a/Documentation/RelNotes/1.7.1.txt b/Documentation/RelNotes/1.7.1.txt
new file mode 100644 (file)
index 0000000..9d89fed
--- /dev/null
@@ -0,0 +1,89 @@
+Git v1.7.1 Release Notes
+========================
+
+Updates since v1.7.0
+--------------------
+
+ * Eric Raymond is the maintainer of updated CIAbot scripts, in contrib/.
+
+ * gitk updates.
+
+ * Some commands (e.g. svn and http interfaces) that interactively ask
+   for a password can be told to use an external program given via
+   GIT_ASKPASS.
+
+ * Conflict markers that lead the common ancestor in diff3-style output
+   now have a label, which hopefully would help third-party tools that
+   expect one.
+
+ * Comes with an updated bash-completion script.
+
+ * "git am" learned "--keep-cr" option to handle inputs that are
+   a mixture of changes to files with and without CRLF line endings.
+
+ * "git cvsimport" learned -R option to leave revision mapping between
+   CVS revisions and resulting git commits.
+
+ * "git diff --submodule" notices and describes dirty submodules.
+
+ * "git for-each-ref" learned %(symref), %(symref:short) and %(flag)
+   tokens.
+
+ * "git hash-object --stdin-paths" can take "--no-filters" option now.
+
+ * "git init" can be told to look at init.templatedir configuration
+   variable (obviously that has to come from either /etc/gitconfig or
+   $HOME/.gitconfig).
+
+ * "git grep" learned "--no-index" option, to search inside contents that
+   are not managed by git.
+
+ * "git grep" learned --color=auto/always/never.
+
+ * "git grep" learned to paint filename and line-number in colors.
+
+ * "git log -p --first-parent -m" shows one-parent diff for merge
+   commits, instead of showing combined diff.
+
+ * "git merge-file" learned to use custom conflict marker size and also
+   to use the "union merge" behaviour.
+
+ * "git notes" command has been rewritten in C and learned many commands
+   and features to help you carry notes forward across rebases and amends.
+
+ * "git request-pull" identifies the commit the request is relative to in
+   a more readable way.
+
+ * "git reset" learned "--keep" option that lets you discard commits
+   near the tip while preserving your local changes in a way similar
+   to how "git checkout branch" does.
+
+ * "git status" notices and describes dirty submodules.
+
+ * "git svn" should work better when interacting with repositories
+   with CRLF line endings.
+
+ * "git imap-send" learned to support CRAM-MD5 authentication.
+
+ * "gitweb" installation procedure can use "minified" js/css files
+   better.
+
+ * Various documentation updates.
+
+Fixes since v1.7.0
+------------------
+
+All of the fixes in v1.7.0.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git add frotz/nitfol" did not complain when the entire frotz/ directory
+   was ignored.
+
+ * "git diff --stat" used "int" to count the size of differences,
+   which could result in overflowing.
+
+ * "git rev-list --pretty=oneline" didn't terminate a record with LF for
+   commits without any message.
+
+ * "git rev-list --abbrev-commit" defaulted to 40-byte abbreviations, unlike
+   newer tools in the git toolset.
diff --git a/Documentation/RelNotes/1.7.2.1.txt b/Documentation/RelNotes/1.7.2.1.txt
new file mode 100644 (file)
index 0000000..1103c47
--- /dev/null
@@ -0,0 +1,25 @@
+Git v1.7.2.1 Release Notes
+==========================
+
+Fixes since v1.7.2
+------------------
+
+ * "git instaweb" wasn't useful when your Apache was installed under a
+   name other than apache2 (e.g. "httpd").
+
+ * Similarly, "git web--browse" (invoked by "git help -w") learned that
+   chrome browser is sometimes called google-chrome.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+   that uses "~/" short-hand segfaulted when $HOME environment variable
+   was not set.
+
+ * Documentation on Cygwin failed to build.
+
+ * The error message from "git pull blarg" when 'blarg' is an unknown
+   remote name has been improved.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.2.2.txt b/Documentation/RelNotes/1.7.2.2.txt
new file mode 100644 (file)
index 0000000..71eb6a8
--- /dev/null
@@ -0,0 +1,22 @@
+Git v1.7.2.2 Release Notes
+==========================
+
+Fixes since v1.7.2.1
+--------------------
+
+ * Object transfer over smart http transport deadlocked the client when
+   the remote HTTP server returned a failure, instead of erroring it out.
+
+ * git-gui honors custom textconv filters when showing diff and blame;
+
+ * git diff --relative=subdir (without the necessary trailing /) did not
+   work well;
+
+ * "git diff-files -p --submodule" was recently broken;
+
+ * "git checkout -b n ':/token'" did not work;
+
+ * "git index-pack" (hence "git fetch/clone/pull/push") enabled the object
+   replacement machinery by mistake (it never should have);
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.2.3.txt b/Documentation/RelNotes/1.7.2.3.txt
new file mode 100644 (file)
index 0000000..610960c
--- /dev/null
@@ -0,0 +1,39 @@
+Git v1.7.2.3 Release Notes
+==========================
+
+Fixes since v1.7.2.2
+--------------------
+
+ * When people try insane things such as delta-compressing 4GiB files, we
+   threw an assertion failure.
+
+ * "git archive" gave the full commit ID for "$Format:%h$".
+
+ * "git fetch --tags" did not fetch tags when remote.<nick>.tagopt was set
+   to --no-tags.  The command line option now overrides the configuration
+   setting.
+
+ * "git for-each-ref --format='%(objectname:short)'" has been completely
+   broken for a long time.
+
+ * "git gc" incorrectly pruned a rerere record that was created long
+   time ago but still is actively and repeatedly used.
+
+ * "git log --follow -M -p" was seriously broken in 1.7.2, reporting
+   assertion failure.
+
+ * Running "git log" with an incorrect option started pager nevertheless,
+   forcing the user to dismiss it.
+
+ * "git rebase" did not work well when the user has diff.renames
+   configuration variable set.
+
+ * An earlier (and rather old) fix to "git rebase" against a rebased
+   upstream broke a more normal, non rebased upstream case rather badly,
+   attempting to re-apply patches that are already accepted upstream.
+
+ * "git submodule sync" forgot to update the superproject's config file
+   when submodule URL changed.
+
+ * "git pack-refs --all --prune" did not remove a directory that has
+   become empty.
diff --git a/Documentation/RelNotes/1.7.2.4.txt b/Documentation/RelNotes/1.7.2.4.txt
new file mode 100644 (file)
index 0000000..f7950a4
--- /dev/null
@@ -0,0 +1,10 @@
+Git v1.7.2.4 Release Notes
+==========================
+
+This is primarily to backport support for the new "add.ignoreErrors"
+name given to the existing "add.ignore-errors" configuration variable.
+
+The next version, Git 1.7.4, and future versions, will support both
+old and incorrect name and the new corrected name, but without this
+backport, users who want to use the new name "add.ignoreErrors" in
+their repositories cannot use older versions of Git.
diff --git a/Documentation/RelNotes/1.7.2.5.txt b/Documentation/RelNotes/1.7.2.5.txt
new file mode 100644 (file)
index 0000000..bf976c4
--- /dev/null
@@ -0,0 +1,8 @@
+Git v1.7.2.5 Release Notes
+==========================
+
+Fixes since v1.7.2.4
+--------------------
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
diff --git a/Documentation/RelNotes/1.7.2.txt b/Documentation/RelNotes/1.7.2.txt
new file mode 100644 (file)
index 0000000..15cf011
--- /dev/null
@@ -0,0 +1,151 @@
+Git v1.7.2 Release Notes
+========================
+
+Updates since v1.7.1
+--------------------
+
+ * core.eol configuration and text/eol attributes are the new way to control
+   the end of line conventions for files in the working tree.
+
+ * core.autocrlf has been made safer - it will now only handle line
+   endings for new files and files that are LF-only in the
+   repository. To normalize content that has been checked in with
+   CRLF, use the new eol/text attributes.
+
+ * The whitespace rules used in "git apply --whitespace" and "git diff"
+   gained a new member in the family (tab-in-indent) to help projects with
+   policy to indent only with spaces.
+
+ * When working from a subdirectory, by default, git does not look for its
+   metadirectory ".git" across filesystems, primarily to help people who
+   have invocations of git in their custom PS1 prompts, as being outside
+   of a git repository would look for ".git" all the way up to the root
+   directory, and NFS mounts are often slow.  DISCOVERY_ACROSS_FILESYSTEM
+   environment variable can be used to tell git not to stop at a
+   filesystem boundary.
+
+ * Usage help messages generated by parse-options library (i.e. most
+   of the Porcelain commands) are sent to the standard output now.
+
+ * ':/<string>' notation to look for a commit now takes regular expression
+   and it is not anchored at the beginning of the commit log message
+   anymore (this is a backward incompatible change).
+
+ * "git" wrapper learned "-c name=value" option to override configuration
+   variable from the command line.
+
+ * Improved portability for various platforms including older SunOS,
+   HP-UX 10/11, AIX, Tru64, etc. and platforms with Python 2.4.
+
+ * The message from "git am -3" has been improved when conflict
+   resolution ended up making the patch a no-op.
+
+ * "git blame" applies the textconv filter to the contents it works
+   on, when available.
+
+ * "git checkout --orphan newbranch" is similar to "-b newbranch" but
+   prepares to create a root commit that is not connected to any existing
+   commit.
+
+ * "git cherry-pick" learned to pick a range of commits
+   (e.g. "cherry-pick A..B" and "cherry-pick --stdin"), so did "git
+   revert"; these do not support the nicer sequencing control "rebase
+   [-i]" has, though.
+
+ * "git cherry-pick" and "git revert" learned --strategy option to specify
+   the merge strategy to be used when performing three-way merges.
+
+ * "git cvsserver" can be told to use pserver; its password file can be
+   stored outside the repository.
+
+ * The output from the textconv filter used by "git diff" can be cached to
+   speed up their reuse.
+
+ * "git diff --word-diff=<mode>" extends the existing "--color-words"
+   option, making it more useful in color-challenged environments.
+
+ * The regexp to detect function headers used by "git diff" for PHP has
+   been enhanced for visibility modifiers (public, protected, etc.) to
+   better support PHP5.
+
+ * "diff.noprefix" configuration variable can be used to implicitly
+   ask for "diff --no-prefix" behaviour.
+
+ * "git for-each-ref" learned "%(objectname:short)" that gives the object
+   name abbreviated.
+
+ * "git format-patch" learned --signature option and format.signature
+   configuration variable to customize the e-mail signature used in the
+   output.
+
+ * Various options to "git grep" (e.g. --count, --name-only) work better
+   with binary files.
+
+ * "git grep" learned "-Ovi" to open the files with hits in your editor.
+
+ * "git help -w" learned "chrome" and "chromium" browsers.
+
+ * "git log --decorate" shows commit decorations in various colours.
+
+ * "git log --follow <path>" follows across copies (it used to only follow
+   renames).  This may make the processing more expensive.
+
+ * "git log --pretty=format:<template>" specifier learned "% <something>"
+   magic that inserts a space only when %<something> expands to a
+   non-empty string; this is similar to "%+<something>" magic, but is
+   useful in a context to generate a single line output.
+
+ * "git notes prune" learned "-n" (dry-run) and "-v" options, similar to
+   what "git prune" has.
+
+ * "git patch-id" can be fed a mbox without getting confused by the
+   signature line in the format-patch output.
+
+ * "git remote" learned "set-branches" subcommand.
+
+ * "git rev-list A..B" learned --ancestry-path option to further limit
+   the result to the commits that are on the ancestry chain between A and
+   B (i.e. commits that are not descendants of A are excluded).
+
+ * "git show -5" is equivalent to "git show --do-walk 5"; this is similar
+   to the update to make "git show master..next" walk the history,
+   introduced in 1.6.4.
+
+ * "git status [-s] --ignored" can be used to list ignored paths.
+
+ * "git status -s -b" shows the current branch in the output.
+
+ * "git status" learned "--ignore-submodules" option.
+
+ * Various "gitweb" enhancements and clean-ups, including syntax
+   highlighting, "plackup" support for instaweb, .fcgi suffix to run
+   it as FastCGI script, etc.
+
+ * The test harness has been updated to produce TAP-friendly output.
+
+ * Many documentation improvement patches are also included.
+
+
+Fixes since v1.7.1
+------------------
+
+All of the fixes in v1.7.1.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * We didn't URL decode "file:///path/to/repo" correctly when path/to/repo
+   had percent-encoded characters (638794c, 9d2e942, ce83eda, 3c73a1d).
+
+ * "git clone" did not configure remote.origin.url correctly for bare
+   clones (df61c889).
+
+ * "git diff --graph" works better with "--color-words" and other options
+   (81fa024..4297c0a).
+
+ * "git diff" could show ambiguous abbreviation of blob object names on
+   its "index" line (3e5a188).
+
+ * "git reset --hard" started from a wrong directory and a working tree in
+   a nonstandard location is in use got confused (560fb6a1).
+
+ * "git read-tree -m A B" used to switch to branch B while retaining
+   local changes added an incorrect cache-tree information (b1f47514).
diff --git a/Documentation/RelNotes/1.7.3.1.txt b/Documentation/RelNotes/1.7.3.1.txt
new file mode 100644 (file)
index 0000000..002c93b
--- /dev/null
@@ -0,0 +1,14 @@
+Git v1.7.3.1 Release Notes
+==========================
+
+Fixes since v1.7.3
+------------------
+
+ * "git stash show stash@{$n}" was accidentally broken in 1.7.3 ("git
+   stash show" without any argument still worked, though).
+
+ * "git stash branch $branch stash@{$n}" was accidentally broken in
+   1.7.3 and started dropping the named stash even when branch creation
+   failed.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.3.2.txt b/Documentation/RelNotes/1.7.3.2.txt
new file mode 100644 (file)
index 0000000..5c93b85
--- /dev/null
@@ -0,0 +1,5 @@
+Git v1.7.3.2 Release Notes
+==========================
+
+This is primarily to push out many documentation fixes accumulated since
+the 1.7.3.1 release.
diff --git a/Documentation/RelNotes/1.7.3.3.txt b/Documentation/RelNotes/1.7.3.3.txt
new file mode 100644 (file)
index 0000000..9b2b244
--- /dev/null
@@ -0,0 +1,54 @@
+Git v1.7.3.3 Release Notes
+==========================
+
+In addition to the usual fixes, this release also includes support for
+the new "add.ignoreErrors" name given to the existing "add.ignore-errors"
+configuration variable.
+
+The next version, Git 1.7.4, and future versions, will support both
+old and incorrect name and the new corrected name, but without this
+backport, users who want to use the new name "add.ignoreErrors" in
+their repositories cannot use older versions of Git.
+
+Fixes since v1.7.3.2
+--------------------
+
+ * "git apply" segfaulted when a bogus input is fed to it.
+
+ * Running "git cherry-pick --ff" on a root commit segfaulted.
+
+ * "diff", "blame" and friends incorrectly applied textconv filters to
+   symlinks.
+
+ * Highlighting of whitespace breakage in "diff" output was showing
+   incorrect amount of whitespaces when blank-at-eol is set and the line
+   consisted only of whitespaces and a TAB.
+
+ * "diff" was overly inefficient when trying to find the line to use for
+   the function header (i.e. equivalent to --show-c-function of GNU diff).
+
+ * "git imap-send" depends on libcrypto but our build rule relied on the
+   linker to implicitly link it via libssl, which was wrong.
+
+ * "git merge-file" can be called from within a subdirectory now.
+
+ * "git repack -f" expanded and recompressed non-delta objects in the
+   existing pack, which was wasteful.  Use new "-F" option if you really
+   want to (e.g. when changing the pack.compression level).
+
+ * "git rev-list --format="...%x00..." incorrectly chopped its output
+   at NUL.
+
+ * "git send-email" did not correctly remove duplicate mail addresses from
+   the Cc: header that appear on the To: header.
+
+ * The completion script (in contrib/completion) ignored lightweight tags
+   in __git_ps1().
+
+ * "git-blame" mode (in contrib/emacs) didn't say (require 'format-spec)
+   even though it depends on it; it didn't work with Emacs 22 or older
+   unless Gnus is used.
+
+ * "git-p4" (in contrib/) did not correctly handle deleted files.
+
+Other minor fixes and documentation updates are also included.
diff --git a/Documentation/RelNotes/1.7.3.4.txt b/Documentation/RelNotes/1.7.3.4.txt
new file mode 100644 (file)
index 0000000..e57f7c1
--- /dev/null
@@ -0,0 +1,45 @@
+Git v1.7.3.4 Release Notes
+==========================
+
+Fixes since v1.7.3.3
+--------------------
+
+ * Smart HTTP transport used to incorrectly retry redirected POST
+   request with GET request.
+
+ * "git apply" did not correctly handle patches that only change modes
+   if told to apply while stripping leading paths with -p option.
+
+ * "git apply" can deal with patches with timezone formatted with a
+   colon between the hours and minutes part (e.g. "-08:00" instead of
+   "-0800").
+
+ * "git checkout" removed an untracked file "foo" from the working
+   tree when switching to a branch that contains a tracked path
+   "foo/bar".  Prevent this, just like the case where the conflicting
+   path were "foo" (c752e7f..7980872d).
+
+ * "git cherry-pick" or "git revert" refused to work when a path that
+   would be modified by the operation was stat-dirty without a real
+   difference in the contents of the file.
+
+ * "git diff --check" reported an incorrect line number for added
+   blank lines at the end of file.
+
+ * "git imap-send" failed to build under NO_OPENSSL.
+
+ * Setting log.decorate configuration variable to "0" or "1" to mean
+   "false" or "true" did not work.
+
+ * "git push" over dumb HTTP protocol did not work against WebDAV
+   servers that did not terminate a collection name with a slash.
+
+ * "git tag -v" did not work with GPG signatures in rfc1991 mode.
+
+ * The post-receive-email sample hook was accidentally broken in 1.7.3.3
+   update.
+
+ * "gitweb" can sometimes be tricked into parrotting a filename argument
+   given in a request without properly quoting.
+
+Other minor fixes and documentation updates are also included.
diff --git a/Documentation/RelNotes/1.7.3.5.txt b/Documentation/RelNotes/1.7.3.5.txt
new file mode 100644 (file)
index 0000000..40f3ba5
--- /dev/null
@@ -0,0 +1,34 @@
+Git 1.7.3.5 Release Notes
+=========================
+
+ * The xfuncname pattern used by "git diff" and "git grep" to show the
+   last notable line in context were broken for python and ruby for a long
+   time.
+
+ * "git merge" into an unborn branch removed an untracked file "foo" from
+   the working tree when merged branch had "foo" (this fix was already in
+   1.7.3.3 but was omitted from the release notes by mistake).
+
+ * "git status -s" did not quote unprintable characters in paths as
+   documented.
+
+ * "git am --abort" used to always reset to the commit at the beginning of
+   the last "am" invocation that has stopped, losing any unrelated commits
+   that may have been made since then.  Now it refrains from doing so and
+   instead issues a warning.
+
+ * "git blame" incorrectly reused bogusly cached result of textconv
+   filter for files from the working tree.
+
+ * "git commit" used to abort after the user edited the log message
+   when the committer information was not correctly set up.  It now
+   aborts before starting the editor.
+
+ * "git commit --date=invalid" used to silently ignore the incorrectly
+   specified date; it is now diagnosed as an error.
+
+ * "git rebase --skip" to skip the last commit in a series used to fail
+   to run post-rewrite hook and to copy notes from old commits that have
+   successfully been rebased so far.  Now it do (backmerge ef88ad2).
+
+ * "gitweb" tried to show a wrong feed logo when none was specified.
diff --git a/Documentation/RelNotes/1.7.3.txt b/Documentation/RelNotes/1.7.3.txt
new file mode 100644 (file)
index 0000000..309c331
--- /dev/null
@@ -0,0 +1,76 @@
+Git v1.7.3 Release Notes
+========================
+
+Updates since v1.7.2
+--------------------
+
+ * git-gui, now at version 0.13.0, got various updates and a new
+   maintainer, Pat Thoyts.
+
+ * Gitweb allows its configuration to change per each request; it used to
+   read the configuration once upon startup.
+
+ * When git finds a corrupt object, it now reports the file that contains
+   it.
+
+ * "git checkout -B <it>" is a shorter way to say "git branch -f <it>"
+   followed by "git checkout <it>".
+
+ * When "git checkout" or "git merge" refuse to proceed in order to
+   protect local modification to your working tree, they used to stop
+   after showing just one path that might be lost.  They now show all,
+   in a format that is easier to read.
+
+ * "git clean" learned "-e" ("--exclude") option.
+
+ * Hunk headers produced for C# files by "git diff" and friends show more
+   relevant context than before.
+
+ * diff.ignoresubmodules configuration variable can be used to squelch the
+   differences in submodules reported when running commands (e.g. "diff",
+   "status", etc.) at the superproject level.
+
+ * http.useragent configuration can be used to lie who you are to your
+   restrictive firewall.
+
+ * "git rebase --strategy <s>" learned "-X" option to pass extra options
+   that are understood by the chosen merge strategy.
+
+ * "git rebase -i" learned "exec" that you can insert into the insn sheet
+   to run a command between its steps.
+
+ * "git rebase" between branches that have many binary changes that do
+   not conflict should be faster.
+
+ * "git rebase -i" peeks into rebase.autosquash configuration and acts as
+   if you gave --autosquash from the command line.
+
+
+Also contains various documentation updates.
+
+
+Fixes since v1.7.2
+------------------
+
+All of the fixes in v1.7.2.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git merge -s recursive" (which is the default) did not handle cases
+   where a directory becomes a file (or vice versa) very well.
+
+ * "git fetch" and friends were accidentally broken for url with "+" in
+   its path, e.g. "git://git.gnome.org/gtk+".
+
+ * "git fetch $url" (i.e. without refspecs) was broken for quite some
+   time, if the current branch happen to be tracking some remote.
+
+ * "git ls-tree dir dirgarbage", when "dir" was a directory,
+   incorrectly recursed into "dir".
+
+ * "git note remove" created unnecessary extra commit when named object
+   did not have any note to begin with.
+
+ * "git rebase" did not work well if you had diff.noprefix configured.
+
+ * "git -c foo=bar subcmd" did not work well for subcmd that is not
+   implemented as a built-in command.
diff --git a/Documentation/RelNotes/1.7.4.txt b/Documentation/RelNotes/1.7.4.txt
new file mode 100644 (file)
index 0000000..d5bca73
--- /dev/null
@@ -0,0 +1,156 @@
+Git v1.7.4 Release Notes
+========================
+
+Updates since v1.7.3
+--------------------
+
+ * The documentation Makefile now assumes by default asciidoc 8 and
+   docbook-xsl >= 1.73. If you have older versions, you can set
+   ASCIIDOC7 and ASCIIDOC_ROFF, respectively.
+
+ * The option parsers of various commands that create new branches (or
+   rename existing ones to a new name) were too loose and users were
+   allowed to give a branch a name that begins with a dash by creative
+   abuse of their command line options, which only led to burning
+   themselves.  The name of a branch cannot begin with a dash now.
+
+ * System-wide fallback default attributes can be stored in
+   /etc/gitattributes; the core.attributesfile configuration variable can
+   be used to customize the path to this file.
+
+ * The thread structure generated by "git send-email" has changed
+   slightly.  Setting the cover letter of the latest series as a reply
+   to the cover letter of the previous series with --in-reply-to used
+   to make the new cover letter and all the patches replies to the
+   cover letter of the previous series; this has been changed to make
+   the patches in the new series replies to the new cover letter.
+
+ * The Bash completion script in contrib/ has been adjusted to be usable with
+   Bash 4 (options with '=value' didn't complete).  It has been also made
+   usable with zsh.
+
+ * Different pagers can be chosen depending on which subcommand is
+   being run under the pager, using the "pager.<subcommand>" variable.
+
+ * The hardcoded tab-width of 8 that is used in whitespace breakage checks is now
+   configurable via the attributes mechanism.
+
+ * Support of case insensitive filesystems (i.e. "core.ignorecase") has
+   been improved.  For example, the gitignore mechanism didn't pay attention
+   to case insensitivity.
+
+ * The <tree>:<path> syntax for naming a blob in a tree, and the :<path>
+   syntax for naming a blob in the index (e.g. "master:Makefile",
+   ":hello.c") have been extended.  You can start <path> with "./" to
+   implicitly have the (sub)directory you are in prefixed to the
+   lookup.  Similarly, ":../Makefile" from a subdirectory would mean
+   "the Makefile of the parent directory in the index".
+
+ * "git blame" learned the --show-email option to display the e-mail
+   addresses instead of the names of authors.
+
+ * "git commit" learned the --fixup and --squash options to help later invocation
+   of interactive rebase.
+
+ * Command line options to "git cvsimport" whose names are in capital
+   letters (-A, -M, -R and -S) can now be specified as the default in
+   the .git/config file by their longer names (cvsimport.authorsFile,
+   cvsimport.mergeRegex, cvsimport.trackRevisions, cvsimport.ignorePaths).
+
+ * "git daemon" can be built in the MinGW environment.
+
+ * "git daemon" can take more than one --listen option to listen to
+   multiple addresses.
+
+ * "git describe --exact-match" was optimized not to read commit
+   objects unnecessarily.
+
+ * "git diff" and "git grep" learned what functions and subroutines
+   in Fortran, Pascal and Perl look like.
+
+ * "git fetch" learned the "--recurse-submodules" option.
+
+ * "git mergetool" tells vim/gvim to show a three-way diff by default
+   (use vimdiff2/gvimdiff2 as the tool name for old behavior).
+
+ * "git log -G<pattern>" limits the output to commits whose change has
+   added or deleted lines that match the given pattern.
+
+ * "git read-tree" with no argument as a way to empty the index is
+   deprecated; we might want to remove it in the future.  Users can
+   use the new --empty option to be more explicit instead.
+
+ * "git repack -f" does not spend cycles to recompress objects in the
+   non-delta representation anymore (use -F if you really mean it
+   e.g. after you changed the core.compression variable setting).
+
+ * "git merge --log" used to limit the resulting merge log to 20
+   entries; this is now customizable by giving e.g. "--log=47".
+
+ * "git merge" may work better when all files were moved out of a
+   directory in one branch while a new file is created in place of that
+   directory in the other branch.
+
+ * "git merge" learned the "--abort" option, synonymous to
+   "git reset --merge" when a merge is in progress.
+
+ * "git notes" learned the "merge" subcommand to merge notes refs.
+   In addition to the default manual conflict resolution, there are
+   also several notes merge strategies for automatically resolving
+   notes merge conflicts.
+
+ * "git rebase --autosquash" can use SHA-1 object names to name the
+   commit which is to be fixed up (e.g. "fixup! e83c5163").
+
+ * The default "recursive" merge strategy learned the --rename-threshold
+   option to influence the rename detection, similar to the -M option
+   of "git diff".  From the "git merge" frontend, the "-X<strategy option>"
+   interface, e.g. "git merge -Xrename-threshold=50% ...", can be used
+   to trigger this.
+
+ * The "recursive" strategy also learned to ignore various whitespace
+   changes; the most notable is -Xignore-space-at-eol.
+
+ * "git send-email" learned "--to-cmd", similar to "--cc-cmd", to read
+   the recipient list from a command output.
+
+ * "git send-email" learned to read and use "To:" from its input files.
+
+ * you can extend "git shell", which is often used on boxes that allow
+   git-only login over ssh as login shell, with a custom set of
+   commands.
+
+ * The current branch name in "git status" output can be colored differently
+   from the generic header color by setting the "color.status.branch" variable.
+
+ * "git submodule sync" updates metainformation for all submodules,
+   not just the ones that have been checked out.
+
+ * gitweb can use a custom 'highlight' command with its configuration file.
+
+ * other gitweb updates.
+
+
+Also contains various documentation updates.
+
+
+Fixes since v1.7.3
+------------------
+
+All of the fixes in the v1.7.3.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git log --author=me --author=her" did not find commits written by
+   me or by her; instead it looked for commits written by me and by
+   her, which is impossible.
+
+ * "git push --progress" shows progress indicators now.
+
+ * "git rebase -i" showed a confusing error message when given a
+   branch name that does not exist.
+
+ * "git repack" places its temporary packs under $GIT_OBJECT_DIRECTORY/pack
+   instead of $GIT_OBJECT_DIRECTORY/ to avoid cross directory renames.
+
+ * "git submodule update --recursive --other-flags" passes flags down
+   to its subinvocations.
index ece3c77482b3ff006b973f1ed90b708e26556862..72741ebda12de8fbf786f12531d70ab465eecf42 100644 (file)
@@ -264,12 +264,21 @@ the change to its true author (see (2) above).
 Also notice that a real name is used in the Signed-off-by: line. Please
 don't hide your real name.
 
-Some people also put extra tags at the end.
-
-"Acked-by:" says that the patch was reviewed by the person who
-is more familiar with the issues and the area the patch attempts
-to modify.  "Tested-by:" says the patch was tested by the person
-and found to have the desired effect.
+If you like, you can put extra tags at the end:
+
+1. "Reported-by:" is used to to credit someone who found the bug that
+   the patch attempts to fix.
+2. "Acked-by:" says that the person who is more familiar with the area
+   the patch attempts to modify liked the patch.
+3. "Reviewed-by:", unlike the other tags, can only be offered by the
+   reviewer and means that she is completely satisfied that the patch
+   is ready for application.  It is usually offered only after a
+   detailed review.
+4. "Tested-by:" is used to indicate that the person applied the patch
+   and found it to have the desired effect.
+
+You can also create your own tag or use one that's in common usage
+such as "Thanks-to:", "Based-on-patch-by:", or "Mentored-by:".
 
 ------------------------------------------------
 An ideal patch flow
index 87a90f2c3fd48bbb593a2cef135310f88d87e4b4..aea8627be088c58ef9ad7fd07f5498da88d9b1fc 100644 (file)
@@ -16,8 +16,11 @@ plus=&#43;
 caret=&#94;
 startsb=&#91;
 endsb=&#93;
+backslash=&#92;
 tilde=&#126;
+apostrophe=&#39;
 backtick=&#96;
+litdd=&#45;&#45;
 
 ifdef::backend-docbook[]
 [linkgit-inlinemacro]
index fdaa586f08b60758aafca7d8b01ab6cd979dd039..312293799d3731f488547b7904398b2bb16de81d 100644 (file)
@@ -317,24 +317,26 @@ false), while all other repositories are assumed to be bare (bare
 = true).
 
 core.worktree::
-       Set the path to the root of the work tree.
+       Set the path to the root of the working tree.
        This can be overridden by the GIT_WORK_TREE environment
-       variable and the '--work-tree' command line option. It can be
-       an absolute path or a relative path to the .git directory,
-       either specified by --git-dir or GIT_DIR, or automatically
-       discovered.
-       If --git-dir or GIT_DIR are specified but none of
+       variable and the '--work-tree' command line option.
+       The value can an absolute path or relative to the path to
+       the .git directory, which is either specified by --git-dir
+       or GIT_DIR, or automatically discovered.
+       If --git-dir or GIT_DIR is specified but none of
        --work-tree, GIT_WORK_TREE and core.worktree is specified,
-       the current working directory is regarded as the root of the
-       work tree.
+       the current working directory is regarded as the top level
+       of your working tree.
 +
 Note that this variable is honored even when set in a configuration
-file in a ".git" subdirectory of a directory, and its value differs
+file in a ".git" subdirectory of a directory and its value differs
 from the latter directory (e.g. "/path/to/.git/config" has
 core.worktree set to "/different/path"), which is most likely a
-misconfiguration.  Running git commands in "/path/to" directory will
+misconfiguration.  Running git commands in the "/path/to" directory will
 still use "/different/path" as the root of the work tree and can cause
-great confusion to the users.
+confusion unless you know what you are doing (e.g. you are creating a
+read-only snapshot of the same index to a location different from the
+repository's usual working tree).
 
 core.logAllRefUpdates::
        Enable the reflog. Updates to a ref <ref> is logged to the file
@@ -374,6 +376,15 @@ core.warnAmbiguousRefs::
        If true, git will warn you if the ref name you passed it is ambiguous
        and might match multiple refs in the .git/refs/ tree. True by default.
 
+core.abbrevguard::
+       Even though git makes sure that it uses enough hexdigits to show
+       an abbreviated object name unambiguously, as more objects are
+       added to the repository over time, a short name that used to be
+       unique will stop being unique.  Git uses this many extra hexdigits
+       that are more than necessary to make the object name currently
+       unique, in the hope that its output will stay unique a bit longer.
+       Defaults to 0.
+
 core.compression::
        An integer -1..9, indicating a default compression level.
        -1 is the zlib default. 0 means no compression,
@@ -450,6 +461,21 @@ core.excludesfile::
        to the value of `$HOME` and "{tilde}user/" to the specified user's
        home directory.  See linkgit:gitignore[5].
 
+core.askpass::
+       Some commands (e.g. svn and http interfaces) that interactively
+       ask for a password can be told to use an external program given
+       via the value of this variable. Can be overridden by the 'GIT_ASKPASS'
+       environment variable. If not set, fall back to the value of the
+       'SSH_ASKPASS' environment variable or, failing that, a simple password
+       prompt. The external program shall be given a suitable prompt as
+       command line argument and write the password on its STDOUT.
+
+core.attributesfile::
+       In addition to '.gitattributes' (per-directory) and
+       '.git/info/attributes', git looks into this file for attributes
+       (see linkgit:gitattributes[5]). Path expansions are made the same
+       way as for `core.excludesfile`.
+
 core.ignoredareprecious::
        By default ignored (i.e. trashable) untracked files are
        automatically removed from the working tree when they get in
@@ -505,6 +531,9 @@ core.whitespace::
   part of the line terminator, i.e. with it, `trailing-space`
   does not trigger if the character before such a carriage-return
   is not a whitespace (not enabled by default).
+* `tabwidth=<n>` tells how many character positions a tab occupies; this
+  is relevant for `indent-with-non-tab` and when git fixes `tab-in-indent`
+  errors. The default tab width is 8. Allowed values are 1 to 63.
 
 core.fsyncobjectfiles::
        This boolean will enable 'fsync()' when writing object files.
@@ -546,9 +575,13 @@ core.sparseCheckout::
        linkgit:git-read-tree[1] for more information.
 
 add.ignore-errors::
+add.ignoreErrors::
        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].
+       option of linkgit:git-add[1].  Older versions of git accept only
+       `add.ignore-errors`, which does not follow the usual naming
+       convention for configuration variables.  Newer versions of git
+       honor `add.ignoreErrors` as well.
 
 alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
@@ -593,8 +626,9 @@ branch.autosetupmerge::
        this behavior can be chosen per-branch using the `--track`
        and `--no-track` options. The valid settings are: `false` -- no
        automatic setup is done; `true` -- automatic setup is done when the
-       starting point is a remote branch; `always` -- automatic setup is
-       done when the starting point is either a local branch or remote
+       starting point is a remote-tracking branch; `always` --
+       automatic setup is done when the starting point is either a
+       local branch or remote-tracking
        branch. This option defaults to true.
 
 branch.autosetuprebase::
@@ -605,7 +639,7 @@ branch.autosetuprebase::
        When `local`, rebase is set to true for tracked branches of
        other local branches.
        When `remote`, rebase is set to true for tracked branches of
-       remote branches.
+       remote-tracking branches.
        When `always`, rebase will be set to true for all tracking
        branches.
        See "branch.autosetupmerge" for details on how to set up a
@@ -672,7 +706,7 @@ color.branch::
 color.branch.<slot>::
        Use customized color for branch coloration. `<slot>` is one of
        `current` (the current branch), `local` (a local branch),
-       `remote` (a tracking branch in refs/remotes/), `plain` (other
+       `remote` (a remote-tracking branch in refs/remotes/), `plain` (other
        refs).
 +
 The value for these configuration variables is a list of colors (at most
@@ -700,7 +734,7 @@ color.diff.<slot>::
 color.decorate.<slot>::
        Use customized color for 'git log --decorate' output.  `<slot>` is one
        of `branch`, `remoteBranch`, `tag`, `stash` or `HEAD` for local
-       branches, remote tracking branches, tags, stash and HEAD, respectively.
+       branches, remote-tracking branches, tags, stash and HEAD, respectively.
 
 color.grep::
        When set to `always`, always highlight matches.  When `false` (or
@@ -765,7 +799,8 @@ color.status.<slot>::
        one of `header` (the header text of the status message),
        `added` or `updated` (files which are added but not committed),
        `changed` (files which are changed but not added in the index),
-       `untracked` (files which are not tracked by git), or
+       `untracked` (files which are not tracked by git),
+       `branch` (the current branch), or
        `nobranch` (the color the 'no branch' warning is shown in, defaulting
        to red). The values of these variables may be specified as in
        color.branch.<slot>.
@@ -811,8 +846,6 @@ diff.mnemonicprefix::
        standard "a/" and "b/" depending on what is being compared.  When
        this configuration is in effect, reverse diff output also swaps
        the order of the prefixes:
-diff.noprefix::
-       If set, 'git diff' does not show any source or destination prefix.
 `git diff`;;
        compares the (i)ndex and the (w)ork tree;
 `git diff HEAD`;;
@@ -824,6 +857,9 @@ diff.noprefix::
 `git diff --no-index a b`;;
        compares two non-git things (1) and (2).
 
+diff.noprefix::
+       If set, 'git diff' does not show any source or destination prefix.
+
 diff.renameLimit::
        The number of files to consider when performing the copy/rename
        detection; equivalent to the 'git diff' option '-l'.
@@ -836,7 +872,8 @@ diff.renames::
 diff.ignoreSubmodules::
        Sets the default value of --ignore-submodules. Note that this
        affects only 'git diff' Porcelain, and not lower level 'diff'
-       commands such as 'git diff-files'.
+       commands such as 'git diff-files'. 'git checkout' also honors
+       this setting when reporting uncommitted changes.
 
 diff.suppressBlankEmpty::
        A boolean to inhibit the standard behavior of printing a space
@@ -869,6 +906,11 @@ diff.wordRegex::
        sequences that match the regular expression are "words", all other
        characters are *ignorable* whitespace.
 
+fetch.recurseSubmodules::
+       A boolean value which changes the behavior for fetch and pull, the
+       default is to not recursively fetch populated submodules unless
+       configured otherwise.
+
 fetch.unpackLimit::
        If the number of objects fetched over the git native
        transfer is below this
@@ -963,7 +1005,7 @@ gc.packrefs::
        Running `git pack-refs` in a repository renders it
        unclonable by Git versions prior to 1.5.1.2 over dumb
        transports such as HTTP.  This variable determines whether
-       'git gc' runs `git pack-refs`. This can be set to `nobare`
+       'git gc' runs `git pack-refs`. This can be set to `notbare`
        to enable it within all non-bare repos or it can be set to a
        boolean value.  The default is `true`.
 
@@ -1092,7 +1134,7 @@ gui.newbranchtemplate::
        linkgit:git-gui[1].
 
 gui.pruneduringfetch::
-       "true" if linkgit:git-gui[1] should prune tracking branches when
+       "true" if linkgit:git-gui[1] should prune remote-tracking branches when
        performing a fetch. The default value is "false".
 
 gui.trustmtime::
@@ -1255,6 +1297,15 @@ http.noEPSV::
        support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
        environment variable. Default is false (curl will use EPSV).
 
+http.useragent::
+       The HTTP USER_AGENT string presented to an HTTP server.  The default
+       value represents the version of the client git such as git/1.7.1.
+       This option allows you to override this value to a more common value
+       such as Mozilla/4.0.  This may be necessary, for instance, if
+       connecting through a firewall that restricts HTTP connections to a set
+       of common USER_AGENT strings (but not including those like git/1.7.1).
+       Can be overridden by the 'GIT_HTTP_USER_AGENT' environment variable.
+
 i18n.commitEncoding::
        Character encoding the commit messages are stored in; git itself
        does not care per se, but this information is necessary e.g. when
@@ -1303,10 +1354,11 @@ interactive.singlekey::
        ignored if portable keystroke input is not available.
 
 log.date::
-       Set default date-time mode for the log command. Setting log.date
-       value is similar to using 'git log'\'s --date option. The value is one of the
-       following alternatives: {relative,local,default,iso,rfc,short}.
-       See linkgit:git-log[1].
+       Set the default date-time mode for the 'log' command.
+       Setting a value for log.date is similar to using 'git log''s
+       `\--date` option.  Possible values are `relative`, `local`,
+       `default`, `iso`, `rfc`, and `short`; see linkgit:git-log[1]
+       for details.
 
 log.decorate::
        Print out the ref names of any commits that are shown by the log
@@ -1452,6 +1504,10 @@ pack.compression::
        not set,  defaults to -1, the zlib default, which is "a default
        compromise between speed and compression (currently equivalent
        to level 6)."
++
+Note that changing the compression level will not automatically recompress
+all existing objects. You can force recompression by passing the -F option
+to linkgit:git-repack[1].
 
 pack.deltaCacheSize::
        The maximum memory in bytes used for caching deltas in
@@ -1507,11 +1563,13 @@ pack.packSizeLimit::
        supported.
 
 pager.<cmd>::
-       Allows turning on or off pagination of the output of a
-       particular git subcommand when writing to a tty.  If
-       `\--paginate` or `\--no-pager` is specified on the command line,
-       it takes precedence over this option.  To disable pagination for
-       all commands, set `core.pager` or `GIT_PAGER` to `cat`.
+       If the value is boolean, turns on or off pagination of the
+       output of a particular git subcommand when writing to a tty.
+       Otherwise, turns on pagination for the subcommand using the
+       pager specified by the value of `pager.<cmd>`.  If `\--paginate`
+       or `\--no-pager` is specified on the command line, it takes
+       precedence over this option.  To disable pagination for all
+       commands, set `core.pager` or `GIT_PAGER` to `cat`.
 
 pretty.<name>::
        Alias for a --pretty= format string, as specified in
@@ -1536,17 +1594,20 @@ push.default::
        no refspec is implied by any of the options given on the command
        line. Possible values are:
 +
-* `nothing` do not push anything.
-* `matching` push all matching branches.
+* `nothing` do not push anything.
+* `matching` push all matching branches.
   All branches having the same name in both ends are considered to be
   matching. This is the default.
-* `tracking` push the current branch to its upstream branch.
-* `current` push the current branch to a branch of the same name.
+* `tracking` push the current branch to its upstream branch.
+* `current` push the current branch to a branch of the same name.
 
 rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
        rebase. False by default.
 
+rebase.autosquash::
+       If set to true enable '--autosquash' option by default.
+
 receive.autogc::
        By default, git-receive-pack will run "git-gc --auto" after
        receiving data from git-push and updating refs.  You can stop
@@ -1641,7 +1702,9 @@ remote.<name>.tagopt::
        Setting this value to \--no-tags disables automatic tag following when
        fetching from remote <name>. Setting it to \--tags will fetch every
        tag from remote <name>, even if they are not reachable from remote
-       branch heads.
+       branch heads. Passing these flags directly to linkgit:git-fetch[1] can
+       override this setting. See options \--tags and \--no-tags of
+       linkgit:git-fetch[1].
 
 remote.<name>.vcs::
        Setting this to a value <vcs> will cause git to interact with
@@ -1708,6 +1771,7 @@ sendemail.to::
 sendemail.smtpdomain::
 sendemail.smtpserver::
 sendemail.smtpserverport::
+sendemail.smtpserveroption::
 sendemail.smtpuser::
 sendemail.thread::
 sendemail.validate::
@@ -1736,9 +1800,9 @@ status.showUntrackedFiles::
        the untracked files. Possible values are:
 +
 --
-       - 'no'     - Show no untracked files
-       - 'normal' - Shows untracked files and directories
-       - 'all'    - Shows also individual files in untracked directories.
+* `no` - Show no untracked files.
+* `normal` - Show untracked files and directories.
+* `all` - Show also individual files in untracked directories.
 --
 +
 If this variable is not specified, it defaults to 'normal'.
@@ -1761,6 +1825,13 @@ submodule.<name>.update::
        URL and other values found in the `.gitmodules` file.  See
        linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
 
+submodule.<name>.fetchRecurseSubmodules::
+       This option can be used to enable/disable recursive fetching of this
+       submodule. It can be overridden by using the --[no-]recurse-submodules
+       command line option to "git fetch" and "git pull".
+       This setting will override that from in the linkgit:gitmodules[5]
+       file.
+
 submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
        a submodule as modified. When set to "all", it will never be considered
@@ -1771,7 +1842,7 @@ submodule.<name>.ignore::
        Using "none" (the default when this option is not set) also shows
        submodules that have untracked files in their work tree as changed.
        This setting overrides any setting made in .gitmodules for this submodule,
-       both settings can be overriden on the command line by using the
+       both settings can be overridden on the command line by using the
        "--ignore-submodules" option.
 
 tar.umask::
index 8f9a2412fd44c80f2bf8d63271e2897ebeb3ddeb..3ac2beac6229955dc2534c428238c3d5ff57d25d 100644 (file)
@@ -9,16 +9,15 @@ patch file.  You can customize the creation of such patches via the
 GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables.
 
 What the -p option produces is slightly different from the traditional
-diff format.
+diff format:
 
-1.   It is preceded with a "git diff" header, that looks like
-     this:
+1.   It is preceded with a "git diff" header that looks like this:
 
        diff --git a/file1 b/file2
 +
 The `a/` and `b/` filenames are the same unless rename/copy is
 involved.  Especially, even for a creation or a deletion,
-`/dev/null` is _not_ used in place of `a/` or `b/` filenames.
+`/dev/null` is _not_ used in place of the `a/` or `b/` filenames.
 +
 When rename/copy is involved, `file1` and `file2` show the
 name of the source file of the rename/copy and the name of
@@ -37,18 +36,39 @@ the file that rename/copy produces, respectively.
        similarity index <number>
        dissimilarity index <number>
        index <hash>..<hash> <mode>
-
-3.  TAB, LF, double quote and backslash characters in pathnames
-    are represented as `\t`, `\n`, `\"` and `\\`, respectively.
-    If there is need for such substitution then the whole
-    pathname is put in double quotes.
-
++
+File modes are printed as 6-digit octal numbers including the file type
+and file permission bits.
++
+Path names in extended headers do not include the `a/` and `b/` prefixes.
++
 The similarity index is the percentage of unchanged lines, and
 the dissimilarity index is the percentage of changed lines.  It
 is a rounded down integer, followed by a percent sign.  The
 similarity index value of 100% is thus reserved for two equal
 files, while 100% dissimilarity means that no line from the old
 file made it into the new one.
++
+The index line includes the SHA-1 checksum before and after the change.
+The <mode> is included if the file mode does not change; otherwise,
+separate lines indicate the old and the new mode.
+
+3.  TAB, LF, double quote and backslash characters in pathnames
+    are represented as `\t`, `\n`, `\"` and `\\`, respectively.
+    If there is need for such substitution then the whole
+    pathname is put in double quotes.
+
+4.  All the `file1` files in the output refer to files before the
+    commit, and all the `file2` files refer to files after the commit.
+    It is incorrect to apply each change to each file sequentially.  For
+    example, this patch will swap a and b:
+
+      diff --git a/a b/b
+      rename from a
+      rename to b
+      diff --git a/b b/a
+      rename from b
+      rename to a
 
 
 combined diff format
index 4656a97e6012796f182a725e14c13dc03f71bba2..c93124be79809e36d9a29fd69ce77cd7f22413e1 100644 (file)
@@ -48,9 +48,9 @@ endif::git-format-patch[]
 --patience::
        Generate a diff using the "patience diff" algorithm.
 
---stat[=width[,name-width]]::
+--stat[=<width>[,<name-width>]]::
        Generate a diffstat.  You can override the default
-       output width for 80-column terminal by `--stat=width`.
+       output width for 80-column terminal by `--stat=<width>`.
        The width of the filename part can be controlled by
        giving another width to it separated by a comma.
 
@@ -66,14 +66,14 @@ endif::git-format-patch[]
        number of modified files, as well as number of added and deleted
        lines.
 
---dirstat[=limit]::
+--dirstat[=<limit>]::
        Output the distribution of relative amount of changes (number of lines added or
        removed) for each sub-directory. Directories with changes below
        a cut-off percent (3% by default) are not shown. The cut-off percent
-       can be set with `--dirstat=limit`. Changes in a child directory is not
+       can be set with `--dirstat=<limit>`. Changes in a child directory are not
        counted for the parent directory, unless `--cumulative` is used.
 
---dirstat-by-file[=limit]::
+--dirstat-by-file[=<limit>]::
        Same as `--dirstat`, but counts changed files instead of lines.
 
 --summary::
@@ -207,6 +207,7 @@ endif::git-format-patch[]
        digits can be specified with `--abbrev=<n>`.
 
 -B[<n>][/<m>]::
+--break-rewrites[=[<n>][/<m>]]::
        Break complete rewrite changes into pairs of delete and
        create. This serves two purposes:
 +
@@ -229,6 +230,7 @@ eligible for being picked up as a possible source of a rename to
 another file.
 
 -M[<n>]::
+--find-renames[=<n>]::
 ifndef::git-log[]
        Detect renames.
 endif::git-log[]
@@ -244,23 +246,10 @@ endif::git-log[]
        hasn't changed.
 
 -C[<n>]::
+--find-copies[=<n>]::
        Detect copies as well as renames.  See also `--find-copies-harder`.
        If `n` is specified, it has the same meaning as for `-M<n>`.
 
-ifndef::git-format-patch[]
---diff-filter=[ACDMRTUXB*]::
-       Select only files that are Added (`A`), Copied (`C`),
-       Deleted (`D`), Modified (`M`), Renamed (`R`), have their
-       type (i.e. regular file, symlink, submodule, ...) changed (`T`),
-       are Unmerged (`U`), are
-       Unknown (`X`), or have had their pairing Broken (`B`).
-       Any combination of the filter characters may be used.
-       When `*` (All-or-none) is added to the combination, all
-       paths are selected if there is any file that matches
-       other criteria in the comparison; if there is no file
-       that matches other criteria, nothing is selected.
-endif::git-format-patch[]
-
 --find-copies-harder::
        For performance reasons, by default, `-C` option finds copies only
        if the original file of the copy was modified in the same
@@ -278,14 +267,30 @@ endif::git-format-patch[]
        number.
 
 ifndef::git-format-patch[]
+--diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]::
+       Select only files that are Added (`A`), Copied (`C`),
+       Deleted (`D`), Modified (`M`), Renamed (`R`), have their
+       type (i.e. regular file, symlink, submodule, ...) changed (`T`),
+       are Unmerged (`U`), are
+       Unknown (`X`), or have had their pairing Broken (`B`).
+       Any combination of the filter characters (including none) can be used.
+       When `*` (All-or-none) is added to the combination, all
+       paths are selected if there is any file that matches
+       other criteria in the comparison; if there is no file
+       that matches other criteria, nothing is selected.
+
 -S<string>::
        Look for differences that introduce or remove an instance of
        <string>. Note that this is different than the string simply
        appearing in diff output; see the 'pickaxe' entry in
        linkgit:gitdiffcore[7] for more details.
 
+-G<regex>::
+       Look for differences whose added or removed line matches
+       the given <regex>.
+
 --pickaxe-all::
-       When `-S` finds a change, show all the changes in that
+       When `-S` or `-G` finds a change, show all the changes in that
        changeset, not just the files that contain the change
        in <string>.
 
index 9a6912c641edf52083b8996fdce3a0be2f4dba45..da8b05b92283a37b9552f322bbe31df884a3cae7 100644 (file)
@@ -1,5 +1,8 @@
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version='1.0'>
  <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
- <xsl:output method="html" encoding="UTF-8" indent="no" />
+ <xsl:output method="html"
+     encoding="UTF-8" indent="no"
+     doctype-public="-//W3C//DTD HTML 4.01//EN"
+     doctype-system="http://www.w3.org/TR/html4/strict.dtd" />
 </xsl:stylesheet>
index e0ba8cc07549af375c89496c57c016a41b8cc699..ae413e52a52618503aa8d569f72ae688ec95a8fa 100644 (file)
@@ -180,12 +180,12 @@ directory; clone from it to start a repository on the satellite
 machine.
 <2> clone sets these configuration variables by default.
 It arranges `git pull` to fetch and store the branches of mothership
-machine to local `remotes/origin/*` tracking branches.
+machine to local `remotes/origin/*` remote-tracking branches.
 <3> arrange `git push` to push local `master` branch to
 `remotes/satellite/master` branch of the mothership machine.
 <4> push will stash our work away on `remotes/satellite/master`
-tracking branch on the mothership machine.  You could use this as
-a back-up method.
+remote-tracking branch on the mothership machine.  You could use this
+as a back-up method.
 <5> on mothership machine, merge the work done on the satellite
 machine into the master branch.
 
index 9333c42c558464ce2430df39e9864f932e786b36..695696da1b6e78639014388e1d4ac8bf09a0e12d 100644 (file)
@@ -36,7 +36,7 @@ ifndef::git-pull[]
 
 -p::
 --prune::
-       After fetching, remove any remote tracking branches which
+       After fetching, remove any remote-tracking branches which
        no longer exist on the remote.
 endif::git-pull[]
 
@@ -49,8 +49,11 @@ ifndef::git-pull[]
 endif::git-pull[]
        By default, tags that point at objects that are downloaded
        from the remote repository are fetched and stored locally.
-       This option disables this automatic tag following.
+       This option disables this automatic tag following. The default
+       behavior for a remote may be specified with the remote.<name>.tagopt
+       setting. See linkgit:git-config[1].
 
+ifndef::git-pull[]
 -t::
 --tags::
        Most of the tags are fetched automatically as branch
@@ -58,7 +61,21 @@ endif::git-pull[]
        objects reachable from the branch heads that are being
        tracked will not be fetched by this mechanism.  This
        flag lets all tags and their associated objects be
-       downloaded.
+       downloaded. The default behavior for a remote may be
+       specified with the remote.<name>.tagopt setting. See
+       linkgit:git-config[1].
+endif::git-pull[]
+
+--[no-]recurse-submodules::
+       This option controls if new commits of all populated submodules should
+       be fetched too (see linkgit:git-config[1] and linkgit:gitmodules[5]).
+
+ifndef::git-pull[]
+--submodule-prefix=<path>::
+       Prepend <path> to paths printed in informative messages
+       such as "Fetching submodule foo".  This option is used
+       internally when recursing over submodules.
+endif::git-pull[]
 
 -u::
 --update-head-ok::
index e22a62f06592c7d233fc06da022962cae59d105a..a03448f9231f78340fc492bffa009b96648ee6ac 100644 (file)
@@ -92,9 +92,11 @@ See ``Interactive mode'' for details.
        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.
+The intent of this option is to pick and choose lines of the patch to
+apply, or even to modify the contents of lines to be staged. This can be
+quicker and more flexible than using the interactive hunk selector.
+However, it is easy to confuse oneself and create a patch that does not
+apply to the index. See EDITING PATCHES below.
 
 -u::
 --update::
@@ -157,14 +159,14 @@ those in info/exclude.  See linkgit:gitrepository-layout[5].
 EXAMPLES
 --------
 
-* Adds content from all `\*.txt` files under `Documentation` directory
+* Adds content from all `*.txt` files under `Documentation` directory
 and its subdirectories:
 +
 ------------
 $ git add Documentation/\*.txt
 ------------
 +
-Note that the asterisk `\*` is quoted from the shell in this
+Note that the asterisk `*` is quoted from the shell in this
 example; this lets the command include the files from
 subdirectories of `Documentation/` directory.
 
@@ -220,7 +222,7 @@ binary so line count cannot be shown) and there is no
 difference between indexed copy and the working tree
 version (if the working tree version were also different,
 'binary' would have been shown in place of 'nothing').  The
-other file, git-add--interactive.perl, has 403 lines added
+other file, git-add{litdd}interactive.perl, has 403 lines added
 and 35 lines deleted if you commit what is in the index, but
 working tree file has further modifications (one addition and
 one deletion).
@@ -295,6 +297,78 @@ diff::
   This lets you review what will be committed (i.e. between
   HEAD and index).
 
+
+EDITING PATCHES
+---------------
+
+Invoking `git add -e` or selecting `e` from the interactive hunk
+selector will open a patch in your editor; after the editor exits, the
+result is applied to the index. You are free to make arbitrary changes
+to the patch, but note that some changes may have confusing results, or
+even result in a patch that cannot be applied.  If you want to abort the
+operation entirely (i.e., stage nothing new in the index), simply delete
+all lines of the patch. The list below describes some common things you
+may see in a patch, and which editing operations make sense on them.
+
+--
+added content::
+
+Added content is represented by lines beginning with "{plus}". You can
+prevent staging any addition lines by deleting them.
+
+removed content::
+
+Removed content is represented by lines beginning with "-". You can
+prevent staging their removal by converting the "-" to a " " (space).
+
+modified content::
+
+Modified content is represented by "-" lines (removing the old content)
+followed by "{plus}" lines (adding the replacement content). You can
+prevent staging the modification by converting "-" lines to " ", and
+removing "{plus}" lines. Beware that modifying only half of the pair is
+likely to introduce confusing changes to the index.
+--
+
+There are also more complex operations that can be performed. But beware
+that because the patch is applied only to the index and not the working
+tree, the working tree will appear to "undo" the change in the index.
+For example, introducing a new line into the index that is in neither
+the HEAD nor the working tree will stage the new line for commit, but
+the line will appear to be reverted in the working tree.
+
+Avoid using these constructs, or do so with extreme caution.
+
+--
+removing untouched content::
+
+Content which does not differ between the index and working tree may be
+shown on context lines, beginning with a " " (space).  You can stage
+context lines for removal by converting the space to a "-". The
+resulting working tree file will appear to re-add the content.
+
+modifying existing content::
+
+One can also modify context lines by staging them for removal (by
+converting " " to "-") and adding a "{plus}" line with the new content.
+Similarly, one can modify "{plus}" lines for existing additions or
+modifications. In all cases, the new modification will appear reverted
+in the working tree.
+
+new content::
+
+You may also add new content that does not exist in the patch; simply
+add new lines, each starting with "{plus}". The addition will appear
+reverted in the working tree.
+--
+
+There are also several operations which should be avoided entirely, as
+they will make the patch impossible to apply:
+
+* adding context (" ") or removal ("-") lines
+* deleting context or removal lines
+* modifying the contents of context or removal lines
+
 SEE ALSO
 --------
 linkgit:git-status[1]
index 9e62f8778f6590328de4d9c0c7c08044e8019eac..51297d09ecdd3468ecd87a4f7aa6fac2b8ef6224 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
         [--ignore-date] [--ignore-space-change | --ignore-whitespace]
         [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
         [--reject] [-q | --quiet] [--scissors | --no-scissors]
-        [<mbox> | <Maildir>...]
+        [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort)
 
 DESCRIPTION
@@ -25,7 +25,7 @@ current branch.
 
 OPTIONS
 -------
-<mbox>|<Maildir>...::
+(<mbox>|<Maildir>)...::
        The list of mailbox files to read patches from. If you do not
        supply this argument, the command reads from the standard input.
        If you supply directories, they will be treated as Maildirs.
index 4a74b23d403d970643fcb8e0438ab6649f54e54c..881652f4904c102104cfe0a0b1de07dbfc32da3f 100644 (file)
@@ -12,10 +12,10 @@ SYNOPSIS
 'git apply' [--stat] [--numstat] [--summary] [--check] [--index]
          [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
          [--allow-binary-replacement | --binary] [--reject] [-z]
-         [-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]
+         [-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached]
          [--ignore-space-change | --ignore-whitespace ]
-         [--whitespace=<nowarn|warn|fix|error|error-all>]
-         [--exclude=PATH] [--include=PATH] [--directory=<root>]
+         [--whitespace=(nowarn|warn|fix|error|error-all)]
+         [--exclude=<path>] [--include=<path>] [--directory=<root>]
          [--verbose] [<patch>...]
 
 DESCRIPTION
index 4d4325f222258e60b2242b13a4b4f1db2790bbd5..2411ce5bfe05a14ed45c1546c3d1a6d92b39d8eb 100644 (file)
@@ -44,7 +44,7 @@ archives that it imports, it is also possible to specify git branch names
 manually.  To do so, write a git branch name after each <archive/branch>
 parameter, separated by a colon.  This way, you can shorten the Arch
 branch names and convert Arch jargon to git jargon, for example mapping a
-"PROJECT--devo--VERSION" branch to "master".
+"PROJECT{litdd}devo{litdd}VERSION" branch to "master".
 
 Associating multiple Arch branches to one git branch is possible; the
 result will make the most sense only if no commits are made to the first
@@ -85,8 +85,8 @@ OPTIONS
 -o::
        Use this for compatibility with old-style branch names used by
        earlier versions of 'git archimport'.  Old-style branch names
-       were category--branch, whereas new-style branch names are
-       archive,category--branch--version.  In both cases, names given
+       were category{litdd}branch, whereas new-style branch names are
+       archive,category{litdd}branch{litdd}version.  In both cases, names given
        on the command-line will override the automatically-generated
        ones.
 
@@ -109,7 +109,7 @@ OPTIONS
 
 Author
 ------
-Written by Martin Langhoff <martin@catalyst.net.nz>.
+Written by Martin Langhoff <martin@laptop.org>.
 
 Documentation
 --------------
index 8d3e66626f7561b0ba7f8140288cfc15167bdc14..bf5037ab2a5042a20e9cb603f0831bb186ac3b74 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
 'git archive' [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>]
              [-o | --output=<file>] [--worktree-attributes]
              [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
-             [path...]
+             [<path>...]
 
 DESCRIPTION
 -----------
@@ -73,7 +73,7 @@ OPTIONS
 <tree-ish>::
        The tree or commit to produce an archive for.
 
-path::
+<path>::
        Without an optional path parameter, all files and subdirectories
        of the current working directory are included in the archive.
        If one or more paths are specified, only these are included.
@@ -116,7 +116,7 @@ Note that attributes are by default taken from the `.gitattributes` files
 in the tree that is being archived.  If you want to tweak the way the
 output is generated after the fact (e.g. you committed without adding an
 appropriate export-ignore in its `.gitattributes`), adjust the checked out
-`.gitattributes` file as necessary and use `--work-tree-attributes`
+`.gitattributes` file as necessary and use `--worktree-attributes`
 option.  Alternatively you can keep necessary attributes that should apply
 while archiving any tree in your `$GIT_DIR/info/attributes` file.
 
index efbe3790bb4a0521c954650ad43075f583500c7b..8a2ba3790417d80f6e71e45bc07b1f3cbea41329 100644 (file)
@@ -873,7 +873,7 @@ c * N * T + b * M * log2(M) tests
 where c is the number of rounds of test (so a small constant) and b is
 the ratio of bug per commit (hopefully a small constant too).
 
-So of course it's much better as it's O(N \* T) vs O(N \* T \* M) if
+So of course it's much better as it's O(N * T) vs O(N * T * M) if
 you would test everything after each commit.
 
 This means that test suites are good to prevent some bugs from being
index a27f43950fdfe8f6a0e8860d79ee31f0bc60750d..c71671b4f99e0be3670fc6d7e4872927a86ed02e 100644 (file)
@@ -8,7 +8,7 @@ git-blame - Show what revision and author last modified each line of a file
 SYNOPSIS
 --------
 [verse]
-'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [--incremental] [-L n,m]
+'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental] [-L n,m]
            [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
            [<rev> | --contents <file> | --reverse <rev>] [--] <file>
 
@@ -65,6 +65,10 @@ include::blame-options.txt[]
 -s::
        Suppress the author name and timestamp from the output.
 
+-e::
+--show-email::
+       Show the author email instead of author name (Default: off).
+
 -w::
        Ignore whitespace when comparing the parent's version and
        the child's to find where the lines came from.
index 1940256930d92c0679b95bb0cd41f24847828bd4..9106d38e406003cd53fd690ca66ee8e98630e4e3 100644 (file)
@@ -37,11 +37,12 @@ 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
 new branch.
 
-When a local branch is started off a remote branch, git sets up the
+When a local branch is started off a remote-tracking branch, git sets up the
 branch so that 'git pull' will appropriately merge from
-the remote branch. This behavior may be changed via the global
+the remote-tracking branch. This behavior may be changed via the global
 `branch.autosetupmerge` configuration flag. That setting can be
-overridden by using the `--track` and `--no-track` options.
+overridden by using the `--track` and `--no-track` options, and
+changed later using `git branch --set-upstream`.
 
 With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>.
 If <oldbranch> had a corresponding reflog, it is renamed to match
@@ -89,7 +90,8 @@ OPTIONS
        Move/rename a branch even if the new branch name already exists.
 
 --color[=<when>]::
-       Color branches to highlight current, local, and remote branches.
+       Color branches to highlight current, local, and
+       remote-tracking branches.
        The value must be always (the default), never, or auto.
 
 --no-color::
@@ -125,11 +127,11 @@ OPTIONS
        it directs `git pull` without arguments to pull from the
        upstream when the new branch is checked out.
 +
-This behavior is the default when the start point is a remote branch.
+This behavior is the default when the start point is a remote-tracking branch.
 Set the branch.autosetupmerge configuration variable to `false` if you
 want `git checkout` and `git branch` to always behave as if '--no-track'
 were given. Set it to `always` if you want this behavior when the
-start-point is either a local or remote branch.
+start-point is either a local or remote-tracking branch.
 
 --no-track::
        Do not set up "upstream" configuration, even if the
index a5ed8fb05b2f491f8f8ed7b95b808d04240327f6..299007b206cb5d810d19eed9b7831c3ea6368043 100644 (file)
@@ -9,10 +9,10 @@ git-bundle - Move objects and refs by archive
 SYNOPSIS
 --------
 [verse]
-'git bundle' create <file> <git-rev-list args>
+'git bundle' create <file> <git-rev-list-args>
 'git bundle' verify <file>
-'git bundle' list-heads <file> [refname...]
-'git bundle' unbundle <file> [refname...]
+'git bundle' list-heads <file> [<refname>...]
+'git bundle' unbundle <file> [<refname>...]
 
 DESCRIPTION
 -----------
@@ -34,57 +34,58 @@ OPTIONS
 -------
 
 create <file>::
-       Used to create a bundle named 'file'.  This requires the
-       'git rev-list' arguments to define the bundle contents.
+       Used to create a bundle named 'file'.  This requires the
+       'git-rev-list-args' arguments to define the bundle contents.
 
 verify <file>::
-       Used to check that a bundle file is valid and will apply
-       cleanly to the current repository.  This includes checks on the
-       bundle format itself as well as checking that the prerequisite
-       commits exist and are fully linked in the current repository.
-       'git bundle' prints a list of missing commits, if any, and exits
-       with a non-zero status.
+       Used to check that a bundle file is valid and will apply
+       cleanly to the current repository.  This includes checks on the
+       bundle format itself as well as checking that the prerequisite
+       commits exist and are fully linked in the current repository.
+       'git bundle' prints a list of missing commits, if any, and exits
+       with a non-zero status.
 
 list-heads <file>::
-       Lists the references defined in the bundle.  If followed by a
-       list of references, only references matching those given are
-       printed out.
+       Lists the references defined in the bundle.  If followed by a
+       list of references, only references matching those given are
+       printed out.
 
 unbundle <file>::
-       Passes the objects in the bundle to 'git index-pack'
-       for storage in the repository, then prints the names of all
-       defined references. If a list of references is given, only
-       references matching those in the list are printed. This command is
-       really plumbing, intended to be called only by 'git fetch'.
-
-[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 transport.  For example, `master\~10..master` causes the
-       current master reference to be packaged along with all objects
-       added since its 10th ancestor commit.  There is no explicit
-       limit to the number of references and objects that may be
-       packaged.
-
-
-[refname...]::
-       A list of references used to limit the references reported as
-       available. This is principally of use to 'git fetch', which
-       expects to receive only those references asked for and not
-       necessarily everything in the pack (in this case, 'git bundle' acts
-       like 'git fetch-pack').
+       Passes the objects in the bundle to 'git index-pack'
+       for storage in the repository, then prints the names of all
+       defined references. If a list of references is given, only
+       references matching those in the list are printed. This command is
+       really plumbing, intended to be called only by 'git fetch'.
+
+<git-rev-list-args>::
+       A list of arguments, acceptable to 'git rev-parse' and
+       'git rev-list' (and containing a named ref, see SPECIFYING REFERENCES
+       below), that specifies the specific objects and references
+       to transport.  For example, `master{tilde}10..master` causes the
+       current master reference to be packaged along with all objects
+       added since its 10th ancestor commit.  There is no explicit
+       limit to the number of references and objects that may be
+       packaged.
+
+
+[<refname>...]::
+       A list of references used to limit the references reported as
+       available. This is principally of use to 'git fetch', which
+       expects to receive only those references asked for and not
+       necessarily everything in the pack (in this case, 'git bundle' acts
+       like 'git fetch-pack').
 
 SPECIFYING REFERENCES
 ---------------------
 
 'git bundle' will only package references that are shown by
 'git show-ref': this includes heads, tags, and remote heads.  References
-such as `master\~1` cannot be packaged, but are perfectly suitable for
+such as `master{tilde}1` cannot be packaged, but are perfectly suitable for
 defining the basis.  More than one reference may be packaged, and more
 than one basis can be specified.  The objects packaged are those not
 contained in the union of the given bases.  Each basis can be
-specified explicitly (e.g. `^master\~10`), or implicitly (e.g.
-`master\~10..master`, `--since=10.days.ago master`).
+specified explicitly (e.g. `^master{tilde}10`), or implicitly (e.g.
+`master{tilde}10..master`, `--since=10.days.ago master`).
 
 It is very important that the basis used be held by the destination.
 It is okay to err on the side of caution, causing the bundle file
@@ -154,7 +155,7 @@ machineB$ git pull
 If you know up to what commit the intended recipient repository should
 have the necessary objects, you can use that knowledge to specify the
 basis, giving a cut-off point to limit the revisions and objects that go
-in the resulting bundle. The previous example used lastR2bundle tag
+in the resulting bundle. The previous example used the lastR2bundle tag
 for this purpose, but you can use any other options that you would give to
 the linkgit:git-log[1] command. Here are more examples:
 
@@ -194,7 +195,7 @@ references when fetching:
 $ git fetch mybundle master:localRef
 ----------------
 
-You can also see what references it offers.
+You can also see what references it offers:
 
 ----------------
 $ git ls-remote mybundle
index a3f56b07fd251cbd121c28189d1f9018b892b48e..544ba7ba218550ac1b09d9c95dc6f60ac604f7e8 100644 (file)
@@ -27,7 +27,7 @@ OPTIONS
 <object>::
        The name of the object to show.
        For a more complete list of ways to spell object names, see
-       the "SPECIFYING REVISIONS" section in linkgit:gitrevisions[1].
+       the "SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
 
 -t::
        Instead of the content, show the object type identified by
index f5c2e0601db6782b2a8690c687611b93da935024..205d83dd0b281d59e0f53284d29846c8eb58a246 100644 (file)
@@ -49,7 +49,7 @@ git imposes the following rules on how references are named:
 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
-reference name expressions (see linkgit:gitrevisions[1]):
+reference name expressions (see linkgit:gitrevisions[7]):
 
 . A double-dot `..` is often used as in `ref1..ref2`, and in some
   contexts this notation means `{caret}ref1 ref2` (i.e. not in
index d6aa6e14eb32967a09c5987fad26ece2749a65b8..0c0a9c14bc8e78ada9fb9b7174fcb90698e497a0 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
                   [--stage=<number>|all]
                   [--temp]
                   [-z] [--stdin]
-                  [--] [<file>]\*
+                  [--] [<file>...]
 
 DESCRIPTION
 -----------
index 66e570113a077d2c52c4fc3ac09d24a272df5301..880763d391d18364485edc2e1e3dfc6f52243973 100644 (file)
@@ -45,14 +45,14 @@ successful.
 
 'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...::
 
-       When <paths> or `--patch` are given, 'git checkout' *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
-       either of them results in an error. The <tree-ish> argument can be
-       used to specify a specific tree-ish (i.e. commit, tag or tree)
-       to update the index for the given paths before updating the
-       working tree.
+       When <paths> or `--patch` are given, 'git checkout' 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 either of them results in an error.  The
+       <tree-ish> argument can be used to specify a specific tree-ish
+       (i.e.  commit, tag or tree) to update the index for the given
+       paths before updating the working tree.
 +
 The index may contain unmerged entries because of a previous failed merge.
 By default, if you try to check out such an entry from the index, the
@@ -98,7 +98,7 @@ entries; instead, unmerged entries are ignored.
        "--track" in linkgit:git-branch[1] for details.
 +
 If no '-b' option is given, the name of the new branch will be
-derived from the remote branch.  If "remotes/" or "refs/remotes/"
+derived from the remote-tracking branch.  If "remotes/" or "refs/remotes/"
 is prefixed it is stripped away, and then the part up to the
 next slash (which would be the nickname of the remote) is removed.
 This would tell us to use "hack" as the local branch when branching
@@ -187,7 +187,7 @@ As a special case, the `"@\{-N\}"` syntax for the N-th last branch
 checks out the branch (instead of detaching).  You may also specify
 `-` which is synonymous with `"@\{-1\}"`.
 +
-As a further special case, you may use `"A...B"` as a shortcut for the
+As a further special case, you may use `"A\...B"` as a shortcut for the
 merge base of `A` and `B` if there is exactly one merge base. You can
 leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
index 2cef579316ba11e68d65dc4d7309f2e56a465b3d..749d68a72bf720d82260fe175be0824f7ad069eb 100644 (file)
@@ -21,7 +21,7 @@ OPTIONS
 <commit>...::
        Commits to cherry-pick.
        For a more complete list of ways to spell commits, see
-       linkgit:gitrevisions[1].
+       linkgit:gitrevisions[7].
        Sets of commits can be passed but no traversal is done by
        default, as if the '--no-walk' option was specified, see
        linkgit:git-rev-list[1].
@@ -79,6 +79,16 @@ effect to your index in a row.
        cherry-pick'ed commit, then a fast forward to this commit will
        be performed.
 
+--strategy=<strategy>::
+       Use the given merge strategy.  Should only be used once.
+       See the MERGE STRATEGIES section in linkgit:git-merge[1]
+       for details.
+
+-X<option>::
+--strategy-option=<option>::
+       Pass the merge strategy-specific option through to the
+       merge strategy.  See linkgit:git-merge[1] for details.
+
 EXAMPLES
 --------
 git cherry-pick master::
@@ -92,7 +102,7 @@ git cherry-pick ^HEAD master::
        Apply the changes introduced by all commits that are ancestors
        of master but not of HEAD to produce new commits.
 
-git cherry-pick master\~4 master~2::
+git cherry-pick master{tilde}4 master{tilde}2::
 
        Apply the changes introduced by the fifth and third last
        commits pointed to by master and create 2 new commits with
@@ -120,6 +130,28 @@ git rev-list --reverse master \-- README | git cherry-pick -n --stdin::
        so the result can be inspected and made into a single new
        commit if suitable.
 
+The following sequence attempts to backport a patch, bails out because
+the code the patch applies to has changed too much, and then tries
+again, this time exercising more care about matching up context lines.
+
+------------
+$ git cherry-pick topic^             <1>
+$ git diff                           <2>
+$ git reset --merge ORIG_HEAD        <3>
+$ git cherry-pick -Xpatience topic^  <4>
+------------
+<1> apply the change that would be shown by `git show topic^`.
+In this example, the patch does not apply cleanly, so
+information about the conflict is written to the index and
+working tree and no new commit results.
+<2> summarize changes to be reconciled
+<3> cancel the cherry-pick.  In other words, return to the
+pre-cherry-pick state, preserving any local modifications you had in
+the working tree.
+<4> try to apply the change introduced by `topic^` again,
+spending extra time to avoid mistakes based on incorrectly matching
+context lines.
+
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com>
index dc7d3d17b151d05827925fdcd2806945e2e278cb..42e7021215563f146a66d14e1b642d4dbe55bfaf 100644 (file)
@@ -12,7 +12,8 @@ SYNOPSIS
 'git clone' [--template=<template_directory>]
          [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
          [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
-         [--depth <depth>] [--recursive] [--] <repository> [<directory>]
+         [--depth <depth>] [--recursive|--recurse-submodules] [--] <repository>
+         [<directory>]
 
 DESCRIPTION
 -----------
@@ -128,7 +129,12 @@ objects from the source repository into a pack in the cloned repository.
        configuration variables are created.
 
 --mirror::
-       Set up a mirror of the remote repository.  This implies `--bare`.
+       Set up a mirror of the source repository.  This implies `--bare`.
+       Compared to `--bare`, `--mirror` not only maps local branches of the
+       source to local branches of the target, it maps all refs (including
+       remote-tracking branches, notes etc.) and sets up a refspec configuration such
+       that all these refs are overwritten by a `git remote update` in the
+       target repository.
 
 --origin <name>::
 -o <name>::
@@ -162,6 +168,7 @@ objects from the source repository into a pack in the cloned repository.
        as patches.
 
 --recursive::
+--recurse-submodules::
        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
index 61888547a16e904a766734c6883d95ee46dfc61e..5dcf4278fc497e4d72093069bdcd18b4ecff0949 100644 (file)
@@ -8,7 +8,7 @@ git-commit-tree - Create a new commit object
 
 SYNOPSIS
 --------
-'git commit-tree' <tree> [-p <parent commit>]\* < changelog
+'git commit-tree' <tree> [(-p <parent commit>)...] < changelog
 
 DESCRIPTION
 -----------
index 42fb1f57b21d7c355d3df6be07329c478786a9a7..b586c0f442afd33c2875af26b83bb261de7361b0 100644 (file)
@@ -9,10 +9,10 @@ SYNOPSIS
 --------
 [verse]
 'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run]
-          [(-c | -C) <commit>] [-F <file> | -m <msg>] [--reset-author]
-          [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
-          [--date=<date>] [--cleanup=<mode>] [--status | --no-status] [--]
-          [[-i | -o ]<file>...]
+          [(-c | -C | --fixup | --squash) <commit>] [-F <file> | -m <msg>]
+          [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify]
+          [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>]
+          [--status | --no-status] [-i | -o] [--] [<file>...]
 
 DESCRIPTION
 -----------
@@ -70,6 +70,19 @@ OPTIONS
        Like '-C', but with '-c' the editor is invoked, so that
        the user can further edit the commit message.
 
+--fixup=<commit>::
+       Construct a commit message for use with `rebase --autosquash`.
+       The commit message will be the subject line from the specified
+       commit with a prefix of "fixup! ".  See linkgit:git-rebase[1]
+       for details.
+
+--squash=<commit>::
+       Construct a commit message for use with `rebase --autosquash`.
+       The commit message subject line is taken from the specified
+       commit with a prefix of "squash! ".  Can be used with additional
+       commit message options (`-m`/`-c`/`-C`/`-F`). See
+       linkgit:git-rebase[1] for details.
+
 --reset-author::
        When used with -C/-c/--amend options, declare that the
        authorship of the resulting commit now belongs of the committer.
index b2696efae95b20b693251a9d9785405218285084..d25661eb215c8bd2002933a48dd6ad5ea360459a 100644 (file)
@@ -114,11 +114,11 @@ $ git cherry cvshead myhead | sed -n 's/^+ //p' | xargs -l1 git cvsexportcommit
 
 Author
 ------
-Written by Martin Langhoff <martin@catalyst.net.nz> and others.
+Written by Martin Langhoff <martin@laptop.org> and others.
 
 Documentation
 --------------
-Documentation by Martin Langhoff <martin@catalyst.net.nz> and others.
+Documentation by Martin Langhoff <martin@laptop.org> and others.
 
 GIT
 ---
index f4472c61dbb297d61053ef2e8503ad7c56d06148..70cbb2cae7b60a5c6514d0990da91ed5b23e0827 100644 (file)
@@ -399,13 +399,13 @@ This program is copyright The Open University UK - 2006.
 Authors:
 
 - Martyn Smith    <martyn@catalyst.net.nz>
-- Martin Langhoff <martin@catalyst.net.nz>
+- Martin Langhoff <martin@laptop.org>
 
 with ideas and patches from participants of the git-list <git@vger.kernel.org>.
 
 Documentation
 --------------
-Documentation by Martyn Smith <martyn@catalyst.net.nz>, Martin Langhoff <martin@catalyst.net.nz>, and Matthias Urlichs <smurf@smurf.noris.de>.
+Documentation by Martyn Smith <martyn@catalyst.net.nz>, Martin Langhoff <martin@laptop.org>, and Matthias Urlichs <smurf@smurf.noris.de>.
 
 GIT
 ---
index 01c9f8eb9eff634fe8ab99908c62207ebecd5e31..d15cb6a845645d7fe76e7782ce580b3678bd25e4 100644 (file)
@@ -9,15 +9,15 @@ SYNOPSIS
 --------
 [verse]
 'git daemon' [--verbose] [--syslog] [--export-all]
-            [--timeout=n] [--init-timeout=n] [--max-connections=n]
-            [--strict-paths] [--base-path=path] [--base-path-relaxed]
-            [--user-path | --user-path=path]
-            [--interpolated-path=pathtemplate]
-            [--reuseaddr] [--detach] [--pid-file=file]
-            [--enable=service] [--disable=service]
-            [--allow-override=service] [--forbid-override=service]
-            [--inetd | [--listen=host_or_ipaddr] [--port=n] [--user=user [--group=group]]
-            [directory...]
+            [--timeout=<n>] [--init-timeout=<n>] [--max-connections=<n>]
+            [--strict-paths] [--base-path=<path>] [--base-path-relaxed]
+            [--user-path | --user-path=<path>]
+            [--interpolated-path=<pathtemplate>]
+            [--reuseaddr] [--detach] [--pid-file=<file>]
+            [--enable=<service>] [--disable=<service>]
+            [--allow-override=<service>] [--forbid-override=<service>]
+            [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>] [--user=<user> [--group=<group>]]
+            [<directory>...]
 
 DESCRIPTION
 -----------
@@ -48,7 +48,7 @@ OPTIONS
        'git daemon' will refuse to start when this option is enabled and no
        whitelist is specified.
 
---base-path=path::
+--base-path=<path>::
        Remap all the path requests as relative to the given path.
        This is sort of "GIT root" - if you run 'git daemon' with
        '--base-path=/srv/git' on example.com, then if you later try to pull
@@ -61,7 +61,7 @@ OPTIONS
        This is useful for switching to --base-path usage, while still
        allowing the old paths.
 
---interpolated-path=pathtemplate::
+--interpolated-path=<pathtemplate>::
        To support virtual hosting, an interpolated path template can be
        used to dynamically construct alternate paths.  The template
        supports %H for the target hostname as supplied by the client but
@@ -78,29 +78,31 @@ OPTIONS
 
 --inetd::
        Have the server run as an inetd service. Implies --syslog.
-       Incompatible with --port, --listen, --user and --group options.
+       Incompatible with --detach, --port, --listen, --user and --group
+       options.
 
---listen=host_or_ipaddr::
+--listen=<host_or_ipaddr>::
        Listen on a specific IP address or hostname.  IP addresses can
        be either an IPv4 address or an IPv6 address if supported.  If IPv6
        is not supported, then --listen=hostname is also not supported and
        --listen must be given an IPv4 address.
+       Can be given more than once.
        Incompatible with '--inetd' option.
 
---port=n::
+--port=<n>::
        Listen on an alternative port.  Incompatible with '--inetd' option.
 
---init-timeout=n::
+--init-timeout=<n>::
        Timeout between the moment the connection is established and the
        client request is received (typically a rather low value, since
        that should be basically immediate).
 
---timeout=n::
+--timeout=<n>::
        Timeout for specific client sub-requests. This includes the time
        it takes for the server to process the sub-request and the time spent
        waiting for the next client's request.
 
---max-connections=n::
+--max-connections=<n>::
        Maximum number of concurrent clients, defaults to 32.  Set it to
        zero for no limit.
 
@@ -109,7 +111,7 @@ OPTIONS
        --verbose, thus by default only error conditions will be logged.
 
 --user-path::
---user-path=path::
+--user-path=<path>::
        Allow {tilde}user notation to be used in requests.  When
        specified with no parameter, requests to
        git://host/{tilde}alice/foo is taken as a request to access
@@ -129,12 +131,12 @@ OPTIONS
 --detach::
        Detach from the shell. Implies --syslog.
 
---pid-file=file::
+--pid-file=<file>::
        Save the process id in 'file'.  Ignored when the daemon
        is run under `--inetd`.
 
---user=user::
---group=group::
+--user=<user>::
+--group=<group>::
        Change daemon's uid and gid before entering the service loop.
        When only `--user` is given without `--group`, the
        primary group ID for the user is used.  The values of
@@ -145,16 +147,16 @@ Giving these options is an error when used with `--inetd`; use
 the facility of inet daemon to achieve the same before spawning
 'git daemon' if needed.
 
---enable=service::
---disable=service::
+--enable=<service>::
+--disable=<service>::
        Enable/disable the service site-wide per default.  Note
        that a service disabled site-wide can still be enabled
        per repository if it is marked overridable and the
        repository enables the service with a configuration
        item.
 
---allow-override=service::
---forbid-override=service::
+--allow-override=<service>::
+--forbid-override=<service>::
        Allow/forbid overriding the site-wide default with per
        repository configuration.  By default, all the services
        are overridable.
index 7ef9d51577594ae6a27f71bae251d6ee2f52befa..02e015ad9c41e760b3ab37d2c5a9e4ef9ef0c4f5 100644 (file)
@@ -37,7 +37,7 @@ OPTIONS
 --all::
        Instead of using only the annotated tags, use any ref
        found in `.git/refs/`.  This option enables matching
-       any known branch, remote branch, or lightweight tag.
+       any known branch, remote-tracking branch, or lightweight tag.
 
 --tags::
        Instead of using only the annotated tags, use any tag
index 08fd4099addac3959e5aedbd1a0668367f07bb21..f6ac847507a635759323565be44b088b479d8bf0 100644 (file)
@@ -8,12 +8,17 @@ git-diff - Show changes between commits, commit and working tree, etc
 
 SYNOPSIS
 --------
-'git diff' [<common diff options>] <commit>{0,2} [--] [<path>...]
+[verse]
+'git diff' [options] [<commit>] [--] [<path>...]
+'git diff' [options] --cached [<commit>] [--] [<path>...]
+'git diff' [options] <commit> <commit> [--] [<path>...]
+'git diff' [options] [--no-index] [--] <path> <path>
 
 DESCRIPTION
 -----------
-Show changes between two trees, a tree and the working tree, a
-tree and the index file, or the index file and the working tree.
+Show changes between the working tree and the index or a tree, changes
+between the index and a tree, changes between two trees, or changes
+between two files on disk.
 
 'git diff' [--options] [--] [<path>...]::
 
@@ -23,9 +28,9 @@ tree and the index file, or the index file and the working tree.
        further add to the index but you still haven't.  You can
        stage these changes by using linkgit:git-add[1].
 +
-If exactly two paths are given, and at least one is untracked,
-compare the two files / directories. This behavior can be
-forced by --no-index.
+If exactly two paths are given and at least one points outside
+the current repository, 'git diff' will compare the two files /
+directories. This behavior can be forced by --no-index.
 
 'git diff' [--options] --cached [<commit>] [--] [<path>...]::
 
@@ -64,15 +69,16 @@ forced by --no-index.
 
 Just in case if you are doing something exotic, it should be
 noted that all of the <commit> in the above description, except
-for the last two forms that use ".." notations, can be any
-<tree-ish>.
+in the last two forms that use ".." notations, can be any
+<tree>.  The third form ('git diff <commit> <commit>') can also
+be used to compare two <blob> objects.
 
 For a more complete list of ways to spell <commit>, see
-"SPECIFYING REVISIONS" section in linkgit:gitrevisions[1].
+"SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
 However, "diff" is about comparing two _endpoints_, not ranges,
 and the range notations ("<commit>..<commit>" and
 "<commit>\...<commit>") do not mean a range as defined in the
-"SPECIFYING RANGES" section in linkgit:gitrevisions[1].
+"SPECIFYING RANGES" section in linkgit:gitrevisions[7].
 
 OPTIONS
 -------
@@ -159,8 +165,12 @@ rewrites (very expensive).
 
 SEE ALSO
 --------
-linkgit:git-difftool[1]::
-       Show changes using common diff tools
+diff(1),
+linkgit:git-difftool[1],
+linkgit:git-log[1],
+linkgit:gitdiffcore[7],
+linkgit:git-format-patch[1],
+linkgit:git-apply[1]
 
 Author
 ------
index 8250bad2ce95245ca8e2a41c0740d0029b92ae4b..db87f1d42382e32d7e3ee94e5c3d0a032b7430e2 100644 (file)
@@ -7,13 +7,14 @@ git-difftool - Show changes using common diff tools
 
 SYNOPSIS
 --------
-'git difftool' [<options>] <commit>{0,2} [--] [<path>...]
+'git difftool' [<options>] [<commit> [<commit>]] [--] [<path>...]
 
 DESCRIPTION
 -----------
 'git difftool' is a git command that allows you to compare and edit files
 between revisions using common diff tools.  'git difftool' is a frontend
-to 'git diff' and accepts the same options and arguments.
+to 'git diff' and accepts the same options and arguments. See
+linkgit:git-diff[1].
 
 OPTIONS
 -------
@@ -55,14 +56,16 @@ the configured command line will be invoked with the following
 variables available: `$LOCAL` is set to the name of the temporary
 file containing the contents of the diff pre-image and `$REMOTE`
 is set to the name of the temporary file containing the contents
-of the diff post-image.  `$BASE` is provided for compatibility
-with custom merge tool commands and has the same value as `$LOCAL`.
+of the diff post-image.  `$MERGED` is the name of the file which is
+being compared. `$BASE` is provided for compatibility
+with custom merge tool commands and has the same value as `$MERGED`.
 
 -x <command>::
 --extcmd=<command>::
        Specify a custom command for viewing diffs.
        'git-difftool' ignores the configured defaults and runs
        `$command $LOCAL $REMOTE` when this option is specified.
+       Additionally, `$BASE` is set in the environment.
 
 -g::
 --gui::
index 98ec6b5871b8dfb3cee2e1302099536d8b0dcd7f..e05b686b1e4784290e40993469a2c5fab9b4ce5d 100644 (file)
@@ -90,10 +90,16 @@ marks the same across runs.
        resulting stream can only be used by a repository which
        already contains the necessary objects.
 
-[git-rev-list-args...]::
+--full-tree::
+       This option will cause fast-export to issue a "deleteall"
+       directive for each commit followed by a full list of all files
+       in the commit (as opposed to just listing the files which are
+       different from the commit's first parent).
+
+[<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
+       to export.  For example, `master{tilde}10..master` causes the
        current master reference to be exported along with all objects
        added since its 10th ancestor commit.
 
index 966ba4f21337e72f7be0a5b852e76a8b12192f7f..4415e636352ea88f34a798b6c29dc5e34d60d868 100644 (file)
@@ -92,6 +92,11 @@ OPTIONS
        --(no-)-relative-marks= with the --(import|export)-marks=
        options.
 
+--cat-blob-fd=<fd>::
+       Specify the file descriptor that will be written to
+       when the `cat-blob` command is encountered in the stream.
+       The default behaviour is to write to `stdout`.
+
 --export-pack-edges=<file>::
        After creating a packfile, print a line of data to
        <file> listing the filename of the packfile and the last
@@ -320,6 +325,11 @@ and control the current import process.  More detailed discussion
        standard output.  This command is optional and is not needed
        to perform an import.
 
+`cat-blob`::
+       Causes fast-import to print a blob in 'cat-file --batch'
+       format to the file descriptor set with `--cat-blob-fd` or
+       `stdout` if unspecified.
+
 `feature`::
        Require that fast-import supports the specified feature, or
        abort if it does not.
@@ -439,7 +449,7 @@ Marks must be declared (via `mark`) before they can be used.
 * A complete 40 byte or abbreviated commit SHA-1 in hex.
 
 * Any valid Git SHA-1 expression that resolves to a commit.  See
-  ``SPECIFYING REVISIONS'' in linkgit:gitrevisions[1] for details.
+  ``SPECIFYING REVISIONS'' in linkgit:gitrevisions[7] for details.
 
 The special case of restarting an incremental import from the
 current branch value should be written as:
@@ -532,6 +542,8 @@ The value of `<path>` must be in canonical form. That is it must not:
 * contain the special component `.` or `..` (e.g. `foo/./bar` and
   `foo/../bar` are invalid).
 
+The root of the tree can be represented by an empty string as `<path>`.
+
 It is recommended that `<path>` always be encoded using UTF-8.
 
 `filedelete`
@@ -876,34 +888,65 @@ Placing a `progress` command immediately after a `checkpoint` will
 inform the reader when the `checkpoint` has been completed and it
 can safely access the refs that fast-import updated.
 
-`feature`
-~~~~~~~~~
-Require that fast-import supports the specified feature, or abort if
-it does not.
+`cat-blob`
+~~~~~~~~~~
+Causes fast-import to print a blob to a file descriptor previously
+arranged with the `--cat-blob-fd` argument.  The command otherwise
+has no impact on the current import; its main purpose is to
+retrieve blobs that may be in fast-import's memory but not
+accessible from the target repository.
 
 ....
-       'feature' SP <feature> LF
+       'cat-blob' SP <dataref> LF
 ....
 
-The <feature> part of the command may be any string matching
-^[a-zA-Z][a-zA-Z-]*$ and should be understood by fast-import.
+The `<dataref>` can be either a mark reference (`:<idnum>`)
+set previously or a full 40-byte SHA-1 of a Git blob, preexisting or
+ready to be written.
 
-Feature work identical as their option counterparts with the
-exception of the import-marks feature, see below.
+Output uses the same format as `git cat-file --batch`:
 
-The following features are currently supported:
+====
+       <sha1> SP 'blob' SP <size> LF
+       <contents> LF
+====
 
-* date-format
-* import-marks
-* export-marks
-* relative-marks
-* no-relative-marks
-* force
+This command can be used anywhere in the stream that comments are
+accepted.  In particular, the `cat-blob` command can be used in the
+middle of a commit but not in the middle of a `data` command.
 
-The import-marks behaves differently from when it is specified as
-commandline option in that only one "feature import-marks" is allowed
-per stream. Also, any --import-marks= specified on the commandline
-will override those from the stream (if any).
+`feature`
+~~~~~~~~~
+Require that fast-import supports the specified feature, or abort if
+it does not.
+
+....
+       'feature' SP <feature> ('=' <argument>)? LF
+....
+
+The <feature> part of the command may be any one of the following:
+
+date-format::
+export-marks::
+relative-marks::
+no-relative-marks::
+force::
+       Act as though the corresponding command-line option with
+       a leading '--' was passed on the command line
+       (see OPTIONS, above).
+
+import-marks::
+       Like --import-marks except in two respects: first, only one
+       "feature import-marks" command is allowed per stream;
+       second, an --import-marks= command-line option overrides
+       any "feature import-marks" command in the stream.
+
+cat-blob::
+       Ignored.  Versions of fast-import not supporting the
+       "cat-blob" command will exit with a message indicating so.
+       This lets the import error out early with a clear message,
+       rather than wasting time on the early part of an import
+       before the unsupported command is detected.
 
 `option`
 ~~~~~~~~
@@ -930,6 +973,7 @@ not be passed as option:
 * date-format
 * import-marks
 * export-marks
+* cat-blob-fd
 * force
 
 Crash Reports
@@ -1230,6 +1274,13 @@ and lazy loading of subtrees, allows fast-import to efficiently import
 projects with 2,000+ branches and 45,114+ files in a very limited
 memory footprint (less than 2.7 MiB per active branch).
 
+Signals
+-------
+Sending *SIGUSR1* to the 'git fast-import' process ends the current
+packfile early, simulating a `checkpoint` command.  The impatient
+operator can use this facility to peek at the objects and refs from an
+import in progress, at the cost of some added running time and worse
+compression.
 
 Author
 ------
index 400fe7f956961ba0ddf09d2dcc6e539adec7ff74..c76e313923f6a6655ad69209070e8333ca360678 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
 
 'git fetch' [<options>] <group>
 
-'git fetch' --multiple [<options>] [<repository> | <group>]...
+'git fetch' --multiple [<options>] [(<repository> | <group>)...]
 
 'git fetch' --all [<options>]
 
@@ -26,7 +26,7 @@ The ref names and their object names of fetched refs are stored
 in `.git/FETCH_HEAD`.  This information is left for a later merge
 operation done by 'git merge'.
 
-When <refspec> stores the fetched result in tracking branches,
+When <refspec> stores the fetched result in remote-tracking branches,
 the tags that point at these branches are automatically
 followed.  This is done by first fetching from the remote using
 the given <refspec>s, and if the repository has objects that are
index 020028cf9a54540f5e0e403b52e08dd2921a644e..796e7489ff7ee573a65647f4de33df932bd1bea1 100644 (file)
@@ -81,7 +81,7 @@ OPTIONS
        This filter may be used if you only need to modify the environment
        in which the commit will be performed.  Specifically, you might
        want to rewrite the author/committer name/email/time environment
-       variables (see linkgit:git-commit[1] for details).  Do not forget
+       variables (see linkgit:git-commit-tree[1] for details).  Do not forget
        to re-export the variables.
 
 --tree-filter <command>::
@@ -117,7 +117,7 @@ OPTIONS
        This is the filter for performing the commit.
        If this filter is specified, it will be called instead of the
        'git commit-tree' command, with arguments of the form
-       "<TREE_ID> [-p <PARENT_COMMIT_ID>]..." and the log message on
+       "<TREE_ID> [(-p <PARENT_COMMIT_ID>)...]" and the log message on
        stdin.  The commit id is expected on stdout.
 +
 As a special extension, the commit filter may emit multiple
@@ -159,18 +159,7 @@ to other tags will be rewritten to point to the underlying commit.
 --subdirectory-filter <directory>::
        Only look at the history which touches the given subdirectory.
        The result will contain that directory (and only that) as its
-       project root.  Implies --remap-to-ancestor.
-
---remap-to-ancestor::
-       Rewrite refs to the nearest rewritten ancestor instead of
-       ignoring them.
-+
-Normally, positive refs on the command line are only changed if the
-commit they point to was rewritten.  However, you can limit the extent
-of this rewriting by using linkgit:rev-list[1] arguments, e.g., path
-limiters.  Refs pointing to such excluded commits would then normally
-be ignored.  With this option, they are instead rewritten to point at
-the nearest ancestor that was not excluded.
+       project root. Implies <<Remap_to_ancestor>>.
 
 --prune-empty::
        Some kind of filters will generate empty commits, that left the tree
@@ -204,7 +193,18 @@ the nearest ancestor that was not excluded.
        Arguments for 'git rev-list'.  All positive refs included by
        these options are rewritten.  You may also specify options
        such as '--all', but you must use '--' to separate them from
-       the 'git filter-branch' options.
+       the 'git filter-branch' options. Implies <<Remap_to_ancestor>>.
+
+
+[[Remap_to_ancestor]]
+Remap to ancestor
+~~~~~~~~~~~~~~~~~
+
+By using linkgit:rev-list[1] arguments, e.g., path limiters, you can limit the
+set of revisions which get rewritten. However, positive refs on the command
+line are distinguished: we don't let them be excluded by such limiters. For
+this purpose, they are instead rewritten to point at the nearest ancestor that
+was not excluded.
 
 
 Examples
index a585dbe898171ddb8a205cc5a898d695540f80a6..75adf7a502e18aa11e7a8424acf217b4bf6be64c 100644 (file)
@@ -9,8 +9,8 @@ git-fmt-merge-msg - Produce a merge commit message
 SYNOPSIS
 --------
 [verse]
-'git fmt-merge-msg' [--log | --no-log] <$GIT_DIR/FETCH_HEAD
-'git fmt-merge-msg' [--log | --no-log] -F <file>
+'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] <$GIT_DIR/FETCH_HEAD
+'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] -F <file>
 
 DESCRIPTION
 -----------
@@ -24,10 +24,12 @@ automatically invoking 'git merge'.
 OPTIONS
 -------
 
---log::
+--log[=<n>]::
        In addition to branch names, populate the log message with
        one-line descriptions from the actual commits that are being
-       merged.
+       merged.  At most <n> commits from each merge parent will be
+       used (20 if <n> is omitted).  This overrides the `merge.log`
+       configuration variable.
 
 --no-log::
        Do not list one-line descriptions from the actual commits being
@@ -38,6 +40,11 @@ OPTIONS
        Synonyms to --log and --no-log; these are deprecated and will be
        removed in the future.
 
+-m <message>::
+--message <message>::
+       Use <message> instead of the branch names for the first line
+       of the log message.  For use with `--log`.
+
 -F <file>::
 --file <file>::
        Take the list of merged objects from <file> instead of
@@ -47,8 +54,10 @@ CONFIGURATION
 -------------
 
 merge.log::
-       Whether to include summaries of merged commits in newly
-       merge commit messages. False by default.
+       In addition to branch names, populate the log message with at
+       most the specified number of one-line descriptions from the
+       actual commits that are being merged.  Defaults to false, and
+       true is a synonym for 20.
 
 merge.summary::
        Synonym to `merge.log`; this is deprecated and will be removed in
index 390d85ccaea6ada3f3bce6148bcdd84d4f0bc2ac..fac1cf55e51ff289167270de4f248dad959741c6 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
-                  [--sort=<key>]\* [--format=<format>] [<pattern>...]
+                  [(--sort=<key>)...] [--format=<format>] [<pattern>...]
 
 DESCRIPTION
 -----------
index 4b3f5ba5358da469feff4e861e69717f16e81d21..9dcafc6d4442b6db66df642e92336537395b9919 100644 (file)
@@ -39,7 +39,7 @@ There are two ways to specify which commits to operate on.
    that leads to the <since> to be output.
 
 2. Generic <revision range> expression (see "SPECIFYING
-   REVISIONS" section in linkgit:gitrevisions[1]) means the
+   REVISIONS" section in linkgit:gitrevisions[7]) means the
    commits in the specified range.
 
 The first rule takes precedence in the case of a single <commit>.  To
@@ -74,7 +74,7 @@ OPTIONS
 include::diff-options.txt[]
 
 -<n>::
-       Limits the number of patches to prepare.
+       Prepare patches from the topmost <n> commits.
 
 -o <dir>::
 --output-directory <dir>::
index 3ad48a633663ec22af16b4737c42fd7e7664e4d2..86f9b2bf91f604cbfd175a4e241fc14fcb5828d5 100644 (file)
@@ -123,9 +123,6 @@ dangling <type> <object>::
        The <type> object <object>, is present in the database but never
        'directly' used. A dangling commit could be a root node.
 
-warning: git-fsck: tree <tree> has full pathnames in it::
-       And it shouldn't...
-
 sha1 mismatch <object>::
        The database has an object who's sha1 doesn't match the
        database value.
index 315f07ef1c6997c98c4e446af507c3e1e4566218..26632414b2646ba80a17fa861d422a9c8c957810 100644 (file)
@@ -89,7 +89,7 @@ are not part of the current project most users will want to expire
 them sooner.  This option defaults to '30 days'.
 
 The above two configuration variables can be given to a pattern.  For
-example, this sets non-default expiry values only to remote tracking
+example, this sets non-default expiry values only to remote-tracking
 branches:
 
 ------------
@@ -107,7 +107,7 @@ how long records of conflicted merge you have not resolved are
 kept.  This defaults to 15 days.
 
 The optional configuration variable 'gc.packrefs' determines if
-'git gc' runs 'git pack-refs'. This can be set to "nobare" to enable
+'git gc' runs 'git pack-refs'. This can be set to "notbare" to enable
 it within all non-bare repos or it can be set to a boolean value.
 This defaults to true.
 
@@ -128,8 +128,8 @@ Notes
 
 'git gc' tries very hard to be safe about the garbage it collects. In
 particular, it will keep not only objects referenced by your current set
-of branches and tags, but also objects referenced by the index, remote
-tracking branches, refs saved by 'git filter-branch' in
+of branches and tags, but also objects referenced by the index,
+remote-tracking branches, refs saved by 'git filter-branch' in
 refs/original/, or reflogs (which may reference commits in branches
 that were later amended or rewound).
 
index 5474dd7f94c4b3217230808e656662d0edcef207..dab0a78fa890d0c0cd193d89b7f4ac9ed19fb001 100644 (file)
@@ -191,11 +191,11 @@ OPTIONS
 Examples
 --------
 
-git grep 'time_t' \-- '*.[ch]'::
+git grep {apostrophe}time_t{apostrophe} \-- {apostrophe}*.[ch]{apostrophe}::
        Looks for `time_t` in all tracked .c and .h files in the working
        directory and its subdirectories.
 
-git grep -e \'#define\' --and \( -e MAX_PATH -e PATH_MAX \)::
+git grep -e {apostrophe}#define{apostrophe} --and \( -e MAX_PATH -e PATH_MAX \)::
        Looks for a line that has `#define` and either `MAX_PATH` or
        `PATH_MAX`.
 
index f8df109d07240d4146ad37928147100f2d3a6bb2..eccd0ffd384bf90f1846bf4cfeb7670dbd64cb6e 100644 (file)
@@ -55,9 +55,9 @@ other display programs (see below).
 +
 The web browser can be specified using the configuration variable
 'help.browser', or 'web.browser' if the former is not set. If none of
-these config variables is set, the 'git web--browse' helper script
+these config variables is set, the 'git web{litdd}browse' helper script
 (called by 'git help') will pick a suitable default. See
-linkgit:git-web--browse[1] for more information about this.
+linkgit:git-web{litdd}browse[1] for more information about this.
 
 CONFIGURATION VARIABLES
 -----------------------
@@ -80,7 +80,7 @@ help.browser, web.browser and browser.<tool>.path
 The 'help.browser', 'web.browser' and 'browser.<tool>.path' will also
 be checked if the 'web' format is chosen (either by command line
 option or configuration variable). See '-w|--web' in the OPTIONS
-section above and linkgit:git-web--browse[1].
+section above and linkgit:git-web{litdd}browse[1].
 
 man.viewer
 ~~~~~~~~~~
index f3ccc72f0d68f03cd7b3fb6a36aabc8d7c7772a1..c2bb81042c80dbf93b7a651c47fc177358132f97 100644 (file)
@@ -59,10 +59,10 @@ OPTIONS
        the newly constructed pack and index before refs can be
        updated to use objects contained in the pack.
 
---keep='why'::
+--keep=<msg>::
        Like --keep create a .keep file before moving the index into
        its final destination, but rather than creating an empty file
-       place 'why' followed by an LF into the .keep file.  The 'why'
+       place '<msg>' followed by an LF into the .keep file.  The '<msg>'
        message can later be searched for within all .keep files to
        locate any which have outlived their usefulness.
 
index 246b07ebf94394d2079c2acc3e8c1a2e6c40bba9..00d4a124c918e7548628140646f698b9e37ab87a 100644 (file)
@@ -31,7 +31,7 @@ current working directory.
 Specify the directory from which templates will be used.  (See the "TEMPLATE
 DIRECTORY" section below.)
 
---shared[={false|true|umask|group|all|world|everybody|0xxx}]::
+--shared[=(false|true|umask|group|all|world|everybody|0xxx)]::
 
 Specify that the git repository is to be shared amongst several users.  This
 allows users belonging to the same group to push into that
index e70cea932063b04f3ab3c78b882f4981514d663e..7477ce8fa8e5185cfbd74a31b8d5accd18216a1b 100644 (file)
@@ -44,9 +44,9 @@ OPTIONS
 -b::
 --browser::
        The web browser that should be used to view the gitweb
-       page. This will be passed to the 'git web--browse' helper
+       page. This will be passed to the 'git web{litdd}browse' helper
        script along with the URL of the gitweb instance. See
-       linkgit:git-web--browse[1] for more information about this. If
+       linkgit:git-web{litdd}browse[1] for more information about this. If
        the script fails, the URL will be printed to stdout.
 
 start::
@@ -82,7 +82,7 @@ You may specify configuration in your .git/config
 
 If the configuration variable 'instaweb.browser' is not set,
 'web.browser' will be used instead if it is defined. See
-linkgit:git-web--browse[1] for more information about this.
+linkgit:git-web{litdd}browse[1] for more information about this.
 
 Author
 ------
index c213bdbdc500b1117805bc84cb2a0f31eeff3bcd..ff41784c60b04a276931fc90ab54a22a67024a1e 100644 (file)
@@ -31,7 +31,7 @@ OPTIONS
        either <since> or <until> is omitted, it defaults to
        `HEAD`, i.e. the tip of the current branch.
        For a more complete list of ways to spell <since>
-       and <until>, see linkgit:gitrevisions[1].
+       and <until>, see linkgit:gitrevisions[7].
 
 --follow::
        Continue listing the history of a file beyond renames
@@ -116,7 +116,7 @@ git log --follow builtin-rev-list.c::
 git log --branches --not --remotes=origin::
 
        Shows all commits that are in any of local branches but not in
-       any of remote tracking branches for 'origin' (what you have that
+       any of remote-tracking branches for 'origin' (what you have that
        origin doesn't).
 
 git log master --not --remotes=*/master::
index a7c8174d01810bfb28e331aafc27414dd6cfad50..86abd1345178d4c5d2362509fc075de80d2fc7e3 100644 (file)
@@ -10,14 +10,14 @@ SYNOPSIS
 --------
 [verse]
 'git ls-files' [-z] [-t] [-v]
-               (--[cached|deleted|others|ignored|stage|unmerged|killed|modified])\*
-               (-[c|d|o|i|s|u|k|m])\*
+               (--[cached|deleted|others|ignored|stage|unmerged|killed|modified])*
+               (-[c|d|o|i|s|u|k|m])*
                [-x <pattern>|--exclude=<pattern>]
                [-X <file>|--exclude-from=<file>]
                [--exclude-per-directory=<file>]
                [--exclude-standard]
                [--error-unmatch] [--with-tree=<tree-ish>]
-               [--full-name] [--abbrev] [--] [<file>]\*
+               [--full-name] [--abbrev] [--] [<file>...]
 
 DESCRIPTION
 -----------
@@ -79,15 +79,16 @@ OPTIONS
 
 -x <pattern>::
 --exclude=<pattern>::
-       Skips files matching pattern.
-       Note that pattern is a shell wildcard pattern.
+       Skip untracked files matching pattern.
+       Note that pattern is a shell wildcard pattern. See EXCLUDE PATTERNS
+       below for more information.
 
 -X <file>::
 --exclude-from=<file>::
-       exclude patterns are read from <file>; 1 per line.
+       Read exclude patterns from <file>; 1 per line.
 
 --exclude-per-directory=<file>::
-       read additional exclude patterns that apply only to the
+       Read additional exclude patterns that apply only to the
        directory and its subdirectories in <file>.
 
 --exclude-standard::
@@ -192,7 +193,7 @@ These exclude patterns come from these places, in order:
      file containing a list of patterns.  Patterns are ordered
      in the same order they appear in the file.
 
-  3. command line flag --exclude-per-directory=<name> specifies
+  3. The command line flag --exclude-per-directory=<name> specifies
      a name of the file in each directory 'git ls-files'
      examines, normally `.gitignore`.  Files in deeper
      directories take precedence.  Patterns are ordered in the
index 1f89d3680043cc98ad39facb1f43ae770ff66e14..76ed625e7b2fe503d3fb2262371ecd4de006411f 100644 (file)
@@ -10,8 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git ls-tree' [-d] [-r] [-t] [-l] [-z]
-           [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev=[<n>]]
-           <tree-ish> [paths...]
+           [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]
+           <tree-ish> [<path>...]
 
 DESCRIPTION
 -----------
@@ -19,11 +19,11 @@ Lists the contents of a given tree object, like what "/bin/ls -a" does
 in the current working directory.  Note that:
 
  - the behaviour is slightly different from that of "/bin/ls" in that the
-   'paths' denote just a list of patterns to match, e.g. so specifying
+   '<path>' denotes just a list of patterns to match, e.g. so specifying
    directory name (without '-r') will behave differently, and order of the
    arguments does not matter.
 
- - the behaviour is similar to that of "/bin/ls" in that the 'paths' is
+ - the behaviour is similar to that of "/bin/ls" in that the '<path>' is
    taken as relative to the current working directory.  E.g. when you are
    in a directory 'sub' that has a directory 'dir', you can run 'git
    ls-tree -r HEAD dir' to list the contents of the tree (that is
@@ -72,7 +72,7 @@ OPTIONS
        Do not limit the listing to the current working directory.
        Implies --full-name.
 
-paths::
+[<path>...]::
        When paths are given, show them (note that this isn't really raw
        pathnames, but rather a list of patterns to match).  Otherwise
        implicitly uses the root level of the tree as the sole path argument.
index a634485281154cb665c8168cc3469dc0f5051697..71912a19a4dcda5f2b76d1c06b4c9ef5051265e3 100644 (file)
@@ -7,7 +7,7 @@ git-mailsplit - Simple UNIX mbox splitter program
 
 SYNOPSIS
 --------
-'git mailsplit' [-b] [-f<nn>] [-d<prec>] [--keep-cr] -o<directory> [--] [<mbox>|<Maildir>...]
+'git mailsplit' [-b] [-f<nn>] [-d<prec>] [--keep-cr] -o<directory> [--] [(<mbox>|<Maildir>)...]
 
 DESCRIPTION
 -----------
index ce5b369985c254ec5d986aa3dd250828cf3cc4cb..eedef1bb1a14591d23b1fe6e93da1e3c2be2786b 100644 (file)
@@ -8,7 +8,9 @@ git-merge-base - Find as good common ancestors as possible for a merge
 
 SYNOPSIS
 --------
-'git merge-base' [-a|--all] <commit> <commit>...
+[verse]
+'git merge-base' [-a|--all] [--octopus] <commit> <commit>...
+'git merge-base' --independent <commit>...
 
 DESCRIPTION
 -----------
@@ -20,12 +22,12 @@ that does not have any better common ancestor is a 'best common
 ancestor', i.e. a 'merge base'.  Note that there can be more than one
 merge base for a pair of commits.
 
-Among the two commits to compute the merge base from, one is specified by
-the first commit argument on the command line; the other commit is a
-(possibly hypothetical) commit that is a merge across all the remaining
-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.
+Unless `--octopus` is given, among the two commits to compute the merge
+base from, one is specified by the first commit argument on the command
+line; the other commit is a (possibly hypothetical) commit that is a merge
+across all the remaining 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
@@ -37,6 +39,18 @@ OPTIONS
 --all::
        Output all merge bases for the commits, instead of just one.
 
+--octopus::
+       Compute the best common ancestors of all supplied commits,
+       in preparation for an n-way merge.  This mimics the behavior
+       of 'git show-branch --merge-base'.
+
+--independent::
+       Instead of printing merge bases, print a minimal subset of
+       the supplied commits with the same ancestors.  In other words,
+       among the commits given, list those which cannot be reached
+       from any other.  This mimics the behavior of 'git show-branch
+       --independent'.
+
 DISCUSSION
 ----------
 
@@ -96,6 +110,12 @@ Documentation
 --------------
 Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
 
+See also
+--------
+linkgit:git-rev-list[1],
+linkgit:git-show-branch[1],
+linkgit:git-merge[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 4d266de9ccb9d6881b18162c132ccd8475ca50ee..921b38f18374c20d1f94c0907a629425acb38aa4 100644 (file)
@@ -8,7 +8,7 @@ git-merge-index - Run a merge for files needing merging
 
 SYNOPSIS
 --------
-'git merge-index' [-o] [-q] <merge-program> (-a | [--] <file>\*)
+'git merge-index' [-o] [-q] <merge-program> (-a | [--] <file>*)
 
 DESCRIPTION
 -----------
index 84043cc5b26db7d34c93a70ec005df913c345d85..c1efaaa5c52cd93e90a1a7427125e470f771146a 100644 (file)
@@ -13,6 +13,7 @@ SYNOPSIS
        [-s <strategy>] [-X <strategy-option>]
        [--[no-]rerere-autoupdate] [-m <msg>] <commit>...
 'git merge' <msg> HEAD <commit>...
+'git merge' --abort
 
 DESCRIPTION
 -----------
@@ -47,6 +48,14 @@ The second syntax (<msg> `HEAD` <commit>...) is supported for
 historical reasons.  Do not use it from the command line or in
 new scripts.  It is the same as `git merge -m <msg> <commit>...`.
 
+The third syntax ("`git merge --abort`") can only be run after the
+merge has resulted in conflicts. 'git merge --abort' will abort the
+merge process and try to reconstruct the pre-merge state. However,
+if there were uncommitted changes when the merge started (and
+especially if those changes were further modified after the merge
+was started), 'git merge --abort' will in some cases be unable to
+reconstruct the original (pre-merge) changes. Therefore:
+
 *Warning*: Running 'git merge' with uncommitted changes is
 discouraged: while possible, it leaves you in a state that is hard to
 back out of in the case of a conflict.
@@ -59,19 +68,31 @@ include::merge-options.txt[]
 -m <msg>::
        Set the commit message to be used for the merge commit (in
        case one is created).
-
-       If `--log` is specified, a shortlog of the commits being merged
-       will be appended to the specified message.
-
-       The 'git fmt-merge-msg' command can be
-       used to give a good default for automated 'git merge'
-       invocations.
++
+If `--log` is specified, a shortlog of the commits being merged
+will be appended to the specified message.
++
+The 'git fmt-merge-msg' command can be
+used to give a good default for automated 'git merge'
+invocations.
 
 --rerere-autoupdate::
 --no-rerere-autoupdate::
        Allow the rerere mechanism to update the index with the
        result of auto-conflict resolution if possible.
 
+--abort::
+       Abort the current conflict resolution process, and
+       try to reconstruct the pre-merge state.
++
+If there were uncommitted worktree changes present when the merge
+started, 'git merge --abort' will in some cases be unable to
+reconstruct these changes. It is therefore recommended to always
+commit or stash your changes before running 'git merge'.
++
+'git merge --abort' is equivalent to 'git reset --merge' when
+`MERGE_HEAD` is present.
+
 <commit>...::
        Commits, usually other branch heads, to merge into our branch.
        You need at least one <commit>.  Specifying more than one
@@ -142,7 +163,7 @@ happens:
    i.e. matching `HEAD`.
 
 If you tried a merge which resulted in complex conflicts and
-want to start over, you can recover with `git reset --merge`.
+want to start over, you can recover with `git merge --abort`.
 
 HOW CONFLICTS ARE PRESENTED
 ---------------------------
@@ -213,8 +234,8 @@ After seeing a conflict, you can do two things:
 
  * Decide not to merge.  The only clean-ups you need are to reset
    the index file to the `HEAD` commit to reverse 2. and to clean
-   up working tree changes made by 2. and 3.; `git-reset --hard` can
-   be used for this.
+   up working tree changes made by 2. and 3.; `git merge --abort`
+   can be used for this.
 
  * Resolve the conflicts.  Git will mark the conflicts in
    the working tree.  Edit the files into shape and
index 78eb03f0ae864f67faac55f450bf0fd2be3416b3..d8df55362ce653516006b02df2950c325f1176b7 100644 (file)
@@ -1,5 +1,5 @@
-git-mergetool--lib(1)
-=====================
+git-mergetool{litdd}lib(1)
+==========================
 
 NAME
 ----
@@ -16,11 +16,11 @@ This is not a command the end user would want to run.  Ever.
 This documentation is meant for people who are studying the
 Porcelain-ish scripts and/or are writing new ones.
 
-The 'git-mergetool--lib' scriptlet is designed to be sourced (using
+The 'git-mergetool{litdd}lib' scriptlet is designed to be sourced (using
 `.`) by other shell scripts to set up functions for working
 with git merge tools.
 
-Before sourcing 'git-mergetool--lib', your script must set `TOOL_MODE`
+Before sourcing 'git-mergetool{litdd}lib', your script must set `TOOL_MODE`
 to define the operation mode for the functions listed below.
 'diff' and 'merge' are valid values.
 
index e4ed0161467866f731c89eb48e64453051c64281..1f75a848ba36728eeaa67e5cbbc209075393193b 100644 (file)
@@ -7,7 +7,7 @@ git-mergetool - Run merge conflict resolution tools to resolve merge conflicts
 
 SYNOPSIS
 --------
-'git mergetool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<file>]...
+'git mergetool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<file>...]
 
 DESCRIPTION
 -----------
index 2981d8c5efd4e7e0f65d051cfe3b28d3430e3213..296f314eae5af684e68965f4b04bd9acbd4c35a1 100644 (file)
@@ -14,8 +14,12 @@ SYNOPSIS
 'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
 'git notes' edit [<object>]
 'git notes' show [<object>]
+'git notes' merge [-v | -q] [-s <strategy> ] <notes_ref>
+'git notes' merge --commit [-v | -q]
+'git notes' merge --abort [-v | -q]
 'git notes' remove [<object>]
 'git notes' prune [-n | -v]
+'git notes' get-ref
 
 
 DESCRIPTION
@@ -83,6 +87,21 @@ edit::
 show::
        Show the notes for a given object (defaults to HEAD).
 
+merge::
+       Merge the given notes ref into the current notes ref.
+       This will try to merge the changes made by the given
+       notes ref (called "remote") since the merge-base (if
+       any) into the current notes ref (called "local").
++
+If conflicts arise and a strategy for automatically resolving
+conflicting notes (see the -s/--strategy option) is not given,
+the "manual" resolver is used. This resolver checks out the
+conflicting notes in a special worktree (`.git/NOTES_MERGE_WORKTREE`),
+and instructs the user to manually resolve the conflicts there.
+When done, the user can either finalize the merge with
+'git notes merge --commit', or abort the merge with
+'git notes merge --abort'.
+
 remove::
        Remove the notes for a given object (defaults to HEAD).
        This is equivalent to specifying an empty note message to
@@ -91,6 +110,10 @@ remove::
 prune::
        Remove all notes for non-existing/unreachable objects.
 
+get-ref::
+       Print the current notes ref. This provides an easy way to
+       retrieve the current notes ref (e.g. from scripts).
+
 OPTIONS
 -------
 -f::
@@ -133,9 +156,37 @@ OPTIONS
        Do not remove anything; just report the object names whose notes
        would be removed.
 
+-s <strategy>::
+--strategy=<strategy>::
+       When merging notes, resolve notes conflicts using the given
+       strategy. The following strategies are recognized: "manual"
+       (default), "ours", "theirs", "union" and "cat_sort_uniq".
+       See the "NOTES MERGE STRATEGIES" section below for more
+       information on each notes merge strategy.
+
+--commit::
+       Finalize an in-progress 'git notes merge'. Use this option
+       when you have resolved the conflicts that 'git notes merge'
+       stored in .git/NOTES_MERGE_WORKTREE. This amends the partial
+       merge commit created by 'git notes merge' (stored in
+       .git/NOTES_MERGE_PARTIAL) by adding the notes in
+       .git/NOTES_MERGE_WORKTREE. The notes ref stored in the
+       .git/NOTES_MERGE_REF symref is updated to the resulting commit.
+
+--abort::
+       Abort/reset a in-progress 'git notes merge', i.e. a notes merge
+       with conflicts. This simply removes all files related to the
+       notes merge.
+
+-q::
+--quiet::
+       When merging notes, operate quietly.
+
 -v::
 --verbose::
-       Report all object names whose notes are removed.
+       When merging notes, be more verbose.
+       When pruning notes, report all object names whose notes are
+       removed.
 
 
 DISCUSSION
@@ -163,6 +214,38 @@ object, in which case the history of the notes can be read with
 `git log -p -g <refname>`.
 
 
+NOTES MERGE STRATEGIES
+----------------------
+
+The default notes merge strategy is "manual", which checks out
+conflicting notes in a special work tree for resolving notes conflicts
+(`.git/NOTES_MERGE_WORKTREE`), and instructs the user to resolve the
+conflicts in that work tree.
+When done, the user can either finalize the merge with
+'git notes merge --commit', or abort the merge with
+'git notes merge --abort'.
+
+"ours" automatically resolves conflicting notes in favor of the local
+version (i.e. the current notes ref).
+
+"theirs" automatically resolves notes conflicts in favor of the remote
+version (i.e. the given notes ref being merged into the current notes
+ref).
+
+"union" automatically resolves notes conflicts by concatenating the
+local and remote versions.
+
+"cat_sort_uniq" is similar to "union", but in addition to concatenating
+the local and remote versions, this strategy also sorts the resulting
+lines, and removes duplicate lines from the result. This is equivalent
+to applying the "cat | sort | uniq" shell pipeline to the local and
+remote versions. This strategy is useful if the notes follow a line-based
+format where one wants to avoid duplicated lines in the merge result.
+Note that if either the local or remote version contain duplicate lines
+prior to the merge, these will also be removed by this notes merge
+strategy.
+
+
 EXAMPLES
 --------
 
index 8ed09c0b3cfc0fff2dbb8907fdc00e027c2c6d33..65eff66afe5990efc4af4f141809f355b7111bd3 100644 (file)
@@ -11,8 +11,8 @@ SYNOPSIS
 [verse]
 'git pack-objects' [-q | --progress | --all-progress] [--all-progress-implied]
        [--no-reuse-delta] [--delta-base-offset] [--non-empty]
-       [--local] [--incremental] [--window=N] [--depth=N]
-       [--revs [--unpacked | --all]*] [--stdout | base-name]
+       [--local] [--incremental] [--window=<n>] [--depth=<n>]
+       [--revs [--unpacked | --all]] [--stdout | base-name]
        [--keep-true-parents] < object-list
 
 
@@ -82,8 +82,8 @@ base-name::
        reference was included in the resulting packfile.  This
        can be useful to send new tags to native git clients.
 
---window=[N]::
---depth=[N]::
+--window=<n>::
+--depth=<n>::
        These two options affect how the objects contained in
        the pack are stored using delta compression.  The
        objects are first internally sorted by type, size and
@@ -95,10 +95,10 @@ base-name::
        times to get to the necessary object.
        The default value for --window is 10 and --depth is 50.
 
---window-memory=[N]::
+--window-memory=<n>::
        This option provides an additional limit on top of `--window`;
        the window size will dynamically scale down so as to not take
-       up more than N bytes in memory.  This is useful in
+       up more than '<n>' bytes in memory.  This is useful in
        repositories with a mix of large and small objects to not run
        out of memory with a large window, but still be able to take
        advantage of the large window for the smaller objects.  The
@@ -106,7 +106,7 @@ base-name::
        `--window-memory=0` makes memory usage unlimited, which is the
        default.
 
---max-pack-size=[N]::
+--max-pack-size=<n>::
        Maximum size of each output pack file. The size can be suffixed with
        "k", "m", or "g". The minimum size allowed is limited to 1 MiB.
        If specified,  multiple packfiles may be created.
@@ -171,7 +171,7 @@ base-name::
        wholesale enforcement of a different compression level on the
        packed data is desired.
 
---compression=[N]::
+--compression=<n>::
        Specifies compression level for newly-compressed data in the
        generated pack.  If not specified,  pack compression level is
        determined first by pack.compression,  then by core.compression,
index c50f7dcb890139d829bae51c30b513d54fd7761e..30466917dad52eefd76de95aead3fdc12ad8425d 100644 (file)
@@ -26,9 +26,9 @@ With `--rebase`, it runs 'git rebase' instead of 'git merge'.
 <repository> should be the name of a remote repository as
 passed to linkgit:git-fetch[1].  <refspec> can name an
 arbitrary remote ref (for example, the name of a tag) or even
-a collection of refs with corresponding remote tracking branches
-(e.g., refs/heads/*:refs/remotes/origin/*), but usually it is
-the name of a branch in the remote repository.
+a collection of refs with corresponding remote-tracking branches
+(e.g., refs/heads/{asterisk}:refs/remotes/origin/{asterisk}),
+but usually it is the name of a branch in the remote repository.
 
 Default values for <repository> and <branch> are read from the
 "remote" and "merge" configuration for the current branch
@@ -92,12 +92,15 @@ include::merge-options.txt[]
 :git-pull: 1
 
 --rebase::
-       Instead of a merge, perform a rebase after fetching.  If
-       there is a remote ref for the upstream branch, and this branch
-       was rebased since last fetched, the rebase uses that information
-       to avoid rebasing non-local changes. To make this the default
-       for branch `<name>`, set configuration `branch.<name>.rebase`
-       to `true`.
+       Rebase the current branch on top of the upstream branch after
+       fetching.  If there is a remote-tracking branch corresponding to
+       the upstream branch and the upstream branch was rebased since last
+       fetched, the rebase uses that information to avoid rebasing
+       non-local changes.
++
+See `branch.<name>.rebase` and `branch.autosetuprebase` in
+linkgit:git-config[1] if you want to make `git pull` always use
+`{litdd}rebase` instead of merging.
 +
 [NOTE]
 This is a potentially _dangerous_ mode of operation.
@@ -134,7 +137,7 @@ and if there is not any such variable, the value on `URL: ` line
 in `$GIT_DIR/remotes/<origin>` file is used.
 
 In order to determine what remote branches to fetch (and
-optionally store in the tracking branches) when the command is
+optionally store in the remote-tracking branches) when the command is
 run without any refspec parameters on the command line, values
 of the configuration variable `remote.<origin>.fetch` are
 consulted, and if there aren't any, `$GIT_DIR/remotes/<origin>`
@@ -147,9 +150,9 @@ refs/heads/*:refs/remotes/origin/*
 ------------
 
 A globbing refspec must have a non-empty RHS (i.e. must store
-what were fetched in tracking branches), and its LHS and RHS
+what were fetched in remote-tracking branches), and its LHS and RHS
 must end with `/*`.  The above specifies that all remote
-branches are tracked using tracking branches in
+branches are tracked using remote-tracking branches in
 `refs/remotes/origin/` hierarchy under the same name.
 
 The rule to determine which remote branch to merge after
index 658ff2ff67f23e9a9d2942e38528dba415139ac3..e11660a2e651b385251bef4f45dc36f212fa58bf 100644 (file)
@@ -41,7 +41,7 @@ OPTIONS[[OPTIONS]]
 +
 The <src> is often the name of the branch you would want to push, but
 it can be any arbitrary "SHA-1 expression", such as `master~4` or
-`HEAD` (see linkgit:gitrevisions[1]).
+`HEAD` (see linkgit:gitrevisions[7]).
 +
 The <dst> tells which ref on the remote side is updated with this
 push. Arbitrary expressions cannot be used here, an actual ref must
@@ -200,7 +200,7 @@ summary::
        For a successfully pushed ref, the summary shows the old and new
        values of the ref in a form suitable for using as an argument to
        `git log` (this is `<old>..<new>` in most cases, and
-       `<old>...<new>` for forced non-fast-forward updates).
+       `<old>\...<new>` for forced non-fast-forward updates).
 +
 For a failed update, more details are given:
 +
index 2e78da448f30ad260cb7683a5610f424d0c5ffea..634423a69ee6a4f2e9c7dd3941fd5160fbf6c99f 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
                [-u [--exclude-per-directory=<gitignore>] | -i]]
                [--index-output=<file>] [--no-sparse-checkout]
-               <tree-ish1> [<tree-ish2> [<tree-ish3>]]
+               (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])
 
 
 DESCRIPTION
@@ -114,6 +114,10 @@ OPTIONS
        Disable sparse checkout support even if `core.sparseCheckout`
        is true.
 
+--empty::
+       Instead of reading tree object(s) into the index, just empty
+       it.
+
 <tree-ish#>::
        The id of the tree object(s) to be read/merged.
 
@@ -412,13 +416,6 @@ turn `core.sparseCheckout` on in order to have sparse checkout
 support.
 
 
-BUGS
-----
-In order to match a directory with $GIT_DIR/info/sparse-checkout,
-trailing slash must be used. The form without trailing slash, while
-works with .gitignore, does not work with sparse checkout.
-
-
 SEE ALSO
 --------
 linkgit:git-write-tree[1]; linkgit:git-ls-files[1];
index b4314568f5a742d186e69b97f782193d2883e6b3..96680c84561c434c4cf3a00cfe958b21f18a3d8d 100644 (file)
@@ -199,6 +199,9 @@ rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
        rebase. False by default.
 
+rebase.autosquash::
+       If set to true enable '--autosquash' option by default.
+
 OPTIONS
 -------
 <newbase>::
@@ -207,7 +210,7 @@ OPTIONS
        <upstream>.  May be any valid commit, and not just an
        existing branch name.
 +
-As a special case, you may use "A...B" as a shortcut for the
+As a special case, you may use "A\...B" as a shortcut for the
 merge base of A and B if there is exactly one merge base. You can
 leave out at most one of A and B, in which case it defaults to HEAD.
 
@@ -276,6 +279,10 @@ which makes little sense.
 --no-verify::
        This option bypasses the pre-rebase hook.  See also linkgit:githooks[5].
 
+--verify::
+       Allows the pre-rebase hook to run, which is the default.  This option can
+       be used to override --no-verify.  See also linkgit:githooks[5].
+
 -C<n>::
        Ensure at least <n> lines of surrounding context match before
        and after each change.  When fewer lines of surrounding
@@ -333,6 +340,7 @@ idea unless you know what you are doing (see BUGS below).
        instead.
 
 --autosquash::
+--no-autosquash::
        When the commit log message begins with "squash! ..." (or
        "fixup! ..."), and there is a commit whose title begins with
        the same ..., automatically modify the todo list of rebase -i
@@ -341,6 +349,10 @@ idea unless you know what you are doing (see BUGS below).
        commit from `pick` to `squash` (or `fixup`).
 +
 This option is only valid when the '--interactive' option is used.
++
+If the '--autosquash' option is enabled by default using the
+configuration variable `rebase.autosquash`, this option can be
+used to override and disable this setting.
 
 --no-ff::
        With --interactive, cherry-pick all rebased commits instead of
@@ -466,6 +478,30 @@ sure that the current HEAD is "B", and call
 $ git rebase -i -p --onto Q O
 -----------------------------
 
+Reordering and editing commits usually creates untested intermediate
+steps.  You may want to check that your history editing did not break
+anything by running a test, or at least recompiling at intermediate
+points in history by using the "exec" command (shortcut "x").  You may
+do so by creating a todo list like this one:
+
+-------------------------------------------
+pick deadbee Implement feature XXX
+fixup f1a5c00 Fix to feature XXX
+exec make
+pick c0ffeee The oneline of the next commit
+edit deadbab The oneline of the commit after
+exec cd subdir; make test
+...
+-------------------------------------------
+
+The interactive rebase will stop when a command fails (i.e. exits with
+non-0 status) to give you an opportunity to fix the problem. You can
+continue with `git rebase --continue`.
+
+The "exec" command launches the command in a shell (the one specified
+in `$SHELL`, or the default shell if `$SHELL` is not set), so you can
+use shell features (like "cd", ">", ";" ...). The command is run from
+the root of the working tree.
 
 SPLITTING COMMITS
 -----------------
index 5a0451aaf377d13590d6112a9ee8cf51e217a21d..e50bd9b68d80e9be997de80252dec5afbb7452b4 100644 (file)
@@ -40,7 +40,7 @@ see linkgit:git-log[1].
 The reflog is useful in various git commands, to specify the old value
 of a reference. For example, `HEAD@\{2\}` means "where HEAD used to be
 two moves ago", `master@\{one.week.ago\}` means "where master used to
-point to one week ago", and so on. See linkgit:gitrevisions[1] for
+point to one week ago", and so on. See linkgit:gitrevisions[7] for
 more details.
 
 To delete single entries from the reflog, use the subcommand "delete"
index 25ff8f9dcbe0db52675338f1429e9169052b9cf1..8fc809f82b2fae71f5cf6315dec707b649931382 100644 (file)
@@ -7,7 +7,7 @@ git-relink - Hardlink common objects in local repositories
 
 SYNOPSIS
 --------
-'git relink' [--safe] <dir> [<dir>]\* <master_dir>
+'git relink' [--safe] <dir>... <master_dir>
 
 DESCRIPTION
 -----------
diff --git a/Documentation/git-remote-ext.txt b/Documentation/git-remote-ext.txt
new file mode 100644 (file)
index 0000000..2d65cfe
--- /dev/null
@@ -0,0 +1,125 @@
+git-remote-ext(1)
+=================
+
+NAME
+----
+git-remote-ext - Bridge smart transport to external command.
+
+SYNOPSIS
+--------
+git remote add nick "ext::<command>[ <arguments>...]"
+
+DESCRIPTION
+-----------
+This remote helper uses the specified 'program' to connect
+to a remote git server.
+
+Data written to stdin of this specified 'program' is assumed
+to be sent to a git:// server, git-upload-pack, git-receive-pack
+or git-upload-archive (depending on situation), and data read
+from stdout of this program is assumed to be received from
+the same service.
+
+Command and arguments are separated by an unescaped space.
+
+The following sequences have a special meaning:
+
+'% '::
+       Literal space in command or argument.
+
+'%%'::
+       Literal percent sign.
+
+'%s'::
+       Replaced with name (receive-pack, upload-pack, or
+       upload-archive) of the service git wants to invoke.
+
+'%S'::
+       Replaced with long name (git-receive-pack,
+       git-upload-pack, or git-upload-archive) of the service
+       git wants to invoke.
+
+'%G' (must be the first characters in an argument)::
+       This argument will not be passed to 'program'. Instead, it
+       will cause the helper to start by sending git:// service requests to
+       the remote side with the service field set to an appropriate value and
+       the repository field set to rest of the argument. Default is not to send
+       such a request.
++
+This is useful if remote side is git:// server accessed over
+some tunnel.
+
+'%V' (must be first characters in argument)::
+       This argument will not be passed to 'program'. Instead it sets
+       the vhost field in the git:// service request (to rest of the argument).
+       Default is not to send vhost in such request (if sent).
+
+ENVIRONMENT VARIABLES:
+----------------------
+
+GIT_TRANSLOOP_DEBUG::
+       If set, prints debugging information about various reads/writes.
+
+ENVIRONMENT VARIABLES PASSED TO COMMAND:
+----------------------------------------
+
+GIT_EXT_SERVICE::
+       Set to long name (git-upload-pack, etc...) of service helper needs
+       to invoke.
+
+GIT_EXT_SERVICE_NOPREFIX::
+       Set to long name (upload-pack, etc...) of service helper needs
+       to invoke.
+
+
+EXAMPLES:
+---------
+This remote helper is transparently used by git when
+you use commands such as "git fetch <URL>", "git clone <URL>",
+, "git push <URL>" or "git remote add nick <URL>", where <URL>
+begins with `ext::`.  Examples:
+
+"ext::ssh -i /home/foo/.ssh/somekey user&#64;host.example %S 'foo/repo'"::
+       Like host.example:foo/repo, but use /home/foo/.ssh/somekey as
+       keypair and user as user on remote side. This avoids needing to
+       edit .ssh/config.
+
+"ext::socat -t3600 - ABSTRACT-CONNECT:/git-server %G/somerepo"::
+       Represents repository with path /somerepo accessable over
+       git protocol at abstract namespace address /git-server.
+
+"ext::git-server-alias foo %G/repo"::
+       Represents a repository with path /repo accessed using the
+       helper program "git-server-alias foo".  The path to the
+       repository and type of request are not passed on the command
+       line but as part of the protocol stream, as usual with git://
+       protocol.
+
+"ext::git-server-alias foo %G/repo %Vfoo"::
+       Represents a repository with path /repo accessed using the
+       helper program "git-server-alias foo".  The hostname for the
+       remote server passed in the protocol stream will be "foo"
+       (this allows multiple virtual git servers to share a
+       link-level address).
+
+"ext::git-server-alias foo %G/repo% with% spaces %Vfoo"::
+       Represents a repository with path '/repo with spaces' accessed
+       using the helper program "git-server-alias foo".  The hostname for
+       the remote server passed in the protocol stream will be "foo"
+       (this allows multiple virtual git servers to share a
+       link-level address).
+
+"ext::git-ssl foo.example /bar"::
+       Represents a repository accessed using the helper program
+       "git-ssl foo.example /bar".  The type of request can be
+       determined by the helper using environment variables (see
+       above).
+
+Documentation
+--------------
+Documentation by Ilari Liusvaara, Jonathan Nieder and the git list
+<git@vger.kernel.org>
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-remote-fd.txt b/Documentation/git-remote-fd.txt
new file mode 100644 (file)
index 0000000..4aecd4d
--- /dev/null
@@ -0,0 +1,59 @@
+git-remote-fd(1)
+================
+
+NAME
+----
+git-remote-fd - Reflect smart transport stream back to caller
+
+SYNOPSIS
+--------
+"fd::<infd>[,<outfd>][/<anything>]" (as URL)
+
+DESCRIPTION
+-----------
+This helper uses specified file descriptors to connect to a remote git server.
+This is not meant for end users but for programs and scripts calling git
+fetch, push or archive.
+
+If only <infd> is given, it is assumed to be a bidirectional socket connected
+to remote git server (git-upload-pack, git-receive-pack or
+git-upload-achive). If both <infd> and <outfd> are given, they are assumed
+to be pipes connected to a remote git server (<infd> being the inbound pipe
+and <outfd> being the outbound pipe.
+
+It is assumed that any handshaking procedures have already been completed
+(such as sending service request for git://) before this helper is started.
+
+<anything> can be any string. It is ignored. It is meant for providing
+information to user in the URL in case that URL is displayed in some
+context.
+
+ENVIRONMENT VARIABLES
+---------------------
+GIT_TRANSLOOP_DEBUG::
+       If set, prints debugging information about various reads/writes.
+
+EXAMPLES
+--------
+git fetch fd::17 master::
+       Fetch master, using file descriptor #17 to communicate with
+       git-upload-pack.
+
+git fetch fd::17/foo master::
+       Same as above.
+
+git push fd::7,8 master (as URL)::
+       Push master, using file descriptor #7 to read data from
+       git-receive-pack and file descriptor #8 to write data to
+       same service.
+
+git push fd::7,8/bar master::
+       Same as above.
+
+Documentation
+--------------
+Documentation by Ilari Liusvaara and the git list <git@vger.kernel.org>
+
+GIT
+---
+Part of the linkgit:git[1] suite
index aa021b0cb803a9cd9f08566f6a6fe407eb025e4b..c258ea48dbb8889a48f3075ab171ebddfd7fda3b 100644 (file)
@@ -20,7 +20,7 @@ SYNOPSIS
 'git remote set-url --delete' [--push] <name> <url>
 'git remote' [-v | --verbose] 'show' [-n] <name>
 'git remote prune' [-n | --dry-run] <name>
-'git remote' [-v | --verbose] 'update' [-p | --prune] [group | remote]...
+'git remote' [-v | --verbose] 'update' [-p | --prune] [(<group> | <remote>)...]
 
 DESCRIPTION
 -----------
@@ -75,7 +75,7 @@ was passed.
 
 'rename'::
 
-Rename the remote named <old> to <new>. All remote tracking branches and
+Rename the remote named <old> to <new>. All remote-tracking branches and
 configuration settings for the remote are updated.
 +
 In case <old> and <new> are the same, and <old> is a file under
@@ -84,7 +84,7 @@ the configuration file format.
 
 'rm'::
 
-Remove the remote named <name>. All remote tracking branches and
+Remove the remote named <name>. All remote-tracking branches and
 configuration settings for the remote are removed.
 
 'set-head'::
@@ -146,7 +146,7 @@ With `-n` option, the remote heads are not queried first with
 
 'prune'::
 
-Deletes all stale tracking branches under <name>.
+Deletes all stale remote-tracking branches under <name>.
 These stale branches have already been removed from the remote repository
 referenced by <name>, but are still locally available in
 "remotes/<name>".
index 8c67d1724f705c94fb8faf6801ee2bc7cd459629..27f7865b061cc96398f7aa052d162723e1f9255e 100644 (file)
@@ -8,7 +8,7 @@ git-repack - Pack unpacked objects in a repository
 
 SYNOPSIS
 --------
-'git repack' [-a] [-A] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
+'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [--window=<n>] [--depth=<n>]
 
 DESCRIPTION
 -----------
@@ -62,6 +62,10 @@ other objects in that pack they already have locally.
        linkgit:git-pack-objects[1].
 
 -f::
+       Pass the `--no-reuse-delta` option to `git-pack-objects`, see
+       linkgit:git-pack-objects[1].
+
+-F::
        Pass the `--no-reuse-object` option to `git-pack-objects`, see
        linkgit:git-pack-objects[1].
 
@@ -76,8 +80,8 @@ other objects in that pack they already have locally.
        this repository (or a direct copy of it)
        over HTTP or FTP.  See linkgit:git-update-server-info[1].
 
---window=[N]::
---depth=[N]::
+--window=<n>::
+--depth=<n>::
        These two options affect how the objects contained in the pack are
        stored using delta compression. The objects are first internally
        sorted by type, size and optionally names and compared against the
@@ -87,10 +91,10 @@ other objects in that pack they already have locally.
        to be applied that many times to get to the necessary object.
        The default value for --window is 10 and --depth is 50.
 
---window-memory=[N]::
+--window-memory=<n>::
        This option provides an additional limit on top of `--window`;
        the window size will dynamically scale down so as to not take
-       up more than N bytes in memory.  This is useful in
+       up more than '<n>' bytes in memory.  This is useful in
        repositories with a mix of large and small objects to not run
        out of memory with a large window, but still be able to take
        advantage of the large window for the smaller objects.  The
@@ -98,7 +102,7 @@ other objects in that pack they already have locally.
        `--window-memory=0` makes memory usage unlimited, which is the
        default.
 
---max-pack-size=[N]::
+--max-pack-size=<n>::
        Maximum size of each output pack file. The size can be suffixed with
        "k", "m", or "g". The minimum size allowed is limited to 1 MiB.
        If specified,  multiple packfiles may be created.
index 9cf31485feec4b03850edb1f2f3a4b765245f4cc..927ecee2f26cdf0bd30457d5016b654c01a23a14 100644 (file)
@@ -15,17 +15,24 @@ SYNOPSIS
 DESCRIPTION
 -----------
 In the first and second form, copy entries from <commit> to the index.
-In the third form, set the current branch to <commit>, optionally
-modifying index and worktree to match.  The <commit> defaults to HEAD
+In the third form, set the current branch head (HEAD) to <commit>, optionally
+modifying index and working tree to match.  The <commit> defaults to HEAD
 in all forms.
 
 'git reset' [-q] [<commit>] [--] <paths>...::
        This form resets the index entries for all <paths> to their
-       state at the <commit>.  (It does not affect the worktree, nor
+       state at <commit>.  (It does not affect the working tree, nor
        the current branch.)
 +
 This means that `git reset <paths>` is the opposite of `git add
 <paths>`.
++
+After running `git reset <paths>` to update the index entry, you can
+use linkgit:git-checkout[1] to check the contents out of the index to
+the working tree.
+Alternatively, using linkgit:git-checkout[1] and specifying a commit, you
+can copy the contents of a path out of a commit to the index and to the
+working tree in one go.
 
 'git reset' --patch|-p [<commit>] [--] [<paths>...]::
        Interactively select hunks in the difference between the index
@@ -36,16 +43,17 @@ This means that `git reset -p` is the opposite of `git add -p` (see
 linkgit:git-add[1]).
 
 'git reset' [--<mode>] [<commit>]::
-       This form points the current branch to <commit> and then
-       updates index and working tree according to <mode>, which must
-       be one of the following:
+       This form resets the current branch head to <commit> and
+       possibly updates the index (resetting it to the tree of <commit>) and
+       the working tree depending on <mode>, which
+       must be one of the following:
 +
 --
 --soft::
-       Does not touch the index file nor the working tree at allbut
-       requires them to be in a good order. This leaves all your changed
-       files "Changes to be committed", as 'git status' would
-       put it.
+       Does not touch the index file nor the working tree at all (but
+       resets the head to <commit>, just like all modes do). This leaves
+       all your changed files "Changes to be committed", as 'git status'
+       would put it.
 
 --mixed::
        Resets the index but not the working tree (i.e., the changed files
@@ -53,22 +61,25 @@ linkgit:git-add[1]).
        been updated. This is the default action.
 
 --hard::
-       Matches the working tree and index to that of the tree being
-       switched to. Any changes to tracked files in the working tree
-       since <commit> are lost.
+       Resets the index and working tree. Any changes to tracked files in the
+       working tree since <commit> are discarded.
 
 --merge::
-       Resets the index to match the tree recorded by the named commit,
-       and updates the files that are different between the named commit
-       and the current commit in the working tree.
+       Resets the index and updates the files in the working tree that are
+       different between <commit> and HEAD, but keeps those which are
+       different between the index and working tree (i.e. which have changes
+       which have not been added).
+       If a file that is different between <commit> and the index has unstaged
+       changes, reset is aborted.
++
+In other words, --merge does something like a 'git read-tree -u -m <commit>',
+but carries forward unmerged index entries.
 
 --keep::
-       Reset the index to the given commit, keeping local changes in
-       the working tree since the current commit, while updating
-       working tree files without local changes to what appears in
-       the given commit.  If a file that is different between the
-       current commit and the given commit has local changes, reset
-       is aborted.
+       Resets index entries and updates files in the working tree that are
+       different between <commit> and HEAD.
+       If a file that is different between <commit> and HEAD has local changes,
+       reset is aborted.
 --
 
 If you want to undo a commit other than the latest on a branch,
@@ -184,7 +195,7 @@ tip of the current branch in ORIG_HEAD, so resetting hard to it
 brings your index file and the working tree back to that state,
 and resets the tip of the branch to that commit.
 
-Undo a merge or pull inside a dirty work tree::
+Undo a merge or pull inside a dirty working tree::
 +
 ------------
 $ git pull                         <1>
@@ -257,7 +268,7 @@ Suppose you are working on something and you commit it, and then you
 continue working a bit more, but now you think that what you have in
 your working tree should be in another branch that has nothing to do
 with what you committed previously. You can start a new branch and
-reset it while keeping the changes in your work tree.
+reset it while keeping the changes in your working tree.
 +
 ------------
 $ git tag start
@@ -294,8 +305,10 @@ In these tables, A, B, C and D are some different states of a
 file. For example, the first line of the first table means that if a
 file is in state A in the working tree, in state B in the index, in
 state C in HEAD and in state D in the target, then "git reset --soft
-target" will put the file in state A in the working tree, in state B
-in the index and in state D in HEAD.
+target" will leave the file in the working tree in state A and in the
+index in state B.  It resets (i.e. moves) the HEAD (i.e. the tip of
+the current branch, if you are on one) to "target" (which has the file
+in state D).
 
       working index HEAD target         working index HEAD
       ----------------------------------------------------
@@ -346,11 +359,11 @@ in the index and in state D in HEAD.
                                --keep   B       C     C
 
 "reset --merge" is meant to be used when resetting out of a conflicted
-merge. Any mergy operation guarantees that the work tree file that is
+merge. Any mergy operation guarantees that the working tree file that is
 involved in the merge does not have local change wrt the index before
-it starts, and that it writes the result out to the work tree. So if
+it starts, and that it writes the result out to the working tree. So if
 we see some difference between the index and the target and also
-between the index and the work tree, then it means that we are not
+between the index and the working tree, then it means that we are not
 resetting out from a state that a mergy operation left after failing
 with a conflict. That is why we disallow --merge option in this case.
 
index 173f3fc78599f268c1e483c4d1abe51e95ad0e59..8e1e32908c31ddb8cb07046a25bcdb6dd38d74f5 100644 (file)
@@ -9,10 +9,10 @@ git-rev-list - Lists commit objects in reverse chronological order
 SYNOPSIS
 --------
 [verse]
-'git rev-list' [ \--max-count=number ]
-            [ \--skip=number ]
-            [ \--max-age=timestamp ]
-            [ \--min-age=timestamp ]
+'git rev-list' [ \--max-count=<number> ]
+            [ \--skip=<number> ]
+            [ \--max-age=<timestamp> ]
+            [ \--min-age=<timestamp> ]
             [ \--sparse ]
             [ \--merges ]
             [ \--no-merges ]
@@ -21,10 +21,10 @@ SYNOPSIS
             [ \--full-history ]
             [ \--not ]
             [ \--all ]
-            [ \--branches[=pattern] ]
-            [ \--tags[=pattern] ]
-            [ \--remotes[=pattern] ]
-            [ \--glob=glob-pattern ]
+            [ \--branches[=<pattern>] ]
+            [ \--tags[=<pattern>] ]
+            [ \--remotes[=<pattern>] ]
+            [ \--glob=<glob-pattern> ]
             [ \--stdin ]
             [ \--quiet ]
             [ \--topo-order ]
@@ -37,7 +37,7 @@ SYNOPSIS
             [ \--regexp-ignore-case | -i ]
             [ \--extended-regexp | -E ]
             [ \--fixed-strings | -F ]
-            [ \--date={local|relative|default|iso|rfc|short} ]
+            [ \--date=(local|relative|default|iso|rfc|short) ]
             [ [\--objects | \--objects-edge] [ \--unpacked ] ]
             [ \--pretty | \--header ]
             [ \--bisect ]
index be4c0533603443cd9b4df5bf732bbcdd31802a11..ff23cb0219d602803a83da273195fcc4ca71b5f5 100644 (file)
@@ -74,7 +74,7 @@ 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-\*'). In contrast to the `--sq-quote` option,
+       'git diff-{asterisk}'). In contrast to the `--sq-quote` option,
        the command input is still interpreted as usual.
 
 --not::
@@ -95,7 +95,7 @@ OPTIONS
        unfortunately named tag "master"), and show them as full
        refnames (e.g. "refs/heads/master").
 
---abbrev-ref[={strict|loose}]::
+--abbrev-ref[=(strict|loose)]::
        A non-ambiguous short name of the objects name.
        The option core.warnAmbiguousRefs is used to select the strict
        abbreviation mode.
@@ -112,14 +112,15 @@ OPTIONS
 +
 If a `pattern` is given, only refs matching the given shell glob are
 shown.  If the pattern does not contain a globbing character (`?`,
-`\*`, or `[`), it is turned into a prefix match by appending `/\*`.
+`{asterisk}`, or `[`), it is turned into a prefix match by
+appending `/{asterisk}`.
 
 --glob=pattern::
        Show all refs matching the shell glob pattern `pattern`. If
        the pattern does not start with `refs/`, this is automatically
        prepended.  If the pattern does not contain a globbing
-       character (`?`, `\*`, or `[`), it is turned into a prefix
-       match by appending `/\*`.
+       character (`?`, `{asterisk}`, or `[`), it is turned into a prefix
+       match by appending `/{asterisk}`.
 
 --show-toplevel::
        Show the absolute path of the top-level directory.
@@ -135,7 +136,12 @@ shown.  If the pattern does not contain a globbing character (`?`,
        directory (typically a sequence of "../", or an empty string).
 
 --git-dir::
-       Show `$GIT_DIR` if defined else show the path to the .git directory.
+       Show `$GIT_DIR` if defined. Otherwise show the path to
+       the .git directory, relative to the current directory.
++
+If `$GIT_DIR` is not defined and the current directory
+is not detected to lie in a git repository or work tree
+print a message to stderr and exit with nonzero status.
 
 --is-inside-git-dir::
        When the current working directory is below the repository
index b7d9ef7e4726db40f3b0d1ec9474494bb6e07dcf..45be851750c2ce81500765f6dfbd23578c68abda 100644 (file)
@@ -31,7 +31,7 @@ OPTIONS
 <commit>...::
        Commits to revert.
        For a more complete list of ways to spell commit names, see
-       linkgit:gitrevisions[1].
+       linkgit:gitrevisions[7].
        Sets of commits can also be given but no traversal is done by
        default, see linkgit:git-rev-list[1] and its '--no-walk'
        option.
@@ -80,6 +80,16 @@ effect to your index in a row.
 --signoff::
        Add Signed-off-by line at the end of the commit message.
 
+--strategy=<strategy>::
+       Use the given merge strategy.  Should only be used once.
+       See the MERGE STRATEGIES section in linkgit:git-merge[1]
+       for details.
+
+-X<option>::
+--strategy-option=<option>::
+       Pass the merge strategy-specific option through to the
+       merge strategy.  See linkgit:git-merge[1] for details.
+
 EXAMPLES
 --------
 git revert HEAD~3::
@@ -87,7 +97,7 @@ git revert HEAD~3::
        Revert the changes specified by the fourth last commit in HEAD
        and create a new commit with the reverted changes.
 
-git revert -n master\~5..master~2::
+git revert -n master{tilde}5..master{tilde}2::
 
        Revert the changes done by commits from the fifth last commit
        in master (included) to the third last commit in master
index c21d19e573d5192597b4766d8d87d17ab8f1c0f3..0adbe8b1f8e6e992a1f991fbdba4deffbd6e15b0 100644 (file)
@@ -78,7 +78,8 @@ a file that you have not told git about does not remove that file.
 
 File globbing matches across directory boundaries.  Thus, given
 two directories `d` and `d2`, there is a difference between
-using `git rm \'d\*\'` and `git rm \'d/\*\'`, as the former will
+using `git rm {apostrophe}d{asterisk}{apostrophe}` and
+`git rm {apostrophe}d/{asterisk}{apostrophe}`, as the former will
 also remove all of directory `d2`.
 
 REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM
@@ -88,8 +89,8 @@ the paths that have disappeared from the filesystem. However,
 depending on the use case, there are several ways that can be
 done.
 
-Using "git commit -a"
-~~~~~~~~~~~~~~~~~~~~~
+Using ``git commit -a''
+~~~~~~~~~~~~~~~~~~~~~~~
 If you intend that your next commit should record all modifications
 of tracked files in the working tree and record all removals of
 files that have been removed from the working tree with `rm`
@@ -97,8 +98,8 @@ files that have been removed from the working tree with `rm`
 automatically notice and record all removals.  You can also have a
 similar effect without committing by using `git add -u`.
 
-Using "git add -A"
-~~~~~~~~~~~~~~~~~~
+Using ``git add -A''
+~~~~~~~~~~~~~~~~~~~~
 When accepting a new code drop for a vendor branch, you probably
 want to record both the removal of paths and additions of new paths
 as well as modifications of existing paths.
@@ -110,8 +111,8 @@ tree using this command:
 git ls-files -z | xargs -0 rm -f
 ----------------
 
-and then "untar" the new code in the working tree. Alternately
-you could "rsync" the changes into the working tree.
+and then untar the new code in the working tree. Alternately
+you could 'rsync' the changes into the working tree.
 
 After that, the easiest way to record all removals, additions, and
 modifications in the working tree is:
@@ -135,11 +136,11 @@ git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached
 
 EXAMPLES
 --------
-git rm Documentation/\\*.txt::
-       Removes all `\*.txt` files from the index that are under the
+git rm Documentation/\*.txt::
+       Removes all `*.txt` files from the index that are under the
        `Documentation` directory and any of its subdirectories.
 +
-Note that the asterisk `\*` is quoted from the shell in this
+Note that the asterisk `*` is quoted from the shell in this
 example; this lets git, and not the shell, expand the pathnames
 of files and subdirectories under the `Documentation/` directory.
 
index c283084272090c28d5529d200a1147c9f57c639b..7ec9dabe6815f2b8ff03048043e1cfd81cd19fcc 100644 (file)
@@ -82,11 +82,26 @@ See the CONFIGURATION section for 'sendemail.multiedit'.
        set, as returned by "git var -l".
 
 --in-reply-to=<identifier>::
-       Specify the contents of the first In-Reply-To header.
-       Subsequent emails will refer to the previous email
-       instead of this if --chain-reply-to is set.
-       Only necessary if --compose is also set.  If --compose
-       is not set, this will be prompted for.
+       Make the first mail (or all the mails with `--no-thread`) appear as a
+       reply to the given Message-Id, which avoids breaking threads to
+       provide a new patch series.
+       The second and subsequent emails will be sent as replies according to
+       the `--[no]-chain-reply-to` setting.
++
+So for example when `--thread` and `--no-chain-reply-to` are specified, the
+second and subsequent patches will be replies to the first one like in the
+illustration below where `[PATCH v2 0/3]` is in reply to `[PATCH 0/2]`:
++
+  [PATCH 0/2] Here is what I did...
+    [PATCH 1/2] Clean up and tests
+    [PATCH 2/2] Implementation
+    [PATCH v2 0/3] Here is a reroll
+      [PATCH v2 1/3] Clean up
+      [PATCH v2 2/3] New tests
+      [PATCH v2 3/3] Implementation
++
+Only necessary if --compose is also set.  If --compose
+is not set, this will be prompted for.
 
 --subject=<string>::
        Specify the initial subject of the email thread.
@@ -97,7 +112,7 @@ See the CONFIGURATION section for 'sendemail.multiedit'.
        Specify the primary recipient of the emails generated. Generally, this
        will be the upstream maintainer of the project involved. Default is the
        value of the 'sendemail.to' configuration value; if that is unspecified,
-       this will be prompted for.
+       and --to-cmd is not specified, this will be prompted for.
 +
 The --to option must be repeated for each user you want on the to list.
 
@@ -165,6 +180,15 @@ user is prompted for a password while the input is masked for privacy.
        are also accepted. The port can also be set with the
        'sendemail.smtpserverport' configuration variable.
 
+--smtp-server-option=<option>::
+       If set, specifies the outgoing SMTP server option to use.
+       Default value can be specified by the 'sendemail.smtpserveroption'
+       configuration option.
++
+The --smtp-server-option option must be repeated for each option you want
+to pass to the server. Likewise, different lines in the configuration files
+must be used for each option.
+
 --smtp-ssl::
        Legacy alias for '--smtp-encryption ssl'.
 
@@ -177,6 +201,12 @@ user is prompted for a password while the input is masked for privacy.
 Automating
 ~~~~~~~~~~
 
+--to-cmd=<command>::
+       Specify a command to execute once per patch file which
+       should generate patch file specific "To:" entries.
+       Output of this command must be single email address per line.
+       Default is the value of 'sendemail.tocmd' configuration value.
+
 --cc-cmd=<command>::
        Specify a command to execute once per patch file which
        should generate patch file specific "Cc:" entries.
@@ -292,6 +322,9 @@ have been specified, in which case default to 'compose'.
 Default is the value of 'sendemail.validate'; if this is not set,
 default to '--validate'.
 
+--force::
+       Send emails even if safety checks would prevent it.
+
 
 CONFIGURATION
 -------------
index 0f3ad811cfa41e65a3d807a5eb766ce2a66a7831..6403126a029bf43acaa219296353f7ab1f2040a0 100644 (file)
@@ -3,24 +3,30 @@ git-shell(1)
 
 NAME
 ----
-git-shell - Restricted login shell for GIT-only SSH access
+git-shell - Restricted login shell for Git-only SSH access
 
 
 SYNOPSIS
 --------
-'$(git --exec-path)/git-shell' -c <command> <argument>
+'git shell' [-c <command> <argument>]
 
 DESCRIPTION
 -----------
-This is meant to be used as a login shell for SSH accounts you want
-to restrict to GIT pull/push access only. It permits execution only
-of server-side GIT commands implementing the pull/push functionality.
-The commands can be executed only by the '-c' option; the shell is not
-interactive.
-
-Currently, only four commands are permitted to be called, 'git-receive-pack'
-'git-upload-pack' and 'git-upload-archive' with a single required argument, or
-'cvs server' (to invoke 'git-cvsserver').
+
+A login shell for SSH accounts to provide restricted Git access. When
+'-c' is given, the program executes <command> non-interactively;
+<command> can be one of 'git receive-pack', 'git upload-pack', 'git
+upload-archive', 'cvs server', or a command in COMMAND_DIR. The shell
+is started in interactive mode when no arguments are given; in this
+case, COMMAND_DIR must exist, and any of the executables in it can be
+invoked.
+
+'cvs server' is a special command which executes git-cvsserver.
+
+COMMAND_DIR is the path "$HOME/git-shell-commands". The user must have
+read and execute permissions to the directory in order to execute the
+programs in it. The programs are executed with a cwd of $HOME, and
+<argument> is parsed as a command-line string.
 
 Author
 ------
index bc1ac77495347967c941298e2da38f76d5d124c6..5cc3baf48dcbf86e976d5d4407831a6d0ceb108f 100644 (file)
@@ -44,7 +44,7 @@ OPTIONS
 --email::
        Show the email address of each author.
 
---format[='<format>']::
+--format[=<format>]::
        Instead of the commit subject, use some other information to
        describe each commit.  '<format>' can be any string accepted
        by the `--format` option of 'git log', such as '{asterisk} [%h] %s'.
index 81ba29669c52a7c50fb126b7b70c3a867df25f00..3b0c88271a80ad9dfc989dcb44247479f09431bf 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
                [--current] [--color[=<when>] | --no-color] [--sparse]
                [--more=<n> | --list | --independent | --merge-base]
                [--no-name | --sha1-name] [--topics]
-               [<rev> | <glob>]...
+               [(<rev> | <glob>)...]
 
 'git show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]
 
@@ -32,7 +32,7 @@ no <rev> nor <glob> is given on the command line.
 OPTIONS
 -------
 <rev>::
-       Arbitrary extended SHA1 expression (see linkgit:gitrevisions[1])
+       Arbitrary extended SHA1 expression (see linkgit:gitrevisions[7])
        that typically names a branch head or a tag.
 
 <glob>::
@@ -168,10 +168,10 @@ $ git show-branch master fixes mhf
 ------------------------------------------------
 
 These three branches all forked from a common commit, [master],
-whose commit message is "Add \'git show-branch\'". The "fixes"
-branch adds one commit "Introduce "reset type" flag to "git reset"".
-The "mhf" branch adds many other commits. The current branch
-is "master".
+whose commit message is "Add {apostrophe}git show-branch{apostrophe}".
+The "fixes" branch adds one commit "Introduce "reset type" flag to
+"git reset"". The "mhf" branch adds many other commits.
+The current branch is "master".
 
 
 EXAMPLE
index 75780d7d63894e220bf938da0b4f5dca40d6301d..be0ec189af45e1006134e5607bb76a7b6ad20699 100644 (file)
@@ -73,8 +73,8 @@ OPTIONS
 --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
-       following actions on each:
+       form "^(?:<anything>\s)?<refname>(?:{backslash}{caret}\{\})?$"
+       and performs the following actions on each:
        (1) strip "^{}" at the end of line if any;
        (2) ignore if pattern is provided and does not head-match refname;
        (3) warn if refname is not a well-formed refname and skip;
@@ -84,7 +84,11 @@ OPTIONS
 
 <pattern>...::
 
-       Show references matching one or more patterns.
+       Show references matching one or more patterns. Patterns are matched from
+       the end of the full name, and only complete parts are matched, e.g.
+       'master' matches 'refs/heads/master', 'refs/remotes/origin/master',
+       'refs/tags/jedi/master' but not 'refs/heads/mymaster' nor
+       'refs/remotes/master/jedi'.
 
 OUTPUT
 ------
index 0002bfb0455b38eecf6b29da6a2464058ba42c57..f0a8a1aff3694cf00587d8670a8fe9962fe98af3 100644 (file)
@@ -36,7 +36,7 @@ OPTIONS
 <object>...::
        The names of objects to show.
        For a more complete list of ways to spell object names, see
-       "SPECIFYING REVISIONS" section in linkgit:gitrevisions[1].
+       "SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
 
 include::pretty-options.txt[]
 
@@ -54,6 +54,10 @@ git show v1.0.0::
 git show v1.0.0^\{tree\}::
        Shows the tree pointed to by the tag `v1.0.0`.
 
+git show -s --format=%s v1.0.0^\{commit\}::
+       Shows the subject of the commit pointed to by the
+       tag `v1.0.0`.
+
 git show next~10:Documentation/README::
        Shows the contents of the file `Documentation/README` as
        they were current in the 10th last commit of the branch
index 473889a660402f5f88b45c6aa3d00d0e85004389..8728f7a5142284d036594628cbb99da677c16651 100644 (file)
@@ -104,18 +104,22 @@ 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.
+When no `<stash>` is given, `stash@\{0}` is assumed, otherwise `<stash>` must
+be a reference of the form `stash@\{<revision>}`.
 
 apply [--index] [-q|--quiet] [<stash>]::
 
-       Like `pop`, but do not remove the state from the stash list.
+       Like `pop`, but do not remove the state from the stash list. Unlike `pop`,
+       `<stash>` may be any commit that looks like a commit created by
+       `stash save` or `stash create`.
 
 branch <branchname> [<stash>]::
 
        Creates and checks out a new branch named `<branchname>` starting from
        the commit at which the `<stash>` was originally created, applies the
-       changes recorded in `<stash>` to the new working tree and index, then
-       drops the `<stash>` if that completes successfully. When no `<stash>`
+       changes recorded in `<stash>` to the new working tree and index.
+       If that succeeds, and `<stash>` is a reference of the form
+       `stash@{<revision>}`, it then drops the `<stash>`. When no `<stash>`
        is given, applies the latest one.
 +
 This is useful if the branch on which you ran `git stash save` has
@@ -132,7 +136,9 @@ clear::
 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}`
+       is given, it removes the latest one. i.e. `stash@\{0}`, otherwise
+       `<stash>` must a valid stash log reference of the form
+       `stash@\{<revision>}`.
 
 create::
 
index 4b84d08fc87c13f6db371ecc52710bd09865d784..0ade2ce54efee3cd2241fc7e3009909eef5cf8bb 100644 (file)
@@ -56,6 +56,8 @@ COMMANDS
        as well, they take precedence.
 --no-metadata;;
        Set the 'noMetadata' option in the [svn-remote] config.
+       This option is not recommended, please read the 'svn.noMetadata'
+       section of this manpage before using this option.
 --use-svm-props;;
        Set the 'useSvmProps' option in the [svn-remote] config.
 --use-svnsync-props;;
@@ -436,7 +438,7 @@ git rebase --onto remotes/git-svn A^ master
 OPTIONS
 -------
 
---shared[={false|true|umask|group|all|world|everybody}]::
+--shared[=(false|true|umask|group|all|world|everybody)]::
 --template=<template_directory>::
        Only used with the 'init' command.
        These are passed directly to 'git init'.
@@ -597,13 +599,22 @@ 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
-be able to rebuild it and you won't be able to fetch again,
-either.  This is fine for one-shot imports.
+This option can only be used for one-shot imports as 'git svn'
+will not be able to fetch again without metadata. Additionally,
+if you lose your .git/svn/**/.rev_map.* files, 'git svn' will not
+be able to rebuild them.
 +
 The 'git svn log' command will not work on repositories using
 this, either.  Using this conflicts with the 'useSvmProps'
 option for (hopefully) obvious reasons.
++
+This option is NOT recommended as it makes it difficult to track down
+old references to SVN revision numbers in existing documentation, bug
+reports and archives.  If you plan to eventually migrate from SVN to git
+and are certain about dropping SVN history, consider
+linkgit:git-filter-branch[1] instead.  filter-branch also allows
+reformatting of metadata for ease-of-reading and rewriting authorship
+info for non-"svn.authorsFile" users.
 
 svn.useSvmProps::
 svn-remote.<name>.useSvmProps::
@@ -718,8 +729,11 @@ have each person clone that repository with 'git clone':
        cd project
        git init
        git remote add origin server:/pub/project
-       git config --add remote.origin.fetch '+refs/remotes/*:refs/remotes/*'
+       git config --replace-all remote.origin.fetch '+refs/remotes/*:refs/remotes/*'
        git fetch
+# Prevent fetch/pull from remote git server in the future,
+# we only want to use git svn for future updates
+       git config --remove-section remote.origin
 # 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)
index 31c78a81e094ffc8a66639119eadefbd1e760e35..8b169e364a32afe7d2ff3fa8ae54121ed44e3788 100644 (file)
@@ -177,7 +177,7 @@ On Automatic following
 ~~~~~~~~~~~~~~~~~~~~~~
 
 If you are following somebody else's tree, you are most likely
-using tracking branches (`refs/heads/origin` in traditional
+using remote-tracking branches (`refs/heads/origin` in traditional
 layout, or `refs/remotes/origin/master` in the separate-remote
 layout).  You usually want the tags from the other end.
 
@@ -232,7 +232,7 @@ this case.
 It may well be that among networking people, they may want to
 exchange the tags internal to their group, but in that workflow
 they are most likely tracking with each other's progress by
-having tracking branches.  Again, the heuristic to automatically
+having remote-tracking branches.  Again, the heuristic to automatically
 follow such tags is a good thing.
 
 
index 765d4b312ed6f062180e83fa0d931e6bd2eef24f..1ca56c85aa66b96dc1774b77ba1e117d93847033 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
 'git update-index'
             [--add] [--remove | --force-remove] [--replace]
             [--refresh] [-q] [--unmerged] [--ignore-missing]
-            [--cacheinfo <mode> <object> <file>]\*
+            [(--cacheinfo <mode> <object> <file>)...]
             [--chmod=(+|-)x]
             [--assume-unchanged | --no-assume-unchanged]
             [--skip-worktree | --no-skip-worktree]
@@ -21,7 +21,7 @@ SYNOPSIS
             [--info-only] [--index-info]
             [-z] [--stdin]
             [--verbose]
-            [--] [<file>]\*
+            [--] [<file>...]
 
 DESCRIPTION
 -----------
@@ -144,8 +144,8 @@ you will need to handle the situation manually.
         Report what is being added and removed from index.
 
 -z::
-       Only meaningful with `--stdin`; paths are separated with
-       NUL character instead of LF.
+       Only meaningful with `--stdin` or `--index-info`; paths are
+       separated with NUL character instead of LF.
 
 \--::
        Do not interpret any more arguments as options.
index dada21242c915148543dda8485b3322894aeb4c5..711219749cac76ff1363ce5f27e28da9a7ecba57 100644 (file)
@@ -15,6 +15,10 @@ Validates the gpg signature created by 'git tag'.
 
 OPTIONS
 -------
+-v::
+--verbose::
+       Print the contents of the tag object before validating it.
+
 <tag>...::
        SHA1 identifiers of git tag objects.
 
index 75720491b29e29135cc486b8280b2fbf0d166f4e..c0416e5e1a5243ef228173a3a45bb47bf1f3a840 100644 (file)
@@ -1,5 +1,5 @@
-git-web--browse(1)
-==================
+git-web{litdd}browse(1)
+=======================
 
 NAME
 ----
@@ -7,7 +7,7 @@ git-web--browse - git helper script to launch a web browser
 
 SYNOPSIS
 --------
-'git web--browse' [OPTIONS] URL/FILE ...
+'git web{litdd}browse' [OPTIONS] URL/FILE ...
 
 DESCRIPTION
 -----------
@@ -20,8 +20,14 @@ The following browsers (or commands) are currently supported:
 
 * firefox (this is the default under X Window when not using KDE)
 * iceweasel
+* seamonkey
+* iceape
+* chromium (also supported as chromium-browser)
+* google-chrome (also supported as chrome)
 * konqueror (this is the default under KDE, see 'Note about konqueror' below)
+* opera
 * w3m (this is the default outside graphical environments)
+* elinks
 * links
 * lynx
 * dillo
@@ -32,19 +38,19 @@ Custom commands may also be specified.
 
 OPTIONS
 -------
--b BROWSER::
---browser=BROWSER::
-       Use the specified BROWSER. It must be in the list of supported
+-b <browser>::
+--browser=<browser>::
+       Use the specified browser. It must be in the list of supported
        browsers.
 
--t BROWSER::
---tool=BROWSER::
+-t <browser>::
+--tool=<browser>::
        Same as above.
 
--c CONF.VAR::
---config=CONF.VAR::
+-c <conf.var>::
+--config=<conf.var>::
        CONF.VAR is looked up in the git config files. If it's set,
-       then its value specify the browser that should be used.
+       then its value specifies the browser that should be used.
 
 CONFIGURATION VARIABLES
 -----------------------
@@ -71,7 +77,7 @@ browser.<tool>.cmd
 When the browser, specified by options or configuration variables, is
 not among the supported ones, then the corresponding
 'browser.<tool>.cmd' configuration variable will be looked up. If this
-variable exists then 'git web--browse' will treat the specified tool
+variable exists then 'git web{litdd}browse' will treat the specified tool
 as a custom command and will use a shell eval to run the command with
 the URLs passed as arguments.
 
index 531789321c3b09f522f3284b736b8c65227c31b4..e968ed4aa09705d98530add23135e76141ec75c2 100644 (file)
@@ -9,11 +9,11 @@ git - the stupid content tracker
 SYNOPSIS
 --------
 [verse]
-'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
+'git' [--version] [--exec-path[=<path>]] [--html-path]
     [-p|--paginate|--no-pager] [--no-replace-objects]
-    [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
-    [-c name=value]
-    [--help] COMMAND [ARGS]
+    [--bare] [--git-dir=<path>] [--work-tree=<path>]
+    [-c <name>=<value>]
+    [--help] <command> [<args>]
 
 DESCRIPTION
 -----------
@@ -28,7 +28,7 @@ also want to read linkgit:gitcvs-migration[7].  See
 the link:user-manual.html[Git User's Manual] for a more in-depth
 introduction.
 
-The COMMAND is either a name of a Git command (see below) or an alias
+The '<command>' is either a name of a Git command (see below) or an alias
 as defined in the configuration file (see linkgit:git-config[1]).
 
 Formatted and hyperlinked version of the latest git
@@ -44,175 +44,200 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.7.2.2/git.html[documentation for release 1.7.2.2]
+* link:v1.7.4/git.html[documentation for release 1.7.4]
 
 * release notes for
-  link:RelNotes-1.7.2.2.txt[1.7.2.2],
-  link:RelNotes-1.7.2.1.txt[1.7.2.1],
-  link:RelNotes-1.7.2.txt[1.7.2].
+  link:RelNotes/1.7.4.txt[1.7.4].
 
-* link:v1.7.1.2/git.html[documentation for release 1.7.1.2]
+* link:v1.7.3.5/git.html[documentation for release 1.7.3.5]
 
 * release notes for
-  link:RelNotes-1.7.1.2.txt[1.7.1.2],
-  link:RelNotes-1.7.1.1.txt[1.7.1.1],
-  link:RelNotes-1.7.1.txt[1.7.1].
+  link:RelNotes/1.7.3.5.txt[1.7.3.5],
+  link:RelNotes/1.7.3.4.txt[1.7.3.4],
+  link:RelNotes/1.7.3.3.txt[1.7.3.3],
+  link:RelNotes/1.7.3.2.txt[1.7.3.2],
+  link:RelNotes/1.7.3.1.txt[1.7.3.1],
+  link:RelNotes/1.7.3.txt[1.7.3].
 
-* link:v1.7.0.7/git.html[documentation for release 1.7.0.7]
+* link:v1.7.2.5/git.html[documentation for release 1.7.2.5]
 
 * release notes for
-  link:RelNotes-1.7.0.7.txt[1.7.0.7],
-  link:RelNotes-1.7.0.6.txt[1.7.0.6],
-  link:RelNotes-1.7.0.5.txt[1.7.0.5],
-  link:RelNotes-1.7.0.4.txt[1.7.0.4],
-  link:RelNotes-1.7.0.3.txt[1.7.0.3],
-  link:RelNotes-1.7.0.2.txt[1.7.0.2],
-  link:RelNotes-1.7.0.1.txt[1.7.0.1],
-  link:RelNotes-1.7.0.txt[1.7.0].
+  link:RelNotes/1.7.2.5.txt[1.7.2.5],
+  link:RelNotes/1.7.2.4.txt[1.7.2.4],
+  link:RelNotes/1.7.2.3.txt[1.7.2.3],
+  link:RelNotes/1.7.2.2.txt[1.7.2.2],
+  link:RelNotes/1.7.2.1.txt[1.7.2.1],
+  link:RelNotes/1.7.2.txt[1.7.2].
 
-* link:v1.6.6.2/git.html[documentation for release 1.6.6.2]
+* link:v1.7.1.4/git.html[documentation for release 1.7.1.4]
 
 * release notes for
-  link:RelNotes-1.6.6.2.txt[1.6.6.2],
-  link:RelNotes-1.6.6.1.txt[1.6.6.1],
-  link:RelNotes-1.6.6.txt[1.6.6].
+  link:RelNotes/1.7.1.4.txt[1.7.1.4],
+  link:RelNotes/1.7.1.3.txt[1.7.1.3],
+  link:RelNotes/1.7.1.2.txt[1.7.1.2],
+  link:RelNotes/1.7.1.1.txt[1.7.1.1],
+  link:RelNotes/1.7.1.txt[1.7.1].
 
-* link:v1.6.5.8/git.html[documentation for release 1.6.5.8]
+* link:v1.7.0.9/git.html[documentation for release 1.7.0.9]
 
 * release notes for
-  link:RelNotes-1.6.5.8.txt[1.6.5.8],
-  link:RelNotes-1.6.5.7.txt[1.6.5.7],
-  link:RelNotes-1.6.5.6.txt[1.6.5.6],
-  link:RelNotes-1.6.5.5.txt[1.6.5.5],
-  link:RelNotes-1.6.5.4.txt[1.6.5.4],
-  link:RelNotes-1.6.5.3.txt[1.6.5.3],
-  link:RelNotes-1.6.5.2.txt[1.6.5.2],
-  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]
+  link:RelNotes/1.7.0.9.txt[1.7.0.9],
+  link:RelNotes/1.7.0.8.txt[1.7.0.8],
+  link:RelNotes/1.7.0.7.txt[1.7.0.7],
+  link:RelNotes/1.7.0.6.txt[1.7.0.6],
+  link:RelNotes/1.7.0.5.txt[1.7.0.5],
+  link:RelNotes/1.7.0.4.txt[1.7.0.4],
+  link:RelNotes/1.7.0.3.txt[1.7.0.3],
+  link:RelNotes/1.7.0.2.txt[1.7.0.2],
+  link:RelNotes/1.7.0.1.txt[1.7.0.1],
+  link:RelNotes/1.7.0.txt[1.7.0].
+
+* link:v1.6.6.3/git.html[documentation for release 1.6.6.3]
 
 * 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:RelNotes/1.6.6.3.txt[1.6.6.3],
+  link:RelNotes/1.6.6.2.txt[1.6.6.2],
+  link:RelNotes/1.6.6.1.txt[1.6.6.1],
+  link:RelNotes/1.6.6.txt[1.6.6].
+
+* link:v1.6.5.9/git.html[documentation for release 1.6.5.9]
+
+* release notes for
+  link:RelNotes/1.6.5.9.txt[1.6.5.9],
+  link:RelNotes/1.6.5.8.txt[1.6.5.8],
+  link:RelNotes/1.6.5.7.txt[1.6.5.7],
+  link:RelNotes/1.6.5.6.txt[1.6.5.6],
+  link:RelNotes/1.6.5.5.txt[1.6.5.5],
+  link:RelNotes/1.6.5.4.txt[1.6.5.4],
+  link:RelNotes/1.6.5.3.txt[1.6.5.3],
+  link:RelNotes/1.6.5.2.txt[1.6.5.2],
+  link:RelNotes/1.6.5.1.txt[1.6.5.1],
+  link:RelNotes/1.6.5.txt[1.6.5].
+
+* link:v1.6.4.5/git.html[documentation for release 1.6.4.5]
+
+* release notes for
+  link:RelNotes/1.6.4.5.txt[1.6.4.5],
+  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].
+  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],
-  link:RelNotes-1.6.2.4.txt[1.6.2.4],
-  link:RelNotes-1.6.2.3.txt[1.6.2.3],
-  link:RelNotes-1.6.2.2.txt[1.6.2.2],
-  link:RelNotes-1.6.2.1.txt[1.6.2.1],
-  link:RelNotes-1.6.2.txt[1.6.2].
+  link:RelNotes/1.6.2.5.txt[1.6.2.5],
+  link:RelNotes/1.6.2.4.txt[1.6.2.4],
+  link:RelNotes/1.6.2.3.txt[1.6.2.3],
+  link:RelNotes/1.6.2.2.txt[1.6.2.2],
+  link:RelNotes/1.6.2.1.txt[1.6.2.1],
+  link:RelNotes/1.6.2.txt[1.6.2].
 
 * link:v1.6.1.3/git.html[documentation for release 1.6.1.3]
 
 * release notes for
-  link:RelNotes-1.6.1.3.txt[1.6.1.3],
-  link:RelNotes-1.6.1.2.txt[1.6.1.2],
-  link:RelNotes-1.6.1.1.txt[1.6.1.1],
-  link:RelNotes-1.6.1.txt[1.6.1].
+  link:RelNotes/1.6.1.3.txt[1.6.1.3],
+  link:RelNotes/1.6.1.2.txt[1.6.1.2],
+  link:RelNotes/1.6.1.1.txt[1.6.1.1],
+  link:RelNotes/1.6.1.txt[1.6.1].
 
 * link:v1.6.0.6/git.html[documentation for release 1.6.0.6]
 
 * release notes for
-  link:RelNotes-1.6.0.6.txt[1.6.0.6],
-  link:RelNotes-1.6.0.5.txt[1.6.0.5],
-  link:RelNotes-1.6.0.4.txt[1.6.0.4],
-  link:RelNotes-1.6.0.3.txt[1.6.0.3],
-  link:RelNotes-1.6.0.2.txt[1.6.0.2],
-  link:RelNotes-1.6.0.1.txt[1.6.0.1],
-  link:RelNotes-1.6.0.txt[1.6.0].
+  link:RelNotes/1.6.0.6.txt[1.6.0.6],
+  link:RelNotes/1.6.0.5.txt[1.6.0.5],
+  link:RelNotes/1.6.0.4.txt[1.6.0.4],
+  link:RelNotes/1.6.0.3.txt[1.6.0.3],
+  link:RelNotes/1.6.0.2.txt[1.6.0.2],
+  link:RelNotes/1.6.0.1.txt[1.6.0.1],
+  link:RelNotes/1.6.0.txt[1.6.0].
 
 * link:v1.5.6.6/git.html[documentation for release 1.5.6.6]
 
 * release notes for
-  link:RelNotes-1.5.6.6.txt[1.5.6.6],
-  link:RelNotes-1.5.6.5.txt[1.5.6.5],
-  link:RelNotes-1.5.6.4.txt[1.5.6.4],
-  link:RelNotes-1.5.6.3.txt[1.5.6.3],
-  link:RelNotes-1.5.6.2.txt[1.5.6.2],
-  link:RelNotes-1.5.6.1.txt[1.5.6.1],
-  link:RelNotes-1.5.6.txt[1.5.6].
+  link:RelNotes/1.5.6.6.txt[1.5.6.6],
+  link:RelNotes/1.5.6.5.txt[1.5.6.5],
+  link:RelNotes/1.5.6.4.txt[1.5.6.4],
+  link:RelNotes/1.5.6.3.txt[1.5.6.3],
+  link:RelNotes/1.5.6.2.txt[1.5.6.2],
+  link:RelNotes/1.5.6.1.txt[1.5.6.1],
+  link:RelNotes/1.5.6.txt[1.5.6].
 
 * link:v1.5.5.6/git.html[documentation for release 1.5.5.6]
 
 * release notes for
-  link:RelNotes-1.5.5.6.txt[1.5.5.6],
-  link:RelNotes-1.5.5.5.txt[1.5.5.5],
-  link:RelNotes-1.5.5.4.txt[1.5.5.4],
-  link:RelNotes-1.5.5.3.txt[1.5.5.3],
-  link:RelNotes-1.5.5.2.txt[1.5.5.2],
-  link:RelNotes-1.5.5.1.txt[1.5.5.1],
-  link:RelNotes-1.5.5.txt[1.5.5].
+  link:RelNotes/1.5.5.6.txt[1.5.5.6],
+  link:RelNotes/1.5.5.5.txt[1.5.5.5],
+  link:RelNotes/1.5.5.4.txt[1.5.5.4],
+  link:RelNotes/1.5.5.3.txt[1.5.5.3],
+  link:RelNotes/1.5.5.2.txt[1.5.5.2],
+  link:RelNotes/1.5.5.1.txt[1.5.5.1],
+  link:RelNotes/1.5.5.txt[1.5.5].
 
 * link:v1.5.4.7/git.html[documentation for release 1.5.4.7]
 
 * release notes for
-  link:RelNotes-1.5.4.7.txt[1.5.4.7],
-  link:RelNotes-1.5.4.6.txt[1.5.4.6],
-  link:RelNotes-1.5.4.5.txt[1.5.4.5],
-  link:RelNotes-1.5.4.4.txt[1.5.4.4],
-  link:RelNotes-1.5.4.3.txt[1.5.4.3],
-  link:RelNotes-1.5.4.2.txt[1.5.4.2],
-  link:RelNotes-1.5.4.1.txt[1.5.4.1],
-  link:RelNotes-1.5.4.txt[1.5.4].
+  link:RelNotes/1.5.4.7.txt[1.5.4.7],
+  link:RelNotes/1.5.4.6.txt[1.5.4.6],
+  link:RelNotes/1.5.4.5.txt[1.5.4.5],
+  link:RelNotes/1.5.4.4.txt[1.5.4.4],
+  link:RelNotes/1.5.4.3.txt[1.5.4.3],
+  link:RelNotes/1.5.4.2.txt[1.5.4.2],
+  link:RelNotes/1.5.4.1.txt[1.5.4.1],
+  link:RelNotes/1.5.4.txt[1.5.4].
 
 * link:v1.5.3.8/git.html[documentation for release 1.5.3.8]
 
 * release notes for
-  link:RelNotes-1.5.3.8.txt[1.5.3.8],
-  link:RelNotes-1.5.3.7.txt[1.5.3.7],
-  link:RelNotes-1.5.3.6.txt[1.5.3.6],
-  link:RelNotes-1.5.3.5.txt[1.5.3.5],
-  link:RelNotes-1.5.3.4.txt[1.5.3.4],
-  link:RelNotes-1.5.3.3.txt[1.5.3.3],
-  link:RelNotes-1.5.3.2.txt[1.5.3.2],
-  link:RelNotes-1.5.3.1.txt[1.5.3.1],
-  link:RelNotes-1.5.3.txt[1.5.3].
+  link:RelNotes/1.5.3.8.txt[1.5.3.8],
+  link:RelNotes/1.5.3.7.txt[1.5.3.7],
+  link:RelNotes/1.5.3.6.txt[1.5.3.6],
+  link:RelNotes/1.5.3.5.txt[1.5.3.5],
+  link:RelNotes/1.5.3.4.txt[1.5.3.4],
+  link:RelNotes/1.5.3.3.txt[1.5.3.3],
+  link:RelNotes/1.5.3.2.txt[1.5.3.2],
+  link:RelNotes/1.5.3.1.txt[1.5.3.1],
+  link:RelNotes/1.5.3.txt[1.5.3].
 
 * link:v1.5.2.5/git.html[documentation for release 1.5.2.5]
 
 * release notes for
-  link:RelNotes-1.5.2.5.txt[1.5.2.5],
-  link:RelNotes-1.5.2.4.txt[1.5.2.4],
-  link:RelNotes-1.5.2.3.txt[1.5.2.3],
-  link:RelNotes-1.5.2.2.txt[1.5.2.2],
-  link:RelNotes-1.5.2.1.txt[1.5.2.1],
-  link:RelNotes-1.5.2.txt[1.5.2].
+  link:RelNotes/1.5.2.5.txt[1.5.2.5],
+  link:RelNotes/1.5.2.4.txt[1.5.2.4],
+  link:RelNotes/1.5.2.3.txt[1.5.2.3],
+  link:RelNotes/1.5.2.2.txt[1.5.2.2],
+  link:RelNotes/1.5.2.1.txt[1.5.2.1],
+  link:RelNotes/1.5.2.txt[1.5.2].
 
 * link:v1.5.1.6/git.html[documentation for release 1.5.1.6]
 
 * release notes for
-  link:RelNotes-1.5.1.6.txt[1.5.1.6],
-  link:RelNotes-1.5.1.5.txt[1.5.1.5],
-  link:RelNotes-1.5.1.4.txt[1.5.1.4],
-  link:RelNotes-1.5.1.3.txt[1.5.1.3],
-  link:RelNotes-1.5.1.2.txt[1.5.1.2],
-  link:RelNotes-1.5.1.1.txt[1.5.1.1],
-  link:RelNotes-1.5.1.txt[1.5.1].
+  link:RelNotes/1.5.1.6.txt[1.5.1.6],
+  link:RelNotes/1.5.1.5.txt[1.5.1.5],
+  link:RelNotes/1.5.1.4.txt[1.5.1.4],
+  link:RelNotes/1.5.1.3.txt[1.5.1.3],
+  link:RelNotes/1.5.1.2.txt[1.5.1.2],
+  link:RelNotes/1.5.1.1.txt[1.5.1.1],
+  link:RelNotes/1.5.1.txt[1.5.1].
 
 * link:v1.5.0.7/git.html[documentation for release 1.5.0.7]
 
 * release notes for
-  link:RelNotes-1.5.0.7.txt[1.5.0.7],
-  link:RelNotes-1.5.0.6.txt[1.5.0.6],
-  link:RelNotes-1.5.0.5.txt[1.5.0.5],
-  link:RelNotes-1.5.0.3.txt[1.5.0.3],
-  link:RelNotes-1.5.0.2.txt[1.5.0.2],
-  link:RelNotes-1.5.0.1.txt[1.5.0.1],
-  link:RelNotes-1.5.0.txt[1.5.0].
+  link:RelNotes/1.5.0.7.txt[1.5.0.7],
+  link:RelNotes/1.5.0.6.txt[1.5.0.6],
+  link:RelNotes/1.5.0.5.txt[1.5.0.5],
+  link:RelNotes/1.5.0.3.txt[1.5.0.3],
+  link:RelNotes/1.5.0.2.txt[1.5.0.2],
+  link:RelNotes/1.5.0.1.txt[1.5.0.1],
+  link:RelNotes/1.5.0.txt[1.5.0].
 
 * documentation for release link:v1.4.4.4/git.html[1.4.4.4],
   link:v1.3.3/git.html[1.3.3],
@@ -245,7 +270,7 @@ help ...`.
        The <name> is expected in the same format as listed by
        'git config' (subkeys separated by dots).
 
---exec-path::
+--exec-path[=<path>]::
        Path to wherever your core git programs are installed.
        This can also be controlled by setting the GIT_EXEC_PATH
        environment variable. If no path is given, 'git' will print
@@ -271,17 +296,12 @@ help ...`.
        path or relative path to current working directory.
 
 --work-tree=<path>::
-       Set the path to the working tree.  The value will not be
-       used in combination with repositories found automatically in
-       a .git directory (i.e. $GIT_DIR is not set).
+       Set the path to the working tree. It can be an absolute path
+       or a path relative to the current working directory.
        This can also be controlled by setting the GIT_WORK_TREE
        environment variable and the core.worktree configuration
-       variable. It can be an absolute path or relative path to
-       the directory specified by --git-dir or GIT_DIR.
-       Note: If --git-dir or GIT_DIR are specified but none of
-       --work-tree, GIT_WORK_TREE and core.worktree is specified,
-       the current working directory is regarded as the top directory
-       of your working tree.
+       variable (see core.worktree in linkgit:git-config[1] for a
+       more detailed discussion).
 
 --bare::
        Treat the repository as a bare repository.  If GIT_DIR
@@ -488,7 +508,7 @@ HEAD::
        (i.e. the contents of `$GIT_DIR/refs/heads/<head>`).
 
 For a more complete list of ways to spell object names, see
-"SPECIFYING REVISIONS" section in linkgit:gitrevisions[1].
+"SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
 
 
 File/Directory Structure
@@ -638,6 +658,13 @@ Usually it is easier to configure any desired options through your
 personal `.ssh/config` file.  Please consult your ssh documentation
 for further details.
 
+'GIT_ASKPASS'::
+       If this environment variable is set, then git commands which need to
+       acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)
+       will call this program with a suitable prompt as command line argument
+       and read the password from its STDOUT. See also the 'core.askpass'
+       option in linkgit:git-config[1].
+
 'GIT_FLUSH'::
        If this environment variable is set to "1", then commands such
        as 'git blame' (in incremental mode), 'git rev-list', 'git log',
index 564586b943f439cb5ae04c4e76bc19d093c625da..7e7e12168eb69d43f6d7eea9cde2176e06fe73d5 100644 (file)
@@ -62,14 +62,21 @@ consults `$GIT_DIR/info/attributes` file (which has the highest
 precedence), `.gitattributes` file in the same directory as the
 path in question, and its parent directories up to the toplevel of the
 work tree (the further the directory that contains `.gitattributes`
-is from the path in question, the lower its precedence).
+is from the path in question, the lower its precedence). Finally
+global and system-wide files are considered (they have the lowest
+precedence).
 
 If you wish to affect only a single repository (i.e., to assign
-attributes to files that are particular to one user's workflow), then
+attributes to files that are particular to
+one user's workflow for that repository), then
 attributes should be placed in the `$GIT_DIR/info/attributes` file.
 Attributes which should be version-controlled and distributed to other
 repositories (i.e., attributes of interest to all users) should go into
-`.gitattributes` files.
+`.gitattributes` files. Attributes that should affect all repositories
+for a single user should be placed in a file specified by the
+`core.attributesfile` configuration option (see linkgit:git-config[1]).
+Attributes for all users on a system should be placed in the
+`$(prefix)/etc/gitattributes` file.
 
 Sometimes you would need to override an setting of an attribute
 for a path to `unspecified` state.  This can be done by listing
@@ -317,6 +324,27 @@ command is "cat").
        smudge = cat
 ------------------------
 
+For best results, `clean` should not alter its output further if it is
+run twice ("clean->clean" should be equivalent to "clean"), and
+multiple `smudge` commands should not alter `clean`'s output
+("smudge->smudge->clean" should be equivalent to "clean").  See the
+section on merging below.
+
+The "indent" filter is well-behaved in this regard: it will not modify
+input that is already correctly indented.  In this case, the lack of a
+smudge filter means that the clean filter _must_ accept its own output
+without modifying it.
+
+Sequence "%f" on the filter command line is replaced with the name of
+the file the filter is working on.  A filter might use this in keyword
+substitution.  For example:
+
+------------------------
+[filter "p4"]
+       clean = git-p4-filter --clean %f
+       smudge = git-p4-filter --smudge %f
+------------------------
+
 
 Interaction between checkin/checkout attributes
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -331,6 +359,29 @@ In the check-out codepath, the blob content is first converted
 with `text`, and then `ident` and fed to `filter`.
 
 
+Merging branches with differing checkin/checkout attributes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you have added attributes to a file that cause the canonical
+repository format for that file to change, such as adding a
+clean/smudge filter or text/eol/ident attributes, merging anything
+where the attribute is not in place would normally cause merge
+conflicts.
+
+To prevent these unnecessary merge conflicts, git can be told to run a
+virtual check-out and check-in of all three stages of a file when
+resolving a three-way merge by setting the `merge.renormalize`
+configuration variable.  This prevents changes caused by check-in
+conversion from causing spurious merge conflicts when a converted file
+is merged with an unconverted file.
+
+As long as a "smudge->clean" results in the same output as a "clean"
+even on files that are already smudged, this strategy will
+automatically resolve all filter-related conflicts.  Filters that do
+not act in this way may cause additional merge conflicts that must be
+resolved manually.
+
+
 Generating diff text
 ~~~~~~~~~~~~~~~~~~~~
 
@@ -441,6 +492,10 @@ patterns are available:
 
 - `cpp` suitable for source code in the C and C++ languages.
 
+- `csharp` suitable for source code in the C# language.
+
+- `fortran` suitable for source code in the Fortran language.
+
 - `html` suitable for HTML/XHTML documents.
 
 - `java` suitable for source code in the Java language.
@@ -449,6 +504,8 @@ patterns are available:
 
 - `pascal` suitable for source code in the Pascal/Delphi language.
 
+- `perl` suitable for source code in the Perl language.
+
 - `php` suitable for source code in the PHP language.
 
 - `python` suitable for source code in the Python language.
@@ -536,6 +593,39 @@ and now produces better output), you can remove the cache
 manually with `git update-ref -d refs/notes/textconv/jpg` (where
 "jpg" is the name of the diff driver, as in the example above).
 
+Marking files as binary
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Git usually guesses correctly whether a blob contains text or binary
+data by examining the beginning of the contents. However, sometimes you
+may want to override its decision, either because a blob contains binary
+data later in the file, or because the content, while technically
+composed of text characters, is opaque to a human reader. For example,
+many postscript files contain only ascii characters, but produce noisy
+and meaningless diffs.
+
+The simplest way to mark a file as binary is to unset the diff
+attribute in the `.gitattributes` file:
+
+------------------------
+*.ps -diff
+------------------------
+
+This will cause git to generate `Binary files differ` (or a binary
+patch, if binary patches are enabled) instead of a regular diff.
+
+However, one may also want to specify other diff driver attributes. For
+example, you might want to use `textconv` to convert postscript files to
+an ascii representation for human viewing, but otherwise treat them as
+binary files. You cannot specify both `-diff` and `diff=ps` attributes.
+The solution is to use the `diff.*.binary` config option:
+
+------------------------
+[diff "ps"]
+  textconv = ps2ascii
+  binary = true
+------------------------
+
 Performing a three-way merge
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -678,6 +768,8 @@ control per path.
 Set::
 
        Notice all types of potential whitespace errors known to git.
+       The tab width is taken from the value of the `core.whitespace`
+       configuration variable.
 
 Unset::
 
@@ -685,13 +777,13 @@ Unset::
 
 Unspecified::
 
-       Use the value of `core.whitespace` configuration variable to
+       Use the value of the `core.whitespace` configuration variable to
        decide what to notice as error.
 
 String::
 
        Specify a comma separate list of common whitespace problems to
-       notice in the same format as `core.whitespace` configuration
+       notice in the same format as the `core.whitespace` configuration
        variable.
 
 
index ed3ddc92cb51eaeb3d4a988e396a4f90297037c6..c27d086f68432d094a0fa7c1f9da353d6d3a0513 100644 (file)
@@ -110,7 +110,7 @@ An 'object' is identified by its 160-bit SHA1 hash, aka 'object name',
 and a reference to an object is always the 40-byte hex
 representation of that SHA1 name. The files in the `refs`
 subdirectory are expected to contain these hex references
-(usually with a final `\'\n\'` at the end), and you should thus
+(usually with a final `\n` at the end), and you should thus
 expect to see a number of 41-byte files containing these
 references in these `refs` subdirectories when you actually start
 populating your tree.
@@ -310,7 +310,7 @@ and this will just output the name of the resulting tree, in this case
 ----------------
 
 which is another incomprehensible object name. Again, if you want to,
-you can use `git cat-file -t 8988d\...` to see that this time the object
+you can use `git cat-file -t 8988d...` to see that this time the object
 is not a "blob" object, but a "tree" object (you can also use
 `git cat-file` to actually output the raw object contents, but you'll see
 mainly a binary mess, so that's less interesting).
@@ -436,8 +436,8 @@ $ git update-index hello
 (note how we didn't need the `\--add` flag this time, since git knew
 about the file already).
 
-Note what happens to the different 'git diff-\*' versions here. After
-we've updated `hello` in the index, `git diff-files -p` now shows no
+Note what happens to the different 'git diff-{asterisk}' versions here.
+After we've updated `hello` in the index, `git diff-files -p` now shows no
 differences, but `git diff-index -p HEAD` still *does* show that the
 current state is different from the state we committed. In fact, now
 'git diff-index' shows the same difference whether we use the `--cached`
@@ -494,7 +494,7 @@ and it will show what the last commit (in `HEAD`) actually changed.
 [NOTE]
 ============
 Here is an ASCII art by Jon Loeliger that illustrates how
-various diff-\* commands compare things.
+various 'diff-{asterisk}' commands compare things.
 
                       diff-tree
                        +----+
@@ -958,11 +958,11 @@ $ git show-branch --topo-order --more=1 master mybranch
 The first two lines indicate that it is showing the two branches
 and the first line of the commit log message from their
 top-of-the-tree commits, you are currently on `master` branch
-(notice the asterisk `\*` character), and the first column for
+(notice the asterisk `{asterisk}` character), and the first column for
 the later output lines is used to show commits contained in the
 `master` branch, and the second column for the `mybranch`
 branch. Three commits are shown along with their log messages.
-All of them have non blank characters in the first column (`*`
+All of them have non blank characters in the first column (`{asterisk}`
 shows an ordinary commit on the current branch, `-` is a merge commit), which
 means they are now part of the `master` branch. Only the "Some
 work" commit has the plus `+` character in the second column,
@@ -971,7 +971,7 @@ commits from the master branch.  The string inside brackets
 before the commit log message is a short name you can use to
 name the commit.  In the above example, 'master' and 'mybranch'
 are branch heads.  'master^' is the first parent of 'master'
-branch head.  Please see linkgit:gitrevisions[1] if you want to
+branch head.  Please see linkgit:gitrevisions[7] if you want to
 see more complex cases.
 
 [NOTE]
@@ -1092,7 +1092,7 @@ Downloader from http and https URL
 first obtains the topmost commit object name from the remote site
 by looking at the specified refname under `repo.git/refs/` directory,
 and then tries to obtain the
-commit object by downloading from `repo.git/objects/xx/xxx\...`
+commit object by downloading from `repo.git/objects/xx/xxx...`
 using the object name of that commit object.  Then it reads the
 commit object to find out its parent commits and the associate
 tree object; it repeats this process until it gets all the
@@ -1420,7 +1420,7 @@ packed, and stores the packed file in `.git/objects/pack`
 directory.
 
 [NOTE]
-You will see two files, `pack-\*.pack` and `pack-\*.idx`,
+You will see two files, `pack-{asterisk}.pack` and `pack-{asterisk}.idx`,
 in `.git/objects/pack` directory. They are closely related to
 each other, and if you ever copy them by hand to a different
 repository for whatever reason, you should make sure you copy
index 5d91a7e5b3a40cbf41f03a81799c162775c36607..6af29a4603de57e0040c4bcd819b831dd6649f92 100644 (file)
@@ -227,9 +227,9 @@ changes that touch a specified string, and is controlled by the
 commands.
 
 When diffcore-pickaxe is in use, it checks if there are
-filepairs whose "result" side has the specified string and
-whose "origin" side does not.  Such a filepair represents "the
-string appeared in this changeset".  It also checks for the
+filepairs whose "result" side and whose "origin" side have
+different number of specified string.  Such a filepair represents
+"the string appeared in this changeset".  It also checks for the
 opposite case that loses the specified string.
 
 When `\--pickaxe-all` is not in effect, diffcore-pickaxe leaves
index 7183aa9abbc35018dc50a7c0e5254cc72115af23..28edefa202fb0d1065f161f59787ceae39439eb3 100644 (file)
@@ -350,10 +350,6 @@ rebase::
 The commits are guaranteed to be listed in the order that they were
 processed by rebase.
 
-There is no default 'post-rewrite' hook, but see the
-`post-receive-copy-notes` script in `contrib/hooks` for an example
-that copies your git-notes to the rewritten commits.
-
 
 GIT
 ---
index e10fa88b8cf98b796fc9354e7671dc7bec25840d..8416f3445a5e5d3b583733136d4211b8d0bd4084 100644 (file)
@@ -14,11 +14,8 @@ DESCRIPTION
 
 A `gitignore` file specifies intentionally untracked files that
 git should ignore.
-Note that all the `gitignore` files really concern only files
-that are not already tracked by git;
-in order to ignore uncommitted changes in already tracked files,
-please refer to the 'git update-index --assume-unchanged'
-documentation.
+Files already tracked by git are not affected; see the NOTES
+below for details.
 
 Each line in a `gitignore` file specifies a pattern.
 When deciding whether to ignore a path, git normally checks
@@ -62,7 +59,8 @@ files specified by command-line options.  Higher-level git
 tools, such as 'git status' and 'git add',
 use patterns from the sources specified above.
 
-Patterns have the following format:
+PATTERN FORMAT
+--------------
 
  - A blank line matches no files, so it can serve as a separator
    for readability.
@@ -90,15 +88,28 @@ Patterns have the following format:
  - Otherwise, git treats the pattern as a shell glob suitable
    for consumption by fnmatch(3) with the FNM_PATHNAME flag:
    wildcards in the pattern will not match a / in the pathname.
-   For example, "Documentation/\*.html" matches
+   For example, "Documentation/{asterisk}.html" matches
    "Documentation/git.html" but not "Documentation/ppc/ppc.html"
    or "tools/perf/Documentation/perf.html".
 
  - A leading slash matches the beginning of the pathname.
-   For example, "/*.c" matches "cat-file.c" but not
+   For example, "/{asterisk}.c" matches "cat-file.c" but not
    "mozilla-sha1/sha1.c".
 
-An example:
+NOTES
+-----
+
+The purpose of gitignore files is to ensure that certain files
+not tracked by git remain untracked.
+
+To ignore uncommitted changes in a file that is already tracked,
+use 'git update-index {litdd}assume-unchanged'.
+
+To stop tracking a file that is currently tracked, use
+'git rm --cached'.
+
+EXAMPLES
+--------
 
 --------------------------------------------------------------
     $ git status
@@ -140,6 +151,11 @@ Another example:
 The second .gitignore prevents git from ignoring
 `arch/foo/kernel/vmlinux.lds.S`.
 
+SEE ALSO
+--------
+linkgit:git-rm[1], linkgit:git-update-index[1],
+linkgit:gitrepository-layout[5]
+
 Documentation
 -------------
 Documentation by David Greaves, Junio C Hamano, Josh Triplett,
index 05ac1c79f768352be8756eeb483e71f0d09131de..e21bac4f3f16a6d0c0b1ff6d3062e8464e182e7f 100644 (file)
@@ -69,7 +69,7 @@ frequently used options.
        the form "'<from>'..'<to>'" to show all revisions between '<from>' and
        back to '<to>'. Note, more advanced revision selection can be applied.
        For a more complete list of ways to spell object names, see
-       linkgit:gitrevisions[1].
+       linkgit:gitrevisions[7].
 
 <path>...::
 
index 8ae107da250ff01b17b5215a474c781a0f0ae57a..68977943e7bba092f5e55e6ad1b9ede83f4c2b67 100644 (file)
@@ -44,6 +44,14 @@ submodule.<name>.update::
        This config option is overridden if 'git submodule update' is given
        the '--merge' or '--rebase' options.
 
+submodule.<name>.fetchRecurseSubmodules::
+       This option can be used to enable/disable recursive fetching of this
+       submodule. If this option is also present in the submodules entry in
+       .git/config of the superproject, the setting there will override the
+       one found in .gitmodules.
+       Both settings can be overridden on the command line by using the
+       "--[no-]recurse-submodules" option to "git fetch" and "git pull"..
+
 submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
        a submodule as modified. When set to "all", it will never be considered
@@ -56,7 +64,7 @@ submodule.<name>.ignore::
        If this option is also present in the submodules entry in .git/config of
        the superproject, the setting there will override the one found in
        .gitmodules.
-       Both settings can be overriden on the command line by using the
+       Both settings can be overridden on the command line by using the
        "--ignore-submodule" option.
 
 
index ecab0c09d01fadd423b8d6bc4ee4bf3dd9e669de..7fe5848d1f7ef77748448c03c4b2b2b33d82f03c 100644 (file)
@@ -373,7 +373,7 @@ $ git status
 #
 #       new file: closing.txt
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #
 #       modified: file.txt
index 1c1606696e3771f1b881c18689b049b53a8a3539..0982f74ef6212577afb74d86079472662e2bffba 100644 (file)
@@ -385,7 +385,7 @@ alice$ git fetch bob
 
 Unlike the longhand form, when Alice fetches from Bob using a
 remote repository shorthand set up with 'git remote', what was
-fetched is stored in a remote tracking branch, in this case
+fetched is stored in a remote-tracking branch, in this case
 `bob/master`.  So after this:
 
 -------------------------------------
@@ -402,8 +402,8 @@ could merge the changes into her master branch:
 alice$ git merge bob/master
 -------------------------------------
 
-This `merge` can also be done by 'pulling from her own remote
-tracking branch', like this:
+This `merge` can also be done by 'pulling from her own remote-tracking
+branch', like this:
 
 -------------------------------------
 alice$ git pull . remotes/bob/master
index 1f029f8aa080c4de6323e8b4905a81fa7e8e2046..33716a31d0c60093c14f894ef07a87bee73fafc9 100644 (file)
@@ -131,7 +131,7 @@ to point at the new commit.
        you have. In such these cases, you do not make a new <<def_merge,merge>>
        <<def_commit,commit>> but instead just update to his
        revision. This will happen frequently on a
-       <<def_tracking_branch,tracking branch>> of a remote
+       <<def_remote_tracking_branch,remote-tracking branch>> of a remote
        <<def_repository,repository>>.
 
 [[def_fetch]]fetch::
@@ -260,7 +260,7 @@ This commit is referred to as a "merge commit", or sometimes just a
        The default upstream <<def_repository,repository>>. Most projects have
        at least one upstream project which they track. By default
        'origin' is used for that purpose. New upstream updates
-       will be fetched into remote <<def_tracking_branch,tracking branches>> named
+       will be fetched into remote <<def_remote_tracking_branch,remote-tracking branches>> named
        origin/name-of-upstream-branch, which you can see using
        `git branch -r`.
 
@@ -273,6 +273,29 @@ This commit is referred to as a "merge commit", or sometimes just a
        <<def_pack,pack>>, to assist in efficiently accessing the contents of a
        pack.
 
+[[def_pathspec]]pathspec::
+       Pattern used to specify paths.
++
+Pathspecs are used on the command line of "git ls-files", "git
+ls-tree", "git grep", "git checkout", and many other commands to
+limit the scope of operations to some subset of the tree or
+worktree.  See the documentation of each command for whether
+paths are relative to the current directory or toplevel.  The
+pathspec syntax is as follows:
+
+* any path matches itself
+* the pathspec up to the last slash represents a
+  directory prefix.  The scope of that pathspec is
+  limited to that subtree.
+* the rest of the pathspec is a pattern for the remainder
+  of the pathname.  Paths relative to the directory
+  prefix will be matched against that pattern using fnmatch(3);
+  in particular, '*' and '?' _can_ match directory separators.
++
+For example, Documentation/*.jpg will match all .jpg files
+in the Documentation subtree,
+including Documentation/chapter_1/figure_1.jpg.
+
 [[def_parent]]parent::
        A <<def_commit_object,commit object>> contains a (possibly empty) list
        of the logical predecessor(s) in the line of development, i.e. its
@@ -349,6 +372,14 @@ This commit is referred to as a "merge commit", or sometimes just a
        master branch head as to-upstream branch at $URL". See also
        linkgit:git-push[1].
 
+[[def_remote_tracking_branch]]remote-tracking branch::
+       A regular git <<def_branch,branch>> that is used to follow changes from
+       another <<def_repository,repository>>. A remote-tracking
+       branch should not contain direct modifications or have local commits
+       made to it. A remote-tracking branch can usually be
+       identified as the right-hand-side <<def_ref,ref>> in a Pull:
+       <<def_refspec,refspec>>.
+
 [[def_repository]]repository::
        A collection of <<def_ref,refs>> together with an
        <<def_object_database,object database>> containing all objects
@@ -418,14 +449,6 @@ This commit is referred to as a "merge commit", or sometimes just a
        that each contain very well defined concepts or small incremental yet
        related changes.
 
-[[def_tracking_branch]]tracking branch::
-       A regular git <<def_branch,branch>> that is used to follow changes from
-       another <<def_repository,repository>>. A tracking
-       branch should not contain direct modifications or have local commits
-       made to it. A tracking branch can usually be
-       identified as the right-hand-side <<def_ref,ref>> in a Pull:
-       <<def_refspec,refspec>>.
-
 [[def_tree]]tree::
        Either a <<def_working_tree,working tree>>, or a <<def_tree_object,tree
        object>> together with the dependent <<def_blob_object,blob>> and tree objects
index ff5c0bc27a416de16f106c15b9076082c6509e42..6fd711996a775d1da8ab2051db98896a601c8971 100644 (file)
@@ -229,7 +229,7 @@ reverting W.  Mainline's history would look like this:
    A---B---C
 
 But if you don't actually need to change commit A, then you need some way to
-recreate it as a new commit with the same changes in it.  The rebase commmand's
+recreate it as a new commit with the same changes in it.  The rebase command's
 --no-ff option provides a way to do this:
 
     $ git rebase [-i] --no-ff P
index 8c32da6deb05b5da700a5bd0a4281bf862b23f2c..093c656048a81e6cdc46d1aecf80fbffd97c94ea 100644 (file)
@@ -112,25 +112,19 @@ $ git tag pu-anchor pu
 $ git rebase master
 * Applying: Redo "revert" using three-way merge machinery.
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: Remove git-apply-patch-script.
 First trying simple merge strategy to cherry-pick.
 Simple cherry-pick fails; trying Automatic cherry-pick.
 Removing Documentation/git-apply-patch-script.txt
 Removing git-apply-patch-script
-Finished one cherry-pick.
 * Applying: Document "git cherry-pick" and "git revert"
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: mailinfo and applymbox updates
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: Show commits in topo order and name all commits.
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: More documentation updates.
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 ------------------------------------------------
 
 The temporary tag 'pu-anchor' is me just being careful, in case 'git
index 34d02a24188921ebac1b70f7cf0c6517a45e8fb1..76d69a907b48a961906e0b85c59a75e9c0c8986d 100755 (executable)
@@ -6,13 +6,13 @@ for h in \
        *.txt *.html \
        howto/*.txt howto/*.html \
        technical/*.txt technical/*.html \
-       RelNotes-*.txt *.css
+       RelNotes/*.txt *.css
 do
        if test ! -f "$h"
        then
                : did not match
        elif test -f "$T/$h" &&
-          $DIFF -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
+               $DIFF -u -I'^Last updated ' "$T/$h" "$h"
        then
                :; # up to date
        else
@@ -30,7 +30,7 @@ for th in \
 do
        h=`expr "$th" : "$strip_leading"'\(.*\)'`
        case "$h" in
-       index.html) continue ;;
+       RelNotes-*.txt | index.html) continue ;;
        esac
        test -f "$h" && continue
        echo >&2 "# rm -f $th"
index a403155052299dd0aaafd6bdfe0fec92d0d0ac7c..1e5c22c5e5c19dab5d5c35ec2401870237830a5d 100644 (file)
@@ -7,14 +7,26 @@ merge.conflictstyle::
        marker and the original text before the `=======` marker.
 
 merge.log::
-       Whether to include summaries of merged commits in newly created
-       merge commit messages. False by default.
+       In addition to branch names, populate the log message with at
+       most the specified number of one-line descriptions from the
+       actual commits that are being merged.  Defaults to false, and
+       true is a synonym for 20.
 
 merge.renameLimit::
        The number of files to consider when performing rename detection
        during a merge; if not specified, defaults to the value of
        diff.renameLimit.
 
+merge.renormalize::
+       Tell git that canonical representation of files in the
+       repository has changed over time (e.g. earlier commits record
+       text files with CRLF line endings, but recent ones use LF line
+       endings).  In such a repository, git can convert the data
+       recorded in commits to a canonical form before performing a
+       merge to reduce unnecessary conflicts.  For more information,
+       see section "Merging branches with differing checkin/checkout
+       attributes" in linkgit:gitattributes[5].
+
 merge.stat::
        Whether to print the diffstat between ORIG_HEAD and the merge result
        at the end of the merge.  True by default.
index 722d704ff2de1abd3d77a18ca396ba96d5cab6bc..e33e0f8e110879a0cd6fe81d97ee6044290e5cfa 100644 (file)
@@ -16,11 +16,11 @@ inspect and further tweak the merge result before committing.
 With --no-ff Generate a merge commit even if the merge
 resolved as a fast-forward.
 
---log::
+--log[=<n>]::
 --no-log::
        In addition to branch names, populate the log message with
-       one-line descriptions from the actual commits that are being
-       merged.
+       one-line descriptions from at most <n> actual commits that are being
+       merged. See also linkgit:git-fmt-merge-msg[1].
 +
 With --no-log do not list one-line descriptions from the
 actual commits being merged.
index a5bc1dbb95b466d0c6e37f683c886663908375e7..595a3cf1a7118ba29a1d57d7fc17d233d89cd3d0 100644 (file)
@@ -40,7 +40,45 @@ the other tree did, declaring 'our' history contains all that happened in it.
 theirs;;
        This is opposite of 'ours'.
 
-subtree[=path];;
+patience;;
+       With this option, 'merge-recursive' spends a little extra time
+       to avoid mismerges that sometimes occur due to unimportant
+       matching lines (e.g., braces from distinct functions).  Use
+       this when the branches to be merged have diverged wildly.
+       See also linkgit:git-diff[1] `--patience`.
+
+ignore-space-change;;
+ignore-all-space;;
+ignore-space-at-eol;;
+       Treats lines with the indicated type of whitespace change as
+       unchanged for the sake of a three-way merge.  Whitespace
+       changes mixed with other changes to a line are not ignored.
+       See also linkgit:git-diff[1] `-b`, `-w`, and
+       `--ignore-space-at-eol`.
++
+* If 'their' version only introduces whitespace changes to a line,
+  'our' version is used;
+* If 'our' version introduces whitespace changes but 'their'
+  version includes a substantial change, 'their' version is used;
+* Otherwise, the merge proceeds in the usual way.
+
+renormalize;;
+       This runs a virtual check-out and check-in of all three stages
+       of a file when resolving a three-way merge.  This option is
+       meant to be used when merging branches with different clean
+       filters or end-of-line normalization rules.  See "Merging
+       branches with differing checkin/checkout attributes" in
+       linkgit:gitattributes[5] for details.
+
+no-renormalize;;
+       Disables the `renormalize` option.  This overrides the
+       `merge.renormalize` configuration variable.
+
+rename-threshold=<n>;;
+       Controls the similarity threshold used for rename detection.
+       See also linkgit:git-diff[1] `-M`.
+
+subtree[=<path>];;
        This option is a more advanced form of 'subtree' strategy, where
        the strategy makes a guess on how two trees must be shifted to
        match with each other when merging.  Instead, the specified path
index 9b6f3899ec28fb431c1f3969bf5bfb717c12f346..50923e2ce9a5bdc3bcabb072a67921c6cf8171c1 100644 (file)
@@ -1,5 +1,5 @@
---pretty[='<format>']::
---format='<format>'::
+--pretty[=<format>]::
+--format=<format>::
 
        Pretty-print the contents of the commit logs in a given format,
        where '<format>' can be one of 'oneline', 'short', 'medium',
index cc562a057af3694d3518bc91b52ae322b6b1d9cc..44a2ef1de15bdce5d67a151a1cb6150031e57c7e 100644 (file)
@@ -13,7 +13,7 @@ include::pretty-options.txt[]
 
        Synonym for `--date=relative`.
 
---date={relative,local,default,iso,rfc,short,raw}::
+--date=(relative|local|default|iso|rfc|short|raw)::
 
        Only takes effect for dates shown in human-readable format, such
        as when using "--pretty". `log.date` config variable sets a default
@@ -45,13 +45,13 @@ endif::git-rev-list[]
 
 --parents::
 
-       Print the parents of the commit.  Also enables parent
-       rewriting, see 'History Simplification' below.
+       Print also the parents of the commit (in the form "commit parent...").
+       Also enables parent rewriting, see 'History Simplification' below.
 
 --children::
 
-       Print the children of the commit.  Also enables parent
-       rewriting, see 'History Simplification' below.
+       Print also the children of the commit (in the form "commit child...").
+       Also enables parent rewriting, see 'History Simplification' below.
 
 ifdef::git-rev-list[]
 --timestamp::
@@ -95,6 +95,8 @@ you would get an output like this:
        to be printed in between commits, in order for the graph history
        to be drawn properly.
 +
+This enables parent rewriting, see 'History Simplification' below.
++
 This implies the '--topo-order' option by default, but the
 '--date-order' option may also be specified.
 
@@ -146,6 +148,9 @@ options may be given. See linkgit:git-diff-files[1] for more options.
 -t::
 
        Show the tree objects in the diff output. This implies '-r'.
+
+-s::
+       Suppress diff output.
 endif::git-rev-list[]
 
 Commit Limiting
@@ -246,29 +251,29 @@ endif::git-rev-list[]
        Pretend as if all the refs in `refs/` are listed on the
        command line as '<commit>'.
 
---branches[=pattern]::
+--branches[=<pattern>]::
 
        Pretend as if all the refs in `refs/heads` are listed
-       on the command line as '<commit>'. If `pattern` is given, limit
+       on the command line as '<commit>'. If '<pattern>' is given, limit
        branches to ones matching given shell glob. If pattern lacks '?',
        '*', or '[', '/*' at the end is implied.
 
---tags[=pattern]::
+--tags[=<pattern>]::
 
        Pretend as if all the refs in `refs/tags` are listed
-       on the command line as '<commit>'. If `pattern` is given, limit
+       on the command line as '<commit>'. If '<pattern>' is given, limit
        tags to ones matching given shell glob. If pattern lacks '?', '*',
        or '[', '/*' at the end is implied.
 
---remotes[=pattern]::
+--remotes[=<pattern>]::
 
        Pretend as if all the refs in `refs/remotes` are listed
-       on the command line as '<commit>'. If `pattern`is given, limit
-       remote tracking branches to ones matching given shell glob.
+       on the command line as '<commit>'. If '<pattern>' is given, limit
+       remote-tracking branches to ones matching given shell glob.
        If pattern lacks '?', '*', or '[', '/*' at the end is implied.
 
---glob=glob-pattern::
-       Pretend as if all the refs matching shell glob `glob-pattern`
+--glob=<glob-pattern>::
+       Pretend as if all the refs matching shell glob '<glob-pattern>'
        are listed on the command line as '<commit>'. Leading 'refs/',
        is automatically prepended if missing. If pattern lacks '?', '*',
        or '[', '/*' at the end is implied.
@@ -321,7 +326,7 @@ excluded from the output.
        reflog entries from the most recent one to older ones.
        When this option is used you cannot specify commits to
        exclude (that is, '{caret}commit', 'commit1..commit2',
-       nor 'commit1...commit2' notations cannot be used).
+       nor 'commit1\...commit2' notations cannot be used).
 +
 With '\--pretty' format other than oneline (for obvious reasons),
 this causes the output to have two extra lines of information
index fe846f043ca30fdfc3bc93b2dbba75154bd38804..9e92734bc1b308c47e5f5726c704a3ddffa5beb9 100644 (file)
@@ -106,12 +106,20 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
   and dereference the tag recursively until a non-tag object is
   found.
 
+* A suffix '{caret}' to a revision parameter followed by a brace
+  pair that contains a text led by a slash (e.g. `HEAD^{/fix nasty bug}`):
+  this is the same as `:/fix nasty bug` syntax below except that
+  it returns the youngest matching commit which is reachable from
+  the ref before '{caret}'.
+
 * A colon, followed by a slash, followed by a text (e.g. `:/fix nasty bug`): this names
-  a commit whose commit message starts with the specified text.
+  a commit whose commit message matches the specified regular expression.
   This name returns the youngest matching commit which is
   reachable from any ref.  If the commit message starts with a
   '!', you have to repeat that;  the special sequence ':/!',
   followed by something else than '!' is reserved for now.
+  The regular expression can match any part of the commit message. To
+  match messages starting with a string, one can use e.g. `:/^foo`.
 
 * A suffix ':' followed by a path (e.g. `HEAD:README`); this names the blob or tree
   at the given path in the tree-ish object named by the part
@@ -119,6 +127,10 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
   ':path' (with an empty part before the colon, e.g. `:README`)
   is a special case of the syntax described next: content
   recorded in the index at the given path.
+  A path starting with './' or '../' is relative to current working directory.
+  The given path will be converted to be relative to working tree's root directory.
+  This is most useful to address a blob or tree from a commit or tree that has
+  the same tree structure with the working tree.
 
 * A colon, optionally followed by a stage number (0 to 3) and a
   colon, followed by a path (e.g. `:0:README`); this names a blob object in the
diff --git a/Documentation/technical/api-merge.txt b/Documentation/technical/api-merge.txt
new file mode 100644 (file)
index 0000000..9dc1bed
--- /dev/null
@@ -0,0 +1,104 @@
+merge API
+=========
+
+The merge API helps a program to reconcile two competing sets of
+improvements to some files (e.g., unregistered changes from the work
+tree versus changes involved in switching to a new branch), reporting
+conflicts if found.  The library called through this API is
+responsible for a few things.
+
+ * determining which trees to merge (recursive ancestor consolidation);
+
+ * lining up corresponding files in the trees to be merged (rename
+   detection, subtree shifting), reporting edge cases like add/add
+   and rename/rename conflicts to the user;
+
+ * performing a three-way merge of corresponding files, taking
+   path-specific merge drivers (specified in `.gitattributes`)
+   into account.
+
+Data structures
+---------------
+
+* `mmbuffer_t`, `mmfile_t`
+
+These store data usable for use by the xdiff backend, for writing and
+for reading, respectively.  See `xdiff/xdiff.h` for the definitions
+and `diff.c` for examples.
+
+* `struct ll_merge_options`
+
+This describes the set of options the calling program wants to affect
+the operation of a low-level (single file) merge.  Some options:
+
+`virtual_ancestor`::
+       Behave as though this were part of a merge between common
+       ancestors in a recursive merge.
+       If a helper program is specified by the
+       `[merge "<driver>"] recursive` configuration, it will
+       be used (see linkgit:gitattributes[5]).
+
+`variant`::
+       Resolve local conflicts automatically in favor
+       of one side or the other (as in 'git merge-file'
+       `--ours`/`--theirs`/`--union`).  Can be `0`,
+       `XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or
+       `XDL_MERGE_FAVOR_UNION`.
+
+`renormalize`::
+       Resmudge and clean the "base", "theirs" and "ours" files
+       before merging.  Use this when the merge is likely to have
+       overlapped with a change in smudge/clean or end-of-line
+       normalization rules.
+
+Low-level (single file) merge
+-----------------------------
+
+`ll_merge`::
+
+       Perform a three-way single-file merge in core.  This is
+       a thin wrapper around `xdl_merge` that takes the path and
+       any merge backend specified in `.gitattributes` or
+       `.git/info/attributes` into account.  Returns 0 for a
+       clean merge.
+
+Calling sequence:
+
+* Prepare a `struct ll_merge_options` to record options.
+  If you have no special requests, skip this and pass `NULL`
+  as the `opts` parameter to use the default options.
+
+* Allocate an mmbuffer_t variable for the result.
+
+* Allocate and fill variables with the file's original content
+  and two modified versions (using `read_mmfile`, for example).
+
+* Call `ll_merge()`.
+
+* Read the merged content from `result_buf.ptr` and `result_buf.size`.
+
+* Release buffers when finished.  A simple
+  `free(ancestor.ptr); free(ours.ptr); free(theirs.ptr);
+  free(result_buf.ptr);` will do.
+
+If the modifications do not merge cleanly, `ll_merge` will return a
+nonzero value and `result_buf` will generally include a description of
+the conflict bracketed by markers such as the traditional `<<<<<<<`
+and `>>>>>>>`.
+
+The `ancestor_label`, `our_label`, and `their_label` parameters are
+used to label the different sides of a conflict if the merge driver
+supports this.
+
+Everything else
+---------------
+
+Talk about <merge-recursive.h> and merge_file():
+
+ - merge_trees() to merge with rename detection
+ - merge_recursive() for ancestor consolidation
+ - try_merge_command() for other strategies
+ - conflict format
+ - merge options
+
+(Daniel, Miklos, Stephan, JC)
index 312e3b2e2ba184b6329298753e996e14fe04386e..f6a4a361bd56e1cc0f4645e09898e6852bfb8a8b 100644 (file)
@@ -118,13 +118,16 @@ There are some macros to easily define options:
 `OPT__COLOR(&int_var, description)`::
        Add `\--color[=<when>]` and `--no-color`.
 
-`OPT__DRY_RUN(&int_var)`::
+`OPT__DRY_RUN(&int_var, description)`::
        Add `-n, \--dry-run`.
 
-`OPT__QUIET(&int_var)`::
+`OPT__FORCE(&int_var, description)`::
+       Add `-f, \--force`.
+
+`OPT__QUIET(&int_var, description)`::
        Add `-q, \--quiet`.
 
-`OPT__VERBOSE(&int_var)`::
+`OPT__VERBOSE(&int_var, description)`::
        Add `-v, \--verbose`.
 
 `OPT_GROUP(description)`::
@@ -201,7 +204,7 @@ The last element of the array must be `OPT_END()`.
 If not stated otherwise, interpret the arguments as follows:
 
 * `short` is a character for the short option
-  (e.g. `\'e\'` for `-e`, use `0` to omit),
+  (e.g. `{apostrophe}e{apostrophe}` for `-e`, use `0` to omit),
 
 * `long` is a string for the long option
   (e.g. `"example"` for `\--example`, use `NULL` to omit),
@@ -228,10 +231,10 @@ The function must be defined in this form:
 The callback mechanism is as follows:
 
 * 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
+  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()`.
-  For example, do `*(unsigned long *)opt->value = 42;` to get 42
+  For example, do `*(unsigned long *)opt\->value = 42;` to get 42
   into an `unsigned long` variable.
 
 * Return value `0` indicates success and non-zero return
diff --git a/Documentation/technical/api-sigchain.txt b/Documentation/technical/api-sigchain.txt
new file mode 100644 (file)
index 0000000..9e1189e
--- /dev/null
@@ -0,0 +1,41 @@
+sigchain API
+============
+
+Code often wants to set a signal handler to clean up temporary files or
+other work-in-progress when we die unexpectedly. For multiple pieces of
+code to do this without conflicting, each piece of code must remember
+the old value of the handler and restore it either when:
+
+  1. The work-in-progress is finished, and the handler is no longer
+     necessary. The handler should revert to the original behavior
+     (either another handler, SIG_DFL, or SIG_IGN).
+
+  2. The signal is received. We should then do our cleanup, then chain
+     to the next handler (or die if it is SIG_DFL).
+
+Sigchain is a tiny library for keeping a stack of handlers. Your handler
+and installation code should look something like:
+
+------------------------------------------
+  void clean_foo_on_signal(int sig)
+  {
+         clean_foo();
+         sigchain_pop(sig);
+         raise(sig);
+  }
+
+  void other_func()
+  {
+         sigchain_push_common(clean_foo_on_signal);
+         mess_up_foo();
+         clean_foo();
+  }
+------------------------------------------
+
+Handlers are given the typedef of sigchain_fun. This is the same type
+that is given to signal() or sigaction(). It is perfectly reasonable to
+push SIG_DFL or SIG_IGN onto the stack.
+
+You can sigchain_push and sigchain_pop individual signals. For
+convenience, sigchain_push_common will push the handler onto the stack
+for many common signals.
index 55b728632cb7fa18de0d1dc070f5007acef053e0..14af37c3f14854e87d9d28f60b5ff72eb189c37f 100644 (file)
@@ -42,6 +42,8 @@ information.
 
 * `data` can be anything the `fn` callback would want to use.
 
+* `show_all_errors` tells whether to stop at the first error or not.
+
 Initializing
 ------------
 
diff --git a/Documentation/technical/index-format.txt b/Documentation/technical/index-format.txt
new file mode 100644 (file)
index 0000000..5b1d70d
--- /dev/null
@@ -0,0 +1,144 @@
+GIT index format
+================
+
+= The git index file has the following format
+
+  All binary numbers are in network byte order. Version 2 is described
+  here unless stated otherwise.
+
+   - A 12-byte header consisting of
+
+     4-byte signature:
+       The signature is { 'D', 'I', 'R', 'C' }
+
+     4-byte version number:
+       The current supported versions are 2 and 3.
+
+     32-bit number of index entries.
+
+   - A number of sorted index entries
+
+   - Extensions
+
+     Extensions are identified by signature. Optional extensions can
+     be ignored if GIT does not understand them.
+
+     GIT currently supports tree cache and resolve undo extensions.
+
+     4-byte extension signature. If the first byte is 'A'..'Z' the
+     extension is optional and can be ignored.
+
+     32-bit size of the extension
+
+     Extension data
+
+   - 160-bit SHA-1 over the content of the index file before this
+     checksum.
+
+== Index entry
+
+  Index entries are sorted in ascending order on the name field,
+  interpreted as a string of unsigned bytes. Entries with the same
+  name are sorted by their stage field.
+
+  32-bit ctime seconds, the last time a file's metadata changed
+    this is stat(2) data
+
+  32-bit ctime nanosecond fractions
+    this is stat(2) data
+
+  32-bit mtime seconds, the last time a file's data changed
+    this is stat(2) data
+
+  32-bit mtime nanosecond fractions
+    this is stat(2) data
+
+  32-bit dev
+    this is stat(2) data
+
+  32-bit ino
+    this is stat(2) data
+
+  32-bit mode, split into (high to low bits)
+
+    4-bit object type
+      valid values in binary are 1000 (blob), 1010 (symbolic link)
+      and 1110 (gitlink)
+
+    3-bit unused
+
+    9-bit unix permission (only 0755 and 0644 are valid)
+
+  32-bit uid
+    this is stat(2) data
+
+  32-bit gid
+    this is stat(2) data
+
+  32-bit file size
+    This is the on-disk size from stat(2)
+
+  160-bit SHA-1 for the represented object
+
+  A 16-bit field split into (high to low bits)
+
+    1-bit assume-valid flag
+
+    1-bit extended flag (must be zero in version 2)
+
+    2-bit stage (during merge)
+
+    12-bit name length if the length is less than 0x0FFF
+
+  (Version 3) A 16-bit field, only applicable if the "extended flag"
+  above is 1, split into (high to low bits).
+
+    1-bit reserved for future
+
+    1-bit skip-worktree flag (used by sparse checkout)
+
+    1-bit intent-to-add flag (used by "git add -N")
+
+    13-bit unused, must be zero
+
+  Entry path name (variable length) relative to top level directory
+    (without leading slash). '/' is used as path separator. The special
+    paths ".", ".." and ".git" (without quotes) are disallowed.
+    Trailing slash is also disallowed.
+
+    The exact encoding is undefined, but the '.' and '/' characters
+    are encoded in 7-bit ASCII and the encoding cannot contain a nul
+    byte. Generally a superset of ASCII.
+
+  1-8 nul bytes as necessary to pad the entry to a multiple of eight bytes
+  while keeping the name NUL-terminated.
+
+== Extensions
+
+=== Tree cache
+
+  Tree cache extension contains pre-computes hashes for all trees that
+  can be derived from the index
+
+  - Extension tag { 'T', 'R', 'E', 'E' }
+
+  - 32-bit size
+
+  - A number of entries
+
+     NUL-terminated tree name
+
+     Blank-terminated ASCII decimal number of entries in this tree
+
+     Newline-terminated position of this tree in the parent tree. 0 for
+     the root tree
+
+     160-bit SHA-1 for this tree and it's children
+
+=== Resolve undo
+
+  TODO
+
+  - Extension tag { 'R', 'E', 'U', 'C' }
+
+  - 32-bit size
index 22aee34d4a49b242370e5244f35d542ad0b79391..f13a84613198936ab238e356ec30091db4844544 100644 (file)
@@ -344,7 +344,8 @@ Examining branches from a remote repository
 The "master" branch that was created at the time you cloned is a copy
 of the HEAD in the repository that you cloned from.  That repository
 may also have had other branches, though, and your local repository
-keeps branches which track each of those remote branches, which you
+keeps branches which track each of those remote branches, called
+remote-tracking branches, which you
 can view using the "-r" option to linkgit:git-branch[1]:
 
 ------------------------------------------------
@@ -359,13 +360,23 @@ $ git branch -r
   origin/todo
 ------------------------------------------------
 
-You cannot check out these remote-tracking branches, but you can
-examine them on a branch of your own, just as you would a tag:
+In this example, "origin" is called a remote repository, or "remote"
+for short. The branches of this repository are called "remote
+branches" from our point of view. The remote-tracking branches listed
+above were created based on the remote branches at clone time and will
+be updated by "git fetch" (hence "git pull") and "git push". See
+<<Updating-a-repository-With-git-fetch>> for details.
+
+You might want to build on one of these remote-tracking branches
+on a branch of your own, just as you would for a tag:
 
 ------------------------------------------------
 $ git checkout -b my-todo-copy origin/todo
 ------------------------------------------------
 
+You can also check out "origin/todo" directly to examine it or
+write a one-off patch.  See <<detached-head,detached head>>.
+
 Note that the name "origin" is just the name that git uses by default
 to refer to the repository that you cloned from.
 
@@ -397,7 +408,7 @@ is usually a shortcut for the HEAD branch in the repository "origin".
 For the complete list of paths which git checks for references, and
 the order it uses to decide which to choose when there are multiple
 references with the same shorthand name, see the "SPECIFYING
-REVISIONS" section of linkgit:gitrevisions[1].
+REVISIONS" section of linkgit:gitrevisions[7].
 
 [[Updating-a-repository-With-git-fetch]]
 Updating a repository with git fetch
@@ -435,7 +446,7 @@ linux-nfs/master
 origin/master
 -------------------------------------------------
 
-If you run "git fetch <remote>" later, the tracking branches for the
+If you run "git fetch <remote>" later, the remote-tracking branches for the
 named <remote> will be updated.
 
 If you examine the file .git/config, you will see that git has added
@@ -568,7 +579,7 @@ We have seen several ways of naming commits already:
        - HEAD: refers to the head of the current branch
 
 There are many more; see the "SPECIFYING REVISIONS" section of the
-linkgit:gitrevisions[1] man page for the complete list of ways to
+linkgit:gitrevisions[7] man page for the complete list of ways to
 name revisions.  Some examples:
 
 -------------------------------------------------
@@ -909,7 +920,7 @@ commits reachable from some head but not from any tag in the repository:
 $ gitk $( git show-ref --heads ) --not  $( git show-ref --tags )
 -------------------------------------------------
 
-(See linkgit:gitrevisions[1] for explanations of commit-selecting
+(See linkgit:gitrevisions[7] for explanations of commit-selecting
 syntax such as `--not`.)
 
 [[making-a-release]]
@@ -955,7 +966,7 @@ echo "git diff --stat --summary -M v$last v$new > ../diffstat-$new"
 and then he just cut-and-pastes the output commands after verifying that
 they look OK.
 
-[[Finding-comments-With-given-Content]]
+[[Finding-commits-With-given-Content]]
 Finding commits referencing a file with given content
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -1635,7 +1646,7 @@ you've checked out.
 The reflogs are kept by default for 30 days, after which they may be
 pruned.  See linkgit:git-reflog[1] and linkgit:git-gc[1] to learn
 how to control this pruning, and see the "SPECIFYING REVISIONS"
-section of linkgit:gitrevisions[1] for details.
+section of linkgit:gitrevisions[7] for details.
 
 Note that the reflog history is very different from normal git history.
 While normal history is shared by every repository that works on the
@@ -1700,7 +1711,7 @@ may wish to check the original repository for updates and merge them
 into your own work.
 
 We have already seen <<Updating-a-repository-With-git-fetch,how to
-keep remote tracking branches up to date>> with linkgit:git-fetch[1],
+keep remote-tracking branches up to date>> with linkgit:git-fetch[1],
 and how to merge two branches.  So you can merge in changes from the
 original repository's master branch with:
 
@@ -1716,15 +1727,21 @@ one step:
 $ git pull origin master
 -------------------------------------------------
 
-In fact, if you have "master" checked out, then by default "git pull"
-merges from the HEAD branch of the origin repository.  So often you can
+In fact, if you have "master" checked out, then this branch has been
+configured by "git clone" to get changes from the HEAD branch of the
+origin repository.  So often you can
 accomplish the above with just a simple
 
 -------------------------------------------------
 $ git pull
 -------------------------------------------------
 
-More generally, a branch that is created from a remote branch will pull
+This command will fetch changes from the remote branches to your
+remote-tracking branches `origin/*`, and merge the default branch into
+the current branch.
+
+More generally, a branch that is created from a remote-tracking branch
+will pull
 by default from that branch.  See the descriptions of the
 branch.<name>.remote and branch.<name>.merge options in
 linkgit:git-config[1], and the discussion of the `--track` option in
@@ -2106,7 +2123,7 @@ $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
 $ cd work
 -------------------------------------------------
 
-Linus's tree will be stored in the remote branch named origin/master,
+Linus's tree will be stored in the remote-tracking branch named origin/master,
 and can be updated using linkgit:git-fetch[1]; you can track other
 public trees using linkgit:git-remote[1] to set up a "remote" and
 linkgit:git-fetch[1] to keep them up-to-date; see
@@ -2171,11 +2188,14 @@ $ git push mytree release
 
 Now to apply some patches from the community.  Think of a short
 snappy name for a branch to hold this patch (or related group of
-patches), and create a new branch from the current tip of Linus's
-branch:
+patches), and create a new branch from a recent stable tag of
+Linus's branch. Picking a stable base for your branch will:
+1) help you: by avoiding inclusion of unrelated and perhaps lightly
+tested changes
+2) help future bug hunters that use "git bisect" to find problems
 
 -------------------------------------------------
-$ git checkout -b speed-up-spinlocks origin
+$ git checkout -b speed-up-spinlocks v2.6.35
 -------------------------------------------------
 
 Now you apply the patch(es), run some tests, and commit the change(s).  If
@@ -2439,9 +2459,9 @@ You have performed no merges into mywork, so it is just a simple linear
 sequence of patches on top of "origin":
 
 ................................................
- o--o--o <-- origin
+ o--o--O <-- origin
         \
-         o--o--o <-- mywork
+        a--b--c <-- mywork
 ................................................
 
 Some more interesting work has been done in the upstream project, and
@@ -2797,8 +2817,8 @@ Be aware that commits that the old version of example/master pointed at
 may be lost, as we saw in the previous section.
 
 [[remote-branch-configuration]]
-Configuring remote branches
----------------------------
+Configuring remote-tracking branches
+------------------------------------
 
 We saw above that "origin" is just a shortcut to refer to the
 repository that you originally cloned from.  This information is
@@ -3850,7 +3870,7 @@ You create a commit object by giving it the tree that describes the
 state at the time of the commit, and a list of parents:
 
 -------------------------------------------------
-$ git commit-tree <tree> -p <parent> [-p <parent2> ..]
+$ git commit-tree <tree> -p <parent> [(-p <parent2>)...]
 -------------------------------------------------
 
 and then giving the reason for the commit on stdin (either through
@@ -4251,9 +4271,9 @@ Two things are interesting here:
   negative numbers in case of different errors--and 0 on success.
 
 - the variable `sha1` in the function signature of `get_sha1()` is `unsigned
-  char \*`, but is actually expected to be a pointer to `unsigned
+  char {asterisk}`, but is actually expected to be a pointer to `unsigned
   char[20]`.  This variable will contain the 160-bit SHA-1 of the given
-  commit.  Note that whenever a SHA-1 is passed as `unsigned char \*`, it
+  commit.  Note that whenever a SHA-1 is passed as `unsigned char {asterisk}`, it
   is the binary representation, as opposed to the ASCII representation in
   hex characters, which is passed as `char *`.
 
index f6d301a10f7b57edb623de8d5a78c90f4fbc71a7..92fe7a59dbf81b0a055280f9351811641ce7044a 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.2.GIT
+DEF_VER=v1.7.4
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index 59200b730ec00a63f981691f7fd37f2eacb11653..16e45f114f18d5d6964ab9fa39eee617dfa59bfc 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -67,10 +67,10 @@ Issues of note:
        - A POSIX-compliant shell is required to run many scripts needed
          for everyday use (e.g. "bisect", "pull").
 
-       - "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.
+       - "Perl" version 5.8 or later 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.
 
        - "openssl" library is used by git-imap-send to use IMAP over SSL.
          If you don't need it, use NO_OPENSSL.
@@ -122,8 +122,9 @@ Issues of note:
    Building and installing the pdf file additionally requires
    dblatex.  Version 0.2.7 with asciidoc >= 8.2.7 is known to work.
 
-   The documentation is written for AsciiDoc 7, but "make
-   ASCIIDOC8=YesPlease doc" will let you format with AsciiDoc 8.
+   The documentation is written for AsciiDoc 7, but by default
+   uses some compatibility wrappers to work on AsciiDoc 8. If you have
+   AsciiDoc 7, try "make ASCIIDOC7=YesPlease".
 
    Alternatively, pre-formatted documentation is available in
    "html" and "man" branches of the git repository itself.  For
index 1f11618cfdd8d1f680bbdcb8672960fdd7074b9a..89c72aca1dca5df15786bd165f286271549f4320 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -68,6 +68,13 @@ all::
 #
 # Define NO_MKSTEMPS if you don't have mkstemps in the C library.
 #
+# Define NO_STRTOK_R if you don't have strtok_r in the C library.
+#
+# Define NO_FNMATCH if you don't have fnmatch in the C library.
+#
+# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
+# FNM_CASEFOLD GNU extension.
+#
 # Define NO_LIBGEN_H if you don't have libgen.h.
 #
 # Define NEEDS_LIBGEN if your libgen needs -lgen when linking
@@ -160,13 +167,13 @@ all::
 # Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
 # field that counts the on-disk footprint in 512-byte blocks.
 #
-# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
+# Define ASCIIDOC7 if you want to format documentation with AsciiDoc 7
 #
 # 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 ASCIIDOC_ROFF if your DocBook XSL does not escape raw roff directives
+# (versions 1.68.1 through v1.72).
 #
 # Define GNU_ROFF if your target system uses GNU groff.  This forces
 # apostrophes to be ASCII so that cut&pasting examples to the shell
@@ -268,6 +275,7 @@ STRIP ?= strip
 #   infodir
 #   htmldir
 #   ETC_GITCONFIG (but not sysconfdir)
+#   ETC_GITATTRIBUTES
 # can be specified as a relative path some/where/else;
 # this is interpreted as relative to $(prefix) and "git" at
 # runtime figures out where they are based on the path to the executable.
@@ -286,9 +294,11 @@ htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
 sysconfdir = /etc
 ETC_GITCONFIG = $(sysconfdir)/gitconfig
+ETC_GITATTRIBUTES = $(sysconfdir)/gitattributes
 else
 sysconfdir = $(prefix)/etc
 ETC_GITCONFIG = etc/gitconfig
+ETC_GITATTRIBUTES = etc/gitattributes
 endif
 lib = lib
 # DESTDIR=
@@ -308,6 +318,7 @@ TCL_PATH = tclsh
 TCLTK_PATH = wish
 PTHREAD_LIBS = -lpthread
 PTHREAD_CFLAGS =
+GCOV = gcov
 
 export TCL_PATH TCLTK_PATH
 
@@ -387,12 +398,15 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
          git-instaweb
 
+ETAGS_TARGET = TAGS
+
 # Empty...
 EXTRA_PROGRAMS =
 
 # ... and all the rest that could be moved out of bindir to gitexecdir
 PROGRAMS += $(EXTRA_PROGRAMS)
 
+PROGRAM_OBJS += daemon.o
 PROGRAM_OBJS += fast-import.o
 PROGRAM_OBJS += imap-send.o
 PROGRAM_OBJS += shell.o
@@ -408,13 +422,20 @@ TEST_PROGRAMS_NEED_X += test-date
 TEST_PROGRAMS_NEED_X += test-delta
 TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 TEST_PROGRAMS_NEED_X += test-genrandom
+TEST_PROGRAMS_NEED_X += test-line-buffer
 TEST_PROGRAMS_NEED_X += test-match-trees
+TEST_PROGRAMS_NEED_X += test-obj-pool
 TEST_PROGRAMS_NEED_X += test-parse-options
 TEST_PROGRAMS_NEED_X += test-path-utils
 TEST_PROGRAMS_NEED_X += test-run-command
 TEST_PROGRAMS_NEED_X += test-sha1
 TEST_PROGRAMS_NEED_X += test-sigchain
+TEST_PROGRAMS_NEED_X += test-string-pool
+TEST_PROGRAMS_NEED_X += test-subprocess
+TEST_PROGRAMS_NEED_X += test-svn-fe
+TEST_PROGRAMS_NEED_X += test-treap
 TEST_PROGRAMS_NEED_X += test-index-version
+TEST_PROGRAMS_NEED_X += test-mktemp
 
 TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
 
@@ -468,6 +489,7 @@ export PYTHON_PATH
 
 LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
+VCSSVN_LIB=vcs-svn/lib.a
 
 LIB_H += advice.h
 LIB_H += archive.h
@@ -482,6 +504,9 @@ LIB_H += compat/bswap.h
 LIB_H += compat/cygwin.h
 LIB_H += compat/mingw.h
 LIB_H += compat/win32/pthread.h
+LIB_H += compat/win32/syslog.h
+LIB_H += compat/win32/sys/poll.h
+LIB_H += compat/win32/dirent.h
 LIB_H += csum-file.h
 LIB_H += decorate.h
 LIB_H += delta.h
@@ -503,6 +528,7 @@ LIB_H += mailmap.h
 LIB_H += merge-recursive.h
 LIB_H += notes.h
 LIB_H += notes-cache.h
+LIB_H += notes-merge.h
 LIB_H += object.h
 LIB_H += pack.h
 LIB_H += pack-refs.h
@@ -593,6 +619,7 @@ LIB_OBJS += merge-recursive.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
+LIB_OBJS += notes-merge.o
 LIB_OBJS += object.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-refs.o
@@ -648,6 +675,7 @@ LIB_OBJS += write_or_die.o
 LIB_OBJS += ws.o
 LIB_OBJS += wt-status.o
 LIB_OBJS += xdiff-interface.o
+LIB_OBJS += zlib.o
 
 BUILTIN_OBJS += builtin/add.o
 BUILTIN_OBJS += builtin/annotate.o
@@ -714,6 +742,8 @@ 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/remote-ext.o
+BUILTIN_OBJS += builtin/remote-fd.o
 BUILTIN_OBJS += builtin/replace.o
 BUILTIN_OBJS += builtin/rerere.o
 BUILTIN_OBJS += builtin/reset.o
@@ -725,6 +755,7 @@ BUILTIN_OBJS += builtin/send-pack.o
 BUILTIN_OBJS += builtin/shortlog.o
 BUILTIN_OBJS += builtin/show-branch.o
 BUILTIN_OBJS += builtin/show-ref.o
+BUILTIN_OBJS += builtin/skew.o
 BUILTIN_OBJS += builtin/stripspace.o
 BUILTIN_OBJS += builtin/symbolic-ref.o
 BUILTIN_OBJS += builtin/tag.o
@@ -832,6 +863,7 @@ ifeq ($(uname_S),SunOS)
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
        NO_REGEX = YesPlease
+       NO_FNMATCH_CASEFOLD = YesPlease
        ifeq ($(uname_R),5.6)
                SOCKLEN_T = int
                NO_HSTRERROR = YesPlease
@@ -973,6 +1005,8 @@ ifeq ($(uname_S),IRIX)
        # NO_MMAP.  If you suspect that your compiler is not affected by this
        # issue, comment out the NO_MMAP statement.
        NO_MMAP = YesPlease
+       NO_REGEX = YesPlease
+       NO_FNMATCH_CASEFOLD = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH = /usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
@@ -991,6 +1025,8 @@ ifeq ($(uname_S),IRIX64)
        # NO_MMAP.  If you suspect that your compiler is not affected by this
        # issue, comment out the NO_MMAP statement.
        NO_MMAP = YesPlease
+       NO_REGEX = YesPlease
+       NO_FNMATCH_CASEFOLD = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
@@ -1035,6 +1071,8 @@ ifeq ($(uname_S),Windows)
        NO_UNSETENV = YesPlease
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
+       NO_STRTOK_R = YesPlease
+       NO_FNMATCH = YesPlease
        NO_MEMMEM = YesPlease
        # NEEDS_LIBICONV = YesPlease
        NO_ICONV = YesPlease
@@ -1047,7 +1085,6 @@ ifeq ($(uname_S),Windows)
        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
@@ -1058,16 +1095,19 @@ ifeq ($(uname_S),Windows)
        NO_CURL = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
+       NO_POSIX_GOODIES = UnfortunatelyYes
        NATIVE_CRLF = 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/win32/pthread.o
-       COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
+       COMPAT_OBJS = compat/msvc.o compat/winansi.o \
+               compat/win32/pthread.o compat/win32/syslog.o \
+               compat/win32/sys/poll.o compat/win32/dirent.o
+       COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -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
+       EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib
        PTHREAD_LIBS =
        lib =
 ifndef DEBUG
@@ -1079,6 +1119,25 @@ else
 endif
        X = .exe
 endif
+ifeq ($(uname_S),Interix)
+       NO_SYS_POLL_H = YesPlease
+       NO_INTTYPES_H = YesPlease
+       NO_INITGROUPS = YesPlease
+       NO_IPV6 = YesPlease
+       NO_MEMMEM = YesPlease
+       NO_MKDTEMP = YesPlease
+       NO_STRTOUMAX = YesPlease
+       NO_NSEC = YesPlease
+       NO_MKSTEMPS = YesPlease
+       ifeq ($(uname_R),3.5)
+               NO_INET_NTOP = YesPlease
+               NO_INET_PTON = YesPlease
+       endif
+       ifeq ($(uname_R),5.2)
+               NO_INET_NTOP = YesPlease
+               NO_INET_PTON = YesPlease
+       endif
+endif
 ifneq (,$(findstring MINGW,$(uname_S)))
        pathsep = ;
        NO_PREAD = YesPlease
@@ -1089,6 +1148,8 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_UNSETENV = YesPlease
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
+       NO_STRTOK_R = YesPlease
+       NO_FNMATCH = YesPlease
        NO_MEMMEM = YesPlease
        NEEDS_LIBICONV = YesPlease
        OLD_ICONV = YesPlease
@@ -1099,7 +1160,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        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
@@ -1109,10 +1169,15 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_REGEX = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
-       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch -Icompat/win32
+       ETAGS_TARGET = ETAGS
+       NO_INET_PTON = YesPlease
+       NO_INET_NTOP = YesPlease
+       NO_POSIX_GOODIES = UnfortunatelyYes
+       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/win32
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
-       COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
-               compat/win32/pthread.o
+       COMPAT_OBJS += compat/mingw.o compat/winansi.o \
+               compat/win32/pthread.o compat/win32/syslog.o \
+               compat/win32/sys/poll.o compat/win32/dirent.o
        EXTLIBS += -lws2_32
        PTHREAD_LIBS =
        X = .exe
@@ -1227,9 +1292,6 @@ ifdef ZLIB_PATH
 endif
 EXTLIBS += -lz
 
-ifndef NO_POSIX_ONLY_PROGRAMS
-       PROGRAM_OBJS += daemon.o
-endif
 ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
        ifdef OPENSSLDIR
@@ -1246,11 +1308,15 @@ else
        BLK_SHA1 = 1
        OPENSSL_LIBSSL =
 endif
+ifdef NO_OPENSSL
+       LIB_4_CRYPTO =
+else
 ifdef NEEDS_SSL_WITH_CRYPTO
        LIB_4_CRYPTO = $(OPENSSL_LINK) -lcrypto -lssl
 else
        LIB_4_CRYPTO = $(OPENSSL_LINK) -lcrypto
 endif
+endif
 ifdef NEEDS_LIBICONV
        ifdef ICONVDIR
                BASIC_CFLAGS += -I$(ICONVDIR)/include
@@ -1319,6 +1385,21 @@ endif
 ifdef NO_STRTOULL
        COMPAT_CFLAGS += -DNO_STRTOULL
 endif
+ifdef NO_STRTOK_R
+       COMPAT_CFLAGS += -DNO_STRTOK_R
+       COMPAT_OBJS += compat/strtok_r.o
+endif
+ifdef NO_FNMATCH
+       COMPAT_CFLAGS += -Icompat/fnmatch
+       COMPAT_CFLAGS += -DNO_FNMATCH
+       COMPAT_OBJS += compat/fnmatch/fnmatch.o
+else
+ifdef NO_FNMATCH_CASEFOLD
+       COMPAT_CFLAGS += -Icompat/fnmatch
+       COMPAT_CFLAGS += -DNO_FNMATCH_CASEFOLD
+       COMPAT_OBJS += compat/fnmatch/fnmatch.o
+endif
+endif
 ifdef NO_SETENV
        COMPAT_CFLAGS += -DNO_SETENV
        COMPAT_OBJS += compat/setenv.o
@@ -1337,6 +1418,15 @@ endif
 ifdef NO_SYS_SELECT_H
        BASIC_CFLAGS += -DNO_SYS_SELECT_H
 endif
+ifdef NO_SYS_POLL_H
+       BASIC_CFLAGS += -DNO_SYS_POLL_H
+endif
+ifdef NO_INTTYPES_H
+       BASIC_CFLAGS += -DNO_INTTYPES_H
+endif
+ifdef NO_INITGROUPS
+       BASIC_CFLAGS += -DNO_INITGROUPS
+endif
 ifdef NO_MMAP
        COMPAT_CFLAGS += -DNO_MMAP
        COMPAT_OBJS += compat/mmap.o
@@ -1374,9 +1464,11 @@ endif
 endif
 ifdef NO_INET_NTOP
        LIB_OBJS += compat/inet_ntop.o
+       BASIC_CFLAGS += -DNO_INET_NTOP
 endif
 ifdef NO_INET_PTON
        LIB_OBJS += compat/inet_pton.o
+       BASIC_CFLAGS += -DNO_INET_PTON
 endif
 
 ifdef NO_ICONV
@@ -1391,6 +1483,10 @@ ifdef NO_DEFLATE_BOUND
        BASIC_CFLAGS += -DNO_DEFLATE_BOUND
 endif
 
+ifdef NO_POSIX_GOODIES
+       BASIC_CFLAGS += -DNO_POSIX_GOODIES
+endif
+
 ifdef BLK_SHA1
        SHA1_HEADER = "block-sha1/sha1.h"
        LIB_OBJS += block-sha1/sha1.o
@@ -1448,7 +1544,7 @@ ifdef NO_REGEX
 endif
 
 ifdef USE_NED_ALLOCATOR
-       COMPAT_CFLAGS += -DUSE_NED_ALLOCATOR -DOVERRIDE_STRDUP -DNDEBUG -DREPLACE_SYSTEM_ALLOCATOR -Icompat/nedmalloc
+       COMPAT_CFLAGS += -Icompat/nedmalloc
        COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
 endif
 
@@ -1485,6 +1581,7 @@ ifndef V
        QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
        QUIET_GEN      = @echo '   ' GEN $@;
        QUIET_LNCP     = @echo '   ' LN/CP $@;
+       QUIET_GCOV     = @echo '   ' GCOV $@;
        QUIET_SUBDIR0  = +@subdir=
        QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
                         $(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1494,14 +1591,15 @@ ifndef V
 endif
 endif
 
-ifdef ASCIIDOC8
-       export ASCIIDOC8
+ifdef ASCIIDOC7
+       export ASCIIDOC7
 endif
 
 # Shell quote (do not use $(call) to accommodate ancient setups);
 
 SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
 ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
+ETC_GITATTRIBUTES_SQ = $(subst ','\'',$(ETC_GITATTRIBUTES))
 
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
@@ -1586,6 +1684,8 @@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
 
+help.o: common-cmds.h
+
 builtin/help.o: common-cmds.h
 builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
        '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
@@ -1739,7 +1839,11 @@ ifndef NO_CURL
 endif
 XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
        xdiff/xmerge.o xdiff/xpatience.o
-OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS)
+VCSSVN_OBJS = vcs-svn/string_pool.o vcs-svn/line_buffer.o \
+       vcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/svndump.o
+VCSSVN_TEST_OBJS = test-obj-pool.o test-string-pool.o \
+       test-line-buffer.o test-treap.o
+OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $(VCSSVN_OBJS)
 
 dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)
 dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))
@@ -1847,20 +1951,26 @@ builtin/branch.o builtin/checkout.o builtin/clone.o builtin/reset.o branch.o tra
 builtin/bundle.o bundle.o transport.o: bundle.h
 builtin/bisect--helper.o builtin/rev-list.o bisect.o: bisect.h
 builtin/clone.o builtin/fetch-pack.o transport.o: fetch-pack.h
-builtin/grep.o: thread-utils.h
+builtin/grep.o builtin/pack-objects.o transport-helper.o: thread-utils.h
 builtin/send-pack.o transport.o: send-pack.h
 builtin/log.o builtin/shortlog.o: shortlog.h
 builtin/prune.o builtin/reflog.o reachable.o: reachable.h
 builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
 builtin/tar-tree.o archive-tar.o: tar.h
-builtin/pack-objects.o: thread-utils.h
 connect.o transport.o http-backend.o: url.h
 http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
-http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h
+http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
 
 xdiff-interface.o $(XDIFF_OBJS): \
        xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
        xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
+
+$(VCSSVN_OBJS) $(VCSSVN_TEST_OBJS): $(LIB_H) \
+       vcs-svn/obj_pool.h vcs-svn/trp.h vcs-svn/string_pool.h \
+       vcs-svn/line_buffer.h vcs-svn/repo_tree.h vcs-svn/fast_export.h \
+       vcs-svn/svndump.h
+
+test-svn-fe.o: vcs-svn/svndump.h
 endif
 
 exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
@@ -1873,18 +1983,29 @@ builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
 
 config.s config.o: EXTRA_CPPFLAGS = -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
 
-http.s http.o: EXTRA_CPPFLAGS = -DGIT_USER_AGENT='"git/$(GIT_VERSION)"'
+attr.s attr.o: EXTRA_CPPFLAGS = -DETC_GITATTRIBUTES='"$(ETC_GITATTRIBUTES_SQ)"'
+
+http.s http.o: EXTRA_CPPFLAGS = -DGIT_HTTP_USER_AGENT='"git/$(GIT_VERSION)"'
 
 ifdef NO_EXPAT
 http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
 endif
 
+ifdef NO_REGEX
+compat/regex/regex.o: EXTRA_CPPFLAGS = -DGAWK -DNO_MBSUPPORT
+endif
+
+ifdef USE_NED_ALLOCATOR
+compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
+       -DNDEBUG -DOVERRIDE_STRDUP -DREPLACE_SYSTEM_ALLOCATOR
+endif
+
 git-%$X: %.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
 git-imap-send$X: imap-send.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-               $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
+               $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
 
 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,$^) \
@@ -1909,6 +2030,8 @@ $(LIB_FILE): $(LIB_OBJS)
 $(XDIFF_LIB): $(XDIFF_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
 
+$(VCSSVN_LIB): $(VCSSVN_OBJS)
+       $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(VCSSVN_OBJS)
 
 doc:
        $(MAKE) -C Documentation all
@@ -1925,11 +2048,11 @@ info:
 pdf:
        $(MAKE) -C Documentation pdf
 
-TAGS:
-       $(RM) TAGS
-       $(FIND) . -name '*.[hcS]' -print | xargs etags -a
+$(ETAGS_TARGET): FORCE
+       $(RM) $(ETAGS_TARGET)
+       $(FIND) . -name '*.[hcS]' -print | xargs etags -a -o $(ETAGS_TARGET)
 
-tags:
+tags: FORCE
        $(RM) tags
        $(FIND) . -name '*.[hcS]' -print | xargs ctags -a
 
@@ -1938,7 +2061,7 @@ cscope:
        $(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 ### Detect prefix changes
-TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
+TRACK_CFLAGS = $(CC):$(subst ','\'',$(ALL_CFLAGS)):\
              $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
 
 GIT-CFLAGS: FORCE
@@ -2007,12 +2130,18 @@ test-date$X: date.o ctype.o
 
 test-delta$X: diff-delta.o patch-delta.o
 
+test-line-buffer$X: vcs-svn/lib.a
+
 test-parse-options$X: parse-options.o
 
+test-string-pool$X: vcs-svn/lib.a
+
+test-svn-fe$X: vcs-svn/lib.a
+
 .PRECIOUS: $(TEST_OBJS)
 
 test-%$X: test-%.o $(GITLIBS)
-       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
 check-sha1:: test-sha1$X
        ./test-sha1.sh
@@ -2187,13 +2316,13 @@ distclean: clean
        $(RM) configure
 
 clean:
-       $(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
-               builtin/*.o $(LIB_FILE) $(XDIFF_LIB)
+       $(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
+               builtin/*.o $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
        $(RM) -r bin-wrappers
        $(RM) -r $(dep_dirs)
-       $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
+       $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
        $(RM) -r autom4te.cache
        $(RM) config.log config.mak.autogen config.mak.append config.status config.cache
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
@@ -2217,7 +2346,7 @@ endif
 
 .PHONY: all install clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
-.PHONY: FORCE TAGS tags cscope
+.PHONY: FORCE cscope
 
 ### Check documentation
 #
@@ -2291,11 +2420,18 @@ coverage:
        $(MAKE) coverage-build
        $(MAKE) coverage-report
 
+object_dirs := $(sort $(dir $(OBJECTS)))
 coverage-clean:
-       rm -f *.gcda *.gcno
+       $(RM) $(addsuffix *.gcov,$(object_dirs))
+       $(RM) $(addsuffix *.gcda,$(object_dirs))
+       $(RM) $(addsuffix *.gcno,$(object_dirs))
+       $(RM) coverage-untested-functions
+       $(RM) -r cover_db/
+       $(RM) -r cover_db_html/
 
 COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs
 COVERAGE_LDFLAGS = $(CFLAGS)  -O0 -lgcov
+GCOVFLAGS = --preserve-paths --branch-probabilities --all-blocks
 
 coverage-build: coverage-clean
        $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all
@@ -2303,7 +2439,17 @@ coverage-build: coverage-clean
                -j1 test
 
 coverage-report:
-       gcov -b *.c
+       $(QUIET_GCOV)for dir in $(object_dirs); do \
+               $(GCOV) $(GCOVFLAGS) --object-directory=$$dir $$dir*.c || exit; \
+       done
+
+coverage-untested-functions: coverage-report
        grep '^function.*called 0 ' *.c.gcov \
                | sed -e 's/\([^:]*\)\.gcov: *function \([^ ]*\) called.*/\1: \2/' \
-               | tee coverage-untested-functions
+               > coverage-untested-functions
+
+cover_db: coverage-report
+       gcov2perl -db cover_db *.gcov
+
+cover_db_html: cover_db
+       cover -report html -outputdir cover_db_html cover_db
index 028ad79e1f7226088102667085ca5ceb43c4d442..b942e499449d97aeb50c73ca2bdc1c6e6d528743 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.7.2.2.txt
\ No newline at end of file
+Documentation/RelNotes/1.7.4.txt
\ No newline at end of file
index c91a29cb298a3ad792ff8745f3e8e0eb28d71678..91ca00f05f7d648fa801a36b78c749f9d691ba43 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -108,10 +108,14 @@ const char *make_nonrelative_path(const char *path)
                if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
                        die("Too long path: %.*s", 60, path);
        } else {
+               size_t len;
+               const char *fmt;
                const char *cwd = get_pwd_cwd();
                if (!cwd)
                        die_errno("Cannot determine the current working directory");
-               if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
+               len = strlen(cwd);
+               fmt = (len > 0 && is_dir_sep(cwd[len-1])) ? "%s%s" : "%s/%s";
+               if (snprintf(buf, PATH_MAX, fmt, cwd, path) >= PATH_MAX)
                        die("Too long path: %.*s", 60, path);
        }
        return buf;
diff --git a/alias.c b/alias.c
index 372b7d809301f9e3e936459e405cd6f2627bd4a9..eb9f08b912b2089d434926fc6083ac5ab90c73bc 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -22,6 +22,13 @@ char *alias_lookup(const char *alias)
        return alias_val;
 }
 
+#define SPLIT_CMDLINE_BAD_ENDING 1
+#define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
+static const char *split_cmdline_errors[] = {
+       "cmdline ends with \\",
+       "unclosed quote"
+};
+
 int split_cmdline(char *cmdline, const char ***argv)
 {
        int src, dst, count = 0, size = 16;
@@ -53,7 +60,7 @@ int split_cmdline(char *cmdline, const char ***argv)
                                if (!c) {
                                        free(*argv);
                                        *argv = NULL;
-                                       return error("cmdline ends with \\");
+                                       return -SPLIT_CMDLINE_BAD_ENDING;
                                }
                        }
                        cmdline[dst++] = c;
@@ -66,7 +73,7 @@ int split_cmdline(char *cmdline, const char ***argv)
        if (quoted) {
                free(*argv);
                *argv = NULL;
-               return error("unclosed quote");
+               return -SPLIT_CMDLINE_UNCLOSED_QUOTE;
        }
 
        ALLOC_GROW(*argv, count+1, size);
@@ -75,3 +82,6 @@ int split_cmdline(char *cmdline, const char ***argv)
        return count;
 }
 
+const char *split_cmdline_strerror(int split_cmdline_errno) {
+       return split_cmdline_errors[-split_cmdline_errno-1];
+}
index edd68534fa847ad839fe8554e6864b39de4fcde0..1944ed4e4dc36addaf6a8b408703a7ff418bd26c 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -7,9 +7,9 @@
 #include "unpack-trees.h"
 
 static char const * const archive_usage[] = {
-       "git archive [options] <tree-ish> [path...]",
+       "git archive [options] <tree-ish> [<path>...]",
        "git archive --list",
-       "git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [path...]",
+       "git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]",
        "git archive --remote <repo> [--exec <cmd>] --list",
        NULL
 };
@@ -314,7 +314,7 @@ static int parse_archive_args(int argc, const char **argv,
                        "write the archive to this file"),
                OPT_BOOLEAN(0, "worktree-attributes", &worktree_attributes,
                        "read .gitattributes in working directory"),
-               OPT__VERBOSE(&verbose),
+               OPT__VERBOSE(&verbose, "report archived files on stderr"),
                OPT__COMPR('0', &compression_level, "store only", 0),
                OPT__COMPR('1', &compression_level, "compress faster", 1),
                OPT__COMPR_HIDDEN('2', &compression_level, 2),
diff --git a/attr.c b/attr.c
index 8ba606c933088e27ac08aabb546b764745f8187e..6aff6951d38d7f672aeb9851eca88c6e5a26b950 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -1,5 +1,6 @@
 #define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
+#include "exec_cmd.h"
 #include "attr.h"
 
 const char git_attr__true[] = "(builtin)true";
@@ -10,6 +11,8 @@ static const char git_attr__unknown[] = "(builtin)unknown";
 #define ATTR__UNSET NULL
 #define ATTR__UNKNOWN git_attr__unknown
 
+static const char *attributes_file;
+
 /*
  * The basic design decision here is that we are not going to have
  * insanely large number of attributes.
@@ -462,6 +465,32 @@ static void drop_attr_stack(void)
        }
 }
 
+const char *git_etc_gitattributes(void)
+{
+       static const char *system_wide;
+       if (!system_wide)
+               system_wide = system_path(ETC_GITATTRIBUTES);
+       return system_wide;
+}
+
+int git_attr_system(void)
+{
+       return !git_env_bool("GIT_ATTR_NOSYSTEM", 0);
+}
+
+int git_attr_global(void)
+{
+       return !git_env_bool("GIT_ATTR_NOGLOBAL", 0);
+}
+
+static int git_attr_config(const char *var, const char *value, void *dummy)
+{
+       if (!strcmp(var, "core.attributesfile"))
+               return git_config_pathname(&attributes_file, var, value);
+
+       return 0;
+}
+
 static void bootstrap_attr_stack(void)
 {
        if (!attr_stack) {
@@ -472,6 +501,25 @@ static void bootstrap_attr_stack(void)
                elem->prev = attr_stack;
                attr_stack = elem;
 
+               if (git_attr_system()) {
+                       elem = read_attr_from_file(git_etc_gitattributes(), 1);
+                       if (elem) {
+                               elem->origin = NULL;
+                               elem->prev = attr_stack;
+                               attr_stack = elem;
+                       }
+               }
+
+               git_config(git_attr_config, NULL);
+               if (git_attr_global() && attributes_file) {
+                       elem = read_attr_from_file(attributes_file, 1);
+                       if (elem) {
+                               elem->origin = NULL;
+                               elem->prev = attr_stack;
+                               attr_stack = elem;
+                       }
+               }
+
                if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
                        elem = read_attr(GITATTRIBUTES_FILE, 1);
                        elem->origin = strdup("");
@@ -499,7 +547,9 @@ static void prepare_attr_stack(const char *path, int dirlen)
 
        /*
         * At the bottom of the attribute stack is the built-in
-        * set of attribute definitions.  Then, contents from
+        * set of attribute definitions, followed by the contents
+        * of $(prefix)/etc/gitattributes and a file specified by
+        * core.attributesfile.  Then, contents from
         * .gitattribute files from directories closer to the
         * root to the ones in deeper directories are pushed
         * to the stack.  Finally, at the very top of the stack
index eed817a64c7620bfe67f395e39b4eef2f85a4ab3..4026e3832b265c4cef6e5bc151976771867b3da9 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -22,8 +22,8 @@ void create_branch(const char *head, const char *name, const char *start_name,
 void remove_branch_state(void);
 
 /*
- * Configure local branch "local" to merge remote branch "remote"
- * taken from origin "origin".
+ * Configure local branch "local" as downstream to branch "remote"
+ * from remote "origin".  Used by git branch --set-upstream.
  */
 #define BRANCH_CONFIG_VERBOSE 01
 extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote);
index ed6ee26933430e1db4e29e62badfaf0b217935ad..68eec3484eb222c1d1b52fd72bd17160fb95ac5a 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -7,17 +7,16 @@
 #include "commit.h"
 #include "notes.h"
 
+#define DEFAULT_MERGE_LOG_LEN 20
+
 extern const char git_version_string[];
 extern const char git_usage_string[];
 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 fmt_merge_msg(int merge_summary, struct strbuf *in,
-       struct strbuf *out);
-extern int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out);
-extern int commit_notes(struct notes_tree *t, const char *msg);
+extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
+                        int merge_title, int shortlog_len);
+extern void commit_notes(struct notes_tree *t, const char *msg);
 
 struct notes_rewrite_cfg {
        struct notes_tree **trees;
@@ -37,7 +36,7 @@ void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
 
 extern int check_pager_config(const char *cmd);
 
-extern int textconv_object(const char *path, const unsigned char *sha1, char **buf, unsigned long *buf_size);
+extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *buf_size);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
@@ -109,6 +108,8 @@ extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
+extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);
+extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 extern int cmd_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
@@ -141,5 +142,6 @@ 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);
+extern int cmd_skew(int argc, const char **argv, const char *prefix);
 
 #endif
index 56a4e0af6b35ea9f6d86accad27a1c87bc5ad0d9..5f817ad77f1b3ba247c3b76d1d9a50f7fcc5edee 100644 (file)
@@ -86,7 +86,7 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
        struct rev_info rev;
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
-       rev.prune_data = pathspec;
+       init_pathspec(&rev.prune_data, pathspec);
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
        data.flags = flags;
@@ -313,13 +313,13 @@ static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
 static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0;
 
 static struct option builtin_add_options[] = {
-       OPT__DRY_RUN(&show_only),
-       OPT__VERBOSE(&verbose),
+       OPT__DRY_RUN(&show_only, "dry run"),
+       OPT__VERBOSE(&verbose, "be verbose"),
        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__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"),
        OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
@@ -331,7 +331,8 @@ static struct option builtin_add_options[] = {
 
 static int add_config(const char *var, const char *value, void *cb)
 {
-       if (!strcasecmp(var, "add.ignore-errors")) {
+       if (!strcasecmp(var, "add.ignoreerrors") ||
+           !strcasecmp(var, "add.ignore-errors")) {
                ignore_add_errors = git_config_bool(var, value);
                return 0;
        }
@@ -446,7 +447,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                        if (!seen[i] && pathspec[i][0]
                            && !file_exists(pathspec[i])) {
                                if (ignore_missing) {
-                                       if (excluded(&dir, pathspec[i], DT_UNKNOWN))
+                                       int dtype = DT_UNKNOWN;
+                                       if (excluded(&dir, pathspec[i], &dtype))
                                                dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
                                } else
                                        die("pathspec '%s' did not match any files",
index f38c1f7b88fafa18550762685e3bb5b15bfa701f..14951daedffa9e8a8a913eee5e8423220e86e291 100644 (file)
@@ -416,48 +416,210 @@ static char *squash_slash(char *name)
        return name;
 }
 
-static char *find_name(const char *line, char *def, int p_value, int terminate)
+static char *find_name_gnu(const char *line, char *def, int p_value)
 {
-       int len;
-       const char *start = NULL;
+       struct strbuf name = STRBUF_INIT;
+       char *cp;
 
-       if (p_value == 0)
-               start = line;
+       /*
+        * Proposed "new-style" GNU patch/diff format; see
+        * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
+        */
+       if (unquote_c_style(&name, line, NULL)) {
+               strbuf_release(&name);
+               return NULL;
+       }
 
-       if (*line == '"') {
-               struct strbuf name = STRBUF_INIT;
+       for (cp = name.buf; p_value; p_value--) {
+               cp = strchr(cp, '/');
+               if (!cp) {
+                       strbuf_release(&name);
+                       return NULL;
+               }
+               cp++;
+       }
 
-               /*
-                * Proposed "new-style" GNU patch/diff format; see
-                * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
-                */
-               if (!unquote_c_style(&name, line, NULL)) {
-                       char *cp;
+       /* name can later be freed, so we need
+        * to memmove, not just return cp
+        */
+       strbuf_remove(&name, 0, cp - name.buf);
+       free(def);
+       if (root)
+               strbuf_insert(&name, 0, root, root_len);
+       return squash_slash(strbuf_detach(&name, NULL));
+}
 
-                       for (cp = name.buf; p_value; p_value--) {
-                               cp = strchr(cp, '/');
-                               if (!cp)
-                                       break;
-                               cp++;
-                       }
-                       if (cp) {
-                               /* name can later be freed, so we need
-                                * to memmove, not just return cp
-                                */
-                               strbuf_remove(&name, 0, cp - name.buf);
-                               free(def);
-                               if (root)
-                                       strbuf_insert(&name, 0, root, root_len);
-                               return squash_slash(strbuf_detach(&name, NULL));
-                       }
-               }
-               strbuf_release(&name);
+static size_t sane_tz_len(const char *line, size_t len)
+{
+       const char *tz, *p;
+
+       if (len < strlen(" +0500") || line[len-strlen(" +0500")] != ' ')
+               return 0;
+       tz = line + len - strlen(" +0500");
+
+       if (tz[1] != '+' && tz[1] != '-')
+               return 0;
+
+       for (p = tz + 2; p != line + len; p++)
+               if (!isdigit(*p))
+                       return 0;
+
+       return line + len - tz;
+}
+
+static size_t tz_with_colon_len(const char *line, size_t len)
+{
+       const char *tz, *p;
+
+       if (len < strlen(" +08:00") || line[len - strlen(":00")] != ':')
+               return 0;
+       tz = line + len - strlen(" +08:00");
+
+       if (tz[0] != ' ' || (tz[1] != '+' && tz[1] != '-'))
+               return 0;
+       p = tz + 2;
+       if (!isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
+           !isdigit(*p++) || !isdigit(*p++))
+               return 0;
+
+       return line + len - tz;
+}
+
+static size_t date_len(const char *line, size_t len)
+{
+       const char *date, *p;
+
+       if (len < strlen("72-02-05") || line[len-strlen("-05")] != '-')
+               return 0;
+       p = date = line + len - strlen("72-02-05");
+
+       if (!isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||
+           !isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||
+           !isdigit(*p++) || !isdigit(*p++))   /* Not a date. */
+               return 0;
+
+       if (date - line >= strlen("19") &&
+           isdigit(date[-1]) && isdigit(date[-2]))     /* 4-digit year */
+               date -= strlen("19");
+
+       return line + len - date;
+}
+
+static size_t short_time_len(const char *line, size_t len)
+{
+       const char *time, *p;
+
+       if (len < strlen(" 07:01:32") || line[len-strlen(":32")] != ':')
+               return 0;
+       p = time = line + len - strlen(" 07:01:32");
+
+       /* Permit 1-digit hours? */
+       if (*p++ != ' ' ||
+           !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
+           !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
+           !isdigit(*p++) || !isdigit(*p++))   /* Not a time. */
+               return 0;
+
+       return line + len - time;
+}
+
+static size_t fractional_time_len(const char *line, size_t len)
+{
+       const char *p;
+       size_t n;
+
+       /* Expected format: 19:41:17.620000023 */
+       if (!len || !isdigit(line[len - 1]))
+               return 0;
+       p = line + len - 1;
+
+       /* Fractional seconds. */
+       while (p > line && isdigit(*p))
+               p--;
+       if (*p != '.')
+               return 0;
+
+       /* Hours, minutes, and whole seconds. */
+       n = short_time_len(line, p - line);
+       if (!n)
+               return 0;
+
+       return line + len - p + n;
+}
+
+static size_t trailing_spaces_len(const char *line, size_t len)
+{
+       const char *p;
+
+       /* Expected format: ' ' x (1 or more)  */
+       if (!len || line[len - 1] != ' ')
+               return 0;
+
+       p = line + len;
+       while (p != line) {
+               p--;
+               if (*p != ' ')
+                       return line + len - (p + 1);
        }
 
-       for (;;) {
+       /* All spaces! */
+       return len;
+}
+
+static size_t diff_timestamp_len(const char *line, size_t len)
+{
+       const char *end = line + len;
+       size_t n;
+
+       /*
+        * Posix: 2010-07-05 19:41:17
+        * GNU: 2010-07-05 19:41:17.620000023 -0500
+        */
+
+       if (!isdigit(end[-1]))
+               return 0;
+
+       n = sane_tz_len(line, end - line);
+       if (!n)
+               n = tz_with_colon_len(line, end - line);
+       end -= n;
+
+       n = short_time_len(line, end - line);
+       if (!n)
+               n = fractional_time_len(line, end - line);
+       end -= n;
+
+       n = date_len(line, end - line);
+       if (!n) /* No date.  Too bad. */
+               return 0;
+       end -= n;
+
+       if (end == line)        /* No space before date. */
+               return 0;
+       if (end[-1] == '\t') {  /* Success! */
+               end--;
+               return line + len - end;
+       }
+       if (end[-1] != ' ')     /* No space before date. */
+               return 0;
+
+       /* Whitespace damage. */
+       end -= trailing_spaces_len(line, end - line);
+       return line + len - end;
+}
+
+static char *find_name_common(const char *line, char *def, int p_value,
+                               const char *end, int terminate)
+{
+       int len;
+       const char *start = NULL;
+
+       if (p_value == 0)
+               start = line;
+       while (line != end) {
                char c = *line;
 
-               if (isspace(c)) {
+               if (!end && isspace(c)) {
                        if (c == '\n')
                                break;
                        if (name_terminate(start, line-start, c, terminate))
@@ -497,6 +659,37 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
        return squash_slash(xmemdupz(start, len));
 }
 
+static char *find_name(const char *line, char *def, int p_value, int terminate)
+{
+       if (*line == '"') {
+               char *name = find_name_gnu(line, def, p_value);
+               if (name)
+                       return name;
+       }
+
+       return find_name_common(line, def, p_value, NULL, terminate);
+}
+
+static char *find_name_traditional(const char *line, char *def, int p_value)
+{
+       size_t len = strlen(line);
+       size_t date_len;
+
+       if (*line == '"') {
+               char *name = find_name_gnu(line, def, p_value);
+               if (name)
+                       return name;
+       }
+
+       len = strchrnul(line, '\n') - line;
+       date_len = diff_timestamp_len(line, len);
+       if (!date_len)
+               return find_name_common(line, def, p_value, NULL, TERM_TAB);
+       len -= date_len;
+
+       return find_name_common(line, def, p_value, line + len, 0);
+}
+
 static int count_slashes(const char *cp)
 {
        int cnt = 0;
@@ -519,7 +712,7 @@ static int guess_p_value(const char *nameline)
 
        if (is_dev_null(nameline))
                return -1;
-       name = find_name(nameline, NULL, 0, TERM_SPACE | TERM_TAB);
+       name = find_name_traditional(nameline, NULL, 0);
        if (!name)
                return -1;
        cp = strchr(name, '/');
@@ -560,8 +753,8 @@ static int has_epoch_timestamp(const char *nameline)
                " "
                "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
                " "
-               "([-+][0-2][0-9][0-5][0-9])\n";
-       const char *timestamp = NULL, *cp;
+               "([-+][0-2][0-9]:?[0-5][0-9])\n";
+       const char *timestamp = NULL, *cp, *colon;
        static regex_t *stamp;
        regmatch_t m[10];
        int zoneoffset;
@@ -591,8 +784,11 @@ static int has_epoch_timestamp(const char *nameline)
                return 0;
        }
 
-       zoneoffset = strtol(timestamp + m[3].rm_so + 1, NULL, 10);
-       zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
+       zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10);
+       if (*colon == ':')
+               zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10);
+       else
+               zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
        if (timestamp[m[3].rm_so] == '-')
                zoneoffset = -zoneoffset;
 
@@ -638,16 +834,16 @@ static void parse_traditional_patch(const char *first, const char *second, struc
        if (is_dev_null(first)) {
                patch->is_new = 1;
                patch->is_delete = 0;
-               name = find_name(second, NULL, p_value, TERM_SPACE | TERM_TAB);
+               name = find_name_traditional(second, NULL, p_value);
                patch->new_name = name;
        } else if (is_dev_null(second)) {
                patch->is_new = 0;
                patch->is_delete = 1;
-               name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
+               name = find_name_traditional(first, NULL, p_value);
                patch->old_name = name;
        } else {
-               name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
-               name = find_name(second, name, p_value, TERM_SPACE | TERM_TAB);
+               name = find_name_traditional(first, NULL, p_value);
+               name = find_name_traditional(second, name, p_value);
                if (has_epoch_timestamp(first)) {
                        patch->is_new = 1;
                        patch->is_delete = 0;
@@ -746,28 +942,28 @@ static int gitdiff_newfile(const char *line, struct patch *patch)
 static int gitdiff_copysrc(const char *line, struct patch *patch)
 {
        patch->is_copy = 1;
-       patch->old_name = find_name(line, NULL, 0, 0);
+       patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
 
 static int gitdiff_copydst(const char *line, struct patch *patch)
 {
        patch->is_copy = 1;
-       patch->new_name = find_name(line, NULL, 0, 0);
+       patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
 
 static int gitdiff_renamesrc(const char *line, struct patch *patch)
 {
        patch->is_rename = 1;
-       patch->old_name = find_name(line, NULL, 0, 0);
+       patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
 
 static int gitdiff_renamedst(const char *line, struct patch *patch)
 {
        patch->is_rename = 1;
-       patch->new_name = find_name(line, NULL, 0, 0);
+       patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
 
@@ -852,7 +1048,7 @@ static char *git_header_name(char *line, int llen)
 {
        const char *name;
        const char *second = NULL;
-       size_t len;
+       size_t len, line_len;
 
        line += strlen("diff --git ");
        llen -= strlen("diff --git ");
@@ -952,6 +1148,10 @@ static char *git_header_name(char *line, int llen)
         * Accept a name only if it shows up twice, exactly the same
         * form.
         */
+       second = strchr(name, '\n');
+       if (!second)
+               return NULL;
+       line_len = second - name;
        for (len = 0 ; ; len++) {
                switch (name[len]) {
                default:
@@ -959,15 +1159,11 @@ static char *git_header_name(char *line, int llen)
                case '\n':
                        return NULL;
                case '\t': case ' ':
-                       second = name+len;
-                       for (;;) {
-                               char c = *second++;
-                               if (c == '\n')
-                                       return NULL;
-                               if (c == '/')
-                                       break;
-                       }
-                       if (second[len] == '\n' && !memcmp(name, second, len)) {
+                       second = stop_at_slash(name + len, line_len - len);
+                       if (!second)
+                               return NULL;
+                       second++;
+                       if (second[len] == '\n' && !strncmp(name, second, len)) {
                                return xmemdupz(name, len);
                        }
                }
@@ -2472,6 +2668,12 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
        unsigned long len;
        void *dst;
 
+       if (!fragment)
+               return error("missing binary patch data for '%s'",
+                            patch->new_name ?
+                            patch->new_name :
+                            patch->old_name);
+
        /* Binary patch is irreversible without the optional second hunk */
        if (apply_in_reverse) {
                if (!fragment->next)
@@ -3606,11 +3808,11 @@ static int option_parse_directory(const struct option *opt,
        return 0;
 }
 
-int cmd_apply(int argc, const char **argv, const char *unused_prefix)
+int cmd_apply(int argc, const char **argv, const char *prefix_)
 {
        int i;
        int errs = 0;
-       int is_not_gitdir;
+       int is_not_gitdir = !startup_info->have_repository;
        int binary;
        int force_apply = 0;
 
@@ -3670,7 +3872,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                        "don't expect at least one line of context"),
                OPT_BOOLEAN(0, "reject", &apply_with_reject,
                        "leave the rejected hunks in corresponding *.rej files"),
-               OPT__VERBOSE(&apply_verbosely),
+               OPT__VERBOSE(&apply_verbosely, "be verbose"),
                OPT_BIT(0, "inaccurate-eof", &options,
                        "tolerate incorrectly detected missing new-line at the end of file",
                        INACCURATE_EOF),
@@ -3683,7 +3885,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                OPT_END()
        };
 
-       prefix = setup_git_directory_gently(&is_not_gitdir);
+       prefix = prefix_;
        prefix_length = prefix ? strlen(prefix) : 0;
        git_config(git_apply_config, NULL);
        if (apply_default_whitespace)
index 437b1a433a78325cfbc61d2f76b5a114b049e679..aa30ec52692558b0d8ed3dc3b6d671185682026e 100644 (file)
@@ -83,6 +83,7 @@ struct origin {
        struct commit *commit;
        mmfile_t file;
        unsigned char blob_sha1[20];
+       unsigned mode;
        char path[FLEX_ARRAY];
 };
 
@@ -92,6 +93,7 @@ struct origin {
  * Return 1 if the conversion succeeds, 0 otherwise.
  */
 int textconv_object(const char *path,
+                   unsigned mode,
                    const unsigned char *sha1,
                    char **buf,
                    unsigned long *buf_size)
@@ -100,7 +102,7 @@ int textconv_object(const char *path,
        struct userdiff_driver *textconv;
 
        df = alloc_filespec(path);
-       fill_filespec(df, sha1, S_IFREG | 0664);
+       fill_filespec(df, sha1, mode);
        textconv = get_textconv(df);
        if (!textconv) {
                free_filespec(df);
@@ -125,7 +127,7 @@ static void fill_origin_blob(struct diff_options *opt,
 
                num_read_blob++;
                if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
-                   textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size))
+                   textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
                        ;
                else
                        file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@@ -313,21 +315,23 @@ static struct origin *get_origin(struct scoreboard *sb,
  * for an origin is also used to pass the blame for the entire file to
  * the parent to detect the case where a child's blob is identical to
  * that of its parent's.
+ *
+ * This also fills origin->mode for corresponding tree path.
  */
-static int fill_blob_sha1(struct origin *origin)
+static int fill_blob_sha1_and_mode(struct origin *origin)
 {
-       unsigned mode;
        if (!is_null_sha1(origin->blob_sha1))
                return 0;
        if (get_tree_entry(origin->commit->object.sha1,
                           origin->path,
-                          origin->blob_sha1, &mode))
+                          origin->blob_sha1, &origin->mode))
                goto error_out;
        if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
                goto error_out;
        return 0;
  error_out:
        hashclr(origin->blob_sha1);
+       origin->mode = S_IFINVALID;
        return -1;
 }
 
@@ -360,12 +364,14 @@ static struct origin *find_origin(struct scoreboard *sb,
                        /*
                         * If the origin was newly created (i.e. get_origin
                         * would call make_origin if none is found in the
-                        * scoreboard), it does not know the blob_sha1,
+                        * scoreboard), it does not know the blob_sha1/mode,
                         * so copy it.  Otherwise porigin was in the
-                        * scoreboard and already knows blob_sha1.
+                        * scoreboard and already knows blob_sha1/mode.
                         */
-                       if (porigin->refcnt == 1)
+                       if (porigin->refcnt == 1) {
                                hashcpy(porigin->blob_sha1, cached->blob_sha1);
+                               porigin->mode = cached->mode;
+                       }
                        return porigin;
                }
                /* otherwise it was not very useful; free it */
@@ -400,6 +406,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                /* The path is the same as parent */
                porigin = get_origin(sb, parent, origin->path);
                hashcpy(porigin->blob_sha1, origin->blob_sha1);
+               porigin->mode = origin->mode;
        } else {
                /*
                 * Since origin->path is a pathspec, if the parent
@@ -425,6 +432,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                case 'M':
                        porigin = get_origin(sb, parent, origin->path);
                        hashcpy(porigin->blob_sha1, p->one->sha1);
+                       porigin->mode = p->one->mode;
                        break;
                case 'A':
                case 'T':
@@ -444,6 +452,7 @@ static struct origin *find_origin(struct scoreboard *sb,
 
                cached = make_origin(porigin->commit, porigin->path);
                hashcpy(cached->blob_sha1, porigin->blob_sha1);
+               cached->mode = porigin->mode;
                parent->util = cached;
        }
        return porigin;
@@ -486,6 +495,7 @@ static struct origin *find_rename(struct scoreboard *sb,
                    !strcmp(p->two->path, origin->path)) {
                        porigin = get_origin(sb, parent, p->one->path);
                        hashcpy(porigin->blob_sha1, p->one->sha1);
+                       porigin->mode = p->one->mode;
                        break;
                }
        }
@@ -1099,6 +1109,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
 
                        norigin = get_origin(sb, parent, p->one->path);
                        hashcpy(norigin->blob_sha1, p->one->sha1);
+                       norigin->mode = p->one->mode;
                        fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
                        if (!file_p.ptr)
                                continue;
@@ -1606,6 +1617,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
 #define OUTPUT_SHOW_NUMBER     040
 #define OUTPUT_SHOW_SCORE      0100
 #define OUTPUT_NO_AUTHOR       0200
+#define OUTPUT_SHOW_EMAIL      0400
 
 static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
 {
@@ -1671,12 +1683,17 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
                }
 
                printf("%.*s", length, hex);
-               if (opt & OUTPUT_ANNOTATE_COMPAT)
-                       printf("\t(%10s\t%10s\t%d)", ci.author,
+               if (opt & OUTPUT_ANNOTATE_COMPAT) {
+                       const char *name;
+                       if (opt & OUTPUT_SHOW_EMAIL)
+                               name = ci.author_mail;
+                       else
+                               name = ci.author;
+                       printf("\t(%10s\t%10s\t%d)", name,
                               format_time(ci.author_time, ci.author_tz,
                                           show_raw_time),
                               ent->lno + 1 + cnt);
-               else {
+               else {
                        if (opt & OUTPUT_SHOW_SCORE)
                                printf(" %*d %02d",
                                       max_score_digits, ent->score,
@@ -1689,9 +1706,15 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
                                       ent->s_lno + 1 + cnt);
 
                        if (!(opt & OUTPUT_NO_AUTHOR)) {
-                               int pad = longest_author - utf8_strwidth(ci.author);
+                               const char *name;
+                               int pad;
+                               if (opt & OUTPUT_SHOW_EMAIL)
+                                       name = ci.author_mail;
+                               else
+                                       name = ci.author;
+                               pad = longest_author - utf8_strwidth(name);
                                printf(" (%s%*s %10s",
-                                      ci.author, pad, "",
+                                      name, pad, "",
                                       format_time(ci.author_time,
                                                   ci.author_tz,
                                                   show_raw_time));
@@ -1829,7 +1852,10 @@ static void find_alignment(struct scoreboard *sb, int *option)
                if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
                        suspect->commit->object.flags |= METAINFO_SHOWN;
                        get_commit_info(suspect->commit, &ci, 1);
-                       num = utf8_strwidth(ci.author);
+                       if (*option & OUTPUT_SHOW_EMAIL)
+                               num = utf8_strwidth(ci.author_mail);
+                       else
+                               num = utf8_strwidth(ci.author);
                        if (longest_author < num)
                                longest_author = num;
                }
@@ -2075,7 +2101,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
-                           textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
+                           textconv_object(read_from, mode, null_sha1, &buf.buf, &buf_len))
                                buf.len = buf_len;
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
@@ -2278,6 +2304,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
                OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
                OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR),
+               OPT_BIT('e', "show-email", &output_option, "Show author email instead of name (Default: off)", OUTPUT_SHOW_EMAIL),
                OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE),
                OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"),
                OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"),
@@ -2298,8 +2325,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        save_commit_buffer = 0;
        dashdash_pos = 0;
 
-       parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
-                           PARSE_OPT_KEEP_ARGV0);
+       parse_options_start(&ctx, argc, argv, prefix, options,
+                           PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
        for (;;) {
                switch (parse_options_step(&ctx, options, blame_opt_usage)) {
                case PARSE_OPT_HELP:
@@ -2368,11 +2395,11 @@ parse_done:
         *
         * The remaining are:
         *
-        * (1) if dashdash_pos != 0, its either
+        * (1) if dashdash_pos != 0, it is either
         *     "blame [revisions] -- <path>" or
         *     "blame -- <path> <rev>"
         *
-        * (2) otherwise, its one of the two:
+        * (2) otherwise, it is one of the two:
         *     "blame [revisions] <path>"
         *     "blame <path> <rev>"
         *
@@ -2455,11 +2482,11 @@ parse_done:
        }
        else {
                o = get_origin(&sb, sb.final, path);
-               if (fill_blob_sha1(o))
+               if (fill_blob_sha1_and_mode(o))
                        die("no such path %s in %s", path, final_commit_name);
 
                if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
-                   textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
+                   textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
                                    &sb.final_buf_size))
                        ;
                else
index 87976f0921d87f2a8564eb54aa3b81d30ee7f214..9e546e4a83d07dd6dd4d4b0cb7d23a02c82747fb 100644 (file)
@@ -313,12 +313,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
                                           (struct object *)commit, refname);
        }
 
-       /* Resize buffer */
-       if (ref_list->index >= ref_list->alloc) {
-               ref_list->alloc = alloc_nr(ref_list->alloc);
-               ref_list->list = xrealloc(ref_list->list,
-                               ref_list->alloc * sizeof(struct ref_item));
-       }
+       ALLOC_GROW(ref_list->list, ref_list->index + 1, ref_list->alloc);
 
        /* Record the new item */
        newitem = &(ref_list->list[ref_list->index++]);
@@ -621,7 +616,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
        struct option options[] = {
                OPT_GROUP("Generic options"),
-               OPT__VERBOSE(&verbose),
+               OPT__VERBOSE(&verbose,
+                       "show hash and subject, give twice for upstream branch"),
                OPT_SET_INT('t', "track",  &track, "set up tracking mode (see git-pull(1))",
                        BRANCH_TRACK_EXPLICIT),
                OPT_SET_INT( 0, "set-upstream",  &track, "change upstream info",
@@ -651,7 +647,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', "force", &force_create, "force creation (when already exists)"),
+               OPT__FORCE(&force_create, "force creation (when already exists)"),
                {
                        OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
                        "commit", "print only not merged branches",
@@ -667,6 +663,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(builtin_branch_usage, options);
+
        git_config(git_branch_config, NULL);
 
        if (branch_use_color == -1)
index 2006cc5cd5cc4d381189f5cf7bdd09fcdd66b9ca..9b87fb9ac2505e746ae34db34740cdfb3cfde127 100644 (file)
 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...]";
+  "   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)
 {
        struct bundle_header header;
-       int nongit;
        const char *cmd, *bundle_file;
        int bundle_fd = -1;
        char buffer[PATH_MAX];
@@ -31,7 +30,6 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
        argc -= 2;
        argv += 2;
 
-       prefix = setup_git_directory_gently(&nongit);
        if (prefix && bundle_file[0] != '/') {
                snprintf(buffer, sizeof(buffer), "%s/%s", prefix, bundle_file);
                bundle_file = buffer;
@@ -54,11 +52,11 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
                return !!list_bundle_refs(&header, argc, argv);
        }
        if (!strcmp(cmd, "create")) {
-               if (nongit)
+               if (!startup_info->have_repository)
                        die("Need a repository to create a bundle.");
                return !!create_bundle(&header, bundle_file, argc, argv);
        } else if (!strcmp(cmd, "unbundle")) {
-               if (nongit)
+               if (!startup_info->have_repository)
                        die("Need a repository to unbundle.");
                return !!unbundle(&header, bundle_fd) ||
                        list_bundle_refs(&header, argc, argv);
index 76ec3fec9279f38520fa4b5b525ce2c03cc9cbb0..94632dbdb400f9a2986d10f06dd16119ce1b4e54 100644 (file)
@@ -143,7 +143,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
                        die("git cat-file --textconv %s: <object> must be <sha1:path>",
                            obj_name);
 
-               if (!textconv_object(obj_context.path, sha1, &buf, &size))
+               if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
                        die("git cat-file --textconv: unable to run textconv on %s",
                            obj_name);
                break;
index a7a5ee10f32d52c7555f85f2dcf6c567425488dd..f1fec24745a854e77555a1b4bea3bc8d7ab04367 100644 (file)
@@ -155,7 +155,7 @@ static void checkout_all(const char *prefix, int prefix_length)
 }
 
 static const char * const builtin_checkout_index_usage[] = {
-       "git checkout-index [options] [--] <file>...",
+       "git checkout-index [options] [--] [<file>...]",
        NULL
 };
 
@@ -217,9 +217,9 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        struct option builtin_checkout_index_options[] = {
                OPT_BOOLEAN('a', "all", &all,
                        "checks out all files in the index"),
-               OPT_BOOLEAN('f', "force", &force,
-                       "forces overwrite of existing files"),
-               OPT__QUIET(&quiet),
+               OPT__FORCE(&force, "forces overwrite of existing files"),
+               OPT__QUIET(&quiet,
+                       "no warning for existing files and files not in index"),
                OPT_BOOLEAN('n', "no-create", &not_new,
                        "don't checkout new files"),
                { OPTION_CALLBACK, 'u', "index", &newfd, NULL,
@@ -241,6 +241,9 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(builtin_checkout_index_usage,
+                                  builtin_checkout_index_options);
        git_config(git_default_config, NULL);
        state.base_dir = "";
        prefix_length = prefix ? strlen(prefix) : 0;
index cb83e6a6318b8b723a3ab9d97eac1556e2aa1d9b..0ca8f6a2061b177410136fb89b988806a05eb16e 100644 (file)
@@ -18,6 +18,7 @@
 #include "xdiff-interface.h"
 #include "ll-merge.h"
 #include "resolve-undo.h"
+#include "submodule.h"
 
 static const char * const checkout_usage[] = {
        "git checkout [options] <branch>",
@@ -40,6 +41,7 @@ struct checkout_opts {
        const char *new_orphan_branch;
        int new_branch_log;
        enum branch_track track;
+       struct diff_options diff_options;
 };
 
 static int post_checkout_hook(struct commit *old, struct commit *new,
@@ -154,8 +156,12 @@ static int checkout_merged(int pos, struct checkout *state)
        read_mmblob(&ours, active_cache[pos+1]->sha1);
        read_mmblob(&theirs, active_cache[pos+2]->sha1);
 
+       /*
+        * NEEDSWORK: re-create conflicts from merges with
+        * merge.renormalize set, too
+        */
        status = ll_merge(&result_buf, path, &ancestor, "base",
-                         &ours, "ours", &theirs, "theirs", 0);
+                         &ours, "ours", &theirs, "theirs", NULL);
        free(ancestor.ptr);
        free(ours.ptr);
        free(theirs.ptr);
@@ -278,11 +284,12 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
        return errs;
 }
 
-static void show_local_changes(struct object *head)
+static void show_local_changes(struct object *head, struct diff_options *opts)
 {
        struct rev_info rev;
        /* I think we want full paths, even if we're in a subdirectory. */
        init_revisions(&rev, NULL);
+       rev.diffopt.flags = opts->flags;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
        if (diff_setup_done(&rev.diffopt) < 0)
                die("diff_setup_done failed");
@@ -376,7 +383,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                topts.src_index = &the_index;
                topts.dst_index = &the_index;
 
-               topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches.";
+               setup_unpack_trees_porcelain(&topts, "checkout");
 
                refresh_cache(REFRESH_QUIET);
 
@@ -438,6 +445,13 @@ static int merge_working_tree(struct checkout_opts *opts,
                         */
 
                        add_files_to_cache(NULL, NULL, 0);
+                       /*
+                        * NEEDSWORK: carrying over local changes
+                        * when branches have different end-of-line
+                        * normalization (or clean+smudge rules) is
+                        * a pain; plumb in an option to set
+                        * o.renormalize?
+                        */
                        init_merge_options(&o);
                        o.verbosity = 0;
                        work = write_tree_from_memory(&o);
@@ -461,7 +475,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                die("unable to write new index file");
 
        if (!opts->force && !opts->quiet)
-               show_local_changes(&new->commit->object);
+               show_local_changes(&new->commit->object, &opts->diff_options);
 
        return 0;
 }
@@ -609,7 +623,16 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 
 static int git_checkout_config(const char *var, const char *value, void *cb)
 {
-       return git_xmerge_config(var, value, cb);
+       if (!strcmp(var, "diff.ignoresubmodules")) {
+               struct checkout_opts *opts = cb;
+               handle_ignore_submodules_arg(&opts->diff_options, value);
+               return 0;
+       }
+
+       if (!prefixcmp(var, "submodule."))
+               return parse_submodule_config_option(var, value);
+
+       return git_xmerge_config(var, value, NULL);
 }
 
 static int interactive_checkout(const char *revision, const char **pathspec,
@@ -657,7 +680,7 @@ static const char *unique_tracking_name(const char *name)
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
        struct checkout_opts opts;
-       unsigned char rev[20];
+       unsigned char rev[20], branch_rev[20];
        const char *arg;
        struct branch_info new;
        struct tree *source_tree = NULL;
@@ -665,23 +688,23 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        int patch_mode = 0;
        int dwim_new_local_branch = 1;
        struct option options[] = {
-               OPT__QUIET(&opts.quiet),
+               OPT__QUIET(&opts.quiet, "suppress progress reporting"),
                OPT_STRING('b', NULL, &opts.new_branch, "branch",
                           "create and checkout a new branch"),
                OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
                           "create/reset and checkout a branch"),
-               OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
-               OPT_SET_INT('t', "track",  &opts.track, "track",
+               OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"),
+               OPT_SET_INT('t', "track",  &opts.track, "set upstream info for new branch",
                        BRANCH_TRACK_EXPLICIT),
                OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
-               OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
+               OPT_SET_INT('2', "ours", &opts.writeout_stage, "checkout our version for unmerged files",
                            2),
-               OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
+               OPT_SET_INT('3', "theirs", &opts.writeout_stage, "checkout their version for unmerged files",
                            3),
-               OPT_BOOLEAN('f', "force", &opts.force, "force"),
+               OPT__FORCE(&opts.force, "force checkout (throw away local modifications)"),
                OPT_BOOLEAN('i', "ignored-are-precious", &ignored_are_precious,
                  "fail when an ignored file needs to be overwritten"),
-               OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
+               OPT_BOOLEAN('m', "merge", &opts.merge, "perform a 3-way merge with the new branch"),
                OPT_STRING(0, "conflict", &conflict_style, "style",
                           "conflict style (merge or diff3)"),
                OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
@@ -695,7 +718,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        memset(&opts, 0, sizeof(opts));
        memset(&new, 0, sizeof(new));
 
-       git_config(git_checkout_config, NULL);
+       gitmodules_config();
+       git_config(git_checkout_config, &opts);
 
        opts.track = BRANCH_TRACK_UNSPECIFIED;
 
@@ -764,9 +788,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
         *   between A and B, A...B names that merge base.
         *
         *   With no paths, if <something> is _not_ a commit, no -t nor -b
-        *   was given, and there is a tracking branch whose name is
+        *   was given, and there is a remote-tracking branch whose name is
         *   <something> in one and only one remote, then this is a short-hand
-        *   to fork local <something> from that remote tracking branch.
+        *   to fork local <something> from that remote-tracking branch.
         *
         *   Otherwise <something> shall not be ambiguous.
         *   - If it's *only* a reference, treat it like case (1).
@@ -812,18 +836,21 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                argc--;
 
                new.name = arg;
-               if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
-                       setup_branch_path(&new);
+               setup_branch_path(&new);
 
-                       if ((check_ref_format(new.path) == CHECK_REF_FORMAT_OK) &&
-                           resolve_ref(new.path, rev, 1, NULL))
-                               ;
-                       else
-                               new.path = NULL;
+               if (check_ref_format(new.path) == CHECK_REF_FORMAT_OK &&
+                   resolve_ref(new.path, branch_rev, 1, NULL))
+                       hashcpy(rev, branch_rev);
+               else
+                       new.path = NULL; /* not an existing branch */
+
+               if (!(new.commit = lookup_commit_reference_gently(rev, 1))) {
+                       /* not a commit */
+                       source_tree = parse_tree_indirect(rev);
+               } else {
                        parse_commit(new.commit);
                        source_tree = new.commit->tree;
-               } else
-                       source_tree = parse_tree_indirect(rev);
+               }
 
                if (!source_tree)                   /* case (1): want a tree */
                        die("reference is not a tree: %s", arg);
index b508d2cab482dffc97be1e8ee0481a6bb6157cf2..4a312abc6b3ecef5f174b2786ad31385ba058b44 100644 (file)
@@ -38,19 +38,19 @@ 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 ignored_only = 0, config_set = 0, errors = 0;
        int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
        struct strbuf directory = STRBUF_INIT;
        struct dir_struct dir;
        static const char **pathspec;
        struct strbuf buf = STRBUF_INIT;
-       struct string_list exclude_list = { NULL, 0, 0, 0 };
+       struct string_list exclude_list = STRING_LIST_INIT_NODUP;
        const char *qname;
        char *seen = NULL;
        struct option options[] = {
-               OPT__QUIET(&quiet),
-               OPT__DRY_RUN(&show_only),
-               OPT_BOOLEAN('f', "force", &force, "force"),
+               OPT__QUIET(&quiet, "do not print names of files removed"),
+               OPT__DRY_RUN(&show_only, "dry run"),
+               OPT__FORCE(&force, "force"),
                OPT_BOOLEAN('d', NULL, &remove_directories,
                                "remove whole directories"),
                { OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern",
@@ -138,7 +138,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                if (pathspec) {
                        memset(seen, 0, argc > 0 ? argc : 1);
                        matches = match_pathspec(pathspec, ent->name, len,
-                                                baselen, seen);
+                                                0, seen);
                }
 
                if (S_ISDIR(st.st_mode)) {
@@ -153,7 +153,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                                        printf("Removing %s\n", qname);
                                if (remove_dir_recursively(&directory,
                                                           rm_flags) != 0) {
-                                       warning("failed to remove '%s'", qname);
+                                       warning("failed to remove %s", qname);
                                        errors++;
                                }
                        } else if (show_only) {
@@ -173,7 +173,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                                printf("Removing %s\n", qname);
                        }
                        if (unlink(ent->name) != 0) {
-                               warning("failed to remove '%s'", qname);
+                               warning("failed to remove %s", qname);
                                errors++;
                        }
                }
index efb1e6faa539935de0e9dc590237185a1d222322..61e0989b5ab8fffb16ec28e64c38b4fa236cb3ed 100644 (file)
@@ -66,6 +66,8 @@ static struct option builtin_clone_options[] = {
                    "setup as shared repository"),
        OPT_BOOLEAN(0, "recursive", &option_recursive,
                    "initialize submodules in the clone"),
+       OPT_BOOLEAN(0, "recurse_submodules", &option_recursive,
+                   "initialize submodules in the clone"),
        OPT_STRING(0, "template", &option_template, "path",
                   "path the template repository"),
        OPT_STRING(0, "reference", &option_reference, "repo",
@@ -361,7 +363,7 @@ static void write_remote_refs(const struct ref *local_refs)
 
 int cmd_clone(int argc, const char **argv, const char *prefix)
 {
-       int is_bundle = 0;
+       int is_bundle = 0, is_local;
        struct stat buf;
        const char *repo_name, *repo, *work_tree, *git_dir;
        char *path, *dir;
@@ -414,6 +416,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                repo = xstrdup(make_absolute_path(repo_name));
        else
                repo = repo_name;
+       is_local = path && !is_bundle;
+       if (is_local && option_depth)
+               warning("--depth is ignored in local clones; use file:// instead.");
 
        if (argc == 2)
                dir = xstrdup(argv[1]);
@@ -514,7 +519,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        strbuf_reset(&value);
 
-       if (path && !is_bundle) {
+       if (is_local) {
                refs = clone_local(path, git_dir);
                mapped_refs = wanted_peer_refs(refs, refspec);
        } else {
index 87f0591c2f68a03e06c73b352282426b803450ba..d083795e26e7893c6b7d466f9bcf2be1311cfeb2 100644 (file)
@@ -9,7 +9,7 @@
 #include "builtin.h"
 #include "utf8.h"
 
-static const char commit_tree_usage[] = "git commit-tree <sha1> [-p <sha1>]* < changelog";
+static const char commit_tree_usage[] = "git commit-tree <sha1> [(-p <sha1>)...] < changelog";
 
 static void new_parent(struct commit *parent, struct commit_list **parents_p)
 {
@@ -56,10 +56,12 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
        if (strbuf_read(&buffer, 0, 0) < 0)
                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));
-               return 0;
-       }
-       else
+       if (commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
+               strbuf_release(&buffer);
                return 1;
+       }
+
+       printf("%s\n", sha1_to_hex(commit_sha1));
+       strbuf_release(&buffer);
+       return 0;
 }
index 66fdd2202495b2893c79b4f92b0fc2e715f5b7bb..03cff5af631fec975442c528b1ffba264b879c8e 100644 (file)
@@ -45,9 +45,9 @@ static const char implicit_ident_advice[] =
 "    git config --global user.name \"Your Name\"\n"
 "    git config --global user.email you@example.com\n"
 "\n"
-"If the identity used for this commit is wrong, you can fix it with:\n"
+"After doing this, you may fix the identity used for this commit with:\n"
 "\n"
-"    git commit --amend --author='Your Name <you@example.com>'\n";
+"    git commit --amend --reset-author\n";
 
 static const char empty_amend_advice[] =
 "You asked to amend the most recent commit, but doing so would make\n"
@@ -69,7 +69,7 @@ static enum {
 static const char *logfile, *force_author;
 static const char *template_file;
 static char *edit_message, *use_message;
-static char *author_name, *author_email, *author_date;
+static char *fixup_message, *squash_message;
 static int all, edit_flag, also, interactive, only, amend, signoff;
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int no_post_rewrite, allow_empty_message;
@@ -114,8 +114,8 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 }
 
 static struct option builtin_commit_options[] = {
-       OPT__QUIET(&quiet),
-       OPT__VERBOSE(&verbose),
+       OPT__QUIET(&quiet, "suppress summary after successful commit"),
+       OPT__VERBOSE(&verbose, "show diff in commit message template"),
 
        OPT_GROUP("Commit message options"),
        OPT_FILENAME('F', "file", &logfile, "read log from file"),
@@ -124,6 +124,8 @@ static struct option builtin_commit_options[] = {
        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_STRING(0, "fixup", &fixup_message, "COMMIT", "use autosquash formatted message to fixup specified commit"),
+       OPT_STRING(0, "squash", &squash_message, "COMMIT", "use autosquash formatted message to squash specified commit"),
        OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"),
        OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
        OPT_FILENAME('t', "template", &template_file, "use specified template file"),
@@ -459,7 +461,7 @@ static int is_a_merge(const unsigned char *sha1)
 
 static const char sign_off_header[] = "Signed-off-by: ";
 
-static void determine_author_info(void)
+static void determine_author_info(struct strbuf *author_ident)
 {
        char *name, *email, *date;
 
@@ -503,10 +505,8 @@ static void determine_author_info(void)
 
        if (force_date)
                date = force_date;
-
-       author_name = name;
-       author_email = email;
-       author_date = date;
+       strbuf_addstr(author_ident, fmt_ident(name, email, date,
+                                             IDENT_ERROR_ON_NO_NAME));
 }
 
 static int ends_rfc2822_footer(struct strbuf *sb)
@@ -550,10 +550,21 @@ static int ends_rfc2822_footer(struct strbuf *sb)
        return 1;
 }
 
+static char *cut_ident_timestamp_part(char *string)
+{
+       char *ket = strrchr(string, '>');
+       if (!ket || ket[1] != ' ')
+               die("Malformed ident string: '%s'", string);
+       *++ket = '\0';
+       return ket;
+}
+
 static int prepare_to_commit(const char *index_file, const char *prefix,
-                            struct wt_status *s)
+                            struct wt_status *s,
+                            struct strbuf *author_ident)
 {
        struct stat statbuf;
+       struct strbuf committer_ident = STRBUF_INIT;
        int commitable, saved_color_setting;
        struct strbuf sb = STRBUF_INIT;
        char *buffer;
@@ -565,6 +576,25 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        if (!no_verify && run_hook(index_file, "pre-commit", NULL))
                return 0;
 
+       if (squash_message) {
+               /*
+                * Insert the proper subject line before other commit
+                * message options add their content.
+                */
+               if (use_message && !strcmp(use_message, squash_message))
+                       strbuf_addstr(&sb, "squash! ");
+               else {
+                       struct pretty_print_context ctx = {0};
+                       struct commit *c;
+                       c = lookup_commit_reference_by_name(squash_message);
+                       if (!c)
+                               die("could not lookup commit %s", squash_message);
+                       ctx.output_encoding = get_commit_output_encoding();
+                       format_commit_message(c, "squash! %s\n\n", &sb,
+                                             &ctx);
+               }
+       }
+
        if (message.len) {
                strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
@@ -586,6 +616,16 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
                hook_arg1 = "commit";
                hook_arg2 = use_message;
+       } else if (fixup_message) {
+               struct pretty_print_context ctx = {0};
+               struct commit *commit;
+               commit = lookup_commit_reference_by_name(fixup_message);
+               if (!commit)
+                       die("could not lookup commit %s", fixup_message);
+               ctx.output_encoding = get_commit_output_encoding();
+               format_commit_message(commit, "fixup! %s\n\n",
+                                     &sb, &ctx);
+               hook_arg1 = "message";
        } else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
                if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
                        die_errno("could not read MERGE_MSG");
@@ -607,6 +647,16 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        else if (in_merge)
                hook_arg1 = "merge";
 
+       if (squash_message) {
+               /*
+                * If squash_commit was used for the commit subject,
+                * then we're possibly hijacking other commit log options.
+                * Reset the hook args to tell the real story.
+                */
+               hook_arg1 = "message";
+               hook_arg2 = "";
+       }
+
        fp = fopen(git_path(commit_editmsg), "w");
        if (fp == NULL)
                die_errno("could not open '%s'", git_path(commit_editmsg));
@@ -637,14 +687,13 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
        strbuf_release(&sb);
 
-       determine_author_info();
+       /* This checks and barfs if author is badly specified */
+       determine_author_info(author_ident);
 
        /* This checks if committer ident is explicitly given */
-       git_committer_info(0);
+       strbuf_addstr(&committer_ident, git_committer_info(0));
        if (use_editor && include_status) {
-               char *author_ident;
-               const char *committer_ident;
-
+               char *ai_tmp, *ci_tmp;
                if (in_merge)
                        fprintf(fp,
                                "#\n"
@@ -672,23 +721,21 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                if (only_include_assumed)
                        fprintf(fp, "# %s\n", only_include_assumed);
 
-               author_ident = xstrdup(fmt_name(author_name, author_email));
-               committer_ident = fmt_name(getenv("GIT_COMMITTER_NAME"),
-                                          getenv("GIT_COMMITTER_EMAIL"));
-               if (strcmp(author_ident, committer_ident))
+               ai_tmp = cut_ident_timestamp_part(author_ident->buf);
+               ci_tmp = cut_ident_timestamp_part(committer_ident.buf);
+               if (strcmp(author_ident->buf, committer_ident.buf))
                        fprintf(fp,
                                "%s"
                                "# Author:    %s\n",
                                ident_shown++ ? "" : "#\n",
-                               author_ident);
-               free(author_ident);
+                               author_ident->buf);
 
                if (!user_ident_sufficiently_given())
                        fprintf(fp,
                                "%s"
                                "# Committer: %s\n",
                                ident_shown++ ? "" : "#\n",
-                               committer_ident);
+                               committer_ident.buf);
 
                if (ident_shown)
                        fprintf(fp, "#\n");
@@ -697,6 +744,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                s->use_color = 0;
                commitable = run_status(fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
+
+               *ai_tmp = ' ';
+               *ci_tmp = ' ';
        } else {
                unsigned char sha1[20];
                const char *parent = "HEAD";
@@ -712,6 +762,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                else
                        commitable = index_differs_from(parent, 0);
        }
+       strbuf_release(&committer_ident);
 
        fclose(fp);
 
@@ -863,7 +914,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (force_author && renew_authorship)
                die("Using both --reset-author and --author does not make sense");
 
-       if (logfile || message.len || use_message)
+       if (logfile || message.len || use_message || fixup_message)
                use_editor = 0;
        if (edit_flag)
                use_editor = 1;
@@ -878,48 +929,35 @@ static int parse_and_validate_options(int argc, const char *argv[],
                die("You have nothing to amend.");
        if (amend && in_merge)
                die("You are in the middle of a merge -- cannot amend.");
-
+       if (fixup_message && squash_message)
+               die("Options --squash and --fixup cannot be used together");
        if (use_message)
                f++;
        if (edit_message)
                f++;
+       if (fixup_message)
+               f++;
        if (logfile)
                f++;
        if (f > 1)
-               die("Only one of -c/-C/-F can be used.");
+               die("Only one of -c/-C/-F/--fixup can be used.");
        if (message.len && f > 0)
-               die("Option -m cannot be combined with -c/-C/-F.");
+               die("Option -m cannot be combined with -c/-C/-F/--fixup.");
        if (edit_message)
                use_message = edit_message;
-       if (amend && !use_message)
+       if (amend && !use_message && !fixup_message)
                use_message = "HEAD";
        if (!use_message && renew_authorship)
                die("--reset-author can be used only with -C, -c or --amend.");
        if (use_message) {
-               unsigned char sha1[20];
-               static char utf8[] = "UTF-8";
                const char *out_enc;
-               char *enc, *end;
                struct commit *commit;
 
-               if (get_sha1(use_message, sha1))
+               commit = lookup_commit_reference_by_name(use_message);
+               if (!commit)
                        die("could not lookup commit %s", use_message);
-               commit = lookup_commit_reference(sha1);
-               if (!commit || parse_commit(commit))
-                       die("could not parse commit %s", use_message);
-
-               enc = strstr(commit->buffer, "\nencoding");
-               if (enc) {
-                       end = strchr(enc + 10, '\n');
-                       enc = xstrndup(enc + 10, end - (enc + 10));
-               } else {
-                       enc = utf8;
-               }
-               out_enc = git_commit_encoding ? git_commit_encoding : utf8;
-
-               if (strcmp(out_enc, enc))
-                       use_message_buffer =
-                               reencode_string(commit->buffer, out_enc, enc);
+               out_enc = get_commit_output_encoding();
+               use_message_buffer = logmsg_reencode(commit, out_enc);
 
                /*
                 * If we failed to reencode the buffer, just copy it
@@ -929,8 +967,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
                 */
                if (use_message_buffer == NULL)
                        use_message_buffer = xstrdup(commit->buffer);
-               if (enc != utf8)
-                       free(enc);
        }
 
        if (!!also + !!only + !!all + !!interactive > 1)
@@ -984,6 +1020,8 @@ static int parse_status_slot(const char *var, int offset)
 {
        if (!strcasecmp(var+offset, "header"))
                return WT_STATUS_HEADER;
+       if (!strcasecmp(var+offset, "branch"))
+               return WT_STATUS_ONBRANCH;
        if (!strcasecmp(var+offset, "updated")
                || !strcasecmp(var+offset, "added"))
                return WT_STATUS_UPDATED;
@@ -1048,7 +1086,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        int fd;
        unsigned char sha1[20];
        static struct option builtin_status_options[] = {
-               OPT__VERBOSE(&verbose),
+               OPT__VERBOSE(&verbose, "be verbose"),
                OPT_SET_INT('s', "short", &status_format,
                            "show status concisely", STATUS_FORMAT_SHORT),
                OPT_BOOLEAN('b', "branch", &status_show_branch,
@@ -1070,6 +1108,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(builtin_status_usage, builtin_status_options);
+
        if (null_termination && status_format == STATUS_FORMAT_LONG)
                status_format = STATUS_FORMAT_PORCELAIN;
 
@@ -1246,6 +1287,7 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
 int cmd_commit(int argc, const char **argv, const char *prefix)
 {
        struct strbuf sb = STRBUF_INIT;
+       struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
        char *nl, *p;
        unsigned char commit_sha1[20];
@@ -1255,6 +1297,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        int allow_fast_forward = 1;
        struct wt_status s;
 
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(builtin_commit_usage, builtin_commit_options);
+
        wt_status_prepare(&s);
        git_config(git_commit_config, &s);
        in_merge = file_exists(git_path("MERGE_HEAD"));
@@ -1273,7 +1318,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        /* 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, &s)) {
+       if (!prepare_to_commit(index_file, prefix, &s, &author_ident)) {
                rollback_index_files();
                return 1;
        }
@@ -1352,11 +1397,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        }
 
        if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1,
-                       fmt_ident(author_name, author_email, author_date,
-                               IDENT_ERROR_ON_NO_NAME))) {
+                       author_ident.buf)) {
                rollback_index_files();
                die("failed to write commit object");
        }
+       strbuf_release(&author_ident);
 
        ref_lock = lock_any_ref_for_update("HEAD",
                                           initial_commit ? NULL : head_sha1,
index f3d1660d023f7893373436617a9f729ce1d60f4d..ca4a0db4a79241d7ded005483b4693967eea636f 100644 (file)
@@ -20,7 +20,7 @@ static char delim = '=';
 static char key_delim = ' ';
 static char term = '\n';
 
-static int use_global_config, use_system_config;
+static int use_global_config, use_system_config, use_local_config;
 static const char *given_config_file;
 static int actions, types;
 static const char *get_color_slot, *get_colorbool_slot;
@@ -51,6 +51,7 @@ static struct option builtin_config_options[] = {
        OPT_GROUP("Config file location"),
        OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
        OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
+       OPT_BOOLEAN(0, "local", &use_local_config, "use repository config file"),
        OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"),
        OPT_GROUP("Action"),
        OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
@@ -330,11 +331,10 @@ static int get_colorbool(int print)
                return get_colorbool_found ? 0 : 1;
 }
 
-int cmd_config(int argc, const char **argv, const char *unused_prefix)
+int cmd_config(int argc, const char **argv, const char *prefix)
 {
-       int nongit;
+       int nongit = !startup_info->have_repository;
        char *value;
-       const char *prefix = setup_git_directory_gently(&nongit);
 
        config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);
 
@@ -342,7 +342,7 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
                             builtin_config_usage,
                             PARSE_OPT_STOP_AT_NON_OPTION);
 
-       if (use_global_config + use_system_config + !!given_config_file > 1) {
+       if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
                error("only one config file at a time.");
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
@@ -358,6 +358,8 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
        }
        else if (use_system_config)
                config_exclusive_filename = git_etc_gitconfig();
+       else if (use_local_config)
+               config_exclusive_filename = git_pathdup("config");
        else if (given_config_file) {
                if (!is_absolute_path(given_config_file) && prefix)
                        config_exclusive_filename = prefix_filename(prefix,
index 2bdd8ebde1002e852055385396ef053f26c91ae8..c37cb98c31ddfaa90d38a0355d32cf3d6c404bff 100644 (file)
@@ -79,7 +79,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
        unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
        off_t loose_size = 0;
        struct option opts[] = {
-               OPT__VERBOSE(&verbose),
+               OPT__VERBOSE(&verbose, "be verbose"),
                OPT_END(),
        };
 
index 43caff2ffe185df6ab224b93b4fd9ce3d82de2d0..342129fdbdc534bdf9277de710f8e7b7ba11c5c4 100644 (file)
@@ -6,6 +6,7 @@
 #include "exec_cmd.h"
 #include "parse-options.h"
 #include "diff.h"
+#include "hash.h"
 
 #define SEEN           (1u<<0)
 #define MAX_TAGS       (FLAG_BITS - 1)
@@ -22,7 +23,8 @@ static int tags;      /* Allow lightweight tags */
 static int longformat;
 static int abbrev = DEFAULT_ABBREV;
 static int max_candidates = 10;
-static int found_names;
+static struct hash_table names;
+static int have_util;
 static const char *pattern;
 static int always;
 static const char *dirty;
@@ -34,16 +36,44 @@ static const char *diff_index_args[] = {
 
 
 struct commit_name {
+       struct commit_name *next;
+       unsigned char peeled[20];
        struct tag *tag;
        unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
        unsigned name_checked:1;
        unsigned char sha1[20];
-       char path[FLEX_ARRAY]; /* more */
+       const char *path;
 };
 static const char *prio_names[] = {
        "head", "lightweight", "annotated",
 };
 
+static inline unsigned int hash_sha1(const unsigned char *sha1)
+{
+       unsigned int hash;
+       memcpy(&hash, sha1, sizeof(hash));
+       return hash;
+}
+
+static inline struct commit_name *find_commit_name(const unsigned char *peeled)
+{
+       struct commit_name *n = lookup_hash(hash_sha1(peeled), &names);
+       while (n && !!hashcmp(peeled, n->peeled))
+               n = n->next;
+       return n;
+}
+
+static int set_util(void *chain)
+{
+       struct commit_name *n;
+       for (n = chain; n; n = n->next) {
+               struct commit *c = lookup_commit_reference_gently(n->peeled, 1);
+               if (c)
+                       c->util = n;
+       }
+       return 0;
+}
+
 static int replace_name(struct commit_name *e,
                               int prio,
                               const unsigned char *sha1,
@@ -78,31 +108,36 @@ static int replace_name(struct commit_name *e,
 }
 
 static void add_to_known_names(const char *path,
-                              struct commit *commit,
+                              const unsigned char *peeled,
                               int prio,
                               const unsigned char *sha1)
 {
-       struct commit_name *e = commit->util;
+       struct commit_name *e = find_commit_name(peeled);
        struct tag *tag = NULL;
        if (replace_name(e, prio, sha1, &tag)) {
-               size_t len = strlen(path)+1;
-               free(e);
-               e = xmalloc(sizeof(struct commit_name) + len);
+               if (!e) {
+                       void **pos;
+                       e = xmalloc(sizeof(struct commit_name));
+                       hashcpy(e->peeled, peeled);
+                       pos = insert_hash(hash_sha1(peeled), e, &names);
+                       if (pos) {
+                               e->next = *pos;
+                               *pos = e;
+                       } else {
+                               e->next = NULL;
+                       }
+               }
                e->tag = tag;
                e->prio = prio;
                e->name_checked = 0;
                hashcpy(e->sha1, sha1);
-               memcpy(e->path, path, len);
-               commit->util = e;
+               e->path = path;
        }
-       found_names = 1;
 }
 
 static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        int might_be_tag = !prefixcmp(path, "refs/tags/");
-       struct commit *commit;
-       struct object *object;
        unsigned char peeled[20];
        int is_tag, prio;
 
@@ -110,16 +145,10 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
                return 0;
 
        if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
-               commit = lookup_commit_reference_gently(peeled, 1);
-               if (!commit)
-                       return 0;
-               is_tag = !!hashcmp(sha1, commit->object.sha1);
+               is_tag = !!hashcmp(sha1, peeled);
        } else {
-               commit = lookup_commit_reference_gently(sha1, 1);
-               object = parse_object(sha1);
-               if (!commit || !object)
-                       return 0;
-               is_tag = object->type == OBJ_TAG;
+               hashcpy(peeled, sha1);
+               is_tag = 0;
        }
 
        /* If --all, then any refs are used.
@@ -142,7 +171,7 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
                if (!prio)
                        return 0;
        }
-       add_to_known_names(all ? path + 5 : path + 10, commit, prio, sha1);
+       add_to_known_names(all ? path + 5 : path + 10, peeled, prio, sha1);
        return 0;
 }
 
@@ -189,7 +218,7 @@ static unsigned long finish_depth_computation(
                        struct commit *p = parents->item;
                        parse_commit(p);
                        if (!(p->object.flags & SEEN))
-                               insert_by_date(p, list);
+                               commit_list_insert_by_date(p, list);
                        p->object.flags |= c->object.flags;
                        parents = parents->next;
                }
@@ -240,7 +269,7 @@ static void describe(const char *arg, int last_one)
        if (!cmit)
                die("%s is not a valid '%s' object", arg, commit_type);
 
-       n = cmit->util;
+       n = find_commit_name(cmit->object.sha1);
        if (n && (tags || all || n->prio == 2)) {
                /*
                 * Exact match to an existing ref.
@@ -259,6 +288,11 @@ static void describe(const char *arg, int last_one)
        if (debug)
                fprintf(stderr, "searching to describe %s\n", arg);
 
+       if (!have_util) {
+               for_each_hash(&names, set_util);
+               have_util = 1;
+       }
+
        list = NULL;
        cmit->object.flags = SEEN;
        commit_list_insert(cmit, &list);
@@ -300,7 +334,7 @@ static void describe(const char *arg, int last_one)
                        struct commit *p = parents->item;
                        parse_commit(p);
                        if (!(p->object.flags & SEEN))
-                               insert_by_date(p, &list);
+                               commit_list_insert_by_date(p, &list);
                        p->object.flags |= c->object.flags;
                        parents = parents->next;
                }
@@ -328,7 +362,7 @@ static void describe(const char *arg, int last_one)
        qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
 
        if (gave_up_on) {
-               insert_by_date(gave_up_on, &list);
+               commit_list_insert_by_date(gave_up_on, &list);
                seen_commits--;
        }
        seen_commits += finish_depth_computation(&list, &all_matches[0]);
@@ -418,8 +452,9 @@ 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 && !always)
+       init_hash(&names);
+       for_each_rawref(get_name, NULL);
+       if (!names.nr && !always)
                die("No names found, cannot describe anything.");
 
        if (argc == 0) {
index 951c7c8994704543fc1784c87a17a1aa47ede257..46085f862f937b005493319cea25b93bcb10c999 100644 (file)
@@ -61,7 +61,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
            (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
                rev.combine_merges = rev.dense_combined_merges = 1;
 
-       if (read_cache_preload(rev.diffopt.paths) < 0) {
+       if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) {
                perror("read_cache_preload");
                return -1;
        }
index a43d3263638daf083c52f37f3bd0872327d7459b..d12de8f392fc660c5048062f2a754a9f63e897fb 100644 (file)
@@ -22,7 +22,7 @@ struct blobinfo {
 };
 
 static const char builtin_diff_usage[] =
-"git diff <options> <rev>{0,2} -- <path>*";
+"git diff [<options>] [<commit> [<commit>]] [--] [<path>...]";
 
 static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
@@ -135,7 +135,7 @@ static int builtin_diff_index(struct rev_info *revs,
            revs->max_count != -1 || revs->min_age != -1 ||
            revs->max_age != -1)
                usage(builtin_diff_usage);
-       if (read_cache_preload(revs->diffopt.paths) < 0) {
+       if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
                perror("read_cache_preload");
                return -1;
        }
@@ -237,7 +237,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
                revs->combine_merges = revs->dense_combined_merges = 1;
 
        setup_work_tree();
-       if (read_cache_preload(revs->diffopt.paths) < 0) {
+       if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
                perror("read_cache_preload");
                return -1;
        }
@@ -371,14 +371,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                }
                die("unhandled object '%s' given.", name);
        }
-       if (rev.prune_data) {
-               const char **pathspec = rev.prune_data;
-               while (*pathspec) {
-                       if (!path)
-                               path = *pathspec;
-                       paths++;
-                       pathspec++;
-               }
+       if (rev.prune_data.nr) {
+               if (!path)
+                       path = rev.prune_data.items[0].match;
+               paths += rev.prune_data.nr;
        }
 
        /*
index 834ec8b464d0ce5845a2312766554b60ed5d5d7e..ba57457cc54812ba935c5259277e12d72463f63f 100644 (file)
@@ -27,6 +27,7 @@ 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 full_tree;
 
 static int parse_opt_signed_tag_mode(const struct option *opt,
                                     const char *arg, int unset)
@@ -147,10 +148,47 @@ static void handle_object(const unsigned char *sha1)
        free(buf);
 }
 
+static int depth_first(const void *a_, const void *b_)
+{
+       const struct diff_filepair *a = *((const struct diff_filepair **)a_);
+       const struct diff_filepair *b = *((const struct diff_filepair **)b_);
+       const char *name_a, *name_b;
+       int len_a, len_b, len;
+       int cmp;
+
+       name_a = a->one ? a->one->path : a->two->path;
+       name_b = b->one ? b->one->path : b->two->path;
+
+       len_a = strlen(name_a);
+       len_b = strlen(name_b);
+       len = (len_a < len_b) ? len_a : len_b;
+
+       /* strcmp will sort 'd' before 'd/e', we want 'd/e' before 'd' */
+       cmp = memcmp(name_a, name_b, len);
+       if (cmp)
+               return cmp;
+       cmp = len_b - len_a;
+       if (cmp)
+               return cmp;
+       /*
+        * Move 'R'ename entries last so that all references of the file
+        * appear in the output before it is renamed (e.g., when a file
+        * was copied and renamed in the same commit).
+        */
+       return (a->status == 'R') - (b->status == 'R');
+}
+
 static void show_filemodify(struct diff_queue_struct *q,
                            struct diff_options *options, void *data)
 {
        int i;
+
+       /*
+        * Handle files below a directory first, in case they are all deleted
+        * and the directory changes to a file or symlink.
+        */
+       qsort(q->queue, q->nr, sizeof(q->queue[0]), depth_first);
+
        for (i = 0; i < q->nr; i++) {
                struct diff_filespec *ospec = q->queue[i]->one;
                struct diff_filespec *spec = q->queue[i]->two;
@@ -241,7 +279,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
                message += 2;
 
        if (commit->parents &&
-           get_object_mark(&commit->parents->item->object) != 0) {
+           get_object_mark(&commit->parents->item->object) != 0 &&
+           !full_tree) {
                parse_commit(commit->parents->item);
                diff_tree_sha1(commit->parents->item->tree->object.sha1,
                               commit->tree->object.sha1, "", &rev->diffopt);
@@ -281,6 +320,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
                i++;
        }
 
+       if (full_tree)
+               printf("deleteall\n");
        log_tree_diff_flush(rev);
        rev->diffopt.output_format = saved_output_format;
 
@@ -565,7 +606,7 @@ static void import_marks(char *input_file)
 int cmd_fast_export(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
-       struct object_array commits = { 0, 0, NULL };
+       struct object_array commits = OBJECT_ARRAY_INIT;
        struct string_list extra_refs = STRING_LIST_INIT_NODUP;
        struct commit *commit;
        char *export_filename = NULL, *import_filename = NULL;
@@ -584,6 +625,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                             "Import marks from this file"),
                OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
                             "Fake a tagger when tags lack one"),
+               OPT_BOOLEAN(0, "full-tree", &full_tree,
+                            "Output full tree for each commit"),
                { OPTION_NEGBIT, 0, "data", &no_data, NULL,
                        "Skip output of blob data",
                        PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
@@ -608,6 +651,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        if (import_filename)
                import_marks(import_filename);
 
+       if (import_filename && revs.prune_data.nr)
+               full_tree = 1;
+
        get_tags_and_duplicates(&revs.pending, &extra_refs);
 
        if (prepare_revision_walk(&revs))
index dbd8b7bcc8b5ddea9bec3a0c774346007cb0d31c..b9994139345834a58b08a5ce57cf59c124e21760 100644 (file)
@@ -47,7 +47,7 @@ static void rev_list_push(struct commit *commit, int mark)
                        if (parse_commit(commit))
                                return;
 
-               insert_by_date(commit, &rev_list);
+               commit_list_insert_by_date(commit, &rev_list);
 
                if (!(commit->object.flags & COMMON))
                        non_common_revs++;
@@ -436,7 +436,7 @@ static int mark_complete(const char *path, const unsigned char *sha1, int flag,
        if (o && o->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *)o;
                commit->object.flags |= COMPLETE;
-               insert_by_date(commit, &complete);
+               commit_list_insert_by_date(commit, &complete);
        }
        return 0;
 }
index ea14d5dc6b00b475b8817768b7d35d0bb54d5236..357f3cdbbfd601e2ce3f1261a4d6b30c1257cda4 100644 (file)
 #include "parse-options.h"
 #include "sigchain.h"
 #include "transport.h"
+#include "submodule.h"
 
 static const char * const builtin_fetch_usage[] = {
        "git fetch [<options>] [<repository> [<refspec>...]]",
        "git fetch [<options>] <group>",
-       "git fetch --multiple [<options>] [<repository> | <group>]...",
+       "git fetch --multiple [<options>] [(<repository> | <group>)...]",
        "git fetch --all [<options>]",
        NULL
 };
@@ -27,13 +28,20 @@ enum {
        TAGS_SET = 2
 };
 
+enum {
+       RECURSE_SUBMODULES_OFF = 0,
+       RECURSE_SUBMODULES_DEFAULT = 1,
+       RECURSE_SUBMODULES_ON = 2
+};
+
 static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
-static int progress;
+static int progress, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
 static struct strbuf default_rla = STRBUF_INIT;
 static struct transport *transport;
+static const char *submodule_prefix = "";
 
 static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
@@ -43,8 +51,7 @@ static struct option builtin_fetch_options[] = {
                    "append to .git/FETCH_HEAD instead of overwriting"),
        OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
                   "path to upload pack on remote end"),
-       OPT_BOOLEAN('f', "force", &force,
-                   "force overwrite of local branch"),
+       OPT__FORCE(&force, "force overwrite of local branch"),
        OPT_BOOLEAN('m', "multiple", &multiple,
                    "fetch from multiple remotes"),
        OPT_SET_INT('t', "tags", &tags,
@@ -52,7 +59,10 @@ static struct option builtin_fetch_options[] = {
        OPT_SET_INT('n', NULL, &tags,
                    "do not fetch all tags (--no-tags)", TAGS_UNSET),
        OPT_BOOLEAN('p', "prune", &prune,
-                   "prune tracking branches no longer on remote"),
+                   "prune remote-tracking branches no longer on remote"),
+       OPT_SET_INT(0, "recurse-submodules", &recurse_submodules,
+                   "control recursive fetching of submodules",
+                   RECURSE_SUBMODULES_ON),
        OPT_BOOLEAN(0, "dry-run", &dry_run,
                    "dry run"),
        OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
@@ -61,6 +71,8 @@ static struct option builtin_fetch_options[] = {
        OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
        OPT_STRING(0, "depth", &depth, "DEPTH",
                   "deepen history of shallow clone"),
+       { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "DIR",
+                  "prepend this to submodule path output", PARSE_OPT_HIDDEN },
        OPT_END()
 };
 
@@ -98,7 +110,7 @@ static void add_merge_config(struct ref **head,
                        continue;
 
                /*
-                * Not fetched to a tracking branch?  We need to fetch
+                * Not fetched to a remote-tracking branch?  We need to fetch
                 * it anyway to allow this branch's "branch.$name.merge"
                 * to be honored by 'git pull', but we do not have to
                 * fail if branch.$name.merge is misconfigured to point
@@ -146,7 +158,10 @@ static struct ref *get_ref_map(struct transport *transport,
                struct remote *remote = transport->remote;
                struct branch *branch = branch_get(NULL);
                int has_merge = branch_has_merge_config(branch);
-               if (remote && (remote->fetch_refspec_nr || has_merge)) {
+               if (remote &&
+                   (remote->fetch_refspec_nr ||
+                    /* Note: has_merge implies non-NULL branch->remote_name */
+                    (has_merge && !strcmp(branch->remote_name, remote->name)))) {
                        for (i = 0; i < remote->fetch_refspec_nr; i++) {
                                get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0);
                                if (remote->fetch[i].dst &&
@@ -160,6 +175,8 @@ static struct ref *get_ref_map(struct transport *transport,
                         * if the remote we're fetching from is the same
                         * as given in branch.<name>.remote, we add the
                         * ref given in branch.<name>.merge, too.
+                        *
+                        * Note: has_merge implies non-NULL branch->remote_name
                         */
                        if (has_merge &&
                            !strcmp(branch->remote_name, remote->name))
@@ -354,7 +371,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                        what = rm->name + 10;
                }
                else if (!prefixcmp(rm->name, "refs/remotes/")) {
-                       kind = "remote branch";
+                       kind = "remote-tracking branch";
                        what = rm->name + 13;
                }
                else {
@@ -659,10 +676,12 @@ static int do_fetch(struct transport *transport,
 
        for_each_ref(add_existing, &existing_refs);
 
-       if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
-               tags = TAGS_SET;
-       if (transport->remote->fetch_tags == -1)
-               tags = TAGS_UNSET;
+       if (tags == TAGS_DEFAULT) {
+               if (transport->remote->fetch_tags == 2)
+                       tags = TAGS_SET;
+               if (transport->remote->fetch_tags == -1)
+                       tags = TAGS_UNSET;
+       }
 
        if (!transport->get_refs_list || !transport->fetch)
                die("Don't know how to fetch from %s", transport->url);
@@ -777,28 +796,36 @@ static int add_remote_or_group(const char *name, struct string_list *list)
        return 1;
 }
 
-static int fetch_multiple(struct string_list *list)
+static void add_options_to_argv(int *argc, const char **argv)
 {
-       int i, result = 0;
-       const char *argv[11] = { "fetch", "--append" };
-       int argc = 2;
-
        if (dry_run)
-               argv[argc++] = "--dry-run";
+               argv[(*argc)++] = "--dry-run";
        if (prune)
-               argv[argc++] = "--prune";
+               argv[(*argc)++] = "--prune";
        if (update_head_ok)
-               argv[argc++] = "--update-head-ok";
+               argv[(*argc)++] = "--update-head-ok";
        if (force)
-               argv[argc++] = "--force";
+               argv[(*argc)++] = "--force";
        if (keep)
-               argv[argc++] = "--keep";
+               argv[(*argc)++] = "--keep";
+       if (recurse_submodules == RECURSE_SUBMODULES_ON)
+               argv[(*argc)++] = "--recurse-submodules";
        if (verbosity >= 2)
-               argv[argc++] = "-v";
+               argv[(*argc)++] = "-v";
        if (verbosity >= 1)
-               argv[argc++] = "-v";
+               argv[(*argc)++] = "-v";
        else if (verbosity < 0)
-               argv[argc++] = "-q";
+               argv[(*argc)++] = "-q";
+
+}
+
+static int fetch_multiple(struct string_list *list)
+{
+       int i, result = 0;
+       const char *argv[12] = { "fetch", "--append" };
+       int argc = 2;
+
+       add_options_to_argv(&argc, argv);
 
        if (!append && !dry_run) {
                int errcode = truncate_fetch_head();
@@ -919,6 +946,21 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
+               const char *options[10];
+               int num_options = 0;
+               /* Set recursion as default when we already are recursing */
+               if (submodule_prefix[0])
+                       set_config_fetch_recurse_submodules(1);
+               gitmodules_config();
+               git_config(submodule_config, NULL);
+               add_options_to_argv(&num_options, options);
+               result = fetch_populated_submodules(num_options, options,
+                                                   submodule_prefix,
+                                                   recurse_submodules == RECURSE_SUBMODULES_ON,
+                                                   verbosity < 0);
+       }
+
        /* All names were strdup()ed or strndup()ed */
        list.strdup_strings = 1;
        string_list_clear(&list, 0);
index a76cd4e9d131eb76edb8a88f675dc14d4b3f597e..5189b16c9e59c20b449435c413c34da9f7bf9baa 100644 (file)
@@ -7,21 +7,22 @@
 #include "string-list.h"
 
 static const char * const fmt_merge_msg_usage[] = {
-       "git fmt-merge-msg [--log|--no-log] [--file <file>]",
+       "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]",
        NULL
 };
 
-static int merge_summary;
+static int shortlog_len;
 
 static int fmt_merge_msg_config(const char *key, const char *value, void *cb)
 {
-       static int found_merge_log = 0;
-       if (!strcmp("merge.log", key)) {
-               found_merge_log = 1;
-               merge_summary = git_config_bool(key, value);
+       if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
+               int is_bool;
+               shortlog_len = git_config_bool_or_int(key, value, &is_bool);
+               if (!is_bool && shortlog_len < 0)
+                       return error("%s: negative length %s", key, value);
+               if (is_bool && shortlog_len)
+                       shortlog_len = DEFAULT_MERGE_LOG_LEN;
        }
-       if (!found_merge_log && !strcmp("merge.summary", key))
-               merge_summary = git_config_bool(key, value);
        return 0;
 }
 
@@ -99,8 +100,8 @@ static int handle_line(char *line)
                origin = line;
                string_list_append(&src_data->tag, origin + 4);
                src_data->head_status |= 2;
-       } else if (!prefixcmp(line, "remote branch ")) {
-               origin = line + 14;
+       } else if (!prefixcmp(line, "remote-tracking branch ")) {
+               origin = line + strlen("remote-tracking branch ");
                string_list_append(&src_data->r_branch, origin);
                src_data->head_status |= 2;
        } else {
@@ -232,7 +233,7 @@ static void do_fmt_merge_msg_title(struct strbuf *out,
                if (src_data->r_branch.nr) {
                        strbuf_addstr(out, subsep);
                        subsep = ", ";
-                       print_joined("remote branch ", "remote branches ",
+                       print_joined("remote-tracking branch ", "remote-tracking branches ",
                                        &src_data->r_branch, out);
                }
                if (src_data->tag.nr) {
@@ -255,9 +256,9 @@ static void do_fmt_merge_msg_title(struct strbuf *out,
                strbuf_addf(out, " into %s\n", current_branch);
 }
 
-static int do_fmt_merge_msg(int merge_title, int merge_summary,
-       struct strbuf *in, struct strbuf *out) {
-       int limit = 20, i = 0, pos = 0;
+static int do_fmt_merge_msg(int merge_title, struct strbuf *in,
+       struct strbuf *out, int shortlog_len) {
+       int i = 0, pos = 0;
        unsigned char head_sha1[20];
        const char *current_branch;
 
@@ -288,7 +289,7 @@ static int do_fmt_merge_msg(int merge_title, int merge_summary,
        if (merge_title)
                do_fmt_merge_msg_title(out, current_branch);
 
-       if (merge_summary) {
+       if (shortlog_len) {
                struct commit *head;
                struct rev_info rev;
 
@@ -303,27 +304,30 @@ static int do_fmt_merge_msg(int merge_title, int merge_summary,
 
                for (i = 0; i < origins.nr; i++)
                        shortlog(origins.items[i].string, origins.items[i].util,
-                                       head, &rev, limit, out);
+                                       head, &rev, shortlog_len, out);
        }
        return 0;
 }
 
-int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
-       return do_fmt_merge_msg(1, merge_summary, in, out);
-}
-
-int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out) {
-       return do_fmt_merge_msg(0, 1, in, out);
+int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
+                 int merge_title, int shortlog_len) {
+       return do_fmt_merge_msg(merge_title, in, out, shortlog_len);
 }
 
 int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
 {
        const char *inpath = NULL;
+       const char *message = NULL;
        struct option options[] = {
-               OPT_BOOLEAN(0, "log",     &merge_summary, "populate log with the shortlog"),
-               { OPTION_BOOLEAN, 0, "summary", &merge_summary, NULL,
+               { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
+                 "populate log with at most <n> entries from shortlog",
+                 PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
+               { OPTION_INTEGER, 0, "summary", &shortlog_len, "n",
                  "alias for --log (deprecated)",
-                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+                 PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, NULL,
+                 DEFAULT_MERGE_LOG_LEN },
+               OPT_STRING('m', "message", &message, "text",
+                       "use <text> as start of message"),
                OPT_FILENAME('F', "file", &inpath, "file to read from"),
                OPT_END()
        };
@@ -337,6 +341,14 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
                             0);
        if (argc > 0)
                usage_with_options(fmt_merge_msg_usage, options);
+       if (message && !shortlog_len) {
+               char nl = '\n';
+               write_in_full(STDOUT_FILENO, message, strlen(message));
+               write_in_full(STDOUT_FILENO, &nl, 1);
+               return 0;
+       }
+       if (shortlog_len < 0)
+               die("Negative --log=%d", shortlog_len);
 
        if (inpath && strcmp(inpath, "-")) {
                in = fopen(inpath, "r");
@@ -346,7 +358,13 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
 
        if (strbuf_read(&input, fileno(in), 0) < 0)
                die_errno("could not read input file");
-       ret = fmt_merge_msg(merge_summary, &input, &output);
+
+       if (message)
+               strbuf_addstr(&output, message);
+       ret = fmt_merge_msg(&input, &output,
+                           message ? 0 : 1,
+                           shortlog_len);
+
        if (ret)
                return ret;
        write_in_full(STDOUT_FILENO, output.buf, output.len);
index a2b28c6962be8ea6f8882bae3d4fde9d972e78d2..89e75c6894e6fdb2abbe464d825f5c7186a14141 100644 (file)
@@ -228,7 +228,8 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
                        v->s = s;
                }
                else if (!strcmp(name, "objectname:short")) {
-                       v->s = find_unique_abbrev(obj->sha1, DEFAULT_ABBREV);
+                       v->s = xstrdup(find_unique_abbrev(obj->sha1,
+                                                         DEFAULT_ABBREV));
                }
        }
 }
index 0929c7f245cbe85a4a68374850871a0ffbcdeec3..6d5ebca7a9bc9333ea8e493057ecce1b4814c252 100644 (file)
@@ -572,7 +572,7 @@ static char const * const fsck_usage[] = {
 };
 
 static struct option fsck_opts[] = {
-       OPT__VERBOSE(&verbose),
+       OPT__VERBOSE(&verbose, "be verbose"),
        OPT_BOOLEAN(0, "unreachable", &show_unreachable, "show unreachable objects"),
        OPT_BOOLEAN(0, "tags", &show_tags, "report tags"),
        OPT_BOOLEAN(0, "root", &show_root, "report root nodes"),
index c304638b7845601f184149d3c48cbc1ea1e195f5..1a80702b3d1c86b55af24be3b3396f17e9d4a21c 100644 (file)
@@ -180,7 +180,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        char buf[80];
 
        struct option builtin_gc_options[] = {
-               OPT__QUIET(&quiet),
+               OPT__QUIET(&quiet, "suppress progress reporting"),
                { OPTION_STRING, 0, "prune", &prune_expire, "date",
                        "prune unreferenced objects",
                        PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
@@ -189,6 +189,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(builtin_gc_usage, builtin_gc_options);
+
        git_config(gc_config, NULL);
 
        if (pack_refs < 0)
index cf6c29fa42d96f8bc8282e779617a60d40796166..c3af8760cc778b4eb835d4fb543f45aa333b214c 100644 (file)
 #include "grep.h"
 #include "quote.h"
 #include "dir.h"
-
-#ifndef NO_PTHREADS
-#include <pthread.h>
 #include "thread-utils.h"
-#endif
 
 static char const * const grep_usage[] = {
-       "git grep [options] [-e] <pattern> [<rev>...] [[--] path...]",
+       "git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]",
        NULL
 };
 
@@ -333,106 +329,6 @@ static int grep_config(const char *var, const char *value, void *cb)
        return 0;
 }
 
-/*
- * 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, int max_depth)
-{
-       int namelen, i;
-       if (!paths || !*paths)
-               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 (is_subdir(name, namelen, match, matchlen, max_depth))
-                       return 1;
-               if (!fnmatch(match, name, 0))
-                       return 1;
-               if (name[namelen-1] != '/')
-                       continue;
-
-               /* We are being asked if the directory ("name") is worth
-                * descending into.
-                *
-                * Find the longest leading directory name that does
-                * not have metacharacter in the pathspec; the name
-                * we are looking at must overlap with that directory.
-                */
-               for (cp = match, meta = NULL; cp - match < matchlen; cp++) {
-                       char ch = *cp;
-                       if (ch == '*' || ch == '[' || ch == '?') {
-                               meta = cp;
-                               break;
-                       }
-               }
-               if (!meta)
-                       meta = cp; /* fully literal */
-
-               if (namelen <= meta - match) {
-                       /* Looking at "Documentation/" and
-                        * the pattern says "Documentation/howto/", or
-                        * "Documentation/diff*.txt".  The name we
-                        * have should match prefix.
-                        */
-                       if (!memcmp(match, name, namelen))
-                               return 1;
-                       continue;
-               }
-
-               if (meta - match < namelen) {
-                       /* Looking at "Documentation/howto/" and
-                        * the pattern says "Documentation/h*";
-                        * match up to "Do.../h"; this avoids descending
-                        * into "Documentation/technical/".
-                        */
-                       if (!memcmp(match, name, meta - match))
-                               return 1;
-                       continue;
-               }
-       }
-       return 0;
-}
-
 static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
 {
        void *data;
@@ -585,7 +481,7 @@ static void run_pager(struct grep_opt *opt, const char *prefix)
        free(argv);
 }
 
-static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
+static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)
 {
        int hit = 0;
        int nr;
@@ -595,7 +491,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, opt->max_depth))
+               if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
                        continue;
                /*
                 * If CE_VALID is on, we assume worktree file and its cache entry
@@ -622,44 +518,29 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
        return hit;
 }
 
-static int grep_tree(struct grep_opt *opt, const char **paths,
-                    struct tree_desc *tree,
-                    const char *tree_name, const char *base)
+static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
+                    struct tree_desc *tree, struct strbuf *base, int tn_len)
 {
-       int len;
-       int hit = 0;
+       int hit = 0, matched = 0;
        struct name_entry entry;
-       char *down;
-       int tn_len = strlen(tree_name);
-       struct strbuf pathbuf;
-
-       strbuf_init(&pathbuf, PATH_MAX + tn_len);
-
-       if (tn_len) {
-               strbuf_add(&pathbuf, tree_name, tn_len);
-               strbuf_addch(&pathbuf, ':');
-               tn_len = pathbuf.len;
-       }
-       strbuf_addstr(&pathbuf, base);
-       len = pathbuf.len;
+       int old_baselen = base->len;
 
        while (tree_entry(tree, &entry)) {
                int te_len = tree_entry_len(entry.path, entry.sha1);
-               pathbuf.len = len;
-               strbuf_add(&pathbuf, entry.path, te_len);
-
-               if (S_ISDIR(entry.mode))
-                       /* Match "abc/" against pathspec to
-                        * decide if we want to descend into "abc"
-                        * directory.
-                        */
-                       strbuf_addch(&pathbuf, '/');
-
-               down = pathbuf.buf + tn_len;
-               if (!pathspec_matches(paths, down, opt->max_depth))
-                       ;
-               else if (S_ISREG(entry.mode))
-                       hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
+
+               if (matched != 2) {
+                       matched = tree_entry_interesting(&entry, base, tn_len, pathspec);
+                       if (matched == -1)
+                               break; /* no more matches */
+                       if (!matched)
+                               continue;
+               }
+
+               strbuf_add(base, entry.path, te_len);
+
+               if (S_ISREG(entry.mode)) {
+                       hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len);
+               }
                else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
                        struct tree_desc sub;
@@ -670,18 +551,21 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
                        if (!data)
                                die("unable to read tree (%s)",
                                    sha1_to_hex(entry.sha1));
+
+                       strbuf_addch(base, '/');
                        init_tree_desc(&sub, data, size);
-                       hit |= grep_tree(opt, paths, &sub, tree_name, down);
+                       hit |= grep_tree(opt, pathspec, &sub, base, tn_len);
                        free(data);
                }
+               strbuf_setlen(base, old_baselen);
+
                if (hit && opt->status_only)
                        break;
        }
-       strbuf_release(&pathbuf);
        return hit;
 }
 
-static int grep_object(struct grep_opt *opt, const char **paths,
+static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                       struct object *obj, const char *name)
 {
        if (obj->type == OBJ_BLOB)
@@ -690,20 +574,30 @@ static int grep_object(struct grep_opt *opt, const char **paths,
                struct tree_desc tree;
                void *data;
                unsigned long size;
-               int hit;
+               struct strbuf base;
+               int hit, len;
+
                data = read_object_with_reference(obj->sha1, tree_type,
                                                  &size, NULL);
                if (!data)
                        die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
+
+               len = name ? strlen(name) : 0;
+               strbuf_init(&base, PATH_MAX + len + 1);
+               if (len) {
+                       strbuf_add(&base, name, len);
+                       strbuf_addch(&base, ':');
+               }
                init_tree_desc(&tree, data, size);
-               hit = grep_tree(opt, paths, &tree, name, "");
+               hit = grep_tree(opt, pathspec, &tree, &base, base.len);
+               strbuf_release(&base);
                free(data);
                return hit;
        }
        die("unable to grep from object of type %s", typename(obj->type));
 }
 
-static int grep_objects(struct grep_opt *opt, const char **paths,
+static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
                        const struct object_array *list)
 {
        unsigned int i;
@@ -713,7 +607,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths,
        for (i = 0; i < nr; i++) {
                struct object *real_obj;
                real_obj = deref_tag(list->objects[i].item, NULL, 0);
-               if (grep_object(opt, paths, real_obj, list->objects[i].name)) {
+               if (grep_object(opt, pathspec, real_obj, list->objects[i].name)) {
                        hit = 1;
                        if (opt->status_only)
                                break;
@@ -722,7 +616,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths,
        return hit;
 }
 
-static int grep_directory(struct grep_opt *opt, const char **paths)
+static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec)
 {
        struct dir_struct dir;
        int i, hit = 0;
@@ -730,7 +624,7 @@ static int grep_directory(struct grep_opt *opt, const char **paths)
        memset(&dir, 0, sizeof(dir));
        setup_standard_excludes(&dir);
 
-       fill_directory(&dir, paths);
+       fill_directory(&dir, pathspec->raw);
        for (i = 0; i < dir.nr; i++) {
                hit |= grep_file(opt, dir.entries[i]->name);
                if (hit && opt->status_only)
@@ -834,12 +728,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        int external_grep_allowed__ignored;
        const char *show_in_pager = NULL, *default_pager = "dummy";
        struct grep_opt opt;
-       struct object_array list = { 0, 0, NULL };
+       struct object_array list = OBJECT_ARRAY_INIT;
        const char **paths = NULL;
+       struct pathspec pathspec;
        struct string_list path_list = STRING_LIST_INIT_NODUP;
        int i;
        int dummy;
-       int nongit = 0, use_index = 1;
+       int use_index = 1;
        struct option options[] = {
                OPT_BOOLEAN(0, "cached", &cached,
                        "search in index instead of in the work tree"),
@@ -915,8 +810,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
                  close_callback },
-               OPT_BOOLEAN('q', "quiet", &opt.status_only,
-                           "indicate hit with exit status without output"),
+               OPT__QUIET(&opt.status_only,
+                          "indicate hit with exit status without output"),
                OPT_BOOLEAN(0, "all-match", &opt.all_match,
                        "show only matches from files that match all patterns"),
                OPT_GROUP(""),
@@ -930,8 +825,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       prefix = setup_git_directory_gently(&nongit);
-
        /*
         * 'git grep -h', unlike 'git grep -h <pattern>', is a request
         * to show usage information and exit.
@@ -976,7 +869,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_STOP_AT_NON_OPTION |
                             PARSE_OPT_NO_INTERNAL_HELP);
 
-       if (use_index && nongit)
+       if (use_index && !startup_info->have_repository)
                /* die the same way as if we did it at the beginning */
                setup_git_directory();
 
@@ -1065,6 +958,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                paths[0] = prefix;
                paths[1] = NULL;
        }
+       init_pathspec(&pathspec, paths);
+       pathspec.max_depth = opt.max_depth;
+       pathspec.recursive = 1;
 
        if (show_in_pager && (cached || list.nr))
                die("--open-files-in-pager only works on the worktree");
@@ -1095,16 +991,16 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        die("--cached cannot be used with --no-index.");
                if (list.nr)
                        die("--no-index cannot be used with revs.");
-               hit = grep_directory(&opt, paths);
+               hit = grep_directory(&opt, &pathspec);
        } else if (!list.nr) {
                if (!cached)
                        setup_work_tree();
 
-               hit = grep_cache(&opt, paths, cached);
+               hit = grep_cache(&opt, &pathspec, cached);
        } else {
                if (cached)
                        die("both --cached and trees are given.");
-               hit = grep_objects(&opt, paths, &list);
+               hit = grep_objects(&opt, &pathspec, &list);
        }
 
        if (use_threads)
index fad76bf7a84a6a103de20732752119d5245e9638..8dc5c0b5410d4bb57607bab690126f22d954dd7a 100644 (file)
@@ -11,7 +11,7 @@
 #include "exec_cmd.h"
 
 static const char index_pack_usage[] =
-"git index-pack [-v] [-o <index-file>] [{ --keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
+"git index-pack [-v] [-o <index-file>] [ --keep | --keep=<msg> ] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
 
 struct object_entry
 {
@@ -161,7 +161,7 @@ static void use(int bytes)
        input_offset += bytes;
 
        /* make sure off_t is sufficiently large not to wrap */
-       if (consumed_bytes > consumed_bytes + bytes)
+       if (signed_add_overflows(consumed_bytes, bytes))
                die("pack too large for current definition of off_t");
        consumed_bytes += bytes;
 }
@@ -886,25 +886,9 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
 
        read_replace_refs = 0;
 
-       /*
-        * We wish to read the repository's config file if any, and
-        * for that it is necessary to call setup_git_directory_gently().
-        * However if the cwd was inside .git/objects/pack/ then we need
-        * to go back there or all the pack name arguments will be wrong.
-        * And in that case we cannot rely on any prefix returned by
-        * setup_git_directory_gently() either.
-        */
-       {
-               char cwd[PATH_MAX+1];
-               int nongit;
-
-               if (!getcwd(cwd, sizeof(cwd)-1))
-                       die("Unable to get current working directory");
-               setup_git_directory_gently(&nongit);
-               git_config(git_index_pack_config, NULL);
-               if (chdir(cwd))
-                       die("Cannot come back to cwd");
-       }
+       git_config(git_index_pack_config, NULL);
+       if (prefix && chdir(prefix))
+               die("Cannot come back to cwd");
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
index 0271285fad6ad532a6133838f7188498476fd77b..e3af9eaa877089639d6ba4f47d7155a0c1ee106e 100644 (file)
@@ -294,11 +294,26 @@ static int create_default_files(const char *template_path)
        return reinit;
 }
 
+static void create_object_directory(void)
+{
+       const char *object_directory = get_object_directory();
+       int len = strlen(object_directory);
+       char *path = xmalloc(len + 40);
+
+       memcpy(path, object_directory, len);
+
+       safe_create_dir(object_directory, 1);
+       strcpy(path+len, "/pack");
+       safe_create_dir(path, 1);
+       strcpy(path+len, "/info");
+       safe_create_dir(path, 1);
+
+       free(path);
+}
+
 int init_db(const char *template_dir, unsigned int flags)
 {
-       const char *sha1_dir;
-       char *path;
-       int len, reinit;
+       int reinit;
 
        safe_create_dir(get_git_dir(), 0);
 
@@ -313,16 +328,7 @@ int init_db(const char *template_dir, unsigned int flags)
 
        reinit = create_default_files(template_dir);
 
-       sha1_dir = get_object_directory();
-       len = strlen(sha1_dir);
-       path = xmalloc(len + 40);
-       memcpy(path, sha1_dir, len);
-
-       safe_create_dir(sha1_dir, 1);
-       strcpy(path+len, "/pack");
-       safe_create_dir(path, 1);
-       strcpy(path+len, "/info");
-       safe_create_dir(path, 1);
+       create_object_directory();
 
        if (shared_repository) {
                char buf[10];
@@ -408,6 +414,7 @@ static const char *const init_db_usage[] = {
 int cmd_init_db(int argc, const char **argv, const char *prefix)
 {
        const char *git_dir;
+       const char *work_tree;
        const char *template_dir = NULL;
        unsigned int flags = 0;
        const struct option init_db_options[] = {
@@ -474,8 +481,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
         * without --bare.  Catch the error early.
         */
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
-       if ((!git_dir || is_bare_repository_cfg == 1)
-           && getenv(GIT_WORK_TREE_ENVIRONMENT))
+       work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
+       if ((!git_dir || is_bare_repository_cfg == 1) && work_tree)
                die("%s (or --work-tree=<directory>) not allowed without "
                    "specifying %s (or --git-dir=<directory>)",
                    GIT_WORK_TREE_ENVIRONMENT,
@@ -504,10 +511,18 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                        if (!getcwd(git_work_tree_cfg, PATH_MAX))
                                die_errno ("Cannot access current working directory");
                }
+               if (work_tree)
+                       set_git_work_tree(make_absolute_path(work_tree));
+               else
+                       set_git_work_tree(git_work_tree_cfg);
                if (access(get_git_work_tree(), X_OK))
                        die_errno ("Cannot access work tree '%s'",
                                   get_git_work_tree());
        }
+       else {
+               if (work_tree)
+                       set_git_work_tree(make_absolute_path(work_tree));
+       }
 
        set_git_dir(make_absolute_path(git_dir));
 
index 08b872263cd646ec9d7dc8ad91273c71b1af2b69..f5ed690c435423662808fe0ff7f1e256374586db 100644 (file)
@@ -89,7 +89,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
                rev->always_show_header = 0;
        if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
                rev->always_show_header = 0;
-               if (rev->diffopt.nr_paths != 1)
+               if (rev->diffopt.pathspec.nr != 1)
                        usage("git logs can only follow renames on one pathname at a time");
        }
        for (i = 1; i < argc; i++) {
@@ -125,6 +125,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
                rev->show_decorations = 1;
                load_ref_decorations(decoration_style);
        }
+       setup_pager();
 }
 
 /*
@@ -328,8 +329,7 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)
        struct strbuf out = STRBUF_INIT;
 
        pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode,
-               git_log_output_encoding ?
-               git_log_output_encoding: git_commit_encoding);
+               get_log_output_encoding());
        printf("%s", out.buf);
        strbuf_release(&out);
 }
@@ -491,12 +491,6 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
        rev.use_terminator = 1;
        rev.always_show_header = 1;
 
-       /*
-        * We get called through "git reflog", so unlike the other log
-        * routines, we need to set up our pager manually..
-        */
-       setup_pager();
-
        return cmd_log_walk(&rev);
 }
 
@@ -1061,8 +1055,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.verbose_header = 1;
        rev.diff = 1;
-       rev.combine_merges = 0;
-       rev.ignore_merges = 1;
+       rev.no_merges = 1;
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
        rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
@@ -1165,6 +1158,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
        if (!use_stdout)
                output_directory = set_outdir(prefix, output_directory);
+       else
+               setup_pager();
 
        if (output_directory) {
                if (use_stdout)
@@ -1233,10 +1228,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
-               /* ignore merges */
-               if (commit->parents && commit->parents->next)
-                       continue;
-
                if (ignore_if_in_upstream &&
                                has_commit_patch_id(commit, &ids))
                        continue;
@@ -1375,7 +1366,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
 
        struct option options[] = {
                OPT__ABBREV(&abbrev),
-               OPT__VERBOSE(&verbose),
+               OPT__VERBOSE(&verbose, "be verbose"),
                OPT_END()
        };
 
index bb4f612b3d48c453d551f251b65887beb283ec7b..fb2d5f4b1fb0ce9ef2fb0c4099b5fea6d07b6838 100644 (file)
@@ -424,7 +424,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
 }
 
 static const char * const ls_files_usage[] = {
-       "git ls-files [options] [<file>]*",
+       "git ls-files [options] [<file>...]",
        NULL
 };
 
@@ -530,6 +530,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                OPT_END()
        };
 
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(ls_files_usage, builtin_ls_files_options);
+
        memset(&dir, 0, sizeof(dir));
        prefix = cmd_prefix;
        if (prefix)
index 34480cfad6a8842d65ae61f6257c597a2a987b33..97eed4012ba23cfe7f13cf2b6c160ade54f6b2d1 100644 (file)
@@ -32,7 +32,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
 {
        int i;
        const char *dest = NULL;
-       int nongit;
        unsigned flags = 0;
        int quiet = 0;
        const char *uploadpack = NULL;
@@ -42,8 +41,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        struct transport *transport;
        const struct ref *ref;
 
-       setup_git_directory_gently(&nongit);
-
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
index dc86b0d9a997f98ad43ca2897055a92b8a3eb7d0..f73e6bd9626a0e929a513fed1fd1227c28732ec7 100644 (file)
@@ -24,7 +24,7 @@ static int chomp_prefix;
 static const char *ls_tree_prefix;
 
 static const  char * const ls_tree_usage[] = {
-       "git ls-tree [<options>] <tree-ish> [path...]",
+       "git ls-tree [<options>] <tree-ish> [<path>...]",
        NULL
 };
 
@@ -52,6 +52,8 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
                speclen = strlen(spec);
                if (speclen <= len)
                        continue;
+               if (spec[len] && spec[len] != '/')
+                       continue;
                if (memcmp(pathname, spec, len))
                        continue;
                return 1;
index 2320d981ceab220e287e42c21f3c9f3be5b3722f..71e6262a87d883a4a82953db5f742bccf99e5c77 100644 (file)
@@ -1032,7 +1032,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
         */
        git_config(git_mailinfo_config, NULL);
 
-       def_charset = (git_commit_encoding ? git_commit_encoding : "UTF-8");
+       def_charset = get_commit_output_encoding();
        metainfo_charset = def_charset;
 
        while (1 < argc && argv[1][0] == '-') {
index 99654d02221c13ff98f268ad2dee99f16e794750..2d4327801e4f7766328389311c87dd63d4c5f8e4 100644 (file)
@@ -10,7 +10,7 @@
 #include "strbuf.h"
 
 static const char git_mailsplit_usage[] =
-"git mailsplit [-d<prec>] [-f<n>] [-b] [--keep-cr] -o<directory> [<mbox>|<Maildir>...]";
+"git mailsplit [-d<prec>] [-f<n>] [-b] [--keep-cr] -o<directory> [(<mbox>|<Maildir>)...]";
 
 static int is_from_line(const char *line, int len)
 {
index 54e7ec22370ce63150ddc93ebe252bea09f5064a..96dd160731ce29b18be646893057929bab97cb8a 100644 (file)
@@ -23,7 +23,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 }
 
 static const char * const merge_base_usage[] = {
-       "git merge-base [-a|--all] <commit> <commit>...",
+       "git merge-base [-a|--all] [--octopus] <commit> <commit>...",
+       "git merge-base --independent <commit>...",
        NULL
 };
 
@@ -41,21 +42,58 @@ static struct commit *get_commit_reference(const char *arg)
        return r;
 }
 
+static int handle_octopus(int count, const char **args, int reduce, int show_all)
+{
+       struct commit_list *revs = NULL;
+       struct commit_list *result;
+       int i;
+
+       if (reduce)
+               show_all = 1;
+
+       for (i = count - 1; i >= 0; i--)
+               commit_list_insert(get_commit_reference(args[i]), &revs);
+
+       result = reduce ? reduce_heads(revs) : get_octopus_merge_bases(revs);
+
+       if (!result)
+               return 1;
+
+       while (result) {
+               printf("%s\n", sha1_to_hex(result->item->object.sha1));
+               if (!show_all)
+                       return 0;
+               result = result->next;
+       }
+
+       return 0;
+}
+
 int cmd_merge_base(int argc, const char **argv, const char *prefix)
 {
        struct commit **rev;
        int rev_nr = 0;
        int show_all = 0;
+       int octopus = 0;
+       int reduce = 0;
 
        struct option options[] = {
-               OPT_BOOLEAN('a', "all", &show_all, "outputs all common ancestors"),
+               OPT_BOOLEAN('a', "all", &show_all, "output all common ancestors"),
+               OPT_BOOLEAN(0, "octopus", &octopus, "find ancestors for a single n-way merge"),
+               OPT_BOOLEAN(0, "independent", &reduce, "list revs not reachable from others"),
                OPT_END()
        };
 
        git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
-       if (argc < 2)
+       if (!octopus && !reduce && argc < 2)
                usage_with_options(merge_base_usage, options);
+       if (reduce && (show_all || octopus))
+               die("--independent cannot be used with other options");
+
+       if (octopus || reduce)
+               return handle_octopus(argc, argv, reduce, show_all);
+
        rev = xmalloc(argc * sizeof(*rev));
        while (argc-- > 0)
                rev[rev_nr++] = get_commit_reference(*argv++);
index b8e9e5ba01658bbdd6e9d712b00da902b6f33fbe..237abd3c0b27b601aca140bfb67a302666ff9cf2 100644 (file)
@@ -28,7 +28,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        xmparam_t xmp = {{0}};
        int ret = 0, i = 0, to_stdout = 0;
        int quiet = 0;
-       int nongit;
+       int prefixlen = 0;
        struct option options[] = {
                OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
                OPT_SET_INT(0, "diff3", &xmp.style, "use a diff3 based merge", XDL_MERGE_DIFF3),
@@ -40,7 +40,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                            XDL_MERGE_FAVOR_UNION),
                OPT_INTEGER(0, "marker-size", &xmp.marker_size,
                            "for conflicts, use this marker size"),
-               OPT__QUIET(&quiet),
+               OPT__QUIET(&quiet, "do not warn about conflicts"),
                OPT_CALLBACK('L', NULL, names, "name",
                             "set labels for file1/orig_file/file2", &label_cb),
                OPT_END(),
@@ -50,8 +50,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        xmp.style = 0;
        xmp.favor = 0;
 
-       prefix = setup_git_directory_gently(&nongit);
-       if (!nongit) {
+       if (startup_info->have_repository) {
                /* Read the configuration file */
                git_config(git_xmerge_config, NULL);
                if (0 <= git_xmerge_style)
@@ -67,10 +66,14 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                                     "%s\n", strerror(errno));
        }
 
+       if (prefix)
+               prefixlen = strlen(prefix);
+
        for (i = 0; i < 3; i++) {
+               const char *fname = prefix_filename(prefix, prefixlen, argv[i]);
                if (!names[i])
                        names[i] = argv[i];
-               if (read_mmfile(mmfs + i, argv[i]))
+               if (read_mmfile(mmfs + i, fname))
                        return -1;
                if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
                        return error("Cannot merge binary files: %s\n",
index d8875d589240e0c78a9e241a7d2bdde1d10ab800..c33091b3ed52bc8539ff82f039ec8c7718f3dcc2 100644 (file)
@@ -2,6 +2,10 @@
 #include "commit.h"
 #include "tag.h"
 #include "merge-recursive.h"
+#include "xdiff-interface.h"
+
+static const char builtin_merge_recursive_usage[] =
+       "git %s <base>... -- <head> <remote> ...";
 
 static const char *better_branch_name(const char *branch)
 {
@@ -29,7 +33,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
                o.subtree_shift = "";
 
        if (argc < 4)
-               usagef("%s <base>... -- <head> <remote> ...", argv[0]);
+               usagef(builtin_merge_recursive_usage, argv[0]);
 
        for (i = 1; i < argc; ++i) {
                const char *arg = argv[i];
@@ -37,15 +41,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
                if (!prefixcmp(arg, "--")) {
                        if (!arg[2])
                                break;
-                       if (!strcmp(arg+2, "ours"))
-                               o.recursive_variant = MERGE_RECURSIVE_OURS;
-                       else if (!strcmp(arg+2, "theirs"))
-                               o.recursive_variant = MERGE_RECURSIVE_THEIRS;
-                       else if (!strcmp(arg+2, "subtree"))
-                               o.subtree_shift = "";
-                       else if (!prefixcmp(arg+2, "subtree="))
-                               o.subtree_shift = arg + 10;
-                       else
+                       if (parse_merge_opt(&o, arg + 2))
                                die("Unknown option %s", arg);
                        continue;
                }
index 9ca7c5c726aa381a980c5a52593d8277717c13bb..b573d9804a4a20b7a05e56db6f81ce6bf0f16cf3 100644 (file)
@@ -42,7 +42,7 @@ static const char * const builtin_merge_usage[] = {
        NULL
 };
 
-static int show_diffstat = 1, option_log, squash;
+static int show_diffstat = 1, shortlog_len, squash;
 static int option_commit = 1, allow_fast_forward = 1;
 static int fast_forward_only;
 static int allow_trivial = 1, have_message;
@@ -54,8 +54,10 @@ static size_t use_strategies_nr, use_strategies_alloc;
 static const char **xopts;
 static size_t xopts_nr, xopts_alloc;
 static const char *branch;
+static int option_renormalize;
 static int verbosity;
 static int allow_rerere_auto;
+static int abort_current_merge;
 
 static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -131,6 +133,7 @@ static struct strategy *get_strategy(const char *name)
 
        ret = xcalloc(1, sizeof(struct strategy));
        ret->name = xstrdup(name);
+       ret->attr = NO_TRIVIAL;
        return ret;
 }
 
@@ -175,8 +178,9 @@ static struct option builtin_merge_options[] = {
        OPT_BOOLEAN(0, "stat", &show_diffstat,
                "show a diffstat at the end of the merge"),
        OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
-       OPT_BOOLEAN(0, "log", &option_log,
-               "add list of one-line log to merge commit message"),
+       { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
+         "add (at most <n>) entries from shortlog to merge commit message",
+         PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
        OPT_BOOLEAN(0, "squash", &squash,
                "create a single commit instead of doing a merge"),
        OPT_BOOLEAN(0, "commit", &option_commit,
@@ -196,6 +200,8 @@ static struct option builtin_merge_options[] = {
                "message to be used for the merge commit (if any)",
                option_parse_message),
        OPT__VERBOSITY(&verbosity),
+       OPT_BOOLEAN(0, "abort", &abort_current_merge,
+               "abort the current in-progress merge"),
        OPT_END()
 };
 
@@ -233,6 +239,24 @@ static void save_state(void)
                die("not a valid object: %s", buffer.buf);
 }
 
+static void read_empty(unsigned const char *sha1, int verbose)
+{
+       int i = 0;
+       const char *args[7];
+
+       args[i++] = "read-tree";
+       if (verbose)
+               args[i++] = "-v";
+       args[i++] = "-m";
+       args[i++] = "-u";
+       args[i++] = EMPTY_TREE_SHA1_HEX;
+       args[i++] = sha1_to_hex(sha1);
+       args[i] = NULL;
+
+       if (run_command_v_opt(args, RUN_GIT_CMD))
+               die("read-tree failed");
+}
+
 static void reset_hard(unsigned const char *sha1, int verbose)
 {
        int i = 0;
@@ -402,7 +426,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
                        goto cleanup;
                }
                if (!prefixcmp(found_ref, "refs/remotes/")) {
-                       strbuf_addf(msg, "%s\t\tremote branch '%s' of .\n",
+                       strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
                }
@@ -439,7 +463,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, NULL)) {
+               if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
                        strbuf_addf(msg,
                                    "%s\t\tbranch '%s'%s of .\n",
                                    sha1_to_hex(remote_head->sha1),
@@ -488,7 +512,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
                buf = xstrdup(v);
                argc = split_cmdline(buf, &argv);
                if (argc < 0)
-                       die("Bad branch.%s.mergeoptions string", branch);
+                       die("Bad branch.%s.mergeoptions string: %s", branch,
+                           split_cmdline_strerror(argc));
                argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
                memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
                argc++;
@@ -503,8 +528,17 @@ static int git_merge_config(const char *k, const char *v, void *cb)
                return git_config_string(&pull_twohead, k, v);
        else if (!strcmp(k, "pull.octopus"))
                return git_config_string(&pull_octopus, k, v);
-       else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary"))
-               option_log = git_config_bool(k, v);
+       else if (!strcmp(k, "merge.renormalize"))
+               option_renormalize = git_config_bool(k, v);
+       else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) {
+               int is_bool;
+               shortlog_len = git_config_bool_or_int(k, v, &is_bool);
+               if (!is_bool && shortlog_len < 0)
+                       return error("%s: negative length %s", k, v);
+               if (is_bool && shortlog_len)
+                       shortlog_len = DEFAULT_MERGE_LOG_LEN;
+               return 0;
+       }
        return git_diff_ui_config(k, v, cb);
 }
 
@@ -550,7 +584,8 @@ static void write_tree_trivial(unsigned char *sha1)
                die("git write-tree failed to write a tree");
 }
 
-int try_merge_command(const char *strategy, struct commit_list *common,
+int try_merge_command(const char *strategy, size_t xopts_nr,
+                     const char **xopts, struct commit_list *common,
                      const char *head_arg, struct commit_list *remotes)
 {
        const char **args;
@@ -626,18 +661,11 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                if (!strcmp(strategy, "subtree"))
                        o.subtree_shift = "";
 
-               for (x = 0; x < xopts_nr; x++) {
-                       if (!strcmp(xopts[x], "ours"))
-                               o.recursive_variant = MERGE_RECURSIVE_OURS;
-                       else if (!strcmp(xopts[x], "theirs"))
-                               o.recursive_variant = MERGE_RECURSIVE_THEIRS;
-                       else if (!strcmp(xopts[x], "subtree"))
-                               o.subtree_shift = "";
-                       else if (!prefixcmp(xopts[x], "subtree="))
-                               o.subtree_shift = xopts[x]+8;
-                       else
+               o.renormalize = option_renormalize;
+
+               for (x = 0; x < xopts_nr; x++)
+                       if (parse_merge_opt(&o, xopts[x]))
                                die("Unknown option for merge-recursive: -X%s", xopts[x]);
-               }
 
                o.branch1 = head_arg;
                o.branch2 = remoteheads->item->util;
@@ -655,7 +683,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                rollback_lock_file(lock);
                return clean ? 0 : 1;
        } else {
-               return try_merge_command(strategy, common, head_arg, remoteheads);
+               return try_merge_command(strategy, xopts_nr, xopts,
+                                               common, head_arg, remoteheads);
        }
 }
 
@@ -708,7 +737,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote
        opts.verbose_update = 1;
        opts.merge = 1;
        opts.fn = twoway_merge;
-       opts.msgs = get_porcelain_error_msgs();
+       setup_unpack_trees_porcelain(&opts, "merge");
 
        trees[nr_trees] = parse_tree_indirect(head);
        if (!trees[nr_trees++])
@@ -820,7 +849,7 @@ static int finish_automerge(struct commit_list *common,
        return 0;
 }
 
-static int suggest_conflicts(void)
+static int suggest_conflicts(int renormalizing)
 {
        FILE *fp;
        int pos;
@@ -899,22 +928,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        const char *best_strategy = NULL, *wt_strategy = NULL;
        struct commit_list **remotes = &remoteheads;
 
-       if (read_cache_unmerged()) {
-               die_resolve_conflict("merge");
-       }
-       if (file_exists(git_path("MERGE_HEAD"))) {
-               /*
-                * There is no unmerged entry, don't advise 'git
-                * add/rm <file>', just 'git commit'.
-                */
-               if (advice_resolve_conflict)
-                       die("You have not concluded your merge (MERGE_HEAD exists).\n"
-                           "Please, commit your changes before you can merge.");
-               else
-                       die("You have not concluded your merge (MERGE_HEAD exists).");
-       }
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(builtin_merge_usage, builtin_merge_options);
 
-       resolve_undo_clear();
        /*
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
@@ -933,6 +949,34 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix, builtin_merge_options,
                        builtin_merge_usage, 0);
+
+       if (abort_current_merge) {
+               int nargc = 2;
+               const char *nargv[] = {"reset", "--merge", NULL};
+
+               if (!file_exists(git_path("MERGE_HEAD")))
+                       die("There is no merge to abort (MERGE_HEAD missing).");
+
+               /* Invoke 'git reset --merge' */
+               return cmd_reset(nargc, nargv, prefix);
+       }
+
+       if (read_cache_unmerged())
+               die_resolve_conflict("merge");
+
+       if (file_exists(git_path("MERGE_HEAD"))) {
+               /*
+                * There is no unmerged entry, don't advise 'git
+                * add/rm <file>', just 'git commit'.
+                */
+               if (advice_resolve_conflict)
+                       die("You have not concluded your merge (MERGE_HEAD exists).\n"
+                           "Please, commit your changes before you can merge.");
+               else
+                       die("You have not concluded your merge (MERGE_HEAD exists).");
+       }
+       resolve_undo_clear();
+
        if (verbosity < 0)
                show_diffstat = 0;
 
@@ -983,7 +1027,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        die("%s - not something we can merge", argv[0]);
                update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
                                DIE_ON_ERR);
-               reset_hard(remote_head->sha1, 0);
+               read_empty(remote_head->sha1, 0);
                return 0;
        } else {
                struct strbuf merge_names = STRBUF_INIT;
@@ -1002,14 +1046,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                for (i = 0; i < argc; i++)
                        merge_name(argv[i], &merge_names);
 
-               if (have_message && option_log)
-                       fmt_merge_msg_shortlog(&merge_names, &merge_msg);
-               else if (!have_message)
-                       fmt_merge_msg(option_log, &merge_names, &merge_msg);
-
-
-               if (!(have_message && !option_log) && merge_msg.len)
-                       strbuf_setlen(&merge_msg, merge_msg.len-1);
+               if (!have_message || shortlog_len) {
+                       fmt_merge_msg(&merge_names, &merge_msg, !have_message,
+                                     shortlog_len);
+                       if (merge_msg.len)
+                               strbuf_setlen(&merge_msg, merge_msg.len - 1);
+               }
        }
 
        if (head_invalid || !argc)
@@ -1305,5 +1347,5 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        "stopped before committing as requested\n");
                return 0;
        } else
-               return suggest_conflicts();
+               return suggest_conflicts(option_renormalize);
 }
index cdbb09473c0c45efff23a63e78b7623cad523350..93e8995d9e704faa041f97f92ba72dabe7839d81 100644 (file)
@@ -55,8 +55,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        int i, newfd;
        int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
        struct option builtin_mv_options[] = {
-               OPT__DRY_RUN(&show_only),
-               OPT_BOOLEAN('f', "force", &force, "force move/rename even if target exists"),
+               OPT__DRY_RUN(&show_only, "dry run"),
+               OPT__FORCE(&force, "force move/rename even if target exists"),
                OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
                OPT_END(),
        };
index 06a38ac8c10126085e6477c205d450da089faae2..31f5c1c971381a9490b717e0413ee9a40260ef39 100644 (file)
@@ -220,7 +220,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
-       struct object_array revs = { 0, 0, NULL };
+       struct object_array revs = OBJECT_ARRAY_INIT;
        int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
        struct name_ref_data data = { 0, 0, NULL };
        struct option opts[] = {
index fbc347c9f09e0da4ba43982a5f7b29882080f150..4d5556e2cb5bccaf0100d9f5019e1a1c493e35e7 100644 (file)
@@ -17,6 +17,7 @@
 #include "run-command.h"
 #include "parse-options.h"
 #include "string-list.h"
+#include "notes-merge.h"
 
 static const char * const git_notes_usage[] = {
        "git notes [--ref <notes_ref>] [list [<object>]]",
@@ -25,8 +26,12 @@ static const char * const git_notes_usage[] = {
        "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
        "git notes [--ref <notes_ref>] edit [<object>]",
        "git notes [--ref <notes_ref>] show [<object>]",
+       "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>",
+       "git notes merge --commit [-v | -q]",
+       "git notes merge --abort [-v | -q]",
        "git notes [--ref <notes_ref>] remove [<object>]",
        "git notes [--ref <notes_ref>] prune [-n | -v]",
+       "git notes [--ref <notes_ref>] get-ref",
        NULL
 };
 
@@ -61,6 +66,13 @@ static const char * const git_notes_show_usage[] = {
        NULL
 };
 
+static const char * const git_notes_merge_usage[] = {
+       "git notes merge [<options>] <notes_ref>",
+       "git notes merge --commit [<options>]",
+       "git notes merge --abort [<options>]",
+       NULL
+};
+
 static const char * const git_notes_remove_usage[] = {
        "git notes remove [<object>]",
        NULL
@@ -71,6 +83,11 @@ static const char * const git_notes_prune_usage[] = {
        NULL
 };
 
+static const char * const git_notes_get_ref_usage[] = {
+       "git notes get-ref",
+       NULL
+};
+
 static const char note_template[] =
        "\n"
        "#\n"
@@ -83,6 +100,16 @@ struct msg_arg {
        struct strbuf buf;
 };
 
+static void expand_notes_ref(struct strbuf *sb)
+{
+       if (!prefixcmp(sb->buf, "refs/notes/"))
+               return; /* we're happy */
+       else if (!prefixcmp(sb->buf, "notes/"))
+               strbuf_insert(sb, 0, "refs/", 5);
+       else
+               strbuf_insert(sb, 0, "refs/notes/", 11);
+}
+
 static int list_each_note(const unsigned char *object_sha1,
                const unsigned char *note_sha1, char *note_path,
                void *cb_data)
@@ -271,18 +298,17 @@ static int parse_reedit_arg(const struct option *opt, const char *arg, int unset
        return parse_reuse_arg(opt, arg, unset);
 }
 
-int commit_notes(struct notes_tree *t, const char *msg)
+void commit_notes(struct notes_tree *t, const char *msg)
 {
-       struct commit_list *parent;
-       unsigned char tree_sha1[20], prev_commit[20], new_commit[20];
        struct strbuf buf = STRBUF_INIT;
+       unsigned char commit_sha1[20];
 
        if (!t)
                t = &default_notes_tree;
        if (!t->initialized || !t->ref || !*t->ref)
                die("Cannot commit uninitialized/unreferenced notes tree");
        if (!t->dirty)
-               return 0; /* don't have to commit an unchanged tree */
+               return; /* don't have to commit an unchanged tree */
 
        /* Prepare commit message and reflog message */
        strbuf_addstr(&buf, "notes: "); /* commit message starts at index 7 */
@@ -290,27 +316,10 @@ int commit_notes(struct notes_tree *t, const char *msg)
        if (buf.buf[buf.len - 1] != '\n')
                strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
 
-       /* Convert notes tree to tree object */
-       if (write_notes_tree(t, tree_sha1))
-               die("Failed to write current notes tree to database");
-
-       /* Create new commit for the tree object */
-       if (!read_ref(t->ref, prev_commit)) { /* retrieve parent commit */
-               parent = xmalloc(sizeof(*parent));
-               parent->item = lookup_commit(prev_commit);
-               parent->next = NULL;
-       } else {
-               hashclr(prev_commit);
-               parent = NULL;
-       }
-       if (commit_tree(buf.buf + 7, tree_sha1, parent, new_commit, NULL))
-               die("Failed to commit notes tree to database");
-
-       /* Update notes ref with new commit */
-       update_ref(buf.buf, t->ref, new_commit, prev_commit, 0, DIE_ON_ERR);
+       create_notes_commit(t, NULL, buf.buf + 7, commit_sha1);
+       update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
 
        strbuf_release(&buf);
-       return 0;
 }
 
 combine_notes_fn parse_combine_notes_fn(const char *v)
@@ -321,6 +330,8 @@ combine_notes_fn parse_combine_notes_fn(const char *v)
                return combine_notes_ignore;
        else if (!strcasecmp(v, "concatenate"))
                return combine_notes_concatenate;
+       else if (!strcasecmp(v, "cat_sort_uniq"))
+               return combine_notes_cat_sort_uniq;
        else
                return NULL;
 }
@@ -538,7 +549,7 @@ static int add(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT",
                        "reuse specified note object", PARSE_OPT_NONEG,
                        parse_reuse_arg},
-               OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
+               OPT__FORCE(&force, "replace existing notes"),
                OPT_END()
        };
 
@@ -573,8 +584,8 @@ static int add(int argc, const char **argv, const char *prefix)
 
        if (is_null_sha1(new_note))
                remove_note(t, object);
-       else
-               add_note(t, object, new_note, combine_notes_overwrite);
+       else if (add_note(t, object, new_note, combine_notes_overwrite))
+               die("BUG: combine_notes_overwrite failed");
 
        snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
                 is_null_sha1(new_note) ? "removed" : "added", "add");
@@ -594,7 +605,7 @@ static int copy(int argc, const char **argv, const char *prefix)
        struct notes_tree *t;
        const char *rewrite_cmd = NULL;
        struct option options[] = {
-               OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
+               OPT__FORCE(&force, "replace existing notes"),
                OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
                OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command",
                           "load rewriting config for <command> (implies "
@@ -653,7 +664,8 @@ static int copy(int argc, const char **argv, const char *prefix)
                goto out;
        }
 
-       add_note(t, object, from_note, combine_notes_overwrite);
+       if (add_note(t, object, from_note, combine_notes_overwrite))
+               die("BUG: combine_notes_overwrite failed");
        commit_notes(t, "Notes added by 'git notes copy'");
 out:
        free_notes(t);
@@ -712,8 +724,8 @@ static int append_edit(int argc, const char **argv, const char *prefix)
 
        if (is_null_sha1(new_note))
                remove_note(t, object);
-       else
-               add_note(t, object, new_note, combine_notes_overwrite);
+       else if (add_note(t, object, new_note, combine_notes_overwrite))
+               die("BUG: combine_notes_overwrite failed");
 
        snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
                 is_null_sha1(new_note) ? "removed" : "added", argv[0]);
@@ -761,6 +773,180 @@ static int show(int argc, const char **argv, const char *prefix)
        return retval;
 }
 
+static int merge_abort(struct notes_merge_options *o)
+{
+       int ret = 0;
+
+       /*
+        * Remove .git/NOTES_MERGE_PARTIAL and .git/NOTES_MERGE_REF, and call
+        * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
+        */
+
+       if (delete_ref("NOTES_MERGE_PARTIAL", NULL, 0))
+               ret += error("Failed to delete ref NOTES_MERGE_PARTIAL");
+       if (delete_ref("NOTES_MERGE_REF", NULL, REF_NODEREF))
+               ret += error("Failed to delete ref NOTES_MERGE_REF");
+       if (notes_merge_abort(o))
+               ret += error("Failed to remove 'git notes merge' worktree");
+       return ret;
+}
+
+static int merge_commit(struct notes_merge_options *o)
+{
+       struct strbuf msg = STRBUF_INIT;
+       unsigned char sha1[20], parent_sha1[20];
+       struct notes_tree *t;
+       struct commit *partial;
+       struct pretty_print_context pretty_ctx;
+
+       /*
+        * Read partial merge result from .git/NOTES_MERGE_PARTIAL,
+        * and target notes ref from .git/NOTES_MERGE_REF.
+        */
+
+       if (get_sha1("NOTES_MERGE_PARTIAL", sha1))
+               die("Failed to read ref NOTES_MERGE_PARTIAL");
+       else if (!(partial = lookup_commit_reference(sha1)))
+               die("Could not find commit from NOTES_MERGE_PARTIAL.");
+       else if (parse_commit(partial))
+               die("Could not parse commit from NOTES_MERGE_PARTIAL.");
+
+       if (partial->parents)
+               hashcpy(parent_sha1, partial->parents->item->object.sha1);
+       else
+               hashclr(parent_sha1);
+
+       t = xcalloc(1, sizeof(struct notes_tree));
+       init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
+
+       o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, 0);
+       if (!o->local_ref)
+               die("Failed to resolve NOTES_MERGE_REF");
+
+       if (notes_merge_commit(o, t, partial, sha1))
+               die("Failed to finalize notes merge");
+
+       /* Reuse existing commit message in reflog message */
+       memset(&pretty_ctx, 0, sizeof(pretty_ctx));
+       format_commit_message(partial, "%s", &msg, &pretty_ctx);
+       strbuf_trim(&msg);
+       strbuf_insert(&msg, 0, "notes: ", 7);
+       update_ref(msg.buf, o->local_ref, sha1,
+                  is_null_sha1(parent_sha1) ? NULL : parent_sha1,
+                  0, DIE_ON_ERR);
+
+       free_notes(t);
+       strbuf_release(&msg);
+       return merge_abort(o);
+}
+
+static int merge(int argc, const char **argv, const char *prefix)
+{
+       struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
+       unsigned char result_sha1[20];
+       struct notes_tree *t;
+       struct notes_merge_options o;
+       int do_merge = 0, do_commit = 0, do_abort = 0;
+       int verbosity = 0, result;
+       const char *strategy = NULL;
+       struct option options[] = {
+               OPT_GROUP("General options"),
+               OPT__VERBOSITY(&verbosity),
+               OPT_GROUP("Merge options"),
+               OPT_STRING('s', "strategy", &strategy, "strategy",
+                          "resolve notes conflicts using the given strategy "
+                          "(manual/ours/theirs/union/cat_sort_uniq)"),
+               OPT_GROUP("Committing unmerged notes"),
+               { OPTION_BOOLEAN, 0, "commit", &do_commit, NULL,
+                       "finalize notes merge by committing unmerged notes",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+               OPT_GROUP("Aborting notes merge resolution"),
+               { OPTION_BOOLEAN, 0, "abort", &do_abort, NULL,
+                       "abort notes merge",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options,
+                            git_notes_merge_usage, 0);
+
+       if (strategy || do_commit + do_abort == 0)
+               do_merge = 1;
+       if (do_merge + do_commit + do_abort != 1) {
+               error("cannot mix --commit, --abort or -s/--strategy");
+               usage_with_options(git_notes_merge_usage, options);
+       }
+
+       if (do_merge && argc != 1) {
+               error("Must specify a notes ref to merge");
+               usage_with_options(git_notes_merge_usage, options);
+       } else if (!do_merge && argc) {
+               error("too many parameters");
+               usage_with_options(git_notes_merge_usage, options);
+       }
+
+       init_notes_merge_options(&o);
+       o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT;
+
+       if (do_abort)
+               return merge_abort(&o);
+       if (do_commit)
+               return merge_commit(&o);
+
+       o.local_ref = default_notes_ref();
+       strbuf_addstr(&remote_ref, argv[0]);
+       expand_notes_ref(&remote_ref);
+       o.remote_ref = remote_ref.buf;
+
+       if (strategy) {
+               if (!strcmp(strategy, "manual"))
+                       o.strategy = NOTES_MERGE_RESOLVE_MANUAL;
+               else if (!strcmp(strategy, "ours"))
+                       o.strategy = NOTES_MERGE_RESOLVE_OURS;
+               else if (!strcmp(strategy, "theirs"))
+                       o.strategy = NOTES_MERGE_RESOLVE_THEIRS;
+               else if (!strcmp(strategy, "union"))
+                       o.strategy = NOTES_MERGE_RESOLVE_UNION;
+               else if (!strcmp(strategy, "cat_sort_uniq"))
+                       o.strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;
+               else {
+                       error("Unknown -s/--strategy: %s", strategy);
+                       usage_with_options(git_notes_merge_usage, options);
+               }
+       }
+
+       t = init_notes_check("merge");
+
+       strbuf_addf(&msg, "notes: Merged notes from %s into %s",
+                   remote_ref.buf, default_notes_ref());
+       strbuf_add(&(o.commit_msg), msg.buf + 7, msg.len - 7); /* skip "notes: " */
+
+       result = notes_merge(&o, t, result_sha1);
+
+       if (result >= 0) /* Merge resulted (trivially) in result_sha1 */
+               /* Update default notes ref with new commit */
+               update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
+                          0, DIE_ON_ERR);
+       else { /* Merge has unresolved conflicts */
+               /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
+               update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
+                          0, DIE_ON_ERR);
+               /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
+               if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
+                       die("Failed to store link to current notes ref (%s)",
+                           default_notes_ref());
+               printf("Automatic notes merge failed. Fix conflicts in %s and "
+                      "commit the result with 'git notes merge --commit', or "
+                      "abort the merge with 'git notes merge --abort'.\n",
+                      git_path(NOTES_MERGE_WORKTREE));
+       }
+
+       free_notes(t);
+       strbuf_release(&remote_ref);
+       strbuf_release(&msg);
+       return result < 0; /* return non-zero on conflicts */
+}
+
 static int remove_cmd(int argc, const char **argv, const char *prefix)
 {
        struct option options[] = {
@@ -769,6 +955,7 @@ static int remove_cmd(int argc, const char **argv, const char *prefix)
        const char *object_ref;
        struct notes_tree *t;
        unsigned char object[20];
+       int retval;
 
        argc = parse_options(argc, argv, prefix, options,
                             git_notes_remove_usage, 0);
@@ -785,12 +972,17 @@ static int remove_cmd(int argc, const char **argv, const char *prefix)
 
        t = init_notes_check("remove");
 
-       fprintf(stderr, "Removing note for object %s\n", sha1_to_hex(object));
-       remove_note(t, object);
+       retval = remove_note(t, object);
+       if (retval)
+               fprintf(stderr, "Object %s has no note\n", sha1_to_hex(object));
+       else {
+               fprintf(stderr, "Removing note for object %s\n",
+                       sha1_to_hex(object));
 
-       commit_notes(t, "Notes removed by 'git notes remove'");
+               commit_notes(t, "Notes removed by 'git notes remove'");
+       }
        free_notes(t);
-       return 0;
+       return retval;
 }
 
 static int prune(int argc, const char **argv, const char *prefix)
@@ -798,9 +990,8 @@ static int prune(int argc, const char **argv, const char *prefix)
        struct notes_tree *t;
        int show_only = 0, verbose = 0;
        struct option options[] = {
-               OPT_BOOLEAN('n', "dry-run", &show_only,
-                           "do not remove, show only"),
-               OPT_BOOLEAN('v', "verbose", &verbose, "report pruned notes"),
+               OPT__DRY_RUN(&show_only, "do not remove, show only"),
+               OPT__VERBOSE(&verbose, "report pruned notes"),
                OPT_END()
        };
 
@@ -822,6 +1013,21 @@ static int prune(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static int get_ref(int argc, const char **argv, const char *prefix)
+{
+       struct option options[] = { OPT_END() };
+       argc = parse_options(argc, argv, prefix, options,
+                            git_notes_get_ref_usage, 0);
+
+       if (argc) {
+               error("too many parameters");
+               usage_with_options(git_notes_get_ref_usage, options);
+       }
+
+       puts(default_notes_ref());
+       return 0;
+}
+
 int cmd_notes(int argc, const char **argv, const char *prefix)
 {
        int result;
@@ -838,13 +1044,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
 
        if (override_notes_ref) {
                struct strbuf sb = STRBUF_INIT;
-               if (!prefixcmp(override_notes_ref, "refs/notes/"))
-                       /* we're happy */;
-               else if (!prefixcmp(override_notes_ref, "notes/"))
-                       strbuf_addstr(&sb, "refs/");
-               else
-                       strbuf_addstr(&sb, "refs/notes/");
                strbuf_addstr(&sb, override_notes_ref);
+               expand_notes_ref(&sb);
                setenv("GIT_NOTES_REF", sb.buf, 1);
                strbuf_release(&sb);
        }
@@ -859,10 +1060,14 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
                result = append_edit(argc, argv, prefix);
        else if (!strcmp(argv[0], "show"))
                result = show(argc, argv, prefix);
+       else if (!strcmp(argv[0], "merge"))
+               result = merge(argc, argv, prefix);
        else if (!strcmp(argv[0], "remove"))
                result = remove_cmd(argc, argv, prefix);
        else if (!strcmp(argv[0], "prune"))
                result = prune(argc, argv, prefix);
+       else if (!strcmp(argv[0], "get-ref"))
+               result = get_ref(argc, argv, prefix);
        else {
                result = error("Unknown subcommand: %s", argv[0]);
                usage_with_options(git_notes_usage, options);
index 0e8167311884d38c48a9421bf8aede89e7f2c22d..b0503b202afb4356caaca794518fb4e21082a48f 100644 (file)
 #include "list-objects.h"
 #include "progress.h"
 #include "refs.h"
-
-#ifndef NO_PTHREADS
-#include <pthread.h>
 #include "thread-utils.h"
-#endif
 
 static const char pack_usage[] =
-  "git pack-objects [{ -q | --progress | --all-progress }]\n"
+  "git pack-objects [ -q | --progress | --all-progress ]\n"
   "        [--all-progress-implied]\n"
-  "        [--max-pack-size=N] [--local] [--incremental]\n"
-  "        [--window=N] [--window-memory=N] [--depth=N]\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"
+  "        [--threads=<n>] [--non-empty] [--revs [--unpacked | --all]]\n"
   "        [--reflog] [--stdout | base-name] [--include-tag]\n"
-  "        [--keep-unreachable | --unpack-unreachable \n"
-  "        [<ref-list | <object-list]";
+  "        [--keep-unreachable | --unpack-unreachable]\n"
+  "        [< ref-list | < object-list]";
 
 struct object_entry {
        struct pack_idx_entry idx;
@@ -431,7 +427,7 @@ static int write_one(struct sha1file *f,
        written_list[nr_written++] = &e->idx;
 
        /* make sure off_t is sufficiently large not to wrap */
-       if (*offset > *offset + size)
+       if (signed_add_overflows(*offset, size))
                die("pack too large for current definition of off_t");
        *offset += size;
        return 1;
@@ -1298,9 +1294,23 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
                read_lock();
                src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
                read_unlock();
-               if (!src->data)
+               if (!src->data) {
+                       if (src_entry->preferred_base) {
+                               static int warned = 0;
+                               if (!warned++)
+                                       warning("object %s cannot be read",
+                                               sha1_to_hex(src_entry->idx.sha1));
+                               /*
+                                * Those objects are not included in the
+                                * resulting pack.  Be resilient and ignore
+                                * them if they can't be read, in case the
+                                * pack could be created nevertheless.
+                                */
+                               return 0;
+                       }
                        die("object %s cannot be read",
                            sha1_to_hex(src_entry->idx.sha1));
+               }
                if (sz != src_size)
                        die("object %s inconsistent object length (%lu vs %lu)",
                            sha1_to_hex(src_entry->idx.sha1), sz, src_size);
@@ -1529,7 +1539,7 @@ static void try_to_free_from_threads(size_t size)
        read_unlock();
 }
 
-try_to_free_t old_try_to_free_routine;
+static try_to_free_t old_try_to_free_routine;
 
 /*
  * The main thread waits on the condition that (at least) one of the workers
index 99218ba49e2c6ea7ff7185e6397bb73bca213a66..e65690ba370511072dfa1e64838eef5e5686aac9 100644 (file)
@@ -125,9 +125,8 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
        const struct option options[] = {
-               OPT_BOOLEAN('n', "dry-run", &show_only,
-                           "do not remove, show only"),
-               OPT_BOOLEAN('v', "verbose", &verbose, "report pruned objects"),
+               OPT__DRY_RUN(&show_only, "do not remove, show only"),
+               OPT__VERBOSE(&verbose, "report pruned objects"),
                OPT_DATE(0, "expire", &expire,
                         "expire objects older than <time>"),
                OPT_END()
index 9ad1e66916545dd2aeeb110661e56edfa5921019..73c89ed15ba2fb0c470141499c3390b77cf1d81c 100644 (file)
@@ -16,6 +16,7 @@
 #include "resolve-undo.h"
 
 static int nr_trees;
+static int read_empty;
 static struct tree *trees[MAX_UNPACK_TREES];
 
 static int list_tree(unsigned char *sha1)
@@ -32,7 +33,7 @@ static int list_tree(unsigned char *sha1)
 }
 
 static const char * const read_tree_usage[] = {
-       "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]",
+       "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])",
        NULL
 };
 
@@ -106,7 +107,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                { OPTION_CALLBACK, 0, "index-output", NULL, "FILE",
                  "write resulting index to <FILE>",
                  PARSE_OPT_NONEG, index_output_cb },
-               OPT__VERBOSE(&opts.verbose_update),
+               OPT_SET_INT(0, "empty", &read_empty,
+                           "only empty the index", 1),
+               OPT__VERBOSE(&opts.verbose_update, "be verbose"),
                OPT_GROUP("Merging"),
                OPT_SET_INT('m', NULL, &opts.merge,
                            "perform a merge in addition to a read", 1),
@@ -166,6 +169,11 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                        die("failed to unpack tree object %s", arg);
                stage++;
        }
+       if (nr_trees == 0 && !read_empty)
+               warning("read-tree: emptying the index with no arguments is deprecated; use --empty");
+       else if (nr_trees > 0 && read_empty)
+               die("passing trees as arguments contradicts --empty");
+
        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)
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
new file mode 100644 (file)
index 0000000..ea71977
--- /dev/null
@@ -0,0 +1,246 @@
+#include "git-compat-util.h"
+#include "transport.h"
+#include "run-command.h"
+
+/*
+ * URL syntax:
+ *     'command [arg1 [arg2 [...]]]'   Invoke command with given arguments.
+ *     Special characters:
+ *     '% ': Literal space in argument.
+ *     '%%': Literal percent sign.
+ *     '%S': Name of service (git-upload-pack/git-upload-archive/
+ *             git-receive-pack.
+ *     '%s': Same as \s, but with possible git- prefix stripped.
+ *     '%G': Only allowed as first 'character' of argument. Do not pass this
+ *             Argument to command, instead send this as name of repository
+ *             in in-line git://-style request (also activates sending this
+ *             style of request).
+ *     '%V': Only allowed as first 'character' of argument. Used in
+ *             conjunction with '%G': Do not pass this argument to command,
+ *             instead send this as vhost in git://-style request (note: does
+ *             not activate sending git:// style request).
+ */
+
+static char *git_req;
+static char *git_req_vhost;
+
+static char *strip_escapes(const char *str, const char *service,
+       const char **next)
+{
+       size_t rpos = 0;
+       int escape = 0;
+       char special = 0;
+       size_t pslen = 0;
+       size_t pSlen = 0;
+       size_t psoff = 0;
+       struct strbuf ret = STRBUF_INIT;
+
+       /* Calculate prefix length for \s and lengths for \s and \S */
+       if (!strncmp(service, "git-", 4))
+               psoff = 4;
+       pSlen = strlen(service);
+       pslen = pSlen - psoff;
+
+       /* Pass the service to command. */
+       setenv("GIT_EXT_SERVICE", service, 1);
+       setenv("GIT_EXT_SERVICE_NOPREFIX", service + psoff, 1);
+
+       /* Scan the length of argument. */
+       while (str[rpos] && (escape || str[rpos] != ' ')) {
+               if (escape) {
+                       switch (str[rpos]) {
+                       case ' ':
+                       case '%':
+                       case 's':
+                       case 'S':
+                               break;
+                       case 'G':
+                       case 'V':
+                               special = str[rpos];
+                               if (rpos == 1)
+                                       break;
+                               /* Fall-through to error. */
+                       default:
+                               die("Bad remote-ext placeholder '%%%c'.",
+                                       str[rpos]);
+                       }
+                       escape = 0;
+               } else
+                       escape = (str[rpos] == '%');
+               rpos++;
+       }
+       if (escape && !str[rpos])
+               die("remote-ext command has incomplete placeholder");
+       *next = str + rpos;
+       if (**next == ' ')
+               ++*next;        /* Skip over space */
+
+       /*
+        * Do the actual placeholder substitution. The string will be short
+        * enough not to overflow integers.
+        */
+       rpos = special ? 2 : 0;         /* Skip first 2 bytes in specials. */
+       escape = 0;
+       while (str[rpos] && (escape || str[rpos] != ' ')) {
+               if (escape) {
+                       switch (str[rpos]) {
+                       case ' ':
+                       case '%':
+                               strbuf_addch(&ret, str[rpos]);
+                               break;
+                       case 's':
+                               strbuf_addstr(&ret, service + psoff);
+                               break;
+                       case 'S':
+                               strbuf_addstr(&ret, service);
+                               break;
+                       }
+                       escape = 0;
+               } else
+                       switch (str[rpos]) {
+                       case '%':
+                               escape = 1;
+                               break;
+                       default:
+                               strbuf_addch(&ret, str[rpos]);
+                               break;
+                       }
+               rpos++;
+       }
+       switch (special) {
+       case 'G':
+               git_req = strbuf_detach(&ret, NULL);
+               return NULL;
+       case 'V':
+               git_req_vhost = strbuf_detach(&ret, NULL);
+               return NULL;
+       default:
+               return strbuf_detach(&ret, NULL);
+       }
+}
+
+/* Should be enough... */
+#define MAXARGUMENTS 256
+
+static const char **parse_argv(const char *arg, const char *service)
+{
+       int arguments = 0;
+       int i;
+       const char **ret;
+       char *temparray[MAXARGUMENTS + 1];
+
+       while (*arg) {
+               char *expanded;
+               if (arguments == MAXARGUMENTS)
+                       die("remote-ext command has too many arguments");
+               expanded = strip_escapes(arg, service, &arg);
+               if (expanded)
+                       temparray[arguments++] = expanded;
+       }
+
+       ret = xmalloc((arguments + 1) * sizeof(char *));
+       for (i = 0; i < arguments; i++)
+               ret[i] = temparray[i];
+       ret[arguments] = NULL;
+       return ret;
+}
+
+static void send_git_request(int stdin_fd, const char *serv, const char *repo,
+       const char *vhost)
+{
+       size_t bufferspace;
+       size_t wpos = 0;
+       char *buffer;
+
+       /*
+        * Request needs 12 bytes extra if there is vhost (xxxx \0host=\0) and
+        * 6 bytes extra (xxxx \0) if there is no vhost.
+        */
+       if (vhost)
+               bufferspace = strlen(serv) + strlen(repo) + strlen(vhost) + 12;
+       else
+               bufferspace = strlen(serv) + strlen(repo) + 6;
+
+       if (bufferspace > 0xFFFF)
+               die("Request too large to send");
+       buffer = xmalloc(bufferspace);
+
+       /* Make the packet. */
+       wpos = sprintf(buffer, "%04x%s %s%c", (unsigned)bufferspace,
+               serv, repo, 0);
+
+       /* Add vhost if any. */
+       if (vhost)
+               sprintf(buffer + wpos, "host=%s%c", vhost, 0);
+
+       /* Send the request */
+       if (write_in_full(stdin_fd, buffer, bufferspace) < 0)
+               die_errno("Failed to send request");
+
+       free(buffer);
+}
+
+static int run_child(const char *arg, const char *service)
+{
+       int r;
+       struct child_process child;
+
+       memset(&child, 0, sizeof(child));
+       child.in = -1;
+       child.out = -1;
+       child.err = 0;
+       child.argv = parse_argv(arg, service);
+
+       if (start_command(&child) < 0)
+               die("Can't run specified command");
+
+       if (git_req)
+               send_git_request(child.in, service, git_req, git_req_vhost);
+
+       r = bidirectional_transfer_loop(child.out, child.in);
+       if (!r)
+               r = finish_command(&child);
+       else
+               finish_command(&child);
+       return r;
+}
+
+#define MAXCOMMAND 4096
+
+static int command_loop(const char *child)
+{
+       char buffer[MAXCOMMAND];
+
+       while (1) {
+               size_t i;
+               if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
+                       if (ferror(stdin))
+                               die("Comammand input error");
+                       exit(0);
+               }
+               /* Strip end of line characters. */
+               i = strlen(buffer);
+               while (i > 0 && isspace(buffer[i - 1]))
+                       buffer[--i] = 0;
+
+               if (!strcmp(buffer, "capabilities")) {
+                       printf("*connect\n\n");
+                       fflush(stdout);
+               } else if (!strncmp(buffer, "connect ", 8)) {
+                       printf("\n");
+                       fflush(stdout);
+                       return run_child(child, buffer + 8);
+               } else {
+                       fprintf(stderr, "Bad command");
+                       return 1;
+               }
+       }
+}
+
+int cmd_remote_ext(int argc, const char **argv, const char *prefix)
+{
+       if (argc != 3)
+               die("Expected two arguments");
+
+       return command_loop(argv[2]);
+}
diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c
new file mode 100644 (file)
index 0000000..1f2467b
--- /dev/null
@@ -0,0 +1,79 @@
+#include "git-compat-util.h"
+#include "transport.h"
+
+/*
+ * URL syntax:
+ *     'fd::<inoutfd>[/<anything>]'            Read/write socket pair
+ *                                             <inoutfd>.
+ *     'fd::<infd>,<outfd>[/<anything>]'       Read pipe <infd> and write
+ *                                             pipe <outfd>.
+ *     [foo] indicates 'foo' is optional. <anything> is any string.
+ *
+ * The data output to <outfd>/<inoutfd> should be passed unmolested to
+ * git-receive-pack/git-upload-pack/git-upload-archive and output of
+ * git-receive-pack/git-upload-pack/git-upload-archive should be passed
+ * unmolested to <infd>/<inoutfd>.
+ *
+ */
+
+#define MAXCOMMAND 4096
+
+static void command_loop(int input_fd, int output_fd)
+{
+       char buffer[MAXCOMMAND];
+
+       while (1) {
+               size_t i;
+               if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
+                       if (ferror(stdin))
+                               die("Input error");
+                       return;
+               }
+               /* Strip end of line characters. */
+               i = strlen(buffer);
+               while (i > 0 && isspace(buffer[i - 1]))
+                       buffer[--i] = 0;
+
+               if (!strcmp(buffer, "capabilities")) {
+                       printf("*connect\n\n");
+                       fflush(stdout);
+               } else if (!strncmp(buffer, "connect ", 8)) {
+                       printf("\n");
+                       fflush(stdout);
+                       if (bidirectional_transfer_loop(input_fd,
+                               output_fd))
+                               die("Copying data between file descriptors failed");
+                       return;
+               } else {
+                       die("Bad command: %s", buffer);
+               }
+       }
+}
+
+int cmd_remote_fd(int argc, const char **argv, const char *prefix)
+{
+       int input_fd = -1;
+       int output_fd = -1;
+       char *end;
+
+       if (argc != 3)
+               die("Expected two arguments");
+
+       input_fd = (int)strtoul(argv[2], &end, 10);
+
+       if ((end == argv[2]) || (*end != ',' && *end != '/' && *end))
+               die("Bad URL syntax");
+
+       if (*end == '/' || !*end) {
+               output_fd = input_fd;
+       } else {
+               char *end2;
+               output_fd = (int)strtoul(end + 1, &end2, 10);
+
+               if ((end2 == end + 1) || (*end2 != '/' && *end2))
+                       die("Bad URL syntax");
+       }
+
+       command_loop(input_fd, output_fd);
+       return 0;
+}
index 48e0a6bf260cab18d44058ade554e3a9d5946b93..cb26080956077f8c9ae02c91ffdc341293a4f9f1 100644 (file)
@@ -15,7 +15,7 @@ static const char * const builtin_remote_usage[] = {
        "git remote set-head <name> (-a | -d | <branch>)",
        "git remote [-v | --verbose] show [-n] <name>",
        "git remote prune [-n | --dry-run] <name>",
-       "git remote [-v | --verbose] update [-p | --prune] [group | remote]",
+       "git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]",
        "git remote set-branches <name> [--add] <branch>...",
        "git remote set-url <name> <newurl> [<oldurl>]",
        "git remote set-url --add <name> <newurl>",
@@ -507,7 +507,7 @@ static int add_branch_for_removal(const char *refname,
                        return 0;
        }
 
-       /* don't delete non-remote refs */
+       /* don't delete non-remote-tracking refs */
        if (prefixcmp(refname, "refs/remotes")) {
                /* advise user how to delete local branches */
                if (!prefixcmp(refname, "refs/heads/"))
@@ -791,9 +791,9 @@ static int rm(int argc, const char **argv)
 
        if (skipped.nr) {
                fprintf(stderr, skipped.nr == 1 ?
-                       "Note: A non-remote branch was not removed; "
+                       "Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
                        "to delete it, use:\n" :
-                       "Note: Non-remote branches were not removed; "
+                       "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
                        "to delete them, use:\n");
                for (i = 0; i < skipped.nr; i++)
                        fprintf(stderr, "  git branch -d %s\n",
@@ -1200,7 +1200,7 @@ static int prune(int argc, const char **argv)
 {
        int dry_run = 0, result = 0;
        struct option options[] = {
-               OPT__DRY_RUN(&dry_run),
+               OPT__DRY_RUN(&dry_run, "dry run"),
                OPT_END()
        };
 
@@ -1512,7 +1512,7 @@ static int show_all(void)
 int cmd_remote(int argc, const char **argv, const char *prefix)
 {
        struct option options[] = {
-               OPT_BOOLEAN('v', "verbose", &verbose, "be verbose; must be placed before a subcommand"),
+               OPT__VERBOSE(&verbose, "be verbose; must be placed before a subcommand"),
                OPT_END()
        };
        int result;
index 9b1e3a7cf50674ffdd834d08edba184f5c7c285d..642bf35587ed994948d3eeac0a189ae29e39bf11 100644 (file)
@@ -1,13 +1,16 @@
 #include "builtin.h"
 #include "cache.h"
 #include "dir.h"
+#include "parse-options.h"
 #include "string-list.h"
 #include "rerere.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
 
-static const char git_rerere_usage[] =
-"git rerere [clear | status | diff | gc]";
+static const char * const rerere_usage[] = {
+       "git rerere [clear | status | diff | gc]",
+       NULL,
+};
 
 /* these values are days */
 static int cutoff_noresolve = 15;
@@ -19,6 +22,12 @@ static time_t rerere_created_at(const char *name)
        return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
 }
 
+static time_t rerere_last_used_at(const char *name)
+{
+       struct stat st;
+       return stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
+}
+
 static void unlink_rr_item(const char *name)
 {
        unlink(rerere_path(name, "thisimage"));
@@ -53,11 +62,16 @@ static void garbage_collect(struct string_list *rr)
        while ((e = readdir(dir))) {
                if (is_dot_or_dotdot(e->d_name))
                        continue;
-               then = rerere_created_at(e->d_name);
-               if (!then)
-                       continue;
-               cutoff = (has_rerere_resolution(e->d_name)
-                         ? cutoff_resolve : cutoff_noresolve);
+
+               then = rerere_last_used_at(e->d_name);
+               if (then) {
+                       cutoff = cutoff_resolve;
+               } else {
+                       then = rerere_created_at(e->d_name);
+                       if (!then)
+                               continue;
+                       cutoff = cutoff_noresolve;
+               }
                if (then < now - cutoff * 86400)
                        string_list_append(&to_remove, e->d_name);
        }
@@ -103,25 +117,26 @@ static int diff_two(const char *file1, const char *label1,
 int cmd_rerere(int argc, const char **argv, const char *prefix)
 {
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
-       int i, fd, flags = 0;
-
-       if (2 < argc) {
-               if (!strcmp(argv[1], "-h"))
-                       usage(git_rerere_usage);
-               if (!strcmp(argv[1], "--rerere-autoupdate"))
-                       flags = RERERE_AUTOUPDATE;
-               else if (!strcmp(argv[1], "--no-rerere-autoupdate"))
-                       flags = RERERE_NOAUTOUPDATE;
-               if (flags) {
-                       argc--;
-                       argv++;
-               }
-       }
-       if (argc < 2)
+       int i, fd, autoupdate = -1, flags = 0;
+
+       struct option options[] = {
+               OPT_SET_INT(0, "rerere-autoupdate", &autoupdate,
+                       "register clean resolutions in index", 1),
+               OPT_END(),
+       };
+
+       argc = parse_options(argc, argv, prefix, options, rerere_usage, 0);
+
+       if (autoupdate == 1)
+               flags = RERERE_AUTOUPDATE;
+       if (autoupdate == 0)
+               flags = RERERE_NOAUTOUPDATE;
+
+       if (argc < 1)
                return rerere(flags);
 
-       if (!strcmp(argv[1], "forget")) {
-               const char **pathspec = get_pathspec(prefix, argv + 2);
+       if (!strcmp(argv[0], "forget")) {
+               const char **pathspec = get_pathspec(prefix, argv + 1);
                return rerere_forget(pathspec);
        }
 
@@ -129,26 +144,26 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
        if (fd < 0)
                return 0;
 
-       if (!strcmp(argv[1], "clear")) {
+       if (!strcmp(argv[0], "clear")) {
                for (i = 0; i < merge_rr.nr; i++) {
                        const char *name = (const char *)merge_rr.items[i].util;
                        if (!has_rerere_resolution(name))
                                unlink_rr_item(name);
                }
                unlink_or_warn(git_path("MERGE_RR"));
-       } else if (!strcmp(argv[1], "gc"))
+       } else if (!strcmp(argv[0], "gc"))
                garbage_collect(&merge_rr);
-       else if (!strcmp(argv[1], "status"))
+       else if (!strcmp(argv[0], "status"))
                for (i = 0; i < merge_rr.nr; i++)
                        printf("%s\n", merge_rr.items[i].string);
-       else if (!strcmp(argv[1], "diff"))
+       else if (!strcmp(argv[0], "diff"))
                for (i = 0; i < merge_rr.nr; i++) {
                        const char *path = merge_rr.items[i].string;
                        const char *name = (const char *)merge_rr.items[i].util;
                        diff_two(rerere_path(name, "preimage"), path, path, path);
                }
        else
-               usage(git_rerere_usage);
+               usage_with_options(rerere_usage, options);
 
        string_list_clear(&merge_rr, 1);
        return 0;
index 1283068fd24c371e39f0271d01dd6b3aac437d4b..5de2bceeec8c1d243ed6da70464d0c4dd60d7352 100644 (file)
@@ -243,7 +243,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        struct commit *commit;
        char *reflog_action, msg[1024];
        const struct option options[] = {
-               OPT__QUIET(&quiet),
+               OPT__QUIET(&quiet, "be quiet, only report errors"),
                OPT_SET_INT(0, "mixed", &reset_type,
                                                "reset HEAD and index", MIXED),
                OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
@@ -318,7 +318,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
         * affecting the working tree nor HEAD. */
        if (i < argc) {
                if (reset_type == MIXED)
-                       warning("--mixed option is deprecated with paths.");
+                       warning("--mixed with paths is deprecated; use 'git reset -- <paths>' instead.");
                else if (reset_type != NONE)
                        die("Cannot do %s reset with paths.",
                                        reset_type_names[reset_type]);
index efe9360e2fdb9aac3bae0c61c85531041f24708a..ba27d39f977f2807cddb6e18364837b9fd50e970 100644 (file)
@@ -11,9 +11,9 @@
 static const char rev_list_usage[] =
 "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
 "  limiting output:\n"
-"    --max-count=nr\n"
-"    --max-age=epoch\n"
-"    --min-age=epoch\n"
+"    --max-count=<n>\n"
+"    --max-age=<epoch>\n"
+"    --min-age=<epoch>\n"
 "    --sparse\n"
 "    --no-merges\n"
 "    --remove-empty\n"
@@ -33,7 +33,7 @@ static const char rev_list_usage[] =
 "    --objects | --objects-edge\n"
 "    --unpacked\n"
 "    --header | --pretty\n"
-"    --abbrev=nr | --no-abbrev\n"
+"    --abbrev=<n> | --no-abbrev\n"
 "    --abbrev-commit\n"
 "    --left-right\n"
 "  special purpose:\n"
@@ -147,8 +147,10 @@ static void show_commit(struct commit *commit, void *data)
                        }
                } else {
                        if (revs->commit_format != CMIT_FMT_USERFORMAT ||
-                           buf.len)
-                               printf("%s%c", buf.buf, info->hdr_termination);
+                           buf.len) {
+                               fwrite(buf.buf, 1, buf.len, stdout);
+                               putchar(info->hdr_termination);
+                       }
                }
                strbuf_release(&buf);
        } else {
index 9215e665046a8991b57f8d4cfda123dae084414c..dc1b702edc6b4bd95ebf41c12bf1da6fe9d6850d 100644 (file)
@@ -44,7 +44,11 @@ static const char **commit_argv;
 static int allow_rerere_auto;
 
 static const char *me;
+
+/* Merge strategy. */
 static const char *strategy;
+static const char **xopts;
+static size_t xopts_nr, xopts_alloc;
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -55,6 +59,17 @@ static const char * const *revert_or_cherry_pick_usage(void)
        return action == REVERT ? revert_usage : cherry_pick_usage;
 }
 
+static int option_parse_x(const struct option *opt,
+                         const char *arg, int unset)
+{
+       if (unset)
+               return 0;
+
+       ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
+       xopts[xopts_nr++] = xstrdup(arg);
+       return 0;
+}
+
 static void parse_args(int argc, const char **argv)
 {
        const char * const * usage_str = revert_or_cherry_pick_usage();
@@ -67,6 +82,8 @@ static void parse_args(int argc, const char **argv)
                OPT_INTEGER('m', "mainline", &mainline, "parent number"),
                OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
                OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
+               OPT_CALLBACK('X', "strategy-option", &xopts, "option",
+                       "option for merge strategy", option_parse_x),
                OPT_END(),
                OPT_END(),
                OPT_END(),
@@ -231,27 +248,30 @@ static void set_author_ident_env(const char *message)
                        sha1_to_hex(commit->object.sha1));
 }
 
-static char *help_msg(void)
+static void advise(const char *advice, ...)
 {
-       struct strbuf helpbuf = STRBUF_INIT;
-       char *msg = getenv("GIT_CHERRY_PICK_HELP");
+       va_list params;
 
-       if (msg)
-               return msg;
+       va_start(params, advice);
+       vreportf("hint: ", advice, params);
+       va_end(params);
+}
 
-       strbuf_addstr(&helpbuf, "  After resolving the conflicts,\n"
-               "mark the corrected paths with 'git add <paths>' or 'git rm <paths>'\n"
-               "and commit the result");
+static void print_advice(void)
+{
+       char *msg = getenv("GIT_CHERRY_PICK_HELP");
 
-       if (action == CHERRY_PICK) {
-               strbuf_addf(&helpbuf, " with: \n"
-                       "\n"
-                       "        git commit -c %s\n",
-                           sha1_to_hex(commit->object.sha1));
+       if (msg) {
+               fprintf(stderr, "%s\n", msg);
+               return;
        }
-       else
-               strbuf_addch(&helpbuf, '.');
-       return strbuf_detach(&helpbuf, NULL);
+
+       advise("after resolving the conflicts, mark the corrected paths");
+       advise("with 'git add <paths>' or 'git rm <paths>'");
+
+       if (action == CHERRY_PICK)
+               advise("and commit the result with 'git commit -c %s'",
+                      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
 }
 
 static void write_message(struct strbuf *msgbuf, const char *filename)
@@ -301,19 +321,20 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)
        return write_ref_sha1(ref_lock, to, "cherry-pick");
 }
 
-static void do_recursive_merge(struct commit *base, struct commit *next,
-                              const char *base_label, const char *next_label,
-                              unsigned char *head, struct strbuf *msgbuf,
-                              char *defmsg)
+static int do_recursive_merge(struct commit *base, struct commit *next,
+                             const char *base_label, const char *next_label,
+                             unsigned char *head, struct strbuf *msgbuf)
 {
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
        int clean, index_fd;
+       const char **xopt;
        static struct lock_file index_lock;
 
        index_fd = hold_locked_index(&index_lock, 1);
 
        read_cache();
+
        init_merge_options(&o);
        o.ancestor = base ? base_label : "(empty tree)";
        o.branch1 = "HEAD";
@@ -323,6 +344,9 @@ static void do_recursive_merge(struct commit *base, struct commit *next,
        next_tree = next ? next->tree : empty_tree();
        base_tree = base ? base->tree : empty_tree();
 
+       for (xopt = xopts; xopt != xopts + xopts_nr; xopt++)
+               parse_merge_opt(&o, *xopt);
+
        clean = merge_trees(&o,
                            head_tree,
                            next_tree, base_tree, &result);
@@ -347,14 +371,35 @@ static void do_recursive_merge(struct commit *base, struct commit *next,
                                        i++;
                        }
                }
-               write_message(msgbuf, defmsg);
-               fprintf(stderr, "Automatic %s failed.%s\n",
-                       me, help_msg());
-               rerere(allow_rerere_auto);
-               exit(1);
        }
-       write_message(msgbuf, defmsg);
-       fprintf(stderr, "Finished one %s.\n", me);
+
+       return !clean;
+}
+
+/*
+ * If we are cherry-pick, and if the merge did not result in
+ * hand-editing, we will hit this commit and inherit the original
+ * author date and name.
+ * If we are revert, or if our cherry-pick results in a hand merge,
+ * we had better say that the current user is responsible for that.
+ */
+static int run_git_commit(const char *defmsg)
+{
+       /* 6 is max possible length of our args array including NULL */
+       const char *args[6];
+       int i = 0;
+
+       args[i++] = "commit";
+       args[i++] = "-n";
+       if (signoff)
+               args[i++] = "-s";
+       if (!edit) {
+               args[i++] = "-F";
+               args[i++] = defmsg;
+       }
+       args[i] = NULL;
+
+       return run_command_v_opt(args, RUN_GIT_CMD);
 }
 
 static int do_pick_commit(void)
@@ -365,6 +410,7 @@ static int do_pick_commit(void)
        struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
+       int res;
 
        if (no_commit) {
                /*
@@ -411,7 +457,7 @@ static int do_pick_commit(void)
        else
                parent = commit->parents->item;
 
-       if (allow_ff && !hashcmp(parent->object.sha1, head))
+       if (allow_ff && parent && !hashcmp(parent->object.sha1, head))
                return fast_forward_to(commit->object.sha1, head);
 
        if (parent && parse_commit(parent) < 0)
@@ -460,63 +506,40 @@ static int do_pick_commit(void)
                }
        }
 
-       if (!strategy || !strcmp(strategy, "recursive") || action == REVERT)
-               do_recursive_merge(base, next, base_label, next_label,
-                                  head, &msgbuf, defmsg);
-       else {
-               int res;
+       if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {
+               res = do_recursive_merge(base, next, base_label, next_label,
+                                        head, &msgbuf);
+               write_message(&msgbuf, defmsg);
+       } else {
                struct commit_list *common = NULL;
                struct commit_list *remotes = NULL;
+
                write_message(&msgbuf, defmsg);
+
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
-               res = try_merge_command(strategy, common,
+               res = try_merge_command(strategy, xopts_nr, xopts, common,
                                        sha1_to_hex(head), remotes);
                free_commit_list(common);
                free_commit_list(remotes);
-               if (res) {
-                       fprintf(stderr, "Automatic %s with strategy %s failed.%s\n",
-                               me, strategy, help_msg());
-                       rerere(allow_rerere_auto);
-                       exit(1);
-               }
        }
 
-       free_message(&msg);
-
-       /*
-        *
-        * If we are cherry-pick, and if the merge did not result in
-        * hand-editing, we will hit this commit and inherit the original
-        * author date and name.
-        * If we are revert, or if our cherry-pick results in a hand merge,
-        * we had better say that the current user is responsible for that.
-        */
-
-       if (!no_commit) {
-               /* 6 is max possible length of our args array including NULL */
-               const char *args[6];
-               int res;
-               int i = 0;
-
-               args[i++] = "commit";
-               args[i++] = "-n";
-               if (signoff)
-                       args[i++] = "-s";
-               if (!edit) {
-                       args[i++] = "-F";
-                       args[i++] = defmsg;
-               }
-               args[i] = NULL;
-               res = run_command_v_opt(args, RUN_GIT_CMD);
-               free(defmsg);
-
-               return res;
+       if (res) {
+               error("could not %s %s... %s",
+                     action == REVERT ? "revert" : "apply",
+                     find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
+                     msg.subject);
+               print_advice();
+               rerere(allow_rerere_auto);
+       } else {
+               if (!no_commit)
+                       res = run_git_commit(defmsg);
        }
 
+       free_message(&msg);
        free(defmsg);
 
-       return 0;
+       return res;
 }
 
 static void prepare_revs(struct rev_info *revs)
@@ -539,6 +562,21 @@ static void prepare_revs(struct rev_info *revs)
                die("empty commit set passed");
 }
 
+static void read_and_refresh_cache(const char *me)
+{
+       static struct lock_file index_lock;
+       int index_fd = hold_locked_index(&index_lock, 0);
+       if (read_index_preload(&the_index, NULL) < 0)
+               die("git %s: failed to read the index", me);
+       refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
+       if (the_index.cache_changed) {
+               if (write_index(&the_index, index_fd) ||
+                   commit_locked_index(&index_lock))
+                       die("git %s: failed to refresh the index", me);
+       }
+       rollback_lock_file(&index_lock);
+}
+
 static int revert_or_cherry_pick(int argc, const char **argv)
 {
        struct rev_info revs;
@@ -559,8 +597,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                        die("cherry-pick --ff cannot be used with --edit");
        }
 
-       if (read_cache() < 0)
-               die("git %s: failed to read the index", me);
+       read_and_refresh_cache(me);
 
        prepare_revs(&revs);
 
index f3772c84de0a1f1a18123e55ae6e6983739f590b..ff491d77612ffb3c906c5e1ffa232f4410e4b661 100644 (file)
@@ -20,15 +20,6 @@ static struct {
        const char **name;
 } list;
 
-static void add_list(const char *name)
-{
-       if (list.nr >= list.alloc) {
-               list.alloc = alloc_nr(list.alloc);
-               list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
-       }
-       list.name[list.nr++] = name;
-}
-
 static int check_local_mod(unsigned char *head, int index_only)
 {
        /*
@@ -139,10 +130,10 @@ static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
 static int ignore_unmatch = 0;
 
 static struct option builtin_rm_options[] = {
-       OPT__DRY_RUN(&show_only),
-       OPT__QUIET(&quiet),
+       OPT__DRY_RUN(&show_only, "dry run"),
+       OPT__QUIET(&quiet, "do not list removed files"),
        OPT_BOOLEAN( 0 , "cached",         &index_only, "only remove from the index"),
-       OPT_BOOLEAN('f', "force",          &force,      "override the up-to-date check"),
+       OPT__FORCE(&force, "override the up-to-date check"),
        OPT_BOOLEAN('r', NULL,             &recursive,  "allow recursive removal"),
        OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch,
                                "exit with a zero status even if nothing matched"),
@@ -182,7 +173,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                struct cache_entry *ce = active_cache[i];
                if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
                        continue;
-               add_list(ce->name);
+               ALLOC_GROW(list.name, list.nr + 1, list.alloc);
+               list.name[list.nr++] = ce->name;
        }
 
        if (pathspec) {
index 481602d8ae73612226bcc758f2aedea7f964779c..2cd1c40b70890732905ad9433e452aa9a9548533 100644 (file)
@@ -48,6 +48,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
                NULL,
                NULL,
                NULL,
+               NULL,
        };
        struct child_process po;
        int i;
@@ -59,6 +60,8 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
                argv[i++] = "--delta-base-offset";
        if (args->quiet)
                argv[i++] = "-q";
+       if (args->progress)
+               argv[i++] = "--progress";
        memset(&po, 0, sizeof(po));
        po.argv = argv;
        po.in = -1;
@@ -101,7 +104,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
        }
 
        if (finish_command(&po))
-               return error("pack-objects died with strange error");
+               return -1;
        return 0;
 }
 
index 0a9681ba7ece52e751b1b54d53f3e8fb4c19464e..1a21e4b0538565f7a64488ed7b3a5c317e559699 100644 (file)
@@ -249,7 +249,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 {
        static struct shortlog log;
        static struct rev_info rev;
-       int nongit;
+       int nongit = !startup_info->have_repository;
 
        static const struct option options[] = {
                OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
@@ -265,12 +265,11 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 
        struct parse_opt_ctx_t ctx;
 
-       prefix = setup_git_directory_gently(&nongit);
        git_config(git_default_config, NULL);
        shortlog_init(&log);
        init_revisions(&rev, prefix);
-       parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
-                           PARSE_OPT_KEEP_ARGV0);
+       parse_options_start(&ctx, argc, argv, prefix, options,
+                           PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
 
        for (;;) {
                switch (parse_options_step(&ctx, options, shortlog_usage)) {
index e8719aa9e9f47c30b697332925fcdd206fdfd55c..da695815e26c281cbacfbf865dfa72da44df9f19 100644 (file)
@@ -6,7 +6,7 @@
 #include "parse-options.h"
 
 static const char* show_branch_usage[] = {
-    "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [<rev> | <glob>]...",
+    "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --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
 };
@@ -243,7 +243,7 @@ static void join_revs(struct commit_list **list_p,
                        if (mark_seen(p, seen_p) && !still_interesting)
                                extra--;
                        p->object.flags |= flags;
-                       insert_by_date(p, list_p);
+                       commit_list_insert_by_date(p, list_p);
                }
        }
 
@@ -859,7 +859,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                 */
                commit->object.flags |= flag;
                if (commit->object.flags == flag)
-                       insert_by_date(commit, &list);
+                       commit_list_insert_by_date(commit, &list);
                rev[num_rev] = commit;
        }
        for (i = 0; i < num_rev; i++)
@@ -868,7 +868,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        if (0 <= extra)
                join_revs(&list, &seen, num_rev, extra);
 
-       sort_by_date(&seen);
+       commit_list_sort_by_date(&seen);
 
        if (merge_base)
                return show_merge_base(seen, num_rev);
index be9b512eebc72984e6ff4cdd4859ab7951539e19..45f0340c3ea004822477ed70fdd14e05dfcd9bb5 100644 (file)
@@ -193,7 +193,8 @@ static const struct option show_ref_options[] = {
          "only show SHA1 hash using <n> digits",
          PARSE_OPT_OPTARG, &hash_callback },
        OPT__ABBREV(&abbrev),
-       OPT__QUIET(&quiet),
+       OPT__QUIET(&quiet,
+                  "do not print results to stdout (useful with --verify)"),
        { 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 },
diff --git a/builtin/skew.c b/builtin/skew.c
new file mode 100644 (file)
index 0000000..1046f5f
--- /dev/null
@@ -0,0 +1,50 @@
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+
+unsigned long worst_skew = 0;
+
+static void check_skew_recurse(struct commit *c, unsigned long when)
+{
+       struct commit_list *p;
+
+       if (c->object.flags & SEEN)
+               return;
+       c->object.flags |= SEEN;
+
+       if (parse_commit(c) < 0)
+               return;
+
+       if (c->date > when) {
+               unsigned long skew = c->date - when;
+               if (skew > worst_skew)
+                       worst_skew = skew;
+       }
+
+       for (p = c->parents; p; p = p->next)
+               check_skew_recurse(p->item, c->date < when ? c->date : when);
+}
+
+static void check_skew(struct commit *c)
+{
+       check_skew_recurse(c, time(NULL));
+}
+
+int cmd_skew(int argc, const char **argv, const char *prefix) {
+       struct rev_info revs;
+       int i;
+
+       git_config(git_default_config, NULL);
+       init_revisions(&revs, prefix);
+       argc = setup_revisions(argc, argv, &revs, NULL);
+
+       for (i = 0; i < revs.pending.nr; i++) {
+               struct object *o = revs.pending.objects[i].item;
+               if (o->type == OBJ_COMMIT)
+                       check_skew((struct commit *)o);
+       }
+
+       printf("%lu\n", worst_skew);
+       return 0;
+}
index ca855a5eb239f4dadccd53369e38db4e78b1d13f..dea849c3c5ec0c0a7ee0ac98e1ae62a6dacf5806 100644 (file)
@@ -30,7 +30,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        const char *msg = NULL;
        struct option options[] = {
-               OPT__QUIET(&quiet),
+               OPT__QUIET(&quiet,
+                       "suppress error message for non-symbolic (detached) refs"),
                OPT_STRING('m', NULL, &msg, "reason", "reason of the update"),
                OPT_END(),
        };
index d311491e492787ae50aa172f51629abea53eec19..92bacb5bc5882be8ef6093391d1e2a23a80d7599 100644 (file)
@@ -29,8 +29,6 @@ struct tag_filter {
        struct commit_list *with_commit;
 };
 
-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
-
 static int show_reference(const char *refname, const unsigned char *sha1,
                          int flag, void *cb_data)
 {
@@ -49,7 +47,7 @@ static int show_reference(const char *refname, const unsigned char *sha1,
                        commit = lookup_commit_reference_gently(sha1, 1);
                        if (!commit)
                                return 0;
-                       if (!is_descendant_of(commit, filter->with_commit))
+                       if (!contains(commit, filter->with_commit))
                                return 0;
                }
 
@@ -70,9 +68,9 @@ static int show_reference(const char *refname, const unsigned char *sha1,
                        return 0;
                }
                /* only take up to "lines" lines, and strip the signature */
+               size = parse_signature(buf, size);
                for (i = 0, sp += 2;
-                               i < filter->lines && sp < buf + size &&
-                               prefixcmp(sp, PGP_SIGNATURE "\n");
+                               i < filter->lines && sp < buf + size;
                                i++) {
                        if (i)
                                printf("\n    ");
@@ -242,8 +240,7 @@ static void write_tag_body(int fd, const unsigned char *sha1)
 {
        unsigned long size;
        enum object_type type;
-       char *buf, *sp, *eob;
-       size_t len;
+       char *buf, *sp;
 
        buf = read_sha1_file(sha1, &type, &size);
        if (!buf)
@@ -256,12 +253,7 @@ static void write_tag_body(int fd, const unsigned char *sha1)
                return;
        }
        sp += 2; /* skip the 2 LFs */
-       eob = strstr(sp, "\n" PGP_SIGNATURE "\n");
-       if (eob)
-               len = eob - sp;
-       else
-               len = buf + size - sp;
-       write_or_die(fd, sp, len);
+       write_or_die(fd, sp, parse_signature(sp, buf + size - sp));
 
        free(buf);
 }
@@ -390,7 +382,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                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', "force", &force, "replace the tag if exists"),
+               OPT__FORCE(&force, "replace the tag if exists"),
 
                OPT_GROUP("Tag listing options"),
                {
index 685566e0b5e458c510fdf989744d63dda29e28f0..f63973c9143c4109a3d1e40df9ec66a32498fe00 100644 (file)
@@ -83,7 +83,7 @@ static void use(int bytes)
        offset += bytes;
 
        /* make sure off_t is sufficiently large not to wrap */
-       if (consumed_bytes > consumed_bytes + bytes)
+       if (signed_add_overflows(consumed_bytes, bytes))
                die("pack too large for current definition of off_t");
        consumed_bytes += bytes;
 }
index 3ab214d24e537865875d6d1e3acd8cf6b019ff22..d7850c6309aec0c1a4f640999fb757d7b32ba1b8 100644 (file)
@@ -10,6 +10,7 @@
 #include "builtin.h"
 #include "refs.h"
 #include "resolve-undo.h"
+#include "parse-options.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -397,8 +398,10 @@ static void read_index_info(int line_termination)
        strbuf_release(&uq);
 }
 
-static const char update_index_usage[] =
-"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--skip-worktree|--no-skip-worktree] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...";
+static const char * const update_index_usage[] = {
+       "git update-index [options] [--] [<file>...]",
+       NULL
+};
 
 static unsigned char head_sha1[20];
 static unsigned char merge_head_sha1[20];
@@ -543,7 +546,10 @@ static int do_reupdate(int ac, const char **av,
         */
        int pos;
        int has_head = 1;
-       const char **pathspec = get_pathspec(prefix, av + 1);
+       const char **paths = get_pathspec(prefix, av + 1);
+       struct pathspec pathspec;
+
+       init_pathspec(&pathspec, paths);
 
        if (read_ref("HEAD", head_sha1))
                /* If there is no HEAD, that means it is an initial
@@ -556,7 +562,7 @@ static int do_reupdate(int ac, const char **av,
                struct cache_entry *old = NULL;
                int save_nr;
 
-               if (ce_stage(ce) || !ce_path_match(ce, pathspec))
+               if (ce_stage(ce) || !ce_path_match(ce, &pathspec))
                        continue;
                if (has_head)
                        old = read_one_ent(NULL, head_sha1,
@@ -575,19 +581,218 @@ static int do_reupdate(int ac, const char **av,
                if (save_nr != active_nr)
                        goto redo;
        }
+       free_pathspec(&pathspec);
+       return 0;
+}
+
+struct refresh_params {
+       unsigned int flags;
+       int *has_errors;
+};
+
+static int refresh(struct refresh_params *o, unsigned int flag)
+{
+       setup_work_tree();
+       *o->has_errors |= refresh_cache(o->flags | flag);
+       return 0;
+}
+
+static int refresh_callback(const struct option *opt,
+                               const char *arg, int unset)
+{
+       return refresh(opt->value, 0);
+}
+
+static int really_refresh_callback(const struct option *opt,
+                               const char *arg, int unset)
+{
+       return refresh(opt->value, REFRESH_REALLY);
+}
+
+static int chmod_callback(const struct option *opt,
+                               const char *arg, int unset)
+{
+       char *flip = opt->value;
+       if ((arg[0] != '-' && arg[0] != '+') || arg[1] != 'x' || arg[2])
+               return error("option 'chmod' expects \"+x\" or \"-x\"");
+       *flip = arg[0];
+       return 0;
+}
+
+static int resolve_undo_clear_callback(const struct option *opt,
+                               const char *arg, int unset)
+{
+       resolve_undo_clear();
+       return 0;
+}
+
+static int cacheinfo_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int unset)
+{
+       unsigned char sha1[20];
+       unsigned int mode;
+
+       if (ctx->argc <= 3)
+               return error("option 'cacheinfo' expects three arguments");
+       if (strtoul_ui(*++ctx->argv, 8, &mode) ||
+           get_sha1_hex(*++ctx->argv, sha1) ||
+           add_cacheinfo(mode, sha1, *++ctx->argv, 0))
+               die("git update-index: --cacheinfo cannot add %s", *ctx->argv);
+       ctx->argc -= 3;
+       return 0;
+}
+
+static int stdin_cacheinfo_callback(struct parse_opt_ctx_t *ctx,
+                             const struct option *opt, int unset)
+{
+       int *line_termination = opt->value;
+
+       if (ctx->argc != 1)
+               return error("option '%s' must be the last argument", opt->long_name);
+       allow_add = allow_replace = allow_remove = 1;
+       read_index_info(*line_termination);
+       return 0;
+}
+
+static int stdin_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int unset)
+{
+       int *read_from_stdin = opt->value;
+
+       if (ctx->argc != 1)
+               return error("option '%s' must be the last argument", opt->long_name);
+       *read_from_stdin = 1;
+       return 0;
+}
+
+static int unresolve_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int flags)
+{
+       int *has_errors = opt->value;
+       const char *prefix = startup_info->prefix;
+
+       /* consume remaining arguments. */
+       *has_errors = do_unresolve(ctx->argc, ctx->argv,
+                               prefix, prefix ? strlen(prefix) : 0);
+       if (*has_errors)
+               active_cache_changed = 0;
+
+       ctx->argv += ctx->argc - 1;
+       ctx->argc = 1;
+       return 0;
+}
+
+static int reupdate_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int flags)
+{
+       int *has_errors = opt->value;
+       const char *prefix = startup_info->prefix;
+
+       /* consume remaining arguments. */
+       setup_work_tree();
+       *has_errors = do_reupdate(ctx->argc, ctx->argv,
+                               prefix, prefix ? strlen(prefix) : 0);
+       if (*has_errors)
+               active_cache_changed = 0;
+
+       ctx->argv += ctx->argc - 1;
+       ctx->argc = 1;
        return 0;
 }
 
 int cmd_update_index(int argc, const char **argv, const char *prefix)
 {
-       int i, newfd, entries, has_errors = 0, line_termination = '\n';
-       int allow_options = 1;
+       int newfd, entries, has_errors = 0, line_termination = '\n';
        int read_from_stdin = 0;
        int prefix_length = prefix ? strlen(prefix) : 0;
        char set_executable_bit = 0;
-       unsigned int refresh_flags = 0;
+       struct refresh_params refresh_args = {0, &has_errors};
        int lock_error = 0;
        struct lock_file *lock_file;
+       struct parse_opt_ctx_t ctx;
+       int parseopt_state = PARSE_OPT_UNKNOWN;
+       struct option options[] = {
+               OPT_BIT('q', NULL, &refresh_args.flags,
+                       "continue refresh even when index needs update",
+                       REFRESH_QUIET),
+               OPT_BIT(0, "ignore-submodules", &refresh_args.flags,
+                       "refresh: ignore submodules",
+                       REFRESH_IGNORE_SUBMODULES),
+               OPT_SET_INT(0, "add", &allow_add,
+                       "do not ignore new files", 1),
+               OPT_SET_INT(0, "replace", &allow_replace,
+                       "let files replace directories and vice-versa", 1),
+               OPT_SET_INT(0, "remove", &allow_remove,
+                       "notice files missing from worktree", 1),
+               OPT_BIT(0, "unmerged", &refresh_args.flags,
+                       "refresh even if index contains unmerged entries",
+                       REFRESH_UNMERGED),
+               {OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL,
+                       "refresh stat information",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       refresh_callback},
+               {OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL,
+                       "like --refresh, but ignore assume-unchanged setting",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       really_refresh_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL,
+                       "<mode> <object> <path>",
+                       "add the specified entry to the index",
+                       PARSE_OPT_NOARG |       /* disallow --cacheinfo=<mode> form */
+                       PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+                       (parse_opt_cb *) cacheinfo_callback},
+               {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+/-)x",
+                       "override the executable bit of the listed files",
+                       PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+                       chmod_callback},
+               {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL,
+                       "mark files as \"not changing\"",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+               {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL,
+                       "clear assumed-unchanged bit",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+               {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL,
+                       "mark files as \"index-only\"",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+               {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
+                       "clear skip-worktree bit",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+               OPT_SET_INT(0, "info-only", &info_only,
+                       "add to index only; do not add content to object database", 1),
+               OPT_SET_INT(0, "force-remove", &force_remove,
+                       "remove named paths even if present in worktree", 1),
+               OPT_SET_INT('z', NULL, &line_termination,
+                       "with --stdin: input lines are terminated by null bytes", '\0'),
+               {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL,
+                       "read list of paths to be updated from standard input",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) stdin_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &line_termination, NULL,
+                       "add entries from standard input to the index",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) stdin_cacheinfo_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL,
+                       "repopulate stages #2 and #3 for the listed paths",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) unresolve_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL,
+                       "only update entries that differ from HEAD",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) reupdate_callback},
+               OPT_BIT(0, "ignore-missing", &refresh_args.flags,
+                       "ignore files missing from worktree",
+                       REFRESH_IGNORE_MISSING),
+               OPT_SET_INT(0, "verbose", &verbose,
+                       "report actions to standard output", 1),
+               {OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL,
+                       "(for porcelains) forget saved unresolved conflicts",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       resolve_undo_clear_callback},
+               OPT_END()
+       };
+
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage(update_index_usage[0]);
 
        git_config(git_default_config, NULL);
 
@@ -602,151 +807,48 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        if (entries < 0)
                die("cache corrupted");
 
-       for (i = 1 ; i < argc; i++) {
-               const char *path = argv[i];
-               const char *p;
+       /*
+        * Custom copy of parse_options() because we want to handle
+        * filename arguments as they come.
+        */
+       parse_options_start(&ctx, argc, argv, prefix,
+                           options, PARSE_OPT_STOP_AT_NON_OPTION);
+       while (ctx.argc) {
+               if (parseopt_state != PARSE_OPT_DONE)
+                       parseopt_state = parse_options_step(&ctx, options,
+                                                           update_index_usage);
+               if (!ctx.argc)
+                       break;
+               switch (parseopt_state) {
+               case PARSE_OPT_HELP:
+                       exit(129);
+               case PARSE_OPT_NON_OPTION:
+               case PARSE_OPT_DONE:
+               {
+                       const char *path = ctx.argv[0];
+                       const char *p;
 
-               if (allow_options && *path == '-') {
-                       if (!strcmp(path, "--")) {
-                               allow_options = 0;
-                               continue;
-                       }
-                       if (!strcmp(path, "-q")) {
-                               refresh_flags |= REFRESH_QUIET;
-                               continue;
-                       }
-                       if (!strcmp(path, "--ignore-submodules")) {
-                               refresh_flags |= REFRESH_IGNORE_SUBMODULES;
-                               continue;
-                       }
-                       if (!strcmp(path, "--add")) {
-                               allow_add = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--replace")) {
-                               allow_replace = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--remove")) {
-                               allow_remove = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--unmerged")) {
-                               refresh_flags |= REFRESH_UNMERGED;
-                               continue;
-                       }
-                       if (!strcmp(path, "--refresh")) {
-                               setup_work_tree();
-                               has_errors |= refresh_cache(refresh_flags);
-                               continue;
-                       }
-                       if (!strcmp(path, "--really-refresh")) {
-                               setup_work_tree();
-                               has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags);
-                               continue;
-                       }
-                       if (!strcmp(path, "--cacheinfo")) {
-                               unsigned char sha1[20];
-                               unsigned int mode;
-
-                               if (i+3 >= argc)
-                                       die("git update-index: --cacheinfo <mode> <sha1> <path>");
-
-                               if (strtoul_ui(argv[i+1], 8, &mode) ||
-                                   get_sha1_hex(argv[i+2], sha1) ||
-                                   add_cacheinfo(mode, sha1, argv[i+3], 0))
-                                       die("git update-index: --cacheinfo"
-                                           " cannot add %s", argv[i+3]);
-                               i += 3;
-                               continue;
-                       }
-                       if (!strcmp(path, "--chmod=-x") ||
-                           !strcmp(path, "--chmod=+x")) {
-                               if (argc <= i+1)
-                                       die("git update-index: %s <path>", path);
-                               set_executable_bit = path[8];
-                               continue;
-                       }
-                       if (!strcmp(path, "--assume-unchanged")) {
-                               mark_valid_only = MARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--no-assume-unchanged")) {
-                               mark_valid_only = UNMARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--no-skip-worktree")) {
-                               mark_skip_worktree_only = UNMARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--skip-worktree")) {
-                               mark_skip_worktree_only = MARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--info-only")) {
-                               info_only = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--force-remove")) {
-                               force_remove = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "-z")) {
-                               line_termination = 0;
-                               continue;
-                       }
-                       if (!strcmp(path, "--stdin")) {
-                               if (i != argc - 1)
-                                       die("--stdin must be at the end");
-                               read_from_stdin = 1;
-                               break;
-                       }
-                       if (!strcmp(path, "--index-info")) {
-                               if (i != argc - 1)
-                                       die("--index-info must be at the end");
-                               allow_add = allow_replace = allow_remove = 1;
-                               read_index_info(line_termination);
-                               break;
-                       }
-                       if (!strcmp(path, "--unresolve")) {
-                               has_errors = do_unresolve(argc - i, argv + i,
-                                                         prefix, prefix_length);
-                               if (has_errors)
-                                       active_cache_changed = 0;
-                               goto finish;
-                       }
-                       if (!strcmp(path, "--again") || !strcmp(path, "-g")) {
-                               setup_work_tree();
-                               has_errors = do_reupdate(argc - i, argv + i,
-                                                        prefix, prefix_length);
-                               if (has_errors)
-                                       active_cache_changed = 0;
-                               goto finish;
-                       }
-                       if (!strcmp(path, "--ignore-missing")) {
-                               refresh_flags |= REFRESH_IGNORE_MISSING;
-                               continue;
-                       }
-                       if (!strcmp(path, "--verbose")) {
-                               verbose = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--clear-resolve-undo")) {
-                               resolve_undo_clear();
-                               continue;
-                       }
-                       if (!strcmp(path, "-h") || !strcmp(path, "--help"))
-                               usage(update_index_usage);
-                       die("unknown option %s", path);
+                       setup_work_tree();
+                       p = prefix_path(prefix, prefix_length, path);
+                       update_one(p, NULL, 0);
+                       if (set_executable_bit)
+                               chmod_path(set_executable_bit, p);
+                       if (p < path || p > path + strlen(path))
+                               free((char *)p);
+                       ctx.argc--;
+                       ctx.argv++;
+                       break;
+               }
+               case PARSE_OPT_UNKNOWN:
+                       if (ctx.argv[0][1] == '-')
+                               error("unknown option '%s'", ctx.argv[0] + 2);
+                       else
+                               error("unknown switch '%c'", *ctx.opt);
+                       usage_with_options(update_index_usage, options);
                }
-               setup_work_tree();
-               p = prefix_path(prefix, prefix_length, path);
-               update_one(p, NULL, 0);
-               if (set_executable_bit)
-                       chmod_path(set_executable_bit, p);
-               if (p < path || p > path + strlen(path))
-                       free((char *)p);
        }
+       argc = parse_options_end(&ctx);
+
        if (read_from_stdin) {
                struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
 
@@ -770,10 +872,9 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                strbuf_release(&buf);
        }
 
- finish:
        if (active_cache_changed) {
                if (newfd < 0) {
-                       if (refresh_flags & REFRESH_QUIET)
+                       if (refresh_args.flags & REFRESH_QUIET)
                                exit(128);
                        unable_to_lock_index_die(get_index_file(), lock_error);
                }
index 2b3fddcc69087d856b6a9c9f4383c5577a759c77..b90dce6358153b274a1e26afde9cc89aad473d14 100644 (file)
@@ -11,8 +11,7 @@ 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__FORCE(&force, "update the info files from scratch"),
                OPT_END()
        };
 
index 70fdb4dec7e8b0c56ded90ee7f086cc9f16293a1..0744bb83185289e26bd32b3b2ecaec717d8c75d7 100644 (file)
@@ -74,14 +74,9 @@ static int show_config(const char *var, const char *value, void *cb)
 
 int cmd_var(int argc, const char **argv, const char *prefix)
 {
-       const char *val;
-       int nongit;
-       if (argc != 2) {
+       const char *val = NULL;
+       if (argc != 2)
                usage(var_usage);
-       }
-
-       setup_git_directory_gently(&nongit);
-       val = NULL;
 
        if (strcmp(argv[1], "-l") == 0) {
                git_config(show_config, NULL);
index 9f482c29f516bde84023f401b28b133c1e605333..313476604967bb2962350c82d45ffad04eab09e1 100644 (file)
@@ -17,13 +17,11 @@ static const char * const verify_tag_usage[] = {
                NULL
 };
 
-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
-
 static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
 {
        struct child_process gpg;
        const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
-       char path[PATH_MAX], *eol;
+       char path[PATH_MAX];
        size_t len;
        int fd, ret;
 
@@ -37,11 +35,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
        close(fd);
 
        /* find the length without signature */
-       len = 0;
-       while (len < size && prefixcmp(buf + len, PGP_SIGNATURE)) {
-               eol = memchr(buf + len, '\n', size - len);
-               len += eol ? eol - (buf + len) + 1 : size - len;
-       }
+       len = parse_signature(buf, size);
        if (verbose)
                write_in_full(1, buf, len);
 
@@ -93,7 +87,7 @@ 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__VERBOSE(&verbose, "print tag contents"),
                OPT_END()
        };
 
index ff97adcb891caf98dcc655e71e29eaec5dae0135..65ea26bdb8c2cef671030c3a62d87d84612e8144 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -372,8 +372,10 @@ int create_bundle(struct bundle_header *header, const char *path,
        close(rls.in);
        if (finish_command(&rls))
                return error ("pack-objects died");
-       if (!bundle_to_stdout)
-               commit_lock_file(&lock);
+       if (!bundle_to_stdout) {
+               if (commit_lock_file(&lock))
+                       die_errno("cannot create '%s'", path);
+       }
        return 0;
 }
 
index c60cf9140dd18254886ea14de763738630f95973..f755590da827234830d8b4359720cfbfd87a3dea 100644 (file)
@@ -22,8 +22,10 @@ void cache_tree_free(struct cache_tree **it_p)
        if (!it)
                return;
        for (i = 0; i < it->subtree_nr; i++)
-               if (it->down[i])
+               if (it->down[i]) {
                        cache_tree_free(&it->down[i]->cache_tree);
+                       free(it->down[i]);
+               }
        free(it->down);
        free(it);
        *it_p = NULL;
diff --git a/cache.h b/cache.h
index cf5516faf0f9e755b38678cc17741f1d186859aa..4b9268dfa022fe0a03f1cd8b1b0b158ab4616f5d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -170,27 +170,26 @@ struct cache_entry {
  *
  * In-memory only flags
  */
-#define CE_UPDATE    (0x10000)
-#define CE_REMOVE    (0x20000)
-#define CE_UPTODATE  (0x40000)
-#define CE_ADDED     (0x80000)
+#define CE_UPDATE            (1 << 16)
+#define CE_REMOVE            (1 << 17)
+#define CE_UPTODATE          (1 << 18)
+#define CE_ADDED             (1 << 19)
 
-#define CE_HASHED    (0x100000)
-#define CE_UNHASHED  (0x200000)
-#define CE_CONFLICTED (0x800000)
+#define CE_HASHED            (1 << 20)
+#define CE_UNHASHED          (1 << 21)
+#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
+#define CE_CONFLICTED        (1 << 23)
 
-/* Only remove in work directory, not index */
-#define CE_WT_REMOVE (0x400000)
-
-#define CE_UNPACKED  (0x1000000)
+#define CE_UNPACKED          (1 << 24)
+#define CE_NEW_SKIP_WORKTREE (1 << 25)
 
 /*
  * Extended on-disk flags
  */
-#define CE_INTENT_TO_ADD 0x20000000
-#define CE_SKIP_WORKTREE 0x40000000
+#define CE_INTENT_TO_ADD     (1 << 29)
+#define CE_SKIP_WORKTREE     (1 << 30)
 /* CE_EXTENDED2 is for future extension */
-#define CE_EXTENDED2 0x80000000
+#define CE_EXTENDED2         (1 << 31)
 
 #define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
 
@@ -278,9 +277,16 @@ static inline int ce_to_dtype(const struct cache_entry *ce)
        else
                return DT_UNKNOWN;
 }
-#define canon_mode(mode) \
-       (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
-       S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
+static inline unsigned int canon_mode(unsigned int mode)
+{
+       if (S_ISREG(mode))
+               return S_IFREG | ce_permissions(mode);
+       if (S_ISLNK(mode))
+               return S_IFLNK;
+       if (S_ISDIR(mode))
+               return S_IFDIR;
+       return S_IFGITLINK;
+}
 
 #define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
 #define cache_entry_size(len) flexible_size(cache_entry,len)
@@ -379,6 +385,7 @@ static inline enum object_type object_type(unsigned int mode)
 #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
 #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
+#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
 #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
 #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
@@ -397,7 +404,7 @@ static inline enum object_type object_type(unsigned int mode)
  * environment creation or simple walk of the list.
  * The number of non-NULL entries is available as a macro.
  */
-#define LOCAL_REPO_ENV_SIZE 8
+#define LOCAL_REPO_ENV_SIZE 9
 extern const char *const local_repo_env[LOCAL_REPO_ENV_SIZE + 1];
 
 extern int is_bare_repository_cfg;
@@ -421,7 +428,7 @@ extern const char **get_pathspec(const char *prefix, const char **pathspec);
 extern void setup_work_tree(void);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
-extern const char *prefix_path(const char *prefix, int len, const char *path);
+extern char *prefix_path(const char *prefix, int len, const char *path);
 extern const char *prefix_filename(const char *prefix, int len, const char *path);
 extern int check_filename(const char *prefix, const char *name);
 extern void verify_filename(const char *prefix, const char *name);
@@ -438,7 +445,7 @@ extern int init_db(const char *template_dir, unsigned int flags);
  * at least 'nr' entries; the number of entries currently allocated
  * is 'alloc', using the standard growing factor alloc_nr() macro.
  *
- * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
+ * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
  */
 #define ALLOC_GROW(x, nr, alloc) \
        do { \
@@ -493,7 +500,22 @@ extern int index_name_is_other(const struct index_state *, const char *, int);
 extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
 extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
 
-extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
+struct pathspec {
+       const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
+       int nr;
+       int has_wildcard:1;
+       int recursive:1;
+       int max_depth;
+       struct pathspec_item {
+               const char *match;
+               int len;
+               int has_wildcard:1;
+       } *items;
+};
+
+extern int init_pathspec(struct pathspec *, const char **);
+extern void free_pathspec(struct pathspec *);
+extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
 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);
@@ -538,6 +560,7 @@ extern int assume_unchanged;
 extern int prefer_symlink_refs;
 extern int log_all_ref_updates;
 extern int warn_ambiguous_refs;
+extern int unique_abbrev_extra_length;
 extern int shared_repository;
 extern const char *apply_default_whitespace;
 extern const char *apply_default_ignorewhitespace;
@@ -552,6 +575,7 @@ extern int ignored_are_precious;
 extern int fsync_object_files;
 extern int core_preload_index;
 extern int core_apply_sparse_checkout;
+extern int core_clock_skew;
 
 enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@ -642,6 +666,9 @@ extern char *git_pathdup(const char *fmt, ...)
 /* Return a statically allocated filename matching the sha1 signature */
 extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern char *git_path_submodule(const char *path, const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
+
 extern char *sha1_file_name(const unsigned char *sha1);
 extern char *sha1_pack_name(const unsigned char *sha1);
 extern char *sha1_pack_index_name(const unsigned char *sha1);
@@ -850,7 +877,7 @@ struct cache_def {
 
 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 check_leading_path(const char *name, int len);
 extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 extern void schedule_dir_for_removal(const char *name, int len);
 extern void remove_scheduled_dirs(void);
@@ -972,9 +999,12 @@ extern int update_server_info(int);
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
+extern void git_config_push_parameter(const char *text);
 extern int git_config_parse_parameter(const char *text);
+extern int git_config_parse_environment(void);
 extern int git_config_from_parameters(config_fn_t fn, void *data);
 extern int git_config(config_fn_t fn, void *);
+extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_config_int(const char *, const char *);
 extern unsigned long git_config_ulong(const char *, const char *);
@@ -992,6 +1022,9 @@ extern int git_env_bool(const char *, int);
 extern int git_config_system(void);
 extern int git_config_global(void);
 extern int config_error_nonbool(const char *);
+extern const char *get_log_output_encoding(void);
+extern const char *get_commit_output_encoding(void);
+
 extern const char *config_exclusive_filename;
 
 #define MAX_GITNAME (1000)
@@ -1031,6 +1064,7 @@ extern int pager_in_use(void);
 extern int pager_use_color;
 
 extern const char *editor_program;
+extern const char *askpass_program;
 extern const char *excludes_file;
 
 /* base85 */
@@ -1050,12 +1084,14 @@ __attribute__((format (printf, 1, 2)))
 extern void trace_printf(const char *format, ...);
 __attribute__((format (printf, 2, 3)))
 extern void trace_argv_printf(const char **argv, const char *format, ...);
+extern void trace_repo_setup(const char *prefix);
 
 /* convert.c */
 /* returns 1 if *dst was used */
 extern int convert_to_git(const char *path, const char *src, size_t len,
                           struct strbuf *dst, enum safe_crlf checksafe);
 extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
+extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst);
 
 /* add */
 /*
@@ -1074,15 +1110,17 @@ void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *
 /*
  * whitespace rules.
  * used by both diff and apply
+ * last two digits are tab width
  */
-#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_TAB_IN_INDENT       040
+#define WS_BLANK_AT_EOL         0100
+#define WS_SPACE_BEFORE_TAB     0200
+#define WS_INDENT_WITH_NON_TAB  0400
+#define WS_CR_AT_EOL           01000
+#define WS_BLANK_AT_EOF        02000
+#define WS_TAB_IN_INDENT       04000
 #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
-#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
+#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
+#define WS_TAB_WIDTH_MASK        077
 extern unsigned whitespace_rule_cfg;
 extern unsigned whitespace_rule(const char *);
 extern unsigned parse_whitespace_rule(const char *);
@@ -1091,6 +1129,7 @@ extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *str
 extern char *whitespace_error_string(unsigned ws);
 extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
 extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
+#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
 
 /* ls-files */
 int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
@@ -1098,6 +1137,15 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
+/* Takes a negative value returned by split_cmdline */
+const char *split_cmdline_strerror(int cmdline_errno);
+
+/* git.c */
+struct startup_info {
+       int have_repository;
+       const char *prefix;
+};
+extern struct startup_info *startup_info;
 
 /* builtin/merge.c */
 int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
diff --git a/color.c b/color.c
index 1b00554dd50d2960b4e66f229b0d2d0da8eb8c28..6a5a54ec668e08ddef0d2f27359f3ebb3272243b 100644 (file)
--- a/color.c
+++ b/color.c
@@ -211,3 +211,8 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
        va_end(args);
        return r;
 }
+
+int color_is_nil(const char *c)
+{
+       return !strcmp(c, "NIL");
+}
diff --git a/color.h b/color.h
index 03ca0647485e8044d4131f2c61c37b4e9408cd2a..170ff4074d220e7e62264c4c63d1419f4a59d664 100644 (file)
--- a/color.h
+++ b/color.h
@@ -43,6 +43,9 @@
 #define GIT_COLOR_BG_MAGENTA   "\033[45m"
 #define GIT_COLOR_BG_CYAN      "\033[46m"
 
+/* A special value meaning "no color selected" */
+#define GIT_COLOR_NIL "NIL"
+
 /*
  * This variable stores the value of color.ui
  */
@@ -62,4 +65,6 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
 __attribute__((format (printf, 3, 4)))
 int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
 
+int color_is_nil(const char *color);
+
 #endif /* COLOR_H */
index 0094ec1c9278332f736d2814c847b272788a7dd3..85a65c096c1288f034084da42376ee3c63578c74 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -7,6 +7,7 @@
 #include "revision.h"
 #include "notes.h"
 
+int core_clock_skew = 86400;
 int save_commit_buffer = 1;
 
 const char *commit_type = "commit";
@@ -49,6 +50,19 @@ struct commit *lookup_commit(const unsigned char *sha1)
        return check_commit(obj, sha1, 0);
 }
 
+struct commit *lookup_commit_reference_by_name(const char *name)
+{
+       unsigned char sha1[20];
+       struct commit *commit;
+
+       if (get_sha1(name, sha1))
+               return NULL;
+       commit = lookup_commit_reference(sha1);
+       if (!commit || parse_commit(commit))
+               return NULL;
+       return commit;
+}
+
 static unsigned long parse_commit_date(const char *buf, const char *tail)
 {
        const char *dateptr;
@@ -137,12 +151,8 @@ struct commit_graft *read_graft_line(char *buf, int len)
                buf[--len] = '\0';
        if (buf[0] == '#' || buf[0] == '\0')
                return NULL;
-       if ((len + 1) % 41) {
-       bad_graft_data:
-               error("bad graft data: %s", buf);
-               free(graft);
-               return NULL;
-       }
+       if ((len + 1) % 41)
+               goto bad_graft_data;
        i = (len + 1) / 41 - 1;
        graft = xmalloc(sizeof(*graft) + 20 * i);
        graft->nr_parent = i;
@@ -155,6 +165,11 @@ struct commit_graft *read_graft_line(char *buf, int len)
                        goto bad_graft_data;
        }
        return graft;
+
+bad_graft_data:
+       error("bad graft data: %s", buf);
+       free(graft);
+       return NULL;
 }
 
 static int read_graft_file(const char *graft_file)
@@ -360,7 +375,7 @@ void free_commit_list(struct commit_list *list)
        }
 }
 
-struct commit_list * insert_by_date(struct commit *item, struct commit_list **list)
+struct commit_list * commit_list_insert_by_date(struct commit *item, struct commit_list **list)
 {
        struct commit_list **pp = list;
        struct commit_list *p;
@@ -374,11 +389,11 @@ struct commit_list * insert_by_date(struct commit *item, struct commit_list **li
 }
 
 
-void sort_by_date(struct commit_list **list)
+void commit_list_sort_by_date(struct commit_list **list)
 {
        struct commit_list *ret = NULL;
        while (*list) {
-               insert_by_date((*list)->item, &ret);
+               commit_list_insert_by_date((*list)->item, &ret);
                *list = (*list)->next;
        }
        *list = ret;
@@ -398,7 +413,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list,
                struct commit *commit = parents->item;
                if (!parse_commit(commit) && !(commit->object.flags & mark)) {
                        commit->object.flags |= mark;
-                       insert_by_date(commit, list);
+                       commit_list_insert_by_date(commit, list);
                }
                parents = parents->next;
        }
@@ -487,7 +502,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
 
        /* process the list in topological order */
        if (!lifo)
-               sort_by_date(&work);
+               commit_list_sort_by_date(&work);
 
        pptr = list;
        *list = NULL;
@@ -513,7 +528,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
                         */
                        if (--parent->indegree == 1) {
                                if (!lifo)
-                                       insert_by_date(parent, &work);
+                                       commit_list_insert_by_date(parent, &work);
                                else
                                        commit_list_insert(parent, &work);
                        }
@@ -573,10 +588,10 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
        }
 
        one->object.flags |= PARENT1;
-       insert_by_date(one, &list);
+       commit_list_insert_by_date(one, &list);
        for (i = 0; i < n; i++) {
                twos[i]->object.flags |= PARENT2;
-               insert_by_date(twos[i], &list);
+               commit_list_insert_by_date(twos[i], &list);
        }
 
        while (interesting(list)) {
@@ -594,7 +609,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
                if (flags == (PARENT1 | PARENT2)) {
                        if (!(commit->object.flags & RESULT)) {
                                commit->object.flags |= RESULT;
-                               insert_by_date(commit, &result);
+                               commit_list_insert_by_date(commit, &result);
                        }
                        /* Mark parents of a found merge stale */
                        flags |= STALE;
@@ -608,7 +623,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
                        if (parse_commit(p))
                                return NULL;
                        p->object.flags |= flags;
-                       insert_by_date(p, &list);
+                       commit_list_insert_by_date(p, &list);
                }
        }
 
@@ -618,7 +633,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
        while (list) {
                struct commit_list *next = list->next;
                if (!(list->item->object.flags & STALE))
-                       insert_by_date(list->item, &result);
+                       commit_list_insert_by_date(list->item, &result);
                free(list);
                list = next;
        }
@@ -711,7 +726,7 @@ struct commit_list *get_merge_bases_many(struct commit *one,
        result = NULL;
        for (i = 0; i < cnt; i++) {
                if (rslt[i])
-                       insert_by_date(rslt[i], &result);
+                       commit_list_insert_by_date(rslt[i], &result);
        }
        free(rslt);
        return result;
@@ -864,3 +879,65 @@ int commit_tree(const char *msg, unsigned char *tree,
        strbuf_release(&buffer);
        return result;
 }
+
+static int in_commit_list(const struct commit_list *want, struct commit *c)
+{
+       for (; want; want = want->next)
+               if (!hashcmp(want->item->object.sha1, c->object.sha1))
+                       return 1;
+       return 0;
+}
+
+static int contains_recurse(struct commit *candidate,
+                           const struct commit_list *want,
+                           unsigned long cutoff)
+{
+       struct commit_list *p;
+
+       /* was it previously marked as containing a want commit? */
+       if (candidate->object.flags & TMP_MARK)
+               return 1;
+       /* or marked as not possibly containing a want commit? */
+       if (candidate->object.flags & UNINTERESTING)
+               return 0;
+       /* or are we it? */
+       if (in_commit_list(want, candidate))
+               return 1;
+
+       if (parse_commit(candidate) < 0)
+               return 0;
+
+       /* stop searching if we go too far back in time */
+       if (candidate->date < cutoff)
+               return 0;
+
+       /* Otherwise recurse and mark ourselves for future traversals. */
+       for (p = candidate->parents; p; p = p->next) {
+               if (contains_recurse(p->item, want, cutoff)) {
+                       candidate->object.flags |= TMP_MARK;
+                       return 1;
+               }
+       }
+       candidate->object.flags |= UNINTERESTING;
+       return 0;
+}
+
+int contains(struct commit *candidate, const struct commit_list *want)
+{
+       unsigned long cutoff = 0;
+
+       if (core_clock_skew >= 0) {
+               const struct commit_list *c;
+               unsigned long min_date = ULONG_MAX;
+               for (c = want; c; c = c->next) {
+                       if (parse_commit(c->item) < 0)
+                               continue;
+                       if (c->item->date < min_date)
+                               min_date = c->item->date;
+               }
+               if (min_date > core_clock_skew)
+                       cutoff = min_date - core_clock_skew;
+       }
+
+       return contains_recurse(candidate, want, cutoff);
+}
index 9113bbe4889d71e824348edcb920110598db18d2..7d92d20b2f3c4e7cb3555e4f4d8390ca5644f095 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -36,22 +36,23 @@ struct commit *lookup_commit(const unsigned char *sha1);
 struct commit *lookup_commit_reference(const unsigned char *sha1);
 struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
                                              int quiet);
+struct commit *lookup_commit_reference_by_name(const char *name);
 
 int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
-
 int parse_commit(struct commit *item);
 
 /* Find beginning and length of commit subject. */
 int find_commit_subject(const char *commit_buffer, const char **subject);
 
-struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
+struct commit_list *commit_list_insert(struct commit *item,
+                                       struct commit_list **list);
 unsigned commit_list_count(const struct commit_list *l);
-struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);
+struct commit_list *commit_list_insert_by_date(struct commit *item,
+                                   struct commit_list **list);
+void commit_list_sort_by_date(struct commit_list **list);
 
 void free_commit_list(struct commit_list *list);
 
-void sort_by_date(struct commit_list **list);
-
 /* Commit formats */
 enum cmit_fmt {
        CMIT_FMT_RAW,
@@ -76,6 +77,7 @@ struct pretty_print_context
        int need_8bit_cte;
        int show_notes;
        struct reflog_walk_info *reflog_info;
+       const char *output_encoding;
 };
 
 struct userformat_want {
@@ -84,6 +86,8 @@ struct userformat_want {
 
 extern int has_non_ascii(const char *text);
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
+extern char *logmsg_reencode(const struct commit *commit,
+                            const char *output_encoding);
 extern char *reencode_commit_message(const struct commit *commit,
                                     const char **encoding_p);
 extern void get_commit_format(const char *arg, struct rev_info *);
@@ -156,6 +160,8 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,
 int is_descendant_of(struct commit *, struct commit_list *);
 int in_merge_bases(struct commit *, struct commit **, int);
 
+int contains(struct commit *, const struct commit_list *);
+
 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);
index f44498258d4c2a0ebd1379ed818d9d04b56f0761..ea249c6ac6423fd4ef865c1a9d0149ac0ba0cc46 100644 (file)
@@ -17,9 +17,9 @@
 
 #include <errno.h>
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+
+#include "../git-compat-util.h"
+
 #include <stdio.h>
 #include <string.h>
 
  *     Paul Vixie, 1996.
  */
 static const char *
-inet_ntop4(src, dst, size)
-       const u_char *src;
-       char *dst;
-       size_t size;
+inet_ntop4(const u_char *src, char *dst, size_t size)
 {
        static const char fmt[] = "%u.%u.%u.%u";
        char tmp[sizeof "255.255.255.255"];
@@ -78,10 +75,7 @@ inet_ntop4(src, dst, size)
  *     Paul Vixie, 1996.
  */
 static const char *
-inet_ntop6(src, dst, size)
-       const u_char *src;
-       char *dst;
-       size_t size;
+inet_ntop6(const u_char *src, char *dst, size_t size)
 {
        /*
         * Note that int32_t and int16_t need only be "at least" large enough
@@ -178,11 +172,7 @@ inet_ntop6(src, dst, size)
  *     Paul Vixie, 1996.
  */
 const char *
-inet_ntop(af, src, dst, size)
-       int af;
-       const void *src;
-       char *dst;
-       size_t size;
+inet_ntop(int af, const void *src, char *dst, size_t size)
 {
        switch (af) {
        case AF_INET:
index 4078fc0877ca99c82152acdd6b7a9eef70a9f8a4..2ec995e63d0cdef6dcd8b2fd6ea7aa544f4d1b33 100644 (file)
@@ -17,9 +17,9 @@
 
 #include <errno.h>
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+
+#include "../git-compat-util.h"
+
 #include <stdio.h>
 #include <string.h>
 
@@ -41,7 +41,9 @@
  */
 
 static int inet_pton4(const char *src, unsigned char *dst);
+#ifndef NO_IPV6
 static int inet_pton6(const char *src, unsigned char *dst);
+#endif
 
 /* int
  * inet_pton4(src, dst)
index 9a8e3365827d303c6513475726113a3952fe0040..bee605441910caeddd1e94eb7795f615a3d1348a 100644 (file)
@@ -127,7 +127,7 @@ int mingw_open (const char *filename, int oflags, ...)
        mode = va_arg(args, int);
        va_end(args);
 
-       if (!strcmp(filename, "/dev/null"))
+       if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
 
        fd = open(filename, oflags, mode);
@@ -160,7 +160,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t count)
 #undef fopen
 FILE *mingw_fopen (const char *filename, const char *otype)
 {
-       if (!strcmp(filename, "/dev/null"))
+       if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
        return fopen(filename, otype);
 }
@@ -192,12 +192,16 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
 /* We keep the do_lstat code in a separate function to avoid recursion.
  * When a path ends with a slash, the stat will fail with ENOENT. In
  * this case, we strip the trailing slashes and stat again.
+ *
+ * If follow is true then act like stat() and report on the link
+ * target. Otherwise report on the link itself.
  */
-static int do_lstat(const char *file_name, struct stat *buf)
+static int do_lstat(int follow, const char *file_name, struct stat *buf)
 {
+       int err;
        WIN32_FILE_ATTRIBUTE_DATA fdata;
 
-       if (!(errno = get_file_attr(file_name, &fdata))) {
+       if (!(err = get_file_attr(file_name, &fdata))) {
                buf->st_ino = 0;
                buf->st_gid = 0;
                buf->st_uid = 0;
@@ -209,8 +213,28 @@ static int do_lstat(const char *file_name, struct stat *buf)
                buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
                buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
                buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
+               if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+                       WIN32_FIND_DATAA findbuf;
+                       HANDLE handle = FindFirstFileA(file_name, &findbuf);
+                       if (handle != INVALID_HANDLE_VALUE) {
+                               if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+                                               (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
+                                       if (follow) {
+                                               char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+                                               buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+                                       } else {
+                                               buf->st_mode = S_IFLNK;
+                                       }
+                                       buf->st_mode |= S_IREAD;
+                                       if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
+                                               buf->st_mode |= S_IWRITE;
+                               }
+                               FindClose(handle);
+                       }
+               }
                return 0;
        }
+       errno = err;
        return -1;
 }
 
@@ -220,12 +244,12 @@ static int do_lstat(const char *file_name, struct stat *buf)
  * complete. Note that Git stat()s are redirected to mingw_lstat()
  * too, since Windows doesn't really handle symlinks that well.
  */
-int mingw_lstat(const char *file_name, struct stat *buf)
+static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
 {
        int namelen;
        static char alt_name[PATH_MAX];
 
-       if (!do_lstat(file_name, buf))
+       if (!do_lstat(follow, file_name, buf))
                return 0;
 
        /* if file_name ended in a '/', Windows returned ENOENT;
@@ -244,7 +268,16 @@ int mingw_lstat(const char *file_name, struct stat *buf)
 
        memcpy(alt_name, file_name, namelen);
        alt_name[namelen] = 0;
-       return do_lstat(alt_name, buf);
+       return do_lstat(follow, alt_name, buf);
+}
+
+int mingw_lstat(const char *file_name, struct stat *buf)
+{
+       return do_stat_internal(0, file_name, buf);
+}
+int mingw_stat(const char *file_name, struct stat *buf)
+{
+       return do_stat_internal(1, file_name, buf);
 }
 
 #undef fstat
@@ -304,8 +337,13 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
                goto revert_attrs;
        }
 
-       time_t_to_filetime(times->modtime, &mft);
-       time_t_to_filetime(times->actime, &aft);
+       if (times) {
+               time_t_to_filetime(times->modtime, &mft);
+               time_t_to_filetime(times->actime, &aft);
+       } else {
+               GetSystemTimeAsFileTime(&mft);
+               aft = mft;
+       }
        if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &aft, &mft)) {
                errno = EINVAL;
                rc = -1;
@@ -372,71 +410,6 @@ int pipe(int filedes[2])
        return 0;
 }
 
-int poll(struct pollfd *ufds, unsigned int nfds, int timeout)
-{
-       int i, pending;
-
-       if (timeout >= 0) {
-               if (nfds == 0) {
-                       Sleep(timeout);
-                       return 0;
-               }
-               return errno = EINVAL, error("poll timeout not supported");
-       }
-
-       /* When there is only one fd to wait for, then we pretend that
-        * input is available and let the actual wait happen when the
-        * caller invokes read().
-        */
-       if (nfds == 1) {
-               if (!(ufds[0].events & POLLIN))
-                       return errno = EINVAL, error("POLLIN not set");
-               ufds[0].revents = POLLIN;
-               return 0;
-       }
-
-repeat:
-       pending = 0;
-       for (i = 0; i < nfds; i++) {
-               DWORD avail = 0;
-               HANDLE h = (HANDLE) _get_osfhandle(ufds[i].fd);
-               if (h == INVALID_HANDLE_VALUE)
-                       return -1;      /* errno was set */
-
-               if (!(ufds[i].events & POLLIN))
-                       return errno = EINVAL, error("POLLIN not set");
-
-               /* this emulation works only for pipes */
-               if (!PeekNamedPipe(h, NULL, 0, NULL, &avail, NULL)) {
-                       int err = GetLastError();
-                       if (err == ERROR_BROKEN_PIPE) {
-                               ufds[i].revents = POLLHUP;
-                               pending++;
-                       } else {
-                               errno = EINVAL;
-                               return error("PeekNamedPipe failed,"
-                                       " GetLastError: %u", err);
-                       }
-               } else if (avail) {
-                       ufds[i].revents = POLLIN;
-                       pending++;
-               } else
-                       ufds[i].revents = 0;
-       }
-       if (!pending) {
-               /* The only times that we spin here is when the process
-                * that is connected through the pipes is waiting for
-                * its own input data to become available. But since
-                * the process (pack-objects) is itself CPU intensive,
-                * it will happily pick up the time slice that we are
-                * relinquishing here.
-                */
-               Sleep(0);
-               goto repeat;
-       }
-       return 0;
-}
-
 struct tm *gmtime_r(const time_t *timep, struct tm *result)
 {
        /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
@@ -641,7 +614,7 @@ static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_on
 }
 
 /*
- * Determines the absolute path of cmd using the the split path in path.
+ * Determines the absolute path of cmd using the split path in path.
  * If cmd contains a slash or backslash, no lookup is performed.
  */
 static char *path_lookup(const char *cmd, char **path, int exe_only)
@@ -666,6 +639,14 @@ static int env_compare(const void *a, const void *b)
        return strcasecmp(*ea, *eb);
 }
 
+struct pinfo_t {
+       struct pinfo_t *next;
+       pid_t pid;
+       HANDLE proc;
+} pinfo_t;
+struct pinfo_t *pinfo = NULL;
+CRITICAL_SECTION pinfo_cs;
+
 static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                              const char *dir,
                              int prepend_cmd, int fhin, int fhout, int fherr)
@@ -758,7 +739,26 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                return -1;
        }
        CloseHandle(pi.hThread);
-       return (pid_t)pi.hProcess;
+
+       /*
+        * The process ID is the human-readable identifier of the process
+        * that we want to present in log and error messages. The handle
+        * is not useful for this purpose. But we cannot close it, either,
+        * because it is not possible to turn a process ID into a process
+        * handle after the process terminated.
+        * Keep the handle in a list for waitpid.
+        */
+       EnterCriticalSection(&pinfo_cs);
+       {
+               struct pinfo_t *info = xmalloc(sizeof(struct pinfo_t));
+               info->pid = pi.dwProcessId;
+               info->proc = pi.hProcess;
+               info->next = pinfo;
+               pinfo = info;
+       }
+       LeaveCriticalSection(&pinfo_cs);
+
+       return (pid_t)pi.dwProcessId;
 }
 
 static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
@@ -868,6 +868,30 @@ void mingw_execvp(const char *cmd, char *const *argv)
        free_path_split(path);
 }
 
+void mingw_execv(const char *cmd, char *const *argv)
+{
+       mingw_execve(cmd, argv, environ);
+}
+
+int mingw_kill(pid_t pid, int sig)
+{
+       if (pid > 0 && sig == SIGTERM) {
+               HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
+
+               if (TerminateProcess(h, -1)) {
+                       CloseHandle(h);
+                       return 0;
+               }
+
+               errno = err_win_to_posix(GetLastError());
+               CloseHandle(h);
+               return -1;
+       }
+
+       errno = EINVAL;
+       return -1;
+}
+
 static char **copy_environ(void)
 {
        char **env;
@@ -952,19 +976,22 @@ static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
                                   const struct addrinfo *hints,
                                   struct addrinfo **res)
 {
-       struct hostent *h = gethostbyname(node);
+       struct hostent *h = NULL;
        struct addrinfo *ai;
        struct sockaddr_in *sin;
 
-       if (!h)
-               return WSAGetLastError();
+       if (node) {
+               h = gethostbyname(node);
+               if (!h)
+                       return WSAGetLastError();
+       }
 
        ai = xmalloc(sizeof(struct addrinfo));
        *res = ai;
        ai->ai_flags = 0;
        ai->ai_family = AF_INET;
-       ai->ai_socktype = hints->ai_socktype;
-       switch (hints->ai_socktype) {
+       ai->ai_socktype = hints ? hints->ai_socktype : 0;
+       switch (ai->ai_socktype) {
        case SOCK_STREAM:
                ai->ai_protocol = IPPROTO_TCP;
                break;
@@ -976,14 +1003,25 @@ static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
                break;
        }
        ai->ai_addrlen = sizeof(struct sockaddr_in);
-       ai->ai_canonname = strdup(h->h_name);
+       if (hints && (hints->ai_flags & AI_CANONNAME))
+               ai->ai_canonname = h ? strdup(h->h_name) : NULL;
+       else
+               ai->ai_canonname = NULL;
 
        sin = xmalloc(ai->ai_addrlen);
        memset(sin, 0, ai->ai_addrlen);
        sin->sin_family = AF_INET;
+       /* Note: getaddrinfo is supposed to allow service to be a string,
+        * which should be looked up using getservbyname. This is
+        * currently not implemented */
        if (service)
                sin->sin_port = htons(atoi(service));
-       sin->sin_addr = *(struct in_addr *)h->h_addr;
+       if (h)
+               sin->sin_addr = *(struct in_addr *)h->h_addr;
+       else if (hints && (hints->ai_flags & AI_PASSIVE))
+               sin->sin_addr.s_addr = INADDR_ANY;
+       else
+               sin->sin_addr.s_addr = INADDR_LOOPBACK;
        ai->ai_addr = (struct sockaddr *)sin;
        ai->ai_next = 0;
        return 0;
@@ -1134,7 +1172,10 @@ int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen,
 int mingw_socket(int domain, int type, int protocol)
 {
        int sockfd;
-       SOCKET s = WSASocket(domain, type, protocol, NULL, 0, 0);
+       SOCKET s;
+
+       ensure_socket_initialization();
+       s = WSASocket(domain, type, protocol, NULL, 0, 0);
        if (s == INVALID_SOCKET) {
                /*
                 * WSAGetLastError() values are regular BSD error codes
@@ -1164,6 +1205,45 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz)
        return connect(s, sa, sz);
 }
 
+#undef bind
+int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz)
+{
+       SOCKET s = (SOCKET)_get_osfhandle(sockfd);
+       return bind(s, sa, sz);
+}
+
+#undef setsockopt
+int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen)
+{
+       SOCKET s = (SOCKET)_get_osfhandle(sockfd);
+       return setsockopt(s, lvl, optname, (const char*)optval, optlen);
+}
+
+#undef listen
+int mingw_listen(int sockfd, int backlog)
+{
+       SOCKET s = (SOCKET)_get_osfhandle(sockfd);
+       return listen(s, backlog);
+}
+
+#undef accept
+int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
+{
+       int sockfd2;
+
+       SOCKET s1 = (SOCKET)_get_osfhandle(sockfd1);
+       SOCKET s2 = accept(s1, sa, sz);
+
+       /* convert into a file descriptor */
+       if ((sockfd2 = _open_osfhandle(s2, O_RDWR|O_BINARY)) < 0) {
+               int err = errno;
+               closesocket(s2);
+               return error("unable to make a socket file descriptor: %s",
+                       strerror(err));
+       }
+       return sockfd2;
+}
+
 #undef rename
 int mingw_rename(const char *pold, const char *pnew)
 {
@@ -1381,6 +1461,7 @@ void mingw_open_html(const char *unixpath)
                        const char *, const char *, const char *, INT);
        T ShellExecute;
        HMODULE shell32;
+       int r;
 
        shell32 = LoadLibrary("shell32.dll");
        if (!shell32)
@@ -1390,9 +1471,12 @@ void mingw_open_html(const char *unixpath)
                die("cannot run browser");
 
        printf("Launching default browser to display HTML ...\n");
-       ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0);
-
+       r = (int)ShellExecute(NULL, "open", htmlpath, NULL, "\\", SW_SHOWNORMAL);
        FreeLibrary(shell32);
+       /* see the MSDN documentation referring to the result codes here */
+       if (r <= 32) {
+               die("failed to launch browser for %.*s", MAX_PATH, unixpath);
+       }
 }
 
 int link(const char *oldpath, const char *newpath)
@@ -1431,62 +1515,54 @@ char *getpass(const char *prompt)
        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)
+pid_t waitpid(pid_t pid, int *status, unsigned options)
 {
-       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;
+       HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
+           FALSE, pid);
+       if (!h) {
+               errno = ECHILD;
+               return -1;
        }
 
-       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;
+       if (pid > 0 && options & WNOHANG) {
+               if (WAIT_OBJECT_0 != WaitForSingleObject(h, 0)) {
+                       CloseHandle(h);
+                       return 0;
                }
-       } 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;
+               options &= ~WNOHANG;
        }
 
-       /* We get here if `buf' contains valid data.  */
-       strcpy(dir->dd_dir.d_name, buf.cFileName);
-       ++dir->dd_stat;
+       if (options == 0) {
+               struct pinfo_t **ppinfo;
+               if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) {
+                       CloseHandle(h);
+                       return 0;
+               }
+
+               if (status)
+                       GetExitCodeProcess(h, (LPDWORD)status);
 
-       /* 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;
+               EnterCriticalSection(&pinfo_cs);
+
+               ppinfo = &pinfo;
+               while (*ppinfo) {
+                       struct pinfo_t *info = *ppinfo;
+                       if (info->pid == pid) {
+                               CloseHandle(info->proc);
+                               *ppinfo = info->next;
+                               free(info);
+                               break;
+                       }
+                       ppinfo = &info->next;
+               }
+
+               LeaveCriticalSection(&pinfo_cs);
 
-       return (struct dirent*)&dir->dd_dir;
+               CloseHandle(h);
+               return pid;
+       }
+       CloseHandle(h);
+
+       errno = EINVAL;
+       return -1;
 }
-#endif // !NO_MINGW_REPLACE_READDIR
index 3b2477be5f658be665f19a12b48cc47fa07d1c6b..cafc1eb08a71dd60566a7389ed606e777cf17fc8 100644 (file)
@@ -6,23 +6,34 @@
  */
 
 typedef int pid_t;
+typedef int uid_t;
+typedef int socklen_t;
 #define hstrerror strerror
 
 #define S_IFLNK    0120000 /* Symbolic link */
 #define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
 #define S_ISSOCK(x) 0
+
 #define S_IRGRP 0
 #define S_IWGRP 0
 #define S_IXGRP 0
-#define S_ISGID 0
+#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
 #define S_IROTH 0
+#define S_IWOTH 0
 #define S_IXOTH 0
+#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
+#define S_ISUID 0
+#define S_ISGID 0
+#define S_ISVTX 0
 
 #define WIFEXITED(x) 1
 #define WIFSIGNALED(x) 0
 #define WEXITSTATUS(x) ((x) & 0xff)
 #define WTERMSIG(x) SIGTERM
 
+#define EWOULDBLOCK EAGAIN
+#define SHUT_WR SD_SEND
+
 #define SIGHUP 1
 #define SIGQUIT 3
 #define SIGKILL 9
@@ -34,6 +45,9 @@ typedef int pid_t;
 #define F_SETFD 2
 #define FD_CLOEXEC 0x1
 
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#define ECONNABORTED WSAECONNABORTED
+
 struct passwd {
        char *pw_name;
        char *pw_gecos;
@@ -42,16 +56,6 @@ struct passwd {
 
 extern char *getpass(const char *prompt);
 
-#ifndef POLLIN
-struct pollfd {
-       int fd;           /* file descriptor */
-       short events;     /* requested events */
-       short revents;    /* returned events */
-};
-#define POLLIN 1
-#define POLLHUP 2
-#endif
-
 typedef void (__cdecl *sig_handler_t)(int);
 struct sigaction {
        sig_handler_t sa_handler;
@@ -65,6 +69,12 @@ struct itimerval {
 };
 #define ITIMER_REAL 0
 
+/*
+ * sanitize preprocessor namespace polluted by Windows headers defining
+ * macros which collide with git local versions
+ */
+#undef HELP_COMMAND /* from winuser.h */
+
 /*
  * trivial stubs
  */
@@ -75,17 +85,17 @@ static inline int symlink(const char *oldpath, const char *newpath)
 { errno = ENOSYS; return -1; }
 static inline int fchmod(int fildes, mode_t mode)
 { errno = ENOSYS; return -1; }
-static inline int fork(void)
+static inline pid_t fork(void)
 { errno = ENOSYS; return -1; }
 static inline unsigned int alarm(unsigned int seconds)
 { return 0; }
 static inline int fsync(int fd)
 { return _commit(fd); }
-static inline int getppid(void)
+static inline pid_t getppid(void)
 { return 1; }
 static inline void sync(void)
 {}
-static inline int getuid()
+static inline uid_t getuid(void)
 { return 1; }
 static inline struct passwd *getpwnam(const char *name)
 { return NULL; }
@@ -117,13 +127,11 @@ static inline int mingw_unlink(const char *pathname)
 }
 #define unlink mingw_unlink
 
-static inline int waitpid(pid_t pid, int *status, unsigned options)
-{
-       if (options == 0)
-               return _cwait(status, pid, 0);
-       errno = EINVAL;
-       return -1;
-}
+#define WNOHANG 1
+pid_t waitpid(pid_t pid, int *status, unsigned options);
+
+#define kill mingw_kill
+int mingw_kill(pid_t pid, int sig);
 
 #ifndef NO_OPENSSL
 #include <openssl/ssl.h>
@@ -154,11 +162,10 @@ int pipe(int filedes[2]);
 unsigned int sleep (unsigned int seconds);
 int mkstemp(char *template);
 int gettimeofday(struct timeval *tv, void *tz);
-int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
 struct tm *gmtime_r(const time_t *timep, struct tm *result);
 struct tm *localtime_r(const time_t *timep, struct tm *result);
 int getpagesize(void); /* defined in MinGW's libgcc.a */
-struct passwd *getpwuid(int uid);
+struct passwd *getpwuid(uid_t uid);
 int setitimer(int type, struct itimerval *in, struct itimerval *out);
 int sigaction(int sig, struct sigaction *in, struct sigaction *out);
 int link(const char *oldpath, const char *newpath);
@@ -206,6 +213,18 @@ int mingw_socket(int domain, int type, int protocol);
 int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
 #define connect mingw_connect
 
+int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
+#define bind mingw_bind
+
+int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
+#define setsockopt mingw_setsockopt
+
+int mingw_listen(int sockfd, int backlog);
+#define listen mingw_listen
+
+int mingw_accept(int sockfd, struct sockaddr *sa, socklen_t *sz);
+#define accept mingw_accept
+
 int mingw_rename(const char*, const char*);
 #define rename mingw_rename
 
@@ -222,10 +241,11 @@ int mingw_getpagesize(void);
 #ifndef ALREADY_DECLARED_STAT_FUNCS
 #define stat _stati64
 int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_stat(const char *file_name, struct stat *buf);
 int mingw_fstat(int fd, struct stat *buf);
 #define fstat mingw_fstat
 #define lstat mingw_lstat
-#define _stati64(x,y) mingw_lstat(x,y)
+#define _stati64(x,y) mingw_stat(x,y)
 #endif
 
 int mingw_utime(const char *file_name, const struct utimbuf *times);
@@ -236,6 +256,8 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
                     int fhin, int fhout, int fherr);
 void mingw_execvp(const char *cmd, char *const *argv);
 #define execvp mingw_execvp
+void mingw_execv(const char *cmd, char *const *argv);
+#define execv mingw_execv
 
 static inline unsigned int git_ntohl(unsigned int x)
 { return (unsigned int)ntohl(x); }
@@ -283,44 +305,17 @@ void free_environ(char **env);
 static int mingw_main(); \
 int main(int argc, const char **argv) \
 { \
+       extern CRITICAL_SECTION pinfo_cs; \
        _fmode = _O_BINARY; \
        _setmode(_fileno(stdin), _O_BINARY); \
        _setmode(_fileno(stdout), _O_BINARY); \
        _setmode(_fileno(stderr), _O_BINARY); \
        argv[0] = xstrdup(_pgmptr); \
+       InitializeCriticalSection(&pinfo_cs); \
        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
-
 /*
  * Used by Pthread API implementation for Windows
  */
index ac04a4ccbdd8319349aa2647374d78fd6639f425..71843d7eefafcea2ddf71b9fbff519790f661481 100644 (file)
@@ -3,33 +3,4 @@
 #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"
index 74c42e31626480900898aba8e12704335330e11e..87260d26425dbb167f64710e71235f60e467a9b5 100644 (file)
@@ -2069,7 +2069,7 @@ static void init_malloc_global_mutex() {
   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
+  ensured by making all allocations from 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.
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
new file mode 100644 (file)
index 0000000..8c96ed9
--- /dev/null
@@ -0,0 +1,3884 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002-2007,2009,2010 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA.  */
+
+static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
+                                         size_t length, reg_syntax_t syntax);
+static void re_compile_fastmap_iter (regex_t *bufp,
+                                    const re_dfastate_t *init_state,
+                                    char *fastmap);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len);
+#ifdef RE_ENABLE_I18N
+static void free_charset (re_charset_t *cset);
+#endif /* RE_ENABLE_I18N */
+static void free_workarea_compile (regex_t *preg);
+static reg_errcode_t create_initial_state (re_dfa_t *dfa);
+#ifdef RE_ENABLE_I18N
+static void optimize_utf8 (re_dfa_t *dfa);
+#endif
+static reg_errcode_t analyze (regex_t *preg);
+static reg_errcode_t preorder (bin_tree_t *root,
+                              reg_errcode_t (fn (void *, bin_tree_t *)),
+                              void *extra);
+static reg_errcode_t postorder (bin_tree_t *root,
+                               reg_errcode_t (fn (void *, bin_tree_t *)),
+                               void *extra);
+static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node);
+static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node);
+static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg,
+                                bin_tree_t *node);
+static reg_errcode_t calc_first (void *extra, bin_tree_t *node);
+static reg_errcode_t calc_next (void *extra, bin_tree_t *node);
+static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node);
+static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint);
+static int search_duplicated_node (const re_dfa_t *dfa, int org_node,
+                                  unsigned int constraint);
+static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
+static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
+                                        int node, int root);
+static reg_errcode_t calc_inveclosure (re_dfa_t *dfa);
+static int fetch_number (re_string_t *input, re_token_t *token,
+                        reg_syntax_t syntax);
+static int peek_token (re_token_t *token, re_string_t *input,
+                       reg_syntax_t syntax) internal_function;
+static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
+                         reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
+                                 re_token_t *token, reg_syntax_t syntax,
+                                 int nest, reg_errcode_t *err);
+static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
+                                re_token_t *token, reg_syntax_t syntax,
+                                int nest, reg_errcode_t *err);
+static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
+                                    re_token_t *token, reg_syntax_t syntax,
+                                    int nest, reg_errcode_t *err);
+static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
+                                 re_token_t *token, reg_syntax_t syntax,
+                                 int nest, reg_errcode_t *err);
+static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
+                                re_dfa_t *dfa, re_token_t *token,
+                                reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
+                                     re_token_t *token, reg_syntax_t syntax,
+                                     reg_errcode_t *err);
+static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
+                                           re_string_t *regexp,
+                                           re_token_t *token, int token_len,
+                                           re_dfa_t *dfa,
+                                           reg_syntax_t syntax,
+                                           int accept_hyphen);
+static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
+                                         re_string_t *regexp,
+                                         re_token_t *token);
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
+                                       re_charset_t *mbcset,
+                                       int *equiv_class_alloc,
+                                       const unsigned char *name);
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+                                     bitset_t sbcset,
+                                     re_charset_t *mbcset,
+                                     int *char_class_alloc,
+                                     const char *class_name,
+                                     reg_syntax_t syntax);
+#else  /* not RE_ENABLE_I18N */
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
+                                       const unsigned char *name);
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+                                     bitset_t sbcset,
+                                     const char *class_name,
+                                     reg_syntax_t syntax);
+#endif /* not RE_ENABLE_I18N */
+static bin_tree_t *build_charclass_op (re_dfa_t *dfa,
+                                      RE_TRANSLATE_TYPE trans,
+                                      const char *class_name,
+                                      const char *extra,
+                                      int non_match, reg_errcode_t *err);
+static bin_tree_t *create_tree (re_dfa_t *dfa,
+                               bin_tree_t *left, bin_tree_t *right,
+                               re_token_type_t type);
+static bin_tree_t *create_token_tree (re_dfa_t *dfa,
+                                     bin_tree_t *left, bin_tree_t *right,
+                                     const re_token_t *token);
+static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+static void free_token (re_token_t *node);
+static reg_errcode_t free_tree (void *extra, bin_tree_t *node);
+static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node);
+\f
+/* This table gives an error message for each of the error codes listed
+   in regex.h.  Obviously the order here has to be same as there.
+   POSIX doesn't require that we do anything for REG_NOERROR,
+   but why not be nice?  */
+
+const char __re_error_msgid[] attribute_hidden =
+  {
+#define REG_NOERROR_IDX        0
+    gettext_noop ("Success")   /* REG_NOERROR */
+    "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+    gettext_noop ("No match")  /* REG_NOMATCH */
+    "\0"
+#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
+    gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+    "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+    gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+    "\0"
+#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+    gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+    "\0"
+#define REG_EESCAPE_IDX        (REG_ECTYPE_IDX + sizeof "Invalid character class name")
+    gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+    "\0"
+#define REG_ESUBREG_IDX        (REG_EESCAPE_IDX + sizeof "Trailing backslash")
+    gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+    "\0"
+#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
+    gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
+    "\0"
+#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+    gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+    "\0"
+#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+    gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+    "\0"
+#define REG_BADBR_IDX  (REG_EBRACE_IDX + sizeof "Unmatched \\{")
+    gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+    "\0"
+#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+    gettext_noop ("Invalid range end") /* REG_ERANGE */
+    "\0"
+#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
+    gettext_noop ("Memory exhausted") /* REG_ESPACE */
+    "\0"
+#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
+    gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+    "\0"
+#define REG_EEND_IDX   (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+    gettext_noop ("Premature end of regular expression") /* REG_EEND */
+    "\0"
+#define REG_ESIZE_IDX  (REG_EEND_IDX + sizeof "Premature end of regular expression")
+    gettext_noop ("Regular expression too big") /* REG_ESIZE */
+    "\0"
+#define REG_ERPAREN_IDX        (REG_ESIZE_IDX + sizeof "Regular expression too big")
+    gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
+  };
+
+const size_t __re_error_msgid_idx[] attribute_hidden =
+  {
+    REG_NOERROR_IDX,
+    REG_NOMATCH_IDX,
+    REG_BADPAT_IDX,
+    REG_ECOLLATE_IDX,
+    REG_ECTYPE_IDX,
+    REG_EESCAPE_IDX,
+    REG_ESUBREG_IDX,
+    REG_EBRACK_IDX,
+    REG_EPAREN_IDX,
+    REG_EBRACE_IDX,
+    REG_BADBR_IDX,
+    REG_ERANGE_IDX,
+    REG_ESPACE_IDX,
+    REG_BADRPT_IDX,
+    REG_EEND_IDX,
+    REG_ESIZE_IDX,
+    REG_ERPAREN_IDX
+  };
+\f
+/* Entry points for GNU code.  */
+
+
+#ifdef ZOS_USS
+
+/* For ZOS USS we must define btowc */
+
+wchar_t 
+btowc (int c)
+{
+   wchar_t wtmp[2];
+   char tmp[2];
+
+   tmp[0] = c;
+   tmp[1] = 0;
+
+   mbtowc (wtmp, tmp, 1);
+   return wtmp[0];
+}
+#endif
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+   compiles PATTERN (of length LENGTH) and puts the result in BUFP.
+   Returns 0 if the pattern was valid, otherwise an error string.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+   are set in BUFP on entry.  */
+
+const char *
+re_compile_pattern (const char *pattern,
+                   size_t length,
+                   struct re_pattern_buffer *bufp)
+{
+  reg_errcode_t ret;
+
+  /* And GNU code determines whether or not to get register information
+     by passing null for the REGS argument to re_match, etc., not by
+     setting no_sub, unless RE_NO_SUB is set.  */
+  bufp->no_sub = !!(re_syntax_options & RE_NO_SUB);
+
+  /* Match anchors at newline.  */
+  bufp->newline_anchor = 1;
+
+  ret = re_compile_internal (bufp, pattern, length, re_syntax_options);
+
+  if (!ret)
+    return NULL;
+  return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+   also be assigned to arbitrarily: each pattern buffer stores its own
+   syntax, so it can be changed between regex compilations.  */
+/* This has no initializer because initialized variables in Emacs
+   become read-only after dumping.  */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation.  This provides
+   for compatibility for various utilities which historically have
+   different, incompatible syntaxes.
+
+   The argument SYNTAX is a bit mask comprised of the various bits
+   defined in regex.h.  We return the old syntax.  */
+
+reg_syntax_t
+re_set_syntax (reg_syntax_t syntax)
+{
+  reg_syntax_t ret = re_syntax_options;
+
+  re_syntax_options = syntax;
+  return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+int
+re_compile_fastmap (struct re_pattern_buffer *bufp)
+{
+  re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+  char *fastmap = bufp->fastmap;
+
+  memset (fastmap, '\0', sizeof (char) * SBC_MAX);
+  re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
+  if (dfa->init_state != dfa->init_state_word)
+    re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
+  if (dfa->init_state != dfa->init_state_nl)
+    re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
+  if (dfa->init_state != dfa->init_state_begbuf)
+    re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
+  bufp->fastmap_accurate = 1;
+  return 0;
+}
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+static inline void
+__attribute ((always_inline))
+re_set_fastmap (char *fastmap, int icase, int ch)
+{
+  fastmap[ch] = 1;
+  if (icase)
+    fastmap[tolower (ch)] = 1;
+}
+
+/* Helper function for re_compile_fastmap.
+   Compile fastmap for the initial_state INIT_STATE.  */
+
+static void
+re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
+                        char *fastmap)
+{
+  volatile re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+  int node_cnt;
+  int icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE));
+  for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
+    {
+      int node = init_state->nodes.elems[node_cnt];
+      re_token_type_t type = dfa->nodes[node].type;
+
+      if (type == CHARACTER)
+       {
+         re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c);
+#ifdef RE_ENABLE_I18N
+         if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+           {
+             unsigned char *buf = re_malloc (unsigned char, dfa->mb_cur_max), *p;
+             wchar_t wc;
+             mbstate_t state;
+
+             p = buf;
+             *p++ = dfa->nodes[node].opr.c;
+             while (++node < dfa->nodes_len
+                    && dfa->nodes[node].type == CHARACTER
+                    && dfa->nodes[node].mb_partial)
+               *p++ = dfa->nodes[node].opr.c;
+             memset (&state, '\0', sizeof (state));
+             if (__mbrtowc (&wc, (const char *) buf, p - buf,
+                            &state) == p - buf
+                 && (__wcrtomb ((char *) buf, towlower (wc), &state)
+                     != (size_t) -1))
+               re_set_fastmap (fastmap, 0, buf[0]);
+             re_free (buf);
+           }
+#endif
+       }
+      else if (type == SIMPLE_BRACKET)
+       {
+         int i, ch;
+         for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+           {
+             int j;
+             bitset_word_t w = dfa->nodes[node].opr.sbcset[i];
+             for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+               if (w & ((bitset_word_t) 1 << j))
+                 re_set_fastmap (fastmap, icase, ch);
+           }
+       }
+#ifdef RE_ENABLE_I18N
+      else if (type == COMPLEX_BRACKET)
+       {
+         re_charset_t *cset = dfa->nodes[node].opr.mbcset;
+         int i;
+
+# ifdef _LIBC
+         /* See if we have to try all bytes which start multiple collation
+            elements.
+            e.g. In da_DK, we want to catch 'a' since "aa" is a valid
+                 collation element, and don't catch 'b' since 'b' is
+                 the only collation element which starts from 'b' (and
+                 it is caught by SIMPLE_BRACKET).  */
+             if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0
+                 && (cset->ncoll_syms || cset->nranges))
+               {
+                 const int32_t *table = (const int32_t *)
+                   _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+                 for (i = 0; i < SBC_MAX; ++i)
+                   if (table[i] < 0)
+                     re_set_fastmap (fastmap, icase, i);
+               }
+# endif /* _LIBC */
+
+         /* See if we have to start the match at all multibyte characters,
+            i.e. where we would not find an invalid sequence.  This only
+            applies to multibyte character sets; for single byte character
+            sets, the SIMPLE_BRACKET again suffices.  */
+         if (dfa->mb_cur_max > 1
+             && (cset->nchar_classes || cset->non_match || cset->nranges
+# ifdef _LIBC
+                 || cset->nequiv_classes
+# endif /* _LIBC */
+                ))
+           {
+             unsigned char c = 0;
+             do
+               {
+                 mbstate_t mbs;
+                 memset (&mbs, 0, sizeof (mbs));
+                 if (__mbrtowc (NULL, (char *) &c, 1, &mbs) == (size_t) -2)
+                   re_set_fastmap (fastmap, false, (int) c);
+               }
+             while (++c != 0);
+           }
+
+         else
+           {
+             /* ... Else catch all bytes which can start the mbchars.  */
+             for (i = 0; i < cset->nmbchars; ++i)
+               {
+                 char buf[256];
+                 mbstate_t state;
+                 memset (&state, '\0', sizeof (state));
+                 if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1)
+                   re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
+                 if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+                   {
+                     if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state)
+                         != (size_t) -1)
+                       re_set_fastmap (fastmap, false, *(unsigned char *) buf);
+                   }
+               }
+           }
+       }
+#endif /* RE_ENABLE_I18N */
+      else if (type == OP_PERIOD
+#ifdef RE_ENABLE_I18N
+              || type == OP_UTF8_PERIOD
+#endif /* RE_ENABLE_I18N */
+              || type == END_OF_RE)
+       {
+         memset (fastmap, '\1', sizeof (char) * SBC_MAX);
+         if (type == END_OF_RE)
+           bufp->can_be_null = 1;
+         return;
+       }
+    }
+}
+\f
+/* Entry point for POSIX code.  */
+/* regcomp takes a regular expression as a string and compiles it.
+
+   PREG is a regex_t *.  We do not expect any fields to be initialized,
+   since POSIX says we shouldn't.  Thus, we set
+
+     `buffer' to the compiled pattern;
+     `used' to the length of the compiled pattern;
+     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+       REG_EXTENDED bit in CFLAGS is set; otherwise, to
+       RE_SYNTAX_POSIX_BASIC;
+     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+     `fastmap' to an allocated space for the fastmap;
+     `fastmap_accurate' to zero;
+     `re_nsub' to the number of subexpressions in PATTERN.
+
+   PATTERN is the address of the pattern string.
+
+   CFLAGS is a series of bits which affect compilation.
+
+     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+     use POSIX basic syntax.
+
+     If REG_NEWLINE is set, then . and [^...] don't match newline.
+     Also, regexec will try a match beginning after every newline.
+
+     If REG_ICASE is set, then we considers upper- and lowercase
+     versions of letters to be equivalent when matching.
+
+     If REG_NOSUB is set, then when PREG is passed to regexec, that
+     routine will report only success or failure, and nothing about the
+     registers.
+
+   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
+   the return codes and their meanings.)  */
+
+int
+regcomp (regex_t *__restrict preg,
+        const char *__restrict pattern,
+        int cflags)
+{
+  reg_errcode_t ret;
+  reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
+                        : RE_SYNTAX_POSIX_BASIC);
+
+  preg->buffer = NULL;
+  preg->allocated = 0;
+  preg->used = 0;
+
+  /* Try to allocate space for the fastmap.  */
+  preg->fastmap = re_malloc (char, SBC_MAX);
+  if (BE (preg->fastmap == NULL, 0))
+    return REG_ESPACE;
+
+  syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
+
+  /* If REG_NEWLINE is set, newlines are treated differently.  */
+  if (cflags & REG_NEWLINE)
+    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
+      syntax &= ~RE_DOT_NEWLINE;
+      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+      /* It also changes the matching behavior.  */
+      preg->newline_anchor = 1;
+    }
+  else
+    preg->newline_anchor = 0;
+  preg->no_sub = !!(cflags & REG_NOSUB);
+  preg->translate = NULL;
+
+  ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
+
+  /* POSIX doesn't distinguish between an unmatched open-group and an
+     unmatched close-group: both are REG_EPAREN.  */
+  if (ret == REG_ERPAREN)
+    ret = REG_EPAREN;
+
+  /* We have already checked preg->fastmap != NULL.  */
+  if (BE (ret == REG_NOERROR, 1))
+    /* Compute the fastmap now, since regexec cannot modify the pattern
+       buffer.  This function never fails in this implementation.  */
+    (void) re_compile_fastmap (preg);
+  else
+    {
+      /* Some error occurred while compiling the expression.  */
+      re_free (preg->fastmap);
+      preg->fastmap = NULL;
+    }
+
+  return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+   from either regcomp or regexec.   We don't use PREG here.  */
+
+size_t
+regerror(int errcode, const regex_t *__restrict preg,
+        char *__restrict errbuf, size_t errbuf_size)
+{
+  const char *msg;
+  size_t msg_size;
+
+  if (BE (errcode < 0
+         || errcode >= (int) (sizeof (__re_error_msgid_idx)
+                              / sizeof (__re_error_msgid_idx[0])), 0))
+    /* Only error codes returned by the rest of the code should be passed
+       to this routine.  If we are given anything else, or if other regex
+       code generates an invalid error code, then the program has a bug.
+       Dump core so we can fix it.  */
+    abort ();
+
+  msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
+
+  msg_size = strlen (msg) + 1; /* Includes the null.  */
+
+  if (BE (errbuf_size != 0, 1))
+    {
+      if (BE (msg_size > errbuf_size, 0))
+       {
+         memcpy (errbuf, msg, errbuf_size - 1);
+         errbuf[errbuf_size - 1] = 0;
+       }
+      else
+       memcpy (errbuf, msg, msg_size);
+    }
+
+  return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+
+#ifdef RE_ENABLE_I18N
+/* This static array is used for the map to single-byte characters when
+   UTF-8 is used.  Otherwise we would allocate memory just to initialize
+   it the same all the time.  UTF-8 is the preferred encoding so this is
+   a worthwhile optimization.  */
+#if __GNUC__ >= 3
+static const bitset_t utf8_sb_map = {
+  /* Set the first 128 bits.  */
+  [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX
+};
+#else /* ! (__GNUC__ >= 3) */
+static bitset_t utf8_sb_map;
+#endif /* __GNUC__ >= 3 */
+#endif /* RE_ENABLE_I18N */
+
+
+static void
+free_dfa_content (re_dfa_t *dfa)
+{
+  int i, j;
+
+  if (dfa->nodes)
+    for (i = 0; i < dfa->nodes_len; ++i)
+      free_token (dfa->nodes + i);
+  re_free (dfa->nexts);
+  for (i = 0; i < dfa->nodes_len; ++i)
+    {
+      if (dfa->eclosures != NULL)
+       re_node_set_free (dfa->eclosures + i);
+      if (dfa->inveclosures != NULL)
+       re_node_set_free (dfa->inveclosures + i);
+      if (dfa->edests != NULL)
+       re_node_set_free (dfa->edests + i);
+    }
+  re_free (dfa->edests);
+  re_free (dfa->eclosures);
+  re_free (dfa->inveclosures);
+  re_free (dfa->nodes);
+
+  if (dfa->state_table)
+    for (i = 0; i <= dfa->state_hash_mask; ++i)
+      {
+       struct re_state_table_entry *entry = dfa->state_table + i;
+       for (j = 0; j < entry->num; ++j)
+         {
+           re_dfastate_t *state = entry->array[j];
+           free_state (state);
+         }
+       re_free (entry->array);
+      }
+  re_free (dfa->state_table);
+#ifdef RE_ENABLE_I18N
+  if (dfa->sb_char != utf8_sb_map)
+    re_free (dfa->sb_char);
+#endif
+  re_free (dfa->subexp_map);
+#ifdef DEBUG
+  re_free (dfa->re_str);
+#endif
+
+  re_free (dfa);
+}
+
+
+/* Free dynamically allocated space used by PREG.  */
+
+void
+regfree (regex_t *preg)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  if (BE (dfa != NULL, 1))
+    free_dfa_content (dfa);
+  preg->buffer = NULL;
+  preg->allocated = 0;
+
+  re_free (preg->fastmap);
+  preg->fastmap = NULL;
+
+  re_free (preg->translate);
+  preg->translate = NULL;
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+\f
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them unless specifically requested.  */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer.  */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+# ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+   these names if they don't use our functions, and still use
+   regcomp/regexec above without link errors.  */
+weak_function
+# endif
+re_comp (s)
+     const char *s;
+{
+  reg_errcode_t ret;
+  char *fastmap;
+
+  if (!s)
+    {
+      if (!re_comp_buf.buffer)
+       return gettext ("No previous regular expression");
+      return 0;
+    }
+
+  if (re_comp_buf.buffer)
+    {
+      fastmap = re_comp_buf.fastmap;
+      re_comp_buf.fastmap = NULL;
+      __regfree (&re_comp_buf);
+      memset (&re_comp_buf, '\0', sizeof (re_comp_buf));
+      re_comp_buf.fastmap = fastmap;
+    }
+
+  if (re_comp_buf.fastmap == NULL)
+    {
+      re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
+      if (re_comp_buf.fastmap == NULL)
+       return (char *) gettext (__re_error_msgid
+                                + __re_error_msgid_idx[(int) REG_ESPACE]);
+    }
+
+  /* Since `re_exec' always passes NULL for the `regs' argument, we
+     don't need to initialize the pattern buffer fields which affect it.  */
+
+  /* Match anchors at newlines.  */
+  re_comp_buf.newline_anchor = 1;
+
+  ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
+
+  if (!ret)
+    return NULL;
+
+  /* Yes, we're discarding `const' here if !HAVE_LIBINTL.  */
+  return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+
+#ifdef _LIBC
+libc_freeres_fn (free_mem)
+{
+  __regfree (&re_comp_buf);
+}
+#endif
+
+#endif /* _REGEX_RE_COMP */
+\f
+/* Internal entry point.
+   Compile the regular expression PATTERN, whose length is LENGTH.
+   SYNTAX indicate regular expression's syntax.  */
+
+static reg_errcode_t
+re_compile_internal (regex_t *preg, const char * pattern, size_t length,
+                    reg_syntax_t syntax)
+{
+  reg_errcode_t err = REG_NOERROR;
+  re_dfa_t *dfa;
+  re_string_t regexp;
+
+  /* Initialize the pattern buffer.  */
+  preg->fastmap_accurate = 0;
+  preg->syntax = syntax;
+  preg->not_bol = preg->not_eol = 0;
+  preg->used = 0;
+  preg->re_nsub = 0;
+  preg->can_be_null = 0;
+  preg->regs_allocated = REGS_UNALLOCATED;
+
+  /* Initialize the dfa.  */
+  dfa = (re_dfa_t *) preg->buffer;
+  if (BE (preg->allocated < sizeof (re_dfa_t), 0))
+    {
+      /* If zero allocated, but buffer is non-null, try to realloc
+        enough space.  This loses if buffer's address is bogus, but
+        that is the user's responsibility.  If ->buffer is NULL this
+        is a simple allocation.  */
+      dfa = re_realloc (preg->buffer, re_dfa_t, 1);
+      if (dfa == NULL)
+       return REG_ESPACE;
+      preg->allocated = sizeof (re_dfa_t);
+      preg->buffer = (unsigned char *) dfa;
+    }
+  preg->used = sizeof (re_dfa_t);
+
+  err = init_dfa (dfa, length);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_dfa_content (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+      return err;
+    }
+#ifdef DEBUG
+  /* Note: length+1 will not overflow since it is checked in init_dfa.  */
+  dfa->re_str = re_malloc (char, length + 1);
+  strncpy (dfa->re_str, pattern, length + 1);
+#endif
+
+  __libc_lock_init (dfa->lock);
+
+  err = re_string_construct (&regexp, pattern, length, preg->translate,
+                            syntax & RE_ICASE, dfa);
+  if (BE (err != REG_NOERROR, 0))
+    {
+    re_compile_internal_free_return:
+      free_workarea_compile (preg);
+      re_string_destruct (&regexp);
+      free_dfa_content (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+      return err;
+    }
+
+  /* Parse the regular expression, and build a structure tree.  */
+  preg->re_nsub = 0;
+  dfa->str_tree = parse (&regexp, preg, syntax, &err);
+  if (BE (dfa->str_tree == NULL, 0))
+    goto re_compile_internal_free_return;
+
+  /* Analyze the tree and create the nfa.  */
+  err = analyze (preg);
+  if (BE (err != REG_NOERROR, 0))
+    goto re_compile_internal_free_return;
+
+#ifdef RE_ENABLE_I18N
+  /* If possible, do searching in single byte encoding to speed things up.  */
+  if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL)
+    optimize_utf8 (dfa);
+#endif
+
+  /* Then create the initial state of the dfa.  */
+  err = create_initial_state (dfa);
+
+  /* Release work areas.  */
+  free_workarea_compile (preg);
+  re_string_destruct (&regexp);
+
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_dfa_content (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+    }
+
+  return err;
+}
+
+/* Initialize DFA.  We use the length of the regular expression PAT_LEN
+   as the initial length of some arrays.  */
+
+static reg_errcode_t
+init_dfa (re_dfa_t *dfa, size_t pat_len)
+{
+  unsigned int table_size;
+#ifndef _LIBC
+  char *codeset_name;
+#endif
+
+  memset (dfa, '\0', sizeof (re_dfa_t));
+
+  /* Force allocation of str_tree_storage the first time.  */
+  dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+
+  /* Avoid overflows.  */
+  if (pat_len == SIZE_MAX)
+    return REG_ESPACE;
+
+  dfa->nodes_alloc = pat_len + 1;
+  dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
+
+  /*  table_size = 2 ^ ceil(log pat_len) */
+  for (table_size = 1; ; table_size <<= 1)
+    if (table_size > pat_len)
+      break;
+
+  dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+  dfa->state_hash_mask = table_size - 1;
+
+  dfa->mb_cur_max = MB_CUR_MAX;
+#ifdef _LIBC
+  if (dfa->mb_cur_max == 6
+      && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0)
+    dfa->is_utf8 = 1;
+  dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII)
+                      != 0);
+#else
+# ifdef HAVE_LANGINFO_CODESET
+  codeset_name = nl_langinfo (CODESET);
+# else
+  codeset_name = getenv ("LC_ALL");
+  if (codeset_name == NULL || codeset_name[0] == '\0')
+    codeset_name = getenv ("LC_CTYPE");
+  if (codeset_name == NULL || codeset_name[0] == '\0')
+    codeset_name = getenv ("LANG");
+  if (codeset_name == NULL)
+    codeset_name = "";
+  else if (strchr (codeset_name, '.') !=  NULL)
+    codeset_name = strchr (codeset_name, '.') + 1;
+# endif
+
+  /* strcasecmp isn't a standard interface. brute force check */
+#if 0
+  if (strcasecmp (codeset_name, "UTF-8") == 0
+      || strcasecmp (codeset_name, "UTF8") == 0)
+    dfa->is_utf8 = 1;
+#else
+  if (   (codeset_name[0] == 'U' || codeset_name[0] == 'u')
+      && (codeset_name[1] == 'T' || codeset_name[1] == 't')
+      && (codeset_name[2] == 'F' || codeset_name[2] == 'f')
+      && (codeset_name[3] == '-'
+          ? codeset_name[4] == '8' && codeset_name[5] == '\0'
+          : codeset_name[3] == '8' && codeset_name[4] == '\0'))
+    dfa->is_utf8 = 1;
+#endif
+
+  /* We check exhaustively in the loop below if this charset is a
+     superset of ASCII.  */
+  dfa->map_notascii = 0;
+#endif
+
+#ifdef RE_ENABLE_I18N
+  if (dfa->mb_cur_max > 1)
+    {
+      if (dfa->is_utf8)
+        {
+#if !defined(__GNUC__) || __GNUC__ < 3
+         static short utf8_sb_map_inited = 0;
+
+         if (! utf8_sb_map_inited)
+           {
+               int i;
+
+               utf8_sb_map_inited = 0;
+               for (i = 0; i <= 0x80 / BITSET_WORD_BITS - 1; i++)
+                 utf8_sb_map[i] = BITSET_WORD_MAX;
+           }
+#endif
+         dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map;
+       }
+      else
+       {
+         int i, j, ch;
+
+         dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+         if (BE (dfa->sb_char == NULL, 0))
+           return REG_ESPACE;
+
+         /* Set the bits corresponding to single byte chars.  */
+         for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+           for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+             {
+               wint_t wch = __btowc (ch);
+               if (wch != WEOF)
+                 dfa->sb_char[i] |= (bitset_word_t) 1 << j;
+# ifndef _LIBC
+               if (isascii (ch) && wch != ch)
+                 dfa->map_notascii = 1;
+# endif
+             }
+       }
+    }
+#endif
+
+  if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0))
+    return REG_ESPACE;
+  return REG_NOERROR;
+}
+
+/* Initialize WORD_CHAR table, which indicate which character is
+   "word".  In this case "word" means that it is the word construction
+   character used by some operators like "\<", "\>", etc.  */
+
+static void
+internal_function
+init_word_char (re_dfa_t *dfa)
+{
+  int i, j, ch;
+  dfa->word_ops_used = 1;
+  for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+    for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+      if (isalnum (ch) || ch == '_')
+       dfa->word_char[i] |= (bitset_word_t) 1 << j;
+}
+
+/* Free the work area which are only used while compiling.  */
+
+static void
+free_workarea_compile (regex_t *preg)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_storage_t *storage, *next;
+  for (storage = dfa->str_tree_storage; storage; storage = next)
+    {
+      next = storage->next;
+      re_free (storage);
+    }
+  dfa->str_tree_storage = NULL;
+  dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+  dfa->str_tree = NULL;
+  re_free (dfa->org_indices);
+  dfa->org_indices = NULL;
+}
+
+/* Create initial states for all contexts.  */
+
+static reg_errcode_t
+create_initial_state (re_dfa_t *dfa)
+{
+  int first, i;
+  reg_errcode_t err;
+  re_node_set init_nodes;
+
+  /* Initial states have the epsilon closure of the node which is
+     the first node of the regular expression.  */
+  first = dfa->str_tree->first->node_idx;
+  dfa->init_node = first;
+  err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  /* The back-references which are in initial states can epsilon transit,
+     since in this case all of the subexpressions can be null.
+     Then we add epsilon closures of the nodes which are the next nodes of
+     the back-references.  */
+  if (dfa->nbackref > 0)
+    for (i = 0; i < init_nodes.nelem; ++i)
+      {
+       int node_idx = init_nodes.elems[i];
+       re_token_type_t type = dfa->nodes[node_idx].type;
+
+       int clexp_idx;
+       if (type != OP_BACK_REF)
+         continue;
+       for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
+         {
+           re_token_t *clexp_node;
+           clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
+           if (clexp_node->type == OP_CLOSE_SUBEXP
+               && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx)
+             break;
+         }
+       if (clexp_idx == init_nodes.nelem)
+         continue;
+
+       if (type == OP_BACK_REF)
+         {
+           int dest_idx = dfa->edests[node_idx].elems[0];
+           if (!re_node_set_contains (&init_nodes, dest_idx))
+             {
+               reg_errcode_t err = re_node_set_merge (&init_nodes,
+                                                      dfa->eclosures
+                                                      + dest_idx);
+               if (err != REG_NOERROR)
+                 return err;
+               i = 0;
+             }
+         }
+      }
+
+  /* It must be the first time to invoke acquire_state.  */
+  dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0);
+  /* We don't check ERR here, since the initial state must not be NULL.  */
+  if (BE (dfa->init_state == NULL, 0))
+    return err;
+  if (dfa->init_state->has_constraint)
+    {
+      dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes,
+                                                      CONTEXT_WORD);
+      dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes,
+                                                    CONTEXT_NEWLINE);
+      dfa->init_state_begbuf = re_acquire_state_context (&err, dfa,
+                                                        &init_nodes,
+                                                        CONTEXT_NEWLINE
+                                                        | CONTEXT_BEGBUF);
+      if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+             || dfa->init_state_begbuf == NULL, 0))
+       return err;
+    }
+  else
+    dfa->init_state_word = dfa->init_state_nl
+      = dfa->init_state_begbuf = dfa->init_state;
+
+  re_node_set_free (&init_nodes);
+  return REG_NOERROR;
+}
+\f
+#ifdef RE_ENABLE_I18N
+/* If it is possible to do searching in single byte encoding instead of UTF-8
+   to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change
+   DFA nodes where needed.  */
+
+static void
+optimize_utf8 (re_dfa_t *dfa)
+{
+  int node, i, mb_chars = 0, has_period = 0;
+
+  for (node = 0; node < dfa->nodes_len; ++node)
+    switch (dfa->nodes[node].type)
+      {
+      case CHARACTER:
+       if (dfa->nodes[node].opr.c >= 0x80)
+         mb_chars = 1;
+       break;
+      case ANCHOR:
+       switch (dfa->nodes[node].opr.ctx_type)
+         {
+         case LINE_FIRST:
+         case LINE_LAST:
+         case BUF_FIRST:
+         case BUF_LAST:
+           break;
+         default:
+           /* Word anchors etc. cannot be handled.  It's okay to test
+              opr.ctx_type since constraints (for all DFA nodes) are
+              created by ORing one or more opr.ctx_type values.  */
+           return;
+         }
+       break;
+      case OP_PERIOD:
+       has_period = 1;
+       break;
+      case OP_BACK_REF:
+      case OP_ALT:
+      case END_OF_RE:
+      case OP_DUP_ASTERISK:
+      case OP_OPEN_SUBEXP:
+      case OP_CLOSE_SUBEXP:
+       break;
+      case COMPLEX_BRACKET:
+       return;
+      case SIMPLE_BRACKET:
+       /* Just double check.  The non-ASCII range starts at 0x80.  */
+       assert (0x80 % BITSET_WORD_BITS == 0);
+       for (i = 0x80 / BITSET_WORD_BITS; i < BITSET_WORDS; ++i)
+         if (dfa->nodes[node].opr.sbcset[i])
+           return;
+       break;
+      default:
+       abort ();
+      }
+
+  if (mb_chars || has_period)
+    for (node = 0; node < dfa->nodes_len; ++node)
+      {
+       if (dfa->nodes[node].type == CHARACTER
+           && dfa->nodes[node].opr.c >= 0x80)
+         dfa->nodes[node].mb_partial = 0;
+       else if (dfa->nodes[node].type == OP_PERIOD)
+         dfa->nodes[node].type = OP_UTF8_PERIOD;
+      }
+
+  /* The search can be in single byte locale.  */
+  dfa->mb_cur_max = 1;
+  dfa->is_utf8 = 0;
+  dfa->has_mb_node = dfa->nbackref > 0 || has_period;
+}
+#endif
+\f
+/* Analyze the structure tree, and calculate "first", "next", "edest",
+   "eclosure", and "inveclosure".  */
+
+static reg_errcode_t
+analyze (regex_t *preg)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  reg_errcode_t ret;
+
+  /* Allocate arrays.  */
+  dfa->nexts = re_malloc (int, dfa->nodes_alloc);
+  dfa->org_indices = re_malloc (int, dfa->nodes_alloc);
+  dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
+  dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+  if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
+         || dfa->eclosures == NULL, 0))
+    return REG_ESPACE;
+
+  dfa->subexp_map = re_malloc (int, preg->re_nsub);
+  if (dfa->subexp_map != NULL)
+    {
+      int i;
+      for (i = 0; i < preg->re_nsub; i++)
+       dfa->subexp_map[i] = i;
+      preorder (dfa->str_tree, optimize_subexps, dfa);
+      for (i = 0; i < preg->re_nsub; i++)
+       if (dfa->subexp_map[i] != i)
+         break;
+      if (i == preg->re_nsub)
+       {
+         free (dfa->subexp_map);
+         dfa->subexp_map = NULL;
+       }
+    }
+
+  ret = postorder (dfa->str_tree, lower_subexps, preg);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  ret = postorder (dfa->str_tree, calc_first, dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  preorder (dfa->str_tree, calc_next, dfa);
+  ret = preorder (dfa->str_tree, link_nfa_nodes, dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  ret = calc_eclosure (dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  /* We only need this during the prune_impossible_nodes pass in regexec.c;
+     skip it if p_i_n will not run, as calc_inveclosure can be quadratic.  */
+  if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match)
+      || dfa->nbackref)
+    {
+      dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len);
+      if (BE (dfa->inveclosures == NULL, 0))
+       return REG_ESPACE;
+      ret = calc_inveclosure (dfa);
+    }
+
+  return ret;
+}
+
+/* Our parse trees are very unbalanced, so we cannot use a stack to
+   implement parse tree visits.  Instead, we use parent pointers and
+   some hairy code in these two functions.  */
+static reg_errcode_t
+postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+          void *extra)
+{
+  bin_tree_t *node, *prev;
+
+  for (node = root; ; )
+    {
+      /* Descend down the tree, preferably to the left (or to the right
+        if that's the only child).  */
+      while (node->left || node->right)
+       if (node->left)
+         node = node->left;
+       else
+         node = node->right;
+
+      do
+       {
+         reg_errcode_t err = fn (extra, node);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+         if (node->parent == NULL)
+           return REG_NOERROR;
+         prev = node;
+         node = node->parent;
+       }
+      /* Go up while we have a node that is reached from the right.  */
+      while (node->right == prev || node->right == NULL);
+      node = node->right;
+    }
+}
+
+static reg_errcode_t
+preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+         void *extra)
+{
+  bin_tree_t *node;
+
+  for (node = root; ; )
+    {
+      reg_errcode_t err = fn (extra, node);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+
+      /* Go to the left node, or up and to the right.  */
+      if (node->left)
+       node = node->left;
+      else
+       {
+         bin_tree_t *prev = NULL;
+         while (node->right == prev || node->right == NULL)
+           {
+             prev = node;
+             node = node->parent;
+             if (!node)
+               return REG_NOERROR;
+           }
+         node = node->right;
+       }
+    }
+}
+
+/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell
+   re_search_internal to map the inner one's opr.idx to this one's.  Adjust
+   backreferences as well.  Requires a preorder visit.  */
+static reg_errcode_t
+optimize_subexps (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+
+  if (node->token.type == OP_BACK_REF && dfa->subexp_map)
+    {
+      int idx = node->token.opr.idx;
+      node->token.opr.idx = dfa->subexp_map[idx];
+      dfa->used_bkref_map |= 1 << node->token.opr.idx;
+    }
+
+  else if (node->token.type == SUBEXP
+          && node->left && node->left->token.type == SUBEXP)
+    {
+      int other_idx = node->left->token.opr.idx;
+
+      node->left = node->left->left;
+      if (node->left)
+       node->left->parent = node;
+
+      dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx];
+      if (other_idx < BITSET_WORD_BITS)
+         dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx);
+    }
+
+  return REG_NOERROR;
+}
+
+/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation
+   of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP.  */
+static reg_errcode_t
+lower_subexps (void *extra, bin_tree_t *node)
+{
+  regex_t *preg = (regex_t *) extra;
+  reg_errcode_t err = REG_NOERROR;
+
+  if (node->left && node->left->token.type == SUBEXP)
+    {
+      node->left = lower_subexp (&err, preg, node->left);
+      if (node->left)
+       node->left->parent = node;
+    }
+  if (node->right && node->right->token.type == SUBEXP)
+    {
+      node->right = lower_subexp (&err, preg, node->right);
+      if (node->right)
+       node->right->parent = node;
+    }
+
+  return err;
+}
+
+static bin_tree_t *
+lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *body = node->left;
+  bin_tree_t *op, *cls, *tree1, *tree;
+
+  if (preg->no_sub
+      /* We do not optimize empty subexpressions, because otherwise we may
+        have bad CONCAT nodes with NULL children.  This is obviously not
+        very common, so we do not lose much.  An example that triggers
+        this case is the sed "script" /\(\)/x.  */
+      && node->left != NULL
+      && (node->token.opr.idx >= BITSET_WORD_BITS
+         || !(dfa->used_bkref_map
+              & ((bitset_word_t) 1 << node->token.opr.idx))))
+    return node->left;
+
+  /* Convert the SUBEXP node to the concatenation of an
+     OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP.  */
+  op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP);
+  cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP);
+  tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls;
+  tree = create_tree (dfa, op, tree1, CONCAT);
+  if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx;
+  op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp;
+  return tree;
+}
+
+/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton
+   nodes.  Requires a postorder visit.  */
+static reg_errcode_t
+calc_first (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+  if (node->token.type == CONCAT)
+    {
+      node->first = node->left->first;
+      node->node_idx = node->left->node_idx;
+    }
+  else
+    {
+      node->first = node;
+      node->node_idx = re_dfa_add_node (dfa, node->token);
+      if (BE (node->node_idx == -1, 0))
+       return REG_ESPACE;
+      if (node->token.type == ANCHOR)
+       dfa->nodes[node->node_idx].constraint = node->token.opr.ctx_type;
+    }
+  return REG_NOERROR;
+}
+
+/* Pass 2: compute NEXT on the tree.  Preorder visit.  */
+static reg_errcode_t
+calc_next (void *extra, bin_tree_t *node)
+{
+  switch (node->token.type)
+    {
+    case OP_DUP_ASTERISK:
+      node->left->next = node;
+      break;
+    case CONCAT:
+      node->left->next = node->right->first;
+      node->right->next = node->next;
+      break;
+    default:
+      if (node->left)
+       node->left->next = node->next;
+      if (node->right)
+       node->right->next = node->next;
+      break;
+    }
+  return REG_NOERROR;
+}
+
+/* Pass 3: link all DFA nodes to their NEXT node (any order will do).  */
+static reg_errcode_t
+link_nfa_nodes (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+  int idx = node->node_idx;
+  reg_errcode_t err = REG_NOERROR;
+
+  switch (node->token.type)
+    {
+    case CONCAT:
+      break;
+
+    case END_OF_RE:
+      assert (node->next == NULL);
+      break;
+
+    case OP_DUP_ASTERISK:
+    case OP_ALT:
+      {
+       int left, right;
+       dfa->has_plural_match = 1;
+       if (node->left != NULL)
+         left = node->left->first->node_idx;
+       else
+         left = node->next->node_idx;
+       if (node->right != NULL)
+         right = node->right->first->node_idx;
+       else
+         right = node->next->node_idx;
+       assert (left > -1);
+       assert (right > -1);
+       err = re_node_set_init_2 (dfa->edests + idx, left, right);
+      }
+      break;
+
+    case ANCHOR:
+    case OP_OPEN_SUBEXP:
+    case OP_CLOSE_SUBEXP:
+      err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx);
+      break;
+
+    case OP_BACK_REF:
+      dfa->nexts[idx] = node->next->node_idx;
+      if (node->token.type == OP_BACK_REF)
+       err = re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]);
+      break;
+
+    default:
+      assert (!IS_EPSILON_NODE (node->token.type));
+      dfa->nexts[idx] = node->next->node_idx;
+      break;
+    }
+
+  return err;
+}
+
+/* Duplicate the epsilon closure of the node ROOT_NODE.
+   Note that duplicated nodes have constraint INIT_CONSTRAINT in addition
+   to their own constraint.  */
+
+static reg_errcode_t
+internal_function
+duplicate_node_closure (re_dfa_t *dfa, int top_org_node, int top_clone_node,
+                       int root_node, unsigned int init_constraint)
+{
+  int org_node, clone_node, ret;
+  unsigned int constraint = init_constraint;
+  for (org_node = top_org_node, clone_node = top_clone_node;;)
+    {
+      int org_dest, clone_dest;
+      if (dfa->nodes[org_node].type == OP_BACK_REF)
+       {
+         /* If the back reference epsilon-transit, its destination must
+            also have the constraint.  Then duplicate the epsilon closure
+            of the destination of the back reference, and store it in
+            edests of the back reference.  */
+         org_dest = dfa->nexts[org_node];
+         re_node_set_empty (dfa->edests + clone_node);
+         clone_dest = duplicate_node (dfa, org_dest, constraint);
+         if (BE (clone_dest == -1, 0))
+           return REG_ESPACE;
+         dfa->nexts[clone_node] = dfa->nexts[org_node];
+         ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+         if (BE (ret < 0, 0))
+           return REG_ESPACE;
+       }
+      else if (dfa->edests[org_node].nelem == 0)
+       {
+         /* In case of the node can't epsilon-transit, don't duplicate the
+            destination and store the original destination as the
+            destination of the node.  */
+         dfa->nexts[clone_node] = dfa->nexts[org_node];
+         break;
+       }
+      else if (dfa->edests[org_node].nelem == 1)
+       {
+         /* In case of the node can epsilon-transit, and it has only one
+            destination.  */
+         org_dest = dfa->edests[org_node].elems[0];
+         re_node_set_empty (dfa->edests + clone_node);
+         /* If the node is root_node itself, it means the epsilon clsoure
+            has a loop.   Then tie it to the destination of the root_node.  */
+         if (org_node == root_node && clone_node != org_node)
+           {
+             ret = re_node_set_insert (dfa->edests + clone_node, org_dest);
+             if (BE (ret < 0, 0))
+               return REG_ESPACE;
+             break;
+           }
+         /* In case of the node has another constraint, add it.  */
+         constraint |= dfa->nodes[org_node].constraint;
+         clone_dest = duplicate_node (dfa, org_dest, constraint);
+         if (BE (clone_dest == -1, 0))
+           return REG_ESPACE;
+         ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+         if (BE (ret < 0, 0))
+           return REG_ESPACE;
+       }
+      else /* dfa->edests[org_node].nelem == 2 */
+       {
+         /* In case of the node can epsilon-transit, and it has two
+            destinations. In the bin_tree_t and DFA, that's '|' and '*'.   */
+         org_dest = dfa->edests[org_node].elems[0];
+         re_node_set_empty (dfa->edests + clone_node);
+         /* Search for a duplicated node which satisfies the constraint.  */
+         clone_dest = search_duplicated_node (dfa, org_dest, constraint);
+         if (clone_dest == -1)
+           {
+             /* There is no such duplicated node, create a new one.  */
+             reg_errcode_t err;
+             clone_dest = duplicate_node (dfa, org_dest, constraint);
+             if (BE (clone_dest == -1, 0))
+               return REG_ESPACE;
+             ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+             if (BE (ret < 0, 0))
+               return REG_ESPACE;
+             err = duplicate_node_closure (dfa, org_dest, clone_dest,
+                                           root_node, constraint);
+             if (BE (err != REG_NOERROR, 0))
+               return err;
+           }
+         else
+           {
+             /* There is a duplicated node which satisfies the constraint,
+                use it to avoid infinite loop.  */
+             ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+             if (BE (ret < 0, 0))
+               return REG_ESPACE;
+           }
+
+         org_dest = dfa->edests[org_node].elems[1];
+         clone_dest = duplicate_node (dfa, org_dest, constraint);
+         if (BE (clone_dest == -1, 0))
+           return REG_ESPACE;
+         ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+         if (BE (ret < 0, 0))
+           return REG_ESPACE;
+       }
+      org_node = org_dest;
+      clone_node = clone_dest;
+    }
+  return REG_NOERROR;
+}
+
+/* Search for a node which is duplicated from the node ORG_NODE, and
+   satisfies the constraint CONSTRAINT.  */
+
+static int
+search_duplicated_node (const re_dfa_t *dfa, int org_node,
+                       unsigned int constraint)
+{
+  int idx;
+  for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
+    {
+      if (org_node == dfa->org_indices[idx]
+         && constraint == dfa->nodes[idx].constraint)
+       return idx; /* Found.  */
+    }
+  return -1; /* Not found.  */
+}
+
+/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
+   Return the index of the new node, or -1 if insufficient storage is
+   available.  */
+
+static int
+duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint)
+{
+  int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]);
+  if (BE (dup_idx != -1, 1))
+    {
+      dfa->nodes[dup_idx].constraint = constraint;
+      dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].constraint;
+      dfa->nodes[dup_idx].duplicated = 1;
+
+      /* Store the index of the original node.  */
+      dfa->org_indices[dup_idx] = org_idx;
+    }
+  return dup_idx;
+}
+
+static reg_errcode_t
+calc_inveclosure (re_dfa_t *dfa)
+{
+  int src, idx, ret;
+  for (idx = 0; idx < dfa->nodes_len; ++idx)
+    re_node_set_init_empty (dfa->inveclosures + idx);
+
+  for (src = 0; src < dfa->nodes_len; ++src)
+    {
+      int *elems = dfa->eclosures[src].elems;
+      for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
+       {
+         ret = re_node_set_insert_last (dfa->inveclosures + elems[idx], src);
+         if (BE (ret == -1, 0))
+           return REG_ESPACE;
+       }
+    }
+
+  return REG_NOERROR;
+}
+
+/* Calculate "eclosure" for all the node in DFA.  */
+
+static reg_errcode_t
+calc_eclosure (re_dfa_t *dfa)
+{
+  int node_idx, incomplete;
+#ifdef DEBUG
+  assert (dfa->nodes_len > 0);
+#endif
+  incomplete = 0;
+  /* For each nodes, calculate epsilon closure.  */
+  for (node_idx = 0; ; ++node_idx)
+    {
+      reg_errcode_t err;
+      re_node_set eclosure_elem;
+      if (node_idx == dfa->nodes_len)
+       {
+         if (!incomplete)
+           break;
+         incomplete = 0;
+         node_idx = 0;
+       }
+
+#ifdef DEBUG
+      assert (dfa->eclosures[node_idx].nelem != -1);
+#endif
+
+      /* If we have already calculated, skip it.  */
+      if (dfa->eclosures[node_idx].nelem != 0)
+       continue;
+      /* Calculate epsilon closure of `node_idx'.  */
+      err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+
+      if (dfa->eclosures[node_idx].nelem == 0)
+       {
+         incomplete = 1;
+         re_node_set_free (&eclosure_elem);
+       }
+    }
+  return REG_NOERROR;
+}
+
+/* Calculate epsilon closure of NODE.  */
+
+static reg_errcode_t
+calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, int node, int root)
+{
+  reg_errcode_t err;
+  int i;
+  re_node_set eclosure;
+  int ret;
+  int incomplete = 0;
+  err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  /* This indicates that we are calculating this node now.
+     We reference this value to avoid infinite loop.  */
+  dfa->eclosures[node].nelem = -1;
+
+  /* If the current node has constraints, duplicate all nodes
+     since they must inherit the constraints.  */
+  if (dfa->nodes[node].constraint
+      && dfa->edests[node].nelem
+      && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
+    {
+      err = duplicate_node_closure (dfa, node, node, node,
+                                   dfa->nodes[node].constraint);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+
+  /* Expand each epsilon destination nodes.  */
+  if (IS_EPSILON_NODE(dfa->nodes[node].type))
+    for (i = 0; i < dfa->edests[node].nelem; ++i)
+      {
+       re_node_set eclosure_elem;
+       int edest = dfa->edests[node].elems[i];
+       /* If calculating the epsilon closure of `edest' is in progress,
+          return intermediate result.  */
+       if (dfa->eclosures[edest].nelem == -1)
+         {
+           incomplete = 1;
+           continue;
+         }
+       /* If we haven't calculated the epsilon closure of `edest' yet,
+          calculate now. Otherwise use calculated epsilon closure.  */
+       if (dfa->eclosures[edest].nelem == 0)
+         {
+           err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0);
+           if (BE (err != REG_NOERROR, 0))
+             return err;
+         }
+       else
+         eclosure_elem = dfa->eclosures[edest];
+       /* Merge the epsilon closure of `edest'.  */
+       err = re_node_set_merge (&eclosure, &eclosure_elem);
+       if (BE (err != REG_NOERROR, 0))
+         return err;
+       /* If the epsilon closure of `edest' is incomplete,
+          the epsilon closure of this node is also incomplete.  */
+       if (dfa->eclosures[edest].nelem == 0)
+         {
+           incomplete = 1;
+           re_node_set_free (&eclosure_elem);
+         }
+      }
+
+  /* An epsilon closure includes itself.  */
+  ret = re_node_set_insert (&eclosure, node);
+  if (BE (ret < 0, 0))
+    return REG_ESPACE;
+  if (incomplete && !root)
+    dfa->eclosures[node].nelem = 0;
+  else
+    dfa->eclosures[node] = eclosure;
+  *new_set = eclosure;
+  return REG_NOERROR;
+}
+\f
+/* Functions for token which are used in the parser.  */
+
+/* Fetch a token from INPUT.
+   We must not use this function inside bracket expressions.  */
+
+static void
+internal_function
+fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax)
+{
+  re_string_skip_bytes (input, peek_token (result, input, syntax));
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+   We must not use this function inside bracket expressions.  */
+
+static int
+internal_function
+peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
+{
+  unsigned char c;
+
+  if (re_string_eoi (input))
+    {
+      token->type = END_OF_RE;
+      return 0;
+    }
+
+  c = re_string_peek_byte (input, 0);
+  token->opr.c = c;
+
+  token->word_char = 0;
+#ifdef RE_ENABLE_I18N
+  token->mb_partial = 0;
+  if (input->mb_cur_max > 1 &&
+      !re_string_first_byte (input, re_string_cur_idx (input)))
+    {
+      token->type = CHARACTER;
+      token->mb_partial = 1;
+      return 1;
+    }
+#endif
+  if (c == '\\')
+    {
+      unsigned char c2;
+      if (re_string_cur_idx (input) + 1 >= re_string_length (input))
+       {
+         token->type = BACK_SLASH;
+         return 1;
+       }
+
+      c2 = re_string_peek_byte_case (input, 1);
+      token->opr.c = c2;
+      token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+      if (input->mb_cur_max > 1)
+       {
+         wint_t wc = re_string_wchar_at (input,
+                                         re_string_cur_idx (input) + 1);
+         token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+       }
+      else
+#endif
+       token->word_char = IS_WORD_CHAR (c2) != 0;
+
+      switch (c2)
+       {
+       case '|':
+         if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
+           token->type = OP_ALT;
+         break;
+       case '1': case '2': case '3': case '4': case '5':
+       case '6': case '7': case '8': case '9':
+         if (!(syntax & RE_NO_BK_REFS))
+           {
+             token->type = OP_BACK_REF;
+             token->opr.idx = c2 - '1';
+           }
+         break;
+       case '<':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.ctx_type = WORD_FIRST;
+           }
+         break;
+       case '>':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.ctx_type = WORD_LAST;
+           }
+         break;
+       case 'b':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.ctx_type = WORD_DELIM;
+           }
+         break;
+       case 'B':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.ctx_type = NOT_WORD_DELIM;
+           }
+         break;
+       case 'w':
+         if (!(syntax & RE_NO_GNU_OPS))
+           token->type = OP_WORD;
+         break;
+       case 'W':
+         if (!(syntax & RE_NO_GNU_OPS))
+           token->type = OP_NOTWORD;
+         break;
+       case 's':
+         if (!(syntax & RE_NO_GNU_OPS))
+           token->type = OP_SPACE;
+         break;
+       case 'S':
+         if (!(syntax & RE_NO_GNU_OPS))
+           token->type = OP_NOTSPACE;
+         break;
+       case '`':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.ctx_type = BUF_FIRST;
+           }
+         break;
+       case '\'':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.ctx_type = BUF_LAST;
+           }
+         break;
+       case '(':
+         if (!(syntax & RE_NO_BK_PARENS))
+           token->type = OP_OPEN_SUBEXP;
+         break;
+       case ')':
+         if (!(syntax & RE_NO_BK_PARENS))
+           token->type = OP_CLOSE_SUBEXP;
+         break;
+       case '+':
+         if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+           token->type = OP_DUP_PLUS;
+         break;
+       case '?':
+         if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+           token->type = OP_DUP_QUESTION;
+         break;
+       case '{':
+         if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+           token->type = OP_OPEN_DUP_NUM;
+         break;
+       case '}':
+         if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+           token->type = OP_CLOSE_DUP_NUM;
+         break;
+       default:
+         break;
+       }
+      return 2;
+    }
+
+  token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1)
+    {
+      wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input));
+      token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+    }
+  else
+#endif
+    token->word_char = IS_WORD_CHAR (token->opr.c);
+
+  switch (c)
+    {
+    case '\n':
+      if (syntax & RE_NEWLINE_ALT)
+       token->type = OP_ALT;
+      break;
+    case '|':
+      if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
+       token->type = OP_ALT;
+      break;
+    case '*':
+      token->type = OP_DUP_ASTERISK;
+      break;
+    case '+':
+      if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+       token->type = OP_DUP_PLUS;
+      break;
+    case '?':
+      if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+       token->type = OP_DUP_QUESTION;
+      break;
+    case '{':
+      if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+       token->type = OP_OPEN_DUP_NUM;
+      break;
+    case '}':
+      if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+       token->type = OP_CLOSE_DUP_NUM;
+      break;
+    case '(':
+      if (syntax & RE_NO_BK_PARENS)
+       token->type = OP_OPEN_SUBEXP;
+      break;
+    case ')':
+      if (syntax & RE_NO_BK_PARENS)
+       token->type = OP_CLOSE_SUBEXP;
+      break;
+    case '[':
+      token->type = OP_OPEN_BRACKET;
+      break;
+    case '.':
+      token->type = OP_PERIOD;
+      break;
+    case '^':
+      if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) &&
+         re_string_cur_idx (input) != 0)
+       {
+         char prev = re_string_peek_byte (input, -1);
+         if (!(syntax & RE_NEWLINE_ALT) || prev != '\n')
+           break;
+       }
+      token->type = ANCHOR;
+      token->opr.ctx_type = LINE_FIRST;
+      break;
+    case '$':
+      if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+         re_string_cur_idx (input) + 1 != re_string_length (input))
+       {
+         re_token_t next;
+         re_string_skip_bytes (input, 1);
+         peek_token (&next, input, syntax);
+         re_string_skip_bytes (input, -1);
+         if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
+           break;
+       }
+      token->type = ANCHOR;
+      token->opr.ctx_type = LINE_LAST;
+      break;
+    default:
+      break;
+    }
+  return 1;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+   We must not use this function out of bracket expressions.  */
+
+static int
+internal_function
+peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
+{
+  unsigned char c;
+  if (re_string_eoi (input))
+    {
+      token->type = END_OF_RE;
+      return 0;
+    }
+  c = re_string_peek_byte (input, 0);
+  token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1 &&
+      !re_string_first_byte (input, re_string_cur_idx (input)))
+    {
+      token->type = CHARACTER;
+      return 1;
+    }
+#endif /* RE_ENABLE_I18N */
+
+  if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS)
+      && re_string_cur_idx (input) + 1 < re_string_length (input))
+    {
+      /* In this case, '\' escape a character.  */
+      unsigned char c2;
+      re_string_skip_bytes (input, 1);
+      c2 = re_string_peek_byte (input, 0);
+      token->opr.c = c2;
+      token->type = CHARACTER;
+      return 1;
+    }
+  if (c == '[') /* '[' is a special char in a bracket exps.  */
+    {
+      unsigned char c2;
+      int token_len;
+      if (re_string_cur_idx (input) + 1 < re_string_length (input))
+       c2 = re_string_peek_byte (input, 1);
+      else
+       c2 = 0;
+      token->opr.c = c2;
+      token_len = 2;
+      switch (c2)
+       {
+       case '.':
+         token->type = OP_OPEN_COLL_ELEM;
+         break;
+       case '=':
+         token->type = OP_OPEN_EQUIV_CLASS;
+         break;
+       case ':':
+         if (syntax & RE_CHAR_CLASSES)
+           {
+             token->type = OP_OPEN_CHAR_CLASS;
+             break;
+           }
+         /* else fall through.  */
+       default:
+         token->type = CHARACTER;
+         token->opr.c = c;
+         token_len = 1;
+         break;
+       }
+      return token_len;
+    }
+  switch (c)
+    {
+    case '-':
+      token->type = OP_CHARSET_RANGE;
+      break;
+    case ']':
+      token->type = OP_CLOSE_BRACKET;
+      break;
+    case '^':
+      token->type = OP_NON_MATCH_LIST;
+      break;
+    default:
+      token->type = CHARACTER;
+    }
+  return 1;
+}
+\f
+/* Functions for parser.  */
+
+/* Entry point of the parser.
+   Parse the regular expression REGEXP and return the structure tree.
+   If an error is occured, ERR is set by error code, and return NULL.
+   This function build the following tree, from regular expression <reg_exp>:
+          CAT
+          / \
+         /   \
+   <reg_exp>  EOR
+
+   CAT means concatenation.
+   EOR means end of regular expression.  */
+
+static bin_tree_t *
+parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax,
+       reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree, *eor, *root;
+  re_token_t current_token;
+  dfa->syntax = syntax;
+  fetch_token (&current_token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+  tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+  eor = create_tree (dfa, NULL, NULL, END_OF_RE);
+  if (tree != NULL)
+    root = create_tree (dfa, tree, eor, CONCAT);
+  else
+    root = eor;
+  if (BE (eor == NULL || root == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+  return root;
+}
+
+/* This function build the following tree, from regular expression
+   <branch1>|<branch2>:
+          ALT
+          / \
+         /   \
+   <branch1> <branch2>
+
+   ALT means alternative, which represents the operator `|'.  */
+
+static bin_tree_t *
+parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+              reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree, *branch = NULL;
+  tree = parse_branch (regexp, preg, token, syntax, nest, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+
+  while (token->type == OP_ALT)
+    {
+      fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+      if (token->type != OP_ALT && token->type != END_OF_RE
+         && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+       {
+         branch = parse_branch (regexp, preg, token, syntax, nest, err);
+         if (BE (*err != REG_NOERROR && branch == NULL, 0))
+           return NULL;
+       }
+      else
+       branch = NULL;
+      tree = create_tree (dfa, tree, branch, OP_ALT);
+      if (BE (tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+    }
+  return tree;
+}
+
+/* This function build the following tree, from regular expression
+   <exp1><exp2>:
+       CAT
+       / \
+       /   \
+   <exp1> <exp2>
+
+   CAT means concatenation.  */
+
+static bin_tree_t *
+parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token,
+             reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  bin_tree_t *tree, *exp;
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  tree = parse_expression (regexp, preg, token, syntax, nest, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+
+  while (token->type != OP_ALT && token->type != END_OF_RE
+        && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+    {
+      exp = parse_expression (regexp, preg, token, syntax, nest, err);
+      if (BE (*err != REG_NOERROR && exp == NULL, 0))
+       {
+         return NULL;
+       }
+      if (tree != NULL && exp != NULL)
+       {
+         tree = create_tree (dfa, tree, exp, CONCAT);
+         if (tree == NULL)
+           {
+             *err = REG_ESPACE;
+             return NULL;
+           }
+       }
+      else if (tree == NULL)
+       tree = exp;
+      /* Otherwise exp == NULL, we don't need to create new tree.  */
+    }
+  return tree;
+}
+
+/* This function build the following tree, from regular expression a*:
+        *
+        |
+        a
+*/
+
+static bin_tree_t *
+parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token,
+                 reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree;
+  switch (token->type)
+    {
+    case CHARACTER:
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+#ifdef RE_ENABLE_I18N
+      if (dfa->mb_cur_max > 1)
+       {
+         while (!re_string_eoi (regexp)
+                && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
+           {
+             bin_tree_t *mbc_remain;
+             fetch_token (token, regexp, syntax);
+             mbc_remain = create_token_tree (dfa, NULL, NULL, token);
+             tree = create_tree (dfa, tree, mbc_remain, CONCAT);
+             if (BE (mbc_remain == NULL || tree == NULL, 0))
+               {
+                 *err = REG_ESPACE;
+                 return NULL;
+               }
+           }
+       }
+#endif
+      break;
+    case OP_OPEN_SUBEXP:
+      tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+      break;
+    case OP_OPEN_BRACKET:
+      tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+      break;
+    case OP_BACK_REF:
+      if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1))
+       {
+         *err = REG_ESUBREG;
+         return NULL;
+       }
+      dfa->used_bkref_map |= 1 << token->opr.idx;
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+      ++dfa->nbackref;
+      dfa->has_mb_node = 1;
+      break;
+    case OP_OPEN_DUP_NUM:
+      if (syntax & RE_CONTEXT_INVALID_DUP)
+       {
+         *err = REG_BADRPT;
+         return NULL;
+       }
+      /* FALLTHROUGH */
+    case OP_DUP_ASTERISK:
+    case OP_DUP_PLUS:
+    case OP_DUP_QUESTION:
+      if (syntax & RE_CONTEXT_INVALID_OPS)
+       {
+         *err = REG_BADRPT;
+         return NULL;
+       }
+      else if (syntax & RE_CONTEXT_INDEP_OPS)
+       {
+         fetch_token (token, regexp, syntax);
+         return parse_expression (regexp, preg, token, syntax, nest, err);
+       }
+      /* else fall through  */
+    case OP_CLOSE_SUBEXP:
+      if ((token->type == OP_CLOSE_SUBEXP) &&
+         !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
+       {
+         *err = REG_ERPAREN;
+         return NULL;
+       }
+      /* else fall through  */
+    case OP_CLOSE_DUP_NUM:
+      /* We treat it as a normal character.  */
+
+      /* Then we can these characters as normal characters.  */
+      token->type = CHARACTER;
+      /* mb_partial and word_char bits should be initialized already
+        by peek_token.  */
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+      break;
+    case ANCHOR:
+      if ((token->opr.ctx_type
+          & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST))
+         && dfa->word_ops_used == 0)
+       init_word_char (dfa);
+      if (token->opr.ctx_type == WORD_DELIM
+         || token->opr.ctx_type == NOT_WORD_DELIM)
+       {
+         bin_tree_t *tree_first, *tree_last;
+         if (token->opr.ctx_type == WORD_DELIM)
+           {
+             token->opr.ctx_type = WORD_FIRST;
+             tree_first = create_token_tree (dfa, NULL, NULL, token);
+             token->opr.ctx_type = WORD_LAST;
+           }
+         else
+           {
+             token->opr.ctx_type = INSIDE_WORD;
+             tree_first = create_token_tree (dfa, NULL, NULL, token);
+             token->opr.ctx_type = INSIDE_NOTWORD;
+           }
+         tree_last = create_token_tree (dfa, NULL, NULL, token);
+         tree = create_tree (dfa, tree_first, tree_last, OP_ALT);
+         if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0))
+           {
+             *err = REG_ESPACE;
+             return NULL;
+           }
+       }
+      else
+       {
+         tree = create_token_tree (dfa, NULL, NULL, token);
+         if (BE (tree == NULL, 0))
+           {
+             *err = REG_ESPACE;
+             return NULL;
+           }
+       }
+      /* We must return here, since ANCHORs can't be followed
+        by repetition operators.
+        eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
+            it must not be "<ANCHOR(^)><REPEAT(*)>".  */
+      fetch_token (token, regexp, syntax);
+      return tree;
+    case OP_PERIOD:
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+      if (dfa->mb_cur_max > 1)
+       dfa->has_mb_node = 1;
+      break;
+    case OP_WORD:
+    case OP_NOTWORD:
+      tree = build_charclass_op (dfa, regexp->trans,
+                                "alnum",
+                                "_",
+                                token->type == OP_NOTWORD, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+      break;
+    case OP_SPACE:
+    case OP_NOTSPACE:
+      tree = build_charclass_op (dfa, regexp->trans,
+                                "space",
+                                "",
+                                token->type == OP_NOTSPACE, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+      break;
+    case OP_ALT:
+    case END_OF_RE:
+      return NULL;
+    case BACK_SLASH:
+      *err = REG_EESCAPE;
+      return NULL;
+    default:
+      /* Must not happen?  */
+#ifdef DEBUG
+      assert (0);
+#endif
+      return NULL;
+    }
+  fetch_token (token, regexp, syntax);
+
+  while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
+        || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
+    {
+      tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+      /* In BRE consecutive duplications are not allowed.  */
+      if ((syntax & RE_CONTEXT_INVALID_DUP)
+         && (token->type == OP_DUP_ASTERISK
+             || token->type == OP_OPEN_DUP_NUM))
+       {
+         *err = REG_BADRPT;
+         return NULL;
+       }
+    }
+
+  return tree;
+}
+
+/* This function build the following tree, from regular expression
+   (<reg_exp>):
+        SUBEXP
+           |
+       <reg_exp>
+*/
+
+static bin_tree_t *
+parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+              reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree;
+  size_t cur_nsub;
+  cur_nsub = preg->re_nsub++;
+
+  fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+
+  /* The subexpression may be a null string.  */
+  if (token->type == OP_CLOSE_SUBEXP)
+    tree = NULL;
+  else
+    {
+      tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
+      if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0))
+       *err = REG_EPAREN;
+      if (BE (*err != REG_NOERROR, 0))
+       return NULL;
+    }
+
+  if (cur_nsub <= '9' - '1')
+    dfa->completed_bkref_map |= 1 << cur_nsub;
+
+  tree = create_tree (dfa, tree, NULL, SUBEXP);
+  if (BE (tree == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+  tree->token.opr.idx = cur_nsub;
+  return tree;
+}
+
+/* This function parse repetition operators like "*", "+", "{1,3}" etc.  */
+
+static bin_tree_t *
+parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa,
+             re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err)
+{
+  bin_tree_t *tree = NULL, *old_tree = NULL;
+  int i, start, end, start_idx = re_string_cur_idx (regexp);
+#ifndef RE_TOKEN_INIT_BUG
+  re_token_t start_token = *token;
+#else
+  re_token_t start_token;
+
+  memcpy ((void *) &start_token, (void *) token, sizeof start_token);
+#endif
+
+  if (token->type == OP_OPEN_DUP_NUM)
+    {
+      end = 0;
+      start = fetch_number (regexp, token, syntax);
+      if (start == -1)
+       {
+         if (token->type == CHARACTER && token->opr.c == ',')
+           start = 0; /* We treat "{,m}" as "{0,m}".  */
+         else
+           {
+             *err = REG_BADBR; /* <re>{} is invalid.  */
+             return NULL;
+           }
+       }
+      if (BE (start != -2, 1))
+       {
+         /* We treat "{n}" as "{n,n}".  */
+         end = ((token->type == OP_CLOSE_DUP_NUM) ? start
+                : ((token->type == CHARACTER && token->opr.c == ',')
+                   ? fetch_number (regexp, token, syntax) : -2));
+       }
+      if (BE (start == -2 || end == -2, 0))
+       {
+         /* Invalid sequence.  */
+         if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+           {
+             if (token->type == END_OF_RE)
+               *err = REG_EBRACE;
+             else
+               *err = REG_BADBR;
+
+             return NULL;
+           }
+
+         /* If the syntax bit is set, rollback.  */
+         re_string_set_index (regexp, start_idx);
+         *token = start_token;
+         token->type = CHARACTER;
+         /* mb_partial and word_char bits should be already initialized by
+            peek_token.  */
+         return elem;
+       }
+
+      if (BE ((end != -1 && start > end) || token->type != OP_CLOSE_DUP_NUM, 0))
+       {
+         /* First number greater than second.  */
+         *err = REG_BADBR;
+         return NULL;
+       }
+    }
+  else
+    {
+      start = (token->type == OP_DUP_PLUS) ? 1 : 0;
+      end = (token->type == OP_DUP_QUESTION) ? 1 : -1;
+    }
+
+  fetch_token (token, regexp, syntax);
+
+  if (BE (elem == NULL, 0))
+    return NULL;
+  if (BE (start == 0 && end == 0, 0))
+    {
+      postorder (elem, free_tree, NULL);
+      return NULL;
+    }
+
+  /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}".  */
+  if (BE (start > 0, 0))
+    {
+      tree = elem;
+      for (i = 2; i <= start; ++i)
+       {
+         elem = duplicate_tree (elem, dfa);
+         tree = create_tree (dfa, tree, elem, CONCAT);
+         if (BE (elem == NULL || tree == NULL, 0))
+           goto parse_dup_op_espace;
+       }
+
+      if (start == end)
+       return tree;
+
+      /* Duplicate ELEM before it is marked optional.  */
+      elem = duplicate_tree (elem, dfa);
+      old_tree = tree;
+    }
+  else
+    old_tree = NULL;
+
+  if (elem->token.type == SUBEXP)
+    postorder (elem, mark_opt_subexp, (void *) (long) elem->token.opr.idx);
+
+  tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT));
+  if (BE (tree == NULL, 0))
+    goto parse_dup_op_espace;
+
+  /* This loop is actually executed only when end != -1,
+     to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?...  We have
+     already created the start+1-th copy.  */
+  for (i = start + 2; i <= end; ++i)
+    {
+      elem = duplicate_tree (elem, dfa);
+      tree = create_tree (dfa, tree, elem, CONCAT);
+      if (BE (elem == NULL || tree == NULL, 0))
+       goto parse_dup_op_espace;
+
+      tree = create_tree (dfa, tree, NULL, OP_ALT);
+      if (BE (tree == NULL, 0))
+       goto parse_dup_op_espace;
+    }
+
+  if (old_tree)
+    tree = create_tree (dfa, old_tree, tree, CONCAT);
+
+  return tree;
+
+ parse_dup_op_espace:
+  *err = REG_ESPACE;
+  return NULL;
+}
+
+/* Size of the names for collating symbol/equivalence_class/character_class.
+   I'm not sure, but maybe enough.  */
+#define BRACKET_NAME_BUF_SIZE 32
+
+#ifndef _LIBC
+  /* Local function for parse_bracket_exp only used in case of NOT _LIBC.
+     Build the range expression which starts from START_ELEM, and ends
+     at END_ELEM.  The result are written to MBCSET and SBCSET.
+     RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+     mbcset->range_ends, is a pointer argument sinse we may
+     update it.  */
+
+static reg_errcode_t
+internal_function
+# ifdef RE_ENABLE_I18N
+build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc,
+                bracket_elem_t *start_elem, bracket_elem_t *end_elem)
+# else /* not RE_ENABLE_I18N */
+build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem,
+                bracket_elem_t *end_elem)
+# endif /* not RE_ENABLE_I18N */
+{
+  unsigned int start_ch, end_ch;
+  /* Equivalence Classes and Character Classes can't be a range start/end.  */
+  if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+         || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+         0))
+    return REG_ERANGE;
+
+  /* We can handle no multi character collating elements without libc
+     support.  */
+  if (BE ((start_elem->type == COLL_SYM
+          && strlen ((char *) start_elem->opr.name) > 1)
+         || (end_elem->type == COLL_SYM
+             && strlen ((char *) end_elem->opr.name) > 1), 0))
+    return REG_ECOLLATE;
+
+# ifdef RE_ENABLE_I18N
+  {
+    wchar_t wc;
+    wint_t start_wc;
+    wint_t end_wc;
+    wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+
+    start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
+               : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+                  : 0));
+    end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch
+             : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+                : 0));
+#ifdef GAWK
+    /*
+     * Fedora Core 2, maybe others, have broken `btowc' that returns -1
+     * for any value > 127. Sigh. Note that `start_ch' and `end_ch' are
+     * unsigned, so we don't have sign extension problems.
+     */
+    start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+               ? start_ch : start_elem->opr.wch);
+    end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+             ? end_ch : end_elem->opr.wch);
+#else
+    start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+               ? __btowc (start_ch) : start_elem->opr.wch);
+    end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+             ? __btowc (end_ch) : end_elem->opr.wch);
+#endif
+    if (start_wc == WEOF || end_wc == WEOF)
+      return REG_ECOLLATE;
+    cmp_buf[0] = start_wc;
+    cmp_buf[4] = end_wc;
+    if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
+      return REG_ERANGE;
+
+    /* Got valid collation sequence values, add them as a new entry.
+       However, for !_LIBC we have no collation elements: if the
+       character set is single byte, the single byte character set
+       that we build below suffices.  parse_bracket_exp passes
+       no MBCSET if dfa->mb_cur_max == 1.  */
+    if (mbcset)
+      {
+       /* Check the space of the arrays.  */
+       if (BE (*range_alloc == mbcset->nranges, 0))
+         {
+           /* There is not enough space, need realloc.  */
+           wchar_t *new_array_start, *new_array_end;
+           int new_nranges;
+
+           /* +1 in case of mbcset->nranges is 0.  */
+           new_nranges = 2 * mbcset->nranges + 1;
+           /* Use realloc since mbcset->range_starts and mbcset->range_ends
+              are NULL if *range_alloc == 0.  */
+           new_array_start = re_realloc (mbcset->range_starts, wchar_t,
+                                         new_nranges);
+           new_array_end = re_realloc (mbcset->range_ends, wchar_t,
+                                       new_nranges);
+
+           if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+             return REG_ESPACE;
+
+           mbcset->range_starts = new_array_start;
+           mbcset->range_ends = new_array_end;
+           *range_alloc = new_nranges;
+         }
+
+       mbcset->range_starts[mbcset->nranges] = start_wc;
+       mbcset->range_ends[mbcset->nranges++] = end_wc;
+      }
+
+    /* Build the table for single byte characters.  */
+    for (wc = 0; wc < SBC_MAX; ++wc)
+      {
+       cmp_buf[2] = wc;
+       if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+           && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+         bitset_set (sbcset, wc);
+      }
+  }
+# else /* not RE_ENABLE_I18N */
+  {
+    unsigned int ch;
+    start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch
+               : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+                  : 0));
+    end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch
+             : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+                : 0));
+    if (start_ch > end_ch)
+      return REG_ERANGE;
+    /* Build the table for single byte characters.  */
+    for (ch = 0; ch < SBC_MAX; ++ch)
+      if (start_ch <= ch  && ch <= end_ch)
+       bitset_set (sbcset, ch);
+  }
+# endif /* not RE_ENABLE_I18N */
+  return REG_NOERROR;
+}
+#endif /* not _LIBC */
+
+#ifndef _LIBC
+/* Helper function for parse_bracket_exp only used in case of NOT _LIBC..
+   Build the collating element which is represented by NAME.
+   The result are written to MBCSET and SBCSET.
+   COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+   pointer argument since we may update it.  */
+
+static reg_errcode_t
+internal_function
+# ifdef RE_ENABLE_I18N
+build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset,
+                       int *coll_sym_alloc, const unsigned char *name)
+# else /* not RE_ENABLE_I18N */
+build_collating_symbol (bitset_t sbcset, const unsigned char *name)
+# endif /* not RE_ENABLE_I18N */
+{
+  size_t name_len = strlen ((const char *) name);
+  if (BE (name_len != 1, 0))
+    return REG_ECOLLATE;
+  else
+    {
+      bitset_set (sbcset, name[0]);
+      return REG_NOERROR;
+    }
+}
+#endif /* not _LIBC */
+
+/* This function parse bracket expression like "[abc]", "[a-c]",
+   "[[.a-a.]]" etc.  */
+
+static bin_tree_t *
+parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
+                  reg_syntax_t syntax, reg_errcode_t *err)
+{
+#ifdef _LIBC
+  const unsigned char *collseqmb;
+  const char *collseqwc;
+  uint32_t nrules;
+  int32_t table_size;
+  const int32_t *symb_table;
+  const unsigned char *extra;
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Seek the collating symbol entry correspondings to NAME.
+     Return the index of the symbol in the SYMB_TABLE.  */
+
+  auto inline int32_t
+  __attribute ((always_inline))
+  seek_collating_symbol_entry (name, name_len)
+        const unsigned char *name;
+        size_t name_len;
+    {
+      int32_t hash = elem_hash ((const char *) name, name_len);
+      int32_t elem = hash % table_size;
+      if (symb_table[2 * elem] != 0)
+       {
+         int32_t second = hash % (table_size - 2) + 1;
+
+         do
+           {
+             /* First compare the hashing value.  */
+             if (symb_table[2 * elem] == hash
+                 /* Compare the length of the name.  */
+                 && name_len == extra[symb_table[2 * elem + 1]]
+                 /* Compare the name.  */
+                 && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
+                            name_len) == 0)
+               {
+                 /* Yep, this is the entry.  */
+                 break;
+               }
+
+             /* Next entry.  */
+             elem += second;
+           }
+         while (symb_table[2 * elem] != 0);
+       }
+      return elem;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environment.
+     Look up the collation sequence value of BR_ELEM.
+     Return the value if succeeded, UINT_MAX otherwise.  */
+
+  auto inline unsigned int
+  __attribute ((always_inline))
+  lookup_collation_sequence_value (br_elem)
+        bracket_elem_t *br_elem;
+    {
+      if (br_elem->type == SB_CHAR)
+       {
+         /*
+         if (MB_CUR_MAX == 1)
+         */
+         if (nrules == 0)
+           return collseqmb[br_elem->opr.ch];
+         else
+           {
+             wint_t wc = __btowc (br_elem->opr.ch);
+             return __collseq_table_lookup (collseqwc, wc);
+           }
+       }
+      else if (br_elem->type == MB_CHAR)
+       {
+         if (nrules != 0)
+           return __collseq_table_lookup (collseqwc, br_elem->opr.wch);
+       }
+      else if (br_elem->type == COLL_SYM)
+       {
+         size_t sym_name_len = strlen ((char *) br_elem->opr.name);
+         if (nrules != 0)
+           {
+             int32_t elem, idx;
+             elem = seek_collating_symbol_entry (br_elem->opr.name,
+                                                 sym_name_len);
+             if (symb_table[2 * elem] != 0)
+               {
+                 /* We found the entry.  */
+                 idx = symb_table[2 * elem + 1];
+                 /* Skip the name of collating element name.  */
+                 idx += 1 + extra[idx];
+                 /* Skip the byte sequence of the collating element.  */
+                 idx += 1 + extra[idx];
+                 /* Adjust for the alignment.  */
+                 idx = (idx + 3) & ~3;
+                 /* Skip the multibyte collation sequence value.  */
+                 idx += sizeof (unsigned int);
+                 /* Skip the wide char sequence of the collating element.  */
+                 idx += sizeof (unsigned int) *
+                   (1 + *(unsigned int *) (extra + idx));
+                 /* Return the collation sequence value.  */
+                 return *(unsigned int *) (extra + idx);
+               }
+             else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
+               {
+                 /* No valid character.  Match it as a single byte
+                    character.  */
+                 return collseqmb[br_elem->opr.name[0]];
+               }
+           }
+         else if (sym_name_len == 1)
+           return collseqmb[br_elem->opr.name[0]];
+       }
+      return UINT_MAX;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Build the range expression which starts from START_ELEM, and ends
+     at END_ELEM.  The result are written to MBCSET and SBCSET.
+     RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+     mbcset->range_ends, is a pointer argument sinse we may
+     update it.  */
+
+  auto inline reg_errcode_t
+  __attribute ((always_inline))
+  build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+        re_charset_t *mbcset;
+        int *range_alloc;
+        bitset_t sbcset;
+        bracket_elem_t *start_elem, *end_elem;
+    {
+      unsigned int ch;
+      uint32_t start_collseq;
+      uint32_t end_collseq;
+
+      /* Equivalence Classes and Character Classes can't be a range
+        start/end.  */
+      if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+             || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+             0))
+       return REG_ERANGE;
+
+      start_collseq = lookup_collation_sequence_value (start_elem);
+      end_collseq = lookup_collation_sequence_value (end_elem);
+      /* Check start/end collation sequence values.  */
+      if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0))
+       return REG_ECOLLATE;
+      if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
+       return REG_ERANGE;
+
+      /* Got valid collation sequence values, add them as a new entry.
+        However, if we have no collation elements, and the character set
+        is single byte, the single byte character set that we
+        build below suffices. */
+      if (nrules > 0 || dfa->mb_cur_max > 1)
+       {
+         /* Check the space of the arrays.  */
+         if (BE (*range_alloc == mbcset->nranges, 0))
+           {
+             /* There is not enough space, need realloc.  */
+             uint32_t *new_array_start;
+             uint32_t *new_array_end;
+             int new_nranges;
+
+             /* +1 in case of mbcset->nranges is 0.  */
+             new_nranges = 2 * mbcset->nranges + 1;
+             new_array_start = re_realloc (mbcset->range_starts, uint32_t,
+                                           new_nranges);
+             new_array_end = re_realloc (mbcset->range_ends, uint32_t,
+                                         new_nranges);
+
+             if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+               return REG_ESPACE;
+
+             mbcset->range_starts = new_array_start;
+             mbcset->range_ends = new_array_end;
+             *range_alloc = new_nranges;
+           }
+
+         mbcset->range_starts[mbcset->nranges] = start_collseq;
+         mbcset->range_ends[mbcset->nranges++] = end_collseq;
+       }
+
+      /* Build the table for single byte characters.  */
+      for (ch = 0; ch < SBC_MAX; ch++)
+       {
+         uint32_t ch_collseq;
+         /*
+         if (MB_CUR_MAX == 1)
+         */
+         if (nrules == 0)
+           ch_collseq = collseqmb[ch];
+         else
+           ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch));
+         if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
+           bitset_set (sbcset, ch);
+       }
+      return REG_NOERROR;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Build the collating element which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+     pointer argument sinse we may update it.  */
+
+  auto inline reg_errcode_t
+  __attribute ((always_inline))
+  build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
+        re_charset_t *mbcset;
+        int *coll_sym_alloc;
+        bitset_t sbcset;
+        const unsigned char *name;
+    {
+      int32_t elem, idx;
+      size_t name_len = strlen ((const char *) name);
+      if (nrules != 0)
+       {
+         elem = seek_collating_symbol_entry (name, name_len);
+         if (symb_table[2 * elem] != 0)
+           {
+             /* We found the entry.  */
+             idx = symb_table[2 * elem + 1];
+             /* Skip the name of collating element name.  */
+             idx += 1 + extra[idx];
+           }
+         else if (symb_table[2 * elem] == 0 && name_len == 1)
+           {
+             /* No valid character, treat it as a normal
+                character.  */
+             bitset_set (sbcset, name[0]);
+             return REG_NOERROR;
+           }
+         else
+           return REG_ECOLLATE;
+
+         /* Got valid collation sequence, add it as a new entry.  */
+         /* Check the space of the arrays.  */
+         if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0))
+           {
+             /* Not enough, realloc it.  */
+             /* +1 in case of mbcset->ncoll_syms is 0.  */
+             int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
+             /* Use realloc since mbcset->coll_syms is NULL
+                if *alloc == 0.  */
+             int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t,
+                                                  new_coll_sym_alloc);
+             if (BE (new_coll_syms == NULL, 0))
+               return REG_ESPACE;
+             mbcset->coll_syms = new_coll_syms;
+             *coll_sym_alloc = new_coll_sym_alloc;
+           }
+         mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
+         return REG_NOERROR;
+       }
+      else
+       {
+         if (BE (name_len != 1, 0))
+           return REG_ECOLLATE;
+         else
+           {
+             bitset_set (sbcset, name[0]);
+             return REG_NOERROR;
+           }
+       }
+    }
+#endif
+
+  re_token_t br_token;
+  re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+  re_charset_t *mbcset;
+  int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
+  int equiv_class_alloc = 0, char_class_alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+  int non_match = 0;
+  bin_tree_t *work_tree;
+  int token_len;
+  int first_round = 1;
+#ifdef _LIBC
+  collseqmb = (const unsigned char *)
+    _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+  nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules)
+    {
+      /*
+      if (MB_CUR_MAX > 1)
+      */
+      collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+      table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
+      symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+                                                 _NL_COLLATE_SYMB_TABLEMB);
+      extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+                                                  _NL_COLLATE_SYMB_EXTRAMB);
+    }
+#endif
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+#ifdef RE_ENABLE_I18N
+  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+#ifdef RE_ENABLE_I18N
+  if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else
+  if (BE (sbcset == NULL, 0))
+#endif /* RE_ENABLE_I18N */
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  token_len = peek_token_bracket (token, regexp, syntax);
+  if (BE (token->type == END_OF_RE, 0))
+    {
+      *err = REG_BADPAT;
+      goto parse_bracket_exp_free_return;
+    }
+  if (token->type == OP_NON_MATCH_LIST)
+    {
+#ifdef RE_ENABLE_I18N
+      mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+      non_match = 1;
+      if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+       bitset_set (sbcset, '\n');
+      re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+      token_len = peek_token_bracket (token, regexp, syntax);
+      if (BE (token->type == END_OF_RE, 0))
+       {
+         *err = REG_BADPAT;
+         goto parse_bracket_exp_free_return;
+       }
+    }
+
+  /* We treat the first ']' as a normal character.  */
+  if (token->type == OP_CLOSE_BRACKET)
+    token->type = CHARACTER;
+
+  while (1)
+    {
+      bracket_elem_t start_elem, end_elem;
+      unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
+      unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
+      reg_errcode_t ret;
+      int token_len2 = 0, is_range_exp = 0;
+      re_token_t token2;
+
+      start_elem.opr.name = start_name_buf;
+      ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
+                                  syntax, first_round);
+      if (BE (ret != REG_NOERROR, 0))
+       {
+         *err = ret;
+         goto parse_bracket_exp_free_return;
+       }
+      first_round = 0;
+
+      /* Get information about the next token.  We need it in any case.  */
+      token_len = peek_token_bracket (token, regexp, syntax);
+
+      /* Do not check for ranges if we know they are not allowed.  */
+      if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS)
+       {
+         if (BE (token->type == END_OF_RE, 0))
+           {
+             *err = REG_EBRACK;
+             goto parse_bracket_exp_free_return;
+           }
+         if (token->type == OP_CHARSET_RANGE)
+           {
+             re_string_skip_bytes (regexp, token_len); /* Skip '-'.  */
+             token_len2 = peek_token_bracket (&token2, regexp, syntax);
+             if (BE (token2.type == END_OF_RE, 0))
+               {
+                 *err = REG_EBRACK;
+                 goto parse_bracket_exp_free_return;
+               }
+             if (token2.type == OP_CLOSE_BRACKET)
+               {
+                 /* We treat the last '-' as a normal character.  */
+                 re_string_skip_bytes (regexp, -token_len);
+                 token->type = CHARACTER;
+               }
+             else
+               is_range_exp = 1;
+           }
+       }
+
+      if (is_range_exp == 1)
+       {
+         end_elem.opr.name = end_name_buf;
+         ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
+                                      dfa, syntax, 1);
+         if (BE (ret != REG_NOERROR, 0))
+           {
+             *err = ret;
+             goto parse_bracket_exp_free_return;
+           }
+
+         token_len = peek_token_bracket (token, regexp, syntax);
+
+#ifdef _LIBC
+         *err = build_range_exp (sbcset, mbcset, &range_alloc,
+                                 &start_elem, &end_elem);
+#else
+# ifdef RE_ENABLE_I18N
+         *err = build_range_exp (sbcset,
+                                 dfa->mb_cur_max > 1 ? mbcset : NULL,
+                                 &range_alloc, &start_elem, &end_elem);
+# else
+         *err = build_range_exp (sbcset, &start_elem, &end_elem);
+# endif
+#endif /* RE_ENABLE_I18N */
+         if (BE (*err != REG_NOERROR, 0))
+           goto parse_bracket_exp_free_return;
+       }
+      else
+       {
+         switch (start_elem.type)
+           {
+           case SB_CHAR:
+             bitset_set (sbcset, start_elem.opr.ch);
+             break;
+#ifdef RE_ENABLE_I18N
+           case MB_CHAR:
+             /* Check whether the array has enough space.  */
+             if (BE (mbchar_alloc == mbcset->nmbchars, 0))
+               {
+                 wchar_t *new_mbchars;
+                 /* Not enough, realloc it.  */
+                 /* +1 in case of mbcset->nmbchars is 0.  */
+                 mbchar_alloc = 2 * mbcset->nmbchars + 1;
+                 /* Use realloc since array is NULL if *alloc == 0.  */
+                 new_mbchars = re_realloc (mbcset->mbchars, wchar_t,
+                                           mbchar_alloc);
+                 if (BE (new_mbchars == NULL, 0))
+                   goto parse_bracket_exp_espace;
+                 mbcset->mbchars = new_mbchars;
+               }
+             mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
+             break;
+#endif /* RE_ENABLE_I18N */
+           case EQUIV_CLASS:
+             *err = build_equiv_class (sbcset,
+#ifdef RE_ENABLE_I18N
+                                       mbcset, &equiv_class_alloc,
+#endif /* RE_ENABLE_I18N */
+                                       start_elem.opr.name);
+             if (BE (*err != REG_NOERROR, 0))
+               goto parse_bracket_exp_free_return;
+             break;
+           case COLL_SYM:
+             *err = build_collating_symbol (sbcset,
+#ifdef RE_ENABLE_I18N
+                                            mbcset, &coll_sym_alloc,
+#endif /* RE_ENABLE_I18N */
+                                            start_elem.opr.name);
+             if (BE (*err != REG_NOERROR, 0))
+               goto parse_bracket_exp_free_return;
+             break;
+           case CHAR_CLASS:
+             *err = build_charclass (regexp->trans, sbcset,
+#ifdef RE_ENABLE_I18N
+                                     mbcset, &char_class_alloc,
+#endif /* RE_ENABLE_I18N */
+                                     (const char *) start_elem.opr.name, syntax);
+             if (BE (*err != REG_NOERROR, 0))
+              goto parse_bracket_exp_free_return;
+             break;
+           default:
+             assert (0);
+             break;
+           }
+       }
+      if (BE (token->type == END_OF_RE, 0))
+       {
+         *err = REG_EBRACK;
+         goto parse_bracket_exp_free_return;
+       }
+      if (token->type == OP_CLOSE_BRACKET)
+       break;
+    }
+
+  re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+
+  /* If it is non-matching list.  */
+  if (non_match)
+    bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+  /* Ensure only single byte characters are set.  */
+  if (dfa->mb_cur_max > 1)
+    bitset_mask (sbcset, dfa->sb_char);
+
+  if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
+      || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes
+                                                    || mbcset->non_match)))
+    {
+      bin_tree_t *mbc_tree;
+      int sbc_idx;
+      /* Build a tree for complex bracket.  */
+      dfa->has_mb_node = 1;
+      br_token.type = COMPLEX_BRACKET;
+      br_token.opr.mbcset = mbcset;
+      mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (mbc_tree == NULL, 0))
+       goto parse_bracket_exp_espace;
+      for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx)
+       if (sbcset[sbc_idx])
+         break;
+      /* If there are no bits set in sbcset, there is no point
+        of having both SIMPLE_BRACKET and COMPLEX_BRACKET.  */
+      if (sbc_idx < BITSET_WORDS)
+       {
+         /* Build a tree for simple bracket.  */
+         br_token.type = SIMPLE_BRACKET;
+         br_token.opr.sbcset = sbcset;
+         work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+         if (BE (work_tree == NULL, 0))
+           goto parse_bracket_exp_espace;
+
+         /* Then join them by ALT node.  */
+         work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT);
+         if (BE (work_tree == NULL, 0))
+           goto parse_bracket_exp_espace;
+       }
+      else
+       {
+         re_free (sbcset);
+         work_tree = mbc_tree;
+       }
+    }
+  else
+#endif /* not RE_ENABLE_I18N */
+    {
+#ifdef RE_ENABLE_I18N
+      free_charset (mbcset);
+#endif
+      /* Build a tree for simple bracket.  */
+      br_token.type = SIMPLE_BRACKET;
+      br_token.opr.sbcset = sbcset;
+      work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (work_tree == NULL, 0))
+       goto parse_bracket_exp_espace;
+    }
+  return work_tree;
+
+ parse_bracket_exp_espace:
+  *err = REG_ESPACE;
+ parse_bracket_exp_free_return:
+  re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+  free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+  return NULL;
+}
+
+/* Parse an element in the bracket expression.  */
+
+static reg_errcode_t
+parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp,
+                      re_token_t *token, int token_len, re_dfa_t *dfa,
+                      reg_syntax_t syntax, int accept_hyphen)
+{
+#ifdef RE_ENABLE_I18N
+  int cur_char_size;
+  cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
+  if (cur_char_size > 1)
+    {
+      elem->type = MB_CHAR;
+      elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
+      re_string_skip_bytes (regexp, cur_char_size);
+      return REG_NOERROR;
+    }
+#endif /* RE_ENABLE_I18N */
+  re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+  if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
+      || token->type == OP_OPEN_EQUIV_CLASS)
+    return parse_bracket_symbol (elem, regexp, token);
+  if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen)
+    {
+      /* A '-' must only appear as anything but a range indicator before
+        the closing bracket.  Everything else is an error.  */
+      re_token_t token2;
+      (void) peek_token_bracket (&token2, regexp, syntax);
+      if (token2.type != OP_CLOSE_BRACKET)
+       /* The actual error value is not standardized since this whole
+          case is undefined.  But ERANGE makes good sense.  */
+       return REG_ERANGE;
+    }
+  elem->type = SB_CHAR;
+  elem->opr.ch = token->opr.c;
+  return REG_NOERROR;
+}
+
+/* Parse a bracket symbol in the bracket expression.  Bracket symbols are
+   such as [:<character_class>:], [.<collating_element>.], and
+   [=<equivalent_class>=].  */
+
+static reg_errcode_t
+parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp,
+                     re_token_t *token)
+{
+  unsigned char ch, delim = token->opr.c;
+  int i = 0;
+  if (re_string_eoi(regexp))
+    return REG_EBRACK;
+  for (;; ++i)
+    {
+      if (i >= BRACKET_NAME_BUF_SIZE)
+       return REG_EBRACK;
+      if (token->type == OP_OPEN_CHAR_CLASS)
+       ch = re_string_fetch_byte_case (regexp);
+      else
+       ch = re_string_fetch_byte (regexp);
+      if (re_string_eoi(regexp))
+       return REG_EBRACK;
+      if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
+       break;
+      elem->opr.name[i] = ch;
+    }
+  re_string_skip_bytes (regexp, 1);
+  elem->opr.name[i] = '\0';
+  switch (token->type)
+    {
+    case OP_OPEN_COLL_ELEM:
+      elem->type = COLL_SYM;
+      break;
+    case OP_OPEN_EQUIV_CLASS:
+      elem->type = EQUIV_CLASS;
+      break;
+    case OP_OPEN_CHAR_CLASS:
+      elem->type = CHAR_CLASS;
+      break;
+    default:
+      break;
+    }
+  return REG_NOERROR;
+}
+
+  /* Helper function for parse_bracket_exp.
+     Build the equivalence class which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
+     is a pointer argument sinse we may update it.  */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_equiv_class (bitset_t sbcset, re_charset_t *mbcset,
+                  int *equiv_class_alloc, const unsigned char *name)
+#else /* not RE_ENABLE_I18N */
+build_equiv_class (bitset_t sbcset, const unsigned char *name)
+#endif /* not RE_ENABLE_I18N */
+{
+#ifdef _LIBC
+  uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules != 0)
+    {
+      const int32_t *table, *indirect;
+      const unsigned char *weights, *extra, *cp;
+      unsigned char char_buf[2];
+      int32_t idx1, idx2;
+      unsigned int ch;
+      size_t len;
+      /* This #include defines a local function!  */
+# include <locale/weight.h>
+      /* Calculate the index for equivalence class.  */
+      cp = name;
+      table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+      weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+                                              _NL_COLLATE_WEIGHTMB);
+      extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+                                                  _NL_COLLATE_EXTRAMB);
+      indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+                                               _NL_COLLATE_INDIRECTMB);
+      idx1 = findidx (&cp);
+      if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
+       /* This isn't a valid character.  */
+       return REG_ECOLLATE;
+
+      /* Build single byte matcing table for this equivalence class.  */
+      char_buf[1] = (unsigned char) '\0';
+      len = weights[idx1 & 0xffffff];
+      for (ch = 0; ch < SBC_MAX; ++ch)
+       {
+         char_buf[0] = ch;
+         cp = char_buf;
+         idx2 = findidx (&cp);
+/*
+         idx2 = table[ch];
+*/
+         if (idx2 == 0)
+           /* This isn't a valid character.  */
+           continue;
+         /* Compare only if the length matches and the collation rule
+            index is the same.  */
+         if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24))
+           {
+             int cnt = 0;
+
+             while (cnt <= len &&
+                    weights[(idx1 & 0xffffff) + 1 + cnt]
+                    == weights[(idx2 & 0xffffff) + 1 + cnt])
+               ++cnt;
+
+             if (cnt > len)
+               bitset_set (sbcset, ch);
+           }
+       }
+      /* Check whether the array has enough space.  */
+      if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0))
+       {
+         /* Not enough, realloc it.  */
+         /* +1 in case of mbcset->nequiv_classes is 0.  */
+         int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+         /* Use realloc since the array is NULL if *alloc == 0.  */
+         int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes,
+                                                  int32_t,
+                                                  new_equiv_class_alloc);
+         if (BE (new_equiv_classes == NULL, 0))
+           return REG_ESPACE;
+         mbcset->equiv_classes = new_equiv_classes;
+         *equiv_class_alloc = new_equiv_class_alloc;
+       }
+      mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
+    }
+  else
+#endif /* _LIBC */
+    {
+      if (BE (strlen ((const char *) name) != 1, 0))
+       return REG_ECOLLATE;
+      bitset_set (sbcset, *name);
+    }
+  return REG_NOERROR;
+}
+
+  /* Helper function for parse_bracket_exp.
+     Build the character class which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
+     is a pointer argument sinse we may update it.  */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+                re_charset_t *mbcset, int *char_class_alloc,
+                const char *class_name, reg_syntax_t syntax)
+#else /* not RE_ENABLE_I18N */
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+                const char *class_name, reg_syntax_t syntax)
+#endif /* not RE_ENABLE_I18N */
+{
+  int i;
+
+  /* In case of REG_ICASE "upper" and "lower" match the both of
+     upper and lower cases.  */
+  if ((syntax & RE_ICASE)
+      && (strcmp (class_name, "upper") == 0 || strcmp (class_name, "lower") == 0))
+    class_name = "alpha";
+
+#ifdef RE_ENABLE_I18N
+  /* Check the space of the arrays.  */
+  if (BE (*char_class_alloc == mbcset->nchar_classes, 0))
+    {
+      /* Not enough, realloc it.  */
+      /* +1 in case of mbcset->nchar_classes is 0.  */
+      int new_char_class_alloc = 2 * mbcset->nchar_classes + 1;
+      /* Use realloc since array is NULL if *alloc == 0.  */
+      wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t,
+                                              new_char_class_alloc);
+      if (BE (new_char_classes == NULL, 0))
+       return REG_ESPACE;
+      mbcset->char_classes = new_char_classes;
+      *char_class_alloc = new_char_class_alloc;
+    }
+  mbcset->char_classes[mbcset->nchar_classes++] = __wctype (class_name);
+#endif /* RE_ENABLE_I18N */
+
+#define BUILD_CHARCLASS_LOOP(ctype_func)       \
+  do {                                         \
+    if (BE (trans != NULL, 0))                 \
+      {                                                \
+       for (i = 0; i < SBC_MAX; ++i)           \
+         if (ctype_func (i))                   \
+           bitset_set (sbcset, trans[i]);      \
+      }                                                \
+    else                                       \
+      {                                                \
+       for (i = 0; i < SBC_MAX; ++i)           \
+         if (ctype_func (i))                   \
+           bitset_set (sbcset, i);             \
+      }                                                \
+  } while (0)
+
+  if (strcmp (class_name, "alnum") == 0)
+    BUILD_CHARCLASS_LOOP (isalnum);
+  else if (strcmp (class_name, "cntrl") == 0)
+    BUILD_CHARCLASS_LOOP (iscntrl);
+  else if (strcmp (class_name, "lower") == 0)
+    BUILD_CHARCLASS_LOOP (islower);
+  else if (strcmp (class_name, "space") == 0)
+    BUILD_CHARCLASS_LOOP (isspace);
+  else if (strcmp (class_name, "alpha") == 0)
+    BUILD_CHARCLASS_LOOP (isalpha);
+  else if (strcmp (class_name, "digit") == 0)
+    BUILD_CHARCLASS_LOOP (isdigit);
+  else if (strcmp (class_name, "print") == 0)
+    BUILD_CHARCLASS_LOOP (isprint);
+  else if (strcmp (class_name, "upper") == 0)
+    BUILD_CHARCLASS_LOOP (isupper);
+  else if (strcmp (class_name, "blank") == 0)
+#ifndef GAWK
+    BUILD_CHARCLASS_LOOP (isblank);
+#else
+    /* see comments above */
+    BUILD_CHARCLASS_LOOP (is_blank);
+#endif
+  else if (strcmp (class_name, "graph") == 0)
+    BUILD_CHARCLASS_LOOP (isgraph);
+  else if (strcmp (class_name, "punct") == 0)
+    BUILD_CHARCLASS_LOOP (ispunct);
+  else if (strcmp (class_name, "xdigit") == 0)
+    BUILD_CHARCLASS_LOOP (isxdigit);
+  else
+    return REG_ECTYPE;
+
+  return REG_NOERROR;
+}
+
+static bin_tree_t *
+build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans,
+                   const char *class_name,
+                   const char *extra, int non_match,
+                   reg_errcode_t *err)
+{
+  re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+  re_charset_t *mbcset;
+  int alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+  reg_errcode_t ret;
+  re_token_t br_token;
+  bin_tree_t *tree;
+
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+#ifdef RE_ENABLE_I18N
+  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+
+#ifdef RE_ENABLE_I18N
+  if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else /* not RE_ENABLE_I18N */
+  if (BE (sbcset == NULL, 0))
+#endif /* not RE_ENABLE_I18N */
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  if (non_match)
+    {
+#ifdef RE_ENABLE_I18N
+      mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+    }
+
+  /* We don't care the syntax in this case.  */
+  ret = build_charclass (trans, sbcset,
+#ifdef RE_ENABLE_I18N
+                        mbcset, &alloc,
+#endif /* RE_ENABLE_I18N */
+                        class_name, 0);
+
+  if (BE (ret != REG_NOERROR, 0))
+    {
+      re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+      free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+      *err = ret;
+      return NULL;
+    }
+  /* \w match '_' also.  */
+  for (; *extra; extra++)
+    bitset_set (sbcset, *extra);
+
+  /* If it is non-matching list.  */
+  if (non_match)
+    bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+  /* Ensure only single byte characters are set.  */
+  if (dfa->mb_cur_max > 1)
+    bitset_mask (sbcset, dfa->sb_char);
+#endif
+
+  /* Build a tree for simple bracket.  */
+  br_token.type = SIMPLE_BRACKET;
+  br_token.opr.sbcset = sbcset;
+  tree = create_token_tree (dfa, NULL, NULL, &br_token);
+  if (BE (tree == NULL, 0))
+    goto build_word_op_espace;
+
+#ifdef RE_ENABLE_I18N
+  if (dfa->mb_cur_max > 1)
+    {
+      bin_tree_t *mbc_tree;
+      /* Build a tree for complex bracket.  */
+      br_token.type = COMPLEX_BRACKET;
+      br_token.opr.mbcset = mbcset;
+      dfa->has_mb_node = 1;
+      mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (mbc_tree == NULL, 0))
+       goto build_word_op_espace;
+      /* Then join them by ALT node.  */
+      tree = create_tree (dfa, tree, mbc_tree, OP_ALT);
+      if (BE (mbc_tree != NULL, 1))
+       return tree;
+    }
+  else
+    {
+      free_charset (mbcset);
+      return tree;
+    }
+#else /* not RE_ENABLE_I18N */
+  return tree;
+#endif /* not RE_ENABLE_I18N */
+
+ build_word_op_espace:
+  re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+  free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+  *err = REG_ESPACE;
+  return NULL;
+}
+
+/* This is intended for the expressions like "a{1,3}".
+   Fetch a number from `input', and return the number.
+   Return -1, if the number field is empty like "{,1}".
+   Return -2, If an error is occured.  */
+
+static int
+fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax)
+{
+  int num = -1;
+  unsigned char c;
+  while (1)
+    {
+      fetch_token (token, input, syntax);
+      c = token->opr.c;
+      if (BE (token->type == END_OF_RE, 0))
+       return -2;
+      if (token->type == OP_CLOSE_DUP_NUM || c == ',')
+       break;
+      num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
+            ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0'));
+      num = (num > RE_DUP_MAX) ? -2 : num;
+    }
+  return num;
+}
+\f
+#ifdef RE_ENABLE_I18N
+static void
+free_charset (re_charset_t *cset)
+{
+  re_free (cset->mbchars);
+# ifdef _LIBC
+  re_free (cset->coll_syms);
+  re_free (cset->equiv_classes);
+  re_free (cset->range_starts);
+  re_free (cset->range_ends);
+# endif
+  re_free (cset->char_classes);
+  re_free (cset);
+}
+#endif /* RE_ENABLE_I18N */
+\f
+/* Functions for binary tree operation.  */
+
+/* Create a tree node.  */
+
+static bin_tree_t *
+create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+            re_token_type_t type)
+{
+  re_token_t t;
+  t.type = type;
+  return create_token_tree (dfa, left, right, &t);
+}
+
+static bin_tree_t *
+create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+                  const re_token_t *token)
+{
+  bin_tree_t *tree;
+  if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0))
+    {
+      bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1);
+
+      if (storage == NULL)
+       return NULL;
+      storage->next = dfa->str_tree_storage;
+      dfa->str_tree_storage = storage;
+      dfa->str_tree_storage_idx = 0;
+    }
+  tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++];
+
+  tree->parent = NULL;
+  tree->left = left;
+  tree->right = right;
+  tree->token = *token;
+  tree->token.duplicated = 0;
+  tree->token.opt_subexp = 0;
+  tree->first = NULL;
+  tree->next = NULL;
+  tree->node_idx = -1;
+
+  if (left != NULL)
+    left->parent = tree;
+  if (right != NULL)
+    right->parent = tree;
+  return tree;
+}
+
+/* Mark the tree SRC as an optional subexpression.
+   To be called from preorder or postorder.  */
+
+static reg_errcode_t
+mark_opt_subexp (void *extra, bin_tree_t *node)
+{
+  int idx = (int) (long) extra;
+  if (node->token.type == SUBEXP && node->token.opr.idx == idx)
+    node->token.opt_subexp = 1;
+
+  return REG_NOERROR;
+}
+
+/* Free the allocated memory inside NODE. */
+
+static void
+free_token (re_token_t *node)
+{
+#ifdef RE_ENABLE_I18N
+  if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
+    free_charset (node->opr.mbcset);
+  else
+#endif /* RE_ENABLE_I18N */
+    if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
+      re_free (node->opr.sbcset);
+}
+
+/* Worker function for tree walking.  Free the allocated memory inside NODE
+   and its children. */
+
+static reg_errcode_t
+free_tree (void *extra, bin_tree_t *node)
+{
+  free_token (&node->token);
+  return REG_NOERROR;
+}
+
+
+/* Duplicate the node SRC, and return new node.  This is a preorder
+   visit similar to the one implemented by the generic visitor, but
+   we need more infrastructure to maintain two parallel trees --- so,
+   it's easier to duplicate.  */
+
+static bin_tree_t *
+duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa)
+{
+  const bin_tree_t *node;
+  bin_tree_t *dup_root;
+  bin_tree_t **p_new = &dup_root, *dup_node = root->parent;
+
+  for (node = root; ; )
+    {
+      /* Create a new tree and link it back to the current parent.  */
+      *p_new = create_token_tree (dfa, NULL, NULL, &node->token);
+      if (*p_new == NULL)
+       return NULL;
+      (*p_new)->parent = dup_node;
+      (*p_new)->token.duplicated = 1;
+      dup_node = *p_new;
+
+      /* Go to the left node, or up and to the right.  */
+      if (node->left)
+       {
+         node = node->left;
+         p_new = &dup_node->left;
+       }
+      else
+       {
+         const bin_tree_t *prev = NULL;
+         while (node->right == prev || node->right == NULL)
+           {
+             prev = node;
+             node = node->parent;
+             dup_node = dup_node->parent;
+             if (!node)
+               return dup_root;
+           }
+         node = node->right;
+         p_new = &dup_node->right;
+       }
+    }
+}
index 556d8ab11f40550335066f491d08383557e02a00..3dd8dfa01f2a6ab7a197ecddd481704150253c36 100644 (file)
-/* Extended regular expression matching and search library,
-   version 0.12.
-   (Implements POSIX draft P10003.2/D11.2, except for
-   internationalization features.)
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
 
-   Copyright (C) 1993 Free Software Foundation, Inc.
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
-
-   This program is distributed in the hope that it will be useful,
+   The GNU C Library 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.  */
-
-/* AIX requires this to be the first thing in the file. */
-#if defined (_AIX) && !defined (REGEX_MALLOC)
-  #pragma alloca
-#endif
-
-#define _GNU_SOURCE
-
-/* We need this for `regex.h', and perhaps for the Emacs include files.  */
-#include <sys/types.h>
-
-/* We used to test for `BSTRING' here, but only GCC and Emacs define
-   `BSTRING', as far as I know, and neither of them use this code.  */
-#include <string.h>
-#ifndef bcmp
-#define bcmp(s1, s2, n)        memcmp ((s1), (s2), (n))
-#endif
-#ifndef bcopy
-#define bcopy(s, d, n) memcpy ((d), (s), (n))
-#endif
-#ifndef bzero
-#define bzero(s, n)    memset ((s), 0, (n))
-#endif
-
-#include <stdlib.h>
-
-
-/* Define the syntax stuff for \<, \>, etc.  */
-
-/* This must be nonzero for the wordchar and notwordchar pattern
-   commands in re_match_2.  */
-#ifndef Sword
-#define Sword 1
-#endif
-
-#ifdef SYNTAX_TABLE
-
-extern char *re_syntax_table;
-
-#else /* not SYNTAX_TABLE */
-
-/* How many characters in the character set.  */
-#define CHAR_SET_SIZE 256
-
-static char re_syntax_table[CHAR_SET_SIZE];
-
-static void
-init_syntax_once ()
-{
-   register int c;
-   static int done = 0;
-
-   if (done)
-     return;
-
-   bzero (re_syntax_table, sizeof re_syntax_table);
-
-   for (c = 'a'; c <= 'z'; c++)
-     re_syntax_table[c] = Sword;
-
-   for (c = 'A'; c <= 'Z'; c++)
-     re_syntax_table[c] = Sword;
-
-   for (c = '0'; c <= '9'; c++)
-     re_syntax_table[c] = Sword;
-
-   re_syntax_table['_'] = Sword;
-
-   done = 1;
-}
-
-#endif /* not SYNTAX_TABLE */
-
-#define SYNTAX(c) re_syntax_table[c]
-
-\f
-/* Get the interface, including the syntax bits.  */
-#include "regex.h"
-
-/* isalpha etc. are used for the character classes.  */
-#include <ctype.h>
-
-#ifndef isascii
-#define isascii(c) 1
-#endif
-
-#ifdef isblank
-#define ISBLANK(c) (isascii (c) && isblank (c))
-#else
-#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
-#endif
-#ifdef isgraph
-#define ISGRAPH(c) (isascii (c) && isgraph (c))
-#else
-#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
-#endif
-
-#define ISPRINT(c) (isascii (c) && isprint (c))
-#define ISDIGIT(c) (isascii (c) && isdigit (c))
-#define ISALNUM(c) (isascii (c) && isalnum (c))
-#define ISALPHA(c) (isascii (c) && isalpha (c))
-#define ISCNTRL(c) (isascii (c) && iscntrl (c))
-#define ISLOWER(c) (isascii (c) && islower (c))
-#define ISPUNCT(c) (isascii (c) && ispunct (c))
-#define ISSPACE(c) (isascii (c) && isspace (c))
-#define ISUPPER(c) (isascii (c) && isupper (c))
-#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-/* We remove any previous definition of `SIGN_EXTEND_CHAR',
-   since ours (we hope) works properly with all combinations of
-   machines, compilers, `char' and `unsigned char' argument types.
-   (Per Bothner suggested the basic approach.)  */
-#undef SIGN_EXTEND_CHAR
-#if __STDC__
-#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
-#else  /* not __STDC__ */
-/* As in Harbison and Steele.  */
-#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
-#endif
-\f
-/* Should we use malloc or alloca?  If REGEX_MALLOC is not defined, we
-   use `alloca' instead of `malloc'.  This is because using malloc in
-   re_search* or re_match* could cause memory leaks when C-g is used in
-   Emacs; also, malloc is slower and causes storage fragmentation.  On
-   the other hand, malloc is more portable, and easier to debug.
-
-   Because we sometimes use alloca, some routines have to be macros,
-   not functions -- `alloca'-allocated space disappears at the end of the
-   function it is called in.  */
-
-#ifdef REGEX_MALLOC
-
-#define REGEX_ALLOCATE malloc
-#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
-
-#else /* not REGEX_MALLOC  */
-
-/* Emacs already defines alloca, sometimes.  */
-#ifndef alloca
-
-/* Make alloca work the best possible way.  */
-#ifdef __GNUC__
-#define alloca __builtin_alloca
-#else /* not __GNUC__ */
-#if HAVE_ALLOCA_H
-#include <alloca.h>
-#else /* not __GNUC__ or HAVE_ALLOCA_H */
-#ifndef _AIX /* Already did AIX, up at the top.  */
-char *alloca ();
-#endif /* not _AIX */
-#endif /* not HAVE_ALLOCA_H */
-#endif /* not __GNUC__ */
-
-#endif /* not alloca */
-
-#define REGEX_ALLOCATE alloca
-
-/* Assumes a `char *destination' variable.  */
-#define REGEX_REALLOCATE(source, osize, nsize)                         \
-  (destination = (char *) alloca (nsize),                              \
-   bcopy (source, destination, osize),                                 \
-   destination)
-
-#endif /* not REGEX_MALLOC */
-
-
-/* True if `size1' is non-NULL and PTR is pointing anywhere inside
-   `string1' or just past its end.  This works if PTR is NULL, which is
-   a good thing.  */
-#define FIRST_STRING_P(ptr)                                    \
-  (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
-
-/* (Re)Allocate N items of type T using malloc, or fail.  */
-#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
-#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
-#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
-
-#define BYTEWIDTH 8 /* In bits.  */
-
-#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
-
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
-typedef char boolean;
-#define false 0
-#define true 1
-\f
-/* These are the command codes that appear in compiled regular
-   expressions.  Some opcodes are followed by argument bytes.  A
-   command code can specify any interpretation whatsoever for its
-   arguments.  Zero bytes may appear in the compiled regular expression.
-
-   The value of `exactn' is needed in search.c (search_buffer) in Emacs.
-   So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
-   `exactn' we use here must also be 1.  */
-
-typedef enum
-{
-  no_op = 0,
-
-       /* Followed by one byte giving n, then by n literal bytes.  */
-  exactn = 1,
-
-       /* Matches any (more or less) character.  */
-  anychar,
-
-       /* Matches any one char belonging to specified set.  First
-          following byte is number of bitmap bytes.  Then come bytes
-          for a bitmap saying which chars are in.  Bits in each byte
-          are ordered low-bit-first.  A character is in the set if its
-          bit is 1.  A character too large to have a bit in the map is
-          automatically not in the set.  */
-  charset,
-
-       /* Same parameters as charset, but match any character that is
-          not one of those specified.  */
-  charset_not,
-
-       /* Start remembering the text that is matched, for storing in a
-          register.  Followed by one byte with the register number, in
-          the range 0 to one less than the pattern buffer's re_nsub
-          field.  Then followed by one byte with the number of groups
-          inner to this one.  (This last has to be part of the
-          start_memory only because we need it in the on_failure_jump
-          of re_match_2.)  */
-  start_memory,
-
-       /* Stop remembering the text that is matched and store it in a
-          memory register.  Followed by one byte with the register
-          number, in the range 0 to one less than `re_nsub' in the
-          pattern buffer, and one byte with the number of inner groups,
-          just like `start_memory'.  (We need the number of inner
-          groups here because we don't have any easy way of finding the
-          corresponding start_memory when we're at a stop_memory.)  */
-  stop_memory,
-
-       /* Match a duplicate of something remembered. Followed by one
-          byte containing the register number.  */
-  duplicate,
-
-       /* Fail unless at beginning of line.  */
-  begline,
-
-       /* Fail unless at end of line.  */
-  endline,
-
-       /* Succeeds if at beginning of buffer (if emacs) or at beginning
-          of string to be matched (if not).  */
-  begbuf,
-
-       /* Analogously, for end of buffer/string.  */
-  endbuf,
-
-       /* Followed by two byte relative address to which to jump.  */
-  jump,
-
-       /* Same as jump, but marks the end of an alternative.  */
-  jump_past_alt,
-
-       /* Followed by two-byte relative address of place to resume at
-          in case of failure.  */
-  on_failure_jump,
-
-       /* Like on_failure_jump, but pushes a placeholder instead of the
-          current string position when executed.  */
-  on_failure_keep_string_jump,
-
-       /* Throw away latest failure point and then jump to following
-          two-byte relative address.  */
-  pop_failure_jump,
-
-       /* Change to pop_failure_jump if know won't have to backtrack to
-          match; otherwise change to jump.  This is used to jump
-          back to the beginning of a repeat.  If what follows this jump
-          clearly won't match what the repeat does, such that we can be
-          sure that there is no use backtracking out of repetitions
-          already matched, then we change it to a pop_failure_jump.
-          Followed by two-byte address.  */
-  maybe_pop_jump,
-
-       /* Jump to following two-byte address, and push a dummy failure
-          point. This failure point will be thrown away if an attempt
-          is made to use it for a failure.  A `+' construct makes this
-          before the first repeat.  Also used as an intermediary kind
-          of jump when compiling an alternative.  */
-  dummy_failure_jump,
-
-       /* Push a dummy failure point and continue.  Used at the end of
-          alternatives.  */
-  push_dummy_failure,
-
-       /* Followed by two-byte relative address and two-byte number n.
-          After matching N times, jump to the address upon failure.  */
-  succeed_n,
-
-       /* Followed by two-byte relative address, and two-byte number n.
-          Jump to the address N times, then fail.  */
-  jump_n,
-
-       /* Set the following two-byte relative address to the
-          subsequent two-byte number.  The address *includes* the two
-          bytes of number.  */
-  set_number_at,
-
-  wordchar,    /* Matches any word-constituent character.  */
-  notwordchar, /* Matches any char that is not a word-constituent.  */
-
-  wordbeg,     /* Succeeds if at word beginning.  */
-  wordend,     /* Succeeds if at word end.  */
-
-  wordbound,   /* Succeeds if at a word boundary.  */
-  notwordbound /* Succeeds if not at a word boundary.  */
-
-#ifdef emacs
-  ,before_dot, /* Succeeds if before point.  */
-  at_dot,      /* Succeeds if at point.  */
-  after_dot,   /* Succeeds if after point.  */
-
-       /* Matches any character whose syntax is specified.  Followed by
-          a byte which contains a syntax code, e.g., Sword.  */
-  syntaxspec,
-
-       /* Matches any character whose syntax is not that specified.  */
-  notsyntaxspec
-#endif /* emacs */
-} re_opcode_t;
-\f
-/* Common operations on the compiled pattern.  */
-
-/* Store NUMBER in two contiguous bytes starting at DESTINATION.  */
-
-#define STORE_NUMBER(destination, number)                              \
-  do {                                                                 \
-    (destination)[0] = (number) & 0377;                                        \
-    (destination)[1] = (number) >> 8;                                  \
-  } while (0)
-
-/* Same as STORE_NUMBER, except increment DESTINATION to
-   the byte after where the number is stored.  Therefore, DESTINATION
-   must be an lvalue.  */
-
-#define STORE_NUMBER_AND_INCR(destination, number)                     \
-  do {                                                                 \
-    STORE_NUMBER (destination, number);                                        \
-    (destination) += 2;                                                        \
-  } while (0)
-
-/* Put into DESTINATION a number stored in two contiguous bytes starting
-   at SOURCE.  */
-
-#define EXTRACT_NUMBER(destination, source)                            \
-  do {                                                                 \
-    (destination) = *(source) & 0377;                                  \
-    (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8;          \
-  } while (0)
-
-#ifdef DEBUG
-static void
-extract_number (dest, source)
-    int *dest;
-    unsigned char *source;
-{
-  int temp = SIGN_EXTEND_CHAR (*(source + 1));
-  *dest = *source & 0377;
-  *dest += temp << 8;
-}
-
-#ifndef EXTRACT_MACROS /* To debug the macros.  */
-#undef EXTRACT_NUMBER
-#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
-#endif /* not EXTRACT_MACROS */
-
-#endif /* DEBUG */
-
-/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
-   SOURCE must be an lvalue.  */
-
-#define EXTRACT_NUMBER_AND_INCR(destination, source)                   \
-  do {                                                                 \
-    EXTRACT_NUMBER (destination, source);                              \
-    (source) += 2;                                                     \
-  } while (0)
-
-#ifdef DEBUG
-static void
-extract_number_and_incr (destination, source)
-    int *destination;
-    unsigned char **source;
-{
-  extract_number (destination, *source);
-  *source += 2;
-}
-
-#ifndef EXTRACT_MACROS
-#undef EXTRACT_NUMBER_AND_INCR
-#define EXTRACT_NUMBER_AND_INCR(dest, src) \
-  extract_number_and_incr (&dest, &src)
-#endif /* not EXTRACT_MACROS */
-
-#endif /* DEBUG */
-\f
-/* If DEBUG is defined, Regex prints many voluminous messages about what
-   it is doing (if the variable `debug' is nonzero).  If linked with the
-   main program in `iregex.c', you can enter patterns and strings
-   interactively.  And if linked with the main program in `main.c' and
-   the other test files, you can run the already-written tests.  */
-
-#ifdef DEBUG
-
-/* We use standard I/O for debugging.  */
-#include <stdio.h>
-
-/* It is useful to test things that ``must'' be true when debugging.  */
-#include <assert.h>
-
-static int debug = 0;
-
-#define DEBUG_STATEMENT(e) e
-#define DEBUG_PRINT1(x) if (debug) printf (x)
-#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
-#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
-#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
-#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)                          \
-  if (debug) print_partial_compiled_pattern (s, e)
-#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)                 \
-  if (debug) print_double_string (w, s1, sz1, s2, sz2)
-
-
-extern void printchar ();
-
-/* Print the fastmap in human-readable form.  */
-
-void
-print_fastmap (fastmap)
-    char *fastmap;
-{
-  unsigned was_a_range = 0;
-  unsigned i = 0;
-
-  while (i < (1 << BYTEWIDTH))
-    {
-      if (fastmap[i++])
-       {
-         was_a_range = 0;
-         printchar (i - 1);
-         while (i < (1 << BYTEWIDTH)  &&  fastmap[i])
-           {
-             was_a_range = 1;
-             i++;
-           }
-         if (was_a_range)
-           {
-             printf ("-");
-             printchar (i - 1);
-           }
-       }
-    }
-  putchar ('\n');
-}
-
-
-/* Print a compiled pattern string in human-readable form, starting at
-   the START pointer into it and ending just before the pointer END.  */
-
-void
-print_partial_compiled_pattern (start, end)
-    unsigned char *start;
-    unsigned char *end;
-{
-  int mcnt, mcnt2;
-  unsigned char *p = start;
-  unsigned char *pend = end;
-
-  if (start == NULL)
-    {
-      printf ("(null)\n");
-      return;
-    }
-
-  /* Loop over pattern commands.  */
-  while (p < pend)
-    {
-      switch ((re_opcode_t) *p++)
-       {
-       case no_op:
-         printf ("/no_op");
-         break;
-
-       case exactn:
-         mcnt = *p++;
-         printf ("/exactn/%d", mcnt);
-         do
-           {
-             putchar ('/');
-             printchar (*p++);
-           }
-         while (--mcnt);
-         break;
-
-       case start_memory:
-         mcnt = *p++;
-         printf ("/start_memory/%d/%d", mcnt, *p++);
-         break;
-
-       case stop_memory:
-         mcnt = *p++;
-         printf ("/stop_memory/%d/%d", mcnt, *p++);
-         break;
-
-       case duplicate:
-         printf ("/duplicate/%d", *p++);
-         break;
-
-       case anychar:
-         printf ("/anychar");
-         break;
-
-       case charset:
-       case charset_not:
-         {
-           register int c;
-
-           printf ("/charset%s",
-                   (re_opcode_t) *(p - 1) == charset_not ? "_not" : "");
-
-           assert (p + *p < pend);
-
-           for (c = 0; c < *p; c++)
-             {
-               unsigned bit;
-               unsigned char map_byte = p[1 + c];
-
-               putchar ('/');
-
-               for (bit = 0; bit < BYTEWIDTH; bit++)
-                 if (map_byte & (1 << bit))
-                   printchar (c * BYTEWIDTH + bit);
-             }
-           p += 1 + *p;
-           break;
-         }
-
-       case begline:
-         printf ("/begline");
-         break;
-
-       case endline:
-         printf ("/endline");
-         break;
-
-       case on_failure_jump:
-         extract_number_and_incr (&mcnt, &p);
-         printf ("/on_failure_jump/0/%d", mcnt);
-         break;
-
-       case on_failure_keep_string_jump:
-         extract_number_and_incr (&mcnt, &p);
-         printf ("/on_failure_keep_string_jump/0/%d", mcnt);
-         break;
-
-       case dummy_failure_jump:
-         extract_number_and_incr (&mcnt, &p);
-         printf ("/dummy_failure_jump/0/%d", mcnt);
-         break;
-
-       case push_dummy_failure:
-         printf ("/push_dummy_failure");
-         break;
-
-       case maybe_pop_jump:
-         extract_number_and_incr (&mcnt, &p);
-         printf ("/maybe_pop_jump/0/%d", mcnt);
-         break;
-
-       case pop_failure_jump:
-         extract_number_and_incr (&mcnt, &p);
-         printf ("/pop_failure_jump/0/%d", mcnt);
-         break;
-
-       case jump_past_alt:
-         extract_number_and_incr (&mcnt, &p);
-         printf ("/jump_past_alt/0/%d", mcnt);
-         break;
-
-       case jump:
-         extract_number_and_incr (&mcnt, &p);
-         printf ("/jump/0/%d", mcnt);
-         break;
-
-       case succeed_n:
-         extract_number_and_incr (&mcnt, &p);
-         extract_number_and_incr (&mcnt2, &p);
-         printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2);
-         break;
-
-       case jump_n:
-         extract_number_and_incr (&mcnt, &p);
-         extract_number_and_incr (&mcnt2, &p);
-         printf ("/jump_n/0/%d/0/%d", mcnt, mcnt2);
-         break;
-
-       case set_number_at:
-         extract_number_and_incr (&mcnt, &p);
-         extract_number_and_incr (&mcnt2, &p);
-         printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2);
-         break;
-
-       case wordbound:
-         printf ("/wordbound");
-         break;
-
-       case notwordbound:
-         printf ("/notwordbound");
-         break;
-
-       case wordbeg:
-         printf ("/wordbeg");
-         break;
-
-       case wordend:
-         printf ("/wordend");
-
-#ifdef emacs
-       case before_dot:
-         printf ("/before_dot");
-         break;
-
-       case at_dot:
-         printf ("/at_dot");
-         break;
-
-       case after_dot:
-         printf ("/after_dot");
-         break;
-
-       case syntaxspec:
-         printf ("/syntaxspec");
-         mcnt = *p++;
-         printf ("/%d", mcnt);
-         break;
-
-       case notsyntaxspec:
-         printf ("/notsyntaxspec");
-         mcnt = *p++;
-         printf ("/%d", mcnt);
-         break;
-#endif /* emacs */
-
-       case wordchar:
-         printf ("/wordchar");
-         break;
-
-       case notwordchar:
-         printf ("/notwordchar");
-         break;
-
-       case begbuf:
-         printf ("/begbuf");
-         break;
-
-       case endbuf:
-         printf ("/endbuf");
-         break;
-
-       default:
-         printf ("?%d", *(p-1));
-       }
-    }
-  printf ("/\n");
-}
-
-
-void
-print_compiled_pattern (bufp)
-    struct re_pattern_buffer *bufp;
-{
-  unsigned char *buffer = bufp->buffer;
-
-  print_partial_compiled_pattern (buffer, buffer + bufp->used);
-  printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
-
-  if (bufp->fastmap_accurate && bufp->fastmap)
-    {
-      printf ("fastmap: ");
-      print_fastmap (bufp->fastmap);
-    }
-
-  printf ("re_nsub: %d\t", bufp->re_nsub);
-  printf ("regs_alloc: %d\t", bufp->regs_allocated);
-  printf ("can_be_null: %d\t", bufp->can_be_null);
-  printf ("newline_anchor: %d\n", bufp->newline_anchor);
-  printf ("no_sub: %d\t", bufp->no_sub);
-  printf ("not_bol: %d\t", bufp->not_bol);
-  printf ("not_eol: %d\t", bufp->not_eol);
-  printf ("syntax: %d\n", bufp->syntax);
-  /* Perhaps we should print the translate table?  */
-}
-
-
-void
-print_double_string (where, string1, size1, string2, size2)
-    const char *where;
-    const char *string1;
-    const char *string2;
-    int size1;
-    int size2;
-{
-  unsigned this_char;
-
-  if (where == NULL)
-    printf ("(null)");
-  else
-    {
-      if (FIRST_STRING_P (where))
-       {
-         for (this_char = where - string1; this_char < size1; this_char++)
-           printchar (string1[this_char]);
-
-         where = string2;
-       }
-
-      for (this_char = where - string2; this_char < size2; this_char++)
-       printchar (string2[this_char]);
-    }
-}
-
-#else /* not DEBUG */
-
-#undef assert
-#define assert(e)
-
-#define DEBUG_STATEMENT(e)
-#define DEBUG_PRINT1(x)
-#define DEBUG_PRINT2(x1, x2)
-#define DEBUG_PRINT3(x1, x2, x3)
-#define DEBUG_PRINT4(x1, x2, x3, x4)
-#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
-#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
-
-#endif /* not DEBUG */
-\f
-/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
-   also be assigned to arbitrarily: each pattern buffer stores its own
-   syntax, so it can be changed between regex compilations.  */
-reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
-
-
-/* Specify the precise syntax of regexps for compilation.  This provides
-   for compatibility for various utilities which historically have
-   different, incompatible syntaxes.
-
-   The argument SYNTAX is a bit mask comprised of the various bits
-   defined in regex.h.  We return the old syntax.  */
-
-reg_syntax_t
-re_set_syntax (syntax)
-    reg_syntax_t syntax;
-{
-  reg_syntax_t ret = re_syntax_options;
-
-  re_syntax_options = syntax;
-  return ret;
-}
-\f
-/* This table gives an error message for each of the error codes listed
-   in regex.h.  Obviously the order here has to be same as there.  */
-
-static const char *re_error_msg[] =
-  { NULL,                                      /* REG_NOERROR */
-    "No match",                                        /* REG_NOMATCH */
-    "Invalid regular expression",              /* REG_BADPAT */
-    "Invalid collation character",             /* REG_ECOLLATE */
-    "Invalid character class name",            /* REG_ECTYPE */
-    "Trailing backslash",                      /* REG_EESCAPE */
-    "Invalid back reference",                  /* REG_ESUBREG */
-    "Unmatched [ or [^",                       /* REG_EBRACK */
-    "Unmatched ( or \\(",                      /* REG_EPAREN */
-    "Unmatched \\{",                           /* REG_EBRACE */
-    "Invalid content of \\{\\}",               /* REG_BADBR */
-    "Invalid range end",                       /* REG_ERANGE */
-    "Memory exhausted",                                /* REG_ESPACE */
-    "Invalid preceding regular expression",    /* REG_BADRPT */
-    "Premature end of regular expression",     /* REG_EEND */
-    "Regular expression too big",              /* REG_ESIZE */
-    "Unmatched ) or \\)",                      /* REG_ERPAREN */
-  };
-\f
-/* Subroutine declarations and macros for regex_compile.  */
-
-static void store_op1 (), store_op2 ();
-static void insert_op1 (), insert_op2 ();
-static boolean at_begline_loc_p (), at_endline_loc_p ();
-static boolean group_in_compile_stack ();
-static reg_errcode_t compile_range ();
-
-/* Fetch the next character in the uncompiled pattern---translating it
-   if necessary.  Also cast from a signed character in the constant
-   string passed to us by the user to an unsigned char that we can use
-   as an array index (in, e.g., `translate').  */
-#define PATFETCH(c)                                                    \
-  do {if (p == pend) return REG_EEND;                                  \
-    c = (unsigned char) *p++;                                          \
-    if (translate) c = translate[c];                                   \
-  } while (0)
-
-/* Fetch the next character in the uncompiled pattern, with no
-   translation.  */
-#define PATFETCH_RAW(c)                                                        \
-  do {if (p == pend) return REG_EEND;                                  \
-    c = (unsigned char) *p++;                                          \
-  } while (0)
-
-/* Go backwards one character in the pattern.  */
-#define PATUNFETCH p--
-
-
-/* If `translate' is non-null, return translate[D], else just D.  We
-   cast the subscript to translate because some data is declared as
-   `char *', to avoid warnings when a string constant is passed.  But
-   when we use a character as a subscript we must make it unsigned.  */
-#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
-
-
-/* Macros for outputting the compiled pattern into `buffer'.  */
-
-/* If the buffer isn't allocated when it comes in, use this.  */
-#define INIT_BUF_SIZE  32
-
-/* Make sure we have at least N more bytes of space in buffer.  */
-#define GET_BUFFER_SPACE(n)                                            \
-    while (b - bufp->buffer + (n) > bufp->allocated)                   \
-      EXTEND_BUFFER ()
-
-/* Make sure we have one more byte of buffer space and then add C to it.  */
-#define BUF_PUSH(c)                                                    \
-  do {                                                                 \
-    GET_BUFFER_SPACE (1);                                              \
-    *b++ = (unsigned char) (c);                                                \
-  } while (0)
-
-
-/* Ensure we have two more bytes of buffer space and then append C1 and C2.  */
-#define BUF_PUSH_2(c1, c2)                                             \
-  do {                                                                 \
-    GET_BUFFER_SPACE (2);                                              \
-    *b++ = (unsigned char) (c1);                                       \
-    *b++ = (unsigned char) (c2);                                       \
-  } while (0)
-
-
-/* As with BUF_PUSH_2, except for three bytes.  */
-#define BUF_PUSH_3(c1, c2, c3)                                         \
-  do {                                                                 \
-    GET_BUFFER_SPACE (3);                                              \
-    *b++ = (unsigned char) (c1);                                       \
-    *b++ = (unsigned char) (c2);                                       \
-    *b++ = (unsigned char) (c3);                                       \
-  } while (0)
-
-
-/* Store a jump with opcode OP at LOC to location TO.  We store a
-   relative address offset by the three bytes the jump itself occupies.  */
-#define STORE_JUMP(op, loc, to) \
-  store_op1 (op, loc, (to) - (loc) - 3)
-
-/* Likewise, for a two-argument jump.  */
-#define STORE_JUMP2(op, loc, to, arg) \
-  store_op2 (op, loc, (to) - (loc) - 3, arg)
-
-/* Like `STORE_JUMP', but for inserting.  Assume `b' is the buffer end.  */
-#define INSERT_JUMP(op, loc, to) \
-  insert_op1 (op, loc, (to) - (loc) - 3, b)
-
-/* Like `STORE_JUMP2', but for inserting.  Assume `b' is the buffer end.  */
-#define INSERT_JUMP2(op, loc, to, arg) \
-  insert_op2 (op, loc, (to) - (loc) - 3, arg, b)
-
-
-/* This is not an arbitrary limit: the arguments which represent offsets
-   into the pattern are two bytes long.  So if 2^16 bytes turns out to
-   be too small, many things would have to change.  */
-#define MAX_BUF_SIZE (1L << 16)
-
-
-/* Extend the buffer by twice its current size via realloc and
-   reset the pointers that pointed into the old block to point to the
-   correct places in the new one.  If extending the buffer results in it
-   being larger than MAX_BUF_SIZE, then flag memory exhausted.  */
-#define EXTEND_BUFFER()                                                        \
-  do {                                                                         \
-    unsigned char *old_buffer = bufp->buffer;                          \
-    if (bufp->allocated == MAX_BUF_SIZE)                               \
-      return REG_ESIZE;                                                        \
-    bufp->allocated <<= 1;                                             \
-    if (bufp->allocated > MAX_BUF_SIZE)                                        \
-      bufp->allocated = MAX_BUF_SIZE;                                  \
-    bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
-    if (bufp->buffer == NULL)                                          \
-      return REG_ESPACE;                                               \
-    /* If the buffer moved, move all the pointers into it.  */         \
-    if (old_buffer != bufp->buffer)                                    \
-      {                                                                        \
-       b = (b - old_buffer) + bufp->buffer;                            \
-       begalt = (begalt - old_buffer) + bufp->buffer;                  \
-       if (fixup_alt_jump)                                             \
-         fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
-       if (laststart)                                                  \
-         laststart = (laststart - old_buffer) + bufp->buffer;          \
-       if (pending_exact)                                              \
-         pending_exact = (pending_exact - old_buffer) + bufp->buffer;  \
-      }                                                                        \
-  } while (0)
-
-
-/* Since we have one byte reserved for the register number argument to
-   {start,stop}_memory, the maximum number of groups we can report
-   things about is what fits in that byte.  */
-#define MAX_REGNUM 255
-
-/* But patterns can have more than `MAX_REGNUM' registers.  We just
-   ignore the excess.  */
-typedef unsigned regnum_t;
-
-
-/* Macros for the compile stack.  */
-
-/* Since offsets can go either forwards or backwards, this type needs to
-   be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1.  */
-typedef int pattern_offset_t;
-
-typedef struct
-{
-  pattern_offset_t begalt_offset;
-  pattern_offset_t fixup_alt_jump;
-  pattern_offset_t inner_group_offset;
-  pattern_offset_t laststart_offset;
-  regnum_t regnum;
-} compile_stack_elt_t;
-
-
-typedef struct
-{
-  compile_stack_elt_t *stack;
-  unsigned size;
-  unsigned avail;                      /* Offset of next open position.  */
-} compile_stack_type;
-
-
-#define INIT_COMPILE_STACK_SIZE 32
-
-#define COMPILE_STACK_EMPTY  (compile_stack.avail == 0)
-#define COMPILE_STACK_FULL  (compile_stack.avail == compile_stack.size)
-
-/* The next available element.  */
-#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
-
-
-/* Set the bit for character C in a list.  */
-#define SET_LIST_BIT(c)                               \
-  (b[((unsigned char) (c)) / BYTEWIDTH]               \
-   |= 1 << (((unsigned char) c) % BYTEWIDTH))
-
-
-/* Get the next unsigned number in the uncompiled pattern.  */
-#define GET_UNSIGNED_NUMBER(num)                                       \
-  { if (p != pend)                                                     \
-     {                                                                 \
-       PATFETCH (c);                                                   \
-       while (ISDIGIT (c))                                             \
-        {                                                              \
-          if (num < 0)                                                 \
-             num = 0;                                                  \
-          num = num * 10 + c - '0';                                    \
-          if (p == pend)                                               \
-             break;                                                    \
-          PATFETCH (c);                                                \
-        }                                                              \
-       }                                                               \
-    }
-
-#define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
-
-#define IS_CHAR_CLASS(string)                                          \
-   (STREQ (string, "alpha") || STREQ (string, "upper")                 \
-    || STREQ (string, "lower") || STREQ (string, "digit")              \
-    || STREQ (string, "alnum") || STREQ (string, "xdigit")             \
-    || STREQ (string, "space") || STREQ (string, "print")              \
-    || STREQ (string, "punct") || STREQ (string, "graph")              \
-    || STREQ (string, "cntrl") || STREQ (string, "blank"))
-\f
-/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
-   Returns one of error codes defined in `regex.h', or zero for success.
-
-   Assumes the `allocated' (and perhaps `buffer') and `translate'
-   fields are set in BUFP on entry.
-
-   If it succeeds, results are put in BUFP (if it returns an error, the
-   contents of BUFP are undefined):
-     `buffer' is the compiled pattern;
-     `syntax' is set to SYNTAX;
-     `used' is set to the length of the compiled pattern;
-     `fastmap_accurate' is zero;
-     `re_nsub' is the number of subexpressions in PATTERN;
-     `not_bol' and `not_eol' are zero;
-
-   The `fastmap' and `newline_anchor' fields are neither
-   examined nor set.  */
-
-static reg_errcode_t
-regex_compile (pattern, size, syntax, bufp)
-     const char *pattern;
-     int size;
-     reg_syntax_t syntax;
-     struct re_pattern_buffer *bufp;
-{
-  /* We fetch characters from PATTERN here.  Even though PATTERN is
-     `char *' (i.e., signed), we declare these variables as unsigned, so
-     they can be reliably used as array indices.  */
-  register unsigned char c, c1;
-
-  /* A random temporary spot in PATTERN.  */
-  const char *p1;
-
-  /* Points to the end of the buffer, where we should append.  */
-  register unsigned char *b;
-
-  /* Keeps track of unclosed groups.  */
-  compile_stack_type compile_stack;
-
-  /* Points to the current (ending) position in the pattern.  */
-  const char *p = pattern;
-  const char *pend = pattern + size;
-
-  /* How to translate the characters in the pattern.  */
-  char *translate = bufp->translate;
-
-  /* Address of the count-byte of the most recently inserted `exactn'
-     command.  This makes it possible to tell if a new exact-match
-     character can be added to that command or if the character requires
-     a new `exactn' command.  */
-  unsigned char *pending_exact = 0;
-
-  /* Address of start of the most recently finished expression.
-     This tells, e.g., postfix * where to find the start of its
-     operand.  Reset at the beginning of groups and alternatives.  */
-  unsigned char *laststart = 0;
-
-  /* Address of beginning of regexp, or inside of last group.  */
-  unsigned char *begalt;
-
-  /* Place in the uncompiled pattern (i.e., the {) to
-     which to go back if the interval is invalid.  */
-  const char *beg_interval;
-
-  /* Address of the place where a forward jump should go to the end of
-     the containing expression.  Each alternative of an `or' -- except the
-     last -- ends with a forward jump of this sort.  */
-  unsigned char *fixup_alt_jump = 0;
-
-  /* Counts open-groups as they are encountered.  Remembered for the
-     matching close-group on the compile stack, so the same register
-     number is put in the stop_memory as the start_memory.  */
-  regnum_t regnum = 0;
-
-#ifdef DEBUG
-  DEBUG_PRINT1 ("\nCompiling pattern: ");
-  if (debug)
-    {
-      unsigned debug_count;
-
-      for (debug_count = 0; debug_count < size; debug_count++)
-       printchar (pattern[debug_count]);
-      putchar ('\n');
-    }
-#endif /* DEBUG */
-
-  /* Initialize the compile stack.  */
-  compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
-  if (compile_stack.stack == NULL)
-    return REG_ESPACE;
-
-  compile_stack.size = INIT_COMPILE_STACK_SIZE;
-  compile_stack.avail = 0;
-
-  /* Initialize the pattern buffer.  */
-  bufp->syntax = syntax;
-  bufp->fastmap_accurate = 0;
-  bufp->not_bol = bufp->not_eol = 0;
-
-  /* Set `used' to zero, so that if we return an error, the pattern
-     printer (for debugging) will think there's no pattern.  We reset it
-     at the end.  */
-  bufp->used = 0;
-
-  /* Always count groups, whether or not bufp->no_sub is set.  */
-  bufp->re_nsub = 0;
-
-#if !defined (emacs) && !defined (SYNTAX_TABLE)
-  /* Initialize the syntax table.  */
-   init_syntax_once ();
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Make sure noone compiles this code with a C++ compiler.  */
+#ifdef __cplusplus
+# error "This is C code, use a C compiler"
+#endif
+
+#ifdef _LIBC
+/* We have to keep the namespace clean.  */
+# define regfree(preg) __regfree (preg)
+# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
+# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
+# define regerror(errcode, preg, errbuf, errbuf_size) \
+       __regerror(errcode, preg, errbuf, errbuf_size)
+# define re_set_registers(bu, re, nu, st, en) \
+       __re_set_registers (bu, re, nu, st, en)
+# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
+       __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+# define re_match(bufp, string, size, pos, regs) \
+       __re_match (bufp, string, size, pos, regs)
+# define re_search(bufp, string, size, startpos, range, regs) \
+       __re_search (bufp, string, size, startpos, range, regs)
+# define re_compile_pattern(pattern, length, bufp) \
+       __re_compile_pattern (pattern, length, bufp)
+# define re_set_syntax(syntax) __re_set_syntax (syntax)
+# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
+       __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
+# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
+
+# include "../locale/localeinfo.h"
+#endif
+
+#if defined (_MSC_VER)
+#include <stdio.h> /* for size_t */
+#endif
+
+/* On some systems, limits.h sets RE_DUP_MAX to a lower value than
+   GNU regex allows.  Include it before <regex.h>, which correctly
+   #undefs RE_DUP_MAX and sets it to the right value.  */
+#include <limits.h>
+
+#ifdef GAWK
+#undef alloca
+#define alloca alloca_is_bad_you_should_never_use_it
+#endif
+#include <regex.h>
+#include "regex_internal.h"
+
+#include "regex_internal.c"
+#ifdef GAWK
+#define bool int
+#define true (1)
+#define false (0)
+#endif
+#include "regcomp.c"
+#include "regexec.c"
+
+/* Binary backward compatibility.  */
+#if _LIBC
+# include <shlib-compat.h>
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
+link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
+int re_max_failures = 2000;
+# endif
 #endif
-
-  if (bufp->allocated == 0)
-    {
-      if (bufp->buffer)
-       { /* If zero allocated, but buffer is non-null, try to realloc
-            enough space.  This loses if buffer's address is bogus, but
-            that is the user's responsibility.  */
-         RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
-       }
-      else
-       { /* Caller did not allocate a buffer.  Do it for them.  */
-         bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
-       }
-      if (!bufp->buffer) return REG_ESPACE;
-
-      bufp->allocated = INIT_BUF_SIZE;
-    }
-
-  begalt = b = bufp->buffer;
-
-  /* Loop through the uncompiled pattern until we're at the end.  */
-  while (p != pend)
-    {
-      PATFETCH (c);
-
-      switch (c)
-       {
-       case '^':
-         {
-           if (   /* If at start of pattern, it's an operator.  */
-                  p == pattern + 1
-                  /* If context independent, it's an operator.  */
-               || syntax & RE_CONTEXT_INDEP_ANCHORS
-                  /* Otherwise, depends on what's come before.  */
-               || at_begline_loc_p (pattern, p, syntax))
-             BUF_PUSH (begline);
-           else
-             goto normal_char;
-         }
-         break;
-
-
-       case '$':
-         {
-           if (   /* If at end of pattern, it's an operator.  */
-                  p == pend
-                  /* If context independent, it's an operator.  */
-               || syntax & RE_CONTEXT_INDEP_ANCHORS
-                  /* Otherwise, depends on what's next.  */
-               || at_endline_loc_p (p, pend, syntax))
-              BUF_PUSH (endline);
-            else
-              goto normal_char;
-          }
-          break;
-
-
-       case '+':
-       case '?':
-         if ((syntax & RE_BK_PLUS_QM)
-             || (syntax & RE_LIMITED_OPS))
-           goto normal_char;
-       handle_plus:
-       case '*':
-         /* If there is no previous pattern... */
-         if (!laststart)
-           {
-             if (syntax & RE_CONTEXT_INVALID_OPS)
-               return REG_BADRPT;
-             else if (!(syntax & RE_CONTEXT_INDEP_OPS))
-               goto normal_char;
-           }
-
-         {
-           /* Are we optimizing this jump?  */
-           boolean keep_string_p = false;
-
-           /* 1 means zero (many) matches is allowed.  */
-           char zero_times_ok = 0, many_times_ok = 0;
-
-           /* If there is a sequence of repetition chars, collapse it
-              down to just one (the right one).  We can't combine
-              interval operators with these because of, e.g., `a{2}*',
-              which should only match an even number of `a's.  */
-
-           for (;;)
-             {
-               zero_times_ok |= c != '+';
-               many_times_ok |= c != '?';
-
-               if (p == pend)
-                 break;
-
-               PATFETCH (c);
-
-               if (c == '*'
-                   || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
-                 ;
-
-               else if (syntax & RE_BK_PLUS_QM  &&  c == '\\')
-                 {
-                   if (p == pend) return REG_EESCAPE;
-
-                   PATFETCH (c1);
-                   if (!(c1 == '+' || c1 == '?'))
-                     {
-                       PATUNFETCH;
-                       PATUNFETCH;
-                       break;
-                     }
-
-                   c = c1;
-                 }
-               else
-                 {
-                   PATUNFETCH;
-                   break;
-                 }
-
-               /* If we get here, we found another repeat character.  */
-              }
-
-           /* Star, etc. applied to an empty pattern is equivalent
-              to an empty pattern.  */
-           if (!laststart)
-             break;
-
-           /* Now we know whether or not zero matches is allowed
-              and also whether or not two or more matches is allowed.  */
-           if (many_times_ok)
-             { /* More than one repetition is allowed, so put in at the
-                  end a backward relative jump from `b' to before the next
-                  jump we're going to put in below (which jumps from
-                  laststart to after this jump).
-
-                  But if we are at the `*' in the exact sequence `.*\n',
-                  insert an unconditional jump backwards to the .,
-                  instead of the beginning of the loop.  This way we only
-                  push a failure point once, instead of every time
-                  through the loop.  */
-               assert (p - 1 > pattern);
-
-               /* Allocate the space for the jump.  */
-               GET_BUFFER_SPACE (3);
-
-               /* We know we are not at the first character of the pattern,
-                  because laststart was nonzero.  And we've already
-                  incremented `p', by the way, to be the character after
-                  the `*'.  Do we have to do something analogous here
-                  for null bytes, because of RE_DOT_NOT_NULL?  */
-               if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
-                   && zero_times_ok
-                   && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
-                   && !(syntax & RE_DOT_NEWLINE))
-                 { /* We have .*\n.  */
-                   STORE_JUMP (jump, b, laststart);
-                   keep_string_p = true;
-                 }
-               else
-                 /* Anything else.  */
-                 STORE_JUMP (maybe_pop_jump, b, laststart - 3);
-
-               /* We've added more stuff to the buffer.  */
-               b += 3;
-             }
-
-           /* On failure, jump from laststart to b + 3, which will be the
-              end of the buffer after this jump is inserted.  */
-           GET_BUFFER_SPACE (3);
-           INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
-                                      : on_failure_jump,
-                        laststart, b + 3);
-           pending_exact = 0;
-           b += 3;
-
-           if (!zero_times_ok)
-             {
-               /* At least one repetition is required, so insert a
-                  `dummy_failure_jump' before the initial
-                  `on_failure_jump' instruction of the loop. This
-                  effects a skip over that instruction the first time
-                  we hit that loop.  */
-               GET_BUFFER_SPACE (3);
-               INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
-               b += 3;
-             }
-           }
-         break;
-
-
-       case '.':
-         laststart = b;
-         BUF_PUSH (anychar);
-         break;
-
-
-       case '[':
-         {
-           boolean had_char_class = false;
-
-           if (p == pend) return REG_EBRACK;
-
-           /* Ensure that we have enough space to push a charset: the
-              opcode, the length count, and the bitset; 34 bytes in all.  */
-           GET_BUFFER_SPACE (34);
-
-           laststart = b;
-
-           /* We test `*p == '^' twice, instead of using an if
-              statement, so we only need one BUF_PUSH.  */
-           BUF_PUSH (*p == '^' ? charset_not : charset);
-           if (*p == '^')
-             p++;
-
-           /* Remember the first position in the bracket expression.  */
-           p1 = p;
-
-           /* Push the number of bytes in the bitmap.  */
-           BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
-
-           /* Clear the whole map.  */
-           bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
-
-           /* charset_not matches newline according to a syntax bit.  */
-           if ((re_opcode_t) b[-2] == charset_not
-               && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
-             SET_LIST_BIT ('\n');
-
-           /* Read in characters and ranges, setting map bits.  */
-           for (;;)
-             {
-               if (p == pend) return REG_EBRACK;
-
-               PATFETCH (c);
-
-               /* \ might escape characters inside [...] and [^...].  */
-               if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
-                 {
-                   if (p == pend) return REG_EESCAPE;
-
-                   PATFETCH (c1);
-                   SET_LIST_BIT (c1);
-                   continue;
-                 }
-
-               /* Could be the end of the bracket expression.  If it's
-                  not (i.e., when the bracket expression is `[]' so
-                  far), the ']' character bit gets set way below.  */
-               if (c == ']' && p != p1 + 1)
-                 break;
-
-               /* Look ahead to see if it's a range when the last thing
-                  was a character class.  */
-               if (had_char_class && c == '-' && *p != ']')
-                 return REG_ERANGE;
-
-               /* Look ahead to see if it's a range when the last thing
-                  was a character: if this is a hyphen not at the
-                  beginning or the end of a list, then it's the range
-                  operator.  */
-               if (c == '-'
-                   && !(p - 2 >= pattern && p[-2] == '[')
-                   && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
-                   && *p != ']')
-                 {
-                   reg_errcode_t ret
-                     = compile_range (&p, pend, translate, syntax, b);
-                   if (ret != REG_NOERROR) return ret;
-                 }
-
-               else if (p[0] == '-' && p[1] != ']')
-                 { /* This handles ranges made up of characters only.  */
-                   reg_errcode_t ret;
-
-                   /* Move past the `-'.  */
-                   PATFETCH (c1);
-
-                   ret = compile_range (&p, pend, translate, syntax, b);
-                   if (ret != REG_NOERROR) return ret;
-                 }
-
-               /* See if we're at the beginning of a possible character
-                  class.  */
-
-               else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
-                 { /* Leave room for the null.  */
-                   char str[CHAR_CLASS_MAX_LENGTH + 1];
-
-                   PATFETCH (c);
-                   c1 = 0;
-
-                   /* If pattern is `[[:'.  */
-                   if (p == pend) return REG_EBRACK;
-
-                   for (;;)
-                     {
-                       PATFETCH (c);
-                       if (c == ':' || c == ']' || p == pend
-                           || c1 == CHAR_CLASS_MAX_LENGTH)
-                         break;
-                       str[c1++] = c;
-                     }
-                   str[c1] = '\0';
-
-                   /* If isn't a word bracketed by `[:' and:`]':
-                      undo the ending character, the letters, and leave
-                      the leading `:' and `[' (but set bits for them).  */
-                   if (c == ':' && *p == ']')
-                     {
-                       int ch;
-                       boolean is_alnum = STREQ (str, "alnum");
-                       boolean is_alpha = STREQ (str, "alpha");
-                       boolean is_blank = STREQ (str, "blank");
-                       boolean is_cntrl = STREQ (str, "cntrl");
-                       boolean is_digit = STREQ (str, "digit");
-                       boolean is_graph = STREQ (str, "graph");
-                       boolean is_lower = STREQ (str, "lower");
-                       boolean is_print = STREQ (str, "print");
-                       boolean is_punct = STREQ (str, "punct");
-                       boolean is_space = STREQ (str, "space");
-                       boolean is_upper = STREQ (str, "upper");
-                       boolean is_xdigit = STREQ (str, "xdigit");
-
-                       if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
-
-                       /* Throw away the ] at the end of the character
-                          class.  */
-                       PATFETCH (c);
-
-                       if (p == pend) return REG_EBRACK;
-
-                       for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
-                         {
-                           if (   (is_alnum  && ISALNUM (ch))
-                               || (is_alpha  && ISALPHA (ch))
-                               || (is_blank  && ISBLANK (ch))
-                               || (is_cntrl  && ISCNTRL (ch))
-                               || (is_digit  && ISDIGIT (ch))
-                               || (is_graph  && ISGRAPH (ch))
-                               || (is_lower  && ISLOWER (ch))
-                               || (is_print  && ISPRINT (ch))
-                               || (is_punct  && ISPUNCT (ch))
-                               || (is_space  && ISSPACE (ch))
-                               || (is_upper  && ISUPPER (ch))
-                               || (is_xdigit && ISXDIGIT (ch)))
-                           SET_LIST_BIT (ch);
-                         }
-                       had_char_class = true;
-                     }
-                   else
-                     {
-                       c1++;
-                       while (c1--)
-                         PATUNFETCH;
-                       SET_LIST_BIT ('[');
-                       SET_LIST_BIT (':');
-                       had_char_class = false;
-                     }
-                 }
-               else
-                 {
-                   had_char_class = false;
-                   SET_LIST_BIT (c);
-                 }
-             }
-
-           /* Discard any (non)matching list bytes that are all 0 at the
-              end of the map.  Decrease the map-length byte too.  */
-           while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
-             b[-1]--;
-           b += b[-1];
-         }
-         break;
-
-
-       case '(':
-         if (syntax & RE_NO_BK_PARENS)
-           goto handle_open;
-         else
-           goto normal_char;
-
-
-       case ')':
-         if (syntax & RE_NO_BK_PARENS)
-           goto handle_close;
-         else
-           goto normal_char;
-
-
-       case '\n':
-         if (syntax & RE_NEWLINE_ALT)
-           goto handle_alt;
-         else
-           goto normal_char;
-
-
-       case '|':
-         if (syntax & RE_NO_BK_VBAR)
-           goto handle_alt;
-         else
-           goto normal_char;
-
-
-       case '{':
-          if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
-            goto handle_interval;
-          else
-            goto normal_char;
-
-
-       case '\\':
-         if (p == pend) return REG_EESCAPE;
-
-         /* Do not translate the character after the \, so that we can
-            distinguish, e.g., \B from \b, even if we normally would
-            translate, e.g., B to b.  */
-         PATFETCH_RAW (c);
-
-         switch (c)
-           {
-           case '(':
-             if (syntax & RE_NO_BK_PARENS)
-               goto normal_backslash;
-
-           handle_open:
-             bufp->re_nsub++;
-             regnum++;
-
-             if (COMPILE_STACK_FULL)
-               {
-                 RETALLOC (compile_stack.stack, compile_stack.size << 1,
-                           compile_stack_elt_t);
-                 if (compile_stack.stack == NULL) return REG_ESPACE;
-
-                 compile_stack.size <<= 1;
-               }
-
-             /* These are the values to restore when we hit end of this
-                group.  They are all relative offsets, so that if the
-                whole pattern moves because of realloc, they will still
-                be valid.  */
-             COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
-             COMPILE_STACK_TOP.fixup_alt_jump
-               = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
-             COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
-             COMPILE_STACK_TOP.regnum = regnum;
-
-             /* We will eventually replace the 0 with the number of
-                groups inner to this one.  But do not push a
-                start_memory for groups beyond the last one we can
-                represent in the compiled pattern.  */
-             if (regnum <= MAX_REGNUM)
-               {
-                 COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
-                 BUF_PUSH_3 (start_memory, regnum, 0);
-               }
-
-             compile_stack.avail++;
-
-             fixup_alt_jump = 0;
-             laststart = 0;
-             begalt = b;
-             /* If we've reached MAX_REGNUM groups, then this open
-                won't actually generate any code, so we'll have to
-                clear pending_exact explicitly.  */
-             pending_exact = 0;
-             break;
-
-
-           case ')':
-             if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
-
-             if (COMPILE_STACK_EMPTY)
-             {
-               if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
-                 goto normal_backslash;
-               else
-                 return REG_ERPAREN;
-             }
-
-           handle_close:
-             if (fixup_alt_jump)
-               { /* Push a dummy failure point at the end of the
-                    alternative for a possible future
-                    `pop_failure_jump' to pop.  See comments at
-                    `push_dummy_failure' in `re_match_2'.  */
-                 BUF_PUSH (push_dummy_failure);
-
-                 /* We allocated space for this jump when we assigned
-                    to `fixup_alt_jump', in the `handle_alt' case below.  */
-                 STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
-               }
-
-             /* See similar code for backslashed left paren above.  */
-             if (COMPILE_STACK_EMPTY)
-             {
-               if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
-                 goto normal_char;
-               else
-                 return REG_ERPAREN;
-             }
-
-             /* Since we just checked for an empty stack above, this
-                ``can't happen''.  */
-             assert (compile_stack.avail != 0);
-             {
-               /* We don't just want to restore into `regnum', because
-                  later groups should continue to be numbered higher,
-                  as in `(ab)c(de)' -- the second group is #2.  */
-               regnum_t this_group_regnum;
-
-               compile_stack.avail--;
-               begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
-               fixup_alt_jump
-                 = COMPILE_STACK_TOP.fixup_alt_jump
-                   ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
-                   : 0;
-               laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
-               this_group_regnum = COMPILE_STACK_TOP.regnum;
-               /* If we've reached MAX_REGNUM groups, then this open
-                  won't actually generate any code, so we'll have to
-                  clear pending_exact explicitly.  */
-               pending_exact = 0;
-
-               /* We're at the end of the group, so now we know how many
-                  groups were inside this one.  */
-               if (this_group_regnum <= MAX_REGNUM)
-                 {
-                   unsigned char *inner_group_loc
-                     = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
-
-                   *inner_group_loc = regnum - this_group_regnum;
-                   BUF_PUSH_3 (stop_memory, this_group_regnum,
-                               regnum - this_group_regnum);
-                 }
-             }
-             break;
-
-
-           case '|':                                   /* `\|'.  */
-             if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
-               goto normal_backslash;
-           handle_alt:
-             if (syntax & RE_LIMITED_OPS)
-               goto normal_char;
-
-             /* Insert before the previous alternative a jump which
-                jumps to this alternative if the former fails.  */
-             GET_BUFFER_SPACE (3);
-             INSERT_JUMP (on_failure_jump, begalt, b + 6);
-             pending_exact = 0;
-             b += 3;
-
-             /* The alternative before this one has a jump after it
-                which gets executed if it gets matched.  Adjust that
-                jump so it will jump to this alternative's analogous
-                jump (put in below, which in turn will jump to the next
-                (if any) alternative's such jump, etc.).  The last such
-                jump jumps to the correct final destination.  A picture:
-                         _____ _____
-                         |   | |   |
-                         |   v |   v
-                        a | b   | c
-
-                If we are at `b', then fixup_alt_jump right now points to a
-                three-byte space after `a'.  We'll put in the jump, set
-                fixup_alt_jump to right after `b', and leave behind three
-                bytes which we'll fill in when we get to after `c'.  */
-
-             if (fixup_alt_jump)
-               STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
-
-             /* Mark and leave space for a jump after this alternative,
-                to be filled in later either by next alternative or
-                when know we're at the end of a series of alternatives.  */
-             fixup_alt_jump = b;
-             GET_BUFFER_SPACE (3);
-             b += 3;
-
-             laststart = 0;
-             begalt = b;
-             break;
-
-
-           case '{':
-             /* If \{ is a literal.  */
-             if (!(syntax & RE_INTERVALS)
-                    /* If we're at `\{' and it's not the open-interval
-                       operator.  */
-                 || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
-                 || (p - 2 == pattern  &&  p == pend))
-               goto normal_backslash;
-
-           handle_interval:
-             {
-               /* If got here, then the syntax allows intervals.  */
-
-               /* At least (most) this many matches must be made.  */
-               int lower_bound = -1, upper_bound = -1;
-
-               beg_interval = p - 1;
-
-               if (p == pend)
-                 {
-                   if (syntax & RE_NO_BK_BRACES)
-                     goto unfetch_interval;
-                   else
-                     return REG_EBRACE;
-                 }
-
-               GET_UNSIGNED_NUMBER (lower_bound);
-
-               if (c == ',')
-                 {
-                   GET_UNSIGNED_NUMBER (upper_bound);
-                   if (upper_bound < 0) upper_bound = RE_DUP_MAX;
-                 }
-               else
-                 /* Interval such as `{1}' => match exactly once. */
-                 upper_bound = lower_bound;
-
-               if (lower_bound < 0 || upper_bound > RE_DUP_MAX
-                   || lower_bound > upper_bound)
-                 {
-                   if (syntax & RE_NO_BK_BRACES)
-                     goto unfetch_interval;
-                   else
-                     return REG_BADBR;
-                 }
-
-               if (!(syntax & RE_NO_BK_BRACES))
-                 {
-                   if (c != '\\') return REG_EBRACE;
-
-                   PATFETCH (c);
-                 }
-
-               if (c != '}')
-                 {
-                   if (syntax & RE_NO_BK_BRACES)
-                     goto unfetch_interval;
-                   else
-                     return REG_BADBR;
-                 }
-
-               /* We just parsed a valid interval.  */
-
-               /* If it's invalid to have no preceding re.  */
-               if (!laststart)
-                 {
-                   if (syntax & RE_CONTEXT_INVALID_OPS)
-                     return REG_BADRPT;
-                   else if (syntax & RE_CONTEXT_INDEP_OPS)
-                     laststart = b;
-                   else
-                     goto unfetch_interval;
-                 }
-
-               /* If the upper bound is zero, don't want to succeed at
-                  all; jump from `laststart' to `b + 3', which will be
-                  the end of the buffer after we insert the jump.  */
-                if (upper_bound == 0)
-                  {
-                    GET_BUFFER_SPACE (3);
-                    INSERT_JUMP (jump, laststart, b + 3);
-                    b += 3;
-                  }
-
-                /* Otherwise, we have a nontrivial interval.  When
-                   we're all done, the pattern will look like:
-                     set_number_at <jump count> <upper bound>
-                     set_number_at <succeed_n count> <lower bound>
-                     succeed_n <after jump addr> <succeed_n count>
-                     <body of loop>
-                     jump_n <succeed_n addr> <jump count>
-                   (The upper bound and `jump_n' are omitted if
-                   `upper_bound' is 1, though.)  */
-                else
-                  { /* If the upper bound is > 1, we need to insert
-                       more at the end of the loop.  */
-                    unsigned nbytes = 10 + (upper_bound > 1) * 10;
-
-                    GET_BUFFER_SPACE (nbytes);
-
-                    /* Initialize lower bound of the `succeed_n', even
-                       though it will be set during matching by its
-                       attendant `set_number_at' (inserted next),
-                       because `re_compile_fastmap' needs to know.
-                       Jump to the `jump_n' we might insert below.  */
-                    INSERT_JUMP2 (succeed_n, laststart,
-                                  b + 5 + (upper_bound > 1) * 5,
-                                  lower_bound);
-                    b += 5;
-
-                    /* Code to initialize the lower bound.  Insert
-                       before the `succeed_n'.  The `5' is the last two
-                       bytes of this `set_number_at', plus 3 bytes of
-                       the following `succeed_n'.  */
-                    insert_op2 (set_number_at, laststart, 5, lower_bound, b);
-                    b += 5;
-
-                    if (upper_bound > 1)
-                      { /* More than one repetition is allowed, so
-                           append a backward jump to the `succeed_n'
-                           that starts this interval.
-
-                           When we've reached this during matching,
-                           we'll have matched the interval once, so
-                           jump back only `upper_bound - 1' times.  */
-                        STORE_JUMP2 (jump_n, b, laststart + 5,
-                                     upper_bound - 1);
-                        b += 5;
-
-                        /* The location we want to set is the second
-                           parameter of the `jump_n'; that is `b-2' as
-                           an absolute address.  `laststart' will be
-                           the `set_number_at' we're about to insert;
-                           `laststart+3' the number to set, the source
-                           for the relative address.  But we are
-                           inserting into the middle of the pattern --
-                           so everything is getting moved up by 5.
-                           Conclusion: (b - 2) - (laststart + 3) + 5,
-                           i.e., b - laststart.
-
-                           We insert this at the beginning of the loop
-                           so that if we fail during matching, we'll
-                           reinitialize the bounds.  */
-                        insert_op2 (set_number_at, laststart, b - laststart,
-                                    upper_bound - 1, b);
-                        b += 5;
-                      }
-                  }
-               pending_exact = 0;
-               beg_interval = NULL;
-             }
-             break;
-
-           unfetch_interval:
-             /* If an invalid interval, match the characters as literals.  */
-              assert (beg_interval);
-              p = beg_interval;
-              beg_interval = NULL;
-
-              /* normal_char and normal_backslash need `c'.  */
-              PATFETCH (c);
-
-              if (!(syntax & RE_NO_BK_BRACES))
-                {
-                  if (p > pattern  &&  p[-1] == '\\')
-                    goto normal_backslash;
-                }
-              goto normal_char;
-
-#ifdef emacs
-           /* There is no way to specify the before_dot and after_dot
-              operators.  rms says this is ok.  --karl  */
-           case '=':
-             BUF_PUSH (at_dot);
-             break;
-
-           case 's':
-             laststart = b;
-             PATFETCH (c);
-             BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
-             break;
-
-           case 'S':
-             laststart = b;
-             PATFETCH (c);
-             BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
-             break;
-#endif /* emacs */
-
-
-           case 'w':
-             laststart = b;
-             BUF_PUSH (wordchar);
-             break;
-
-
-           case 'W':
-             laststart = b;
-             BUF_PUSH (notwordchar);
-             break;
-
-
-           case '<':
-             BUF_PUSH (wordbeg);
-             break;
-
-           case '>':
-             BUF_PUSH (wordend);
-             break;
-
-           case 'b':
-             BUF_PUSH (wordbound);
-             break;
-
-           case 'B':
-             BUF_PUSH (notwordbound);
-             break;
-
-           case '`':
-             BUF_PUSH (begbuf);
-             break;
-
-           case '\'':
-             BUF_PUSH (endbuf);
-             break;
-
-           case '1': case '2': case '3': case '4': case '5':
-           case '6': case '7': case '8': case '9':
-             if (syntax & RE_NO_BK_REFS)
-               goto normal_char;
-
-             c1 = c - '0';
-
-             if (c1 > regnum)
-               return REG_ESUBREG;
-
-             /* Can't back reference to a subexpression if inside of it.  */
-             if (group_in_compile_stack (compile_stack, c1))
-               goto normal_char;
-
-             laststart = b;
-             BUF_PUSH_2 (duplicate, c1);
-             break;
-
-
-           case '+':
-           case '?':
-             if (syntax & RE_BK_PLUS_QM)
-               goto handle_plus;
-             else
-               goto normal_backslash;
-
-           default:
-           normal_backslash:
-             /* You might think it would be useful for \ to mean
-                not to translate; but if we don't translate it
-                it will never match anything.  */
-             c = TRANSLATE (c);
-             goto normal_char;
-           }
-         break;
-
-
-       default:
-       /* Expects the character in `c'.  */
-       normal_char:
-             /* If no exactn currently being built.  */
-         if (!pending_exact
-
-             /* If last exactn not at current position.  */
-             || pending_exact + *pending_exact + 1 != b
-
-             /* We have only one byte following the exactn for the count.  */
-             || *pending_exact == (1 << BYTEWIDTH) - 1
-
-             /* If followed by a repetition operator.  */
-             || *p == '*' || *p == '^'
-             || ((syntax & RE_BK_PLUS_QM)
-                 ? *p == '\\' && (p[1] == '+' || p[1] == '?')
-                 : (*p == '+' || *p == '?'))
-             || ((syntax & RE_INTERVALS)
-                 && ((syntax & RE_NO_BK_BRACES)
-                     ? *p == '{'
-                     : (p[0] == '\\' && p[1] == '{'))))
-           {
-             /* Start building a new exactn.  */
-
-             laststart = b;
-
-             BUF_PUSH_2 (exactn, 0);
-             pending_exact = b - 1;
-           }
-
-         BUF_PUSH (c);
-         (*pending_exact)++;
-         break;
-       } /* switch (c) */
-    } /* while p != pend */
-
-
-  /* Through the pattern now.  */
-
-  if (fixup_alt_jump)
-    STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
-
-  if (!COMPILE_STACK_EMPTY)
-    return REG_EPAREN;
-
-  free (compile_stack.stack);
-
-  /* We have succeeded; set the length of the buffer.  */
-  bufp->used = b - bufp->buffer;
-
-#ifdef DEBUG
-  if (debug)
-    {
-      DEBUG_PRINT1 ("\nCompiled pattern: ");
-      print_compiled_pattern (bufp);
-    }
-#endif /* DEBUG */
-
-  return REG_NOERROR;
-} /* regex_compile */
-\f
-/* Subroutines for `regex_compile'.  */
-
-/* Store OP at LOC followed by two-byte integer parameter ARG.  */
-
-static void
-store_op1 (op, loc, arg)
-    re_opcode_t op;
-    unsigned char *loc;
-    int arg;
-{
-  *loc = (unsigned char) op;
-  STORE_NUMBER (loc + 1, arg);
-}
-
-
-/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2.  */
-
-static void
-store_op2 (op, loc, arg1, arg2)
-    re_opcode_t op;
-    unsigned char *loc;
-    int arg1, arg2;
-{
-  *loc = (unsigned char) op;
-  STORE_NUMBER (loc + 1, arg1);
-  STORE_NUMBER (loc + 3, arg2);
-}
-
-
-/* Copy the bytes from LOC to END to open up three bytes of space at LOC
-   for OP followed by two-byte integer parameter ARG.  */
-
-static void
-insert_op1 (op, loc, arg, end)
-    re_opcode_t op;
-    unsigned char *loc;
-    int arg;
-    unsigned char *end;
-{
-  register unsigned char *pfrom = end;
-  register unsigned char *pto = end + 3;
-
-  while (pfrom != loc)
-    *--pto = *--pfrom;
-
-  store_op1 (op, loc, arg);
-}
-
-
-/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2.  */
-
-static void
-insert_op2 (op, loc, arg1, arg2, end)
-    re_opcode_t op;
-    unsigned char *loc;
-    int arg1, arg2;
-    unsigned char *end;
-{
-  register unsigned char *pfrom = end;
-  register unsigned char *pto = end + 5;
-
-  while (pfrom != loc)
-    *--pto = *--pfrom;
-
-  store_op2 (op, loc, arg1, arg2);
-}
-
-
-/* P points to just after a ^ in PATTERN.  Return true if that ^ comes
-   after an alternative or a begin-subexpression.  We assume there is at
-   least one character before the ^.  */
-
-static boolean
-at_begline_loc_p (pattern, p, syntax)
-    const char *pattern, *p;
-    reg_syntax_t syntax;
-{
-  const char *prev = p - 2;
-  boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
-
-  return
-       /* After a subexpression?  */
-       (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
-       /* After an alternative?  */
-    || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
-}
-
-
-/* The dual of at_begline_loc_p.  This one is for $.  We assume there is
-   at least one character after the $, i.e., `P < PEND'.  */
-
-static boolean
-at_endline_loc_p (p, pend, syntax)
-    const char *p, *pend;
-    int syntax;
-{
-  const char *next = p;
-  boolean next_backslash = *next == '\\';
-  const char *next_next = p + 1 < pend ? p + 1 : NULL;
-
-  return
-       /* Before a subexpression?  */
-       (syntax & RE_NO_BK_PARENS ? *next == ')'
-       : next_backslash && next_next && *next_next == ')')
-       /* Before an alternative?  */
-    || (syntax & RE_NO_BK_VBAR ? *next == '|'
-       : next_backslash && next_next && *next_next == '|');
-}
-
-
-/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
-   false if it's not.  */
-
-static boolean
-group_in_compile_stack (compile_stack, regnum)
-    compile_stack_type compile_stack;
-    regnum_t regnum;
-{
-  int this_element;
-
-  for (this_element = compile_stack.avail - 1;
-       this_element >= 0;
-       this_element--)
-    if (compile_stack.stack[this_element].regnum == regnum)
-      return true;
-
-  return false;
-}
-
-
-/* Read the ending character of a range (in a bracket expression) from the
-   uncompiled pattern *P_PTR (which ends at PEND).  We assume the
-   starting character is in `P[-2]'.  (`P[-1]' is the character `-'.)
-   Then we set the translation of all bits between the starting and
-   ending characters (inclusive) in the compiled pattern B.
-
-   Return an error code.
-
-   We use these short variable names so we can use the same macros as
-   `regex_compile' itself.  */
-
-static reg_errcode_t
-compile_range (p_ptr, pend, translate, syntax, b)
-    const char **p_ptr, *pend;
-    char *translate;
-    reg_syntax_t syntax;
-    unsigned char *b;
-{
-  unsigned this_char;
-
-  const char *p = *p_ptr;
-  int range_start, range_end;
-
-  if (p == pend)
-    return REG_ERANGE;
-
-  /* Even though the pattern is a signed `char *', we need to fetch
-     with unsigned char *'s; if the high bit of the pattern character
-     is set, the range endpoints will be negative if we fetch using a
-     signed char *.
-
-     We also want to fetch the endpoints without translating them; the
-     appropriate translation is done in the bit-setting loop below.  */
-  range_start = ((unsigned char *) p)[-2];
-  range_end   = ((unsigned char *) p)[0];
-
-  /* Have to increment the pointer into the pattern string, so the
-     caller isn't still at the ending character.  */
-  (*p_ptr)++;
-
-  /* If the start is after the end, the range is empty.  */
-  if (range_start > range_end)
-    return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
-
-  /* Here we see why `this_char' has to be larger than an `unsigned
-     char' -- the range is inclusive, so if `range_end' == 0xff
-     (assuming 8-bit characters), we would otherwise go into an infinite
-     loop, since all characters <= 0xff.  */
-  for (this_char = range_start; this_char <= range_end; this_char++)
-    {
-      SET_LIST_BIT (TRANSLATE (this_char));
-    }
-
-  return REG_NOERROR;
-}
-\f
-/* Failure stack declarations and macros; both re_compile_fastmap and
-   re_match_2 use a failure stack.  These have to be macros because of
-   REGEX_ALLOCATE.  */
-
-
-/* Number of failure points for which to initially allocate space
-   when matching.  If this number is exceeded, we allocate more
-   space, so it is not a hard limit.  */
-#ifndef INIT_FAILURE_ALLOC
-#define INIT_FAILURE_ALLOC 5
-#endif
-
-/* Roughly the maximum number of failure points on the stack.  Would be
-   exactly that if always used MAX_FAILURE_SPACE each time we failed.
-   This is a variable only so users of regex can assign to it; we never
-   change it ourselves.  */
-int re_max_failures = 2000;
-
-typedef const unsigned char *fail_stack_elt_t;
-
-typedef struct
-{
-  fail_stack_elt_t *stack;
-  unsigned size;
-  unsigned avail;                      /* Offset of next open position.  */
-} fail_stack_type;
-
-#define FAIL_STACK_EMPTY()     (fail_stack.avail == 0)
-#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
-#define FAIL_STACK_FULL()      (fail_stack.avail == fail_stack.size)
-#define FAIL_STACK_TOP()       (fail_stack.stack[fail_stack.avail])
-
-
-/* Initialize `fail_stack'.  Do `return -2' if the alloc fails.  */
-
-#define INIT_FAIL_STACK()                                              \
-  do {                                                                 \
-    fail_stack.stack = (fail_stack_elt_t *)                            \
-      REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
-                                                                       \
-    if (fail_stack.stack == NULL)                                      \
-      return -2;                                                       \
-                                                                       \
-    fail_stack.size = INIT_FAILURE_ALLOC;                              \
-    fail_stack.avail = 0;                                              \
-  } while (0)
-
-
-/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
-
-   Return 1 if succeeds, and 0 if either ran out of memory
-   allocating space for it or it was already too large.
-
-   REGEX_REALLOCATE requires `destination' be declared.   */
-
-#define DOUBLE_FAIL_STACK(fail_stack)                                  \
-  ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS             \
-   ? 0                                                                 \
-   : ((fail_stack).stack = (fail_stack_elt_t *)                                \
-       REGEX_REALLOCATE ((fail_stack).stack,                           \
-         (fail_stack).size * sizeof (fail_stack_elt_t),                \
-         ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)),        \
-                                                                       \
-      (fail_stack).stack == NULL                                       \
-      ? 0                                                              \
-      : ((fail_stack).size <<= 1,                                      \
-        1)))
-
-
-/* Push PATTERN_OP on FAIL_STACK.
-
-   Return 1 if was able to do so and 0 if ran out of memory allocating
-   space to do so.  */
-#define PUSH_PATTERN_OP(pattern_op, fail_stack)                                \
-  ((FAIL_STACK_FULL ()                                                 \
-    && !DOUBLE_FAIL_STACK (fail_stack))                                        \
-    ? 0                                                                        \
-    : ((fail_stack).stack[(fail_stack).avail++] = pattern_op,          \
-       1))
-
-/* This pushes an item onto the failure stack.  Must be a four-byte
-   value.  Assumes the variable `fail_stack'.  Probably should only
-   be called from within `PUSH_FAILURE_POINT'.  */
-#define PUSH_FAILURE_ITEM(item)                                                \
-  fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
-
-/* The complement operation.  Assumes `fail_stack' is nonempty.  */
-#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
-
-/* Used to omit pushing failure point id's when we're not debugging.  */
-#ifdef DEBUG
-#define DEBUG_PUSH PUSH_FAILURE_ITEM
-#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
-#else
-#define DEBUG_PUSH(item)
-#define DEBUG_POP(item_addr)
-#endif
-
-
-/* Push the information about the state we will need
-   if we ever fail back to it.
-
-   Requires variables fail_stack, regstart, regend, reg_info, and
-   num_regs be declared.  DOUBLE_FAIL_STACK requires `destination' be
-   declared.
-
-   Does `return FAILURE_CODE' if runs out of memory.  */
-
-#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code)  \
-  do {                                                                 \
-    char *destination;                                                 \
-    /* Must be int, so when we don't save any registers, the arithmetic        \
-       of 0 + -1 isn't done as unsigned.  */                           \
-    int this_reg;                                                      \
-                                                                       \
-    DEBUG_STATEMENT (failure_id++);                                    \
-    DEBUG_STATEMENT (nfailure_points_pushed++);                                \
-    DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id);          \
-    DEBUG_PRINT2 ("  Before push, next avail: %d\n", (fail_stack).avail);\
-    DEBUG_PRINT2 ("                     size: %d\n", (fail_stack).size);\
-                                                                       \
-    DEBUG_PRINT2 ("  slots needed: %d\n", NUM_FAILURE_ITEMS);          \
-    DEBUG_PRINT2 ("     available: %d\n", REMAINING_AVAIL_SLOTS);      \
-                                                                       \
-    /* Ensure we have enough space allocated for what we will push.  */        \
-    while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS)                  \
-      {                                                                        \
-       if (!DOUBLE_FAIL_STACK (fail_stack))                    \
-         return failure_code;                                          \
-                                                                       \
-       DEBUG_PRINT2 ("\n  Doubled stack; size now: %d\n",              \
-                      (fail_stack).size);                              \
-       DEBUG_PRINT2 ("  slots available: %d\n", REMAINING_AVAIL_SLOTS);\
-      }                                                                        \
-                                                                       \
-    /* Push the info, starting with the registers.  */                 \
-    DEBUG_PRINT1 ("\n");                                               \
-                                                                       \
-    for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
-        this_reg++)                                                    \
-      {                                                                        \
-       DEBUG_PRINT2 ("  Pushing reg: %d\n", this_reg);                 \
-       DEBUG_STATEMENT (num_regs_pushed++);                            \
-                                                                       \
-       DEBUG_PRINT2 ("    start: 0x%x\n", regstart[this_reg]);         \
-       PUSH_FAILURE_ITEM (regstart[this_reg]);                         \
-                                                                       \
-       DEBUG_PRINT2 ("    end: 0x%x\n", regend[this_reg]);             \
-       PUSH_FAILURE_ITEM (regend[this_reg]);                           \
-                                                                       \
-       DEBUG_PRINT2 ("    info: 0x%x\n      ", reg_info[this_reg]);    \
-       DEBUG_PRINT2 (" match_null=%d",                                 \
-                     REG_MATCH_NULL_STRING_P (reg_info[this_reg]));    \
-       DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg]));    \
-       DEBUG_PRINT2 (" matched_something=%d",                          \
-                     MATCHED_SOMETHING (reg_info[this_reg]));          \
-       DEBUG_PRINT2 (" ever_matched=%d",                               \
-                     EVER_MATCHED_SOMETHING (reg_info[this_reg]));     \
-       DEBUG_PRINT1 ("\n");                                            \
-       PUSH_FAILURE_ITEM (reg_info[this_reg].word);                    \
-      }                                                                        \
-                                                                       \
-    DEBUG_PRINT2 ("  Pushing  low active reg: %d\n", lowest_active_reg);\
-    PUSH_FAILURE_ITEM (lowest_active_reg);                             \
-                                                                       \
-    DEBUG_PRINT2 ("  Pushing high active reg: %d\n", highest_active_reg);\
-    PUSH_FAILURE_ITEM (highest_active_reg);                            \
-                                                                       \
-    DEBUG_PRINT2 ("  Pushing pattern 0x%x: ", pattern_place);          \
-    DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend);          \
-    PUSH_FAILURE_ITEM (pattern_place);                                 \
-                                                                       \
-    DEBUG_PRINT2 ("  Pushing string 0x%x: `", string_place);           \
-    DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2,   \
-                                size2);                                \
-    DEBUG_PRINT1 ("'\n");                                              \
-    PUSH_FAILURE_ITEM (string_place);                                  \
-                                                                       \
-    DEBUG_PRINT2 ("  Pushing failure id: %u\n", failure_id);           \
-    DEBUG_PUSH (failure_id);                                           \
-  } while (0)
-
-/* This is the number of items that are pushed and popped on the stack
-   for each register.  */
-#define NUM_REG_ITEMS  3
-
-/* Individual items aside from the registers.  */
-#ifdef DEBUG
-#define NUM_NONREG_ITEMS 5 /* Includes failure point id.  */
-#else
-#define NUM_NONREG_ITEMS 4
-#endif
-
-/* We push at most this many items on the stack.  */
-#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
-
-/* We actually push this many items.  */
-#define NUM_FAILURE_ITEMS                                              \
-  ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS        \
-    + NUM_NONREG_ITEMS)
-
-/* How many items can still be added to the stack without overflowing it.  */
-#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
-
-
-/* Pops what PUSH_FAIL_STACK pushes.
-
-   We restore into the parameters, all of which should be lvalues:
-     STR -- the saved data position.
-     PAT -- the saved pattern position.
-     LOW_REG, HIGH_REG -- the highest and lowest active registers.
-     REGSTART, REGEND -- arrays of string positions.
-     REG_INFO -- array of information about each subexpression.
-
-   Also assumes the variables `fail_stack' and (if debugging), `bufp',
-   `pend', `string1', `size1', `string2', and `size2'.  */
-
-#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
-{                                                                      \
-  DEBUG_STATEMENT (fail_stack_elt_t failure_id;)                       \
-  int this_reg;                                                                \
-  const unsigned char *string_temp;                                    \
-                                                                       \
-  assert (!FAIL_STACK_EMPTY ());                                       \
-                                                                       \
-  /* Remove failure points and point to how many regs pushed.  */      \
-  DEBUG_PRINT1 ("POP_FAILURE_POINT:\n");                               \
-  DEBUG_PRINT2 ("  Before pop, next avail: %d\n", fail_stack.avail);   \
-  DEBUG_PRINT2 ("                    size: %d\n", fail_stack.size);    \
-                                                                       \
-  assert (fail_stack.avail >= NUM_NONREG_ITEMS);                       \
-                                                                       \
-  DEBUG_POP (&failure_id);                                             \
-  DEBUG_PRINT2 ("  Popping failure id: %u\n", failure_id);             \
-                                                                       \
-  /* If the saved string location is NULL, it came from an             \
-     on_failure_keep_string_jump opcode, and we want to throw away the \
-     saved NULL, thus retaining our current position in the string.  */        \
-  string_temp = POP_FAILURE_ITEM ();                                   \
-  if (string_temp != NULL)                                             \
-    str = (const char *) string_temp;                                  \
-                                                                       \
-  DEBUG_PRINT2 ("  Popping string 0x%x: `", str);                      \
-  DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2);     \
-  DEBUG_PRINT1 ("'\n");                                                        \
-                                                                       \
-  pat = (unsigned char *) POP_FAILURE_ITEM ();                         \
-  DEBUG_PRINT2 ("  Popping pattern 0x%x: ", pat);                      \
-  DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend);                      \
-                                                                       \
-  /* Restore register info.  */                                                \
-  high_reg = (unsigned) POP_FAILURE_ITEM ();                           \
-  DEBUG_PRINT2 ("  Popping high active reg: %d\n", high_reg);          \
-                                                                       \
-  low_reg = (unsigned) POP_FAILURE_ITEM ();                            \
-  DEBUG_PRINT2 ("  Popping  low active reg: %d\n", low_reg);           \
-                                                                       \
-  for (this_reg = high_reg; this_reg >= low_reg; this_reg--)           \
-    {                                                                  \
-      DEBUG_PRINT2 ("    Popping reg: %d\n", this_reg);                        \
-                                                                       \
-      reg_info[this_reg].word = POP_FAILURE_ITEM ();                   \
-      DEBUG_PRINT2 ("      info: 0x%x\n", reg_info[this_reg]);         \
-                                                                       \
-      regend[this_reg] = (const char *) POP_FAILURE_ITEM ();           \
-      DEBUG_PRINT2 ("      end: 0x%x\n", regend[this_reg]);            \
-                                                                       \
-      regstart[this_reg] = (const char *) POP_FAILURE_ITEM ();         \
-      DEBUG_PRINT2 ("      start: 0x%x\n", regstart[this_reg]);                \
-    }                                                                  \
-                                                                       \
-  DEBUG_STATEMENT (nfailure_points_popped++);                          \
-} /* POP_FAILURE_POINT */
-\f
-/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
-   BUFP.  A fastmap records which of the (1 << BYTEWIDTH) possible
-   characters can start a string that matches the pattern.  This fastmap
-   is used by re_search to skip quickly over impossible starting points.
-
-   The caller must supply the address of a (1 << BYTEWIDTH)-byte data
-   area as BUFP->fastmap.
-
-   We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
-   the pattern buffer.
-
-   Returns 0 if we succeed, -2 if an internal error.   */
-
-int
-re_compile_fastmap (bufp)
-     struct re_pattern_buffer *bufp;
-{
-  int j, k;
-  fail_stack_type fail_stack;
-#ifndef REGEX_MALLOC
-  char *destination;
-#endif
-  /* We don't push any register information onto the failure stack.  */
-  unsigned num_regs = 0;
-
-  register char *fastmap = bufp->fastmap;
-  unsigned char *pattern = bufp->buffer;
-  unsigned long size = bufp->used;
-  const unsigned char *p = pattern;
-  register unsigned char *pend = pattern + size;
-
-  /* Assume that each path through the pattern can be null until
-     proven otherwise.  We set this false at the bottom of switch
-     statement, to which we get only if a particular path doesn't
-     match the empty string.  */
-  boolean path_can_be_null = true;
-
-  /* We aren't doing a `succeed_n' to begin with.  */
-  boolean succeed_n_p = false;
-
-  assert (fastmap != NULL && p != NULL);
-
-  INIT_FAIL_STACK ();
-  bzero (fastmap, 1 << BYTEWIDTH);  /* Assume nothing's valid.  */
-  bufp->fastmap_accurate = 1;      /* It will be when we're done.  */
-  bufp->can_be_null = 0;
-
-  while (p != pend || !FAIL_STACK_EMPTY ())
-    {
-      if (p == pend)
-       {
-         bufp->can_be_null |= path_can_be_null;
-
-         /* Reset for next path.  */
-         path_can_be_null = true;
-
-         p = fail_stack.stack[--fail_stack.avail];
-       }
-
-      /* We should never be about to go beyond the end of the pattern.  */
-      assert (p < pend);
-
-#ifdef SWITCH_ENUM_BUG
-      switch ((int) ((re_opcode_t) *p++))
-#else
-      switch ((re_opcode_t) *p++)
-#endif
-       {
-
-       /* I guess the idea here is to simply not bother with a fastmap
-          if a backreference is used, since it's too hard to figure out
-          the fastmap for the corresponding group.  Setting
-          `can_be_null' stops `re_search_2' from using the fastmap, so
-          that is all we do.  */
-       case duplicate:
-         bufp->can_be_null = 1;
-         return 0;
-
-
-      /* Following are the cases which match a character.  These end
-        with `break'.  */
-
-       case exactn:
-         fastmap[p[1]] = 1;
-         break;
-
-
-       case charset:
-         for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
-           if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
-             fastmap[j] = 1;
-         break;
-
-
-       case charset_not:
-         /* Chars beyond end of map must be allowed.  */
-         for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
-           fastmap[j] = 1;
-
-         for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
-           if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
-             fastmap[j] = 1;
-         break;
-
-
-       case wordchar:
-         for (j = 0; j < (1 << BYTEWIDTH); j++)
-           if (SYNTAX (j) == Sword)
-             fastmap[j] = 1;
-         break;
-
-
-       case notwordchar:
-         for (j = 0; j < (1 << BYTEWIDTH); j++)
-           if (SYNTAX (j) != Sword)
-             fastmap[j] = 1;
-         break;
-
-
-       case anychar:
-         /* `.' matches anything ...  */
-         for (j = 0; j < (1 << BYTEWIDTH); j++)
-           fastmap[j] = 1;
-
-         /* ... except perhaps newline.  */
-         if (!(bufp->syntax & RE_DOT_NEWLINE))
-           fastmap['\n'] = 0;
-
-         /* Return if we have already set `can_be_null'; if we have,
-            then the fastmap is irrelevant.  Something's wrong here.  */
-         else if (bufp->can_be_null)
-           return 0;
-
-         /* Otherwise, have to check alternative paths.  */
-         break;
-
-
-#ifdef emacs
-       case syntaxspec:
-         k = *p++;
-         for (j = 0; j < (1 << BYTEWIDTH); j++)
-           if (SYNTAX (j) == (enum syntaxcode) k)
-             fastmap[j] = 1;
-         break;
-
-
-       case notsyntaxspec:
-         k = *p++;
-         for (j = 0; j < (1 << BYTEWIDTH); j++)
-           if (SYNTAX (j) != (enum syntaxcode) k)
-             fastmap[j] = 1;
-         break;
-
-
-      /* All cases after this match the empty string.  These end with
-        `continue'.  */
-
-
-       case before_dot:
-       case at_dot:
-       case after_dot:
-         continue;
-#endif /* not emacs */
-
-
-       case no_op:
-       case begline:
-       case endline:
-       case begbuf:
-       case endbuf:
-       case wordbound:
-       case notwordbound:
-       case wordbeg:
-       case wordend:
-       case push_dummy_failure:
-         continue;
-
-
-       case jump_n:
-       case pop_failure_jump:
-       case maybe_pop_jump:
-       case jump:
-       case jump_past_alt:
-       case dummy_failure_jump:
-         EXTRACT_NUMBER_AND_INCR (j, p);
-         p += j;
-         if (j > 0)
-           continue;
-
-         /* Jump backward implies we just went through the body of a
-            loop and matched nothing.  Opcode jumped to should be
-            `on_failure_jump' or `succeed_n'.  Just treat it like an
-            ordinary jump.  For a * loop, it has pushed its failure
-            point already; if so, discard that as redundant.  */
-         if ((re_opcode_t) *p != on_failure_jump
-             && (re_opcode_t) *p != succeed_n)
-           continue;
-
-         p++;
-         EXTRACT_NUMBER_AND_INCR (j, p);
-         p += j;
-
-         /* If what's on the stack is where we are now, pop it.  */
-         if (!FAIL_STACK_EMPTY ()
-             && fail_stack.stack[fail_stack.avail - 1] == p)
-           fail_stack.avail--;
-
-         continue;
-
-
-       case on_failure_jump:
-       case on_failure_keep_string_jump:
-       handle_on_failure_jump:
-         EXTRACT_NUMBER_AND_INCR (j, p);
-
-         /* For some patterns, e.g., `(a?)?', `p+j' here points to the
-            end of the pattern.  We don't want to push such a point,
-            since when we restore it above, entering the switch will
-            increment `p' past the end of the pattern.  We don't need
-            to push such a point since we obviously won't find any more
-            fastmap entries beyond `pend'.  Such a pattern can match
-            the null string, though.  */
-         if (p + j < pend)
-           {
-             if (!PUSH_PATTERN_OP (p + j, fail_stack))
-               return -2;
-           }
-         else
-           bufp->can_be_null = 1;
-
-         if (succeed_n_p)
-           {
-             EXTRACT_NUMBER_AND_INCR (k, p);   /* Skip the n.  */
-             succeed_n_p = false;
-           }
-
-         continue;
-
-
-       case succeed_n:
-         /* Get to the number of times to succeed.  */
-         p += 2;
-
-         /* Increment p past the n for when k != 0.  */
-         EXTRACT_NUMBER_AND_INCR (k, p);
-         if (k == 0)
-           {
-             p -= 4;
-             succeed_n_p = true;  /* Spaghetti code alert.  */
-             goto handle_on_failure_jump;
-           }
-         continue;
-
-
-       case set_number_at:
-         p += 4;
-         continue;
-
-
-       case start_memory:
-       case stop_memory:
-         p += 2;
-         continue;
-
-
-       default:
-         abort (); /* We have listed all the cases.  */
-       } /* switch *p++ */
-
-      /* Getting here means we have found the possible starting
-        characters for one path of the pattern -- and that the empty
-        string does not match.  We need not follow this path further.
-        Instead, look at the next alternative (remembered on the
-        stack), or quit if no more.  The test at the top of the loop
-        does these things.  */
-      path_can_be_null = false;
-      p = pend;
-    } /* while p */
-
-  /* Set `can_be_null' for the last path (also the first path, if the
-     pattern is empty).  */
-  bufp->can_be_null |= path_can_be_null;
-  return 0;
-} /* re_compile_fastmap */
-\f
-/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
-   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
-   this memory for recording register information.  STARTS and ENDS
-   must be allocated using the malloc library routine, and must each
-   be at least NUM_REGS * sizeof (regoff_t) bytes long.
-
-   If NUM_REGS == 0, then subsequent matches should allocate their own
-   register data.
-
-   Unless this function is called, the first search or match using
-   PATTERN_BUFFER will allocate its own register data, without
-   freeing the old data.  */
-
-void
-re_set_registers (bufp, regs, num_regs, starts, ends)
-    struct re_pattern_buffer *bufp;
-    struct re_registers *regs;
-    unsigned num_regs;
-    regoff_t *starts, *ends;
-{
-  if (num_regs)
-    {
-      bufp->regs_allocated = REGS_REALLOCATE;
-      regs->num_regs = num_regs;
-      regs->start = starts;
-      regs->end = ends;
-    }
-  else
-    {
-      bufp->regs_allocated = REGS_UNALLOCATED;
-      regs->num_regs = 0;
-      regs->start = regs->end = (regoff_t *) 0;
-    }
-}
-\f
-/* Searching routines.  */
-
-/* Like re_search_2, below, but only one string is specified, and
-   doesn't let you say where to stop matching. */
-
-int
-re_search (bufp, string, size, startpos, range, regs)
-     struct re_pattern_buffer *bufp;
-     const char *string;
-     int size, startpos, range;
-     struct re_registers *regs;
-{
-  return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
-                     regs, size);
-}
-
-
-/* Using the compiled pattern in BUFP->buffer, first tries to match the
-   virtual concatenation of STRING1 and STRING2, starting first at index
-   STARTPOS, then at STARTPOS + 1, and so on.
-
-   STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
-
-   RANGE is how far to scan while trying to match.  RANGE = 0 means try
-   only at STARTPOS; in general, the last start tried is STARTPOS +
-   RANGE.
-
-   In REGS, return the indices of the virtual concatenation of STRING1
-   and STRING2 that matched the entire BUFP->buffer and its contained
-   subexpressions.
-
-   Do not consider matching one past the index STOP in the virtual
-   concatenation of STRING1 and STRING2.
-
-   We return either the position in the strings at which the match was
-   found, -1 if no match, or -2 if error (such as failure
-   stack overflow).  */
-
-int
-re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
-     struct re_pattern_buffer *bufp;
-     const char *string1, *string2;
-     int size1, size2;
-     int startpos;
-     int range;
-     struct re_registers *regs;
-     int stop;
-{
-  int val;
-  register char *fastmap = bufp->fastmap;
-  register char *translate = bufp->translate;
-  int total_size = size1 + size2;
-  int endpos = startpos + range;
-
-  /* Check for out-of-range STARTPOS.  */
-  if (startpos < 0 || startpos > total_size)
-    return -1;
-
-  /* Fix up RANGE if it might eventually take us outside
-     the virtual concatenation of STRING1 and STRING2.  */
-  if (endpos < -1)
-    range = -1 - startpos;
-  else if (endpos > total_size)
-    range = total_size - startpos;
-
-  /* If the search isn't to be a backwards one, don't waste time in a
-     search for a pattern that must be anchored.  */
-  if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
-    {
-      if (startpos > 0)
-       return -1;
-      else
-       range = 1;
-    }
-
-  /* Update the fastmap now if not correct already.  */
-  if (fastmap && !bufp->fastmap_accurate)
-    if (re_compile_fastmap (bufp) == -2)
-      return -2;
-
-  /* Loop through the string, looking for a place to start matching.  */
-  for (;;)
-    {
-      /* If a fastmap is supplied, skip quickly over characters that
-        cannot be the start of a match.  If the pattern can match the
-        null string, however, we don't need to skip characters; we want
-        the first null string.  */
-      if (fastmap && startpos < total_size && !bufp->can_be_null)
-       {
-         if (range > 0)        /* Searching forwards.  */
-           {
-             register const char *d;
-             register int lim = 0;
-             int irange = range;
-
-             if (startpos < size1 && startpos + range >= size1)
-               lim = range - (size1 - startpos);
-
-             d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
-
-             /* Written out as an if-else to avoid testing `translate'
-                inside the loop.  */
-             if (translate)
-               while (range > lim
-                      && !fastmap[(unsigned char)
-                                  translate[(unsigned char) *d++]])
-                 range--;
-             else
-               while (range > lim && !fastmap[(unsigned char) *d++])
-                 range--;
-
-             startpos += irange - range;
-           }
-         else                          /* Searching backwards.  */
-           {
-             register char c = (size1 == 0 || startpos >= size1
-                                ? string2[startpos - size1]
-                                : string1[startpos]);
-
-             if (!fastmap[(unsigned char) TRANSLATE (c)])
-               goto advance;
-           }
-       }
-
-      /* If can't match the null string, and that's all we have left, fail.  */
-      if (range >= 0 && startpos == total_size && fastmap
-         && !bufp->can_be_null)
-       return -1;
-
-      val = re_match_2 (bufp, string1, size1, string2, size2,
-                       startpos, regs, stop);
-      if (val >= 0)
-       return startpos;
-
-      if (val == -2)
-       return -2;
-
-    advance:
-      if (!range)
-       break;
-      else if (range > 0)
-       {
-         range--;
-         startpos++;
-       }
-      else
-       {
-         range++;
-         startpos--;
-       }
-    }
-  return -1;
-} /* re_search_2 */
-\f
-/* Declarations and macros for re_match_2.  */
-
-static int bcmp_translate ();
-static boolean alt_match_null_string_p (),
-              common_op_match_null_string_p (),
-              group_match_null_string_p ();
-
-/* Structure for per-register (a.k.a. per-group) information.
-   This must not be longer than one word, because we push this value
-   onto the failure stack.  Other register information, such as the
-   starting and ending positions (which are addresses), and the list of
-   inner groups (which is a bits list) are maintained in separate
-   variables.
-
-   We are making a (strictly speaking) nonportable assumption here: that
-   the compiler will pack our bit fields into something that fits into
-   the type of `word', i.e., is something that fits into one item on the
-   failure stack.  */
-typedef union
-{
-  fail_stack_elt_t word;
-  struct
-  {
-      /* This field is one if this group can match the empty string,
-        zero if not.  If not yet determined,  `MATCH_NULL_UNSET_VALUE'.  */
-#define MATCH_NULL_UNSET_VALUE 3
-    unsigned match_null_string_p : 2;
-    unsigned is_active : 1;
-    unsigned matched_something : 1;
-    unsigned ever_matched_something : 1;
-  } bits;
-} register_info_type;
-
-#define REG_MATCH_NULL_STRING_P(R)  ((R).bits.match_null_string_p)
-#define IS_ACTIVE(R)  ((R).bits.is_active)
-#define MATCHED_SOMETHING(R)  ((R).bits.matched_something)
-#define EVER_MATCHED_SOMETHING(R)  ((R).bits.ever_matched_something)
-
-
-/* Call this when have matched a real character; it sets `matched' flags
-   for the subexpressions which we are currently inside.  Also records
-   that those subexprs have matched.  */
-#define SET_REGS_MATCHED()                                             \
-  do                                                                   \
-    {                                                                  \
-      unsigned r;                                                      \
-      for (r = lowest_active_reg; r <= highest_active_reg; r++)                \
-       {                                                               \
-         MATCHED_SOMETHING (reg_info[r])                               \
-           = EVER_MATCHED_SOMETHING (reg_info[r])                      \
-           = 1;                                                        \
-       }                                                               \
-    }                                                                  \
-  while (0)
-
-
-/* This converts PTR, a pointer into one of the search strings `string1'
-   and `string2' into an offset from the beginning of that string.  */
-#define POINTER_TO_OFFSET(ptr)                                         \
-  (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
-
-/* Registers are set to a sentinel when they haven't yet matched.  */
-#define REG_UNSET_VALUE ((char *) -1)
-#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
-
-
-/* Macros for dealing with the split strings in re_match_2.  */
-
-#define MATCHING_IN_FIRST_STRING  (dend == end_match_1)
-
-/* Call before fetching a character with *d.  This switches over to
-   string2 if necessary.  */
-#define PREFETCH()                                                     \
-  while (d == dend)                                                    \
-    {                                                                  \
-      /* End of string2 => fail.  */                                   \
-      if (dend == end_match_2)                                                 \
-       goto fail;                                                      \
-      /* End of string1 => advance to string2.  */                     \
-      d = string2;                                                     \
-      dend = end_match_2;                                              \
-    }
-
-
-/* Test if at very beginning or at very end of the virtual concatenation
-   of `string1' and `string2'.  If only one string, it's `string2'.  */
-#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
-#define AT_STRINGS_END(d) ((d) == end2)
-
-
-/* Test if D points to a character which is word-constituent.  We have
-   two special cases to check for: if past the end of string1, look at
-   the first character in string2; and if before the beginning of
-   string2, look at the last character in string1.  */
-#define WORDCHAR_P(d)                                                  \
-  (SYNTAX ((d) == end1 ? *string2                                      \
-          : (d) == string2 - 1 ? *(end1 - 1) : *(d))                   \
-   == Sword)
-
-/* Test if the character before D and the one at D differ with respect
-   to being word-constituent.  */
-#define AT_WORD_BOUNDARY(d)                                            \
-  (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)                            \
-   || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
-
-
-/* Free everything we malloc.  */
-#ifdef REGEX_MALLOC
-#define FREE_VAR(var) if (var) free (var); var = NULL
-#define FREE_VARIABLES()                                               \
-  do {                                                                 \
-    FREE_VAR (fail_stack.stack);                                       \
-    FREE_VAR (regstart);                                               \
-    FREE_VAR (regend);                                                 \
-    FREE_VAR (old_regstart);                                           \
-    FREE_VAR (old_regend);                                             \
-    FREE_VAR (best_regstart);                                          \
-    FREE_VAR (best_regend);                                            \
-    FREE_VAR (reg_info);                                               \
-    FREE_VAR (reg_dummy);                                              \
-    FREE_VAR (reg_info_dummy);                                         \
-  } while (0)
-#else /* not REGEX_MALLOC */
-/* Some MIPS systems (at least) want this to free alloca'd storage.  */
-#define FREE_VARIABLES() alloca (0)
-#endif /* not REGEX_MALLOC */
-
-
-/* These values must meet several constraints.  They must not be valid
-   register values; since we have a limit of 255 registers (because
-   we use only one byte in the pattern for the register number), we can
-   use numbers larger than 255.  They must differ by 1, because of
-   NUM_FAILURE_ITEMS above.  And the value for the lowest register must
-   be larger than the value for the highest register, so we do not try
-   to actually save any registers when none are active.  */
-#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
-#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
-\f
-/* Matching routines.  */
-
-#ifndef emacs   /* Emacs never uses this.  */
-/* re_match is like re_match_2 except it takes only a single string.  */
-
-int
-re_match (bufp, string, size, pos, regs)
-     struct re_pattern_buffer *bufp;
-     const char *string;
-     int size, pos;
-     struct re_registers *regs;
- {
-  return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size);
-}
-#endif /* not emacs */
-
-
-/* re_match_2 matches the compiled pattern in BUFP against the
-   the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
-   and SIZE2, respectively).  We start matching at POS, and stop
-   matching at STOP.
-
-   If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
-   store offsets for the substring each group matched in REGS.  See the
-   documentation for exactly how many groups we fill.
-
-   We return -1 if no match, -2 if an internal error (such as the
-   failure stack overflowing).  Otherwise, we return the length of the
-   matched substring.  */
-
-int
-re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
-     struct re_pattern_buffer *bufp;
-     const char *string1, *string2;
-     int size1, size2;
-     int pos;
-     struct re_registers *regs;
-     int stop;
-{
-  /* General temporaries.  */
-  int mcnt;
-  unsigned char *p1;
-
-  /* Just past the end of the corresponding string.  */
-  const char *end1, *end2;
-
-  /* Pointers into string1 and string2, just past the last characters in
-     each to consider matching.  */
-  const char *end_match_1, *end_match_2;
-
-  /* Where we are in the data, and the end of the current string.  */
-  const char *d, *dend;
-
-  /* Where we are in the pattern, and the end of the pattern.  */
-  unsigned char *p = bufp->buffer;
-  register unsigned char *pend = p + bufp->used;
-
-  /* We use this to map every character in the string.  */
-  char *translate = bufp->translate;
-
-  /* Failure point stack.  Each place that can handle a failure further
-     down the line pushes a failure point on this stack.  It consists of
-     restart, regend, and reg_info for all registers corresponding to
-     the subexpressions we're currently inside, plus the number of such
-     registers, and, finally, two char *'s.  The first char * is where
-     to resume scanning the pattern; the second one is where to resume
-     scanning the strings.  If the latter is zero, the failure point is
-     a ``dummy''; if a failure happens and the failure point is a dummy,
-     it gets discarded and the next next one is tried.  */
-  fail_stack_type fail_stack;
-#ifdef DEBUG
-  static unsigned failure_id = 0;
-  unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
-#endif
-
-  /* We fill all the registers internally, independent of what we
-     return, for use in backreferences.  The number here includes
-     an element for register zero.  */
-  unsigned num_regs = bufp->re_nsub + 1;
-
-  /* The currently active registers.  */
-  unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
-  unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
-
-  /* Information on the contents of registers. These are pointers into
-     the input strings; they record just what was matched (on this
-     attempt) by a subexpression part of the pattern, that is, the
-     regnum-th regstart pointer points to where in the pattern we began
-     matching and the regnum-th regend points to right after where we
-     stopped matching the regnum-th subexpression.  (The zeroth register
-     keeps track of what the whole pattern matches.)  */
-  const char **regstart = NULL, **regend = NULL;
-
-  /* If a group that's operated upon by a repetition operator fails to
-     match anything, then the register for its start will need to be
-     restored because it will have been set to wherever in the string we
-     are when we last see its open-group operator.  Similarly for a
-     register's end.  */
-  const char **old_regstart = NULL, **old_regend = NULL;
-
-  /* The is_active field of reg_info helps us keep track of which (possibly
-     nested) subexpressions we are currently in. The matched_something
-     field of reg_info[reg_num] helps us tell whether or not we have
-     matched any of the pattern so far this time through the reg_num-th
-     subexpression.  These two fields get reset each time through any
-     loop their register is in.  */
-  register_info_type *reg_info = NULL;
-
-  /* The following record the register info as found in the above
-     variables when we find a match better than any we've seen before.
-     This happens as we backtrack through the failure points, which in
-     turn happens only if we have not yet matched the entire string. */
-  unsigned best_regs_set = false;
-  const char **best_regstart = NULL, **best_regend = NULL;
-
-  /* Logically, this is `best_regend[0]'.  But we don't want to have to
-     allocate space for that if we're not allocating space for anything
-     else (see below).  Also, we never need info about register 0 for
-     any of the other register vectors, and it seems rather a kludge to
-     treat `best_regend' differently than the rest.  So we keep track of
-     the end of the best match so far in a separate variable.  We
-     initialize this to NULL so that when we backtrack the first time
-     and need to test it, it's not garbage.  */
-  const char *match_end = NULL;
-
-  /* Used when we pop values we don't care about.  */
-  const char **reg_dummy = NULL;
-  register_info_type *reg_info_dummy = NULL;
-
-#ifdef DEBUG
-  /* Counts the total number of registers pushed.  */
-  unsigned num_regs_pushed = 0;
-#endif
-
-  DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
-
-  INIT_FAIL_STACK ();
-
-  /* Do not bother to initialize all the register variables if there are
-     no groups in the pattern, as it takes a fair amount of time.  If
-     there are groups, we include space for register 0 (the whole
-     pattern), even though we never use it, since it simplifies the
-     array indexing.  We should fix this.  */
-  if (bufp->re_nsub)
-    {
-      regstart = REGEX_TALLOC (num_regs, const char *);
-      regend = REGEX_TALLOC (num_regs, const char *);
-      old_regstart = REGEX_TALLOC (num_regs, const char *);
-      old_regend = REGEX_TALLOC (num_regs, const char *);
-      best_regstart = REGEX_TALLOC (num_regs, const char *);
-      best_regend = REGEX_TALLOC (num_regs, const char *);
-      reg_info = REGEX_TALLOC (num_regs, register_info_type);
-      reg_dummy = REGEX_TALLOC (num_regs, const char *);
-      reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
-
-      if (!(regstart && regend && old_regstart && old_regend && reg_info
-           && best_regstart && best_regend && reg_dummy && reg_info_dummy))
-       {
-         FREE_VARIABLES ();
-         return -2;
-       }
-    }
-#ifdef REGEX_MALLOC
-  else
-    {
-      /* We must initialize all our variables to NULL, so that
-        `FREE_VARIABLES' doesn't try to free them.  */
-      regstart = regend = old_regstart = old_regend = best_regstart
-       = best_regend = reg_dummy = NULL;
-      reg_info = reg_info_dummy = (register_info_type *) NULL;
-    }
-#endif /* REGEX_MALLOC */
-
-  /* The starting position is bogus.  */
-  if (pos < 0 || pos > size1 + size2)
-    {
-      FREE_VARIABLES ();
-      return -1;
-    }
-
-  /* Initialize subexpression text positions to -1 to mark ones that no
-     start_memory/stop_memory has been seen for. Also initialize the
-     register information struct.  */
-  for (mcnt = 1; mcnt < num_regs; mcnt++)
-    {
-      regstart[mcnt] = regend[mcnt]
-       = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
-
-      REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
-      IS_ACTIVE (reg_info[mcnt]) = 0;
-      MATCHED_SOMETHING (reg_info[mcnt]) = 0;
-      EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
-    }
-
-  /* We move `string1' into `string2' if the latter's empty -- but not if
-     `string1' is null.  */
-  if (size2 == 0 && string1 != NULL)
-    {
-      string2 = string1;
-      size2 = size1;
-      string1 = 0;
-      size1 = 0;
-    }
-  end1 = string1 + size1;
-  end2 = string2 + size2;
-
-  /* Compute where to stop matching, within the two strings.  */
-  if (stop <= size1)
-    {
-      end_match_1 = string1 + stop;
-      end_match_2 = string2;
-    }
-  else
-    {
-      end_match_1 = end1;
-      end_match_2 = string2 + stop - size1;
-    }
-
-  /* `p' scans through the pattern as `d' scans through the data.
-     `dend' is the end of the input string that `d' points within.  `d'
-     is advanced into the following input string whenever necessary, but
-     this happens before fetching; therefore, at the beginning of the
-     loop, `d' can be pointing at the end of a string, but it cannot
-     equal `string2'.  */
-  if (size1 > 0 && pos <= size1)
-    {
-      d = string1 + pos;
-      dend = end_match_1;
-    }
-  else
-    {
-      d = string2 + pos - size1;
-      dend = end_match_2;
-    }
-
-  DEBUG_PRINT1 ("The compiled pattern is: ");
-  DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
-  DEBUG_PRINT1 ("The string to match is: `");
-  DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
-  DEBUG_PRINT1 ("'\n");
-
-  /* This loops over pattern commands.  It exits by returning from the
-     function if the match is complete, or it drops through if the match
-     fails at this starting point in the input data.  */
-  for (;;)
-    {
-      DEBUG_PRINT2 ("\n0x%x: ", p);
-
-      if (p == pend)
-       { /* End of pattern means we might have succeeded.  */
-         DEBUG_PRINT1 ("end of pattern ... ");
-
-         /* If we haven't matched the entire string, and we want the
-            longest match, try backtracking.  */
-         if (d != end_match_2)
-           {
-             DEBUG_PRINT1 ("backtracking.\n");
-
-             if (!FAIL_STACK_EMPTY ())
-               { /* More failure points to try.  */
-                 boolean same_str_p = (FIRST_STRING_P (match_end)
-                                       == MATCHING_IN_FIRST_STRING);
-
-                 /* If exceeds best match so far, save it.  */
-                 if (!best_regs_set
-                     || (same_str_p && d > match_end)
-                     || (!same_str_p && !MATCHING_IN_FIRST_STRING))
-                   {
-                     best_regs_set = true;
-                     match_end = d;
-
-                     DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
-
-                     for (mcnt = 1; mcnt < num_regs; mcnt++)
-                       {
-                         best_regstart[mcnt] = regstart[mcnt];
-                         best_regend[mcnt] = regend[mcnt];
-                       }
-                   }
-                 goto fail;
-               }
-
-             /* If no failure points, don't restore garbage.  */
-             else if (best_regs_set)
-               {
-               restore_best_regs:
-                 /* Restore best match.  It may happen that `dend ==
-                    end_match_1' while the restored d is in string2.
-                    For example, the pattern `x.*y.*z' against the
-                    strings `x-' and `y-z-', if the two strings are
-                    not consecutive in memory.  */
-                 DEBUG_PRINT1 ("Restoring best registers.\n");
-
-                 d = match_end;
-                 dend = ((d >= string1 && d <= end1)
-                          ? end_match_1 : end_match_2);
-
-                 for (mcnt = 1; mcnt < num_regs; mcnt++)
-                   {
-                     regstart[mcnt] = best_regstart[mcnt];
-                     regend[mcnt] = best_regend[mcnt];
-                   }
-               }
-           } /* d != end_match_2 */
-
-         DEBUG_PRINT1 ("Accepting match.\n");
-
-         /* If caller wants register contents data back, do it.  */
-         if (regs && !bufp->no_sub)
-           {
-             /* Have the register data arrays been allocated?  */
-             if (bufp->regs_allocated == REGS_UNALLOCATED)
-               { /* No.  So allocate them with malloc.  We need one
-                    extra element beyond `num_regs' for the `-1' marker
-                    GNU code uses.  */
-                 regs->num_regs = MAX (RE_NREGS, num_regs + 1);
-                 regs->start = TALLOC (regs->num_regs, regoff_t);
-                 regs->end = TALLOC (regs->num_regs, regoff_t);
-                 if (regs->start == NULL || regs->end == NULL)
-                   return -2;
-                 bufp->regs_allocated = REGS_REALLOCATE;
-               }
-             else if (bufp->regs_allocated == REGS_REALLOCATE)
-               { /* Yes.  If we need more elements than were already
-                    allocated, reallocate them.  If we need fewer, just
-                    leave it alone.  */
-                 if (regs->num_regs < num_regs + 1)
-                   {
-                     regs->num_regs = num_regs + 1;
-                     RETALLOC (regs->start, regs->num_regs, regoff_t);
-                     RETALLOC (regs->end, regs->num_regs, regoff_t);
-                     if (regs->start == NULL || regs->end == NULL)
-                       return -2;
-                   }
-               }
-             else
-               assert (bufp->regs_allocated == REGS_FIXED);
-
-             /* Convert the pointer data in `regstart' and `regend' to
-                indices.  Register zero has to be set differently,
-                since we haven't kept track of any info for it.  */
-             if (regs->num_regs > 0)
-               {
-                 regs->start[0] = pos;
-                 regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
-                                 : d - string2 + size1);
-               }
-
-             /* Go through the first `min (num_regs, regs->num_regs)'
-                registers, since that is all we initialized.  */
-             for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
-               {
-                 if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
-                   regs->start[mcnt] = regs->end[mcnt] = -1;
-                 else
-                   {
-                     regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
-                     regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
-                   }
-               }
-
-             /* If the regs structure we return has more elements than
-                were in the pattern, set the extra elements to -1.  If
-                we (re)allocated the registers, this is the case,
-                because we always allocate enough to have at least one
-                -1 at the end.  */
-             for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
-               regs->start[mcnt] = regs->end[mcnt] = -1;
-           } /* regs && !bufp->no_sub */
-
-         FREE_VARIABLES ();
-         DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
-                       nfailure_points_pushed, nfailure_points_popped,
-                       nfailure_points_pushed - nfailure_points_popped);
-         DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
-
-         mcnt = d - pos - (MATCHING_IN_FIRST_STRING
-                           ? string1
-                           : string2 - size1);
-
-         DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
-
-         return mcnt;
-       }
-
-      /* Otherwise match next pattern command.  */
-#ifdef SWITCH_ENUM_BUG
-      switch ((int) ((re_opcode_t) *p++))
-#else
-      switch ((re_opcode_t) *p++)
-#endif
-       {
-       /* Ignore these.  Used to ignore the n of succeed_n's which
-          currently have n == 0.  */
-       case no_op:
-         DEBUG_PRINT1 ("EXECUTING no_op.\n");
-         break;
-
-
-       /* Match the next n pattern characters exactly.  The following
-          byte in the pattern defines n, and the n bytes after that
-          are the characters to match.  */
-       case exactn:
-         mcnt = *p++;
-         DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
-
-         /* This is written out as an if-else so we don't waste time
-            testing `translate' inside the loop.  */
-         if (translate)
-           {
-             do
-               {
-                 PREFETCH ();
-                 if (translate[(unsigned char) *d++] != (char) *p++)
-                   goto fail;
-               }
-             while (--mcnt);
-           }
-         else
-           {
-             do
-               {
-                 PREFETCH ();
-                 if (*d++ != (char) *p++) goto fail;
-               }
-             while (--mcnt);
-           }
-         SET_REGS_MATCHED ();
-         break;
-
-
-       /* Match any character except possibly a newline or a null.  */
-       case anychar:
-         DEBUG_PRINT1 ("EXECUTING anychar.\n");
-
-         PREFETCH ();
-
-         if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
-             || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
-           goto fail;
-
-         SET_REGS_MATCHED ();
-         DEBUG_PRINT2 ("  Matched `%d'.\n", *d);
-         d++;
-         break;
-
-
-       case charset:
-       case charset_not:
-         {
-           register unsigned char c;
-           boolean not = (re_opcode_t) *(p - 1) == charset_not;
-
-           DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
-
-           PREFETCH ();
-           c = TRANSLATE (*d); /* The character to match.  */
-
-           /* Cast to `unsigned' instead of `unsigned char' in case the
-              bit list is a full 32 bytes long.  */
-           if (c < (unsigned) (*p * BYTEWIDTH)
-               && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
-             not = !not;
-
-           p += 1 + *p;
-
-           if (!not) goto fail;
-
-           SET_REGS_MATCHED ();
-           d++;
-           break;
-         }
-
-
-       /* The beginning of a group is represented by start_memory.
-          The arguments are the register number in the next byte, and the
-          number of groups inner to this one in the next.  The text
-          matched within the group is recorded (in the internal
-          registers data structure) under the register number.  */
-       case start_memory:
-         DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
-
-         /* Find out if this group can match the empty string.  */
-         p1 = p;               /* To send to group_match_null_string_p.  */
-
-         if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
-           REG_MATCH_NULL_STRING_P (reg_info[*p])
-             = group_match_null_string_p (&p1, pend, reg_info);
-
-         /* Save the position in the string where we were the last time
-            we were at this open-group operator in case the group is
-            operated upon by a repetition operator, e.g., with `(a*)*b'
-            against `ab'; then we want to ignore where we are now in
-            the string in case this attempt to match fails.  */
-         old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
-                            ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
-                            : regstart[*p];
-         DEBUG_PRINT2 ("  old_regstart: %d\n",
-                        POINTER_TO_OFFSET (old_regstart[*p]));
-
-         regstart[*p] = d;
-         DEBUG_PRINT2 ("  regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
-
-         IS_ACTIVE (reg_info[*p]) = 1;
-         MATCHED_SOMETHING (reg_info[*p]) = 0;
-
-         /* This is the new highest active register.  */
-         highest_active_reg = *p;
-
-         /* If nothing was active before, this is the new lowest active
-            register.  */
-         if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
-           lowest_active_reg = *p;
-
-         /* Move past the register number and inner group count.  */
-         p += 2;
-         break;
-
-
-       /* The stop_memory opcode represents the end of a group.  Its
-          arguments are the same as start_memory's: the register
-          number, and the number of inner groups.  */
-       case stop_memory:
-         DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
-
-         /* We need to save the string position the last time we were at
-            this close-group operator in case the group is operated
-            upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
-            against `aba'; then we want to ignore where we are now in
-            the string in case this attempt to match fails.  */
-         old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
-                          ? REG_UNSET (regend[*p]) ? d : regend[*p]
-                          : regend[*p];
-         DEBUG_PRINT2 ("      old_regend: %d\n",
-                        POINTER_TO_OFFSET (old_regend[*p]));
-
-         regend[*p] = d;
-         DEBUG_PRINT2 ("      regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
-
-         /* This register isn't active anymore.  */
-         IS_ACTIVE (reg_info[*p]) = 0;
-
-         /* If this was the only register active, nothing is active
-            anymore.  */
-         if (lowest_active_reg == highest_active_reg)
-           {
-             lowest_active_reg = NO_LOWEST_ACTIVE_REG;
-             highest_active_reg = NO_HIGHEST_ACTIVE_REG;
-           }
-         else
-           { /* We must scan for the new highest active register, since
-                it isn't necessarily one less than now: consider
-                (a(b)c(d(e)f)g).  When group 3 ends, after the f), the
-                new highest active register is 1.  */
-             unsigned char r = *p - 1;
-             while (r > 0 && !IS_ACTIVE (reg_info[r]))
-               r--;
-
-             /* If we end up at register zero, that means that we saved
-                the registers as the result of an `on_failure_jump', not
-                a `start_memory', and we jumped to past the innermost
-                `stop_memory'.  For example, in ((.)*) we save
-                registers 1 and 2 as a result of the *, but when we pop
-                back to the second ), we are at the stop_memory 1.
-                Thus, nothing is active.  */
-             if (r == 0)
-               {
-                 lowest_active_reg = NO_LOWEST_ACTIVE_REG;
-                 highest_active_reg = NO_HIGHEST_ACTIVE_REG;
-               }
-             else
-               highest_active_reg = r;
-           }
-
-         /* If just failed to match something this time around with a
-            group that's operated on by a repetition operator, try to
-            force exit from the ``loop'', and restore the register
-            information for this group that we had before trying this
-            last match.  */
-         if ((!MATCHED_SOMETHING (reg_info[*p])
-              || (re_opcode_t) p[-3] == start_memory)
-             && (p + 2) < pend)
-           {
-             boolean is_a_jump_n = false;
-
-             p1 = p + 2;
-             mcnt = 0;
-             switch ((re_opcode_t) *p1++)
-               {
-                 case jump_n:
-                   is_a_jump_n = true;
-                 case pop_failure_jump:
-                 case maybe_pop_jump:
-                 case jump:
-                 case dummy_failure_jump:
-                   EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-                   if (is_a_jump_n)
-                     p1 += 2;
-                   break;
-
-                 default:
-                   /* do nothing */ ;
-               }
-             p1 += mcnt;
-
-             /* If the next operation is a jump backwards in the pattern
-                to an on_failure_jump right before the start_memory
-                corresponding to this stop_memory, exit from the loop
-                by forcing a failure after pushing on the stack the
-                on_failure_jump's jump in the pattern, and d.  */
-             if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
-                 && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
-               {
-                 /* If this group ever matched anything, then restore
-                    what its registers were before trying this last
-                    failed match, e.g., with `(a*)*b' against `ab' for
-                    regstart[1], and, e.g., with `((a*)*(b*)*)*'
-                    against `aba' for regend[3].
-
-                    Also restore the registers for inner groups for,
-                    e.g., `((a*)(b*))*' against `aba' (register 3 would
-                    otherwise get trashed).  */
-
-                 if (EVER_MATCHED_SOMETHING (reg_info[*p]))
-                   {
-                     unsigned r;
-
-                     EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
-
-                     /* Restore this and inner groups' (if any) registers.  */
-                     for (r = *p; r < *p + *(p + 1); r++)
-                       {
-                         regstart[r] = old_regstart[r];
-
-                         /* xx why this test?  */
-                         if ((int) old_regend[r] >= (int) regstart[r])
-                           regend[r] = old_regend[r];
-                       }
-                   }
-                 p1++;
-                 EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-                 PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
-
-                 goto fail;
-               }
-           }
-
-         /* Move past the register number and the inner group count.  */
-         p += 2;
-         break;
-
-
-       /* \<digit> has been turned into a `duplicate' command which is
-          followed by the numeric value of <digit> as the register number.  */
-       case duplicate:
-         {
-           register const char *d2, *dend2;
-           int regno = *p++;   /* Get which register to match against.  */
-           DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
-
-           /* Can't back reference a group which we've never matched.  */
-           if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
-             goto fail;
-
-           /* Where in input to try to start matching.  */
-           d2 = regstart[regno];
-
-           /* Where to stop matching; if both the place to start and
-              the place to stop matching are in the same string, then
-              set to the place to stop, otherwise, for now have to use
-              the end of the first string.  */
-
-           dend2 = ((FIRST_STRING_P (regstart[regno])
-                     == FIRST_STRING_P (regend[regno]))
-                    ? regend[regno] : end_match_1);
-           for (;;)
-             {
-               /* If necessary, advance to next segment in register
-                  contents.  */
-               while (d2 == dend2)
-                 {
-                   if (dend2 == end_match_2) break;
-                   if (dend2 == regend[regno]) break;
-
-                   /* End of string1 => advance to string2. */
-                   d2 = string2;
-                   dend2 = regend[regno];
-                 }
-               /* At end of register contents => success */
-               if (d2 == dend2) break;
-
-               /* If necessary, advance to next segment in data.  */
-               PREFETCH ();
-
-               /* How many characters left in this segment to match.  */
-               mcnt = dend - d;
-
-               /* Want how many consecutive characters we can match in
-                  one shot, so, if necessary, adjust the count.  */
-               if (mcnt > dend2 - d2)
-                 mcnt = dend2 - d2;
-
-               /* Compare that many; failure if mismatch, else move
-                  past them.  */
-               if (translate
-                   ? bcmp_translate (d, d2, mcnt, translate)
-                   : bcmp (d, d2, mcnt))
-                 goto fail;
-               d += mcnt, d2 += mcnt;
-             }
-         }
-         break;
-
-
-       /* begline matches the empty string at the beginning of the string
-          (unless `not_bol' is set in `bufp'), and, if
-          `newline_anchor' is set, after newlines.  */
-       case begline:
-         DEBUG_PRINT1 ("EXECUTING begline.\n");
-
-         if (AT_STRINGS_BEG (d))
-           {
-             if (!bufp->not_bol) break;
-           }
-         else if (d[-1] == '\n' && bufp->newline_anchor)
-           {
-             break;
-           }
-         /* In all other cases, we fail.  */
-         goto fail;
-
-
-       /* endline is the dual of begline.  */
-       case endline:
-         DEBUG_PRINT1 ("EXECUTING endline.\n");
-
-         if (AT_STRINGS_END (d))
-           {
-             if (!bufp->not_eol) break;
-           }
-
-         /* We have to ``prefetch'' the next character.  */
-         else if ((d == end1 ? *string2 : *d) == '\n'
-                  && bufp->newline_anchor)
-           {
-             break;
-           }
-         goto fail;
-
-
-       /* Match at the very beginning of the data.  */
-       case begbuf:
-         DEBUG_PRINT1 ("EXECUTING begbuf.\n");
-         if (AT_STRINGS_BEG (d))
-           break;
-         goto fail;
-
-
-       /* Match at the very end of the data.  */
-       case endbuf:
-         DEBUG_PRINT1 ("EXECUTING endbuf.\n");
-         if (AT_STRINGS_END (d))
-           break;
-         goto fail;
-
-
-       /* on_failure_keep_string_jump is used to optimize `.*\n'.  It
-          pushes NULL as the value for the string on the stack.  Then
-          `pop_failure_point' will keep the current value for the
-          string, instead of restoring it.  To see why, consider
-          matching `foo\nbar' against `.*\n'.  The .* matches the foo;
-          then the . fails against the \n.  But the next thing we want
-          to do is match the \n against the \n; if we restored the
-          string value, we would be back at the foo.
-
-          Because this is used only in specific cases, we don't need to
-          check all the things that `on_failure_jump' does, to make
-          sure the right things get saved on the stack.  Hence we don't
-          share its code.  The only reason to push anything on the
-          stack at all is that otherwise we would have to change
-          `anychar's code to do something besides goto fail in this
-          case; that seems worse than this.  */
-       case on_failure_keep_string_jump:
-         DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
-
-         EXTRACT_NUMBER_AND_INCR (mcnt, p);
-         DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
-
-         PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
-         break;
-
-
-       /* Uses of on_failure_jump:
-
-          Each alternative starts with an on_failure_jump that points
-          to the beginning of the next alternative.  Each alternative
-          except the last ends with a jump that in effect jumps past
-          the rest of the alternatives.  (They really jump to the
-          ending jump of the following alternative, because tensioning
-          these jumps is a hassle.)
-
-          Repeats start with an on_failure_jump that points past both
-          the repetition text and either the following jump or
-          pop_failure_jump back to this on_failure_jump.  */
-       case on_failure_jump:
-       on_failure:
-         DEBUG_PRINT1 ("EXECUTING on_failure_jump");
-
-         EXTRACT_NUMBER_AND_INCR (mcnt, p);
-         DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
-
-         /* If this on_failure_jump comes right before a group (i.e.,
-            the original * applied to a group), save the information
-            for that group and all inner ones, so that if we fail back
-            to this point, the group's information will be correct.
-            For example, in \(a*\)*\1, we need the preceding group,
-            and in \(\(a*\)b*\)\2, we need the inner group.  */
-
-         /* We can't use `p' to check ahead because we push
-            a failure point to `p + mcnt' after we do this.  */
-         p1 = p;
-
-         /* We need to skip no_op's before we look for the
-            start_memory in case this on_failure_jump is happening as
-            the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
-            against aba.  */
-         while (p1 < pend && (re_opcode_t) *p1 == no_op)
-           p1++;
-
-         if (p1 < pend && (re_opcode_t) *p1 == start_memory)
-           {
-             /* We have a new highest active register now.  This will
-                get reset at the start_memory we are about to get to,
-                but we will have saved all the registers relevant to
-                this repetition op, as described above.  */
-             highest_active_reg = *(p1 + 1) + *(p1 + 2);
-             if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
-               lowest_active_reg = *(p1 + 1);
-           }
-
-         DEBUG_PRINT1 (":\n");
-         PUSH_FAILURE_POINT (p + mcnt, d, -2);
-         break;
-
-
-       /* A smart repeat ends with `maybe_pop_jump'.
-          We change it to either `pop_failure_jump' or `jump'.  */
-       case maybe_pop_jump:
-         EXTRACT_NUMBER_AND_INCR (mcnt, p);
-         DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
-         {
-           register unsigned char *p2 = p;
-
-           /* Compare the beginning of the repeat with what in the
-              pattern follows its end. If we can establish that there
-              is nothing that they would both match, i.e., that we
-              would have to backtrack because of (as in, e.g., `a*a')
-              then we can change to pop_failure_jump, because we'll
-              never have to backtrack.
-
-              This is not true in the case of alternatives: in
-              `(a|ab)*' we do need to backtrack to the `ab' alternative
-              (e.g., if the string was `ab').  But instead of trying to
-              detect that here, the alternative has put on a dummy
-              failure point which is what we will end up popping.  */
-
-           /* Skip over open/close-group commands.  */
-           while (p2 + 2 < pend
-                  && ((re_opcode_t) *p2 == stop_memory
-                      || (re_opcode_t) *p2 == start_memory))
-             p2 += 3;                  /* Skip over args, too.  */
-
-           /* If we're at the end of the pattern, we can change.  */
-           if (p2 == pend)
-             {
-               /* Consider what happens when matching ":\(.*\)"
-                  against ":/".  I don't really understand this code
-                  yet.  */
-               p[-3] = (unsigned char) pop_failure_jump;
-               DEBUG_PRINT1
-                 ("  End of pattern: change to `pop_failure_jump'.\n");
-             }
-
-           else if ((re_opcode_t) *p2 == exactn
-                    || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
-             {
-               register unsigned char c
-                 = *p2 == (unsigned char) endline ? '\n' : p2[2];
-               p1 = p + mcnt;
-
-               /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
-                  to the `maybe_finalize_jump' of this case.  Examine what
-                  follows.  */
-               if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
-                 {
-                   p[-3] = (unsigned char) pop_failure_jump;
-                   DEBUG_PRINT3 ("  %c != %c => pop_failure_jump.\n",
-                                 c, p1[5]);
-                 }
-
-               else if ((re_opcode_t) p1[3] == charset
-                        || (re_opcode_t) p1[3] == charset_not)
-                 {
-                   int not = (re_opcode_t) p1[3] == charset_not;
-
-                   if (c < (unsigned char) (p1[4] * BYTEWIDTH)
-                       && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
-                     not = !not;
-
-                   /* `not' is equal to 1 if c would match, which means
-                       that we can't change to pop_failure_jump.  */
-                   if (!not)
-                     {
-                       p[-3] = (unsigned char) pop_failure_jump;
-                       DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
-                     }
-                 }
-             }
-         }
-         p -= 2;               /* Point at relative address again.  */
-         if ((re_opcode_t) p[-1] != pop_failure_jump)
-           {
-             p[-1] = (unsigned char) jump;
-             DEBUG_PRINT1 ("  Match => jump.\n");
-             goto unconditional_jump;
-           }
-       /* Note fall through.  */
-
-
-       /* The end of a simple repeat has a pop_failure_jump back to
-          its matching on_failure_jump, where the latter will push a
-          failure point.  The pop_failure_jump takes off failure
-          points put on by this pop_failure_jump's matching
-          on_failure_jump; we got through the pattern to here from the
-          matching on_failure_jump, so didn't fail.  */
-       case pop_failure_jump:
-         {
-           /* We need to pass separate storage for the lowest and
-              highest registers, even though we don't care about the
-              actual values.  Otherwise, we will restore only one
-              register from the stack, since lowest will == highest in
-              `pop_failure_point'.  */
-           unsigned dummy_low_reg, dummy_high_reg;
-           unsigned char *pdummy;
-           const char *sdummy;
-
-           DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
-           POP_FAILURE_POINT (sdummy, pdummy,
-                              dummy_low_reg, dummy_high_reg,
-                              reg_dummy, reg_dummy, reg_info_dummy);
-         }
-         /* Note fall through.  */
-
-
-       /* Unconditionally jump (without popping any failure points).  */
-       case jump:
-       unconditional_jump:
-         EXTRACT_NUMBER_AND_INCR (mcnt, p);    /* Get the amount to jump.  */
-         DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
-         p += mcnt;                            /* Do the jump.  */
-         DEBUG_PRINT2 ("(to 0x%x).\n", p);
-         break;
-
-
-       /* We need this opcode so we can detect where alternatives end
-          in `group_match_null_string_p' et al.  */
-       case jump_past_alt:
-         DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
-         goto unconditional_jump;
-
-
-       /* Normally, the on_failure_jump pushes a failure point, which
-          then gets popped at pop_failure_jump.  We will end up at
-          pop_failure_jump, also, and with a pattern of, say, `a+', we
-          are skipping over the on_failure_jump, so we have to push
-          something meaningless for pop_failure_jump to pop.  */
-       case dummy_failure_jump:
-         DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
-         /* It doesn't matter what we push for the string here.  What
-            the code at `fail' tests is the value for the pattern.  */
-         PUSH_FAILURE_POINT (0, 0, -2);
-         goto unconditional_jump;
-
-
-       /* At the end of an alternative, we need to push a dummy failure
-          point in case we are followed by a `pop_failure_jump', because
-          we don't want the failure point for the alternative to be
-          popped.  For example, matching `(a|ab)*' against `aab'
-          requires that we match the `ab' alternative.  */
-       case push_dummy_failure:
-         DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
-         /* See comments just above at `dummy_failure_jump' about the
-            two zeroes.  */
-         PUSH_FAILURE_POINT (0, 0, -2);
-         break;
-
-       /* Have to succeed matching what follows at least n times.
-          After that, handle like `on_failure_jump'.  */
-       case succeed_n:
-         EXTRACT_NUMBER (mcnt, p + 2);
-         DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
-
-         assert (mcnt >= 0);
-         /* Originally, this is how many times we HAVE to succeed.  */
-         if (mcnt > 0)
-           {
-              mcnt--;
-              p += 2;
-              STORE_NUMBER_AND_INCR (p, mcnt);
-              DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p, mcnt);
-           }
-         else if (mcnt == 0)
-           {
-             DEBUG_PRINT2 ("  Setting two bytes from 0x%x to no_op.\n", p+2);
-             p[2] = (unsigned char) no_op;
-             p[3] = (unsigned char) no_op;
-             goto on_failure;
-           }
-         break;
-
-       case jump_n:
-         EXTRACT_NUMBER (mcnt, p + 2);
-         DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
-
-         /* Originally, this is how many times we CAN jump.  */
-         if (mcnt)
-           {
-              mcnt--;
-              STORE_NUMBER (p + 2, mcnt);
-              goto unconditional_jump;
-           }
-         /* If don't have to jump any more, skip over the rest of command.  */
-         else
-           p += 4;
-         break;
-
-       case set_number_at:
-         {
-           DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
-
-           EXTRACT_NUMBER_AND_INCR (mcnt, p);
-           p1 = p + mcnt;
-           EXTRACT_NUMBER_AND_INCR (mcnt, p);
-           DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p1, mcnt);
-           STORE_NUMBER (p1, mcnt);
-           break;
-         }
-
-       case wordbound:
-         DEBUG_PRINT1 ("EXECUTING wordbound.\n");
-         if (AT_WORD_BOUNDARY (d))
-           break;
-         goto fail;
-
-       case notwordbound:
-         DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
-         if (AT_WORD_BOUNDARY (d))
-           goto fail;
-         break;
-
-       case wordbeg:
-         DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
-         if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
-           break;
-         goto fail;
-
-       case wordend:
-         DEBUG_PRINT1 ("EXECUTING wordend.\n");
-         if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
-             && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
-           break;
-         goto fail;
-
-#ifdef emacs
-#ifdef emacs19
-       case before_dot:
-         DEBUG_PRINT1 ("EXECUTING before_dot.\n");
-         if (PTR_CHAR_POS ((unsigned char *) d) >= point)
-           goto fail;
-         break;
-
-       case at_dot:
-         DEBUG_PRINT1 ("EXECUTING at_dot.\n");
-         if (PTR_CHAR_POS ((unsigned char *) d) != point)
-           goto fail;
-         break;
-
-       case after_dot:
-         DEBUG_PRINT1 ("EXECUTING after_dot.\n");
-         if (PTR_CHAR_POS ((unsigned char *) d) <= point)
-           goto fail;
-         break;
-#else /* not emacs19 */
-       case at_dot:
-         DEBUG_PRINT1 ("EXECUTING at_dot.\n");
-         if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
-           goto fail;
-         break;
-#endif /* not emacs19 */
-
-       case syntaxspec:
-         DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
-         mcnt = *p++;
-         goto matchsyntax;
-
-       case wordchar:
-         DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
-         mcnt = (int) Sword;
-       matchsyntax:
-         PREFETCH ();
-         if (SYNTAX (*d++) != (enum syntaxcode) mcnt)
-           goto fail;
-         SET_REGS_MATCHED ();
-         break;
-
-       case notsyntaxspec:
-         DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
-         mcnt = *p++;
-         goto matchnotsyntax;
-
-       case notwordchar:
-         DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
-         mcnt = (int) Sword;
-       matchnotsyntax:
-         PREFETCH ();
-         if (SYNTAX (*d++) == (enum syntaxcode) mcnt)
-           goto fail;
-         SET_REGS_MATCHED ();
-         break;
-
-#else /* not emacs */
-       case wordchar:
-         DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
-         PREFETCH ();
-         if (!WORDCHAR_P (d))
-           goto fail;
-         SET_REGS_MATCHED ();
-         d++;
-         break;
-
-       case notwordchar:
-         DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
-         PREFETCH ();
-         if (WORDCHAR_P (d))
-           goto fail;
-         SET_REGS_MATCHED ();
-         d++;
-         break;
-#endif /* not emacs */
-
-       default:
-         abort ();
-       }
-      continue;  /* Successfully executed one pattern command; keep going.  */
-
-
-    /* We goto here if a matching operation fails. */
-    fail:
-      if (!FAIL_STACK_EMPTY ())
-       { /* A restart point is known.  Restore to that state.  */
-         DEBUG_PRINT1 ("\nFAIL:\n");
-         POP_FAILURE_POINT (d, p,
-                            lowest_active_reg, highest_active_reg,
-                            regstart, regend, reg_info);
-
-         /* If this failure point is a dummy, try the next one.  */
-         if (!p)
-           goto fail;
-
-         /* If we failed to the end of the pattern, don't examine *p.  */
-         assert (p <= pend);
-         if (p < pend)
-           {
-             boolean is_a_jump_n = false;
-
-             /* If failed to a backwards jump that's part of a repetition
-                loop, need to pop this failure point and use the next one.  */
-             switch ((re_opcode_t) *p)
-               {
-               case jump_n:
-                 is_a_jump_n = true;
-               case maybe_pop_jump:
-               case pop_failure_jump:
-               case jump:
-                 p1 = p + 1;
-                 EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-                 p1 += mcnt;
-
-                 if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
-                     || (!is_a_jump_n
-                         && (re_opcode_t) *p1 == on_failure_jump))
-                   goto fail;
-                 break;
-               default:
-                 /* do nothing */ ;
-               }
-           }
-
-         if (d >= string1 && d <= end1)
-           dend = end_match_1;
-       }
-      else
-       break;   /* Matching at this starting point really fails.  */
-    } /* for (;;) */
-
-  if (best_regs_set)
-    goto restore_best_regs;
-
-  FREE_VARIABLES ();
-
-  return -1;                           /* Failure to match.  */
-} /* re_match_2 */
-\f
-/* Subroutine definitions for re_match_2.  */
-
-
-/* We are passed P pointing to a register number after a start_memory.
-
-   Return true if the pattern up to the corresponding stop_memory can
-   match the empty string, and false otherwise.
-
-   If we find the matching stop_memory, sets P to point to one past its number.
-   Otherwise, sets P to an undefined byte less than or equal to END.
-
-   We don't handle duplicates properly (yet).  */
-
-static boolean
-group_match_null_string_p (p, end, reg_info)
-    unsigned char **p, *end;
-    register_info_type *reg_info;
-{
-  int mcnt;
-  /* Point to after the args to the start_memory.  */
-  unsigned char *p1 = *p + 2;
-
-  while (p1 < end)
-    {
-      /* Skip over opcodes that can match nothing, and return true or
-        false, as appropriate, when we get to one that can't, or to the
-        matching stop_memory.  */
-
-      switch ((re_opcode_t) *p1)
-       {
-       /* Could be either a loop or a series of alternatives.  */
-       case on_failure_jump:
-         p1++;
-         EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-
-         /* If the next operation is not a jump backwards in the
-            pattern.  */
-
-         if (mcnt >= 0)
-           {
-             /* Go through the on_failure_jumps of the alternatives,
-                seeing if any of the alternatives cannot match nothing.
-                The last alternative starts with only a jump,
-                whereas the rest start with on_failure_jump and end
-                with a jump, e.g., here is the pattern for `a|b|c':
-
-                /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
-                /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
-                /exactn/1/c
-
-                So, we have to first go through the first (n-1)
-                alternatives and then deal with the last one separately.  */
-
-
-             /* Deal with the first (n-1) alternatives, which start
-                with an on_failure_jump (see above) that jumps to right
-                past a jump_past_alt.  */
-
-             while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
-               {
-                 /* `mcnt' holds how many bytes long the alternative
-                    is, including the ending `jump_past_alt' and
-                    its number.  */
-
-                 if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
-                                                     reg_info))
-                   return false;
-
-                 /* Move to right after this alternative, including the
-                    jump_past_alt.  */
-                 p1 += mcnt;
-
-                 /* Break if it's the beginning of an n-th alternative
-                    that doesn't begin with an on_failure_jump.  */
-                 if ((re_opcode_t) *p1 != on_failure_jump)
-                   break;
-
-                 /* Still have to check that it's not an n-th
-                    alternative that starts with an on_failure_jump.  */
-                 p1++;
-                 EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-                 if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
-                   {
-                     /* Get to the beginning of the n-th alternative.  */
-                     p1 -= 3;
-                     break;
-                   }
-               }
-
-             /* Deal with the last alternative: go back and get number
-                of the `jump_past_alt' just before it.  `mcnt' contains
-                the length of the alternative.  */
-             EXTRACT_NUMBER (mcnt, p1 - 2);
-
-             if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
-               return false;
-
-             p1 += mcnt;       /* Get past the n-th alternative.  */
-           } /* if mcnt > 0 */
-         break;
-
-
-       case stop_memory:
-         assert (p1[1] == **p);
-         *p = p1 + 2;
-         return true;
-
-
-       default:
-         if (!common_op_match_null_string_p (&p1, end, reg_info))
-           return false;
-       }
-    } /* while p1 < end */
-
-  return false;
-} /* group_match_null_string_p */
-
-
-/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
-   It expects P to be the first byte of a single alternative and END one
-   byte past the last. The alternative can contain groups.  */
-
-static boolean
-alt_match_null_string_p (p, end, reg_info)
-    unsigned char *p, *end;
-    register_info_type *reg_info;
-{
-  int mcnt;
-  unsigned char *p1 = p;
-
-  while (p1 < end)
-    {
-      /* Skip over opcodes that can match nothing, and break when we get
-        to one that can't.  */
-
-      switch ((re_opcode_t) *p1)
-       {
-       /* It's a loop.  */
-       case on_failure_jump:
-         p1++;
-         EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-         p1 += mcnt;
-         break;
-
-       default:
-         if (!common_op_match_null_string_p (&p1, end, reg_info))
-           return false;
-       }
-    }  /* while p1 < end */
-
-  return true;
-} /* alt_match_null_string_p */
-
-
-/* Deals with the ops common to group_match_null_string_p and
-   alt_match_null_string_p.
-
-   Sets P to one after the op and its arguments, if any.  */
-
-static boolean
-common_op_match_null_string_p (p, end, reg_info)
-    unsigned char **p, *end;
-    register_info_type *reg_info;
-{
-  int mcnt;
-  boolean ret;
-  int reg_no;
-  unsigned char *p1 = *p;
-
-  switch ((re_opcode_t) *p1++)
-    {
-    case no_op:
-    case begline:
-    case endline:
-    case begbuf:
-    case endbuf:
-    case wordbeg:
-    case wordend:
-    case wordbound:
-    case notwordbound:
-#ifdef emacs
-    case before_dot:
-    case at_dot:
-    case after_dot:
-#endif
-      break;
-
-    case start_memory:
-      reg_no = *p1;
-      assert (reg_no > 0 && reg_no <= MAX_REGNUM);
-      ret = group_match_null_string_p (&p1, end, reg_info);
-
-      /* Have to set this here in case we're checking a group which
-        contains a group and a back reference to it.  */
-
-      if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
-       REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
-
-      if (!ret)
-       return false;
-      break;
-
-    /* If this is an optimized succeed_n for zero times, make the jump.  */
-    case jump:
-      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-      if (mcnt >= 0)
-       p1 += mcnt;
-      else
-       return false;
-      break;
-
-    case succeed_n:
-      /* Get to the number of times to succeed.  */
-      p1 += 2;
-      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-
-      if (mcnt == 0)
-       {
-         p1 -= 4;
-         EXTRACT_NUMBER_AND_INCR (mcnt, p1);
-         p1 += mcnt;
-       }
-      else
-       return false;
-      break;
-
-    case duplicate:
-      if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
-       return false;
-      break;
-
-    case set_number_at:
-      p1 += 4;
-
-    default:
-      /* All other opcodes mean we cannot match the empty string.  */
-      return false;
-  }
-
-  *p = p1;
-  return true;
-} /* common_op_match_null_string_p */
-
-
-/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
-   bytes; nonzero otherwise.  */
-
-static int
-bcmp_translate(
-     unsigned char *s1,
-     unsigned char *s2,
-     int len,
-     char *translate
-)
-{
-  register unsigned char *p1 = s1, *p2 = s2;
-  while (len)
-    {
-      if (translate[*p1++] != translate[*p2++]) return 1;
-      len--;
-    }
-  return 0;
-}
-\f
-/* Entry points for GNU code.  */
-
-/* re_compile_pattern is the GNU regular expression compiler: it
-   compiles PATTERN (of length SIZE) and puts the result in BUFP.
-   Returns 0 if the pattern was valid, otherwise an error string.
-
-   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
-   are set in BUFP on entry.
-
-   We call regex_compile to do the actual compilation.  */
-
-const char *
-re_compile_pattern (pattern, length, bufp)
-     const char *pattern;
-     int length;
-     struct re_pattern_buffer *bufp;
-{
-  reg_errcode_t ret;
-
-  /* GNU code is written to assume at least RE_NREGS registers will be set
-     (and at least one extra will be -1).  */
-  bufp->regs_allocated = REGS_UNALLOCATED;
-
-  /* And GNU code determines whether or not to get register information
-     by passing null for the REGS argument to re_match, etc., not by
-     setting no_sub.  */
-  bufp->no_sub = 0;
-
-  /* Match anchors at newline.  */
-  bufp->newline_anchor = 1;
-
-  ret = regex_compile (pattern, length, re_syntax_options, bufp);
-
-  return re_error_msg[(int) ret];
-}
-\f
-/* Entry points compatible with 4.2 BSD regex library.  We don't define
-   them if this is an Emacs or POSIX compilation.  */
-
-#if !defined (emacs) && !defined (_POSIX_SOURCE)
-
-/* BSD has one and only one pattern buffer.  */
-static struct re_pattern_buffer re_comp_buf;
-
-char *
-re_comp (s)
-    const char *s;
-{
-  reg_errcode_t ret;
-
-  if (!s)
-    {
-      if (!re_comp_buf.buffer)
-       return "No previous regular expression";
-      return 0;
-    }
-
-  if (!re_comp_buf.buffer)
-    {
-      re_comp_buf.buffer = (unsigned char *) malloc (200);
-      if (re_comp_buf.buffer == NULL)
-       return "Memory exhausted";
-      re_comp_buf.allocated = 200;
-
-      re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
-      if (re_comp_buf.fastmap == NULL)
-       return "Memory exhausted";
-    }
-
-  /* Since `re_exec' always passes NULL for the `regs' argument, we
-     don't need to initialize the pattern buffer fields which affect it.  */
-
-  /* Match anchors at newlines.  */
-  re_comp_buf.newline_anchor = 1;
-
-  ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
-
-  /* Yes, we're discarding `const' here.  */
-  return (char *) re_error_msg[(int) ret];
-}
-
-
-int
-re_exec (s)
-    const char *s;
-{
-  const int len = strlen (s);
-  return
-    0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
-}
-#endif /* not emacs and not _POSIX_SOURCE */
-\f
-/* POSIX.2 functions.  Don't define these for Emacs.  */
-
-#ifndef emacs
-
-/* regcomp takes a regular expression as a string and compiles it.
-
-   PREG is a regex_t *.  We do not expect any fields to be initialized,
-   since POSIX says we shouldn't.  Thus, we set
-
-     `buffer' to the compiled pattern;
-     `used' to the length of the compiled pattern;
-     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
-       REG_EXTENDED bit in CFLAGS is set; otherwise, to
-       RE_SYNTAX_POSIX_BASIC;
-     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
-     `fastmap' and `fastmap_accurate' to zero;
-     `re_nsub' to the number of subexpressions in PATTERN.
-
-   PATTERN is the address of the pattern string.
-
-   CFLAGS is a series of bits which affect compilation.
-
-     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
-     use POSIX basic syntax.
-
-     If REG_NEWLINE is set, then . and [^...] don't match newline.
-     Also, regexec will try a match beginning after every newline.
-
-     If REG_ICASE is set, then we considers upper- and lowercase
-     versions of letters to be equivalent when matching.
-
-     If REG_NOSUB is set, then when PREG is passed to regexec, that
-     routine will report only success or failure, and nothing about the
-     registers.
-
-   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
-   the return codes and their meanings.)  */
-
-int
-regcomp (preg, pattern, cflags)
-    regex_t *preg;
-    const char *pattern;
-    int cflags;
-{
-  reg_errcode_t ret;
-  unsigned syntax
-    = (cflags & REG_EXTENDED) ?
-      RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
-
-  /* regex_compile will allocate the space for the compiled pattern.  */
-  preg->buffer = 0;
-  preg->allocated = 0;
-
-  /* Don't bother to use a fastmap when searching.  This simplifies the
-     REG_NEWLINE case: if we used a fastmap, we'd have to put all the
-     characters after newlines into the fastmap.  This way, we just try
-     every character.  */
-  preg->fastmap = 0;
-
-  if (cflags & REG_ICASE)
-    {
-      unsigned i;
-
-      preg->translate = (char *) malloc (CHAR_SET_SIZE);
-      if (preg->translate == NULL)
-       return (int) REG_ESPACE;
-
-      /* Map uppercase characters to corresponding lowercase ones.  */
-      for (i = 0; i < CHAR_SET_SIZE; i++)
-       preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
-    }
-  else
-    preg->translate = NULL;
-
-  /* If REG_NEWLINE is set, newlines are treated differently.  */
-  if (cflags & REG_NEWLINE)
-    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
-      syntax &= ~RE_DOT_NEWLINE;
-      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
-      /* It also changes the matching behavior.  */
-      preg->newline_anchor = 1;
-    }
-  else
-    preg->newline_anchor = 0;
-
-  preg->no_sub = !!(cflags & REG_NOSUB);
-
-  /* POSIX says a null character in the pattern terminates it, so we
-     can use strlen here in compiling the pattern.  */
-  ret = regex_compile (pattern, strlen (pattern), syntax, preg);
-
-  /* POSIX doesn't distinguish between an unmatched open-group and an
-     unmatched close-group: both are REG_EPAREN.  */
-  if (ret == REG_ERPAREN) ret = REG_EPAREN;
-
-  return (int) ret;
-}
-
-
-/* regexec searches for a given pattern, specified by PREG, in the
-   string STRING.
-
-   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
-   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
-   least NMATCH elements, and we set them to the offsets of the
-   corresponding matched substrings.
-
-   EFLAGS specifies `execution flags' which affect matching: if
-   REG_NOTBOL is set, then ^ does not match at the beginning of the
-   string; if REG_NOTEOL is set, then $ does not match at the end.
-
-   We return 0 if we find a match and REG_NOMATCH if not.  */
-
-int
-regexec (preg, string, nmatch, pmatch, eflags)
-    const regex_t *preg;
-    const char *string;
-    size_t nmatch;
-    regmatch_t pmatch[];
-    int eflags;
-{
-  int ret;
-  struct re_registers regs;
-  regex_t private_preg;
-  int len = strlen (string);
-  boolean want_reg_info = !preg->no_sub && nmatch > 0;
-
-  private_preg = *preg;
-
-  private_preg.not_bol = !!(eflags & REG_NOTBOL);
-  private_preg.not_eol = !!(eflags & REG_NOTEOL);
-
-  /* The user has told us exactly how many registers to return
-     information about, via `nmatch'.  We have to pass that on to the
-     matching routines.  */
-  private_preg.regs_allocated = REGS_FIXED;
-
-  if (want_reg_info)
-    {
-      regs.num_regs = nmatch;
-      regs.start = TALLOC (nmatch, regoff_t);
-      regs.end = TALLOC (nmatch, regoff_t);
-      if (regs.start == NULL || regs.end == NULL)
-       return (int) REG_NOMATCH;
-    }
-
-  /* Perform the searching operation.  */
-  ret = re_search (&private_preg, string, len,
-                  /* start: */ 0, /* range: */ len,
-                  want_reg_info ? &regs : (struct re_registers *) 0);
-
-  /* Copy the register information to the POSIX structure.  */
-  if (want_reg_info)
-    {
-      if (ret >= 0)
-       {
-         unsigned r;
-
-         for (r = 0; r < nmatch; r++)
-           {
-             pmatch[r].rm_so = regs.start[r];
-             pmatch[r].rm_eo = regs.end[r];
-           }
-       }
-
-      /* If we needed the temporary register info, free the space now.  */
-      free (regs.start);
-      free (regs.end);
-    }
-
-  /* We want zero return to mean success, unlike `re_search'.  */
-  return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
-}
-
-
-/* Returns a message corresponding to an error code, ERRCODE, returned
-   from either regcomp or regexec.   We don't use PREG here.  */
-
-size_t
-regerror(int errcode, const regex_t *preg,
-        char *errbuf, size_t errbuf_size)
-{
-  const char *msg;
-  size_t msg_size;
-
-  if (errcode < 0
-      || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0])))
-    /* Only error codes returned by the rest of the code should be passed
-       to this routine.  If we are given anything else, or if other regex
-       code generates an invalid error code, then the program has a bug.
-       Dump core so we can fix it.  */
-    abort ();
-
-  msg = re_error_msg[errcode];
-
-  /* POSIX doesn't require that we do anything in this case, but why
-     not be nice.  */
-  if (! msg)
-    msg = "Success";
-
-  msg_size = strlen (msg) + 1; /* Includes the null.  */
-
-  if (errbuf_size != 0)
-    {
-      if (msg_size > errbuf_size)
-       {
-         strncpy (errbuf, msg, errbuf_size - 1);
-         errbuf[errbuf_size - 1] = 0;
-       }
-      else
-       strcpy (errbuf, msg);
-    }
-
-  return msg_size;
-}
-
-
-/* Free dynamically allocated space used by PREG.  */
-
-void
-regfree (preg)
-    regex_t *preg;
-{
-  if (preg->buffer != NULL)
-    free (preg->buffer);
-  preg->buffer = NULL;
-
-  preg->allocated = 0;
-  preg->used = 0;
-
-  if (preg->fastmap != NULL)
-    free (preg->fastmap);
-  preg->fastmap = NULL;
-  preg->fastmap_accurate = 0;
-
-  if (preg->translate != NULL)
-    free (preg->translate);
-  preg->translate = NULL;
-}
-
-#endif /* not emacs  */
-\f
-/*
-Local variables:
-make-backup-files: t
-version-control: t
-trim-versions-without-asking: nil
-End:
-*/
index 6eb64f14020db0a20ba596de5b58b3c552157f16..61c96838721501031935dd560eb86d8dbcedd2b0 100644 (file)
@@ -1,70 +1,90 @@
-/* Definitions for data structures and routines for the regular
-   expression library, version 0.12.
+#include <stdio.h>
+#include <stddef.h>
 
-   Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+/* Definitions for data structures and routines for the regular
+   expression library.
+   Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008
+   Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
 
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
-   This program is distributed in the hope that it will be useful,
+   The GNU C Library 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.  */
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
 
-#ifndef __REGEXP_LIBRARY_H__
-#define __REGEXP_LIBRARY_H__
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA.  */
 
-/* POSIX says that <sys/types.h> must be included (by the caller) before
-   <regex.h>.  */
+#ifndef _REGEX_H
+#define _REGEX_H 1
 
-#ifdef VMS
-/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
-   should be there.  */
+#ifdef HAVE_STDDEF_H
 #include <stddef.h>
 #endif
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifndef _LIBC
+#define __USE_GNU      1
+#endif
+
+/* Allow the use in C++ code.  */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The following two types have to be signed and unsigned integer type
+   wide enough to hold a value of a pointer.  For most ANSI compilers
+   ptrdiff_t and size_t should be likely OK.  Still size of these two
+   types is 2 for Microsoft C.  Ugh... */
+typedef long int s_reg_t;
+typedef unsigned long int active_reg_t;
 
 /* The following bits are used to determine the regexp syntax we
    recognize.  The set/not-set meanings are chosen so that Emacs syntax
    remains the value 0.  The bits are given in alphabetical order, and
    the definitions shifted by one from the previous bit; thus, when we
    add or remove a bit, only one other definition need change.  */
-typedef unsigned reg_syntax_t;
+typedef unsigned long int reg_syntax_t;
 
+#ifdef __USE_GNU
 /* If this bit is not set, then \ inside a bracket expression is literal.
    If set, then such a \ quotes the following character.  */
-#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+# define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
 
 /* If this bit is not set, then + and ? are operators, and \+ and \? are
      literals.
    If set, then \+ and \? are operators and + and ? are literals.  */
-#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+# define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
 
 /* If this bit is set, then character classes are supported.  They are:
      [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
      [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
    If not set, then character classes are not supported.  */
-#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+# define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
 
 /* If this bit is set, then ^ and $ are always anchors (outside bracket
      expressions, of course).
    If this bit is not set, then it depends:
-       ^  is an anchor if it is at the beginning of a regular
-          expression or after an open-group or an alternation operator;
-       $  is an anchor if it is at the end of a regular expression, or
-          before a close-group or an alternation operator.
+        ^  is an anchor if it is at the beginning of a regular
+           expression or after an open-group or an alternation operator;
+        $  is an anchor if it is at the end of a regular expression, or
+           before a close-group or an alternation operator.
 
    This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
    POSIX draft 11.2 says that * etc. in leading positions is undefined.
    We already implemented a previous draft which made those constructs
    invalid, though, so we haven't changed the code back.  */
-#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+# define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
 
 /* If this bit is set, then special characters are always special
      regardless of where they are in the pattern.
@@ -72,63 +92,94 @@ typedef unsigned reg_syntax_t;
      some contexts; otherwise they are ordinary.  Specifically,
      * + ? and intervals are only special when not after the beginning,
      open-group, or alternation operator.  */
-#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+# define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
 
 /* If this bit is set, then *, +, ?, and { cannot be first in an re or
      immediately after an alternation or begin-group operator.  */
-#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+# define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
 
 /* If this bit is set, then . matches newline.
    If not set, then it doesn't.  */
-#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+# define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
 
 /* If this bit is set, then . doesn't match NUL.
    If not set, then it does.  */
-#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+# define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
 
 /* If this bit is set, nonmatching lists [^...] do not match newline.
    If not set, they do.  */
-#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+# define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
 
 /* If this bit is set, either \{...\} or {...} defines an
      interval, depending on RE_NO_BK_BRACES.
    If not set, \{, \}, {, and } are literals.  */
-#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+# define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
 
 /* If this bit is set, +, ? and | aren't recognized as operators.
    If not set, they are.  */
-#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+# define RE_LIMITED_OPS (RE_INTERVALS << 1)
 
 /* If this bit is set, newline is an alternation operator.
    If not set, newline is literal.  */
-#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+# define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
 
 /* If this bit is set, then `{...}' defines an interval, and \{ and \}
      are literals.
   If not set, then `\{...\}' defines an interval.  */
-#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+# define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
 
 /* If this bit is set, (...) defines a group, and \( and \) are literals.
    If not set, \(...\) defines a group, and ( and ) are literals.  */
-#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+# define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
 
 /* If this bit is set, then \<digit> matches <digit>.
    If not set, then \<digit> is a back-reference.  */
-#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+# define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
 
 /* If this bit is set, then | is an alternation operator, and \| is literal.
    If not set, then \| is an alternation operator, and | is literal.  */
-#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+# define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
 
 /* If this bit is set, then an ending range point collating higher
      than the starting range point, as in [z-a], is invalid.
    If not set, then when ending range point collates higher than the
      starting range point, the range is ignored.  */
-#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+# define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
 
 /* If this bit is set, then an unmatched ) is ordinary.
    If not set, then an unmatched ) is invalid.  */
-#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+# define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, succeed as soon as we match the whole pattern,
+   without further backtracking.  */
+# define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* If this bit is set, do not process the GNU regex operators.
+   If not set, then the GNU regex operators are recognized. */
+# define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
+
+/* If this bit is set, a syntactically invalid interval is treated as
+   a string of ordinary characters.  For example, the ERE 'a{1' is
+   treated as 'a\{1'.  */
+# define RE_INVALID_INTERVAL_ORD (RE_NO_GNU_OPS << 1)
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+# define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
+
+/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
+   for ^, because it is difficult to scan the regex backwards to find
+   whether ^ should be special.  */
+# define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)
+
+/* If this bit is set, then \{ cannot be first in an bre or
+   immediately after an alternation or begin-group operator.  */
+# define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)
+
+/* If this bit is set, then no_sub will be set to 1 during
+   re_compile_pattern.  */
+#define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1)
+#endif
 
 /* This global variable defines the particular regexp syntax to use (for
    some interfaces).  When a regexp is compiled, the syntax used is
@@ -136,6 +187,7 @@ typedef unsigned reg_syntax_t;
    already-compiled regexps.  */
 extern reg_syntax_t re_syntax_options;
 \f
+#ifdef __USE_GNU
 /* Define combinations of the above bits for the standard possibilities.
    (The [[[ comments delimit what gets put into the Texinfo file, so
    don't delete them!)  */
@@ -143,13 +195,22 @@ extern reg_syntax_t re_syntax_options;
 #define RE_SYNTAX_EMACS 0
 
 #define RE_SYNTAX_AWK                                                  \
-  (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL                      \
-   | RE_NO_BK_PARENS            | RE_NO_BK_REFS                                \
-   | RE_NO_BK_VBAR               | RE_NO_EMPTY_RANGES                  \
-   | RE_UNMATCHED_RIGHT_PAREN_ORD)
-
-#define RE_SYNTAX_POSIX_AWK                                            \
-  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+  (RE_BACKSLASH_ESCAPE_IN_LISTS   | RE_DOT_NOT_NULL                    \
+   | RE_NO_BK_PARENS              | RE_NO_BK_REFS                      \
+   | RE_NO_BK_VBAR                | RE_NO_EMPTY_RANGES                 \
+   | RE_DOT_NEWLINE              | RE_CONTEXT_INDEP_ANCHORS            \
+   | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GNU_AWK                                              \
+  ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS            \
+   | RE_INVALID_INTERVAL_ORD)                                          \
+   & ~(RE_DOT_NOT_NULL | RE_CONTEXT_INDEP_OPS                          \
+       | RE_CONTEXT_INVALID_OPS ))
+
+#define RE_SYNTAX_POSIX_AWK                                            \
+  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS             \
+   | RE_INTERVALS          | RE_NO_GNU_OPS                             \
+   | RE_INVALID_INTERVAL_ORD)
 
 #define RE_SYNTAX_GREP                                                 \
   (RE_BK_PLUS_QM              | RE_CHAR_CLASSES                                \
@@ -163,7 +224,8 @@ extern reg_syntax_t re_syntax_options;
    | RE_NO_BK_VBAR)
 
 #define RE_SYNTAX_POSIX_EGREP                                          \
-  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES                    \
+   | RE_INVALID_INTERVAL_ORD)
 
 /* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
 #define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
@@ -176,7 +238,7 @@ extern reg_syntax_t re_syntax_options;
    | RE_INTERVALS  | RE_NO_EMPTY_RANGES)
 
 #define RE_SYNTAX_POSIX_BASIC                                          \
-  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)
 
 /* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
    RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
@@ -185,13 +247,13 @@ extern reg_syntax_t re_syntax_options;
   (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
 
 #define RE_SYNTAX_POSIX_EXTENDED                                       \
-  (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS                  \
-   | RE_CONTEXT_INDEP_OPS  | RE_NO_BK_BRACES                           \
-   | RE_NO_BK_PARENS       | RE_NO_BK_VBAR                             \
-   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS                 \
+   | RE_CONTEXT_INDEP_OPS   | RE_NO_BK_BRACES                          \
+   | RE_NO_BK_PARENS        | RE_NO_BK_VBAR                            \
+   | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
 
-/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
-   replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added.  */
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
+   removed and RE_NO_BK_REFS is added.  */
 #define RE_SYNTAX_POSIX_MINIMAL_EXTENDED                               \
   (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS                 \
    | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES                          \
@@ -202,10 +264,12 @@ extern reg_syntax_t re_syntax_options;
 /* Maximum number of duplicates an interval can allow.  Some systems
    (erroneously) define this in other header files, but we want our
    value, so remove any previous define.  */
-#ifdef RE_DUP_MAX
-#undef RE_DUP_MAX
+# ifdef RE_DUP_MAX
+#  undef RE_DUP_MAX
+# endif
+/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows.  */
+# define RE_DUP_MAX (0x7fff)
 #endif
-#define RE_DUP_MAX ((1 << 15) - 1)
 
 
 /* POSIX `cflags' bits (i.e., information for `regcomp').  */
@@ -240,18 +304,26 @@ extern reg_syntax_t re_syntax_options;
 /* Like REG_NOTBOL, except for the end-of-line.  */
 #define REG_NOTEOL (1 << 1)
 
+/* Use PMATCH[0] to delimit the start and end of the search in the
+   buffer.  */
+#define REG_STARTEND (1 << 2)
+
 
 /* If any error codes are removed, changed, or added, update the
    `re_error_msg' table in regex.c.  */
 typedef enum
 {
+#if defined _XOPEN_SOURCE || defined __USE_XOPEN2K
+  REG_ENOSYS = -1,     /* This will never happen for this implementation.  */
+#endif
+
   REG_NOERROR = 0,     /* Success.  */
   REG_NOMATCH,         /* Didn't find a match (for regexec).  */
 
   /* POSIX regcomp return error codes.  (In the order listed in the
      standard.)  */
   REG_BADPAT,          /* Invalid pattern.  */
-  REG_ECOLLATE,                /* Not implemented.  */
+  REG_ECOLLATE,                /* Inalid collating element.  */
   REG_ECTYPE,          /* Invalid character class name.  */
   REG_EESCAPE,         /* Trailing backslash.  */
   REG_ESUBREG,         /* Invalid back reference.  */
@@ -275,85 +347,92 @@ typedef enum
    compiled, the `re_nsub' field is available.  All other fields are
    private to the regex routines.  */
 
+#ifndef RE_TRANSLATE_TYPE
+# define __RE_TRANSLATE_TYPE unsigned char *
+# ifdef __USE_GNU
+#  define RE_TRANSLATE_TYPE __RE_TRANSLATE_TYPE
+# endif
+#endif
+
+#ifdef __USE_GNU
+# define __REPB_PREFIX(name) name
+#else
+# define __REPB_PREFIX(name) __##name
+#endif
+
 struct re_pattern_buffer
 {
-/* [[[begin pattern_buffer]]] */
-       /* Space that holds the compiled pattern.  It is declared as
-         `unsigned char *' because its elements are
-          sometimes used as array indexes.  */
-  unsigned char *buffer;
+  /* Space that holds the compiled pattern.  It is declared as
+     `unsigned char *' because its elements are sometimes used as
+     array indexes.  */
+  unsigned char *__REPB_PREFIX(buffer);
 
-       /* Number of bytes to which `buffer' points.  */
-  unsigned long allocated;
+  /* Number of bytes to which `buffer' points.  */
+  unsigned long int __REPB_PREFIX(allocated);
 
-       /* Number of bytes actually used in `buffer'.  */
-  unsigned long used;
+  /* Number of bytes actually used in `buffer'.  */
+  unsigned long int __REPB_PREFIX(used);
 
-       /* Syntax setting with which the pattern was compiled.  */
-  reg_syntax_t syntax;
+  /* Syntax setting with which the pattern was compiled.  */
+  reg_syntax_t __REPB_PREFIX(syntax);
 
-       /* Pointer to a fastmap, if any, otherwise zero.  re_search uses
-          the fastmap, if there is one, to skip over impossible
-          starting points for matches.  */
-  char *fastmap;
+  /* Pointer to a fastmap, if any, otherwise zero.  re_search uses the
+     fastmap, if there is one, to skip over impossible starting points
+     for matches.  */
+  char *__REPB_PREFIX(fastmap);
 
-       /* Either a translate table to apply to all characters before
-          comparing them, or zero for no translation.  The translation
-          is applied to a pattern when it is compiled and to a string
-          when it is matched.  */
-  char *translate;
+  /* Either a translate table to apply to all characters before
+     comparing them, or zero for no translation.  The translation is
+     applied to a pattern when it is compiled and to a string when it
+     is matched.  */
+  __RE_TRANSLATE_TYPE __REPB_PREFIX(translate);
 
-       /* Number of subexpressions found by the compiler.  */
+  /* Number of subexpressions found by the compiler.  */
   size_t re_nsub;
 
-       /* Zero if this pattern cannot match the empty string, one else.
-          Well, in truth it's used only in `re_search_2', to see
-          whether or not we should use the fastmap, so we don't set
-          this absolutely perfectly; see `re_compile_fastmap' (the
-          `duplicate' case).  */
-  unsigned can_be_null : 1;
-
-       /* If REGS_UNALLOCATED, allocate space in the `regs' structure
-            for `max (RE_NREGS, re_nsub + 1)' groups.
-          If REGS_REALLOCATE, reallocate space if necessary.
-          If REGS_FIXED, use what's there.  */
-#define REGS_UNALLOCATED 0
-#define REGS_REALLOCATE 1
-#define REGS_FIXED 2
-  unsigned regs_allocated : 2;
-
-       /* Set to zero when `regex_compile' compiles a pattern; set to one
-          by `re_compile_fastmap' if it updates the fastmap.  */
-  unsigned fastmap_accurate : 1;
-
-       /* If set, `re_match_2' does not return information about
-          subexpressions.  */
-  unsigned no_sub : 1;
-
-       /* If set, a beginning-of-line anchor doesn't match at the
-          beginning of the string.  */
-  unsigned not_bol : 1;
-
-       /* Similarly for an end-of-line anchor.  */
-  unsigned not_eol : 1;
-
-       /* If true, an anchor at a newline matches.  */
-  unsigned newline_anchor : 1;
-
-/* [[[end pattern_buffer]]] */
-};
+  /* Zero if this pattern cannot match the empty string, one else.
+     Well, in truth it's used only in `re_search_2', to see whether or
+     not we should use the fastmap, so we don't set this absolutely
+     perfectly; see `re_compile_fastmap' (the `duplicate' case).  */
+  unsigned __REPB_PREFIX(can_be_null) : 1;
+
+  /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+     for `max (RE_NREGS, re_nsub + 1)' groups.
+     If REGS_REALLOCATE, reallocate space if necessary.
+     If REGS_FIXED, use what's there.  */
+#ifdef __USE_GNU
+# define REGS_UNALLOCATED 0
+# define REGS_REALLOCATE 1
+# define REGS_FIXED 2
+#endif
+  unsigned __REPB_PREFIX(regs_allocated) : 2;
 
-typedef struct re_pattern_buffer regex_t;
+  /* Set to zero when `regex_compile' compiles a pattern; set to one
+     by `re_compile_fastmap' if it updates the fastmap.  */
+  unsigned __REPB_PREFIX(fastmap_accurate) : 1;
+
+  /* If set, `re_match_2' does not return information about
+     subexpressions.  */
+  unsigned __REPB_PREFIX(no_sub) : 1;
+
+  /* If set, a beginning-of-line anchor doesn't match at the beginning
+     of the string.  */
+  unsigned __REPB_PREFIX(not_bol) : 1;
+
+  /* Similarly for an end-of-line anchor.  */
+  unsigned __REPB_PREFIX(not_eol) : 1;
 
+  /* If true, an anchor at a newline matches.  */
+  unsigned __REPB_PREFIX(newline_anchor) : 1;
+};
 
-/* search.c (search_buffer) in Emacs needs this one opcode value.  It is
-   defined both in `regex.c' and here.  */
-#define RE_EXACTN_VALUE 1
+typedef struct re_pattern_buffer regex_t;
 \f
 /* Type for byte offsets within the string.  POSIX mandates this.  */
 typedef int regoff_t;
 
 
+#ifdef __USE_GNU
 /* This is the structure we store register match data in.  See
    regex.texinfo for a full description of what registers match.  */
 struct re_registers
@@ -367,8 +446,9 @@ struct re_registers
 /* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
    `re_match_2' returns information about at least this many registers
    the first time a `regs' structure is passed.  */
-#ifndef RE_NREGS
-#define RE_NREGS 30
+# ifndef RE_NREGS
+#  define RE_NREGS 30
+# endif
 #endif
 
 
@@ -383,38 +463,22 @@ typedef struct
 \f
 /* Declarations for routines.  */
 
-/* To avoid duplicating every routine declaration -- once with a
-   prototype (if we are ANSI), and once without (if we aren't) -- we
-   use the following macro to declare argument types.  This
-   unfortunately clutters up the declarations a bit, but I think it's
-   worth it.  */
-
-#if __STDC__
-
-#define _RE_ARGS(args) args
-
-#else /* not __STDC__ */
-
-#define _RE_ARGS(args) ()
-
-#endif /* not __STDC__ */
-
+#ifdef __USE_GNU
 /* Sets the current default syntax to SYNTAX, and return the old syntax.
    You can also simply assign to the `re_syntax_options' variable.  */
-extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax);
 
 /* Compile the regular expression PATTERN, with length LENGTH
    and syntax given by the global `re_syntax_options', into the buffer
    BUFFER.  Return NULL if successful, and an error string if not.  */
-extern const char *re_compile_pattern
-  _RE_ARGS ((const char *pattern, int length,
-            struct re_pattern_buffer *buffer));
+extern const char *re_compile_pattern (const char *__pattern, size_t __length,
+                                      struct re_pattern_buffer *__buffer);
 
 
 /* Compile a fastmap for the compiled pattern in BUFFER; used to
    accelerate searches.  Return 0 if successful and -2 if was an
    internal error.  */
-extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+extern int re_compile_fastmap (struct re_pattern_buffer *__buffer);
 
 
 /* Search in the string STRING (with length LENGTH) for the pattern
@@ -422,31 +486,30 @@ extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
    characters.  Return the starting position of the match, -1 for no
    match, or -2 for an internal error.  Also return register
    information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
-extern int re_search
-  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
-           int length, int start, int range, struct re_registers *regs));
+extern int re_search (struct re_pattern_buffer *__buffer, const char *__cstring,
+                     int __length, int __start, int __range,
+                     struct re_registers *__regs);
 
 
 /* Like `re_search', but search in the concatenation of STRING1 and
    STRING2.  Also, stop searching at index START + STOP.  */
-extern int re_search_2
-  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
-            int length1, const char *string2, int length2,
-            int start, int range, struct re_registers *regs, int stop));
+extern int re_search_2 (struct re_pattern_buffer *__buffer,
+                       const char *__string1, int __length1,
+                       const char *__string2, int __length2, int __start,
+                       int __range, struct re_registers *__regs, int __stop);
 
 
 /* Like `re_search', but return how many characters in STRING the regexp
    in BUFFER matched, starting at position START.  */
-extern int re_match
-  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
-            int length, int start, struct re_registers *regs));
+extern int re_match (struct re_pattern_buffer *__buffer, const char *__cstring,
+                    int __length, int __start, struct re_registers *__regs);
 
 
 /* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
-extern int re_match_2
-  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
-            int length1, const char *string2, int length2,
-            int start, struct re_registers *regs, int stop));
+extern int re_match_2 (struct re_pattern_buffer *__buffer,
+                      const char *__string1, int __length1,
+                      const char *__string2, int __length2, int __start,
+                      struct re_registers *__regs, int __stop);
 
 
 /* Set REGS to hold NUM_REGS registers, storing them in STARTS and
@@ -461,30 +524,59 @@ extern int re_match_2
    Unless this function is called, the first search or match using
    PATTERN_BUFFER will allocate its own register data, without
    freeing the old data.  */
-extern void re_set_registers
-  _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
-            unsigned num_regs, regoff_t *starts, regoff_t *ends));
-
+extern void re_set_registers (struct re_pattern_buffer *__buffer,
+                             struct re_registers *__regs,
+                             unsigned int __num_regs,
+                             regoff_t *__starts, regoff_t *__ends);
+#endif /* Use GNU */
+
+#if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_BSD)
+# ifndef _CRAY
 /* 4.2 bsd compatibility.  */
-extern char *re_comp _RE_ARGS ((const char *));
-extern int re_exec _RE_ARGS ((const char *));
+extern char *re_comp (const char *);
+extern int re_exec (const char *);
+# endif
+#endif
+
+/* GCC 2.95 and later have "__restrict"; C99 compilers have
+   "restrict", and "configure" may have defined "restrict".  */
+#ifndef __restrict
+# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
+#  if defined restrict || 199901L <= __STDC_VERSION__
+#   define __restrict restrict
+#  else
+#   define __restrict
+#  endif
+# endif
+#endif
+/* gcc 3.1 and up support the [restrict] syntax.  */
+#ifndef __restrict_arr
+# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) \
+     && !defined __GNUG__
+#  define __restrict_arr __restrict
+# else
+#  define __restrict_arr
+# endif
+#endif
 
 /* POSIX compatibility.  */
-extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
-extern int regexec
-  _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
-            regmatch_t pmatch[], int eflags));
-extern size_t regerror
-  _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
-            size_t errbuf_size));
-extern void regfree _RE_ARGS ((regex_t *preg));
-
-#endif /* not __REGEXP_LIBRARY_H__ */
-\f
-/*
-Local variables:
-make-backup-files: t
-version-control: t
-trim-versions-without-asking: nil
-End:
-*/
+extern int regcomp (regex_t *__restrict __preg,
+                   const char *__restrict __pattern,
+                   int __cflags);
+
+extern int regexec (const regex_t *__restrict __preg,
+                   const char *__restrict __cstring, size_t __nmatch,
+                   regmatch_t __pmatch[__restrict_arr],
+                   int __eflags);
+
+extern size_t regerror (int __errcode, const regex_t *__restrict __preg,
+                       char *__restrict __errbuf, size_t __errbuf_size);
+
+extern void regfree (regex_t *__preg);
+
+
+#ifdef __cplusplus
+}
+#endif /* C++ */
+
+#endif /* regex.h */
diff --git a/compat/regex/regex_internal.c b/compat/regex/regex_internal.c
new file mode 100644 (file)
index 0000000..193854c
--- /dev/null
@@ -0,0 +1,1744 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002-2006, 2010 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA.  */
+
+static void re_string_construct_common (const char *str, int len,
+                                       re_string_t *pstr,
+                                       RE_TRANSLATE_TYPE trans, int icase,
+                                       const re_dfa_t *dfa) internal_function;
+static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa,
+                                         const re_node_set *nodes,
+                                         unsigned int hash) internal_function;
+static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa,
+                                         const re_node_set *nodes,
+                                         unsigned int context,
+                                         unsigned int hash) internal_function;
+
+#ifdef GAWK
+#undef MAX     /* safety */
+static int
+MAX(size_t a, size_t b)
+{
+       return (a > b ? a : b);
+}
+#endif
+\f
+/* Functions for string operation.  */
+
+/* This function allocate the buffers.  It is necessary to call
+   re_string_reconstruct before using the object.  */
+
+static reg_errcode_t
+internal_function
+re_string_allocate (re_string_t *pstr, const char *str, int len, int init_len,
+                   RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa)
+{
+  reg_errcode_t ret;
+  int init_buf_len;
+
+  /* Ensure at least one character fits into the buffers.  */
+  if (init_len < dfa->mb_cur_max)
+    init_len = dfa->mb_cur_max;
+  init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
+  re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+  ret = re_string_realloc_buffers (pstr, init_buf_len);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  pstr->word_char = dfa->word_char;
+  pstr->word_ops_used = dfa->word_ops_used;
+  pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+  pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len;
+  pstr->valid_raw_len = pstr->valid_len;
+  return REG_NOERROR;
+}
+
+/* This function allocate the buffers, and initialize them.  */
+
+static reg_errcode_t
+internal_function
+re_string_construct (re_string_t *pstr, const char *str, int len,
+                    RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa)
+{
+  reg_errcode_t ret;
+  memset (pstr, '\0', sizeof (re_string_t));
+  re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+  if (len > 0)
+    {
+      ret = re_string_realloc_buffers (pstr, len + 1);
+      if (BE (ret != REG_NOERROR, 0))
+       return ret;
+    }
+  pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+
+  if (icase)
+    {
+#ifdef RE_ENABLE_I18N
+      if (dfa->mb_cur_max > 1)
+       {
+         while (1)
+           {
+             ret = build_wcs_upper_buffer (pstr);
+             if (BE (ret != REG_NOERROR, 0))
+               return ret;
+             if (pstr->valid_raw_len >= len)
+               break;
+             if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max)
+               break;
+             ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+             if (BE (ret != REG_NOERROR, 0))
+               return ret;
+           }
+       }
+      else
+#endif /* RE_ENABLE_I18N  */
+       build_upper_buffer (pstr);
+    }
+  else
+    {
+#ifdef RE_ENABLE_I18N
+      if (dfa->mb_cur_max > 1)
+       build_wcs_buffer (pstr);
+      else
+#endif /* RE_ENABLE_I18N  */
+       {
+         if (trans != NULL)
+           re_string_translate_buffer (pstr);
+         else
+           {
+             pstr->valid_len = pstr->bufs_len;
+             pstr->valid_raw_len = pstr->bufs_len;
+           }
+       }
+    }
+
+  return REG_NOERROR;
+}
+
+/* Helper functions for re_string_allocate, and re_string_construct.  */
+
+static reg_errcode_t
+internal_function
+re_string_realloc_buffers (re_string_t *pstr, int new_buf_len)
+{
+#ifdef RE_ENABLE_I18N
+  if (pstr->mb_cur_max > 1)
+    {
+      wint_t *new_wcs;
+
+      /* Avoid overflow in realloc.  */
+      const size_t max_object_size = MAX (sizeof (wint_t), sizeof (int));
+      if (BE (SIZE_MAX / max_object_size < new_buf_len, 0))
+       return REG_ESPACE;
+
+      new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len);
+      if (BE (new_wcs == NULL, 0))
+       return REG_ESPACE;
+      pstr->wcs = new_wcs;
+      if (pstr->offsets != NULL)
+       {
+         int *new_offsets = re_realloc (pstr->offsets, int, new_buf_len);
+         if (BE (new_offsets == NULL, 0))
+           return REG_ESPACE;
+         pstr->offsets = new_offsets;
+       }
+    }
+#endif /* RE_ENABLE_I18N  */
+  if (pstr->mbs_allocated)
+    {
+      unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char,
+                                          new_buf_len);
+      if (BE (new_mbs == NULL, 0))
+       return REG_ESPACE;
+      pstr->mbs = new_mbs;
+    }
+  pstr->bufs_len = new_buf_len;
+  return REG_NOERROR;
+}
+
+
+static void
+internal_function
+re_string_construct_common (const char *str, int len, re_string_t *pstr,
+                           RE_TRANSLATE_TYPE trans, int icase,
+                           const re_dfa_t *dfa)
+{
+  pstr->raw_mbs = (const unsigned char *) str;
+  pstr->len = len;
+  pstr->raw_len = len;
+  pstr->trans = trans;
+  pstr->icase = icase ? 1 : 0;
+  pstr->mbs_allocated = (trans != NULL || icase);
+  pstr->mb_cur_max = dfa->mb_cur_max;
+  pstr->is_utf8 = dfa->is_utf8;
+  pstr->map_notascii = dfa->map_notascii;
+  pstr->stop = pstr->len;
+  pstr->raw_stop = pstr->stop;
+}
+
+#ifdef RE_ENABLE_I18N
+
+/* Build wide character buffer PSTR->WCS.
+   If the byte sequence of the string are:
+     <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
+   Then wide character buffer will be:
+     <wc1>   , WEOF    , <wc2>   , WEOF    , <wc3>
+   We use WEOF for padding, they indicate that the position isn't
+   a first byte of a multibyte character.
+
+   Note that this function assumes PSTR->VALID_LEN elements are already
+   built and starts from PSTR->VALID_LEN.  */
+
+static void
+internal_function
+build_wcs_buffer (re_string_t *pstr)
+{
+#ifdef _LIBC
+  unsigned char buf[MB_LEN_MAX];
+  assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+  unsigned char buf[64];
+#endif
+  mbstate_t prev_st;
+  int byte_idx, end_idx, remain_len;
+  size_t mbclen;
+
+  /* Build the buffers from pstr->valid_len to either pstr->len or
+     pstr->bufs_len.  */
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+  for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
+    {
+      wchar_t wc;
+      const char *p;
+
+      remain_len = end_idx - byte_idx;
+      prev_st = pstr->cur_state;
+      /* Apply the translation if we need.  */
+      if (BE (pstr->trans != NULL, 0))
+       {
+         int i, ch;
+
+         for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+           {
+             ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i];
+             buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch];
+           }
+         p = (const char *) buf;
+       }
+      else
+       p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx;
+      mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+      if (BE (mbclen == (size_t) -2, 0))
+       {
+         /* The buffer doesn't have enough space, finish to build.  */
+         pstr->cur_state = prev_st;
+         break;
+       }
+      else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
+       {
+         /* We treat these cases as a singlebyte character.  */
+         mbclen = 1;
+         wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+         if (BE (pstr->trans != NULL, 0))
+           wc = pstr->trans[wc];
+         pstr->cur_state = prev_st;
+       }
+
+      /* Write wide character and padding.  */
+      pstr->wcs[byte_idx++] = wc;
+      /* Write paddings.  */
+      for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+       pstr->wcs[byte_idx++] = WEOF;
+    }
+  pstr->valid_len = byte_idx;
+  pstr->valid_raw_len = byte_idx;
+}
+
+/* Build wide character buffer PSTR->WCS like build_wcs_buffer,
+   but for REG_ICASE.  */
+
+static reg_errcode_t
+internal_function
+build_wcs_upper_buffer (re_string_t *pstr)
+{
+  mbstate_t prev_st;
+  int src_idx, byte_idx, end_idx, remain_len;
+  size_t mbclen;
+#ifdef _LIBC
+  char buf[MB_LEN_MAX];
+  assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+  char buf[64];
+#endif
+
+  byte_idx = pstr->valid_len;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  /* The following optimization assumes that ASCII characters can be
+     mapped to wide characters with a simple cast.  */
+  if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed)
+    {
+      while (byte_idx < end_idx)
+       {
+         wchar_t wc;
+
+         if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx])
+             && mbsinit (&pstr->cur_state))
+           {
+             /* In case of a singlebyte character.  */
+             pstr->mbs[byte_idx]
+               = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]);
+             /* The next step uses the assumption that wchar_t is encoded
+                ASCII-safe: all ASCII values can be converted like this.  */
+             pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx];
+             ++byte_idx;
+             continue;
+           }
+
+         remain_len = end_idx - byte_idx;
+         prev_st = pstr->cur_state;
+         mbclen = __mbrtowc (&wc,
+                             ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+                              + byte_idx), remain_len, &pstr->cur_state);
+         if (BE (mbclen + 2 > 2, 1))
+           {
+             wchar_t wcu = wc;
+             if (iswlower (wc))
+               {
+                 size_t mbcdlen;
+
+                 wcu = towupper (wc);
+                 mbcdlen = wcrtomb (buf, wcu, &prev_st);
+                 if (BE (mbclen == mbcdlen, 1))
+                   memcpy (pstr->mbs + byte_idx, buf, mbclen);
+                 else
+                   {
+                     src_idx = byte_idx;
+                     goto offsets_needed;
+                   }
+               }
+             else
+               memcpy (pstr->mbs + byte_idx,
+                       pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
+             pstr->wcs[byte_idx++] = wcu;
+             /* Write paddings.  */
+             for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+               pstr->wcs[byte_idx++] = WEOF;
+           }
+         else if (mbclen == (size_t) -1 || mbclen == 0)
+           {
+             /* It is an invalid character or '\0'.  Just use the byte.  */
+             int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+             pstr->mbs[byte_idx] = ch;
+             /* And also cast it to wide char.  */
+             pstr->wcs[byte_idx++] = (wchar_t) ch;
+             if (BE (mbclen == (size_t) -1, 0))
+               pstr->cur_state = prev_st;
+           }
+         else
+           {
+             /* The buffer doesn't have enough space, finish to build.  */
+             pstr->cur_state = prev_st;
+             break;
+           }
+       }
+      pstr->valid_len = byte_idx;
+      pstr->valid_raw_len = byte_idx;
+      return REG_NOERROR;
+    }
+  else
+    for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;)
+      {
+       wchar_t wc;
+       const char *p;
+      offsets_needed:
+       remain_len = end_idx - byte_idx;
+       prev_st = pstr->cur_state;
+       if (BE (pstr->trans != NULL, 0))
+         {
+           int i, ch;
+
+           for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+             {
+               ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i];
+               buf[i] = pstr->trans[ch];
+             }
+           p = (const char *) buf;
+         }
+       else
+         p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx;
+       mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+       if (BE (mbclen + 2 > 2, 1))
+         {
+           wchar_t wcu = wc;
+           if (iswlower (wc))
+             {
+               size_t mbcdlen;
+
+               wcu = towupper (wc);
+               mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st);
+               if (BE (mbclen == mbcdlen, 1))
+                 memcpy (pstr->mbs + byte_idx, buf, mbclen);
+               else if (mbcdlen != (size_t) -1)
+                 {
+                   size_t i;
+
+                   if (byte_idx + mbcdlen > pstr->bufs_len)
+                     {
+                       pstr->cur_state = prev_st;
+                       break;
+                     }
+
+                   if (pstr->offsets == NULL)
+                     {
+                       pstr->offsets = re_malloc (int, pstr->bufs_len);
+
+                       if (pstr->offsets == NULL)
+                         return REG_ESPACE;
+                     }
+                   if (!pstr->offsets_needed)
+                     {
+                       for (i = 0; i < (size_t) byte_idx; ++i)
+                         pstr->offsets[i] = i;
+                       pstr->offsets_needed = 1;
+                     }
+
+                   memcpy (pstr->mbs + byte_idx, buf, mbcdlen);
+                   pstr->wcs[byte_idx] = wcu;
+                   pstr->offsets[byte_idx] = src_idx;
+                   for (i = 1; i < mbcdlen; ++i)
+                     {
+                       pstr->offsets[byte_idx + i]
+                         = src_idx + (i < mbclen ? i : mbclen - 1);
+                       pstr->wcs[byte_idx + i] = WEOF;
+                     }
+                   pstr->len += mbcdlen - mbclen;
+                   if (pstr->raw_stop > src_idx)
+                     pstr->stop += mbcdlen - mbclen;
+                   end_idx = (pstr->bufs_len > pstr->len)
+                             ? pstr->len : pstr->bufs_len;
+                   byte_idx += mbcdlen;
+                   src_idx += mbclen;
+                   continue;
+                 }
+               else
+                 memcpy (pstr->mbs + byte_idx, p, mbclen);
+             }
+           else
+             memcpy (pstr->mbs + byte_idx, p, mbclen);
+
+           if (BE (pstr->offsets_needed != 0, 0))
+             {
+               size_t i;
+               for (i = 0; i < mbclen; ++i)
+                 pstr->offsets[byte_idx + i] = src_idx + i;
+             }
+           src_idx += mbclen;
+
+           pstr->wcs[byte_idx++] = wcu;
+           /* Write paddings.  */
+           for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+             pstr->wcs[byte_idx++] = WEOF;
+         }
+       else if (mbclen == (size_t) -1 || mbclen == 0)
+         {
+           /* It is an invalid character or '\0'.  Just use the byte.  */
+           int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx];
+
+           if (BE (pstr->trans != NULL, 0))
+             ch = pstr->trans [ch];
+           pstr->mbs[byte_idx] = ch;
+
+           if (BE (pstr->offsets_needed != 0, 0))
+             pstr->offsets[byte_idx] = src_idx;
+           ++src_idx;
+
+           /* And also cast it to wide char.  */
+           pstr->wcs[byte_idx++] = (wchar_t) ch;
+           if (BE (mbclen == (size_t) -1, 0))
+             pstr->cur_state = prev_st;
+         }
+       else
+         {
+           /* The buffer doesn't have enough space, finish to build.  */
+           pstr->cur_state = prev_st;
+           break;
+         }
+      }
+  pstr->valid_len = byte_idx;
+  pstr->valid_raw_len = src_idx;
+  return REG_NOERROR;
+}
+
+/* Skip characters until the index becomes greater than NEW_RAW_IDX.
+   Return the index.  */
+
+static int
+internal_function
+re_string_skip_chars (re_string_t *pstr, int new_raw_idx, wint_t *last_wc)
+{
+  mbstate_t prev_st;
+  int rawbuf_idx;
+  size_t mbclen;
+  wint_t wc = WEOF;
+
+  /* Skip the characters which are not necessary to check.  */
+  for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len;
+       rawbuf_idx < new_raw_idx;)
+    {
+      wchar_t wc2;
+      int remain_len = pstr->len - rawbuf_idx;
+      prev_st = pstr->cur_state;
+      mbclen = __mbrtowc (&wc2, (const char *) pstr->raw_mbs + rawbuf_idx,
+                         remain_len, &pstr->cur_state);
+      if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0))
+       {
+         /* We treat these cases as a single byte character.  */
+         if (mbclen == 0 || remain_len == 0)
+           wc = L'\0';
+         else
+           wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx);
+         mbclen = 1;
+         pstr->cur_state = prev_st;
+       }
+      else
+       wc = (wint_t) wc2;
+      /* Then proceed the next character.  */
+      rawbuf_idx += mbclen;
+    }
+  *last_wc = (wint_t) wc;
+  return rawbuf_idx;
+}
+#endif /* RE_ENABLE_I18N  */
+
+/* Build the buffer PSTR->MBS, and apply the translation if we need.
+   This function is used in case of REG_ICASE.  */
+
+static void
+internal_function
+build_upper_buffer (re_string_t *pstr)
+{
+  int char_idx, end_idx;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx)
+    {
+      int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
+      if (BE (pstr->trans != NULL, 0))
+       ch = pstr->trans[ch];
+      if (islower (ch))
+       pstr->mbs[char_idx] = toupper (ch);
+      else
+       pstr->mbs[char_idx] = ch;
+    }
+  pstr->valid_len = char_idx;
+  pstr->valid_raw_len = char_idx;
+}
+
+/* Apply TRANS to the buffer in PSTR.  */
+
+static void
+internal_function
+re_string_translate_buffer (re_string_t *pstr)
+{
+  int buf_idx, end_idx;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx)
+    {
+      int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx];
+      pstr->mbs[buf_idx] = pstr->trans[ch];
+    }
+
+  pstr->valid_len = buf_idx;
+  pstr->valid_raw_len = buf_idx;
+}
+
+/* This function re-construct the buffers.
+   Concretely, convert to wide character in case of pstr->mb_cur_max > 1,
+   convert to upper case in case of REG_ICASE, apply translation.  */
+
+static reg_errcode_t
+internal_function
+re_string_reconstruct (re_string_t *pstr, int idx, int eflags)
+{
+  int offset = idx - pstr->raw_mbs_idx;
+  if (BE (offset < 0, 0))
+    {
+      /* Reset buffer.  */
+#ifdef RE_ENABLE_I18N
+      if (pstr->mb_cur_max > 1)
+       memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+#endif /* RE_ENABLE_I18N */
+      pstr->len = pstr->raw_len;
+      pstr->stop = pstr->raw_stop;
+      pstr->valid_len = 0;
+      pstr->raw_mbs_idx = 0;
+      pstr->valid_raw_len = 0;
+      pstr->offsets_needed = 0;
+      pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+                          : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
+      if (!pstr->mbs_allocated)
+       pstr->mbs = (unsigned char *) pstr->raw_mbs;
+      offset = idx;
+    }
+
+  if (BE (offset != 0, 1))
+    {
+      /* Should the already checked characters be kept?  */
+      if (BE (offset < pstr->valid_raw_len, 1))
+       {
+         /* Yes, move them to the front of the buffer.  */
+#ifdef RE_ENABLE_I18N
+         if (BE (pstr->offsets_needed, 0))
+           {
+             int low = 0, high = pstr->valid_len, mid;
+             do
+               {
+                 mid = (high + low) / 2;
+                 if (pstr->offsets[mid] > offset)
+                   high = mid;
+                 else if (pstr->offsets[mid] < offset)
+                   low = mid + 1;
+                 else
+                   break;
+               }
+             while (low < high);
+             if (pstr->offsets[mid] < offset)
+               ++mid;
+             pstr->tip_context = re_string_context_at (pstr, mid - 1,
+                                                       eflags);
+             /* This can be quite complicated, so handle specially
+                only the common and easy case where the character with
+                different length representation of lower and upper
+                case is present at or after offset.  */
+             if (pstr->valid_len > offset
+                 && mid == offset && pstr->offsets[mid] == offset)
+               {
+                 memmove (pstr->wcs, pstr->wcs + offset,
+                          (pstr->valid_len - offset) * sizeof (wint_t));
+                 memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset);
+                 pstr->valid_len -= offset;
+                 pstr->valid_raw_len -= offset;
+                 for (low = 0; low < pstr->valid_len; low++)
+                   pstr->offsets[low] = pstr->offsets[low + offset] - offset;
+               }
+             else
+               {
+                 /* Otherwise, just find out how long the partial multibyte
+                    character at offset is and fill it with WEOF/255.  */
+                 pstr->len = pstr->raw_len - idx + offset;
+                 pstr->stop = pstr->raw_stop - idx + offset;
+                 pstr->offsets_needed = 0;
+                 while (mid > 0 && pstr->offsets[mid - 1] == offset)
+                   --mid;
+                 while (mid < pstr->valid_len)
+                   if (pstr->wcs[mid] != WEOF)
+                     break;
+                   else
+                     ++mid;
+                 if (mid == pstr->valid_len)
+                   pstr->valid_len = 0;
+                 else
+                   {
+                     pstr->valid_len = pstr->offsets[mid] - offset;
+                     if (pstr->valid_len)
+                       {
+                         for (low = 0; low < pstr->valid_len; ++low)
+                           pstr->wcs[low] = WEOF;
+                         memset (pstr->mbs, 255, pstr->valid_len);
+                       }
+                   }
+                 pstr->valid_raw_len = pstr->valid_len;
+               }
+           }
+         else
+#endif
+           {
+             pstr->tip_context = re_string_context_at (pstr, offset - 1,
+                                                       eflags);
+#ifdef RE_ENABLE_I18N
+             if (pstr->mb_cur_max > 1)
+               memmove (pstr->wcs, pstr->wcs + offset,
+                        (pstr->valid_len - offset) * sizeof (wint_t));
+#endif /* RE_ENABLE_I18N */
+             if (BE (pstr->mbs_allocated, 0))
+               memmove (pstr->mbs, pstr->mbs + offset,
+                        pstr->valid_len - offset);
+             pstr->valid_len -= offset;
+             pstr->valid_raw_len -= offset;
+#if DEBUG
+             assert (pstr->valid_len > 0);
+#endif
+           }
+       }
+      else
+       {
+#ifdef RE_ENABLE_I18N
+         /* No, skip all characters until IDX.  */
+         int prev_valid_len = pstr->valid_len;
+
+         if (BE (pstr->offsets_needed, 0))
+           {
+             pstr->len = pstr->raw_len - idx + offset;
+             pstr->stop = pstr->raw_stop - idx + offset;
+             pstr->offsets_needed = 0;
+           }
+#endif
+         pstr->valid_len = 0;
+#ifdef RE_ENABLE_I18N
+         if (pstr->mb_cur_max > 1)
+           {
+             int wcs_idx;
+             wint_t wc = WEOF;
+
+             if (pstr->is_utf8)
+               {
+                 const unsigned char *raw, *p, *end;
+
+                 /* Special case UTF-8.  Multi-byte chars start with any
+                    byte other than 0x80 - 0xbf.  */
+                 raw = pstr->raw_mbs + pstr->raw_mbs_idx;
+                 end = raw + (offset - pstr->mb_cur_max);
+                 if (end < pstr->raw_mbs)
+                   end = pstr->raw_mbs;
+                 p = raw + offset - 1;
+#ifdef _LIBC
+                 /* We know the wchar_t encoding is UCS4, so for the simple
+                    case, ASCII characters, skip the conversion step.  */
+                 if (isascii (*p) && BE (pstr->trans == NULL, 1))
+                   {
+                     memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+                     /* pstr->valid_len = 0; */
+                     wc = (wchar_t) *p;
+                   }
+                 else
+#endif
+                   for (; p >= end; --p)
+                     if ((*p & 0xc0) != 0x80)
+                       {
+                         mbstate_t cur_state;
+                         wchar_t wc2;
+                         int mlen = raw + pstr->len - p;
+                         unsigned char buf[6];
+                         size_t mbclen;
+
+                         if (BE (pstr->trans != NULL, 0))
+                           {
+                             int i = mlen < 6 ? mlen : 6;
+                             while (--i >= 0)
+                               buf[i] = pstr->trans[p[i]];
+                           }
+                         /* XXX Don't use mbrtowc, we know which conversion
+                            to use (UTF-8 -> UCS4).  */
+                         memset (&cur_state, 0, sizeof (cur_state));
+                         mbclen = __mbrtowc (&wc2, (const char *) p, mlen,
+                                             &cur_state);
+                         if (raw + offset - p <= mbclen
+                             && mbclen < (size_t) -2)
+                           {
+                             memset (&pstr->cur_state, '\0',
+                                     sizeof (mbstate_t));
+                             pstr->valid_len = mbclen - (raw + offset - p);
+                             wc = wc2;
+                           }
+                         break;
+                       }
+               }
+
+             if (wc == WEOF)
+               pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx;
+             if (wc == WEOF)
+               pstr->tip_context
+                 = re_string_context_at (pstr, prev_valid_len - 1, eflags);
+             else
+               pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0)
+                                     && IS_WIDE_WORD_CHAR (wc))
+                                    ? CONTEXT_WORD
+                                    : ((IS_WIDE_NEWLINE (wc)
+                                        && pstr->newline_anchor)
+                                       ? CONTEXT_NEWLINE : 0));
+             if (BE (pstr->valid_len, 0))
+               {
+                 for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
+                   pstr->wcs[wcs_idx] = WEOF;
+                 if (pstr->mbs_allocated)
+                   memset (pstr->mbs, 255, pstr->valid_len);
+               }
+             pstr->valid_raw_len = pstr->valid_len;
+           }
+         else
+#endif /* RE_ENABLE_I18N */
+           {
+             int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1];
+             pstr->valid_raw_len = 0;
+             if (pstr->trans)
+               c = pstr->trans[c];
+             pstr->tip_context = (bitset_contain (pstr->word_char, c)
+                                  ? CONTEXT_WORD
+                                  : ((IS_NEWLINE (c) && pstr->newline_anchor)
+                                     ? CONTEXT_NEWLINE : 0));
+           }
+       }
+      if (!BE (pstr->mbs_allocated, 0))
+       pstr->mbs += offset;
+    }
+  pstr->raw_mbs_idx = idx;
+  pstr->len -= offset;
+  pstr->stop -= offset;
+
+  /* Then build the buffers.  */
+#ifdef RE_ENABLE_I18N
+  if (pstr->mb_cur_max > 1)
+    {
+      if (pstr->icase)
+       {
+         reg_errcode_t ret = build_wcs_upper_buffer (pstr);
+         if (BE (ret != REG_NOERROR, 0))
+           return ret;
+       }
+      else
+       build_wcs_buffer (pstr);
+    }
+  else
+#endif /* RE_ENABLE_I18N */
+    if (BE (pstr->mbs_allocated, 0))
+      {
+       if (pstr->icase)
+         build_upper_buffer (pstr);
+       else if (pstr->trans != NULL)
+         re_string_translate_buffer (pstr);
+      }
+    else
+      pstr->valid_len = pstr->len;
+
+  pstr->cur_idx = 0;
+  return REG_NOERROR;
+}
+
+static unsigned char
+internal_function __attribute ((pure))
+re_string_peek_byte_case (const re_string_t *pstr, int idx)
+{
+  int ch, off;
+
+  /* Handle the common (easiest) cases first.  */
+  if (BE (!pstr->mbs_allocated, 1))
+    return re_string_peek_byte (pstr, idx);
+
+#ifdef RE_ENABLE_I18N
+  if (pstr->mb_cur_max > 1
+      && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx))
+    return re_string_peek_byte (pstr, idx);
+#endif
+
+  off = pstr->cur_idx + idx;
+#ifdef RE_ENABLE_I18N
+  if (pstr->offsets_needed)
+    off = pstr->offsets[off];
+#endif
+
+  ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+#ifdef RE_ENABLE_I18N
+  /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I
+     this function returns CAPITAL LETTER I instead of first byte of
+     DOTLESS SMALL LETTER I.  The latter would confuse the parser,
+     since peek_byte_case doesn't advance cur_idx in any way.  */
+  if (pstr->offsets_needed && !isascii (ch))
+    return re_string_peek_byte (pstr, idx);
+#endif
+
+  return ch;
+}
+
+static unsigned char
+internal_function __attribute ((pure))
+re_string_fetch_byte_case (re_string_t *pstr)
+{
+  if (BE (!pstr->mbs_allocated, 1))
+    return re_string_fetch_byte (pstr);
+
+#ifdef RE_ENABLE_I18N
+  if (pstr->offsets_needed)
+    {
+      int off, ch;
+
+      /* For tr_TR.UTF-8 [[:islower:]] there is
+        [[: CAPITAL LETTER I WITH DOT lower:]] in mbs.  Skip
+        in that case the whole multi-byte character and return
+        the original letter.  On the other side, with
+        [[: DOTLESS SMALL LETTER I return [[:I, as doing
+        anything else would complicate things too much.  */
+
+      if (!re_string_first_byte (pstr, pstr->cur_idx))
+       return re_string_fetch_byte (pstr);
+
+      off = pstr->offsets[pstr->cur_idx];
+      ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+      if (! isascii (ch))
+       return re_string_fetch_byte (pstr);
+
+      re_string_skip_bytes (pstr,
+                           re_string_char_size_at (pstr, pstr->cur_idx));
+      return ch;
+    }
+#endif
+
+  return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++];
+}
+
+static void
+internal_function
+re_string_destruct (re_string_t *pstr)
+{
+#ifdef RE_ENABLE_I18N
+  re_free (pstr->wcs);
+  re_free (pstr->offsets);
+#endif /* RE_ENABLE_I18N  */
+  if (pstr->mbs_allocated)
+    re_free (pstr->mbs);
+}
+
+/* Return the context at IDX in INPUT.  */
+
+static unsigned int
+internal_function
+re_string_context_at (const re_string_t *input, int idx, int eflags)
+{
+  int c;
+  if (BE (idx < 0, 0))
+    /* In this case, we use the value stored in input->tip_context,
+       since we can't know the character in input->mbs[-1] here.  */
+    return input->tip_context;
+  if (BE (idx == input->len, 0))
+    return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
+           : CONTEXT_NEWLINE | CONTEXT_ENDBUF);
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1)
+    {
+      wint_t wc;
+      int wc_idx = idx;
+      while(input->wcs[wc_idx] == WEOF)
+       {
+#ifdef DEBUG
+         /* It must not happen.  */
+         assert (wc_idx >= 0);
+#endif
+         --wc_idx;
+         if (wc_idx < 0)
+           return input->tip_context;
+       }
+      wc = input->wcs[wc_idx];
+      if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc))
+       return CONTEXT_WORD;
+      return (IS_WIDE_NEWLINE (wc) && input->newline_anchor
+             ? CONTEXT_NEWLINE : 0);
+    }
+  else
+#endif
+    {
+      c = re_string_byte_at (input, idx);
+      if (bitset_contain (input->word_char, c))
+       return CONTEXT_WORD;
+      return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0;
+    }
+}
+\f
+/* Functions for set operation.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_alloc (re_node_set *set, int size)
+{
+  /*
+   * ADR: valgrind says size can be 0, which then doesn't
+   * free the block of size 0.  Harumph. This seems
+   * to work ok, though.
+   */
+  if (size == 0)
+    {
+       memset(set, 0, sizeof(*set));
+       return REG_NOERROR;
+    }
+  set->alloc = size;
+  set->nelem = 0;
+  set->elems = re_malloc (int, size);
+  if (BE (set->elems == NULL, 0))
+    return REG_ESPACE;
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_1 (re_node_set *set, int elem)
+{
+  set->alloc = 1;
+  set->nelem = 1;
+  set->elems = re_malloc (int, 1);
+  if (BE (set->elems == NULL, 0))
+    {
+      set->alloc = set->nelem = 0;
+      return REG_ESPACE;
+    }
+  set->elems[0] = elem;
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_2 (re_node_set *set, int elem1, int elem2)
+{
+  set->alloc = 2;
+  set->elems = re_malloc (int, 2);
+  if (BE (set->elems == NULL, 0))
+    return REG_ESPACE;
+  if (elem1 == elem2)
+    {
+      set->nelem = 1;
+      set->elems[0] = elem1;
+    }
+  else
+    {
+      set->nelem = 2;
+      if (elem1 < elem2)
+       {
+         set->elems[0] = elem1;
+         set->elems[1] = elem2;
+       }
+      else
+       {
+         set->elems[0] = elem2;
+         set->elems[1] = elem1;
+       }
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_copy (re_node_set *dest, const re_node_set *src)
+{
+  dest->nelem = src->nelem;
+  if (src->nelem > 0)
+    {
+      dest->alloc = dest->nelem;
+      dest->elems = re_malloc (int, dest->alloc);
+      if (BE (dest->elems == NULL, 0))
+       {
+         dest->alloc = dest->nelem = 0;
+         return REG_ESPACE;
+       }
+      memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+    }
+  else
+    re_node_set_init_empty (dest);
+  return REG_NOERROR;
+}
+
+/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.
+   Note: We assume dest->elems is NULL, when dest->alloc is 0.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1,
+                          const re_node_set *src2)
+{
+  int i1, i2, is, id, delta, sbase;
+  if (src1->nelem == 0 || src2->nelem == 0)
+    return REG_NOERROR;
+
+  /* We need dest->nelem + 2 * elems_in_intersection; this is a
+     conservative estimate.  */
+  if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
+    {
+      int new_alloc = src1->nelem + src2->nelem + dest->alloc;
+      int *new_elems = re_realloc (dest->elems, int, new_alloc);
+      if (BE (new_elems == NULL, 0))
+       return REG_ESPACE;
+      dest->elems = new_elems;
+      dest->alloc = new_alloc;
+    }
+
+  /* Find the items in the intersection of SRC1 and SRC2, and copy
+     into the top of DEST those that are not already in DEST itself.  */
+  sbase = dest->nelem + src1->nelem + src2->nelem;
+  i1 = src1->nelem - 1;
+  i2 = src2->nelem - 1;
+  id = dest->nelem - 1;
+  for (;;)
+    {
+      if (src1->elems[i1] == src2->elems[i2])
+       {
+         /* Try to find the item in DEST.  Maybe we could binary search?  */
+         while (id >= 0 && dest->elems[id] > src1->elems[i1])
+           --id;
+
+         if (id < 0 || dest->elems[id] != src1->elems[i1])
+           dest->elems[--sbase] = src1->elems[i1];
+
+         if (--i1 < 0 || --i2 < 0)
+           break;
+       }
+
+      /* Lower the highest of the two items.  */
+      else if (src1->elems[i1] < src2->elems[i2])
+       {
+         if (--i2 < 0)
+           break;
+       }
+      else
+       {
+         if (--i1 < 0)
+           break;
+       }
+    }
+
+  id = dest->nelem - 1;
+  is = dest->nelem + src1->nelem + src2->nelem - 1;
+  delta = is - sbase + 1;
+
+  /* Now copy.  When DELTA becomes zero, the remaining
+     DEST elements are already in place; this is more or
+     less the same loop that is in re_node_set_merge.  */
+  dest->nelem += delta;
+  if (delta > 0 && id >= 0)
+    for (;;)
+      {
+       if (dest->elems[is] > dest->elems[id])
+         {
+           /* Copy from the top.  */
+           dest->elems[id + delta--] = dest->elems[is--];
+           if (delta == 0)
+             break;
+         }
+       else
+         {
+           /* Slide from the bottom.  */
+           dest->elems[id + delta] = dest->elems[id];
+           if (--id < 0)
+             break;
+         }
+      }
+
+  /* Copy remaining SRC elements.  */
+  memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int));
+
+  return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets SRC1 and SRC2. And store it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_init_union (re_node_set *dest, const re_node_set *src1,
+                       const re_node_set *src2)
+{
+  int i1, i2, id;
+  if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
+    {
+      dest->alloc = src1->nelem + src2->nelem;
+      dest->elems = re_malloc (int, dest->alloc);
+      if (BE (dest->elems == NULL, 0))
+       return REG_ESPACE;
+    }
+  else
+    {
+      if (src1 != NULL && src1->nelem > 0)
+       return re_node_set_init_copy (dest, src1);
+      else if (src2 != NULL && src2->nelem > 0)
+       return re_node_set_init_copy (dest, src2);
+      else
+       re_node_set_init_empty (dest);
+      return REG_NOERROR;
+    }
+  for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+    {
+      if (src1->elems[i1] > src2->elems[i2])
+       {
+         dest->elems[id++] = src2->elems[i2++];
+         continue;
+       }
+      if (src1->elems[i1] == src2->elems[i2])
+       ++i2;
+      dest->elems[id++] = src1->elems[i1++];
+    }
+  if (i1 < src1->nelem)
+    {
+      memcpy (dest->elems + id, src1->elems + i1,
+            (src1->nelem - i1) * sizeof (int));
+      id += src1->nelem - i1;
+    }
+  else if (i2 < src2->nelem)
+    {
+      memcpy (dest->elems + id, src2->elems + i2,
+            (src2->nelem - i2) * sizeof (int));
+      id += src2->nelem - i2;
+    }
+  dest->nelem = id;
+  return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets DEST and SRC. And store it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_merge (re_node_set *dest, const re_node_set *src)
+{
+  int is, id, sbase, delta;
+  if (src == NULL || src->nelem == 0)
+    return REG_NOERROR;
+  if (dest->alloc < 2 * src->nelem + dest->nelem)
+    {
+      int new_alloc = 2 * (src->nelem + dest->alloc);
+      int *new_buffer = re_realloc (dest->elems, int, new_alloc);
+      if (BE (new_buffer == NULL, 0))
+       return REG_ESPACE;
+      dest->elems = new_buffer;
+      dest->alloc = new_alloc;
+    }
+
+  if (BE (dest->nelem == 0, 0))
+    {
+      dest->nelem = src->nelem;
+      memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+      return REG_NOERROR;
+    }
+
+  /* Copy into the top of DEST the items of SRC that are not
+     found in DEST.  Maybe we could binary search in DEST?  */
+  for (sbase = dest->nelem + 2 * src->nelem,
+       is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; )
+    {
+      if (dest->elems[id] == src->elems[is])
+       is--, id--;
+      else if (dest->elems[id] < src->elems[is])
+       dest->elems[--sbase] = src->elems[is--];
+      else /* if (dest->elems[id] > src->elems[is]) */
+       --id;
+    }
+
+  if (is >= 0)
+    {
+      /* If DEST is exhausted, the remaining items of SRC must be unique.  */
+      sbase -= is + 1;
+      memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (int));
+    }
+
+  id = dest->nelem - 1;
+  is = dest->nelem + 2 * src->nelem - 1;
+  delta = is - sbase + 1;
+  if (delta == 0)
+    return REG_NOERROR;
+
+  /* Now copy.  When DELTA becomes zero, the remaining
+     DEST elements are already in place.  */
+  dest->nelem += delta;
+  for (;;)
+    {
+      if (dest->elems[is] > dest->elems[id])
+       {
+         /* Copy from the top.  */
+         dest->elems[id + delta--] = dest->elems[is--];
+         if (delta == 0)
+           break;
+       }
+      else
+       {
+         /* Slide from the bottom.  */
+         dest->elems[id + delta] = dest->elems[id];
+         if (--id < 0)
+           {
+             /* Copy remaining SRC elements.  */
+             memcpy (dest->elems, dest->elems + sbase,
+                     delta * sizeof (int));
+             break;
+           }
+       }
+    }
+
+  return REG_NOERROR;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+   SET should not already have ELEM.
+   return -1 if an error is occured, return 1 otherwise.  */
+
+static int
+internal_function
+re_node_set_insert (re_node_set *set, int elem)
+{
+  int idx;
+  /* In case the set is empty.  */
+  if (set->alloc == 0)
+    {
+      if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1))
+       return 1;
+      else
+       return -1;
+    }
+
+  if (BE (set->nelem, 0) == 0)
+    {
+      /* We already guaranteed above that set->alloc != 0.  */
+      set->elems[0] = elem;
+      ++set->nelem;
+      return 1;
+    }
+
+  /* Realloc if we need.  */
+  if (set->alloc == set->nelem)
+    {
+      int *new_elems;
+      set->alloc = set->alloc * 2;
+      new_elems = re_realloc (set->elems, int, set->alloc);
+      if (BE (new_elems == NULL, 0))
+       return -1;
+      set->elems = new_elems;
+    }
+
+  /* Move the elements which follows the new element.  Test the
+     first element separately to skip a check in the inner loop.  */
+  if (elem < set->elems[0])
+    {
+      idx = 0;
+      for (idx = set->nelem; idx > 0; idx--)
+       set->elems[idx] = set->elems[idx - 1];
+    }
+  else
+    {
+      for (idx = set->nelem; set->elems[idx - 1] > elem; idx--)
+       set->elems[idx] = set->elems[idx - 1];
+    }
+
+  /* Insert the new element.  */
+  set->elems[idx] = elem;
+  ++set->nelem;
+  return 1;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+   SET should not already have any element greater than or equal to ELEM.
+   Return -1 if an error is occured, return 1 otherwise.  */
+
+static int
+internal_function
+re_node_set_insert_last (re_node_set *set, int elem)
+{
+  /* Realloc if we need.  */
+  if (set->alloc == set->nelem)
+    {
+      int *new_elems;
+      set->alloc = (set->alloc + 1) * 2;
+      new_elems = re_realloc (set->elems, int, set->alloc);
+      if (BE (new_elems == NULL, 0))
+       return -1;
+      set->elems = new_elems;
+    }
+
+  /* Insert the new element.  */
+  set->elems[set->nelem++] = elem;
+  return 1;
+}
+
+/* Compare two node sets SET1 and SET2.
+   return 1 if SET1 and SET2 are equivalent, return 0 otherwise.  */
+
+static int
+internal_function __attribute ((pure))
+re_node_set_compare (const re_node_set *set1, const re_node_set *set2)
+{
+  int i;
+  if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
+    return 0;
+  for (i = set1->nelem ; --i >= 0 ; )
+    if (set1->elems[i] != set2->elems[i])
+      return 0;
+  return 1;
+}
+
+/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise.  */
+
+static int
+internal_function __attribute ((pure))
+re_node_set_contains (const re_node_set *set, int elem)
+{
+  unsigned int idx, right, mid;
+  if (set->nelem <= 0)
+    return 0;
+
+  /* Binary search the element.  */
+  idx = 0;
+  right = set->nelem - 1;
+  while (idx < right)
+    {
+      mid = (idx + right) / 2;
+      if (set->elems[mid] < elem)
+       idx = mid + 1;
+      else
+       right = mid;
+    }
+  return set->elems[idx] == elem ? idx + 1 : 0;
+}
+
+static void
+internal_function
+re_node_set_remove_at (re_node_set *set, int idx)
+{
+  if (idx < 0 || idx >= set->nelem)
+    return;
+  --set->nelem;
+  for (; idx < set->nelem; idx++)
+    set->elems[idx] = set->elems[idx + 1];
+}
+\f
+
+/* Add the token TOKEN to dfa->nodes, and return the index of the token.
+   Or return -1, if an error will be occured.  */
+
+static int
+internal_function
+re_dfa_add_node (re_dfa_t *dfa, re_token_t token)
+{
+  if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0))
+    {
+      size_t new_nodes_alloc = dfa->nodes_alloc * 2;
+      int *new_nexts, *new_indices;
+      re_node_set *new_edests, *new_eclosures;
+      re_token_t *new_nodes;
+
+      /* Avoid overflows in realloc.  */
+      const size_t max_object_size = MAX (sizeof (re_token_t),
+                                         MAX (sizeof (re_node_set),
+                                              sizeof (int)));
+      if (BE (SIZE_MAX / max_object_size < new_nodes_alloc, 0))
+       return -1;
+
+      new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc);
+      if (BE (new_nodes == NULL, 0))
+       return -1;
+      dfa->nodes = new_nodes;
+      new_nexts = re_realloc (dfa->nexts, int, new_nodes_alloc);
+      new_indices = re_realloc (dfa->org_indices, int, new_nodes_alloc);
+      new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc);
+      new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc);
+      if (BE (new_nexts == NULL || new_indices == NULL
+             || new_edests == NULL || new_eclosures == NULL, 0))
+       return -1;
+      dfa->nexts = new_nexts;
+      dfa->org_indices = new_indices;
+      dfa->edests = new_edests;
+      dfa->eclosures = new_eclosures;
+      dfa->nodes_alloc = new_nodes_alloc;
+    }
+  dfa->nodes[dfa->nodes_len] = token;
+  dfa->nodes[dfa->nodes_len].constraint = 0;
+#ifdef RE_ENABLE_I18N
+  dfa->nodes[dfa->nodes_len].accept_mb =
+    (token.type == OP_PERIOD && dfa->mb_cur_max > 1) || token.type == COMPLEX_BRACKET;
+#endif
+  dfa->nexts[dfa->nodes_len] = -1;
+  re_node_set_init_empty (dfa->edests + dfa->nodes_len);
+  re_node_set_init_empty (dfa->eclosures + dfa->nodes_len);
+  return dfa->nodes_len++;
+}
+
+static inline unsigned int
+internal_function
+calc_state_hash (const re_node_set *nodes, unsigned int context)
+{
+  unsigned int hash = nodes->nelem + context;
+  int i;
+  for (i = 0 ; i < nodes->nelem ; i++)
+    hash += nodes->elems[i];
+  return hash;
+}
+
+/* Search for the state whose node_set is equivalent to NODES.
+   Return the pointer to the state, if we found it in the DFA.
+   Otherwise create the new one and return it.  In case of an error
+   return NULL and set the error code in ERR.
+   Note: - We assume NULL as the invalid state, then it is possible that
+          return value is NULL and ERR is REG_NOERROR.
+        - We never return non-NULL value in case of any errors, it is for
+          optimization.  */
+
+static re_dfastate_t *
+internal_function
+re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa,
+                 const re_node_set *nodes)
+{
+  unsigned int hash;
+  re_dfastate_t *new_state;
+  struct re_state_table_entry *spot;
+  int i;
+  if (BE (nodes->nelem == 0, 0))
+    {
+      *err = REG_NOERROR;
+      return NULL;
+    }
+  hash = calc_state_hash (nodes, 0);
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+  for (i = 0 ; i < spot->num ; i++)
+    {
+      re_dfastate_t *state = spot->array[i];
+      if (hash != state->hash)
+       continue;
+      if (re_node_set_compare (&state->nodes, nodes))
+       return state;
+    }
+
+  /* There are no appropriate state in the dfa, create the new one.  */
+  new_state = create_ci_newstate (dfa, nodes, hash);
+  if (BE (new_state == NULL, 0))
+    *err = REG_ESPACE;
+
+  return new_state;
+}
+
+/* Search for the state whose node_set is equivalent to NODES and
+   whose context is equivalent to CONTEXT.
+   Return the pointer to the state, if we found it in the DFA.
+   Otherwise create the new one and return it.  In case of an error
+   return NULL and set the error code in ERR.
+   Note: - We assume NULL as the invalid state, then it is possible that
+          return value is NULL and ERR is REG_NOERROR.
+        - We never return non-NULL value in case of any errors, it is for
+          optimization.  */
+
+static re_dfastate_t *
+internal_function
+re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa,
+                         const re_node_set *nodes, unsigned int context)
+{
+  unsigned int hash;
+  re_dfastate_t *new_state;
+  struct re_state_table_entry *spot;
+  int i;
+  if (nodes->nelem == 0)
+    {
+      *err = REG_NOERROR;
+      return NULL;
+    }
+  hash = calc_state_hash (nodes, context);
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+  for (i = 0 ; i < spot->num ; i++)
+    {
+      re_dfastate_t *state = spot->array[i];
+      if (state->hash == hash
+         && state->context == context
+         && re_node_set_compare (state->entrance_nodes, nodes))
+       return state;
+    }
+  /* There are no appropriate state in `dfa', create the new one.  */
+  new_state = create_cd_newstate (dfa, nodes, context, hash);
+  if (BE (new_state == NULL, 0))
+    *err = REG_ESPACE;
+
+  return new_state;
+}
+
+/* Finish initialization of the new state NEWSTATE, and using its hash value
+   HASH put in the appropriate bucket of DFA's state table.  Return value
+   indicates the error code if failed.  */
+
+static reg_errcode_t
+register_state (const re_dfa_t *dfa, re_dfastate_t *newstate,
+               unsigned int hash)
+{
+  struct re_state_table_entry *spot;
+  reg_errcode_t err;
+  int i;
+
+  newstate->hash = hash;
+  err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem);
+  if (BE (err != REG_NOERROR, 0))
+    return REG_ESPACE;
+  for (i = 0; i < newstate->nodes.nelem; i++)
+    {
+      int elem = newstate->nodes.elems[i];
+      if (!IS_EPSILON_NODE (dfa->nodes[elem].type))
+       if (re_node_set_insert_last (&newstate->non_eps_nodes, elem) < 0)
+         return REG_ESPACE;
+    }
+
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+  if (BE (spot->alloc <= spot->num, 0))
+    {
+      int new_alloc = 2 * spot->num + 2;
+      re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *,
+                                             new_alloc);
+      if (BE (new_array == NULL, 0))
+       return REG_ESPACE;
+      spot->array = new_array;
+      spot->alloc = new_alloc;
+    }
+  spot->array[spot->num++] = newstate;
+  return REG_NOERROR;
+}
+
+static void
+free_state (re_dfastate_t *state)
+{
+  re_node_set_free (&state->non_eps_nodes);
+  re_node_set_free (&state->inveclosure);
+  if (state->entrance_nodes != &state->nodes)
+    {
+      re_node_set_free (state->entrance_nodes);
+      re_free (state->entrance_nodes);
+    }
+  re_node_set_free (&state->nodes);
+  re_free (state->word_trtable);
+  re_free (state->trtable);
+  re_free (state);
+}
+
+/* Create the new state which is independ of contexts.
+   Return the new state if succeeded, otherwise return NULL.  */
+
+static re_dfastate_t *
+internal_function
+create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+                   unsigned int hash)
+{
+  int i;
+  reg_errcode_t err;
+  re_dfastate_t *newstate;
+
+  newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+  if (BE (newstate == NULL, 0))
+    return NULL;
+  err = re_node_set_init_copy (&newstate->nodes, nodes);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      re_free (newstate);
+      return NULL;
+    }
+
+  newstate->entrance_nodes = &newstate->nodes;
+  for (i = 0 ; i < nodes->nelem ; i++)
+    {
+      re_token_t *node = dfa->nodes + nodes->elems[i];
+      re_token_type_t type = node->type;
+      if (type == CHARACTER && !node->constraint)
+       continue;
+#ifdef RE_ENABLE_I18N
+      newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
+
+      /* If the state has the halt node, the state is a halt state.  */
+      if (type == END_OF_RE)
+       newstate->halt = 1;
+      else if (type == OP_BACK_REF)
+       newstate->has_backref = 1;
+      else if (type == ANCHOR || node->constraint)
+       newstate->has_constraint = 1;
+    }
+  err = register_state (dfa, newstate, hash);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_state (newstate);
+      newstate = NULL;
+    }
+  return newstate;
+}
+
+/* Create the new state which is depend on the context CONTEXT.
+   Return the new state if succeeded, otherwise return NULL.  */
+
+static re_dfastate_t *
+internal_function
+create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+                   unsigned int context, unsigned int hash)
+{
+  int i, nctx_nodes = 0;
+  reg_errcode_t err;
+  re_dfastate_t *newstate;
+
+  newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+  if (BE (newstate == NULL, 0))
+    return NULL;
+  err = re_node_set_init_copy (&newstate->nodes, nodes);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      re_free (newstate);
+      return NULL;
+    }
+
+  newstate->context = context;
+  newstate->entrance_nodes = &newstate->nodes;
+
+  for (i = 0 ; i < nodes->nelem ; i++)
+    {
+      re_token_t *node = dfa->nodes + nodes->elems[i];
+      re_token_type_t type = node->type;
+      unsigned int constraint = node->constraint;
+
+      if (type == CHARACTER && !constraint)
+       continue;
+#ifdef RE_ENABLE_I18N
+      newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
+
+      /* If the state has the halt node, the state is a halt state.  */
+      if (type == END_OF_RE)
+       newstate->halt = 1;
+      else if (type == OP_BACK_REF)
+       newstate->has_backref = 1;
+
+      if (constraint)
+       {
+         if (newstate->entrance_nodes == &newstate->nodes)
+           {
+             newstate->entrance_nodes = re_malloc (re_node_set, 1);
+             if (BE (newstate->entrance_nodes == NULL, 0))
+               {
+                 free_state (newstate);
+                 return NULL;
+               }
+             if (re_node_set_init_copy (newstate->entrance_nodes, nodes)
+                 != REG_NOERROR)
+               return NULL;
+             nctx_nodes = 0;
+             newstate->has_constraint = 1;
+           }
+
+         if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
+           {
+             re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
+             ++nctx_nodes;
+           }
+       }
+    }
+  err = register_state (dfa, newstate, hash);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_state (newstate);
+      newstate = NULL;
+    }
+  return  newstate;
+}
diff --git a/compat/regex/regex_internal.h b/compat/regex/regex_internal.h
new file mode 100644 (file)
index 0000000..4184d7f
--- /dev/null
@@ -0,0 +1,810 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002-2005, 2007, 2008, 2010 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _REGEX_INTERNAL_H
+#define _REGEX_INTERNAL_H 1
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC
+# include <langinfo.h>
+#endif
+#if defined HAVE_LOCALE_H || defined _LIBC
+# include <locale.h>
+#endif
+#if defined HAVE_WCHAR_H || defined _LIBC
+# include <wchar.h>
+#endif /* HAVE_WCHAR_H || _LIBC */
+#if defined HAVE_WCTYPE_H || defined _LIBC
+# include <wctype.h>
+#endif /* HAVE_WCTYPE_H || _LIBC */
+#if defined HAVE_STDBOOL_H || defined _LIBC
+# include <stdbool.h>
+#endif /* HAVE_STDBOOL_H || _LIBC */
+#if !defined(ZOS_USS)
+#if defined HAVE_STDINT_H || defined _LIBC
+# include <stdint.h>
+#endif /* HAVE_STDINT_H || _LIBC */
+#endif /* !ZOS_USS */
+#if defined _LIBC
+# include <bits/libc-lock.h>
+#else
+# define __libc_lock_define(CLASS,NAME)
+# define __libc_lock_init(NAME) do { } while (0)
+# define __libc_lock_lock(NAME) do { } while (0)
+# define __libc_lock_unlock(NAME) do { } while (0)
+#endif
+
+#ifndef GAWK
+/* In case that the system doesn't have isblank().  */
+#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank
+# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
+#endif
+#else /* GAWK */
+/*
+ * This is a freaking mess. On glibc systems you have to define
+ * a magic constant to get isblank() out of <ctype.h>, since it's
+ * a C99 function.  To heck with all that and borrow a page from
+ * dfa.c's book.
+ */
+
+static int
+is_blank (int c)
+{
+   return (c == ' ' || c == '\t');
+}
+#endif /* GAWK */
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+#  define _RE_DEFINE_LOCALE_FUNCTIONS 1
+#   include <locale/localeinfo.h>
+#   include <locale/elem-hash.h>
+#   include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages.  */
+#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+#  undef gettext
+#  define gettext(msgid) \
+  INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+   strings.  */
+# define gettext_noop(String) String
+#endif
+
+/* For loser systems without the definition.  */
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#ifndef NO_MBSUPPORT
+#include "mbsupport.h" /* gawk */
+#endif
+#ifndef MB_CUR_MAX
+#define MB_CUR_MAX 1
+#endif
+
+#if (defined MBS_SUPPORT) || _LIBC
+# define RE_ENABLE_I18N
+#endif
+
+#if __GNUC__ >= 3
+# define BE(expr, val) __builtin_expect (expr, val)
+#else
+# define BE(expr, val) (expr)
+# ifdef inline
+# undef inline
+# endif
+# define inline
+#endif
+
+/* Number of single byte character.  */
+#define SBC_MAX 256
+
+#define COLL_ELEM_LEN_MAX 8
+
+/* The character which represents newline.  */
+#define NEWLINE_CHAR '\n'
+#define WIDE_NEWLINE_CHAR L'\n'
+
+/* Rename to standard API for using out of glibc.  */
+#ifndef _LIBC
+# ifdef __wctype
+# undef __wctype
+# endif
+# define __wctype wctype
+# ifdef __iswctype
+# undef __iswctype
+# endif
+# define __iswctype iswctype
+# define __btowc btowc
+# define __mbrtowc mbrtowc
+#undef __mempcpy       /* GAWK */
+# define __mempcpy mempcpy
+# define __wcrtomb wcrtomb
+# define __regfree regfree
+# define attribute_hidden
+#endif /* not _LIBC */
+
+#ifdef __GNUC__
+# define __attribute(arg) __attribute__ (arg)
+#else
+# define __attribute(arg)
+#endif
+
+extern const char __re_error_msgid[] attribute_hidden;
+extern const size_t __re_error_msgid_idx[] attribute_hidden;
+
+/* An integer used to represent a set of bits.  It must be unsigned,
+   and must be at least as wide as unsigned int.  */
+typedef unsigned long int bitset_word_t;
+/* All bits set in a bitset_word_t.  */
+#define BITSET_WORD_MAX ULONG_MAX
+/* Number of bits in a bitset_word_t.  */
+#define BITSET_WORD_BITS (sizeof (bitset_word_t) * CHAR_BIT)
+/* Number of bitset_word_t in a bit_set.  */
+#define BITSET_WORDS (SBC_MAX / BITSET_WORD_BITS)
+typedef bitset_word_t bitset_t[BITSET_WORDS];
+typedef bitset_word_t *re_bitset_ptr_t;
+typedef const bitset_word_t *re_const_bitset_ptr_t;
+
+#define bitset_set(set,i) \
+  (set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS)
+#define bitset_clear(set,i) \
+  (set[i / BITSET_WORD_BITS] &= ~((bitset_word_t) 1 << i % BITSET_WORD_BITS))
+#define bitset_contain(set,i) \
+  (set[i / BITSET_WORD_BITS] & ((bitset_word_t) 1 << i % BITSET_WORD_BITS))
+#define bitset_empty(set) memset (set, '\0', sizeof (bitset_t))
+#define bitset_set_all(set) memset (set, '\xff', sizeof (bitset_t))
+#define bitset_copy(dest,src) memcpy (dest, src, sizeof (bitset_t))
+
+#define PREV_WORD_CONSTRAINT 0x0001
+#define PREV_NOTWORD_CONSTRAINT 0x0002
+#define NEXT_WORD_CONSTRAINT 0x0004
+#define NEXT_NOTWORD_CONSTRAINT 0x0008
+#define PREV_NEWLINE_CONSTRAINT 0x0010
+#define NEXT_NEWLINE_CONSTRAINT 0x0020
+#define PREV_BEGBUF_CONSTRAINT 0x0040
+#define NEXT_ENDBUF_CONSTRAINT 0x0080
+#define WORD_DELIM_CONSTRAINT 0x0100
+#define NOT_WORD_DELIM_CONSTRAINT 0x0200
+
+typedef enum
+{
+  INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+  WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+  WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+  INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+  LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
+  LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
+  BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
+  BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
+  WORD_DELIM = WORD_DELIM_CONSTRAINT,
+  NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT
+} re_context_type;
+
+typedef struct
+{
+  int alloc;
+  int nelem;
+  int *elems;
+} re_node_set;
+
+typedef enum
+{
+  NON_TYPE = 0,
+
+  /* Node type, These are used by token, node, tree.  */
+  CHARACTER = 1,
+  END_OF_RE = 2,
+  SIMPLE_BRACKET = 3,
+  OP_BACK_REF = 4,
+  OP_PERIOD = 5,
+#ifdef RE_ENABLE_I18N
+  COMPLEX_BRACKET = 6,
+  OP_UTF8_PERIOD = 7,
+#endif /* RE_ENABLE_I18N */
+
+  /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used
+     when the debugger shows values of this enum type.  */
+#define EPSILON_BIT 8
+  OP_OPEN_SUBEXP = EPSILON_BIT | 0,
+  OP_CLOSE_SUBEXP = EPSILON_BIT | 1,
+  OP_ALT = EPSILON_BIT | 2,
+  OP_DUP_ASTERISK = EPSILON_BIT | 3,
+  ANCHOR = EPSILON_BIT | 4,
+
+  /* Tree type, these are used only by tree. */
+  CONCAT = 16,
+  SUBEXP = 17,
+
+  /* Token type, these are used only by token.  */
+  OP_DUP_PLUS = 18,
+  OP_DUP_QUESTION,
+  OP_OPEN_BRACKET,
+  OP_CLOSE_BRACKET,
+  OP_CHARSET_RANGE,
+  OP_OPEN_DUP_NUM,
+  OP_CLOSE_DUP_NUM,
+  OP_NON_MATCH_LIST,
+  OP_OPEN_COLL_ELEM,
+  OP_CLOSE_COLL_ELEM,
+  OP_OPEN_EQUIV_CLASS,
+  OP_CLOSE_EQUIV_CLASS,
+  OP_OPEN_CHAR_CLASS,
+  OP_CLOSE_CHAR_CLASS,
+  OP_WORD,
+  OP_NOTWORD,
+  OP_SPACE,
+  OP_NOTSPACE,
+  BACK_SLASH
+
+} re_token_type_t;
+
+#ifdef RE_ENABLE_I18N
+typedef struct
+{
+  /* Multibyte characters.  */
+  wchar_t *mbchars;
+
+  /* Collating symbols.  */
+# ifdef _LIBC
+  int32_t *coll_syms;
+# endif
+
+  /* Equivalence classes. */
+# ifdef _LIBC
+  int32_t *equiv_classes;
+# endif
+
+  /* Range expressions. */
+# ifdef _LIBC
+  uint32_t *range_starts;
+  uint32_t *range_ends;
+# else /* not _LIBC */
+  wchar_t *range_starts;
+  wchar_t *range_ends;
+# endif /* not _LIBC */
+
+  /* Character classes. */
+  wctype_t *char_classes;
+
+  /* If this character set is the non-matching list.  */
+  unsigned int non_match : 1;
+
+  /* # of multibyte characters.  */
+  int nmbchars;
+
+  /* # of collating symbols.  */
+  int ncoll_syms;
+
+  /* # of equivalence classes. */
+  int nequiv_classes;
+
+  /* # of range expressions. */
+  int nranges;
+
+  /* # of character classes. */
+  int nchar_classes;
+} re_charset_t;
+#endif /* RE_ENABLE_I18N */
+
+typedef struct
+{
+  union
+  {
+    unsigned char c;           /* for CHARACTER */
+    re_bitset_ptr_t sbcset;    /* for SIMPLE_BRACKET */
+#ifdef RE_ENABLE_I18N
+    re_charset_t *mbcset;      /* for COMPLEX_BRACKET */
+#endif /* RE_ENABLE_I18N */
+    int idx;                   /* for BACK_REF */
+    re_context_type ctx_type;  /* for ANCHOR */
+  } opr;
+#if __GNUC__ >= 2
+  re_token_type_t type : 8;
+#else
+  re_token_type_t type;
+#endif
+  unsigned int constraint : 10;        /* context constraint */
+  unsigned int duplicated : 1;
+  unsigned int opt_subexp : 1;
+#ifdef RE_ENABLE_I18N
+  unsigned int accept_mb : 1;
+  /* These 2 bits can be moved into the union if needed (e.g. if running out
+     of bits; move opr.c to opr.c.c and move the flags to opr.c.flags).  */
+  unsigned int mb_partial : 1;
+#endif
+  unsigned int word_char : 1;
+} re_token_t;
+
+#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT)
+
+struct re_string_t
+{
+  /* Indicate the raw buffer which is the original string passed as an
+     argument of regexec(), re_search(), etc..  */
+  const unsigned char *raw_mbs;
+  /* Store the multibyte string.  In case of "case insensitive mode" like
+     REG_ICASE, upper cases of the string are stored, otherwise MBS points
+     the same address that RAW_MBS points.  */
+  unsigned char *mbs;
+#ifdef RE_ENABLE_I18N
+  /* Store the wide character string which is corresponding to MBS.  */
+  wint_t *wcs;
+  int *offsets;
+  mbstate_t cur_state;
+#endif
+  /* Index in RAW_MBS.  Each character mbs[i] corresponds to
+     raw_mbs[raw_mbs_idx + i].  */
+  int raw_mbs_idx;
+  /* The length of the valid characters in the buffers.  */
+  int valid_len;
+  /* The corresponding number of bytes in raw_mbs array.  */
+  int valid_raw_len;
+  /* The length of the buffers MBS and WCS.  */
+  int bufs_len;
+  /* The index in MBS, which is updated by re_string_fetch_byte.  */
+  int cur_idx;
+  /* length of RAW_MBS array.  */
+  int raw_len;
+  /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN.  */
+  int len;
+  /* End of the buffer may be shorter than its length in the cases such
+     as re_match_2, re_search_2.  Then, we use STOP for end of the buffer
+     instead of LEN.  */
+  int raw_stop;
+  /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS.  */
+  int stop;
+
+  /* The context of mbs[0].  We store the context independently, since
+     the context of mbs[0] may be different from raw_mbs[0], which is
+     the beginning of the input string.  */
+  unsigned int tip_context;
+  /* The translation passed as a part of an argument of re_compile_pattern.  */
+  RE_TRANSLATE_TYPE trans;
+  /* Copy of re_dfa_t's word_char.  */
+  re_const_bitset_ptr_t word_char;
+  /* 1 if REG_ICASE.  */
+  unsigned char icase;
+  unsigned char is_utf8;
+  unsigned char map_notascii;
+  unsigned char mbs_allocated;
+  unsigned char offsets_needed;
+  unsigned char newline_anchor;
+  unsigned char word_ops_used;
+  int mb_cur_max;
+};
+typedef struct re_string_t re_string_t;
+
+
+struct re_dfa_t;
+typedef struct re_dfa_t re_dfa_t;
+
+#ifndef _LIBC
+# ifdef __i386__
+#  define internal_function   __attribute ((regparm (3), stdcall))
+# else
+#  define internal_function
+# endif
+#endif
+
+#ifndef NOT_IN_libc
+static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
+                                               int new_buf_len)
+     internal_function;
+# ifdef RE_ENABLE_I18N
+static void build_wcs_buffer (re_string_t *pstr) internal_function;
+static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr)
+  internal_function;
+# endif /* RE_ENABLE_I18N */
+static void build_upper_buffer (re_string_t *pstr) internal_function;
+static void re_string_translate_buffer (re_string_t *pstr) internal_function;
+static unsigned int re_string_context_at (const re_string_t *input, int idx,
+                                         int eflags)
+     internal_function __attribute ((pure));
+#endif
+#define re_string_peek_byte(pstr, offset) \
+  ((pstr)->mbs[(pstr)->cur_idx + offset])
+#define re_string_fetch_byte(pstr) \
+  ((pstr)->mbs[(pstr)->cur_idx++])
+#define re_string_first_byte(pstr, idx) \
+  ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF)
+#define re_string_is_single_byte_char(pstr, idx) \
+  ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \
+                               || (pstr)->wcs[(idx) + 1] != WEOF))
+#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
+#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
+#define re_string_get_buffer(pstr) ((pstr)->mbs)
+#define re_string_length(pstr) ((pstr)->len)
+#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
+#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
+#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
+
+#ifndef _LIBC
+# if HAVE_ALLOCA
+#  if (_MSC_VER)
+#   include <malloc.h>
+#   define __libc_use_alloca(n) 0
+#  else
+#   include <alloca.h>
+/* The OS usually guarantees only one guard page at the bottom of the stack,
+   and a page size can be as small as 4096 bytes.  So we cannot safely
+   allocate anything larger than 4096 bytes.  Also care for the possibility
+   of a few compiler-allocated temporary stack slots.  */
+#  define __libc_use_alloca(n) ((n) < 4032)
+#  endif
+# else
+/* alloca is implemented with malloc, so just use malloc.  */
+#  define __libc_use_alloca(n) 0
+# endif
+#endif
+
+#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
+/* SunOS 4.1.x realloc doesn't accept null pointers: pre-Standard C. Sigh. */
+#define re_realloc(p,t,n) ((p != NULL) ? (t *) realloc (p,(n)*sizeof(t)) : (t *) calloc(n,sizeof(t)))
+#define re_free(p) free (p)
+
+struct bin_tree_t
+{
+  struct bin_tree_t *parent;
+  struct bin_tree_t *left;
+  struct bin_tree_t *right;
+  struct bin_tree_t *first;
+  struct bin_tree_t *next;
+
+  re_token_t token;
+
+  /* `node_idx' is the index in dfa->nodes, if `type' == 0.
+     Otherwise `type' indicate the type of this node.  */
+  int node_idx;
+};
+typedef struct bin_tree_t bin_tree_t;
+
+#define BIN_TREE_STORAGE_SIZE \
+  ((1024 - sizeof (void *)) / sizeof (bin_tree_t))
+
+struct bin_tree_storage_t
+{
+  struct bin_tree_storage_t *next;
+  bin_tree_t data[BIN_TREE_STORAGE_SIZE];
+};
+typedef struct bin_tree_storage_t bin_tree_storage_t;
+
+#define CONTEXT_WORD 1
+#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
+#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
+#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
+
+#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
+#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
+#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
+#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
+#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
+
+#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
+#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
+#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
+#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
+
+#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
+ ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+  || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+  || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
+  || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
+
+#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
+ ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+  || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+  || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
+  || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
+
+struct re_dfastate_t
+{
+  unsigned int hash;
+  re_node_set nodes;
+  re_node_set non_eps_nodes;
+  re_node_set inveclosure;
+  re_node_set *entrance_nodes;
+  struct re_dfastate_t **trtable, **word_trtable;
+  unsigned int context : 4;
+  unsigned int halt : 1;
+  /* If this state can accept `multi byte'.
+     Note that we refer to multibyte characters, and multi character
+     collating elements as `multi byte'.  */
+  unsigned int accept_mb : 1;
+  /* If this state has backreference node(s).  */
+  unsigned int has_backref : 1;
+  unsigned int has_constraint : 1;
+};
+typedef struct re_dfastate_t re_dfastate_t;
+
+struct re_state_table_entry
+{
+  int num;
+  int alloc;
+  re_dfastate_t **array;
+};
+
+/* Array type used in re_sub_match_last_t and re_sub_match_top_t.  */
+
+typedef struct
+{
+  int next_idx;
+  int alloc;
+  re_dfastate_t **array;
+} state_array_t;
+
+/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP.  */
+
+typedef struct
+{
+  int node;
+  int str_idx; /* The position NODE match at.  */
+  state_array_t path;
+} re_sub_match_last_t;
+
+/* Store information about the node NODE whose type is OP_OPEN_SUBEXP.
+   And information about the node, whose type is OP_CLOSE_SUBEXP,
+   corresponding to NODE is stored in LASTS.  */
+
+typedef struct
+{
+  int str_idx;
+  int node;
+  state_array_t *path;
+  int alasts; /* Allocation size of LASTS.  */
+  int nlasts; /* The number of LASTS.  */
+  re_sub_match_last_t **lasts;
+} re_sub_match_top_t;
+
+struct re_backref_cache_entry
+{
+  int node;
+  int str_idx;
+  int subexp_from;
+  int subexp_to;
+  char more;
+  char unused;
+  unsigned short int eps_reachable_subexps_map;
+};
+
+typedef struct
+{
+  /* The string object corresponding to the input string.  */
+  re_string_t input;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+  const re_dfa_t *const dfa;
+#else
+  const re_dfa_t *dfa;
+#endif
+  /* EFLAGS of the argument of regexec.  */
+  int eflags;
+  /* Where the matching ends.  */
+  int match_last;
+  int last_node;
+  /* The state log used by the matcher.  */
+  re_dfastate_t **state_log;
+  int state_log_top;
+  /* Back reference cache.  */
+  int nbkref_ents;
+  int abkref_ents;
+  struct re_backref_cache_entry *bkref_ents;
+  int max_mb_elem_len;
+  int nsub_tops;
+  int asub_tops;
+  re_sub_match_top_t **sub_tops;
+} re_match_context_t;
+
+typedef struct
+{
+  re_dfastate_t **sifted_states;
+  re_dfastate_t **limited_states;
+  int last_node;
+  int last_str_idx;
+  re_node_set limits;
+} re_sift_context_t;
+
+struct re_fail_stack_ent_t
+{
+  int idx;
+  int node;
+  regmatch_t *regs;
+  re_node_set eps_via_nodes;
+};
+
+struct re_fail_stack_t
+{
+  int num;
+  int alloc;
+  struct re_fail_stack_ent_t *stack;
+};
+
+struct re_dfa_t
+{
+  re_token_t *nodes;
+  size_t nodes_alloc;
+  size_t nodes_len;
+  int *nexts;
+  int *org_indices;
+  re_node_set *edests;
+  re_node_set *eclosures;
+  re_node_set *inveclosures;
+  struct re_state_table_entry *state_table;
+  re_dfastate_t *init_state;
+  re_dfastate_t *init_state_word;
+  re_dfastate_t *init_state_nl;
+  re_dfastate_t *init_state_begbuf;
+  bin_tree_t *str_tree;
+  bin_tree_storage_t *str_tree_storage;
+  re_bitset_ptr_t sb_char;
+  int str_tree_storage_idx;
+
+  /* number of subexpressions `re_nsub' is in regex_t.  */
+  unsigned int state_hash_mask;
+  int init_node;
+  int nbackref; /* The number of backreference in this dfa.  */
+
+  /* Bitmap expressing which backreference is used.  */
+  bitset_word_t used_bkref_map;
+  bitset_word_t completed_bkref_map;
+
+  unsigned int has_plural_match : 1;
+  /* If this dfa has "multibyte node", which is a backreference or
+     a node which can accept multibyte character or multi character
+     collating element.  */
+  unsigned int has_mb_node : 1;
+  unsigned int is_utf8 : 1;
+  unsigned int map_notascii : 1;
+  unsigned int word_ops_used : 1;
+  int mb_cur_max;
+  bitset_t word_char;
+  reg_syntax_t syntax;
+  int *subexp_map;
+#ifdef DEBUG
+  char* re_str;
+#endif
+#if defined _LIBC
+  __libc_lock_define (, lock)
+#endif
+};
+
+#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
+#define re_node_set_remove(set,id) \
+  (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1))
+#define re_node_set_empty(p) ((p)->nelem = 0)
+#define re_node_set_free(set) re_free ((set)->elems)
+\f
+
+typedef enum
+{
+  SB_CHAR,
+  MB_CHAR,
+  EQUIV_CLASS,
+  COLL_SYM,
+  CHAR_CLASS
+} bracket_elem_type;
+
+typedef struct
+{
+  bracket_elem_type type;
+  union
+  {
+    unsigned char ch;
+    unsigned char *name;
+    wchar_t wch;
+  } opr;
+} bracket_elem_t;
+
+
+/* Inline functions for bitset operation.  */
+static inline void
+bitset_not (bitset_t set)
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+    set[bitset_i] = ~set[bitset_i];
+}
+
+static inline void
+bitset_merge (bitset_t dest, const bitset_t src)
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+    dest[bitset_i] |= src[bitset_i];
+}
+
+static inline void
+bitset_mask (bitset_t dest, const bitset_t src)
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+    dest[bitset_i] &= src[bitset_i];
+}
+
+#ifdef RE_ENABLE_I18N
+/* Inline functions for re_string.  */
+static inline int
+internal_function __attribute ((pure))
+re_string_char_size_at (const re_string_t *pstr, int idx)
+{
+  int byte_idx;
+  if (pstr->mb_cur_max == 1)
+    return 1;
+  for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx)
+    if (pstr->wcs[idx + byte_idx] != WEOF)
+      break;
+  return byte_idx;
+}
+
+static inline wint_t
+internal_function __attribute ((pure))
+re_string_wchar_at (const re_string_t *pstr, int idx)
+{
+  if (pstr->mb_cur_max == 1)
+    return (wint_t) pstr->mbs[idx];
+  return (wint_t) pstr->wcs[idx];
+}
+
+# ifndef NOT_IN_libc
+static int
+internal_function __attribute ((pure))
+re_string_elem_size_at (const re_string_t *pstr, int idx)
+{
+#  ifdef _LIBC
+  const unsigned char *p, *extra;
+  const int32_t *table, *indirect;
+  int32_t tmp;
+#   include <locale/weight.h>
+  uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+
+  if (nrules != 0)
+    {
+      table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+      extra = (const unsigned char *)
+       _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+      indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+                                               _NL_COLLATE_INDIRECTMB);
+      p = pstr->mbs + idx;
+      tmp = findidx (&p);
+      return p - pstr->mbs - idx;
+    }
+  else
+#  endif /* _LIBC */
+    return 1;
+}
+# endif
+#endif /* RE_ENABLE_I18N */
+
+#endif /*  _REGEX_INTERNAL_H */
diff --git a/compat/regex/regexec.c b/compat/regex/regexec.c
new file mode 100644 (file)
index 0000000..0194965
--- /dev/null
@@ -0,0 +1,4369 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002-2005, 2007, 2009, 2010 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA.  */
+
+static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
+                                    int n) internal_function;
+static void match_ctx_clean (re_match_context_t *mctx) internal_function;
+static void match_ctx_free (re_match_context_t *cache) internal_function;
+static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node,
+                                         int str_idx, int from, int to)
+     internal_function;
+static int search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx)
+     internal_function;
+static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node,
+                                          int str_idx) internal_function;
+static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop,
+                                                  int node, int str_idx)
+     internal_function;
+static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+                          re_dfastate_t **limited_sts, int last_node,
+                          int last_str_idx)
+     internal_function;
+static reg_errcode_t re_search_internal (const regex_t *preg,
+                                        const char *string, int length,
+                                        int start, int range, int stop,
+                                        size_t nmatch, regmatch_t pmatch[],
+                                        int eflags);
+static int re_search_2_stub (struct re_pattern_buffer *bufp,
+                            const char *string1, int length1,
+                            const char *string2, int length2,
+                            int start, int range, struct re_registers *regs,
+                            int stop, int ret_len);
+static int re_search_stub (struct re_pattern_buffer *bufp,
+                          const char *string, int length, int start,
+                          int range, int stop, struct re_registers *regs,
+                          int ret_len);
+static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch,
+                             int nregs, int regs_allocated);
+static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx);
+static int check_matching (re_match_context_t *mctx, int fl_longest_match,
+                          int *p_match_first) internal_function;
+static int check_halt_state_context (const re_match_context_t *mctx,
+                                    const re_dfastate_t *state, int idx)
+     internal_function;
+static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+                        regmatch_t *prev_idx_match, int cur_node,
+                        int cur_idx, int nmatch) internal_function;
+static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
+                                     int str_idx, int dest_node, int nregs,
+                                     regmatch_t *regs,
+                                     re_node_set *eps_via_nodes)
+     internal_function;
+static reg_errcode_t set_regs (const regex_t *preg,
+                              const re_match_context_t *mctx,
+                              size_t nmatch, regmatch_t *pmatch,
+                              int fl_backtrack) internal_function;
+static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs)
+     internal_function;
+
+#ifdef RE_ENABLE_I18N
+static int sift_states_iter_mb (const re_match_context_t *mctx,
+                               re_sift_context_t *sctx,
+                               int node_idx, int str_idx, int max_str_idx)
+     internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t sift_states_backward (const re_match_context_t *mctx,
+                                          re_sift_context_t *sctx)
+     internal_function;
+static reg_errcode_t build_sifted_states (const re_match_context_t *mctx,
+                                         re_sift_context_t *sctx, int str_idx,
+                                         re_node_set *cur_dest)
+     internal_function;
+static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx,
+                                             re_sift_context_t *sctx,
+                                             int str_idx,
+                                             re_node_set *dest_nodes)
+     internal_function;
+static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa,
+                                           re_node_set *dest_nodes,
+                                           const re_node_set *candidates)
+     internal_function;
+static int check_dst_limits (const re_match_context_t *mctx,
+                            re_node_set *limits,
+                            int dst_node, int dst_idx, int src_node,
+                            int src_idx) internal_function;
+static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx,
+                                       int boundaries, int subexp_idx,
+                                       int from_node, int bkref_idx)
+     internal_function;
+static int check_dst_limits_calc_pos (const re_match_context_t *mctx,
+                                     int limit, int subexp_idx,
+                                     int node, int str_idx,
+                                     int bkref_idx) internal_function;
+static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa,
+                                         re_node_set *dest_nodes,
+                                         const re_node_set *candidates,
+                                         re_node_set *limits,
+                                         struct re_backref_cache_entry *bkref_ents,
+                                         int str_idx) internal_function;
+static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx,
+                                       re_sift_context_t *sctx,
+                                       int str_idx, const re_node_set *candidates)
+     internal_function;
+static reg_errcode_t merge_state_array (const re_dfa_t *dfa,
+                                       re_dfastate_t **dst,
+                                       re_dfastate_t **src, int num)
+     internal_function;
+static re_dfastate_t *find_recover_state (reg_errcode_t *err,
+                                        re_match_context_t *mctx) internal_function;
+static re_dfastate_t *transit_state (reg_errcode_t *err,
+                                    re_match_context_t *mctx,
+                                    re_dfastate_t *state) internal_function;
+static re_dfastate_t *merge_state_with_log (reg_errcode_t *err,
+                                           re_match_context_t *mctx,
+                                           re_dfastate_t *next_state)
+     internal_function;
+static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx,
+                                               re_node_set *cur_nodes,
+                                               int str_idx) internal_function;
+#if 0
+static re_dfastate_t *transit_state_sb (reg_errcode_t *err,
+                                       re_match_context_t *mctx,
+                                       re_dfastate_t *pstate)
+     internal_function;
+#endif
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t transit_state_mb (re_match_context_t *mctx,
+                                      re_dfastate_t *pstate)
+     internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t transit_state_bkref (re_match_context_t *mctx,
+                                         const re_node_set *nodes)
+     internal_function;
+static reg_errcode_t get_subexp (re_match_context_t *mctx,
+                                int bkref_node, int bkref_str_idx)
+     internal_function;
+static reg_errcode_t get_subexp_sub (re_match_context_t *mctx,
+                                    const re_sub_match_top_t *sub_top,
+                                    re_sub_match_last_t *sub_last,
+                                    int bkref_node, int bkref_str)
+     internal_function;
+static int find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+                            int subexp_idx, int type) internal_function;
+static reg_errcode_t check_arrival (re_match_context_t *mctx,
+                                   state_array_t *path, int top_node,
+                                   int top_str, int last_node, int last_str,
+                                   int type) internal_function;
+static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx,
+                                                  int str_idx,
+                                                  re_node_set *cur_nodes,
+                                                  re_node_set *next_nodes)
+     internal_function;
+static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa,
+                                              re_node_set *cur_nodes,
+                                              int ex_subexp, int type)
+     internal_function;
+static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa,
+                                                  re_node_set *dst_nodes,
+                                                  int target, int ex_subexp,
+                                                  int type) internal_function;
+static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx,
+                                        re_node_set *cur_nodes, int cur_str,
+                                        int subexp_num, int type)
+     internal_function;
+static int build_trtable (const re_dfa_t *dfa,
+                         re_dfastate_t *state) internal_function;
+#ifdef RE_ENABLE_I18N
+static int check_node_accept_bytes (const re_dfa_t *dfa, int node_idx,
+                                   const re_string_t *input, int idx)
+     internal_function;
+# ifdef _LIBC
+static unsigned int find_collation_sequence_value (const unsigned char *mbs,
+                                                  size_t name_len)
+     internal_function;
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+static int group_nodes_into_DFAstates (const re_dfa_t *dfa,
+                                      const re_dfastate_t *state,
+                                      re_node_set *states_node,
+                                      bitset_t *states_ch) internal_function;
+static int check_node_accept (const re_match_context_t *mctx,
+                             const re_token_t *node, int idx)
+     internal_function;
+static reg_errcode_t extend_buffers (re_match_context_t *mctx)
+     internal_function;
+\f
+/* Entry point for POSIX code.  */
+
+/* regexec searches for a given pattern, specified by PREG, in the
+   string STRING.
+
+   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
+   least NMATCH elements, and we set them to the offsets of the
+   corresponding matched substrings.
+
+   EFLAGS specifies `execution flags' which affect matching: if
+   REG_NOTBOL is set, then ^ does not match at the beginning of the
+   string; if REG_NOTEOL is set, then $ does not match at the end.
+
+   We return 0 if we find a match and REG_NOMATCH if not.  */
+
+int
+regexec (
+       const regex_t *__restrict preg,
+       const char *__restrict string,
+       size_t nmatch,
+       regmatch_t pmatch[],
+       int eflags)
+{
+  reg_errcode_t err;
+  int start, length;
+
+  if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND))
+    return REG_BADPAT;
+
+  if (eflags & REG_STARTEND)
+    {
+      start = pmatch[0].rm_so;
+      length = pmatch[0].rm_eo;
+    }
+  else
+    {
+      start = 0;
+      length = strlen (string);
+    }
+
+  __libc_lock_lock (dfa->lock);
+  if (preg->no_sub)
+    err = re_search_internal (preg, string, length, start, length - start,
+                             length, 0, NULL, eflags);
+  else
+    err = re_search_internal (preg, string, length, start, length - start,
+                             length, nmatch, pmatch, eflags);
+  __libc_lock_unlock (dfa->lock);
+  return err != REG_NOERROR;
+}
+
+#ifdef _LIBC
+# include <shlib-compat.h>
+versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4);
+
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4)
+__typeof__ (__regexec) __compat_regexec;
+
+int
+attribute_compat_text_section
+__compat_regexec (const regex_t *__restrict preg,
+                 const char *__restrict string, size_t nmatch,
+                 regmatch_t pmatch[], int eflags)
+{
+  return regexec (preg, string, nmatch, pmatch,
+                 eflags & (REG_NOTBOL | REG_NOTEOL));
+}
+compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0);
+# endif
+#endif
+
+/* Entry points for GNU code.  */
+
+/* re_match, re_search, re_match_2, re_search_2
+
+   The former two functions operate on STRING with length LENGTH,
+   while the later two operate on concatenation of STRING1 and STRING2
+   with lengths LENGTH1 and LENGTH2, respectively.
+
+   re_match() matches the compiled pattern in BUFP against the string,
+   starting at index START.
+
+   re_search() first tries matching at index START, then it tries to match
+   starting from index START + 1, and so on.  The last start position tried
+   is START + RANGE.  (Thus RANGE = 0 forces re_search to operate the same
+   way as re_match().)
+
+   The parameter STOP of re_{match,search}_2 specifies that no match exceeding
+   the first STOP characters of the concatenation of the strings should be
+   concerned.
+
+   If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match
+   and all groups is stroed in REGS.  (For the "_2" variants, the offsets are
+   computed relative to the concatenation, not relative to the individual
+   strings.)
+
+   On success, re_match* functions return the length of the match, re_search*
+   return the position of the start of the match.  Return value -1 means no
+   match was found and -2 indicates an internal error.  */
+
+int
+re_match (struct re_pattern_buffer *bufp,
+         const char *string,
+         int length,
+         int start,
+         struct re_registers *regs)
+{
+  return re_search_stub (bufp, string, length, start, 0, length, regs, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match, re_match)
+#endif
+
+int
+re_search (struct re_pattern_buffer *bufp,
+          const char *string,
+          int length, int start, int range,
+          struct re_registers *regs)
+{
+  return re_search_stub (bufp, string, length, start, range, length, regs, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+int
+re_match_2 (struct re_pattern_buffer *bufp,
+           const char *string1, int length1,
+           const char *string2, int length2, int start,
+           struct re_registers *regs, int stop)
+{
+  return re_search_2_stub (bufp, string1, length1, string2, length2,
+                          start, 0, regs, stop, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+int
+re_search_2 (struct re_pattern_buffer *bufp,
+            const char *string1, int length1,
+            const char *string2, int length2, int start,
+            int range, struct re_registers *regs,  int stop)
+{
+  return re_search_2_stub (bufp, string1, length1, string2, length2,
+                          start, range, regs, stop, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+static int
+re_search_2_stub (struct re_pattern_buffer *bufp,
+                 const char *string1, int length1,
+                 const char *string2, int length2, int start,
+                 int range, struct re_registers *regs,
+                 int stop, int ret_len)
+{
+  const char *str;
+  int rval;
+  int len = length1 + length2;
+  int free_str = 0;
+
+  if (BE (length1 < 0 || length2 < 0 || stop < 0, 0))
+    return -2;
+
+  /* Concatenate the strings.  */
+  if (length2 > 0)
+    if (length1 > 0)
+      {
+       char *s = re_malloc (char, len);
+
+       if (BE (s == NULL, 0))
+         return -2;
+       memcpy (s, string1, length1);
+       memcpy (s + length1, string2, length2);
+       str = s;
+       free_str = 1;
+      }
+    else
+      str = string2;
+  else
+    str = string1;
+
+  rval = re_search_stub (bufp, str, len, start, range, stop, regs, ret_len);
+  if (free_str)
+    re_free ((char *) str);
+  return rval;
+}
+
+/* The parameters have the same meaning as those of re_search.
+   Additional parameters:
+   If RET_LEN is nonzero the length of the match is returned (re_match style);
+   otherwise the position of the match is returned.  */
+
+static int
+re_search_stub (struct re_pattern_buffer *bufp,
+               const char *string, int length, int start,
+               int range, int stop,
+               struct re_registers *regs, int ret_len)
+{
+  reg_errcode_t result;
+  regmatch_t *pmatch;
+  int nregs, rval;
+  int eflags = 0;
+
+  /* Check for out-of-range.  */
+  if (BE (start < 0 || start > length, 0))
+    return -1;
+  if (BE (start + range > length, 0))
+    range = length - start;
+  else if (BE (start + range < 0, 0))
+    range = -start;
+
+  __libc_lock_lock (dfa->lock);
+
+  eflags |= (bufp->not_bol) ? REG_NOTBOL : 0;
+  eflags |= (bufp->not_eol) ? REG_NOTEOL : 0;
+
+  /* Compile fastmap if we haven't yet.  */
+  if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate)
+    re_compile_fastmap (bufp);
+
+  if (BE (bufp->no_sub, 0))
+    regs = NULL;
+
+  /* We need at least 1 register.  */
+  if (regs == NULL)
+    nregs = 1;
+  else if (BE (bufp->regs_allocated == REGS_FIXED &&
+              regs->num_regs < bufp->re_nsub + 1, 0))
+    {
+      nregs = regs->num_regs;
+      if (BE (nregs < 1, 0))
+       {
+         /* Nothing can be copied to regs.  */
+         regs = NULL;
+         nregs = 1;
+       }
+    }
+  else
+    nregs = bufp->re_nsub + 1;
+  pmatch = re_malloc (regmatch_t, nregs);
+  if (BE (pmatch == NULL, 0))
+    {
+      rval = -2;
+      goto out;
+    }
+
+  result = re_search_internal (bufp, string, length, start, range, stop,
+                              nregs, pmatch, eflags);
+
+  rval = 0;
+
+  /* I hope we needn't fill ther regs with -1's when no match was found.  */
+  if (result != REG_NOERROR)
+    rval = -1;
+  else if (regs != NULL)
+    {
+      /* If caller wants register contents data back, copy them.  */
+      bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs,
+                                          bufp->regs_allocated);
+      if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0))
+       rval = -2;
+    }
+
+  if (BE (rval == 0, 1))
+    {
+      if (ret_len)
+       {
+         assert (pmatch[0].rm_so == start);
+         rval = pmatch[0].rm_eo - start;
+       }
+      else
+       rval = pmatch[0].rm_so;
+    }
+  re_free (pmatch);
+ out:
+  __libc_lock_unlock (dfa->lock);
+  return rval;
+}
+
+static unsigned
+re_copy_regs (struct re_registers *regs,
+             regmatch_t *pmatch,
+             int nregs, int regs_allocated)
+{
+  int rval = REGS_REALLOCATE;
+  int i;
+  int need_regs = nregs + 1;
+  /* We need one extra element beyond `num_regs' for the `-1' marker GNU code
+     uses.  */
+
+  /* Have the register data arrays been allocated?  */
+  if (regs_allocated == REGS_UNALLOCATED)
+    { /* No.  So allocate them with malloc.  */
+      regs->start = re_malloc (regoff_t, need_regs);
+      if (BE (regs->start == NULL, 0))
+       return REGS_UNALLOCATED;
+      regs->end = re_malloc (regoff_t, need_regs);
+      if (BE (regs->end == NULL, 0))
+       {
+         re_free (regs->start);
+         return REGS_UNALLOCATED;
+       }
+      regs->num_regs = need_regs;
+    }
+  else if (regs_allocated == REGS_REALLOCATE)
+    { /* Yes.  If we need more elements than were already
+        allocated, reallocate them.  If we need fewer, just
+        leave it alone.  */
+      if (BE (need_regs > regs->num_regs, 0))
+       {
+         regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs);
+         regoff_t *new_end;
+         if (BE (new_start == NULL, 0))
+           return REGS_UNALLOCATED;
+         new_end = re_realloc (regs->end, regoff_t, need_regs);
+         if (BE (new_end == NULL, 0))
+           {
+             re_free (new_start);
+             return REGS_UNALLOCATED;
+           }
+         regs->start = new_start;
+         regs->end = new_end;
+         regs->num_regs = need_regs;
+       }
+    }
+  else
+    {
+      assert (regs_allocated == REGS_FIXED);
+      /* This function may not be called with REGS_FIXED and nregs too big.  */
+      assert (regs->num_regs >= nregs);
+      rval = REGS_FIXED;
+    }
+
+  /* Copy the regs.  */
+  for (i = 0; i < nregs; ++i)
+    {
+      regs->start[i] = pmatch[i].rm_so;
+      regs->end[i] = pmatch[i].rm_eo;
+    }
+  for ( ; i < regs->num_regs; ++i)
+    regs->start[i] = regs->end[i] = -1;
+
+  return rval;
+}
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
+   this memory for recording register information.  STARTS and ENDS
+   must be allocated using the malloc library routine, and must each
+   be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+
+void
+re_set_registers (struct re_pattern_buffer *bufp,
+                 struct re_registers *regs,
+                 unsigned num_regs,
+                 regoff_t *starts,
+                 regoff_t *ends)
+{
+  if (num_regs)
+    {
+      bufp->regs_allocated = REGS_REALLOCATE;
+      regs->num_regs = num_regs;
+      regs->start = starts;
+      regs->end = ends;
+    }
+  else
+    {
+      bufp->regs_allocated = REGS_UNALLOCATED;
+      regs->num_regs = 0;
+      regs->start = regs->end = (regoff_t *) 0;
+    }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+\f
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them unless specifically requested.  */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+int
+# ifdef _LIBC
+weak_function
+# endif
+re_exec (s)
+     const char *s;
+{
+  return 0 == regexec (&re_comp_buf, s, 0, NULL, 0);
+}
+#endif /* _REGEX_RE_COMP */
+\f
+/* Internal entry point.  */
+
+/* Searches for a compiled pattern PREG in the string STRING, whose
+   length is LENGTH.  NMATCH, PMATCH, and EFLAGS have the same
+   mingings with regexec.  START, and RANGE have the same meanings
+   with re_search.
+   Return REG_NOERROR if we find a match, and REG_NOMATCH if not,
+   otherwise return the error code.
+   Note: We assume front end functions already check ranges.
+   (START + RANGE >= 0 && START + RANGE <= LENGTH)  */
+
+static reg_errcode_t
+re_search_internal (const regex_t *preg,
+                   const char *string,
+                   int length, int start, int range, int stop,
+                   size_t nmatch, regmatch_t pmatch[],
+                   int eflags)
+{
+  reg_errcode_t err;
+  const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer;
+  int left_lim, right_lim, incr;
+  int fl_longest_match, match_first, match_kind, match_last = -1;
+  int extra_nmatch;
+  int sb, ch;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+  re_match_context_t mctx = { .dfa = dfa };
+#else
+  re_match_context_t mctx;
+#endif
+  char *fastmap = (preg->fastmap != NULL && preg->fastmap_accurate
+                  && range && !preg->can_be_null) ? preg->fastmap : NULL;
+  RE_TRANSLATE_TYPE t = preg->translate;
+
+#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L))
+  memset (&mctx, '\0', sizeof (re_match_context_t));
+  mctx.dfa = dfa;
+#endif
+
+  extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0;
+  nmatch -= extra_nmatch;
+
+  /* Check if the DFA haven't been compiled.  */
+  if (BE (preg->used == 0 || dfa->init_state == NULL
+         || dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+         || dfa->init_state_begbuf == NULL, 0))
+    return REG_NOMATCH;
+
+#ifdef DEBUG
+  /* We assume front-end functions already check them.  */
+  assert (start + range >= 0 && start + range <= length);
+#endif
+
+  /* If initial states with non-begbuf contexts have no elements,
+     the regex must be anchored.  If preg->newline_anchor is set,
+     we'll never use init_state_nl, so do not check it.  */
+  if (dfa->init_state->nodes.nelem == 0
+      && dfa->init_state_word->nodes.nelem == 0
+      && (dfa->init_state_nl->nodes.nelem == 0
+         || !preg->newline_anchor))
+    {
+      if (start != 0 && start + range != 0)
+       return REG_NOMATCH;
+      start = range = 0;
+    }
+
+  /* We must check the longest matching, if nmatch > 0.  */
+  fl_longest_match = (nmatch != 0 || dfa->nbackref);
+
+  err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1,
+                           preg->translate, preg->syntax & RE_ICASE, dfa);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+  mctx.input.stop = stop;
+  mctx.input.raw_stop = stop;
+  mctx.input.newline_anchor = preg->newline_anchor;
+
+  err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+
+  /* We will log all the DFA states through which the dfa pass,
+     if nmatch > 1, or this dfa has "multibyte node", which is a
+     back-reference or a node which can accept multibyte character or
+     multi character collating element.  */
+  if (nmatch > 1 || dfa->has_mb_node)
+    {
+      /* Avoid overflow.  */
+      if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= mctx.input.bufs_len, 0))
+       {
+         err = REG_ESPACE;
+         goto free_return;
+       }
+
+      mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1);
+      if (BE (mctx.state_log == NULL, 0))
+       {
+         err = REG_ESPACE;
+         goto free_return;
+       }
+    }
+  else
+    mctx.state_log = NULL;
+
+  match_first = start;
+  mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+                          : CONTEXT_NEWLINE | CONTEXT_BEGBUF;
+
+  /* Check incrementally whether of not the input string match.  */
+  incr = (range < 0) ? -1 : 1;
+  left_lim = (range < 0) ? start + range : start;
+  right_lim = (range < 0) ? start : start + range;
+  sb = dfa->mb_cur_max == 1;
+  match_kind =
+    (fastmap
+     ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0)
+       | (range >= 0 ? 2 : 0)
+       | (t != NULL ? 1 : 0))
+     : 8);
+
+  for (;; match_first += incr)
+    {
+      err = REG_NOMATCH;
+      if (match_first < left_lim || right_lim < match_first)
+       goto free_return;
+
+      /* Advance as rapidly as possible through the string, until we
+        find a plausible place to start matching.  This may be done
+        with varying efficiency, so there are various possibilities:
+        only the most common of them are specialized, in order to
+        save on code size.  We use a switch statement for speed.  */
+      switch (match_kind)
+       {
+       case 8:
+         /* No fastmap.  */
+         break;
+
+       case 7:
+         /* Fastmap with single-byte translation, match forward.  */
+         while (BE (match_first < right_lim, 1)
+                && !fastmap[t[(unsigned char) string[match_first]]])
+           ++match_first;
+         goto forward_match_found_start_or_reached_end;
+
+       case 6:
+         /* Fastmap without translation, match forward.  */
+         while (BE (match_first < right_lim, 1)
+                && !fastmap[(unsigned char) string[match_first]])
+           ++match_first;
+
+       forward_match_found_start_or_reached_end:
+         if (BE (match_first == right_lim, 0))
+           {
+             ch = match_first >= length
+                      ? 0 : (unsigned char) string[match_first];
+             if (!fastmap[t ? t[ch] : ch])
+               goto free_return;
+           }
+         break;
+
+       case 4:
+       case 5:
+         /* Fastmap without multi-byte translation, match backwards.  */
+         while (match_first >= left_lim)
+           {
+             ch = match_first >= length
+                      ? 0 : (unsigned char) string[match_first];
+             if (fastmap[t ? t[ch] : ch])
+               break;
+             --match_first;
+           }
+         if (match_first < left_lim)
+           goto free_return;
+         break;
+
+       default:
+         /* In this case, we can't determine easily the current byte,
+            since it might be a component byte of a multibyte
+            character.  Then we use the constructed buffer instead.  */
+         for (;;)
+           {
+             /* If MATCH_FIRST is out of the valid range, reconstruct the
+                buffers.  */
+             unsigned int offset = match_first - mctx.input.raw_mbs_idx;
+             if (BE (offset >= (unsigned int) mctx.input.valid_raw_len, 0))
+               {
+                 err = re_string_reconstruct (&mctx.input, match_first,
+                                              eflags);
+                 if (BE (err != REG_NOERROR, 0))
+                   goto free_return;
+
+                 offset = match_first - mctx.input.raw_mbs_idx;
+               }
+             /* If MATCH_FIRST is out of the buffer, leave it as '\0'.
+                Note that MATCH_FIRST must not be smaller than 0.  */
+             ch = (match_first >= length
+                   ? 0 : re_string_byte_at (&mctx.input, offset));
+             if (fastmap[ch])
+               break;
+             match_first += incr;
+             if (match_first < left_lim || match_first > right_lim)
+               {
+                 err = REG_NOMATCH;
+                 goto free_return;
+               }
+           }
+         break;
+       }
+
+      /* Reconstruct the buffers so that the matcher can assume that
+        the matching starts from the beginning of the buffer.  */
+      err = re_string_reconstruct (&mctx.input, match_first, eflags);
+      if (BE (err != REG_NOERROR, 0))
+       goto free_return;
+
+#ifdef RE_ENABLE_I18N
+     /* Don't consider this char as a possible match start if it part,
+       yet isn't the head, of a multibyte character.  */
+      if (!sb && !re_string_first_byte (&mctx.input, 0))
+       continue;
+#endif
+
+      /* It seems to be appropriate one, then use the matcher.  */
+      /* We assume that the matching starts from 0.  */
+      mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0;
+      match_last = check_matching (&mctx, fl_longest_match,
+                                  range >= 0 ? &match_first : NULL);
+      if (match_last != -1)
+       {
+         if (BE (match_last == -2, 0))
+           {
+             err = REG_ESPACE;
+             goto free_return;
+           }
+         else
+           {
+             mctx.match_last = match_last;
+             if ((!preg->no_sub && nmatch > 1) || dfa->nbackref)
+               {
+                 re_dfastate_t *pstate = mctx.state_log[match_last];
+                 mctx.last_node = check_halt_state_context (&mctx, pstate,
+                                                            match_last);
+               }
+             if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match)
+                 || dfa->nbackref)
+               {
+                 err = prune_impossible_nodes (&mctx);
+                 if (err == REG_NOERROR)
+                   break;
+                 if (BE (err != REG_NOMATCH, 0))
+                   goto free_return;
+                 match_last = -1;
+               }
+             else
+               break; /* We found a match.  */
+           }
+       }
+
+      match_ctx_clean (&mctx);
+    }
+
+#ifdef DEBUG
+  assert (match_last != -1);
+  assert (err == REG_NOERROR);
+#endif
+
+  /* Set pmatch[] if we need.  */
+  if (nmatch > 0)
+    {
+      int reg_idx;
+
+      /* Initialize registers.  */
+      for (reg_idx = 1; reg_idx < nmatch; ++reg_idx)
+       pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1;
+
+      /* Set the points where matching start/end.  */
+      pmatch[0].rm_so = 0;
+      pmatch[0].rm_eo = mctx.match_last;
+
+      if (!preg->no_sub && nmatch > 1)
+       {
+         err = set_regs (preg, &mctx, nmatch, pmatch,
+                         dfa->has_plural_match && dfa->nbackref > 0);
+         if (BE (err != REG_NOERROR, 0))
+           goto free_return;
+       }
+
+      /* At last, add the offset to the each registers, since we slided
+        the buffers so that we could assume that the matching starts
+        from 0.  */
+      for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+       if (pmatch[reg_idx].rm_so != -1)
+         {
+#ifdef RE_ENABLE_I18N
+           if (BE (mctx.input.offsets_needed != 0, 0))
+             {
+               pmatch[reg_idx].rm_so =
+                 (pmatch[reg_idx].rm_so == mctx.input.valid_len
+                  ? mctx.input.valid_raw_len
+                  : mctx.input.offsets[pmatch[reg_idx].rm_so]);
+               pmatch[reg_idx].rm_eo =
+                 (pmatch[reg_idx].rm_eo == mctx.input.valid_len
+                  ? mctx.input.valid_raw_len
+                  : mctx.input.offsets[pmatch[reg_idx].rm_eo]);
+             }
+#else
+           assert (mctx.input.offsets_needed == 0);
+#endif
+           pmatch[reg_idx].rm_so += match_first;
+           pmatch[reg_idx].rm_eo += match_first;
+         }
+      for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx)
+       {
+         pmatch[nmatch + reg_idx].rm_so = -1;
+         pmatch[nmatch + reg_idx].rm_eo = -1;
+       }
+
+      if (dfa->subexp_map)
+       for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++)
+         if (dfa->subexp_map[reg_idx] != reg_idx)
+           {
+             pmatch[reg_idx + 1].rm_so
+               = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so;
+             pmatch[reg_idx + 1].rm_eo
+               = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo;
+           }
+    }
+
+ free_return:
+  re_free (mctx.state_log);
+  if (dfa->nbackref)
+    match_ctx_free (&mctx);
+  re_string_destruct (&mctx.input);
+  return err;
+}
+
+static reg_errcode_t
+prune_impossible_nodes (re_match_context_t *mctx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int halt_node, match_last;
+  reg_errcode_t ret;
+  re_dfastate_t **sifted_states;
+  re_dfastate_t **lim_states = NULL;
+  re_sift_context_t sctx;
+#ifdef DEBUG
+  assert (mctx->state_log != NULL);
+#endif
+  match_last = mctx->match_last;
+  halt_node = mctx->last_node;
+
+  /* Avoid overflow.  */
+  if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= match_last, 0))
+    return REG_ESPACE;
+
+  sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
+  if (BE (sifted_states == NULL, 0))
+    {
+      ret = REG_ESPACE;
+      goto free_return;
+    }
+  if (dfa->nbackref)
+    {
+      lim_states = re_malloc (re_dfastate_t *, match_last + 1);
+      if (BE (lim_states == NULL, 0))
+       {
+         ret = REG_ESPACE;
+         goto free_return;
+       }
+      while (1)
+       {
+         memset (lim_states, '\0',
+                 sizeof (re_dfastate_t *) * (match_last + 1));
+         sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
+                        match_last);
+         ret = sift_states_backward (mctx, &sctx);
+         re_node_set_free (&sctx.limits);
+         if (BE (ret != REG_NOERROR, 0))
+             goto free_return;
+         if (sifted_states[0] != NULL || lim_states[0] != NULL)
+           break;
+         do
+           {
+             --match_last;
+             if (match_last < 0)
+               {
+                 ret = REG_NOMATCH;
+                 goto free_return;
+               }
+           } while (mctx->state_log[match_last] == NULL
+                    || !mctx->state_log[match_last]->halt);
+         halt_node = check_halt_state_context (mctx,
+                                               mctx->state_log[match_last],
+                                               match_last);
+       }
+      ret = merge_state_array (dfa, sifted_states, lim_states,
+                              match_last + 1);
+      re_free (lim_states);
+      lim_states = NULL;
+      if (BE (ret != REG_NOERROR, 0))
+       goto free_return;
+    }
+  else
+    {
+      sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last);
+      ret = sift_states_backward (mctx, &sctx);
+      re_node_set_free (&sctx.limits);
+      if (BE (ret != REG_NOERROR, 0))
+       goto free_return;
+      if (sifted_states[0] == NULL)
+       {
+         ret = REG_NOMATCH;
+         goto free_return;
+       }
+    }
+  re_free (mctx->state_log);
+  mctx->state_log = sifted_states;
+  sifted_states = NULL;
+  mctx->last_node = halt_node;
+  mctx->match_last = match_last;
+  ret = REG_NOERROR;
+ free_return:
+  re_free (sifted_states);
+  re_free (lim_states);
+  return ret;
+}
+
+/* Acquire an initial state and return it.
+   We must select appropriate initial state depending on the context,
+   since initial states may have constraints like "\<", "^", etc..  */
+
+static inline re_dfastate_t *
+__attribute ((always_inline)) internal_function
+acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx,
+                           int idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  if (dfa->init_state->has_constraint)
+    {
+      unsigned int context;
+      context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags);
+      if (IS_WORD_CONTEXT (context))
+       return dfa->init_state_word;
+      else if (IS_ORDINARY_CONTEXT (context))
+       return dfa->init_state;
+      else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context))
+       return dfa->init_state_begbuf;
+      else if (IS_NEWLINE_CONTEXT (context))
+       return dfa->init_state_nl;
+      else if (IS_BEGBUF_CONTEXT (context))
+       {
+         /* It is relatively rare case, then calculate on demand.  */
+         return re_acquire_state_context (err, dfa,
+                                          dfa->init_state->entrance_nodes,
+                                          context);
+       }
+      else
+       /* Must not happen?  */
+       return dfa->init_state;
+    }
+  else
+    return dfa->init_state;
+}
+
+/* Check whether the regular expression match input string INPUT or not,
+   and return the index where the matching end, return -1 if not match,
+   or return -2 in case of an error.
+   FL_LONGEST_MATCH means we want the POSIX longest matching.
+   If P_MATCH_FIRST is not NULL, and the match fails, it is set to the
+   next place where we may want to try matching.
+   Note that the matcher assume that the maching starts from the current
+   index of the buffer.  */
+
+static int
+internal_function
+check_matching (re_match_context_t *mctx, int fl_longest_match,
+               int *p_match_first)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int match = 0;
+  int match_last = -1;
+  int cur_str_idx = re_string_cur_idx (&mctx->input);
+  re_dfastate_t *cur_state;
+  int at_init_state = p_match_first != NULL;
+  int next_start_idx = cur_str_idx;
+
+  err = REG_NOERROR;
+  cur_state = acquire_init_state_context (&err, mctx, cur_str_idx);
+  /* An initial state must not be NULL (invalid).  */
+  if (BE (cur_state == NULL, 0))
+    {
+      assert (err == REG_ESPACE);
+      return -2;
+    }
+
+  if (mctx->state_log != NULL)
+    {
+      mctx->state_log[cur_str_idx] = cur_state;
+
+      /* Check OP_OPEN_SUBEXP in the initial state in case that we use them
+        later.  E.g. Processing back references.  */
+      if (BE (dfa->nbackref, 0))
+       {
+         at_init_state = 0;
+         err = check_subexp_matching_top (mctx, &cur_state->nodes, 0);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+
+         if (cur_state->has_backref)
+           {
+             err = transit_state_bkref (mctx, &cur_state->nodes);
+             if (BE (err != REG_NOERROR, 0))
+               return err;
+           }
+       }
+    }
+
+  /* If the RE accepts NULL string.  */
+  if (BE (cur_state->halt, 0))
+    {
+      if (!cur_state->has_constraint
+         || check_halt_state_context (mctx, cur_state, cur_str_idx))
+       {
+         if (!fl_longest_match)
+           return cur_str_idx;
+         else
+           {
+             match_last = cur_str_idx;
+             match = 1;
+           }
+       }
+    }
+
+  while (!re_string_eoi (&mctx->input))
+    {
+      re_dfastate_t *old_state = cur_state;
+      int next_char_idx = re_string_cur_idx (&mctx->input) + 1;
+
+      if (BE (next_char_idx >= mctx->input.bufs_len, 0)
+         || (BE (next_char_idx >= mctx->input.valid_len, 0)
+             && mctx->input.valid_len < mctx->input.len))
+       {
+         err = extend_buffers (mctx);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             assert (err == REG_ESPACE);
+             return -2;
+           }
+       }
+
+      cur_state = transit_state (&err, mctx, cur_state);
+      if (mctx->state_log != NULL)
+       cur_state = merge_state_with_log (&err, mctx, cur_state);
+
+      if (cur_state == NULL)
+       {
+         /* Reached the invalid state or an error.  Try to recover a valid
+            state using the state log, if available and if we have not
+            already found a valid (even if not the longest) match.  */
+         if (BE (err != REG_NOERROR, 0))
+           return -2;
+
+         if (mctx->state_log == NULL
+             || (match && !fl_longest_match)
+             || (cur_state = find_recover_state (&err, mctx)) == NULL)
+           break;
+       }
+
+      if (BE (at_init_state, 0))
+       {
+         if (old_state == cur_state)
+           next_start_idx = next_char_idx;
+         else
+           at_init_state = 0;
+       }
+
+      if (cur_state->halt)
+       {
+         /* Reached a halt state.
+            Check the halt state can satisfy the current context.  */
+         if (!cur_state->has_constraint
+             || check_halt_state_context (mctx, cur_state,
+                                          re_string_cur_idx (&mctx->input)))
+           {
+             /* We found an appropriate halt state.  */
+             match_last = re_string_cur_idx (&mctx->input);
+             match = 1;
+
+             /* We found a match, do not modify match_first below.  */
+             p_match_first = NULL;
+             if (!fl_longest_match)
+               break;
+           }
+       }
+    }
+
+  if (p_match_first)
+    *p_match_first += next_start_idx;
+
+  return match_last;
+}
+
+/* Check NODE match the current context.  */
+
+static int
+internal_function
+check_halt_node_context (const re_dfa_t *dfa, int node, unsigned int context)
+{
+  re_token_type_t type = dfa->nodes[node].type;
+  unsigned int constraint = dfa->nodes[node].constraint;
+  if (type != END_OF_RE)
+    return 0;
+  if (!constraint)
+    return 1;
+  if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context))
+    return 0;
+  return 1;
+}
+
+/* Check the halt state STATE match the current context.
+   Return 0 if not match, if the node, STATE has, is a halt node and
+   match the context, return the node.  */
+
+static int
+internal_function
+check_halt_state_context (const re_match_context_t *mctx,
+                         const re_dfastate_t *state, int idx)
+{
+  int i;
+  unsigned int context;
+#ifdef DEBUG
+  assert (state->halt);
+#endif
+  context = re_string_context_at (&mctx->input, idx, mctx->eflags);
+  for (i = 0; i < state->nodes.nelem; ++i)
+    if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context))
+      return state->nodes.elems[i];
+  return 0;
+}
+
+/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA
+   corresponding to the DFA).
+   Return the destination node, and update EPS_VIA_NODES, return -1 in case
+   of errors.  */
+
+static int
+internal_function
+proceed_next_node (const re_match_context_t *mctx, int nregs, regmatch_t *regs,
+                  int *pidx, int node, re_node_set *eps_via_nodes,
+                  struct re_fail_stack_t *fs)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int i, err;
+  if (IS_EPSILON_NODE (dfa->nodes[node].type))
+    {
+      re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes;
+      re_node_set *edests = &dfa->edests[node];
+      int dest_node;
+      err = re_node_set_insert (eps_via_nodes, node);
+      if (BE (err < 0, 0))
+       return -2;
+      /* Pick up a valid destination, or return -1 if none is found.  */
+      for (dest_node = -1, i = 0; i < edests->nelem; ++i)
+       {
+         int candidate = edests->elems[i];
+         if (!re_node_set_contains (cur_nodes, candidate))
+           continue;
+         if (dest_node == -1)
+           dest_node = candidate;
+
+         else
+           {
+             /* In order to avoid infinite loop like "(a*)*", return the second
+                epsilon-transition if the first was already considered.  */
+             if (re_node_set_contains (eps_via_nodes, dest_node))
+               return candidate;
+
+             /* Otherwise, push the second epsilon-transition on the fail stack.  */
+             else if (fs != NULL
+                      && push_fail_stack (fs, *pidx, candidate, nregs, regs,
+                                          eps_via_nodes))
+               return -2;
+
+             /* We know we are going to exit.  */
+             break;
+           }
+       }
+      return dest_node;
+    }
+  else
+    {
+      int naccepted = 0;
+      re_token_type_t type = dfa->nodes[node].type;
+
+#ifdef RE_ENABLE_I18N
+      if (dfa->nodes[node].accept_mb)
+       naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx);
+      else
+#endif /* RE_ENABLE_I18N */
+      if (type == OP_BACK_REF)
+       {
+         int subexp_idx = dfa->nodes[node].opr.idx + 1;
+         naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
+         if (fs != NULL)
+           {
+             if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1)
+               return -1;
+             else if (naccepted)
+               {
+                 char *buf = (char *) re_string_get_buffer (&mctx->input);
+                 if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
+                             naccepted) != 0)
+                   return -1;
+               }
+           }
+
+         if (naccepted == 0)
+           {
+             int dest_node;
+             err = re_node_set_insert (eps_via_nodes, node);
+             if (BE (err < 0, 0))
+               return -2;
+             dest_node = dfa->edests[node].elems[0];
+             if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+                                       dest_node))
+               return dest_node;
+           }
+       }
+
+      if (naccepted != 0
+         || check_node_accept (mctx, dfa->nodes + node, *pidx))
+       {
+         int dest_node = dfa->nexts[node];
+         *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
+         if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL
+                    || !re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+                                              dest_node)))
+           return -1;
+         re_node_set_empty (eps_via_nodes);
+         return dest_node;
+       }
+    }
+  return -1;
+}
+
+static reg_errcode_t
+internal_function
+push_fail_stack (struct re_fail_stack_t *fs, int str_idx, int dest_node,
+                int nregs, regmatch_t *regs, re_node_set *eps_via_nodes)
+{
+  reg_errcode_t err;
+  int num = fs->num++;
+  if (fs->num == fs->alloc)
+    {
+      struct re_fail_stack_ent_t *new_array;
+      new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t)
+                                      * fs->alloc * 2));
+      if (new_array == NULL)
+       return REG_ESPACE;
+      fs->alloc *= 2;
+      fs->stack = new_array;
+    }
+  fs->stack[num].idx = str_idx;
+  fs->stack[num].node = dest_node;
+  fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+  if (fs->stack[num].regs == NULL)
+    return REG_ESPACE;
+  memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
+  err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes);
+  return err;
+}
+
+static int
+internal_function
+pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs,
+               regmatch_t *regs, re_node_set *eps_via_nodes)
+{
+  int num = --fs->num;
+  assert (num >= 0);
+  *pidx = fs->stack[num].idx;
+  memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs);
+  re_node_set_free (eps_via_nodes);
+  re_free (fs->stack[num].regs);
+  *eps_via_nodes = fs->stack[num].eps_via_nodes;
+  return fs->stack[num].node;
+}
+
+/* Set the positions where the subexpressions are starts/ends to registers
+   PMATCH.
+   Note: We assume that pmatch[0] is already set, and
+   pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch.  */
+
+static reg_errcode_t
+internal_function
+set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch,
+         regmatch_t *pmatch, int fl_backtrack)
+{
+  const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer;
+  int idx, cur_node;
+  re_node_set eps_via_nodes;
+  struct re_fail_stack_t *fs;
+  struct re_fail_stack_t fs_body = { 0, 2, NULL };
+  regmatch_t *prev_idx_match;
+  int prev_idx_match_malloced = 0;
+
+#ifdef DEBUG
+  assert (nmatch > 1);
+  assert (mctx->state_log != NULL);
+#endif
+  if (fl_backtrack)
+    {
+      fs = &fs_body;
+      fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+      if (fs->stack == NULL)
+       return REG_ESPACE;
+    }
+  else
+    fs = NULL;
+
+  cur_node = dfa->init_node;
+  re_node_set_init_empty (&eps_via_nodes);
+
+#ifdef HAVE_ALLOCA
+  if (__libc_use_alloca (nmatch * sizeof (regmatch_t)))
+    prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t));
+  else
+#endif
+    {
+      prev_idx_match = re_malloc (regmatch_t, nmatch);
+      if (prev_idx_match == NULL)
+       {
+         free_fail_stack_return (fs);
+         return REG_ESPACE;
+       }
+      prev_idx_match_malloced = 1;
+    }
+  memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+
+  for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+    {
+      update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch);
+
+      if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+       {
+         int reg_idx;
+         if (fs)
+           {
+             for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+               if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1)
+                 break;
+             if (reg_idx == nmatch)
+               {
+                 re_node_set_free (&eps_via_nodes);
+                 if (prev_idx_match_malloced)
+                   re_free (prev_idx_match);
+                 return free_fail_stack_return (fs);
+               }
+             cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+                                        &eps_via_nodes);
+           }
+         else
+           {
+             re_node_set_free (&eps_via_nodes);
+             if (prev_idx_match_malloced)
+               re_free (prev_idx_match);
+             return REG_NOERROR;
+           }
+       }
+
+      /* Proceed to next node.  */
+      cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node,
+                                   &eps_via_nodes, fs);
+
+      if (BE (cur_node < 0, 0))
+       {
+         if (BE (cur_node == -2, 0))
+           {
+             re_node_set_free (&eps_via_nodes);
+             if (prev_idx_match_malloced)
+               re_free (prev_idx_match);
+             free_fail_stack_return (fs);
+             return REG_ESPACE;
+           }
+         if (fs)
+           cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+                                      &eps_via_nodes);
+         else
+           {
+             re_node_set_free (&eps_via_nodes);
+             if (prev_idx_match_malloced)
+               re_free (prev_idx_match);
+             return REG_NOMATCH;
+           }
+       }
+    }
+  re_node_set_free (&eps_via_nodes);
+  if (prev_idx_match_malloced)
+    re_free (prev_idx_match);
+  return free_fail_stack_return (fs);
+}
+
+static reg_errcode_t
+internal_function
+free_fail_stack_return (struct re_fail_stack_t *fs)
+{
+  if (fs)
+    {
+      int fs_idx;
+      for (fs_idx = 0; fs_idx < fs->num; ++fs_idx)
+       {
+         re_node_set_free (&fs->stack[fs_idx].eps_via_nodes);
+         re_free (fs->stack[fs_idx].regs);
+       }
+      re_free (fs->stack);
+    }
+  return REG_NOERROR;
+}
+
+static void
+internal_function
+update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+            regmatch_t *prev_idx_match, int cur_node, int cur_idx, int nmatch)
+{
+  int type = dfa->nodes[cur_node].type;
+  if (type == OP_OPEN_SUBEXP)
+    {
+      int reg_num = dfa->nodes[cur_node].opr.idx + 1;
+
+      /* We are at the first node of this sub expression.  */
+      if (reg_num < nmatch)
+       {
+         pmatch[reg_num].rm_so = cur_idx;
+         pmatch[reg_num].rm_eo = -1;
+       }
+    }
+  else if (type == OP_CLOSE_SUBEXP)
+    {
+      int reg_num = dfa->nodes[cur_node].opr.idx + 1;
+      if (reg_num < nmatch)
+       {
+         /* We are at the last node of this sub expression.  */
+         if (pmatch[reg_num].rm_so < cur_idx)
+           {
+             pmatch[reg_num].rm_eo = cur_idx;
+             /* This is a non-empty match or we are not inside an optional
+                subexpression.  Accept this right away.  */
+             memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+           }
+         else
+           {
+             if (dfa->nodes[cur_node].opt_subexp
+                 && prev_idx_match[reg_num].rm_so != -1)
+               /* We transited through an empty match for an optional
+                  subexpression, like (a?)*, and this is not the subexp's
+                  first match.  Copy back the old content of the registers
+                  so that matches of an inner subexpression are undone as
+                  well, like in ((a?))*.  */
+               memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch);
+             else
+               /* We completed a subexpression, but it may be part of
+                  an optional one, so do not update PREV_IDX_MATCH.  */
+               pmatch[reg_num].rm_eo = cur_idx;
+           }
+       }
+    }
+}
+
+/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0
+   and sift the nodes in each states according to the following rules.
+   Updated state_log will be wrote to STATE_LOG.
+
+   Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if...
+     1. When STR_IDX == MATCH_LAST(the last index in the state_log):
+       If `a' isn't the LAST_NODE and `a' can't epsilon transit to
+       the LAST_NODE, we throw away the node `a'.
+     2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts
+       string `s' and transit to `b':
+       i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw
+          away the node `a'.
+       ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is
+           thrown away, we throw away the node `a'.
+     3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b':
+       i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the
+          node `a'.
+       ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away,
+           we throw away the node `a'.  */
+
+#define STATE_NODE_CONTAINS(state,node) \
+  ((state) != NULL && re_node_set_contains (&(state)->nodes, node))
+
+static reg_errcode_t
+internal_function
+sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx)
+{
+  reg_errcode_t err;
+  int null_cnt = 0;
+  int str_idx = sctx->last_str_idx;
+  re_node_set cur_dest;
+
+#ifdef DEBUG
+  assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL);
+#endif
+
+  /* Build sifted state_log[str_idx].  It has the nodes which can epsilon
+     transit to the last_node and the last_node itself.  */
+  err = re_node_set_init_1 (&cur_dest, sctx->last_node);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+
+  /* Then check each states in the state_log.  */
+  while (str_idx > 0)
+    {
+      /* Update counters.  */
+      null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0;
+      if (null_cnt > mctx->max_mb_elem_len)
+       {
+         memset (sctx->sifted_states, '\0',
+                 sizeof (re_dfastate_t *) * str_idx);
+         re_node_set_free (&cur_dest);
+         return REG_NOERROR;
+       }
+      re_node_set_empty (&cur_dest);
+      --str_idx;
+
+      if (mctx->state_log[str_idx])
+       {
+         err = build_sifted_states (mctx, sctx, str_idx, &cur_dest);
+         if (BE (err != REG_NOERROR, 0))
+           goto free_return;
+       }
+
+      /* Add all the nodes which satisfy the following conditions:
+        - It can epsilon transit to a node in CUR_DEST.
+        - It is in CUR_SRC.
+        And update state_log.  */
+      err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+      if (BE (err != REG_NOERROR, 0))
+       goto free_return;
+    }
+  err = REG_NOERROR;
+ free_return:
+  re_node_set_free (&cur_dest);
+  return err;
+}
+
+static reg_errcode_t
+internal_function
+build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx,
+                    int str_idx, re_node_set *cur_dest)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes;
+  int i;
+
+  /* Then build the next sifted state.
+     We build the next sifted state on `cur_dest', and update
+     `sifted_states[str_idx]' with `cur_dest'.
+     Note:
+     `cur_dest' is the sifted state from `state_log[str_idx + 1]'.
+     `cur_src' points the node_set of the old `state_log[str_idx]'
+     (with the epsilon nodes pre-filtered out).  */
+  for (i = 0; i < cur_src->nelem; i++)
+    {
+      int prev_node = cur_src->elems[i];
+      int naccepted = 0;
+      int ret;
+
+#ifdef DEBUG
+      re_token_type_t type = dfa->nodes[prev_node].type;
+      assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+      /* If the node may accept `multi byte'.  */
+      if (dfa->nodes[prev_node].accept_mb)
+       naccepted = sift_states_iter_mb (mctx, sctx, prev_node,
+                                        str_idx, sctx->last_str_idx);
+#endif /* RE_ENABLE_I18N */
+
+      /* We don't check backreferences here.
+        See update_cur_sifted_state().  */
+      if (!naccepted
+         && check_node_accept (mctx, dfa->nodes + prev_node, str_idx)
+         && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1],
+                                 dfa->nexts[prev_node]))
+       naccepted = 1;
+
+      if (naccepted == 0)
+       continue;
+
+      if (sctx->limits.nelem)
+       {
+         int to_idx = str_idx + naccepted;
+         if (check_dst_limits (mctx, &sctx->limits,
+                               dfa->nexts[prev_node], to_idx,
+                               prev_node, str_idx))
+           continue;
+       }
+      ret = re_node_set_insert (cur_dest, prev_node);
+      if (BE (ret == -1, 0))
+       return REG_ESPACE;
+    }
+
+  return REG_NOERROR;
+}
+
+/* Helper functions.  */
+
+static reg_errcode_t
+internal_function
+clean_state_log_if_needed (re_match_context_t *mctx, int next_state_log_idx)
+{
+  int top = mctx->state_log_top;
+
+  if (next_state_log_idx >= mctx->input.bufs_len
+      || (next_state_log_idx >= mctx->input.valid_len
+         && mctx->input.valid_len < mctx->input.len))
+    {
+      reg_errcode_t err;
+      err = extend_buffers (mctx);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+
+  if (top < next_state_log_idx)
+    {
+      memset (mctx->state_log + top + 1, '\0',
+             sizeof (re_dfastate_t *) * (next_state_log_idx - top));
+      mctx->state_log_top = next_state_log_idx;
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst,
+                  re_dfastate_t **src, int num)
+{
+  int st_idx;
+  reg_errcode_t err;
+  for (st_idx = 0; st_idx < num; ++st_idx)
+    {
+      if (dst[st_idx] == NULL)
+       dst[st_idx] = src[st_idx];
+      else if (src[st_idx] != NULL)
+       {
+         re_node_set merged_set;
+         err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes,
+                                       &src[st_idx]->nodes);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+         dst[st_idx] = re_acquire_state (&err, dfa, &merged_set);
+         re_node_set_free (&merged_set);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+update_cur_sifted_state (const re_match_context_t *mctx,
+                        re_sift_context_t *sctx, int str_idx,
+                        re_node_set *dest_nodes)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err = REG_NOERROR;
+  const re_node_set *candidates;
+  candidates = ((mctx->state_log[str_idx] == NULL) ? NULL
+               : &mctx->state_log[str_idx]->nodes);
+
+  if (dest_nodes->nelem == 0)
+    sctx->sifted_states[str_idx] = NULL;
+  else
+    {
+      if (candidates)
+       {
+         /* At first, add the nodes which can epsilon transit to a node in
+            DEST_NODE.  */
+         err = add_epsilon_src_nodes (dfa, dest_nodes, candidates);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+
+         /* Then, check the limitations in the current sift_context.  */
+         if (sctx->limits.nelem)
+           {
+             err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits,
+                                        mctx->bkref_ents, str_idx);
+             if (BE (err != REG_NOERROR, 0))
+               return err;
+           }
+       }
+
+      sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+
+  if (candidates && mctx->state_log[str_idx]->has_backref)
+    {
+      err = sift_states_bkref (mctx, sctx, str_idx, candidates);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes,
+                      const re_node_set *candidates)
+{
+  reg_errcode_t err = REG_NOERROR;
+  int i;
+
+  re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  if (!state->inveclosure.alloc)
+    {
+      err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem);
+      if (BE (err != REG_NOERROR, 0))
+       return REG_ESPACE;
+      for (i = 0; i < dest_nodes->nelem; i++)
+       {
+         err = re_node_set_merge (&state->inveclosure,
+                                  dfa->inveclosures + dest_nodes->elems[i]);
+         if (BE (err != REG_NOERROR, 0))
+           return REG_ESPACE;
+       }
+    }
+  return re_node_set_add_intersect (dest_nodes, candidates,
+                                   &state->inveclosure);
+}
+
+static reg_errcode_t
+internal_function
+sub_epsilon_src_nodes (const re_dfa_t *dfa, int node, re_node_set *dest_nodes,
+                      const re_node_set *candidates)
+{
+    int ecl_idx;
+    reg_errcode_t err;
+    re_node_set *inv_eclosure = dfa->inveclosures + node;
+    re_node_set except_nodes;
+    re_node_set_init_empty (&except_nodes);
+    for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+      {
+       int cur_node = inv_eclosure->elems[ecl_idx];
+       if (cur_node == node)
+         continue;
+       if (IS_EPSILON_NODE (dfa->nodes[cur_node].type))
+         {
+           int edst1 = dfa->edests[cur_node].elems[0];
+           int edst2 = ((dfa->edests[cur_node].nelem > 1)
+                        ? dfa->edests[cur_node].elems[1] : -1);
+           if ((!re_node_set_contains (inv_eclosure, edst1)
+                && re_node_set_contains (dest_nodes, edst1))
+               || (edst2 > 0
+                   && !re_node_set_contains (inv_eclosure, edst2)
+                   && re_node_set_contains (dest_nodes, edst2)))
+             {
+               err = re_node_set_add_intersect (&except_nodes, candidates,
+                                                dfa->inveclosures + cur_node);
+               if (BE (err != REG_NOERROR, 0))
+                 {
+                   re_node_set_free (&except_nodes);
+                   return err;
+                 }
+             }
+         }
+      }
+    for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+      {
+       int cur_node = inv_eclosure->elems[ecl_idx];
+       if (!re_node_set_contains (&except_nodes, cur_node))
+         {
+           int idx = re_node_set_contains (dest_nodes, cur_node) - 1;
+           re_node_set_remove_at (dest_nodes, idx);
+         }
+      }
+    re_node_set_free (&except_nodes);
+    return REG_NOERROR;
+}
+
+static int
+internal_function
+check_dst_limits (const re_match_context_t *mctx, re_node_set *limits,
+                 int dst_node, int dst_idx, int src_node, int src_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int lim_idx, src_pos, dst_pos;
+
+  int dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx);
+  int src_bkref_idx = search_cur_bkref_entry (mctx, src_idx);
+  for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+    {
+      int subexp_idx;
+      struct re_backref_cache_entry *ent;
+      ent = mctx->bkref_ents + limits->elems[lim_idx];
+      subexp_idx = dfa->nodes[ent->node].opr.idx;
+
+      dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+                                          subexp_idx, dst_node, dst_idx,
+                                          dst_bkref_idx);
+      src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+                                          subexp_idx, src_node, src_idx,
+                                          src_bkref_idx);
+
+      /* In case of:
+        <src> <dst> ( <subexp> )
+        ( <subexp> ) <src> <dst>
+        ( <subexp1> <src> <subexp2> <dst> <subexp3> )  */
+      if (src_pos == dst_pos)
+       continue; /* This is unrelated limitation.  */
+      else
+       return 1;
+    }
+  return 0;
+}
+
+static int
+internal_function
+check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries,
+                            int subexp_idx, int from_node, int bkref_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  const re_node_set *eclosures = dfa->eclosures + from_node;
+  int node_idx;
+
+  /* Else, we are on the boundary: examine the nodes on the epsilon
+     closure.  */
+  for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
+    {
+      int node = eclosures->elems[node_idx];
+      switch (dfa->nodes[node].type)
+       {
+       case OP_BACK_REF:
+         if (bkref_idx != -1)
+           {
+             struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx;
+             do
+               {
+                 int dst, cpos;
+
+                 if (ent->node != node)
+                   continue;
+
+                 if (subexp_idx < BITSET_WORD_BITS
+                     && !(ent->eps_reachable_subexps_map
+                          & ((bitset_word_t) 1 << subexp_idx)))
+                   continue;
+
+                 /* Recurse trying to reach the OP_OPEN_SUBEXP and
+                    OP_CLOSE_SUBEXP cases below.  But, if the
+                    destination node is the same node as the source
+                    node, don't recurse because it would cause an
+                    infinite loop: a regex that exhibits this behavior
+                    is ()\1*\1*  */
+                 dst = dfa->edests[node].elems[0];
+                 if (dst == from_node)
+                   {
+                     if (boundaries & 1)
+                       return -1;
+                     else /* if (boundaries & 2) */
+                       return 0;
+                   }
+
+                 cpos =
+                   check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+                                                dst, bkref_idx);
+                 if (cpos == -1 /* && (boundaries & 1) */)
+                   return -1;
+                 if (cpos == 0 && (boundaries & 2))
+                   return 0;
+
+                 if (subexp_idx < BITSET_WORD_BITS)
+                   ent->eps_reachable_subexps_map
+                     &= ~((bitset_word_t) 1 << subexp_idx);
+               }
+             while (ent++->more);
+           }
+         break;
+
+       case OP_OPEN_SUBEXP:
+         if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx)
+           return -1;
+         break;
+
+       case OP_CLOSE_SUBEXP:
+         if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx)
+           return 0;
+         break;
+
+       default:
+           break;
+       }
+    }
+
+  return (boundaries & 2) ? 1 : 0;
+}
+
+static int
+internal_function
+check_dst_limits_calc_pos (const re_match_context_t *mctx, int limit,
+                          int subexp_idx, int from_node, int str_idx,
+                          int bkref_idx)
+{
+  struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
+  int boundaries;
+
+  /* If we are outside the range of the subexpression, return -1 or 1.  */
+  if (str_idx < lim->subexp_from)
+    return -1;
+
+  if (lim->subexp_to < str_idx)
+    return 1;
+
+  /* If we are within the subexpression, return 0.  */
+  boundaries = (str_idx == lim->subexp_from);
+  boundaries |= (str_idx == lim->subexp_to) << 1;
+  if (boundaries == 0)
+    return 0;
+
+  /* Else, examine epsilon closure.  */
+  return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+                                     from_node, bkref_idx);
+}
+
+/* Check the limitations of sub expressions LIMITS, and remove the nodes
+   which are against limitations from DEST_NODES. */
+
+static reg_errcode_t
+internal_function
+check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes,
+                    const re_node_set *candidates, re_node_set *limits,
+                    struct re_backref_cache_entry *bkref_ents, int str_idx)
+{
+  reg_errcode_t err;
+  int node_idx, lim_idx;
+
+  for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+    {
+      int subexp_idx;
+      struct re_backref_cache_entry *ent;
+      ent = bkref_ents + limits->elems[lim_idx];
+
+      if (str_idx <= ent->subexp_from || ent->str_idx < str_idx)
+       continue; /* This is unrelated limitation.  */
+
+      subexp_idx = dfa->nodes[ent->node].opr.idx;
+      if (ent->subexp_to == str_idx)
+       {
+         int ops_node = -1;
+         int cls_node = -1;
+         for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+           {
+             int node = dest_nodes->elems[node_idx];
+             re_token_type_t type = dfa->nodes[node].type;
+             if (type == OP_OPEN_SUBEXP
+                 && subexp_idx == dfa->nodes[node].opr.idx)
+               ops_node = node;
+             else if (type == OP_CLOSE_SUBEXP
+                      && subexp_idx == dfa->nodes[node].opr.idx)
+               cls_node = node;
+           }
+
+         /* Check the limitation of the open subexpression.  */
+         /* Note that (ent->subexp_to = str_idx != ent->subexp_from).  */
+         if (ops_node >= 0)
+           {
+             err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes,
+                                          candidates);
+             if (BE (err != REG_NOERROR, 0))
+               return err;
+           }
+
+         /* Check the limitation of the close subexpression.  */
+         if (cls_node >= 0)
+           for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+             {
+               int node = dest_nodes->elems[node_idx];
+               if (!re_node_set_contains (dfa->inveclosures + node,
+                                          cls_node)
+                   && !re_node_set_contains (dfa->eclosures + node,
+                                             cls_node))
+                 {
+                   /* It is against this limitation.
+                      Remove it form the current sifted state.  */
+                   err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+                                                candidates);
+                   if (BE (err != REG_NOERROR, 0))
+                     return err;
+                   --node_idx;
+                 }
+             }
+       }
+      else /* (ent->subexp_to != str_idx)  */
+       {
+         for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+           {
+             int node = dest_nodes->elems[node_idx];
+             re_token_type_t type = dfa->nodes[node].type;
+             if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP)
+               {
+                 if (subexp_idx != dfa->nodes[node].opr.idx)
+                   continue;
+                 /* It is against this limitation.
+                    Remove it form the current sifted state.  */
+                 err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+                                              candidates);
+                 if (BE (err != REG_NOERROR, 0))
+                   return err;
+               }
+           }
+       }
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx,
+                  int str_idx, const re_node_set *candidates)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int node_idx, node;
+  re_sift_context_t local_sctx;
+  int first_idx = search_cur_bkref_entry (mctx, str_idx);
+
+  if (first_idx == -1)
+    return REG_NOERROR;
+
+  local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized.  */
+
+  for (node_idx = 0; node_idx < candidates->nelem; ++node_idx)
+    {
+      int enabled_idx;
+      re_token_type_t type;
+      struct re_backref_cache_entry *entry;
+      node = candidates->elems[node_idx];
+      type = dfa->nodes[node].type;
+      /* Avoid infinite loop for the REs like "()\1+".  */
+      if (node == sctx->last_node && str_idx == sctx->last_str_idx)
+       continue;
+      if (type != OP_BACK_REF)
+       continue;
+
+      entry = mctx->bkref_ents + first_idx;
+      enabled_idx = first_idx;
+      do
+       {
+         int subexp_len;
+         int to_idx;
+         int dst_node;
+         int ret;
+         re_dfastate_t *cur_state;
+
+         if (entry->node != node)
+           continue;
+         subexp_len = entry->subexp_to - entry->subexp_from;
+         to_idx = str_idx + subexp_len;
+         dst_node = (subexp_len ? dfa->nexts[node]
+                     : dfa->edests[node].elems[0]);
+
+         if (to_idx > sctx->last_str_idx
+             || sctx->sifted_states[to_idx] == NULL
+             || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node)
+             || check_dst_limits (mctx, &sctx->limits, node,
+                                  str_idx, dst_node, to_idx))
+           continue;
+
+         if (local_sctx.sifted_states == NULL)
+           {
+             local_sctx = *sctx;
+             err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits);
+             if (BE (err != REG_NOERROR, 0))
+               goto free_return;
+           }
+         local_sctx.last_node = node;
+         local_sctx.last_str_idx = str_idx;
+         ret = re_node_set_insert (&local_sctx.limits, enabled_idx);
+         if (BE (ret < 0, 0))
+           {
+             err = REG_ESPACE;
+             goto free_return;
+           }
+         cur_state = local_sctx.sifted_states[str_idx];
+         err = sift_states_backward (mctx, &local_sctx);
+         if (BE (err != REG_NOERROR, 0))
+           goto free_return;
+         if (sctx->limited_states != NULL)
+           {
+             err = merge_state_array (dfa, sctx->limited_states,
+                                      local_sctx.sifted_states,
+                                      str_idx + 1);
+             if (BE (err != REG_NOERROR, 0))
+               goto free_return;
+           }
+         local_sctx.sifted_states[str_idx] = cur_state;
+         re_node_set_remove (&local_sctx.limits, enabled_idx);
+
+         /* mctx->bkref_ents may have changed, reload the pointer.  */
+         entry = mctx->bkref_ents + enabled_idx;
+       }
+      while (enabled_idx++, entry++->more);
+    }
+  err = REG_NOERROR;
+ free_return:
+  if (local_sctx.sifted_states != NULL)
+    {
+      re_node_set_free (&local_sctx.limits);
+    }
+
+  return err;
+}
+
+
+#ifdef RE_ENABLE_I18N
+static int
+internal_function
+sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx,
+                    int node_idx, int str_idx, int max_str_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int naccepted;
+  /* Check the node can accept `multi byte'.  */
+  naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx);
+  if (naccepted > 0 && str_idx + naccepted <= max_str_idx &&
+      !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted],
+                           dfa->nexts[node_idx]))
+    /* The node can't accept the `multi byte', or the
+       destination was already thrown away, then the node
+       could't accept the current input `multi byte'.   */
+    naccepted = 0;
+  /* Otherwise, it is sure that the node could accept
+     `naccepted' bytes input.  */
+  return naccepted;
+}
+#endif /* RE_ENABLE_I18N */
+
+\f
+/* Functions for state transition.  */
+
+/* Return the next state to which the current state STATE will transit by
+   accepting the current input byte, and update STATE_LOG if necessary.
+   If STATE can accept a multibyte char/collating element/back reference
+   update the destination of STATE_LOG.  */
+
+static re_dfastate_t *
+internal_function
+transit_state (reg_errcode_t *err, re_match_context_t *mctx,
+              re_dfastate_t *state)
+{
+  re_dfastate_t **trtable;
+  unsigned char ch;
+
+#ifdef RE_ENABLE_I18N
+  /* If the current state can accept multibyte.  */
+  if (BE (state->accept_mb, 0))
+    {
+      *err = transit_state_mb (mctx, state);
+      if (BE (*err != REG_NOERROR, 0))
+       return NULL;
+    }
+#endif /* RE_ENABLE_I18N */
+
+  /* Then decide the next state with the single byte.  */
+#if 0
+  if (0)
+    /* don't use transition table  */
+    return transit_state_sb (err, mctx, state);
+#endif
+
+  /* Use transition table  */
+  ch = re_string_fetch_byte (&mctx->input);
+  for (;;)
+    {
+      trtable = state->trtable;
+      if (BE (trtable != NULL, 1))
+       return trtable[ch];
+
+      trtable = state->word_trtable;
+      if (BE (trtable != NULL, 1))
+       {
+         unsigned int context;
+         context
+           = re_string_context_at (&mctx->input,
+                                   re_string_cur_idx (&mctx->input) - 1,
+                                   mctx->eflags);
+         if (IS_WORD_CONTEXT (context))
+           return trtable[ch + SBC_MAX];
+         else
+           return trtable[ch];
+       }
+
+      if (!build_trtable (mctx->dfa, state))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+
+      /* Retry, we now have a transition table.  */
+    }
+}
+
+/* Update the state_log if we need */
+re_dfastate_t *
+internal_function
+merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx,
+                     re_dfastate_t *next_state)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int cur_idx = re_string_cur_idx (&mctx->input);
+
+  if (cur_idx > mctx->state_log_top)
+    {
+      mctx->state_log[cur_idx] = next_state;
+      mctx->state_log_top = cur_idx;
+    }
+  else if (mctx->state_log[cur_idx] == 0)
+    {
+      mctx->state_log[cur_idx] = next_state;
+    }
+  else
+    {
+      re_dfastate_t *pstate;
+      unsigned int context;
+      re_node_set next_nodes, *log_nodes, *table_nodes = NULL;
+      /* If (state_log[cur_idx] != 0), it implies that cur_idx is
+        the destination of a multibyte char/collating element/
+        back reference.  Then the next state is the union set of
+        these destinations and the results of the transition table.  */
+      pstate = mctx->state_log[cur_idx];
+      log_nodes = pstate->entrance_nodes;
+      if (next_state != NULL)
+       {
+         table_nodes = next_state->entrance_nodes;
+         *err = re_node_set_init_union (&next_nodes, table_nodes,
+                                            log_nodes);
+         if (BE (*err != REG_NOERROR, 0))
+           return NULL;
+       }
+      else
+       next_nodes = *log_nodes;
+      /* Note: We already add the nodes of the initial state,
+        then we don't need to add them here.  */
+
+      context = re_string_context_at (&mctx->input,
+                                     re_string_cur_idx (&mctx->input) - 1,
+                                     mctx->eflags);
+      next_state = mctx->state_log[cur_idx]
+       = re_acquire_state_context (err, dfa, &next_nodes, context);
+      /* We don't need to check errors here, since the return value of
+        this function is next_state and ERR is already set.  */
+
+      if (table_nodes != NULL)
+       re_node_set_free (&next_nodes);
+    }
+
+  if (BE (dfa->nbackref, 0) && next_state != NULL)
+    {
+      /* Check OP_OPEN_SUBEXP in the current state in case that we use them
+        later.  We must check them here, since the back references in the
+        next state might use them.  */
+      *err = check_subexp_matching_top (mctx, &next_state->nodes,
+                                       cur_idx);
+      if (BE (*err != REG_NOERROR, 0))
+       return NULL;
+
+      /* If the next state has back references.  */
+      if (next_state->has_backref)
+       {
+         *err = transit_state_bkref (mctx, &next_state->nodes);
+         if (BE (*err != REG_NOERROR, 0))
+           return NULL;
+         next_state = mctx->state_log[cur_idx];
+       }
+    }
+
+  return next_state;
+}
+
+/* Skip bytes in the input that correspond to part of a
+   multi-byte match, then look in the log for a state
+   from which to restart matching.  */
+re_dfastate_t *
+internal_function
+find_recover_state (reg_errcode_t *err, re_match_context_t *mctx)
+{
+  re_dfastate_t *cur_state;
+  do
+    {
+      int max = mctx->state_log_top;
+      int cur_str_idx = re_string_cur_idx (&mctx->input);
+
+      do
+       {
+         if (++cur_str_idx > max)
+           return NULL;
+         re_string_skip_bytes (&mctx->input, 1);
+       }
+      while (mctx->state_log[cur_str_idx] == NULL);
+
+      cur_state = merge_state_with_log (err, mctx, NULL);
+    }
+  while (*err == REG_NOERROR && cur_state == NULL);
+  return cur_state;
+}
+
+/* Helper functions for transit_state.  */
+
+/* From the node set CUR_NODES, pick up the nodes whose types are
+   OP_OPEN_SUBEXP and which have corresponding back references in the regular
+   expression. And register them to use them later for evaluating the
+   correspoding back references.  */
+
+static reg_errcode_t
+internal_function
+check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes,
+                          int str_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int node_idx;
+  reg_errcode_t err;
+
+  /* TODO: This isn't efficient.
+          Because there might be more than one nodes whose types are
+          OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+          nodes.
+          E.g. RE: (a){2}  */
+  for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx)
+    {
+      int node = cur_nodes->elems[node_idx];
+      if (dfa->nodes[node].type == OP_OPEN_SUBEXP
+         && dfa->nodes[node].opr.idx < BITSET_WORD_BITS
+         && (dfa->used_bkref_map
+             & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx)))
+       {
+         err = match_ctx_add_subtop (mctx, node, str_idx);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+    }
+  return REG_NOERROR;
+}
+
+#if 0
+/* Return the next state to which the current state STATE will transit by
+   accepting the current input byte.  */
+
+static re_dfastate_t *
+transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx,
+                 re_dfastate_t *state)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  re_node_set next_nodes;
+  re_dfastate_t *next_state;
+  int node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input);
+  unsigned int context;
+
+  *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1);
+  if (BE (*err != REG_NOERROR, 0))
+    return NULL;
+  for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt)
+    {
+      int cur_node = state->nodes.elems[node_cnt];
+      if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx))
+       {
+         *err = re_node_set_merge (&next_nodes,
+                                   dfa->eclosures + dfa->nexts[cur_node]);
+         if (BE (*err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return NULL;
+           }
+       }
+    }
+  context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags);
+  next_state = re_acquire_state_context (err, dfa, &next_nodes, context);
+  /* We don't need to check errors here, since the return value of
+     this function is next_state and ERR is already set.  */
+
+  re_node_set_free (&next_nodes);
+  re_string_skip_bytes (&mctx->input, 1);
+  return next_state;
+}
+#endif
+
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t
+internal_function
+transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int i;
+
+  for (i = 0; i < pstate->nodes.nelem; ++i)
+    {
+      re_node_set dest_nodes, *new_nodes;
+      int cur_node_idx = pstate->nodes.elems[i];
+      int naccepted, dest_idx;
+      unsigned int context;
+      re_dfastate_t *dest_state;
+
+      if (!dfa->nodes[cur_node_idx].accept_mb)
+       continue;
+
+      if (dfa->nodes[cur_node_idx].constraint)
+       {
+         context = re_string_context_at (&mctx->input,
+                                         re_string_cur_idx (&mctx->input),
+                                         mctx->eflags);
+         if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint,
+                                          context))
+           continue;
+       }
+
+      /* How many bytes the node can accept?  */
+      naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input,
+                                          re_string_cur_idx (&mctx->input));
+      if (naccepted == 0)
+       continue;
+
+      /* The node can accepts `naccepted' bytes.  */
+      dest_idx = re_string_cur_idx (&mctx->input) + naccepted;
+      mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted
+                              : mctx->max_mb_elem_len);
+      err = clean_state_log_if_needed (mctx, dest_idx);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+#ifdef DEBUG
+      assert (dfa->nexts[cur_node_idx] != -1);
+#endif
+      new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx];
+
+      dest_state = mctx->state_log[dest_idx];
+      if (dest_state == NULL)
+       dest_nodes = *new_nodes;
+      else
+       {
+         err = re_node_set_init_union (&dest_nodes,
+                                       dest_state->entrance_nodes, new_nodes);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+      context = re_string_context_at (&mctx->input, dest_idx - 1,
+                                     mctx->eflags);
+      mctx->state_log[dest_idx]
+       = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+      if (dest_state != NULL)
+       re_node_set_free (&dest_nodes);
+      if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0))
+       return err;
+    }
+  return REG_NOERROR;
+}
+#endif /* RE_ENABLE_I18N */
+
+static reg_errcode_t
+internal_function
+transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int i;
+  int cur_str_idx = re_string_cur_idx (&mctx->input);
+
+  for (i = 0; i < nodes->nelem; ++i)
+    {
+      int dest_str_idx, prev_nelem, bkc_idx;
+      int node_idx = nodes->elems[i];
+      unsigned int context;
+      const re_token_t *node = dfa->nodes + node_idx;
+      re_node_set *new_dest_nodes;
+
+      /* Check whether `node' is a backreference or not.  */
+      if (node->type != OP_BACK_REF)
+       continue;
+
+      if (node->constraint)
+       {
+         context = re_string_context_at (&mctx->input, cur_str_idx,
+                                         mctx->eflags);
+         if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+           continue;
+       }
+
+      /* `node' is a backreference.
+        Check the substring which the substring matched.  */
+      bkc_idx = mctx->nbkref_ents;
+      err = get_subexp (mctx, node_idx, cur_str_idx);
+      if (BE (err != REG_NOERROR, 0))
+       goto free_return;
+
+      /* And add the epsilon closures (which is `new_dest_nodes') of
+        the backreference to appropriate state_log.  */
+#ifdef DEBUG
+      assert (dfa->nexts[node_idx] != -1);
+#endif
+      for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx)
+       {
+         int subexp_len;
+         re_dfastate_t *dest_state;
+         struct re_backref_cache_entry *bkref_ent;
+         bkref_ent = mctx->bkref_ents + bkc_idx;
+         if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx)
+           continue;
+         subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from;
+         new_dest_nodes = (subexp_len == 0
+                           ? dfa->eclosures + dfa->edests[node_idx].elems[0]
+                           : dfa->eclosures + dfa->nexts[node_idx]);
+         dest_str_idx = (cur_str_idx + bkref_ent->subexp_to
+                         - bkref_ent->subexp_from);
+         context = re_string_context_at (&mctx->input, dest_str_idx - 1,
+                                         mctx->eflags);
+         dest_state = mctx->state_log[dest_str_idx];
+         prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0
+                       : mctx->state_log[cur_str_idx]->nodes.nelem);
+         /* Add `new_dest_node' to state_log.  */
+         if (dest_state == NULL)
+           {
+             mctx->state_log[dest_str_idx]
+               = re_acquire_state_context (&err, dfa, new_dest_nodes,
+                                           context);
+             if (BE (mctx->state_log[dest_str_idx] == NULL
+                     && err != REG_NOERROR, 0))
+               goto free_return;
+           }
+         else
+           {
+             re_node_set dest_nodes;
+             err = re_node_set_init_union (&dest_nodes,
+                                           dest_state->entrance_nodes,
+                                           new_dest_nodes);
+             if (BE (err != REG_NOERROR, 0))
+               {
+                 re_node_set_free (&dest_nodes);
+                 goto free_return;
+               }
+             mctx->state_log[dest_str_idx]
+               = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+             re_node_set_free (&dest_nodes);
+             if (BE (mctx->state_log[dest_str_idx] == NULL
+                     && err != REG_NOERROR, 0))
+               goto free_return;
+           }
+         /* We need to check recursively if the backreference can epsilon
+            transit.  */
+         if (subexp_len == 0
+             && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem)
+           {
+             err = check_subexp_matching_top (mctx, new_dest_nodes,
+                                              cur_str_idx);
+             if (BE (err != REG_NOERROR, 0))
+               goto free_return;
+             err = transit_state_bkref (mctx, new_dest_nodes);
+             if (BE (err != REG_NOERROR, 0))
+               goto free_return;
+           }
+       }
+    }
+  err = REG_NOERROR;
+ free_return:
+  return err;
+}
+
+/* Enumerate all the candidates which the backreference BKREF_NODE can match
+   at BKREF_STR_IDX, and register them by match_ctx_add_entry().
+   Note that we might collect inappropriate candidates here.
+   However, the cost of checking them strictly here is too high, then we
+   delay these checking for prune_impossible_nodes().  */
+
+static reg_errcode_t
+internal_function
+get_subexp (re_match_context_t *mctx, int bkref_node, int bkref_str_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int subexp_num, sub_top_idx;
+  const char *buf = (const char *) re_string_get_buffer (&mctx->input);
+  /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX.  */
+  int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx);
+  if (cache_idx != -1)
+    {
+      const struct re_backref_cache_entry *entry
+       = mctx->bkref_ents + cache_idx;
+      do
+       if (entry->node == bkref_node)
+         return REG_NOERROR; /* We already checked it.  */
+      while (entry++->more);
+    }
+
+  subexp_num = dfa->nodes[bkref_node].opr.idx;
+
+  /* For each sub expression  */
+  for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx)
+    {
+      reg_errcode_t err;
+      re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx];
+      re_sub_match_last_t *sub_last;
+      int sub_last_idx, sl_str, bkref_str_off;
+
+      if (dfa->nodes[sub_top->node].opr.idx != subexp_num)
+       continue; /* It isn't related.  */
+
+      sl_str = sub_top->str_idx;
+      bkref_str_off = bkref_str_idx;
+      /* At first, check the last node of sub expressions we already
+        evaluated.  */
+      for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx)
+       {
+         int sl_str_diff;
+         sub_last = sub_top->lasts[sub_last_idx];
+         sl_str_diff = sub_last->str_idx - sl_str;
+         /* The matched string by the sub expression match with the substring
+            at the back reference?  */
+         if (sl_str_diff > 0)
+           {
+             if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0))
+               {
+                 /* Not enough chars for a successful match.  */
+                 if (bkref_str_off + sl_str_diff > mctx->input.len)
+                   break;
+
+                 err = clean_state_log_if_needed (mctx,
+                                                  bkref_str_off
+                                                  + sl_str_diff);
+                 if (BE (err != REG_NOERROR, 0))
+                   return err;
+                 buf = (const char *) re_string_get_buffer (&mctx->input);
+               }
+             if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0)
+               /* We don't need to search this sub expression any more.  */
+               break;
+           }
+         bkref_str_off += sl_str_diff;
+         sl_str += sl_str_diff;
+         err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+                               bkref_str_idx);
+
+         /* Reload buf, since the preceding call might have reallocated
+            the buffer.  */
+         buf = (const char *) re_string_get_buffer (&mctx->input);
+
+         if (err == REG_NOMATCH)
+           continue;
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+
+      if (sub_last_idx < sub_top->nlasts)
+       continue;
+      if (sub_last_idx > 0)
+       ++sl_str;
+      /* Then, search for the other last nodes of the sub expression.  */
+      for (; sl_str <= bkref_str_idx; ++sl_str)
+       {
+         int cls_node, sl_str_off;
+         const re_node_set *nodes;
+         sl_str_off = sl_str - sub_top->str_idx;
+         /* The matched string by the sub expression match with the substring
+            at the back reference?  */
+         if (sl_str_off > 0)
+           {
+             if (BE (bkref_str_off >= mctx->input.valid_len, 0))
+               {
+                 /* If we are at the end of the input, we cannot match.  */
+                 if (bkref_str_off >= mctx->input.len)
+                   break;
+
+                 err = extend_buffers (mctx);
+                 if (BE (err != REG_NOERROR, 0))
+                   return err;
+
+                 buf = (const char *) re_string_get_buffer (&mctx->input);
+               }
+             if (buf [bkref_str_off++] != buf[sl_str - 1])
+               break; /* We don't need to search this sub expression
+                         any more.  */
+           }
+         if (mctx->state_log[sl_str] == NULL)
+           continue;
+         /* Does this state have a ')' of the sub expression?  */
+         nodes = &mctx->state_log[sl_str]->nodes;
+         cls_node = find_subexp_node (dfa, nodes, subexp_num,
+                                      OP_CLOSE_SUBEXP);
+         if (cls_node == -1)
+           continue; /* No.  */
+         if (sub_top->path == NULL)
+           {
+             sub_top->path = calloc (sizeof (state_array_t),
+                                     sl_str - sub_top->str_idx + 1);
+             if (sub_top->path == NULL)
+               return REG_ESPACE;
+           }
+         /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node
+            in the current context?  */
+         err = check_arrival (mctx, sub_top->path, sub_top->node,
+                              sub_top->str_idx, cls_node, sl_str,
+                              OP_CLOSE_SUBEXP);
+         if (err == REG_NOMATCH)
+             continue;
+         if (BE (err != REG_NOERROR, 0))
+             return err;
+         sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str);
+         if (BE (sub_last == NULL, 0))
+           return REG_ESPACE;
+         err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+                               bkref_str_idx);
+         if (err == REG_NOMATCH)
+           continue;
+       }
+    }
+  return REG_NOERROR;
+}
+
+/* Helper functions for get_subexp().  */
+
+/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR.
+   If it can arrive, register the sub expression expressed with SUB_TOP
+   and SUB_LAST.  */
+
+static reg_errcode_t
+internal_function
+get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top,
+               re_sub_match_last_t *sub_last, int bkref_node, int bkref_str)
+{
+  reg_errcode_t err;
+  int to_idx;
+  /* Can the subexpression arrive the back reference?  */
+  err = check_arrival (mctx, &sub_last->path, sub_last->node,
+                      sub_last->str_idx, bkref_node, bkref_str,
+                      OP_OPEN_SUBEXP);
+  if (err != REG_NOERROR)
+    return err;
+  err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx,
+                            sub_last->str_idx);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx;
+  return clean_state_log_if_needed (mctx, to_idx);
+}
+
+/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX.
+   Search '(' if FL_OPEN, or search ')' otherwise.
+   TODO: This function isn't efficient...
+        Because there might be more than one nodes whose types are
+        OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+        nodes.
+        E.g. RE: (a){2}  */
+
+static int
+internal_function
+find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+                 int subexp_idx, int type)
+{
+  int cls_idx;
+  for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx)
+    {
+      int cls_node = nodes->elems[cls_idx];
+      const re_token_t *node = dfa->nodes + cls_node;
+      if (node->type == type
+         && node->opr.idx == subexp_idx)
+       return cls_node;
+    }
+  return -1;
+}
+
+/* Check whether the node TOP_NODE at TOP_STR can arrive to the node
+   LAST_NODE at LAST_STR.  We record the path onto PATH since it will be
+   heavily reused.
+   Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise.  */
+
+static reg_errcode_t
+internal_function
+check_arrival (re_match_context_t *mctx, state_array_t *path, int top_node,
+              int top_str, int last_node, int last_str, int type)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err = REG_NOERROR;
+  int subexp_num, backup_cur_idx, str_idx, null_cnt;
+  re_dfastate_t *cur_state = NULL;
+  re_node_set *cur_nodes, next_nodes;
+  re_dfastate_t **backup_state_log;
+  unsigned int context;
+
+  subexp_num = dfa->nodes[top_node].opr.idx;
+  /* Extend the buffer if we need.  */
+  if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0))
+    {
+      re_dfastate_t **new_array;
+      int old_alloc = path->alloc;
+      path->alloc += last_str + mctx->max_mb_elem_len + 1;
+      new_array = re_realloc (path->array, re_dfastate_t *, path->alloc);
+      if (BE (new_array == NULL, 0))
+       {
+         path->alloc = old_alloc;
+         return REG_ESPACE;
+       }
+      path->array = new_array;
+      memset (new_array + old_alloc, '\0',
+             sizeof (re_dfastate_t *) * (path->alloc - old_alloc));
+    }
+
+  str_idx = path->next_idx ? path->next_idx : top_str;
+
+  /* Temporary modify MCTX.  */
+  backup_state_log = mctx->state_log;
+  backup_cur_idx = mctx->input.cur_idx;
+  mctx->state_log = path->array;
+  mctx->input.cur_idx = str_idx;
+
+  /* Setup initial node set.  */
+  context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+  if (str_idx == top_str)
+    {
+      err = re_node_set_init_1 (&next_nodes, top_node);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+      err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+      if (BE (err != REG_NOERROR, 0))
+       {
+         re_node_set_free (&next_nodes);
+         return err;
+       }
+    }
+  else
+    {
+      cur_state = mctx->state_log[str_idx];
+      if (cur_state && cur_state->has_backref)
+       {
+         err = re_node_set_init_copy (&next_nodes, &cur_state->nodes);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+      else
+       re_node_set_init_empty (&next_nodes);
+    }
+  if (str_idx == top_str || (cur_state && cur_state->has_backref))
+    {
+      if (next_nodes.nelem)
+       {
+         err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+                                   subexp_num, type);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return err;
+           }
+       }
+      cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+      if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+       {
+         re_node_set_free (&next_nodes);
+         return err;
+       }
+      mctx->state_log[str_idx] = cur_state;
+    }
+
+  for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;)
+    {
+      re_node_set_empty (&next_nodes);
+      if (mctx->state_log[str_idx + 1])
+       {
+         err = re_node_set_merge (&next_nodes,
+                                  &mctx->state_log[str_idx + 1]->nodes);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return err;
+           }
+       }
+      if (cur_state)
+       {
+         err = check_arrival_add_next_nodes (mctx, str_idx,
+                                             &cur_state->non_eps_nodes,
+                                             &next_nodes);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return err;
+           }
+       }
+      ++str_idx;
+      if (next_nodes.nelem)
+       {
+         err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return err;
+           }
+         err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+                                   subexp_num, type);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return err;
+           }
+       }
+      context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+      cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+      if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+       {
+         re_node_set_free (&next_nodes);
+         return err;
+       }
+      mctx->state_log[str_idx] = cur_state;
+      null_cnt = cur_state == NULL ? null_cnt + 1 : 0;
+    }
+  re_node_set_free (&next_nodes);
+  cur_nodes = (mctx->state_log[last_str] == NULL ? NULL
+              : &mctx->state_log[last_str]->nodes);
+  path->next_idx = str_idx;
+
+  /* Fix MCTX.  */
+  mctx->state_log = backup_state_log;
+  mctx->input.cur_idx = backup_cur_idx;
+
+  /* Then check the current node set has the node LAST_NODE.  */
+  if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node))
+    return REG_NOERROR;
+
+  return REG_NOMATCH;
+}
+
+/* Helper functions for check_arrival.  */
+
+/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them
+   to NEXT_NODES.
+   TODO: This function is similar to the functions transit_state*(),
+        however this function has many additional works.
+        Can't we unify them?  */
+
+static reg_errcode_t
+internal_function
+check_arrival_add_next_nodes (re_match_context_t *mctx, int str_idx,
+                             re_node_set *cur_nodes, re_node_set *next_nodes)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int result;
+  int cur_idx;
+#ifdef RE_ENABLE_I18N
+  reg_errcode_t err = REG_NOERROR;
+#endif
+  re_node_set union_set;
+  re_node_set_init_empty (&union_set);
+  for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx)
+    {
+      int naccepted = 0;
+      int cur_node = cur_nodes->elems[cur_idx];
+#ifdef DEBUG
+      re_token_type_t type = dfa->nodes[cur_node].type;
+      assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+      /* If the node may accept `multi byte'.  */
+      if (dfa->nodes[cur_node].accept_mb)
+       {
+         naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input,
+                                              str_idx);
+         if (naccepted > 1)
+           {
+             re_dfastate_t *dest_state;
+             int next_node = dfa->nexts[cur_node];
+             int next_idx = str_idx + naccepted;
+             dest_state = mctx->state_log[next_idx];
+             re_node_set_empty (&union_set);
+             if (dest_state)
+               {
+                 err = re_node_set_merge (&union_set, &dest_state->nodes);
+                 if (BE (err != REG_NOERROR, 0))
+                   {
+                     re_node_set_free (&union_set);
+                     return err;
+                   }
+               }
+             result = re_node_set_insert (&union_set, next_node);
+             if (BE (result < 0, 0))
+               {
+                 re_node_set_free (&union_set);
+                 return REG_ESPACE;
+               }
+             mctx->state_log[next_idx] = re_acquire_state (&err, dfa,
+                                                           &union_set);
+             if (BE (mctx->state_log[next_idx] == NULL
+                     && err != REG_NOERROR, 0))
+               {
+                 re_node_set_free (&union_set);
+                 return err;
+               }
+           }
+       }
+#endif /* RE_ENABLE_I18N */
+      if (naccepted
+         || check_node_accept (mctx, dfa->nodes + cur_node, str_idx))
+       {
+         result = re_node_set_insert (next_nodes, dfa->nexts[cur_node]);
+         if (BE (result < 0, 0))
+           {
+             re_node_set_free (&union_set);
+             return REG_ESPACE;
+           }
+       }
+    }
+  re_node_set_free (&union_set);
+  return REG_NOERROR;
+}
+
+/* For all the nodes in CUR_NODES, add the epsilon closures of them to
+   CUR_NODES, however exclude the nodes which are:
+    - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN.
+    - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN.
+*/
+
+static reg_errcode_t
+internal_function
+check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes,
+                         int ex_subexp, int type)
+{
+  reg_errcode_t err;
+  int idx, outside_node;
+  re_node_set new_nodes;
+#ifdef DEBUG
+  assert (cur_nodes->nelem);
+#endif
+  err = re_node_set_alloc (&new_nodes, cur_nodes->nelem);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  /* Create a new node set NEW_NODES with the nodes which are epsilon
+     closures of the node in CUR_NODES.  */
+
+  for (idx = 0; idx < cur_nodes->nelem; ++idx)
+    {
+      int cur_node = cur_nodes->elems[idx];
+      const re_node_set *eclosure = dfa->eclosures + cur_node;
+      outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type);
+      if (outside_node == -1)
+       {
+         /* There are no problematic nodes, just merge them.  */
+         err = re_node_set_merge (&new_nodes, eclosure);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&new_nodes);
+             return err;
+           }
+       }
+      else
+       {
+         /* There are problematic nodes, re-calculate incrementally.  */
+         err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node,
+                                             ex_subexp, type);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&new_nodes);
+             return err;
+           }
+       }
+    }
+  re_node_set_free (cur_nodes);
+  *cur_nodes = new_nodes;
+  return REG_NOERROR;
+}
+
+/* Helper function for check_arrival_expand_ecl.
+   Check incrementally the epsilon closure of TARGET, and if it isn't
+   problematic append it to DST_NODES.  */
+
+static reg_errcode_t
+internal_function
+check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes,
+                             int target, int ex_subexp, int type)
+{
+  int cur_node;
+  for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);)
+    {
+      int err;
+
+      if (dfa->nodes[cur_node].type == type
+         && dfa->nodes[cur_node].opr.idx == ex_subexp)
+       {
+         if (type == OP_CLOSE_SUBEXP)
+           {
+             err = re_node_set_insert (dst_nodes, cur_node);
+             if (BE (err == -1, 0))
+               return REG_ESPACE;
+           }
+         break;
+       }
+      err = re_node_set_insert (dst_nodes, cur_node);
+      if (BE (err == -1, 0))
+       return REG_ESPACE;
+      if (dfa->edests[cur_node].nelem == 0)
+       break;
+      if (dfa->edests[cur_node].nelem == 2)
+       {
+         err = check_arrival_expand_ecl_sub (dfa, dst_nodes,
+                                             dfa->edests[cur_node].elems[1],
+                                             ex_subexp, type);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+      cur_node = dfa->edests[cur_node].elems[0];
+    }
+  return REG_NOERROR;
+}
+
+
+/* For all the back references in the current state, calculate the
+   destination of the back references by the appropriate entry
+   in MCTX->BKREF_ENTS.  */
+
+static reg_errcode_t
+internal_function
+expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes,
+                   int cur_str, int subexp_num, int type)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int cache_idx_start = search_cur_bkref_entry (mctx, cur_str);
+  struct re_backref_cache_entry *ent;
+
+  if (cache_idx_start == -1)
+    return REG_NOERROR;
+
+ restart:
+  ent = mctx->bkref_ents + cache_idx_start;
+  do
+    {
+      int to_idx, next_node;
+
+      /* Is this entry ENT is appropriate?  */
+      if (!re_node_set_contains (cur_nodes, ent->node))
+       continue; /* No.  */
+
+      to_idx = cur_str + ent->subexp_to - ent->subexp_from;
+      /* Calculate the destination of the back reference, and append it
+        to MCTX->STATE_LOG.  */
+      if (to_idx == cur_str)
+       {
+         /* The backreference did epsilon transit, we must re-check all the
+            node in the current state.  */
+         re_node_set new_dests;
+         reg_errcode_t err2, err3;
+         next_node = dfa->edests[ent->node].elems[0];
+         if (re_node_set_contains (cur_nodes, next_node))
+           continue;
+         err = re_node_set_init_1 (&new_dests, next_node);
+         err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type);
+         err3 = re_node_set_merge (cur_nodes, &new_dests);
+         re_node_set_free (&new_dests);
+         if (BE (err != REG_NOERROR || err2 != REG_NOERROR
+                 || err3 != REG_NOERROR, 0))
+           {
+             err = (err != REG_NOERROR ? err
+                    : (err2 != REG_NOERROR ? err2 : err3));
+             return err;
+           }
+         /* TODO: It is still inefficient...  */
+         goto restart;
+       }
+      else
+       {
+         re_node_set union_set;
+         next_node = dfa->nexts[ent->node];
+         if (mctx->state_log[to_idx])
+           {
+             int ret;
+             if (re_node_set_contains (&mctx->state_log[to_idx]->nodes,
+                                       next_node))
+               continue;
+             err = re_node_set_init_copy (&union_set,
+                                          &mctx->state_log[to_idx]->nodes);
+             ret = re_node_set_insert (&union_set, next_node);
+             if (BE (err != REG_NOERROR || ret < 0, 0))
+               {
+                 re_node_set_free (&union_set);
+                 err = err != REG_NOERROR ? err : REG_ESPACE;
+                 return err;
+               }
+           }
+         else
+           {
+             err = re_node_set_init_1 (&union_set, next_node);
+             if (BE (err != REG_NOERROR, 0))
+               return err;
+           }
+         mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set);
+         re_node_set_free (&union_set);
+         if (BE (mctx->state_log[to_idx] == NULL
+                 && err != REG_NOERROR, 0))
+           return err;
+       }
+    }
+  while (ent++->more);
+  return REG_NOERROR;
+}
+
+/* Build transition table for the state.
+   Return 1 if succeeded, otherwise return NULL.  */
+
+static int
+internal_function
+build_trtable (const re_dfa_t *dfa, re_dfastate_t *state)
+{
+  reg_errcode_t err;
+  int i, j, ch, need_word_trtable = 0;
+  bitset_word_t elem, mask;
+  bool dests_node_malloced = false;
+  bool dest_states_malloced = false;
+  int ndests; /* Number of the destination states from `state'.  */
+  re_dfastate_t **trtable;
+  re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl;
+  re_node_set follows, *dests_node;
+  bitset_t *dests_ch;
+  bitset_t acceptable;
+
+  struct dests_alloc
+  {
+    re_node_set dests_node[SBC_MAX];
+    bitset_t dests_ch[SBC_MAX];
+  } *dests_alloc;
+
+  /* We build DFA states which corresponds to the destination nodes
+     from `state'.  `dests_node[i]' represents the nodes which i-th
+     destination state contains, and `dests_ch[i]' represents the
+     characters which i-th destination state accepts.  */
+#ifdef HAVE_ALLOCA
+  if (__libc_use_alloca (sizeof (struct dests_alloc)))
+    dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc));
+  else
+#endif
+    {
+      dests_alloc = re_malloc (struct dests_alloc, 1);
+      if (BE (dests_alloc == NULL, 0))
+       return 0;
+      dests_node_malloced = true;
+    }
+  dests_node = dests_alloc->dests_node;
+  dests_ch = dests_alloc->dests_ch;
+
+  /* Initialize transiton table.  */
+  state->word_trtable = state->trtable = NULL;
+
+  /* At first, group all nodes belonging to `state' into several
+     destinations.  */
+  ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch);
+  if (BE (ndests <= 0, 0))
+    {
+      if (dests_node_malloced)
+       free (dests_alloc);
+      /* Return 0 in case of an error, 1 otherwise.  */
+      if (ndests == 0)
+       {
+         state->trtable = (re_dfastate_t **)
+           calloc (sizeof (re_dfastate_t *), SBC_MAX);
+         return 1;
+       }
+      return 0;
+    }
+
+  err = re_node_set_alloc (&follows, ndests + 1);
+  if (BE (err != REG_NOERROR, 0))
+    goto out_free;
+
+  /* Avoid arithmetic overflow in size calculation.  */
+  if (BE ((((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX)
+           / (3 * sizeof (re_dfastate_t *)))
+          < ndests),
+         0))
+    goto out_free;
+
+#ifdef HAVE_ALLOCA
+  if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX
+                        + ndests * 3 * sizeof (re_dfastate_t *)))
+    dest_states = (re_dfastate_t **)
+      alloca (ndests * 3 * sizeof (re_dfastate_t *));
+  else
+#endif
+    {
+      dest_states = (re_dfastate_t **)
+       malloc (ndests * 3 * sizeof (re_dfastate_t *));
+      if (BE (dest_states == NULL, 0))
+       {
+out_free:
+         if (dest_states_malloced)
+           free (dest_states);
+         re_node_set_free (&follows);
+         for (i = 0; i < ndests; ++i)
+           re_node_set_free (dests_node + i);
+         if (dests_node_malloced)
+           free (dests_alloc);
+         return 0;
+       }
+      dest_states_malloced = true;
+    }
+  dest_states_word = dest_states + ndests;
+  dest_states_nl = dest_states_word + ndests;
+  bitset_empty (acceptable);
+
+  /* Then build the states for all destinations.  */
+  for (i = 0; i < ndests; ++i)
+    {
+      int next_node;
+      re_node_set_empty (&follows);
+      /* Merge the follows of this destination states.  */
+      for (j = 0; j < dests_node[i].nelem; ++j)
+       {
+         next_node = dfa->nexts[dests_node[i].elems[j]];
+         if (next_node != -1)
+           {
+             err = re_node_set_merge (&follows, dfa->eclosures + next_node);
+             if (BE (err != REG_NOERROR, 0))
+               goto out_free;
+           }
+       }
+      dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0);
+      if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0))
+       goto out_free;
+      /* If the new state has context constraint,
+        build appropriate states for these contexts.  */
+      if (dest_states[i]->has_constraint)
+       {
+         dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows,
+                                                         CONTEXT_WORD);
+         if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0))
+           goto out_free;
+
+         if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1)
+           need_word_trtable = 1;
+
+         dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows,
+                                                       CONTEXT_NEWLINE);
+         if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0))
+           goto out_free;
+       }
+      else
+       {
+         dest_states_word[i] = dest_states[i];
+         dest_states_nl[i] = dest_states[i];
+       }
+      bitset_merge (acceptable, dests_ch[i]);
+    }
+
+  if (!BE (need_word_trtable, 0))
+    {
+      /* We don't care about whether the following character is a word
+        character, or we are in a single-byte character set so we can
+        discern by looking at the character code: allocate a
+        256-entry transition table.  */
+      trtable = state->trtable =
+       (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+      if (BE (trtable == NULL, 0))
+       goto out_free;
+
+      /* For all characters ch...:  */
+      for (i = 0; i < BITSET_WORDS; ++i)
+       for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+            elem;
+            mask <<= 1, elem >>= 1, ++ch)
+         if (BE (elem & 1, 0))
+           {
+             /* There must be exactly one destination which accepts
+                character ch.  See group_nodes_into_DFAstates.  */
+             for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+               ;
+
+             /* j-th destination accepts the word character ch.  */
+             if (dfa->word_char[i] & mask)
+               trtable[ch] = dest_states_word[j];
+             else
+               trtable[ch] = dest_states[j];
+           }
+    }
+  else
+    {
+      /* We care about whether the following character is a word
+        character, and we are in a multi-byte character set: discern
+        by looking at the character code: build two 256-entry
+        transition tables, one starting at trtable[0] and one
+        starting at trtable[SBC_MAX].  */
+      trtable = state->word_trtable =
+       (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX);
+      if (BE (trtable == NULL, 0))
+       goto out_free;
+
+      /* For all characters ch...:  */
+      for (i = 0; i < BITSET_WORDS; ++i)
+       for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+            elem;
+            mask <<= 1, elem >>= 1, ++ch)
+         if (BE (elem & 1, 0))
+           {
+             /* There must be exactly one destination which accepts
+                character ch.  See group_nodes_into_DFAstates.  */
+             for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+               ;
+
+             /* j-th destination accepts the word character ch.  */
+             trtable[ch] = dest_states[j];
+             trtable[ch + SBC_MAX] = dest_states_word[j];
+           }
+    }
+
+  /* new line */
+  if (bitset_contain (acceptable, NEWLINE_CHAR))
+    {
+      /* The current state accepts newline character.  */
+      for (j = 0; j < ndests; ++j)
+       if (bitset_contain (dests_ch[j], NEWLINE_CHAR))
+         {
+           /* k-th destination accepts newline character.  */
+           trtable[NEWLINE_CHAR] = dest_states_nl[j];
+           if (need_word_trtable)
+             trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j];
+           /* There must be only one destination which accepts
+              newline.  See group_nodes_into_DFAstates.  */
+           break;
+         }
+    }
+
+  if (dest_states_malloced)
+    free (dest_states);
+
+  re_node_set_free (&follows);
+  for (i = 0; i < ndests; ++i)
+    re_node_set_free (dests_node + i);
+
+  if (dests_node_malloced)
+    free (dests_alloc);
+
+  return 1;
+}
+
+/* Group all nodes belonging to STATE into several destinations.
+   Then for all destinations, set the nodes belonging to the destination
+   to DESTS_NODE[i] and set the characters accepted by the destination
+   to DEST_CH[i].  This function return the number of destinations.  */
+
+static int
+internal_function
+group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state,
+                           re_node_set *dests_node, bitset_t *dests_ch)
+{
+  reg_errcode_t err;
+  int result;
+  int i, j, k;
+  int ndests; /* Number of the destinations from `state'.  */
+  bitset_t accepts; /* Characters a node can accept.  */
+  const re_node_set *cur_nodes = &state->nodes;
+  bitset_empty (accepts);
+  ndests = 0;
+
+  /* For all the nodes belonging to `state',  */
+  for (i = 0; i < cur_nodes->nelem; ++i)
+    {
+      re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
+      re_token_type_t type = node->type;
+      unsigned int constraint = node->constraint;
+
+      /* Enumerate all single byte character this node can accept.  */
+      if (type == CHARACTER)
+       bitset_set (accepts, node->opr.c);
+      else if (type == SIMPLE_BRACKET)
+       {
+         bitset_merge (accepts, node->opr.sbcset);
+       }
+      else if (type == OP_PERIOD)
+       {
+#ifdef RE_ENABLE_I18N
+         if (dfa->mb_cur_max > 1)
+           bitset_merge (accepts, dfa->sb_char);
+         else
+#endif
+           bitset_set_all (accepts);
+         if (!(dfa->syntax & RE_DOT_NEWLINE))
+           bitset_clear (accepts, '\n');
+         if (dfa->syntax & RE_DOT_NOT_NULL)
+           bitset_clear (accepts, '\0');
+       }
+#ifdef RE_ENABLE_I18N
+      else if (type == OP_UTF8_PERIOD)
+       {
+         memset (accepts, '\xff', sizeof (bitset_t) / 2);
+         if (!(dfa->syntax & RE_DOT_NEWLINE))
+           bitset_clear (accepts, '\n');
+         if (dfa->syntax & RE_DOT_NOT_NULL)
+           bitset_clear (accepts, '\0');
+       }
+#endif
+      else
+       continue;
+
+      /* Check the `accepts' and sift the characters which are not
+        match it the context.  */
+      if (constraint)
+       {
+         if (constraint & NEXT_NEWLINE_CONSTRAINT)
+           {
+             bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
+             bitset_empty (accepts);
+             if (accepts_newline)
+               bitset_set (accepts, NEWLINE_CHAR);
+             else
+               continue;
+           }
+         if (constraint & NEXT_ENDBUF_CONSTRAINT)
+           {
+             bitset_empty (accepts);
+             continue;
+           }
+
+         if (constraint & NEXT_WORD_CONSTRAINT)
+           {
+             bitset_word_t any_set = 0;
+             if (type == CHARACTER && !node->word_char)
+               {
+                 bitset_empty (accepts);
+                 continue;
+               }
+#ifdef RE_ENABLE_I18N
+             if (dfa->mb_cur_max > 1)
+               for (j = 0; j < BITSET_WORDS; ++j)
+                 any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j]));
+             else
+#endif
+               for (j = 0; j < BITSET_WORDS; ++j)
+                 any_set |= (accepts[j] &= dfa->word_char[j]);
+             if (!any_set)
+               continue;
+           }
+         if (constraint & NEXT_NOTWORD_CONSTRAINT)
+           {
+             bitset_word_t any_set = 0;
+             if (type == CHARACTER && node->word_char)
+               {
+                 bitset_empty (accepts);
+                 continue;
+               }
+#ifdef RE_ENABLE_I18N
+             if (dfa->mb_cur_max > 1)
+               for (j = 0; j < BITSET_WORDS; ++j)
+                 any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j]));
+             else
+#endif
+               for (j = 0; j < BITSET_WORDS; ++j)
+                 any_set |= (accepts[j] &= ~dfa->word_char[j]);
+             if (!any_set)
+               continue;
+           }
+       }
+
+      /* Then divide `accepts' into DFA states, or create a new
+        state.  Above, we make sure that accepts is not empty.  */
+      for (j = 0; j < ndests; ++j)
+       {
+         bitset_t intersec; /* Intersection sets, see below.  */
+         bitset_t remains;
+         /* Flags, see below.  */
+         bitset_word_t has_intersec, not_subset, not_consumed;
+
+         /* Optimization, skip if this state doesn't accept the character.  */
+         if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c))
+           continue;
+
+         /* Enumerate the intersection set of this state and `accepts'.  */
+         has_intersec = 0;
+         for (k = 0; k < BITSET_WORDS; ++k)
+           has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k];
+         /* And skip if the intersection set is empty.  */
+         if (!has_intersec)
+           continue;
+
+         /* Then check if this state is a subset of `accepts'.  */
+         not_subset = not_consumed = 0;
+         for (k = 0; k < BITSET_WORDS; ++k)
+           {
+             not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k];
+             not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k];
+           }
+
+         /* If this state isn't a subset of `accepts', create a
+            new group state, which has the `remains'. */
+         if (not_subset)
+           {
+             bitset_copy (dests_ch[ndests], remains);
+             bitset_copy (dests_ch[j], intersec);
+             err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]);
+             if (BE (err != REG_NOERROR, 0))
+               goto error_return;
+             ++ndests;
+           }
+
+         /* Put the position in the current group. */
+         result = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
+         if (BE (result < 0, 0))
+           goto error_return;
+
+         /* If all characters are consumed, go to next node. */
+         if (!not_consumed)
+           break;
+       }
+      /* Some characters remain, create a new group. */
+      if (j == ndests)
+       {
+         bitset_copy (dests_ch[ndests], accepts);
+         err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]);
+         if (BE (err != REG_NOERROR, 0))
+           goto error_return;
+         ++ndests;
+         bitset_empty (accepts);
+       }
+    }
+  return ndests;
+ error_return:
+  for (j = 0; j < ndests; ++j)
+    re_node_set_free (dests_node + j);
+  return -1;
+}
+
+#ifdef RE_ENABLE_I18N
+/* Check how many bytes the node `dfa->nodes[node_idx]' accepts.
+   Return the number of the bytes the node accepts.
+   STR_IDX is the current index of the input string.
+
+   This function handles the nodes which can accept one character, or
+   one collating element like '.', '[a-z]', opposite to the other nodes
+   can only accept one byte.  */
+
+static int
+internal_function
+check_node_accept_bytes (const re_dfa_t *dfa, int node_idx,
+                        const re_string_t *input, int str_idx)
+{
+  const re_token_t *node = dfa->nodes + node_idx;
+  int char_len, elem_len;
+  int i;
+  wint_t wc;
+
+  if (BE (node->type == OP_UTF8_PERIOD, 0))
+    {
+      unsigned char c = re_string_byte_at (input, str_idx), d;
+      if (BE (c < 0xc2, 1))
+       return 0;
+
+      if (str_idx + 2 > input->len)
+       return 0;
+
+      d = re_string_byte_at (input, str_idx + 1);
+      if (c < 0xe0)
+       return (d < 0x80 || d > 0xbf) ? 0 : 2;
+      else if (c < 0xf0)
+       {
+         char_len = 3;
+         if (c == 0xe0 && d < 0xa0)
+           return 0;
+       }
+      else if (c < 0xf8)
+       {
+         char_len = 4;
+         if (c == 0xf0 && d < 0x90)
+           return 0;
+       }
+      else if (c < 0xfc)
+       {
+         char_len = 5;
+         if (c == 0xf8 && d < 0x88)
+           return 0;
+       }
+      else if (c < 0xfe)
+       {
+         char_len = 6;
+         if (c == 0xfc && d < 0x84)
+           return 0;
+       }
+      else
+       return 0;
+
+      if (str_idx + char_len > input->len)
+       return 0;
+
+      for (i = 1; i < char_len; ++i)
+       {
+         d = re_string_byte_at (input, str_idx + i);
+         if (d < 0x80 || d > 0xbf)
+           return 0;
+       }
+      return char_len;
+    }
+
+  char_len = re_string_char_size_at (input, str_idx);
+  if (node->type == OP_PERIOD)
+    {
+      if (char_len <= 1)
+       return 0;
+      /* FIXME: I don't think this if is needed, as both '\n'
+        and '\0' are char_len == 1.  */
+      /* '.' accepts any one character except the following two cases.  */
+      if ((!(dfa->syntax & RE_DOT_NEWLINE) &&
+          re_string_byte_at (input, str_idx) == '\n') ||
+         ((dfa->syntax & RE_DOT_NOT_NULL) &&
+          re_string_byte_at (input, str_idx) == '\0'))
+       return 0;
+      return char_len;
+    }
+
+  elem_len = re_string_elem_size_at (input, str_idx);
+  wc = __btowc(*(input->mbs+str_idx));
+  if (((elem_len <= 1 && char_len <= 1) || char_len == 0) && (wc != WEOF && wc < SBC_MAX))
+    return 0;
+
+  if (node->type == COMPLEX_BRACKET)
+    {
+      const re_charset_t *cset = node->opr.mbcset;
+# ifdef _LIBC
+      const unsigned char *pin
+       = ((const unsigned char *) re_string_get_buffer (input) + str_idx);
+      int j;
+      uint32_t nrules;
+# endif /* _LIBC */
+      int match_len = 0;
+      wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars)
+                   ? re_string_wchar_at (input, str_idx) : 0);
+
+      /* match with multibyte character?  */
+      for (i = 0; i < cset->nmbchars; ++i)
+       if (wc == cset->mbchars[i])
+         {
+           match_len = char_len;
+           goto check_node_accept_bytes_match;
+         }
+      /* match with character_class?  */
+      for (i = 0; i < cset->nchar_classes; ++i)
+       {
+         wctype_t wt = cset->char_classes[i];
+         if (__iswctype (wc, wt))
+           {
+             match_len = char_len;
+             goto check_node_accept_bytes_match;
+           }
+       }
+
+# ifdef _LIBC
+      nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+      if (nrules != 0)
+       {
+         unsigned int in_collseq = 0;
+         const int32_t *table, *indirect;
+         const unsigned char *weights, *extra;
+         const char *collseqwc;
+         /* This #include defines a local function!  */
+#  include <locale/weight.h>
+
+         /* match with collating_symbol?  */
+         if (cset->ncoll_syms)
+           extra = (const unsigned char *)
+             _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+         for (i = 0; i < cset->ncoll_syms; ++i)
+           {
+             const unsigned char *coll_sym = extra + cset->coll_syms[i];
+             /* Compare the length of input collating element and
+                the length of current collating element.  */
+             if (*coll_sym != elem_len)
+               continue;
+             /* Compare each bytes.  */
+             for (j = 0; j < *coll_sym; j++)
+               if (pin[j] != coll_sym[1 + j])
+                 break;
+             if (j == *coll_sym)
+               {
+                 /* Match if every bytes is equal.  */
+                 match_len = j;
+                 goto check_node_accept_bytes_match;
+               }
+           }
+
+         if (cset->nranges)
+           {
+             if (elem_len <= char_len)
+               {
+                 collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+                 in_collseq = __collseq_table_lookup (collseqwc, wc);
+               }
+             else
+               in_collseq = find_collation_sequence_value (pin, elem_len);
+           }
+         /* match with range expression?  */
+         for (i = 0; i < cset->nranges; ++i)
+           if (cset->range_starts[i] <= in_collseq
+               && in_collseq <= cset->range_ends[i])
+             {
+               match_len = elem_len;
+               goto check_node_accept_bytes_match;
+             }
+
+         /* match with equivalence_class?  */
+         if (cset->nequiv_classes)
+           {
+             const unsigned char *cp = pin;
+             table = (const int32_t *)
+               _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+             weights = (const unsigned char *)
+               _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+             extra = (const unsigned char *)
+               _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+             indirect = (const int32_t *)
+               _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+             int32_t idx = findidx (&cp);
+             if (idx > 0)
+               for (i = 0; i < cset->nequiv_classes; ++i)
+                 {
+                   int32_t equiv_class_idx = cset->equiv_classes[i];
+                   size_t weight_len = weights[idx & 0xffffff];
+                   if (weight_len == weights[equiv_class_idx & 0xffffff]
+                       && (idx >> 24) == (equiv_class_idx >> 24))
+                     {
+                       int cnt = 0;
+
+                       idx &= 0xffffff;
+                       equiv_class_idx &= 0xffffff;
+
+                       while (cnt <= weight_len
+                              && (weights[equiv_class_idx + 1 + cnt]
+                                  == weights[idx + 1 + cnt]))
+                         ++cnt;
+                       if (cnt > weight_len)
+                         {
+                           match_len = elem_len;
+                           goto check_node_accept_bytes_match;
+                         }
+                     }
+                 }
+           }
+       }
+      else
+# endif /* _LIBC */
+       {
+         /* match with range expression?  */
+#if __GNUC__ >= 2
+         wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'};
+#else
+         wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+         cmp_buf[2] = wc;
+#endif
+         for (i = 0; i < cset->nranges; ++i)
+           {
+             cmp_buf[0] = cset->range_starts[i];
+             cmp_buf[4] = cset->range_ends[i];
+             if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+                 && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+               {
+                 match_len = char_len;
+                 goto check_node_accept_bytes_match;
+               }
+           }
+       }
+    check_node_accept_bytes_match:
+      if (!cset->non_match)
+       return match_len;
+      else
+       {
+         if (match_len > 0)
+           return 0;
+         else
+           return (elem_len > char_len) ? elem_len : char_len;
+       }
+    }
+  return 0;
+}
+
+# ifdef _LIBC
+static unsigned int
+internal_function
+find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len)
+{
+  uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules == 0)
+    {
+      if (mbs_len == 1)
+       {
+         /* No valid character.  Match it as a single byte character.  */
+         const unsigned char *collseq = (const unsigned char *)
+           _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+         return collseq[mbs[0]];
+       }
+      return UINT_MAX;
+    }
+  else
+    {
+      int32_t idx;
+      const unsigned char *extra = (const unsigned char *)
+       _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+      int32_t extrasize = (const unsigned char *)
+       _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra;
+
+      for (idx = 0; idx < extrasize;)
+       {
+         int mbs_cnt, found = 0;
+         int32_t elem_mbs_len;
+         /* Skip the name of collating element name.  */
+         idx = idx + extra[idx] + 1;
+         elem_mbs_len = extra[idx++];
+         if (mbs_len == elem_mbs_len)
+           {
+             for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt)
+               if (extra[idx + mbs_cnt] != mbs[mbs_cnt])
+                 break;
+             if (mbs_cnt == elem_mbs_len)
+               /* Found the entry.  */
+               found = 1;
+           }
+         /* Skip the byte sequence of the collating element.  */
+         idx += elem_mbs_len;
+         /* Adjust for the alignment.  */
+         idx = (idx + 3) & ~3;
+         /* Skip the collation sequence value.  */
+         idx += sizeof (uint32_t);
+         /* Skip the wide char sequence of the collating element.  */
+         idx = idx + sizeof (uint32_t) * (extra[idx] + 1);
+         /* If we found the entry, return the sequence value.  */
+         if (found)
+           return *(uint32_t *) (extra + idx);
+         /* Skip the collation sequence value.  */
+         idx += sizeof (uint32_t);
+       }
+      return UINT_MAX;
+    }
+}
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+
+/* Check whether the node accepts the byte which is IDX-th
+   byte of the INPUT.  */
+
+static int
+internal_function
+check_node_accept (const re_match_context_t *mctx, const re_token_t *node,
+                  int idx)
+{
+  unsigned char ch;
+  ch = re_string_byte_at (&mctx->input, idx);
+  switch (node->type)
+    {
+    case CHARACTER:
+      if (node->opr.c != ch)
+       return 0;
+      break;
+
+    case SIMPLE_BRACKET:
+      if (!bitset_contain (node->opr.sbcset, ch))
+       return 0;
+      break;
+
+#ifdef RE_ENABLE_I18N
+    case OP_UTF8_PERIOD:
+      if (ch >= 0x80)
+       return 0;
+      /* FALLTHROUGH */
+#endif
+    case OP_PERIOD:
+      if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE))
+         || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL)))
+       return 0;
+      break;
+
+    default:
+      return 0;
+    }
+
+  if (node->constraint)
+    {
+      /* The node has constraints.  Check whether the current context
+        satisfies the constraints.  */
+      unsigned int context = re_string_context_at (&mctx->input, idx,
+                                                  mctx->eflags);
+      if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+       return 0;
+    }
+
+  return 1;
+}
+
+/* Extend the buffers, if the buffers have run out.  */
+
+static reg_errcode_t
+internal_function
+extend_buffers (re_match_context_t *mctx)
+{
+  reg_errcode_t ret;
+  re_string_t *pstr = &mctx->input;
+
+  /* Avoid overflow.  */
+  if (BE (INT_MAX / 2 / sizeof (re_dfastate_t *) <= pstr->bufs_len, 0))
+    return REG_ESPACE;
+
+  /* Double the lengthes of the buffers.  */
+  ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  if (mctx->state_log != NULL)
+    {
+      /* And double the length of state_log.  */
+      /* XXX We have no indication of the size of this buffer.  If this
+        allocation fail we have no indication that the state_log array
+        does not have the right size.  */
+      re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *,
+                                             pstr->bufs_len + 1);
+      if (BE (new_array == NULL, 0))
+       return REG_ESPACE;
+      mctx->state_log = new_array;
+    }
+
+  /* Then reconstruct the buffers.  */
+  if (pstr->icase)
+    {
+#ifdef RE_ENABLE_I18N
+      if (pstr->mb_cur_max > 1)
+       {
+         ret = build_wcs_upper_buffer (pstr);
+         if (BE (ret != REG_NOERROR, 0))
+           return ret;
+       }
+      else
+#endif /* RE_ENABLE_I18N  */
+       build_upper_buffer (pstr);
+    }
+  else
+    {
+#ifdef RE_ENABLE_I18N
+      if (pstr->mb_cur_max > 1)
+       build_wcs_buffer (pstr);
+      else
+#endif /* RE_ENABLE_I18N  */
+       {
+         if (pstr->trans != NULL)
+           re_string_translate_buffer (pstr);
+       }
+    }
+  return REG_NOERROR;
+}
+
+\f
+/* Functions for matching context.  */
+
+/* Initialize MCTX.  */
+
+static reg_errcode_t
+internal_function
+match_ctx_init (re_match_context_t *mctx, int eflags, int n)
+{
+  mctx->eflags = eflags;
+  mctx->match_last = -1;
+  if (n > 0)
+    {
+      mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
+      mctx->sub_tops = re_malloc (re_sub_match_top_t *, n);
+      if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0))
+       return REG_ESPACE;
+    }
+  /* Already zero-ed by the caller.
+     else
+       mctx->bkref_ents = NULL;
+     mctx->nbkref_ents = 0;
+     mctx->nsub_tops = 0;  */
+  mctx->abkref_ents = n;
+  mctx->max_mb_elem_len = 1;
+  mctx->asub_tops = n;
+  return REG_NOERROR;
+}
+
+/* Clean the entries which depend on the current input in MCTX.
+   This function must be invoked when the matcher changes the start index
+   of the input, or changes the input string.  */
+
+static void
+internal_function
+match_ctx_clean (re_match_context_t *mctx)
+{
+  int st_idx;
+  for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx)
+    {
+      int sl_idx;
+      re_sub_match_top_t *top = mctx->sub_tops[st_idx];
+      for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx)
+       {
+         re_sub_match_last_t *last = top->lasts[sl_idx];
+         re_free (last->path.array);
+         re_free (last);
+       }
+      re_free (top->lasts);
+      if (top->path)
+       {
+         re_free (top->path->array);
+         re_free (top->path);
+       }
+      free (top);
+    }
+
+  mctx->nsub_tops = 0;
+  mctx->nbkref_ents = 0;
+}
+
+/* Free all the memory associated with MCTX.  */
+
+static void
+internal_function
+match_ctx_free (re_match_context_t *mctx)
+{
+  /* First, free all the memory associated with MCTX->SUB_TOPS.  */
+  match_ctx_clean (mctx);
+  re_free (mctx->sub_tops);
+  re_free (mctx->bkref_ents);
+}
+
+/* Add a new backreference entry to MCTX.
+   Note that we assume that caller never call this function with duplicate
+   entry, and call with STR_IDX which isn't smaller than any existing entry.
+*/
+
+static reg_errcode_t
+internal_function
+match_ctx_add_entry (re_match_context_t *mctx, int node, int str_idx, int from,
+                    int to)
+{
+  if (mctx->nbkref_ents >= mctx->abkref_ents)
+    {
+      struct re_backref_cache_entry* new_entry;
+      new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry,
+                             mctx->abkref_ents * 2);
+      if (BE (new_entry == NULL, 0))
+       {
+         re_free (mctx->bkref_ents);
+         return REG_ESPACE;
+       }
+      mctx->bkref_ents = new_entry;
+      memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
+             sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
+      mctx->abkref_ents *= 2;
+    }
+  if (mctx->nbkref_ents > 0
+      && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx)
+    mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1;
+
+  mctx->bkref_ents[mctx->nbkref_ents].node = node;
+  mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx;
+  mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from;
+  mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to;
+
+  /* This is a cache that saves negative results of check_dst_limits_calc_pos.
+     If bit N is clear, means that this entry won't epsilon-transition to
+     an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression.  If
+     it is set, check_dst_limits_calc_pos_1 will recurse and try to find one
+     such node.
+
+     A backreference does not epsilon-transition unless it is empty, so set
+     to all zeros if FROM != TO.  */
+  mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map
+    = (from == to ? ~0 : 0);
+
+  mctx->bkref_ents[mctx->nbkref_ents++].more = 0;
+  if (mctx->max_mb_elem_len < to - from)
+    mctx->max_mb_elem_len = to - from;
+  return REG_NOERROR;
+}
+
+/* Search for the first entry which has the same str_idx, or -1 if none is
+   found.  Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX.  */
+
+static int
+internal_function
+search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx)
+{
+  int left, right, mid, last;
+  last = right = mctx->nbkref_ents;
+  for (left = 0; left < right;)
+    {
+      mid = (left + right) / 2;
+      if (mctx->bkref_ents[mid].str_idx < str_idx)
+       left = mid + 1;
+      else
+       right = mid;
+    }
+  if (left < last && mctx->bkref_ents[left].str_idx == str_idx)
+    return left;
+  else
+    return -1;
+}
+
+/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches
+   at STR_IDX.  */
+
+static reg_errcode_t
+internal_function
+match_ctx_add_subtop (re_match_context_t *mctx, int node, int str_idx)
+{
+#ifdef DEBUG
+  assert (mctx->sub_tops != NULL);
+  assert (mctx->asub_tops > 0);
+#endif
+  if (BE (mctx->nsub_tops == mctx->asub_tops, 0))
+    {
+      int new_asub_tops = mctx->asub_tops * 2;
+      re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops,
+                                                  re_sub_match_top_t *,
+                                                  new_asub_tops);
+      if (BE (new_array == NULL, 0))
+       return REG_ESPACE;
+      mctx->sub_tops = new_array;
+      mctx->asub_tops = new_asub_tops;
+    }
+  mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t));
+  if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0))
+    return REG_ESPACE;
+  mctx->sub_tops[mctx->nsub_tops]->node = node;
+  mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx;
+  return REG_NOERROR;
+}
+
+/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches
+   at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP.  */
+
+static re_sub_match_last_t *
+internal_function
+match_ctx_add_sublast (re_sub_match_top_t *subtop, int node, int str_idx)
+{
+  re_sub_match_last_t *new_entry;
+  if (BE (subtop->nlasts == subtop->alasts, 0))
+    {
+      int new_alasts = 2 * subtop->alasts + 1;
+      re_sub_match_last_t **new_array = re_realloc (subtop->lasts,
+                                                   re_sub_match_last_t *,
+                                                   new_alasts);
+      if (BE (new_array == NULL, 0))
+       return NULL;
+      subtop->lasts = new_array;
+      subtop->alasts = new_alasts;
+    }
+  new_entry = calloc (1, sizeof (re_sub_match_last_t));
+  if (BE (new_entry != NULL, 1))
+    {
+      subtop->lasts[subtop->nlasts] = new_entry;
+      new_entry->node = node;
+      new_entry->str_idx = str_idx;
+      ++subtop->nlasts;
+    }
+  return new_entry;
+}
+
+static void
+internal_function
+sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+              re_dfastate_t **limited_sts, int last_node, int last_str_idx)
+{
+  sctx->sifted_states = sifted_sts;
+  sctx->limited_states = limited_sts;
+  sctx->last_node = last_node;
+  sctx->last_str_idx = last_str_idx;
+  re_node_set_init_empty (&sctx->limits);
+}
diff --git a/compat/strtok_r.c b/compat/strtok_r.c
new file mode 100644 (file)
index 0000000..7b5d568
--- /dev/null
@@ -0,0 +1,61 @@
+/* Reentrant string tokenizer.  Generic version.
+   Copyright (C) 1991,1996-1999,2001,2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include "../git-compat-util.h"
+
+/* Parse S into tokens separated by characters in DELIM.
+   If S is NULL, the saved pointer in SAVE_PTR is used as
+   the next starting point.  For example:
+       char s[] = "-abc-=-def";
+       char *sp;
+       x = strtok_r(s, "-", &sp);      // x = "abc", sp = "=-def"
+       x = strtok_r(NULL, "-=", &sp);  // x = "def", sp = NULL
+       x = strtok_r(NULL, "=", &sp);   // x = NULL
+               // s = "abc\0-def\0"
+*/
+char *
+gitstrtok_r (char *s, const char *delim, char **save_ptr)
+{
+  char *token;
+
+  if (s == NULL)
+    s = *save_ptr;
+
+  /* Scan leading delimiters.  */
+  s += strspn (s, delim);
+  if (*s == '\0')
+    {
+      *save_ptr = s;
+      return NULL;
+    }
+
+  /* Find the end of the token.  */
+  token = s;
+  s = strpbrk (token, delim);
+  if (s == NULL)
+    /* This token finishes the string.  */
+    *save_ptr = token + strlen (token);
+  else
+    {
+      /* Terminate the token and make *SAVE_PTR point past it.  */
+      *s = '\0';
+      *save_ptr = s + 1;
+    }
+  return token;
+}
diff --git a/compat/vcbuild/include/dirent.h b/compat/vcbuild/include/dirent.h
deleted file mode 100644 (file)
index 440618d..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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_ */
index 2a4f276869c507c49c3f1d066fa648c6ea78890d..b14fcf94da405c6f3267437e59936d598168e00f 100644 (file)
@@ -45,6 +45,10 @@ typedef unsigned long long uintmax_t;
 
 typedef int64_t off64_t;
 
+#define INTMAX_MIN  _I64_MIN
+#define INTMAX_MAX  _I64_MAX
+#define UINTMAX_MAX _UI64_MAX
+
 #define STDOUT_FILENO 1
 #define STDERR_FILENO 2
 
diff --git a/compat/win32/dirent.c b/compat/win32/dirent.c
new file mode 100644 (file)
index 0000000..7a0debe
--- /dev/null
@@ -0,0 +1,108 @@
+#include "../git-compat-util.h"
+#include "dirent.h"
+
+struct DIR {
+       struct dirent dd_dir; /* includes d_type */
+       HANDLE dd_handle;     /* FindFirstFile handle */
+       int dd_stat;          /* 0-based index */
+       char dd_name[1];      /* extend struct */
+};
+
+DIR *opendir(const char *name)
+{
+       DWORD attrs = GetFileAttributesA(name);
+       int len;
+       DIR *p;
+
+       /* check for valid path */
+       if (attrs == INVALID_FILE_ATTRIBUTES) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       /* check if it's a directory */
+       if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
+               errno = ENOTDIR;
+               return NULL;
+       }
+
+       /* check that the pattern won't be too long for FindFirstFileA */
+       len = strlen(name);
+       if (is_dir_sep(name[len - 1]))
+               len--;
+       if (len + 2 >= MAX_PATH) {
+               errno = ENAMETOOLONG;
+               return NULL;
+       }
+
+       p = malloc(sizeof(DIR) + len + 2);
+       if (!p)
+               return NULL;
+
+       memset(p, 0, sizeof(DIR) + len + 2);
+       strcpy(p->dd_name, name);
+       p->dd_name[len] = '/';
+       p->dd_name[len+1] = '*';
+
+       p->dd_handle = INVALID_HANDLE_VALUE;
+       return p;
+}
+
+struct dirent *readdir(DIR *dir)
+{
+       WIN32_FIND_DATAA buf;
+       HANDLE handle;
+
+       if (!dir || !dir->dd_handle) {
+               errno = EBADF; /* No set_errno for mingw */
+               return NULL;
+       }
+
+       if (dir->dd_handle == INVALID_HANDLE_VALUE && dir->dd_stat == 0) {
+               DWORD lasterr;
+               handle = FindFirstFileA(dir->dd_name, &buf);
+               lasterr = GetLastError();
+               dir->dd_handle = handle;
+               if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
+                       errno = err_win_to_posix(lasterr);
+                       return NULL;
+               }
+       } else if (dir->dd_handle == INVALID_HANDLE_VALUE) {
+               return NULL;
+       } else if (!FindNextFileA(dir->dd_handle, &buf)) {
+               DWORD lasterr = GetLastError();
+               FindClose(dir->dd_handle);
+               dir->dd_handle = 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 */
+       dir->dd_dir.d_type = 0;
+       if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+               dir->dd_dir.d_type |= DT_DIR;
+       else
+               dir->dd_dir.d_type |= DT_REG;
+
+       return &dir->dd_dir;
+}
+
+int closedir(DIR *dir)
+{
+       if (!dir) {
+               errno = EBADF;
+               return -1;
+       }
+
+       if (dir->dd_handle != INVALID_HANDLE_VALUE)
+               FindClose(dir->dd_handle);
+       free(dir);
+       return 0;
+}
diff --git a/compat/win32/dirent.h b/compat/win32/dirent.h
new file mode 100644 (file)
index 0000000..927a25c
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef DIRENT_H
+#define DIRENT_H
+
+typedef struct DIR DIR;
+
+#define DT_UNKNOWN 0
+#define DT_DIR     1
+#define DT_REG     2
+#define DT_LNK     3
+
+struct dirent {
+       long d_ino;                      /* Always zero. */
+       char d_name[FILENAME_MAX];       /* File name. */
+       union {
+               unsigned short d_reclen; /* Always zero. */
+               unsigned char  d_type;   /* Reimplementation adds this */
+       };
+};
+
+DIR *opendir(const char *dirname);
+struct dirent *readdir(DIR *dir);
+int closedir(DIR *dir);
+
+#endif /* DIRENT_H */
diff --git a/compat/win32/sys/poll.c b/compat/win32/sys/poll.c
new file mode 100644 (file)
index 0000000..708a6c9
--- /dev/null
@@ -0,0 +1,599 @@
+/* Emulation for poll(2)
+   Contributed by Paolo Bonzini.
+
+   Copyright 2001-2003, 2006-2010 Free Software Foundation, Inc.
+
+   This file is part of gnulib.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Tell gcc not to warn about the (nfd < 0) tests, below.  */
+#if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__
+# pragma GCC diagnostic ignored "-Wtype-limits"
+#endif
+
+#include <malloc.h>
+
+#include <sys/types.h>
+#include "poll.h"
+#include <errno.h>
+#include <limits.h>
+#include <assert.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# define WIN32_NATIVE
+# if defined (_MSC_VER)
+#  define _WIN32_WINNT 0x0502
+# endif
+# include <winsock2.h>
+# include <windows.h>
+# include <io.h>
+# include <stdio.h>
+# include <conio.h>
+#else
+# include <sys/time.h>
+# include <sys/socket.h>
+# include <sys/select.h>
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+#include <time.h>
+
+#ifndef INFTIM
+# define INFTIM (-1)
+#endif
+
+/* BeOS does not have MSG_PEEK.  */
+#ifndef MSG_PEEK
+# define MSG_PEEK 0
+#endif
+
+#ifdef WIN32_NATIVE
+
+#define IsConsoleHandle(h) (((long) (h) & 3) == 3)
+
+static BOOL
+IsSocketHandle (HANDLE h)
+{
+  WSANETWORKEVENTS ev;
+
+  if (IsConsoleHandle (h))
+    return FALSE;
+
+  /* Under Wine, it seems that getsockopt returns 0 for pipes too.
+     WSAEnumNetworkEvents instead distinguishes the two correctly.  */
+  ev.lNetworkEvents = 0xDEADBEEF;
+  WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
+  return ev.lNetworkEvents != 0xDEADBEEF;
+}
+
+/* Declare data structures for ntdll functions.  */
+typedef struct _FILE_PIPE_LOCAL_INFORMATION {
+  ULONG NamedPipeType;
+  ULONG NamedPipeConfiguration;
+  ULONG MaximumInstances;
+  ULONG CurrentInstances;
+  ULONG InboundQuota;
+  ULONG ReadDataAvailable;
+  ULONG OutboundQuota;
+  ULONG WriteQuotaAvailable;
+  ULONG NamedPipeState;
+  ULONG NamedPipeEnd;
+} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
+
+typedef struct _IO_STATUS_BLOCK
+{
+  union {
+    DWORD Status;
+    PVOID Pointer;
+  } u;
+  ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef enum _FILE_INFORMATION_CLASS {
+  FilePipeLocalInformation = 24
+} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
+
+typedef DWORD (WINAPI *PNtQueryInformationFile)
+        (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
+
+# ifndef PIPE_BUF
+#  define PIPE_BUF      512
+# endif
+
+/* Compute revents values for file handle H.  If some events cannot happen
+   for the handle, eliminate them from *P_SOUGHT.  */
+
+static int
+win32_compute_revents (HANDLE h, int *p_sought)
+{
+  int i, ret, happened;
+  INPUT_RECORD *irbuffer;
+  DWORD avail, nbuffer;
+  BOOL bRet;
+  IO_STATUS_BLOCK iosb;
+  FILE_PIPE_LOCAL_INFORMATION fpli;
+  static PNtQueryInformationFile NtQueryInformationFile;
+  static BOOL once_only;
+
+  switch (GetFileType (h))
+    {
+    case FILE_TYPE_PIPE:
+      if (!once_only)
+       {
+         NtQueryInformationFile = (PNtQueryInformationFile)
+           GetProcAddress (GetModuleHandle ("ntdll.dll"),
+                           "NtQueryInformationFile");
+         once_only = TRUE;
+       }
+
+      happened = 0;
+      if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
+       {
+         if (avail)
+           happened |= *p_sought & (POLLIN | POLLRDNORM);
+       }
+      else if (GetLastError () == ERROR_BROKEN_PIPE)
+       happened |= POLLHUP;
+
+      else
+       {
+         /* It was the write-end of the pipe.  Check if it is writable.
+            If NtQueryInformationFile fails, optimistically assume the pipe is
+            writable.  This could happen on Win9x, where NtQueryInformationFile
+            is not available, or if we inherit a pipe that doesn't permit
+            FILE_READ_ATTRIBUTES access on the write end (I think this should
+            not happen since WinXP SP2; WINE seems fine too).  Otherwise,
+            ensure that enough space is available for atomic writes.  */
+         memset (&iosb, 0, sizeof (iosb));
+         memset (&fpli, 0, sizeof (fpli));
+
+         if (!NtQueryInformationFile
+             || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
+                                        FilePipeLocalInformation)
+             || fpli.WriteQuotaAvailable >= PIPE_BUF
+             || (fpli.OutboundQuota < PIPE_BUF &&
+                 fpli.WriteQuotaAvailable == fpli.OutboundQuota))
+           happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
+       }
+      return happened;
+
+    case FILE_TYPE_CHAR:
+      ret = WaitForSingleObject (h, 0);
+      if (!IsConsoleHandle (h))
+       return ret == WAIT_OBJECT_0 ? *p_sought & ~(POLLPRI | POLLRDBAND) : 0;
+
+      nbuffer = avail = 0;
+      bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
+      if (bRet)
+       {
+         /* Input buffer.  */
+         *p_sought &= POLLIN | POLLRDNORM;
+         if (nbuffer == 0)
+           return POLLHUP;
+         if (!*p_sought)
+           return 0;
+
+         irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
+         bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
+         if (!bRet || avail == 0)
+           return POLLHUP;
+
+         for (i = 0; i < avail; i++)
+           if (irbuffer[i].EventType == KEY_EVENT)
+             return *p_sought;
+         return 0;
+       }
+      else
+       {
+         /* Screen buffer.  */
+         *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND;
+         return *p_sought;
+       }
+
+    default:
+      ret = WaitForSingleObject (h, 0);
+      if (ret == WAIT_OBJECT_0)
+       return *p_sought & ~(POLLPRI | POLLRDBAND);
+
+      return *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
+    }
+}
+
+/* Convert fd_sets returned by select into revents values.  */
+
+static int
+win32_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents)
+{
+  int happened = 0;
+
+  if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT)
+    happened |= (POLLIN | POLLRDNORM) & sought;
+
+  else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
+    {
+      int r, error;
+
+      char data[64];
+      WSASetLastError (0);
+      r = recv (h, data, sizeof (data), MSG_PEEK);
+      error = WSAGetLastError ();
+      WSASetLastError (0);
+
+      if (r > 0 || error == WSAENOTCONN)
+       happened |= (POLLIN | POLLRDNORM) & sought;
+
+      /* Distinguish hung-up sockets from other errors.  */
+      else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET
+              || error == WSAECONNABORTED || error == WSAENETRESET)
+       happened |= POLLHUP;
+
+      else
+       happened |= POLLERR;
+    }
+
+  if (lNetworkEvents & (FD_WRITE | FD_CONNECT))
+    happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
+
+  if (lNetworkEvents & FD_OOB)
+    happened |= (POLLPRI | POLLRDBAND) & sought;
+
+  return happened;
+}
+
+#else /* !MinGW */
+
+/* Convert select(2) returned fd_sets into poll(2) revents values.  */
+static int
+compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds)
+{
+  int happened = 0;
+  if (FD_ISSET (fd, rfds))
+    {
+      int r;
+      int socket_errno;
+
+# if defined __MACH__ && defined __APPLE__
+      /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
+        for some kinds of descriptors.  Detect if this descriptor is a
+        connected socket, a server socket, or something else using a
+        0-byte recv, and use ioctl(2) to detect POLLHUP.  */
+      r = recv (fd, NULL, 0, MSG_PEEK);
+      socket_errno = (r < 0) ? errno : 0;
+      if (r == 0 || socket_errno == ENOTSOCK)
+       ioctl (fd, FIONREAD, &r);
+# else
+      char data[64];
+      r = recv (fd, data, sizeof (data), MSG_PEEK);
+      socket_errno = (r < 0) ? errno : 0;
+# endif
+      if (r == 0)
+       happened |= POLLHUP;
+
+      /* If the event happened on an unconnected server socket,
+        that's fine. */
+      else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN))
+       happened |= (POLLIN | POLLRDNORM) & sought;
+
+      /* Distinguish hung-up sockets from other errors.  */
+      else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET
+              || socket_errno == ECONNABORTED || socket_errno == ENETRESET)
+       happened |= POLLHUP;
+
+      else
+       happened |= POLLERR;
+    }
+
+  if (FD_ISSET (fd, wfds))
+    happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
+
+  if (FD_ISSET (fd, efds))
+    happened |= (POLLPRI | POLLRDBAND) & sought;
+
+  return happened;
+}
+#endif /* !MinGW */
+
+int
+poll (pfd, nfd, timeout)
+     struct pollfd *pfd;
+     nfds_t nfd;
+     int timeout;
+{
+#ifndef WIN32_NATIVE
+  fd_set rfds, wfds, efds;
+  struct timeval tv;
+  struct timeval *ptv;
+  int maxfd, rc;
+  nfds_t i;
+
+# ifdef _SC_OPEN_MAX
+  static int sc_open_max = -1;
+
+  if (nfd < 0
+      || (nfd > sc_open_max
+         && (sc_open_max != -1
+             || nfd > (sc_open_max = sysconf (_SC_OPEN_MAX)))))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+# else /* !_SC_OPEN_MAX */
+#  ifdef OPEN_MAX
+  if (nfd < 0 || nfd > OPEN_MAX)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+#  endif /* OPEN_MAX -- else, no check is needed */
+# endif /* !_SC_OPEN_MAX */
+
+  /* EFAULT is not necessary to implement, but let's do it in the
+     simplest case. */
+  if (!pfd)
+    {
+      errno = EFAULT;
+      return -1;
+    }
+
+  /* convert timeout number into a timeval structure */
+  if (timeout == 0)
+    {
+      ptv = &tv;
+      ptv->tv_sec = 0;
+      ptv->tv_usec = 0;
+    }
+  else if (timeout > 0)
+    {
+      ptv = &tv;
+      ptv->tv_sec = timeout / 1000;
+      ptv->tv_usec = (timeout % 1000) * 1000;
+    }
+  else if (timeout == INFTIM)
+    /* wait forever */
+    ptv = NULL;
+  else
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* create fd sets and determine max fd */
+  maxfd = -1;
+  FD_ZERO (&rfds);
+  FD_ZERO (&wfds);
+  FD_ZERO (&efds);
+  for (i = 0; i < nfd; i++)
+    {
+      if (pfd[i].fd < 0)
+       continue;
+
+      if (pfd[i].events & (POLLIN | POLLRDNORM))
+       FD_SET (pfd[i].fd, &rfds);
+
+      /* see select(2): "the only exceptional condition detectable
+        is out-of-band data received on a socket", hence we push
+        POLLWRBAND events onto wfds instead of efds. */
+      if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
+       FD_SET (pfd[i].fd, &wfds);
+      if (pfd[i].events & (POLLPRI | POLLRDBAND))
+       FD_SET (pfd[i].fd, &efds);
+      if (pfd[i].fd >= maxfd
+         && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI
+                              | POLLRDNORM | POLLRDBAND
+                              | POLLWRNORM | POLLWRBAND)))
+       {
+         maxfd = pfd[i].fd;
+         if (maxfd > FD_SETSIZE)
+           {
+             errno = EOVERFLOW;
+             return -1;
+           }
+       }
+    }
+
+  /* examine fd sets */
+  rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
+  if (rc < 0)
+    return rc;
+
+  /* establish results */
+  rc = 0;
+  for (i = 0; i < nfd; i++)
+    if (pfd[i].fd < 0)
+      pfd[i].revents = 0;
+    else
+      {
+       int happened = compute_revents (pfd[i].fd, pfd[i].events,
+                                       &rfds, &wfds, &efds);
+       if (happened)
+         {
+           pfd[i].revents = happened;
+           rc++;
+         }
+      }
+
+  return rc;
+#else
+  static struct timeval tv0;
+  static HANDLE hEvent;
+  WSANETWORKEVENTS ev;
+  HANDLE h, handle_array[FD_SETSIZE + 2];
+  DWORD ret, wait_timeout, nhandles;
+  fd_set rfds, wfds, xfds;
+  BOOL poll_again;
+  MSG msg;
+  int rc = 0;
+  nfds_t i;
+
+  if (nfd < 0 || timeout < -1)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (!hEvent)
+    hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+  handle_array[0] = hEvent;
+  nhandles = 1;
+  FD_ZERO (&rfds);
+  FD_ZERO (&wfds);
+  FD_ZERO (&xfds);
+
+  /* Classify socket handles and create fd sets. */
+  for (i = 0; i < nfd; i++)
+    {
+      int sought = pfd[i].events;
+      pfd[i].revents = 0;
+      if (pfd[i].fd < 0)
+       continue;
+      if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND
+                     | POLLPRI | POLLRDBAND)))
+       continue;
+
+      h = (HANDLE) _get_osfhandle (pfd[i].fd);
+      assert (h != NULL);
+      if (IsSocketHandle (h))
+       {
+         int requested = FD_CLOSE;
+
+         /* see above; socket handles are mapped onto select.  */
+         if (sought & (POLLIN | POLLRDNORM))
+           {
+             requested |= FD_READ | FD_ACCEPT;
+             FD_SET ((SOCKET) h, &rfds);
+           }
+         if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND))
+           {
+             requested |= FD_WRITE | FD_CONNECT;
+             FD_SET ((SOCKET) h, &wfds);
+           }
+         if (sought & (POLLPRI | POLLRDBAND))
+           {
+             requested |= FD_OOB;
+             FD_SET ((SOCKET) h, &xfds);
+           }
+
+         if (requested)
+           WSAEventSelect ((SOCKET) h, hEvent, requested);
+       }
+      else
+       {
+         /* Poll now.  If we get an event, do not poll again.  Also,
+            screen buffer handles are waitable, and they'll block until
+            a character is available.  win32_compute_revents eliminates
+            bits for the "wrong" direction. */
+         pfd[i].revents = win32_compute_revents (h, &sought);
+         if (sought)
+           handle_array[nhandles++] = h;
+         if (pfd[i].revents)
+           timeout = 0;
+       }
+    }
+
+  if (select (0, &rfds, &wfds, &xfds, &tv0) > 0)
+    {
+      /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but
+        no need to call select again.  */
+      poll_again = FALSE;
+      wait_timeout = 0;
+    }
+  else
+    {
+      poll_again = TRUE;
+      if (timeout == INFTIM)
+       wait_timeout = INFINITE;
+      else
+       wait_timeout = timeout;
+    }
+
+  for (;;)
+    {
+      ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
+                                      wait_timeout, QS_ALLINPUT);
+
+      if (ret == WAIT_OBJECT_0 + nhandles)
+       {
+         /* new input of some other kind */
+         BOOL bRet;
+         while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
+           {
+             TranslateMessage (&msg);
+             DispatchMessage (&msg);
+           }
+       }
+      else
+       break;
+    }
+
+  if (poll_again)
+    select (0, &rfds, &wfds, &xfds, &tv0);
+
+  /* Place a sentinel at the end of the array.  */
+  handle_array[nhandles] = NULL;
+  nhandles = 1;
+  for (i = 0; i < nfd; i++)
+    {
+      int happened;
+
+      if (pfd[i].fd < 0)
+       continue;
+      if (!(pfd[i].events & (POLLIN | POLLRDNORM |
+                            POLLOUT | POLLWRNORM | POLLWRBAND)))
+       continue;
+
+      h = (HANDLE) _get_osfhandle (pfd[i].fd);
+      if (h != handle_array[nhandles])
+       {
+         /* It's a socket.  */
+         WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
+         WSAEventSelect ((SOCKET) h, 0, 0);
+
+         /* If we're lucky, WSAEnumNetworkEvents already provided a way
+            to distinguish FD_READ and FD_ACCEPT; this saves a recv later.  */
+         if (FD_ISSET ((SOCKET) h, &rfds)
+             && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT)))
+           ev.lNetworkEvents |= FD_READ | FD_ACCEPT;
+         if (FD_ISSET ((SOCKET) h, &wfds))
+           ev.lNetworkEvents |= FD_WRITE | FD_CONNECT;
+         if (FD_ISSET ((SOCKET) h, &xfds))
+           ev.lNetworkEvents |= FD_OOB;
+
+         happened = win32_compute_revents_socket ((SOCKET) h, pfd[i].events,
+                                                  ev.lNetworkEvents);
+       }
+      else
+       {
+         /* Not a socket.  */
+         int sought = pfd[i].events;
+         happened = win32_compute_revents (h, &sought);
+         nhandles++;
+       }
+
+       if ((pfd[i].revents |= happened) != 0)
+       rc++;
+    }
+
+  return rc;
+#endif
+}
diff --git a/compat/win32/sys/poll.h b/compat/win32/sys/poll.h
new file mode 100644 (file)
index 0000000..b7aa59d
--- /dev/null
@@ -0,0 +1,53 @@
+/* Header for poll(2) emulation
+   Contributed by Paolo Bonzini.
+
+   Copyright 2001, 2002, 2003, 2007, 2009, 2010 Free Software Foundation, Inc.
+
+   This file is part of gnulib.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifndef _GL_POLL_H
+#define _GL_POLL_H
+
+/* fake a poll(2) environment */
+#define POLLIN      0x0001      /* any readable data available   */
+#define POLLPRI     0x0002      /* OOB/Urgent readable data      */
+#define POLLOUT     0x0004      /* file descriptor is writeable  */
+#define POLLERR     0x0008      /* some poll error occurred      */
+#define POLLHUP     0x0010      /* file descriptor was "hung up" */
+#define POLLNVAL    0x0020      /* requested events "invalid"    */
+#define POLLRDNORM  0x0040
+#define POLLRDBAND  0x0080
+#define POLLWRNORM  0x0100
+#define POLLWRBAND  0x0200
+
+struct pollfd
+{
+  int fd;                       /* which file descriptor to poll */
+  short events;                 /* events we are interested in   */
+  short revents;                /* events found on return        */
+};
+
+typedef unsigned long nfds_t;
+
+extern int poll (struct pollfd *pfd, nfds_t nfd, int timeout);
+
+/* Define INFTIM only if doing so conforms to POSIX.  */
+#if !defined (_POSIX_C_SOURCE) && !defined (_XOPEN_SOURCE)
+#define INFTIM (-1)
+#endif
+
+#endif /* _GL_POLL_H */
diff --git a/compat/win32/syslog.c b/compat/win32/syslog.c
new file mode 100644 (file)
index 0000000..42b95a9
--- /dev/null
@@ -0,0 +1,72 @@
+#include "../../git-compat-util.h"
+#include "../../strbuf.h"
+
+static HANDLE ms_eventlog;
+
+void openlog(const char *ident, int logopt, int facility)
+{
+       if (ms_eventlog)
+               return;
+
+       ms_eventlog = RegisterEventSourceA(NULL, ident);
+
+       if (!ms_eventlog)
+               warning("RegisterEventSource() failed: %lu", GetLastError());
+}
+
+void syslog(int priority, const char *fmt, ...)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct strbuf_expand_dict_entry dict[] = {
+               {"1", "% 1"},
+               {NULL, NULL}
+       };
+       WORD logtype;
+       char *str;
+       int str_len;
+       va_list ap;
+
+       if (!ms_eventlog)
+               return;
+
+       va_start(ap, fmt);
+       str_len = vsnprintf(NULL, 0, fmt, ap);
+       va_end(ap);
+
+       if (str_len < 0) {
+               warning("vsnprintf failed: '%s'", strerror(errno));
+               return;
+       }
+
+       str = malloc(str_len + 1);
+       va_start(ap, fmt);
+       vsnprintf(str, str_len + 1, fmt, ap);
+       va_end(ap);
+       strbuf_expand(&sb, str, strbuf_expand_dict_cb, &dict);
+       free(str);
+
+       switch (priority) {
+       case LOG_EMERG:
+       case LOG_ALERT:
+       case LOG_CRIT:
+       case LOG_ERR:
+               logtype = EVENTLOG_ERROR_TYPE;
+               break;
+
+       case LOG_WARNING:
+               logtype = EVENTLOG_WARNING_TYPE;
+               break;
+
+       case LOG_NOTICE:
+       case LOG_INFO:
+       case LOG_DEBUG:
+       default:
+               logtype = EVENTLOG_INFORMATION_TYPE;
+               break;
+       }
+
+       ReportEventA(ms_eventlog, logtype, 0, 0, NULL, 1, 0,
+           (const char **)&sb.buf, NULL);
+
+       strbuf_release(&sb);
+}
diff --git a/compat/win32/syslog.h b/compat/win32/syslog.h
new file mode 100644 (file)
index 0000000..70daa7c
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef SYSLOG_H
+#define SYSLOG_H
+
+#define LOG_PID     0x01
+
+#define LOG_EMERG   0
+#define LOG_ALERT   1
+#define LOG_CRIT    2
+#define LOG_ERR     3
+#define LOG_WARNING 4
+#define LOG_NOTICE  5
+#define LOG_INFO    6
+#define LOG_DEBUG   7
+
+#define LOG_DAEMON  (3<<3)
+
+void openlog(const char *ident, int logopt, int facility);
+void syslog(int priority, const char *fmt, ...);
+
+#endif /* SYSLOG_H */
index c06b7f6440defc7ebe6c79e0945ceaf4c9d48383..9b09375b65c3414544bfa02c1011d3a34eb568c7 100644 (file)
--- a/config.c
+++ b/config.c
@@ -8,6 +8,7 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include "strbuf.h"
+#include "quote.h"
 
 #define MAXNAME (256)
 
@@ -34,6 +35,19 @@ static void lowercase(char *p)
                *p = tolower(*p);
 }
 
+void git_config_push_parameter(const char *text)
+{
+       struct strbuf env = STRBUF_INIT;
+       const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
+       if (old) {
+               strbuf_addstr(&env, old);
+               strbuf_addch(&env, ' ');
+       }
+       sq_quote_buf(&env, text);
+       setenv(CONFIG_DATA_ENVIRONMENT, env.buf, 1);
+       strbuf_release(&env);
+}
+
 int git_config_parse_parameter(const char *text)
 {
        struct config_item *ct;
@@ -61,6 +75,37 @@ int git_config_parse_parameter(const char *text)
        return 0;
 }
 
+int git_config_parse_environment(void) {
+       const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
+       char *envw;
+       const char **argv = NULL;
+       int nr = 0, alloc = 0;
+       int i;
+
+       if (!env)
+               return 0;
+       /* sq_dequote will write over it */
+       envw = xstrdup(env);
+
+       if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
+               free(envw);
+               return error("bogus format in " CONFIG_DATA_ENVIRONMENT);
+       }
+
+       for (i = 0; i < nr; i++) {
+               if (git_config_parse_parameter(argv[i]) < 0) {
+                       error("bogus config parameter: %s", argv[i]);
+                       free(argv);
+                       free(envw);
+                       return -1;
+               }
+       }
+
+       free(argv);
+       free(envw);
+       return 0;
+}
+
 static int get_next_char(void)
 {
        int c;
@@ -365,7 +410,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
        return ret;
 }
 
-int git_config_maybe_bool(const char *name, const char *value)
+static int git_config_maybe_bool_text(const char *name, const char *value)
 {
        if (!value)
                return 1;
@@ -382,9 +427,19 @@ int git_config_maybe_bool(const char *name, const char *value)
        return -1;
 }
 
+int git_config_maybe_bool(const char *name, const char *value)
+{
+       long v = git_config_maybe_bool_text(name, value);
+       if (0 <= v)
+               return v;
+       if (git_parse_long(value, &v))
+               return !!v;
+       return -1;
+}
+
 int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
 {
-       int v = git_config_maybe_bool(name, value);
+       int v = git_config_maybe_bool_text(name, value);
        if (0 <= v) {
                *is_bool = 1;
                return v;
@@ -444,6 +499,13 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.abbrevguard")) {
+               unique_abbrev_extra_length = git_config_int(var, value);
+               if (unique_abbrev_extra_length < 0)
+                       unique_abbrev_extra_length = 0;
+               return 0;
+       }
+
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
@@ -560,6 +622,9 @@ static int git_default_core_config(const char *var, const char *value)
        if (!strcmp(var, "core.editor"))
                return git_config_string(&editor_program, var, value);
 
+       if (!strcmp(var, "core.askpass"))
+               return git_config_string(&askpass_program, var, value);
+
        if (!strcmp(var, "core.excludesfile"))
                return git_config_pathname(&excludes_file, var, value);
 
@@ -600,6 +665,14 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.clockskew")) {
+               if (!value || !strcmp(value, "none"))
+                       core_clock_skew = -1;
+               else
+                       core_clock_skew = git_config_int(var, value);
+               return 0;
+       }
+
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
 }
@@ -778,17 +851,23 @@ int git_config_global(void)
 
 int git_config_from_parameters(config_fn_t fn, void *data)
 {
+       static int loaded_environment;
        const struct config_item *ct;
+
+       if (!loaded_environment) {
+               if (git_config_parse_environment() < 0)
+                       return -1;
+               loaded_environment = 1;
+       }
        for (ct = config_parameters; ct; ct = ct->next)
                if (fn(ct->name, ct->value, data) < 0)
                        return -1;
        return 0;
 }
 
-int git_config(config_fn_t fn, void *data)
+int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 {
        int ret = 0, found = 0;
-       char *repo_config = NULL;
        const char *home = NULL;
 
        /* Setting $GIT_CONFIG makes git read _only_ the given config file. */
@@ -810,20 +889,27 @@ int git_config(config_fn_t fn, void *data)
                free(user_config);
        }
 
-       repo_config = git_pathdup("config");
-       if (!access(repo_config, R_OK)) {
+       if (repo_config && !access(repo_config, R_OK)) {
                ret += git_config_from_file(fn, repo_config, data);
                found += 1;
        }
-       free(repo_config);
 
-       if (config_parameters) {
-               ret += git_config_from_parameters(fn, data);
+       ret += git_config_from_parameters(fn, data);
+       if (config_parameters)
                found += 1;
-       }
 
-       if (found == 0)
-               return -1;
+       return ret == 0 ? found : ret;
+}
+
+int git_config(config_fn_t fn, void *data)
+{
+       char *repo_config = NULL;
+       int ret;
+
+       repo_config = git_pathdup("config");
+       ret = git_config_early(fn, data, repo_config);
+       if (repo_config)
+               free(repo_config);
        return ret;
 }
 
index b4e65c32b235eafafefeed1c755be7e1ad5c71ae..961497305777fcbd67001fdae1335fcfe2910e88 100644 (file)
@@ -27,7 +27,7 @@ VPATH = @srcdir@
 export exec_prefix mandir
 export srcdir VPATH
 
-ASCIIDOC8=@ASCIIDOC8@
+ASCIIDOC7=@ASCIIDOC7@
 NEEDS_SSL_WITH_CRYPTO=@NEEDS_SSL_WITH_CRYPTO@
 NO_OPENSSL=@NO_OPENSSL@
 NO_CURL=@NO_CURL@
@@ -46,6 +46,9 @@ NO_IPV6=@NO_IPV6@
 NO_C99_FORMAT=@NO_C99_FORMAT@
 NO_HSTRERROR=@NO_HSTRERROR@
 NO_STRCASESTR=@NO_STRCASESTR@
+NO_STRTOK_R=@NO_STRTOK_R@
+NO_FNMATCH=@NO_FNMATCH@
+NO_FNMATCH_CASEFOLD=@NO_FNMATCH_CASEFOLD@
 NO_MEMMEM=@NO_MEMMEM@
 NO_STRLCPY=@NO_STRLCPY@
 NO_UINTMAX_T=@NO_UINTMAX_T@
@@ -58,6 +61,7 @@ NO_INET_NTOP=@NO_INET_NTOP@
 NO_INET_PTON=@NO_INET_PTON@
 NO_ICONV=@NO_ICONV@
 OLD_ICONV=@OLD_ICONV@
+NO_REGEX=@NO_REGEX@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
 INLINE=@INLINE@
 SOCKLEN_T=@SOCKLEN_T@
index 5601e8bac953c670e35f32ffe48d157dd5694ce7..5792425a4948148b5a110346f5b830063dd78914 100644 (file)
@@ -282,7 +282,15 @@ GIT_PARSE_WITH(iconv))
 GIT_PARSE_WITH_SET_MAKE_VAR(gitconfig, ETC_GITCONFIG,
                        Use VALUE instead of /etc/gitconfig as the
                        global git configuration file.
-                       If VALUE is not fully qualified it will be interpretted
+                       If VALUE is not fully qualified it will be interpreted
+                       as a path relative to the computed prefix at runtime.)
+
+#
+# Allow user to set ETC_GITATTRIBUTES variable
+GIT_PARSE_WITH_SET_MAKE_VAR(gitattributes, ETC_GITATTRIBUTES,
+                       Use VALUE instead of /etc/gitattributes as the
+                       global git attributes file.
+                       If VALUE is not fully qualified it will be interpreted
                        as a path relative to the computed prefix at runtime.)
 
 #
@@ -390,21 +398,21 @@ if test -n "$ASCIIDOC"; then
        AC_MSG_CHECKING([for asciidoc version])
        asciidoc_version=`$ASCIIDOC --version 2>/dev/null`
        case "${asciidoc_version}" in
-       asciidoc' '8*)
-               ASCIIDOC8=YesPlease
+       asciidoc' '7*)
+               ASCIIDOC7=YesPlease
                AC_MSG_RESULT([${asciidoc_version} > 7])
                ;;
-       asciidoc' '7*)
-               ASCIIDOC8=
+       asciidoc' '8*)
+               ASCIIDOC7=
                AC_MSG_RESULT([${asciidoc_version}])
                ;;
        *)
-               ASCIIDOC8=
+               ASCIIDOC7=
                AC_MSG_RESULT([${asciidoc_version} (unknown)])
                ;;
        esac
 fi
-AC_SUBST(ASCIIDOC8)
+AC_SUBST(ASCIIDOC7)
 
 
 ## Checks for libraries.
@@ -609,6 +617,18 @@ AC_CHECK_HEADER([sys/select.h],
 [NO_SYS_SELECT_H=UnfortunatelyYes])
 AC_SUBST(NO_SYS_SELECT_H)
 #
+# Define NO_SYS_POLL_H if you don't have sys/poll.h
+AC_CHECK_HEADER([sys/poll.h],
+[NO_SYS_POLL_H=],
+[NO_SYS_POLL_H=UnfortunatelyYes])
+AC_SUBST(NO_SYS_POLL_H)
+#
+# Define NO_INTTYPES_H if you don't have inttypes.h
+AC_CHECK_HEADER([inttypes.h],
+[NO_INTTYPES_H=],
+[NO_INTTYPES_H=UnfortunatelyYes])
+AC_SUBST(NO_INTTYPES_H)
+#
 # Define OLD_ICONV if your library has an old iconv(), where the second
 # (input buffer pointer) parameter is declared with type (const char **).
 AC_DEFUN([OLDICONVTEST_SRC], [[
@@ -706,6 +726,27 @@ else
 fi
 AC_SUBST(NO_C99_FORMAT)
 #
+# Define NO_REGEX if you have no or inferior regex support in your C library.
+AC_CACHE_CHECK([whether the platform regex can handle null bytes],
+ [ac_cv_c_excellent_regex], [
+AC_EGREP_CPP(yippeeyeswehaveit,
+       AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT
+#include <regex.h>
+],
+[#ifdef REG_STARTEND
+yippeeyeswehaveit
+#endif
+]),
+       [ac_cv_c_excellent_regex=yes],
+       [ac_cv_c_excellent_regex=no])
+])
+if test $ac_cv_c_excellent_regex = yes; then
+       NO_REGEX=
+else
+       NO_REGEX=YesPlease
+fi
+AC_SUBST(NO_REGEX)
+#
 # Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
 # when attempting to read from an fopen'ed directory.
 AC_CACHE_CHECK([whether system succeeds to read fopen'ed directory],
@@ -783,6 +824,40 @@ GIT_CHECK_FUNC(strcasestr,
 [NO_STRCASESTR=YesPlease])
 AC_SUBST(NO_STRCASESTR)
 #
+# Define NO_STRTOK_R if you don't have strtok_r
+GIT_CHECK_FUNC(strtok_r,
+[NO_STRTOK_R=],
+[NO_STRTOK_R=YesPlease])
+AC_SUBST(NO_STRTOK_R)
+#
+# Define NO_FNMATCH if you don't have fnmatch
+GIT_CHECK_FUNC(fnmatch,
+[NO_FNMATCH=],
+[NO_FNMATCH=YesPlease])
+AC_SUBST(NO_FNMATCH)
+#
+# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
+# FNM_CASEFOLD GNU extension.
+AC_CACHE_CHECK([whether the fnmatch function supports the FNMATCH_CASEFOLD GNU extension],
+ [ac_cv_c_excellent_fnmatch], [
+AC_EGREP_CPP(yippeeyeswehaveit,
+       AC_LANG_PROGRAM([
+#include <fnmatch.h>
+],
+[#ifdef FNM_CASEFOLD
+yippeeyeswehaveit
+#endif
+]),
+       [ac_cv_c_excellent_fnmatch=yes],
+       [ac_cv_c_excellent_fnmatch=no])
+])
+if test $ac_cv_c_excellent_fnmatch = yes; then
+       NO_FNMATCH_CASEFOLD=
+else
+       NO_FNMATCH_CASEFOLD=YesPlease
+fi
+AC_SUBST(NO_FNMATCH_CASEFOLD)
+#
 # Define NO_MEMMEM if you don't have memmem.
 GIT_CHECK_FUNC(memmem,
 [NO_MEMMEM=],
@@ -833,6 +908,12 @@ GIT_CHECK_FUNC(mkstemps,
 [NO_MKSTEMPS=YesPlease])
 AC_SUBST(NO_MKSTEMPS)
 #
+# Define NO_INITGROUPS if you don't have initgroups in the C library.
+GIT_CHECK_FUNC(initgroups,
+[NO_INITGROUPS=],
+[NO_INITGROUPS=YesPlease])
+AC_SUBST(NO_INITGROUPS)
+#
 #
 # Define NO_MMAP if you want to avoid mmap.
 #
index 02e738a0146a5c46aaf3f1d8edc3c055a99e98b9..57dc20c43ca1ba205ec0a18e263f9dde081390f4 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -621,15 +621,22 @@ int finish_connect(struct child_process *conn)
 
 char *git_getpass(const char *prompt)
 {
-       char *askpass;
+       const char *askpass;
        struct child_process pass;
        const char *args[3];
        static struct strbuf buffer = STRBUF_INIT;
 
        askpass = getenv("GIT_ASKPASS");
-
-       if (!askpass || !(*askpass))
-               return getpass(prompt);
+       if (!askpass)
+               askpass = askpass_program;
+       if (!askpass)
+               askpass = getenv("SSH_ASKPASS");
+       if (!askpass || !(*askpass)) {
+               char *result = getpass(prompt);
+               if (!result)
+                       die_errno("Could not read password");
+               return result;
+       }
 
        args[0] = askpass;
        args[1] = prompt;
index d0627e0852cc47df3ffd29754254ccb99c3b2ad0..9775dffb5d49521a7078643104762cd136edffcc 100755 (executable)
@@ -122,7 +122,7 @@ def report(refname, merged):
     branch = os.path.basename(refname)
 
     # Compute a shortnane for the revision
-    rev = do("git describe ${merged} 2>/dev/null") or merged[:12]
+    rev = do("git describe '"+ merged +"' 2>/dev/null") or merged[:12]
 
     # Extract the neta-information for the commit
     rawcommit = do("git cat-file commit " + merged)
index 67569901e71e5062199e48304afc424f15b57ba1..893b7716cafa4811d237480a980140d308aa20dc 100755 (executable)
 #    2) Added the following line to your .bashrc:
 #        source ~/.git-completion.sh
 #
+#       Or, add the following lines to your .zshrc:
+#        autoload bashcompinit
+#        bashcompinit
+#        source ~/.git-completion.sh
+#
 #    3) Consider changing your PS1 to also show the current branch:
 #        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
 #
@@ -138,11 +143,12 @@ __git_ps1_show_upstream ()
                # get the upstream from the "git-svn-id: ..." in a commit message
                # (git-svn uses essentially the same procedure internally)
                local svn_upstream=($(git log --first-parent -1 \
-                                       --grep="^git-svn-id: \(${svn_url_pattern:2}\)" 2>/dev/null))
+                                       --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
                if [[ 0 -ne ${#svn_upstream[@]} ]]; then
                        svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
                        svn_upstream=${svn_upstream%@*}
-                       for ((n=1; "$n" <= "${#svn_remote[@]}"; ++n)); do
+                       local n_stop="${#svn_remote[@]}"
+                       for ((n=1; n <= n_stop; ++n)); do
                                svn_upstream=${svn_upstream#${svn_remote[$n]}}
                        done
 
@@ -255,7 +261,7 @@ __git_ps1 ()
                                (describe)
                                        git describe HEAD ;;
                                (* | default)
-                                       git describe --exact-match HEAD ;;
+                                       git describe --tags --exact-match HEAD ;;
                                esac 2>/dev/null)" ||
 
                                b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
@@ -321,11 +327,168 @@ __gitcomp_1 ()
        done
 }
 
+# The following function is based on code from:
+#
+#   bash_completion - programmable completion functions for bash 3.2+
+#
+#   Copyright © 2006-2008, Ian Macdonald <ian@caliban.org>
+#             © 2009-2010, Bash Completion Maintainers
+#                     <bash-completion-devel@lists.alioth.debian.org>
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2, or (at your option)
+#   any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software Foundation,
+#   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#   The latest version of this software can be obtained here:
+#
+#   http://bash-completion.alioth.debian.org/
+#
+#   RELEASE: 2.x
+
+# This function can be used to access a tokenized list of words
+# on the command line:
+#
+#      __git_reassemble_comp_words_by_ref '=:'
+#      if test "${words_[cword_-1]}" = -w
+#      then
+#              ...
+#      fi
+#
+# The argument should be a collection of characters from the list of
+# word completion separators (COMP_WORDBREAKS) to treat as ordinary
+# characters.
+#
+# This is roughly equivalent to going back in time and setting
+# COMP_WORDBREAKS to exclude those characters.  The intent is to
+# make option types like --date=<type> and <rev>:<path> easy to
+# recognize by treating each shell word as a single token.
+#
+# It is best not to set COMP_WORDBREAKS directly because the value is
+# shared with other completion scripts.  By the time the completion
+# function gets called, COMP_WORDS has already been populated so local
+# changes to COMP_WORDBREAKS have no effect.
+#
+# Output: words_, cword_, cur_.
+
+__git_reassemble_comp_words_by_ref()
+{
+       local exclude i j first
+       # Which word separators to exclude?
+       exclude="${1//[^$COMP_WORDBREAKS]}"
+       cword_=$COMP_CWORD
+       if [ -z "$exclude" ]; then
+               words_=("${COMP_WORDS[@]}")
+               return
+       fi
+       # List of word completion separators has shrunk;
+       # re-assemble words to complete.
+       for ((i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do
+               # Append each nonempty word consisting of just
+               # word separator characters to the current word.
+               first=t
+               while
+                       [ $i -gt 0 ] &&
+                       [ -n "${COMP_WORDS[$i]}" ] &&
+                       # word consists of excluded word separators
+                       [ "${COMP_WORDS[$i]//[^$exclude]}" = "${COMP_WORDS[$i]}" ]
+               do
+                       # Attach to the previous token,
+                       # unless the previous token is the command name.
+                       if [ $j -ge 2 ] && [ -n "$first" ]; then
+                               ((j--))
+                       fi
+                       first=
+                       words_[$j]=${words_[j]}${COMP_WORDS[i]}
+                       if [ $i = $COMP_CWORD ]; then
+                               cword_=$j
+                       fi
+                       if (($i < ${#COMP_WORDS[@]} - 1)); then
+                               ((i++))
+                       else
+                               # Done.
+                               return
+                       fi
+               done
+               words_[$j]=${words_[j]}${COMP_WORDS[i]}
+               if [ $i = $COMP_CWORD ]; then
+                       cword_=$j
+               fi
+       done
+}
+
+if ! type _get_comp_words_by_ref >/dev/null 2>&1; then
+if [[ -z ${ZSH_VERSION:+set} ]]; then
+_get_comp_words_by_ref ()
+{
+       local exclude cur_ words_ cword_
+       if [ "$1" = "-n" ]; then
+               exclude=$2
+               shift 2
+       fi
+       __git_reassemble_comp_words_by_ref "$exclude"
+       cur_=${words_[cword_]}
+       while [ $# -gt 0 ]; do
+               case "$1" in
+               cur)
+                       cur=$cur_
+                       ;;
+               prev)
+                       prev=${words_[$cword_-1]}
+                       ;;
+               words)
+                       words=("${words_[@]}")
+                       ;;
+               cword)
+                       cword=$cword_
+                       ;;
+               esac
+               shift
+       done
+}
+else
+_get_comp_words_by_ref ()
+{
+       while [ $# -gt 0 ]; do
+               case "$1" in
+               cur)
+                       cur=${COMP_WORDS[COMP_CWORD]}
+                       ;;
+               prev)
+                       prev=${COMP_WORDS[COMP_CWORD-1]}
+                       ;;
+               words)
+                       words=("${COMP_WORDS[@]}")
+                       ;;
+               cword)
+                       cword=$COMP_CWORD
+                       ;;
+               -n)
+                       # assume COMP_WORDBREAKS is already set sanely
+                       shift
+                       ;;
+               esac
+               shift
+       done
+}
+fi
+fi
+
 # __gitcomp accepts 1, 2, 3, or 4 arguments
 # generates completion reply with compgen
 __gitcomp ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        if [ $# -gt 2 ]; then
                cur="$3"
        fi
@@ -380,16 +543,20 @@ __git_tags ()
        done
 }
 
-# __git_refs accepts 0 or 1 arguments (to pass to __gitdir)
+# __git_refs accepts 0, 1 (to pass to __gitdir), or 2 arguments
+# presence of 2nd argument means use the guess heuristic employed
+# by checkout for tracking branches
 __git_refs ()
 {
-       local i is_hash=y dir="$(__gitdir "${1-}")"
-       local cur="${COMP_WORDS[COMP_CWORD]}" format refs
+       local i is_hash=y dir="$(__gitdir "${1-}")" track="${2-}"
+       local cur format refs
+       _get_comp_words_by_ref -n =: cur
        if [ -d "$dir" ]; then
                case "$cur" in
                refs|refs/*)
                        format="refname"
                        refs="${cur%/*}"
+                       track=""
                        ;;
                *)
                        for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
@@ -401,6 +568,21 @@ __git_refs ()
                esac
                git --git-dir="$dir" for-each-ref --format="%($format)" \
                        $refs
+               if [ -n "$track" ]; then
+                       # employ the heuristic used by git checkout
+                       # Try to find a remote branch that matches the completion word
+                       # but only output if the branch name is unique
+                       local ref entry
+                       git --git-dir="$dir" for-each-ref --shell --format="ref=%(refname:short)" \
+                               "refs/remotes/" | \
+                       while read entry; do
+                               eval "$entry"
+                               ref="${ref#*/}"
+                               if [[ "$ref" == "$cur"* ]]; then
+                                       echo "$ref"
+                               fi
+                       done | uniq -u
+               fi
                return
        fi
        for i in $(git ls-remote "$dir" 2>/dev/null); do
@@ -482,7 +664,8 @@ __git_compute_merge_strategies ()
 
 __git_complete_file ()
 {
-       local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
+       local pfx ls ref cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        ?*:*)
                ref="${cur%%:*}"
@@ -530,7 +713,8 @@ __git_complete_file ()
 
 __git_complete_revlist ()
 {
-       local pfx cur="${COMP_WORDS[COMP_CWORD]}"
+       local pfx cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        *...*)
                pfx="${cur%...*}..."
@@ -550,11 +734,12 @@ __git_complete_revlist ()
 
 __git_complete_remote_or_refspec ()
 {
-       local cmd="${COMP_WORDS[1]}"
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur words cword
+       _get_comp_words_by_ref -n =: cur words cword
+       local cmd="${words[1]}"
        local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
-       while [ $c -lt $COMP_CWORD ]; do
-               i="${COMP_WORDS[c]}"
+       while [ $c -lt $cword ]; do
+               i="${words[c]}"
                case "$i" in
                --mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
                --all)
@@ -622,13 +807,14 @@ __git_complete_remote_or_refspec ()
 
 __git_complete_strategy ()
 {
+       local cur prev
+       _get_comp_words_by_ref -n =: cur prev
        __git_compute_merge_strategies
-       case "${COMP_WORDS[COMP_CWORD-1]}" in
+       case "$prev" in
        -s|--strategy)
                __gitcomp "$__git_merge_strategies"
                return 0
        esac
-       local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --strategy=*)
                __gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}"
@@ -711,7 +897,6 @@ __git_list_porcelain_commands ()
                quiltimport)      : import;;
                read-tree)        : plumbing;;
                receive-pack)     : plumbing;;
-               reflog)           : plumbing;;
                remote-*)         : transport;;
                repo-config)      : deprecated;;
                rerere)           : plumbing;;
@@ -750,6 +935,19 @@ __git_compute_porcelain_commands ()
        : ${__git_porcelain_commands:=$(__git_list_porcelain_commands)}
 }
 
+__git_pretty_aliases ()
+{
+       local i IFS=$'\n'
+       for i in $(git --git-dir="$(__gitdir)" config --get-regexp "pretty\..*" 2>/dev/null); do
+               case "$i" in
+               pretty.*)
+                       i="${i#pretty.}"
+                       echo "${i/ */}"
+                       ;;
+               esac
+       done
+}
+
 __git_aliases ()
 {
        local i IFS=$'\n'
@@ -788,10 +986,10 @@ __git_aliased_command ()
 # __git_find_on_cmdline requires 1 argument
 __git_find_on_cmdline ()
 {
-       local word subcommand c=1
-
-       while [ $c -lt $COMP_CWORD ]; do
-               word="${COMP_WORDS[c]}"
+       local word subcommand c=1 words cword
+       _get_comp_words_by_ref -n =: words cword
+       while [ $c -lt $cword ]; do
+               word="${words[c]}"
                for subcommand in $1; do
                        if [ "$subcommand" = "$word" ]; then
                                echo "$subcommand"
@@ -804,9 +1002,10 @@ __git_find_on_cmdline ()
 
 __git_has_doubledash ()
 {
-       local c=1
-       while [ $c -lt $COMP_CWORD ]; do
-               if [ "--" = "${COMP_WORDS[c]}" ]; then
+       local c=1 words cword
+       _get_comp_words_by_ref -n =: words cword
+       while [ $c -lt $cword ]; do
+               if [ "--" = "${words[c]}" ]; then
                        return 0
                fi
                c=$((++c))
@@ -818,7 +1017,8 @@ __git_whitespacelist="nowarn warn error error-all fix"
 
 _git_am ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+       local cur dir="$(__gitdir)"
+       _get_comp_words_by_ref -n =: cur
        if [ -d "$dir"/rebase-apply ]; then
                __gitcomp "--skip --continue --resolved --abort"
                return
@@ -842,7 +1042,8 @@ _git_am ()
 
 _git_apply ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --whitespace=*)
                __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
@@ -865,7 +1066,8 @@ _git_add ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@ -879,7 +1081,8 @@ _git_add ()
 
 _git_archive ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --format=*)
                __gitcomp "$(git archive --list)" "" "${cur##--format=}"
@@ -907,12 +1110,16 @@ _git_bisect ()
        local subcommands="start bad good skip reset visualize replay log run"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
-               __gitcomp "$subcommands"
+               if [ -f "$(__gitdir)"/BISECT_START ]; then
+                       __gitcomp "$subcommands"
+               else
+                       __gitcomp "replay start"
+               fi
                return
        fi
 
        case "$subcommand" in
-       bad|good|reset|skip)
+       bad|good|reset|skip|start)
                __gitcomp "$(__git_refs)"
                ;;
        *)
@@ -923,10 +1130,11 @@ _git_bisect ()
 
 _git_branch ()
 {
-       local i c=1 only_local_ref="n" has_r="n"
+       local i c=1 only_local_ref="n" has_r="n" cur words cword
 
-       while [ $c -lt $COMP_CWORD ]; do
-               i="${COMP_WORDS[c]}"
+       _get_comp_words_by_ref -n =: cur words cword
+       while [ $c -lt $cword ]; do
+               i="${words[c]}"
                case "$i" in
                -d|-m)  only_local_ref="y" ;;
                -r)     has_r="y" ;;
@@ -934,7 +1142,7 @@ _git_branch ()
                c=$((++c))
        done
 
-       case "${COMP_WORDS[COMP_CWORD]}" in
+       case "$cur" in
        --*)
                __gitcomp "
                        --color --no-color --verbose --abbrev= --no-abbrev
@@ -954,8 +1162,10 @@ _git_branch ()
 
 _git_bundle ()
 {
-       local cmd="${COMP_WORDS[2]}"
-       case "$COMP_CWORD" in
+       local words cword
+       _get_comp_words_by_ref -n =: words cword
+       local cmd="${words[2]}"
+       case "$cword" in
        2)
                __gitcomp "create list-heads verify unbundle"
                ;;
@@ -976,7 +1186,8 @@ _git_checkout ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --conflict=*)
                __gitcomp "diff3 merge" "" "${cur##--conflict=}"
@@ -988,7 +1199,13 @@ _git_checkout ()
                        "
                ;;
        *)
-               __gitcomp "$(__git_refs)"
+               # check if --track, --no-track, or --no-guess was specified
+               # if so, disable DWIM mode
+               local flags="--track --no-track --no-guess" track=1
+               if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
+                       track=''
+               fi
+               __gitcomp "$(__git_refs '' $track)"
                ;;
        esac
 }
@@ -1000,7 +1217,8 @@ _git_cherry ()
 
 _git_cherry_pick ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--edit --no-commit"
@@ -1015,7 +1233,8 @@ _git_clean ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--dry-run --quiet"
@@ -1027,7 +1246,8 @@ _git_clean ()
 
 _git_clone ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@ -1054,7 +1274,8 @@ _git_commit ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --cleanup=*)
                __gitcomp "default strip verbatim whitespace
@@ -1089,7 +1310,8 @@ _git_commit ()
 
 _git_describe ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@ -1121,11 +1343,12 @@ _git_diff ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
-                       --base --ours --theirs
+                       --base --ours --theirs --no-index
                        $__git_diff_common_options
                        "
                return
@@ -1142,7 +1365,8 @@ _git_difftool ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --tool=*)
                __gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
@@ -1167,7 +1391,8 @@ __git_fetch_options="
 
 _git_fetch ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "$__git_fetch_options"
@@ -1179,7 +1404,8 @@ _git_fetch ()
 
 _git_format_patch ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --thread=*)
                __gitcomp "
@@ -1211,7 +1437,8 @@ _git_format_patch ()
 
 _git_fsck ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@ -1226,7 +1453,8 @@ _git_fsck ()
 
 _git_gc ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--prune --aggressive"
@@ -1245,7 +1473,8 @@ _git_grep ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@ -1268,7 +1497,8 @@ _git_grep ()
 
 _git_help ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--all --info --man --web"
@@ -1286,7 +1516,8 @@ _git_help ()
 
 _git_init ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --shared=*)
                __gitcomp "
@@ -1306,7 +1537,8 @@ _git_ls_files ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--cached --deleted --modified --others --ignored
@@ -1360,20 +1592,21 @@ _git_log ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
        local g="$(git rev-parse --git-dir 2>/dev/null)"
        local merge=""
        if [ -f "$g/MERGE_HEAD" ]; then
                merge="--merge"
        fi
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --pretty=*)
-               __gitcomp "$__git_log_pretty_formats
+               __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
                        " "" "${cur##--pretty=}"
                return
                ;;
        --format=*)
-               __gitcomp "$__git_log_pretty_formats
+               __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
                        " "" "${cur##--format=}"
                return
                ;;
@@ -1419,7 +1652,8 @@ _git_merge ()
 {
        __git_complete_strategy && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "$__git_merge_options"
@@ -1430,7 +1664,8 @@ _git_merge ()
 
 _git_mergetool ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --tool=*)
                __gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
@@ -1451,7 +1686,8 @@ _git_merge_base ()
 
 _git_mv ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--dry-run"
@@ -1468,18 +1704,51 @@ _git_name_rev ()
 
 _git_notes ()
 {
-       local subcommands="edit show"
-       if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
-               __gitcomp "$subcommands"
-               return
-       fi
+       local subcommands='add append copy edit list prune remove show'
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
+       local cur words cword
+       _get_comp_words_by_ref -n =: cur words cword
 
-       case "${COMP_WORDS[COMP_CWORD-1]}" in
-       -m|-F)
-               COMPREPLY=()
+       case "$subcommand,$cur" in
+       ,--*)
+               __gitcomp '--ref'
+               ;;
+       ,*)
+               case "${words[cword-1]}" in
+               --ref)
+                       __gitcomp "$(__git_refs)"
+                       ;;
+               *)
+                       __gitcomp "$subcommands --ref"
+                       ;;
+               esac
+               ;;
+       add,--reuse-message=*|append,--reuse-message=*)
+               __gitcomp "$(__git_refs)" "" "${cur##--reuse-message=}"
+               ;;
+       add,--reedit-message=*|append,--reedit-message=*)
+               __gitcomp "$(__git_refs)" "" "${cur##--reedit-message=}"
+               ;;
+       add,--*|append,--*)
+               __gitcomp '--file= --message= --reedit-message=
+                               --reuse-message='
+               ;;
+       copy,--*)
+               __gitcomp '--stdin'
+               ;;
+       prune,--*)
+               __gitcomp '--dry-run --verbose'
+               ;;
+       prune,*)
                ;;
        *)
-               __gitcomp "$(__git_refs)"
+               case "${words[cword-1]}" in
+               -m|-F)
+                       ;;
+               *)
+                       __gitcomp "$(__git_refs)"
+                       ;;
+               esac
                ;;
        esac
 }
@@ -1488,7 +1757,8 @@ _git_pull ()
 {
        __git_complete_strategy && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@ -1504,8 +1774,9 @@ _git_pull ()
 
 _git_push ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
-       case "${COMP_WORDS[COMP_CWORD-1]}" in
+       local cur prev
+       _get_comp_words_by_ref -n =: cur prev
+       case "$prev" in
        --repo)
                __gitcomp "$(__git_remotes)"
                return
@@ -1528,7 +1799,9 @@ _git_push ()
 
 _git_rebase ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+       local dir="$(__gitdir)"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
                __gitcomp "--continue --skip --abort"
                return
@@ -1553,12 +1826,25 @@ _git_rebase ()
        __gitcomp "$(__git_refs)"
 }
 
+_git_reflog ()
+{
+       local subcommands="show delete expire"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
+
+       if [ -z "$subcommand" ]; then
+               __gitcomp "$subcommands"
+       else
+               __gitcomp "$(__git_refs)"
+       fi
+}
+
 __git_send_email_confirm_options="always never auto cc compose"
 __git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all"
 
 _git_send_email ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --confirm=*)
                __gitcomp "
@@ -1600,9 +1886,11 @@ _git_stage ()
 
 __git_config_get_set_variables ()
 {
-       local prevword word config_file= c=$COMP_CWORD
+       local words cword
+       _get_comp_words_by_ref -n =: words cword
+       local prevword word config_file= c=$cword
        while [ $c -gt 1 ]; do
-               word="${COMP_WORDS[c]}"
+               word="${words[c]}"
                case "$word" in
                --global|--system|--file=*)
                        config_file="$word"
@@ -1630,9 +1918,9 @@ __git_config_get_set_variables ()
 
 _git_config ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
-       local prv="${COMP_WORDS[COMP_CWORD-1]}"
-       case "$prv" in
+       local cur prev
+       _get_comp_words_by_ref -n =: cur prev
+       case "$prev" in
        branch.*.remote)
                __gitcomp "$(__git_remotes)"
                return
@@ -1642,13 +1930,13 @@ _git_config ()
                return
                ;;
        remote.*.fetch)
-               local remote="${prv#remote.}"
+               local remote="${prev#remote.}"
                remote="${remote%.fetch}"
                __gitcomp "$(__git_refs_remotes "$remote")"
                return
                ;;
        remote.*.push)
-               local remote="${prv#remote.}"
+               local remote="${prev#remote.}"
                remote="${remote%.push}"
                __gitcomp "$(git --git-dir="$(__gitdir)" \
                        for-each-ref --format='%(refname):%(refname)' \
@@ -1785,30 +2073,50 @@ _git_config ()
                ;;
        esac
        __gitcomp "
-               add.ignore-errors
+               add.ignoreErrors
+               advice.commitBeforeMerge
+               advice.detachedHead
+               advice.implicitIdentity
+               advice.pushNonFastForward
+               advice.resolveConflict
+               advice.statusHints
                alias.
+               am.keepcr
                apply.ignorewhitespace
                apply.whitespace
                branch.autosetupmerge
                branch.autosetuprebase
+               browser.
                clean.requireForce
                color.branch
                color.branch.current
                color.branch.local
                color.branch.plain
                color.branch.remote
+               color.decorate.HEAD
+               color.decorate.branch
+               color.decorate.remoteBranch
+               color.decorate.stash
+               color.decorate.tag
                color.diff
                color.diff.commit
                color.diff.frag
+               color.diff.func
                color.diff.meta
                color.diff.new
                color.diff.old
                color.diff.plain
                color.diff.whitespace
                color.grep
-               color.grep.external
+               color.grep.context
+               color.grep.filename
+               color.grep.function
+               color.grep.linenumber
                color.grep.match
+               color.grep.selected
+               color.grep.separator
                color.interactive
+               color.interactive.error
                color.interactive.header
                color.interactive.help
                color.interactive.prompt
@@ -1822,21 +2130,29 @@ _git_config ()
                color.status.untracked
                color.status.updated
                color.ui
+               commit.status
                commit.template
+               core.abbrevguard
+               core.askpass
+               core.attributesfile
                core.autocrlf
                core.bare
+               core.bigFileThreshold
                core.compression
                core.createObject
                core.deltaBaseCacheLimit
                core.editor
+               core.eol
                core.excludesfile
                core.fileMode
                core.fsyncobjectfiles
                core.gitProxy
                core.ignoreCygwinFSTricks
                core.ignoreStat
+               core.ignorecase
                core.logAllRefUpdates
                core.loosecompression
+               core.notesRef
                core.packedGitLimit
                core.packedGitWindowSize
                core.pager
@@ -1846,6 +2162,7 @@ _git_config ()
                core.repositoryFormatVersion
                core.safecrlf
                core.sharedRepository
+               core.sparseCheckout
                core.symlinks
                core.trustctime
                core.warnAmbiguousRefs
@@ -1853,15 +2170,17 @@ _git_config ()
                core.worktree
                diff.autorefreshindex
                diff.external
+               diff.ignoreSubmodules
                diff.mnemonicprefix
+               diff.noprefix
                diff.renameLimit
-               diff.renameLimit.
                diff.renames
                diff.suppressBlankEmpty
                diff.tool
                diff.wordRegex
                difftool.
                difftool.prompt
+               fetch.recurseSubmodules
                fetch.unpackLimit
                format.attach
                format.cc
@@ -1873,6 +2192,8 @@ _git_config ()
                format.subjectprefix
                format.suffix
                format.thread
+               format.to
+               gc.
                gc.aggressiveWindow
                gc.auto
                gc.autopacklimit
@@ -1910,15 +2231,20 @@ _git_config ()
                http.lowSpeedLimit
                http.lowSpeedTime
                http.maxRequests
+               http.minSessions
                http.noEPSV
+               http.postBuffer
                http.proxy
                http.sslCAInfo
                http.sslCAPath
                http.sslCert
+               http.sslCertPasswordProtected
                http.sslKey
                http.sslVerify
+               http.useragent
                i18n.commitEncoding
                i18n.logOutputEncoding
+               imap.authMethod
                imap.folder
                imap.host
                imap.pass
@@ -1927,6 +2253,7 @@ _git_config ()
                imap.sslverify
                imap.tunnel
                imap.user
+               init.templatedir
                instaweb.browser
                instaweb.httpd
                instaweb.local
@@ -1934,19 +2261,29 @@ _git_config ()
                instaweb.port
                interactive.singlekey
                log.date
+               log.decorate
                log.showroot
                mailmap.file
                man.
                man.viewer
+               merge.
                merge.conflictstyle
                merge.log
                merge.renameLimit
+               merge.renormalize
                merge.stat
                merge.tool
                merge.verbosity
                mergetool.
                mergetool.keepBackup
+               mergetool.keepTemporaries
                mergetool.prompt
+               notes.displayRef
+               notes.rewrite.
+               notes.rewrite.amend
+               notes.rewrite.rebase
+               notes.rewriteMode
+               notes.rewriteRef
                pack.compression
                pack.deltaCacheLimit
                pack.deltaCacheSize
@@ -1957,31 +2294,42 @@ _git_config ()
                pack.window
                pack.windowMemory
                pager.
+               pretty.
                pull.octopus
                pull.twohead
                push.default
+               rebase.autosquash
                rebase.stat
+               receive.autogc
                receive.denyCurrentBranch
+               receive.denyDeleteCurrent
                receive.denyDeletes
                receive.denyNonFastForwards
                receive.fsckObjects
                receive.unpackLimit
+               receive.updateserverinfo
+               remotes.
                repack.usedeltabaseoffset
                rerere.autoupdate
                rerere.enabled
+               sendemail.
                sendemail.aliasesfile
-               sendemail.aliasesfiletype
+               sendemail.aliasfiletype
                sendemail.bcc
                sendemail.cc
                sendemail.cccmd
                sendemail.chainreplyto
                sendemail.confirm
                sendemail.envelopesender
+               sendemail.from
+               sendemail.identity
                sendemail.multiedit
                sendemail.signedoffbycc
+               sendemail.smtpdomain
                sendemail.smtpencryption
                sendemail.smtppass
                sendemail.smtpserver
+               sendemail.smtpserveroption
                sendemail.smtpserverport
                sendemail.smtpuser
                sendemail.suppresscc
@@ -1992,6 +2340,8 @@ _git_config ()
                showbranch.default
                status.relativePaths
                status.showUntrackedFiles
+               status.submodulesummary
+               submodule.
                tar.umask
                transfer.unpackLimit
                url.
@@ -2039,7 +2389,8 @@ _git_reset ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--merge --mixed --hard --soft --patch"
@@ -2051,7 +2402,8 @@ _git_reset ()
 
 _git_revert ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--edit --mainline --no-edit --no-commit --signoff"
@@ -2065,7 +2417,8 @@ _git_rm ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--cached --dry-run --ignore-unmatch --quiet"
@@ -2079,7 +2432,8 @@ _git_shortlog ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@ -2097,15 +2451,16 @@ _git_show ()
 {
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --pretty=*)
-               __gitcomp "$__git_log_pretty_formats
+               __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
                        " "" "${cur##--pretty=}"
                return
                ;;
        --format=*)
-               __gitcomp "$__git_log_pretty_formats
+               __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
                        " "" "${cur##--format=}"
                return
                ;;
@@ -2121,7 +2476,8 @@ _git_show ()
 
 _git_show_branch ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@ -2138,7 +2494,8 @@ _git_show_branch ()
 
 _git_stash ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
+       _get_comp_words_by_ref -n =: cur
        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_on_cmdline "$subcommands")"
@@ -2183,7 +2540,8 @@ _git_submodule ()
 
        local subcommands="add status init update summary foreach sync"
        if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
-               local cur="${COMP_WORDS[COMP_CWORD]}"
+               local cur
+               _get_comp_words_by_ref -n =: cur
                case "$cur" in
                --*)
                        __gitcomp "--quiet --cached"
@@ -2227,7 +2585,8 @@ _git_svn ()
                        --edit --rmdir --find-copies-harder --copy-similarity=
                        "
 
-               local cur="${COMP_WORDS[COMP_CWORD]}"
+               local cur
+               _get_comp_words_by_ref -n =: cur
                case "$subcommand,$cur" in
                fetch,--*)
                        __gitcomp "--revision= --fetch-all $fc_opts"
@@ -2299,8 +2658,10 @@ _git_svn ()
 _git_tag ()
 {
        local i c=1 f=0
-       while [ $c -lt $COMP_CWORD ]; do
-               i="${COMP_WORDS[c]}"
+       local words cword prev
+       _get_comp_words_by_ref -n =: words cword prev
+       while [ $c -lt $cword ]; do
+               i="${words[c]}"
                case "$i" in
                -d|-v)
                        __gitcomp "$(__git_tags)"
@@ -2313,7 +2674,7 @@ _git_tag ()
                c=$((++c))
        done
 
-       case "${COMP_WORDS[COMP_CWORD-1]}" in
+       case "$prev" in
        -m|-F)
                COMPREPLY=()
                ;;
@@ -2339,8 +2700,15 @@ _git ()
 {
        local i c=1 command __git_dir
 
-       while [ $c -lt $COMP_CWORD ]; do
-               i="${COMP_WORDS[c]}"
+       if [[ -n ${ZSH_VERSION-} ]]; then
+               emulate -L bash
+               setopt KSH_TYPESET
+       fi
+
+       local cur words cword
+       _get_comp_words_by_ref -n =: cur words cword
+       while [ $c -lt $cword ]; do
+               i="${words[c]}"
                case "$i" in
                --git-dir=*) __git_dir="${i#--git-dir=}" ;;
                --bare)      __git_dir="." ;;
@@ -2352,7 +2720,7 @@ _git ()
        done
 
        if [ -z "$command" ]; then
-               case "${COMP_WORDS[COMP_CWORD]}" in
+               case "$cur" in
                --*)   __gitcomp "
                        --paginate
                        --no-pager
@@ -2372,25 +2740,31 @@ _git ()
        fi
 
        local completion_func="_git_${command//-/_}"
-       declare -F $completion_func >/dev/null && $completion_func && return
+       declare -f $completion_func >/dev/null && $completion_func && return
 
        local expansion=$(__git_aliased_command "$command")
        if [ -n "$expansion" ]; then
                completion_func="_git_${expansion//-/_}"
-               declare -F $completion_func >/dev/null && $completion_func
+               declare -f $completion_func >/dev/null && $completion_func
        fi
 }
 
 _gitk ()
 {
+       if [[ -n ${ZSH_VERSION-} ]]; then
+               emulate -L bash
+               setopt KSH_TYPESET
+       fi
+
        __git_has_doubledash && return
 
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local cur
        local g="$(__gitdir)"
        local merge=""
        if [ -f "$g/MERGE_HEAD" ]; then
                merge="--merge"
        fi
+       _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@ -2417,3 +2791,29 @@ if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
 complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
        || complete -o default -o nospace -F _git git.exe
 fi
+
+if [[ -n ${ZSH_VERSION-} ]]; then
+       shopt () {
+               local option
+               if [ $# -ne 2 ]; then
+                       echo "USAGE: $0 (-q|-s|-u) <option>" >&2
+                       return 1
+               fi
+               case "$2" in
+               nullglob)
+                       option="$2"
+                       ;;
+               *)
+                       echo "$0: invalid option: $2" >&2
+                       return 1
+               esac
+               case "$1" in
+               -q)     setopt | grep -q "$option" ;;
+               -u)     unsetopt "$option" ;;
+               -s)     setopt "$option" ;;
+               *)
+                       echo "$0: invalid flag: $1" >&2
+                       return 1
+               esac
+       }
+fi
index 7f4c7929784f8e2d8b24b0eaf9f05a2f10c1c6eb..d351cfb6e7e818f5f760e83889586c6dbf6e3a11 100644 (file)
@@ -79,6 +79,7 @@
 ;;; Code:
 
 (eval-when-compile (require 'cl))                            ; to use `push', `pop'
+(require 'format-spec)
 
 (defface git-blame-prefix-face
   '((((background dark)) (:foreground "gray"
index cd10dbcbc90ee155e05cffb49e88defcf35c5a59..3140e405fa130a0fa20b2f6ee4171da70935de66 100644 (file)
@@ -148,7 +148,7 @@ static int append_fetch_head(FILE *fp,
                what = remote_name + 10;
        }
        else if (!strncmp(remote_name, "refs/remotes/", 13)) {
-               kind = "remote branch";
+               kind = "remote-tracking branch";
                what = remote_name + 13;
        }
        else {
index 8f617fcb7089caedce163871c3eb3a97eadb204b..7b922c39480c8b78d0687cbabac59672f9ae6ede 100755 (executable)
@@ -15,7 +15,10 @@ log                  add list of one-line log to merge commit message
 squash               create a single commit instead of doing a merge
 commit               perform a commit if the merge succeeds (default)
 ff                   allow fast-forward (default)
+ff-only              abort if fast-forward is not possible
+rerere-autoupdate    update index with any reused conflict resolution
 s,strategy=          merge strategy to use
+X=                   option for selected merge strategy
 m,message=           message to be used for the merge commit (if any)
 "
 
@@ -25,26 +28,32 @@ require_work_tree
 cd_to_toplevel
 
 test -z "$(git ls-files -u)" ||
-       die "You are in the middle of a conflicted merge."
+       die "Merge is not possible because you have unmerged files."
+
+! test -e "$GIT_DIR/MERGE_HEAD" ||
+       die 'You have not concluded your merge (MERGE_HEAD exists).'
 
 LF='
 '
 
 all_strategies='recur recursive octopus resolve stupid ours subtree'
 all_strategies="$all_strategies recursive-ours recursive-theirs"
+not_strategies='base file index tree'
 default_twohead_strategies='recursive'
 default_octopus_strategies='octopus'
 no_fast_forward_strategies='subtree ours'
 no_trivial_strategies='recursive recur subtree ours recursive-ours recursive-theirs'
 use_strategies=
+xopt=
 
 allow_fast_forward=t
+fast_forward_only=
 allow_trivial_merge=t
-squash= no_commit= log_arg=
+squash= no_commit= log_arg= rr_arg=
 
 dropsave() {
        rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
-                "$GIT_DIR/MERGE_STASH" || exit 1
+                "$GIT_DIR/MERGE_STASH" "$GIT_DIR/MERGE_MODE" || exit 1
 }
 
 savestate() {
@@ -131,21 +140,34 @@ finish () {
 merge_name () {
        remote="$1"
        rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return
-       bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
-       if test "$rh" = "$bh"
-       then
-               echo "$rh               branch '$remote' of ."
-       elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') &&
+       if truname=$(expr "$remote" : '\(.*\)~[0-9]*$') &&
                git show-ref -q --verify "refs/heads/$truname" 2>/dev/null
        then
                echo "$rh               branch '$truname' (early part) of ."
-       elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
+               return
+       fi
+       if found_ref=$(git rev-parse --symbolic-full-name --verify \
+                                                       "$remote" 2>/dev/null)
+       then
+               expanded=$(git check-ref-format --branch "$remote") ||
+                       exit
+               if test "${found_ref#refs/heads/}" != "$found_ref"
+               then
+                       echo "$rh               branch '$expanded' of ."
+                       return
+               elif test "${found_ref#refs/remotes/}" != "$found_ref"
+               then
+                       echo "$rh               remote branch '$expanded' of ."
+                       return
+               fi
+       fi
+       if test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
        then
                sed -e 's/      not-for-merge   /               /' -e 1q \
                        "$GIT_DIR/FETCH_HEAD"
-       else
-               echo "$rh               commit '$remote'"
+               return
        fi
+       echo "$rh               commit '$remote'"
 }
 
 parse_config () {
@@ -172,16 +194,36 @@ parse_config () {
                --no-ff)
                        test "$squash" != t ||
                                die "You cannot combine --squash with --no-ff."
+                       test "$fast_forward_only" != t ||
+                               die "You cannot combine --ff-only with --no-ff."
                        allow_fast_forward=f ;;
+               --ff-only)
+                       test "$allow_fast_forward" != f ||
+                               die "You cannot combine --ff-only with --no-ff."
+                       fast_forward_only=t ;;
+               --rerere-autoupdate|--no-rerere-autoupdate)
+                       rr_arg=$1 ;;
                -s|--strategy)
                        shift
                        case " $all_strategies " in
                        *" $1 "*)
-                               use_strategies="$use_strategies$1 " ;;
+                               use_strategies="$use_strategies$1 "
+                               ;;
                        *)
-                               die "available strategies are: $all_strategies" ;;
+                               case " $not_strategies " in
+                               *" $1 "*)
+                                       false
+                               esac &&
+                               type "git-merge-$1" >/dev/null 2>&1 ||
+                                       die "available strategies are: $all_strategies"
+                               use_strategies="$use_strategies$1 "
+                               ;;
                        esac
                        ;;
+               -X)
+                       shift
+                       xopt="${xopt:+$xopt }$(git rev-parse --sq-quote "--$1")"
+                       ;;
                -m|--message)
                        shift
                        merge_msg="$1"
@@ -245,6 +287,10 @@ then
                exit 1
        fi
 
+       test "$squash" != t ||
+               die "Squash commit into empty head not supported yet"
+       test "$allow_fast_forward" = t ||
+               die "Non-fast-forward into an empty head does not make sense"
        rh=$(git rev-parse --verify "$1^0") ||
                die "$1 - not something we can merge"
 
@@ -261,12 +307,18 @@ else
        # the given message.  If remote is invalid we will die
        # later in the common codepath so we discard the error
        # in this loop.
-       merge_name=$(for remote
+       merge_msg="$(
+               for remote
                do
                        merge_name "$remote"
-               done | git fmt-merge-msg $log_arg
-       )
-       merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
+               done |
+               if test "$have_message" = t
+               then
+                       git fmt-merge-msg -m "$merge_msg" $log_arg
+               else
+                       git fmt-merge-msg $log_arg
+               fi
+       )"
 fi
 head=$(git rev-parse --verify "$head_arg"^0) || usage
 
@@ -335,7 +387,7 @@ case "$#" in
        common=$(git merge-base --all $head "$@")
        ;;
 *)
-       common=$(git show-branch --merge-base $head "$@")
+       common=$(git merge-base --all --octopus $head "$@")
        ;;
 esac
 echo "$head" >"$GIT_DIR/ORIG_HEAD"
@@ -373,8 +425,8 @@ t,1,"$head",*)
        # We are not doing octopus, not fast-forward, and have only
        # one common.
        git update-index --refresh 2>/dev/null
-       case "$allow_trivial_merge" in
-       t)
+       case "$allow_trivial_merge,$fast_forward_only" in
+       t,)
                # See if it is really trivial.
                git var GIT_COMMITTER_IDENT >/dev/null || exit
                echo "Trying really trivial in-index merge..."
@@ -413,6 +465,11 @@ t,1,"$head",*)
        ;;
 esac
 
+if test "$fast_forward_only" = t
+then
+       die "Not possible to fast-forward, aborting."
+fi
+
 # We are going to make a new commit.
 git var GIT_COMMITTER_IDENT >/dev/null || exit
 
@@ -451,7 +508,7 @@ do
     # Remember which strategy left the state in the working tree
     wt_strategy=$strategy
 
-    git-merge-$strategy $common -- "$head_arg" "$@"
+    eval 'git-merge-$strategy '"$xopt"' $common -- "$head_arg" "$@"'
     exit=$?
     if test "$no_commit" = t && test "$exit" = 0
     then
@@ -489,9 +546,9 @@ if test '' != "$result_tree"
 then
     if test "$allow_fast_forward" = "t"
     then
-        parents=$(git show-branch --independent "$head" "$@")
+       parents=$(git merge-base --independent "$head" "$@")
     else
-        parents=$(git rev-parse "$head" "$@")
+       parents=$(git rev-parse "$head" "$@")
     fi
     parents=$(echo "$parents" | sed -e 's/^/-p /')
     result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit
@@ -533,7 +590,15 @@ else
        do
                echo $remote
        done >"$GIT_DIR/MERGE_HEAD"
-       printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG"
+       printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG" ||
+               die "Could not write to $GIT_DIR/MERGE_MSG"
+       if test "$allow_fast_forward" != t
+       then
+               printf "%s" no-ff
+       else
+               :
+       fi >"$GIT_DIR/MERGE_MODE" ||
+               die "Could not write to $GIT_DIR/MERGE_MODE"
 fi
 
 if test "$merge_was_ok" = t
@@ -550,6 +615,6 @@ Conflicts:
                sed -e 's/^[^   ]*      /       /' |
                uniq
        } >>"$GIT_DIR/MERGE_MSG"
-       git rerere
+       git rerere $rr_arg
        die "Automatic merge failed; fix conflicts and then commit the result."
 fi
index 49f00321b28833c24ebb78ea2104f34091d43017..6bf155cbdb61498bdaf84712a2dea6fb05361ce2 100755 (executable)
@@ -26,6 +26,7 @@ require_work_tree
 cd_to_toplevel
 
 no_commit=
+xopt=
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -44,6 +45,16 @@ do
        -x|--i-really-want-to-expose-my-private-commit-object-name)
                replay=
                ;;
+       -X?*)
+               xopt="$xopt$(git rev-parse --sq-quote "--${1#-X}")"
+               ;;
+       --strategy-option=*)
+               xopt="$xopt$(git rev-parse --sq-quote "--${1#--strategy-option=}")"
+               ;;
+       -X|--strategy-option)
+               shift
+               xopt="$xopt$(git rev-parse --sq-quote "--$1")"
+               ;;
        -*)
                usage
                ;;
@@ -159,7 +170,7 @@ export GITHEAD_$head GITHEAD_$next
 # and $prev on top of us (when reverting), or the change between
 # $prev and $commit on top of us (when cherry-picking or replaying).
 
-git-merge-recursive $base -- $head $next &&
+eval "git merge-recursive $xopt $base -- $head $next" &&
 result=$(git-write-tree 2>/dev/null) || {
        mv -f .msg "$GIT_DIR/MERGE_MSG"
        {
@@ -181,7 +192,6 @@ Conflicts:
        esac
        exit 1
 }
-echo >&2 "Finished one $me."
 
 # If we are cherry-pick, and if the merge did not result in
 # hand-editing, we will hit this commit and inherit the original
index 4576c4a862c8ea0565a67eccdae6ef1ac4d9af9a..b09ff8f12f7e5b5b6faeaf857d7c61973de8590e 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
 # This tool is copyright (c) 2005, Matthias Urlichs.
 # It is released under the Gnu Public License, version 2.
@@ -289,7 +289,7 @@ my $current_rev = $opt_s || 1;
 unless(-d $git_dir) {
        system("git init");
        die "Cannot init the GIT db at $git_tree: $?\n" if $?;
-       system("git read-tree");
+       system("git read-tree --empty");
        die "Cannot init an empty tree: $?\n" if $?;
 
        $last_branch = $opt_o;
index c1ea643ace920e83a5577948553d4c9d1046abec..04ce7e3b020f489d59fe103970fc989d7b351127 100755 (executable)
@@ -706,7 +706,9 @@ class P4Submit(Command):
             submitTemplate = self.prepareLogMessage(template, logMessage)
             if os.environ.has_key("P4DIFF"):
                 del(os.environ["P4DIFF"])
-            diff = p4_read_pipe("diff -du ...")
+            diff = ""
+            for editedFile in editedFiles:
+                diff += p4_read_pipe("diff -du %r" % editedFile)
 
             newdiff = ""
             for newFile in filesToAdd:
index 3a5da4ab00f2e29c3244611f40d776293e796e7d..7f3afa5ac4a4ca979a4e5dd63ebc59344f20857b 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 #
 # Copyright 2008-2009 Peter Krefting <peter@softwolves.pp.se>
 #
@@ -140,6 +140,7 @@ by whitespace or other characters.
 
 # Globals
 use strict;
+use warnings;
 use integer;
 my $crlfmode = 0;
 my @revs;
diff --git a/contrib/git-shell-commands/README b/contrib/git-shell-commands/README
new file mode 100644 (file)
index 0000000..438463b
--- /dev/null
@@ -0,0 +1,18 @@
+Sample programs callable through git-shell.  Place a directory named
+'git-shell-commands' in the home directory of a user whose shell is
+git-shell.  Then anyone logging in as that user will be able to run
+executables in the 'git-shell-commands' directory.
+
+Provided commands:
+
+help: Prints out the names of available commands.  When run
+interactively, git-shell will automatically run 'help' on startup,
+provided it exists.
+
+list: Displays any bare repository whose name ends with ".git" under
+user's home directory.  No other git repositories are visible,
+although they might be clonable through git-shell.  'list' is designed
+to minimize the number of calls to git that must be made in finding
+available repositories; if your setup has additional repositories that
+should be user-discoverable, you may wish to modify 'list'
+accordingly.
diff --git a/contrib/git-shell-commands/help b/contrib/git-shell-commands/help
new file mode 100755 (executable)
index 0000000..535770c
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if tty -s
+then
+       echo "Run 'help' for help, or 'exit' to leave.  Available commands:"
+else
+       echo "Run 'help' for help.  Available commands:"
+fi
+
+cd "$(dirname "$0")"
+
+for cmd in *
+do
+       case "$cmd" in
+       help) ;;
+       *) [ -f "$cmd" ] && [ -x "$cmd" ] && echo "$cmd" ;;
+       esac
+done
diff --git a/contrib/git-shell-commands/list b/contrib/git-shell-commands/list
new file mode 100755 (executable)
index 0000000..6f89938
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+print_if_bare_repo='
+       if "$(git --git-dir="$1" rev-parse --is-bare-repository)" = true
+       then
+               printf "%s\n" "${1#./}"
+       fi
+'
+
+find -type d -name "*.git" -exec sh -c "$print_if_bare_repo" -- \{} \; -prune 2>/dev/null
index 00850864371e16d1fa572b0d0e5832b8a88d8666..f99ea9585014face001849aeeefa5f20e79bc1e2 100755 (executable)
 # ---------------------------- Functions
 
 #
-# Top level email generation function.  This decides what type of update
-# this is and calls the appropriate body-generation routine after outputting
-# the common header
+# Function to prepare for email generation. This decides what type
+# of update this is and whether an email should even be generated.
 #
-# Note this function doesn't actually generate any email output, that is
-# taken care of by the functions it calls:
-#  - generate_email_header
-#  - generate_create_XXXX_email
-#  - generate_update_XXXX_email
-#  - generate_delete_XXXX_email
-#  - generate_email_footer
-#
-generate_email()
+prep_for_email()
 {
        # --- Arguments
        oldrev=$(git rev-parse $1)
@@ -153,13 +144,13 @@ generate_email()
                        short_refname=${refname##refs/remotes/}
                        echo >&2 "*** Push-update of tracking branch, $refname"
                        echo >&2 "***  - no email generated."
-                       exit 0
+                       return 1
                        ;;
                *)
                        # Anything else (is there anything else?)
                        echo >&2 "*** Unknown type of update to $refname ($rev_type)"
                        echo >&2 "***  - no email generated"
-                       exit 1
+                       return 1
                        ;;
        esac
 
@@ -175,9 +166,32 @@ generate_email()
                esac
                echo >&2 "*** $config_name is not set so no email will be sent"
                echo >&2 "*** for $refname update $oldrev->$newrev"
-               exit 0
+               return 1
        fi
 
+       return 0
+}
+
+#
+# Top level email generation function.  This calls the appropriate
+# body-generation routine after outputting the common header.
+#
+# Note this function doesn't actually generate any email output, that is
+# taken care of by the functions it calls:
+#  - generate_email_header
+#  - generate_create_XXXX_email
+#  - generate_update_XXXX_email
+#  - generate_delete_XXXX_email
+#  - generate_email_footer
+#
+# Note also that this function cannot 'exit' from the script; when this
+# function is running (in hook script mode), the send_mail() function
+# is already executing in another process, connected via a pipe, and
+# if this function exits without, whatever has been generated to that
+# point will be sent as an email... even if nothing has been generated.
+#
+generate_email()
+{
        # Email parameters
        # The email subject will contain the best description of the ref
        # that we can build from the parameters
@@ -717,10 +731,11 @@ if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
        # Output to the terminal in command line mode - if someone wanted to
        # resend an email; they could redirect the output to sendmail
        # themselves
-       PAGER= generate_email $2 $3 $1
+       prep_for_email $2 $3 $1 && PAGER= generate_email
 else
        while read oldrev newrev refname
        do
-               generate_email $oldrev $newrev $refname $maxlines | send_mail
+               prep_for_email $oldrev $newrev $refname || continue
+               generate_email $maxlines | send_mail
        done
 fi
index e9b9ba4da41c2de0a8ed8e1c540d2c108d0616f0..a2677b03e0ff8347d13a5d56f4fa2e1aba18824a 100644 (file)
@@ -10,6 +10,7 @@ int main(int argc, char **argv)
 {
        svndump_init(NULL);
        svndump_read((argc > 1) ? argv[1] : NULL);
+       svndump_deinit();
        svndump_reset();
        return 0;
 }
index de30f83a1f94aa8dd2bf442e22634524e2689187..35f84bd9e7577b58bc9905c8b46806bfb37963ff 100644 (file)
@@ -12,7 +12,7 @@ svnadmin dump --incremental REPO | svn-fe [url] | git fast-import
 DESCRIPTION
 -----------
 
-Converts a Subversion dumpfile (version: 2) into input suitable for
+Converts a Subversion dumpfile into input suitable for
 git-fast-import(1) and similar importers. REPO is a path to a
 Subversion repository mirrored on the local disk. Remote Subversion
 repositories can be mirrored on local disk using the `svnsync`
@@ -25,6 +25,9 @@ Subversion's repository dump format is documented in full in
 Files in this format can be generated using the 'svnadmin dump' or
 'svk admin dump' command.
 
+Dumps produced with 'svnadmin dump --deltas' (dumpfile format v3)
+are not supported.
+
 OUTPUT FORMAT
 -------------
 The fast-import format is documented by the git-fast-import(1)
@@ -43,11 +46,9 @@ user <user@UUID>
 as committer, where 'user' is the value of the `svn:author` property
 and 'UUID' the repository's identifier.
 
-To support incremental imports, 'svn-fe' will put a `git-svn-id`
-line at the end of each commit log message if passed an url on the
-command line.  This line has the form `git-svn-id: URL@REVNO UUID`.
-
-Empty directories and unknown properties are silently discarded.
+To support incremental imports, 'svn-fe' puts a `git-svn-id` line at
+the end of each commit log message if passed an url on the command
+line.  This line has the form `git-svn-id: URL@REVNO UUID`.
 
 The resulting repository will generally require further processing
 to put each project in its own repository and to separate the history
@@ -56,9 +57,9 @@ may be useful for this purpose.
 
 BUGS
 ----
-Litters the current working directory with .bin files for
-persistence. Will be fixed when the svn-fe infrastructure is aware of
-a Git working directory.
+Empty directories and unknown properties are silently discarded.
+
+The exit status does not reflect whether an error was detected.
 
 SEE ALSO
 --------
index 993cacf324b8595e5be583ff372b25353c7af95c..75e8b258177f7f04dadcac125f2bf7ebea4d0f81 100755 (executable)
@@ -42,7 +42,7 @@ then
 fi
 
 # don't link to a workdir
-if test -L "$git_dir/config"
+if test -h "$git_dir/config"
 then
        die "\"$orig_git\" is a working directory only, please specify" \
                "a complete repository."
@@ -54,13 +54,13 @@ then
        die "destination directory '$new_workdir' already exists."
 fi
 
-# make sure the the links use full paths
+# make sure the links use full paths
 git_dir=$(cd "$git_dir"; pwd)
 
 # create the workdir
 mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
 
-# create the links to the original repo.  explictly exclude index, HEAD and
+# create the links to the original repo.  explicitly exclude index, HEAD and
 # logs/HEAD from the list since they are purely related to the current working
 # directory, and should not be shared.
 for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn
index e41a31e4807e92e210854214d46767b9752c5181..d5aebed48df3387e966a23b6ee460dc2244f728e 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "attr.h"
 #include "run-command.h"
+#include "quote.h"
 
 /*
  * convert.c - convert a file when checking it out and checking it in.
@@ -93,7 +94,8 @@ static int is_binary(unsigned long size, struct text_stat *stats)
        return 0;
 }
 
-static enum eol determine_output_conversion(enum action action) {
+static enum eol determine_output_conversion(enum action action)
+{
        switch (action) {
        case CRLF_BINARY:
                return EOL_UNSET;
@@ -317,6 +319,7 @@ struct filter_params {
        const char *src;
        unsigned long size;
        const char *cmd;
+       const char *path;
 };
 
 static int filter_buffer(int in, int out, void *data)
@@ -329,7 +332,23 @@ static int filter_buffer(int in, int out, void *data)
        int write_err, status;
        const char *argv[] = { NULL, NULL };
 
-       argv[0] = params->cmd;
+       /* apply % substitution to cmd */
+       struct strbuf cmd = STRBUF_INIT;
+       struct strbuf path = STRBUF_INIT;
+       struct strbuf_expand_dict_entry dict[] = {
+               { "f", NULL, },
+               { NULL, NULL, },
+       };
+
+       /* quote the path to preserve spaces, etc. */
+       sq_quote_buf(&path, params->path);
+       dict[0].value = path.buf;
+
+       /* expand all %f with the quoted path */
+       strbuf_expand(&cmd, params->cmd, strbuf_expand_dict_cb, &dict);
+       strbuf_release(&path);
+
+       argv[0] = cmd.buf;
 
        memset(&child_process, 0, sizeof(child_process));
        child_process.argv = argv;
@@ -349,6 +368,8 @@ static int filter_buffer(int in, int out, void *data)
        status = finish_command(&child_process);
        if (status)
                error("external filter %s failed %d", params->cmd, status);
+
+       strbuf_release(&cmd);
        return (write_err || status);
 }
 
@@ -376,6 +397,7 @@ static int apply_filter(const char *path, const char *src, size_t len,
        params.src = src;
        params.size = len;
        params.cmd = cmd;
+       params.path = path;
 
        fflush(NULL);
        if (start_async(&async))
@@ -693,7 +715,8 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check)
        return !!ATTR_TRUE(value);
 }
 
-enum action determine_action(enum action text_attr, enum eol eol_attr) {
+static enum action determine_action(enum action text_attr, enum eol eol_attr)
+{
        if (text_attr == CRLF_BINARY)
                return CRLF_BINARY;
        if (eol_attr == EOL_LF)
@@ -739,7 +762,9 @@ int convert_to_git(const char *path, const char *src, size_t len,
        return ret | ident_to_git(path, src, len, dst, ident);
 }
 
-int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
+static int convert_to_working_tree_internal(const char *path, const char *src,
+                                           size_t len, struct strbuf *dst,
+                                           int normalizing)
 {
        struct git_attr_check check[5];
        enum action action = CRLF_GUESS;
@@ -765,11 +790,32 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc
                src = dst->buf;
                len = dst->len;
        }
-       action = determine_action(action, eol_attr);
-       ret |= crlf_to_worktree(path, src, len, dst, action);
+       /*
+        * CRLF conversion can be skipped if normalizing, unless there
+        * is a smudge filter.  The filter might expect CRLFs.
+        */
+       if (filter || !normalizing) {
+               action = determine_action(action, eol_attr);
+               ret |= crlf_to_worktree(path, src, len, dst, action);
+               if (ret) {
+                       src = dst->buf;
+                       len = dst->len;
+               }
+       }
+       return ret | apply_filter(path, src, len, dst, filter);
+}
+
+int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
+{
+       return convert_to_working_tree_internal(path, src, len, dst, 0);
+}
+
+int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst)
+{
+       int ret = convert_to_working_tree_internal(path, src, len, dst, 1);
        if (ret) {
                src = dst->buf;
                len = dst->len;
        }
-       return ret | apply_filter(path, src, len, dst, filter);
+       return ret | convert_to_git(path, src, len, dst, 0);
 }
index e22a2b7fa5f6fcfc5b26541a21c10d3c8b7eda81..347fd0c52b4cd797f5dafffb6885324f7c7f0274 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -3,8 +3,7 @@
 #include "exec_cmd.h"
 #include "run-command.h"
 #include "strbuf.h"
-
-#include <syslog.h>
+#include "string-list.h"
 
 #ifndef HOST_NAME_MAX
 #define HOST_NAME_MAX 256
 #define NI_MAXSERV 32
 #endif
 
+#ifdef NO_INITGROUPS
+#define initgroups(x, y) (0) /* nothing */
+#endif
+
 static int log_syslog;
 static int verbose;
 static int reuseaddr;
 
 static const char daemon_usage[] =
 "git daemon [--verbose] [--syslog] [--export-all]\n"
-"           [--timeout=n] [--init-timeout=n] [--max-connections=n]\n"
-"           [--strict-paths] [--base-path=path] [--base-path-relaxed]\n"
-"           [--user-path | --user-path=path]\n"
-"           [--interpolated-path=path]\n"
-"           [--reuseaddr] [--detach] [--pid-file=file]\n"
-"           [--[enable|disable|allow-override|forbid-override]=service]\n"
-"           [--inetd | [--listen=host_or_ipaddr] [--port=n]\n"
-"                      [--user=user [--group=group]]\n"
-"           [directory...]";
+"           [--timeout=<n>] [--init-timeout=<n>] [--max-connections=<n>]\n"
+"           [--strict-paths] [--base-path=<path>] [--base-path-relaxed]\n"
+"           [--user-path | --user-path=<path>]\n"
+"           [--interpolated-path=<path>]\n"
+"           [--reuseaddr] [--pid-file=<file>]\n"
+"           [--(enable|disable|allow-override|forbid-override)=<service>]\n"
+"           [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>]\n"
+"                      [--detach] [--user=<user> [--group=<group>]]\n"
+"           [<directory>...]";
 
 /* List of acceptable pathname prefixes */
 static char **ok_paths;
@@ -68,12 +71,14 @@ static void logreport(int priority, const char *err, va_list params)
                syslog(priority, "%s", buf);
        } else {
                /*
-                * Since stderr is set to linebuffered mode, the
+                * Since stderr is set to buffered mode, the
                 * logging of different processes will not overlap
+                * unless they overflow the (rather big) buffers.
                 */
                fprintf(stderr, "[%"PRIuMAX"] ", (uintmax_t)getpid());
                vfprintf(stderr, err, params);
                fputc('\n', stderr);
+               fflush(stderr);
        }
 }
 
@@ -515,37 +520,14 @@ static void parse_host_arg(char *extra_args, int buflen)
 }
 
 
-static int execute(struct sockaddr *addr)
+static int execute(void)
 {
        static char line[1000];
        int pktlen, len, i;
+       char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
 
-       if (addr) {
-               char addrbuf[256] = "";
-               int port = -1;
-
-               if (addr->sa_family == AF_INET) {
-                       struct sockaddr_in *sin_addr = (void *) addr;
-                       inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
-                       port = ntohs(sin_addr->sin_port);
-#ifndef NO_IPV6
-               } else if (addr && addr->sa_family == AF_INET6) {
-                       struct sockaddr_in6 *sin6_addr = (void *) addr;
-
-                       char *buf = addrbuf;
-                       *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
-                       inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
-                       strcat(buf, "]");
-
-                       port = ntohs(sin6_addr->sin6_port);
-#endif
-               }
-               loginfo("Connection from %s:%d", addrbuf, port);
-               setenv("REMOTE_ADDR", addrbuf, 1);
-       }
-       else {
-               unsetenv("REMOTE_ADDR");
-       }
+       if (addr)
+               loginfo("Connection from %s:%s", addr, port);
 
        alarm(init_timeout ? init_timeout : timeout);
        pktlen = packet_read_line(0, line, sizeof(line));
@@ -615,17 +597,17 @@ static unsigned int live_children;
 
 static struct child {
        struct child *next;
-       pid_t pid;
+       struct child_process cld;
        struct sockaddr_storage address;
 } *firstborn;
 
-static void add_child(pid_t pid, struct sockaddr *addr, int addrlen)
+static void add_child(struct child_process *cld, struct sockaddr *addr, socklen_t addrlen)
 {
        struct child *newborn, **cradle;
 
        newborn = xcalloc(1, sizeof(*newborn));
        live_children++;
-       newborn->pid = pid;
+       memcpy(&newborn->cld, cld, sizeof(*cld));
        memcpy(&newborn->address, addr, addrlen);
        for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
                if (!addrcmp(&(*cradle)->address, &newborn->address))
@@ -634,19 +616,6 @@ static void add_child(pid_t pid, struct sockaddr *addr, int addrlen)
        *cradle = newborn;
 }
 
-static void remove_child(pid_t pid)
-{
-       struct child **cradle, *blanket;
-
-       for (cradle = &firstborn; (blanket = *cradle); cradle = &blanket->next)
-               if (blanket->pid == pid) {
-                       *cradle = blanket->next;
-                       live_children--;
-                       free(blanket);
-                       break;
-               }
-}
-
 /*
  * This gets called if the number of connections grows
  * past "max_connections".
@@ -662,7 +631,7 @@ static void kill_some_child(void)
 
        for (; (next = blanket->next); blanket = next)
                if (!addrcmp(&blanket->address, &next->address)) {
-                       kill(blanket->pid, SIGTERM);
+                       kill(blanket->cld.pid, SIGTERM);
                        break;
                }
 }
@@ -672,18 +641,28 @@ static void check_dead_children(void)
        int status;
        pid_t pid;
 
-       while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
-               const char *dead = "";
-               remove_child(pid);
-               if (!WIFEXITED(status) || (WEXITSTATUS(status) > 0))
-                       dead = " (with error)";
-               loginfo("[%"PRIuMAX"] Disconnected%s", (uintmax_t)pid, dead);
-       }
+       struct child **cradle, *blanket;
+       for (cradle = &firstborn; (blanket = *cradle);)
+               if ((pid = waitpid(blanket->cld.pid, &status, WNOHANG)) > 1) {
+                       const char *dead = "";
+                       if (status)
+                               dead = " (with error)";
+                       loginfo("[%"PRIuMAX"] Disconnected%s", (uintmax_t)pid, dead);
+
+                       /* remove the child */
+                       *cradle = blanket->next;
+                       live_children--;
+                       free(blanket);
+               } else
+                       cradle = &blanket->next;
 }
 
-static void handle(int incoming, struct sockaddr *addr, int addrlen)
+static char **cld_argv;
+static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
 {
-       pid_t pid;
+       struct child_process cld = { 0 };
+       char addrbuf[300] = "REMOTE_ADDR=", portbuf[300];
+       char *env[] = { addrbuf, portbuf, NULL };
 
        if (max_connections && live_children >= max_connections) {
                kill_some_child();
@@ -696,22 +675,37 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen)
                }
        }
 
-       if ((pid = fork())) {
-               close(incoming);
-               if (pid < 0) {
-                       logerror("Couldn't fork %s", strerror(errno));
-                       return;
-               }
+       if (addr->sa_family == AF_INET) {
+               struct sockaddr_in *sin_addr = (void *) addr;
+               inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf + 12,
+                   sizeof(addrbuf) - 12);
+               snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d",
+                   ntohs(sin_addr->sin_port));
+#ifndef NO_IPV6
+       } else if (addr && addr->sa_family == AF_INET6) {
+               struct sockaddr_in6 *sin6_addr = (void *) addr;
 
-               add_child(pid, addr, addrlen);
-               return;
+               char *buf = addrbuf + 12;
+               *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
+               inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf,
+                   sizeof(addrbuf) - 13);
+               strcat(buf, "]");
+
+               snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d",
+                   ntohs(sin6_addr->sin6_port));
+#endif
        }
 
-       dup2(incoming, 0);
-       dup2(incoming, 1);
-       close(incoming);
+       cld.env = (const char **)env;
+       cld.argv = (const char **)cld_argv;
+       cld.in = incoming;
+       cld.out = dup(incoming);
 
-       exit(execute(addr));
+       if (start_command(&cld))
+               logerror("unable to fork");
+       else
+               add_child(&cld, addr, addrlen);
+       close(incoming);
 }
 
 static void child_handler(int signo)
@@ -734,11 +728,17 @@ static int set_reuse_addr(int sockfd)
                          &on, sizeof(on));
 }
 
+struct socketlist {
+       int *list;
+       size_t nr;
+       size_t alloc;
+};
+
 #ifndef NO_IPV6
 
-static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
+static int setup_named_sock(char *listen_addr, int listen_port, struct socketlist *socklist)
 {
-       int socknum = 0, *socklist = NULL;
+       int socknum = 0;
        int maxfd = -1;
        char pbuf[NI_MAXSERV];
        struct addrinfo hints, *ai0, *ai;
@@ -753,8 +753,10 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
        hints.ai_flags = AI_PASSIVE;
 
        gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0);
-       if (gai)
-               die("getaddrinfo() failed: %s", gai_strerror(gai));
+       if (gai) {
+               logerror("getaddrinfo() for %s failed: %s", listen_addr, gai_strerror(gai));
+               return 0;
+       }
 
        for (ai = ai0; ai; ai = ai->ai_next) {
                int sockfd;
@@ -795,8 +797,9 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
                if (flags >= 0)
                        fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
 
-               socklist = xrealloc(socklist, sizeof(int) * (socknum + 1));
-               socklist[socknum++] = sockfd;
+               ALLOC_GROW(socklist->list, socklist->nr + 1, socklist->alloc);
+               socklist->list[socklist->nr++] = sockfd;
+               socknum++;
 
                if (maxfd < sockfd)
                        maxfd = sockfd;
@@ -804,13 +807,12 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
 
        freeaddrinfo(ai0);
 
-       *socklist_p = socklist;
        return socknum;
 }
 
 #else /* NO_IPV6 */
 
-static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
+static int setup_named_sock(char *listen_addr, int listen_port, struct socketlist *socklist)
 {
        struct sockaddr_in sin;
        int sockfd;
@@ -851,22 +853,39 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
        if (flags >= 0)
                fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
 
-       *socklist_p = xmalloc(sizeof(int));
-       **socklist_p = sockfd;
+       ALLOC_GROW(socklist->list, socklist->nr + 1, socklist->alloc);
+       socklist->list[socklist->nr++] = sockfd;
        return 1;
 }
 
 #endif
 
-static int service_loop(int socknum, int *socklist)
+static void socksetup(struct string_list *listen_addr, int listen_port, struct socketlist *socklist)
+{
+       if (!listen_addr->nr)
+               setup_named_sock(NULL, listen_port, socklist);
+       else {
+               int i, socknum;
+               for (i = 0; i < listen_addr->nr; i++) {
+                       socknum = setup_named_sock(listen_addr->items[i].string,
+                                                  listen_port, socklist);
+
+                       if (socknum == 0)
+                               logerror("unable to allocate any listen sockets for host %s on port %u",
+                                        listen_addr->items[i].string, listen_port);
+               }
+       }
+}
+
+static int service_loop(struct socketlist *socklist)
 {
        struct pollfd *pfd;
        int i;
 
-       pfd = xcalloc(socknum, sizeof(struct pollfd));
+       pfd = xcalloc(socklist->nr, sizeof(struct pollfd));
 
-       for (i = 0; i < socknum; i++) {
-               pfd[i].fd = socklist[i];
+       for (i = 0; i < socklist->nr; i++) {
+               pfd[i].fd = socklist->list[i];
                pfd[i].events = POLLIN;
        }
 
@@ -877,7 +896,7 @@ static int service_loop(int socknum, int *socklist)
 
                check_dead_children();
 
-               if (poll(pfd, socknum, -1) < 0) {
+               if (poll(pfd, socklist->nr, -1) < 0) {
                        if (errno != EINTR) {
                                logerror("Poll failed, resuming: %s",
                                      strerror(errno));
@@ -886,11 +905,17 @@ static int service_loop(int socknum, int *socklist)
                        continue;
                }
 
-               for (i = 0; i < socknum; i++) {
+               for (i = 0; i < socklist->nr; i++) {
                        if (pfd[i].revents & POLLIN) {
-                               struct sockaddr_storage ss;
-                               unsigned int sslen = sizeof(ss);
-                               int incoming = accept(pfd[i].fd, (struct sockaddr *)&ss, &sslen);
+                               union {
+                                       struct sockaddr sa;
+                                       struct sockaddr_in sai;
+#ifndef NO_IPV6
+                                       struct sockaddr_in6 sai6;
+#endif
+                               } ss;
+                               socklen_t sslen = sizeof(ss);
+                               int incoming = accept(pfd[i].fd, &ss.sa, &sslen);
                                if (incoming < 0) {
                                        switch (errno) {
                                        case EAGAIN:
@@ -901,7 +926,7 @@ static int service_loop(int socknum, int *socklist)
                                                die_errno("accept returned");
                                        }
                                }
-                               handle(incoming, (struct sockaddr *)&ss, sslen);
+                               handle(incoming, &ss.sa, sslen);
                        }
                }
        }
@@ -919,6 +944,62 @@ static void sanitize_stdfds(void)
                close(fd);
 }
 
+#ifdef NO_POSIX_GOODIES
+
+struct credentials;
+
+static void drop_privileges(struct credentials *cred)
+{
+       /* nothing */
+}
+
+static void daemonize(void)
+{
+       die("--detach not supported on this platform");
+}
+
+static struct credentials *prepare_credentials(const char *user_name,
+    const char *group_name)
+{
+       die("--user not supported on this platform");
+}
+
+#else
+
+struct credentials {
+       struct passwd *pass;
+       gid_t gid;
+};
+
+static void drop_privileges(struct credentials *cred)
+{
+       if (cred && (initgroups(cred->pass->pw_name, cred->gid) ||
+           setgid (cred->gid) || setuid(cred->pass->pw_uid)))
+               die("cannot drop privileges");
+}
+
+static struct credentials *prepare_credentials(const char *user_name,
+    const char *group_name)
+{
+       static struct credentials c;
+
+       c.pass = getpwnam(user_name);
+       if (!c.pass)
+               die("user not found - %s", user_name);
+
+       if (!group_name)
+               c.gid = c.pass->pw_gid;
+       else {
+               struct group *group = getgrnam(group_name);
+               if (!group)
+                       die("group not found - %s", group_name);
+
+               c.gid = group->gr_gid;
+       }
+
+       return &c;
+}
+
 static void daemonize(void)
 {
        switch (fork()) {
@@ -936,6 +1017,7 @@ static void daemonize(void)
        close(2);
        sanitize_stdfds();
 }
+#endif
 
 static void store_pid(const char *path)
 {
@@ -946,33 +1028,29 @@ static void store_pid(const char *path)
                die_errno("failed to write pid file '%s'", path);
 }
 
-static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
+static int serve(struct string_list *listen_addr, int listen_port,
+    struct credentials *cred)
 {
-       int socknum, *socklist;
+       struct socketlist socklist = { NULL, 0, 0 };
 
-       socknum = socksetup(listen_addr, listen_port, &socklist);
-       if (socknum == 0)
-               die("unable to allocate any listen sockets on host %s port %u",
-                   listen_addr, listen_port);
+       socksetup(listen_addr, listen_port, &socklist);
+       if (socklist.nr == 0)
+               die("unable to allocate any listen sockets on port %u",
+                   listen_port);
 
-       if (pass && gid &&
-           (initgroups(pass->pw_name, gid) || setgid (gid) ||
-            setuid(pass->pw_uid)))
-               die("cannot drop privileges");
+       drop_privileges(cred);
 
-       return service_loop(socknum, socklist);
+       return service_loop(&socklist);
 }
 
 int main(int argc, char **argv)
 {
        int listen_port = 0;
-       char *listen_addr = NULL;
-       int inetd_mode = 0;
+       struct string_list listen_addr = STRING_LIST_INIT_NODUP;
+       int serve_mode = 0, inetd_mode = 0;
        const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
        int detach = 0;
-       struct passwd *pass = NULL;
-       struct group *group;
-       gid_t gid = 0;
+       struct credentials *cred = NULL;
        int i;
 
        git_extract_argv0_path(argv[0]);
@@ -981,7 +1059,7 @@ int main(int argc, char **argv)
                char *arg = argv[i];
 
                if (!prefixcmp(arg, "--listen=")) {
-                       listen_addr = xstrdup_tolower(arg + 9);
+                       string_list_append(&listen_addr, xstrdup_tolower(arg + 9));
                        continue;
                }
                if (!prefixcmp(arg, "--port=")) {
@@ -993,6 +1071,10 @@ int main(int argc, char **argv)
                                continue;
                        }
                }
+               if (!strcmp(arg, "--serve")) {
+                       serve_mode = 1;
+                       continue;
+               }
                if (!strcmp(arg, "--inetd")) {
                        inetd_mode = 1;
                        log_syslog = 1;
@@ -1101,12 +1183,12 @@ int main(int argc, char **argv)
                set_die_routine(daemon_die);
        } else
                /* avoid splitting a message in the middle */
-               setvbuf(stderr, NULL, _IOLBF, 0);
+               setvbuf(stderr, NULL, _IOFBF, 4096);
 
-       if (inetd_mode && (group_name || user_name))
-               die("--user and --group are incompatible with --inetd");
+       if (inetd_mode && (detach || group_name || user_name))
+               die("--detach, --user and --group are incompatible with --inetd");
 
-       if (inetd_mode && (listen_port || listen_addr))
+       if (inetd_mode && (listen_port || (listen_addr.nr > 0)))
                die("--listen= and --port= are incompatible with --inetd");
        else if (listen_port == 0)
                listen_port = DEFAULT_GIT_PORT;
@@ -1114,21 +1196,8 @@ int main(int argc, char **argv)
        if (group_name && !user_name)
                die("--group supplied without --user");
 
-       if (user_name) {
-               pass = getpwnam(user_name);
-               if (!pass)
-                       die("user not found - %s", user_name);
-
-               if (!group_name)
-                       gid = pass->pw_gid;
-               else {
-                       group = getgrnam(group_name);
-                       if (!group)
-                               die("group not found - %s", group_name);
-
-                       gid = group->gr_gid;
-               }
-       }
+       if (user_name)
+               cred = prepare_credentials(user_name, group_name);
 
        if (strict_paths && (!ok_paths || !*ok_paths))
                die("option --strict-paths requires a whitelist");
@@ -1138,19 +1207,13 @@ int main(int argc, char **argv)
                    base_path);
 
        if (inetd_mode) {
-               struct sockaddr_storage ss;
-               struct sockaddr *peer = (struct sockaddr *)&ss;
-               socklen_t slen = sizeof(ss);
-
                if (!freopen("/dev/null", "w", stderr))
                        die_errno("failed to redirect stderr to /dev/null");
-
-               if (getpeername(0, peer, &slen))
-                       peer = NULL;
-
-               return execute(peer);
        }
 
+       if (inetd_mode || serve_mode)
+               return execute();
+
        if (detach) {
                daemonize();
                loginfo("Ready to rumble");
@@ -1161,5 +1224,13 @@ int main(int argc, char **argv)
        if (pid_file)
                store_pid(pid_file);
 
-       return serve(listen_addr, listen_port, pass, gid);
+       /* prepare argv for serving-processes */
+       cld_argv = xmalloc(sizeof (char *) * (argc + 2));
+       cld_argv[0] = argv[0];  /* git-daemon */
+       cld_argv[1] = "--serve";
+       for (i = 1; i < argc; ++i)
+               cld_argv[i+1] = argv[i];
+       cld_argv[argc+1] = NULL;
+
+       return serve(&listen_addr, listen_port, cred);
 }
index 464ac3ffc0a45e95637e2cecdf97b3d39e5c7933..93385e12baa0d90ae475bd02500edf5ddcce320c 100644 (file)
@@ -146,7 +146,14 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
        /* Determine index hash size.  Note that indexing skips the
           first byte to allow for optimizing the Rabin's polynomial
           initialization in create_delta(). */
-       entries = (bufsize - 1)  / RABIN_WINDOW;
+       entries = (bufsize - 1) / RABIN_WINDOW;
+       if (bufsize >= 0xffffffffUL) {
+               /*
+                * Current delta format can't encode offsets into
+                * reference buffer with more than 32 bits.
+                */
+               entries = 0xfffffffeU / RABIN_WINDOW;
+       }
        hsize = entries / 4;
        for (i = 4; (1u << i) < hsize && i < 31; i++);
        hsize = 1 << i;
index 392ce2bef05746cea7922d39da67bf25d1d3d192..1e22992cb10420b9dd6def16f80efc5f196ffbbb 100644 (file)
@@ -106,7 +106,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
                        break;
 
-               if (!ce_path_match(ce, revs->prune_data))
+               if (!ce_path_match(ce, &revs->prune_data))
                        continue;
 
                if (ce_stage(ce)) {
@@ -427,7 +427,7 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
        if (tree == o->df_conflict_entry)
                tree = NULL;
 
-       if (ce_path_match(idx ? idx : tree, revs->prune_data))
+       if (ce_path_match(idx ? idx : tree, &revs->prune_data))
                do_oneway_diff(o, idx, tree);
 
        return 0;
@@ -501,7 +501,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
        active_nr = dst - active_cache;
 
        init_revisions(&revs, NULL);
-       revs.prune_data = opt->paths;
+       init_pathspec(&revs.prune_data, opt->pathspec.raw);
        tree = parse_tree_indirect(tree_sha1);
        if (!tree)
                die("bad tree object %s", sha1_to_hex(tree_sha1));
index ce9e783407437bb1e0efc6d5bc2392af26da5a41..3a36144687ae2f5bf7bb3afc914ddbada8d5ff93 100644 (file)
@@ -231,8 +231,9 @@ void diff_no_index(struct rev_info *revs,
 
        if (prefix) {
                int len = strlen(prefix);
+               const char *paths[3];
+               memset(paths, 0, sizeof(paths));
 
-               revs->diffopt.paths = xcalloc(2, sizeof(char *));
                for (i = 0; i < 2; i++) {
                        const char *p = argv[argc - 2 + i];
                        /*
@@ -242,12 +243,12 @@ void diff_no_index(struct rev_info *revs,
                        p = (strcmp(p, "-")
                             ? xstrdup(prefix_filename(prefix, len, p))
                             : p);
-                       revs->diffopt.paths[i] = p;
+                       paths[i] = p;
                }
+               diff_tree_setup_paths(paths, &revs->diffopt);
        }
        else
-               revs->diffopt.paths = argv + argc - 2;
-       revs->diffopt.nr_paths = 2;
+               diff_tree_setup_paths(argv + argc - 2, &revs->diffopt);
        revs->diffopt.skip_stat_unmatch = 1;
        if (!revs->diffopt.output_format)
                revs->diffopt.output_format = DIFF_FORMAT_PATCH;
@@ -259,8 +260,8 @@ void diff_no_index(struct rev_info *revs,
        if (diff_setup_done(&revs->diffopt) < 0)
                die("diff_setup_done failed");
 
-       if (queue_diff(&revs->diffopt, revs->diffopt.paths[0],
-                      revs->diffopt.paths[1]))
+       if (queue_diff(&revs->diffopt, revs->diffopt.pathspec.raw[0],
+                      revs->diffopt.pathspec.raw[1]))
                exit(1);
        diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
        diffcore_std(&revs->diffopt);
diff --git a/diff.c b/diff.c
index 7a75bd74c9ab9c98da12f5511ff321645cfa883b..5422c438826254f36d1e00af0e8b882690661276 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -919,7 +919,10 @@ static void free_diff_words_data(struct emit_callback *ecbdata)
                free (ecbdata->diff_words->minus.orig);
                free (ecbdata->diff_words->plus.text.ptr);
                free (ecbdata->diff_words->plus.orig);
-               free(ecbdata->diff_words->word_regex);
+               if (ecbdata->diff_words->word_regex) {
+                       regfree(ecbdata->diff_words->word_regex);
+                       free(ecbdata->diff_words->word_regex);
+               }
                free(ecbdata->diff_words);
                ecbdata->diff_words = NULL;
        }
@@ -1768,8 +1771,14 @@ static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two, char *pre
 
 static void diff_filespec_load_driver(struct diff_filespec *one)
 {
-       if (!one->driver)
+       /* Use already-loaded driver */
+       if (one->driver)
+               return;
+
+       if (S_ISREG(one->mode))
                one->driver = userdiff_find_by_path(one->path);
+
+       /* Fallback to default settings */
        if (!one->driver)
                one->driver = userdiff_find_by_name("default");
 }
@@ -1817,8 +1826,7 @@ struct userdiff_driver *get_textconv(struct diff_filespec *one)
 {
        if (!DIFF_FILE_VALID(one))
                return NULL;
-       if (!S_ISREG(one->mode))
-               return NULL;
+
        diff_filespec_load_driver(one);
        if (!one->driver->textconv)
                return NULL;
@@ -2150,7 +2158,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
 
                        ecbdata.ws_rule = data.ws_rule;
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
-                       blank_at_eof = ecbdata.blank_at_eof_in_preimage;
+                       blank_at_eof = ecbdata.blank_at_eof_in_postimage;
 
                        if (blank_at_eof) {
                                static char *err;
@@ -2383,10 +2391,14 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
        }
        else {
                enum object_type type;
-               if (size_only)
+               if (size_only) {
                        type = sha1_object_info(s->sha1, &s->size);
-               else {
+                       if (type < 0)
+                               die("unable to read %s", sha1_to_hex(s->sha1));
+               } else {
                        s->data = read_sha1_file(s->sha1, &type, &s->size);
+                       if (!s->data)
+                               die("unable to read %s", sha1_to_hex(s->sha1));
                        s->should_free = 1;
                }
        }
@@ -3002,9 +3014,100 @@ static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *va
 
 static int diff_scoreopt_parse(const char *opt);
 
+static inline int short_opt(char opt, const char **argv,
+                           const char **optarg)
+{
+       const char *arg = argv[0];
+       if (arg[0] != '-' || arg[1] != opt)
+               return 0;
+       if (arg[2] != '\0') {
+               *optarg = arg + 2;
+               return 1;
+       }
+       if (!argv[1])
+               die("Option '%c' requires a value", opt);
+       *optarg = argv[1];
+       return 2;
+}
+
+int parse_long_opt(const char *opt, const char **argv,
+                  const char **optarg)
+{
+       const char *arg = argv[0];
+       if (arg[0] != '-' || arg[1] != '-')
+               return 0;
+       arg += strlen("--");
+       if (prefixcmp(arg, opt))
+               return 0;
+       arg += strlen(opt);
+       if (*arg == '=') { /* sticked form: --option=value */
+               *optarg = arg + 1;
+               return 1;
+       }
+       if (*arg != '\0')
+               return 0;
+       /* separate form: --option value */
+       if (!argv[1])
+               die("Option '--%s' requires a value", opt);
+       *optarg = argv[1];
+       return 2;
+}
+
+static int stat_opt(struct diff_options *options, const char **av)
+{
+       const char *arg = av[0];
+       char *end;
+       int width = options->stat_width;
+       int name_width = options->stat_name_width;
+       int argcount = 1;
+
+       arg += strlen("--stat");
+       end = (char *)arg;
+
+       switch (*arg) {
+       case '-':
+               if (!prefixcmp(arg, "-width")) {
+                       arg += strlen("-width");
+                       if (*arg == '=')
+                               width = strtoul(arg + 1, &end, 10);
+                       else if (!*arg && !av[1])
+                               die("Option '--stat-width' requires a value");
+                       else if (!*arg) {
+                               width = strtoul(av[1], &end, 10);
+                               argcount = 2;
+                       }
+               } else if (!prefixcmp(arg, "-name-width")) {
+                       arg += strlen("-name-width");
+                       if (*arg == '=')
+                               name_width = strtoul(arg + 1, &end, 10);
+                       else if (!*arg && !av[1])
+                               die("Option '--stat-name-width' requires a value");
+                       else if (!*arg) {
+                               name_width = strtoul(av[1], &end, 10);
+                               argcount = 2;
+                       }
+               }
+               break;
+       case '=':
+               width = strtoul(arg+1, &end, 10);
+               if (*end == ',')
+                       name_width = strtoul(end+1, &end, 10);
+       }
+
+       /* Important! This checks all the error cases! */
+       if (*end)
+               return 0;
+       options->output_format |= DIFF_FORMAT_DIFFSTAT;
+       options->stat_name_width = name_width;
+       options->stat_width = width;
+       return argcount;
+}
+
 int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 {
        const char *arg = av[0];
+       const char *optarg;
+       int argcount;
 
        /* Output format options */
        if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch"))
@@ -3041,49 +3144,28 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_NAME_STATUS;
        else if (!strcmp(arg, "-s"))
                options->output_format |= DIFF_FORMAT_NO_OUTPUT;
-       else if (!prefixcmp(arg, "--stat")) {
-               char *end;
-               int width = options->stat_width;
-               int name_width = options->stat_name_width;
-               arg += 6;
-               end = (char *)arg;
-
-               switch (*arg) {
-               case '-':
-                       if (!prefixcmp(arg, "-width="))
-                               width = strtoul(arg + 7, &end, 10);
-                       else if (!prefixcmp(arg, "-name-width="))
-                               name_width = strtoul(arg + 12, &end, 10);
-                       break;
-               case '=':
-                       width = strtoul(arg+1, &end, 10);
-                       if (*end == ',')
-                               name_width = strtoul(end+1, &end, 10);
-               }
-
-               /* Important! This checks all the error cases! */
-               if (*end)
-                       return 0;
-               options->output_format |= DIFF_FORMAT_DIFFSTAT;
-               options->stat_name_width = name_width;
-               options->stat_width = width;
-       }
+       else if (!prefixcmp(arg, "--stat"))
+               /* --stat, --stat-width, or --stat-name-width */
+               return stat_opt(options, av);
 
        /* renames options */
-       else if (!prefixcmp(arg, "-B")) {
+       else if (!prefixcmp(arg, "-B") || !prefixcmp(arg, "--break-rewrites=") ||
+                !strcmp(arg, "--break-rewrites")) {
                if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
-                       return -1;
+                       return error("invalid argument to -B: %s", arg+2);
        }
-       else if (!prefixcmp(arg, "-M")) {
+       else if (!prefixcmp(arg, "-M") || !prefixcmp(arg, "--find-renames=") ||
+                !strcmp(arg, "--find-renames")) {
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
-                       return -1;
+                       return error("invalid argument to -M: %s", arg+2);
                options->detect_rename = DIFF_DETECT_RENAME;
        }
-       else if (!prefixcmp(arg, "-C")) {
+       else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--find-copies=") ||
+                !strcmp(arg, "--find-copies")) {
                if (options->detect_rename == DIFF_DETECT_COPY)
                        DIFF_OPT_SET(options, FIND_COPIES_HARDER);
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
-                       return -1;
+                       return error("invalid argument to -C: %s", arg+2);
                options->detect_rename = DIFF_DETECT_COPY;
        }
        else if (!strcmp(arg, "--no-renames"))
@@ -3161,10 +3243,11 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                else
                        die("bad --word-diff argument: %s", type);
        }
-       else if (!prefixcmp(arg, "--word-diff-regex=")) {
+       else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {
                if (options->word_diff == DIFF_WORDS_NONE)
                        options->word_diff = DIFF_WORDS_PLAIN;
-               options->word_regex = arg + 18;
+               options->word_regex = optarg;
+               return argcount;
        }
        else if (!strcmp(arg, "--exit-code"))
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
@@ -3194,18 +3277,31 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        /* misc options */
        else if (!strcmp(arg, "-z"))
                options->line_termination = 0;
-       else if (!prefixcmp(arg, "-l"))
-               options->rename_limit = strtoul(arg+2, NULL, 10);
-       else if (!prefixcmp(arg, "-S"))
-               options->pickaxe = arg + 2;
+       else if ((argcount = short_opt('l', av, &optarg))) {
+               options->rename_limit = strtoul(optarg, NULL, 10);
+               return argcount;
+       }
+       else if ((argcount = short_opt('S', av, &optarg))) {
+               options->pickaxe = optarg;
+               options->pickaxe_opts |= DIFF_PICKAXE_KIND_S;
+               return argcount;
+       } else if ((argcount = short_opt('G', av, &optarg))) {
+               options->pickaxe = optarg;
+               options->pickaxe_opts |= DIFF_PICKAXE_KIND_G;
+               return argcount;
+       }
        else if (!strcmp(arg, "--pickaxe-all"))
-               options->pickaxe_opts = DIFF_PICKAXE_ALL;
+               options->pickaxe_opts |= DIFF_PICKAXE_ALL;
        else if (!strcmp(arg, "--pickaxe-regex"))
-               options->pickaxe_opts = DIFF_PICKAXE_REGEX;
-       else if (!prefixcmp(arg, "-O"))
-               options->orderfile = arg + 2;
-       else if (!prefixcmp(arg, "--diff-filter="))
-               options->filter = arg + 14;
+               options->pickaxe_opts |= DIFF_PICKAXE_REGEX;
+       else if ((argcount = short_opt('O', av, &optarg))) {
+               options->orderfile = optarg;
+               return argcount;
+       }
+       else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
+               options->filter = optarg;
+               return argcount;
+       }
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
        else if (!prefixcmp(arg, "--abbrev=")) {
@@ -3215,26 +3311,31 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                else if (40 < options->abbrev)
                        options->abbrev = 40;
        }
-       else if (!prefixcmp(arg, "--src-prefix="))
-               options->a_prefix = arg + 13;
-       else if (!prefixcmp(arg, "--dst-prefix="))
-               options->b_prefix = arg + 13;
+       else if ((argcount = parse_long_opt("src-prefix", av, &optarg))) {
+               options->a_prefix = optarg;
+               return argcount;
+       }
+       else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) {
+               options->b_prefix = optarg;
+               return argcount;
+       }
        else if (!strcmp(arg, "--no-prefix"))
                options->a_prefix = options->b_prefix = "";
        else if (opt_arg(arg, '\0', "inter-hunk-context",
                         &options->interhunkcontext))
                ;
-       else if (!prefixcmp(arg, "--output=")) {
-               options->file = fopen(arg + strlen("--output="), "w");
+       else if ((argcount = parse_long_opt("output", av, &optarg))) {
+               options->file = fopen(optarg, "w");
                if (!options->file)
-                       die_errno("Could not open '%s'", arg + strlen("--output="));
+                       die_errno("Could not open '%s'", optarg);
                options->close_file = 1;
+               return argcount;
        } else
                return 0;
        return 1;
 }
 
-static int parse_num(const char **cp_p)
+int parse_rename_score(const char **cp_p)
 {
        unsigned long num, scale;
        int ch, dot;
@@ -3277,10 +3378,26 @@ static int diff_scoreopt_parse(const char *opt)
        if (*opt++ != '-')
                return -1;
        cmd = *opt++;
+       if (cmd == '-') {
+               /* convert the long-form arguments into short-form versions */
+               if (!prefixcmp(opt, "break-rewrites")) {
+                       opt += strlen("break-rewrites");
+                       if (*opt == 0 || *opt++ == '=')
+                               cmd = 'B';
+               } else if (!prefixcmp(opt, "find-copies")) {
+                       opt += strlen("find-copies");
+                       if (*opt == 0 || *opt++ == '=')
+                               cmd = 'C';
+               } else if (!prefixcmp(opt, "find-renames")) {
+                       opt += strlen("find-renames");
+                       if (*opt == 0 || *opt++ == '=')
+                               cmd = 'M';
+               }
+       }
        if (cmd != 'M' && cmd != 'C' && cmd != 'B')
                return -1; /* that is not a -M, -C nor -B option */
 
-       opt1 = parse_num(&opt);
+       opt1 = parse_rename_score(&opt);
        if (cmd != 'B')
                opt2 = 0;
        else {
@@ -3290,7 +3407,7 @@ static int diff_scoreopt_parse(const char *opt)
                        return -1; /* we expect -B80/99 or -B80 */
                else {
                        opt++;
-                       opt2 = parse_num(&opt);
+                       opt2 = parse_rename_score(&opt);
                }
        }
        if (*opt != 0)
@@ -3443,7 +3560,7 @@ static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
 
        if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
            (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
-               return; /* no tree diffs in patch format */
+               return; /* no useful stat for tree diffs */
 
        run_diffstat(p, o, diffstat);
 }
@@ -3456,7 +3573,7 @@ static void diff_flush_checkdiff(struct diff_filepair *p,
 
        if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
            (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
-               return; /* no tree diffs in patch format */
+               return; /* nothing to check in tree diffs */
 
        run_checkdiff(p, o);
 }
@@ -3772,9 +3889,16 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
                                        len2, p->two->path);
                git_SHA1_Update(&ctx, buffer, len1);
 
+               if (diff_filespec_is_binary(p->one) ||
+                   diff_filespec_is_binary(p->two)) {
+                       git_SHA1_Update(&ctx, sha1_to_hex(p->one->sha1), 40);
+                       git_SHA1_Update(&ctx, sha1_to_hex(p->two->sha1), 40);
+                       continue;
+               }
+
                xpp.flags = 0;
                xecfg.ctxlen = 3;
-               xecfg.flags = XDL_EMIT_FUNCNAMES;
+               xecfg.flags = 0;
                xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
                              &xpp, &xecfg);
        }
@@ -4085,7 +4209,7 @@ void diffcore_std(struct diff_options *options)
                        diffcore_merge_broken();
        }
        if (options->pickaxe)
-               diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
+               diffcore_pickaxe(options);
        if (options->orderfile)
                diffcore_order(options->orderfile);
        if (!options->found_follow)
@@ -4288,7 +4412,7 @@ size_t fill_textconv(struct userdiff_driver *driver,
                return df->size;
        }
 
-       if (driver->textconv_cache) {
+       if (driver->textconv_cache && df->sha1_valid) {
                *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
                                          &size);
                if (*outbuf)
@@ -4299,7 +4423,7 @@ size_t fill_textconv(struct userdiff_driver *driver,
        if (!*outbuf)
                die("unable to read files to diff");
 
-       if (driver->textconv_cache) {
+       if (driver->textconv_cache && df->sha1_valid) {
                /* ignore errors, as we might be in a readonly repository */
                notes_cache_put(driver->textconv_cache, df->sha1, *outbuf,
                                size);
diff --git a/diff.h b/diff.h
index d43da9da95fae4dd13d3abbe738948708cf90939..310bd6b2832ce7f874aff735bb19b4813f117d4d 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -133,9 +133,7 @@ struct diff_options {
        FILE *file;
        int close_file;
 
-       int nr_paths;
-       const char **paths;
-       int *pathlens;
+       struct pathspec pathspec;
        change_fn_t change;
        add_remove_fn_t add_remove;
        diff_format_fn_t format_callback;
@@ -218,6 +216,13 @@ extern void diff_unmerge(struct diff_options *,
 #define DIFF_SETUP_USE_CACHE           2
 #define DIFF_SETUP_USE_SIZE_CACHE      4
 
+/*
+ * Poor man's alternative to parse-option, to allow both sticked form
+ * (--option=value) and separate form (--option value).
+ */
+extern int parse_long_opt(const char *opt, const char **argv,
+                        const char **optarg);
+
 extern int git_diff_basic_config(const char *var, const char *value, void *cb);
 extern int git_diff_ui_config(const char *var, const char *value, void *cb);
 extern int diff_use_color_default;
@@ -231,6 +236,9 @@ extern int diff_setup_done(struct diff_options *);
 #define DIFF_PICKAXE_ALL       1
 #define DIFF_PICKAXE_REGEX     2
 
+#define DIFF_PICKAXE_KIND_S    4 /* traditional plumbing counter */
+#define DIFF_PICKAXE_KIND_G    8 /* grep in the patch */
+
 extern void diffcore_std(struct diff_options *);
 extern void diffcore_fix_diff_index(struct diff_options *);
 
@@ -305,4 +313,6 @@ extern size_t fill_textconv(struct userdiff_driver *driver,
 
 extern struct userdiff_driver *get_textconv(struct diff_filespec *one);
 
+extern int parse_rename_score(const char **cp_p);
+
 #endif /* DIFF_H */
index 929de15aa9228099b3a772be788d746e440a9e8c..ea03b9107eb2e85dd4a4c5ad93ac08dfe7e1c9f0 100644 (file)
@@ -1,9 +1,148 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
+ * Copyright (C) 2010 Google Inc.
  */
 #include "cache.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "xdiff-interface.h"
+
+struct diffgrep_cb {
+       regex_t *regexp;
+       int hit;
+};
+
+static void diffgrep_consume(void *priv, char *line, unsigned long len)
+{
+       struct diffgrep_cb *data = priv;
+       regmatch_t regmatch;
+       int hold;
+
+       if (line[0] != '+' && line[0] != '-')
+               return;
+       if (data->hit)
+               /*
+                * NEEDSWORK: we should have a way to terminate the
+                * caller early.
+                */
+               return;
+       /* Yuck -- line ought to be "const char *"! */
+       hold = line[len];
+       line[len] = '\0';
+       data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
+       line[len] = hold;
+}
+
+static void fill_one(struct diff_filespec *one,
+                    mmfile_t *mf, struct userdiff_driver **textconv)
+{
+       if (DIFF_FILE_VALID(one)) {
+               *textconv = get_textconv(one);
+               mf->size = fill_textconv(*textconv, one, &mf->ptr);
+       } else {
+               memset(mf, 0, sizeof(*mf));
+       }
+}
+
+static int diff_grep(struct diff_filepair *p, regex_t *regexp, struct diff_options *o)
+{
+       regmatch_t regmatch;
+       struct userdiff_driver *textconv_one = NULL;
+       struct userdiff_driver *textconv_two = NULL;
+       mmfile_t mf1, mf2;
+       int hit;
+
+       if (diff_unmodified_pair(p))
+               return 0;
+
+       fill_one(p->one, &mf1, &textconv_one);
+       fill_one(p->two, &mf2, &textconv_two);
+
+       if (!mf1.ptr) {
+               if (!mf2.ptr)
+                       return 0; /* ignore unmerged */
+               /* created "two" -- does it have what we are looking for? */
+               hit = !regexec(regexp, p->two->data, 1, &regmatch, 0);
+       } else if (!mf2.ptr) {
+               /* removed "one" -- did it have what we are looking for? */
+               hit = !regexec(regexp, p->one->data, 1, &regmatch, 0);
+       } else {
+               /*
+                * We have both sides; need to run textual diff and see if
+                * the pattern appears on added/deleted lines.
+                */
+               struct diffgrep_cb ecbdata;
+               xpparam_t xpp;
+               xdemitconf_t xecfg;
+
+               memset(&xpp, 0, sizeof(xpp));
+               memset(&xecfg, 0, sizeof(xecfg));
+               ecbdata.regexp = regexp;
+               ecbdata.hit = 0;
+               xecfg.ctxlen = o->context;
+               xecfg.interhunkctxlen = o->interhunkcontext;
+               xdi_diff_outf(&mf1, &mf2, diffgrep_consume, &ecbdata,
+                             &xpp, &xecfg);
+               hit = ecbdata.hit;
+       }
+       if (textconv_one)
+               free(mf1.ptr);
+       if (textconv_two)
+               free(mf2.ptr);
+       return hit;
+}
+
+static void diffcore_pickaxe_grep(struct diff_options *o)
+{
+       struct diff_queue_struct *q = &diff_queued_diff;
+       int i, has_changes, err;
+       regex_t regex;
+       struct diff_queue_struct outq;
+       outq.queue = NULL;
+       outq.nr = outq.alloc = 0;
+
+       err = regcomp(&regex, o->pickaxe, REG_EXTENDED | REG_NEWLINE);
+       if (err) {
+               char errbuf[1024];
+               regerror(err, &regex, errbuf, 1024);
+               regfree(&regex);
+               die("invalid log-grep regex: %s", errbuf);
+       }
+
+       if (o->pickaxe_opts & DIFF_PICKAXE_ALL) {
+               /* Showing the whole changeset if needle exists */
+               for (i = has_changes = 0; !has_changes && i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       if (diff_grep(p, &regex, o))
+                               has_changes++;
+               }
+               if (has_changes)
+                       return; /* do not munge the queue */
+
+               /*
+                * Otherwise we will clear the whole queue by copying
+                * the empty outq at the end of this function, but
+                * first clear the current entries in the queue.
+                */
+               for (i = 0; i < q->nr; i++)
+                       diff_free_filepair(q->queue[i]);
+       } else {
+               /* Showing only the filepairs that has the needle */
+               for (i = 0; i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       if (diff_grep(p, &regex, o))
+                               diff_q(&outq, p);
+                       else
+                               diff_free_filepair(p);
+               }
+       }
+
+       regfree(&regex);
+
+       free(q->queue);
+       *q = outq;
+       return;
+}
 
 static unsigned int contains(struct diff_filespec *one,
                             const char *needle, unsigned long len,
@@ -48,8 +187,10 @@ static unsigned int contains(struct diff_filespec *one,
        return cnt;
 }
 
-void diffcore_pickaxe(const char *needle, int opts)
+static void diffcore_pickaxe_count(struct diff_options *o)
 {
+       const char *needle = o->pickaxe;
+       int opts = o->pickaxe_opts;
        struct diff_queue_struct *q = &diff_queued_diff;
        unsigned long len = strlen(needle);
        int i, has_changes;
@@ -128,11 +269,19 @@ void diffcore_pickaxe(const char *needle, int opts)
                                diff_free_filepair(p);
                }
 
-       if (opts & DIFF_PICKAXE_REGEX) {
+       if (opts & DIFF_PICKAXE_REGEX)
                regfree(&regex);
-       }
 
        free(q->queue);
        *q = outq;
        return;
 }
+
+void diffcore_pickaxe(struct diff_options *o)
+{
+       /* Might want to warn when both S and G are on; I don't care... */
+       if (o->pickaxe_opts & DIFF_PICKAXE_KIND_G)
+               diffcore_pickaxe_grep(o);
+       else
+               diffcore_pickaxe_count(o);
+}
index 8b3241ad137f5934e32336cd1caf8d99ca11d1f5..b8f1fdecf4d9e5e8f8e834eb55a374f1af8352fd 100644 (file)
@@ -107,7 +107,7 @@ extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);
 extern void diffcore_break(int);
 extern void diffcore_rename(struct diff_options *);
 extern void diffcore_merge_broken(void);
-extern void diffcore_pickaxe(const char *needle, int opts);
+extern void diffcore_pickaxe(struct diff_options *);
 extern void diffcore_order(const char *orderfile);
 
 #define DIFF_DEBUG 0
diff --git a/dir.c b/dir.c
index 133f472a1e73786e781c1021eea17e543858937f..168dad615230d77d7719101b76b50b4f6fe02777 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -18,6 +18,22 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, in
        int check_only, const struct path_simplify *simplify);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
+/* helper string functions with support for the ignore_case flag */
+int strcmp_icase(const char *a, const char *b)
+{
+       return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
+}
+
+int strncmp_icase(const char *a, const char *b, size_t count)
+{
+       return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
+}
+
+int fnmatch_icase(const char *pattern, const char *string, int flags)
+{
+       return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));
+}
+
 static int common_prefix(const char **pathspec)
 {
        const char *path, *slash, *next;
@@ -71,6 +87,21 @@ int fill_directory(struct dir_struct *dir, const char **pathspec)
        return len;
 }
 
+int within_depth(const char *name, int namelen,
+                       int depth, int max_depth)
+{
+       const char *cp = name, *cpe = name + namelen;
+
+       while (cp < cpe) {
+               if (*cp++ != '/')
+                       continue;
+               depth++;
+               if (depth > max_depth)
+                       return 0;
+       }
+       return 1;
+}
+
 /*
  * Does 'match' match the given name?
  * A match is found if
@@ -91,16 +122,30 @@ static int match_one(const char *match, const char *name, int namelen)
        if (!*match)
                return MATCHED_RECURSIVELY;
 
-       for (;;) {
-               unsigned char c1 = *match;
-               unsigned char c2 = *name;
-               if (c1 == '\0' || is_glob_special(c1))
-                       break;
-               if (c1 != c2)
-                       return 0;
-               match++;
-               name++;
-               namelen--;
+       if (ignore_case) {
+               for (;;) {
+                       unsigned char c1 = tolower(*match);
+                       unsigned char c2 = tolower(*name);
+                       if (c1 == '\0' || is_glob_special(c1))
+                               break;
+                       if (c1 != c2)
+                               return 0;
+                       match++;
+                       name++;
+                       namelen--;
+               }
+       } else {
+               for (;;) {
+                       unsigned char c1 = *match;
+                       unsigned char c2 = *name;
+                       if (c1 == '\0' || is_glob_special(c1))
+                               break;
+                       if (c1 != c2)
+                               return 0;
+                       match++;
+                       name++;
+                       namelen--;
+               }
        }
 
 
@@ -109,8 +154,8 @@ static int match_one(const char *match, const char *name, int namelen)
         * we need to match by fnmatch
         */
        matchlen = strlen(match);
-       if (strncmp(match, name, matchlen))
-               return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
+       if (strncmp_icase(match, name, matchlen))
+               return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
 
        if (namelen == matchlen)
                return MATCHED_EXACTLY;
@@ -154,6 +199,95 @@ int match_pathspec(const char **pathspec, const char *name, int namelen,
        return retval;
 }
 
+/*
+ * Does 'match' match the given name?
+ * A match is found if
+ *
+ * (1) the 'match' string is leading directory of 'name', or
+ * (2) the 'match' string is a wildcard and matches 'name', or
+ * (3) the 'match' string is exactly the same as 'name'.
+ *
+ * and the return value tells which case it was.
+ *
+ * It returns 0 when there is no match.
+ */
+static int match_pathspec_item(const struct pathspec_item *item, int prefix,
+                              const char *name, int namelen)
+{
+       /* name/namelen has prefix cut off by caller */
+       const char *match = item->match + prefix;
+       int matchlen = item->len - prefix;
+
+       /* If the match was just the prefix, we matched */
+       if (!*match)
+               return MATCHED_RECURSIVELY;
+
+       if (matchlen <= namelen && !strncmp(match, name, matchlen)) {
+               if (matchlen == namelen)
+                       return MATCHED_EXACTLY;
+
+               if (match[matchlen-1] == '/' || name[matchlen] == '/')
+                       return MATCHED_RECURSIVELY;
+       }
+
+       if (item->has_wildcard && !fnmatch(match, name, 0))
+               return MATCHED_FNMATCH;
+
+       return 0;
+}
+
+/*
+ * Given a name and a list of pathspecs, see if the name matches
+ * any of the pathspecs.  The caller is also interested in seeing
+ * all pathspec matches some names it calls this function with
+ * (otherwise the user could have mistyped the unmatched pathspec),
+ * and a mark is left in seen[] array for pathspec element that
+ * actually matched anything.
+ */
+int match_pathspec_depth(const struct pathspec *ps,
+                        const char *name, int namelen,
+                        int prefix, char *seen)
+{
+       int i, retval = 0;
+
+       if (!ps->nr) {
+               if (!ps->recursive || ps->max_depth == -1)
+                       return MATCHED_RECURSIVELY;
+
+               if (within_depth(name, namelen, 0, ps->max_depth))
+                       return MATCHED_EXACTLY;
+               else
+                       return 0;
+       }
+
+       name += prefix;
+       namelen -= prefix;
+
+       for (i = ps->nr - 1; i >= 0; i--) {
+               int how;
+               if (seen && seen[i] == MATCHED_EXACTLY)
+                       continue;
+               how = match_pathspec_item(ps->items+i, prefix, name, namelen);
+               if (ps->recursive && ps->max_depth != -1 &&
+                   how && how != MATCHED_FNMATCH) {
+                       int len = ps->items[i].len;
+                       if (name[len] == '/')
+                               len++;
+                       if (within_depth(name+len, namelen-len, 0, ps->max_depth))
+                               how = MATCHED_EXACTLY;
+                       else
+                               how = 0;
+               }
+               if (how) {
+                       if (retval < how)
+                               retval = how;
+                       if (seen && seen[i] < how)
+                               seen[i] = how;
+               }
+       }
+       return retval;
+}
+
 static int no_wildcard(const char *string)
 {
        return string[strcspn(string, "*?[{\\")] == '\0';
@@ -223,6 +357,18 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
        return data;
 }
 
+void free_excludes(struct exclude_list *el)
+{
+       int i;
+
+       for (i = 0; i < el->nr; i++)
+               free(el->excludes[i]);
+       free(el->excludes);
+
+       el->nr = 0;
+       el->excludes = NULL;
+}
+
 int add_excludes_from_file_to_list(const char *fname,
                                   const char *base,
                                   int baselen,
@@ -232,7 +378,7 @@ int add_excludes_from_file_to_list(const char *fname,
 {
        struct stat st;
        int fd, i;
-       size_t size;
+       size_t size = 0;
        char *buf, *entry;
 
        fd = open(fname, O_RDONLY);
@@ -359,12 +505,6 @@ int excluded_from_list(const char *pathname,
                        int to_exclude = x->to_exclude;
 
                        if (x->flags & EXC_FLAG_MUSTBEDIR) {
-                               if (!dtype) {
-                                       if (!prefixcmp(pathname, exclude))
-                                               return to_exclude;
-                                       else
-                                               continue;
-                               }
                                if (*dtype == DT_UNKNOWN)
                                        *dtype = get_dtype(NULL, pathname, pathlen);
                                if (*dtype != DT_DIR)
@@ -374,14 +514,14 @@ int excluded_from_list(const char *pathname,
                        if (x->flags & EXC_FLAG_NODIR) {
                                /* match basename */
                                if (x->flags & EXC_FLAG_NOWILDCARD) {
-                                       if (!strcmp(exclude, basename))
+                                       if (!strcmp_icase(exclude, basename))
                                                return to_exclude;
                                } else if (x->flags & EXC_FLAG_ENDSWITH) {
                                        if (x->patternlen - 1 <= pathlen &&
-                                           !strcmp(exclude + 1, pathname + pathlen - x->patternlen + 1))
+                                           !strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))
                                                return to_exclude;
                                } else {
-                                       if (fnmatch(exclude, basename, 0) == 0)
+                                       if (fnmatch_icase(exclude, basename, 0) == 0)
                                                return to_exclude;
                                }
                        }
@@ -396,14 +536,14 @@ int excluded_from_list(const char *pathname,
 
                                if (pathlen < baselen ||
                                    (baselen && pathname[baselen-1] != '/') ||
-                                   strncmp(pathname, x->base, baselen))
+                                   strncmp_icase(pathname, x->base, baselen))
                                    continue;
 
                                if (x->flags & EXC_FLAG_NOWILDCARD) {
-                                       if (!strcmp(exclude, pathname + baselen))
+                                       if (!strcmp_icase(exclude, pathname + baselen))
                                                return to_exclude;
                                } else {
-                                       if (fnmatch(exclude, pathname+baselen,
+                                       if (fnmatch_icase(exclude, pathname+baselen,
                                                    FNM_PATHNAME) == 0)
                                            return to_exclude;
                                }
@@ -468,6 +608,39 @@ enum exist_status {
        index_gitdir
 };
 
+/*
+ * Do not use the alphabetically stored index to look up
+ * the directory name; instead, use the case insensitive
+ * name hash.
+ */
+static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
+{
+       struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
+       unsigned char endchar;
+
+       if (!ce)
+               return index_nonexistent;
+       endchar = ce->name[len];
+
+       /*
+        * The cache_entry structure returned will contain this dirname
+        * and possibly additional path components.
+        */
+       if (endchar == '/')
+               return index_directory;
+
+       /*
+        * If there are no additional path components, then this cache_entry
+        * represents a submodule.  Submodules, despite being directories,
+        * are stored in the cache without a closing slash.
+        */
+       if (!endchar && S_ISGITLINK(ce->ce_mode))
+               return index_gitdir;
+
+       /* This should never be hit, but it exists just in case. */
+       return index_nonexistent;
+}
+
 /*
  * The index sorts alphabetically by entry name, which
  * means that a gitlink sorts as '\0' at the end, while
@@ -477,7 +650,12 @@ enum exist_status {
  */
 static enum exist_status directory_exists_in_index(const char *dirname, int len)
 {
-       int pos = cache_name_pos(dirname, len);
+       int pos;
+
+       if (ignore_case)
+               return directory_exists_in_index_icase(dirname, len);
+
+       pos = cache_name_pos(dirname, len);
        if (pos < 0)
                pos = -pos-1;
        while (pos < active_nr) {
@@ -964,6 +1142,12 @@ char *get_relative_cwd(char *buffer, int size, const char *dir)
        case '/':
                return cwd + 1;
        default:
+               /*
+                * dir can end with a path separator when it's root
+                * directory. Return proper prefix in that case.
+                */
+               if (dir[-1] == '/')
+                       return cwd;
                return NULL;
        }
 }
@@ -1071,3 +1255,50 @@ int remove_path(const char *name)
        return 0;
 }
 
+static int pathspec_item_cmp(const void *a_, const void *b_)
+{
+       struct pathspec_item *a, *b;
+
+       a = (struct pathspec_item *)a_;
+       b = (struct pathspec_item *)b_;
+       return strcmp(a->match, b->match);
+}
+
+int init_pathspec(struct pathspec *pathspec, const char **paths)
+{
+       const char **p = paths;
+       int i;
+
+       memset(pathspec, 0, sizeof(*pathspec));
+       if (!p)
+               return 0;
+       while (*p)
+               p++;
+       pathspec->raw = paths;
+       pathspec->nr = p - paths;
+       if (!pathspec->nr)
+               return 0;
+
+       pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);
+       for (i = 0; i < pathspec->nr; i++) {
+               struct pathspec_item *item = pathspec->items+i;
+               const char *path = paths[i];
+
+               item->match = path;
+               item->len = strlen(path);
+               item->has_wildcard = !no_wildcard(path);
+               if (item->has_wildcard)
+                       pathspec->has_wildcard = 1;
+       }
+
+       qsort(pathspec->items, pathspec->nr,
+             sizeof(struct pathspec_item), pathspec_item_cmp);
+
+       return 0;
+}
+
+void free_pathspec(struct pathspec *pathspec)
+{
+       free(pathspec->items);
+       pathspec->items = NULL;
+}
diff --git a/dir.h b/dir.h
index 278d84cdf7df01c33a45e6dc9c20592cecff9d85..aa511da77b0ec54cbdbd3786faad0c1285df7182 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -65,6 +65,10 @@ struct dir_struct {
 #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 match_pathspec_depth(const struct pathspec *pathspec,
+                               const char *name, int namelen,
+                               int prefix, char *seen);
+extern int within_depth(const char *name, int namelen, int depth, int max_depth);
 
 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);
@@ -78,6 +82,7 @@ extern int add_excludes_from_file_to_list(const char *fname, const char *base, i
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
 extern void add_exclude(const char *string, const char *base,
                        int baselen, struct exclude_list *which);
+extern void free_excludes(struct exclude_list *el);
 extern int file_exists(const char *);
 
 extern char *get_relative_cwd(char *buffer, int size, const char *dir);
@@ -101,4 +106,8 @@ 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);
 
+extern int strcmp_icase(const char *a, const char *b);
+extern int strncmp_icase(const char *a, const char *b, size_t count);
+extern int fnmatch_icase(const char *pattern, const char *string, int flags);
+
 #endif
diff --git a/entry.c b/entry.c
index 004182c99d27a6a5825d2429f9104fc7a8f1dc80..b017167f2015623fb9c721e91d0a940abd1d5196 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -106,14 +106,14 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
        case S_IFLNK:
                new = read_blob_entry(ce, &size);
                if (!new)
-                       return error("git checkout-index: unable to read sha1 file of %s (%s)",
+                       return error("unable to read sha1 file of %s (%s)",
                                path, sha1_to_hex(ce->sha1));
 
                if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) {
                        ret = symlink(new, path);
                        free(new);
                        if (ret)
-                               return error("git checkout-index: unable to create symlink %s (%s)",
+                               return error("unable to create symlink %s (%s)",
                                             path, strerror(errno));
                        break;
                }
@@ -141,7 +141,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                }
                if (fd < 0) {
                        free(new);
-                       return error("git checkout-index: unable to create file %s (%s)",
+                       return error("unable to create file %s (%s)",
                                path, strerror(errno));
                }
 
@@ -155,16 +155,16 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                close(fd);
                free(new);
                if (wrote != size)
-                       return error("git checkout-index: unable to write file %s", path);
+                       return error("unable to write file %s", path);
                break;
        case S_IFGITLINK:
                if (to_tempfile)
-                       return error("git checkout-index: cannot create temporary subproject %s", path);
+                       return error("cannot create temporary subproject %s", path);
                if (mkdir(path, 0777) < 0)
-                       return error("git checkout-index: cannot create subproject directory %s", path);
+                       return error("cannot create subproject directory %s", path);
                break;
        default:
-               return error("git checkout-index: unknown file mode for %s", path);
+               return error("unknown file mode for %s in index", path);
        }
 
        if (state->refresh_cache) {
@@ -211,7 +211,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
                        return 0;
                if (!state->force) {
                        if (!state->quiet)
-                               fprintf(stderr, "git-checkout-index: %s already exists\n", path);
+                               fprintf(stderr, "%s already exists, no checkout\n", path);
                        return -1;
                }
 
index 0667c1f170444dd03fdf11f24e5def481359d0cd..be744cc8a13401e75f10ecddc0b4f66c309d0f9c 100644 (file)
@@ -21,6 +21,7 @@ int prefer_symlink_refs;
 int is_bare_repository_cfg = -1; /* unspecified */
 int log_all_ref_updates = -1; /* unspecified */
 int warn_ambiguous_refs = 1;
+int unique_abbrev_extra_length;
 int repository_format_version;
 const char *git_commit_encoding;
 const char *git_log_output_encoding;
@@ -38,6 +39,7 @@ size_t delta_base_cache_limit = 16 * 1024 * 1024;
 const char *pager_program;
 int pager_use_color = 1;
 const char *editor_program;
+const char *askpass_program;
 const char *excludes_file;
 enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 int read_replace_refs = 1;
@@ -54,6 +56,7 @@ enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
 char *notes_ref_name;
 int grafts_replace_parents = 1;
 int core_apply_sparse_checkout;
+struct startup_info *startup_info;
 
 /* Parallel index stat data preload? */
 int core_preload_index = 0;
@@ -63,7 +66,7 @@ char *git_work_tree_cfg;
 static char *work_tree;
 
 static const char *git_dir;
-static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
+static char *git_object_dir, *git_index_file, *git_graft_file;
 
 /*
  * Repository-local GIT_* environment variables
@@ -73,6 +76,7 @@ static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
 const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {
        ALTERNATE_DB_ENVIRONMENT,
        CONFIG_ENVIRONMENT,
+       CONFIG_DATA_ENVIRONMENT,
        DB_ENVIRONMENT,
        GIT_DIR_ENVIRONMENT,
        GIT_WORK_TREE_ENVIRONMENT,
@@ -85,8 +89,11 @@ const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {
 static void setup_git_env(void)
 {
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
-       if (!git_dir)
+       git_dir = git_dir ? xstrdup(git_dir) : NULL;
+       if (!git_dir) {
                git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+               git_dir = git_dir ? xstrdup(git_dir) : NULL;
+       }
        if (!git_dir)
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
        git_object_dir = getenv(DB_ENVIRONMENT);
@@ -94,8 +101,6 @@ static void setup_git_env(void)
                git_object_dir = xmalloc(strlen(git_dir) + 9);
                sprintf(git_object_dir, "%s/objects", git_dir);
        }
-       git_refs_dir = xmalloc(strlen(git_dir) + 6);
-       sprintf(git_refs_dir, "%s/refs", git_dir);
        git_index_file = getenv(INDEX_ENVIRONMENT);
        if (!git_index_file) {
                git_index_file = xmalloc(strlen(git_dir) + 7);
@@ -135,30 +140,20 @@ static int git_work_tree_initialized;
  */
 void set_git_work_tree(const char *new_work_tree)
 {
-       if (is_bare_repository_cfg >= 0)
-               die("cannot set work tree after initialization");
+       if (git_work_tree_initialized) {
+               new_work_tree = make_absolute_path(new_work_tree);
+               if (strcmp(new_work_tree, work_tree))
+                       die("internal error: work tree has already been set\n"
+                           "Current worktree: %s\nNew worktree: %s",
+                           work_tree, new_work_tree);
+               return;
+       }
        git_work_tree_initialized = 1;
-       free(work_tree);
        work_tree = xstrdup(make_absolute_path(new_work_tree));
-       is_bare_repository_cfg = 0;
 }
 
 const char *get_git_work_tree(void)
 {
-       if (!git_work_tree_initialized) {
-               work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
-               /* core.bare = true overrides implicit and config work tree */
-               if (!work_tree && is_bare_repository_cfg < 1) {
-                       work_tree = git_work_tree_cfg;
-                       /* make_absolute_path also normalizes the path */
-                       if (work_tree && !is_absolute_path(work_tree))
-                               work_tree = xstrdup(make_absolute_path(git_path("%s", work_tree)));
-               } else if (work_tree)
-                       work_tree = xstrdup(make_absolute_path(work_tree));
-               git_work_tree_initialized = 1;
-               if (work_tree)
-                       is_bare_repository_cfg = 0;
-       }
        return work_tree;
 }
 
@@ -169,6 +164,43 @@ char *get_object_directory(void)
        return git_object_dir;
 }
 
+int odb_mkstemp(char *template, size_t limit, const char *pattern)
+{
+       int fd;
+       /*
+        * we let the umask do its job, don't try to be more
+        * restrictive except to remove write permission.
+        */
+       int mode = 0444;
+       snprintf(template, limit, "%s/%s",
+                get_object_directory(), pattern);
+       fd = git_mkstemp_mode(template, mode);
+       if (0 <= fd)
+               return fd;
+
+       /* slow path */
+       /* some mkstemp implementations erase template on failure */
+       snprintf(template, limit, "%s/%s",
+                get_object_directory(), pattern);
+       safe_create_leading_directories(template);
+       return xmkstemp_mode(template, mode);
+}
+
+int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
+{
+       int fd;
+
+       snprintf(name, namesz, "%s/pack/pack-%s.keep",
+                get_object_directory(), sha1_to_hex(sha1));
+       fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+       if (0 <= fd)
+               return fd;
+
+       /* slow path */
+       safe_create_leading_directories(name);
+       return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+}
+
 char *get_index_file(void)
 {
        if (!git_index_file)
@@ -190,3 +222,14 @@ int set_git_dir(const char *path)
        setup_git_env();
        return 0;
 }
+
+const char *get_log_output_encoding(void)
+{
+       return git_log_output_encoding ? git_log_output_encoding
+               : get_commit_output_encoding();
+}
+
+const char *get_commit_output_encoding(void)
+{
+       return git_commit_encoding ? git_commit_encoding : "UTF-8";
+}
index bf225706ee377b89035eb21f76f9957cfaf6363b..38545e8bfd72c8cd4f91e0d33257b143db86bac5 100644 (file)
@@ -3,7 +3,6 @@
 #include "quote.h"
 #define MAX_ARGS       32
 
-extern char **environ;
 static const char *argv_exec_path;
 static const char *argv0_path;
 
index dd51ac48b60d93031aa761d6731dd066c04e6989..60f26fe473ad6922166e952f4f7e5be2b79d45de 100644 (file)
@@ -132,14 +132,17 @@ Format of STDIN stream:
   ts    ::= # time since the epoch in seconds, ascii base10 notation;
   tz    ::= # GIT style timezone;
 
-     # note: comments may appear anywhere in the input, except
-     # within a data command.  Any form of the data command
-     # always escapes the related input from comment processing.
+     # note: comments and cat requests may appear anywhere
+     # in the input, except within a data command.  Any form
+     # of the data command always escapes the related input
+     # from comment processing.
      #
      # In case it is not clear, the '#' that starts the comment
      # must be the first character on that line (an lf
      # preceded it).
      #
+  cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
+
   comment ::= '#' not_lf* lf;
   not_lf  ::= # Any byte that is not ASCII newline (LF);
 */
@@ -156,6 +159,7 @@ Format of STDIN stream:
 #include "csum-file.h"
 #include "quote.h"
 #include "exec_cmd.h"
+#include "dir.h"
 
 #define PACK_ID_BITS 16
 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@ -361,7 +365,14 @@ static uintmax_t next_mark;
 static struct strbuf new_data = STRBUF_INIT;
 static int seen_data_command;
 
+/* Signal handling */
+static volatile sig_atomic_t checkpoint_requested;
+
+/* Where to write output of cat-blob commands */
+static int cat_blob_fd = STDOUT_FILENO;
+
 static void parse_argv(void);
+static void parse_cat_blob(void);
 
 static void write_branch_report(FILE *rpt, struct branch *b)
 {
@@ -500,6 +511,32 @@ static NORETURN void die_nicely(const char *err, va_list params)
        exit(128);
 }
 
+#ifndef SIGUSR1        /* Windows, for example */
+
+static void set_checkpoint_signal(void)
+{
+}
+
+#else
+
+static void checkpoint_signal(int signo)
+{
+       checkpoint_requested = 1;
+}
+
+static void set_checkpoint_signal(void)
+{
+       struct sigaction sa;
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = checkpoint_signal;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = SA_RESTART;
+       sigaction(SIGUSR1, &sa, NULL);
+}
+
+#endif
+
 static void alloc_objects(unsigned int cnt)
 {
        struct object_entry_pool *b;
@@ -539,22 +576,17 @@ static struct object_entry *insert_object(unsigned char *sha1)
 {
        unsigned int h = sha1[0] << 8 | sha1[1];
        struct object_entry *e = object_table[h];
-       struct object_entry *p = NULL;
 
        while (e) {
                if (!hashcmp(sha1, e->idx.sha1))
                        return e;
-               p = e;
                e = e->next;
        }
 
        e = new_object(sha1);
-       e->next = NULL;
+       e->next = object_table[h];
        e->idx.offset = 0;
-       if (p)
-               p->next = e;
-       else
-               object_table[h] = e;
+       object_table[h] = e;
        return e;
 }
 
@@ -1437,6 +1469,20 @@ static void store_tree(struct tree_entry *root)
        t->entry_count -= del;
 }
 
+static void tree_content_replace(
+       struct tree_entry *root,
+       const unsigned char *sha1,
+       const uint16_t mode,
+       struct tree_content *newtree)
+{
+       if (!S_ISDIR(mode))
+               die("Root cannot be a non-directory");
+       hashcpy(root->versions[1].sha1, sha1);
+       if (root->tree)
+               release_tree_content_recursive(root->tree);
+       root->tree = newtree;
+}
+
 static int tree_content_set(
        struct tree_entry *root,
        const char *p,
@@ -1444,7 +1490,7 @@ static int tree_content_set(
        const uint16_t mode,
        struct tree_content *subtree)
 {
-       struct tree_content *t = root->tree;
+       struct tree_content *t;
        const char *slash1;
        unsigned int i, n;
        struct tree_entry *e;
@@ -1459,9 +1505,12 @@ static int tree_content_set(
        if (!slash1 && !S_ISDIR(mode) && subtree)
                die("Non-directories cannot have subtrees");
 
+       if (!root->tree)
+               load_tree(root);
+       t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
                        if (!slash1) {
                                if (!S_ISDIR(mode)
                                                && e->versions[1].mode == mode
@@ -1514,7 +1563,7 @@ static int tree_content_remove(
        const char *p,
        struct tree_entry *backup_leaf)
 {
-       struct tree_content *t = root->tree;
+       struct tree_content *t;
        const char *slash1;
        unsigned int i, n;
        struct tree_entry *e;
@@ -1525,9 +1574,20 @@ static int tree_content_remove(
        else
                n = strlen(p);
 
+       if (!root->tree)
+               load_tree(root);
+       t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+                       if (slash1 && !S_ISDIR(e->versions[1].mode))
+                               /*
+                                * If p names a file in some subdirectory, and a
+                                * file or symlink matching the name of the
+                                * parent directory of p exists, then p cannot
+                                * exist and need not be deleted.
+                                */
+                               return 1;
                        if (!slash1 || !S_ISDIR(e->versions[1].mode))
                                goto del_entry;
                        if (!e->tree)
@@ -1564,7 +1624,7 @@ static int tree_content_get(
        const char *p,
        struct tree_entry *leaf)
 {
-       struct tree_content *t = root->tree;
+       struct tree_content *t;
        const char *slash1;
        unsigned int i, n;
        struct tree_entry *e;
@@ -1575,9 +1635,12 @@ static int tree_content_get(
        else
                n = strlen(p);
 
+       if (!root->tree)
+               load_tree(root);
+       t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
                        if (!slash1) {
                                memcpy(leaf, e, sizeof(*leaf));
                                if (e->tree && is_null_sha1(e->versions[1].sha1))
@@ -1773,7 +1836,7 @@ static int read_next_command(void)
                return EOF;
        }
 
-       do {
+       for (;;) {
                if (unread_command_buf) {
                        unread_command_buf = 0;
                } else {
@@ -1806,9 +1869,14 @@ static int read_next_command(void)
                        rc->prev->next = rc;
                        cmd_tail = rc;
                }
-       } while (command_buf.buf[0] == '#');
-
-       return 0;
+               if (!prefixcmp(command_buf.buf, "cat-blob ")) {
+                       parse_cat_blob();
+                       continue;
+               }
+               if (command_buf.buf[0] == '#')
+                       continue;
+               return 0;
+       }
 }
 
 static void skip_optional_lf(void)
@@ -2163,6 +2231,12 @@ static void file_change_m(struct branch *b)
                p = uq.buf;
        }
 
+       /* Git does not track empty, non-toplevel directories. */
+       if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) {
+               tree_content_remove(&b->branch_tree, p, NULL);
+               return;
+       }
+
        if (S_ISGITLINK(mode)) {
                if (inline_data)
                        die("Git links cannot be specified 'inline': %s",
@@ -2201,6 +2275,10 @@ static void file_change_m(struct branch *b)
                                command_buf.buf);
        }
 
+       if (!*p) {
+               tree_content_replace(&b->branch_tree, sha1, mode, NULL);
+               return;
+       }
        tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
 }
 
@@ -2259,6 +2337,13 @@ static void file_change_cr(struct branch *b, int rename)
                tree_content_get(&b->branch_tree, s, &leaf);
        if (!leaf.versions[1].mode)
                die("Path %s not in branch", s);
+       if (!*d) {      /* C "path/to/subdir" "" */
+               tree_content_replace(&b->branch_tree,
+                       leaf.versions[1].sha1,
+                       leaf.versions[1].mode,
+                       leaf.tree);
+               return;
+       }
        tree_content_set(&b->branch_tree, d,
                leaf.versions[1].sha1,
                leaf.versions[1].mode,
@@ -2672,14 +2757,95 @@ static void parse_reset_branch(void)
                unread_command_buf = 1;
 }
 
-static void parse_checkpoint(void)
+static void cat_blob_write(const char *buf, unsigned long size)
+{
+       if (write_in_full(cat_blob_fd, buf, size) != size)
+               die_errno("Write to frontend failed");
+}
+
+static void cat_blob(struct object_entry *oe, unsigned char sha1[20])
 {
+       struct strbuf line = STRBUF_INIT;
+       unsigned long size;
+       enum object_type type = 0;
+       char *buf;
+
+       if (!oe || oe->pack_id == MAX_PACK_ID) {
+               buf = read_sha1_file(sha1, &type, &size);
+       } else {
+               type = oe->type;
+               buf = gfi_unpack_entry(oe, &size);
+       }
+
+       /*
+        * Output based on batch_one_object() from cat-file.c.
+        */
+       if (type <= 0) {
+               strbuf_reset(&line);
+               strbuf_addf(&line, "%s missing\n", sha1_to_hex(sha1));
+               cat_blob_write(line.buf, line.len);
+               strbuf_release(&line);
+               free(buf);
+               return;
+       }
+       if (!buf)
+               die("Can't read object %s", sha1_to_hex(sha1));
+       if (type != OBJ_BLOB)
+               die("Object %s is a %s but a blob was expected.",
+                   sha1_to_hex(sha1), typename(type));
+       strbuf_reset(&line);
+       strbuf_addf(&line, "%s %s %lu\n", sha1_to_hex(sha1),
+                                               typename(type), size);
+       cat_blob_write(line.buf, line.len);
+       strbuf_release(&line);
+       cat_blob_write(buf, size);
+       cat_blob_write("\n", 1);
+       free(buf);
+}
+
+static void parse_cat_blob(void)
+{
+       const char *p;
+       struct object_entry *oe = oe;
+       unsigned char sha1[20];
+
+       /* cat-blob SP <object> LF */
+       p = command_buf.buf + strlen("cat-blob ");
+       if (*p == ':') {
+               char *x;
+               oe = find_mark(strtoumax(p + 1, &x, 10));
+               if (x == p + 1)
+                       die("Invalid mark: %s", command_buf.buf);
+               if (!oe)
+                       die("Unknown mark: %s", command_buf.buf);
+               if (*x)
+                       die("Garbage after mark: %s", command_buf.buf);
+               hashcpy(sha1, oe->idx.sha1);
+       } else {
+               if (get_sha1_hex(p, sha1))
+                       die("Invalid SHA1: %s", command_buf.buf);
+               if (p[40])
+                       die("Garbage after SHA1: %s", command_buf.buf);
+               oe = find_object(sha1);
+       }
+
+       cat_blob(oe, sha1);
+}
+
+static void checkpoint(void)
+{
+       checkpoint_requested = 0;
        if (object_count) {
                cycle_packfile();
                dump_branches();
                dump_tags();
                dump_marks();
        }
+}
+
+static void parse_checkpoint(void)
+{
+       checkpoint_requested = 1;
        skip_optional_lf();
 }
 
@@ -2729,16 +2895,25 @@ static void option_date_format(const char *fmt)
                die("unknown --date-format argument %s", fmt);
 }
 
+static unsigned long ulong_arg(const char *option, const char *arg)
+{
+       char *endptr;
+       unsigned long rv = strtoul(arg, &endptr, 0);
+       if (strchr(arg, '-') || endptr == arg || *endptr)
+               die("%s: argument must be a non-negative integer", option);
+       return rv;
+}
+
 static void option_depth(const char *depth)
 {
-       max_depth = strtoul(depth, NULL, 0);
+       max_depth = ulong_arg("--depth", depth);
        if (max_depth > MAX_DEPTH)
                die("--depth cannot exceed %u", MAX_DEPTH);
 }
 
 static void option_active_branches(const char *branches)
 {
-       max_active_branches = strtoul(branches, NULL, 0);
+       max_active_branches = ulong_arg("--active-branches", branches);
 }
 
 static void option_export_marks(const char *marks)
@@ -2747,6 +2922,14 @@ static void option_export_marks(const char *marks)
        safe_create_leading_directories_const(export_marks_file);
 }
 
+static void option_cat_blob_fd(const char *fd)
+{
+       unsigned long n = ulong_arg("--cat-blob-fd", fd);
+       if (n > (unsigned long) INT_MAX)
+               die("--cat-blob-fd cannot exceed %d", INT_MAX);
+       cat_blob_fd = (int) n;
+}
+
 static void option_export_pack_edges(const char *edges)
 {
        if (pack_edges)
@@ -2800,6 +2983,8 @@ static int parse_one_feature(const char *feature, int from_stream)
                option_import_marks(feature + 13, from_stream);
        } else if (!prefixcmp(feature, "export-marks=")) {
                option_export_marks(feature + 13);
+       } else if (!strcmp(feature, "cat-blob")) {
+               ; /* Don't die - this feature is supported */
        } else if (!prefixcmp(feature, "relative-marks")) {
                relative_marks_paths = 1;
        } else if (!prefixcmp(feature, "no-relative-marks")) {
@@ -2876,7 +3061,7 @@ static int git_pack_config(const char *k, const char *v, void *cb)
 }
 
 static const char fast_import_usage[] =
-"git fast-import [--date-format=f] [--max-pack-size=n] [--big-file-threshold=n] [--depth=n] [--active-branches=n] [--export-marks=marks.file]";
+"git fast-import [--date-format=<f>] [--max-pack-size=<n>] [--big-file-threshold=<n>] [--depth=<n>] [--active-branches=<n>] [--export-marks=<marks.file>]";
 
 static void parse_argv(void)
 {
@@ -2894,6 +3079,11 @@ static void parse_argv(void)
                if (parse_one_feature(a + 2, 0))
                        continue;
 
+               if (!prefixcmp(a + 2, "cat-blob-fd=")) {
+                       option_cat_blob_fd(a + 2 + strlen("cat-blob-fd="));
+                       continue;
+               }
+
                die("unknown option %s", a);
        }
        if (i != global_argc)
@@ -2936,6 +3126,7 @@ int main(int argc, const char **argv)
        prepare_packed_git();
        start_packfile();
        set_die_routine(die_nicely);
+       set_checkpoint_signal();
        while (read_next_command() != EOF) {
                if (!strcmp("blob", command_buf.buf))
                        parse_new_blob();
@@ -2957,6 +3148,9 @@ int main(int argc, const char **argv)
                        /* ignore non-git options*/;
                else
                        die("Unsupported command: %s", command_buf.buf);
+
+               if (checkpoint_requested)
+                       checkpoint();
        }
 
        /* argv hasn't been parsed yet, do so */
index 27fc79347af428dd39daa5b550814cc6cf980510..a329c5a1f8c3b63ee6976a8e0e3903a11783324b 100755 (executable)
@@ -1,6 +1,8 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
+use 5.008;
 use strict;
+use warnings;
 use Git;
 
 binmode(STDOUT, ":raw");
@@ -87,6 +89,7 @@ my %patch_modes = (
                TARGET => '',
                PARTICIPLE => 'staging',
                FILTER => 'file-only',
+               IS_REVERSE => 0,
        },
        'stash' => {
                DIFF => 'diff-index -p HEAD',
@@ -96,6 +99,7 @@ my %patch_modes = (
                TARGET => '',
                PARTICIPLE => 'stashing',
                FILTER => undef,
+               IS_REVERSE => 0,
        },
        'reset_head' => {
                DIFF => 'diff-index -p --cached',
@@ -105,6 +109,7 @@ my %patch_modes = (
                TARGET => '',
                PARTICIPLE => 'unstaging',
                FILTER => 'index-only',
+               IS_REVERSE => 1,
        },
        'reset_nothead' => {
                DIFF => 'diff-index -R -p --cached',
@@ -114,6 +119,7 @@ my %patch_modes = (
                TARGET => ' to index',
                PARTICIPLE => 'applying',
                FILTER => 'index-only',
+               IS_REVERSE => 0,
        },
        'checkout_index' => {
                DIFF => 'diff-files -p',
@@ -123,6 +129,7 @@ my %patch_modes = (
                TARGET => ' from worktree',
                PARTICIPLE => 'discarding',
                FILTER => 'file-only',
+               IS_REVERSE => 1,
        },
        'checkout_head' => {
                DIFF => 'diff-index -p',
@@ -132,6 +139,7 @@ my %patch_modes = (
                TARGET => ' from index and worktree',
                PARTICIPLE => 'discarding',
                FILTER => undef,
+               IS_REVERSE => 1,
        },
        'checkout_nothead' => {
                DIFF => 'diff-index -R -p',
@@ -141,6 +149,7 @@ my %patch_modes = (
                TARGET => ' to index and worktree',
                PARTICIPLE => 'applying',
                FILTER => undef,
+               IS_REVERSE => 0,
        },
 );
 
@@ -999,10 +1008,12 @@ sub edit_hunk_manually {
        print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
        print $fh @$oldtext;
        my $participle = $patch_mode_flavour{PARTICIPLE};
+       my $is_reverse = $patch_mode_flavour{IS_REVERSE};
+       my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', '-');
        print $fh <<EOF;
 # ---
-# To remove '-' lines, make them ' ' lines (context).
-# To remove '+' lines, delete them.
+# To remove '$remove_minus' lines, make them ' ' lines (context).
+# To remove '$remove_plus' lines, delete them.
 # Lines starting with # will be removed.
 #
 # If the patch applies cleanly, the edited hunk will immediately be
index e7f008c7baae2ff484e16882e199b6b9d75195aa..6cdd5910db50c96df3d149fba172750cb10c09cb 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -5,7 +5,7 @@
 SUBDIRECTORY_OK=Yes
 OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC="\
-git am [options] [<mbox>|<Maildir>...]
+git am [options] [(<mbox>|<Maildir>)...]
 git am [options] (--resolved | --skip | --abort)
 --
 i,interactive   run interactively
@@ -68,9 +68,31 @@ sq () {
 
 stop_here () {
     echo "$1" >"$dotest/next"
+    git rev-parse --verify -q HEAD >"$dotest/abort-safety"
     exit 1
 }
 
+safe_to_abort () {
+       if test -f "$dotest/dirtyindex"
+       then
+               return 1
+       fi
+
+       if ! test -s "$dotest/abort-safety"
+       then
+               return 0
+       fi
+
+       abort_safety=$(cat "$dotest/abort-safety")
+       if test "z$(git rev-parse --verify -q HEAD)" = "z$abort_safety"
+       then
+               return 0
+       fi
+       echo >&2 "You seem to have moved HEAD since the last 'am' failure."
+       echo >&2 "Not rewinding to ORIG_HEAD"
+       return 1
+}
+
 stop_here_user_resolve () {
     if [ -n "$resolvemsg" ]; then
            printf '%s\n' "$resolvemsg"
@@ -137,7 +159,7 @@ It does not apply to blobs recorded in its index."
     export GITHEAD_$his_tree
     if test -n "$GIT_QUIET"
     then
-           export GIT_MERGE_VERBOSITY=0
+           GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
     fi
     git-merge-recursive $orig_tree -- HEAD $his_tree || {
            git rerere $allow_rerere_autoupdate
@@ -419,10 +441,11 @@ then
                        exec git rebase --abort
                fi
                git rerere clear
-               test -f "$dotest/dirtyindex" || {
+               if safe_to_abort
+               then
                        git read-tree --reset -u HEAD ORIG_HEAD
                        git reset ORIG_HEAD
-               }
+               fi
                rm -fr "$dotest"
                exit ;;
        esac
@@ -444,12 +467,12 @@ else
                                set x
                                first=
                        }
-                       case "$arg" in
-                       /*)
-                               set "$@" "$arg" ;;
-                       *)
-                               set "$@" "$prefix$arg" ;;
-                       esac
+                       if is_absolute_path "$arg"
+                       then
+                               set "$@" "$arg"
+                       else
+                               set "$@" "$prefix$arg"
+                       fi
                done
                shift
        fi
@@ -554,13 +577,6 @@ then
        resume=
 fi
 
-if test "$this" -gt "$last"
-then
-       say Nothing to do.
-       rm -fr "$dotest"
-       exit
-fi
-
 while test "$this" -le "$last"
 do
        msgnum=`printf "%0${prec}d" $this`
index 98f3ede566a6cb0c902ce84795f7de8f8afbe633..bc32f18d6d9cbf915eb7797817390adbbc111506 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 #
 # This tool is copyright (c) 2005, Martin Langhoff.
 # It is released under the Gnu Public License, version 2.
@@ -54,6 +54,7 @@ and can contain multiple, unrelated branches.
 
 =cut
 
+use 5.008;
 use strict;
 use warnings;
 use Getopt::Std;
index 6e2acb8ef29e5003945bed17014a68b141ada454..c21e33c8d133af0e9e0ae3edaffd0a5593ff4ac3 100755 (executable)
@@ -316,7 +316,12 @@ bisect_reset() {
        *)
            usage ;;
        esac
-       git checkout "$branch" -- && bisect_clean_state
+       if git checkout "$branch" -- ; then
+               bisect_clean_state
+       else
+               die "Could not check out original HEAD '$branch'." \
+                               "Try 'git bisect reset <commit>'."
+       fi
 }
 
 bisect_clean_state() {
@@ -338,6 +343,7 @@ bisect_clean_state() {
 }
 
 bisect_replay () {
+       test "$#" -eq 1 || die "No logfile given"
        test -r "$1" || die "cannot read $1 for replaying"
        bisect_reset
        while read git bisect command rev
@@ -412,6 +418,10 @@ bisect_run () {
     done
 }
 
+bisect_log () {
+       test -s "$GIT_DIR/BISECT_LOG" || die "We are not bisecting."
+       cat "$GIT_DIR/BISECT_LOG"
+}
 
 case "$#" in
 0)
@@ -438,7 +448,7 @@ case "$#" in
     replay)
        bisect_replay "$@" ;;
     log)
-       cat "$GIT_DIR/BISECT_LOG" ;;
+       bisect_log ;;
     run)
         bisect_run "$@" ;;
     *)
index fe845ae639767dc8f56a9196a7faefb468737bba..d6d269f138b06791ba6e8712b06036f00a73c7e3 100644 (file)
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 #define bitsizeof(x)  (CHAR_BIT * sizeof(x))
 
+#define maximum_signed_value_of_type(a) \
+    (INTMAX_MAX >> (bitsizeof(intmax_t) - bitsizeof(a)))
+
+/*
+ * Signed integer overflow is undefined in C, so here's a helper macro
+ * to detect if the sum of two integers will overflow.
+ *
+ * Requires: a >= 0, typeof(a) equals typeof(b)
+ */
+#define signed_add_overflows(a, b) \
+    ((b) > maximum_signed_value_of_type(a) - (a))
+
 #ifdef __GNUC__
 #define TYPEOF(x) (__typeof__(x))
 #else
 #include <assert.h>
 #include <regex.h>
 #include <utime.h>
+#include <syslog.h>
+#ifndef NO_SYS_POLL_H
+#include <sys/poll.h>
+#else
+#include <poll.h>
+#endif
 #ifndef __MINGW32__
 #include <sys/wait.h>
-#include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <termios.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <pwd.h>
+#ifndef NO_INTTYPES_H
 #include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
 #if defined(__CYGWIN__)
 #undef _XOPEN_SOURCE
 #include <grp.h>
@@ -160,6 +181,10 @@ extern char *gitbasename(char *);
 #define PRIx32 "x"
 #endif
 
+#ifndef PRIo32
+#define PRIo32 "o"
+#endif
+
 #ifndef PATH_SEP
 #define PATH_SEP ':'
 #endif
@@ -312,6 +337,11 @@ extern size_t gitstrlcpy(char *, const char *, size_t);
 extern uintmax_t gitstrtoumax(const char *, char **, int);
 #endif
 
+#ifdef NO_STRTOK_R
+#define strtok_r gitstrtok_r
+extern char *gitstrtok_r(char *s, const char *delim, char **save_ptr);
+#endif
+
 #ifdef NO_HSTRERROR
 #define hstrerror githstrerror
 extern const char *githstrerror(int herror);
@@ -365,6 +395,14 @@ static inline void *gitmempcpy(void *dest, const void *src, size_t n)
 }
 #endif
 
+#ifdef NO_INET_PTON
+int inet_pton(int af, const char *src, void *dst);
+#endif
+
+#ifdef NO_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+
 extern void release_pack_memory(size_t, int);
 
 typedef void (*try_to_free_t)(size_t);
@@ -383,6 +421,7 @@ extern ssize_t xwrite(int fd, const void *buf, size_t len);
 extern int xdup(int fd);
 extern FILE *xfdopen(int fd, const char *mode);
 extern int xmkstemp(char *template);
+extern int xmkstemp_mode(char *template, int mode);
 extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
 extern int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1);
 
index 59b672213bfc36f95db089f0e13bafc1c2f2ed71..39a426e067c76e01d3f2a0d63243ec494436da46 100755 (executable)
@@ -1,6 +1,8 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
+use 5.008;
 use strict;
+use warnings;
 use Getopt::Std;
 use File::Temp qw(tempdir);
 use Data::Dumper;
index 9e03eee4586ca3b7476b56f66e9dcf6ffe3088cf..8e683e54783ab6e916bdb8ade56832ac81470648 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
 # This tool is copyright (c) 2005, Matthias Urlichs.
 # It is released under the Gnu Public License, version 2.
@@ -13,6 +13,7 @@
 # The head revision is on branch "origin" by default.
 # You can change that with the '-o' option.
 
+use 5.008;
 use strict;
 use warnings;
 use Getopt::Long;
@@ -89,23 +90,40 @@ sub write_author_info($) {
 }
 
 # convert getopts specs for use by git config
+my %longmap = (
+       'A:' => 'authors-file',
+       'M:' => 'merge-regex',
+       'P:' => undef,
+       'R' => 'track-revisions',
+       'S:' => 'ignore-paths',
+);
+
 sub read_repo_config {
-    # Split the string between characters, unless there is a ':'
-    # So "abc:de" becomes ["a", "b", "c:", "d", "e"]
+       # Split the string between characters, unless there is a ':'
+       # So "abc:de" becomes ["a", "b", "c:", "d", "e"]
        my @opts = split(/ *(?!:)/, shift);
        foreach my $o (@opts) {
                my $key = $o;
                $key =~ s/://g;
                my $arg = 'git config';
                $arg .= ' --bool' if ($o !~ /:$/);
-
-        chomp(my $tmp = `$arg --get cvsimport.$key`);
+               my $ckey = $key;
+
+               if (exists $longmap{$o}) {
+                       # An uppercase option like -R cannot be
+                       # expressed in the configuration, as the
+                       # variable names are downcased.
+                       $ckey = $longmap{$o};
+                       next if (! defined $ckey);
+                       $ckey =~ s/-//g;
+               }
+               chomp(my $tmp = `$arg --get cvsimport.$ckey`);
                if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
-            no strict 'refs';
-            my $opt_name = "opt_" . $key;
-            if (!$$opt_name) {
-                $$opt_name = $tmp;
-            }
+                       no strict 'refs';
+                       my $opt_name = "opt_" . $key;
+                       if (!$$opt_name) {
+                               $$opt_name = $tmp;
+                       }
                }
        }
 }
@@ -611,7 +629,7 @@ my %index; # holds filenames of one index per branch
 unless (-d $git_dir) {
        system(qw(git init));
        die "Cannot init the GIT db at $git_tree: $?\n" if $?;
-       system(qw(git read-tree));
+       system(qw(git read-tree --empty));
        die "Cannot init an empty tree: $?\n" if $?;
 
        $last_branch = $opt_o;
index e9f3037df351ceed0262a8995f19903464163af8..1b8bff2cac163a6588df397168f57214c30b4784 100755 (executable)
@@ -8,13 +8,14 @@
 #### Copyright The Open University UK - 2006.
 ####
 #### Authors: Martyn Smith    <martyn@catalyst.net.nz>
-####          Martin Langhoff <martin@catalyst.net.nz>
+####          Martin Langhoff <martin@laptop.org>
 ####
 ####
 #### Released under the GNU Public License, version 2.
 ####
 ####
 
+use 5.008;
 use strict;
 use warnings;
 use bytes;
@@ -2680,7 +2681,7 @@ package GITCVS::log;
 #### Copyright The Open University UK - 2006.
 ####
 #### Authors: Martyn Smith    <martyn@catalyst.net.nz>
-####          Martin Langhoff <martin@catalyst.net.nz>
+####          Martin Langhoff <martin@laptop.org>
 ####
 ####
 
@@ -2847,7 +2848,7 @@ package GITCVS::updater;
 #### Copyright The Open University UK - 2006.
 ####
 #### Authors: Martyn Smith    <martyn@catalyst.net.nz>
-####          Martin Langhoff <martin@catalyst.net.nz>
+####          Martin Langhoff <martin@laptop.org>
 ####
 ####
 
index 524f5ea8ab14bb44e5a8e6393e40c0ec5c1ddaa2..0594bf7ca54c6b91d5f96dd86d8962e93459d004 100755 (executable)
@@ -49,6 +49,7 @@ launch_merge_tool () {
        fi
 
        if use_ext_cmd; then
+               export BASE
                eval $GIT_DIFFTOOL_EXTCMD '"$LOCAL"' '"$REMOTE"'
        else
                run_merge_tool "$merge_tool"
index adc42de8752fd395707f1e395c61991b3146cebb..ced1615e216018bc20303cd91eaea05c69cb6b11 100755 (executable)
@@ -10,6 +10,7 @@
 #
 # Any arguments that are unknown to this script are forwarded to 'git diff'.
 
+use 5.008;
 use strict;
 use warnings;
 use Cwd qw(abs_path);
@@ -51,6 +52,7 @@ sub generate_command
        my @command = (exe('git'), 'diff');
        my $skip_next = 0;
        my $idx = -1;
+       my $prompt = '';
        for my $arg (@ARGV) {
                $idx++;
                if ($skip_next) {
@@ -88,13 +90,11 @@ sub generate_command
                        next;
                }
                if ($arg eq '-y' || $arg eq '--no-prompt') {
-                       $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
-                       delete $ENV{GIT_DIFFTOOL_PROMPT};
+                       $prompt = 'no';
                        next;
                }
                if ($arg eq '--prompt') {
-                       $ENV{GIT_DIFFTOOL_PROMPT} = 'true';
-                       delete $ENV{GIT_DIFFTOOL_NO_PROMPT};
+                       $prompt = 'yes';
                        next;
                }
                if ($arg eq '-h' || $arg eq '--help') {
@@ -102,6 +102,11 @@ sub generate_command
                }
                push @command, $arg;
        }
+       if ($prompt eq 'yes') {
+               $ENV{GIT_DIFFTOOL_PROMPT} = 'true';
+       } elsif ($prompt eq 'no') {
+               $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
+       }
        return @command
 }
 
index 88fb0f070e5f32c62ae47f90f1f27ffeef836d8f..962a93b586571eb6fc60aae53c77f6e6b9fb281f 100755 (executable)
@@ -139,6 +139,7 @@ do
                continue
                ;;
        --remap-to-ancestor)
+               # deprecated ($remap_to_ancestor is set now automatically)
                shift
                remap_to_ancestor=t
                continue
@@ -265,7 +266,14 @@ mkdir ../map || die "Could not create map/ directory"
 
 # we need "--" only if there are no path arguments in $@
 nonrevs=$(git rev-parse --no-revs "$@") || exit
-test -z "$nonrevs" && dashdash=-- || dashdash=
+if test -z "$nonrevs"
+then
+       dashdash=--
+else
+       dashdash=
+       remap_to_ancestor=t
+fi
+
 rev_args=$(git rev-parse --revs-only "$@")
 
 case "$filter_subdir" in
index b3f937eace99ce1e25afbdbea678e9f6926dbc75..1fb4d9b4b7393d2c803457365127692fb093338f 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=0.12.GITGUI
+DEF_VER=0.13.GITGUI
 
 LF='
 '
index 197b55edf3eeeaec345d88227825cc550e6de72d..e22ba5c321dc47fdf512f30c5e6c741c41fb99eb 100644 (file)
@@ -215,6 +215,7 @@ endif
 $(GITGUI_MAIN): git-gui.sh GIT-VERSION-FILE GIT-GUI-VARS
        $(QUIET_GEN)rm -f $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+               -e 's|@@SHELL_PATH@@|$(SHELL_PATH_SQ)|' \
                -e '1,30s|^ argv0=$$0| argv0=$(GITGUI_SCRIPT)|' \
                -e '1,30s|^ exec wish | exec '\''$(TCLTK_PATH_SED)'\'' |' \
                -e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \
index 12e117ecb11afabef1d0d751ebee442ce515dc1e..4277f30c4116faf2788243af4ec23f1d077698e8 100755 (executable)
@@ -5,6 +5,8 @@ exec wish "$0" -- "$@"
 # This is a trivial implementation of an SSH_ASKPASS handler.
 # Git-gui uses this script if none are already configured.
 
+package require Tk
+
 set answer {}
 set yesno  0
 set rc     255
@@ -30,16 +32,20 @@ if {!$yesno} {
 
 frame .b
 button .b.ok     -text OK     -command finish
-button .b.cancel -text Cancel -command {destroy .}
+button .b.cancel -text Cancel -command cancel
 
 pack .b.ok -side left -expand 1
 pack .b.cancel -side right -expand 1
 pack .b -side bottom -fill x -padx 10 -pady 10
 
 bind . <Visibility> {focus -force .e}
-bind . <Key-Return> finish
-bind . <Key-Escape> {destroy .}
-bind . <Destroy>    {exit $rc}
+bind . <Key-Return> [list .b.ok invoke]
+bind . <Key-Escape> [list .b.cancel invoke]
+bind . <Destroy>    {set rc $rc}
+
+proc cancel {} {
+       set ::rc 255
+}
 
 proc finish {} {
        if {$::yesno} {
@@ -50,10 +56,11 @@ proc finish {} {
                }
        }
 
-       set ::rc 0
        puts $::answer
-       destroy .
+       set ::rc 0
 }
 
 wm title . "OpenSSH"
 tk::PlaceWindow .
+vwait rc
+exit $rc
index bb104895a94450a2ade7446c8fda52910bae0868..d3acf0d2134b40b8ffb3d0e8c3229f71fa6db663 100755 (executable)
@@ -10,8 +10,8 @@
  exec wish "$argv0" -- "$@"
 
 set appvers {@@GITGUI_VERSION@@}
-set copyright [encoding convertfrom utf-8 {
-Copyright © 2006, 2007 Shawn Pearce, et. al.
+set copyright [string map [list (c) \u00a9] {
+Copyright (c) 2006-2010 Shawn Pearce, et. al.
 
 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
@@ -83,6 +83,7 @@ if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
                puts stderr "source    $name"
                uplevel 1 real__source $name
        }
+       if {[tk windowingsystem] eq "win32"} { console show }
 }
 
 ######################################################################
@@ -128,6 +129,7 @@ set _githtmldir {}
 set _reponame {}
 set _iscygwin {}
 set _search_path {}
+set _shellpath {@@SHELL_PATH@@}
 
 set _trace [lsearch -exact $argv --trace]
 if {$_trace >= 0} {
@@ -137,6 +139,18 @@ if {$_trace >= 0} {
        set _trace 0
 }
 
+proc shellpath {} {
+       global _shellpath env
+       if {[string match @@* $_shellpath]} {
+               if {[info exists env(SHELL)]} {
+                       return $env(SHELL)
+               } else {
+                       return /bin/sh
+               }
+       }
+       return $_shellpath
+}
+
 proc appname {} {
        global _appname
        return $_appname
@@ -431,6 +445,8 @@ proc _lappend_nice {cmd_var} {
                set _nice [_which nice]
                if {[catch {exec $_nice git version}]} {
                        set _nice {}
+               } elseif {[is_Windows] && [file dirname $_nice] ne [file dirname $::_git]} {
+                       set _nice {}
                }
        }
        if {$_nice ne {}} {
@@ -660,6 +676,7 @@ bind . <Visibility> {
 if {[is_Windows]} {
        wm iconbitmap . -default $oguilib/git-gui.ico
        set ::tk::AlwaysShowSelection 1
+       bind . <Control-F2> {console show}
 
        # Spoof an X11 display for SSH
        if {![info exists env(DISPLAY)]} {
@@ -861,12 +878,19 @@ if {![regsub {^git version } $_git_version {} _git_version]} {
        exit 1
 }
 
+proc get_trimmed_version {s} {
+    set r {}
+    foreach x [split $s -._] {
+        if {[string is integer -strict $x]} {
+            lappend r $x
+        } else {
+            break
+        }
+    }
+    return [join $r .]
+}
 set _real_git_version $_git_version
-regsub -- {[\-\.]dirty$} $_git_version {} _git_version
-regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
-regsub {\.[a-zA-Z]+\.?[0-9]+$} $_git_version {} _git_version
-regsub {\.GIT$} $_git_version {} _git_version
-regsub {\.[a-zA-Z]+\.?[0-9]+$} $_git_version {} _git_version
+set _git_version [get_trimmed_version $_git_version]
 
 if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
        catch {wm withdraw .}
@@ -1170,13 +1194,22 @@ if {![file isdirectory $_gitdir]} {
 # _gitdir exists, so try loading the config
 load_config 0
 apply_config
-# try to set work tree from environment, falling back to core.worktree
-if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
-       set _gitworktree [get_config core.worktree]
-       if {$_gitworktree eq ""} {
-               set _gitworktree [file dirname [file normalize $_gitdir]]
+
+# v1.7.0 introduced --show-toplevel to return the canonical work-tree
+if {[package vsatisfies $_git_version 1.7.0]} {
+       set _gitworktree [git rev-parse --show-toplevel]
+} else {
+       # try to set work tree from environment, core.worktree or use
+       # cdup to obtain a relative path to the top of the worktree. If
+       # run from the top, the ./ prefix ensures normalize expands pwd.
+       if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
+               set _gitworktree [get_config core.worktree]
+               if {$_gitworktree eq ""} {
+                       set _gitworktree [file normalize ./[git rev-parse --show-cdup]]
+               }
        }
 }
+
 if {$_prefix ne {}} {
        if {$_gitworktree eq {}} {
                regsub -all {[^/]+/} $_prefix ../ cdup
@@ -2845,7 +2878,14 @@ bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
 
 set subcommand_args {}
 proc usage {} {
-       puts stderr "usage: $::argv0 $::subcommand $::subcommand_args"
+       set s "usage: $::argv0 $::subcommand $::subcommand_args"
+       if {[tk windowingsystem] eq "win32"} {
+               wm withdraw .
+               tk_messageBox -icon info -message $s \
+                       -title [mc "Usage"]
+       } else {
+               puts stderr $s
+       }
        exit 1
 }
 
@@ -2915,7 +2955,11 @@ blame {
                        if {[catch {
                                        set head [git rev-parse --verify $head]
                                } err]} {
-                               puts stderr $err
+                               if {[tk windowingsystem] eq "win32"} {
+                                       tk_messageBox -icon error -title [mc Error] -message $err
+                               } else {
+                                       puts stderr $err
+                               }
                                exit 1
                        }
                }
@@ -2938,7 +2982,12 @@ blame {
        }
        blame   {
                if {$head eq {} && ![file exists $path]} {
-                       puts stderr [mc "fatal: cannot stat path %s: No such file or directory" $path]
+                       catch {wm withdraw .}
+                       tk_messageBox \
+                               -icon error \
+                               -type ok \
+                               -title [mc "git-gui: fatal error"] \
+                               -message [mc "fatal: cannot stat path %s: No such file or directory" $path]
                        exit 1
                }
                blame::new $head $path $jump_spec
@@ -2949,18 +2998,19 @@ blame {
 citool -
 gui {
        if {[llength $argv] != 0} {
-               puts -nonewline stderr "usage: $argv0"
-               if {$subcommand ne {gui}
-                       && [file tail $argv0] ne "git-$subcommand"} {
-                       puts -nonewline stderr " $subcommand"
-               }
-               puts stderr {}
-               exit 1
+               usage
        }
        # fall through to setup UI for commits
 }
 default {
-       puts stderr "usage: $argv0 \[{blame|browser|citool}\]"
+       set err "usage: $argv0 \[{blame|browser|citool}\]"
+       if {[tk windowingsystem] eq "win32"} {
+               wm withdraw .
+               tk_messageBox -icon error -message $err \
+                       -title [mc "Usage"]
+       } else {
+               puts stderr $err
+       }
        exit 1
 }
 }
@@ -3262,6 +3312,7 @@ text $ui_diff -background white -foreground black \
        -xscrollcommand {.vpane.lower.diff.body.sbx set} \
        -yscrollcommand {.vpane.lower.diff.body.sby set} \
        -state disabled
+catch {$ui_diff configure -tabstyle wordprocessor}
 ${NS}::scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
        -command [list $ui_diff xview]
 ${NS}::scrollbar .vpane.lower.diff.body.sby -orient vertical \
@@ -3272,8 +3323,16 @@ pack $ui_diff -side left -fill both -expand 1
 pack .vpane.lower.diff.header -side top -fill x
 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
 
+foreach {n c} {0 black 1 red4 2 green4 3 yellow4 4 blue4 5 magenta4 6 cyan4 7 grey60} {
+       $ui_diff tag configure clr4$n -background $c
+       $ui_diff tag configure clri4$n -foreground $c
+       $ui_diff tag configure clr3$n -foreground $c
+       $ui_diff tag configure clri3$n -background $c
+}
+$ui_diff tag configure clr1 -font font_diffbold
+
 $ui_diff tag conf d_cr -elide true
-$ui_diff tag conf d_@ -foreground blue -font font_diffbold
+$ui_diff tag conf d_@ -font font_diffbold
 $ui_diff tag conf d_+ -foreground {#00a000}
 $ui_diff tag conf d_- -foreground red
 
index 2137ec9684d0acdb386e6b50e1b41aa7705c9746..61e358f960ca949cac5664c67442b82d0afca65f 100644 (file)
@@ -460,7 +460,14 @@ method _load {jump} {
        }
        if {$commit eq {}} {
                if {$do_textconv ne 0} {
-                       set fd [open |[list $textconv $path] r]
+                       # Run textconv with sh -c "..." to allow it to
+                       # contain command + arguments. On windows, just
+                       # call the filter command.
+                       if {![file executable [shellpath]]} {
+                               set fd [open |[linsert $textconv end $path] r]
+                       } else {
+                               set fd [open |[list [shellpath] -c "$textconv \"\$0\"" $path] r]
+                       }
                } else {
                        set fd [open $path r]
                }
index 63988773ba0d02af3dc91474b4a1669ecda27c5c..6e510ec2e39305e6d89de61ab2c925bcbc821a65 100644 (file)
@@ -53,7 +53,7 @@ constructor dialog {} {
                        return 1
                }
 
-       grid $w.rename.oldname_l $w.rename.oldname_m -sticky w  -padx {0 5}
+       grid $w.rename.oldname_l $w.rename.oldname_m -sticky we -padx {0 5}
        grid $w.rename.newname_l $w.rename.newname_t -sticky we -padx {0 5}
        grid columnconfigure $w.rename 1 -weight 1
        pack $w.rename -anchor nw -fill x -pady 5 -padx 5
index c628750276303fca620c9292463c4c59690c764f..dcf0711be0119a8f5ec42b48d97980330f7113b3 100644 (file)
@@ -294,7 +294,7 @@ proc start_show_diff {cont_info {add_opts {}}} {
        }
 
        lappend cmd -p
-       lappend cmd --no-color
+       lappend cmd --color
        if {$repo_config(gui.diffcontext) >= 1} {
                lappend cmd "-U$repo_config(gui.diffcontext)"
        }
@@ -332,6 +332,23 @@ proc start_show_diff {cont_info {add_opts {}}} {
        fileevent $fd readable [list read_diff $fd $cont_info]
 }
 
+proc parse_color_line {line} {
+       set start 0
+       set result ""
+       set markup [list]
+       set regexp {\033\[((?:\d+;)*\d+)?m}
+       while {[regexp -indices -start $start $regexp $line match code]} {
+               foreach {begin end} $match break
+               append result [string range $line $start [expr {$begin - 1}]]
+               lappend markup [string length $result] \
+                       [eval [linsert $code 0 string range $line]]
+               set start [incr end]
+       }
+       append result [string range $line $start end]
+       if {[llength $markup] < 4} {set markup {}}
+       return [list $result $markup]
+}
+
 proc read_diff {fd cont_info} {
        global ui_diff diff_active is_submodule_diff
        global is_3way_diff is_conflict_diff current_diff_header
@@ -340,6 +357,9 @@ proc read_diff {fd cont_info} {
 
        $ui_diff conf -state normal
        while {[gets $fd line] >= 0} {
+               foreach {line markup} [parse_color_line $line] break
+               set line [string map {\033 ^} $line]
+
                # -- Cleanup uninteresting diff header lines.
                #
                if {$::current_diff_inheader} {
@@ -434,11 +454,23 @@ proc read_diff {fd cont_info} {
                        }
                        }
                }
+               set mark [$ui_diff index "end - 1 line linestart"]
                $ui_diff insert end $line $tags
                if {[string index $line end] eq "\r"} {
                        $ui_diff tag add d_cr {end - 2c}
                }
                $ui_diff insert end "\n" $tags
+
+               foreach {posbegin colbegin posend colend} $markup {
+                       set prefix clr
+                       foreach style [split $colbegin ";"] {
+                               if {$style eq "7"} {append prefix i; continue}
+                               if {$style < 30 || $style > 47} {continue}
+                               set a "$mark linestart + $posbegin chars"
+                               set b "$mark linestart + $posend chars"
+                               catch {$ui_diff tag add $prefix$style $a $b}
+                       }
+               }
        }
        $ui_diff conf -state disabled
 
index d8d73acf2cdc770a794fa348aace0963dd7844ff..8bd3c5d75feea3dfe310adbfab705d8045aa8367 100644 (file)
@@ -8,41 +8,41 @@ msgid ""
 msgstr ""
 "Project-Id-Version: sv\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-01-26 15:47-0800\n"
-"PO-Revision-Date: 2010-01-28 13:57+0100\n"
+"POT-Creation-Date: 2010-09-12 21:11+0100\n"
+"PO-Revision-Date: 2010-09-12 21:12+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"
 
-#: git-gui.sh:41 git-gui.sh:793 git-gui.sh:807 git-gui.sh:820 git-gui.sh:903
-#: git-gui.sh:922
-msgid "git-gui: fatal error"
-msgstr "git-gui: ödesdigert fel"
-
-#: git-gui.sh:743
+#: git-gui.sh:781
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Ogiltigt teckensnitt angivet i %s:"
 
-#: git-gui.sh:779
+#: git-gui.sh:831
 msgid "Main Font"
 msgstr "Huvudteckensnitt"
 
-#: git-gui.sh:780
+#: git-gui.sh:832
 msgid "Diff/Console Font"
 msgstr "Diff/konsolteckensnitt"
 
-#: git-gui.sh:794
+#: git-gui.sh:845 git-gui.sh:859 git-gui.sh:872 git-gui.sh:955 git-gui.sh:974
+#: git-gui.sh:2964
+msgid "git-gui: fatal error"
+msgstr "git-gui: ödesdigert fel"
+
+#: git-gui.sh:846
 msgid "Cannot find git in PATH."
 msgstr "Hittar inte git i PATH."
 
-#: git-gui.sh:821
+#: git-gui.sh:873
 msgid "Cannot parse Git version string:"
 msgstr "Kan inte tolka versionssträng från Git:"
 
-#: git-gui.sh:839
+#: git-gui.sh:891
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -61,478 +61,478 @@ msgstr ""
 "\n"
 "Anta att \"%s\" är version 1.5.0?\n"
 
-#: git-gui.sh:1128
+#: git-gui.sh:1180
 msgid "Git directory not found:"
 msgstr "Git-katalogen hittades inte:"
 
-#: git-gui.sh:1146
+#: git-gui.sh:1201
 msgid "Cannot move to top of working directory:"
 msgstr "Kan inte gå till början på arbetskatalogen:"
 
-#: git-gui.sh:1154
+#: git-gui.sh:1209
 msgid "Cannot use bare repository:"
 msgstr "Kan inte använda naket arkiv:"
 
-#: git-gui.sh:1162
+#: git-gui.sh:1217
 msgid "No working directory"
 msgstr "Ingen arbetskatalog"
 
-#: git-gui.sh:1334 lib/checkout_op.tcl:306
+#: git-gui.sh:1389 lib/checkout_op.tcl:306
 msgid "Refreshing file status..."
 msgstr "Uppdaterar filstatus..."
 
-#: git-gui.sh:1390
+#: git-gui.sh:1445
 msgid "Scanning for modified files ..."
 msgstr "Söker efter ändrade filer..."
 
-#: git-gui.sh:1454
+#: git-gui.sh:1509
 msgid "Calling prepare-commit-msg hook..."
 msgstr ""
 "Anropar kroken för förberedelse av incheckningsmeddelande (prepare-commit-"
 "msg)..."
 
-#: git-gui.sh:1471
+#: git-gui.sh:1526
 msgid "Commit declined by prepare-commit-msg hook."
 msgstr ""
 "Incheckningen avvisades av kroken för förberedelse av incheckningsmeddelande "
 "(prepare-commit-msg)."
 
-#: git-gui.sh:1629 lib/browser.tcl:246
+#: git-gui.sh:1684 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Klar."
 
-#: git-gui.sh:1787
+#: git-gui.sh:1842
 #, tcl-format
 msgid "Displaying only %s of %s files."
 msgstr "Visar endast %s av %s filer."
 
-#: git-gui.sh:1913
+#: git-gui.sh:1968
 msgid "Unmodified"
 msgstr "Oförändrade"
 
-#: git-gui.sh:1915
+#: git-gui.sh:1970
 msgid "Modified, not staged"
 msgstr "Förändrade, ej köade"
 
-#: git-gui.sh:1916 git-gui.sh:1924
+#: git-gui.sh:1971 git-gui.sh:1979
 msgid "Staged for commit"
 msgstr "Köade för incheckning"
 
-#: git-gui.sh:1917 git-gui.sh:1925
+#: git-gui.sh:1972 git-gui.sh:1980
 msgid "Portions staged for commit"
 msgstr "Delar köade för incheckning"
 
-#: git-gui.sh:1918 git-gui.sh:1926
+#: git-gui.sh:1973 git-gui.sh:1981
 msgid "Staged for commit, missing"
 msgstr "Köade för incheckning, saknade"
 
-#: git-gui.sh:1920
+#: git-gui.sh:1975
 msgid "File type changed, not staged"
 msgstr "Filtyp ändrad, ej köade"
 
-#: git-gui.sh:1921
+#: git-gui.sh:1976
 msgid "File type changed, staged"
 msgstr "Filtyp ändrad, köade"
 
-#: git-gui.sh:1923
+#: git-gui.sh:1978
 msgid "Untracked, not staged"
 msgstr "Ej spårade, ej köade"
 
-#: git-gui.sh:1928
+#: git-gui.sh:1983
 msgid "Missing"
 msgstr "Saknade"
 
-#: git-gui.sh:1929
+#: git-gui.sh:1984
 msgid "Staged for removal"
 msgstr "Köade för borttagning"
 
-#: git-gui.sh:1930
+#: git-gui.sh:1985
 msgid "Staged for removal, still present"
 msgstr "Köade för borttagning, fortfarande närvarande"
 
-#: git-gui.sh:1932 git-gui.sh:1933 git-gui.sh:1934 git-gui.sh:1935
-#: git-gui.sh:1936 git-gui.sh:1937
+#: git-gui.sh:1987 git-gui.sh:1988 git-gui.sh:1989 git-gui.sh:1990
+#: git-gui.sh:1991 git-gui.sh:1992
 msgid "Requires merge resolution"
 msgstr "Kräver konflikthantering efter sammanslagning"
 
-#: git-gui.sh:1972
+#: git-gui.sh:2027
 msgid "Starting gitk... please wait..."
 msgstr "Startar gitk... vänta..."
 
-#: git-gui.sh:1984
+#: git-gui.sh:2039
 msgid "Couldn't find gitk in PATH"
 msgstr "Hittade inte gitk i PATH."
 
-#: git-gui.sh:2043
+#: git-gui.sh:2098
 msgid "Couldn't find git gui in PATH"
 msgstr "Hittade inte git gui i PATH."
 
-#: git-gui.sh:2455 lib/choose_repository.tcl:36
+#: git-gui.sh:2515 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Arkiv"
 
-#: git-gui.sh:2456
+#: git-gui.sh:2516
 msgid "Edit"
 msgstr "Redigera"
 
-#: git-gui.sh:2458 lib/choose_rev.tcl:561
+#: git-gui.sh:2518 lib/choose_rev.tcl:566
 msgid "Branch"
 msgstr "Gren"
 
-#: git-gui.sh:2461 lib/choose_rev.tcl:548
+#: git-gui.sh:2521 lib/choose_rev.tcl:553
 msgid "Commit@@noun"
 msgstr "Incheckning"
 
-#: git-gui.sh:2464 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:2524 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "Slå ihop"
 
-#: git-gui.sh:2465 lib/choose_rev.tcl:557
+#: git-gui.sh:2525 lib/choose_rev.tcl:562
 msgid "Remote"
 msgstr "Fjärrarkiv"
 
-#: git-gui.sh:2468
+#: git-gui.sh:2528
 msgid "Tools"
 msgstr "Verktyg"
 
-#: git-gui.sh:2477
+#: git-gui.sh:2537
 msgid "Explore Working Copy"
 msgstr "Utforska arbetskopia"
 
-#: git-gui.sh:2483
+#: git-gui.sh:2543
 msgid "Browse Current Branch's Files"
 msgstr "Bläddra i grenens filer"
 
-#: git-gui.sh:2487
+#: git-gui.sh:2547
 msgid "Browse Branch Files..."
 msgstr "Bläddra filer på gren..."
 
-#: git-gui.sh:2492
+#: git-gui.sh:2552
 msgid "Visualize Current Branch's History"
 msgstr "Visualisera grenens historik"
 
-#: git-gui.sh:2496
+#: git-gui.sh:2556
 msgid "Visualize All Branch History"
 msgstr "Visualisera alla grenars historik"
 
-#: git-gui.sh:2503
+#: git-gui.sh:2563
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Bläddra i filer för %s"
 
-#: git-gui.sh:2505
+#: git-gui.sh:2565
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Visualisera historik för %s"
 
-#: git-gui.sh:2510 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2570 lib/database.tcl:40 lib/database.tcl:66
 msgid "Database Statistics"
 msgstr "Databasstatistik"
 
-#: git-gui.sh:2513 lib/database.tcl:34
+#: git-gui.sh:2573 lib/database.tcl:33
 msgid "Compress Database"
 msgstr "Komprimera databas"
 
-#: git-gui.sh:2516
+#: git-gui.sh:2576
 msgid "Verify Database"
 msgstr "Verifiera databas"
 
-#: git-gui.sh:2523 git-gui.sh:2527 git-gui.sh:2531 lib/shortcut.tcl:8
+#: git-gui.sh:2583 git-gui.sh:2587 git-gui.sh:2591 lib/shortcut.tcl:8
 #: lib/shortcut.tcl:40 lib/shortcut.tcl:72
 msgid "Create Desktop Icon"
 msgstr "Skapa skrivbordsikon"
 
-#: git-gui.sh:2539 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
+#: git-gui.sh:2599 lib/choose_repository.tcl:188 lib/choose_repository.tcl:196
 msgid "Quit"
 msgstr "Avsluta"
 
-#: git-gui.sh:2547
+#: git-gui.sh:2607
 msgid "Undo"
 msgstr "Ångra"
 
-#: git-gui.sh:2550
+#: git-gui.sh:2610
 msgid "Redo"
 msgstr "Gör om"
 
-#: git-gui.sh:2554 git-gui.sh:3109
+#: git-gui.sh:2614 git-gui.sh:3190
 msgid "Cut"
 msgstr "Klipp ut"
 
-#: git-gui.sh:2557 git-gui.sh:3112 git-gui.sh:3186 git-gui.sh:3259
+#: git-gui.sh:2617 git-gui.sh:3193 git-gui.sh:3267 git-gui.sh:3340
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Kopiera"
 
-#: git-gui.sh:2560 git-gui.sh:3115
+#: git-gui.sh:2620 git-gui.sh:3196
 msgid "Paste"
 msgstr "Klistra in"
 
-#: git-gui.sh:2563 git-gui.sh:3118 lib/branch_delete.tcl:26
-#: lib/remote_branch_delete.tcl:38
+#: git-gui.sh:2623 git-gui.sh:3199 lib/branch_delete.tcl:28
+#: lib/remote_branch_delete.tcl:39
 msgid "Delete"
 msgstr "Ta bort"
 
-#: git-gui.sh:2567 git-gui.sh:3122 git-gui.sh:3263 lib/console.tcl:71
+#: git-gui.sh:2627 git-gui.sh:3203 git-gui.sh:3344 lib/console.tcl:71
 msgid "Select All"
 msgstr "Markera alla"
 
-#: git-gui.sh:2576
+#: git-gui.sh:2636
 msgid "Create..."
 msgstr "Skapa..."
 
-#: git-gui.sh:2582
+#: git-gui.sh:2642
 msgid "Checkout..."
 msgstr "Checka ut..."
 
-#: git-gui.sh:2588
+#: git-gui.sh:2648
 msgid "Rename..."
 msgstr "Byt namn..."
 
-#: git-gui.sh:2593
+#: git-gui.sh:2653
 msgid "Delete..."
 msgstr "Ta bort..."
 
-#: git-gui.sh:2598
+#: git-gui.sh:2658
 msgid "Reset..."
 msgstr "Återställ..."
 
-#: git-gui.sh:2608
+#: git-gui.sh:2668
 msgid "Done"
 msgstr "Färdig"
 
-#: git-gui.sh:2610
+#: git-gui.sh:2670
 msgid "Commit@@verb"
 msgstr "Checka in"
 
-#: git-gui.sh:2619 git-gui.sh:3050
+#: git-gui.sh:2679 git-gui.sh:3131
 msgid "New Commit"
 msgstr "Ny incheckning"
 
-#: git-gui.sh:2627 git-gui.sh:3057
+#: git-gui.sh:2687 git-gui.sh:3138
 msgid "Amend Last Commit"
 msgstr "Lägg till föregående incheckning"
 
-#: git-gui.sh:2637 git-gui.sh:3011 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2697 git-gui.sh:3092 lib/remote_branch_delete.tcl:101
 msgid "Rescan"
 msgstr "Sök på nytt"
 
-#: git-gui.sh:2643
+#: git-gui.sh:2703
 msgid "Stage To Commit"
 msgstr "Köa för incheckning"
 
-#: git-gui.sh:2649
+#: git-gui.sh:2709
 msgid "Stage Changed Files To Commit"
 msgstr "Köa ändrade filer för incheckning"
 
-#: git-gui.sh:2655
+#: git-gui.sh:2715
 msgid "Unstage From Commit"
 msgstr "Ta bort från incheckningskö"
 
-#: git-gui.sh:2661 lib/index.tcl:412
+#: git-gui.sh:2721 lib/index.tcl:415
 msgid "Revert Changes"
 msgstr "Återställ ändringar"
 
-#: git-gui.sh:2669 git-gui.sh:3310 git-gui.sh:3341
+#: git-gui.sh:2729 git-gui.sh:3391 git-gui.sh:3422
 msgid "Show Less Context"
 msgstr "Visa mindre sammanhang"
 
-#: git-gui.sh:2673 git-gui.sh:3314 git-gui.sh:3345
+#: git-gui.sh:2733 git-gui.sh:3395 git-gui.sh:3426
 msgid "Show More Context"
 msgstr "Visa mer sammanhang"
 
-#: git-gui.sh:2680 git-gui.sh:3024 git-gui.sh:3133
+#: git-gui.sh:2740 git-gui.sh:3105 git-gui.sh:3214
 msgid "Sign Off"
 msgstr "Skriv under"
 
-#: git-gui.sh:2696
+#: git-gui.sh:2756
 msgid "Local Merge..."
 msgstr "Lokal sammanslagning..."
 
-#: git-gui.sh:2701
+#: git-gui.sh:2761
 msgid "Abort Merge..."
 msgstr "Avbryt sammanslagning..."
 
-#: git-gui.sh:2713 git-gui.sh:2741
+#: git-gui.sh:2773 git-gui.sh:2801
 msgid "Add..."
 msgstr "Lägg till..."
 
-#: git-gui.sh:2717
+#: git-gui.sh:2777
 msgid "Push..."
 msgstr "Sänd..."
 
-#: git-gui.sh:2721
+#: git-gui.sh:2781
 msgid "Delete Branch..."
 msgstr "Ta bort gren..."
 
-#: git-gui.sh:2731 git-gui.sh:3292
+#: git-gui.sh:2791 git-gui.sh:3373
 msgid "Options..."
 msgstr "Alternativ..."
 
-#: git-gui.sh:2742
+#: git-gui.sh:2802
 msgid "Remove..."
 msgstr "Ta bort..."
 
-#: git-gui.sh:2751 lib/choose_repository.tcl:50
+#: git-gui.sh:2811 lib/choose_repository.tcl:50
 msgid "Help"
 msgstr "Hjälp"
 
-#: git-gui.sh:2755 git-gui.sh:2759 lib/about.tcl:14
+#: git-gui.sh:2815 git-gui.sh:2819 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "Om %s"
 
-#: git-gui.sh:2783
+#: git-gui.sh:2843
 msgid "Online Documentation"
 msgstr "Webbdokumentation"
 
-#: git-gui.sh:2786 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+#: git-gui.sh:2846 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
 msgid "Show SSH Key"
 msgstr "Visa SSH-nyckel"
 
-#: git-gui.sh:2893
+#: git-gui.sh:2965
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "ödesdigert: kunde inte ta status på sökvägen %s: Fil eller katalog saknas"
 
-#: git-gui.sh:2926
+#: git-gui.sh:2997
 msgid "Current Branch:"
 msgstr "Aktuell gren:"
 
-#: git-gui.sh:2947
+#: git-gui.sh:3023
 msgid "Staged Changes (Will Commit)"
 msgstr "Köade ändringar (kommer att checkas in)"
 
-#: git-gui.sh:2967
+#: git-gui.sh:3043
 msgid "Unstaged Changes"
 msgstr "Oköade ändringar"
 
-#: git-gui.sh:3017
+#: git-gui.sh:3098
 msgid "Stage Changed"
 msgstr "Köa ändrade"
 
-#: git-gui.sh:3036 lib/transport.tcl:104 lib/transport.tcl:193
+#: git-gui.sh:3117 lib/transport.tcl:107 lib/transport.tcl:196
 msgid "Push"
 msgstr "Sänd"
 
-#: git-gui.sh:3071
+#: git-gui.sh:3152
 msgid "Initial Commit Message:"
 msgstr "Inledande incheckningsmeddelande:"
 
-#: git-gui.sh:3072
+#: git-gui.sh:3153
 msgid "Amended Commit Message:"
 msgstr "Utökat incheckningsmeddelande:"
 
-#: git-gui.sh:3073
+#: git-gui.sh:3154
 msgid "Amended Initial Commit Message:"
 msgstr "Utökat inledande incheckningsmeddelande:"
 
-#: git-gui.sh:3074
+#: git-gui.sh:3155
 msgid "Amended Merge Commit Message:"
 msgstr "Utökat incheckningsmeddelande för sammanslagning:"
 
-#: git-gui.sh:3075
+#: git-gui.sh:3156
 msgid "Merge Commit Message:"
 msgstr "Incheckningsmeddelande för sammanslagning:"
 
-#: git-gui.sh:3076
+#: git-gui.sh:3157
 msgid "Commit Message:"
 msgstr "Incheckningsmeddelande:"
 
-#: git-gui.sh:3125 git-gui.sh:3267 lib/console.tcl:73
+#: git-gui.sh:3206 git-gui.sh:3348 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Kopiera alla"
 
-#: git-gui.sh:3149 lib/blame.tcl:104
+#: git-gui.sh:3230 lib/blame.tcl:104
 msgid "File:"
 msgstr "Fil:"
 
-#: git-gui.sh:3255
+#: git-gui.sh:3336
 msgid "Refresh"
 msgstr "Uppdatera"
 
-#: git-gui.sh:3276
+#: git-gui.sh:3357
 msgid "Decrease Font Size"
 msgstr "Minska teckensnittsstorlek"
 
-#: git-gui.sh:3280
+#: git-gui.sh:3361
 msgid "Increase Font Size"
 msgstr "Öka teckensnittsstorlek"
 
-#: git-gui.sh:3288 lib/blame.tcl:281
+#: git-gui.sh:3369 lib/blame.tcl:281
 msgid "Encoding"
 msgstr "Teckenkodning"
 
-#: git-gui.sh:3299
+#: git-gui.sh:3380
 msgid "Apply/Reverse Hunk"
 msgstr "Använd/återställ del"
 
-#: git-gui.sh:3304
+#: git-gui.sh:3385
 msgid "Apply/Reverse Line"
 msgstr "Använd/återställ rad"
 
-#: git-gui.sh:3323
+#: git-gui.sh:3404
 msgid "Run Merge Tool"
 msgstr "Starta verktyg för sammanslagning"
 
-#: git-gui.sh:3328
+#: git-gui.sh:3409
 msgid "Use Remote Version"
 msgstr "Använd versionen från fjärrarkivet"
 
-#: git-gui.sh:3332
+#: git-gui.sh:3413
 msgid "Use Local Version"
 msgstr "Använd lokala versionen"
 
-#: git-gui.sh:3336
+#: git-gui.sh:3417
 msgid "Revert To Base"
 msgstr "Återställ till basversionen"
 
-#: git-gui.sh:3354
+#: git-gui.sh:3435
 msgid "Visualize These Changes In The Submodule"
 msgstr "Visualisera ändringarna i undermodulen"
 
-#: git-gui.sh:3358
+#: git-gui.sh:3439
 msgid "Visualize Current Branch History In The Submodule"
 msgstr "Visualisera grenens historik i undermodulen"
 
-#: git-gui.sh:3362
+#: git-gui.sh:3443
 msgid "Visualize All Branch History In The Submodule"
 msgstr "Visualisera alla grenars historik i undermodulen"
 
-#: git-gui.sh:3367
+#: git-gui.sh:3448
 msgid "Start git gui In The Submodule"
 msgstr "Starta git gui i undermodulen"
 
-#: git-gui.sh:3389
+#: git-gui.sh:3483
 msgid "Unstage Hunk From Commit"
 msgstr "Ta bort del ur incheckningskö"
 
-#: git-gui.sh:3391
+#: git-gui.sh:3485
 msgid "Unstage Lines From Commit"
 msgstr "Ta bort rader ur incheckningskö"
 
-#: git-gui.sh:3393
+#: git-gui.sh:3487
 msgid "Unstage Line From Commit"
 msgstr "Ta bort rad ur incheckningskö"
 
-#: git-gui.sh:3396
+#: git-gui.sh:3490
 msgid "Stage Hunk For Commit"
 msgstr "Ställ del i incheckningskö"
 
-#: git-gui.sh:3398
+#: git-gui.sh:3492
 msgid "Stage Lines For Commit"
 msgstr "Ställ rader i incheckningskö"
 
-#: git-gui.sh:3400
+#: git-gui.sh:3494
 msgid "Stage Line For Commit"
 msgstr "Ställ rad i incheckningskö"
 
-#: git-gui.sh:3424
+#: git-gui.sh:3519
 msgid "Initializing..."
 msgstr "Initierar..."
 
-#: git-gui.sh:3541
+#: git-gui.sh:3658
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -549,7 +549,7 @@ msgstr ""
 "av %s:\n"
 "\n"
 
-#: git-gui.sh:3570
+#: git-gui.sh:3687
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -559,7 +559,7 @@ msgstr ""
 "Detta beror på ett känt problem med\n"
 "Tcl-binären som följer med Cygwin."
 
-#: git-gui.sh:3575
+#: git-gui.sh:3692
 #, tcl-format
 msgid ""
 "\n"
@@ -613,132 +613,132 @@ msgstr "Klandra föräldraincheckning"
 msgid "Reading %s..."
 msgstr "Läser %s..."
 
-#: lib/blame.tcl:557
+#: lib/blame.tcl:581
 msgid "Loading copy/move tracking annotations..."
 msgstr "Läser annoteringar för kopiering/flyttning..."
 
-#: lib/blame.tcl:577
+#: lib/blame.tcl:601
 msgid "lines annotated"
 msgstr "rader annoterade"
 
-#: lib/blame.tcl:769
+#: lib/blame.tcl:793
 msgid "Loading original location annotations..."
 msgstr "Läser in annotering av originalplacering..."
 
-#: lib/blame.tcl:772
+#: lib/blame.tcl:796
 msgid "Annotation complete."
 msgstr "Annotering fullbordad."
 
-#: lib/blame.tcl:802
+#: lib/blame.tcl:826
 msgid "Busy"
 msgstr "Upptagen"
 
-#: lib/blame.tcl:803
+#: lib/blame.tcl:827
 msgid "Annotation process is already running."
 msgstr "Annoteringsprocess körs redan."
 
-#: lib/blame.tcl:842
+#: lib/blame.tcl:866
 msgid "Running thorough copy detection..."
 msgstr "Kör grundlig kopieringsigenkänning..."
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:934
 msgid "Loading annotation..."
 msgstr "Läser in annotering..."
 
-#: lib/blame.tcl:963
+#: lib/blame.tcl:987
 msgid "Author:"
 msgstr "Författare:"
 
-#: lib/blame.tcl:967
+#: lib/blame.tcl:991
 msgid "Committer:"
 msgstr "Incheckare:"
 
-#: lib/blame.tcl:972
+#: lib/blame.tcl:996
 msgid "Original File:"
 msgstr "Ursprunglig fil:"
 
-#: lib/blame.tcl:1020
+#: lib/blame.tcl:1044
 msgid "Cannot find HEAD commit:"
 msgstr "Hittar inte incheckning för HEAD:"
 
-#: lib/blame.tcl:1075
+#: lib/blame.tcl:1099
 msgid "Cannot find parent commit:"
 msgstr "Hittar inte föräldraincheckning:"
 
-#: lib/blame.tcl:1090
+#: lib/blame.tcl:1114
 msgid "Unable to display parent"
 msgstr "Kan inte visa förälder"
 
-#: lib/blame.tcl:1091 lib/diff.tcl:320
+#: lib/blame.tcl:1115 lib/diff.tcl:323
 msgid "Error loading diff:"
 msgstr "Fel vid inläsning av differens:"
 
-#: lib/blame.tcl:1231
+#: lib/blame.tcl:1255
 msgid "Originally By:"
 msgstr "Ursprungligen av:"
 
-#: lib/blame.tcl:1237
+#: lib/blame.tcl:1261
 msgid "In File:"
 msgstr "I filen:"
 
-#: lib/blame.tcl:1242
+#: lib/blame.tcl:1266
 msgid "Copied Or Moved Here By:"
 msgstr "Kopierad eller flyttad hit av:"
 
-#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
+#: lib/branch_checkout.tcl:16 lib/branch_checkout.tcl:21
 msgid "Checkout Branch"
 msgstr "Checka ut gren"
 
-#: lib/branch_checkout.tcl:23
+#: lib/branch_checkout.tcl:26
 msgid "Checkout"
 msgstr "Checka ut"
 
-#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:579 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
-#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
-#: lib/transport.tcl:108
+#: lib/branch_checkout.tcl:30 lib/branch_create.tcl:37
+#: lib/branch_delete.tcl:34 lib/branch_rename.tcl:32 lib/browser.tcl:286
+#: lib/checkout_op.tcl:579 lib/choose_font.tcl:45 lib/merge.tcl:172
+#: lib/option.tcl:127 lib/remote_add.tcl:34 lib/remote_branch_delete.tcl:43
+#: lib/tools_dlg.tcl:41 lib/tools_dlg.tcl:202 lib/tools_dlg.tcl:345
+#: lib/transport.tcl:111
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
+#: lib/branch_checkout.tcl:35 lib/browser.tcl:291 lib/tools_dlg.tcl:321
 msgid "Revision"
 msgstr "Revision"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
+#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:69 lib/option.tcl:287
 msgid "Options"
 msgstr "Alternativ"
 
-#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92
+#: lib/branch_checkout.tcl:42 lib/branch_create.tcl:92
 msgid "Fetch Tracking Branch"
 msgstr "Hämta spårande gren"
 
-#: lib/branch_checkout.tcl:44
+#: lib/branch_checkout.tcl:47
 msgid "Detach From Local Branch"
 msgstr "Koppla bort från lokal gren"
 
-#: lib/branch_create.tcl:22
+#: lib/branch_create.tcl:23
 msgid "Create Branch"
 msgstr "Skapa gren"
 
-#: lib/branch_create.tcl:27
+#: lib/branch_create.tcl:28
 msgid "Create New Branch"
 msgstr "Skapa ny gren"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:381
+#: lib/branch_create.tcl:33 lib/choose_repository.tcl:389
 msgid "Create"
 msgstr "Skapa"
 
-#: lib/branch_create.tcl:40
+#: lib/branch_create.tcl:42
 msgid "Branch Name"
 msgstr "Namn på gren"
 
-#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
+#: lib/branch_create.tcl:44 lib/remote_add.tcl:41 lib/tools_dlg.tcl:51
 msgid "Name:"
 msgstr "Namn:"
 
-#: lib/branch_create.tcl:58
+#: lib/branch_create.tcl:57
 msgid "Match Tracking Branch Name"
 msgstr "Använd namn på spårad gren"
 
@@ -766,41 +766,41 @@ msgstr "Återställ"
 msgid "Checkout After Creation"
 msgstr "Checka ut när skapad"
 
-#: lib/branch_create.tcl:131
+#: lib/branch_create.tcl:132
 msgid "Please select a tracking branch."
 msgstr "Välj en gren att spåra."
 
-#: lib/branch_create.tcl:140
+#: lib/branch_create.tcl:141
 #, tcl-format
 msgid "Tracking branch %s is not a branch in the remote repository."
 msgstr "Den spårade grenen %s är inte en gren i fjärrarkivet."
 
-#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86
+#: lib/branch_create.tcl:154 lib/branch_rename.tcl:92
 msgid "Please supply a branch name."
 msgstr "Ange ett namn för grenen."
 
-#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106
+#: lib/branch_create.tcl:165 lib/branch_rename.tcl:112
 #, tcl-format
 msgid "'%s' is not an acceptable branch name."
 msgstr "\"%s\" kan inte användas som namn på grenen."
 
-#: lib/branch_delete.tcl:15
+#: lib/branch_delete.tcl:16
 msgid "Delete Branch"
 msgstr "Ta bort gren"
 
-#: lib/branch_delete.tcl:20
+#: lib/branch_delete.tcl:21
 msgid "Delete Local Branch"
 msgstr "Ta bort lokal gren"
 
-#: lib/branch_delete.tcl:37
+#: lib/branch_delete.tcl:39
 msgid "Local Branches"
 msgstr "Lokala grenar"
 
-#: lib/branch_delete.tcl:52
+#: lib/branch_delete.tcl:51
 msgid "Delete Only If Merged Into"
 msgstr "Ta bara bort om sammanslagen med"
 
-#: lib/branch_delete.tcl:54 lib/remote_branch_delete.tcl:119
+#: lib/branch_delete.tcl:53 lib/remote_branch_delete.tcl:120
 msgid "Always (Do not perform merge checks)"
 msgstr "Alltid (utför inte sammanslagningstest)"
 
@@ -809,7 +809,7 @@ msgstr "Alltid (utför inte sammanslagningstest)"
 msgid "The following branches are not completely merged into %s:"
 msgstr "Följande grenar är inte till fullo sammanslagna med %s:"
 
-#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:217
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:218
 msgid ""
 "Recovering deleted branches is difficult.\n"
 "\n"
@@ -828,32 +828,32 @@ msgstr ""
 "Kunde inte ta bort grenar:\n"
 "%s"
 
-#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22
+#: lib/branch_rename.tcl:15 lib/branch_rename.tcl:23
 msgid "Rename Branch"
 msgstr "Byt namn på gren"
 
-#: lib/branch_rename.tcl:26
+#: lib/branch_rename.tcl:28
 msgid "Rename"
 msgstr "Byt namn"
 
-#: lib/branch_rename.tcl:36
+#: lib/branch_rename.tcl:38
 msgid "Branch:"
 msgstr "Gren:"
 
-#: lib/branch_rename.tcl:39
+#: lib/branch_rename.tcl:46
 msgid "New Name:"
 msgstr "Nytt namn:"
 
-#: lib/branch_rename.tcl:75
+#: lib/branch_rename.tcl:81
 msgid "Please select a branch to rename."
 msgstr "Välj en gren att byta namn på."
 
-#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:202
+#: lib/branch_rename.tcl:102 lib/checkout_op.tcl:202
 #, tcl-format
 msgid "Branch '%s' already exists."
 msgstr "Grenen \"%s\" finns redan."
 
-#: lib/branch_rename.tcl:117
+#: lib/branch_rename.tcl:123
 #, tcl-format
 msgid "Failed to rename '%s'."
 msgstr "Kunde inte byta namn på \"%s\"."
@@ -862,7 +862,7 @@ msgstr "Kunde inte byta namn på \"%s\"."
 msgid "Starting..."
 msgstr "Startar..."
 
-#: lib/browser.tcl:26
+#: lib/browser.tcl:27
 msgid "File Browser"
 msgstr "Filbläddrare"
 
@@ -875,13 +875,13 @@ msgstr "Läser %s..."
 msgid "[Up To Parent]"
 msgstr "[Upp till förälder]"
 
-#: lib/browser.tcl:267 lib/browser.tcl:273
+#: lib/browser.tcl:269 lib/browser.tcl:276
 msgid "Browse Branch Files"
 msgstr "Bläddra filer på grenen"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:398
-#: lib/choose_repository.tcl:486 lib/choose_repository.tcl:497
-#: lib/choose_repository.tcl:1028
+#: lib/browser.tcl:282 lib/choose_repository.tcl:404
+#: lib/choose_repository.tcl:491 lib/choose_repository.tcl:500
+#: lib/choose_repository.tcl:1027
 msgid "Browse"
 msgstr "Bläddra"
 
@@ -895,8 +895,8 @@ msgstr "Hämtar %s från %s"
 msgid "fatal: Cannot resolve %s"
 msgstr "ödesdigert: Kunde inte slå upp %s"
 
-#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:31
-#: lib/sshkey.tcl:53
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:30
+#: lib/sshkey.tcl:55
 msgid "Close"
 msgstr "Stäng"
 
@@ -1008,7 +1008,7 @@ msgstr "Det kanske inte är så enkelt att återskapa förlorade incheckningar."
 msgid "Reset '%s'?"
 msgstr "Återställa \"%s\"?"
 
-#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:343
+#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:336
 msgid "Visualize"
 msgstr "Visualisera"
 
@@ -1029,23 +1029,23 @@ msgstr ""
 "\n"
 "Detta skulle inte ha hänt. %s kommer nu stängas och ge upp."
 
-#: lib/choose_font.tcl:39
+#: lib/choose_font.tcl:41
 msgid "Select"
 msgstr "Välj"
 
-#: lib/choose_font.tcl:53
+#: lib/choose_font.tcl:55
 msgid "Font Family"
 msgstr "Teckensnittsfamilj"
 
-#: lib/choose_font.tcl:74
+#: lib/choose_font.tcl:76
 msgid "Font Size"
 msgstr "Storlek"
 
-#: lib/choose_font.tcl:91
+#: lib/choose_font.tcl:93
 msgid "Font Example"
 msgstr "Exempel"
 
-#: lib/choose_font.tcl:103
+#: lib/choose_font.tcl:105
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
@@ -1057,7 +1057,7 @@ msgstr ""
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:386
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:394
 msgid "Create New Repository"
 msgstr "Skapa nytt arkiv"
 
@@ -1065,76 +1065,76 @@ msgstr "Skapa nytt arkiv"
 msgid "New..."
 msgstr "Nytt..."
 
-#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:471
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:478
 msgid "Clone Existing Repository"
 msgstr "Klona befintligt arkiv"
 
-#: lib/choose_repository.tcl:106
+#: lib/choose_repository.tcl:111
 msgid "Clone..."
 msgstr "Klona..."
 
-#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:1016
+#: lib/choose_repository.tcl:118 lib/choose_repository.tcl:1017
 msgid "Open Existing Repository"
 msgstr "Öppna befintligt arkiv"
 
-#: lib/choose_repository.tcl:119
+#: lib/choose_repository.tcl:124
 msgid "Open..."
 msgstr "Öppna..."
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:137
 msgid "Recent Repositories"
 msgstr "Senaste arkiven"
 
-#: lib/choose_repository.tcl:138
+#: lib/choose_repository.tcl:143
 msgid "Open Recent Repository:"
 msgstr "Öppna tidigare arkiv:"
 
-#: lib/choose_repository.tcl:306 lib/choose_repository.tcl:313
-#: lib/choose_repository.tcl:320
+#: lib/choose_repository.tcl:313 lib/choose_repository.tcl:320
+#: lib/choose_repository.tcl:327
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Kunde inte skapa arkivet %s:"
 
-#: lib/choose_repository.tcl:391
+#: lib/choose_repository.tcl:399
 msgid "Directory:"
 msgstr "Katalog:"
 
-#: lib/choose_repository.tcl:423 lib/choose_repository.tcl:550
-#: lib/choose_repository.tcl:1052
+#: lib/choose_repository.tcl:429 lib/choose_repository.tcl:550
+#: lib/choose_repository.tcl:1051
 msgid "Git Repository"
 msgstr "Gitarkiv"
 
-#: lib/choose_repository.tcl:448
+#: lib/choose_repository.tcl:454
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Katalogen %s finns redan."
 
-#: lib/choose_repository.tcl:452
+#: lib/choose_repository.tcl:458
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Filen %s finns redan."
 
-#: lib/choose_repository.tcl:466
+#: lib/choose_repository.tcl:473
 msgid "Clone"
 msgstr "Klona"
 
-#: lib/choose_repository.tcl:479
+#: lib/choose_repository.tcl:486
 msgid "Source Location:"
 msgstr "Plats för källkod:"
 
-#: lib/choose_repository.tcl:490
+#: lib/choose_repository.tcl:495
 msgid "Target Directory:"
 msgstr "Målkatalog:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:505
 msgid "Clone Type:"
 msgstr "Typ av klon:"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:510
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (snabb, semiredundant, hårda länkar)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:515
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Full kopia (långsammare, redundant säkerhetskopia)"
 
@@ -1144,7 +1144,7 @@ msgstr "Delad (snabbast, rekommenderas ej, ingen säkerhetskopia)"
 
 #: lib/choose_repository.tcl:556 lib/choose_repository.tcl:603
 #: lib/choose_repository.tcl:749 lib/choose_repository.tcl:819
-#: lib/choose_repository.tcl:1058 lib/choose_repository.tcl:1066
+#: lib/choose_repository.tcl:1057 lib/choose_repository.tcl:1065
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Inte ett Gitarkiv: %s"
@@ -1258,8 +1258,8 @@ msgstr "Kunde inte slå upp %s till någon incheckning."
 msgid "Creating working directory"
 msgstr "Skapar arbetskatalog"
 
-#: lib/choose_repository.tcl:939 lib/index.tcl:67 lib/index.tcl:130
-#: lib/index.tcl:198
+#: lib/choose_repository.tcl:939 lib/index.tcl:70 lib/index.tcl:133
+#: lib/index.tcl:201
 msgid "files"
 msgstr "filer"
 
@@ -1267,20 +1267,20 @@ msgstr "filer"
 msgid "Initial file checkout failed."
 msgstr "Inledande filutcheckning misslyckades."
 
-#: lib/choose_repository.tcl:1011
+#: lib/choose_repository.tcl:1012
 msgid "Open"
 msgstr "Öppna"
 
-#: lib/choose_repository.tcl:1021
+#: lib/choose_repository.tcl:1022
 msgid "Repository:"
 msgstr "Arkiv:"
 
-#: lib/choose_repository.tcl:1072
+#: lib/choose_repository.tcl:1071
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Kunde inte öppna arkivet %s:"
 
-#: lib/choose_rev.tcl:53
+#: lib/choose_rev.tcl:52
 msgid "This Detached Checkout"
 msgstr "Denna frånkopplade utcheckning"
 
@@ -1288,36 +1288,36 @@ msgstr "Denna frånkopplade utcheckning"
 msgid "Revision Expression:"
 msgstr "Revisionsuttryck:"
 
-#: lib/choose_rev.tcl:74
+#: lib/choose_rev.tcl:72
 msgid "Local Branch"
 msgstr "Lokal gren"
 
-#: lib/choose_rev.tcl:79
+#: lib/choose_rev.tcl:77
 msgid "Tracking Branch"
 msgstr "Spårande gren"
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
+#: lib/choose_rev.tcl:82 lib/choose_rev.tcl:543
 msgid "Tag"
 msgstr "Tagg"
 
-#: lib/choose_rev.tcl:317
+#: lib/choose_rev.tcl:321
 #, tcl-format
 msgid "Invalid revision: %s"
 msgstr "Ogiltig revision: %s"
 
-#: lib/choose_rev.tcl:338
+#: lib/choose_rev.tcl:342
 msgid "No revision selected."
 msgstr "Ingen revision vald."
 
-#: lib/choose_rev.tcl:346
+#: lib/choose_rev.tcl:350
 msgid "Revision expression is empty."
 msgstr "Revisionsuttrycket är tomt."
 
-#: lib/choose_rev.tcl:531
+#: lib/choose_rev.tcl:536
 msgid "Updated"
 msgstr "Uppdaterad"
 
-#: lib/choose_rev.tcl:559
+#: lib/choose_rev.tcl:564
 msgid "URL"
 msgstr "Webbadress"
 
@@ -1508,31 +1508,31 @@ msgstr "Lyckades"
 msgid "Error: Command Failed"
 msgstr "Fel: Kommando misslyckades"
 
-#: lib/database.tcl:43
+#: lib/database.tcl:42
 msgid "Number of loose objects"
 msgstr "Antal lösa objekt"
 
-#: lib/database.tcl:44
+#: lib/database.tcl:43
 msgid "Disk space used by loose objects"
 msgstr "Diskutrymme använt av lösa objekt"
 
-#: lib/database.tcl:45
+#: lib/database.tcl:44
 msgid "Number of packed objects"
 msgstr "Antal packade objekt"
 
-#: lib/database.tcl:46
+#: lib/database.tcl:45
 msgid "Number of packs"
 msgstr "Antal paket"
 
-#: lib/database.tcl:47
+#: lib/database.tcl:46
 msgid "Disk space used by packed objects"
 msgstr "Diskutrymme använt av packade objekt"
 
-#: lib/database.tcl:48
+#: lib/database.tcl:47
 msgid "Packed objects waiting for pruning"
 msgstr "Packade objekt som väntar på städning"
 
-#: lib/database.tcl:49
+#: lib/database.tcl:48
 msgid "Garbage files"
 msgstr "Skräpfiler"
 
@@ -1618,7 +1618,7 @@ msgstr "LOKAL:\n"
 msgid "REMOTE:\n"
 msgstr "FJÄRR:\n"
 
-#: lib/diff.tcl:202 lib/diff.tcl:319
+#: lib/diff.tcl:202 lib/diff.tcl:322
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Kan inte visa %s"
@@ -1655,19 +1655,19 @@ msgstr ""
 "* Den ospårade filen klipptes här av %s.\n"
 "* För att se hela filen, använd ett externt redigeringsprogram.\n"
 
-#: lib/diff.tcl:482
+#: lib/diff.tcl:485
 msgid "Failed to unstage selected hunk."
 msgstr "Kunde inte ta bort den valda delen från kön."
 
-#: lib/diff.tcl:489
+#: lib/diff.tcl:492
 msgid "Failed to stage selected hunk."
 msgstr "Kunde inte lägga till den valda delen till kön."
 
-#: lib/diff.tcl:568
+#: lib/diff.tcl:571
 msgid "Failed to unstage selected line."
 msgstr "Kunde inte ta bort den valda raden från kön."
 
-#: lib/diff.tcl:576
+#: lib/diff.tcl:579
 msgid "Failed to stage selected line."
 msgstr "Kunde inte lägga till den valda raden till kön."
 
@@ -1684,7 +1684,7 @@ msgstr "Systemets (%s)"
 msgid "Other"
 msgstr "Annan"
 
-#: lib/error.tcl:20 lib/error.tcl:114
+#: lib/error.tcl:20 lib/error.tcl:116
 msgid "error"
 msgstr "fel"
 
@@ -1692,7 +1692,7 @@ msgstr "fel"
 msgid "warning"
 msgstr "varning"
 
-#: lib/error.tcl:94
+#: lib/error.tcl:96
 msgid "You must correct the above errors before committing."
 msgstr "Du måste rätta till felen ovan innan du checkar in."
 
@@ -1700,11 +1700,11 @@ msgstr "Du måste rätta till felen ovan innan du checkar in."
 msgid "Unable to unlock the index."
 msgstr "Kunde inte låsa upp indexet."
 
-#: lib/index.tcl:15
+#: lib/index.tcl:17
 msgid "Index Error"
 msgstr "Indexfel"
 
-#: lib/index.tcl:17
+#: lib/index.tcl:19
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
@@ -1712,52 +1712,52 @@ msgstr ""
 "Misslyckades med att uppdatera Gitindexet. En omsökning kommer att startas "
 "automatiskt för att synkronisera om git-gui."
 
-#: lib/index.tcl:28
+#: lib/index.tcl:30
 msgid "Continue"
 msgstr "Forstätt"
 
-#: lib/index.tcl:31
+#: lib/index.tcl:33
 msgid "Unlock Index"
 msgstr "Lås upp index"
 
-#: lib/index.tcl:289
+#: lib/index.tcl:292
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "Tar bort %s för incheckningskön"
 
-#: lib/index.tcl:328
+#: lib/index.tcl:331
 msgid "Ready to commit."
 msgstr "Redo att checka in."
 
-#: lib/index.tcl:341
+#: lib/index.tcl:344
 #, tcl-format
 msgid "Adding %s"
 msgstr "Lägger till %s"
 
-#: lib/index.tcl:398
+#: lib/index.tcl:401
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Återställ ändringarna i filen %s?"
 
-#: lib/index.tcl:400
+#: lib/index.tcl:403
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Återställ ändringarna i dessa %i filer?"
 
-#: lib/index.tcl:408
+#: lib/index.tcl:411
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Alla oköade ändringar kommer permanent gå förlorade vid återställningen."
 
-#: lib/index.tcl:411
+#: lib/index.tcl:414
 msgid "Do Nothing"
 msgstr "Gör ingenting"
 
-#: lib/index.tcl:429
+#: lib/index.tcl:432
 msgid "Reverting selected files"
 msgstr "Återställer valda filer"
 
-#: lib/index.tcl:433
+#: lib/index.tcl:436
 #, tcl-format
 msgid "Reverting %s"
 msgstr "Återställer %s"
@@ -2004,145 +2004,133 @@ msgstr "Den globala teckenkodningen \"%s\" är ogiltig"
 msgid "Invalid repo encoding '%s'"
 msgstr "Arkivets teckenkodning \"%s\" är ogiltig"
 
-#: lib/option.tcl:117
+#: lib/option.tcl:119
 msgid "Restore Defaults"
 msgstr "Återställ standardvärden"
 
-#: lib/option.tcl:121
+#: lib/option.tcl:123
 msgid "Save"
 msgstr "Spara"
 
-#: lib/option.tcl:131
+#: lib/option.tcl:133
 #, tcl-format
 msgid "%s Repository"
 msgstr "Arkivet %s"
 
-#: lib/option.tcl:132
+#: lib/option.tcl:134
 msgid "Global (All Repositories)"
 msgstr "Globalt (alla arkiv)"
 
-#: lib/option.tcl:138
+#: lib/option.tcl:140
 msgid "User Name"
 msgstr "Användarnamn"
 
-#: lib/option.tcl:139
+#: lib/option.tcl:141
 msgid "Email Address"
 msgstr "E-postadress"
 
-#: lib/option.tcl:141
+#: lib/option.tcl:143
 msgid "Summarize Merge Commits"
 msgstr "Summera sammanslagningsincheckningar"
 
-#: lib/option.tcl:142
+#: lib/option.tcl:144
 msgid "Merge Verbosity"
 msgstr "Pratsamhet för sammanslagningar"
 
-#: lib/option.tcl:143
+#: lib/option.tcl:145
 msgid "Show Diffstat After Merge"
 msgstr "Visa diffstatistik efter sammanslagning"
 
-#: lib/option.tcl:144
+#: lib/option.tcl:146
 msgid "Use Merge Tool"
 msgstr "Använd verktyg för sammanslagning"
 
-#: lib/option.tcl:146
+#: lib/option.tcl:148
 msgid "Trust File Modification Timestamps"
 msgstr "Lita på filändringstidsstämplar"
 
-#: lib/option.tcl:147
+#: lib/option.tcl:149
 msgid "Prune Tracking Branches During Fetch"
 msgstr "Städa spårade grenar vid hämtning"
 
-#: lib/option.tcl:148
+#: lib/option.tcl:150
 msgid "Match Tracking Branches"
 msgstr "Matcha spårade grenar"
 
-#: lib/option.tcl:149
+#: lib/option.tcl:151
+msgid "Use Textconv For Diffs and Blames"
+msgstr "Använd Textconv för diff och klandring"
+
+#: lib/option.tcl:152
 msgid "Blame Copy Only On Changed Files"
 msgstr "Klandra kopiering bara i ändrade filer"
 
-#: lib/option.tcl:150
+#: lib/option.tcl:153
 msgid "Minimum Letters To Blame Copy On"
 msgstr "Minsta antal tecken att klandra kopiering för"
 
-#: lib/option.tcl:151
+#: lib/option.tcl:154
 msgid "Blame History Context Radius (days)"
 msgstr "Historikradie för klandring (dagar)"
 
-#: lib/option.tcl:152
+#: lib/option.tcl:155
 msgid "Number of Diff Context Lines"
 msgstr "Antal rader sammanhang i differenser"
 
-#: lib/option.tcl:153
+#: lib/option.tcl:156
 msgid "Commit Message Text Width"
 msgstr "Textbredd för incheckningsmeddelande"
 
-#: lib/option.tcl:154
+#: lib/option.tcl:157
 msgid "New Branch Name Template"
 msgstr "Mall för namn på nya grenar"
 
-#: lib/option.tcl:155
+#: lib/option.tcl:158
 msgid "Default File Contents Encoding"
 msgstr "Standardteckenkodning för filinnehåll"
 
-#: lib/option.tcl:203
+#: lib/option.tcl:204
 msgid "Change"
 msgstr "Ändra"
 
-#: lib/option.tcl:230
+#: lib/option.tcl:231
 msgid "Spelling Dictionary:"
 msgstr "Stavningsordlista:"
 
-#: lib/option.tcl:254
+#: lib/option.tcl:261
 msgid "Change Font"
 msgstr "Byt teckensnitt"
 
-#: lib/option.tcl:258
+#: lib/option.tcl:265
 #, tcl-format
 msgid "Choose %s"
 msgstr "Välj %s"
 
-#: lib/option.tcl:264
+#: lib/option.tcl:271
 msgid "pt."
 msgstr "p."
 
-#: lib/option.tcl:278
+#: lib/option.tcl:285
 msgid "Preferences"
 msgstr "Inställningar"
 
-#: lib/option.tcl:314
+#: lib/option.tcl:322
 msgid "Failed to completely save options:"
 msgstr "Misslyckades med att helt spara alternativ:"
 
-#: lib/remote.tcl:163
-msgid "Remove Remote"
-msgstr "Ta bort fjärrarkiv"
-
-#: lib/remote.tcl:168
-msgid "Prune from"
-msgstr "Ta bort från"
-
-#: lib/remote.tcl:173
-msgid "Fetch from"
-msgstr "Hämta från"
-
-#: lib/remote.tcl:215
-msgid "Push to"
-msgstr "Sänd till"
-
-#: lib/remote_add.tcl:19
+#: lib/remote_add.tcl:20
 msgid "Add Remote"
 msgstr "Lägg till fjärrarkiv"
 
-#: lib/remote_add.tcl:24
+#: lib/remote_add.tcl:25
 msgid "Add New Remote"
 msgstr "Lägg till nytt fjärrarkiv"
 
-#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+#: lib/remote_add.tcl:30 lib/tools_dlg.tcl:37
 msgid "Add"
 msgstr "Lägg till"
 
-#: lib/remote_add.tcl:37
+#: lib/remote_add.tcl:39
 msgid "Remote Details"
 msgstr "Detaljer för fjärrarkiv"
 
@@ -2150,58 +2138,58 @@ msgstr "Detaljer för fjärrarkiv"
 msgid "Location:"
 msgstr "Plats:"
 
-#: lib/remote_add.tcl:62
+#: lib/remote_add.tcl:60
 msgid "Further Action"
 msgstr "Ytterligare åtgärd"
 
-#: lib/remote_add.tcl:65
+#: lib/remote_add.tcl:63
 msgid "Fetch Immediately"
 msgstr "Hämta omedelbart"
 
-#: lib/remote_add.tcl:71
+#: lib/remote_add.tcl:69
 msgid "Initialize Remote Repository and Push"
 msgstr "Initiera fjärrarkiv och sänd till"
 
-#: lib/remote_add.tcl:77
+#: lib/remote_add.tcl:75
 msgid "Do Nothing Else Now"
 msgstr "Gör ingent mer nu"
 
-#: lib/remote_add.tcl:101
+#: lib/remote_add.tcl:100
 msgid "Please supply a remote name."
 msgstr "Ange ett namn för fjärrarkivet."
 
-#: lib/remote_add.tcl:114
+#: lib/remote_add.tcl:113
 #, tcl-format
 msgid "'%s' is not an acceptable remote name."
 msgstr "\"%s\" kan inte användas som namn på fjärrarkivet."
 
-#: lib/remote_add.tcl:125
+#: lib/remote_add.tcl:124
 #, tcl-format
 msgid "Failed to add remote '%s' of location '%s'."
 msgstr "Kunde inte lägga till fjärrarkivet \"%s\" på platsen \"%s\"."
 
-#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#: lib/remote_add.tcl:132 lib/transport.tcl:6
 #, tcl-format
 msgid "fetch %s"
 msgstr "hämta %s"
 
-#: lib/remote_add.tcl:134
+#: lib/remote_add.tcl:133
 #, tcl-format
 msgid "Fetching the %s"
 msgstr "Hämtar %s"
 
-#: lib/remote_add.tcl:157
+#: lib/remote_add.tcl:156
 #, tcl-format
 msgid "Do not know how to initialize repository at location '%s'."
 msgstr "Vet inte hur arkivet på platsen \"%s\" skall initieras."
 
-#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/remote_add.tcl:162 lib/transport.tcl:25 lib/transport.tcl:63
 #: lib/transport.tcl:81
 #, tcl-format
 msgid "push %s"
 msgstr "sänd %s"
 
-#: lib/remote_add.tcl:164
+#: lib/remote_add.tcl:163
 #, tcl-format
 msgid "Setting up the %s (at %s)"
 msgstr "Konfigurerar %s (på %s)"
@@ -2210,35 +2198,35 @@ msgstr "Konfigurerar %s (på %s)"
 msgid "Delete Branch Remotely"
 msgstr "Ta bort gren från fjärrarkiv"
 
-#: lib/remote_branch_delete.tcl:47
+#: lib/remote_branch_delete.tcl:48
 msgid "From Repository"
 msgstr "Från arkiv"
 
-#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
+#: lib/remote_branch_delete.tcl:51 lib/transport.tcl:134
 msgid "Remote:"
 msgstr "Fjärrarkiv:"
 
-#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
+#: lib/remote_branch_delete.tcl:72 lib/transport.tcl:154
 msgid "Arbitrary Location:"
 msgstr "Godtycklig plats:"
 
-#: lib/remote_branch_delete.tcl:84
+#: lib/remote_branch_delete.tcl:88
 msgid "Branches"
 msgstr "Grenar"
 
-#: lib/remote_branch_delete.tcl:109
+#: lib/remote_branch_delete.tcl:110
 msgid "Delete Only If"
 msgstr "Ta endast bort om"
 
-#: lib/remote_branch_delete.tcl:111
+#: lib/remote_branch_delete.tcl:112
 msgid "Merged Into:"
 msgstr "Sammanslagen i:"
 
-#: lib/remote_branch_delete.tcl:152
+#: lib/remote_branch_delete.tcl:153
 msgid "A branch is required for 'Merged Into'."
 msgstr "En gren krävs för \"Sammanslagen i\"."
 
-#: lib/remote_branch_delete.tcl:184
+#: lib/remote_branch_delete.tcl:185
 #, tcl-format
 msgid ""
 "The following branches are not completely merged into %s:\n"
@@ -2249,7 +2237,7 @@ msgstr ""
 "\n"
 " - %s"
 
-#: lib/remote_branch_delete.tcl:189
+#: lib/remote_branch_delete.tcl:190
 #, tcl-format
 msgid ""
 "One or more of the merge tests failed because you have not fetched the "
@@ -2258,37 +2246,53 @@ msgstr ""
 "En eller flera av sammanslagningstesterna misslyckades eftersom du inte har "
 "hämtat de nödvändiga incheckningarna. Försök hämta från %s först."
 
-#: lib/remote_branch_delete.tcl:207
+#: lib/remote_branch_delete.tcl:208
 msgid "Please select one or more branches to delete."
 msgstr "Välj en eller flera grenar att ta bort."
 
-#: lib/remote_branch_delete.tcl:226
+#: lib/remote_branch_delete.tcl:227
 #, tcl-format
 msgid "Deleting branches from %s"
 msgstr "Tar bort grenar från %s"
 
-#: lib/remote_branch_delete.tcl:292
+#: lib/remote_branch_delete.tcl:293
 msgid "No repository selected."
 msgstr "Inget arkiv markerat."
 
-#: lib/remote_branch_delete.tcl:297
+#: lib/remote_branch_delete.tcl:298
 #, tcl-format
 msgid "Scanning %s..."
 msgstr "Söker %s..."
 
-#: lib/search.tcl:21
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Ta bort fjärrarkiv"
+
+#: lib/remote.tcl:168
+msgid "Prune from"
+msgstr "Ta bort från"
+
+#: lib/remote.tcl:173
+msgid "Fetch from"
+msgstr "Hämta från"
+
+#: lib/remote.tcl:215
+msgid "Push to"
+msgstr "Sänd till"
+
+#: lib/search.tcl:22
 msgid "Find:"
 msgstr "Sök:"
 
-#: lib/search.tcl:23
+#: lib/search.tcl:24
 msgid "Next"
 msgstr "Nästa"
 
-#: lib/search.tcl:24
+#: lib/search.tcl:25
 msgid "Prev"
 msgstr "Föreg"
 
-#: lib/search.tcl:25
+#: lib/search.tcl:26
 msgid "Case-Sensitive"
 msgstr "Skilj på VERSALER/gemener"
 
@@ -2350,19 +2354,19 @@ msgstr "Hittade öppen nyckel i: %s"
 msgid "Generate Key"
 msgstr "Skapa nyckel"
 
-#: lib/sshkey.tcl:56
+#: lib/sshkey.tcl:58
 msgid "Copy To Clipboard"
 msgstr "Kopiera till Urklipp"
 
-#: lib/sshkey.tcl:70
+#: lib/sshkey.tcl:72
 msgid "Your OpenSSH Public Key"
 msgstr "Din öppna OpenSSH-nyckel"
 
-#: lib/sshkey.tcl:78
+#: lib/sshkey.tcl:80
 msgid "Generating..."
 msgstr "Skapar..."
 
-#: lib/sshkey.tcl:84
+#: lib/sshkey.tcl:86
 #, tcl-format
 msgid ""
 "Could not start ssh-keygen:\n"
@@ -2373,54 +2377,24 @@ msgstr ""
 "\n"
 "%s"
 
-#: lib/sshkey.tcl:111
+#: lib/sshkey.tcl:113
 msgid "Generation failed."
 msgstr "Misslyckades med att skapa."
 
-#: lib/sshkey.tcl:118
+#: lib/sshkey.tcl:120
 msgid "Generation succeded, but no keys found."
 msgstr "Lyckades skapa nyckeln, men hittar inte någon nyckel."
 
-#: lib/sshkey.tcl:121
+#: lib/sshkey.tcl:123
 #, tcl-format
 msgid "Your key is in: %s"
 msgstr "Din nyckel finns i: %s"
 
-#: lib/status_bar.tcl:83
+#: lib/status_bar.tcl:86
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%s... %*i av %*i %s (%3i%%)"
 
-#: lib/tools.tcl:75
-#, tcl-format
-msgid "Running %s requires a selected file."
-msgstr "För att starta %s måste du välja en fil."
-
-#: lib/tools.tcl:90
-#, tcl-format
-msgid "Are you sure you want to run %s?"
-msgstr "Är du säker på att du vill starta %s?"
-
-#: lib/tools.tcl:110
-#, tcl-format
-msgid "Tool: %s"
-msgstr "Verktyg: %s"
-
-#: lib/tools.tcl:111
-#, tcl-format
-msgid "Running: %s"
-msgstr "Exekverar: %s"
-
-#: lib/tools.tcl:149
-#, tcl-format
-msgid "Tool completed successfully: %s"
-msgstr "Verktyget avslutades framgångsrikt: %s"
-
-#: lib/tools.tcl:151
-#, tcl-format
-msgid "Tool failed: %s"
-msgstr "Verktyget misslyckades: %s"
-
 #: lib/tools_dlg.tcl:22
 msgid "Add Tool"
 msgstr "Lägg till verktyg"
@@ -2429,52 +2403,52 @@ msgstr "Lägg till verktyg"
 msgid "Add New Tool Command"
 msgstr "Lägg till nytt verktygskommando"
 
-#: lib/tools_dlg.tcl:33
+#: lib/tools_dlg.tcl:34
 msgid "Add globally"
 msgstr "Lägg till globalt"
 
-#: lib/tools_dlg.tcl:45
+#: lib/tools_dlg.tcl:46
 msgid "Tool Details"
 msgstr "Detaljer för verktyg"
 
-#: lib/tools_dlg.tcl:48
+#: lib/tools_dlg.tcl:49
 msgid "Use '/' separators to create a submenu tree:"
 msgstr "Använd \"/\"-avdelare för att skapa ett undermenyträd:"
 
-#: lib/tools_dlg.tcl:61
+#: lib/tools_dlg.tcl:60
 msgid "Command:"
 msgstr "Kommando:"
 
-#: lib/tools_dlg.tcl:74
+#: lib/tools_dlg.tcl:71
 msgid "Show a dialog before running"
 msgstr "Visa dialog innan programmet startas"
 
-#: lib/tools_dlg.tcl:80
+#: lib/tools_dlg.tcl:77
 msgid "Ask the user to select a revision (sets $REVISION)"
 msgstr "Be användaren välja en version (sätter $REVISION)"
 
-#: lib/tools_dlg.tcl:85
+#: lib/tools_dlg.tcl:82
 msgid "Ask the user for additional arguments (sets $ARGS)"
 msgstr "Be användaren om ytterligare parametrar (sätter $ARGS)"
 
-#: lib/tools_dlg.tcl:92
+#: lib/tools_dlg.tcl:89
 msgid "Don't show the command output window"
 msgstr "Visa inte kommandots utdatafönster"
 
-#: lib/tools_dlg.tcl:97
+#: lib/tools_dlg.tcl:94
 msgid "Run only if a diff is selected ($FILENAME not empty)"
 msgstr "Kör endast om en diff har markerats ($FILENAME är inte tomt)"
 
-#: lib/tools_dlg.tcl:121
+#: lib/tools_dlg.tcl:118
 msgid "Please supply a name for the tool."
 msgstr "Ange ett namn för verktyget."
 
-#: lib/tools_dlg.tcl:129
+#: lib/tools_dlg.tcl:126
 #, tcl-format
 msgid "Tool '%s' already exists."
 msgstr "Verktyget \"%s\" finns redan."
 
-#: lib/tools_dlg.tcl:151
+#: lib/tools_dlg.tcl:148
 #, tcl-format
 msgid ""
 "Could not add tool:\n"
@@ -2483,35 +2457,65 @@ msgstr ""
 "Kunde inte lägga till verktyget:\n"
 "%s"
 
-#: lib/tools_dlg.tcl:190
+#: lib/tools_dlg.tcl:187
 msgid "Remove Tool"
 msgstr "Ta bort verktyg"
 
-#: lib/tools_dlg.tcl:196
+#: lib/tools_dlg.tcl:193
 msgid "Remove Tool Commands"
 msgstr "Ta bort verktygskommandon"
 
-#: lib/tools_dlg.tcl:200
+#: lib/tools_dlg.tcl:198
 msgid "Remove"
 msgstr "Ta bort"
 
-#: lib/tools_dlg.tcl:236
+#: lib/tools_dlg.tcl:231
 msgid "(Blue denotes repository-local tools)"
 msgstr "(Blått anger verktyg lokala för arkivet)"
 
-#: lib/tools_dlg.tcl:297
+#: lib/tools_dlg.tcl:292
 #, tcl-format
 msgid "Run Command: %s"
 msgstr "Kör kommandot: %s"
 
-#: lib/tools_dlg.tcl:311
+#: lib/tools_dlg.tcl:306
 msgid "Arguments"
 msgstr "Argument"
 
-#: lib/tools_dlg.tcl:348
+#: lib/tools_dlg.tcl:341
 msgid "OK"
 msgstr "OK"
 
+#: lib/tools.tcl:75
+#, tcl-format
+msgid "Running %s requires a selected file."
+msgstr "För att starta %s måste du välja en fil."
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Är du säker på att du vill starta %s?"
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Verktyg: %s"
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr "Exekverar: %s"
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed successfully: %s"
+msgstr "Verktyget avslutades framgångsrikt: %s"
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "Verktyget misslyckades: %s"
+
 #: lib/transport.tcl:7
 #, tcl-format
 msgid "Fetching new changes from %s"
@@ -2542,11 +2546,11 @@ msgstr "Speglar till %s"
 msgid "Pushing %s %s to %s"
 msgstr "Sänder %s %s till %s"
 
-#: lib/transport.tcl:100
+#: lib/transport.tcl:102
 msgid "Push Branches"
 msgstr "Sänd grenar"
 
-#: lib/transport.tcl:114
+#: lib/transport.tcl:117
 msgid "Source Branches"
 msgstr "Källgrenar"
 
@@ -2554,19 +2558,19 @@ msgstr "Källgrenar"
 msgid "Destination Repository"
 msgstr "Destinationsarkiv"
 
-#: lib/transport.tcl:169
+#: lib/transport.tcl:172
 msgid "Transfer Options"
 msgstr "Överföringsalternativ"
 
-#: lib/transport.tcl:171
+#: lib/transport.tcl:174
 msgid "Force overwrite existing branch (may discard changes)"
 msgstr "Tvinga överskrivning av befintlig gren (kan kasta bort ändringar)"
 
-#: lib/transport.tcl:175
+#: lib/transport.tcl:178
 msgid "Use thin pack (for slow network connections)"
 msgstr "Använd tunt paket (för långsamma nätverksanslutningar)"
 
-#: lib/transport.tcl:179
+#: lib/transport.tcl:182
 msgid "Include tags"
 msgstr "Ta med taggar"
 
index e6f6ecda177c66a79b18359f7bf8cad9c4744c8c..10fcebb119ce2af81527aa3c24a9b7c3ab3b3e7f 100755 (executable)
@@ -580,6 +580,8 @@ gitweb_conf() {
 our \$projectroot = "$(dirname "$fqgitdir")";
 our \$git_temp = "$fqgitdir/gitweb/tmp";
 our \$projects_list = \$projectroot;
+
+\$feature{'remote_heads'}{'default'} = [1];
 EOF
 }
 
index 615753c83c38f4a97752d5ca3400beeef4acd5f1..8643f74cb09f278c37851c418e839c7d160f36ca 100755 (executable)
@@ -61,6 +61,11 @@ do
        esac
 
        eval pretty_name=\${GITHEAD_$SHA1:-$SHA1}
+       if test "$SHA1" = "$pretty_name"
+       then
+               SHA1_UP="$(echo "$SHA1" | tr a-z A-Z)"
+               eval pretty_name=\${GITHEAD_$SHA1_UP:-$pretty_name}
+       fi
        common=$(git merge-base --all $SHA1 $MRC) ||
                die "Unable to find common commit with $pretty_name"
 
index 51dd0d67ba951873df4f26b1707a1e8a713082b9..77d4aee20ee5b8b01f9a0e3d1f8dbc9f7fdf3eb9 100644 (file)
@@ -10,10 +10,10 @@ merge_mode() {
 
 translate_merge_tool_path () {
        case "$1" in
-       vimdiff)
+       vimdiff|vimdiff2)
                echo vim
                ;;
-       gvimdiff)
+       gvimdiff|gvimdiff2)
                echo gvim
                ;;
        emerge)
@@ -35,7 +35,7 @@ check_unchanged () {
                while true; do
                        echo "$MERGED seems unchanged."
                        printf "Was the merge successful? [y/n] "
-                       read answer < /dev/tty
+                       read answer
                        case "$answer" in
                        y*|Y*) status=0; break ;;
                        n*|N*) status=1; break ;;
@@ -47,7 +47,8 @@ check_unchanged () {
 valid_tool () {
        case "$1" in
        kdiff3 | tkdiff | xxdiff | meld | opendiff | \
-       emerge | vimdiff | gvimdiff | ecmerge | diffuse | araxis | p4merge)
+       vimdiff | gvimdiff | vimdiff2 | gvimdiff2 | \
+       emerge | ecmerge | diffuse | araxis | p4merge)
                ;; # happy
        tortoisemerge)
                if ! merge_mode; then
@@ -169,25 +170,30 @@ run_merge_tool () {
                        "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
                fi
                ;;
-       vimdiff)
+       vimdiff|gvimdiff)
                if merge_mode; then
                        touch "$BACKUP"
-                       "$merge_tool_path" -d -c "wincmd l" \
-                               "$LOCAL" "$MERGED" "$REMOTE"
+                       if $base_present; then
+                               "$merge_tool_path" -f -d -c "wincmd J" \
+                                       "$MERGED" "$LOCAL" "$BASE" "$REMOTE"
+                       else
+                               "$merge_tool_path" -f -d -c "wincmd l" \
+                                       "$LOCAL" "$MERGED" "$REMOTE"
+                       fi
                        check_unchanged
                else
-                       "$merge_tool_path" -d -c "wincmd l" \
+                       "$merge_tool_path" -f -d -c "wincmd l" \
                                "$LOCAL" "$REMOTE"
                fi
                ;;
-       gvimdiff)
+       vimdiff2|gvimdiff2)
                if merge_mode; then
                        touch "$BACKUP"
-                       "$merge_tool_path" -d -c "wincmd l" -f \
+                       "$merge_tool_path" -f -d -c "wincmd l" \
                                "$LOCAL" "$MERGED" "$REMOTE"
                        check_unchanged
                else
-                       "$merge_tool_path" -d -c "wincmd l" -f \
+                       "$merge_tool_path" -f -d -c "wincmd l" \
                                "$LOCAL" "$REMOTE"
                fi
                ;;
index bd7ab02f1161d2d97ea57b2a2ceacc307f6a74e8..2f8dc441c6d575323f2fad920ecdb7b20e1445e6 100755 (executable)
@@ -289,6 +289,10 @@ if test $# -eq 0 ; then
        echo "No files need merging"
        exit 0
     fi
+
+    # Save original stdin
+    exec 3<&0
+
     printf "Merging:\n"
     printf "$files\n"
 
@@ -296,10 +300,10 @@ if test $# -eq 0 ; then
     while IFS= read i
     do
        if test $last_status -ne 0; then
-           prompt_after_failed_merge < /dev/tty || exit 1
+           prompt_after_failed_merge <&3 || exit 1
        fi
        printf "\n"
-       merge_file "$i" < /dev/tty > /dev/tty
+       merge_file "$i" <&3
        last_status=$?
        if test $last_status -ne 0; then
            rollup_status=1
index 5f47b18141a0758c3d0dae2cfe764495c92139c5..1cc2ba6e09614fa55ad205145a3bdacfb78bf283 100644 (file)
@@ -66,7 +66,7 @@ get_remote_merge_branch () {
            origin="$1"
            default=$(get_default_remote)
            test -z "$origin" && origin=$default
-           curr_branch=$(git symbolic-ref -q HEAD)
+           curr_branch=$(git symbolic-ref -q HEAD) &&
            [ "$origin" = "$default" ] &&
            echo $(git for-each-ref --format='%(upstream)' $curr_branch)
            ;;
@@ -89,7 +89,13 @@ get_remote_merge_branch () {
            refs/heads/*) remote=${remote#refs/heads/} ;;
            refs/* | tags/* | remotes/* ) remote=
            esac
-
-           [ -n "$remote" ] && echo "refs/remotes/$repo/$remote"
+           [ -n "$remote" ] && case "$repo" in
+               .)
+                   echo "refs/heads/$remote"
+                   ;;
+               *)
+                   echo "refs/remotes/$repo/$remote"
+                   ;;
+           esac
        esac
 }
index a09a44ec4ce3d82cebc5f972f09e94c7c35dc948..eb87f49062b70d707adebca4013809822123608b 100755 (executable)
@@ -38,7 +38,7 @@ test -z "$(git ls-files -u)" || die_conflict
 test -f "$GIT_DIR/MERGE_HEAD" && die_merge
 
 strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
-log_arg= verbosity= progress=
+log_arg= verbosity= progress= recurse_submodules=
 merge_args=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short="${curr_branch#refs/heads/}"
@@ -105,6 +105,12 @@ do
        --no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)
                rebase=false
                ;;
+       --recurse-submodules)
+               recurse_submodules=--recurse-submodules
+               ;;
+       --no-recurse-submodules)
+               recurse_submodules=--no-recurse-submodules
+               ;;
        --d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)
                dry_run=--dry-run
                ;;
@@ -201,10 +207,7 @@ test true = "$rebase" && {
                        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"
+               require_clean_work_tree "pull with rebase" "Please commit or stash them."
        fi
        oldremoteref= &&
        . git-parse-remote &&
@@ -220,7 +223,7 @@ test true = "$rebase" && {
        done
 }
 orig_head=$(git rev-parse -q --verify HEAD)
-git fetch $verbosity $progress $dry_run --update-head-ok "$@" || exit 1
+git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok "$@" || exit 1
 test -z "$dry_run" || exit 0
 
 curr_head=$(git rev-parse -q --verify HEAD)
@@ -273,6 +276,15 @@ then
        exit
 fi
 
+if test true = "$rebase"
+then
+       o=$(git show-branch --merge-base $curr_branch $merge_head $oldremoteref)
+       if test "$oldremoteref" = "$o"
+       then
+               unset oldremoteref
+       fi
+fi
+
 merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 case "$rebase" in
 true)
index b94c2a03867ddcd1fa0c2051f81cf14547b550f0..5873ba4bc3b2a94cd7754ae572072fd745dc059d 100755 (executable)
@@ -28,6 +28,7 @@ continue           continue rebasing process
 abort              abort rebasing process and restore original branch
 skip               skip current patch and continue rebasing process
 no-verify          override pre-rebase hook from stopping the operation
+verify             allow pre-rebase hook to run
 root               rebase all reachable commmits up to the root(s)
 autosquash         move commits that begin with squash!/fixup! under -i
 "
@@ -111,11 +112,12 @@ VERBOSE=
 OK_TO_SKIP_PRE_REBASE=
 REBASE_ROOT=
 AUTOSQUASH=
+test "$(git config --bool rebase.autosquash)" = "true" && AUTOSQUASH=t
 NEVER_FF=
 
-GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
-mark the corrected paths with 'git add <paths>', and
-run 'git rebase --continue'"
+GIT_CHERRY_PICK_HELP="\
+hint: after resolving the conflicts, mark the corrected paths
+hint: with 'git add <paths>' and run 'git rebase --continue'"
 export GIT_CHERRY_PICK_HELP
 
 warn () {
@@ -152,14 +154,6 @@ run_pre_rebase_hook () {
        fi
 }
 
-require_clean_work_tree () {
-       # test if working tree is dirty
-       git rev-parse --verify HEAD > /dev/null &&
-       git update-index --ignore-submodules --refresh &&
-       git diff-files --quiet --ignore-submodules &&
-       git diff-index --cached --quiet HEAD --ignore-submodules -- ||
-       die "Working tree is dirty"
-}
 
 ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION"
 
@@ -537,6 +531,34 @@ do_next () {
                esac
                record_in_rewritten $sha1
                ;;
+       x|"exec")
+               read -r command rest < "$TODO"
+               mark_action_done
+               printf 'Executing: %s\n' "$rest"
+               # "exec" command doesn't take a sha1 in the todo-list.
+               # => can't just use $sha1 here.
+               git rev-parse --verify HEAD > "$DOTEST"/stopped-sha
+               ${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution
+               status=$?
+               if test "$status" -ne 0
+               then
+                       warn "Execution failed: $rest"
+                       warn "You can fix the problem, and then run"
+                       warn
+                       warn "  git rebase --continue"
+                       warn
+                       exit "$status"
+               fi
+               # Run in subshell because require_clean_work_tree can die.
+               if ! (require_clean_work_tree "rebase")
+               then
+                       warn "Commit or stash your changes, and then run"
+                       warn
+                       warn "  git rebase --continue"
+                       warn
+                       exit 1
+               fi
+               ;;
        *)
                warn "Unknown command: $command $sha1 $rest"
                if git rev-parse --verify -q "$sha1" >/dev/null
@@ -591,22 +613,30 @@ do_rest () {
 # skip picking commits whose parents are unchanged
 skip_unnecessary_picks () {
        fd=3
-       while read -r command sha1 rest
+       while read -r command rest
        do
                # fd=3 means we skip the command
-               case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in
-               3,pick,"$ONTO"*|3,p,"$ONTO"*)
+               case "$fd,$command" in
+               3,pick|3,p)
                        # pick a commit whose parent is current $ONTO -> skip
-                       ONTO=$sha1
+                       sha1=${rest%% *}
+                       case "$(git rev-parse --verify --quiet "$sha1"^)" in
+                       "$ONTO"*)
+                               ONTO=$sha1
+                               ;;
+                       *)
+                               fd=1
+                               ;;
+                       esac
                        ;;
-               3,#*|3,,*)
+               3,#*|3,)
                        # copy comments
                        ;;
                *)
                        fd=1
                        ;;
                esac
-               printf '%s\n' "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
+               printf '%s\n' "$command${rest:+ }$rest" >&$fd
        done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
        mv -f "$TODO".new "$TODO" &&
        case "$(peek_next_command)" in
@@ -638,9 +668,27 @@ get_saved_options () {
 # comes immediately after the former, and change "pick" to
 # "fixup"/"squash".
 rearrange_squash () {
-       sed -n -e 's/^pick \([0-9a-f]*\) \(squash\)! /\1 \2 /p' \
-               -e 's/^pick \([0-9a-f]*\) \(fixup\)! /\1 \2 /p' \
-               "$1" >"$1.sq"
+       # extract fixup!/squash! lines and resolve any referenced sha1's
+       while read -r pick sha1 message
+       do
+               case "$message" in
+               "squash! "*|"fixup! "*)
+                       action="${message%%!*}"
+                       rest="${message#*! }"
+                       echo "$sha1 $action $rest"
+                       # if it's a single word, try to resolve to a full sha1 and
+                       # emit a second copy. This allows us to match on both message
+                       # and on sha1 prefix
+                       if test "${rest#* }" = "$rest"; then
+                               fullsha="$(git rev-parse -q --verify "$rest" 2>/dev/null)"
+                               if test -n "$fullsha"; then
+                                       # prefix the action to uniquely identify this line as
+                                       # intended for full sha1 match
+                                       echo "$sha1 +$action $fullsha"
+                               fi
+                       fi
+               esac
+       done >"$1.sq" <"$1"
        test -s "$1.sq" || return
 
        used=
@@ -650,14 +698,26 @@ rearrange_squash () {
                *" $sha1 "*) continue ;;
                esac
                printf '%s\n' "$pick $sha1 $message"
+               used="$used$sha1 "
                while read -r squash action msg
                do
-                       case "$message" in
-                       "$msg"*)
+                       case " $used" in
+                       *" $squash "*) continue ;;
+                       esac
+                       emit=0
+                       case "$action" in
+                       +*)
+                               action="${action#+}"
+                               # full sha1 prefix test
+                               case "$msg" in "$sha1"*) emit=1;; esac ;;
+                       *)
+                               # message prefix test
+                               case "$message" in "$msg"*) emit=1;; esac ;;
+                       esac
+                       if test $emit = 1; then
                                printf '%s\n' "$action $squash $action! $msg"
                                used="$used$squash "
-                               ;;
-                       esac
+                       fi
                done <"$1.sq"
        done >"$1.rearranged" <"$1"
        cat "$1.rearranged" >"$1"
@@ -690,6 +750,7 @@ do
                OK_TO_SKIP_PRE_REBASE=yes
                ;;
        --verify)
+               OK_TO_SKIP_PRE_REBASE=
                ;;
        --continue)
                is_standalone "$@" || usage
@@ -731,7 +792,7 @@ first and then run 'git rebase --continue' again."
 
                record_in_rewritten "$(cat "$DOTEST"/stopped-sha)"
 
-               require_clean_work_tree
+               require_clean_work_tree "rebase"
                do_rest
                ;;
        --abort)
@@ -795,6 +856,9 @@ first and then run 'git rebase --continue' again."
        --autosquash)
                AUTOSQUASH=t
                ;;
+       --no-autosquash)
+               AUTOSQUASH=
+               ;;
        --onto)
                shift
                ONTO=$(parse_onto "$1") ||
@@ -826,11 +890,11 @@ first and then run 'git rebase --continue' again."
 
                comment_for_reflog start
 
-               require_clean_work_tree
+               require_clean_work_tree "rebase" "Please commit or stash them."
 
                if test ! -z "$1"
                then
-                       output git checkout "$1" ||
+                       output git checkout "$1" -- ||
                                die "Could not checkout $1"
                fi
 
@@ -957,6 +1021,7 @@ first and then run 'git rebase --continue' again."
 #  e, edit = use commit, but stop for amending
 #  s, squash = use commit, but meld into previous commit
 #  f, fixup = like "squash", but discard this commit's log message
+#  x, exec = run command (the rest of the line) using shell
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
index 7508463b30e7d1659ac9423a19d195e494600bd3..cbb0ea90ed410d5af68ce814b7fd93a90829f3ee 100755 (executable)
@@ -49,7 +49,8 @@ do_merge=
 dotest="$GIT_DIR"/rebase-merge
 prec=4
 verbose=
-diffstat=$(git config --bool rebase.stat)
+diffstat=
+test "$(git config --bool rebase.stat)" = true && diffstat=t
 git_am_opt=
 rebase_root=
 force_rebase=
@@ -111,7 +112,7 @@ call_merge () {
        export GITHEAD_$cmt GITHEAD_$hd
        if test -n "$GIT_QUIET"
        then
-               export GIT_MERGE_VERBOSITY=1
+               GIT_MERGE_VERBOSITY=1 && export GIT_MERGE_VERBOSITY
        fi
        eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"'
        rv=$?
@@ -205,6 +206,9 @@ do
        --no-verify)
                OK_TO_SKIP_PRE_REBASE=yes
                ;;
+       --verify)
+               OK_TO_SKIP_PRE_REBASE=
+               ;;
        --continue)
                test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
                        die "No rebase in progress?"
@@ -274,15 +278,16 @@ do
                        die "No rebase in progress?"
 
                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
+
+               test -d "$dotest" || dotest="$GIT_DIR"/rebase-apply
+
+               head_name="$(cat "$dotest"/head-name)" &&
+               case "$head_name" in
+               refs/*)
+                       git symbolic-ref HEAD $head_name ||
+                       die "Could not move back to $head_name"
+                       ;;
+               esac
                git reset --hard $(cat "$dotest/orig-head")
                rm -r "$dotest"
                exit
@@ -311,10 +316,6 @@ do
                esac
                strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$newopt")"
                do_merge=t
-               if test -n "$strategy"
-               then
-                       strategy=recursive
-               fi
                ;;
        -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
                --strateg=*|--strategy=*|\
@@ -416,19 +417,7 @@ else
        fi
 fi
 
-# The tree must be really really clean.
-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 --)
-case "$diff" in
-?*)    echo >&2 "cannot rebase: your index contains uncommitted changes"
-       echo >&2 "$diff"
-       exit 1
-       ;;
-esac
+require_clean_work_tree "rebase" "Please commit or stash them."
 
 if test -z "$rebase_root"
 then
@@ -493,6 +482,7 @@ case "$#" in
        then
                head_name="detached HEAD"
        else
+               echo >&2 "fatal: no such branch: $1"
                usage
        fi
        ;;
@@ -524,7 +514,7 @@ then
        if test -z "$force_rebase"
        then
                # Lazily switch to the target branch if needed...
-               test -z "$switch_to" || git checkout "$switch_to"
+               test -z "$switch_to" || git checkout "$switch_to" --
                say "Current branch $branch_name is up to date."
                exit 0
        else
@@ -566,6 +556,7 @@ fi
 if test -z "$do_merge"
 then
        git format-patch -k --stdout --full-index --ignore-if-in-upstream \
+               --src-prefix=a/ --dst-prefix=b/ \
                --no-renames $root_flag "$revisions" |
        git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
        move_to_original_branch
index 937c69a74858a8a3c63bb41a23705b579df1b3a3..e136732cea80c1594ac02c93cf246e8c77ff89b8 100755 (executable)
@@ -6,7 +6,7 @@
 #
 # Scan two git object-trees, and hardlink any common objects between them.
 
-use 5.006;
+use 5.008;
 use strict;
 use warnings;
 use Getopt::Long;
@@ -163,7 +163,7 @@ sub link_two_files($$) {
 
 
 sub usage() {
-       print("Usage: git relink [--safe] <dir> [<dir> ...] <master_dir> \n");
+       print("Usage: git relink [--safe] <dir>... <master_dir> \n");
        print("All directories should contain a .git/objects/ subdirectory.\n");
        print("Options\n");
        print("\t--safe\t" .
index 1eb3bca352f38ec3807383cc5fff7f7e62731b2b..624feec26f66b15926ffa11767c146222bc9a185 100755 (executable)
@@ -10,7 +10,8 @@ git repack [options]
 a               pack everything in a single pack
 A               same as -a, and turn unreachable objects loose
 d               remove redundant packs, and run git-prune-packed
-f               pass --no-reuse-object to git-pack-objects
+f               pass --no-reuse-delta to git-pack-objects
+F               pass --no-reuse-object to git-pack-objects
 n               do not run git-update-server-info
 q,quiet         be quiet
 l               pass --local to git-pack-objects
@@ -34,7 +35,8 @@ do
                unpack_unreachable=--unpack-unreachable ;;
        -d)     remove_redundant=t ;;
        -q)     GIT_QUIET=t ;;
-       -f)     no_reuse=--no-reuse-object ;;
+       -f)     no_reuse=--no-reuse-delta ;;
+       -F)     no_reuse=--no-reuse-object ;;
        -l)     local=--local ;;
        --max-pack-size|--window|--window-memory|--depth)
                extra="$extra $1=$2"; shift ;;
@@ -50,7 +52,7 @@ true)
 esac
 
 PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
-PACKTMP="$GIT_OBJECT_DIRECTORY/.tmp-$$-pack"
+PACKTMP="$PACKDIR/.tmp-$$-pack"
 rm -f "$PACKTMP"-*
 trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15
 
@@ -80,6 +82,8 @@ case ",$all_into_one," in
        ;;
 esac
 
+mkdir -p "$PACKDIR" || exit
+
 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
@@ -88,7 +92,6 @@ if [ -z "$names" ]; then
 fi
 
 # Ok we have prepared all new packfiles.
-mkdir -p "$PACKDIR" || exit
 
 # First see if there are packs of the same name and if so
 # if we can move them out of the way (this can happen if we
index 6dab3bf6a74bb52c7294e33dbac5bf87b51a9fdf..76565de2ee517f48001ffacca32e3c08320cfe38 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 #
 # Copyright 2002,2005 Greg Kroah-Hartman <greg@kroah.com>
 # Copyright 2005 Ryan Anderson <ryan@michonline.com>
@@ -16,6 +16,7 @@
 #    and second line is the subject of the message.
 #
 
+use 5.008;
 use strict;
 use warnings;
 use Term::ReadLine;
@@ -24,6 +25,7 @@ use Text::ParseWords;
 use Data::Dumper;
 use Term::ANSIColor;
 use File::Temp qw/ tempdir tempfile /;
+use File::Spec::Functions qw(catfile);
 use Error qw(:try);
 use Git;
 
@@ -60,6 +62,7 @@ git send-email [options] <file | directory | rev-list options >
     --envelope-sender       <str>  * Email envelope sender.
     --smtp-server       <str:int>  * Outgoing SMTP server to use. The port
                                      is optional. Default 'localhost'.
+    --smtp-server-option    <str>  * Outgoing SMTP server option to use.
     --smtp-server-port      <int>  * Outgoing SMTP server port.
     --smtp-user             <str>  * Username for SMTP-AUTH.
     --smtp-pass             <str>  * Password for SMTP-AUTH; not necessary.
@@ -70,6 +73,7 @@ git send-email [options] <file | directory | rev-list options >
 
   Automating:
     --identity              <str>  * Use the sendemail.<id> options.
+    --to-cmd                <str>  * Email To: via `<str> \$patch_path`
     --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
     --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, all.
     --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on.
@@ -85,6 +89,7 @@ git send-email [options] <file | directory | rev-list options >
     --[no-]validate                * Perform patch sanity checks. Default on.
     --[no-]format-patch            * understand any non optional arguments as
                                      `git format-patch` ones.
+    --force                        * Send even if safety checks would prevent it.
 
 EOT
        exit(1);
@@ -134,11 +139,8 @@ my $have_mail_address = eval { require Mail::Address; 1 };
 my $smtp;
 my $auth;
 
-sub unique_email_list(@);
-sub cleanup_compose_files();
-
 # Variables we fill in automatically, or via prompting:
-my (@to,$no_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
+my (@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
        $initial_reply_to,$initial_subject,@files,
        $author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
@@ -162,6 +164,7 @@ if ($@) {
 my ($quiet, $dry_run) = (0, 0);
 my $format_patch;
 my $compose_filename;
+my $force = 0;
 
 # Handle interactive edition of files.
 my $multiedit;
@@ -187,9 +190,11 @@ sub do_edit {
 }
 
 # Variables with corresponding config settings
-my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
-my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
-my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts, $smtp_domain);
+my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
+my ($to_cmd, $cc_cmd);
+my ($smtp_server, $smtp_server_port, @smtp_server_options);
+my ($smtp_authuser, $smtp_encryption);
+my ($identity, $aliasfiletype, @alias_files, $smtp_domain);
 my ($validate, $confirm);
 my (@suppress_cc);
 my ($auto_8bit_encoding);
@@ -210,10 +215,12 @@ my %config_bool_settings = (
 my %config_settings = (
     "smtpserver" => \$smtp_server,
     "smtpserverport" => \$smtp_server_port,
+    "smtpserveroption" => \@smtp_server_options,
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
-       "smtpdomain" => \$smtp_domain,
-    "to" => \@to,
+    "smtpdomain" => \$smtp_domain,
+    "to" => \@initial_to,
+    "tocmd" => \$to_cmd,
     "cc" => \@initial_cc,
     "cccmd" => \$cc_cmd,
     "aliasfiletype" => \$aliasfiletype,
@@ -271,7 +278,8 @@ $SIG{INT}  = \&signal_handler;
 my $rc = GetOptions("sender|from=s" => \$sender,
                     "in-reply-to=s" => \$initial_reply_to,
                    "subject=s" => \$initial_subject,
-                   "to=s" => \@to,
+                   "to=s" => \@initial_to,
+                   "to-cmd=s" => \$to_cmd,
                    "no-to" => \$no_to,
                    "cc=s" => \@initial_cc,
                    "no-cc" => \$no_cc,
@@ -279,6 +287,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
                    "no-bcc" => \$no_bcc,
                    "chain-reply-to!" => \$chain_reply_to,
                    "smtp-server=s" => \$smtp_server,
+                   "smtp-server-option=s" => \@smtp_server_options,
                    "smtp-server-port=s" => \$smtp_server_port,
                    "smtp-user=s" => \$smtp_authuser,
                    "smtp-pass:s" => \$smtp_authpass,
@@ -301,6 +310,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
                    "validate!" => \$validate,
                    "format-patch!" => \$format_patch,
                    "8bit-encoding=s" => \$auto_8bit_encoding,
+                   "force" => \$force,
         );
 
 unless ($rc) {
@@ -364,7 +374,7 @@ my(%suppress_cc);
 if (@suppress_cc) {
        foreach my $entry (@suppress_cc) {
                die "Unknown --suppress-cc field: '$entry'\n"
-                       unless $entry =~ /^(all|cccmd|cc|author|self|sob|body|bodycc)$/;
+                       unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
                $suppress_cc{$entry} = 1;
        }
 }
@@ -409,7 +419,7 @@ my ($repoauthor, $repocommitter);
 
 # Verify the user input
 
-foreach my $entry (@to) {
+foreach my $entry (@initial_to) {
        die "Comma in --to entry: $entry'\n" unless $entry !~ m/,/;
 }
 
@@ -508,12 +518,12 @@ while (defined(my $f = shift @ARGV)) {
                push @rev_list_opts, "--", @ARGV;
                @ARGV = ();
        } elsif (-d $f and !check_file_rev_conflict($f)) {
-               opendir(DH,$f)
+               opendir my $dh, $f
                        or die "Failed to opendir $f: $!";
 
-               push @files, grep { -f $_ } map { +$f . "/" . $_ }
-                               sort readdir(DH);
-               closedir(DH);
+               push @files, grep { -f $_ } map { catfile($f, $_) }
+                               sort readdir $dh;
+               closedir $dh;
        } elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
                push @files, $f;
        } else {
@@ -545,7 +555,7 @@ if (@files) {
        usage();
 }
 
-sub get_patch_subject($) {
+sub get_patch_subject {
        my $fn = shift;
        open (my $fh, '<', $fn);
        while (my $line = <$fh>) {
@@ -563,7 +573,7 @@ if ($compose) {
        $compose_filename = ($repo ?
                tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
                tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
-       open(C,">",$compose_filename)
+       open my $c, ">", $compose_filename
                or die "Failed to open for writing $compose_filename: $!";
 
 
@@ -571,7 +581,7 @@ if ($compose) {
        my $tpl_subject = $initial_subject || '';
        my $tpl_reply_to = $initial_reply_to || '';
 
-       print C <<EOT;
+       print $c <<EOT;
 From $tpl_sender # This line is ignored.
 GIT: Lines beginning in "GIT:" will be removed.
 GIT: Consider including an overall diffstat or table of contents
@@ -584,9 +594,9 @@ In-Reply-To: $tpl_reply_to
 
 EOT
        for my $f (@files) {
-               print C get_patch_subject($f);
+               print $c get_patch_subject($f);
        }
-       close(C);
+       close $c;
 
        if ($annotate) {
                do_edit($compose_filename, @files);
@@ -594,23 +604,23 @@ EOT
                do_edit($compose_filename);
        }
 
-       open(C2,">",$compose_filename . ".final")
+       open my $c2, ">", $compose_filename . ".final"
                or die "Failed to open $compose_filename.final : " . $!;
 
-       open(C,"<",$compose_filename)
+       open $c, "<", $compose_filename
                or die "Failed to open $compose_filename : " . $!;
 
        my $need_8bit_cte = file_has_nonascii($compose_filename);
        my $in_body = 0;
        my $summary_empty = 1;
-       while(<C>) {
+       while(<$c>) {
                next if m/^GIT:/;
                if ($in_body) {
                        $summary_empty = 0 unless (/^\n$/);
                } elsif (/^\n$/) {
                        $in_body = 1;
                        if ($need_8bit_cte) {
-                               print C2 "MIME-Version: 1.0\n",
+                               print $c2 "MIME-Version: 1.0\n",
                                         "Content-Type: text/plain; ",
                                           "charset=UTF-8\n",
                                         "Content-Transfer-Encoding: 8bit\n";
@@ -635,10 +645,10 @@ EOT
                        print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
                        next;
                }
-               print C2 $_;
+               print $c2 $_;
        }
-       close(C);
-       close(C2);
+       close $c;
+       close $c2;
 
        if ($summary_empty) {
                print "Summary email is empty, skipping it\n";
@@ -675,7 +685,7 @@ sub ask {
 
 my %broken_encoding;
 
-sub file_declares_8bit_cte($) {
+sub file_declares_8bit_cte {
        my $fn = shift;
        open (my $fh, '<', $fn);
        while (my $line = <$fh>) {
@@ -702,6 +712,16 @@ if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
                                  default => "UTF-8");
 }
 
+if (!$force) {
+       for my $f (@files) {
+               if (get_patch_subject($f) =~ /\Q*** SUBJECT HERE ***\E/) {
+                       die "Refusing to send because the patch\n\t$f\n"
+                               . "has the template subject '*** SUBJECT HERE ***'. "
+                               . "Pass --force if you really want to send.\n";
+               }
+       }
+}
+
 my $prompting = 0;
 if (!defined $sender) {
        $sender = $repoauthor || $repocommitter || '';
@@ -711,9 +731,9 @@ if (!defined $sender) {
        $prompting++;
 }
 
-if (!@to) {
+if (!@initial_to && !defined $to_cmd) {
        my $to = ask("Who should the emails be sent to? ");
-       push @to, parse_address_line($to) if defined $to; # sanitized/validated later
+       push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later
        $prompting++;
 }
 
@@ -731,8 +751,8 @@ sub expand_one_alias {
        return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias;
 }
 
-@to = expand_aliases(@to);
-@to = (map { sanitize_address($_) } @to);
+@initial_to = expand_aliases(@initial_to);
+@initial_to = (map { sanitize_address($_) } @initial_to);
 @initial_cc = expand_aliases(@initial_cc);
 @bcclist = expand_aliases(@bcclist);
 
@@ -766,8 +786,8 @@ our ($message_id, %mail, $subject, $reply_to, $references, $message,
 
 sub extract_valid_address {
        my $address = shift;
-       my $local_part_regexp = '[^<>"\s@]+';
-       my $domain_regexp = '[^.<>"\s@]+(?:\.[^.<>"\s@]+)+';
+       my $local_part_regexp = qr/[^<>"\s@]+/;
+       my $domain_regexp = qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/;
 
        # check for a local address:
        return $address if ($address =~ /^($local_part_regexp)$/);
@@ -808,7 +828,7 @@ sub make_message_id {
                last if (defined $du_part and $du_part ne '');
        }
        if (not defined $du_part or $du_part eq '') {
-               use Sys::Hostname qw();
+               require Sys::Hostname;
                $du_part = 'user@' . Sys::Hostname::hostname();
        }
        my $message_id_template = "<%s-git-send-email-%s>";
@@ -841,8 +861,8 @@ sub quote_rfc2047 {
 
 sub is_rfc2047_quoted {
        my $s = shift;
-       my $token = '[^][()<>@,;:"\/?.= \000-\037\177-\377]+';
-       my $encoded_text = '[!->@-~]+';
+       my $token = qr/[^][()<>@,;:"\/?.= \000-\037\177-\377]+/;
+       my $encoded_text = qr/[!->@-~]+/;
        length($s) <= 75 &&
        $s =~ m/^(?:"[[:ascii:]]*"|=\?$token\?$token\?$encoded_text\?=)$/o;
 }
@@ -853,7 +873,7 @@ sub sanitize_address {
        my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
 
        if (not $recipient_name) {
-               return "$recipient";
+               return $recipient;
        }
 
        # if recipient_name is already quoted, do nothing
@@ -870,7 +890,7 @@ sub sanitize_address {
        # double quotes are needed if specials or CTLs are included
        elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) {
                $recipient_name =~ s/(["\\\r])/\\$1/g;
-               $recipient_name = "\"$recipient_name\"";
+               $recipient_name = qq["$recipient_name"];
        }
 
        return "$recipient_name $recipient_addr";
@@ -895,7 +915,7 @@ sub sanitize_address {
 
 sub valid_fqdn {
        my $domain = shift;
-       return !($^O eq 'darwin' && $domain =~ /\.local$/) && $domain =~ /\./;
+       return defined $domain && !($^O eq 'darwin' && $domain =~ /\.local$/) && $domain =~ /\./;
 }
 
 sub maildomain_net {
@@ -940,7 +960,7 @@ sub maildomain {
 sub send_message {
        my @recipients = unique_email_list(@to);
        @cc = (grep { my $cc = extract_valid_address($_);
-                     not grep { $cc eq $_ } @recipients
+                     not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients
                    }
               map { sanitize_address($_) }
               @cc);
@@ -1015,6 +1035,8 @@ X-Mailer: git-send-email $gitversion
                }
        }
 
+       unshift (@sendmail_parameters, @smtp_server_options);
+
        if ($dry_run) {
                # We don't want to send the email.
        } elsif ($smtp_server =~ m#^/#) {
@@ -1024,7 +1046,7 @@ X-Mailer: git-send-email $gitversion
                        exec($smtp_server, @sendmail_parameters) or die $!;
                }
                print $sm "$header\n$message";
-               close $sm or die $?;
+               close $sm or die $!;
        } else {
 
                if (!defined $smtp_server) {
@@ -1130,12 +1152,13 @@ $subject = $initial_subject;
 $message_num = 0;
 
 foreach my $t (@files) {
-       open(F,"<",$t) or die "can't open file $t";
+       open my $fh, "<", $t or die "can't open file $t";
 
        my $author = undef;
        my $author_encoding;
        my $has_content_type;
        my $body_encoding;
+       @to = ();
        @cc = ();
        @xh = ();
        my $input_format = undef;
@@ -1143,7 +1166,7 @@ foreach my $t (@files) {
        $message = "";
        $message_num++;
        # First unfold multiline header fields
-       while(<F>) {
+       while(<$fh>) {
                last if /^\s*$/;
                if (/^\s+\S/ and @header) {
                        chomp($header[$#header]);
@@ -1176,6 +1199,13 @@ foreach my $t (@files) {
                                        $1, $_) unless $quiet;
                                push @cc, $1;
                        }
+                       elsif (/^To:\s+(.*)$/) {
+                               foreach my $addr (parse_address_line($1)) {
+                                       printf("(mbox) Adding to: %s from line '%s'\n",
+                                               $addr, $_) unless $quiet;
+                                       push @to, sanitize_address($addr);
+                               }
+                       }
                        elsif (/^Cc:\s+(.*)$/) {
                                foreach my $addr (parse_address_line($1)) {
                                        if (unquote_rfc2047($addr) eq $sender) {
@@ -1219,7 +1249,7 @@ foreach my $t (@files) {
                }
        }
        # Now parse the message body
-       while(<F>) {
+       while(<$fh>) {
                $message .=  $_;
                if (/^(Signed-off-by|Cc): (.*)$/i) {
                        chomp;
@@ -1236,23 +1266,12 @@ foreach my $t (@files) {
                                $c, $_) unless $quiet;
                }
        }
-       close F;
-
-       if (defined $cc_cmd && !$suppress_cc{'cccmd'}) {
-               open(F, "$cc_cmd \Q$t\E |")
-                       or die "(cc-cmd) Could not execute '$cc_cmd'";
-               while(<F>) {
-                       my $c = $_;
-                       $c =~ s/^\s*//g;
-                       $c =~ s/\n$//g;
-                       next if ($c eq $sender and $suppress_from);
-                       push @cc, $c;
-                       printf("(cc-cmd) Adding cc: %s from: '%s'\n",
-                               $c, $cc_cmd) unless $quiet;
-               }
-               close F
-                       or die "(cc-cmd) failed to close pipe to '$cc_cmd'";
-       }
+       close $fh;
+
+       push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t)
+               if defined $to_cmd;
+       push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t)
+               if defined $cc_cmd && !$suppress_cc{'cccmd'};
 
        if ($broken_encoding{$t} && !$has_content_type) {
                $has_content_type = 1;
@@ -1293,13 +1312,15 @@ foreach my $t (@files) {
                ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1));
        $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc);
 
+       @to = (@initial_to, @to);
        @cc = (@initial_cc, @cc);
 
        my $message_was_sent = send_message();
 
        # set up for the next message
        if ($thread && $message_was_sent &&
-               (chain_reply_to() || !defined $reply_to || length($reply_to) == 0)) {
+               (chain_reply_to() || !defined $reply_to || length($reply_to) == 0 ||
+               $message_num == 1)) {
                $reply_to = $message_id;
                if (length $references > 0) {
                        $references .= "\n $message_id";
@@ -1310,15 +1331,38 @@ foreach my $t (@files) {
        $message_id = undef;
 }
 
+# Execute a command (e.g. $to_cmd) to get a list of email addresses
+# and return a results array
+sub recipients_cmd {
+       my ($prefix, $what, $cmd, $file) = @_;
+
+       my $sanitized_sender = sanitize_address($sender);
+       my @addresses = ();
+       open my $fh, "$cmd \Q$file\E |"
+           or die "($prefix) Could not execute '$cmd'";
+       while (my $address = <$fh>) {
+               $address =~ s/^\s*//g;
+               $address =~ s/\s*$//g;
+               $address = sanitize_address($address);
+               next if ($address eq $sanitized_sender and $suppress_from);
+               push @addresses, $address;
+               printf("($prefix) Adding %s: %s from: '%s'\n",
+                      $what, $address, $cmd) unless $quiet;
+               }
+       close $fh
+           or die "($prefix) failed to close pipe to '$cmd'";
+       return @addresses;
+}
+
 cleanup_compose_files();
 
-sub cleanup_compose_files() {
+sub cleanup_compose_files {
        unlink($compose_filename, $compose_filename . ".final") if $compose;
 }
 
 $smtp->quit if $smtp;
 
-sub unique_email_list(@) {
+sub unique_email_list {
        my %seen;
        my @emails;
 
index 6131670860514c215a6b9b7417dd05c3124e81b4..aa16b8356507e4e669ee5c4cb4b5a667942d559e 100644 (file)
@@ -145,23 +145,49 @@ require_work_tree () {
        die "fatal: $0 cannot be used without a working tree."
 }
 
+require_clean_work_tree () {
+       git rev-parse --verify HEAD >/dev/null || exit 1
+       git update-index -q --ignore-submodules --refresh
+       err=0
+
+       if ! git diff-files --quiet --ignore-submodules
+       then
+               echo >&2 "Cannot $1: You have unstaged changes."
+               err=1
+       fi
+
+       if ! git diff-index --cached --quiet --ignore-submodules HEAD --
+       then
+               if [ $err = 0 ]
+               then
+                   echo >&2 "Cannot $1: Your index contains uncommitted changes."
+               else
+                   echo >&2 "Additionally, your index contains uncommitted changes."
+               fi
+               err=1
+       fi
+
+       if [ $err = 1 ]
+       then
+               test -n "$2" && echo >&2 "$2"
+               exit 1
+       fi
+}
+
 get_author_ident_from_commit () {
        pick_author_script='
        /^author /{
                s/'\''/'\''\\'\'\''/g
                h
                s/^author \([^<]*\) <[^>]*> .*$/\1/
-               s/'\''/'\''\'\'\''/g
                s/.*/GIT_AUTHOR_NAME='\''&'\''/p
 
                g
                s/^author [^<]* <\([^>]*\)> .*$/\1/
-               s/'\''/'\''\'\'\''/g
                s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
 
                g
                s/^author [^<]* <[^>]*> \(.*\)$/\1/
-               s/'\''/'\''\'\'\''/g
                s/.*/GIT_AUTHOR_DATE='\''&'\''/p
 
                q
@@ -209,5 +235,20 @@ case $(uname -s) in
        find () {
                /usr/bin/find "$@"
        }
+       is_absolute_path () {
+               case "$1" in
+               [/\\]* | [A-Za-z]:*)
+                       return 0 ;;
+               esac
+               return 1
+       }
        ;;
+*)
+       is_absolute_path () {
+               case "$1" in
+               /*)
+                       return 0 ;;
+               esac
+               return 1
+       }
 esac
index 1d95447d03f342811dd77386f2e83d0814beabf2..7561b374d2ec90fe774e98e39a121a1a3a3cdfa9 100755 (executable)
@@ -210,56 +210,139 @@ list_stash () {
 }
 
 show_stash () {
-       have_stash || die 'No stash found'
+       assert_stash_like "$@"
 
-       flags=$(git rev-parse --no-revs --flags "$@")
-       if test -z "$flags"
-       then
-               flags=--stat
-       fi
-
-       w_commit=$(git rev-parse --quiet --verify --default $ref_stash "$@") &&
-       b_commit=$(git rev-parse --quiet --verify "$w_commit^") ||
-               die "'$*' is not a stash"
-
-       git diff $flags $b_commit $w_commit
+       git diff ${FLAGS:---stat} $b_commit $w_commit
 }
 
-apply_stash () {
-       applied_stash=
-       unstash_index=
-
-       while test $# != 0
+#
+# Parses the remaining options looking for flags and
+# at most one revision defaulting to ${ref_stash}@{0}
+# if none found.
+#
+# Derives related tree and commit objects from the
+# revision, if one is found.
+#
+# stash records the work tree, and is a merge between the
+# base commit (first parent) and the index tree (second parent).
+#
+#   REV is set to the symbolic version of the specified stash-like commit
+#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash
+#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref
+#   s is set to the SHA1 of the stash commit
+#   w_commit is set to the commit containing the working tree
+#   b_commit is set to the base commit
+#   i_commit is set to the commit containing the index tree
+#   w_tree is set to the working tree
+#   b_tree is set to the base tree
+#   i_tree is set to the index tree
+#
+#   GIT_QUIET is set to t if -q is specified
+#   INDEX_OPTION is set to --index if --index is specified.
+#   FLAGS is set to the remaining flags
+#
+# dies if:
+#   * too many revisions specified
+#   * no revision is specified and there is no stash stack
+#   * a revision is specified which cannot be resolve to a SHA1
+#   * a non-existent stash reference is specified
+#
+
+parse_flags_and_rev()
+{
+       test "$PARSE_CACHE" = "$*" && return 0 # optimisation
+       PARSE_CACHE="$*"
+
+       IS_STASH_LIKE=
+       IS_STASH_REF=
+       INDEX_OPTION=
+       s=
+       w_commit=
+       b_commit=
+       i_commit=
+       w_tree=
+       b_tree=
+       i_tree=
+
+       REV=$(git rev-parse --no-flags --symbolic "$@" 2>/dev/null)
+
+       FLAGS=
+       for opt
        do
-               case "$1" in
-               --index)
-                       unstash_index=t
+               case "$opt" in
+                       -q|--quiet)
+                               GIT_QUIET=-t
                        ;;
-               -q|--quiet)
-                       GIT_QUIET=t
+                       --index)
+                               INDEX_OPTION=--index
                        ;;
-               *)
-                       break
+                       -*)
+                               FLAGS="${FLAGS}${FLAGS:+ }$opt"
                        ;;
                esac
-               shift
        done
 
-       if test $# = 0
+       set -- $REV
+
+       case $# in
+               0)
+                       have_stash || die "No stash found."
+                       set -- ${ref_stash}@{0}
+               ;;
+               1)
+                       :
+               ;;
+               *)
+                       die "Too many revisions specified: $REV"
+               ;;
+       esac
+
+       REV=$(git rev-parse --quiet --symbolic --verify $1 2>/dev/null) || die "$1 is not valid reference"
+
+       i_commit=$(git rev-parse --quiet --verify $REV^2 2>/dev/null) &&
+       set -- $(git rev-parse $REV $REV^1 $REV: $REV^1: $REV^2: 2>/dev/null) &&
+       s=$1 &&
+       w_commit=$1 &&
+       b_commit=$2 &&
+       w_tree=$3 &&
+       b_tree=$4 &&
+       i_tree=$5 &&
+       IS_STASH_LIKE=t &&
+       test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
+       IS_STASH_REF=t
+
+       if test "${REV}" != "${REV%{*\}}"
        then
-               have_stash || die 'Nothing to apply'
-               applied_stash="$ref_stash@{0}"
-       else
-               applied_stash="$*"
+               # maintainers: it would be better if git rev-parse indicated
+               # this condition with a non-zero status code but as of 1.7.2.1 it
+               # it did not. So, we use non-empty stderr output as a proxy for the
+               # condition of interest.
+               test -z "$(git rev-parse "$REV" 2>&1 >/dev/null)" || die "$REV does not exist in the stash log"
        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 --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"
+}
+
+is_stash_like()
+{
+       parse_flags_and_rev "$@"
+       test -n "$IS_STASH_LIKE"
+}
+
+assert_stash_like() {
+       is_stash_like "$@" || die "'$*' is not a stash-like commit"
+}
+
+is_stash_ref() {
+       is_stash_like "$@" && test -n "$IS_STASH_REF"
+}
+
+assert_stash_ref() {
+       is_stash_ref "$@" || die "'$*' is not a stash reference"
+}
+
+apply_stash () {
+
+       assert_stash_like "$@"
 
        git update-index -q --refresh &&
        git diff-files --quiet --ignore-submodules ||
@@ -270,7 +353,7 @@ apply_stash () {
                die 'Cannot apply a stash in the middle of a merge'
 
        unstashed_index_tree=
-       if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
+       if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
                        test "$c_tree" != "$i_tree"
        then
                git diff-tree --binary $s^2^..$s^2 | git apply --cached
@@ -290,7 +373,7 @@ apply_stash () {
 
        if test -n "$GIT_QUIET"
        then
-               export GIT_MERGE_VERBOSITY=0
+               GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
        fi
        if git merge-recursive $b_tree -- $c_tree $w_tree
        then
@@ -315,7 +398,7 @@ apply_stash () {
        else
                # Merge conflict; keep the exit status from merge-recursive
                status=$?
-               if test -n "$unstash_index"
+               if test -n "$INDEX_OPTION"
                then
                        echo >&2 'Index was not unstashed.'
                fi
@@ -323,58 +406,38 @@ apply_stash () {
        fi
 }
 
-drop_stash () {
-       have_stash || die 'No stash entries to drop'
+pop_stash() {
+       assert_stash_ref "$@"
 
-       while test $# != 0
-       do
-               case "$1" in
-               -q|--quiet)
-                       GIT_QUIET=t
-                       ;;
-               *)
-                       break
-                       ;;
-               esac
-               shift
-       done
+       apply_stash "$@" &&
+       drop_stash "$@"
+}
 
-       if test $# = 0
-       then
-               set x "$ref_stash@{0}"
-               shift
-       fi
-       # Verify supplied argument looks like a stash entry
-       s=$(git rev-parse --verify "$@") &&
-       git rev-parse --verify "$s:"   > /dev/null 2>&1 &&
-       git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
-       git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
-               die "$*: not a valid stashed state"
+drop_stash () {
+       assert_stash_ref "$@"
 
-       git reflog delete --updateref --rewrite "$@" &&
-               say "Dropped $* ($s)" || die "$*: Could not drop stash entry"
+       git reflog delete --updateref --rewrite "${REV}" &&
+               say "Dropped ${REV} ($s)" || die "${REV}: 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
 }
 
 apply_to_branch () {
-       have_stash || die 'Nothing to apply'
-
        test -n "$1" || die 'No branch name specified'
        branch=$1
+       shift 1
 
-       if test -z "$2"
-       then
-               set x "$ref_stash@{0}"
-       fi
-       stash=$2
+       set -- --index "$@"
+       assert_stash_like "$@"
 
-       git checkout -b $branch $stash^ &&
-       apply_stash --index $stash &&
-       drop_stash $stash
+       git checkout -b $branch $REV^ &&
+       apply_stash "$@" && {
+               test -z "$IS_STASH_REF" || drop_stash "$@"
+       }
 }
 
+PARSE_CACHE='--not-parsed'
 # The default command is "save" if nothing but options are given
 seen_non_option=
 for opt
@@ -422,10 +485,7 @@ drop)
        ;;
 pop)
        shift
-       if apply_stash "$@"
-       then
-               drop_stash "$applied_stash"
-       fi
+       pop_stash "$@"
        ;;
 branch)
        shift
index 170186f4946859e04d6a0b4ce248dd17bc877b31..8b9058971767dbb4d94e996876f6ba7ed178ddd6 100755 (executable)
@@ -37,12 +37,24 @@ resolve_relative_url ()
                die "remote ($remote) does not have a url defined in .git/config"
        url="$1"
        remoteurl=${remoteurl%/}
+       sep=/
        while test -n "$url"
        do
                case "$url" in
                ../*)
                        url="${url#../}"
-                       remoteurl="${remoteurl%/*}"
+                       case "$remoteurl" in
+                       */*)
+                               remoteurl="${remoteurl%/*}"
+                               ;;
+                       *:*)
+                               remoteurl="${remoteurl%:*}"
+                               sep=:
+                               ;;
+                       *)
+                               die "cannot strip one component off url '$remoteurl'"
+                               ;;
+                       esac
                        ;;
                ./*)
                        url="${url#./}"
@@ -51,7 +63,7 @@ resolve_relative_url ()
                        break;;
                esac
        done
-       echo "$remoteurl/${url%/}"
+       echo "$remoteurl$sep${url%/}"
 }
 
 #
@@ -93,20 +105,6 @@ module_clone()
        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
-       # action) and try to remove it.
-       # Note: if $path is a symlink to a directory the test will
-       # succeed but the rmdir will fail. We might want to fix this.
-       if test -d "$path"
-       then
-               rmdir "$path" 2>/dev/null ||
-               die "Directory '$path' exists, but is neither empty nor a git repository"
-       fi
-
-       test -e "$path" &&
-       die "A file already exist at path '$path'"
-
        if test -n "$reference"
        then
                git-clone "$reference" -n "$url" "$path"
@@ -241,7 +239,7 @@ cmd_add()
                        # ash fails to wordsplit ${branch:+-b "$branch"...}
                        case "$branch" in
                        '') git checkout -f -q ;;
-                       ?*) git checkout -f -q -b "$branch" "origin/$branch" ;;
+                       ?*) git checkout -f -q -B "$branch" "origin/$branch" ;;
                        esac
                ) || die "Unable to checkout submodule '$path'"
        fi
@@ -374,41 +372,35 @@ cmd_init()
 cmd_update()
 {
        # parse $args after "submodule ... update".
-       orig_args="$@"
+       orig_flags=
        while test $# -ne 0
        do
                case "$1" in
                -q|--quiet)
-                       shift
                        GIT_QUIET=1
                        ;;
                -i|--init)
                        init=1
-                       shift
                        ;;
                -N|--no-fetch)
-                       shift
                        nofetch=1
                        ;;
                -r|--rebase)
-                       shift
                        update="rebase"
                        ;;
                --reference)
                        case "$2" in '') usage ;; esac
                        reference="--reference=$2"
-                       shift 2
+                       orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
+                       shift
                        ;;
                --reference=*)
                        reference="$1"
-                       shift
                        ;;
                -m|--merge)
-                       shift
                        update="merge"
                        ;;
                --recursive)
-                       shift
                        recursive=1
                        ;;
                --)
@@ -422,6 +414,8 @@ cmd_update()
                        break
                        ;;
                esac
+               orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
+               shift
        done
 
        if test -n "$init"
@@ -500,7 +494,7 @@ cmd_update()
 
                if test -n "$recursive"
                then
-                       (clear_local_git_env; cd "$path" && cmd_update $orig_args) ||
+                       (clear_local_git_env; cd "$path" && eval cmd_update "$orig_flags") ||
                        die "Failed to recurse into submodule path '$path'"
                fi
        done
@@ -733,7 +727,7 @@ cmd_summary() {
 cmd_status()
 {
        # parse $args after "submodule ... status".
-       orig_args="$@"
+       orig_flags=
        while test $# -ne 0
        do
                case "$1" in
@@ -757,6 +751,7 @@ cmd_status()
                        break
                        ;;
                esac
+               orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
                shift
        done
 
@@ -790,7 +785,7 @@ cmd_status()
                                prefix="$displaypath/"
                                clear_local_git_env
                                cd "$path" &&
-                               cmd_status $orig_args
+                               eval cmd_status "$orig_args"
                        ) ||
                        die "Failed to recurse into submodule path '$path'"
                fi
@@ -836,13 +831,15 @@ cmd_sync()
                        ;;
                esac
 
+               say "Synchronizing submodule url for '$name'"
+               git config submodule."$name".url "$url"
+
                if test -e "$path"/.git
                then
                (
                        clear_local_git_env
                        cd "$path"
                        remote=$(get_default_remote)
-                       say "Synchronizing submodule url for '$name'"
                        git config remote."$remote".url "$url"
                )
                fi
index 9b046b693fe82c992b3844bf6e8f79214dfa7a59..177dd259cd53cde017d73db5ccbecc7ed7dfa573 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/env perl
 # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
 # License: GPL v2 or later
+use 5.008;
 use warnings;
 use strict;
 use vars qw/   $AUTHOR $VERSION
@@ -83,7 +84,7 @@ my ($_stdin, $_help, $_edit,
        $_version, $_fetch_all, $_no_rebase, $_fetch_parent,
        $_merge, $_strategy, $_dry_run, $_local,
        $_prefix, $_no_checkout, $_url, $_verbose,
-       $_git_format, $_commit_url, $_tag);
+       $_git_format, $_commit_url, $_tag, $_merge_info);
 $Git::SVN::_follow_parent = 1;
 $_q ||= 0;
 my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
@@ -153,6 +154,7 @@ my %cmd = (
                          'commit-url=s' => \$_commit_url,
                          'revision|r=i' => \$_revision,
                          'no-rebase' => \$_no_rebase,
+                         'mergeinfo=s' => \$_merge_info,
                        %cmt_opts, %fc_opts } ],
        branch => [ \&cmd_branch,
                    'Create a branch in the SVN repository',
@@ -568,6 +570,7 @@ sub cmd_dcommit {
                                               print "Committed r$_[0]\n";
                                               $cmt_rev = $_[0];
                                        },
+                                       mergeinfo => $_merge_info,
                                        svn_path => '');
                        if (!SVN::Git::Editor->new(\%ed_opts)->apply_diff) {
                                print "No changes\n$d~1 == $d\n";
@@ -1513,7 +1516,8 @@ sub cmt_sha2rev_batch {
 
 sub working_head_info {
        my ($head, $refs) = @_;
-       my @args = ('log', '--no-color', '--first-parent', '--pretty=medium');
+       my @args = qw/log --no-color --no-decorate --first-parent
+                     --pretty=medium/;
        my ($fh, $ctx) = command_output_pipe(@args, $head);
        my $hash;
        my %max;
@@ -3118,9 +3122,10 @@ sub _rev_list {
 sub check_cherry_pick {
        my $base = shift;
        my $tip = shift;
+       my $parents = shift;
        my @ranges = @_;
        my %commits = map { $_ => 1 }
-               _rev_list("--no-merges", $tip, "--not", $base);
+               _rev_list("--no-merges", $tip, "--not", $base, @$parents);
        for my $range ( @ranges ) {
                delete @commits{_rev_list($range)};
        }
@@ -3296,6 +3301,7 @@ sub find_extra_svn_parents {
                # double check that there are no missing non-merge commits
                my (@incomplete) = check_cherry_pick(
                        $merge_base, $merge_tip,
+                       $parents,
                        @$ranges,
                       );
 
@@ -4447,6 +4453,7 @@ sub new {
        $self->{path_prefix} = length $self->{svn_path} ?
                               "$self->{svn_path}/" : '';
        $self->{config} = $opts->{config};
+       $self->{mergeinfo} = $opts->{mergeinfo};
        return $self;
 }
 
@@ -4756,6 +4763,11 @@ sub change_file_prop {
        $self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool});
 }
 
+sub change_dir_prop {
+       my ($self, $pbat, $pname, $pval) = @_;
+       $self->SUPER::change_dir_prop($pbat, $pname, $pval, $self->{pool});
+}
+
 sub _chg_file_get_blob ($$$$) {
        my ($self, $fbat, $m, $which) = @_;
        my $fh = $::_repository->temp_acquire("git_blob_$which");
@@ -4849,6 +4861,11 @@ sub apply_diff {
                        fatal("Invalid change type: $f");
                }
        }
+
+       if (defined($self->{mergeinfo})) {
+               $self->change_dir_prop($self->{bat}{''}, "svn:mergeinfo",
+                                      $self->{mergeinfo});
+       }
        $self->rmdirs if $_rmdir;
        if (@$mods == 0) {
                $self->abort_edit;
index 3fc4166b25714911b6b1294c7439da1e237e4918..e9de241dd004a9f5d6d6d8e6a300a1fa5949a46d 100755 (executable)
@@ -31,154 +31,161 @@ valid_custom_tool()
 
 valid_tool() {
        case "$1" in
-               firefox | iceweasel | chrome | google-chrome | chromium | konqueror | w3m | links | lynx | dillo | open | start)
-                       ;; # happy
-               *)
-                       valid_custom_tool "$1" || return 1
-                       ;;
+       firefox | iceweasel | seamonkey | iceape | \
+       chrome | google-chrome | chromium | chromium-browser |\
+       konqueror | opera | w3m | elinks | links | lynx | dillo | open | start)
+               ;; # happy
+       *)
+               valid_custom_tool "$1" || return 1
+               ;;
        esac
 }
 
 init_browser_path() {
        browser_path=$(git config "browser.$1.path")
-       test -z "$browser_path" && browser_path="$1"
+       if test -z "$browser_path" &&
+          test "$1" = chromium &&
+          type chromium-browser >/dev/null 2>&1
+       then
+               browser_path=chromium-browser
+       fi
+       : ${browser_path:="$1"}
 }
 
 while test $# != 0
 do
-    case "$1" in
+       case "$1" in
        -b|--browser*|-t|--tool*)
-           case "$#,$1" in
+               case "$#,$1" in
                *,*=*)
-                   browser=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-                   ;;
+                       browser=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+                       ;;
                1,*)
-                   usage ;;
+                       usage ;;
                *)
-                   browser="$2"
-                   shift ;;
-           esac
-           ;;
+                       browser="$2"
+                       shift ;;
+               esac
+               ;;
        -c|--config*)
-           case "$#,$1" in
+               case "$#,$1" in
                *,*=*)
-                   conf=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-                   ;;
+                       conf=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+                       ;;
                1,*)
-                   usage ;;
+                       usage ;;
                *)
-                   conf="$2"
-                   shift ;;
-           esac
-           ;;
+                       conf="$2"
+                       shift ;;
+               esac
+               ;;
        --)
-           break
-           ;;
+               break
+               ;;
        -*)
-           usage
-           ;;
+               usage
+               ;;
        *)
-           break
-           ;;
-    esac
-    shift
+               break
+               ;;
+       esac
+       shift
 done
 
 test $# = 0 && usage
 
 if test -z "$browser"
 then
-    for opt in "$conf" "web.browser"
-    do
-       test -z "$opt" && continue
-       browser="`git config $opt`"
-       test -z "$browser" || break
-    done
-    if test -n "$browser" && ! valid_tool "$browser"; then
-       echo >&2 "git config option $opt set to unknown browser: $browser"
-       echo >&2 "Resetting to default..."
-       unset browser
-    fi
+       for opt in "$conf" "web.browser"
+       do
+               test -z "$opt" && continue
+               browser="`git config $opt`"
+               test -z "$browser" || break
+       done
+       if test -n "$browser" && ! valid_tool "$browser"; then
+               echo >&2 "git config option $opt set to unknown browser: $browser"
+               echo >&2 "Resetting to default..."
+               unset browser
+       fi
 fi
 
 if test -z "$browser" ; then
-    if test -n "$DISPLAY"; then
-       browser_candidates="firefox iceweasel google-chrome chrome chromium konqueror w3m links lynx dillo"
-       if test "$KDE_FULL_SESSION" = "true"; then
-           browser_candidates="konqueror $browser_candidates"
+       if test -n "$DISPLAY"; then
+               browser_candidates="firefox iceweasel google-chrome chrome chromium chromium-browser konqueror opera seamonkey iceape w3m elinks links lynx dillo"
+               if test "$KDE_FULL_SESSION" = "true"; then
+                       browser_candidates="konqueror $browser_candidates"
+               fi
+       else
+               browser_candidates="w3m elinks links lynx"
        fi
-    else
-       browser_candidates="w3m links lynx"
-    fi
-    # SECURITYSESSIONID indicates an OS X GUI login session
-    if test -n "$SECURITYSESSIONID" \
-           -o "$TERM_PROGRAM" = "Apple_Terminal" ; then
-       browser_candidates="open $browser_candidates"
-    fi
-    # /bin/start indicates MinGW
-    if test -x /bin/start; then
-       browser_candidates="start $browser_candidates"
-    fi
-
-    for i in $browser_candidates; do
-       init_browser_path $i
-       if type "$browser_path" > /dev/null 2>&1; then
-           browser=$i
-           break
+       # SECURITYSESSIONID indicates an OS X GUI login session
+       if test -n "$SECURITYSESSIONID" \
+               -o "$TERM_PROGRAM" = "Apple_Terminal" ; then
+               browser_candidates="open $browser_candidates"
        fi
-    done
-    test -z "$browser" && die "No known browser available."
+       # /bin/start indicates MinGW
+       if test -x /bin/start; then
+               browser_candidates="start $browser_candidates"
+       fi
+
+       for i in $browser_candidates; do
+               init_browser_path $i
+               if type "$browser_path" > /dev/null 2>&1; then
+                       browser=$i
+                       break
+               fi
+       done
+       test -z "$browser" && die "No known browser available."
 else
-    valid_tool "$browser" || die "Unknown browser '$browser'."
+       valid_tool "$browser" || die "Unknown browser '$browser'."
 
-    init_browser_path "$browser"
+       init_browser_path "$browser"
 
-    if test -z "$browser_cmd" && ! type "$browser_path" > /dev/null 2>&1; then
-       die "The browser $browser is not available as '$browser_path'."
-    fi
+       if test -z "$browser_cmd" && ! type "$browser_path" > /dev/null 2>&1; then
+               die "The browser $browser is not available as '$browser_path'."
+       fi
 fi
 
 case "$browser" in
-    firefox|iceweasel)
+firefox|iceweasel|seamonkey|iceape)
        # Check version because firefox < 2.0 does not support "-new-tab".
        vers=$(expr "$($browser_path -version)" : '.* \([0-9][0-9]*\)\..*')
        NEWTAB='-new-tab'
        test "$vers" -lt 2 && NEWTAB=''
        "$browser_path" $NEWTAB "$@" &
        ;;
-    google-chrome|chrome|chromium)
-       # Actual command for chromium is chromium-browser.
+google-chrome|chrome|chromium|chromium-browser)
        # No need to specify newTab. It's default in chromium
        eval "$browser_path" "$@" &
        ;;
-    konqueror)
+konqueror)
        case "$(basename "$browser_path")" in
-           konqueror)
+       konqueror)
                # It's simpler to use kfmclient to open a new tab in konqueror.
                browser_path="$(echo "$browser_path" | sed -e 's/konqueror$/kfmclient/')"
                type "$browser_path" > /dev/null 2>&1 || die "No '$browser_path' found."
                eval "$browser_path" newTab "$@"
                ;;
-           kfmclient)
+       kfmclient)
                eval "$browser_path" newTab "$@"
                ;;
-           *)
+       *)
                "$browser_path" "$@" &
                ;;
        esac
        ;;
-    w3m|links|lynx|open)
+w3m|elinks|links|lynx|open)
        eval "$browser_path" "$@"
        ;;
-    start)
-        exec "$browser_path" '"web-browse"' "$@"
-        ;;
-    dillo)
+start)
+       exec "$browser_path" '"web-browse"' "$@"
+       ;;
+opera|dillo)
        "$browser_path" "$@" &
        ;;
-    *)
+*)
        if test -n "$browser_cmd"; then
-           ( eval $browser_cmd "$@" )
+               ( eval $browser_cmd "$@" )
        fi
        ;;
 esac
diff --git a/git.c b/git.c
index f37028b3b722e6203ae8dac1ccf40a94421597d2..1578b313c09fdf620b4804969aff6c020e5fa2ad 100644 (file)
--- a/git.c
+++ b/git.c
@@ -1,30 +1,40 @@
 #include "builtin.h"
-#include "exec_cmd.h"
 #include "cache.h"
+#include "exec_cmd.h"
+#include "help.h"
 #include "quote.h"
 #include "run-command.h"
 
 const char git_usage_string[] =
-       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
+       "git [--version] [--exec-path[=<path>]] [--html-path]\n"
        "           [-p|--paginate|--no-pager] [--no-replace-objects]\n"
-       "           [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
+       "           [--bare] [--git-dir=<path>] [--work-tree=<path>]\n"
        "           [-c name=value] [--help]\n"
-       "           COMMAND [ARGS]";
+       "           <command> [<args>]";
 
 const char git_more_info_string[] =
-       "See 'git help COMMAND' for more information on a specific command.";
+       "See 'git help <command>' for more information on a specific command.";
 
+static struct startup_info git_startup_info;
 static int use_pager = -1;
 struct pager_config {
        const char *cmd;
-       int val;
+       int want;
+       char *value;
 };
 
 static int pager_command_config(const char *var, const char *value, void *data)
 {
        struct pager_config *c = data;
-       if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd))
-               c->val = git_config_bool(var, value);
+       if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
+               int b = git_config_maybe_bool(var, value);
+               if (b >= 0)
+                       c->want = b;
+               else {
+                       c->want = 1;
+                       c->value = xstrdup(value);
+               }
+       }
        return 0;
 }
 
@@ -33,9 +43,12 @@ int check_pager_config(const char *cmd)
 {
        struct pager_config c;
        c.cmd = cmd;
-       c.val = -1;
+       c.want = -1;
+       c.value = NULL;
        git_config(pager_command_config, &c);
-       return c.val;
+       if (c.value)
+               pager_program = c.value;
+       return c.want;
 }
 
 static void commit_pager_choice(void) {
@@ -55,9 +68,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 {
        int handled = 0;
 
-       if (!getenv("GIT_ASKPASS") && getenv("SSH_ASKPASS"))
-               setenv("GIT_ASKPASS", getenv("SSH_ASKPASS"), 1);
-
        while (*argc > 0) {
                const char *cmd = (*argv)[0];
                if (cmd[0] != '-')
@@ -136,7 +146,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                fprintf(stderr, "-c expects a configuration string\n" );
                                usage(git_usage_string);
                        }
-                       git_config_parse_parameter((*argv)[1]);
+                       git_config_push_parameter((*argv)[1]);
                        (*argv)++;
                        (*argc)--;
                } else {
@@ -167,28 +177,29 @@ static int handle_alias(int *argcp, const char ***argv)
        alias_string = alias_lookup(alias_command);
        if (alias_string) {
                if (alias_string[0] == '!') {
+                       const char **alias_argv;
+                       int argc = *argcp, i;
+
                        commit_pager_choice();
-                       if (*argcp > 1) {
-                               struct strbuf buf;
-
-                               strbuf_init(&buf, PATH_MAX);
-                               strbuf_addstr(&buf, alias_string);
-                               sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
-                               free(alias_string);
-                               alias_string = buf.buf;
-                       }
-                       trace_printf("trace: alias to shell cmd: %s => %s\n",
-                                    alias_command, alias_string + 1);
-                       ret = system(alias_string + 1);
-                       if (ret >= 0 && WIFEXITED(ret) &&
-                           WEXITSTATUS(ret) != 127)
-                               exit(WEXITSTATUS(ret));
-                       die("Failed to run '%s' when expanding alias '%s'",
-                           alias_string + 1, alias_command);
+
+                       /* build alias_argv */
+                       alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
+                       alias_argv[0] = alias_string + 1;
+                       for (i = 1; i < argc; ++i)
+                               alias_argv[i] = (*argv)[i];
+                       alias_argv[argc] = NULL;
+
+                       ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
+                       if (ret >= 0)   /* normal exit */
+                               exit(ret);
+
+                       die_errno("While expanding alias '%s': '%s'",
+                           alias_command, alias_string + 1);
                }
                count = split_cmdline(alias_string, &new_argv);
                if (count < 0)
-                       die("Bad alias.%s string", alias_command);
+                       die("Bad alias.%s string: %s", alias_command,
+                           split_cmdline_strerror(count));
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
                        die("alias '%s' changes environment variables\n"
@@ -229,13 +240,14 @@ static int handle_alias(int *argcp, const char ***argv)
 
 const char git_version_string[] = GIT_VERSION;
 
-#define RUN_SETUP      (1<<0)
-#define USE_PAGER      (1<<1)
+#define RUN_SETUP              (1<<0)
+#define RUN_SETUP_GENTLY       (1<<1)
+#define USE_PAGER              (1<<2)
 /*
  * require working tree to be present -- anything uses this needs
  * RUN_SETUP for reading from the configuration file.
  */
-#define NEED_WORK_TREE (1<<2)
+#define NEED_WORK_TREE         (1<<3)
 
 struct cmd_struct {
        const char *cmd;
@@ -254,11 +266,19 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
        if (!help) {
                if (p->option & RUN_SETUP)
                        prefix = setup_git_directory();
+               if (p->option & RUN_SETUP_GENTLY) {
+                       int nongit_ok;
+                       prefix = setup_git_directory_gently(&nongit_ok);
+               }
 
-               if (use_pager == -1 && p->option & RUN_SETUP)
+               if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))
                        use_pager = check_pager_config(p->cmd);
                if (use_pager == -1 && p->option & USE_PAGER)
                        use_pager = 1;
+
+               if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) &&
+                   startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */
+                       trace_repo_setup(prefix);
        }
        commit_pager_choice();
 
@@ -295,12 +315,12 @@ static void handle_internal_command(int argc, const char **argv)
                { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
                { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
                { "annotate", cmd_annotate, RUN_SETUP },
-               { "apply", cmd_apply },
+               { "apply", cmd_apply, RUN_SETUP_GENTLY },
                { "archive", cmd_archive },
                { "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
                { "blame", cmd_blame, RUN_SETUP },
                { "branch", cmd_branch, RUN_SETUP },
-               { "bundle", cmd_bundle },
+               { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
                { "cat-file", cmd_cat_file, RUN_SETUP },
                { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
                { "checkout-index", cmd_checkout_index,
@@ -313,7 +333,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
                { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
-               { "config", cmd_config },
+               { "config", cmd_config, RUN_SETUP_GENTLY },
                { "count-objects", cmd_count_objects, RUN_SETUP },
                { "describe", cmd_describe, RUN_SETUP },
                { "diff", cmd_diff },
@@ -330,21 +350,21 @@ static void handle_internal_command(int argc, const char **argv)
                { "fsck-objects", cmd_fsck, RUN_SETUP },
                { "gc", cmd_gc, RUN_SETUP },
                { "get-tar-commit-id", cmd_get_tar_commit_id },
-               { "grep", cmd_grep },
+               { "grep", cmd_grep, RUN_SETUP_GENTLY },
                { "hash-object", cmd_hash_object },
                { "help", cmd_help },
-               { "index-pack", cmd_index_pack },
+               { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
                { "init", cmd_init_db },
                { "init-db", cmd_init_db },
-               { "log", cmd_log, RUN_SETUP | USE_PAGER },
+               { "log", cmd_log, RUN_SETUP },
                { "ls-files", cmd_ls_files, RUN_SETUP },
                { "ls-tree", cmd_ls_tree, RUN_SETUP },
-               { "ls-remote", cmd_ls_remote },
+               { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
                { "mailinfo", cmd_mailinfo },
                { "mailsplit", cmd_mailsplit },
                { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
                { "merge-base", cmd_merge_base, RUN_SETUP },
-               { "merge-file", cmd_merge_file },
+               { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
                { "merge-index", cmd_merge_index, RUN_SETUP },
                { "merge-ours", cmd_merge_ours, RUN_SETUP },
                { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
@@ -360,7 +380,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
                { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
                { "patch-id", cmd_patch_id },
-               { "peek-remote", cmd_ls_remote },
+               { "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
                { "pickaxe", cmd_blame, RUN_SETUP },
                { "prune", cmd_prune, RUN_SETUP },
                { "prune-packed", cmd_prune_packed, RUN_SETUP },
@@ -369,8 +389,10 @@ 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 },
+               { "remote-ext", cmd_remote_ext },
+               { "remote-fd", cmd_remote_fd },
                { "replace", cmd_replace, RUN_SETUP },
-               { "repo-config", cmd_config },
+               { "repo-config", cmd_config, RUN_SETUP_GENTLY },
                { "rerere", cmd_rerere, RUN_SETUP },
                { "reset", cmd_reset, RUN_SETUP },
                { "rev-list", cmd_rev_list, RUN_SETUP },
@@ -378,9 +400,9 @@ static void handle_internal_command(int argc, const char **argv)
                { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
                { "rm", cmd_rm, RUN_SETUP },
                { "send-pack", cmd_send_pack, RUN_SETUP },
-               { "shortlog", cmd_shortlog, USE_PAGER },
+               { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },
-               { "show", cmd_show, RUN_SETUP | USE_PAGER },
+               { "show", cmd_show, RUN_SETUP },
                { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
                { "stripspace", cmd_stripspace },
                { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
@@ -392,14 +414,15 @@ static void handle_internal_command(int argc, const char **argv)
                { "update-ref", cmd_update_ref, RUN_SETUP },
                { "update-server-info", cmd_update_server_info, RUN_SETUP },
                { "upload-archive", cmd_upload_archive },
-               { "var", cmd_var },
+               { "var", cmd_var, RUN_SETUP_GENTLY },
                { "verify-tag", cmd_verify_tag, RUN_SETUP },
                { "version", cmd_version },
-               { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
+               { "whatchanged", cmd_whatchanged, RUN_SETUP },
                { "write-tree", cmd_write_tree, RUN_SETUP },
                { "verify-pack", cmd_verify_pack },
                { "show-ref", cmd_show_ref, RUN_SETUP },
                { "pack-refs", cmd_pack_refs, RUN_SETUP },
+               { "skew", cmd_skew, RUN_SETUP },
        };
        int i;
        static const char ext[] = STRIP_EXTENSION;
@@ -489,6 +512,8 @@ int main(int argc, const char **argv)
 {
        const char *cmd;
 
+       startup_info = &git_startup_info;
+
        cmd = git_extract_argv0_path(argv[0]);
        if (!cmd)
                cmd = "git-help";
diff --git a/git_remote_helpers/setup.cfg b/git_remote_helpers/setup.cfg
new file mode 100644 (file)
index 0000000..4bff887
--- /dev/null
@@ -0,0 +1,3 @@
+[build]
+build_purelib = build/lib
+build_platlib = build/lib
old mode 100644 (file)
new mode 100755 (executable)
index 1b0e09a..e82c6bf
@@ -131,6 +131,7 @@ proc unmerged_files {files} {
 
 proc parseviewargs {n arglist} {
     global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env
+    global worddiff git_version
 
     set vdatemode($n) 0
     set vmergeonly($n) 0
@@ -168,7 +169,7 @@ proc parseviewargs {n arglist} {
                lappend diffargs $arg
            }
            "--raw" - "--patch-with-raw" - "--patch-with-stat" -
-           "--name-only" - "--name-status" - "--color" - "--color-words" -
+           "--name-only" - "--name-status" - "--color" -
            "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
            "--cc" - "-z" - "--header" - "--parents" - "--boundary" -
            "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
@@ -177,6 +178,18 @@ proc parseviewargs {n arglist} {
                # These cause our parsing of git log's output to fail, or else
                # they're options we want to set ourselves, so ignore them.
            }
+           "--color-words*" - "--word-diff=color" {
+               # These trigger a word diff in the console interface,
+               # so help the user by enabling our own support
+               if {[package vcompare $git_version "1.7.2"] >= 0} {
+                   set worddiff [mc "Color words"]
+               }
+           }
+           "--word-diff*" {
+               if {[package vcompare $git_version "1.7.2"] >= 0} {
+                   set worddiff [mc "Markup words"]
+               }
+           }
            "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
            "--check" - "--exit-code" - "--quiet" - "--topo-order" -
            "--full-history" - "--dense" - "--sparse" -
@@ -313,6 +326,7 @@ proc start_rev_list {view} {
     global viewactive viewinstances vmergeonly
     global mainheadid viewmainheadid viewmainheadid_orig
     global vcanopt vflags vrevs vorigargs
+    global show_notes
 
     set startmsecs [clock clicks -milliseconds]
     set commitidx($view) 0
@@ -361,8 +375,8 @@ proc start_rev_list {view} {
     }
 
     if {[catch {
-       set fd [open [concat | git log --no-color -z --pretty=raw --parents \
-                        --boundary $args "--" $files] r]
+       set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
+                       --parents --boundary $args "--" $files] r]
     } err]} {
        error_popup "[mc "Error executing git log:"] $err"
        return 0
@@ -456,6 +470,7 @@ proc updatecommits {} {
     global mainheadid viewmainheadid viewmainheadid_orig pending_select
     global isworktree
     global varcid vposids vnegids vflags vrevs
+    global show_notes
 
     set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
     rereadrefs
@@ -508,8 +523,8 @@ proc updatecommits {} {
        set args $vorigargs($view)
     }
     if {[catch {
-       set fd [open [concat | git log --no-color -z --pretty=raw --parents \
-                         --boundary $args "--" $vfilelimit($view)] r]
+       set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
+                       --parents --boundary $args "--" $vfilelimit($view)] r]
     } err]} {
        error_popup "[mc "Error executing git log:"] $err"
        return
@@ -1970,6 +1985,8 @@ proc makewindow {} {
     global fprogitem fprogcoord lastprogupdate progupdatepending
     global rprogitem rprogcoord rownumsel numcommits
     global have_tk85 use_ttk NS
+    global git_version
+    global worddiff
 
     # The "mc" arguments here are purely so that xgettext
     # sees the following string as needing to be translated
@@ -2243,6 +2260,15 @@ proc makewindow {} {
     ${NS}::checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
        -command changeignorespace -variable ignorespace
     pack .bleft.mid.ignspace -side left -padx 5
+
+    set worddiff [mc "Line diff"]
+    if {[package vcompare $git_version "1.7.2"] >= 0} {
+       makedroplist .bleft.mid.worddiff worddiff [mc "Line diff"] \
+           [mc "Markup words"] [mc "Color words"]
+       trace add variable worddiff write changeworddiff
+       pack .bleft.mid.worddiff -side left -padx 5
+    }
+
     set ctext .bleft.bottom.ctext
     text $ctext -background $bgcolor -foreground $fgcolor \
        -state disabled -font textfont \
@@ -2451,6 +2477,7 @@ proc makewindow {} {
     global ctxbut
     bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
     bind $ctext $ctxbut {pop_diff_menu %W %X %Y %x %y}
+    bind $ctext <Button-1> {focus %W}
 
     set maincursor [. cget -cursor]
     set textcursor [$ctext cget -cursor]
@@ -7301,6 +7328,7 @@ proc getblobline {bf id} {
                            [lindex [split $commentend .] 0]}]
            mark_ctext_line $lnum
        }
+       $ctext config -state disabled
        return 0
     }
     $ctext config -state disabled
@@ -7502,11 +7530,16 @@ proc changeignorespace {} {
     reselectline
 }
 
+proc changeworddiff {name ix op} {
+    reselectline
+}
+
 proc getblobdiffs {ids} {
     global blobdifffd diffids env
     global diffinhdr treediffs
     global diffcontext
     global ignorespace
+    global worddiff
     global limitdiffs vfilelimit curview
     global diffencoding targetline diffnparents
     global git_version currdiffsubmod
@@ -7523,6 +7556,9 @@ proc getblobdiffs {ids} {
     if {$ignorespace} {
        append cmd " -w"
     }
+    if {$worddiff ne [mc "Line diff"]} {
+       append cmd " --word-diff=porcelain"
+    }
     if {$limitdiffs && $vfilelimit($curview) ne {}} {
        set cmd [concat $cmd -- $vfilelimit($curview)]
     }
@@ -7608,6 +7644,7 @@ proc getblobdiffline {bdf ids} {
     global ctext_file_names ctext_file_lines
     global diffinhdr treediffs mergemax diffnparents
     global diffencoding jump_to_here targetline diffline currdiffsubmod
+    global worddiff
 
     set nr 0
     $ctext conf -state normal
@@ -7747,15 +7784,28 @@ proc getblobdiffline {bdf ids} {
            # parse the prefix - one ' ', '-' or '+' for each parent
            set prefix [string range $line 0 [expr {$diffnparents - 1}]]
            set tag [expr {$diffnparents > 1? "m": "d"}]
+           set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}]
+           set words_pre_markup ""
+           set words_post_markup ""
            if {[string trim $prefix " -+"] eq {}} {
                # prefix only has " ", "-" and "+" in it: normal diff line
                set num [string first "-" $prefix]
+               if {$dowords} {
+                   set line [string range $line 1 end]
+               }
                if {$num >= 0} {
                    # removed line, first parent with line is $num
                    if {$num >= $mergemax} {
                        set num "max"
                    }
-                   $ctext insert end "$line\n" $tag$num
+                   if {$dowords && $worddiff eq [mc "Markup words"]} {
+                       $ctext insert end "\[-$line-\]" $tag$num
+                   } else {
+                       $ctext insert end "$line" $tag$num
+                   }
+                   if {!$dowords} {
+                       $ctext insert end "\n" $tag$num
+                   }
                } else {
                    set tags {}
                    if {[string first "+" $prefix] >= 0} {
@@ -7770,6 +7820,8 @@ proc getblobdiffline {bdf ids} {
                                lappend tags m$num
                            }
                        }
+                       set words_pre_markup "{+"
+                       set words_post_markup "+}"
                    }
                    if {$targetline ne {}} {
                        if {$diffline == $targetline} {
@@ -7779,8 +7831,17 @@ proc getblobdiffline {bdf ids} {
                            incr diffline
                        }
                    }
-                   $ctext insert end "$line\n" $tags
+                   if {$dowords && $worddiff eq [mc "Markup words"]} {
+                       $ctext insert end "$words_pre_markup$line$words_post_markup" $tags
+                   } else {
+                       $ctext insert end "$line" $tags
+                   }
+                   if {!$dowords} {
+                       $ctext insert end "\n" $tags
+                   }
                }
+           } elseif {$dowords && $prefix eq "~"} {
+               $ctext insert end "\n" {}
            } else {
                # "\ No newline at end of file",
                # or something else we don't recognize
@@ -11391,6 +11452,7 @@ if {[tk windowingsystem] eq "win32"} {
 set diffcolors {red "#00a000" blue}
 set diffcontext 3
 set ignorespace 0
+set worddiff ""
 set markbgcolor "#e0e0ff"
 
 set circlecolors {white blue gray blue blue}
@@ -11521,6 +11583,11 @@ set NS [expr {$use_ttk ? "ttk" : ""}]
 
 set git_version [join [lrange [split [lindex [exec git version] end] .] 0 2] .]
 
+set show_notes {}
+if {[package vcompare $git_version "1.6.6.2"] >= 0} {
+    set show_notes "--show-notes"
+}
+
 set runq {}
 set history {}
 set historyindex 0
diff --git a/gitk-git/po/pt_br.po b/gitk-git/po/pt_br.po
new file mode 100644 (file)
index 0000000..1486e32
--- /dev/null
@@ -0,0 +1,1277 @@
+# Translation of gitk to Brazilian Portuguese.
+# Copyright (C) 2007 Paul Mackerras, et al.
+# This file is distributed under the same license as the gitk package.
+#
+# Alexandre Erwin Ittner <alexandre@ittner.com.br>, 2010.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-01-26 15:47-0800\n"
+"PO-Revision-Date: 2010-12-06 23:39-0200\n"
+"Last-Translator: Alexandre Erwin Ittner <alexandre@ittner.com.br>\n"
+"Language-Team: Brazilian Portuguese <>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: gitk:115
+msgid "Couldn't get list of unmerged files:"
+msgstr "Não foi possível obter a lista dos arquivos não mesclados:"
+
+#: gitk:274
+msgid "Error parsing revisions:"
+msgstr "Erro ao interpretar revisões:"
+
+#: gitk:330
+msgid "Error executing --argscmd command:"
+msgstr "Erro ao executar o comando--argscmd:"
+
+#: gitk:343
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Nenhum arquivo foi selecionado: --merge especificado mas não há arquivos não-"
+"mesclados."
+
+#: gitk:346
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Nenhum arquivo foi selecionado: --merge especificado mas não há arquivos não-"
+"mesclados dentro dos limites."
+
+#: gitk:368 gitk:516
+msgid "Error executing git log:"
+msgstr "Erro ao executar git log:"
+
+#: gitk:386 gitk:532
+msgid "Reading"
+msgstr "Lendo"
+
+#: gitk:446 gitk:4271
+msgid "Reading commits..."
+msgstr "Lendo revisões..."
+
+#: gitk:449 gitk:1580 gitk:4274
+msgid "No commits selected"
+msgstr "Nenhuma revisão foi selecionada"
+
+#: gitk:1456
+msgid "Can't parse git log output:"
+msgstr "Não foi possível interpretar a saída do \"git log\":"
+
+#: gitk:1676
+msgid "No commit information available"
+msgstr "Não há informações disponíveis sobre a revisão"
+
+#: gitk:1818
+msgid "mc"
+msgstr "mc"
+
+#: gitk:1853 gitk:4064 gitk:9067 gitk:10607 gitk:10817
+msgid "OK"
+msgstr "Ok"
+
+#: gitk:1855 gitk:4066 gitk:8657 gitk:8736 gitk:8851 gitk:8900 gitk:9069
+#: gitk:10608 gitk:10818
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: gitk:1980
+msgid "Update"
+msgstr "Atualizar"
+
+#: gitk:1981
+msgid "Reload"
+msgstr "Recarregar"
+
+#: gitk:1982
+msgid "Reread references"
+msgstr "Ler as referências novamente"
+
+#: gitk:1983
+msgid "List references"
+msgstr "Listar referências"
+
+#: gitk:1985
+msgid "Start git gui"
+msgstr "Iniciar Git GUI"
+
+#: gitk:1987
+msgid "Quit"
+msgstr "Sair"
+
+#: gitk:1979
+msgid "File"
+msgstr "Arquivo"
+
+#: gitk:1991
+msgid "Preferences"
+msgstr "Preferências"
+
+#: gitk:1990
+msgid "Edit"
+msgstr "Editar"
+
+#: gitk:1995
+msgid "New view..."
+msgstr "Nova vista..."
+
+#: gitk:1996
+msgid "Edit view..."
+msgstr "Editar vista..."
+
+#: gitk:1997
+msgid "Delete view"
+msgstr "Apagar vista"
+
+#: gitk:1999
+msgid "All files"
+msgstr "Todos os arquivos"
+
+#: gitk:1994 gitk:3817
+msgid "View"
+msgstr "Exibir"
+
+#: gitk:2004 gitk:2014 gitk:2787
+msgid "About gitk"
+msgstr "Sobre o gitk"
+
+#: gitk:2005 gitk:2019
+msgid "Key bindings"
+msgstr "Atalhos de teclado"
+
+#: gitk:2003 gitk:2018
+msgid "Help"
+msgstr "Ajuda"
+
+#: gitk:2096 gitk:8132
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:2127
+msgid "Row"
+msgstr "Linha"
+
+#: gitk:2165
+msgid "Find"
+msgstr "Encontrar"
+
+#: gitk:2166
+msgid "next"
+msgstr "Próximo"
+
+#: gitk:2167
+msgid "prev"
+msgstr "Anterior"
+
+#: gitk:2168
+msgid "commit"
+msgstr "Revisão"
+
+#: gitk:2171 gitk:2173 gitk:4432 gitk:4455 gitk:4479 gitk:6420 gitk:6492
+#: gitk:6576
+msgid "containing:"
+msgstr "contendo:"
+
+#: gitk:2174 gitk:3298 gitk:3303 gitk:4507
+msgid "touching paths:"
+msgstr "envolvendo os caminhos:"
+
+#: gitk:2175 gitk:4512
+msgid "adding/removing string:"
+msgstr "Adicionando/removendo texto:"
+
+#: gitk:2184 gitk:2186
+msgid "Exact"
+msgstr "Exatamente"
+
+#: gitk:2186 gitk:4587 gitk:6388
+msgid "IgnCase"
+msgstr "Ignorar maiúsculas/minúsculas"
+
+#: gitk:2186 gitk:4481 gitk:4585 gitk:6384
+msgid "Regexp"
+msgstr "Expressão regular"
+
+#: gitk:2188 gitk:2189 gitk:4606 gitk:4636 gitk:4643 gitk:6512 gitk:6580
+msgid "All fields"
+msgstr "Todos os campos"
+
+#: gitk:2189 gitk:4604 gitk:4636 gitk:6451
+msgid "Headline"
+msgstr "Assunto"
+
+#: gitk:2190 gitk:4604 gitk:6451 gitk:6580 gitk:7013
+msgid "Comments"
+msgstr "Descrição da revisão"
+
+#: gitk:2190 gitk:4604 gitk:4608 gitk:4643 gitk:6451 gitk:6948 gitk:8307
+#: gitk:8322
+msgid "Author"
+msgstr "Autor"
+
+#: gitk:2190 gitk:4604 gitk:6451 gitk:6950
+msgid "Committer"
+msgstr "Revisor"
+
+#: gitk:2221
+msgid "Search"
+msgstr "Buscar"
+
+#: gitk:2229
+msgid "Diff"
+msgstr "Diferenças"
+
+#: gitk:2231
+msgid "Old version"
+msgstr "Versão antiga"
+
+#: gitk:2233
+msgid "New version"
+msgstr "Versão nova"
+
+#: gitk:2235
+msgid "Lines of context"
+msgstr "Número de linhas de contexto"
+
+#: gitk:2245
+msgid "Ignore space change"
+msgstr "Ignorar mudanças de caixa"
+
+#: gitk:2304
+msgid "Patch"
+msgstr "Diferenças"
+
+#: gitk:2306
+msgid "Tree"
+msgstr "Árvore"
+
+#: gitk:2463 gitk:2480
+msgid "Diff this -> selected"
+msgstr "Comparar esta revisão com a selecionada"
+
+#: gitk:2464 gitk:2481
+msgid "Diff selected -> this"
+msgstr "Comparar a revisão selecionada com esta"
+
+#: gitk:2465 gitk:2482
+msgid "Make patch"
+msgstr "Criar patch"
+
+#: gitk:2466 gitk:8715
+msgid "Create tag"
+msgstr "Criar etiqueta"
+
+#: gitk:2467 gitk:8831
+msgid "Write commit to file"
+msgstr "Salvar revisão para um arquivo"
+
+#: gitk:2468 gitk:8888
+msgid "Create new branch"
+msgstr "Criar novo ramo"
+
+#: gitk:2469
+msgid "Cherry-pick this commit"
+msgstr "Fazer cherry-pick desta revisão"
+
+#: gitk:2470
+msgid "Reset HEAD branch to here"
+msgstr "Redefinir HEAD para cá"
+
+#: gitk:2471
+msgid "Mark this commit"
+msgstr "Marcar esta revisão"
+
+#: gitk:2472
+msgid "Return to mark"
+msgstr "Voltar à marca"
+
+#: gitk:2473
+msgid "Find descendant of this and mark"
+msgstr "Encontrar descendente e marcar"
+
+#: gitk:2474
+msgid "Compare with marked commit"
+msgstr "Comparar com a revisão marcada"
+
+#: gitk:2488
+msgid "Check out this branch"
+msgstr "Efetuar checkout deste ramo"
+
+#: gitk:2489
+msgid "Remove this branch"
+msgstr "Excluir este ramo"
+
+#: gitk:2496
+msgid "Highlight this too"
+msgstr "Marcar este também"
+
+#: gitk:2497
+msgid "Highlight this only"
+msgstr "Marcar apenas este"
+
+#: gitk:2498
+msgid "External diff"
+msgstr "Diff externo"
+
+#: gitk:2499
+msgid "Blame parent commit"
+msgstr "Anotar revisão anterior"
+
+#: gitk:2506
+msgid "Show origin of this line"
+msgstr "Exibir origem desta linha"
+
+#: gitk:2507
+msgid "Run git gui blame on this line"
+msgstr "Executar 'git blame' nesta linha"
+
+#: gitk:2789
+msgid "\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright ©9 2005-2010 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr "\n"
+"Gitk - um visualizador de revisões para o git \n"
+"\n"
+"Copyright ©9 2005-2010 Paul Mackerras\n"
+"\n"
+"Uso e distribuição segundo os termos da Licença Pública Geral GNU"
+
+#: gitk:2797 gitk:2862 gitk:9253
+msgid "Close"
+msgstr "Fechar"
+
+#: gitk:2818
+msgid "Gitk key bindings"
+msgstr "Atalhos de teclado"
+
+#: gitk:2821
+msgid "Gitk key bindings:"
+msgstr "Atalhos de teclado:"
+
+#: gitk:2823
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tSair"
+
+#: gitk:2824
+#, tcl-format
+msgid "<%s-W>\t\tClose window"
+msgstr "<%s-W>\t\tFechar janela"
+
+#: gitk:2825
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tIr para a primeira revisão"
+
+#: gitk:2826
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tIr para a última revisão"
+
+#: gitk:2827
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Up>, p, i\tIr para uma revisão acima"
+
+#: gitk:2828
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Down>, n, k\tIr para uma revisão abaixo"
+
+#: gitk:2829
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Left>, z, j\tVoltar no histórico"
+
+#: gitk:2830
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\tAvançar no histórico"
+
+#: gitk:2831
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tSubir uma página na lista de revisões"
+
+#: gitk:2832
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tDescer uma página na lista de revisões"
+
+#: gitk:2833
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tRolar para o início da lista de revisões"
+
+#: gitk:2834
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tRolar para o final da lista de revisões"
+
+#: gitk:2835
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tRolar uma linha acima na lista de revisões"
+
+#: gitk:2836
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tRolar uma linha abaixo na lista de revisões"
+
+#: gitk:2837
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tRolar uma página acima na lista de revisões"
+
+#: gitk:2838
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tRolar uma página abaixo na lista de revisões"
+
+#: gitk:2839
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\tProcurar próxima (revisões mas recentes)"
+
+#: gitk:2840
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\tProcurar anterior (revisões mais antigas)"
+
+#: gitk:2841
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tRola alterações uma página acima"
+
+#: gitk:2842
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tRolar alterações uma página abaixo"
+
+#: gitk:2843
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\tRolar alterações uma página abaixo"
+
+#: gitk:2844
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tRolar alterações 18 linhas acima"
+
+#: gitk:2845
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tRolar alterações 18 linhas abaixo"
+
+#: gitk:2846
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tProcurar"
+
+#: gitk:2847
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tIr para a próxima ocorrência"
+
+#: gitk:2848
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\tIr para a próxima ocorrência"
+
+#: gitk:2849
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tPor foco na caixa de busca"
+
+#: gitk:2850
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tIr para a ocorrência anterior"
+
+#: gitk:2851
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tRolar alterações para o próximo arquivo"
+
+#: gitk:2852
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tProcurar a próxima ocorrência na lista de alterações"
+
+#: gitk:2853
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tProcurar ocorrência anterior na lista de alterações"
+
+#: gitk:2854
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tAumentar tamanho da fonte"
+
+#: gitk:2855
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tAumentar tamanho da fonte"
+
+#: gitk:2856
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tReduzir tamanho da fonte"
+
+#: gitk:2857
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tReduzir tamanho da fonte"
+
+#: gitk:2858
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tAtualizar"
+
+#: gitk:3313 gitk:3322
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Erro ao criar o diretório temporário %s:"
+
+#: gitk:3335
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Erro ao ler \"%s\" de %s:"
+
+#: gitk:3398
+msgid "command failed:"
+msgstr "O comando falhou:"
+
+#: gitk:3547
+msgid "No such commit"
+msgstr "Revisão não encontrada"
+
+#: gitk:3561
+msgid "git gui blame: command failed:"
+msgstr "Comando 'git gui blame' falhou:"
+
+#: gitk:3592
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Impossível ler merge head: %s"
+
+#: gitk:3600
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Erro ao ler o índice: %s"
+
+#: gitk:3625
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Não foi possível inciar o 'git blame': %s"
+
+#: gitk:3628 gitk:6419
+msgid "Searching"
+msgstr "Procurando"
+
+#: gitk:3660
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Erro ao executar 'git blame': %s"
+
+#: gitk:3688
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "Esta linha vem da revisão %s, que não está nesta vista"
+
+#: gitk:3702
+msgid "External diff viewer failed:"
+msgstr "Erro do visualizador de alterações externo:"
+
+#: gitk:3820
+msgid "Gitk view definition"
+msgstr "Definir vista"
+
+#: gitk:3824
+msgid "Remember this view"
+msgstr "Lembrar esta vista"
+
+#: gitk:3825
+msgid "References (space separated list):"
+msgstr "Referências (separar a lista com um espaço):"
+
+#: gitk:3826
+msgid "Branches & tags:"
+msgstr "Ramos & etiquetas:"
+
+#: gitk:3827
+msgid "All refs"
+msgstr "Todas as referências"
+
+#: gitk:3828
+msgid "All (local) branches"
+msgstr "Todos os ramos locais"
+
+#: gitk:3829
+msgid "All tags"
+msgstr "Todas as etiquetas"
+
+#: gitk:3830
+msgid "All remote-tracking branches"
+msgstr "Todos os ramos de rastreio"
+
+#: gitk:3831
+msgid "Commit Info (regular expressions):"
+msgstr "Informações da revisão (expressões regulares):"
+
+#: gitk:3832
+msgid "Author:"
+msgstr "Autor:"
+
+#: gitk:3833
+msgid "Committer:"
+msgstr "Revisor:"
+
+#: gitk:3834
+msgid "Commit Message:"
+msgstr "Descrição da revisão:"
+
+#: gitk:3835
+msgid "Matches all Commit Info criteria"
+msgstr "Coincidir todos os critérios de informações da revisão"
+
+#: gitk:3836
+msgid "Changes to Files:"
+msgstr "Mudanças para os arquivos:"
+
+#: gitk:3837
+msgid "Fixed String"
+msgstr "Texto fixo"
+
+#: gitk:3838
+msgid "Regular Expression"
+msgstr "Expressão regular"
+
+#: gitk:3839
+msgid "Search string:"
+msgstr "Texto de busca"
+
+#: gitk:3840
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr ""
+"Datas de revisão (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+
+#: gitk:3841
+msgid "Since:"
+msgstr "Desde:"
+
+#: gitk:3842
+msgid "Until:"
+msgstr "Até:"
+
+#: gitk:3843
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Limitar e/ou ignorar um número de revisões (inteiro positivo):"
+
+#: gitk:3844
+msgid "Number to show:"
+msgstr "Número para mostrar:"
+
+#: gitk:3845
+msgid "Number to skip:"
+msgstr "Número para ignorar:"
+
+#: gitk:3846
+msgid "Miscellaneous options:"
+msgstr "Opções diversas:"
+
+#: gitk:3847
+msgid "Strictly sort by date"
+msgstr "Ordenar estritamente pela data"
+
+#: gitk:3848
+msgid "Mark branch sides"
+msgstr "Marcar os dois lados do ramo"
+
+#: gitk:3849
+msgid "Limit to first parent"
+msgstr "Limitar ao primeiro antecessor"
+
+#: gitk:3850
+msgid "Simple history"
+msgstr "Histórico simplificado"
+
+#: gitk:3851
+msgid "Additional arguments to git log:"
+msgstr "Argumentos adicionais para o 'git log':"
+
+#: gitk:3852
+msgid "Enter files and directories to include, one per line:"
+msgstr "Arquivos e diretórios para incluir, um por linha"
+
+#: gitk:3853
+msgid "Command to generate more commits to include:"
+msgstr "Comando para gerar mais revisões para incluir:"
+
+#: gitk:3977
+msgid "Gitk: edit view"
+msgstr "Gitk: editar vista"
+
+#: gitk:3985
+msgid "-- criteria for selecting revisions"
+msgstr "-- critérios para selecionar revisões"
+
+#: gitk:3990
+msgid "View Name"
+msgstr "Nome da vista"
+
+#: gitk:4065
+msgid "Apply (F5)"
+msgstr "Aplicar (F5)"
+
+#: gitk:4103
+msgid "Error in commit selection arguments:"
+msgstr "Erro nos argumentos de seleção de revisões:"
+
+#: gitk:4156 gitk:4208 gitk:4656 gitk:4670 gitk:5931 gitk:11551 gitk:11552
+msgid "None"
+msgstr "Nenhum"
+
+#: gitk:4604 gitk:6451 gitk:8309 gitk:8324
+msgid "Date"
+msgstr "Data"
+
+#: gitk:4604 gitk:6451
+msgid "CDate"
+msgstr "DataR"
+
+#: gitk:4753 gitk:4758
+msgid "Descendant"
+msgstr "Descendente de"
+
+#: gitk:4754
+msgid "Not descendant"
+msgstr "Não descendente de"
+
+#: gitk:4761 gitk:4766
+msgid "Ancestor"
+msgstr "Antecessor de"
+
+#: gitk:4762
+msgid "Not ancestor"
+msgstr "Não antecessor de"
+
+#: gitk:5052
+msgid "Local changes checked in to index but not committed"
+msgstr "Mudanças locais marcadas, porém não salvas"
+
+#: gitk:5088
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Mudanças locais não marcadas"
+
+#: gitk:6769
+msgid "many"
+msgstr "muitas"
+
+#: gitk:6952
+msgid "Tags:"
+msgstr "Etiquetas:"
+
+#: gitk:6969 gitk:6975 gitk:8302
+msgid "Parent"
+msgstr "Antecessor"
+
+#: gitk:6980
+msgid "Child"
+msgstr "Descendente"
+
+#: gitk:6989
+msgid "Branch"
+msgstr "Ramo"
+
+#: gitk:6992
+msgid "Follows"
+msgstr "Segue"
+
+#: gitk:6995
+msgid "Precedes"
+msgstr "Precede"
+
+#: gitk:7532
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Erro ao obter diferenças: %s"
+
+#: gitk:8130
+msgid "Goto:"
+msgstr "Ir para:"
+
+#: gitk:8151
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "O id SHA1 %s é ambíguo"
+
+#: gitk:8158
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "Revisão %s desconhecida"
+
+#: gitk:8168
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "Id SHA1 %s desconhecido"
+
+#: gitk:8170
+#, tcl-format
+msgid "Revision %s is not in the current view"
+msgstr "A revisão %s não está na vista atual"
+
+#: gitk:8312
+msgid "Children"
+msgstr "Descendentes"
+
+#: gitk:8370
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Redefinir ramo %s para este ponto"
+
+#: gitk:8372
+msgid "Detached head: can't reset"
+msgstr "Detached head: impossível redefinir"
+
+#: gitk:8481 gitk:8487
+msgid "Skipping merge commit "
+msgstr "Saltando revisão de mesclagem"
+
+#: gitk:8496 gitk:8501
+msgid "Error getting patch ID for "
+msgstr "Erro ao obter patch ID para"
+
+#: gitk:8497 gitk:8502
+msgid " - stopping\n"
+msgstr "- parando\n"
+
+#: gitk:8507 gitk:8510 gitk:8518 gitk:8532 gitk:8541
+msgid "Commit "
+msgstr "Revisão"
+
+#: gitk:8511
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+"é o mesmo patch que\n"
+"       "
+
+#: gitk:8519
+msgid ""
+" differs from\n"
+"       "
+msgstr "difere de"
+
+#: gitk:8521
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr ""
+"Diferença de revisões:\n"
+"\n"
+
+#: gitk:8533 gitk:8542
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr "possui %s descendentes - parando\n"
+
+#: gitk:8561
+#, tcl-format
+msgid "Error writing commit to file: %s"
+msgstr "Erro ao salvar revisão para o arquivo: %s"
+
+#: gitk:8567
+#, tcl-format
+msgid "Error diffing commits: %s"
+msgstr "Erro ao comparar revisões: %s"
+
+#: gitk:8598
+msgid "Top"
+msgstr "Início"
+
+#: gitk:8599
+msgid "From"
+msgstr "De"
+
+#: gitk:8604
+msgid "To"
+msgstr "Para"
+
+#: gitk:8628
+msgid "Generate patch"
+msgstr "Gerar patch"
+
+#: gitk:8630
+msgid "From:"
+msgstr "De:"
+
+#: gitk:8639
+msgid "To:"
+msgstr "Para:"
+
+#: gitk:8648
+msgid "Reverse"
+msgstr "Inverter"
+
+#: gitk:8650 gitk:8845
+msgid "Output file:"
+msgstr "Arquivo de saída:"
+
+#: gitk:8656
+msgid "Generate"
+msgstr "Gerar"
+
+#: gitk:8694
+msgid "Error creating patch:"
+msgstr "Erro ao criar patch:"
+
+#: gitk:8717 gitk:8833 gitk:8890
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:8726
+msgid "Tag name:"
+msgstr "Nome da etiqueta:"
+
+#: gitk:8729
+msgid "Tag message is optional"
+msgstr "A descrição da etiqueta é opcional"
+
+#: gitk:8731
+msgid "Tag message:"
+msgstr "Descrição da etiqueta"
+
+#: gitk:8735 gitk:8899
+msgid "Create"
+msgstr "Criar"
+
+#: gitk:8753
+msgid "No tag name specified"
+msgstr "Nome da etiqueta não indicado"
+
+#: gitk:8757
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "Etiqueta \"%s\" já existe"
+
+#: gitk:8767
+msgid "Error creating tag:"
+msgstr "Erro ao criar etiqueta:"
+
+#: gitk:8842
+msgid "Command:"
+msgstr "Comando:"
+
+#: gitk:8850
+msgid "Write"
+msgstr "Exportar"
+
+#: gitk:8868
+msgid "Error writing commit:"
+msgstr "Erro ao exportar revisão"
+
+#: gitk:8895
+msgid "Name:"
+msgstr "Nome:"
+
+#: gitk:8918
+msgid "Please specify a name for the new branch"
+msgstr "Indique um nome para o novo ramo"
+
+#: gitk:8923
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "O ramo \"%s\" já existe. Sobrescrever?"
+
+#: gitk:8989
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "Revisão %s já inclusa no ramo %s -- você realmente deseja reaplicá-la?"
+
+#: gitk:8994
+msgid "Cherry-picking"
+msgstr "Cherry-picking"
+
+#: gitk:9003
+#, 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 ""
+"O cherry-pick falhou porque o arquivo \"%s\" possui mudanças locais.\n"
+"Salve a uma revisão, redefina ou armazene (stash) suas mudanças e tente "
+"novamente."
+
+#: gitk:9009
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"O cherry-pick falhou porque houve um conflito na mesclagem.\n"
+"Executar o 'git citool' para resolvê-lo?"
+
+#: gitk:9025
+msgid "No changes committed"
+msgstr "Nenhuma revisão foi salva"
+
+#: gitk:9051
+msgid "Confirm reset"
+msgstr "Confirmar redefinição"
+
+#: gitk:9053
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Você realmente deseja redefinir o ramo %s para %s?"
+
+#: gitk:9055
+msgid "Reset type:"
+msgstr "Tipo de redefinição"
+
+#: gitk:9058
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Soft: deixa a árvore de trabalho e o índice intocados"
+
+#: gitk:9061
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Misto: Deixa a árvore de trabalho intocada, redefine o índice"
+
+#: gitk:9064
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Hard: Redefine a árvore de trabalho e o índice\n"
+"(descarta TODAS as mudanças locais)"
+
+#: gitk:9081
+msgid "Resetting"
+msgstr "Redefinindo"
+
+#: gitk:9141
+msgid "Checking out"
+msgstr "Abrindo"
+
+#: gitk:9194
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Impossível excluir o ramo atualmente aberto"
+
+#: gitk:9200
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"As revisões do ramo \"%s\" não existem em nenhum outro ramo.\n"
+"Você realmente deseja excluir ramo \"%s\"?"
+
+#: gitk:9231
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Referências: %s"
+
+#: gitk:9246
+msgid "Filter"
+msgstr "Filtro"
+
+#: gitk:9541
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Erro ao ler a topologia das revisões; as informações dos ramos e etiquetas "
+"antecessoras/sucessoras estarão incompletas"
+
+#: gitk:10527
+msgid "Tag"
+msgstr "Etiqueta"
+
+#: gitk:10527
+msgid "Id"
+msgstr "Id"
+
+#: gitk:10576
+msgid "Gitk font chooser"
+msgstr "Selecionar fontes do Gitk"
+
+#: gitk:10593
+msgid "B"
+msgstr "B"
+
+#: gitk:10596
+msgid "I"
+msgstr "I"
+
+#: gitk:10714
+msgid "Gitk preferences"
+msgstr "Preferências do Gitk"
+
+#: gitk:10716
+msgid "Commit list display options"
+msgstr "Opções da lista de revisões"
+
+#: gitk:10719
+msgid "Maximum graph width (lines)"
+msgstr "Largura máxima do grafo (linhas)"
+
+#: gitk:10722
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Largura máxima do grafo (% do painel)"
+
+#: gitk:10725
+msgid "Show local changes"
+msgstr "Exibir mudanças locais"
+
+#: gitk:10728
+msgid "Auto-select SHA1"
+msgstr "Selecionar o SHA1 automaticamente"
+
+#: gitk:10731
+msgid "Hide remote refs"
+msgstr "Ocultar referências remotas"
+
+#: gitk:10735
+msgid "Diff display options"
+msgstr "Opções de exibição das alterações"
+
+#: gitk:10737
+msgid "Tab spacing"
+msgstr "Espaços por tabulação"
+
+#: gitk:10740
+msgid "Display nearby tags"
+msgstr "Exibir etiquetas próximas"
+
+#: gitk:10743
+msgid "Limit diffs to listed paths"
+msgstr "Limitar diferenças aos caminhos listados"
+
+#: gitk:10746
+msgid "Support per-file encodings"
+msgstr "Usar codificações distintas por arquivo"
+
+#: gitk:10752 gitk:10832
+msgid "External diff tool"
+msgstr "Ferramenta 'diff' externa"
+
+#: gitk:10753
+msgid "Choose..."
+msgstr "Selecionar..."
+
+#: gitk:10758
+msgid "General options"
+msgstr "Opções gerais"
+
+#: gitk:10761
+msgid "Use themed widgets"
+msgstr "Usar temas para as janelas"
+
+#: gitk:10763
+msgid "(change requires restart)"
+msgstr "(exige reinicialização)"
+
+#: gitk:10765
+msgid "(currently unavailable)"
+msgstr "(atualmente indisponível)"
+
+#: gitk:10769
+msgid "Colors: press to choose"
+msgstr "Cores: clique para escolher"
+
+#: gitk:10772
+msgid "Interface"
+msgstr "Interface"
+
+#: gitk:10773
+msgid "interface"
+msgstr "interface"
+
+#: gitk:10776
+msgid "Background"
+msgstr "Segundo plano"
+
+#: gitk:10777 gitk:10807
+msgid "background"
+msgstr "segundo plano"
+
+#: gitk:10780
+msgid "Foreground"
+msgstr "Primeiro plano"
+
+#: gitk:10781
+msgid "foreground"
+msgstr "primeiro plano"
+
+#: gitk:10784
+msgid "Diff: old lines"
+msgstr "Diff: linhas excluídas"
+
+#: gitk:10785
+msgid "diff old lines"
+msgstr "linhas excluídas"
+
+#: gitk:10789
+msgid "Diff: new lines"
+msgstr "Diff: linhas adicionadas"
+
+#: gitk:10790
+msgid "diff new lines"
+msgstr "linhas adicionadas"
+
+#: gitk:10794
+msgid "Diff: hunk header"
+msgstr "Diff: cabeçalho do bloco"
+
+#: gitk:10796
+msgid "diff hunk header"
+msgstr "cabeçalho do bloco"
+
+#: gitk:10800
+msgid "Marked line bg"
+msgstr "2º plano da linha marcada"
+
+#: gitk:10802
+msgid "marked line background"
+msgstr "segundo plano da linha marcada"
+
+#: gitk:10806
+msgid "Select bg"
+msgstr "2º plano da seleção"
+
+#: gitk:10810
+msgid "Fonts: press to choose"
+msgstr "Fontes: clique para escolher"
+
+#: gitk:10812
+msgid "Main font"
+msgstr "Fonte principal"
+
+#: gitk:10813
+msgid "Diff display font"
+msgstr "Fonte da lista de mudanças"
+
+#: gitk:10814
+msgid "User interface font"
+msgstr "Fonte da interface"
+
+#: gitk:10842
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: selecionar cor para %s"
+
+#: gitk:11445
+msgid "Cannot find a git repository here."
+msgstr "Não há nenhum repositório git aqui."
+
+#: gitk:11449
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "Impossível encontrar o diretório git \"%s\"."
+
+#: gitk:11496
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr ""
+"O argumento \"%s\" é ambíguo (especifica tanto uma revisão e um nome de "
+"arquivo)"
+
+#: gitk:11508
+msgid "Bad arguments to gitk:"
+msgstr "Argumentos incorretos para o gitk:"
+
+#: gitk:11604
+msgid "Command line"
+msgstr "Linha de comando"
index 386763ade786d96b657dd0ab737b633eef44b424..2f07a2e2d93d655cfe5cf2d3535e7b86422233c6 100644 (file)
@@ -1,5 +1,5 @@
 # Swedish translation for gitk
-# Copyright (C) 2005-2009 Paul Mackerras
+# Copyright (C) 2005-2010 Paul Mackerras
 # This file is distributed under the same license as the gitk package.
 #
 # Peter Krefting <peter@softwolves.pp.se>, 2008-2010.
@@ -8,8 +8,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: sv\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-01-28 13:16+0100\n"
-"PO-Revision-Date: 2010-01-28 13:48+0100\n"
+"POT-Creation-Date: 2010-09-12 21:14+0100\n"
+"PO-Revision-Date: 2010-09-12 21:16+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"
@@ -24,17 +24,17 @@ msgstr "Kunde inte hämta lista över ej sammanslagna filer:"
 msgid "Error parsing revisions:"
 msgstr "Fel vid tolkning av revisioner:"
 
-#: gitk:329
+#: gitk:330
 msgid "Error executing --argscmd command:"
 msgstr "Fel vid körning av --argscmd-kommando:"
 
-#: gitk:342
+#: gitk:343
 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:345
+#: gitk:346
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -42,600 +42,605 @@ msgstr ""
 "Inga filer valdes: --merge angavs men det finns inga filer inom "
 "filbegränsningen."
 
-#: gitk:367 gitk:514
+#: gitk:368 gitk:516
 msgid "Error executing git log:"
 msgstr "Fel vid körning av git log:"
 
-#: gitk:385 gitk:530
+#: gitk:386 gitk:532
 msgid "Reading"
 msgstr "Läser"
 
-#: gitk:445 gitk:4261
+#: gitk:446 gitk:4271
 msgid "Reading commits..."
 msgstr "Läser incheckningar..."
 
-#: gitk:448 gitk:1578 gitk:4264
+#: gitk:449 gitk:1580 gitk:4274
 msgid "No commits selected"
 msgstr "Inga incheckningar markerade"
 
-#: gitk:1454
+#: gitk:1456
 msgid "Can't parse git log output:"
 msgstr "Kan inte tolka utdata från git log:"
 
-#: gitk:1674
+#: gitk:1676
 msgid "No commit information available"
 msgstr "Ingen incheckningsinformation är tillgänglig"
 
-#: gitk:1816
+#: gitk:1818
 msgid "mc"
 msgstr "mc"
 
-#: gitk:1851 gitk:4054 gitk:9044 gitk:10585 gitk:10804
+#: gitk:1853 gitk:4064 gitk:9067 gitk:10607 gitk:10817
 msgid "OK"
 msgstr "OK"
 
-#: gitk:1853 gitk:4056 gitk:8634 gitk:8713 gitk:8828 gitk:8877 gitk:9046
-#: gitk:10586 gitk:10805
+#: gitk:1855 gitk:4066 gitk:8657 gitk:8736 gitk:8851 gitk:8900 gitk:9069
+#: gitk:10608 gitk:10818
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: gitk:1975
+#: gitk:1980
 msgid "Update"
 msgstr "Uppdatera"
 
-#: gitk:1976
+#: gitk:1981
 msgid "Reload"
 msgstr "Ladda om"
 
-#: gitk:1977
+#: gitk:1982
 msgid "Reread references"
 msgstr "Läs om referenser"
 
-#: gitk:1978
+#: gitk:1983
 msgid "List references"
 msgstr "Visa referenser"
 
-#: gitk:1980
+#: gitk:1985
 msgid "Start git gui"
 msgstr "Starta git gui"
 
-#: gitk:1982
+#: gitk:1987
 msgid "Quit"
 msgstr "Avsluta"
 
-#: gitk:1974
+#: gitk:1979
 msgid "File"
 msgstr "Arkiv"
 
-#: gitk:1986
+#: gitk:1991
 msgid "Preferences"
 msgstr "Inställningar"
 
-#: gitk:1985
+#: gitk:1990
 msgid "Edit"
 msgstr "Redigera"
 
-#: gitk:1990
+#: gitk:1995
 msgid "New view..."
 msgstr "Ny vy..."
 
-#: gitk:1991
+#: gitk:1996
 msgid "Edit view..."
 msgstr "Ändra vy..."
 
-#: gitk:1992
+#: gitk:1997
 msgid "Delete view"
 msgstr "Ta bort vy"
 
-#: gitk:1994
+#: gitk:1999
 msgid "All files"
 msgstr "Alla filer"
 
-#: gitk:1989 gitk:3808
+#: gitk:1994 gitk:3817
 msgid "View"
 msgstr "Visa"
 
-#: gitk:1999 gitk:2009 gitk:2780
+#: gitk:2004 gitk:2014 gitk:2787
 msgid "About gitk"
 msgstr "Om gitk"
 
-#: gitk:2000 gitk:2014
+#: gitk:2005 gitk:2019
 msgid "Key bindings"
 msgstr "Tangentbordsbindningar"
 
-#: gitk:1998 gitk:2013
+#: gitk:2003 gitk:2018
 msgid "Help"
 msgstr "Hjälp"
 
-#: gitk:2091 gitk:8110
+#: gitk:2096 gitk:8132
 msgid "SHA1 ID:"
 msgstr "SHA1-id:"
 
-#: gitk:2122
+#: gitk:2127
 msgid "Row"
 msgstr "Rad"
 
-#: gitk:2160
+#: gitk:2165
 msgid "Find"
 msgstr "Sök"
 
-#: gitk:2161
+#: gitk:2166
 msgid "next"
 msgstr "nästa"
 
-#: gitk:2162
+#: gitk:2167
 msgid "prev"
 msgstr "föreg"
 
-#: gitk:2163
+#: gitk:2168
 msgid "commit"
 msgstr "incheckning"
 
-#: gitk:2166 gitk:2168 gitk:4422 gitk:4445 gitk:4469 gitk:6410 gitk:6482
-#: gitk:6566
+#: gitk:2171 gitk:2173 gitk:4432 gitk:4455 gitk:4479 gitk:6420 gitk:6492
+#: gitk:6576
 msgid "containing:"
 msgstr "som innehåller:"
 
-#: gitk:2169 gitk:3290 gitk:3295 gitk:4497
+#: gitk:2174 gitk:3298 gitk:3303 gitk:4507
 msgid "touching paths:"
 msgstr "som rör sökväg:"
 
-#: gitk:2170 gitk:4502
+#: gitk:2175 gitk:4512
 msgid "adding/removing string:"
 msgstr "som lägger/till tar bort sträng:"
 
-#: gitk:2179 gitk:2181
+#: gitk:2184 gitk:2186
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:2181 gitk:4577 gitk:6378
+#: gitk:2186 gitk:4587 gitk:6388
 msgid "IgnCase"
 msgstr "IgnVersaler"
 
-#: gitk:2181 gitk:4471 gitk:4575 gitk:6374
+#: gitk:2186 gitk:4481 gitk:4585 gitk:6384
 msgid "Regexp"
 msgstr "Reg.uttr."
 
-#: gitk:2183 gitk:2184 gitk:4596 gitk:4626 gitk:4633 gitk:6502 gitk:6570
+#: gitk:2188 gitk:2189 gitk:4606 gitk:4636 gitk:4643 gitk:6512 gitk:6580
 msgid "All fields"
 msgstr "Alla fält"
 
-#: gitk:2184 gitk:4594 gitk:4626 gitk:6441
+#: gitk:2189 gitk:4604 gitk:4636 gitk:6451
 msgid "Headline"
 msgstr "Rubrik"
 
-#: gitk:2185 gitk:4594 gitk:6441 gitk:6570 gitk:7003
+#: gitk:2190 gitk:4604 gitk:6451 gitk:6580 gitk:7013
 msgid "Comments"
 msgstr "Kommentarer"
 
-#: gitk:2185 gitk:4594 gitk:4598 gitk:4633 gitk:6441 gitk:6938 gitk:8285
-#: gitk:8300
+#: gitk:2190 gitk:4604 gitk:4608 gitk:4643 gitk:6451 gitk:6948 gitk:8307
+#: gitk:8322
 msgid "Author"
 msgstr "Författare"
 
-#: gitk:2185 gitk:4594 gitk:6441 gitk:6940
+#: gitk:2190 gitk:4604 gitk:6451 gitk:6950
 msgid "Committer"
 msgstr "Incheckare"
 
-#: gitk:2216
+#: gitk:2221
 msgid "Search"
 msgstr "Sök"
 
-#: gitk:2224
+#: gitk:2229
 msgid "Diff"
 msgstr "Diff"
 
-#: gitk:2226
+#: gitk:2231
 msgid "Old version"
 msgstr "Gammal version"
 
-#: gitk:2228
+#: gitk:2233
 msgid "New version"
 msgstr "Ny version"
 
-#: gitk:2230
+#: gitk:2235
 msgid "Lines of context"
 msgstr "Rader sammanhang"
 
-#: gitk:2240
+#: gitk:2245
 msgid "Ignore space change"
 msgstr "Ignorera ändringar i blanksteg"
 
-#: gitk:2299
+#: gitk:2304
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:2301
+#: gitk:2306
 msgid "Tree"
 msgstr "Träd"
 
-#: gitk:2456 gitk:2473
+#: gitk:2463 gitk:2480
 msgid "Diff this -> selected"
 msgstr "Diff denna -> markerad"
 
-#: gitk:2457 gitk:2474
+#: gitk:2464 gitk:2481
 msgid "Diff selected -> this"
 msgstr "Diff markerad -> denna"
 
-#: gitk:2458 gitk:2475
+#: gitk:2465 gitk:2482
 msgid "Make patch"
 msgstr "Skapa patch"
 
-#: gitk:2459 gitk:8692
+#: gitk:2466 gitk:8715
 msgid "Create tag"
 msgstr "Skapa tagg"
 
-#: gitk:2460 gitk:8808
+#: gitk:2467 gitk:8831
 msgid "Write commit to file"
 msgstr "Skriv incheckning till fil"
 
-#: gitk:2461 gitk:8865
+#: gitk:2468 gitk:8888
 msgid "Create new branch"
 msgstr "Skapa ny gren"
 
-#: gitk:2462
+#: gitk:2469
 msgid "Cherry-pick this commit"
 msgstr "Plocka denna incheckning"
 
-#: gitk:2463
+#: gitk:2470
 msgid "Reset HEAD branch to here"
 msgstr "Återställ HEAD-grenen hit"
 
-#: gitk:2464
+#: gitk:2471
 msgid "Mark this commit"
 msgstr "Markera denna incheckning"
 
-#: gitk:2465
+#: gitk:2472
 msgid "Return to mark"
 msgstr "Återgå till markering"
 
-#: gitk:2466
+#: gitk:2473
 msgid "Find descendant of this and mark"
 msgstr "Hitta efterföljare till denna och markera"
 
-#: gitk:2467
+#: gitk:2474
 msgid "Compare with marked commit"
 msgstr "Jämför med markerad incheckning"
 
-#: gitk:2481
+#: gitk:2488
 msgid "Check out this branch"
 msgstr "Checka ut denna gren"
 
-#: gitk:2482
+#: gitk:2489
 msgid "Remove this branch"
 msgstr "Ta bort denna gren"
 
-#: gitk:2489
+#: gitk:2496
 msgid "Highlight this too"
 msgstr "Markera även detta"
 
-#: gitk:2490
+#: gitk:2497
 msgid "Highlight this only"
 msgstr "Markera bara detta"
 
-#: gitk:2491
+#: gitk:2498
 msgid "External diff"
 msgstr "Extern diff"
 
-#: gitk:2492
+#: gitk:2499
 msgid "Blame parent commit"
 msgstr "Klandra föräldraincheckning"
 
-#: gitk:2499
+#: gitk:2506
 msgid "Show origin of this line"
 msgstr "Visa ursprunget för den här raden"
 
-#: gitk:2500
+#: gitk:2507
 msgid "Run git gui blame on this line"
 msgstr "Kör git gui blame på den här raden"
 
-#: gitk:2782
+#: gitk:2789
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
+"Copyright ©9 2005-2010 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - en incheckningsvisare för git\n"
 "\n"
-"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
+"Copyright ©9 2005-2010 Paul Mackerras\n"
 "\n"
 "Använd och vidareförmedla enligt villkoren i GNU General Public License"
 
-#: gitk:2790 gitk:2854 gitk:9230
+#: gitk:2797 gitk:2862 gitk:9253
 msgid "Close"
 msgstr "Stäng"
 
-#: gitk:2811
+#: gitk:2818
 msgid "Gitk key bindings"
 msgstr "Tangentbordsbindningar för Gitk"
 
-#: gitk:2814
+#: gitk:2821
 msgid "Gitk key bindings:"
 msgstr "Tangentbordsbindningar för Gitk:"
 
-#: gitk:2816
+#: gitk:2823
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tAvsluta"
 
-#: gitk:2817
+#: gitk:2824
+#, tcl-format
+msgid "<%s-W>\t\tClose window"
+msgstr "<%s-W>\t\tStäng fönster"
+
+#: gitk:2825
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tGå till första incheckning"
 
-#: gitk:2818
+#: gitk:2826
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tGå till sista incheckning"
 
-#: gitk:2819
+#: gitk:2827
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Upp>, p, i\tGå en incheckning upp"
 
-#: gitk:2820
+#: gitk:2828
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Ned>, n, k\tGå en incheckning ned"
 
-#: gitk:2821
+#: gitk:2829
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Vänster>, z, j\tGå bakåt i historiken"
 
-#: gitk:2822
+#: gitk:2830
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Höger>, x, l\tGå framåt i historiken"
 
-#: gitk:2823
+#: gitk:2831
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tGå upp en sida i incheckningslistan"
 
-#: gitk:2824
+#: gitk:2832
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tGå ned en sida i incheckningslistan"
 
-#: gitk:2825
+#: gitk:2833
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tRulla till början av incheckningslistan"
 
-#: gitk:2826
+#: gitk:2834
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tRulla till slutet av incheckningslistan"
 
-#: gitk:2827
+#: gitk:2835
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Upp>\tRulla incheckningslistan upp ett steg"
 
-#: gitk:2828
+#: gitk:2836
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Ned>\tRulla incheckningslistan ned ett steg"
 
-#: gitk:2829
+#: gitk:2837
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-PageUp>\tRulla incheckningslistan upp en sida"
 
-#: gitk:2830
+#: gitk:2838
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-PageDown>\tRulla incheckningslistan ned en sida"
 
-#: gitk:2831
+#: gitk:2839
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Skift-Upp>\tSök bakåt (uppåt, senare incheckningar)"
 
-#: gitk:2832
+#: gitk:2840
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Skift-Ned>\tSök framåt (nedåt, tidigare incheckningar)"
 
-#: gitk:2833
+#: gitk:2841
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tRulla diffvisningen upp en sida"
 
-#: gitk:2834
+#: gitk:2842
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Baksteg>\tRulla diffvisningen upp en sida"
 
-#: gitk:2835
+#: gitk:2843
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Blanksteg>\tRulla diffvisningen ned en sida"
 
-#: gitk:2836
+#: gitk:2844
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tRulla diffvisningen upp 18 rader"
 
-#: gitk:2837
+#: gitk:2845
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tRulla diffvisningen ned 18 rader"
 
-#: gitk:2838
+#: gitk:2846
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSök"
 
-#: gitk:2839
+#: gitk:2847
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tGå till nästa sökträff"
 
-#: gitk:2840
+#: gitk:2848
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\t\tGå till nästa sökträff"
 
-#: gitk:2841
+#: gitk:2849
 msgid "/\t\tFocus the search box"
 msgstr "/\t\tFokusera sökrutan"
 
-#: gitk:2842
+#: gitk:2850
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tGå till föregående sökträff"
 
-#: gitk:2843
+#: gitk:2851
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tRulla diffvisningen till nästa fil"
 
-#: gitk:2844
+#: gitk:2852
 #, 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:2845
+#: gitk:2853
 #, 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:2846
+#: gitk:2854
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-Num+>\tÖka teckenstorlek"
 
-#: gitk:2847
+#: gitk:2855
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tÖka teckenstorlek"
 
-#: gitk:2848
+#: gitk:2856
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-Num->\tMinska teckenstorlek"
 
-#: gitk:2849
+#: gitk:2857
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tMinska teckenstorlek"
 
-#: gitk:2850
+#: gitk:2858
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tUppdatera"
 
-#: gitk:3305 gitk:3314
+#: gitk:3313 gitk:3322
 #, tcl-format
 msgid "Error creating temporary directory %s:"
 msgstr "Fel vid skapande av temporär katalog %s:"
 
-#: gitk:3327
+#: gitk:3335
 #, tcl-format
 msgid "Error getting \"%s\" from %s:"
 msgstr "Fel vid hämtning av  \"%s\" från %s:"
 
-#: gitk:3390
+#: gitk:3398
 msgid "command failed:"
 msgstr "kommando misslyckades:"
 
-#: gitk:3539
+#: gitk:3547
 msgid "No such commit"
 msgstr "Incheckning saknas"
 
-#: gitk:3553
+#: gitk:3561
 msgid "git gui blame: command failed:"
 msgstr "git gui blame: kommando misslyckades:"
 
-#: gitk:3584
+#: gitk:3592
 #, tcl-format
 msgid "Couldn't read merge head: %s"
 msgstr "Kunde inte läsa sammanslagningshuvud: %s"
 
-#: gitk:3592
+#: gitk:3600
 #, tcl-format
 msgid "Error reading index: %s"
 msgstr "Fel vid läsning av index: %s"
 
-#: gitk:3617
+#: gitk:3625
 #, tcl-format
 msgid "Couldn't start git blame: %s"
 msgstr "Kunde inte starta git blame: %s"
 
-#: gitk:3620 gitk:6409
+#: gitk:3628 gitk:6419
 msgid "Searching"
 msgstr "Söker"
 
-#: gitk:3652
+#: gitk:3660
 #, tcl-format
 msgid "Error running git blame: %s"
 msgstr "Fel vid körning av git blame: %s"
 
-#: gitk:3680
+#: gitk:3688
 #, 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:3694
+#: gitk:3702
 msgid "External diff viewer failed:"
 msgstr "Externt diff-verktyg misslyckades:"
 
-#: gitk:3812
+#: gitk:3820
 msgid "Gitk view definition"
 msgstr "Definition av Gitk-vy"
 
-#: gitk:3816
+#: gitk:3824
 msgid "Remember this view"
 msgstr "Spara denna vy"
 
-#: gitk:3817
+#: gitk:3825
 msgid "References (space separated list):"
 msgstr "Referenser (blankstegsavdelad lista):"
 
-#: gitk:3818
+#: gitk:3826
 msgid "Branches & tags:"
 msgstr "Grenar & taggar:"
 
-#: gitk:3819
+#: gitk:3827
 msgid "All refs"
 msgstr "Alla referenser"
 
-#: gitk:3820
+#: gitk:3828
 msgid "All (local) branches"
 msgstr "Alla (lokala) grenar"
 
-#: gitk:3821
+#: gitk:3829
 msgid "All tags"
 msgstr "Alla taggar"
 
-#: gitk:3822
+#: gitk:3830
 msgid "All remote-tracking branches"
 msgstr "Alla fjärrspårande grenar"
 
-#: gitk:3823
+#: gitk:3831
 msgid "Commit Info (regular expressions):"
 msgstr "Incheckningsinfo (reguljära uttryck):"
 
-#: gitk:3824
+#: gitk:3832
 msgid "Author:"
 msgstr "Författare:"
 
-#: gitk:3825
+#: gitk:3833
 msgid "Committer:"
 msgstr "Incheckare:"
 
-#: gitk:3826
+#: gitk:3834
 msgid "Commit Message:"
 msgstr "Incheckningsmeddelande:"
 
-#: gitk:3827
+#: gitk:3835
 msgid "Matches all Commit Info criteria"
 msgstr "Motsvarar alla kriterier för incheckningsinfo"
 
-#: gitk:3828
+#: gitk:3836
 msgid "Changes to Files:"
 msgstr "Ändringar av filer:"
 
-#: gitk:3829
+#: gitk:3837
 msgid "Fixed String"
 msgstr "Fast sträng"
 
-#: gitk:3830
+#: gitk:3838
 msgid "Regular Expression"
 msgstr "Reguljärt uttryck"
 
-#: gitk:3831
+#: gitk:3839
 msgid "Search string:"
 msgstr "Söksträng:"
 
-#: gitk:3832
+#: gitk:3840
 msgid ""
 "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
 "15:27:38\"):"
@@ -643,201 +648,201 @@ msgstr ""
 "Incheckingsdatum (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
 "15:27:38\"):"
 
-#: gitk:3833
+#: gitk:3841
 msgid "Since:"
 msgstr "Från:"
 
-#: gitk:3834
+#: gitk:3842
 msgid "Until:"
 msgstr "Till:"
 
-#: gitk:3835
+#: gitk:3843
 msgid "Limit and/or skip a number of revisions (positive integer):"
 msgstr "Begränsa och/eller hoppa över ett antal revisioner (positivt heltal):"
 
-#: gitk:3836
+#: gitk:3844
 msgid "Number to show:"
 msgstr "Antal att visa:"
 
-#: gitk:3837
+#: gitk:3845
 msgid "Number to skip:"
 msgstr "Antal att hoppa över:"
 
-#: gitk:3838
+#: gitk:3846
 msgid "Miscellaneous options:"
 msgstr "Diverse alternativ:"
 
-#: gitk:3839
+#: gitk:3847
 msgid "Strictly sort by date"
 msgstr "Strikt datumsortering"
 
-#: gitk:3840
+#: gitk:3848
 msgid "Mark branch sides"
 msgstr "Markera sidogrenar"
 
-#: gitk:3841
+#: gitk:3849
 msgid "Limit to first parent"
 msgstr "Begränsa till första förälder"
 
-#: gitk:3842
+#: gitk:3850
 msgid "Simple history"
 msgstr "Enkel historik"
 
-#: gitk:3843
+#: gitk:3851
 msgid "Additional arguments to git log:"
 msgstr "Ytterligare argument till git log:"
 
-#: gitk:3844
+#: gitk:3852
 msgid "Enter files and directories to include, one per line:"
 msgstr "Ange filer och kataloger att ta med, en per rad:"
 
-#: gitk:3845
+#: gitk:3853
 msgid "Command to generate more commits to include:"
 msgstr "Kommando för att generera fler incheckningar att ta med:"
 
-#: gitk:3967
+#: gitk:3977
 msgid "Gitk: edit view"
 msgstr "Gitk: redigera vy"
 
-#: gitk:3975
+#: gitk:3985
 msgid "-- criteria for selecting revisions"
 msgstr " - kriterier för val av revisioner"
 
-#: gitk:3980
+#: gitk:3990
 msgid "View Name"
 msgstr "Namn på vy"
 
-#: gitk:4055
+#: gitk:4065
 msgid "Apply (F5)"
 msgstr "Använd (F5)"
 
-#: gitk:4093
+#: gitk:4103
 msgid "Error in commit selection arguments:"
 msgstr "Fel i argument för val av incheckningar:"
 
-#: gitk:4146 gitk:4198 gitk:4646 gitk:4660 gitk:5921 gitk:11534 gitk:11535
+#: gitk:4156 gitk:4208 gitk:4656 gitk:4670 gitk:5931 gitk:11551 gitk:11552
 msgid "None"
 msgstr "Inget"
 
-#: gitk:4594 gitk:6441 gitk:8287 gitk:8302
+#: gitk:4604 gitk:6451 gitk:8309 gitk:8324
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:4594 gitk:6441
+#: gitk:4604 gitk:6451
 msgid "CDate"
 msgstr "Skapat datum"
 
-#: gitk:4743 gitk:4748
+#: gitk:4753 gitk:4758
 msgid "Descendant"
 msgstr "Avkomling"
 
-#: gitk:4744
+#: gitk:4754
 msgid "Not descendant"
 msgstr "Inte avkomling"
 
-#: gitk:4751 gitk:4756
+#: gitk:4761 gitk:4766
 msgid "Ancestor"
 msgstr "Förfader"
 
-#: gitk:4752
+#: gitk:4762
 msgid "Not ancestor"
 msgstr "Inte förfader"
 
-#: gitk:5042
+#: gitk:5052
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokala ändringar sparade i indexet men inte incheckade"
 
-#: gitk:5078
+#: gitk:5088
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokala ändringar, ej sparade i indexet"
 
-#: gitk:6759
+#: gitk:6769
 msgid "many"
 msgstr "många"
 
-#: gitk:6942
+#: gitk:6952
 msgid "Tags:"
 msgstr "Taggar:"
 
-#: gitk:6959 gitk:6965 gitk:8280
+#: gitk:6969 gitk:6975 gitk:8302
 msgid "Parent"
 msgstr "Förälder"
 
-#: gitk:6970
+#: gitk:6980
 msgid "Child"
 msgstr "Barn"
 
-#: gitk:6979
+#: gitk:6989
 msgid "Branch"
 msgstr "Gren"
 
-#: gitk:6982
+#: gitk:6992
 msgid "Follows"
 msgstr "Följer"
 
-#: gitk:6985
+#: gitk:6995
 msgid "Precedes"
 msgstr "Föregår"
 
-#: gitk:7522
+#: gitk:7532
 #, tcl-format
 msgid "Error getting diffs: %s"
 msgstr "Fel vid hämtning av diff: %s"
 
-#: gitk:8108
+#: gitk:8130
 msgid "Goto:"
 msgstr "Gå till:"
 
-#: gitk:8129
+#: gitk:8151
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Förkortat SHA1-id %s är tvetydigt"
 
-#: gitk:8136
+#: gitk:8158
 #, tcl-format
 msgid "Revision %s is not known"
 msgstr "Revisionen %s är inte känd"
 
-#: gitk:8146
+#: gitk:8168
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA-id:t %s är inte känt"
 
-#: gitk:8148
+#: gitk:8170
 #, tcl-format
 msgid "Revision %s is not in the current view"
 msgstr "Revisionen %s finns inte i den nuvarande vyn"
 
-#: gitk:8290
+#: gitk:8312
 msgid "Children"
 msgstr "Barn"
 
-#: gitk:8348
+#: gitk:8370
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Återställ grenen %s hit"
 
-#: gitk:8350
+#: gitk:8372
 msgid "Detached head: can't reset"
 msgstr "Frånkopplad head: kan inte återställa"
 
-#: gitk:8459 gitk:8465
+#: gitk:8481 gitk:8487
 msgid "Skipping merge commit "
 msgstr "Hoppar över sammanslagningsincheckning "
 
-#: gitk:8474 gitk:8479
+#: gitk:8496 gitk:8501
 msgid "Error getting patch ID for "
 msgstr "Fel vid hämtning av patch-id för "
 
-#: gitk:8475 gitk:8480
+#: gitk:8497 gitk:8502
 msgid " - stopping\n"
 msgstr " - stannar\n"
 
-#: gitk:8485 gitk:8488 gitk:8496 gitk:8510 gitk:8519
+#: gitk:8507 gitk:8510 gitk:8518 gitk:8532 gitk:8541
 msgid "Commit "
 msgstr "Incheckning "
 
-#: gitk:8489
+#: gitk:8511
 msgid ""
 " is the same patch as\n"
 "       "
@@ -845,7 +850,7 @@ msgstr ""
 " är samma patch som\n"
 "       "
 
-#: gitk:8497
+#: gitk:8519
 msgid ""
 " differs from\n"
 "       "
@@ -853,139 +858,139 @@ msgstr ""
 " skiljer sig från\n"
 "       "
 
-#: gitk:8499
+#: gitk:8521
 msgid ""
 "Diff of commits:\n"
 "\n"
-msgstr "Skillnad mellan incheckningar:\n"
+msgstr ""
+"Skillnad mellan incheckningar:\n"
 "\n"
-""
 
-#: gitk:8511 gitk:8520
+#: gitk:8533 gitk:8542
 #, tcl-format
 msgid " has %s children - stopping\n"
 msgstr " har %s barn - stannar\n"
 
-#: gitk:8539
+#: gitk:8561
 #, tcl-format
 msgid "Error writing commit to file: %s"
 msgstr "Fel vid skrivning av incheckning till fil: %s"
 
-#: gitk:8545
+#: gitk:8567
 #, tcl-format
 msgid "Error diffing commits: %s"
 msgstr "Fel vid jämförelse av incheckningar: %s"
 
-#: gitk:8575
+#: gitk:8598
 msgid "Top"
 msgstr "Topp"
 
-#: gitk:8576
+#: gitk:8599
 msgid "From"
 msgstr "Från"
 
-#: gitk:8581
+#: gitk:8604
 msgid "To"
 msgstr "Till"
 
-#: gitk:8605
+#: gitk:8628
 msgid "Generate patch"
 msgstr "Generera patch"
 
-#: gitk:8607
+#: gitk:8630
 msgid "From:"
 msgstr "Från:"
 
-#: gitk:8616
+#: gitk:8639
 msgid "To:"
 msgstr "Till:"
 
-#: gitk:8625
+#: gitk:8648
 msgid "Reverse"
 msgstr "Vänd"
 
-#: gitk:8627 gitk:8822
+#: gitk:8650 gitk:8845
 msgid "Output file:"
 msgstr "Utdatafil:"
 
-#: gitk:8633
+#: gitk:8656
 msgid "Generate"
 msgstr "Generera"
 
-#: gitk:8671
+#: gitk:8694
 msgid "Error creating patch:"
 msgstr "Fel vid generering av patch:"
 
-#: gitk:8694 gitk:8810 gitk:8867
+#: gitk:8717 gitk:8833 gitk:8890
 msgid "ID:"
 msgstr "Id:"
 
-#: gitk:8703
+#: gitk:8726
 msgid "Tag name:"
 msgstr "Taggnamn:"
 
-#: gitk:8706
+#: gitk:8729
 msgid "Tag message is optional"
 msgstr "Taggmeddelandet är valfritt"
 
-#: gitk:8708
+#: gitk:8731
 msgid "Tag message:"
 msgstr "Taggmeddelande:"
 
-#: gitk:8712 gitk:8876
+#: gitk:8735 gitk:8899
 msgid "Create"
 msgstr "Skapa"
 
-#: gitk:8730
+#: gitk:8753
 msgid "No tag name specified"
 msgstr "Inget taggnamn angavs"
 
-#: gitk:8734
+#: gitk:8757
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Taggen \"%s\" finns redan"
 
-#: gitk:8744
+#: gitk:8767
 msgid "Error creating tag:"
 msgstr "Fel vid skapande av tagg:"
 
-#: gitk:8819
+#: gitk:8842
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:8827
+#: gitk:8850
 msgid "Write"
 msgstr "Skriv"
 
-#: gitk:8845
+#: gitk:8868
 msgid "Error writing commit:"
 msgstr "Fel vid skrivning av incheckning:"
 
-#: gitk:8872
+#: gitk:8895
 msgid "Name:"
 msgstr "Namn:"
 
-#: gitk:8895
+#: gitk:8918
 msgid "Please specify a name for the new branch"
 msgstr "Ange ett namn för den nya grenen"
 
-#: gitk:8900
+#: gitk:8923
 #, tcl-format
 msgid "Branch '%s' already exists. Overwrite?"
 msgstr "Grenen \"%s\" finns redan. Skriva över?"
 
-#: gitk:8966
+#: gitk:8989
 #, 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:8971
+#: gitk:8994
 msgid "Cherry-picking"
 msgstr "Plockar"
 
-#: gitk:8980
+#: gitk:9003
 #, tcl-format
 msgid ""
 "Cherry-pick failed because of local changes to file '%s'.\n"
@@ -995,7 +1000,7 @@ msgstr ""
 "Checka in, återställ eller spara undan (stash) dina ändringar och försök "
 "igen."
 
-#: gitk:8986
+#: gitk:9009
 msgid ""
 "Cherry-pick failed because of merge conflict.\n"
 "Do you wish to run git citool to resolve it?"
@@ -1003,32 +1008,32 @@ msgstr ""
 "Cherry-pick misslyckades på grund av en sammanslagningskonflikt.\n"
 "Vill du köra git citool för att lösa den?"
 
-#: gitk:9002
+#: gitk:9025
 msgid "No changes committed"
 msgstr "Inga ändringar incheckade"
 
-#: gitk:9028
+#: gitk:9051
 msgid "Confirm reset"
 msgstr "Bekräfta återställning"
 
-#: gitk:9030
+#: gitk:9053
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Återställa grenen %s till %s?"
 
-#: gitk:9032
+#: gitk:9055
 msgid "Reset type:"
 msgstr "Typ av återställning:"
 
-#: gitk:9035
+#: gitk:9058
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Mjuk: Rör inte utcheckning och index"
 
-#: gitk:9038
+#: gitk:9061
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Blandad: Rör inte utcheckning, återställ index"
 
-#: gitk:9041
+#: gitk:9064
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -1036,19 +1041,19 @@ msgstr ""
 "Hård: Återställ utcheckning och index\n"
 "(förkastar ALLA lokala ändringar)"
 
-#: gitk:9058
+#: gitk:9081
 msgid "Resetting"
 msgstr "Återställer"
 
-#: gitk:9118
+#: gitk:9141
 msgid "Checking out"
 msgstr "Checkar ut"
 
-#: gitk:9171
+#: gitk:9194
 msgid "Cannot delete the currently checked-out branch"
 msgstr "Kan inte ta bort den just nu utcheckade grenen"
 
-#: gitk:9177
+#: gitk:9200
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -1057,16 +1062,16 @@ msgstr ""
 "Incheckningarna på grenen %s existerar inte på någon annan gren.\n"
 "Vill du verkligen ta bort grenen %s?"
 
-#: gitk:9208
+#: gitk:9231
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Taggar och huvuden: %s"
 
-#: gitk:9223
+#: gitk:9246
 msgid "Filter"
 msgstr "Filter"
 
-#: gitk:9518
+#: gitk:9541
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -1074,203 +1079,203 @@ msgstr ""
 "Fel vid läsning av information om incheckningstopologi; information om "
 "grenar och föregående/senare taggar kommer inte vara komplett."
 
-#: gitk:10504
+#: gitk:10527
 msgid "Tag"
 msgstr "Tagg"
 
-#: gitk:10504
+#: gitk:10527
 msgid "Id"
 msgstr "Id"
 
-#: gitk:10554
+#: gitk:10576
 msgid "Gitk font chooser"
 msgstr "Teckensnittsväljare för Gitk"
 
-#: gitk:10571
+#: gitk:10593
 msgid "B"
 msgstr "F"
 
-#: gitk:10574
+#: gitk:10596
 msgid "I"
 msgstr "K"
 
-#: gitk:10692
+#: gitk:10714
 msgid "Gitk preferences"
 msgstr "Inställningar för Gitk"
 
-#: gitk:10694
+#: gitk:10716
 msgid "Commit list display options"
 msgstr "Alternativ för incheckningslistvy"
 
-#: gitk:10697
+#: gitk:10719
 msgid "Maximum graph width (lines)"
 msgstr "Maximal grafbredd (rader)"
 
-#: gitk:10700
+#: gitk:10722
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximal grafbredd (% av ruta)"
 
-#: gitk:10703
+#: gitk:10725
 msgid "Show local changes"
 msgstr "Visa lokala ändringar"
 
-#: gitk:10706
+#: gitk:10728
 msgid "Auto-select SHA1"
 msgstr "Välj SHA1 automatiskt"
 
-#: gitk:10709
+#: gitk:10731
 msgid "Hide remote refs"
 msgstr "Dölj fjärr-referenser"
 
-#: gitk:10713
+#: gitk:10735
 msgid "Diff display options"
 msgstr "Alternativ för diffvy"
 
-#: gitk:10715
+#: gitk:10737
 msgid "Tab spacing"
 msgstr "Blanksteg för tabulatortecken"
 
-#: gitk:10718
+#: gitk:10740
 msgid "Display nearby tags"
 msgstr "Visa närliggande taggar"
 
-#: gitk:10721
+#: gitk:10743
 msgid "Limit diffs to listed paths"
 msgstr "Begränsa diff till listade sökvägar"
 
-#: gitk:10724
+#: gitk:10746
 msgid "Support per-file encodings"
 msgstr "Stöd för filspecifika teckenkodningar"
 
-#: gitk:10730 gitk:10819
+#: gitk:10752 gitk:10832
 msgid "External diff tool"
 msgstr "Externt diff-verktyg"
 
-#: gitk:10731
+#: gitk:10753
 msgid "Choose..."
 msgstr "Välj..."
 
-#: gitk:10736
+#: gitk:10758
 msgid "General options"
 msgstr "Allmänna inställningar"
 
-#: gitk:10739
+#: gitk:10761
 msgid "Use themed widgets"
 msgstr "Använd tema på fönsterelement"
 
-#: gitk:10741
+#: gitk:10763
 msgid "(change requires restart)"
 msgstr "(ändringen kräver omstart)"
 
-#: gitk:10743
+#: gitk:10765
 msgid "(currently unavailable)"
 msgstr "(för närvarande inte tillgängligt)"
 
-#: gitk:10747
+#: gitk:10769
 msgid "Colors: press to choose"
 msgstr "Färger: tryck för att välja"
 
-#: gitk:10750
+#: gitk:10772
 msgid "Interface"
 msgstr "Gränssnitt"
 
-#: gitk:10751
+#: gitk:10773
 msgid "interface"
 msgstr "gränssnitt"
 
-#: gitk:10754
+#: gitk:10776
 msgid "Background"
 msgstr "Bakgrund"
 
-#: gitk:10755 gitk:10785
+#: gitk:10777 gitk:10807
 msgid "background"
 msgstr "bakgrund"
 
-#: gitk:10758
+#: gitk:10780
 msgid "Foreground"
 msgstr "Förgrund"
 
-#: gitk:10759
+#: gitk:10781
 msgid "foreground"
 msgstr "förgrund"
 
-#: gitk:10762
+#: gitk:10784
 msgid "Diff: old lines"
 msgstr "Diff: gamla rader"
 
-#: gitk:10763
+#: gitk:10785
 msgid "diff old lines"
 msgstr "diff gamla rader"
 
-#: gitk:10767
+#: gitk:10789
 msgid "Diff: new lines"
 msgstr "Diff: nya rader"
 
-#: gitk:10768
+#: gitk:10790
 msgid "diff new lines"
 msgstr "diff nya rader"
 
-#: gitk:10772
+#: gitk:10794
 msgid "Diff: hunk header"
 msgstr "Diff: delhuvud"
 
-#: gitk:10774
+#: gitk:10796
 msgid "diff hunk header"
 msgstr "diff delhuvud"
 
-#: gitk:10778
+#: gitk:10800
 msgid "Marked line bg"
 msgstr "Markerad rad bakgrund"
 
-#: gitk:10780
+#: gitk:10802
 msgid "marked line background"
 msgstr "markerad rad bakgrund"
 
-#: gitk:10784
+#: gitk:10806
 msgid "Select bg"
 msgstr "Markerad bakgrund"
 
-#: gitk:10788
+#: gitk:10810
 msgid "Fonts: press to choose"
 msgstr "Teckensnitt: tryck för att välja"
 
-#: gitk:10790
+#: gitk:10812
 msgid "Main font"
 msgstr "Huvudteckensnitt"
 
-#: gitk:10791
+#: gitk:10813
 msgid "Diff display font"
 msgstr "Teckensnitt för diffvisning"
 
-#: gitk:10792
+#: gitk:10814
 msgid "User interface font"
 msgstr "Teckensnitt för användargränssnitt"
 
-#: gitk:10829
+#: gitk:10842
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: välj färg för %s"
 
-#: gitk:11433
+#: gitk:11445
 msgid "Cannot find a git repository here."
-msgstr "Hittar inget gitk-arkiv här."
+msgstr "Hittar inget git-arkiv här."
 
-#: gitk:11437
+#: gitk:11449
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Hittar inte git-katalogen \"%s\"."
 
-#: gitk:11484
+#: gitk:11496
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Tvetydigt argument \"%s\": både revision och filnamn"
 
-#: gitk:11496
+#: gitk:11508
 msgid "Bad arguments to gitk:"
 msgstr "Felaktiga argument till gitk:"
 
-#: gitk:11587
+#: gitk:11604
 msgid "Command line"
 msgstr "Kommandorad"
 
index 2fb7c2d77bbd5f2041341822859dce51ae504d83..0a6ac00631ed8eac5c20248a345074e4b8ff3707 100644 (file)
@@ -35,10 +35,12 @@ GITWEB_FAVICON = static/git-favicon.png
 GITWEB_JS = static/gitweb.js
 GITWEB_SITE_HEADER =
 GITWEB_SITE_FOOTER =
+HIGHLIGHT_BIN = highlight
 
 # include user config
 -include ../config.mak.autogen
 -include ../config.mak
+-include config.mak
 
 # determine version
 ../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
@@ -129,7 +131,8 @@ GITWEB_REPLACE = \
        -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
        -e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \
        -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
-       -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g'
+       -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
+       -e 's|++HIGHLIGHT_BIN++|$(HIGHLIGHT_BIN)|g'
 
 GITWEB-BUILD-OPTIONS: FORCE
        @rm -f $@+
@@ -143,6 +146,15 @@ gitweb.cgi: gitweb.perl GITWEB-BUILD-OPTIONS
        chmod +x $@+ && \
        mv $@+ $@
 
+### Testing rules
+
+test:
+       $(MAKE) -C ../t gitweb-test
+
+test-installed:
+       GITWEB_TEST_INSTALLED='$(DESTDIR_SQ)$(gitwebdir_SQ)' \
+               $(MAKE) -C ../t gitweb-test
+
 ### Installation rules
 
 install: all
@@ -156,5 +168,5 @@ install: all
 clean:
        $(RM) gitweb.cgi static/gitweb.min.js static/gitweb.min.css GITWEB-BUILD-OPTIONS
 
-.PHONY: all clean install .FORCE-GIT-VERSION-FILE FORCE
+.PHONY: all clean install test test-installed .FORCE-GIT-VERSION-FILE FORCE
 
index 0e19be8d216aa813221c8663d6424f96f80bfb14..4a673933acee475dc423007063d1300b44176a61 100644 (file)
@@ -95,7 +95,7 @@ You can specify the following configuration variables when building GIT:
    in the browser's URL bar and next to site name in bookmarks).  Relative
    to base URI of gitweb.  [Default: static/git-favicon.png]
  * GITWEB_JS
-   Points to the localtion where you put gitweb.js on your web server
+   Points to the location where you put gitweb.js on your web server
    (or to be more generic URI of JavaScript code used by gitweb).
    Relative to base URI of gitweb.  [Default: static/gitweb.js (or
    static/gitweb.min.js if JSMIN build variable is defined / JavaScript
@@ -114,6 +114,11 @@ You can specify the following configuration variables when building GIT:
    when gitweb.cgi is executed, then the file specified in the environment
    variable will be loaded instead of the file specified when gitweb.cgi was
    created.  [Default: /etc/gitweb.conf]
+ * HIGHLIGHT_BIN
+   Path to the highlight executable to use (must be the one from
+   http://www.andre-simon.de due to assumptions about parameters and output).
+   Useful if highlight is not installed on your webserver's PATH.
+   [Default: highlight]
 
 
 Runtime gitweb configuration
@@ -172,13 +177,15 @@ not include variables usually directly set during build):
  * $my_url, $my_uri
    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.
+   variables, now there should be no need to do it.  See
+   $per_request_config if you need to set them still.
  * $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 "/".
+   See $per_request_config if you need to set it anyway.
  * $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).
@@ -233,10 +240,24 @@ not include variables usually directly set during build):
    is false.
  * $maxload
    Used to set the maximum load that we will still respond to gitweb queries.
-   If server load exceed this value then return "503 Service Unavaliable" error.
+   If server load exceed this value then return "503 Service Unavailable" error.
    Server load is taken to be 0 if gitweb cannot determine its value.  Set it to
    undefined value to turn it off.  The default is 300.
-
+ * $highlight_bin
+   Path to the highlight executable to use (must be the one from
+   http://www.andre-simon.de due to assumptions about parameters and output).
+   Useful if highlight is not installed on your webserver's PATH.
+   [Default: highlight]
+ * $per_request_config
+   If set to code reference, it would be run once per each request.  You can
+   set parts of configuration that change per session, e.g. by setting it to
+     sub { $ENV{GL_USER} = $cgi->remote_user || "gitweb"; }
+   Otherwise it is treated as boolean value: if true gitweb would process
+   config file once per request, if false it would process config file only
+   once.  Note: $my_url, $my_uri, and $base_url are overwritten with
+   their default values before every request, so if you want to change
+   them, be sure to set this variable to true or a code reference effecting
+   the desired changes.  The default is true.
 
 Projects list file format
 ~~~~~~~~~~~~~~~~~~~~~~~~~
index 898b121b50f5a84cf2186fe547ea65a79728e21d..0779f12d6178c23a47f4b9a08152176d0e19a2a8 100755 (executable)
@@ -7,6 +7,7 @@
 #
 # This program is licensed under the GPLv2
 
+use 5.008;
 use strict;
 use warnings;
 use CGI qw(:standard :escapeHTML -nosticky);
@@ -16,12 +17,10 @@ use Encode;
 use Fcntl ':mode';
 use File::Find qw();
 use File::Basename qw(basename);
+use Time::HiRes qw(gettimeofday tv_interval);
 binmode STDOUT, ':utf8';
 
-our $t0;
-if (eval { require Time::HiRes; 1; }) {
-       $t0 = [Time::HiRes::gettimeofday()];
-}
+our $t0 = [ gettimeofday() ];
 our $number_of_git_cmds = 0;
 
 BEGIN {
@@ -165,6 +164,12 @@ our @diff_opts = ('-M'); # taken from git_commit
 # the gitweb domain.
 our $prevent_xss = 0;
 
+# Path to the highlight executable to use (must be the one from
+# http://www.andre-simon.de due to assumptions about parameters and output).
+# Useful if highlight is not installed on your webserver's PATH.
+# [Default: highlight]
+our $highlight_bin = "++HIGHLIGHT_BIN++";
+
 # information about snapshot formats that gitweb is capable of serving
 our %known_snapshot_formats = (
        # name => {
@@ -245,13 +250,14 @@ our %highlight_ext = (
        # main extensions, defining name of syntax;
        # see files in /usr/share/highlight/langDefs/ directory
        map { $_ => $_ }
-               qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
+               qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl sql make),
        # alternate extensions, see /etc/highlight/filetypes.conf
        'h' => 'c',
+       map { $_ => 'sh'  } qw(bash zsh ksh),
        map { $_ => 'cpp' } qw(cxx c++ cc),
-       map { $_ => 'php' } qw(php3 php4),
+       map { $_ => 'php' } qw(php3 php4 php5 phps),
        map { $_ => 'pl'  } qw(perl pm), # perhaps also 'cgi'
-       'mak' => 'make',
+       map { $_ => 'make'} qw(mak mk),
        map { $_ => 'xml' } qw(xhtml html htm),
 );
 
@@ -268,7 +274,7 @@ our %feature = (
        # return value of feature-sub indicates if to enable specified feature
        #
        # if there is no 'sub' key (no feature-sub), then feature cannot be
-       # overriden
+       # overridden
        #
        # use gitweb_get_feature(<feature>) to retrieve the <feature> value
        # (an array) or gitweb_check_feature(<feature>) to check if <feature>
@@ -486,6 +492,18 @@ our %feature = (
                'sub' => sub { feature_bool('highlight', @_) },
                'override' => 0,
                'default' => [0]},
+
+       # Enable displaying of remote heads in the heads list
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'remote_heads'}{'default'} = [1];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'remote_heads'}{'override'} = 1;
+       # and in project config gitweb.remote_heads = 0|1;
+       'remote_heads' => {
+               'sub' => sub { feature_bool('remote_heads', @_) },
+               'override' => 0,
+               'default' => [0]},
 );
 
 sub gitweb_get_feature {
@@ -594,6 +612,14 @@ sub filter_snapshot_fmts {
                !$known_snapshot_formats{$_}{'disabled'}} @fmts;
 }
 
+# If it is set to code reference, it is code that it is to be run once per
+# request, allowing updating configurations that change with each request,
+# while running other code in config file only once.
+#
+# Otherwise, if it is false then gitweb would process config file only once;
+# if it is true then gitweb config would be run for each request.
+our $per_request_config = 1;
+
 our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM);
 sub evaluate_gitweb_config {
        our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
@@ -700,6 +726,7 @@ our %actions = (
        "log" => \&git_log,
        "patch" => \&git_patch,
        "patches" => \&git_patches,
+       "remotes" => \&git_remotes,
        "rss" => \&git_rss,
        "atom" => \&git_atom,
        "search" => \&git_search,
@@ -774,10 +801,10 @@ sub evaluate_path_info {
                'history',
        );
 
-       # we want to catch
+       # we want to catch, among others
        # [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name]
        my ($parentrefname, $parentpathname, $refname, $pathname) =
-               ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/);
+               ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?([^:]+?)?(?::(.+))?$/);
 
        # first, analyze the 'current' part
        if (defined $pathname) {
@@ -813,8 +840,15 @@ sub evaluate_path_info {
                # hash_base instead. It should also be noted that hand-crafted
                # links having 'history' as an action and no pathname or hash
                # set will fail, but that happens regardless of PATH_INFO.
-               $input_params{'action'} ||= "shortlog";
-               if (grep { $_ eq $input_params{'action'} } @wants_base) {
+               if (defined $parentrefname) {
+                       # if there is parent let the default be 'shortlog' action
+                       # (for http://git.example.com/repo.git/A..B links); if there
+                       # is no parent, dispatch will detect type of object and set
+                       # action appropriately if required (if action is not set)
+                       $input_params{'action'} ||= "shortlog";
+               }
+               if ($input_params{'action'} &&
+                   grep { $_ eq $input_params{'action'} } @wants_base) {
                        $input_params{'hash_base'} ||= $refname;
                } else {
                        $input_params{'hash'} ||= $refname;
@@ -1051,16 +1085,27 @@ sub dispatch {
 }
 
 sub reset_timer {
-       our $t0 = [Time::HiRes::gettimeofday()]
+       our $t0 = [ gettimeofday() ]
                if defined $t0;
        our $number_of_git_cmds = 0;
 }
 
+our $first_request = 1;
 sub run_request {
        reset_timer();
 
        evaluate_uri();
-       evaluate_gitweb_config();
+       if ($first_request) {
+               evaluate_gitweb_config();
+               evaluate_git_version();
+       }
+       if ($per_request_config) {
+               if (ref($per_request_config) eq 'CODE') {
+                       $per_request_config->();
+               } elsif (!$first_request) {
+                       evaluate_gitweb_config();
+               }
+       }
        check_loadavg();
 
        # $projectroot and $projects_list might be set in gitweb config file
@@ -1113,8 +1158,8 @@ sub evaluate_argv {
 
 sub run {
        evaluate_argv();
-       evaluate_git_version();
 
+       $first_request = 1;
        $pre_listen_hook->()
                if $pre_listen_hook;
 
@@ -1127,6 +1172,7 @@ sub run {
 
                $post_dispatch_hook->()
                        if $post_dispatch_hook;
+               $first_request = 0;
 
                last REQUEST if ($is_last_request->());
        }
@@ -1185,7 +1231,7 @@ sub href {
                $href =~ s,/$,,;
 
                # Then add the project name, if present
-               $href .= "/".esc_url($params{'project'});
+               $href .= "/".esc_path_info($params{'project'});
                delete $params{'project'};
 
                # since we destructively absorb parameters, we keep this
@@ -1195,7 +1241,8 @@ sub href {
                # Summary just uses the project path URL, any other action is
                # added to the URL
                if (defined $params{'action'}) {
-                       $href .= "/".esc_url($params{'action'}) unless $params{'action'} eq 'summary';
+                       $href .= "/".esc_path_info($params{'action'})
+                               unless $params{'action'} eq 'summary';
                        delete $params{'action'};
                }
 
@@ -1205,13 +1252,13 @@ sub href {
                        || $params{'hash_parent'} || $params{'hash'});
                if (defined $params{'hash_base'}) {
                        if (defined $params{'hash_parent_base'}) {
-                               $href .= esc_url($params{'hash_parent_base'});
+                               $href .= esc_path_info($params{'hash_parent_base'});
                                # skip the file_parent if it's the same as the file_name
                                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'});
+                                               $href .= ":/".esc_path_info($params{'file_parent'});
                                                delete $params{'file_parent'};
                                        }
                                }
@@ -1219,19 +1266,19 @@ sub href {
                                delete $params{'hash_parent'};
                                delete $params{'hash_parent_base'};
                        } elsif (defined $params{'hash_parent'}) {
-                               $href .= esc_url($params{'hash_parent'}). "..";
+                               $href .= esc_path_info($params{'hash_parent'}). "..";
                                delete $params{'hash_parent'};
                        }
 
-                       $href .= esc_url($params{'hash_base'});
+                       $href .= esc_path_info($params{'hash_base'});
                        if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) {
-                               $href .= ":/".esc_url($params{'file_name'});
+                               $href .= ":/".esc_path_info($params{'file_name'});
                                delete $params{'file_name'};
                        }
                        delete $params{'hash'};
                        delete $params{'hash_base'};
                } elsif (defined $params{'hash'}) {
-                       $href .= esc_url($params{'hash'});
+                       $href .= esc_path_info($params{'hash'});
                        delete $params{'hash'};
                }
 
@@ -1264,6 +1311,9 @@ sub href {
        }
        $href .= "?" . join(';', @result) if scalar @result;
 
+       # final transformation: trailing spaces must be escaped (URI-encoded)
+       $href =~ s/(\s+)$/CGI::escape($1)/e;
+
        return $href;
 }
 
@@ -1346,7 +1396,18 @@ sub esc_param {
        return $str;
 }
 
-# quote unsafe chars in whole URL, so some charactrs cannot be quoted
+# the quoting rules for path_info fragment are slightly different
+sub esc_path_info {
+       my $str = shift;
+       return undef unless defined $str;
+
+       # path_info doesn't treat '+' as space (specially), but '?' must be escaped
+       $str =~ s/([^A-Za-z0-9\-_.~();\/;:@&= +]+)/CGI::escape($1)/eg;
+
+       return $str;
+}
+
+# quote unsafe chars in whole URL, so some characters cannot be quoted
 sub esc_url {
        my $str = shift;
        return undef unless defined $str;
@@ -1355,6 +1416,13 @@ sub esc_url {
        return $str;
 }
 
+# quote unsafe characters in HTML attributes
+sub esc_attr {
+
+       # for XHTML conformance escaping '"' to '&quot;' is not enough
+       return esc_html(@_);
+}
+
 # replace invalid utf8 character with SUBSTITUTION sequence
 sub esc_html {
        my $str = shift;
@@ -1760,7 +1828,7 @@ sub format_ref_marker {
                                        hash=>$dest
                                )}, $name);
 
-                       $markers .= " <span class=\"$class\" title=\"$ref\">" .
+                       $markers .= " <span class=\"".esc_attr($class)."\" title=\"".esc_attr($ref)."\">" .
                                $link . "</span>";
                }
        }
@@ -1844,7 +1912,7 @@ sub git_get_avatar {
                return $pre_white .
                       "<img width=\"$size\" " .
                            "class=\"avatar\" " .
-                           "src=\"$url\" " .
+                           "src=\"".esc_url($url)."\" " .
                            "alt=\"\" " .
                       "/>" . $post_white;
        } else {
@@ -2555,7 +2623,7 @@ sub git_show_project_tagcloud {
        } else {
                my @tags = sort { $cloud->{$a}->{count} <=> $cloud->{$b}->{count} } keys %$cloud;
                return '<p align="center">' . join (', ', map {
-                       "<a href=\"$home_link?by_tag=$_\">$cloud->{$_}->{topname}</a>"
+                       $cgi->a({-href=>"$home_link?by_tag=$_"}, $cloud->{$_}->{topname})
                } splice(@tags, 0, $count)) . '</p>';
        }
 }
@@ -2745,6 +2813,44 @@ sub git_get_last_activity {
        return (undef, undef);
 }
 
+# Implementation note: when a single remote is wanted, we cannot use 'git
+# remote show -n' because that command always work (assuming it's a remote URL
+# if it's not defined), and we cannot use 'git remote show' because that would
+# try to make a network roundtrip. So the only way to find if that particular
+# remote is defined is to walk the list provided by 'git remote -v' and stop if
+# and when we find what we want.
+sub git_get_remotes_list {
+       my $wanted = shift;
+       my %remotes = ();
+
+       open my $fd, '-|' , git_cmd(), 'remote', '-v';
+       return unless $fd;
+       while (my $remote = <$fd>) {
+               chomp $remote;
+               $remote =~ s!\t(.*?)\s+\((\w+)\)$!!;
+               next if $wanted and not $remote eq $wanted;
+               my ($url, $key) = ($1, $2);
+
+               $remotes{$remote} ||= { 'heads' => () };
+               $remotes{$remote}{$key} = $url;
+       }
+       close $fd or return;
+       return wantarray ? %remotes : \%remotes;
+}
+
+# Takes a hash of remotes as first parameter and fills it by adding the
+# available remote heads for each of the indicated remotes.
+sub fill_remote_heads {
+       my $remotes = shift;
+       my @heads = map { "remotes/$_" } keys %$remotes;
+       my @remoteheads = git_get_heads_list(undef, @heads);
+       foreach my $remote (keys %$remotes) {
+               $remotes->{$remote}{'heads'} = [ grep {
+                       $_->{'name'} =~ s!^$remote/!!
+                       } @remoteheads ];
+       }
+}
+
 sub git_get_references {
        my $type = shift || "";
        my %refs;
@@ -3143,13 +3249,15 @@ sub parse_from_to_diffinfo {
 ## parse to array of hashes functions
 
 sub git_get_heads_list {
-       my $limit = shift;
+       my ($limit, @classes) = @_;
+       @classes = ('heads') unless @classes;
+       my @patterns = map { "refs/$_" } @classes;
        my @headslist;
 
        open my $fd, '-|', git_cmd(), 'for-each-ref',
                ($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate',
                '--format=%(objectname) %(refname) %(subject)%00%(committer)',
-               'refs/heads'
+               @patterns
                or return;
        while (my $line = <$fd>) {
                my %ref_item;
@@ -3160,7 +3268,7 @@ sub git_get_heads_list {
                my ($committer, $epoch, $tz) =
                        ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);
                $ref_item{'fullname'}  = $name;
-               $name =~ s!^refs/heads/!!;
+               $name =~ s!^refs/(?:head|remote)s/!!;
 
                $ref_item{'name'}  = $name;
                $ref_item{'id'}    = $hash;
@@ -3357,10 +3465,10 @@ sub run_highlighter {
        my ($fd, $highlight, $syntax) = @_;
        return $fd unless ($highlight && defined $syntax);
 
-       close $fd
-               or die_error(404, "Reading blob failed");
+       close $fd;
        open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
-                 "highlight --xhtml --fragment --syntax $syntax |"
+                 quote_command($highlight_bin).
+                 " --fragment --syntax $syntax |"
                or die_error(500, "Couldn't open file or run syntax highlighter");
        return $fd;
 }
@@ -3386,6 +3494,51 @@ sub get_page_title {
        return $title;
 }
 
+sub print_feed_meta {
+       if (defined $project) {
+               my %href_params = get_feed_info();
+               if (!exists $href_params{'-title'}) {
+                       $href_params{'-title'} = 'log';
+               }
+
+               foreach my $format qw(RSS Atom) {
+                       my $type = lc($format);
+                       my %link_attr = (
+                               '-rel' => 'alternate',
+                               '-title' => esc_attr("$project - $href_params{'-title'} - $format feed"),
+                               '-type' => "application/$type+xml"
+                       );
+
+                       $href_params{'action'} = $type;
+                       $link_attr{'-href'} = href(%href_params);
+                       print "<link ".
+                             "rel=\"$link_attr{'-rel'}\" ".
+                             "title=\"$link_attr{'-title'}\" ".
+                             "href=\"$link_attr{'-href'}\" ".
+                             "type=\"$link_attr{'-type'}\" ".
+                             "/>\n";
+
+                       $href_params{'extra_options'} = '--no-merges';
+                       $link_attr{'-href'} = href(%href_params);
+                       $link_attr{'-title'} .= ' (no merges)';
+                       print "<link ".
+                             "rel=\"$link_attr{'-rel'}\" ".
+                             "title=\"$link_attr{'-title'}\" ".
+                             "href=\"$link_attr{'-href'}\" ".
+                             "type=\"$link_attr{'-type'}\" ".
+                             "/>\n";
+               }
+
+       } else {
+               printf('<link rel="alternate" title="%s projects list" '.
+                      'href="%s" type="text/plain; charset=utf-8" />'."\n",
+                      esc_attr($site_name), href(project=>undef, action=>"project_index"));
+               printf('<link rel="alternate" title="%s projects feeds" '.
+                      'href="%s" type="text/x-opml" />'."\n",
+                      esc_attr($site_name), href(project=>undef, action=>"opml"));
+       }
+}
+
 sub git_header_html {
        my $status = shift || "200 OK";
        my $expires = shift;
@@ -3428,57 +3581,17 @@ EOF
        # print out each stylesheet that exist, providing backwards capability
        # for those people who defined $stylesheet in a config file
        if (defined $stylesheet) {
-               print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
+               print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
        } else {
                foreach my $stylesheet (@stylesheets) {
                        next unless $stylesheet;
-                       print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
-               }
-       }
-       if (defined $project) {
-               my %href_params = get_feed_info();
-               if (!exists $href_params{'-title'}) {
-                       $href_params{'-title'} = 'log';
-               }
-
-               foreach my $format qw(RSS Atom) {
-                       my $type = lc($format);
-                       my %link_attr = (
-                               '-rel' => 'alternate',
-                               '-title' => "$project - $href_params{'-title'} - $format feed",
-                               '-type' => "application/$type+xml"
-                       );
-
-                       $href_params{'action'} = $type;
-                       $link_attr{'-href'} = href(%href_params);
-                       print "<link ".
-                             "rel=\"$link_attr{'-rel'}\" ".
-                             "title=\"$link_attr{'-title'}\" ".
-                             "href=\"$link_attr{'-href'}\" ".
-                             "type=\"$link_attr{'-type'}\" ".
-                             "/>\n";
-
-                       $href_params{'extra_options'} = '--no-merges';
-                       $link_attr{'-href'} = href(%href_params);
-                       $link_attr{'-title'} .= ' (no merges)';
-                       print "<link ".
-                             "rel=\"$link_attr{'-rel'}\" ".
-                             "title=\"$link_attr{'-title'}\" ".
-                             "href=\"$link_attr{'-href'}\" ".
-                             "type=\"$link_attr{'-type'}\" ".
-                             "/>\n";
+                       print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
                }
-
-       } else {
-               printf('<link rel="alternate" title="%s projects list" '.
-                      'href="%s" type="text/plain; charset=utf-8" />'."\n",
-                      $site_name, href(project=>undef, action=>"project_index"));
-               printf('<link rel="alternate" title="%s projects feeds" '.
-                      'href="%s" type="text/x-opml" />'."\n",
-                      $site_name, href(project=>undef, action=>"opml"));
        }
+       print_feed_meta()
+               if ($status eq '200 OK');
        if (defined $favicon) {
-               print qq(<link rel="shortcut icon" href="$favicon" type="image/png" />\n);
+               print qq(<link rel="shortcut icon" href=").esc_url($favicon).qq(" type="image/png" />\n);
        }
 
        print "</head>\n" .
@@ -3488,15 +3601,28 @@ EOF
                insert_file($site_header);
        }
 
-       print "<div class=\"page_header\">\n" .
-             $cgi->a({-href => esc_url($logo_url),
-                      -title => $logo_label},
-                     qq(<img src="$logo" width="72" height="27" alt="git" class="logo"/>));
+       print "<div class=\"page_header\">\n";
+       if (defined $logo) {
+               print $cgi->a({-href => esc_url($logo_url),
+                              -title => $logo_label},
+                             $cgi->img({-src => esc_url($logo),
+                                        -width => 72, -height => 27,
+                                        -alt => "git",
+                                        -class => "logo"}));
+       }
        print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
        if (defined $project) {
                print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
                if (defined $action) {
-                       print " / $action";
+                       my $action_print = $action ;
+                       if (defined $opts{-action_extra}) {
+                               $action_print = $cgi->a({-href => href(action=>$action)},
+                                       $action);
+                       }
+                       print " / $action_print";
+               }
+               if (defined $opts{-action_extra}) {
+                       print " / $opts{-action_extra}";
                }
                print "\n";
        }
@@ -3575,7 +3701,7 @@ sub git_footer_html {
                print "<div id=\"generating_info\">\n";
                print 'This page took '.
                      '<span id="generating_time" class="time_span">'.
-                     Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]).
+                     tv_interval($t0, [ gettimeofday() ]).
                      ' seconds </span>'.
                      ' and '.
                      '<span id="generating_cmd">'.
@@ -3589,7 +3715,7 @@ sub git_footer_html {
                insert_file($site_footer);
        }
 
-       print qq!<script type="text/javascript" src="$javascript"></script>\n!;
+       print qq!<script type="text/javascript" src="!.esc_url($javascript).qq!"></script>\n!;
        if (defined $action &&
            $action eq 'blame_incremental') {
                print qq!<script type="text/javascript">\n!.
@@ -3703,6 +3829,19 @@ sub git_print_page_nav {
              "</div>\n";
 }
 
+# returns a submenu for the nagivation of the refs views (tags, heads,
+# remotes) with the current view disabled and the remotes view only
+# available if the feature is enabled
+sub format_ref_views {
+       my ($current) = @_;
+       my @ref_views = qw{tags heads};
+       push @ref_views, 'remotes' if gitweb_check_feature('remote_heads');
+       return join " | ", map {
+               $_ eq $current ? $_ :
+               $cgi->a({-href => href(action=>$_)}, $_)
+       } @ref_views
+}
+
 sub format_paging_nav {
        my ($action, $page, $has_next_link) = @_;
        my $paging_nav;
@@ -3746,6 +3885,49 @@ sub git_print_header_div {
              "\n</div>\n";
 }
 
+sub format_repo_url {
+       my ($name, $url) = @_;
+       return "<tr class=\"metadata_url\"><td>$name</td><td>$url</td></tr>\n";
+}
+
+# Group output by placing it in a DIV element and adding a header.
+# Options for start_div() can be provided by passing a hash reference as the
+# first parameter to the function.
+# Options to git_print_header_div() can be provided by passing an array
+# reference. This must follow the options to start_div if they are present.
+# The content can be a scalar, which is output as-is, a scalar reference, which
+# is output after html escaping, an IO handle passed either as *handle or
+# *handle{IO}, or a function reference. In the latter case all following
+# parameters will be taken as argument to the content function call.
+sub git_print_section {
+       my ($div_args, $header_args, $content);
+       my $arg = shift;
+       if (ref($arg) eq 'HASH') {
+               $div_args = $arg;
+               $arg = shift;
+       }
+       if (ref($arg) eq 'ARRAY') {
+               $header_args = $arg;
+               $arg = shift;
+       }
+       $content = $arg;
+
+       print $cgi->start_div($div_args);
+       git_print_header_div(@$header_args);
+
+       if (ref($content) eq 'CODE') {
+               $content->(@_);
+       } elsif (ref($content) eq 'SCALAR') {
+               print esc_html($$content);
+       } elsif (ref($content) eq 'GLOB' or ref($content) eq 'IO::Handle') {
+               print <$content>;
+       } elsif (!ref($content) && defined($content)) {
+               print $content;
+       }
+
+       print $cgi->end_div;
+}
+
 sub print_local_time {
        print format_local_time(@_);
 }
@@ -3781,9 +3963,9 @@ sub git_print_authorship {
 }
 
 # Outputs table rows containing the full author or committer information,
-# in the format expected for 'commit' view (& similia).
+# in the format expected for 'commit' view (& similar).
 # 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
+# to output information for. If the list is empty it defaults to both
 # author and committer.
 sub git_print_authorship_rows {
        my $co = shift;
@@ -4512,8 +4694,8 @@ sub git_patchset_body {
                print "</div>\n"; # class="patch"
        }
 
-       # for compact combined (--cc) format, with chunk and patch simpliciaction
-       # patchset might be empty, but there might be unprocessed raw lines
+       # for compact combined (--cc) format, with chunk and patch simplification
+       # the patchset might be empty, but there might be unprocessed raw lines
        for (++$patch_idx if $patch_number > 0;
             $patch_idx < @$difftree;
             ++$patch_idx) {
@@ -4945,7 +5127,7 @@ sub git_heads_body {
                      "<td class=\"link\">" .
                      $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'})}, "shortlog") . " | " .
                      $cgi->a({-href => href(action=>"log", hash=>$ref{'fullname'})}, "log") . " | " .
-                     $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'name'})}, "tree") .
+                     $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'fullname'})}, "tree") .
                      "</td>\n" .
                      "</tr>";
        }
@@ -4957,6 +5139,101 @@ sub git_heads_body {
        print "</table>\n";
 }
 
+# Display a single remote block
+sub git_remote_block {
+       my ($remote, $rdata, $limit, $head) = @_;
+
+       my $heads = $rdata->{'heads'};
+       my $fetch = $rdata->{'fetch'};
+       my $push = $rdata->{'push'};
+
+       my $urls_table = "<table class=\"projects_list\">\n" ;
+
+       if (defined $fetch) {
+               if ($fetch eq $push) {
+                       $urls_table .= format_repo_url("URL", $fetch);
+               } else {
+                       $urls_table .= format_repo_url("Fetch URL", $fetch);
+                       $urls_table .= format_repo_url("Push URL", $push) if defined $push;
+               }
+       } elsif (defined $push) {
+               $urls_table .= format_repo_url("Push URL", $push);
+       } else {
+               $urls_table .= format_repo_url("", "No remote URL");
+       }
+
+       $urls_table .= "</table>\n";
+
+       my $dots;
+       if (defined $limit && $limit < @$heads) {
+               $dots = $cgi->a({-href => href(action=>"remotes", hash=>$remote)}, "...");
+       }
+
+       print $urls_table;
+       git_heads_body($heads, $head, 0, $limit, $dots);
+}
+
+# Display a list of remote names with the respective fetch and push URLs
+sub git_remotes_list {
+       my ($remotedata, $limit) = @_;
+       print "<table class=\"heads\">\n";
+       my $alternate = 1;
+       my @remotes = sort keys %$remotedata;
+
+       my $limited = $limit && $limit < @remotes;
+
+       $#remotes = $limit - 1 if $limited;
+
+       while (my $remote = shift @remotes) {
+               my $rdata = $remotedata->{$remote};
+               my $fetch = $rdata->{'fetch'};
+               my $push = $rdata->{'push'};
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+               print "<td>" .
+                     $cgi->a({-href=> href(action=>'remotes', hash=>$remote),
+                              -class=> "list name"},esc_html($remote)) .
+                     "</td>";
+               print "<td class=\"link\">" .
+                     (defined $fetch ? $cgi->a({-href=> $fetch}, "fetch") : "fetch") .
+                     " | " .
+                     (defined $push ? $cgi->a({-href=> $push}, "push") : "push") .
+                     "</td>";
+
+               print "</tr>\n";
+       }
+
+       if ($limited) {
+               print "<tr>\n" .
+                     "<td colspan=\"3\">" .
+                     $cgi->a({-href => href(action=>"remotes")}, "...") .
+                     "</td>\n" . "</tr>\n";
+       }
+
+       print "</table>";
+}
+
+# Display remote heads grouped by remote, unless there are too many
+# remotes, in which case we only display the remote names
+sub git_remotes_body {
+       my ($remotedata, $limit, $head) = @_;
+       if ($limit and $limit < keys %$remotedata) {
+               git_remotes_list($remotedata, $limit);
+       } else {
+               fill_remote_heads($remotedata);
+               while (my ($remote, $rdata) = each %$remotedata) {
+                       git_print_section({-class=>"remote", -id=>$remote},
+                               ["remotes", $remote, $remote], sub {
+                                       git_remote_block($remote, $rdata, $limit, $head);
+                               });
+               }
+       }
+}
+
 sub git_search_grep_body {
        my ($commitlist, $from, $to, $extra) = @_;
        $from = 0 unless defined $from;
@@ -5094,6 +5371,7 @@ sub git_summary {
        my %co = parse_commit("HEAD");
        my %cd = %co ? parse_date($co{'committer_epoch'}, $co{'committer_tz'}) : ();
        my $head = $co{'id'};
+       my $remote_heads = gitweb_check_feature('remote_heads');
 
        my $owner = git_get_project_owner($project);
 
@@ -5102,6 +5380,7 @@ sub git_summary {
        # there are more ...
        my @taglist  = git_get_tags_list(16);
        my @headlist = git_get_heads_list(16);
+       my %remotedata = $remote_heads ? git_get_remotes_list() : ();
        my @forklist;
        my $check_forks = gitweb_check_feature('forks');
 
@@ -5127,7 +5406,7 @@ sub git_summary {
        @url_list = map { "$_/$project" } @git_base_url_list unless @url_list;
        foreach my $git_url (@url_list) {
                next unless $git_url;
-               print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
+               print format_repo_url($url_tag, $git_url);
                $url_tag = "";
        }
 
@@ -5179,6 +5458,11 @@ sub git_summary {
                               $cgi->a({-href => href(action=>"heads")}, "..."));
        }
 
+       if (%remotedata) {
+               git_print_header_div('remotes');
+               git_remotes_body(\%remotedata, 15, $head);
+       }
+
        if (@forklist) {
                git_print_header_div('forks');
                git_project_list_body(\@forklist, 'age', 0, 15,
@@ -5191,15 +5475,15 @@ sub git_summary {
 }
 
 sub git_tag {
-       my $head = git_get_head_hash($project);
-       git_header_html();
-       git_print_page_nav('','', $head,undef,$head);
        my %tag = parse_tag($hash);
 
        if (! %tag) {
                die_error(404, "Unknown tag object");
        }
 
+       my $head = git_get_head_hash($project);
+       git_header_html();
+       git_print_page_nav('','', $head,undef,$head);
        git_print_header_div('commit', esc_html($tag{'name'}), $hash);
        print "<div class=\"title_text\">\n" .
              "<table class=\"object_header\">\n" .
@@ -5283,7 +5567,7 @@ sub git_blame_common {
                print 'END';
                if (defined $t0 && gitweb_check_feature('timed')) {
                        print ' '.
-                             Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]).
+                             tv_interval($t0, [ gettimeofday() ]).
                              ' '.$number_of_git_cmds;
                }
                print "\n";
@@ -5470,7 +5754,7 @@ sub git_blame_data {
 sub git_tags {
        my $head = git_get_head_hash($project);
        git_header_html();
-       git_print_page_nav('','', $head,undef,$head);
+       git_print_page_nav('','', $head,undef,$head,format_ref_views('tags'));
        git_print_header_div('summary', $project);
 
        my @tagslist = git_get_tags_list();
@@ -5483,7 +5767,7 @@ sub git_tags {
 sub git_heads {
        my $head = git_get_head_hash($project);
        git_header_html();
-       git_print_page_nav('','', $head,undef,$head);
+       git_print_page_nav('','', $head,undef,$head,format_ref_views('heads'));
        git_print_header_div('summary', $project);
 
        my @headslist = git_get_heads_list();
@@ -5493,6 +5777,39 @@ sub git_heads {
        git_footer_html();
 }
 
+# used both for single remote view and for list of all the remotes
+sub git_remotes {
+       gitweb_check_feature('remote_heads')
+               or die_error(403, "Remote heads view is disabled");
+
+       my $head = git_get_head_hash($project);
+       my $remote = $input_params{'hash'};
+
+       my $remotedata = git_get_remotes_list($remote);
+       die_error(500, "Unable to get remote information") unless defined $remotedata;
+
+       unless (%$remotedata) {
+               die_error(404, defined $remote ?
+                       "Remote $remote not found" :
+                       "No remotes found");
+       }
+
+       git_header_html(undef, undef, -action_extra => $remote);
+       git_print_page_nav('', '',  $head, undef, $head,
+               format_ref_views($remote ? '' : 'remotes'));
+
+       fill_remote_heads($remotedata);
+       if (defined $remote) {
+               git_print_header_div('remotes', "$remote remote for $project");
+               git_remote_block($remote, $remotedata->{$remote}, undef, $head);
+       } else {
+               git_print_header_div('summary', "$project remotes");
+               git_remotes_body($remotedata, undef, $head);
+       }
+
+       git_footer_html();
+}
+
 sub git_blob_plain {
        my $type = shift;
        my $expires;
@@ -5609,14 +5926,14 @@ sub git_blob {
        } else {
                print "<div class=\"page_nav\">\n" .
                      "<br/><br/></div>\n" .
-                     "<div class=\"title\">$hash</div>\n";
+                     "<div class=\"title\">".esc_html($hash)."</div>\n";
        }
        git_print_page_path($file_name, "blob", $hash_base);
        print "<div class=\"page_body\">\n";
        if ($mimetype =~ m!^image/!) {
-               print qq!<img type="$mimetype"!;
+               print qq!<img type="!.esc_attr($mimetype).qq!"!;
                if ($file_name) {
-                       print qq! alt="$file_name" title="$file_name"!;
+                       print qq! alt="!.esc_attr($file_name).qq!" title="!.esc_attr($file_name).qq!"!;
                }
                print qq! src="! .
                      href(action=>"blob_plain", hash=>$hash,
@@ -5629,7 +5946,7 @@ sub git_blob {
                        $nr++;
                        $line = untabify($line);
                        printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!,
-                              $nr, href(-replay => 1), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
+                              $nr, esc_attr(href(-replay => 1)), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
                }
        }
        close $fd
@@ -5691,7 +6008,7 @@ sub git_tree {
                undef $hash_base;
                print "<div class=\"page_nav\">\n";
                print "<br/><br/></div>\n";
-               print "<div class=\"title\">$hash</div>\n";
+               print "<div class=\"title\">".esc_html($hash)."</div>\n";
        }
        if (defined $file_name) {
                $basedir = $file_name;
@@ -6159,7 +6476,7 @@ sub git_blobdiff {
                        git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
                } else {
                        print "<div class=\"page_nav\"><br/>$formats_nav<br/></div>\n";
-                       print "<div class=\"title\">$hash vs $hash_parent</div>\n";
+                       print "<div class=\"title\">".esc_html("$hash vs $hash_parent")."</div>\n";
                }
                if (defined $file_name) {
                        git_print_page_path($file_name, "blob", $hash_base);
@@ -6857,7 +7174,7 @@ XML
                if (defined $favicon) {
                        print "<icon>" . esc_url($favicon) . "</icon>\n";
                }
-               if (defined $logo_url) {
+               if (defined $logo) {
                        # not twice as wide as tall: 72 x 27 pixels
                        print "<logo>" . esc_url($logo) . "</logo>\n";
                }
index 4132aabcdb4ef402ceb1a890b61778b3254fdf18..79d7eebba797f3bd80a639f6ca0835e15016762d 100644 (file)
@@ -573,6 +573,12 @@ div.binary {
        font-style: italic;
 }
 
+div.remote {
+       margin: .5em;
+       border: 1px solid #d9d8d1;
+       display: inline-block;
+}
+
 /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
 
 /* Highlighting theme definition: */
diff --git a/graph.c b/graph.c
index e2a58605cab62b27dd055985d8156bfe52ad6fe3..f1a63c2241a30cce0141b87b5e8005b1a899a8ef 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -443,7 +443,7 @@ static void graph_update_width(struct git_graph *graph,
                max_cols++;
 
        /*
-        * We added a column for the the current commit as part of
+        * We added a column for the current commit as part of
         * graph->num_parents.  If the current commit was already in
         * graph->columns, then we have double counted it.
         */
diff --git a/grep.c b/grep.c
index 82fb349be6f6ef8211d4828101cd8e88bcbb304f..63c4280cac9a87f867451c0d9787d1fe21ea9c2d 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -189,30 +189,74 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
        return compile_pattern_or(list);
 }
 
-void compile_grep_patterns(struct grep_opt *opt)
+static struct grep_expr *grep_true_expr(void)
+{
+       struct grep_expr *z = xcalloc(1, sizeof(*z));
+       z->node = GREP_NODE_TRUE;
+       return z;
+}
+
+static struct grep_expr *grep_or_expr(struct grep_expr *left, struct grep_expr *right)
+{
+       struct grep_expr *z = xcalloc(1, sizeof(*z));
+       z->node = GREP_NODE_OR;
+       z->u.binary.left = left;
+       z->u.binary.right = right;
+       return z;
+}
+
+static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
 {
        struct grep_pat *p;
-       struct grep_expr *header_expr = NULL;
-
-       if (opt->header_list) {
-               p = opt->header_list;
-               header_expr = compile_pattern_expr(&p);
-               if (p)
-                       die("incomplete pattern expression: %s", p->pattern);
-               for (p = opt->header_list; p; p = p->next) {
-                       switch (p->token) {
-                       case GREP_PATTERN: /* atom */
-                       case GREP_PATTERN_HEAD:
-                       case GREP_PATTERN_BODY:
-                               compile_regexp(p, opt);
-                               break;
-                       default:
-                               opt->extended = 1;
-                               break;
-                       }
+       struct grep_expr *header_expr;
+       struct grep_expr *(header_group[GREP_HEADER_FIELD_MAX]);
+       enum grep_header_field fld;
+
+       if (!opt->header_list)
+               return NULL;
+       p = opt->header_list;
+       for (p = opt->header_list; p; p = p->next) {
+               if (p->token != GREP_PATTERN_HEAD)
+                       die("bug: a non-header pattern in grep header list.");
+               if (p->field < 0 || GREP_HEADER_FIELD_MAX <= p->field)
+                       die("bug: unknown header field %d", p->field);
+               compile_regexp(p, opt);
+       }
+
+       for (fld = 0; fld < GREP_HEADER_FIELD_MAX; fld++)
+               header_group[fld] = NULL;
+
+       for (p = opt->header_list; p; p = p->next) {
+               struct grep_expr *h;
+               struct grep_pat *pp = p;
+
+               h = compile_pattern_atom(&pp);
+               if (!h || pp != p->next)
+                       die("bug: malformed header expr");
+               if (!header_group[p->field]) {
+                       header_group[p->field] = h;
+                       continue;
                }
+               header_group[p->field] = grep_or_expr(h, header_group[p->field]);
        }
 
+       header_expr = NULL;
+
+       for (fld = 0; fld < GREP_HEADER_FIELD_MAX; fld++) {
+               if (!header_group[fld])
+                       continue;
+               if (!header_expr)
+                       header_expr = grep_true_expr();
+               header_expr = grep_or_expr(header_group[fld], header_expr);
+       }
+       return header_expr;
+}
+
+void compile_grep_patterns(struct grep_opt *opt)
+{
+       struct grep_pat *p;
+       struct grep_expr *header_expr = prep_header_patterns(opt);
+
        for (p = opt->pattern_list; p; p = p->next) {
                switch (p->token) {
                case GREP_PATTERN: /* atom */
@@ -231,9 +275,6 @@ void compile_grep_patterns(struct grep_opt *opt)
        else if (!opt->extended)
                return;
 
-       /* Then bundle them up in an expression.
-        * A classic recursive descent parser would do.
-        */
        p = opt->pattern_list;
        if (p)
                opt->pattern_expression = compile_pattern_expr(&p);
@@ -243,22 +284,18 @@ void compile_grep_patterns(struct grep_opt *opt)
        if (!header_expr)
                return;
 
-       if (opt->pattern_expression) {
-               struct grep_expr *z;
-               z = xcalloc(1, sizeof(*z));
-               z->node = GREP_NODE_OR;
-               z->u.binary.left = opt->pattern_expression;
-               z->u.binary.right = header_expr;
-               opt->pattern_expression = z;
-       } else {
+       if (!opt->pattern_expression)
                opt->pattern_expression = header_expr;
-       }
+       else
+               opt->pattern_expression = grep_or_expr(opt->pattern_expression,
+                                                      header_expr);
        opt->all_match = 1;
 }
 
 static void free_pattern_expr(struct grep_expr *x)
 {
        switch (x->node) {
+       case GREP_NODE_TRUE:
        case GREP_NODE_ATOM:
                break;
        case GREP_NODE_NOT:
@@ -487,6 +524,9 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
        if (!x)
                die("Not a valid grep expression");
        switch (x->node) {
+       case GREP_NODE_TRUE:
+               h = 1;
+               break;
        case GREP_NODE_ATOM:
                h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
                break;
diff --git a/grep.h b/grep.h
index efa8cff980af2b2c06dad080876637d16b5c4985..06621fe663545af52fbc42827a8374ab5bd42f38 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -22,6 +22,7 @@ enum grep_header_field {
        GREP_HEADER_AUTHOR = 0,
        GREP_HEADER_COMMITTER
 };
+#define GREP_HEADER_FIELD_MAX (GREP_HEADER_COMMITTER + 1)
 
 struct grep_pat {
        struct grep_pat *next;
@@ -41,6 +42,7 @@ enum grep_expr_node {
        GREP_NODE_ATOM,
        GREP_NODE_NOT,
        GREP_NODE_AND,
+       GREP_NODE_TRUE,
        GREP_NODE_OR
 };
 
diff --git a/help.c b/help.c
index 7f4928e45954465d0401964289952aec4bd59e2a..7654f1bfd608d151a213937614449cc497bb638b 100644 (file)
--- a/help.c
+++ b/help.c
@@ -3,6 +3,7 @@
 #include "exec_cmd.h"
 #include "levenshtein.h"
 #include "help.h"
+#include "common-cmds.h"
 
 /* most GUI terminals set COLUMNS (although some don't export it) */
 static int term_columns(void)
@@ -298,7 +299,8 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
 }
 
 /* An empirically derived magic number */
-#define SIMILAR_ENOUGH(x) ((x) < 6)
+#define SIMILARITY_FLOOR 7
+#define SIMILAR_ENOUGH(x) ((x) < SIMILARITY_FLOOR)
 
 const char *help_unknown_cmd(const char *cmd)
 {
@@ -319,10 +321,28 @@ const char *help_unknown_cmd(const char *cmd)
              sizeof(main_cmds.names), cmdname_compare);
        uniq(&main_cmds);
 
-       /* This reuses cmdname->len for similarity index */
-       for (i = 0; i < main_cmds.cnt; ++i)
+       /* This abuses cmdname->len for levenshtein distance */
+       for (i = 0, n = 0; i < main_cmds.cnt; i++) {
+               int cmp = 0; /* avoid compiler stupidity */
+               const char *candidate = main_cmds.names[i]->name;
+
+               /* Does the candidate appear in common_cmds list? */
+               while (n < ARRAY_SIZE(common_cmds) &&
+                      (cmp = strcmp(common_cmds[n].name, candidate)) < 0)
+                       n++;
+               if ((n < ARRAY_SIZE(common_cmds)) && !cmp) {
+                       /* Yes, this is one of the common commands */
+                       n++; /* use the entry from common_cmds[] */
+                       if (!prefixcmp(candidate, cmd)) {
+                               /* Give prefix match a very good score */
+                               main_cmds.names[i]->len = 0;
+                               continue;
+                       }
+               }
+
                main_cmds.names[i]->len =
-                       levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
+                       levenshtein(cmd, candidate, 0, 2, 1, 4) + 1;
+       }
 
        qsort(main_cmds.names, main_cmds.cnt,
              sizeof(*main_cmds.names), levenshtein_compare);
@@ -330,10 +350,21 @@ const char *help_unknown_cmd(const char *cmd)
        if (!main_cmds.cnt)
                die ("Uh oh. Your system reports no Git commands at all.");
 
-       best_similarity = main_cmds.names[0]->len;
-       n = 1;
-       while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
-               ++n;
+       /* skip and count prefix matches */
+       for (n = 0; n < main_cmds.cnt && !main_cmds.names[n]->len; n++)
+               ; /* still counting */
+
+       if (main_cmds.cnt <= n) {
+               /* prefix matches with everything? that is too ambiguous */
+               best_similarity = SIMILARITY_FLOOR + 1;
+       } else {
+               /* count all the most similar ones */
+               for (best_similarity = main_cmds.names[n++]->len;
+                    (n < main_cmds.cnt &&
+                     best_similarity == main_cmds.names[n]->len);
+                    n++)
+                       ; /* still counting */
+       }
        if (autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
                const char *assumed = main_cmds.names[0]->name;
                main_cmds.names[0] = NULL;
diff --git a/help.h b/help.h
index 56bc15406ffc55115fa1c827e0d1d5a7e74c516d..b6b12d5754888438085bb4e574b034951ffec473 100644 (file)
--- a/help.h
+++ b/help.h
@@ -16,14 +16,17 @@ static inline void mput_char(char c, unsigned int num)
                putchar(c);
 }
 
-void load_command_list(const char *prefix,
-               struct cmdnames *main_cmds,
-               struct cmdnames *other_cmds);
-void add_cmdname(struct cmdnames *cmds, const char *name, int len);
+extern void list_common_cmds_help(void);
+extern const char *help_unknown_cmd(const char *cmd);
+extern void load_command_list(const char *prefix,
+                             struct cmdnames *main_cmds,
+                             struct cmdnames *other_cmds);
+extern void add_cmdname(struct cmdnames *cmds, const char *name, int len);
 /* Here we require that excludes is a sorted list. */
-void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
-int is_in_cmdlist(struct cmdnames *c, const char *s);
-void list_commands(const char *title, struct cmdnames *main_cmds,
-                  struct cmdnames *other_cmds);
+extern void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
+extern int is_in_cmdlist(struct cmdnames *cmds, const char *name);
+extern void list_commands(const char *title,
+                         struct cmdnames *main_cmds,
+                         struct cmdnames *other_cmds);
 
 #endif /* HELP_H */
index 14c90c2e84afd9997e1a6453f0065b3f59b32e57..85015048dd466fb2f2afe12086379ecf579d0a56 100644 (file)
@@ -510,9 +510,7 @@ static char* getdir(void)
                        die("GIT_PROJECT_ROOT is set but PATH_INFO is not");
                if (daemon_avoid_alias(pathinfo))
                        die("'%s': aliased", pathinfo);
-               strbuf_addstr(&buf, root);
-               if (buf.buf[buf.len - 1] != '/')
-                       strbuf_addch(&buf, '/');
+               end_url_with_slash(&buf, root);
                if (pathinfo[0] == '/')
                        pathinfo++;
                strbuf_addstr(&buf, pathinfo);
index 762c750d7af3651287c147034d3dead469453e7c..923904f97f9274d09a0a0b08543492f78a141f79 100644 (file)
@@ -14,8 +14,7 @@ int main(int argc, const char **argv)
        int commits;
        const char **write_ref = NULL;
        char **commit_id;
-       const char *url;
-       char *rewritten_url = NULL;
+       char *url = NULL;
        int arg = 1;
        int rc = 0;
        int get_tree = 0;
@@ -57,19 +56,14 @@ int main(int argc, const char **argv)
                commit_id = (char **) &argv[arg++];
                commits = 1;
        }
-       url = argv[arg];
+
+       if (argv[arg])
+               str_end_url_with_slash(argv[arg], &url);
 
        prefix = setup_git_directory();
 
        git_config(git_default_config, NULL);
 
-       if (url && url[strlen(url)-1] != '/') {
-               rewritten_url = xmalloc(strlen(url)+2);
-               strcpy(rewritten_url, url);
-               strcat(rewritten_url, "/");
-               url = rewritten_url;
-       }
-
        http_init(NULL);
        walker = get_http_walker(url);
        walker->get_tree = get_tree;
@@ -93,7 +87,7 @@ int main(int argc, const char **argv)
        walker_free(walker);
        http_cleanup();
 
-       free(rewritten_url);
+       free(url);
 
        return rc;
 }
index c9bcd116975635cabf30eceb7909abfc7f341948..ff41a0e183dd3a449f5136c6d1488a31b4a8960b 100644 (file)
@@ -1090,6 +1090,10 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
        if (tag_closed) {
                if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {
                        if (ls->dentry_flags & IS_DIR) {
+
+                               /* ensure collection names end with slash */
+                               str_end_url_with_slash(ls->dentry_name, &ls->dentry_name);
+
                                if (ls->flags & PROCESS_DIRS) {
                                        ls->userFunc(ls);
                                }
@@ -1112,8 +1116,16 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
                                }
                        }
                        if (path) {
-                               path += repo->path_len;
-                               ls->dentry_name = xstrdup(path);
+                               const char *url = repo->url;
+                               if (repo->path)
+                                       url = repo->path;
+                               if (strncmp(path, url, repo->path_len))
+                                       error("Parsed path '%s' does not match url: '%s'\n",
+                                             path, url);
+                               else {
+                                       path += repo->path_len;
+                                       ls->dentry_name = xstrdup(path);
+                               }
                        }
                } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
                        ls->dentry_flags |= IS_DIR;
@@ -1789,7 +1801,6 @@ int main(int argc, char **argv)
        int new_refs;
        struct ref *ref, *local_refs;
        struct remote *remote;
-       char *rewritten_url = NULL;
 
        git_extract_argv0_path(argv[0]);
 
@@ -1835,8 +1846,8 @@ int main(int argc, char **argv)
                }
                if (!repo->url) {
                        char *path = strstr(arg, "//");
-                       repo->url = arg;
-                       repo->path_len = strlen(arg);
+                       str_end_url_with_slash(arg, &repo->url);
+                       repo->path_len = strlen(repo->url);
                        if (path) {
                                repo->path = strchr(path+2, '/');
                                if (repo->path)
@@ -1872,15 +1883,6 @@ int main(int argc, char **argv)
        remote->url[remote->url_nr++] = repo->url;
        http_init(remote);
 
-       if (repo->url && repo->url[strlen(repo->url)-1] != '/') {
-               rewritten_url = xmalloc(strlen(repo->url)+2);
-               strcpy(rewritten_url, repo->url);
-               strcat(rewritten_url, "/");
-               repo->path = rewritten_url + (repo->path - repo->url);
-               repo->path_len++;
-               repo->url = rewritten_url;
-       }
-
 #ifdef USE_CURL_MULTI
        is_running_queue = 0;
 #endif
@@ -2088,7 +2090,6 @@ int main(int argc, char **argv)
        }
 
  cleanup:
-       free(rewritten_url);
        if (info_ref_lock)
                unlock_remote(info_ref_lock);
        free(repo);
diff --git a/http.c b/http.c
index 1320c50e32eb7b8715b263bc2af089c3dbce39fa..9e767723ed1045445e91e79489cb8ec8bb861965 100644 (file)
--- a/http.c
+++ b/http.c
@@ -2,6 +2,7 @@
 #include "pack.h"
 #include "sideband.h"
 #include "run-command.h"
+#include "url.h"
 
 int data_received;
 int active_requests;
@@ -41,6 +42,7 @@ static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
 static char *user_name, *user_pass;
+static const char *user_agent;
 
 #if LIBCURL_VERSION_NUM >= 0x071700
 /* Use CURLOPT_KEYPASSWD as is */
@@ -196,6 +198,9 @@ static int http_options(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp("http.useragent", var))
+               return git_config_string(&user_agent, var, value);
+
        /* Fall back on the default ones */
        return git_default_config(var, value, cb);
 }
@@ -275,11 +280,17 @@ static CURL *get_curl_handle(void)
        }
 
        curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
+#if LIBCURL_VERSION_NUM >= 0x071301
+       curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
+#elif LIBCURL_VERSION_NUM >= 0x071101
+       curl_easy_setopt(result, CURLOPT_POST301, 1);
+#endif
 
        if (getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
 
-       curl_easy_setopt(result, CURLOPT_USERAGENT, GIT_USER_AGENT);
+       curl_easy_setopt(result, CURLOPT_USERAGENT,
+               user_agent ? user_agent : GIT_HTTP_USER_AGENT);
 
        if (curl_ftp_no_epsv)
                curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
@@ -292,7 +303,7 @@ static CURL *get_curl_handle(void)
 
 static void http_auth_init(const char *url)
 {
-       char *at, *colon, *cp, *slash;
+       char *at, *colon, *cp, *slash, *decoded;
        int len;
 
        cp = strstr(url, "://");
@@ -317,16 +328,25 @@ static void http_auth_init(const char *url)
                user_name = xmalloc(len + 1);
                memcpy(user_name, cp, len);
                user_name[len] = '\0';
+               decoded = url_decode(user_name);
+               free(user_name);
+               user_name = decoded;
                user_pass = NULL;
        } else {
                len = colon - cp;
                user_name = xmalloc(len + 1);
                memcpy(user_name, cp, len);
                user_name[len] = '\0';
+               decoded = url_decode(user_name);
+               free(user_name);
+               user_name = decoded;
                len = at - (colon + 1);
                user_pass = xmalloc(len + 1);
                memcpy(user_pass, colon + 1, len);
                user_pass[len] = '\0';
+               decoded = url_decode(user_pass);
+               free(user_pass);
+               user_pass = decoded;
        }
 }
 
@@ -380,6 +400,8 @@ void http_init(struct remote *remote)
 #endif
        set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
 
+       set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");
+
        low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
        if (low_speed_limit != NULL)
                curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
@@ -721,13 +743,6 @@ static inline int hex(int v)
                return 'A' + v - 10;
 }
 
-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;
diff --git a/http.h b/http.h
index 173f74c8298c3fa3989f88b06e2085127fe96db8..5c6e243dde3c654a3c0a705146fb15395c27fb95 100644 (file)
--- a/http.h
+++ b/http.h
@@ -8,6 +8,7 @@
 
 #include "strbuf.h"
 #include "remote.h"
+#include "url.h"
 
 /*
  * We detect based on the cURL version if multi-transfer is
@@ -117,7 +118,6 @@ extern void append_remote_object_url(struct strbuf *buf, const char *url,
                                     int only_two_digit_prefix);
 extern char *get_remote_object_url(const char *url, const char *hex,
                                   int only_two_digit_prefix);
-extern void end_url_with_slash(struct strbuf *buf, const char *url);
 
 /* Options for http_request_*() */
 #define HTTP_NO_CACHE          1
diff --git a/ident.c b/ident.c
index 9e2438826dfce158e04549933d5c588dd6abcf5c..1c4adb0a9a7e94936ba64d286cedd65f4b8255a6 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -217,8 +217,10 @@ const char *fmt_ident(const char *name, const char *email,
        }
 
        strcpy(date, git_default_date);
-       if (!name_addr_only && date_str)
-               parse_date(date_str, date, sizeof(date));
+       if (!name_addr_only && date_str && date_str[0]) {
+               if (parse_date(date_str, date, sizeof(date)) < 0)
+                       die("invalid date format: %s", date_str);
+       }
 
        i = copy(buffer, sizeof(buffer), 0, name);
        i = add_raw(buffer, sizeof(buffer), i, " <");
index 8953548c07bb36f20798c7ca344d07960c22618c..61f6cc98d917c3e4395ec397ac74df6a40eeb07f 100644 (file)
@@ -61,12 +61,15 @@ static void process_tree(struct rev_info *revs,
                         struct tree *tree,
                         show_object_fn show,
                         struct name_path *path,
+                        struct strbuf *base,
                         const char *name)
 {
        struct object *obj = &tree->object;
        struct tree_desc desc;
        struct name_entry entry;
        struct name_path me;
+       int all_interesting = (revs->diffopt.pathspec.nr == 0);
+       int baselen = base->len;
 
        if (!revs->tree_objects)
                return;
@@ -82,13 +85,32 @@ static void process_tree(struct rev_info *revs,
        me.elem = name;
        me.elem_len = strlen(name);
 
+       if (!all_interesting) {
+               strbuf_addstr(base, name);
+               if (base->len)
+                       strbuf_addch(base, '/');
+       }
+
        init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
+               if (!all_interesting) {
+                       int showit = tree_entry_interesting(&entry,
+                                                           base, 0,
+                                                           &revs->diffopt.pathspec);
+
+                       if (showit < 0)
+                               break;
+                       else if (!showit)
+                               continue;
+                       else if (showit == 2)
+                               all_interesting = 1;
+               }
+
                if (S_ISDIR(entry.mode))
                        process_tree(revs,
                                     lookup_tree(entry.sha1),
-                                    show, &me, entry.path);
+                                    show, &me, base, entry.path);
                else if (S_ISGITLINK(entry.mode))
                        process_gitlink(revs, entry.sha1,
                                        show, &me, entry.path);
@@ -97,6 +119,7 @@ static void process_tree(struct rev_info *revs,
                                     lookup_blob(entry.sha1),
                                     show, &me, entry.path);
        }
+       strbuf_setlen(base, baselen);
        free(tree->buffer);
        tree->buffer = NULL;
 }
@@ -146,7 +169,9 @@ void traverse_commit_list(struct rev_info *revs,
 {
        int i;
        struct commit *commit;
+       struct strbuf base;
 
+       strbuf_init(&base, PATH_MAX);
        while ((commit = get_revision(revs)) != NULL) {
                add_pending_tree(revs, commit->tree);
                show_commit(commit, data);
@@ -164,7 +189,7 @@ void traverse_commit_list(struct rev_info *revs,
                }
                if (obj->type == OBJ_TREE) {
                        process_tree(revs, (struct tree *)obj, show_object,
-                                    NULL, name);
+                                    NULL, &base, name);
                        continue;
                }
                if (obj->type == OBJ_BLOB) {
@@ -181,4 +206,5 @@ void traverse_commit_list(struct rev_info *revs,
                revs->pending.alloc = 0;
                revs->pending.objects = NULL;
        }
+       strbuf_release(&base);
 }
index 3764a1ab72354a3ce643c899c1e8cf5443d4e7cb..6ce512efc4cce8042481e8a6947d033c272e78e6 100644 (file)
@@ -18,7 +18,7 @@ typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
                           mmfile_t *orig, const char *orig_name,
                           mmfile_t *src1, const char *name1,
                           mmfile_t *src2, const char *name2,
-                          int flag,
+                          const struct ll_merge_options *opts,
                           int marker_size);
 
 struct ll_merge_driver {
@@ -39,14 +39,18 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
                           mmfile_t *orig, const char *orig_name,
                           mmfile_t *src1, const char *name1,
                           mmfile_t *src2, const char *name2,
-                          int flag, int marker_size)
+                          const struct ll_merge_options *opts,
+                          int marker_size)
 {
+       mmfile_t *stolen;
+       assert(opts);
+
        /*
         * The tentative merge result is "ours" for the final round,
         * or common ancestor for an internal merge.  Still return
         * "conflicted merge" status.
         */
-       mmfile_t *stolen = (flag & 01) ? orig : src1;
+       stolen = opts->virtual_ancestor ? orig : src1;
 
        result->ptr = stolen->ptr;
        result->size = stolen->size;
@@ -60,9 +64,11 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
                        mmfile_t *orig, const char *orig_name,
                        mmfile_t *src1, const char *name1,
                        mmfile_t *src2, const char *name2,
-                       int flag, int marker_size)
+                       const struct ll_merge_options *opts,
+                       int marker_size)
 {
        xmparam_t xmp;
+       assert(opts);
 
        if (buffer_is_binary(orig->ptr, orig->size) ||
            buffer_is_binary(src1->ptr, src1->size) ||
@@ -74,12 +80,13 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
                                       orig, orig_name,
                                       src1, name1,
                                       src2, name2,
-                                      flag, marker_size);
+                                      opts, marker_size);
        }
 
        memset(&xmp, 0, sizeof(xmp));
        xmp.level = XDL_MERGE_ZEALOUS;
-       xmp.favor= (flag >> 1) & 03;
+       xmp.favor = opts->variant;
+       xmp.xpp.flags = opts->xdl_opts;
        if (git_xmerge_style >= 0)
                xmp.style = git_xmerge_style;
        if (marker_size > 0)
@@ -96,14 +103,17 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
                          mmfile_t *orig, const char *orig_name,
                          mmfile_t *src1, const char *name1,
                          mmfile_t *src2, const char *name2,
-                         int flag, int marker_size)
+                         const struct ll_merge_options *opts,
+                         int marker_size)
 {
        /* Use union favor */
-       flag = (flag & 1) | (XDL_MERGE_FAVOR_UNION << 1);
+       struct ll_merge_options o;
+       assert(opts);
+       o = *opts;
+       o.variant = XDL_MERGE_FAVOR_UNION;
        return ll_xdl_merge(drv_unused, result, path_unused,
                            orig, NULL, src1, NULL, src2, NULL,
-                           flag, marker_size);
-       return 0;
+                           &o, marker_size);
 }
 
 #define LL_BINARY_MERGE 0
@@ -135,7 +145,8 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
                        mmfile_t *orig, const char *orig_name,
                        mmfile_t *src1, const char *name1,
                        mmfile_t *src2, const char *name2,
-                       int flag, int marker_size)
+                       const struct ll_merge_options *opts,
+                       int marker_size)
 {
        char temp[4][50];
        struct strbuf cmd = STRBUF_INIT;
@@ -143,6 +154,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
        const char *args[] = { NULL, NULL };
        int status, fd, i;
        struct stat st;
+       assert(opts);
 
        dict[0].placeholder = "O"; dict[0].value = temp[0];
        dict[1].placeholder = "A"; dict[1].value = temp[1];
@@ -321,19 +333,37 @@ static int git_path_check_merge(const char *path, struct git_attr_check check[2]
        return git_checkattr(path, 2, check);
 }
 
+static void normalize_file(mmfile_t *mm, const char *path)
+{
+       struct strbuf strbuf = STRBUF_INIT;
+       if (renormalize_buffer(path, mm->ptr, mm->size, &strbuf)) {
+               free(mm->ptr);
+               mm->size = strbuf.len;
+               mm->ptr = strbuf_detach(&strbuf, NULL);
+       }
+}
+
 int ll_merge(mmbuffer_t *result_buf,
             const char *path,
             mmfile_t *ancestor, const char *ancestor_label,
             mmfile_t *ours, const char *our_label,
             mmfile_t *theirs, const char *their_label,
-            int flag)
+            const struct ll_merge_options *opts)
 {
        static struct git_attr_check check[2];
+       static const struct ll_merge_options default_opts;
        const char *ll_driver_name = NULL;
        int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
        const struct ll_merge_driver *driver;
-       int virtual_ancestor = flag & 01;
 
+       if (!opts)
+               opts = &default_opts;
+
+       if (opts->renormalize) {
+               normalize_file(ancestor, path);
+               normalize_file(ours, path);
+               normalize_file(theirs, path);
+       }
        if (!git_path_check_merge(path, check)) {
                ll_driver_name = check[0].value;
                if (check[1].value) {
@@ -343,11 +373,11 @@ int ll_merge(mmbuffer_t *result_buf,
                }
        }
        driver = find_ll_merge_driver(ll_driver_name);
-       if (virtual_ancestor && driver->recursive)
+       if (opts->virtual_ancestor && driver->recursive)
                driver = find_ll_merge_driver(driver->recursive);
        return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
                          ours, our_label, theirs, their_label,
-                         flag, marker_size);
+                         opts, marker_size);
 }
 
 int ll_merge_marker_size(const char *path)
index 57754cc8ca7b378a86b168a4fd6299fa3dfba045..244a31f55ac7375b59d7c257bfc2391957fdca94 100644 (file)
@@ -5,12 +5,19 @@
 #ifndef LL_MERGE_H
 #define LL_MERGE_H
 
+struct ll_merge_options {
+       unsigned virtual_ancestor : 1;
+       unsigned variant : 2;   /* favor ours, favor theirs, or union merge */
+       unsigned renormalize : 1;
+       long xdl_opts;
+};
+
 int ll_merge(mmbuffer_t *result_buf,
             const char *path,
             mmfile_t *ancestor, const char *ancestor_label,
             mmfile_t *ours, const char *our_label,
             mmfile_t *theirs, const char *their_label,
-            int flag);
+            const struct ll_merge_options *opts);
 
 int ll_merge_marker_size(const char *path);
 
index f80b701292f0d852950735ab20a63dac13e92362..02fcfde0b0b786af5229559586cf8ff5758429bc 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -79,12 +79,14 @@ static void add_mapping(struct string_list *map,
        if (old_name == NULL) {
                debug_mm("mailmap: adding (simple) entry for %s at index %d\n", old_email, index);
                /* Replace current name and new email for simple entry */
-               free(me->name);
-               free(me->email);
-               if (new_name)
+               if (new_name) {
+                       free(me->name);
                        me->name = xstrdup(new_name);
-               if (new_email)
+               }
+               if (new_email) {
+                       free(me->email);
                        me->email = xstrdup(new_email);
+               }
        } else {
                struct mailmap_info *mi = xmalloc(sizeof(struct mailmap_info));
                debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index);
index db4d0d50d32d8852d1cb5173125e4173c3badb49..f7f4533926e35867725db00f0c89522c4b290898 100644 (file)
@@ -37,7 +37,7 @@ static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our
         * common ancestor.
         */
        merge_status = ll_merge(&res, path, base, NULL,
-                               our, ".our", their, ".their", 0);
+                               our, ".our", their, ".their", NULL);
        if (merge_status < 0)
                return NULL;
 
index 6cff9cdc86b1db7c4bd0a75175a77b2474417dfb..16c2dbeab95a0f4099e3de8badf688bb4dee8189 100644 (file)
@@ -20,6 +20,7 @@
 #include "attr.h"
 #include "merge-recursive.h"
 #include "dir.h"
+#include "submodule.h"
 
 static struct tree *shift_tree_object(struct tree *one, struct tree *two,
                                      const char *subtree_shift)
@@ -62,6 +63,22 @@ static int sha_eq(const unsigned char *a, const unsigned char *b)
        return a && b && hashcmp(a, b) == 0;
 }
 
+enum rename_type {
+       RENAME_NORMAL = 0,
+       RENAME_DELETE,
+       RENAME_ONE_FILE_TO_TWO
+};
+
+struct rename_df_conflict_info {
+       enum rename_type rename_type;
+       struct diff_filepair *pair1;
+       struct diff_filepair *pair2;
+       const char *branch1;
+       const char *branch2;
+       struct stage_data *dst_entry1;
+       struct stage_data *dst_entry2;
+};
+
 /*
  * Since we want to write the index eventually, we cannot reuse the index
  * for these (temporary) data.
@@ -73,9 +90,37 @@ struct stage_data
                unsigned mode;
                unsigned char sha[20];
        } stages[4];
+       struct rename_df_conflict_info *rename_df_conflict_info;
        unsigned processed:1;
 };
 
+static inline void setup_rename_df_conflict_info(enum rename_type rename_type,
+                                                struct diff_filepair *pair1,
+                                                struct diff_filepair *pair2,
+                                                const char *branch1,
+                                                const char *branch2,
+                                                struct stage_data *dst_entry1,
+                                                struct stage_data *dst_entry2)
+{
+       struct rename_df_conflict_info *ci = xcalloc(1, sizeof(struct rename_df_conflict_info));
+       ci->rename_type = rename_type;
+       ci->pair1 = pair1;
+       ci->branch1 = branch1;
+       ci->branch2 = branch2;
+
+       ci->dst_entry1 = dst_entry1;
+       dst_entry1->rename_df_conflict_info = ci;
+       dst_entry1->processed = 0;
+
+       assert(!pair2 == !dst_entry2);
+       if (dst_entry2) {
+               ci->dst_entry2 = dst_entry2;
+               ci->pair2 = pair2;
+               dst_entry2->rename_df_conflict_info = ci;
+               dst_entry2->processed = 0;
+       }
+}
+
 static int show(struct merge_options *o, int v)
 {
        return (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;
@@ -179,7 +224,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 = get_porcelain_error_msgs();
+       setup_unpack_trees_porcelain(&opts, "merge");
 
        init_tree_desc_from_tree(t+0, common);
        init_tree_desc_from_tree(t+1, head);
@@ -301,6 +346,63 @@ static struct string_list *get_unmerged(void)
        return unmerged;
 }
 
+static void make_room_for_directories_of_df_conflicts(struct merge_options *o,
+                                                     struct string_list *entries)
+{
+       /* If there are D/F conflicts, and the paths currently exist
+        * in the working copy as a file, we want to remove them to
+        * make room for the corresponding directory.  Such paths will
+        * later be processed in process_df_entry() at the end.  If
+        * the corresponding directory ends up being removed by the
+        * merge, then the file will be reinstated at that time;
+        * otherwise, if the file is not supposed to be removed by the
+        * merge, the contents of the file will be placed in another
+        * unique filename.
+        *
+        * NOTE: This function relies on the fact that entries for a
+        * D/F conflict will appear adjacent in the index, with the
+        * entries for the file appearing before entries for paths
+        * below the corresponding directory.
+        */
+       const char *last_file = NULL;
+       int last_len = 0;
+       struct stage_data *last_e;
+       int i;
+
+       for (i = 0; i < entries->nr; i++) {
+               const char *path = entries->items[i].string;
+               int len = strlen(path);
+               struct stage_data *e = entries->items[i].util;
+
+               /*
+                * Check if last_file & path correspond to a D/F conflict;
+                * i.e. whether path is last_file+'/'+<something>.
+                * If so, remove last_file to make room for path and friends.
+                */
+               if (last_file &&
+                   len > last_len &&
+                   memcmp(path, last_file, last_len) == 0 &&
+                   path[last_len] == '/') {
+                       output(o, 3, "Removing %s to make room for subdirectory; may re-add later.", last_file);
+                       unlink(last_file);
+               }
+
+               /*
+                * Determine whether path could exist as a file in the
+                * working directory as a possible D/F conflict.  This
+                * will only occur when it exists in stage 2 as a
+                * file.
+                */
+               if (S_ISREG(e->stages[2].mode) || S_ISLNK(e->stages[2].mode)) {
+                       last_file = path;
+                       last_len = len;
+                       last_e = e;
+               } else {
+                       last_file = NULL;
+               }
+       }
+}
+
 struct rename
 {
        struct diff_filepair *pair;
@@ -333,6 +435,7 @@ static struct string_list *get_renames(struct merge_options *o,
        opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
                            o->diff_rename_limit >= 0 ? o->diff_rename_limit :
                            500;
+       opts.rename_score = o->rename_score;
        opts.warn_on_too_large_rename = 1;
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        if (diff_setup_done(&opts) < 0)
@@ -372,11 +475,10 @@ static struct string_list *get_renames(struct merge_options *o,
        return renames;
 }
 
-static int update_stages(const char *path, struct diff_filespec *o,
+static int update_stages_options(const char *path, struct diff_filespec *o,
                         struct diff_filespec *a, struct diff_filespec *b,
-                        int clear)
+                        int clear, int options)
 {
-       int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
        if (clear)
                if (remove_file_from_cache(path))
                        return -1;
@@ -392,6 +494,34 @@ static int update_stages(const char *path, struct diff_filespec *o,
        return 0;
 }
 
+static int update_stages(const char *path, struct diff_filespec *o,
+                        struct diff_filespec *a, struct diff_filespec *b,
+                        int clear)
+{
+       int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
+       return update_stages_options(path, o, a, b, clear, options);
+}
+
+static int update_stages_and_entry(const char *path,
+                                  struct stage_data *entry,
+                                  struct diff_filespec *o,
+                                  struct diff_filespec *a,
+                                  struct diff_filespec *b,
+                                  int clear)
+{
+       int options;
+
+       entry->processed = 0;
+       entry->stages[1].mode = o->mode;
+       entry->stages[2].mode = a->mode;
+       entry->stages[3].mode = b->mode;
+       hashcpy(entry->stages[1].sha, o->sha1);
+       hashcpy(entry->stages[2].sha, a->sha1);
+       hashcpy(entry->stages[3].sha, b->sha1);
+       options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;
+       return update_stages_options(path, o, a, b, clear, options);
+}
+
 static int remove_file(struct merge_options *o, int clean,
                       const char *path, int no_wd)
 {
@@ -519,13 +649,15 @@ static void update_file_flags(struct merge_options *o,
                void *buf;
                unsigned long size;
 
-               if (S_ISGITLINK(mode))
+               if (S_ISGITLINK(mode)) {
                        /*
                         * We may later decide to recursively descend into
                         * the submodule directory and update its index
                         * and/or work tree, but we do not do that now.
                         */
+                       update_wd = 0;
                        goto update_index;
+               }
 
                buf = read_sha1_file(sha, &type, &size);
                if (!buf)
@@ -602,22 +734,26 @@ static int merge_3way(struct merge_options *o,
                      const char *branch2)
 {
        mmfile_t orig, src1, src2;
+       struct ll_merge_options ll_opts = {0};
        char *base_name, *name1, *name2;
        int merge_status;
-       int favor;
 
-       if (o->call_depth)
-               favor = 0;
-       else {
+       ll_opts.renormalize = o->renormalize;
+       ll_opts.xdl_opts = o->xdl_opts;
+
+       if (o->call_depth) {
+               ll_opts.virtual_ancestor = 1;
+               ll_opts.variant = 0;
+       } else {
                switch (o->recursive_variant) {
                case MERGE_RECURSIVE_OURS:
-                       favor = XDL_MERGE_FAVOR_OURS;
+                       ll_opts.variant = XDL_MERGE_FAVOR_OURS;
                        break;
                case MERGE_RECURSIVE_THEIRS:
-                       favor = XDL_MERGE_FAVOR_THEIRS;
+                       ll_opts.variant = XDL_MERGE_FAVOR_THEIRS;
                        break;
                default:
-                       favor = 0;
+                       ll_opts.variant = 0;
                        break;
                }
        }
@@ -640,8 +776,7 @@ static int merge_3way(struct merge_options *o,
        read_mmblob(&src2, b->sha1);
 
        merge_status = ll_merge(result_buf, a->path, &orig, base_name,
-                               &src1, name1, &src2, name2,
-                               (!!o->call_depth) | (favor << 1));
+                               &src1, name1, &src2, name2, &ll_opts);
 
        free(name1);
        free(name2);
@@ -710,8 +845,8 @@ static struct merge_file_info merge_file(struct merge_options *o,
                        free(result_buf.ptr);
                        result.clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
-                       result.clean = 0;
-                       hashcpy(result.sha, a->sha1);
+                       result.clean = merge_submodule(result.sha, one->path, one->sha1,
+                                                      a->sha1, b->sha1);
                } else if (S_ISLNK(a->mode)) {
                        hashcpy(result.sha, a->sha1);
 
@@ -725,29 +860,56 @@ static struct merge_file_info merge_file(struct merge_options *o,
        return result;
 }
 
-static void conflict_rename_rename(struct merge_options *o,
-                                  struct rename *ren1,
-                                  const char *branch1,
-                                  struct rename *ren2,
-                                  const char *branch2)
+static void conflict_rename_delete(struct merge_options *o,
+                                  struct diff_filepair *pair,
+                                  const char *rename_branch,
+                                  const char *other_branch)
+{
+       char *dest_name = pair->two->path;
+       int df_conflict = 0;
+       struct stat st;
+
+       output(o, 1, "CONFLICT (rename/delete): Rename %s->%s in %s "
+              "and deleted in %s",
+              pair->one->path, pair->two->path, rename_branch,
+              other_branch);
+       if (!o->call_depth)
+               update_stages(dest_name, NULL,
+                             rename_branch == o->branch1 ? pair->two : NULL,
+                             rename_branch == o->branch1 ? NULL : pair->two,
+                             1);
+       if (lstat(dest_name, &st) == 0 && S_ISDIR(st.st_mode)) {
+               dest_name = unique_path(o, dest_name, rename_branch);
+               df_conflict = 1;
+       }
+       update_file(o, 0, pair->two->sha1, pair->two->mode, dest_name);
+       if (df_conflict)
+               free(dest_name);
+}
+
+static void conflict_rename_rename_1to2(struct merge_options *o,
+                                       struct diff_filepair *pair1,
+                                       const char *branch1,
+                                       struct diff_filepair *pair2,
+                                       const char *branch2)
 {
+       /* One file was renamed in both branches, but to different names. */
        char *del[2];
        int delp = 0;
-       const char *ren1_dst = ren1->pair->two->path;
-       const char *ren2_dst = ren2->pair->two->path;
+       const char *ren1_dst = pair1->two->path;
+       const char *ren2_dst = pair2->two->path;
        const char *dst_name1 = ren1_dst;
        const char *dst_name2 = ren2_dst;
-       if (string_list_has_string(&o->current_directory_set, ren1_dst)) {
+       struct stat st;
+       if (lstat(ren1_dst, &st) == 0 && S_ISDIR(st.st_mode)) {
                dst_name1 = del[delp++] = unique_path(o, ren1_dst, branch1);
                output(o, 1, "%s is a directory in %s adding as %s instead",
                       ren1_dst, branch2, dst_name1);
-               remove_file(o, 0, ren1_dst, 0);
        }
-       if (string_list_has_string(&o->current_directory_set, ren2_dst)) {
+       if (lstat(ren2_dst, &st) == 0 && S_ISDIR(st.st_mode)) {
                dst_name2 = del[delp++] = unique_path(o, ren2_dst, branch2);
                output(o, 1, "%s is a directory in %s adding as %s instead",
                       ren2_dst, branch1, dst_name2);
-               remove_file(o, 0, ren2_dst, 0);
        }
        if (o->call_depth) {
                remove_file_from_cache(dst_name1);
@@ -755,34 +917,27 @@ static void conflict_rename_rename(struct merge_options *o,
                /*
                 * Uncomment to leave the conflicting names in the resulting tree
                 *
-                * update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_name1);
-                * update_file(o, 0, ren2->pair->two->sha1, ren2->pair->two->mode, dst_name2);
+                * update_file(o, 0, pair1->two->sha1, pair1->two->mode, dst_name1);
+                * update_file(o, 0, pair2->two->sha1, pair2->two->mode, dst_name2);
                 */
        } else {
-               update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
-               update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
+               update_stages(ren1_dst, NULL, pair1->two, NULL, 1);
+               update_stages(ren2_dst, NULL, NULL, pair2->two, 1);
+
+               update_file(o, 0, pair1->two->sha1, pair1->two->mode, dst_name1);
+               update_file(o, 0, pair2->two->sha1, pair2->two->mode, dst_name2);
        }
        while (delp--)
                free(del[delp]);
 }
 
-static void conflict_rename_dir(struct merge_options *o,
-                               struct rename *ren1,
-                               const char *branch1)
-{
-       char *new_path = unique_path(o, ren1->pair->two->path, branch1);
-       output(o, 1, "Renaming %s to %s instead", ren1->pair->one->path, new_path);
-       remove_file(o, 0, ren1->pair->two->path, 0);
-       update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path);
-       free(new_path);
-}
-
-static void conflict_rename_rename_2(struct merge_options *o,
-                                    struct rename *ren1,
-                                    const char *branch1,
-                                    struct rename *ren2,
-                                    const char *branch2)
+static void conflict_rename_rename_2to1(struct merge_options *o,
+                                       struct rename *ren1,
+                                       const char *branch1,
+                                       struct rename *ren2,
+                                       const char *branch2)
 {
+       /* Two files were renamed to the same thing. */
        char *new_path1 = unique_path(o, ren1->pair->two->path, branch1);
        char *new_path2 = unique_path(o, ren2->pair->two->path, branch2);
        output(o, 1, "Renaming %s to %s and %s to %s instead",
@@ -872,84 +1027,66 @@ static int process_renames(struct merge_options *o,
                        ren2->dst_entry->processed = 1;
                        ren2->processed = 1;
                        if (strcmp(ren1_dst, ren2_dst) != 0) {
-                               clean_merge = 0;
-                               output(o, 1, "CONFLICT (rename/rename): "
-                                      "Rename \"%s\"->\"%s\" in branch \"%s\" "
-                                      "rename \"%s\"->\"%s\" in \"%s\"%s",
-                                      src, ren1_dst, branch1,
-                                      src, ren2_dst, branch2,
-                                      o->call_depth ? " (left unresolved)": "");
-                               if (o->call_depth) {
-                                       remove_file_from_cache(src);
-                                       update_file(o, 0, ren1->pair->one->sha1,
-                                                   ren1->pair->one->mode, src);
-                               }
-                               conflict_rename_rename(o, ren1, branch1, ren2, branch2);
+                               setup_rename_df_conflict_info(RENAME_ONE_FILE_TO_TWO,
+                                                             ren1->pair,
+                                                             ren2->pair,
+                                                             branch1,
+                                                             branch2,
+                                                             ren1->dst_entry,
+                                                             ren2->dst_entry);
                        } else {
-                               struct merge_file_info mfi;
                                remove_file(o, 1, ren1_src, 1);
-                               mfi = merge_file(o,
-                                                ren1->pair->one,
-                                                ren1->pair->two,
-                                                ren2->pair->two,
-                                                branch1,
-                                                branch2);
-                               if (mfi.merge || !mfi.clean)
-                                       output(o, 1, "Renaming %s->%s", src, ren1_dst);
-
-                               if (mfi.merge)
-                                       output(o, 2, "Auto-merging %s", ren1_dst);
-
-                               if (!mfi.clean) {
-                                       output(o, 1, "CONFLICT (content): merge conflict in %s",
-                                              ren1_dst);
-                                       clean_merge = 0;
-
-                                       if (!o->call_depth)
-                                               update_stages(ren1_dst,
-                                                             ren1->pair->one,
-                                                             ren1->pair->two,
-                                                             ren2->pair->two,
-                                                             1 /* clear */);
-                               }
-                               update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+                               update_stages_and_entry(ren1_dst,
+                                                       ren1->dst_entry,
+                                                       ren1->pair->one,
+                                                       ren1->pair->two,
+                                                       ren2->pair->two,
+                                                       1 /* clear */);
                        }
                } else {
                        /* Renamed in 1, maybe changed in 2 */
                        struct string_list_item *item;
                        /* we only use sha1 and mode of these */
                        struct diff_filespec src_other, dst_other;
-                       int try_merge, stage = a_renames == renames1 ? 3: 2;
+                       int try_merge;
 
-                       remove_file(o, 1, ren1_src, o->call_depth || stage == 3);
+                       /*
+                        * unpack_trees loads entries from common-commit
+                        * into stage 1, from head-commit into stage 2, and
+                        * from merge-commit into stage 3.  We keep track
+                        * of which side corresponds to the rename.
+                        */
+                       int renamed_stage = a_renames == renames1 ? 2 : 3;
+                       int other_stage =   a_renames == renames1 ? 3 : 2;
 
-                       hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha);
-                       src_other.mode = ren1->src_entry->stages[stage].mode;
-                       hashcpy(dst_other.sha1, ren1->dst_entry->stages[stage].sha);
-                       dst_other.mode = ren1->dst_entry->stages[stage].mode;
+                       remove_file(o, 1, ren1_src, o->call_depth || renamed_stage == 2);
 
+                       hashcpy(src_other.sha1, ren1->src_entry->stages[other_stage].sha);
+                       src_other.mode = ren1->src_entry->stages[other_stage].mode;
+                       hashcpy(dst_other.sha1, ren1->dst_entry->stages[other_stage].sha);
+                       dst_other.mode = ren1->dst_entry->stages[other_stage].mode;
                        try_merge = 0;
 
-                       if (string_list_has_string(&o->current_directory_set, ren1_dst)) {
-                               clean_merge = 0;
-                               output(o, 1, "CONFLICT (rename/directory): Rename %s->%s in %s "
-                                      " directory %s added in %s",
-                                      ren1_src, ren1_dst, branch1,
-                                      ren1_dst, branch2);
-                               conflict_rename_dir(o, ren1, branch1);
-                       } else if (sha_eq(src_other.sha1, null_sha1)) {
-                               clean_merge = 0;
-                               output(o, 1, "CONFLICT (rename/delete): Rename %s->%s in %s "
-                                      "and deleted in %s",
-                                      ren1_src, ren1_dst, branch1,
-                                      branch2);
-                               update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
-                               if (!o->call_depth)
-                                       update_stages(ren1_dst, NULL,
-                                                       branch1 == o->branch1 ?
-                                                       ren1->pair->two : NULL,
-                                                       branch1 == o->branch1 ?
-                                                       NULL : ren1->pair->two, 1);
+                       if (sha_eq(src_other.sha1, null_sha1)) {
+                               if (string_list_has_string(&o->current_directory_set, ren1_dst)) {
+                                       ren1->dst_entry->processed = 0;
+                                       setup_rename_df_conflict_info(RENAME_DELETE,
+                                                                     ren1->pair,
+                                                                     NULL,
+                                                                     branch1,
+                                                                     branch2,
+                                                                     ren1->dst_entry,
+                                                                     NULL);
+                               } else {
+                                       clean_merge = 0;
+                                       conflict_rename_delete(o, ren1->pair, branch1, branch2);
+                               }
+                       } else if ((dst_other.mode == ren1->pair->two->mode) &&
+                                  sha_eq(dst_other.sha1, ren1->pair->two->sha1)) {
+                               /* Added file on the other side
+                                  identical to the file being
+                                  renamed: clean merge */
+                               update_file(o, 1, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
                        } else if (!sha_eq(dst_other.sha1, null_sha1)) {
                                const char *new_path;
                                clean_merge = 0;
@@ -978,6 +1115,7 @@ static int process_renames(struct merge_options *o,
                                                    mfi.sha,
                                                    mfi.mode,
                                                    ren1_dst);
+                                       try_merge = 0;
                                } else {
                                        new_path = unique_path(o, ren1_dst, branch2);
                                        output(o, 1, "Adding as %s instead", new_path);
@@ -992,13 +1130,12 @@ static int process_renames(struct merge_options *o,
                                       "Rename %s->%s in %s",
                                       ren1_src, ren1_dst, branch1,
                                       ren2->pair->one->path, ren2->pair->two->path, branch2);
-                               conflict_rename_rename_2(o, ren1, branch1, ren2, branch2);
+                               conflict_rename_rename_2to1(o, ren1, branch1, ren2, branch2);
                        } else
                                try_merge = 1;
 
                        if (try_merge) {
                                struct diff_filespec *one, *a, *b;
-                               struct merge_file_info mfi;
                                src_other.path = (char *)ren1_src;
 
                                one = ren1->pair->one;
@@ -1009,33 +1146,15 @@ static int process_renames(struct merge_options *o,
                                        b = ren1->pair->two;
                                        a = &src_other;
                                }
-                               mfi = merge_file(o, one, a, b,
-                                               o->branch1, o->branch2);
-
-                               if (mfi.clean &&
-                                   sha_eq(mfi.sha, ren1->pair->two->sha1) &&
-                                   mfi.mode == ren1->pair->two->mode)
-                                       /*
-                                        * This messaged is part of
-                                        * t6022 test. If you change
-                                        * it update the test too.
-                                        */
-                                       output(o, 3, "Skipped %s (merged same as existing)", ren1_dst);
-                               else {
-                                       if (mfi.merge || !mfi.clean)
-                                               output(o, 1, "Renaming %s => %s", ren1_src, ren1_dst);
-                                       if (mfi.merge)
-                                               output(o, 2, "Auto-merging %s", ren1_dst);
-                                       if (!mfi.clean) {
-                                               output(o, 1, "CONFLICT (rename/modify): Merge conflict in %s",
-                                                      ren1_dst);
-                                               clean_merge = 0;
-
-                                               if (!o->call_depth)
-                                                       update_stages(ren1_dst,
-                                                                     one, a, b, 1);
-                                       }
-                                       update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+                               update_stages_and_entry(ren1_dst, ren1->dst_entry, one, a, b, 1);
+                               if (string_list_has_string(&o->current_directory_set, ren1_dst)) {
+                                       setup_rename_df_conflict_info(RENAME_NORMAL,
+                                                                     ren1->pair,
+                                                                     NULL,
+                                                                     branch1,
+                                                                     NULL,
+                                                                     ren1->dst_entry,
+                                                                     NULL);
                                }
                        }
                }
@@ -1051,6 +1170,137 @@ static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)
        return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
 }
 
+static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)
+{
+       void *buf;
+       enum object_type type;
+       unsigned long size;
+       buf = read_sha1_file(sha1, &type, &size);
+       if (!buf)
+               return error("cannot read object %s", sha1_to_hex(sha1));
+       if (type != OBJ_BLOB) {
+               free(buf);
+               return error("object %s is not a blob", sha1_to_hex(sha1));
+       }
+       strbuf_attach(dst, buf, size, size + 1);
+       return 0;
+}
+
+static int blob_unchanged(const unsigned char *o_sha,
+                         const unsigned char *a_sha,
+                         int renormalize, const char *path)
+{
+       struct strbuf o = STRBUF_INIT;
+       struct strbuf a = STRBUF_INIT;
+       int ret = 0; /* assume changed for safety */
+
+       if (sha_eq(o_sha, a_sha))
+               return 1;
+       if (!renormalize)
+               return 0;
+
+       assert(o_sha && a_sha);
+       if (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a))
+               goto error_return;
+       /*
+        * Note: binary | is used so that both renormalizations are
+        * performed.  Comparison can be skipped if both files are
+        * unchanged since their sha1s have already been compared.
+        */
+       if (renormalize_buffer(path, o.buf, o.len, &o) |
+           renormalize_buffer(path, a.buf, o.len, &a))
+               ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
+
+error_return:
+       strbuf_release(&o);
+       strbuf_release(&a);
+       return ret;
+}
+
+static void handle_delete_modify(struct merge_options *o,
+                                const char *path,
+                                const char *new_path,
+                                unsigned char *a_sha, int a_mode,
+                                unsigned char *b_sha, int b_mode)
+{
+       if (!a_sha) {
+               output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
+                      "and modified in %s. Version %s of %s left in tree%s%s.",
+                      path, o->branch1,
+                      o->branch2, o->branch2, path,
+                      path == new_path ? "" : " at ",
+                      path == new_path ? "" : new_path);
+               update_file(o, 0, b_sha, b_mode, new_path);
+       } else {
+               output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
+                      "and modified in %s. Version %s of %s left in tree%s%s.",
+                      path, o->branch2,
+                      o->branch1, o->branch1, path,
+                      path == new_path ? "" : " at ",
+                      path == new_path ? "" : new_path);
+               update_file(o, 0, a_sha, a_mode, new_path);
+       }
+}
+
+static int merge_content(struct merge_options *o,
+                        const char *path,
+                        unsigned char *o_sha, int o_mode,
+                        unsigned char *a_sha, int a_mode,
+                        unsigned char *b_sha, int b_mode,
+                        const char *df_rename_conflict_branch)
+{
+       const char *reason = "content";
+       struct merge_file_info mfi;
+       struct diff_filespec one, a, b;
+       struct stat st;
+       unsigned df_conflict_remains = 0;
+
+       if (!o_sha) {
+               reason = "add/add";
+               o_sha = (unsigned char *)null_sha1;
+       }
+       one.path = a.path = b.path = (char *)path;
+       hashcpy(one.sha1, o_sha);
+       one.mode = o_mode;
+       hashcpy(a.sha1, a_sha);
+       a.mode = a_mode;
+       hashcpy(b.sha1, b_sha);
+       b.mode = b_mode;
+
+       mfi = merge_file(o, &one, &a, &b, o->branch1, o->branch2);
+       if (df_rename_conflict_branch &&
+           lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
+               df_conflict_remains = 1;
+       }
+
+       if (mfi.clean && !df_conflict_remains &&
+           sha_eq(mfi.sha, a_sha) && mfi.mode == a.mode)
+               output(o, 3, "Skipped %s (merged same as existing)", path);
+       else
+               output(o, 2, "Auto-merging %s", path);
+
+       if (!mfi.clean) {
+               if (S_ISGITLINK(mfi.mode))
+                       reason = "submodule";
+               output(o, 1, "CONFLICT (%s): Merge conflict in %s",
+                               reason, path);
+       }
+
+       if (df_conflict_remains) {
+               const char *new_path;
+               update_file_flags(o, mfi.sha, mfi.mode, path,
+                                 o->call_depth || mfi.clean, 0);
+               new_path = unique_path(o, path, df_rename_conflict_branch);
+               mfi.clean = 0;
+               output(o, 1, "Adding as %s instead", new_path);
+               update_file_flags(o, mfi.sha, mfi.mode, new_path, 0, 1);
+       } else {
+               update_file(o, mfi.clean, mfi.sha, mfi.mode, path);
+       }
+       return mfi.clean;
+
+}
+
 /* Per entry merge function */
 static int process_entry(struct merge_options *o,
                         const char *path, struct stage_data *entry)
@@ -1060,6 +1310,7 @@ static int process_entry(struct merge_options *o,
        print_index_entry("\tpath: ", entry);
        */
        int clean_merge = 1;
+       int normalize = o->renormalize;
        unsigned o_mode = entry->stages[1].mode;
        unsigned a_mode = entry->stages[2].mode;
        unsigned b_mode = entry->stages[3].mode;
@@ -1067,65 +1318,49 @@ static int process_entry(struct merge_options *o,
        unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
        unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
 
+       if (entry->rename_df_conflict_info)
+               return 1; /* Such cases are handled elsewhere. */
+
+       entry->processed = 1;
        if (o_sha && (!a_sha || !b_sha)) {
                /* Case A: Deleted in one */
                if ((!a_sha && !b_sha) ||
-                   (sha_eq(a_sha, o_sha) && !b_sha) ||
-                   (!a_sha && sha_eq(b_sha, o_sha))) {
+                   (!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) ||
+                   (!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) {
                        /* Deleted in both or deleted in one and
                         * unchanged in the other */
                        if (a_sha)
                                output(o, 2, "Removing %s", path);
                        /* do not touch working file if it did not exist */
                        remove_file(o, 1, path, !a_sha);
+               } else if (string_list_has_string(&o->current_directory_set,
+                                                 path)) {
+                       entry->processed = 0;
+                       return 1; /* Assume clean until processed */
                } else {
                        /* Deleted in one and changed in the other */
                        clean_merge = 0;
-                       if (!a_sha) {
-                               output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
-                                      "and modified in %s. Version %s of %s left in tree.",
-                                      path, o->branch1,
-                                      o->branch2, o->branch2, path);
-                               update_file(o, 0, b_sha, b_mode, path);
-                       } else {
-                               output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
-                                      "and modified in %s. Version %s of %s left in tree.",
-                                      path, o->branch2,
-                                      o->branch1, o->branch1, path);
-                               update_file(o, 0, a_sha, a_mode, path);
-                       }
+                       handle_delete_modify(o, path, path,
+                                            a_sha, a_mode, b_sha, b_mode);
                }
 
        } else if ((!o_sha && a_sha && !b_sha) ||
                   (!o_sha && !a_sha && b_sha)) {
                /* Case B: Added in one. */
-               const char *add_branch;
-               const char *other_branch;
                unsigned mode;
                const unsigned char *sha;
-               const char *conf;
 
                if (a_sha) {
-                       add_branch = o->branch1;
-                       other_branch = o->branch2;
                        mode = a_mode;
                        sha = a_sha;
-                       conf = "file/directory";
                } else {
-                       add_branch = o->branch2;
-                       other_branch = o->branch1;
                        mode = b_mode;
                        sha = b_sha;
-                       conf = "directory/file";
                }
                if (string_list_has_string(&o->current_directory_set, path)) {
-                       const char *new_path = unique_path(o, path, add_branch);
-                       clean_merge = 0;
-                       output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. "
-                              "Adding %s as %s",
-                              conf, path, other_branch, path, new_path);
-                       remove_file(o, 0, path, 0);
-                       update_file(o, 0, sha, mode, new_path);
+                       /* Handle D->F conflicts after all subfiles */
+                       entry->processed = 0;
+                       return 1; /* Assume clean until processed */
                } else {
                        output(o, 2, "Adding %s", path);
                        update_file(o, 1, sha, mode, path);
@@ -1133,34 +1368,9 @@ static int process_entry(struct merge_options *o,
        } else if (a_sha && b_sha) {
                /* Case C: Added in both (check for same permissions) and */
                /* case D: Modified in both, but differently. */
-               const char *reason = "content";
-               struct merge_file_info mfi;
-               struct diff_filespec one, a, b;
-
-               if (!o_sha) {
-                       reason = "add/add";
-                       o_sha = (unsigned char *)null_sha1;
-               }
-               output(o, 2, "Auto-merging %s", path);
-               one.path = a.path = b.path = (char *)path;
-               hashcpy(one.sha1, o_sha);
-               one.mode = o_mode;
-               hashcpy(a.sha1, a_sha);
-               a.mode = a_mode;
-               hashcpy(b.sha1, b_sha);
-               b.mode = b_mode;
-
-               mfi = merge_file(o, &one, &a, &b,
-                                o->branch1, o->branch2);
-
-               clean_merge = mfi.clean;
-               if (!mfi.clean) {
-                       if (S_ISGITLINK(mfi.mode))
-                               reason = "submodule";
-                       output(o, 1, "CONFLICT (%s): Merge conflict in %s",
-                                       reason, path);
-               }
-               update_file(o, mfi.clean, mfi.sha, mfi.mode, path);
+               clean_merge = merge_content(o, path,
+                                           o_sha, o_mode, a_sha, a_mode, b_sha, b_mode,
+                                           NULL);
        } else if (!o_sha && !a_sha && !b_sha) {
                /*
                 * this entry was deleted altogether. a_mode == 0 means
@@ -1173,26 +1383,116 @@ static int process_entry(struct merge_options *o,
        return clean_merge;
 }
 
-struct unpack_trees_error_msgs get_porcelain_error_msgs(void)
+/*
+ * Per entry merge function for D/F (and/or rename) conflicts.  In the
+ * cases we can cleanly resolve D/F conflicts, process_entry() can
+ * clean out all the files below the directory for us.  All D/F
+ * conflict cases must be handled here at the end to make sure any
+ * directories that can be cleaned out, are.
+ *
+ * Some rename conflicts may also be handled here that don't necessarily
+ * involve D/F conflicts, since the code to handle them is generic enough
+ * to handle those rename conflicts with or without D/F conflicts also
+ * being involved.
+ */
+static int process_df_entry(struct merge_options *o,
+                           const char *path, struct stage_data *entry)
 {
-       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,
-       };
-       if (advice_commit_before_merge) {
-               msgs.would_overwrite = msgs.not_uptodate_file =
-                       "Your local changes to '%s' would be overwritten by merge.  Aborting.\n"
-                       "Please, commit your changes or stash them before you can merge.";
+       int clean_merge = 1;
+       unsigned o_mode = entry->stages[1].mode;
+       unsigned a_mode = entry->stages[2].mode;
+       unsigned b_mode = entry->stages[3].mode;
+       unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);
+       unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
+       unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
+       struct stat st;
+
+       entry->processed = 1;
+       if (entry->rename_df_conflict_info) {
+               struct rename_df_conflict_info *conflict_info = entry->rename_df_conflict_info;
+               char *src;
+               switch (conflict_info->rename_type) {
+               case RENAME_NORMAL:
+                       clean_merge = merge_content(o, path,
+                                                   o_sha, o_mode, a_sha, a_mode, b_sha, b_mode,
+                                                   conflict_info->branch1);
+                       break;
+               case RENAME_DELETE:
+                       clean_merge = 0;
+                       conflict_rename_delete(o, conflict_info->pair1,
+                                              conflict_info->branch1,
+                                              conflict_info->branch2);
+                       break;
+               case RENAME_ONE_FILE_TO_TWO:
+                       src = conflict_info->pair1->one->path;
+                       clean_merge = 0;
+                       output(o, 1, "CONFLICT (rename/rename): "
+                              "Rename \"%s\"->\"%s\" in branch \"%s\" "
+                              "rename \"%s\"->\"%s\" in \"%s\"%s",
+                              src, conflict_info->pair1->two->path, conflict_info->branch1,
+                              src, conflict_info->pair2->two->path, conflict_info->branch2,
+                              o->call_depth ? " (left unresolved)" : "");
+                       if (o->call_depth) {
+                               remove_file_from_cache(src);
+                               update_file(o, 0, conflict_info->pair1->one->sha1,
+                                           conflict_info->pair1->one->mode, src);
+                       }
+                       conflict_rename_rename_1to2(o, conflict_info->pair1,
+                                                   conflict_info->branch1,
+                                                   conflict_info->pair2,
+                                                   conflict_info->branch2);
+                       conflict_info->dst_entry2->processed = 1;
+                       break;
+               default:
+                       entry->processed = 0;
+                       break;
+               }
+       } else if (o_sha && (!a_sha || !b_sha)) {
+               /* Modify/delete; deleted side may have put a directory in the way */
+               const char *new_path = path;
+               if (lstat(path, &st) == 0 && S_ISDIR(st.st_mode))
+                       new_path = unique_path(o, path, a_sha ? o->branch1 : o->branch2);
+               clean_merge = 0;
+               handle_delete_modify(o, path, new_path,
+                                    a_sha, a_mode, b_sha, b_mode);
+       } else if (!o_sha && !!a_sha != !!b_sha) {
+               /* directory -> (directory, file) */
+               const char *add_branch;
+               const char *other_branch;
+               unsigned mode;
+               const unsigned char *sha;
+               const char *conf;
+
+               if (a_sha) {
+                       add_branch = o->branch1;
+                       other_branch = o->branch2;
+                       mode = a_mode;
+                       sha = a_sha;
+                       conf = "file/directory";
+               } else {
+                       add_branch = o->branch2;
+                       other_branch = o->branch1;
+                       mode = b_mode;
+                       sha = b_sha;
+                       conf = "directory/file";
+               }
+               if (lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
+                       const char *new_path = unique_path(o, path, add_branch);
+                       clean_merge = 0;
+                       output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. "
+                              "Adding %s as %s",
+                              conf, path, other_branch, path, new_path);
+                       update_file(o, 0, sha, mode, new_path);
+               } else {
+                       output(o, 2, "Adding %s", path);
+                       update_file(o, 1, sha, mode, path);
+               }
+       } else {
+               entry->processed = 0;
+               return 1; /* not handled; assume clean until processed */
        }
-       return msgs;
+
+       return clean_merge;
 }
 
 int merge_trees(struct merge_options *o,
@@ -1234,6 +1534,7 @@ int merge_trees(struct merge_options *o,
                get_files_dirs(o, merge);
 
                entries = get_unmerged();
+               make_room_for_directories_of_df_conflicts(o, entries);
                re_head  = get_renames(o, head, common, head, merge, entries);
                re_merge = get_renames(o, merge, common, head, merge, entries);
                clean = process_renames(o, re_head, re_merge);
@@ -1244,6 +1545,19 @@ int merge_trees(struct merge_options *o,
                                && !process_entry(o, path, e))
                                clean = 0;
                }
+               for (i = 0; i < entries->nr; i++) {
+                       const char *path = entries->items[i].string;
+                       struct stage_data *e = entries->items[i].util;
+                       if (!e->processed
+                               && !process_df_entry(o, path, e))
+                               clean = 0;
+               }
+               for (i = 0; i < entries->nr; i++) {
+                       struct stage_data *e = entries->items[i].util;
+                       if (!e->processed)
+                               die("Unprocessed path??? %s",
+                                   entries->items[i].string);
+               }
 
                string_list_clear(re_merge, 0);
                string_list_clear(re_head, 0);
@@ -1431,6 +1745,7 @@ void init_merge_options(struct merge_options *o)
        o->buffer_output = 1;
        o->diff_rename_limit = -1;
        o->merge_rename_limit = -1;
+       o->renormalize = 0;
        git_config(merge_recursive_config, o);
        if (getenv("GIT_MERGE_VERBOSITY"))
                o->verbosity =
@@ -1443,3 +1758,37 @@ void init_merge_options(struct merge_options *o)
        memset(&o->current_directory_set, 0, sizeof(struct string_list));
        o->current_directory_set.strdup_strings = 1;
 }
+
+int parse_merge_opt(struct merge_options *o, const char *s)
+{
+       if (!s || !*s)
+               return -1;
+       if (!strcmp(s, "ours"))
+               o->recursive_variant = MERGE_RECURSIVE_OURS;
+       else if (!strcmp(s, "theirs"))
+               o->recursive_variant = MERGE_RECURSIVE_THEIRS;
+       else if (!strcmp(s, "subtree"))
+               o->subtree_shift = "";
+       else if (!prefixcmp(s, "subtree="))
+               o->subtree_shift = s + strlen("subtree=");
+       else if (!strcmp(s, "patience"))
+               o->xdl_opts |= XDF_PATIENCE_DIFF;
+       else if (!strcmp(s, "ignore-space-change"))
+               o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
+       else if (!strcmp(s, "ignore-all-space"))
+               o->xdl_opts |= XDF_IGNORE_WHITESPACE;
+       else if (!strcmp(s, "ignore-space-at-eol"))
+               o->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL;
+       else if (!strcmp(s, "renormalize"))
+               o->renormalize = 1;
+       else if (!strcmp(s, "no-renormalize"))
+               o->renormalize = 0;
+       else if (!prefixcmp(s, "rename-threshold=")) {
+               const char *score = s + strlen("rename-threshold=");
+               if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0)
+                       return -1;
+       }
+       else
+               return -1;
+       return 0;
+}
index b831293b3865ae52584565039e4a893c3c6f45ff..981ed6ac94b5ed73f15a3b0dca723586374ca0b8 100644 (file)
@@ -14,18 +14,18 @@ struct merge_options {
        } recursive_variant;
        const char *subtree_shift;
        unsigned buffer_output : 1;
+       unsigned renormalize : 1;
+       long xdl_opts;
        int verbosity;
        int diff_rename_limit;
        int merge_rename_limit;
+       int rename_score;
        int call_depth;
        struct strbuf obuf;
        struct string_list current_file_set;
        struct string_list current_directory_set;
 };
 
-/* Return a list of user-friendly error messages to be used by merge */
-struct unpack_trees_error_msgs get_porcelain_error_msgs(void);
-
 /* merge_trees() but with recursive ancestor consolidation */
 int merge_recursive(struct merge_options *o,
                    struct commit *h1,
@@ -54,7 +54,11 @@ int merge_recursive_generic(struct merge_options *o,
 void init_merge_options(struct merge_options *o);
 struct tree *write_tree_from_memory(struct merge_options *o);
 
+int parse_merge_opt(struct merge_options *out, const char *s);
+
 /* builtin/merge.c */
-int try_merge_command(const char *strategy, struct commit_list *common, const char *head_arg, struct commit_list *remotes);
+int try_merge_command(const char *strategy, size_t xopts_nr,
+               const char **xopts, struct commit_list *common,
+               const char *head_arg, struct commit_list *remotes);
 
 #endif
index 0031d78e8c98a32d61cd0dc0f939a033e24ed890..c6b6a3fe4cd94e48893b172c17b6e7df3bfa36f8 100644 (file)
@@ -32,6 +32,42 @@ static unsigned int hash_name(const char *name, int namelen)
        return hash;
 }
 
+static void hash_index_entry_directories(struct index_state *istate, struct cache_entry *ce)
+{
+       /*
+        * Throw each directory component in the hash for quick lookup
+        * during a git status. Directory components are stored with their
+        * closing slash.  Despite submodules being a directory, they never
+        * reach this point, because they are stored without a closing slash
+        * in the cache.
+        *
+        * Note that the cache_entry stored with the directory does not
+        * represent the directory itself.  It is a pointer to an existing
+        * filename, and its only purpose is to represent existence of the
+        * directory in the cache.  It is very possible multiple directory
+        * hash entries may point to the same cache_entry.
+        */
+       unsigned int hash;
+       void **pos;
+
+       const char *ptr = ce->name;
+       while (*ptr) {
+               while (*ptr && *ptr != '/')
+                       ++ptr;
+               if (*ptr == '/') {
+                       ++ptr;
+                       hash = hash_name(ce->name, ptr - ce->name);
+                       if (!lookup_hash(hash, &istate->name_hash)) {
+                               pos = insert_hash(hash, ce, &istate->name_hash);
+                               if (pos) {
+                                       ce->next = *pos;
+                                       *pos = ce;
+                               }
+                       }
+               }
+       }
+}
+
 static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
 {
        void **pos;
@@ -47,6 +83,9 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
                ce->next = *pos;
                *pos = ce;
        }
+
+       if (ignore_case)
+               hash_index_entry_directories(istate, ce);
 }
 
 static void lazy_init_name_hash(struct index_state *istate)
@@ -97,7 +136,21 @@ static int same_name(const struct cache_entry *ce, const char *name, int namelen
        if (len == namelen && !cache_name_compare(name, namelen, ce->name, len))
                return 1;
 
-       return icase && slow_same_name(name, namelen, ce->name, len);
+       if (!icase)
+               return 0;
+
+       /*
+        * If the entry we're comparing is a filename (no trailing slash), then compare
+        * the lengths exactly.
+        */
+       if (name[namelen - 1] != '/')
+               return slow_same_name(name, namelen, ce->name, len);
+
+       /*
+        * For a directory, we point to an arbitrary cache_entry filename.  Just
+        * make sure the directory portion matches.
+        */
+       return slow_same_name(name, namelen, ce->name, namelen < len ? namelen : len);
 }
 
 struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
@@ -115,5 +168,22 @@ struct cache_entry *index_name_exists(struct index_state *istate, const char *na
                }
                ce = ce->next;
        }
+
+       /*
+        * Might be a submodule.  Despite submodules being directories,
+        * they are stored in the name hash without a closing slash.
+        * When ignore_case is 1, directories are stored in the name hash
+        * with their closing slash.
+        *
+        * The side effect of this storage technique is we have need to
+        * remove the slash from name and perform the lookup again without
+        * the slash.  If a match is made, S_ISGITLINK(ce->mode) will be
+        * true.
+        */
+       if (icase && name[namelen - 1] == '/') {
+               ce = index_name_exists(istate, name, namelen - 1, icase);
+               if (ce && S_ISGITLINK(ce->ce_mode))
+                       return ce;
+       }
        return NULL;
 }
index dee6d62e72703dfa68b4a3fb8ed069ccdc2a5004..4c8984ede1e218d3e0aaadb6d8c72bfcafddeee9 100644 (file)
@@ -89,6 +89,5 @@ int notes_cache_put(struct notes_cache *c, unsigned char key_sha1[20],
 
        if (write_sha1_file(data, size, "blob", value_sha1) < 0)
                return -1;
-       add_note(&c->tree, key_sha1, value_sha1, NULL);
-       return 0;
+       return add_note(&c->tree, key_sha1, value_sha1, NULL);
 }
diff --git a/notes-merge.c b/notes-merge.c
new file mode 100644 (file)
index 0000000..71c4d45
--- /dev/null
@@ -0,0 +1,737 @@
+#include "cache.h"
+#include "commit.h"
+#include "refs.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "xdiff-interface.h"
+#include "ll-merge.h"
+#include "dir.h"
+#include "notes.h"
+#include "notes-merge.h"
+#include "strbuf.h"
+
+struct notes_merge_pair {
+       unsigned char obj[20], base[20], local[20], remote[20];
+};
+
+void init_notes_merge_options(struct notes_merge_options *o)
+{
+       memset(o, 0, sizeof(struct notes_merge_options));
+       strbuf_init(&(o->commit_msg), 0);
+       o->verbosity = NOTES_MERGE_VERBOSITY_DEFAULT;
+}
+
+#define OUTPUT(o, v, ...) \
+       do { \
+               if ((o)->verbosity >= (v)) { \
+                       printf(__VA_ARGS__); \
+                       puts(""); \
+               } \
+       } while (0)
+
+static int path_to_sha1(const char *path, unsigned char *sha1)
+{
+       char hex_sha1[40];
+       int i = 0;
+       while (*path && i < 40) {
+               if (*path != '/')
+                       hex_sha1[i++] = *path;
+               path++;
+       }
+       if (*path || i != 40)
+               return -1;
+       return get_sha1_hex(hex_sha1, sha1);
+}
+
+static int verify_notes_filepair(struct diff_filepair *p, unsigned char *sha1)
+{
+       switch (p->status) {
+       case DIFF_STATUS_MODIFIED:
+               assert(p->one->mode == p->two->mode);
+               assert(!is_null_sha1(p->one->sha1));
+               assert(!is_null_sha1(p->two->sha1));
+               break;
+       case DIFF_STATUS_ADDED:
+               assert(is_null_sha1(p->one->sha1));
+               break;
+       case DIFF_STATUS_DELETED:
+               assert(is_null_sha1(p->two->sha1));
+               break;
+       default:
+               return -1;
+       }
+       assert(!strcmp(p->one->path, p->two->path));
+       return path_to_sha1(p->one->path, sha1);
+}
+
+static struct notes_merge_pair *find_notes_merge_pair_pos(
+               struct notes_merge_pair *list, int len, unsigned char *obj,
+               int insert_new, int *occupied)
+{
+       /*
+        * Both diff_tree_remote() and diff_tree_local() tend to process
+        * merge_pairs in ascending order. Therefore, cache last returned
+        * index, and search sequentially from there until the appropriate
+        * position is found.
+        *
+        * Since inserts only happen from diff_tree_remote() (which mainly
+        * _appends_), we don't care that inserting into the middle of the
+        * list is expensive (using memmove()).
+        */
+       static int last_index;
+       int i = last_index < len ? last_index : len - 1;
+       int prev_cmp = 0, cmp = -1;
+       while (i >= 0 && i < len) {
+               cmp = hashcmp(obj, list[i].obj);
+               if (!cmp) /* obj belongs @ i */
+                       break;
+               else if (cmp < 0 && prev_cmp <= 0) /* obj belongs < i */
+                       i--;
+               else if (cmp < 0) /* obj belongs between i-1 and i */
+                       break;
+               else if (cmp > 0 && prev_cmp >= 0) /* obj belongs > i */
+                       i++;
+               else /* if (cmp > 0) */ { /* obj belongs between i and i+1 */
+                       i++;
+                       break;
+               }
+               prev_cmp = cmp;
+       }
+       if (i < 0)
+               i = 0;
+       /* obj belongs at, or immediately preceding, index i (0 <= i <= len) */
+
+       if (!cmp)
+               *occupied = 1;
+       else {
+               *occupied = 0;
+               if (insert_new && i < len) {
+                       memmove(list + i + 1, list + i,
+                               (len - i) * sizeof(struct notes_merge_pair));
+                       memset(list + i, 0, sizeof(struct notes_merge_pair));
+               }
+       }
+       last_index = i;
+       return list + i;
+}
+
+static unsigned char uninitialized[20] =
+       "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+       "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
+
+static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
+                                                const unsigned char *base,
+                                                const unsigned char *remote,
+                                                int *num_changes)
+{
+       struct diff_options opt;
+       struct notes_merge_pair *changes;
+       int i, len = 0;
+
+       trace_printf("\tdiff_tree_remote(base = %.7s, remote = %.7s)\n",
+              sha1_to_hex(base), sha1_to_hex(remote));
+
+       diff_setup(&opt);
+       DIFF_OPT_SET(&opt, RECURSIVE);
+       opt.output_format = DIFF_FORMAT_NO_OUTPUT;
+       if (diff_setup_done(&opt) < 0)
+               die("diff_setup_done failed");
+       diff_tree_sha1(base, remote, "", &opt);
+       diffcore_std(&opt);
+
+       changes = xcalloc(diff_queued_diff.nr, sizeof(struct notes_merge_pair));
+
+       for (i = 0; i < diff_queued_diff.nr; i++) {
+               struct diff_filepair *p = diff_queued_diff.queue[i];
+               struct notes_merge_pair *mp;
+               int occupied;
+               unsigned char obj[20];
+
+               if (verify_notes_filepair(p, obj)) {
+                       trace_printf("\t\tCannot merge entry '%s' (%c): "
+                              "%.7s -> %.7s. Skipping!\n", p->one->path,
+                              p->status, sha1_to_hex(p->one->sha1),
+                              sha1_to_hex(p->two->sha1));
+                       continue;
+               }
+               mp = find_notes_merge_pair_pos(changes, len, obj, 1, &occupied);
+               if (occupied) {
+                       /* We've found an addition/deletion pair */
+                       assert(!hashcmp(mp->obj, obj));
+                       if (is_null_sha1(p->one->sha1)) { /* addition */
+                               assert(is_null_sha1(mp->remote));
+                               hashcpy(mp->remote, p->two->sha1);
+                       } else if (is_null_sha1(p->two->sha1)) { /* deletion */
+                               assert(is_null_sha1(mp->base));
+                               hashcpy(mp->base, p->one->sha1);
+                       } else
+                               assert(!"Invalid existing change recorded");
+               } else {
+                       hashcpy(mp->obj, obj);
+                       hashcpy(mp->base, p->one->sha1);
+                       hashcpy(mp->local, uninitialized);
+                       hashcpy(mp->remote, p->two->sha1);
+                       len++;
+               }
+               trace_printf("\t\tStored remote change for %s: %.7s -> %.7s\n",
+                      sha1_to_hex(mp->obj), sha1_to_hex(mp->base),
+                      sha1_to_hex(mp->remote));
+       }
+       diff_flush(&opt);
+       diff_tree_release_paths(&opt);
+
+       *num_changes = len;
+       return changes;
+}
+
+static void diff_tree_local(struct notes_merge_options *o,
+                           struct notes_merge_pair *changes, int len,
+                           const unsigned char *base,
+                           const unsigned char *local)
+{
+       struct diff_options opt;
+       int i;
+
+       trace_printf("\tdiff_tree_local(len = %i, base = %.7s, local = %.7s)\n",
+              len, sha1_to_hex(base), sha1_to_hex(local));
+
+       diff_setup(&opt);
+       DIFF_OPT_SET(&opt, RECURSIVE);
+       opt.output_format = DIFF_FORMAT_NO_OUTPUT;
+       if (diff_setup_done(&opt) < 0)
+               die("diff_setup_done failed");
+       diff_tree_sha1(base, local, "", &opt);
+       diffcore_std(&opt);
+
+       for (i = 0; i < diff_queued_diff.nr; i++) {
+               struct diff_filepair *p = diff_queued_diff.queue[i];
+               struct notes_merge_pair *mp;
+               int match;
+               unsigned char obj[20];
+
+               if (verify_notes_filepair(p, obj)) {
+                       trace_printf("\t\tCannot merge entry '%s' (%c): "
+                              "%.7s -> %.7s. Skipping!\n", p->one->path,
+                              p->status, sha1_to_hex(p->one->sha1),
+                              sha1_to_hex(p->two->sha1));
+                       continue;
+               }
+               mp = find_notes_merge_pair_pos(changes, len, obj, 0, &match);
+               if (!match) {
+                       trace_printf("\t\tIgnoring local-only change for %s: "
+                              "%.7s -> %.7s\n", sha1_to_hex(obj),
+                              sha1_to_hex(p->one->sha1),
+                              sha1_to_hex(p->two->sha1));
+                       continue;
+               }
+
+               assert(!hashcmp(mp->obj, obj));
+               if (is_null_sha1(p->two->sha1)) { /* deletion */
+                       /*
+                        * Either this is a true deletion (1), or it is part
+                        * of an A/D pair (2), or D/A pair (3):
+                        *
+                        * (1) mp->local is uninitialized; set it to null_sha1
+                        * (2) mp->local is not uninitialized; don't touch it
+                        * (3) mp->local is uninitialized; set it to null_sha1
+                        *     (will be overwritten by following addition)
+                        */
+                       if (!hashcmp(mp->local, uninitialized))
+                               hashclr(mp->local);
+               } else if (is_null_sha1(p->one->sha1)) { /* addition */
+                       /*
+                        * Either this is a true addition (1), or it is part
+                        * of an A/D pair (2), or D/A pair (3):
+                        *
+                        * (1) mp->local is uninitialized; set to p->two->sha1
+                        * (2) mp->local is uninitialized; set to p->two->sha1
+                        * (3) mp->local is null_sha1;     set to p->two->sha1
+                        */
+                       assert(is_null_sha1(mp->local) ||
+                              !hashcmp(mp->local, uninitialized));
+                       hashcpy(mp->local, p->two->sha1);
+               } else { /* modification */
+                       /*
+                        * This is a true modification. p->one->sha1 shall
+                        * match mp->base, and mp->local shall be uninitialized.
+                        * Set mp->local to p->two->sha1.
+                        */
+                       assert(!hashcmp(p->one->sha1, mp->base));
+                       assert(!hashcmp(mp->local, uninitialized));
+                       hashcpy(mp->local, p->two->sha1);
+               }
+               trace_printf("\t\tStored local change for %s: %.7s -> %.7s\n",
+                      sha1_to_hex(mp->obj), sha1_to_hex(mp->base),
+                      sha1_to_hex(mp->local));
+       }
+       diff_flush(&opt);
+       diff_tree_release_paths(&opt);
+}
+
+static void check_notes_merge_worktree(struct notes_merge_options *o)
+{
+       if (!o->has_worktree) {
+               /*
+                * Must establish NOTES_MERGE_WORKTREE.
+                * Abort if NOTES_MERGE_WORKTREE already exists
+                */
+               if (file_exists(git_path(NOTES_MERGE_WORKTREE))) {
+                       if (advice_resolve_conflict)
+                               die("You have not concluded your previous "
+                                   "notes merge (%s exists).\nPlease, use "
+                                   "'git notes merge --commit' or 'git notes "
+                                   "merge --abort' to commit/abort the "
+                                   "previous merge before you start a new "
+                                   "notes merge.", git_path("NOTES_MERGE_*"));
+                       else
+                               die("You have not concluded your notes merge "
+                                   "(%s exists).", git_path("NOTES_MERGE_*"));
+               }
+
+               if (safe_create_leading_directories(git_path(
+                               NOTES_MERGE_WORKTREE "/.test")))
+                       die_errno("unable to create directory %s",
+                                 git_path(NOTES_MERGE_WORKTREE));
+               o->has_worktree = 1;
+       } else if (!file_exists(git_path(NOTES_MERGE_WORKTREE)))
+               /* NOTES_MERGE_WORKTREE should already be established */
+               die("missing '%s'. This should not happen",
+                   git_path(NOTES_MERGE_WORKTREE));
+}
+
+static void write_buf_to_worktree(const unsigned char *obj,
+                                 const char *buf, unsigned long size)
+{
+       int fd;
+       char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
+       if (safe_create_leading_directories(path))
+               die_errno("unable to create directory for '%s'", path);
+       if (file_exists(path))
+               die("found existing file at '%s'", path);
+
+       fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0666);
+       if (fd < 0)
+               die_errno("failed to open '%s'", path);
+
+       while (size > 0) {
+               long ret = write_in_full(fd, buf, size);
+               if (ret < 0) {
+                       /* Ignore epipe */
+                       if (errno == EPIPE)
+                               break;
+                       die_errno("notes-merge");
+               } else if (!ret) {
+                       die("notes-merge: disk full?");
+               }
+               size -= ret;
+               buf += ret;
+       }
+
+       close(fd);
+}
+
+static void write_note_to_worktree(const unsigned char *obj,
+                                  const unsigned char *note)
+{
+       enum object_type type;
+       unsigned long size;
+       void *buf = read_sha1_file(note, &type, &size);
+
+       if (!buf)
+               die("cannot read note %s for object %s",
+                   sha1_to_hex(note), sha1_to_hex(obj));
+       if (type != OBJ_BLOB)
+               die("blob expected in note %s for object %s",
+                   sha1_to_hex(note), sha1_to_hex(obj));
+       write_buf_to_worktree(obj, buf, size);
+       free(buf);
+}
+
+static int ll_merge_in_worktree(struct notes_merge_options *o,
+                               struct notes_merge_pair *p)
+{
+       mmbuffer_t result_buf;
+       mmfile_t base, local, remote;
+       int status;
+
+       read_mmblob(&base, p->base);
+       read_mmblob(&local, p->local);
+       read_mmblob(&remote, p->remote);
+
+       status = ll_merge(&result_buf, sha1_to_hex(p->obj), &base, NULL,
+                         &local, o->local_ref, &remote, o->remote_ref, 0);
+
+       free(base.ptr);
+       free(local.ptr);
+       free(remote.ptr);
+
+       if ((status < 0) || !result_buf.ptr)
+               die("Failed to execute internal merge");
+
+       write_buf_to_worktree(p->obj, result_buf.ptr, result_buf.size);
+       free(result_buf.ptr);
+
+       return status;
+}
+
+static int merge_one_change_manual(struct notes_merge_options *o,
+                                  struct notes_merge_pair *p,
+                                  struct notes_tree *t)
+{
+       const char *lref = o->local_ref ? o->local_ref : "local version";
+       const char *rref = o->remote_ref ? o->remote_ref : "remote version";
+
+       trace_printf("\t\t\tmerge_one_change_manual(obj = %.7s, base = %.7s, "
+              "local = %.7s, remote = %.7s)\n",
+              sha1_to_hex(p->obj), sha1_to_hex(p->base),
+              sha1_to_hex(p->local), sha1_to_hex(p->remote));
+
+       /* add "Conflicts:" section to commit message first time through */
+       if (!o->has_worktree)
+               strbuf_addstr(&(o->commit_msg), "\n\nConflicts:\n");
+
+       strbuf_addf(&(o->commit_msg), "\t%s\n", sha1_to_hex(p->obj));
+
+       OUTPUT(o, 2, "Auto-merging notes for %s", sha1_to_hex(p->obj));
+       check_notes_merge_worktree(o);
+       if (is_null_sha1(p->local)) {
+               /* D/F conflict, checkout p->remote */
+               assert(!is_null_sha1(p->remote));
+               OUTPUT(o, 1, "CONFLICT (delete/modify): Notes for object %s "
+                      "deleted in %s and modified in %s. Version from %s "
+                      "left in tree.", sha1_to_hex(p->obj), lref, rref, rref);
+               write_note_to_worktree(p->obj, p->remote);
+       } else if (is_null_sha1(p->remote)) {
+               /* D/F conflict, checkout p->local */
+               assert(!is_null_sha1(p->local));
+               OUTPUT(o, 1, "CONFLICT (delete/modify): Notes for object %s "
+                      "deleted in %s and modified in %s. Version from %s "
+                      "left in tree.", sha1_to_hex(p->obj), rref, lref, lref);
+               write_note_to_worktree(p->obj, p->local);
+       } else {
+               /* "regular" conflict, checkout result of ll_merge() */
+               const char *reason = "content";
+               if (is_null_sha1(p->base))
+                       reason = "add/add";
+               assert(!is_null_sha1(p->local));
+               assert(!is_null_sha1(p->remote));
+               OUTPUT(o, 1, "CONFLICT (%s): Merge conflict in notes for "
+                      "object %s", reason, sha1_to_hex(p->obj));
+               ll_merge_in_worktree(o, p);
+       }
+
+       trace_printf("\t\t\tremoving from partial merge result\n");
+       remove_note(t, p->obj);
+
+       return 1;
+}
+
+static int merge_one_change(struct notes_merge_options *o,
+                           struct notes_merge_pair *p, struct notes_tree *t)
+{
+       /*
+        * Return 0 if change is successfully resolved (stored in notes_tree).
+        * Return 1 is change results in a conflict (NOT stored in notes_tree,
+        * but instead written to NOTES_MERGE_WORKTREE with conflict markers).
+        */
+       switch (o->strategy) {
+       case NOTES_MERGE_RESOLVE_MANUAL:
+               return merge_one_change_manual(o, p, t);
+       case NOTES_MERGE_RESOLVE_OURS:
+               OUTPUT(o, 2, "Using local notes for %s", sha1_to_hex(p->obj));
+               /* nothing to do */
+               return 0;
+       case NOTES_MERGE_RESOLVE_THEIRS:
+               OUTPUT(o, 2, "Using remote notes for %s", sha1_to_hex(p->obj));
+               if (add_note(t, p->obj, p->remote, combine_notes_overwrite))
+                       die("BUG: combine_notes_overwrite failed");
+               return 0;
+       case NOTES_MERGE_RESOLVE_UNION:
+               OUTPUT(o, 2, "Concatenating local and remote notes for %s",
+                      sha1_to_hex(p->obj));
+               if (add_note(t, p->obj, p->remote, combine_notes_concatenate))
+                       die("failed to concatenate notes "
+                           "(combine_notes_concatenate)");
+               return 0;
+       case NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ:
+               OUTPUT(o, 2, "Concatenating unique lines in local and remote "
+                      "notes for %s", sha1_to_hex(p->obj));
+               if (add_note(t, p->obj, p->remote, combine_notes_cat_sort_uniq))
+                       die("failed to concatenate notes "
+                           "(combine_notes_cat_sort_uniq)");
+               return 0;
+       }
+       die("Unknown strategy (%i).", o->strategy);
+}
+
+static int merge_changes(struct notes_merge_options *o,
+                        struct notes_merge_pair *changes, int *num_changes,
+                        struct notes_tree *t)
+{
+       int i, conflicts = 0;
+
+       trace_printf("\tmerge_changes(num_changes = %i)\n", *num_changes);
+       for (i = 0; i < *num_changes; i++) {
+               struct notes_merge_pair *p = changes + i;
+               trace_printf("\t\t%.7s: %.7s -> %.7s/%.7s\n",
+                      sha1_to_hex(p->obj), sha1_to_hex(p->base),
+                      sha1_to_hex(p->local), sha1_to_hex(p->remote));
+
+               if (!hashcmp(p->base, p->remote)) {
+                       /* no remote change; nothing to do */
+                       trace_printf("\t\t\tskipping (no remote change)\n");
+               } else if (!hashcmp(p->local, p->remote)) {
+                       /* same change in local and remote; nothing to do */
+                       trace_printf("\t\t\tskipping (local == remote)\n");
+               } else if (!hashcmp(p->local, uninitialized) ||
+                          !hashcmp(p->local, p->base)) {
+                       /* no local change; adopt remote change */
+                       trace_printf("\t\t\tno local change, adopted remote\n");
+                       if (add_note(t, p->obj, p->remote,
+                                    combine_notes_overwrite))
+                               die("BUG: combine_notes_overwrite failed");
+               } else {
+                       /* need file-level merge between local and remote */
+                       trace_printf("\t\t\tneed content-level merge\n");
+                       conflicts += merge_one_change(o, p, t);
+               }
+       }
+
+       return conflicts;
+}
+
+static int merge_from_diffs(struct notes_merge_options *o,
+                           const unsigned char *base,
+                           const unsigned char *local,
+                           const unsigned char *remote, struct notes_tree *t)
+{
+       struct notes_merge_pair *changes;
+       int num_changes, conflicts;
+
+       trace_printf("\tmerge_from_diffs(base = %.7s, local = %.7s, "
+              "remote = %.7s)\n", sha1_to_hex(base), sha1_to_hex(local),
+              sha1_to_hex(remote));
+
+       changes = diff_tree_remote(o, base, remote, &num_changes);
+       diff_tree_local(o, changes, num_changes, base, local);
+
+       conflicts = merge_changes(o, changes, &num_changes, t);
+       free(changes);
+
+       OUTPUT(o, 4, "Merge result: %i unmerged notes and a %s notes tree",
+              conflicts, t->dirty ? "dirty" : "clean");
+
+       return conflicts ? -1 : 1;
+}
+
+void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
+                        const char *msg, unsigned char *result_sha1)
+{
+       unsigned char tree_sha1[20];
+
+       assert(t->initialized);
+
+       if (write_notes_tree(t, tree_sha1))
+               die("Failed to write notes tree to database");
+
+       if (!parents) {
+               /* Deduce parent commit from t->ref */
+               unsigned char parent_sha1[20];
+               if (!read_ref(t->ref, parent_sha1)) {
+                       struct commit *parent = lookup_commit(parent_sha1);
+                       if (!parent || parse_commit(parent))
+                               die("Failed to find/parse commit %s", t->ref);
+                       commit_list_insert(parent, &parents);
+               }
+               /* else: t->ref points to nothing, assume root/orphan commit */
+       }
+
+       if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL))
+               die("Failed to commit notes tree to database");
+}
+
+int notes_merge(struct notes_merge_options *o,
+               struct notes_tree *local_tree,
+               unsigned char *result_sha1)
+{
+       unsigned char local_sha1[20], remote_sha1[20];
+       struct commit *local, *remote;
+       struct commit_list *bases = NULL;
+       const unsigned char *base_sha1, *base_tree_sha1;
+       int result = 0;
+
+       assert(o->local_ref && o->remote_ref);
+       assert(!strcmp(o->local_ref, local_tree->ref));
+       hashclr(result_sha1);
+
+       trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n",
+              o->local_ref, o->remote_ref);
+
+       /* Dereference o->local_ref into local_sha1 */
+       if (!resolve_ref(o->local_ref, local_sha1, 0, NULL))
+               die("Failed to resolve local notes ref '%s'", o->local_ref);
+       else if (!check_ref_format(o->local_ref) && is_null_sha1(local_sha1))
+               local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */
+       else if (!(local = lookup_commit_reference(local_sha1)))
+               die("Could not parse local commit %s (%s)",
+                   sha1_to_hex(local_sha1), o->local_ref);
+       trace_printf("\tlocal commit: %.7s\n", sha1_to_hex(local_sha1));
+
+       /* Dereference o->remote_ref into remote_sha1 */
+       if (get_sha1(o->remote_ref, remote_sha1)) {
+               /*
+                * Failed to get remote_sha1. If o->remote_ref looks like an
+                * unborn ref, perform the merge using an empty notes tree.
+                */
+               if (!check_ref_format(o->remote_ref)) {
+                       hashclr(remote_sha1);
+                       remote = NULL;
+               } else {
+                       die("Failed to resolve remote notes ref '%s'",
+                           o->remote_ref);
+               }
+       } else if (!(remote = lookup_commit_reference(remote_sha1))) {
+               die("Could not parse remote commit %s (%s)",
+                   sha1_to_hex(remote_sha1), o->remote_ref);
+       }
+       trace_printf("\tremote commit: %.7s\n", sha1_to_hex(remote_sha1));
+
+       if (!local && !remote)
+               die("Cannot merge empty notes ref (%s) into empty notes ref "
+                   "(%s)", o->remote_ref, o->local_ref);
+       if (!local) {
+               /* result == remote commit */
+               hashcpy(result_sha1, remote_sha1);
+               goto found_result;
+       }
+       if (!remote) {
+               /* result == local commit */
+               hashcpy(result_sha1, local_sha1);
+               goto found_result;
+       }
+       assert(local && remote);
+
+       /* Find merge bases */
+       bases = get_merge_bases(local, remote, 1);
+       if (!bases) {
+               base_sha1 = null_sha1;
+               base_tree_sha1 = (unsigned char *)EMPTY_TREE_SHA1_BIN;
+               OUTPUT(o, 4, "No merge base found; doing history-less merge");
+       } else if (!bases->next) {
+               base_sha1 = bases->item->object.sha1;
+               base_tree_sha1 = bases->item->tree->object.sha1;
+               OUTPUT(o, 4, "One merge base found (%.7s)",
+                      sha1_to_hex(base_sha1));
+       } else {
+               /* TODO: How to handle multiple merge-bases? */
+               base_sha1 = bases->item->object.sha1;
+               base_tree_sha1 = bases->item->tree->object.sha1;
+               OUTPUT(o, 3, "Multiple merge bases found. Using the first "
+                      "(%.7s)", sha1_to_hex(base_sha1));
+       }
+
+       OUTPUT(o, 4, "Merging remote commit %.7s into local commit %.7s with "
+              "merge-base %.7s", sha1_to_hex(remote->object.sha1),
+              sha1_to_hex(local->object.sha1), sha1_to_hex(base_sha1));
+
+       if (!hashcmp(remote->object.sha1, base_sha1)) {
+               /* Already merged; result == local commit */
+               OUTPUT(o, 2, "Already up-to-date!");
+               hashcpy(result_sha1, local->object.sha1);
+               goto found_result;
+       }
+       if (!hashcmp(local->object.sha1, base_sha1)) {
+               /* Fast-forward; result == remote commit */
+               OUTPUT(o, 2, "Fast-forward");
+               hashcpy(result_sha1, remote->object.sha1);
+               goto found_result;
+       }
+
+       result = merge_from_diffs(o, base_tree_sha1, local->tree->object.sha1,
+                                 remote->tree->object.sha1, local_tree);
+
+       if (result != 0) { /* non-trivial merge (with or without conflicts) */
+               /* Commit (partial) result */
+               struct commit_list *parents = NULL;
+               commit_list_insert(remote, &parents); /* LIFO order */
+               commit_list_insert(local, &parents);
+               create_notes_commit(local_tree, parents, o->commit_msg.buf,
+                                   result_sha1);
+       }
+
+found_result:
+       free_commit_list(bases);
+       strbuf_release(&(o->commit_msg));
+       trace_printf("notes_merge(): result = %i, result_sha1 = %.7s\n",
+              result, sha1_to_hex(result_sha1));
+       return result;
+}
+
+int notes_merge_commit(struct notes_merge_options *o,
+                      struct notes_tree *partial_tree,
+                      struct commit *partial_commit,
+                      unsigned char *result_sha1)
+{
+       /*
+        * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all
+        * found notes to 'partial_tree'. Write the updates notes tree to
+        * the DB, and commit the resulting tree object while reusing the
+        * commit message and parents from 'partial_commit'.
+        * Finally store the new commit object SHA1 into 'result_sha1'.
+        */
+       struct dir_struct dir;
+       const char *path = git_path(NOTES_MERGE_WORKTREE "/");
+       int path_len = strlen(path), i;
+       const char *msg = strstr(partial_commit->buffer, "\n\n");
+
+       OUTPUT(o, 3, "Committing notes in notes merge worktree at %.*s",
+              path_len - 1, path);
+
+       if (!msg || msg[2] == '\0')
+               die("partial notes commit has empty message");
+       msg += 2;
+
+       memset(&dir, 0, sizeof(dir));
+       read_directory(&dir, path, path_len, NULL);
+       for (i = 0; i < dir.nr; i++) {
+               struct dir_entry *ent = dir.entries[i];
+               struct stat st;
+               const char *relpath = ent->name + path_len;
+               unsigned char obj_sha1[20], blob_sha1[20];
+
+               if (ent->len - path_len != 40 || get_sha1_hex(relpath, obj_sha1)) {
+                       OUTPUT(o, 3, "Skipping non-SHA1 entry '%s'", ent->name);
+                       continue;
+               }
+
+               /* write file as blob, and add to partial_tree */
+               if (stat(ent->name, &st))
+                       die_errno("Failed to stat '%s'", ent->name);
+               if (index_path(blob_sha1, ent->name, &st, 1))
+                       die("Failed to write blob object from '%s'", ent->name);
+               if (add_note(partial_tree, obj_sha1, blob_sha1, NULL))
+                       die("Failed to add resolved note '%s' to notes tree",
+                           ent->name);
+               OUTPUT(o, 4, "Added resolved note for object %s: %s",
+                      sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1));
+       }
+
+       create_notes_commit(partial_tree, partial_commit->parents, msg,
+                           result_sha1);
+       OUTPUT(o, 4, "Finalized notes merge commit: %s",
+              sha1_to_hex(result_sha1));
+       return 0;
+}
+
+int notes_merge_abort(struct notes_merge_options *o)
+{
+       /* Remove .git/NOTES_MERGE_WORKTREE directory and all files within */
+       struct strbuf buf = STRBUF_INIT;
+       int ret;
+
+       strbuf_addstr(&buf, git_path(NOTES_MERGE_WORKTREE));
+       OUTPUT(o, 3, "Removing notes merge worktree at %s", buf.buf);
+       ret = remove_dir_recursively(&buf, 0);
+       strbuf_release(&buf);
+       return ret;
+}
diff --git a/notes-merge.h b/notes-merge.h
new file mode 100644 (file)
index 0000000..168a672
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef NOTES_MERGE_H
+#define NOTES_MERGE_H
+
+#define NOTES_MERGE_WORKTREE "NOTES_MERGE_WORKTREE"
+
+enum notes_merge_verbosity {
+       NOTES_MERGE_VERBOSITY_DEFAULT = 2,
+       NOTES_MERGE_VERBOSITY_MAX = 5
+};
+
+struct notes_merge_options {
+       const char *local_ref;
+       const char *remote_ref;
+       struct strbuf commit_msg;
+       int verbosity;
+       enum {
+               NOTES_MERGE_RESOLVE_MANUAL = 0,
+               NOTES_MERGE_RESOLVE_OURS,
+               NOTES_MERGE_RESOLVE_THEIRS,
+               NOTES_MERGE_RESOLVE_UNION,
+               NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ
+       } strategy;
+       unsigned has_worktree:1;
+};
+
+void init_notes_merge_options(struct notes_merge_options *o);
+
+/*
+ * Create new notes commit from the given notes tree
+ *
+ * Properties of the created commit:
+ * - tree: the result of converting t to a tree object with write_notes_tree().
+ * - parents: the given parents OR (if NULL) the commit referenced by t->ref.
+ * - author/committer: the default determined by commmit_tree().
+ * - commit message: msg
+ *
+ * The resulting commit SHA1 is stored in result_sha1.
+ */
+void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
+                        const char *msg, unsigned char *result_sha1);
+
+/*
+ * Merge notes from o->remote_ref into o->local_ref
+ *
+ * The given notes_tree 'local_tree' must be the notes_tree referenced by the
+ * o->local_ref. This is the notes_tree in which the object-level merge is
+ * performed.
+ *
+ * The commits given by the two refs are merged, producing one of the following
+ * outcomes:
+ *
+ * 1. The merge trivially results in an existing commit (e.g. fast-forward or
+ *    already-up-to-date). 'local_tree' is untouched, the SHA1 of the result
+ *    is written into 'result_sha1' and 0 is returned.
+ * 2. The merge successfully completes, producing a merge commit. local_tree
+ *    contains the updated notes tree, the SHA1 of the resulting commit is
+ *    written into 'result_sha1', and 1 is returned.
+ * 3. The merge results in conflicts. This is similar to #2 in that the
+ *    partial merge result (i.e. merge result minus the unmerged entries)
+ *    are stored in 'local_tree', and the SHA1 or the resulting commit
+ *    (to be amended when the conflicts have been resolved) is written into
+ *    'result_sha1'. The unmerged entries are written into the
+ *    .git/NOTES_MERGE_WORKTREE directory with conflict markers.
+ *    -1 is returned.
+ *
+ * Both o->local_ref and o->remote_ref must be given (non-NULL), but either ref
+ * (although not both) may refer to a non-existing notes ref, in which case
+ * that notes ref is interpreted as an empty notes tree, and the merge
+ * trivially results in what the other ref points to.
+ */
+int notes_merge(struct notes_merge_options *o,
+               struct notes_tree *local_tree,
+               unsigned char *result_sha1);
+
+/*
+ * Finalize conflict resolution from an earlier notes_merge()
+ *
+ * The given notes tree 'partial_tree' must be the notes_tree corresponding to
+ * the given 'partial_commit', the partial result commit created by a previous
+ * call to notes_merge().
+ *
+ * This function will add the (now resolved) notes in .git/NOTES_MERGE_WORKTREE
+ * to 'partial_tree', and create a final notes merge commit, the SHA1 of which
+ * will be stored in 'result_sha1'.
+ */
+int notes_merge_commit(struct notes_merge_options *o,
+                      struct notes_tree *partial_tree,
+                      struct commit *partial_commit,
+                      unsigned char *result_sha1);
+
+/*
+ * Abort conflict resolution from an earlier notes_merge()
+ *
+ * Removes the notes merge worktree in .git/NOTES_MERGE_WORKTREE.
+ */
+int notes_merge_abort(struct notes_merge_options *o);
+
+#endif
diff --git a/notes.c b/notes.c
index 7fd203560aaded5ebbd80d67c805df5a707aa60d..a013c1bc638dbf5a8111183a3f9d154721ec5e04 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -149,86 +149,6 @@ static struct leaf_node *note_tree_find(struct notes_tree *t,
        return NULL;
 }
 
-/*
- * To insert a leaf_node:
- * Search to the tree location appropriate for the given leaf_node's key:
- * - If location is unused (NULL), store the tweaked pointer directly there
- * - If location holds a note entry that matches the note-to-be-inserted, then
- *   combine the two notes (by calling the given combine_notes function).
- * - If location holds a note entry that matches the subtree-to-be-inserted,
- *   then unpack the subtree-to-be-inserted into the location.
- * - If location holds a matching subtree entry, unpack the subtree at that
- *   location, and restart the insert operation from that level.
- * - Else, create a new int_node, holding both the node-at-location and the
- *   node-to-be-inserted, and store the new int_node into the location.
- */
-static void note_tree_insert(struct notes_tree *t, struct int_node *tree,
-               unsigned char n, struct leaf_node *entry, unsigned char type,
-               combine_notes_fn combine_notes)
-{
-       struct int_node *new_node;
-       struct leaf_node *l;
-       void **p = note_tree_search(t, &tree, &n, entry->key_sha1);
-
-       assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */
-       l = (struct leaf_node *) CLR_PTR_TYPE(*p);
-       switch (GET_PTR_TYPE(*p)) {
-       case PTR_TYPE_NULL:
-               assert(!*p);
-               *p = SET_PTR_TYPE(entry, type);
-               return;
-       case PTR_TYPE_NOTE:
-               switch (type) {
-               case PTR_TYPE_NOTE:
-                       if (!hashcmp(l->key_sha1, entry->key_sha1)) {
-                               /* skip concatenation if l == entry */
-                               if (!hashcmp(l->val_sha1, entry->val_sha1))
-                                       return;
-
-                               if (combine_notes(l->val_sha1, entry->val_sha1))
-                                       die("failed to combine notes %s and %s"
-                                           " for object %s",
-                                           sha1_to_hex(l->val_sha1),
-                                           sha1_to_hex(entry->val_sha1),
-                                           sha1_to_hex(l->key_sha1));
-                               free(entry);
-                               return;
-                       }
-                       break;
-               case PTR_TYPE_SUBTREE:
-                       if (!SUBTREE_SHA1_PREFIXCMP(l->key_sha1,
-                                                   entry->key_sha1)) {
-                               /* unpack 'entry' */
-                               load_subtree(t, entry, tree, n);
-                               free(entry);
-                               return;
-                       }
-                       break;
-               }
-               break;
-       case PTR_TYPE_SUBTREE:
-               if (!SUBTREE_SHA1_PREFIXCMP(entry->key_sha1, l->key_sha1)) {
-                       /* unpack 'l' and restart insert */
-                       *p = NULL;
-                       load_subtree(t, l, tree, n);
-                       free(l);
-                       note_tree_insert(t, tree, n, entry, type,
-                                        combine_notes);
-                       return;
-               }
-               break;
-       }
-
-       /* non-matching leaf_node */
-       assert(GET_PTR_TYPE(*p) == PTR_TYPE_NOTE ||
-              GET_PTR_TYPE(*p) == PTR_TYPE_SUBTREE);
-       new_node = (struct int_node *) xcalloc(sizeof(struct int_node), 1);
-       note_tree_insert(t, new_node, n + 1, l, GET_PTR_TYPE(*p),
-                        combine_notes);
-       *p = SET_PTR_TYPE(new_node, PTR_TYPE_INTERNAL);
-       note_tree_insert(t, new_node, n + 1, entry, type, combine_notes);
-}
-
 /*
  * How to consolidate an int_node:
  * If there are > 1 non-NULL entries, give up and return non-zero.
@@ -263,11 +183,13 @@ static int note_tree_consolidate(struct int_node *tree,
  * To remove a leaf_node:
  * Search to the tree location appropriate for the given leaf_node's key:
  * - If location does not hold a matching entry, abort and do nothing.
+ * - Copy the matching entry's value into the given entry.
  * - Replace the matching leaf_node with a NULL entry (and free the leaf_node).
  * - Consolidate int_nodes repeatedly, while walking up the tree towards root.
  */
-static void note_tree_remove(struct notes_tree *t, struct int_node *tree,
-               unsigned char n, struct leaf_node *entry)
+static void note_tree_remove(struct notes_tree *t,
+               struct int_node *tree, unsigned char n,
+               struct leaf_node *entry)
 {
        struct leaf_node *l;
        struct int_node *parent_stack[20];
@@ -282,6 +204,7 @@ static void note_tree_remove(struct notes_tree *t, struct int_node *tree,
                return; /* key mismatch, nothing to remove */
 
        /* we have found a matching entry */
+       hashcpy(entry->val_sha1, l->val_sha1);
        free(l);
        *p = SET_PTR_TYPE(NULL, PTR_TYPE_NULL);
 
@@ -302,6 +225,93 @@ static void note_tree_remove(struct notes_tree *t, struct int_node *tree,
                i--;
 }
 
+/*
+ * To insert a leaf_node:
+ * Search to the tree location appropriate for the given leaf_node's key:
+ * - If location is unused (NULL), store the tweaked pointer directly there
+ * - If location holds a note entry that matches the note-to-be-inserted, then
+ *   combine the two notes (by calling the given combine_notes function).
+ * - If location holds a note entry that matches the subtree-to-be-inserted,
+ *   then unpack the subtree-to-be-inserted into the location.
+ * - If location holds a matching subtree entry, unpack the subtree at that
+ *   location, and restart the insert operation from that level.
+ * - Else, create a new int_node, holding both the node-at-location and the
+ *   node-to-be-inserted, and store the new int_node into the location.
+ */
+static int note_tree_insert(struct notes_tree *t, struct int_node *tree,
+               unsigned char n, struct leaf_node *entry, unsigned char type,
+               combine_notes_fn combine_notes)
+{
+       struct int_node *new_node;
+       struct leaf_node *l;
+       void **p = note_tree_search(t, &tree, &n, entry->key_sha1);
+       int ret = 0;
+
+       assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */
+       l = (struct leaf_node *) CLR_PTR_TYPE(*p);
+       switch (GET_PTR_TYPE(*p)) {
+       case PTR_TYPE_NULL:
+               assert(!*p);
+               if (is_null_sha1(entry->val_sha1))
+                       free(entry);
+               else
+                       *p = SET_PTR_TYPE(entry, type);
+               return 0;
+       case PTR_TYPE_NOTE:
+               switch (type) {
+               case PTR_TYPE_NOTE:
+                       if (!hashcmp(l->key_sha1, entry->key_sha1)) {
+                               /* skip concatenation if l == entry */
+                               if (!hashcmp(l->val_sha1, entry->val_sha1))
+                                       return 0;
+
+                               ret = combine_notes(l->val_sha1,
+                                                   entry->val_sha1);
+                               if (!ret && is_null_sha1(l->val_sha1))
+                                       note_tree_remove(t, tree, n, entry);
+                               free(entry);
+                               return ret;
+                       }
+                       break;
+               case PTR_TYPE_SUBTREE:
+                       if (!SUBTREE_SHA1_PREFIXCMP(l->key_sha1,
+                                                   entry->key_sha1)) {
+                               /* unpack 'entry' */
+                               load_subtree(t, entry, tree, n);
+                               free(entry);
+                               return 0;
+                       }
+                       break;
+               }
+               break;
+       case PTR_TYPE_SUBTREE:
+               if (!SUBTREE_SHA1_PREFIXCMP(entry->key_sha1, l->key_sha1)) {
+                       /* unpack 'l' and restart insert */
+                       *p = NULL;
+                       load_subtree(t, l, tree, n);
+                       free(l);
+                       return note_tree_insert(t, tree, n, entry, type,
+                                               combine_notes);
+               }
+               break;
+       }
+
+       /* non-matching leaf_node */
+       assert(GET_PTR_TYPE(*p) == PTR_TYPE_NOTE ||
+              GET_PTR_TYPE(*p) == PTR_TYPE_SUBTREE);
+       if (is_null_sha1(entry->val_sha1)) { /* skip insertion of empty note */
+               free(entry);
+               return 0;
+       }
+       new_node = (struct int_node *) xcalloc(sizeof(struct int_node), 1);
+       ret = note_tree_insert(t, new_node, n + 1, l, GET_PTR_TYPE(*p),
+                              combine_notes);
+       if (ret)
+               return ret;
+       *p = SET_PTR_TYPE(new_node, PTR_TYPE_INTERNAL);
+       return note_tree_insert(t, new_node, n + 1, entry, type, combine_notes);
+}
+
 /* Free the entire notes data contained in the given tree */
 static void note_tree_free(struct int_node *tree)
 {
@@ -442,8 +452,12 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
                                l->key_sha1[19] = (unsigned char) len;
                                type = PTR_TYPE_SUBTREE;
                        }
-                       note_tree_insert(t, node, n, l, type,
-                                        combine_notes_concatenate);
+                       if (note_tree_insert(t, node, n, l, type,
+                                            combine_notes_concatenate))
+                               die("Failed to load %s %s into notes tree "
+                                   "from %s",
+                                   type == PTR_TYPE_NOTE ? "note" : "subtree",
+                                   sha1_to_hex(l->key_sha1), t->ref);
                }
                continue;
 
@@ -801,16 +815,17 @@ int combine_notes_concatenate(unsigned char *cur_sha1,
                return 0;
        }
 
-       /* we will separate the notes by a newline anyway */
+       /* we will separate the notes by two newlines anyway */
        if (cur_msg[cur_len - 1] == '\n')
                cur_len--;
 
        /* concatenate cur_msg and new_msg into buf */
-       buf_len = cur_len + 1 + new_len;
+       buf_len = cur_len + 2 + new_len;
        buf = (char *) xmalloc(buf_len);
        memcpy(buf, cur_msg, cur_len);
        buf[cur_len] = '\n';
-       memcpy(buf + cur_len + 1, new_msg, new_len);
+       buf[cur_len + 1] = '\n';
+       memcpy(buf + cur_len + 2, new_msg, new_len);
        free(cur_msg);
        free(new_msg);
 
@@ -833,6 +848,82 @@ int combine_notes_ignore(unsigned char *cur_sha1,
        return 0;
 }
 
+static int string_list_add_note_lines(struct string_list *sort_uniq_list,
+                                     const unsigned char *sha1)
+{
+       char *data;
+       unsigned long len;
+       enum object_type t;
+       struct strbuf buf = STRBUF_INIT;
+       struct strbuf **lines = NULL;
+       int i, list_index;
+
+       if (is_null_sha1(sha1))
+               return 0;
+
+       /* read_sha1_file NUL-terminates */
+       data = read_sha1_file(sha1, &t, &len);
+       if (t != OBJ_BLOB || !data || !len) {
+               free(data);
+               return t != OBJ_BLOB || !data;
+       }
+
+       strbuf_attach(&buf, data, len, len + 1);
+       lines = strbuf_split(&buf, '\n');
+
+       for (i = 0; lines[i]; i++) {
+               if (lines[i]->buf[lines[i]->len - 1] == '\n')
+                       strbuf_setlen(lines[i], lines[i]->len - 1);
+               if (!lines[i]->len)
+                       continue; /* skip empty lines */
+               list_index = string_list_find_insert_index(sort_uniq_list,
+                                                          lines[i]->buf, 0);
+               if (list_index < 0)
+                       continue; /* skip duplicate lines */
+               string_list_insert_at_index(sort_uniq_list, list_index,
+                                           lines[i]->buf);
+       }
+
+       strbuf_list_free(lines);
+       strbuf_release(&buf);
+       return 0;
+}
+
+static int string_list_join_lines_helper(struct string_list_item *item,
+                                        void *cb_data)
+{
+       struct strbuf *buf = cb_data;
+       strbuf_addstr(buf, item->string);
+       strbuf_addch(buf, '\n');
+       return 0;
+}
+
+int combine_notes_cat_sort_uniq(unsigned char *cur_sha1,
+               const unsigned char *new_sha1)
+{
+       struct string_list sort_uniq_list = { NULL, 0, 0, 1 };
+       struct strbuf buf = STRBUF_INIT;
+       int ret = 1;
+
+       /* read both note blob objects into unique_lines */
+       if (string_list_add_note_lines(&sort_uniq_list, cur_sha1))
+               goto out;
+       if (string_list_add_note_lines(&sort_uniq_list, new_sha1))
+               goto out;
+
+       /* create a new blob object from sort_uniq_list */
+       if (for_each_string_list(&sort_uniq_list,
+                                string_list_join_lines_helper, &buf))
+               goto out;
+
+       ret = write_sha1_file(buf.buf, buf.len, blob_type, cur_sha1);
+
+out:
+       strbuf_release(&buf);
+       string_list_clear(&sort_uniq_list, 0);
+       return ret;
+}
+
 static int string_list_add_one_ref(const char *path, const unsigned char *sha1,
                                   int flag, void *cb)
 {
@@ -890,7 +981,7 @@ static int notes_display_config(const char *k, const char *v, void *cb)
        return 0;
 }
 
-static const char *default_notes_ref(void)
+const char *default_notes_ref(void)
 {
        const char *notes_ref = NULL;
        if (!notes_ref)
@@ -932,7 +1023,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
                return;
        if (get_tree_entry(object_sha1, "", sha1, &mode))
                die("Failed to read notes tree referenced by %s (%s)",
-                   notes_ref, object_sha1);
+                   notes_ref, sha1_to_hex(object_sha1));
 
        hashclr(root_tree.key_sha1);
        hashcpy(root_tree.val_sha1, sha1);
@@ -986,7 +1077,7 @@ void init_display_notes(struct display_notes_opt *opt)
        string_list_clear(&display_notes_refs, 0);
 }
 
-void add_note(struct notes_tree *t, const unsigned char *object_sha1,
+int add_note(struct notes_tree *t, const unsigned char *object_sha1,
                const unsigned char *note_sha1, combine_notes_fn combine_notes)
 {
        struct leaf_node *l;
@@ -1000,20 +1091,23 @@ void add_note(struct notes_tree *t, const unsigned char *object_sha1,
        l = (struct leaf_node *) xmalloc(sizeof(struct leaf_node));
        hashcpy(l->key_sha1, object_sha1);
        hashcpy(l->val_sha1, note_sha1);
-       note_tree_insert(t, t->root, 0, l, PTR_TYPE_NOTE, combine_notes);
+       return note_tree_insert(t, t->root, 0, l, PTR_TYPE_NOTE, combine_notes);
 }
 
-void remove_note(struct notes_tree *t, const unsigned char *object_sha1)
+int remove_note(struct notes_tree *t, const unsigned char *object_sha1)
 {
        struct leaf_node l;
 
        if (!t)
                t = &default_notes_tree;
        assert(t->initialized);
-       t->dirty = 1;
        hashcpy(l.key_sha1, object_sha1);
        hashclr(l.val_sha1);
        note_tree_remove(t, t->root, 0, &l);
+       if (is_null_sha1(l.val_sha1)) // no note was removed
+               return 1;
+       t->dirty = 1;
+       return 0;
 }
 
 const unsigned char *get_note(struct notes_tree *t,
@@ -1176,7 +1270,7 @@ void format_display_notes(const unsigned char *object_sha1,
 
 int copy_note(struct notes_tree *t,
              const unsigned char *from_obj, const unsigned char *to_obj,
-             int force, combine_notes_fn combine_fn)
+             int force, combine_notes_fn combine_notes)
 {
        const unsigned char *note = get_note(t, from_obj);
        const unsigned char *existing_note = get_note(t, to_obj);
@@ -1185,9 +1279,9 @@ int copy_note(struct notes_tree *t,
                return 1;
 
        if (note)
-               add_note(t, to_obj, note, combine_fn);
+               return add_note(t, to_obj, note, combine_notes);
        else if (existing_note)
-               add_note(t, to_obj, null_sha1, combine_fn);
+               return add_note(t, to_obj, null_sha1, combine_notes);
 
        return 0;
 }
diff --git a/notes.h b/notes.h
index 65fc3a66b2f575dd078cc390e7f5ea5cbebffc96..83bd6e0ec02a1a8650004f14cd7476eedbdff518 100644 (file)
--- a/notes.h
+++ b/notes.h
  * resulting SHA1 into the first SHA1 argument (cur_sha1). A non-zero return
  * value indicates failure.
  *
- * The two given SHA1s must both be non-NULL and different from each other.
+ * The two given SHA1s shall both be non-NULL and different from each other.
+ * Either of them (but not both) may be == null_sha1, which indicates an
+ * empty/non-existent note. If the resulting SHA1 (cur_sha1) is == null_sha1,
+ * the note will be removed from the notes tree.
  *
  * The default combine_notes function (you get this when passing NULL) is
  * combine_notes_concatenate(), which appends the contents of the new note to
@@ -24,6 +27,7 @@ typedef int (*combine_notes_fn)(unsigned char *cur_sha1, const unsigned char *ne
 int combine_notes_concatenate(unsigned char *cur_sha1, const unsigned char *new_sha1);
 int combine_notes_overwrite(unsigned char *cur_sha1, const unsigned char *new_sha1);
 int combine_notes_ignore(unsigned char *cur_sha1, const unsigned char *new_sha1);
+int combine_notes_cat_sort_uniq(unsigned char *cur_sha1, const unsigned char *new_sha1);
 
 /*
  * Notes tree object
@@ -43,6 +47,20 @@ extern struct notes_tree {
        int dirty;
 } default_notes_tree;
 
+/*
+ * Return the default notes ref.
+ *
+ * The default notes ref is the notes ref that is used when notes_ref == NULL
+ * is passed to init_notes().
+ *
+ * This the first of the following to be defined:
+ * 1. The '--ref' option to 'git notes', if given
+ * 2. The $GIT_NOTES_REF environment variable, if set
+ * 3. The value of the core.notesRef config variable, if set
+ * 4. GIT_NOTES_DEFAULT_REF (i.e. "refs/notes/commits")
+ */
+const char *default_notes_ref(void);
+
 /*
  * Flags controlling behaviour of notes tree initialization
  *
@@ -76,11 +94,24 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
 /*
  * Add the given note object to the given notes_tree structure
  *
+ * If there already exists a note for the given object_sha1, the given
+ * combine_notes function is invoked to break the tie. If not given (i.e.
+ * combine_notes == NULL), the default combine_notes function for the given
+ * notes_tree is used.
+ *
+ * Passing note_sha1 == null_sha1 indicates the addition of an
+ * empty/non-existent note. This is a (potentially expensive) no-op unless
+ * there already exists a note for the given object_sha1, AND combining that
+ * note with the empty note (using the given combine_notes function) results
+ * in a new/changed note.
+ *
+ * Returns zero on success; non-zero means combine_notes failed.
+ *
  * IMPORTANT: The changes made by add_note() to the given notes_tree structure
  * are not persistent until a subsequent call to write_notes_tree() returns
  * zero.
  */
-void add_note(struct notes_tree *t, const unsigned char *object_sha1,
+int add_note(struct notes_tree *t, const unsigned char *object_sha1,
                const unsigned char *note_sha1, combine_notes_fn combine_notes);
 
 /*
@@ -89,8 +120,10 @@ void add_note(struct notes_tree *t, const unsigned char *object_sha1,
  * IMPORTANT: The changes made by remove_note() to the given notes_tree
  * structure are not persistent until a subsequent call to write_notes_tree()
  * returns zero.
+ *
+ * Return 0 if a note was removed; 1 if there was no note to remove.
  */
-void remove_note(struct notes_tree *t, const unsigned char *object_sha1);
+int remove_note(struct notes_tree *t, const unsigned char *object_sha1);
 
 /*
  * Get the note object SHA1 containing the note data for the given object
@@ -103,11 +136,18 @@ const unsigned char *get_note(struct notes_tree *t,
 /*
  * Copy a note from one object to another in the given notes_tree.
  *
- * Fails if the to_obj already has a note unless 'force' is true.
+ * Returns 1 if the to_obj already has a note and 'force' is false. Otherwise,
+ * returns non-zero if 'force' is true, but the given combine_notes function
+ * failed to combine from_obj's note with to_obj's existing note.
+ * Returns zero on success.
+ *
+ * IMPORTANT: The changes made by copy_note() to the given notes_tree structure
+ * are not persistent until a subsequent call to write_notes_tree() returns
+ * zero.
  */
 int copy_note(struct notes_tree *t,
              const unsigned char *from_obj, const unsigned char *to_obj,
-             int force, combine_notes_fn combine_fn);
+             int force, combine_notes_fn combine_notes);
 
 /*
  * Flags controlling behaviour of for_each_note()
@@ -149,6 +189,7 @@ int copy_note(struct notes_tree *t,
  * notes tree) from within the callback:
  * - add_note()
  * - remove_note()
+ * - copy_note()
  * - free_notes()
  */
 typedef int each_note_fn(const unsigned char *object_sha1,
index 277b3ddba7dc5387cd97cb35c23d3358727898be..7e1f2bbed2ad7f331fefc78d759acb14050bea48 100644 (file)
--- a/object.c
+++ b/object.c
@@ -199,7 +199,7 @@ struct object *parse_object(const unsigned char *sha1)
                        return NULL;
                }
 
-               obj = parse_object_buffer(repl, type, size, buffer, &eaten);
+               obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
                if (!eaten)
                        free(buffer);
                return obj;
@@ -211,10 +211,10 @@ struct object_list *object_list_insert(struct object *item,
                                       struct object_list **list_p)
 {
        struct object_list *new_list = xmalloc(sizeof(struct object_list));
-        new_list->item = item;
-        new_list->next = *list_p;
-        *list_p = new_list;
-        return new_list;
+       new_list->item = item;
+       new_list->next = *list_p;
+       *list_p = new_list;
+       return new_list;
 }
 
 int object_list_contains(struct object_list *list, struct object *obj)
index 82877c831ca7fe39abbb98805f5801bdc6fcac9c..4d1d61546f0441bd198cda3ad153b35a91444b72 100644 (file)
--- a/object.h
+++ b/object.h
@@ -21,6 +21,8 @@ struct object_array {
        } *objects;
 };
 
+#define OBJECT_ARRAY_INIT { 0, 0, NULL }
+
 #define TYPE_BITS   3
 #define FLAG_BITS  27
 
index 395fb9527a3bc6dd8ca648233911a1c35604440d..9d0cb9a114cb3e5b0a4a4a132b9289ef18d8ae21 100644 (file)
@@ -77,7 +77,7 @@ static int verify_packfile(struct packed_git *p,
                err = error("%s SHA1 checksum mismatch",
                            p->pack_name);
        if (hashcmp(index_base + index_size - 40, pack_sig))
-               err = error("%s SHA1 does not match its inddex",
+               err = error("%s SHA1 does not match its index",
                            p->pack_name);
        unuse_pack(w_curs);
 
index 0fa79bc31d322e2aab8fce738ca6489a35a51ca4..42b51ef14514f3c3df454e4b718718a9b58f1612 100644 (file)
@@ -11,6 +11,13 @@ static int parse_options_usage(struct parse_opt_ctx_t *ctx,
 #define OPT_SHORT 1
 #define OPT_UNSET 2
 
+static int optbug(const struct option *opt, const char *reason)
+{
+       if (opt->long_name)
+               return error("BUG: option '%s' %s", opt->long_name, reason);
+       return error("BUG: switch '%c' %s", opt->short_name, reason);
+}
+
 static int opterror(const struct option *opt, const char *reason, int flags)
 {
        if (flags & OPT_SHORT)
@@ -55,25 +62,13 @@ static int get_value(struct parse_opt_ctx_t *p,
                return opterror(opt, "takes no value", flags);
        if (unset && (opt->flags & PARSE_OPT_NONEG))
                return opterror(opt, "isn't available", flags);
-
-       if (!(flags & OPT_SHORT) && p->opt) {
-               switch (opt->type) {
-               case OPTION_CALLBACK:
-                       if (!(opt->flags & PARSE_OPT_NOARG))
-                               break;
-                       /* 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);
-               default:
-                       break;
-               }
-       }
+       if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
+               return opterror(opt, "takes no value", flags);
 
        switch (opt->type) {
+       case OPTION_LOWLEVEL_CALLBACK:
+               return (*(parse_opt_ll_cb *)opt->callback)(p, opt, unset);
+
        case OPTION_BIT:
                if (unset)
                        *(int *)opt->value &= ~opt->defval;
@@ -281,13 +276,6 @@ static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
        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);
        }
@@ -320,25 +308,37 @@ static void parse_options_check(const struct option *opts)
 
        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;
+                   (opts->flags & PARSE_OPT_OPTARG))
+                       err |= optbug(opts, "uses incompatible flags "
+                                       "LASTARG_DEFAULT and OPTARG");
+               if (opts->flags & PARSE_OPT_NODASH &&
+                   ((opts->flags & PARSE_OPT_OPTARG) ||
+                    !(opts->flags & PARSE_OPT_NOARG) ||
+                    !(opts->flags & PARSE_OPT_NONEG) ||
+                    opts->long_name))
+                       err |= optbug(opts, "uses feature "
+                                       "not supported for dashless options");
+               switch (opts->type) {
+               case OPTION_BOOLEAN:
+               case OPTION_BIT:
+               case OPTION_NEGBIT:
+               case OPTION_SET_INT:
+               case OPTION_SET_PTR:
+               case OPTION_NUMBER:
+                       if ((opts->flags & PARSE_OPT_OPTARG) ||
+                           !(opts->flags & PARSE_OPT_NOARG))
+                               err |= optbug(opts, "should not accept an argument");
+               default:
+                       ; /* ok. (usually accepts an argument) */
                }
        }
-
        if (err)
-               exit(129);
+               exit(128);
 }
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
                         int argc, const char **argv, const char *prefix,
-                        int flags)
+                        const struct option *options, int flags)
 {
        memset(ctx, 0, sizeof(*ctx));
        ctx->argc = argc - 1;
@@ -350,6 +350,7 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
        if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
            (flags & PARSE_OPT_STOP_AT_NON_OPTION))
                die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
+       parse_options_check(options);
 }
 
 static int usage_with_options_internal(struct parse_opt_ctx_t *,
@@ -362,8 +363,6 @@ 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;
 
@@ -374,7 +373,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                        if (parse_nodash_opt(ctx, arg, options) == 0)
                                continue;
                        if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
-                               break;
+                               return PARSE_OPT_NON_OPTION;
                        ctx->out[ctx->cpidx++] = ctx->argv[0];
                        continue;
                }
@@ -452,10 +451,11 @@ int parse_options(int argc, const char **argv, const char *prefix,
 {
        struct parse_opt_ctx_t ctx;
 
-       parse_options_start(&ctx, argc, argv, prefix, flags);
+       parse_options_start(&ctx, argc, argv, prefix, options, flags);
        switch (parse_options_step(&ctx, options, usagestr)) {
        case PARSE_OPT_HELP:
                exit(129);
+       case PARSE_OPT_NON_OPTION:
        case PARSE_OPT_DONE:
                break;
        default: /* PARSE_OPT_UNKNOWN */
@@ -541,7 +541,8 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
                if (opts->type == OPTION_NUMBER)
                        pos += fprintf(outfile, "-NUM");
 
-               if (!(opts->flags & PARSE_OPT_NOARG))
+               if ((opts->flags & PARSE_OPT_LITERAL_ARGHELP) ||
+                   !(opts->flags & PARSE_OPT_NOARG))
                        pos += usage_argh(opts, outfile);
 
                if (pos <= USAGE_OPTS_WIDTH)
index 7435cdbf1d94378cf1c3408ae17cb610165a00ba..31ec5d24763b13b3e97c79f7a3edd6e7799bcd91 100644 (file)
@@ -17,6 +17,7 @@ enum parse_opt_type {
        OPTION_STRING,
        OPTION_INTEGER,
        OPTION_CALLBACK,
+       OPTION_LOWLEVEL_CALLBACK,
        OPTION_FILENAME
 };
 
@@ -43,6 +44,10 @@ enum parse_opt_option_flags {
 struct option;
 typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
 
+struct parse_opt_ctx_t;
+typedef int parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int unset);
+
 /*
  * `type`::
  *   holds the type of the option, you must have an OPTION_END last in your
@@ -69,7 +74,7 @@ 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
+ *   PARSE_OPT_NOARG: says that this option does not take an argument
  *   PARSE_OPT_NONEG: says that this option cannot be negated
  *   PARSE_OPT_HIDDEN: this option is skipped in the default usage, and
  *                     shown only in the full usage.
@@ -87,7 +92,8 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
  *                             useful for users of OPTION_NEGBIT.
  *
  * `callback`::
- *   pointer to the callback to use for OPTION_CALLBACK.
+ *   pointer to the callback to use for OPTION_CALLBACK or
+ *   OPTION_LOWLEVEL_CALLBACK.
  *
  * `defval`::
  *   default value to fill (*->value) with for PARSE_OPT_OPTARG.
@@ -161,6 +167,7 @@ extern NORETURN void usage_msg_opt(const char *msg,
 enum {
        PARSE_OPT_HELP = -1,
        PARSE_OPT_DONE,
+       PARSE_OPT_NON_OPTION,
        PARSE_OPT_UNKNOWN
 };
 
@@ -180,7 +187,7 @@ struct parse_opt_ctx_t {
 
 extern void parse_options_start(struct parse_opt_ctx_t *ctx,
                                int argc, const char **argv, const char *prefix,
-                               int flags);
+                               const struct option *options, int flags);
 
 extern int parse_options_step(struct parse_opt_ctx_t *ctx,
                              const struct option *options,
@@ -198,14 +205,15 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
 extern int parse_opt_with_commit(const struct option *, const char *, int);
 extern int parse_opt_tertiary(const struct option *, const char *, int);
 
-#define OPT__VERBOSE(var)  OPT_BOOLEAN('v', "verbose", (var), "be verbose")
-#define OPT__QUIET(var)    OPT_BOOLEAN('q', "quiet",   (var), "be quiet")
+#define OPT__VERBOSE(var, h)  OPT_BOOLEAN('v', "verbose", (var), (h))
+#define OPT__QUIET(var, h)    OPT_BOOLEAN('q', "quiet",   (var), (h))
 #define OPT__VERBOSITY(var) \
        { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \
          PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
        { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \
          PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
-#define OPT__DRY_RUN(var)  OPT_BOOLEAN('n', "dry-run", (var), "dry run")
+#define OPT__DRY_RUN(var, h)  OPT_BOOLEAN('n', "dry-run", (var), (h))
+#define OPT__FORCE(var, h)    OPT_BOOLEAN('f', "force",   (var), (h))
 #define OPT__ABBREV(var)  \
        { OPTION_CALLBACK, 0, "abbrev", (var), "n", \
          "use <n> digits to display SHA-1s", \
diff --git a/path.c b/path.c
index 6b23023095d7e1a5cfc1aef8db6d6e5fb56b32de..8951333cb88a6e28ef29538183ebf71e3b0913fa 100644 (file)
--- a/path.c
+++ b/path.c
@@ -122,118 +122,43 @@ char *git_path(const char *fmt, ...)
        return cleanup_path(pathname);
 }
 
-
-/* git_mkstemp() - create tmp file honoring TMPDIR variable */
-int git_mkstemp(char *path, size_t len, const char *template)
-{
-       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 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)
+char *git_path_submodule(const char *path, const char *fmt, ...)
 {
-       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);
-}
-
-/* Adapted from libiberty's mkstemp.c. */
-
-#undef TMP_MAX
-#define TMP_MAX 16384
-
-int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
-{
-       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;
+       char *pathname = get_pathname();
+       struct strbuf buf = STRBUF_INIT;
+       const char *git_dir;
+       va_list args;
+       unsigned len;
 
-       len = strlen(pattern);
+       len = strlen(path);
+       if (len > PATH_MAX-100)
+               return bad_path;
 
-       if (len < 6 + suffix_len) {
-               errno = EINVAL;
-               return -1;
-       }
+       strbuf_addstr(&buf, path);
+       if (len && path[len-1] != '/')
+               strbuf_addch(&buf, '/');
+       strbuf_addstr(&buf, ".git");
 
-       if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
-               errno = EINVAL;
-               return -1;
+       git_dir = read_gitfile_gently(buf.buf);
+       if (git_dir) {
+               strbuf_reset(&buf);
+               strbuf_addstr(&buf, git_dir);
        }
+       strbuf_addch(&buf, '/');
 
-       /*
-        * 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, mode);
-               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';
-       return -1;
-}
+       if (buf.len >= PATH_MAX)
+               return bad_path;
+       memcpy(pathname, buf.buf, buf.len + 1);
 
-int git_mkstemp_mode(char *pattern, int mode)
-{
-       /* mkstemp is just mkstemps with no suffix */
-       return git_mkstemps_mode(pattern, 0, mode);
-}
+       strbuf_release(&buf);
+       len = strlen(pathname);
 
-int gitmkstemps(char *pattern, int suffix_len)
-{
-       return git_mkstemps_mode(pattern, suffix_len, 0600);
+       va_start(args, fmt);
+       len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
+       va_end(args);
+       if (len >= PATH_MAX)
+               return bad_path;
+       return cleanup_path(pathname);
 }
 
 int validate_headref(const char *path)
index 6cb0dd19344ba1169b23b7e4949033f8a9088a00..205e48aa3a7b912005565ac9c296205a897a1476 100644 (file)
@@ -7,6 +7,7 @@ Git - Perl interface to the Git version control system
 
 package Git;
 
+use 5.008;
 use strict;
 
 
index 4ab21d61b808d2e396419fb41419bcea7ab3cde4..a2ffb6402d45420dff4dcd545dfa08b57305d8cd 100644 (file)
@@ -38,7 +38,7 @@ $(makfile): ../GIT-CFLAGS Makefile
        echo '  echo $(instdir_SQ)' >> $@
 else
 $(makfile): Makefile.PL ../GIT-CFLAGS
-       $(PERL_PATH) $< PREFIX='$(prefix_SQ)'
+       $(PERL_PATH) $< PREFIX='$(prefix_SQ)' INSTALL_BASE=''
 endif
 
 # this is just added comfort for calling make directly in perl dir
index e3d0bda31a98372eb9b6a8c2cd0fd65917a9dbde..49cb08df96faa90101bb25d8f96acaffc066f19b 100644 (file)
@@ -35,7 +35,9 @@ static void *preload_thread(void *_data)
        struct index_state *index = p->index;
        struct cache_entry **cep = index->cache + p->offset;
        struct cache_def cache;
+       struct pathspec pathspec;
 
+       init_pathspec(&pathspec, p->pathspec);
        memset(&cache, 0, sizeof(cache));
        nr = p->nr;
        if (nr + p->offset > index->cache_nr)
@@ -51,7 +53,7 @@ static void *preload_thread(void *_data)
                        continue;
                if (ce_uptodate(ce))
                        continue;
-               if (!ce_path_match(ce, p->pathspec))
+               if (!ce_path_match(ce, &pathspec))
                        continue;
                if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
                        continue;
@@ -61,6 +63,7 @@ static void *preload_thread(void *_data)
                        continue;
                ce_mark_uptodate(ce);
        } while (--nr > 0);
+       free_pathspec(&pathspec);
        return NULL;
 }
 
index f85444b27d4815547f7cef542291b1f6616f77d5..85499347514ec3e1d62d6caa9f53b886c556e345 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -403,8 +403,8 @@ static char *replace_encoding_header(char *buf, const char *encoding)
        return strbuf_detach(&tmp, NULL);
 }
 
-static char *logmsg_reencode(const struct commit *commit,
-                            const char *output_encoding)
+char *logmsg_reencode(const struct commit *commit,
+                     const char *output_encoding)
 {
        static const char *utf8 = "UTF-8";
        const char *use_encoding;
@@ -555,6 +555,7 @@ struct format_commit_context {
        const struct pretty_print_context *pretty_ctx;
        unsigned commit_header_parsed:1;
        unsigned commit_message_parsed:1;
+       char *message;
        size_t width, indent1, indent2;
 
        /* These offsets are relative to the start of the commit message. */
@@ -591,7 +592,7 @@ static int add_again(struct strbuf *sb, struct chunk *chunk)
 
 static void parse_commit_header(struct format_commit_context *context)
 {
-       const char *msg = context->commit->buffer;
+       const char *msg = context->message;
        int i;
 
        for (i = 0; msg[i]; i++) {
@@ -677,8 +678,8 @@ const char *format_subject(struct strbuf *sb, const char *msg,
 
 static void parse_commit_message(struct format_commit_context *c)
 {
-       const char *msg = c->commit->buffer + c->message_off;
-       const char *start = c->commit->buffer;
+       const char *msg = c->message + c->message_off;
+       const char *start = c->message;
 
        msg = skip_empty_lines(msg);
        c->subject_off = msg - start;
@@ -741,7 +742,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
 {
        struct format_commit_context *c = context;
        const struct commit *commit = c->commit;
-       const char *msg = commit->buffer;
+       const char *msg = c->message;
        struct commit_list *p;
        int h1, h2;
 
@@ -886,8 +887,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
        case 'N':
                if (c->pretty_ctx->show_notes) {
                        format_display_notes(commit->object.sha1, sb,
-                                   git_log_output_encoding ? git_log_output_encoding
-                                                           : git_commit_encoding, 0);
+                                   get_log_output_encoding(), 0);
                        return 1;
                }
                return 0;
@@ -1012,13 +1012,27 @@ void format_commit_message(const struct commit *commit,
                           const struct pretty_print_context *pretty_ctx)
 {
        struct format_commit_context context;
+       static const char utf8[] = "UTF-8";
+       const char *enc;
+       const char *output_enc = pretty_ctx->output_encoding;
 
        memset(&context, 0, sizeof(context));
        context.commit = commit;
        context.pretty_ctx = pretty_ctx;
        context.wrap_start = sb->len;
+       context.message = commit->buffer;
+       if (output_enc) {
+               enc = get_header(commit, "encoding");
+               enc = enc ? enc : utf8;
+               if (strcmp(enc, output_enc))
+                       context.message = logmsg_reencode(commit, output_enc);
+       }
+
        strbuf_expand(sb, format, format_commit_item, &context);
        rewrap_message_tail(sb, &context, 0, 0, 0);
+
+       if (context.message != commit->buffer)
+               free(context.message);
 }
 
 static void pp_header(enum cmit_fmt fmt,
@@ -1159,11 +1173,7 @@ char *reencode_commit_message(const struct commit *commit, const char **encoding
 {
        const char *encoding;
 
-       encoding = (git_log_output_encoding
-                   ? git_log_output_encoding
-                   : git_commit_encoding);
-       if (!encoding)
-               encoding = "UTF-8";
+       encoding = get_log_output_encoding();
        if (encoding_p)
                *encoding_p = encoding;
        return logmsg_reencode(commit, encoding);
index b515fa2de332cc570a8a32861bd8d6491b61133e..a03fabf060081e9fa5e0d753cd0d9cf4bca5062e 100644 (file)
@@ -90,7 +90,7 @@ static void walk_commit_list(struct rev_info *revs)
 {
        int i;
        struct commit *commit;
-       struct object_array objects = { 0, 0, NULL };
+       struct object_array objects = OBJECT_ARRAY_INIT;
 
        /* Walk all commits, process their trees */
        while ((commit = get_revision(revs)) != NULL)
index 1f42473e8070a05ada8c56b0d60537227a5223ec..7772079ad3101a2a51cfef1c5b1c250ed3ff37de 100644 (file)
@@ -608,6 +608,29 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                ce->ce_mode = ce_mode_from_stat(ent, st_mode);
        }
 
+       /* When core.ignorecase=true, determine if a directory of the same name but differing
+        * case already exists within the Git repository.  If it does, ensure the directory
+        * case of the file being added to the repository matches (is folded into) the existing
+        * entry's directory case.
+        */
+       if (ignore_case) {
+               const char *startPtr = ce->name;
+               const char *ptr = startPtr;
+               while (*ptr) {
+                       while (*ptr && *ptr != '/')
+                               ++ptr;
+                       if (*ptr == '/') {
+                               struct cache_entry *foundce;
+                               ++ptr;
+                               foundce = index_name_exists(&the_index, ce->name, ptr - ce->name, ignore_case);
+                               if (foundce) {
+                                       memcpy((void *)startPtr, foundce->name + (startPtr - ce->name), ptr - startPtr);
+                                       startPtr = ptr;
+                               }
+                       }
+               }
+       }
+
        alias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case);
        if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
                /* Nothing changed, really */
@@ -683,30 +706,9 @@ int ce_same_name(struct cache_entry *a, struct cache_entry *b)
        return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
 }
 
-int ce_path_match(const struct cache_entry *ce, const char **pathspec)
+int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec)
 {
-       const char *match, *name;
-       int len;
-
-       if (!pathspec)
-               return 1;
-
-       len = ce_namelen(ce);
-       name = ce->name;
-       while ((match = *pathspec++) != NULL) {
-               int matchlen = strlen(match);
-               if (matchlen > len)
-                       continue;
-               if (memcmp(name, match, matchlen))
-                       continue;
-               if (matchlen && name[matchlen-1] == '/')
-                       return 1;
-               if (name[matchlen] == '/' || !name[matchlen])
-                       return 1;
-               if (!matchlen)
-                       return 1;
-       }
-       return 0;
+       return match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL);
 }
 
 /*
index 4879615cad7527dc5346cd1a85bd56b9d11e052b..5d81d39a525830f6bacba88143ab6a4552748441 100644 (file)
@@ -239,7 +239,6 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
 
        commit->parents = xcalloc(sizeof(struct commit_list), 1);
        commit->parents->item = commit_info->commit;
-       commit->object.flags &= ~(ADDED | SEEN | SHOWN);
 }
 
 void get_reflog_selector(struct strbuf *sb,
diff --git a/refs.c b/refs.c
index b5400674d7a1fcf4cc16dd630b8671b2ad7b9f7b..e3c05110e58ca5684f9c43b1e1a5eb2587c96828 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -157,7 +157,7 @@ static struct cached_refs {
        char did_packed;
        struct ref_list *loose;
        struct ref_list *packed;
-} cached_refs;
+} cached_refs, submodule_refs;
 static struct ref_list *current_ref;
 
 static struct ref_list *extra_refs;
@@ -229,23 +229,45 @@ void clear_extra_refs(void)
        extra_refs = NULL;
 }
 
-static struct ref_list *get_packed_refs(void)
+static struct ref_list *get_packed_refs(const char *submodule)
 {
-       if (!cached_refs.did_packed) {
-               FILE *f = fopen(git_path("packed-refs"), "r");
-               cached_refs.packed = NULL;
+       const char *packed_refs_file;
+       struct cached_refs *refs;
+
+       if (submodule) {
+               packed_refs_file = git_path_submodule(submodule, "packed-refs");
+               refs = &submodule_refs;
+               free_ref_list(refs->packed);
+       } else {
+               packed_refs_file = git_path("packed-refs");
+               refs = &cached_refs;
+       }
+
+       if (!refs->did_packed || submodule) {
+               FILE *f = fopen(packed_refs_file, "r");
+               refs->packed = NULL;
                if (f) {
-                       read_packed_refs(f, &cached_refs);
+                       read_packed_refs(f, refs);
                        fclose(f);
                }
-               cached_refs.did_packed = 1;
+               refs->did_packed = 1;
        }
-       return cached_refs.packed;
+       return refs->packed;
 }
 
-static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
+static struct ref_list *get_ref_dir(const char *submodule, const char *base,
+                                   struct ref_list *list)
 {
-       DIR *dir = opendir(git_path("%s", base));
+       DIR *dir;
+       const char *path;
+
+       if (submodule)
+               path = git_path_submodule(submodule, "%s", base);
+       else
+               path = git_path("%s", base);
+
+
+       dir = opendir(path);
 
        if (dir) {
                struct dirent *de;
@@ -261,6 +283,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
                        struct stat st;
                        int flag;
                        int namelen;
+                       const char *refdir;
 
                        if (de->d_name[0] == '.')
                                continue;
@@ -270,16 +293,27 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
                        if (has_extension(de->d_name, ".lock"))
                                continue;
                        memcpy(ref + baselen, de->d_name, namelen+1);
-                       if (stat(git_path("%s", ref), &st) < 0)
+                       refdir = submodule
+                               ? git_path_submodule(submodule, "%s", ref)
+                               : git_path("%s", ref);
+                       if (stat(refdir, &st) < 0)
                                continue;
                        if (S_ISDIR(st.st_mode)) {
-                               list = get_ref_dir(ref, list);
+                               list = get_ref_dir(submodule, ref, list);
                                continue;
                        }
-                       if (!resolve_ref(ref, sha1, 1, &flag)) {
+                       if (submodule) {
                                hashclr(sha1);
-                               flag |= REF_BROKEN;
-                       }
+                               flag = 0;
+                               if (resolve_gitlink_ref(submodule, ref, sha1) < 0) {
+                                       hashclr(sha1);
+                                       flag |= REF_BROKEN;
+                               }
+                       } else
+                               if (!resolve_ref(ref, sha1, 1, &flag)) {
+                                       hashclr(sha1);
+                                       flag |= REF_BROKEN;
+                               }
                        list = add_ref(ref, sha1, flag, list, NULL);
                }
                free(ref);
@@ -322,10 +356,16 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
        for_each_rawref(warn_if_dangling_symref, &data);
 }
 
-static struct ref_list *get_loose_refs(void)
+static struct ref_list *get_loose_refs(const char *submodule)
 {
+       if (submodule) {
+               free_ref_list(submodule_refs.loose);
+               submodule_refs.loose = get_ref_dir(submodule, "refs", NULL);
+               return submodule_refs.loose;
+       }
+
        if (!cached_refs.did_loose) {
-               cached_refs.loose = get_ref_dir("refs", NULL);
+               cached_refs.loose = get_ref_dir(NULL, "refs", NULL);
                cached_refs.did_loose = 1;
        }
        return cached_refs.loose;
@@ -459,7 +499,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                git_snpath(path, sizeof(path), "%s", ref);
                /* Special case: non-existing file. */
                if (lstat(path, &st) < 0) {
-                       struct ref_list *list = get_packed_refs();
+                       struct ref_list *list = get_packed_refs(NULL);
                        while (list) {
                                if (!strcmp(ref, list->name)) {
                                        hashcpy(sha1, list->sha1);
@@ -588,7 +628,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
                return -1;
 
        if ((flag & REF_ISPACKED)) {
-               struct ref_list *list = get_packed_refs();
+               struct ref_list *list = get_packed_refs(NULL);
 
                while (list) {
                        if (!strcmp(list->name, ref)) {
@@ -615,12 +655,12 @@ fallback:
        return -1;
 }
 
-static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
-                          int flags, void *cb_data)
+static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,
+                          int trim, int flags, void *cb_data)
 {
        int retval = 0;
-       struct ref_list *packed = get_packed_refs();
-       struct ref_list *loose = get_loose_refs();
+       struct ref_list *packed = get_packed_refs(submodule);
+       struct ref_list *loose = get_loose_refs(submodule);
 
        struct ref_list *extra;
 
@@ -657,24 +697,54 @@ end_each:
        return retval;
 }
 
-int head_ref(each_ref_fn fn, void *cb_data)
+
+static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
        unsigned char sha1[20];
        int flag;
 
+       if (submodule) {
+               if (resolve_gitlink_ref(submodule, "HEAD", sha1) == 0)
+                       return fn("HEAD", sha1, 0, cb_data);
+
+               return 0;
+       }
+
        if (resolve_ref("HEAD", sha1, 1, &flag))
                return fn("HEAD", sha1, flag, cb_data);
+
        return 0;
 }
 
+int head_ref(each_ref_fn fn, void *cb_data)
+{
+       return do_head_ref(NULL, fn, cb_data);
+}
+
+int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return do_head_ref(submodule, fn, cb_data);
+}
+
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/", fn, 0, 0, cb_data);
+       return do_for_each_ref(NULL, "refs/", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(submodule, "refs/", fn, 0, 0, cb_data);
 }
 
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(prefix, fn, strlen(prefix), 0, cb_data);
+       return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+}
+
+int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+               each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
@@ -682,19 +752,34 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)
        return for_each_ref_in("refs/tags/", fn, cb_data);
 }
 
+int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data);
+}
+
 int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
        return for_each_ref_in("refs/heads/", fn, cb_data);
 }
 
+int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in_submodule(submodule, "refs/heads/", fn, cb_data);
+}
+
 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_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in_submodule(submodule, "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);
+       return do_for_each_ref(NULL, "refs/replace/", fn, 13, 0, cb_data);
 }
 
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
@@ -734,7 +819,7 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
 
 int for_each_rawref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/", fn, 0,
+       return do_for_each_ref(NULL, "refs/", fn, 0,
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
@@ -958,7 +1043,7 @@ 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(NULL), 0)) {
                last_errno = ENOTDIR;
                goto error_return;
        }
@@ -1021,7 +1106,7 @@ static int repack_without_ref(const char *refname)
        int fd;
        int found = 0;
 
-       packed_ref_list = get_packed_refs();
+       packed_ref_list = get_packed_refs(NULL);
        for (list = packed_ref_list; list; list = list->next) {
                if (!strcmp(refname, list->name)) {
                        found = 1;
@@ -1119,10 +1204,10 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        if (!symref)
                return error("refname %s not found", oldref);
 
-       if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
+       if (!is_refname_available(newref, oldref, get_packed_refs(NULL), 0))
                return 1;
 
-       if (!is_refname_available(newref, oldref, get_loose_refs(), 0))
+       if (!is_refname_available(newref, oldref, get_loose_refs(NULL), 0))
                return 1;
 
        lock = lock_ref_sha1_basic(renamed_ref, NULL, 0, NULL);
diff --git a/refs.h b/refs.h
index 762ce504b5eba3aacff204321a7c41db2b1e6053..5e7a9a59f5f6b687d15eb37a11696433bdc8f15c 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -28,6 +28,14 @@ extern int for_each_replace_ref(each_ref_fn, void *);
 extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
 extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);
 
+extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+               each_ref_fn fn, void *cb_data);
+extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+
 static inline const char *has_glob_specials(const char *pattern)
 {
        return strpbrk(pattern, "?*[");
index 9143ec7a1772c45b6c3d4c84f6fc000040400dee..ca42a126ad04514f0ed8378768ebce98cfc5a659 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -493,7 +493,7 @@ static void read_config(void)
 }
 
 /*
- * We need to make sure the tracking branches are well formed, but a
+ * We need to make sure the remote-tracking branches are well formed, but a
  * wildcard refspec in "struct refspec" must have a trailing slash. We
  * temporarily drop the trailing '/' while calling check_ref_format(),
  * and put it back.  The caller knows that a CHECK_REF_FORMAT_ONELEVEL
index 78bbcf140346f14bbb961ed5e39e5752568b8d95..d2608434750c336c3f3881efda8373b7c67d4b11 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -319,9 +319,13 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
                if (!mmfile[i].ptr && !mmfile[i].size)
                        mmfile[i].ptr = xstrdup("");
        }
+       /*
+        * NEEDSWORK: handle conflicts from merges with
+        * merge.renormalize set, too
+        */
        ll_merge(&result, path, &mmfile[0], NULL,
                 &mmfile[1], "ours",
-                &mmfile[2], "theirs", 0);
+                &mmfile[2], "theirs", NULL);
        for (i = 0; i < 3; i++)
                free(mmfile[i].ptr);
 
@@ -378,7 +382,13 @@ static int merge(const char *name, const char *path)
        }
        ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", 0);
        if (!ret) {
-               FILE *f = fopen(path, "w");
+               FILE *f;
+
+               if (utime(rerere_path(name, "postimage"), NULL) < 0)
+                       warning("failed utime() on %s: %s",
+                                       rerere_path(name, "postimage"),
+                                       strerror(errno));
+               f = fopen(path, "w");
                if (!f)
                        return error("Could not open %s: %s", path,
                                     strerror(errno));
index 7e82efd9324e84582732485c53c6c25a24c29997..86d24704896d71de7bb554a3925ea88e2be3417b 100644 (file)
@@ -323,7 +323,7 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct
                 * tagged commit by specifying both --simplify-by-decoration
                 * and pathspec.
                 */
-               if (!revs->prune_data)
+               if (!revs->prune_data.nr)
                        return REV_TREE_SAME;
        }
 
@@ -444,15 +444,15 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
        commit->object.flags |= TREESAME;
 }
 
-static void insert_by_date_cached(struct commit *p, struct commit_list **head,
+static void commit_list_insert_by_date_cached(struct commit *p, struct commit_list **head,
                    struct commit_list *cached_base, struct commit_list **cache)
 {
        struct commit_list *new_entry;
 
        if (cached_base && p->date < cached_base->item->date)
-               new_entry = insert_by_date(p, &cached_base->next);
+               new_entry = commit_list_insert_by_date(p, &cached_base->next);
        else
-               new_entry = insert_by_date(p, head);
+               new_entry = commit_list_insert_by_date(p, head);
 
        if (cache && (!*cache || p->date < (*cache)->item->date))
                *cache = new_entry;
@@ -494,7 +494,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
                        if (p->object.flags & SEEN)
                                continue;
                        p->object.flags |= SEEN;
-                       insert_by_date_cached(p, list, cached_base, cache_ptr);
+                       commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
                return 0;
        }
@@ -521,7 +521,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
                p->object.flags |= left_flag;
                if (!(p->object.flags & SEEN)) {
                        p->object.flags |= SEEN;
-                       insert_by_date_cached(p, list, cached_base, cache_ptr);
+                       commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
                if (revs->first_parent_only)
                        break;
@@ -553,11 +553,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
 
        left_first = left_count < right_count;
        init_patch_ids(&ids);
-       if (revs->diffopt.nr_paths) {
-               ids.diffopts.nr_paths = revs->diffopt.nr_paths;
-               ids.diffopts.paths = revs->diffopt.paths;
-               ids.diffopts.pathlens = revs->diffopt.pathlens;
-       }
+       ids.diffopts.pathspec = revs->diffopt.pathspec;
 
        /* Compute patch-ids for one side */
        for (p = list; p; p = p->next) {
@@ -820,12 +816,12 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
        cb->all_flags = flags;
 }
 
-static void handle_refs(struct rev_info *revs, unsigned flags,
-               int (*for_each)(each_ref_fn, void *))
+static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
+               int (*for_each)(const char *, each_ref_fn, void *))
 {
        struct all_refs_cb cb;
        init_all_refs_cb(&cb, revs, flags);
-       for_each(handle_one_ref, &cb);
+       for_each(submodule, handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
@@ -973,7 +969,7 @@ static void prepare_show_merge(struct rev_info *revs)
                struct cache_entry *ce = active_cache[i];
                if (!ce_stage(ce))
                        continue;
-               if (ce_path_match(ce, revs->prune_data)) {
+               if (ce_path_match(ce, &revs->prune_data)) {
                        prune_num++;
                        prune = xrealloc(prune, sizeof(*prune) * prune_num);
                        prune[prune_num-2] = ce->name;
@@ -983,7 +979,8 @@ static void prepare_show_merge(struct rev_info *revs)
                       ce_same_name(ce, active_cache[i+1]))
                        i++;
        }
-       revs->prune_data = prune;
+       free_pathspec(&revs->prune_data);
+       init_pathspec(&revs->prune_data, prune);
        revs->limited = 1;
 }
 
@@ -1148,6 +1145,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                               int *unkc, const char **unkv)
 {
        const char *arg = argv[0];
+       const char *optarg;
+       int argcount;
 
        /* pseudo revision arguments */
        if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
@@ -1160,11 +1159,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                return 1;
        }
 
-       if (!prefixcmp(arg, "--max-count=")) {
-               revs->max_count = atoi(arg + 12);
+       if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
+               revs->max_count = atoi(optarg);
                revs->no_walk = 0;
-       } else if (!prefixcmp(arg, "--skip=")) {
-               revs->skip_count = atoi(arg + 7);
+               return argcount;
+       } else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
+               revs->skip_count = atoi(optarg);
+               return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
        /* accept -<digit>, like traditional "head" */
                revs->max_count = atoi(arg + 1);
@@ -1178,18 +1179,24 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!prefixcmp(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
                revs->no_walk = 0;
-       } else if (!prefixcmp(arg, "--max-age=")) {
-               revs->max_age = atoi(arg + 10);
-       } else if (!prefixcmp(arg, "--since=")) {
-               revs->max_age = approxidate(arg + 8);
-       } else if (!prefixcmp(arg, "--after=")) {
-               revs->max_age = approxidate(arg + 8);
-       } else if (!prefixcmp(arg, "--min-age=")) {
-               revs->min_age = atoi(arg + 10);
-       } else if (!prefixcmp(arg, "--before=")) {
-               revs->min_age = approxidate(arg + 9);
-       } else if (!prefixcmp(arg, "--until=")) {
-               revs->min_age = approxidate(arg + 8);
+       } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
+               revs->max_age = atoi(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("since", argv, &optarg))) {
+               revs->max_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("after", argv, &optarg))) {
+               revs->max_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
+               revs->min_age = atoi(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("before", argv, &optarg))) {
+               revs->min_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("until", argv, &optarg))) {
+               revs->min_age = approxidate(optarg);
+               return argcount;
        } else if (!strcmp(arg, "--first-parent")) {
                revs->first_parent_only = 1;
        } else if (!strcmp(arg, "--ancestry-path")) {
@@ -1295,6 +1302,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
        } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+               /*
+                * Detached form ("--pretty X" as opposed to "--pretty=X")
+                * not allowed, since the argument is optional.
+                */
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+9, revs);
@@ -1359,21 +1370,25 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } 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);
+       } else if ((argcount = parse_long_opt("date", argv, &optarg))) {
+               revs->date_mode = parse_date_format(optarg);
                revs->date_mode_explicit = 1;
+               return argcount;
        } else if (!strcmp(arg, "--log-size")) {
                revs->show_log_size = 1;
        }
        /*
         * Grepping the commit log
         */
-       else if (!prefixcmp(arg, "--author=")) {
-               add_header_grep(revs, GREP_HEADER_AUTHOR, arg+9);
-       } else if (!prefixcmp(arg, "--committer=")) {
-               add_header_grep(revs, GREP_HEADER_COMMITTER, arg+12);
-       } else if (!prefixcmp(arg, "--grep=")) {
-               add_message_grep(revs, arg+7);
+       else if ((argcount = parse_long_opt("author", argv, &optarg))) {
+               add_header_grep(revs, GREP_HEADER_AUTHOR, optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("committer", argv, &optarg))) {
+               add_header_grep(revs, GREP_HEADER_COMMITTER, optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
+               add_message_grep(revs, optarg);
+               return argcount;
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
@@ -1382,12 +1397,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
-       } else if (!prefixcmp(arg, "--encoding=")) {
-               arg += 11;
-               if (strcmp(arg, "none"))
-                       git_log_output_encoding = xstrdup(arg);
+       } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+               if (strcmp(optarg, "none"))
+                       git_log_output_encoding = xstrdup(optarg);
                else
                        git_log_output_encoding = "";
+               return argcount;
        } else if (!strcmp(arg, "--reverse")) {
                revs->reverse ^= 1;
        } else if (!strcmp(arg, "--children")) {
@@ -1417,14 +1432,14 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
        ctx->argc -= n;
 }
 
-static int for_each_bad_bisect_ref(each_ref_fn fn, void *cb_data)
+static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/bisect/bad", fn, cb_data);
+       return for_each_ref_in_submodule(submodule, "refs/bisect/bad", fn, cb_data);
 }
 
-static int for_each_good_bisect_ref(each_ref_fn fn, void *cb_data)
+static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/bisect/good", fn, cb_data);
+       return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data);
 }
 
 static void append_prune_data(const char ***prune_data, const char **av)
@@ -1466,6 +1481,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 {
        int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
        const char **prune_data = NULL;
+       const char *submodule = NULL;
+       const char *optarg;
+       int argcount;
+
+       if (opt)
+               submodule = opt->submodule;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -1490,32 +1511,33 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                        int opts;
 
                        if (!strcmp(arg, "--all")) {
-                               handle_refs(revs, flags, for_each_ref);
-                               handle_refs(revs, flags, head_ref);
+                               handle_refs(submodule, revs, flags, for_each_ref_submodule);
+                               handle_refs(submodule, revs, flags, head_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--branches")) {
-                               handle_refs(revs, flags, for_each_branch_ref);
+                               handle_refs(submodule, revs, flags, for_each_branch_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
-                               handle_refs(revs, flags, for_each_bad_bisect_ref);
-                               handle_refs(revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
+                               handle_refs(submodule, revs, flags, for_each_bad_bisect_ref);
+                               handle_refs(submodule, revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
                                revs->bisect = 1;
                                continue;
                        }
                        if (!strcmp(arg, "--tags")) {
-                               handle_refs(revs, flags, for_each_tag_ref);
+                               handle_refs(submodule, revs, flags, for_each_tag_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--remotes")) {
-                               handle_refs(revs, flags, for_each_remote_ref);
+                               handle_refs(submodule, revs, flags, for_each_remote_ref_submodule);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--glob=")) {
+                       if ((argcount = parse_long_opt("glob", argv + i, &optarg))) {
                                struct all_refs_cb cb;
+                               i += argcount - 1;
                                init_all_refs_cb(&cb, revs, flags);
-                               for_each_glob_ref(handle_one_ref, arg + 7, &cb);
+                               for_each_glob_ref(handle_one_ref, optarg, &cb);
                                continue;
                        }
                        if (!prefixcmp(arg, "--branches=")) {
@@ -1595,7 +1617,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        }
 
        if (prune_data)
-               revs->prune_data = get_pathspec(revs->prefix, prune_data);
+               init_pathspec(&revs->prune_data, get_pathspec(revs->prefix, prune_data));
 
        if (revs->def == NULL)
                revs->def = opt ? opt->def : NULL;
@@ -1626,13 +1648,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        if (revs->topo_order)
                revs->limited = 1;
 
-       if (revs->prune_data) {
-               diff_tree_setup_paths(revs->prune_data, &revs->pruning);
+       if (revs->prune_data.nr) {
+               diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning);
                /* Can't prune commits with rename following: the paths change.. */
                if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
                        revs->prune = 1;
                if (!revs->full_diff)
-                       diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
+                       diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt);
        }
        if (revs->combine_merges)
                revs->ignore_merges = 0;
@@ -1866,7 +1888,7 @@ int prepare_revision_walk(struct rev_info *revs)
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
-                               insert_by_date(commit, &revs->commits);
+                               commit_list_insert_by_date(commit, &revs->commits);
                        }
                }
                e++;
@@ -2005,8 +2027,10 @@ static struct commit *get_revision_1(struct rev_info *revs)
                revs->commits = entry->next;
                free(entry);
 
-               if (revs->reflog_info)
+               if (revs->reflog_info) {
                        fake_reflog_parent(revs->reflog_info, commit);
+                       commit->object.flags &= ~(ADDED | SEEN | SHOWN);
+               }
 
                /*
                 * If we haven't done the list limiting, we need to look at
index 36fdf22b299c4cc7e6f20d767d5c6a6c3f69d952..82509dd1d9ad7b1824971be4884409060f10aee3 100644 (file)
@@ -34,7 +34,7 @@ struct rev_info {
        /* Basic information */
        const char *prefix;
        const char *def;
-       void *prune_data;
+       struct pathspec prune_data;
        unsigned int early_output;
 
        /* Traversal flags */
@@ -151,6 +151,7 @@ extern volatile show_early_output_fn_t show_early_output;
 struct setup_revision_opt {
        const char *def;
        void (*tweak)(struct rev_info *, struct setup_revision_opt *);
+       const char *submodule;
 };
 
 extern void init_revisions(struct rev_info *revs, const char *prefix);
index 60b4ba66eb8cac3378326378dc4e0cbdb88162ac..05d7ab118b3e1473cdf559d3f6337b26bb9aac81 100644 (file)
@@ -5,6 +5,7 @@ struct send_pack_args {
        unsigned verbose:1,
                quiet:1,
                porcelain:1,
+               progress:1,
                send_mirror:1,
                force_update:1,
                use_thin_pack:1,
diff --git a/setup.c b/setup.c
index 276916052795c5ffa872b57a7e6723edc71dfdc4..021d0133ae1d6cf7f3a6a8284cb5b6e9a42dbe0d 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -4,13 +4,16 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 
-const char *prefix_path(const char *prefix, int len, const char *path)
+char *prefix_path(const char *prefix, int len, const char *path)
 {
        const char *orig = path;
-       char *sanitized = xmalloc(len + strlen(path) + 1);
-       if (is_absolute_path(orig))
-               strcpy(sanitized, path);
-       else {
+       char *sanitized;
+       if (is_absolute_path(orig)) {
+               const char *temp = make_absolute_path(path);
+               sanitized = xmalloc(len + strlen(temp) + 1);
+               strcpy(sanitized, temp);
+       } else {
+               sanitized = xmalloc(len + strlen(path) + 1);
                if (len)
                        memcpy(sanitized, prefix, len);
                strcpy(sanitized + len, path);
@@ -46,7 +49,7 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
        static char path[PATH_MAX];
 #ifndef WIN32
-       if (!pfx || !*pfx || is_absolute_path(arg))
+       if (!pfx_len || is_absolute_path(arg))
                return arg;
        memcpy(path, pfx, pfx_len);
        strcpy(path + pfx_len, arg);
@@ -55,7 +58,7 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
        /* don't add prefix to absolute paths, but still replace '\' by '/' */
        if (is_absolute_path(arg))
                pfx_len = 0;
-       else
+       else if (pfx_len)
                memcpy(path, pfx, pfx_len);
        strcpy(path + pfx_len, arg);
        for (p = path + pfx_len; *p; p++)
@@ -208,24 +211,6 @@ int is_inside_work_tree(void)
        return inside_work_tree;
 }
 
-/*
- * set_work_tree() is only ever called if you set GIT_DIR explicitly.
- * The old behaviour (which we retain here) is to set the work tree root
- * to the cwd, unless overridden by the config, the command line, or
- * GIT_WORK_TREE.
- */
-static const char *set_work_tree(const char *dir)
-{
-       char buffer[PATH_MAX + 1];
-
-       if (!getcwd(buffer, sizeof(buffer)))
-               die ("Could not get the current working directory");
-       git_work_tree_cfg = xstrdup(buffer);
-       inside_work_tree = 1;
-
-       return NULL;
-}
-
 void setup_work_tree(void)
 {
        const char *work_tree, *git_dir;
@@ -239,13 +224,33 @@ void setup_work_tree(void)
                git_dir = make_absolute_path(git_dir);
        if (!work_tree || chdir(work_tree))
                die("This operation must be run in a work tree");
+
+       /*
+        * Make sure subsequent git processes find correct worktree
+        * if $GIT_WORK_TREE is set relative
+        */
+       if (getenv(GIT_WORK_TREE_ENVIRONMENT))
+               setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
+
        set_git_dir(make_relative_path(git_dir, work_tree));
        initialized = 1;
 }
 
-static int check_repository_format_gently(int *nongit_ok)
+static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
 {
-       git_config(check_repository_format_version, NULL);
+       char repo_config[PATH_MAX+1];
+
+       /*
+        * git_config() can't be used here because it calls git_pathdup()
+        * to get $GIT_CONFIG/config. That call will make setup_git_env()
+        * set git_dir to ".git".
+        *
+        * We are in gitdir setup, no git dir has been found useable yet.
+        * Use a gentler version of git_config() to check if this repo
+        * is a good one.
+        */
+       snprintf(repo_config, PATH_MAX, "%s/config", gitdir);
+       git_config_early(check_repository_format_version, NULL, repo_config);
        if (GIT_REPO_VERSION < repository_format_version) {
                if (!nongit_ok)
                        die ("Expected git repo version <= %d, found %d",
@@ -313,21 +318,200 @@ const char *read_gitfile_gently(const char *path)
        return path;
 }
 
+static const char *setup_explicit_git_dir(const char *gitdirenv,
+                                         char *cwd, int len,
+                                         int *nongit_ok)
+{
+       const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
+       const char *worktree;
+       char *gitfile;
+
+       if (PATH_MAX - 40 < strlen(gitdirenv))
+               die("'$%s' too big", GIT_DIR_ENVIRONMENT);
+
+       gitfile = (char*)read_gitfile_gently(gitdirenv);
+       if (gitfile) {
+               gitfile = xstrdup(gitfile);
+               gitdirenv = gitfile;
+       }
+
+       if (!is_git_directory(gitdirenv)) {
+               if (nongit_ok) {
+                       *nongit_ok = 1;
+                       free(gitfile);
+                       return NULL;
+               }
+               die("Not a git repository: '%s'", gitdirenv);
+       }
+
+       if (check_repository_format_gently(gitdirenv, nongit_ok)) {
+               free(gitfile);
+               return NULL;
+       }
+
+       /* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */
+       if (work_tree_env)
+               set_git_work_tree(work_tree_env);
+       else if (is_bare_repository_cfg > 0) {
+               if (git_work_tree_cfg) /* #22.2, #30 */
+                       die("core.bare and core.worktree do not make sense");
+
+               /* #18, #26 */
+               set_git_dir(gitdirenv);
+               free(gitfile);
+               return NULL;
+       }
+       else if (git_work_tree_cfg) { /* #6, #14 */
+               if (is_absolute_path(git_work_tree_cfg))
+                       set_git_work_tree(git_work_tree_cfg);
+               else {
+                       char core_worktree[PATH_MAX];
+                       if (chdir(gitdirenv))
+                               die_errno("Could not chdir to '%s'", gitdirenv);
+                       if (chdir(git_work_tree_cfg))
+                               die_errno("Could not chdir to '%s'", git_work_tree_cfg);
+                       if (!getcwd(core_worktree, PATH_MAX))
+                               die_errno("Could not get directory '%s'", git_work_tree_cfg);
+                       if (chdir(cwd))
+                               die_errno("Could not come back to cwd");
+                       set_git_work_tree(core_worktree);
+               }
+       }
+       else /* #2, #10 */
+               set_git_work_tree(".");
+
+       /* set_git_work_tree() must have been called by now */
+       worktree = get_git_work_tree();
+
+       /* both get_git_work_tree() and cwd are already normalized */
+       if (!strcmp(cwd, worktree)) { /* cwd == worktree */
+               set_git_dir(gitdirenv);
+               free(gitfile);
+               return NULL;
+       }
+
+       if (!prefixcmp(cwd, worktree) &&
+           cwd[strlen(worktree)] == '/') { /* cwd inside worktree */
+               set_git_dir(make_absolute_path(gitdirenv));
+               if (chdir(worktree))
+                       die_errno("Could not chdir to '%s'", worktree);
+               cwd[len++] = '/';
+               cwd[len] = '\0';
+               free(gitfile);
+               return cwd + strlen(worktree) + 1;
+       }
+
+       /* cwd outside worktree */
+       set_git_dir(gitdirenv);
+       free(gitfile);
+       return NULL;
+}
+
+static const char *setup_discovered_git_dir(const char *gitdir,
+                                           char *cwd, int offset, int len,
+                                           int *nongit_ok)
+{
+       if (check_repository_format_gently(gitdir, nongit_ok))
+               return NULL;
+
+       /* --work-tree is set without --git-dir; use discovered one */
+       if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
+               if (offset != len && !is_absolute_path(gitdir))
+                       gitdir = xstrdup(make_absolute_path(gitdir));
+               if (chdir(cwd))
+                       die_errno("Could not come back to cwd");
+               return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok);
+       }
+
+       /* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
+       if (is_bare_repository_cfg > 0) {
+               set_git_dir(offset == len ? gitdir : make_absolute_path(gitdir));
+               if (chdir(cwd))
+                       die_errno("Could not come back to cwd");
+               return NULL;
+       }
+
+       /* #0, #1, #5, #8, #9, #12, #13 */
+       set_git_work_tree(".");
+       if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
+               set_git_dir(gitdir);
+       inside_git_dir = 0;
+       inside_work_tree = 1;
+       if (offset == len)
+               return NULL;
+
+       /* Make "offset" point to past the '/', and add a '/' at the end */
+       offset++;
+       cwd[len++] = '/';
+       cwd[len] = 0;
+       return cwd + offset;
+}
+
+/* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
+static const char *setup_bare_git_dir(char *cwd, int offset, int len, int *nongit_ok)
+{
+       int root_len;
+
+       if (check_repository_format_gently(".", nongit_ok))
+               return NULL;
+
+       /* --work-tree is set without --git-dir; use discovered one */
+       if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
+               const char *gitdir;
+
+               gitdir = offset == len ? "." : xmemdupz(cwd, offset);
+               if (chdir(cwd))
+                       die_errno("Could not come back to cwd");
+               return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok);
+       }
+
+       inside_git_dir = 1;
+       inside_work_tree = 0;
+       if (offset != len) {
+               if (chdir(cwd))
+                       die_errno("Cannot come back to cwd");
+               root_len = offset_1st_component(cwd);
+               cwd[offset > root_len ? offset : root_len] = '\0';
+               set_git_dir(cwd);
+       }
+       else
+               set_git_dir(".");
+       return NULL;
+}
+
+static const char *setup_nongit(const char *cwd, int *nongit_ok)
+{
+       if (!nongit_ok)
+               die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
+       if (chdir(cwd))
+               die_errno("Cannot come back to cwd");
+       *nongit_ok = 1;
+       return NULL;
+}
+
+static dev_t get_device_or_die(const char *path, const char *prefix)
+{
+       struct stat buf;
+       if (stat(path, &buf))
+               die_errno("failed to stat '%s%s%s'",
+                               prefix ? prefix : "",
+                               prefix ? "/" : "", path);
+       return buf.st_dev;
+}
+
 /*
  * We cannot decide in this function whether we are in the work tree or
  * not, since the config can only be read _after_ this function was called.
  */
-const char *setup_git_directory_gently(int *nongit_ok)
+static const char *setup_git_directory_gently_1(int *nongit_ok)
 {
-       const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
        const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
        static char cwd[PATH_MAX+1];
-       const char *gitdirenv;
-       const char *gitfile_dir;
-       int len, offset, ceil_offset, root_len;
+       const char *gitdirenv, *ret;
+       char *gitfile;
+       int len, offset, ceil_offset;
        dev_t current_device = 0;
        int one_filesystem = 1;
-       struct stat buf;
 
        /*
         * Let's assume that we are in a git repository.
@@ -337,47 +521,18 @@ const char *setup_git_directory_gently(int *nongit_ok)
        if (nongit_ok)
                *nongit_ok = 0;
 
+       if (!getcwd(cwd, sizeof(cwd)-1))
+               die_errno("Unable to read current working directory");
+       offset = len = strlen(cwd);
+
        /*
         * If GIT_DIR is set explicitly, we're not going
         * to do any discovery, but we still do repository
         * validation.
         */
        gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
-       if (gitdirenv) {
-               if (PATH_MAX - 40 < strlen(gitdirenv))
-                       die("'$%s' too big", GIT_DIR_ENVIRONMENT);
-               if (is_git_directory(gitdirenv)) {
-                       static char buffer[1024 + 1];
-                       const char *retval;
-
-                       if (!work_tree_env) {
-                               retval = set_work_tree(gitdirenv);
-                               /* config may override worktree */
-                               if (check_repository_format_gently(nongit_ok))
-                                       return NULL;
-                               return retval;
-                       }
-                       if (check_repository_format_gently(nongit_ok))
-                               return NULL;
-                       retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
-                                       get_git_work_tree());
-                       if (!retval || !*retval)
-                               return NULL;
-                       set_git_dir(make_absolute_path(gitdirenv));
-                       if (chdir(work_tree_env) < 0)
-                               die_errno ("Could not chdir to '%s'", work_tree_env);
-                       strcat(buffer, "/");
-                       return retval;
-               }
-               if (nongit_ok) {
-                       *nongit_ok = 1;
-                       return NULL;
-               }
-               die("Not a git repository: '%s'", gitdirenv);
-       }
-
-       if (!getcwd(cwd, sizeof(cwd)-1))
-               die_errno("Unable to read current working directory");
+       if (gitdirenv)
+               return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok);
 
        ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
        if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
@@ -394,51 +549,36 @@ const char *setup_git_directory_gently(int *nongit_ok)
         * - ../../.git/
         *   etc.
         */
-       offset = len = strlen(cwd);
        one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
-       if (one_filesystem) {
-               if (stat(".", &buf))
-                       die_errno("failed to stat '.'");
-               current_device = buf.st_dev;
-       }
+       if (one_filesystem)
+               current_device = get_device_or_die(".", NULL);
        for (;;) {
-               gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
-               if (gitfile_dir) {
-                       if (set_git_dir(gitfile_dir))
-                               die("Repository setup failed");
-                       break;
+               gitfile = (char*)read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+               if (gitfile)
+                       gitdirenv = gitfile = xstrdup(gitfile);
+               else {
+                       if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
+                               gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
                }
-               if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
-                       break;
-               if (is_git_directory(".")) {
-                       inside_git_dir = 1;
-                       if (!work_tree_env)
-                               inside_work_tree = 0;
-                       if (offset != len) {
-                               root_len = offset_1st_component(cwd);
-                               cwd[offset > root_len ? offset : root_len] = '\0';
-                               set_git_dir(cwd);
-                       } else
-                               set_git_dir(".");
-                       check_repository_format_gently(nongit_ok);
-                       return NULL;
+
+               if (gitdirenv) {
+                       ret = setup_discovered_git_dir(gitdirenv,
+                                                      cwd, offset, len,
+                                                      nongit_ok);
+                       free(gitfile);
+                       return ret;
                }
+               free(gitfile);
+
+               if (is_git_directory("."))
+                       return setup_bare_git_dir(cwd, offset, len, nongit_ok);
+
                while (--offset > ceil_offset && cwd[offset] != '/');
-               if (offset <= ceil_offset) {
-                       if (nongit_ok) {
-                               if (chdir(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 (offset <= ceil_offset)
+                       return setup_nongit(cwd, nongit_ok);
                if (one_filesystem) {
-                       if (stat("..", &buf)) {
-                               cwd[offset] = '\0';
-                               die_errno("failed to stat '%s/..'", cwd);
-                       }
-                       if (buf.st_dev != current_device) {
+                       dev_t parent_device = get_device_or_die("..", cwd);
+                       if (parent_device != current_device) {
                                if (nongit_ok) {
                                        if (chdir(cwd))
                                                die_errno("Cannot come back to cwd");
@@ -455,22 +595,18 @@ const char *setup_git_directory_gently(int *nongit_ok)
                        die_errno("Cannot change to '%s/..'", cwd);
                }
        }
+}
 
-       inside_git_dir = 0;
-       if (!work_tree_env)
-               inside_work_tree = 1;
-       root_len = offset_1st_component(cwd);
-       git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
-       if (check_repository_format_gently(nongit_ok))
-               return NULL;
-       if (offset == len)
-               return NULL;
+const char *setup_git_directory_gently(int *nongit_ok)
+{
+       const char *prefix;
 
-       /* Make "offset" point to past the '/', and add a '/' at the end */
-       offset++;
-       cwd[len++] = '/';
-       cwd[len] = 0;
-       return cwd + offset;
+       prefix = setup_git_directory_gently_1(nongit_ok);
+       if (startup_info) {
+               startup_info->have_repository = !nongit_ok || !*nongit_ok;
+               startup_info->prefix = prefix;
+       }
+       return prefix;
 }
 
 int git_config_perm(const char *var, const char *value)
@@ -546,7 +682,7 @@ int check_repository_format_version(const char *var, const char *value, void *cb
 
 int check_repository_format(void)
 {
-       return check_repository_format_gently(NULL);
+       return check_repository_format_gently(get_git_dir(), NULL);
 }
 
 /*
@@ -557,19 +693,5 @@ int check_repository_format(void)
  */
 const char *setup_git_directory(void)
 {
-       const char *retval = setup_git_directory_gently(NULL);
-
-       /* If the work tree is not the default one, recompute prefix */
-       if (inside_work_tree < 0) {
-               static char buffer[PATH_MAX + 1];
-               char *rel;
-               if (retval && chdir(retval))
-                       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_errno ("Could not jump to working directory");
-               return rel && *rel ? strcat(rel, "/") : NULL;
-       }
-
-       return retval;
+       return setup_git_directory_gently(NULL);
 }
index 0cd9435619f1e0637584289b45d52c1cdd8a9460..d86a8db69ade6fd26ebd88bbb361a7a86838f14e 100644 (file)
@@ -35,6 +35,8 @@ static size_t sz_fmt(size_t s) { return s; }
 
 const unsigned char null_sha1[20];
 
+static int git_open_noatime(const char *name, struct packed_git *p);
+
 int safe_create_leading_directories(char *path)
 {
        char *pos = path + offset_1st_component(path);
@@ -298,7 +300,7 @@ static void read_info_alternates(const char * relative_base, int depth)
        int fd;
 
        sprintf(path, "%s/%s", relative_base, alt_file_name);
-       fd = open(path, O_RDONLY);
+       fd = git_open_noatime(path, NULL);
        if (fd < 0)
                return;
        if (fstat(fd, &st) || (st.st_size == 0)) {
@@ -411,7 +413,7 @@ static int check_packed_git_idx(const char *path,  struct packed_git *p)
        struct pack_idx_header *hdr;
        size_t idx_size;
        uint32_t version, nr, i, *index;
-       int fd = open(path, O_RDONLY);
+       int fd = git_open_noatime(path, p);
        struct stat st;
 
        if (fd < 0)
@@ -576,6 +578,21 @@ void release_pack_memory(size_t need, int fd)
                ; /* nothing */
 }
 
+void *xmmap(void *start, size_t length,
+       int prot, int flags, int fd, off_t offset)
+{
+       void *ret = mmap(start, length, prot, flags, fd, offset);
+       if (ret == MAP_FAILED) {
+               if (!length)
+                       return NULL;
+               release_pack_memory(length, fd);
+               ret = mmap(start, length, prot, flags, fd, offset);
+               if (ret == MAP_FAILED)
+                       die_errno("Out of memory? mmap failed");
+       }
+       return ret;
+}
+
 void close_pack_windows(struct packed_git *p)
 {
        while (p->windows) {
@@ -655,9 +672,7 @@ static int open_packed_git_1(struct packed_git *p)
        if (!p->index_data && open_pack_index(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);
+       p->pack_fd = git_open_noatime(p->pack_name, p);
        if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
                return -1;
 
@@ -803,11 +818,22 @@ static struct packed_git *alloc_packed_git(int extra)
        return p;
 }
 
+static void try_to_free_pack_memory(size_t size)
+{
+       release_pack_memory(size, -1);
+}
+
 struct packed_git *add_packed_git(const char *path, int path_len, int local)
 {
+       static int have_set_try_to_free_routine;
        struct stat st;
        struct packed_git *p = alloc_packed_git(path_len + 2);
 
+       if (!have_set_try_to_free_routine) {
+               have_set_try_to_free_routine = 1;
+               set_try_to_free_routine(try_to_free_pack_memory);
+       }
+
        /*
         * Make sure a corresponding .pack file exists and that
         * the index looks sane.
@@ -874,7 +900,7 @@ 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))
+       while (!dir && errno == EMFILE && unuse_one_window(NULL, -1))
                dir = opendir(path);
        if (!dir) {
                if (errno != ENOENT)
@@ -1003,7 +1029,7 @@ static void mark_bad_packed_object(struct packed_git *p,
        p->num_bad_objects++;
 }
 
-static int has_packed_and_bad(const unsigned char *sha1)
+static const struct packed_git *has_packed_and_bad(const unsigned char *sha1)
 {
        struct packed_git *p;
        unsigned i;
@@ -1011,8 +1037,8 @@ static int has_packed_and_bad(const unsigned char *sha1)
        for (p = packed_git; p; p = p->next)
                for (i = 0; i < p->num_bad_objects; i++)
                        if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
-                               return 1;
-       return 0;
+                               return p;
+       return NULL;
 }
 
 int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
@@ -1022,18 +1048,31 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz
        return hashcmp(sha1, real_sha1) ? -1 : 0;
 }
 
-static int git_open_noatime(const char *name)
+static int git_open_noatime(const char *name, struct packed_git *p)
 {
        static int sha1_file_open_flag = O_NOATIME;
-       int fd = open(name, O_RDONLY | sha1_file_open_flag);
 
-       /* Might the failure be due to O_NOATIME? */
-       if (fd < 0 && errno != ENOENT && sha1_file_open_flag) {
-               fd = open(name, O_RDONLY);
+       for (;;) {
+               int fd = open(name, O_RDONLY | sha1_file_open_flag);
                if (fd >= 0)
+                       return fd;
+
+               /* Might the failure be insufficient file descriptors? */
+               if (errno == EMFILE) {
+                       if (unuse_one_window(p, -1))
+                               continue;
+                       else
+                               return -1;
+               }
+
+               /* Might the failure be due to O_NOATIME? */
+               if (errno != ENOENT && sha1_file_open_flag) {
                        sha1_file_open_flag = 0;
+                       continue;
+               }
+
+               return -1;
        }
-       return fd;
 }
 
 static int open_sha1_file(const unsigned char *sha1)
@@ -1042,7 +1081,7 @@ static int open_sha1_file(const unsigned char *sha1)
        char *name = sha1_file_name(sha1);
        struct alternate_object_database *alt;
 
-       fd = git_open_noatime(name);
+       fd = git_open_noatime(name, NULL);
        if (fd >= 0)
                return fd;
 
@@ -1051,7 +1090,7 @@ static int open_sha1_file(const unsigned char *sha1)
        for (alt = alt_odb_list; alt; alt = alt->next) {
                name = alt->name;
                fill_sha1_path(name, sha1);
-               fd = git_open_noatime(alt->base);
+               fd = git_open_noatime(alt->base, NULL);
                if (fd >= 0)
                        return fd;
        }
@@ -2079,36 +2118,48 @@ static void *read_object(const unsigned char *sha1, enum object_type *type,
        return read_packed_sha1(sha1, type, size);
 }
 
+/*
+ * This function dies on corrupt objects; the callers who want to
+ * deal with them should arrange to call read_object() and give error
+ * messages themselves.
+ */
 void *read_sha1_file_repl(const unsigned char *sha1,
                          enum object_type *type,
                          unsigned long *size,
                          const unsigned char **replacement)
 {
        const unsigned char *repl = lookup_replace_object(sha1);
-       void *data = read_object(repl, type, size);
+       void *data;
        char *path;
+       const struct packed_git *p;
+
+       errno = 0;
+       data = read_object(repl, type, size);
+       if (data) {
+               if (replacement)
+                       *replacement = repl;
+               return data;
+       }
+
+       if (errno && errno != ENOENT)
+               die_errno("failed to read object %s", sha1_to_hex(sha1));
 
        /* die if we replaced an object with one that does not exist */
-       if (!data && repl != sha1)
+       if (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) {
-               if (has_loose_object(repl)) {
-                       path = sha1_file_name(sha1);
-                       die("loose object %s (stored in %s) is corrupted", sha1_to_hex(repl), path);
-               }
-               if (has_packed_and_bad(repl)) {
-                       path = sha1_pack_name(sha1);
-                       die("packed object %s (stored in %s) is corrupted", sha1_to_hex(repl), path);
-               }
+       if (has_loose_object(repl)) {
+               path = sha1_file_name(sha1);
+               die("loose object %s (stored in %s) is corrupt",
+                   sha1_to_hex(repl), path);
        }
 
-       if (replacement)
-               *replacement = repl;
+       if ((p = has_packed_and_bad(repl)) != NULL)
+               die("packed object %s (stored in %s) is corrupt",
+                   sha1_to_hex(repl), p->pack_name);
 
-       return data;
+       return NULL;
 }
 
 void *read_object_with_reference(const unsigned char *sha1,
@@ -2300,7 +2351,7 @@ 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))
+       while (fd < 0 && errno == EMFILE && unuse_one_window(NULL, -1))
                fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
        if (fd < 0) {
                if (errno == EACCES)
index 4af94fa59806c570c177a68139b54d46772d68a5..709ff2eee64cf106191ad274bede82a95d00e2a3 100644 (file)
@@ -7,6 +7,8 @@
 #include "refs.h"
 #include "remote.h"
 
+static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
+
 static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
 {
        struct alternate_object_database *alt;
@@ -206,7 +208,9 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
                if (exists
                    ? !status
                    : status == SHORT_NAME_NOT_FOUND) {
-                       hex[len] = 0;
+                       int cut_at = len + unique_abbrev_extra_length;
+                       cut_at = (cut_at < 40) ? cut_at : 40;
+                       hex[cut_at] = 0;
                        return hex;
                }
                len++;
@@ -342,7 +346,7 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1);
 
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
-       static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
+       static const char *warn_msg = "refname '%.*s' is ambiguous.";
        char *real_ref = NULL;
        int refs_found = 0;
        int at, reflog_len;
@@ -390,7 +394,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                return -1;
 
        if (warn_ambiguous_refs && refs_found > 1)
-               fprintf(stderr, warning, len, str);
+               warning(warn_msg, len, str);
 
        if (reflog_len) {
                int nth, i;
@@ -426,14 +430,14 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (at_time)
-                               fprintf(stderr,
-                                       "warning: Log for '%.*s' only goes "
-                                       "back to %s.\n", len, str,
+                               warning("Log for '%.*s' only goes "
+                                       "back to %s.", len, str,
                                        show_date(co_time, co_tz, DATE_RFC2822));
-                       else
-                               fprintf(stderr,
-                                       "warning: Log for '%.*s' only has "
-                                       "%d entries.\n", len, str, co_cnt);
+                       else {
+                               free(real_ref);
+                               die("Log for '%.*s' only has %d entries.",
+                                   len, str, co_cnt);
+                       }
                }
        }
 
@@ -560,6 +564,8 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
                expected_type = OBJ_BLOB;
        else if (sp[0] == '}')
                expected_type = OBJ_NONE;
+       else if (sp[0] == '/')
+               expected_type = OBJ_COMMIT;
        else
                return -1;
 
@@ -574,19 +580,37 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
                if (!o || (!o->parsed && !parse_object(o->sha1)))
                        return -1;
                hashcpy(sha1, o->sha1);
+               return 0;
        }
-       else {
+
+       /*
+        * At this point, the syntax look correct, so
+        * if we do not get the needed object, we should
+        * barf.
+        */
+       o = peel_to_type(name, len, o, expected_type);
+       if (!o)
+               return -1;
+
+       hashcpy(sha1, o->sha1);
+       if (sp[0] == '/') {
+               /* "$commit^{/foo}" */
+               char *prefix;
+               int ret;
+               struct commit_list *list = NULL;
+
                /*
-                * At this point, the syntax look correct, so
-                * if we do not get the needed object, we should
-                * barf.
+                * $commit^{/}. Some regex implementation may reject.
+                * We don't need regex anyway. '' pattern always matches.
                 */
-               o = peel_to_type(name, len, o, expected_type);
-               if (o) {
-                       hashcpy(sha1, o->sha1);
+               if (sp[1] == '}')
                        return 0;
-               }
-               return -1;
+
+               prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
+               commit_list_insert((struct commit *)o, &list);
+               ret = get_sha1_oneline(prefix, sha1, list);
+               free(prefix);
+               return ret;
        }
        return 0;
 }
@@ -683,16 +707,15 @@ static int handle_one_ref(const char *path,
        }
        if (object->type != OBJ_COMMIT)
                return 0;
-       insert_by_date((struct commit *)object, list);
-       object->flags |= ONELINE_SEEN;
+       commit_list_insert_by_date((struct commit *)object, list);
        return 0;
 }
 
-static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
+static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
+                           struct commit_list *list)
 {
-       struct commit_list *list = NULL, *backup = NULL, *l;
-       int retval = -1;
-       char *temp_commit_buffer = NULL;
+       struct commit_list *backup = NULL, *l;
+       int found = 0;
        regex_t regex;
 
        if (prefix[0] == '!') {
@@ -704,41 +727,45 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
        if (regcomp(&regex, prefix, REG_EXTENDED))
                die("Invalid search pattern: %s", prefix);
 
-       for_each_ref(handle_one_ref, &list);
-       for (l = list; l; l = l->next)
+       for (l = list; l; l = l->next) {
+               l->item->object.flags |= ONELINE_SEEN;
                commit_list_insert(l->item, &backup);
+       }
        while (list) {
-               char *p;
+               char *p, *to_free = NULL;
                struct commit *commit;
                enum object_type type;
                unsigned long size;
+               int matches;
 
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
                if (!parse_object(commit->object.sha1))
                        continue;
-               free(temp_commit_buffer);
                if (commit->buffer)
                        p = commit->buffer;
                else {
                        p = read_sha1_file(commit->object.sha1, &type, &size);
                        if (!p)
                                continue;
-                       temp_commit_buffer = p;
+                       to_free = p;
                }
-               if (!(p = strstr(p, "\n\n")))
-                       continue;
-               if (!regexec(&regex, p + 2, 0, NULL, 0)) {
+
+               p = strstr(p, "\n\n");
+               matches = p && !regexec(&regex, p + 2, 0, NULL, 0);
+               free(to_free);
+
+               if (matches) {
                        hashcpy(sha1, commit->object.sha1);
-                       retval = 0;
+                       found = 1;
                        break;
                }
        }
        regfree(&regex);
-       free(temp_commit_buffer);
        free_commit_list(list);
        for (l = backup; l; l = l->next)
                clear_commit_marks(l->item, ONELINE_SEEN);
-       return retval;
+       free_commit_list(backup);
+       return found ? 0 : -1;
 }
 
 struct grab_nth_branch_switch_cbdata {
@@ -934,6 +961,24 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
        return len;
 }
 
+int strbuf_branchname(struct strbuf *sb, const char *name)
+{
+       int len = strlen(name);
+       if (interpret_branch_name(name, sb) == len)
+               return 0;
+       strbuf_add(sb, name, len);
+       return len;
+}
+
+int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
+{
+       strbuf_branchname(sb, name);
+       if (name[0] == '-')
+               return CHECK_REF_FORMAT_ERROR;
+       strbuf_splice(sb, 0, 0, "refs/heads/", 11);
+       return check_ref_format(sb->buf);
+}
+
 /*
  * This is like "get_sha1_basic()", except it allows "sha1 expressions",
  * notably "xyz^" for "parent of xyz"
@@ -1046,6 +1091,23 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
        return ret;
 }
 
+static char *resolve_relative_path(const char *rel)
+{
+       if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
+               return NULL;
+
+       if (!startup_info)
+               die("BUG: startup_info struct is not initialized.");
+
+       if (!is_inside_work_tree())
+               die("relative path syntax can't be used outside working tree.");
+
+       /* die() inside prefix_path() if resolved path is outside worktree */
+       return prefix_path(startup_info->prefix,
+                          startup_info->prefix ? strlen(startup_info->prefix) : 0,
+                          rel);
+}
+
 int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                            struct object_context *oc,
                            int gently, const char *prefix)
@@ -1060,15 +1122,21 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
        if (!ret)
                return ret;
        /* sha1:path --> object name of path in ent sha1
-        * :path -> object name of path in index
+        * :path -> object name of absolute path in index
+        * :./path -> object name of path relative to cwd in index
         * :[0-3]:path -> object name of path in index at stage
+        * :/foo -> recent commit matching foo
         */
        if (name[0] == ':') {
                int stage = 0;
                struct cache_entry *ce;
+               char *new_path = NULL;
                int pos;
-               if (namelen > 2 && name[1] == '/')
-                       return get_sha1_oneline(name + 2, sha1);
+               if (namelen > 2 && name[1] == '/') {
+                       struct commit_list *list = NULL;
+                       for_each_ref(handle_one_ref, &list);
+                       return get_sha1_oneline(name + 2, sha1, list);
+               }
                if (namelen < 3 ||
                    name[2] != ':' ||
                    name[1] < '0' || '3' < name[1])
@@ -1077,7 +1145,13 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                        stage = name[1] - '0';
                        cp = name + 3;
                }
-               namelen = namelen - (cp - name);
+               new_path = resolve_relative_path(cp);
+               if (!new_path) {
+                       namelen = namelen - (cp - name);
+               } else {
+                       cp = new_path;
+                       namelen = strlen(cp);
+               }
 
                strncpy(oc->path, cp,
                        sizeof(oc->path));
@@ -1095,12 +1169,15 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                                break;
                        if (ce_stage(ce) == stage) {
                                hashcpy(sha1, ce->sha1);
+                               oc->mode = ce->ce_mode;
+                               free(new_path);
                                return 0;
                        }
                        pos++;
                }
                if (!gently)
                        diagnose_invalid_index_path(stage, prefix, cp);
+               free(new_path);
                return -1;
        }
        for (cp = name, bracket_depth = 0; *cp; cp++) {
@@ -1121,6 +1198,11 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                }
                if (!get_sha1_1(name, cp-name, tree_sha1)) {
                        const char *filename = cp+1;
+                       char *new_filename = NULL;
+
+                       new_filename = resolve_relative_path(filename);
+                       if (new_filename)
+                               filename = new_filename;
                        ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
                        if (!gently) {
                                diagnose_invalid_sha1_path(prefix, filename,
@@ -1132,6 +1214,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                                sizeof(oc->path));
                        oc->path[sizeof(oc->path)-1] = '\0';
 
+                       free(new_filename);
                        return ret;
                } else {
                        if (!gently)
index 4d90eda19efe0a80c1cb39e8897ab3ed5e6fcf56..a0363dea203d2a06e985bb4e140ffc675428cbf8 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -47,7 +47,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
 {
        int i = 0, cur_depth = 0;
        struct commit_list *result = NULL;
-       struct object_array stack = {0, 0, NULL};
+       struct object_array stack = OBJECT_ARRAY_INIT;
        struct commit *commit = NULL;
 
        while (commit || i < heads->nr || stack.nr) {
diff --git a/shell.c b/shell.c
index e4864e04da3b0e237342c2ca0548c0ec0082c171..dea4cfdd2c230af6afd6233cacfa5c776a9962f9 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -2,6 +2,10 @@
 #include "quote.h"
 #include "exec_cmd.h"
 #include "strbuf.h"
+#include "run-command.h"
+
+#define COMMAND_DIR "git-shell-commands"
+#define HELP_COMMAND COMMAND_DIR "/help"
 
 static int do_generic_cmd(const char *me, char *arg)
 {
@@ -33,6 +37,86 @@ static int do_cvs_cmd(const char *me, char *arg)
        return execv_git_cmd(cvsserver_argv);
 }
 
+static int is_valid_cmd_name(const char *cmd)
+{
+       /* Test command contains no . or / characters */
+       return cmd[strcspn(cmd, "./")] == '\0';
+}
+
+static char *make_cmd(const char *prog)
+{
+       char *prefix = xmalloc((strlen(prog) + strlen(COMMAND_DIR) + 2));
+       strcpy(prefix, COMMAND_DIR);
+       strcat(prefix, "/");
+       strcat(prefix, prog);
+       return prefix;
+}
+
+static void cd_to_homedir(void)
+{
+       const char *home = getenv("HOME");
+       if (!home)
+               die("could not determine user's home directory; HOME is unset");
+       if (chdir(home) == -1)
+               die("could not chdir to user's home directory");
+}
+
+static void run_shell(void)
+{
+       int done = 0;
+       static const char *help_argv[] = { HELP_COMMAND, NULL };
+       /* Print help if enabled */
+       run_command_v_opt(help_argv, RUN_SILENT_EXEC_FAILURE);
+
+       do {
+               struct strbuf line = STRBUF_INIT;
+               const char *prog;
+               char *full_cmd;
+               char *rawargs;
+               char *split_args;
+               const char **argv;
+               int code;
+               int count;
+
+               fprintf(stderr, "git> ");
+               if (strbuf_getline(&line, stdin, '\n') == EOF) {
+                       fprintf(stderr, "\n");
+                       strbuf_release(&line);
+                       break;
+               }
+               strbuf_trim(&line);
+               rawargs = strbuf_detach(&line, NULL);
+               split_args = xstrdup(rawargs);
+               count = split_cmdline(split_args, &argv);
+               if (count < 0) {
+                       fprintf(stderr, "invalid command format '%s': %s\n", rawargs,
+                               split_cmdline_strerror(count));
+                       free(split_args);
+                       free(rawargs);
+                       continue;
+               }
+
+               prog = argv[0];
+               if (!strcmp(prog, "")) {
+               } else if (!strcmp(prog, "quit") || !strcmp(prog, "logout") ||
+                          !strcmp(prog, "exit") || !strcmp(prog, "bye")) {
+                       done = 1;
+               } else if (is_valid_cmd_name(prog)) {
+                       full_cmd = make_cmd(prog);
+                       argv[0] = full_cmd;
+                       code = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
+                       if (code == -1 && errno == ENOENT) {
+                               fprintf(stderr, "unrecognized command '%s'\n", prog);
+                       }
+                       free(full_cmd);
+               } else {
+                       fprintf(stderr, "invalid command format '%s'\n", prog);
+               }
+
+               free(argv);
+               free(rawargs);
+       } while (!done);
+}
 
 static struct commands {
        const char *name;
@@ -48,8 +132,10 @@ static struct commands {
 int main(int argc, char **argv)
 {
        char *prog;
+       const char **user_argv;
        struct commands *cmd;
        int devnull_fd;
+       int count;
 
        /*
         * Always open file descriptors 0/1/2 to avoid clobbering files
@@ -66,17 +152,28 @@ int main(int argc, char **argv)
        /*
         * Special hack to pretend to be a CVS server
         */
-       if (argc == 2 && !strcmp(argv[1], "cvs server"))
+       if (argc == 2 && !strcmp(argv[1], "cvs server")) {
                argv--;
+       } else if (argc == 1) {
+               /* Allow the user to run an interactive shell */
+               cd_to_homedir();
+               if (access(COMMAND_DIR, R_OK | X_OK) == -1) {
+                       die("Interactive git shell is not enabled.\n"
+                           "hint: ~/" COMMAND_DIR " should exist "
+                           "and have read and execute access.");
+               }
+               run_shell();
+               exit(0);
+       } else if (argc != 3 || strcmp(argv[1], "-c")) {
+               /*
+                * We do not accept any other modes except "-c" followed by
+                * "cmd arg", where "cmd" is a very limited subset of git
+                * commands or a command in the COMMAND_DIR
+                */
+               die("Run with no arguments or with -c cmd");
+       }
 
-       /*
-        * We do not accept anything but "-c" followed by "cmd arg",
-        * where "cmd" is a very limited subset of git commands.
-        */
-       else if (argc != 3 || strcmp(argv[1], "-c"))
-               die("What do you think I am? A shell?");
-
-       prog = argv[2];
+       prog = xstrdup(argv[2]);
        if (!strncmp(prog, "git", 3) && isspace(prog[3]))
                /* Accept "git foo" as if the caller said "git-foo". */
                prog[3] = '-';
@@ -99,5 +196,21 @@ int main(int argc, char **argv)
                }
                exit(cmd->exec(cmd->name, arg));
        }
-       die("unrecognized command '%s'", prog);
+
+       cd_to_homedir();
+       count = split_cmdline(prog, &user_argv);
+       if (count >= 0) {
+               if (is_valid_cmd_name(user_argv[0])) {
+                       prog = make_cmd(user_argv[0]);
+                       user_argv[0] = prog;
+                       execv(user_argv[0], (char *const *) user_argv);
+               }
+               free(prog);
+               free(user_argv);
+               die("unrecognized command '%s'", argv[2]);
+       } else {
+               free(prog);
+               die("invalid command format '%s': %s", argv[2],
+                   split_cmdline_strerror(count));
+       }
 }
index bc3a0802ea7e7b1743602972de182391b4bf0b3f..9b3c4457f229041784edfa65218aa09de7a5eff8 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -386,19 +386,3 @@ int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
 
        return len;
 }
-
-int strbuf_branchname(struct strbuf *sb, const char *name)
-{
-       int len = strlen(name);
-       if (interpret_branch_name(name, sb) == len)
-               return 0;
-       strbuf_add(sb, name, len);
-       return len;
-}
-
-int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
-{
-       strbuf_branchname(sb, name);
-       strbuf_splice(sb, 0, 0, "refs/heads/", 11);
-       return check_ref_format(sb->buf);
-}
index fac2dbc24fc654e58840727204043a198d065921..675a91f93821421aa79de51857b22a4960da8ec1 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -16,7 +16,7 @@
  *
  * 2. the ->buf member is a byte array that has at least ->len + 1 bytes
  *    allocated. The extra byte is used to store a '\0', allowing the ->buf
- *    member to be a valid C-string. Every strbuf function ensure this
+ *    member to be a valid C-string. Every strbuf function ensures this
  *    invariant is preserved.
  *
  *    Note that it is OK to "play" with the buffer directly if you work it
index 7f0da48bc25e086aa8dd55b5a6f7f9e54e66d41a..6f1c10722f744f4a27f18dae4867b15ecbf57d49 100644 (file)
@@ -6,10 +6,13 @@
 #include "revision.h"
 #include "run-command.h"
 #include "diffcore.h"
+#include "refs.h"
 #include "string-list.h"
 
 struct string_list config_name_for_path;
+struct string_list config_fetch_recurse_submodules_for_name;
 struct string_list config_ignore_for_name;
+static int config_fetch_recurse_submodules;
 
 static int add_submodule_odb(const char *path)
 {
@@ -62,10 +65,14 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
        }
 }
 
-static int submodule_config(const char *var, const char *value, void *cb)
+int submodule_config(const char *var, const char *value, void *cb)
 {
        if (!prefixcmp(var, "submodule."))
                return parse_submodule_config_option(var, value);
+       else if (!strcmp(var, "fetch.recursesubmodules")) {
+               config_fetch_recurse_submodules = git_config_bool(var, value);
+               return 0;
+       }
        return 0;
 }
 
@@ -99,6 +106,14 @@ int parse_submodule_config_option(const char *var, const char *value)
                        config = string_list_append(&config_name_for_path, xstrdup(value));
                config->util = strbuf_detach(&submodname, NULL);
                strbuf_release(&submodname);
+       } else if ((len > 23) && !strcmp(var + len - 23, ".fetchrecursesubmodules")) {
+               strbuf_add(&submodname, var, len - 23);
+               config = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, submodname.buf);
+               if (!config)
+                       config = string_list_append(&config_fetch_recurse_submodules_for_name,
+                                                   strbuf_detach(&submodname, NULL));
+               config->util = git_config_bool(var, value) ? (void *)1 : NULL;
+               strbuf_release(&submodname);
        } else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
                if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
                    strcmp(value, "all") && strcmp(value, "none")) {
@@ -228,6 +243,89 @@ void show_submodule_summary(FILE *f, const char *path,
        strbuf_release(&sb);
 }
 
+void set_config_fetch_recurse_submodules(int value)
+{
+       config_fetch_recurse_submodules = value;
+}
+
+int fetch_populated_submodules(int num_options, const char **options,
+                              const char *prefix, int ignore_config,
+                              int quiet)
+{
+       int i, result = 0, argc = 0;
+       struct child_process cp;
+       const char **argv;
+       struct string_list_item *name_for_path;
+       const char *work_tree = get_git_work_tree();
+       if (!work_tree)
+               return 0;
+
+       if (!the_index.initialized)
+               if (read_cache() < 0)
+                       die("index file corrupt");
+
+       /* 4: "fetch" (options) "--submodule-prefix" prefix NULL */
+       argv = xcalloc(num_options + 4, sizeof(const char *));
+       argv[argc++] = "fetch";
+       for (i = 0; i < num_options; i++)
+               argv[argc++] = options[i];
+       argv[argc++] = "--submodule-prefix";
+
+       memset(&cp, 0, sizeof(cp));
+       cp.argv = argv;
+       cp.env = local_repo_env;
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+
+       for (i = 0; i < active_nr; i++) {
+               struct strbuf submodule_path = STRBUF_INIT;
+               struct strbuf submodule_git_dir = STRBUF_INIT;
+               struct strbuf submodule_prefix = STRBUF_INIT;
+               struct cache_entry *ce = active_cache[i];
+               const char *git_dir, *name;
+
+               if (!S_ISGITLINK(ce->ce_mode))
+                       continue;
+
+               name = ce->name;
+               name_for_path = unsorted_string_list_lookup(&config_name_for_path, ce->name);
+               if (name_for_path)
+                       name = name_for_path->util;
+
+               if (!ignore_config) {
+                       struct string_list_item *fetch_recurse_submodules_option;
+                       fetch_recurse_submodules_option = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name);
+                       if (fetch_recurse_submodules_option) {
+                               if (!fetch_recurse_submodules_option->util)
+                                       continue;
+                       } else {
+                               if (!config_fetch_recurse_submodules)
+                                       continue;
+                       }
+               }
+
+               strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name);
+               strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
+               strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name);
+               git_dir = read_gitfile_gently(submodule_git_dir.buf);
+               if (!git_dir)
+                       git_dir = submodule_git_dir.buf;
+               if (is_directory(git_dir)) {
+                       if (!quiet)
+                               printf("Fetching submodule %s%s\n", prefix, ce->name);
+                       cp.dir = submodule_path.buf;
+                       argv[argc] = submodule_prefix.buf;
+                       if (run_command(&cp))
+                               result = 1;
+               }
+               strbuf_release(&submodule_path);
+               strbuf_release(&submodule_git_dir);
+               strbuf_release(&submodule_prefix);
+       }
+       free(argv);
+       return result;
+}
+
 unsigned is_submodule_modified(const char *path, int ignore_untracked)
 {
        ssize_t len;
@@ -296,3 +394,163 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
        strbuf_release(&buf);
        return dirty_submodule;
 }
+
+static int find_first_merges(struct object_array *result, const char *path,
+               struct commit *a, struct commit *b)
+{
+       int i, j;
+       struct object_array merges;
+       struct commit *commit;
+       int contains_another;
+
+       char merged_revision[42];
+       const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
+                                  "--all", merged_revision, NULL };
+       struct rev_info revs;
+       struct setup_revision_opt rev_opts;
+
+       memset(&merges, 0, sizeof(merges));
+       memset(result, 0, sizeof(struct object_array));
+       memset(&rev_opts, 0, sizeof(rev_opts));
+
+       /* get all revisions that merge commit a */
+       snprintf(merged_revision, sizeof(merged_revision), "^%s",
+                       sha1_to_hex(a->object.sha1));
+       init_revisions(&revs, NULL);
+       rev_opts.submodule = path;
+       setup_revisions(sizeof(rev_args)/sizeof(char *)-1, rev_args, &revs, &rev_opts);
+
+       /* save all revisions from the above list that contain b */
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
+       while ((commit = get_revision(&revs)) != NULL) {
+               struct object *o = &(commit->object);
+               if (in_merge_bases(b, &commit, 1))
+                       add_object_array(o, NULL, &merges);
+       }
+
+       /* Now we've got all merges that contain a and b. Prune all
+        * merges that contain another found merge and save them in
+        * result.
+        */
+       for (i = 0; i < merges.nr; i++) {
+               struct commit *m1 = (struct commit *) merges.objects[i].item;
+
+               contains_another = 0;
+               for (j = 0; j < merges.nr; j++) {
+                       struct commit *m2 = (struct commit *) merges.objects[j].item;
+                       if (i != j && in_merge_bases(m2, &m1, 1)) {
+                               contains_another = 1;
+                               break;
+                       }
+               }
+
+               if (!contains_another)
+                       add_object_array(merges.objects[i].item,
+                                        merges.objects[i].name, result);
+       }
+
+       free(merges.objects);
+       return result->nr;
+}
+
+static void print_commit(struct commit *commit)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct pretty_print_context ctx = {0};
+       ctx.date_mode = DATE_NORMAL;
+       format_commit_message(commit, " %h: %m %s", &sb, &ctx);
+       fprintf(stderr, "%s\n", sb.buf);
+       strbuf_release(&sb);
+}
+
+#define MERGE_WARNING(path, msg) \
+       warning("Failed to merge submodule %s (%s)", path, msg);
+
+int merge_submodule(unsigned char result[20], const char *path,
+                   const unsigned char base[20], const unsigned char a[20],
+                   const unsigned char b[20])
+{
+       struct commit *commit_base, *commit_a, *commit_b;
+       int parent_count;
+       struct object_array merges;
+
+       int i;
+
+       /* store a in result in case we fail */
+       hashcpy(result, a);
+
+       /* we can not handle deletion conflicts */
+       if (is_null_sha1(base))
+               return 0;
+       if (is_null_sha1(a))
+               return 0;
+       if (is_null_sha1(b))
+               return 0;
+
+       if (add_submodule_odb(path)) {
+               MERGE_WARNING(path, "not checked out");
+               return 0;
+       }
+
+       if (!(commit_base = lookup_commit_reference(base)) ||
+           !(commit_a = lookup_commit_reference(a)) ||
+           !(commit_b = lookup_commit_reference(b))) {
+               MERGE_WARNING(path, "commits not present");
+               return 0;
+       }
+
+       /* check whether both changes are forward */
+       if (!in_merge_bases(commit_base, &commit_a, 1) ||
+           !in_merge_bases(commit_base, &commit_b, 1)) {
+               MERGE_WARNING(path, "commits don't follow merge-base");
+               return 0;
+       }
+
+       /* Case #1: a is contained in b or vice versa */
+       if (in_merge_bases(commit_a, &commit_b, 1)) {
+               hashcpy(result, b);
+               return 1;
+       }
+       if (in_merge_bases(commit_b, &commit_a, 1)) {
+               hashcpy(result, a);
+               return 1;
+       }
+
+       /*
+        * Case #2: There are one or more merges that contain a and b in
+        * the submodule. If there is only one, then present it as a
+        * suggestion to the user, but leave it marked unmerged so the
+        * user needs to confirm the resolution.
+        */
+
+       /* find commit which merges them */
+       parent_count = find_first_merges(&merges, path, commit_a, commit_b);
+       switch (parent_count) {
+       case 0:
+               MERGE_WARNING(path, "merge following commits not found");
+               break;
+
+       case 1:
+               MERGE_WARNING(path, "not fast-forward");
+               fprintf(stderr, "Found a possible merge resolution "
+                               "for the submodule:\n");
+               print_commit((struct commit *) merges.objects[0].item);
+               fprintf(stderr,
+                       "If this is correct simply add it to the index "
+                       "for example\n"
+                       "by using:\n\n"
+                       "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
+                       "which will accept this suggestion.\n",
+                       sha1_to_hex(merges.objects[0].item->sha1), path);
+               break;
+
+       default:
+               MERGE_WARNING(path, "multiple merges found");
+               for (i = 0; i < merges.nr; i++)
+                       print_commit((struct commit *) merges.objects[i].item);
+       }
+
+       free(merges.objects);
+       return 0;
+}
index 8ac4037b56210d54616dfa68655722b246c26f1b..4729023aa5bbd7d7c95981b995c379407ba6423d 100644 (file)
@@ -5,6 +5,7 @@ struct diff_options;
 
 void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
                const char *path);
+int submodule_config(const char *var, const char *value, void *cb);
 void gitmodules_config();
 int parse_submodule_config_option(const char *var, const char *value);
 void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
@@ -12,6 +13,12 @@ void show_submodule_summary(FILE *f, const char *path,
                unsigned char one[20], unsigned char two[20],
                unsigned dirty_submodule,
                const char *del, const char *add, const char *reset);
+void set_config_fetch_recurse_submodules(int value);
+int fetch_populated_submodules(int num_options, const char **options,
+                              const char *prefix, int ignore_config,
+                              int quiet);
 unsigned is_submodule_modified(const char *path, int ignore_untracked);
+int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20],
+                   const unsigned char a[20], const unsigned char b[20]);
 
 #endif
index 88601200114f857a57214a9bf0d02ffd39a83504..3cacebd91adc2958e81d952523bd7cfe0078078c 100644 (file)
@@ -64,11 +64,13 @@ static inline void reset_lstat_cache(struct cache_def *cache)
  * 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(struct cache_def *cache, const char *name, int len,
-                      int track_flags, int prefix_len_stat_func)
+static int lstat_cache_matchlen(struct cache_def *cache,
+                               const char *name, int len,
+                               int *ret_flags, 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;
+       int save_flags, max_len, ret;
        struct stat st;
 
        if (cache->track_flags != track_flags ||
@@ -90,13 +92,13 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
                match_len = last_slash =
                        longest_path_match(name, len, cache->path, cache->len,
                                           &previous_slash);
-               match_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
+               *ret_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 (*ret_flags && match_len == cache->len)
+                       return match_len;
                /*
                 * If we now have match_len > 0, we would know that
                 * the matched part will always be a directory.
@@ -105,16 +107,16 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
                 * a substring of the cache on a path component basis,
                 * we can return immediately.
                 */
-               match_flags = track_flags & FL_DIR;
-               if (match_flags && len == match_len)
-                       return match_flags;
+               *ret_flags = track_flags & FL_DIR;
+               if (*ret_flags && len == match_len)
+                       return match_len;
        }
 
        /*
         * Okay, no match from the cache so far, so now we have to
         * check the rest of the path components.
         */
-       ret_flags = FL_DIR;
+       *ret_flags = FL_DIR;
        last_slash_dir = last_slash;
        max_len = len < PATH_MAX ? len : PATH_MAX;
        while (match_len < max_len) {
@@ -133,16 +135,16 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
                        ret = lstat(cache->path, &st);
 
                if (ret) {
-                       ret_flags = FL_LSTATERR;
+                       *ret_flags = FL_LSTATERR;
                        if (errno == ENOENT)
-                               ret_flags |= FL_NOENT;
+                               *ret_flags |= FL_NOENT;
                } else if (S_ISDIR(st.st_mode)) {
                        last_slash_dir = last_slash;
                        continue;
                } else if (S_ISLNK(st.st_mode)) {
-                       ret_flags = FL_SYMLINK;
+                       *ret_flags = FL_SYMLINK;
                } else {
-                       ret_flags = FL_ERR;
+                       *ret_flags = FL_ERR;
                }
                break;
        }
@@ -152,7 +154,7 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
         * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached
         * for the moment!
         */
-       save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
+       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;
@@ -176,7 +178,16 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
        } else {
                reset_lstat_cache(cache);
        }
-       return ret_flags;
+       return match_len;
+}
+
+static int lstat_cache(struct cache_def *cache, const char *name, int len,
+                      int track_flags, int prefix_len_stat_func)
+{
+       int flags;
+       (void)lstat_cache_matchlen(cache, name, len, &flags, track_flags,
+                       prefix_len_stat_func);
+       return flags;
 }
 
 #define USE_ONLY_LSTAT  0
@@ -198,15 +209,26 @@ int has_symlink_leading_path(const char *name, int len)
 }
 
 /*
- * Return non-zero if path 'name' has a leading symlink component or
+ * Return zero if path 'name' has a leading symlink component or
  * if some leading path component does not exists.
+ *
+ * Return -1 if leading path exists and is a directory.
+ *
+ * Return path length if leading path exists and is neither a
+ * directory nor a symlink.
  */
-int has_symlink_or_noent_leading_path(const char *name, int len)
+int check_leading_path(const char *name, int 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);
+       int flags;
+       int match_len = lstat_cache_matchlen(cache, name, len, &flags,
+                          FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT);
+       if (flags & (FL_SYMLINK|FL_NOENT))
+               return 0;
+       else if (flags & FL_DIR)
+               return -1;
+       else
+               return match_len;
 }
 
 /*
index 819b936870300fc8325c0f24f8c68ea8bdaa2727..47cbeb6e68ec910a691f2feca18b7b160efd0ced 100644 (file)
@@ -8,18 +8,28 @@
 
 #GIT_TEST_OPTS=--verbose --debug
 SHELL_PATH ?= $(SHELL)
+PERL_PATH ?= /usr/bin/perl
 TAR ?= $(TAR)
 RM ?= rm -f
+PROVE ?= prove
+DEFAULT_TEST_TARGET ?= test
 
 # Shell quote;
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 
 T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
 TSVN = $(wildcard t91[0-9][0-9]-*.sh)
+TGITWEB = $(wildcard t95[0-9][0-9]-*.sh)
 
-all: pre-clean
+all: $(DEFAULT_TEST_TARGET)
+
+test: pre-clean $(TEST_LINT)
        $(MAKE) aggregate-results-and-cleanup
 
+prove: pre-clean $(TEST_LINT)
+       @echo "*** prove ***"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+       $(MAKE) clean
+
 $(T):
        @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
@@ -28,10 +38,21 @@ pre-clean:
 
 clean:
        $(RM) -r 'trash directory'.* test-results
-       $(RM) t????/cvsroot/CVSROOT/?*
        $(RM) -r valgrind/bin
        $(RM) .prove
 
+test-lint: test-lint-duplicates test-lint-executable
+
+test-lint-duplicates:
+       @dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
+               test -z "$$dups" || { \
+               echo >&2 "duplicate test numbers:" $$dups; exit 1; }
+
+test-lint-executable:
+       @bad=`for i in $(T); do test -x "$$i" || echo $$i; done` && \
+               test -z "$$bad" || { \
+               echo >&2 "non-executable tests:" $$bad; exit 1; }
+
 aggregate-results-and-cleanup: $(T)
        $(MAKE) aggregate-results
        $(MAKE) clean
@@ -46,7 +67,48 @@ full-svn-test:
        $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
        $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8
 
+gitweb-test:
+       $(MAKE) $(TGITWEB)
+
 valgrind:
        GIT_TEST_OPTS=--valgrind $(MAKE)
 
-.PHONY: pre-clean $(T) aggregate-results clean valgrind
+# Smoke testing targets
+-include ../GIT-VERSION-FILE
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo unknown')
+uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo unknown')
+
+test-results:
+       mkdir -p test-results
+
+test-results/git-smoke.tar.gz: test-results
+       $(PERL_PATH) ./harness \
+               --archive="test-results/git-smoke.tar.gz" \
+               $(T)
+
+smoke: test-results/git-smoke.tar.gz
+
+SMOKE_UPLOAD_FLAGS =
+ifdef SMOKE_USERNAME
+       SMOKE_UPLOAD_FLAGS += -F username="$(SMOKE_USERNAME)" -F password="$(SMOKE_PASSWORD)"
+endif
+ifdef SMOKE_COMMENT
+       SMOKE_UPLOAD_FLAGS += -F comments="$(SMOKE_COMMENT)"
+endif
+ifdef SMOKE_TAGS
+       SMOKE_UPLOAD_FLAGS += -F tags="$(SMOKE_TAGS)"
+endif
+
+smoke_report: smoke
+       curl \
+               -H "Expect: " \
+               -F project=Git \
+               -F architecture="$(uname_M)" \
+               -F platform="$(uname_S)" \
+               -F revision="$(GIT_VERSION)" \
+               -F report_file=@test-results/git-smoke.tar.gz \
+               $(SMOKE_UPLOAD_FLAGS) \
+               http://smoke.git.nix.is/app/projects/process_add_report/1 \
+       | grep -v ^Redirecting
+
+.PHONY: pre-clean $(T) aggregate-results clean valgrind smoke smoke_report
index 0d1183c3e69904e9e3543d757f14f10c629e199b..25f7d2d2e3cf70d54f5b854ad4199c831a74ae2a 100644 (file)
--- a/t/README
+++ b/t/README
@@ -50,6 +50,12 @@ prove and other harnesses come with a lot of useful options. The
     # Repeat until no more failures
     $ prove -j 15 --state=failed,save ./t[0-9]*.sh
 
+You can give DEFAULT_TEST_TARGET=prove on the make command (or define it
+in config.mak) to cause "make test" to run tests under prove.
+GIT_PROVE_OPTS can be used to pass additional options, e.g.
+
+    $ make DEFAULT_TEST_TARGET=prove GIT_PROVE_OPTS='--timer --jobs 16' test
+
 You can also run each test individually from command line, like this:
 
     $ sh ./t3010-ls-files-killed-modified.sh
@@ -259,14 +265,29 @@ Do:
        test ...
 
    That way all of the commands in your tests will succeed or fail. If
-   you must ignore the return value of something (e.g., the return
-   after unsetting a variable that was already unset is unportable) it's
-   best to indicate so explicitly with a semicolon:
-
-       unset HLAGH;
-       git merge hla &&
-       git push gh &&
-       test ...
+   you must ignore the return value of something, consider using a
+   helper function (e.g. use sane_unset instead of unset, in order
+   to avoid unportable return value for unsetting a variable that was
+   already unset), or prepending the command with test_might_fail or
+   test_must_fail.
+
+ - Check the test coverage for your tests. See the "Test coverage"
+   below.
+
+   Don't blindly follow test coverage metrics, they're a good way to
+   spot if you've missed something. If a new function you added
+   doesn't have any coverage you're probably doing something wrong,
+   but having 100% coverage doesn't necessarily mean that you tested
+   everything.
+
+   Tests that are likely to smoke out future regressions are better
+   than tests that just inflate the coverage metrics.
+
+ - When a test checks for an absolute path that a git command generated,
+   construct the expected value using $(pwd) rather than $PWD,
+   $TEST_DIRECTORY, or $TRASH_DIRECTORY. It makes a difference on
+   Windows, where the shell (MSYS bash) mangles absolute path names.
+   For details, see the commit message of 4114156ae9.
 
 Don't:
 
@@ -307,9 +328,21 @@ Keep in mind:
 Skipping tests
 --------------
 
-If you need to skip all the remaining tests you should set skip_all
-and immediately call test_done. The string you give to skip_all will
-be used as an explanation for why the test was skipped. for instance:
+If you need to skip tests you should do so be using the three-arg form
+of the test_* functions (see the "Test harness library" section
+below), e.g.:
+
+    test_expect_success PERL 'I need Perl' "
+        '$PERL_PATH' -e 'hlagh() if unf_unf()'
+    "
+
+The advantage of skipping tests like this is that platforms that don't
+have the PERL and other optional dependencies get an indication of how
+many tests they're missing.
+
+If the test code is too hairy for that (i.e. does a lot of setup work
+outside test assertions) you can also skip all remaining tests by
+setting skip_all and immediately call test_done:
 
        if ! test_have_prereq PERL
        then
@@ -317,6 +350,9 @@ be used as an explanation for why the test was skipped. for instance:
            test_done
        fi
 
+The string you give to skip_all will be used as an explanation for why
+the test was skipped.
+
 End with test_done
 ------------------
 
@@ -350,6 +386,12 @@ library for your script to use.
        test_expect_success TTY 'git --paginate rev-list uses a pager' \
            ' ... '
 
+   You can also supply a comma-separated list of prerequisites, in the
+   rare case where your test depends on more than one:
+
+       test_expect_success PERL,PYTHON 'yo dawg' \
+           ' test $(perl -E 'print eval "1 +" . qx[python -c "print 2"]') == "4" '
+
  - test_expect_failure [<prereq>] <message> <script>
 
    This is NOT the opposite of test_expect_success, but is used
@@ -362,13 +404,6 @@ library for your script to use.
    Like test_expect_success this function can optionally use a three
    argument invocation with a prerequisite as the first argument.
 
- - test_expect_code [<prereq>] <code> <message> <script>
-
-   Analogous to test_expect_success, but pass the test if it exits
-   with a given exit <code>
-
- test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
-
  - test_debug <script>
 
    This takes a single argument, <script>, and evaluates it only
@@ -404,11 +439,12 @@ library for your script to use.
  - test_set_prereq SOME_PREREQ
 
    Set a test prerequisite to be used later with test_have_prereq. The
-   test-lib will set some prerequisites for you, e.g. PERL and PYTHON
-   which are derived from ./GIT-BUILD-OPTIONS (grep test_set_prereq
-   test-lib.sh for more). Others you can set yourself and use later
-   with either test_have_prereq directly, or the three argument
-   invocation of test_expect_success and test_expect_failure.
+   test-lib will set some prerequisites for you, see the
+   "Prerequisites" section below for a full list of these.
+
+   Others you can set yourself and use later with either
+   test_have_prereq directly, or the three argument invocation of
+   test_expect_success and test_expect_failure.
 
  - test_have_prereq SOME PREREQ
 
@@ -448,6 +484,15 @@ library for your script to use.
            'Perl API' \
            "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
 
+ - test_expect_code <exit-code> <command>
+
+   Run a command and ensure that it exits with the given exit code.
+   For example:
+
+       test_expect_success 'Merge with d/f conflicts' '
+               test_expect_code 1 git merge "merge msg" B master
+       '
+
  - test_must_fail <git-command>
 
    Run a git command and ensure it fails in a controlled way.  Use
@@ -467,6 +512,17 @@ library for your script to use.
    <expected> file.  This behaves like "cmp" but produces more
    helpful output when the test is run with "-v" option.
 
+ - test_line_count (= | -lt | -ge | ...) <length> <file>
+
+   Check whether a file has the length it is expected to.
+
+ - test_path_is_file <file> [<diagnosis>]
+   test_path_is_dir <dir> [<diagnosis>]
+   test_path_is_missing <path> [<diagnosis>]
+
+   Check whether a file/directory exists or doesn't. <diagnosis> will
+   be displayed if the test fails.
+
  - test_when_finished <script>
 
    Prepend <script> to a list of commands to run to clean up
@@ -481,6 +537,45 @@ library for your script to use.
                ...
        '
 
+Prerequisites
+-------------
+
+These are the prerequisites that the test library predefines with
+test_have_prereq.
+
+See the prereq argument to the test_* functions in the "Test harness
+library" section above and the "test_have_prereq" function for how to
+use these, and "test_set_prereq" for how to define your own.
+
+ - PERL & PYTHON
+
+   Git wasn't compiled with NO_PERL=YesPlease or
+   NO_PYTHON=YesPlease. Wrap any tests that need Perl or Python in
+   these.
+
+ - POSIXPERM
+
+   The filesystem supports POSIX style permission bits.
+
+ - BSLASHPSPEC
+
+   Backslashes in pathspec are not directory separators. This is not
+   set on Windows. See 6fd1106a for details.
+
+ - EXECKEEPSPID
+
+   The process retains the same pid across exec(2). See fb9a2bea for
+   details.
+
+ - SYMLINKS
+
+   The filesystem we're on supports symbolic links. E.g. a FAT
+   filesystem doesn't support these. See 704a3143 for details.
+
+ - SANITY
+
+   Test is not run by root user, and an attempt to write to an
+   unwritable file is expected to fail correctly.
 
 Tips for Writing Tests
 ----------------------
@@ -508,3 +603,115 @@ the purpose of t0000-basic.sh, which is to isolate that level of
 validation in one place.  Your test also ends up needing
 updating when such a change to the internal happens, so do _not_
 do it and leave the low level of validation to t0000-basic.sh.
+
+Test coverage
+-------------
+
+You can use the coverage tests to find code paths that are not being
+used or properly exercised yet.
+
+To do that, run the coverage target at the top-level (not in the t/
+directory):
+
+    make coverage
+
+That'll compile Git with GCC's coverage arguments, and generate a test
+report with gcov after the tests finish. Running the coverage tests
+can take a while, since running the tests in parallel is incompatible
+with GCC's coverage mode.
+
+After the tests have run you can generate a list of untested
+functions:
+
+    make coverage-untested-functions
+
+You can also generate a detailed per-file HTML report using the
+Devel::Cover module. To install it do:
+
+   # On Debian or Ubuntu:
+   sudo aptitude install libdevel-cover-perl
+
+   # From the CPAN with cpanminus
+   curl -L http://cpanmin.us | perl - --sudo --self-upgrade
+   cpanm --sudo Devel::Cover
+
+Then, at the top-level:
+
+    make cover_db_html
+
+That'll generate a detailed cover report in the "cover_db_html"
+directory, which you can then copy to a webserver, or inspect locally
+in a browser.
+
+Smoke testing
+-------------
+
+The Git test suite has support for smoke testing. Smoke testing is
+when you submit the results of a test run to a central server for
+analysis and aggregation.
+
+Running a smoke tester is an easy and valuable way of contributing to
+Git development, particularly if you have access to an uncommon OS on
+obscure hardware.
+
+After building Git you can generate a smoke report like this in the
+"t" directory:
+
+    make clean smoke
+
+You can also pass arguments via the environment. This should make it
+faster:
+
+    GIT_TEST_OPTS='--root=/dev/shm' TEST_JOBS=10 make clean smoke
+
+The "smoke" target will run the Git test suite with Perl's
+"TAP::Harness" module, and package up the results in a .tar.gz archive
+with "TAP::Harness::Archive". The former is included with Perl v5.10.1
+or later, but you'll need to install the latter from the CPAN. See the
+"Test coverage" section above for how you might do that.
+
+Once the "smoke" target finishes you'll see a message like this:
+
+    TAP Archive created at <path to git>/t/test-results/git-smoke.tar.gz
+
+To upload the smoke report you need to have curl(1) installed, then
+do:
+
+    make smoke_report
+
+To upload the report anonymously. Hopefully that'll return something
+like "Reported #7 added.".
+
+If you're going to be uploading reports frequently please request a
+user account by E-Mailing gitsmoke@v.nix.is. Once you have a username
+and password you'll be able to do:
+
+    SMOKE_USERNAME=<username> SMOKE_PASSWORD=<password> make smoke_report
+
+You can also add an additional comment to attach to the report, and/or
+a comma separated list of tags:
+
+    SMOKE_USERNAME=<username> SMOKE_PASSWORD=<password> \
+        SMOKE_COMMENT=<comment> SMOKE_TAGS=<tags> \
+        make smoke_report
+
+Once the report is uploaded it'll be made available at
+http://smoke.git.nix.is, here's an overview of Recent Smoke Reports
+for Git:
+
+    http://smoke.git.nix.is/app/projects/smoke_reports/1
+
+The reports will also be mirrored to GitHub every few hours:
+
+    http://github.com/gitsmoke/smoke-reports
+
+The Smolder SQLite database is also mirrored and made available for
+download:
+
+    http://github.com/gitsmoke/smoke-database
+
+Note that the database includes hashed (with crypt()) user passwords
+and E-Mail addresses. Don't use a valuable password for the smoke
+service if you have an account, or an E-Mail address you don't want to
+be publicly known. The user accounts are just meant to be convenient
+labels, they're not meant to be secure.
index 396b9653a3ad80490cf360c86a910edff58862a2..d34208cc27623a6105f6b12f863c648429d2d34d 100644 (file)
@@ -8,27 +8,27 @@ check_count () {
        $PROG file $head >.result || return 1
        cat .result | perl -e '
                my %expect = (@ARGV);
-               my %count = ();
+               my %count = map { $_ => 0 } keys %expect;
                while (<STDIN>) {
                        if (/^[0-9a-f]+\t\(([^\t]+)\t/) {
                                my $author = $1;
                                for ($author) { s/^\s*//; s/\s*$//; }
-                               if (exists $expect{$author}) {
-                                       $count{$author}++;
-                               }
+                               $count{$author}++;
                        }
                }
                my $bad = 0;
                while (my ($author, $count) = each %count) {
                        my $ok;
-                       if ($expect{$author} != $count) {
+                       my $value = 0;
+                       $value = $expect{$author} if defined $expect{$author};
+                       if ($value != $count) {
                                $bad = 1;
                                $ok = "bad";
                        }
                        else {
                                $ok = "good";
                        }
-                       print STDERR "Author $author (expected $expect{$author}, attributed $count) $ok\n";
+                       print STDERR "Author $author (expected $value, attributed $count) $ok\n";
                }
                exit($bad);
        ' "$@"
@@ -38,8 +38,8 @@ test_expect_success \
     'prepare reference tree' \
     'echo "1A quick brown fox jumps over the" >file &&
      echo "lazy dog" >>file &&
-     git add file
-     GIT_AUTHOR_NAME="A" git commit -a -m "Initial."'
+     git add file &&
+     GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" git commit -a -m "Initial."'
 
 test_expect_success \
     'check all lines blamed on A' \
@@ -49,7 +49,7 @@ test_expect_success \
     'Setup new lines blamed on B' \
     'echo "2A quick brown fox jumps over the" >>file &&
      echo "lazy dog" >> file &&
-     GIT_AUTHOR_NAME="B" git commit -a -m "Second."'
+     GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" git commit -a -m "Second."'
 
 test_expect_success \
     'Two lines blamed on A, two on B' \
@@ -60,7 +60,7 @@ test_expect_success \
     'git checkout -b branch1 master &&
      echo "3A slow green fox jumps into the" >> file &&
      echo "well." >> file &&
-     GIT_AUTHOR_NAME="B1" git commit -a -m "Branch1-1"'
+     GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" git commit -a -m "Branch1-1"'
 
 test_expect_success \
     'Two lines blamed on A, two on B, two on B1' \
@@ -71,7 +71,7 @@ test_expect_success \
     'git checkout -b branch2 master &&
      sed -e "s/2A quick brown/4A quick brown lazy dog/" < file > file.new &&
      mv file.new file &&
-     GIT_AUTHOR_NAME="B2" git commit -a -m "Branch2-1"'
+     GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" git commit -a -m "Branch2-1"'
 
 test_expect_success \
     'Two lines blamed on A, one on B, one on B2' \
@@ -105,7 +105,7 @@ test_expect_success \
 test_expect_success \
     'an incomplete line added' \
     'echo "incomplete" | tr -d "\\012" >>file &&
-    GIT_AUTHOR_NAME="C" git commit -a -m "Incomplete"'
+    GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" git commit -a -m "Incomplete"'
 
 test_expect_success \
     'With incomplete lines.' \
@@ -119,7 +119,7 @@ test_expect_success \
        echo
     } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" > file &&
     echo "incomplete" | tr -d "\\012" >>file &&
-    GIT_AUTHOR_NAME="D" git commit -a -m "edit"'
+    GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" git commit -a -m "edit"'
 
 test_expect_success \
     'some edit' \
index 81ef2a0969d98f05ce22cd8a06fcb55a7af9aee8..b9bb95feaa5088254b002c2806c2c2ae9e9d7be4 100644 (file)
@@ -19,9 +19,9 @@ our \$site_name = '[localhost]';
 our \$site_header = '';
 our \$site_footer = '';
 our \$home_text = 'indextext.html';
-our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/static/gitweb.css');
-our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/static/git-logo.png';
-our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/static/git-favicon.png';
+our @stylesheets = ('file:///$GIT_BUILD_DIR/gitweb/static/gitweb.css');
+our \$logo = 'file:///$GIT_BUILD_DIR/gitweb/static/git-logo.png';
+our \$favicon = 'file:///$GIT_BUILD_DIR/gitweb/static/git-favicon.png';
 our \$projects_list = '';
 our \$export_ok = '';
 our \$strict_export = '';
@@ -32,17 +32,34 @@ EOF
        cat >.git/description <<EOF
 $0 test repository
 EOF
+
+       # You can set the GITWEB_TEST_INSTALLED environment variable to
+       # the gitwebdir (the directory where gitweb is installed / deployed to)
+       # of an existing gitweb instalation to test that installation,
+       # or simply to pathname of installed gitweb script.
+       if test -n "$GITWEB_TEST_INSTALLED" ; then
+               if test -d $GITWEB_TEST_INSTALLED; then
+                       SCRIPT_NAME="$GITWEB_TEST_INSTALLED/gitweb.cgi"
+               else
+                       SCRIPT_NAME="$GITWEB_TEST_INSTALLED"
+               fi
+               test -f "$SCRIPT_NAME" ||
+               error "Cannot find gitweb at $GITWEB_TEST_INSTALLED."
+               say "# Testing $SCRIPT_NAME"
+       else # normal case, use source version of gitweb
+               SCRIPT_NAME="$GIT_BUILD_DIR/gitweb/gitweb.perl"
+       fi
+       export SCRIPT_NAME
 }
 
 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
+               QUERY_STRING PATH_INFO
 
        GITWEB_CONFIG=$(pwd)/gitweb_config.perl
        export GITWEB_CONFIG
@@ -80,9 +97,9 @@ if ! test_have_prereq PERL; then
        test_done
 fi
 
-perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
-    skip_all='skipping gitweb tests, perl version is too old'
-    test_done
+perl -MEncode -e '$e="";decode_utf8($e, Encode::FB_CROAK)' >/dev/null 2>&1 || {
+       skip_all='skipping gitweb tests, perl version is too old'
+       test_done
 }
 
 gitweb_init
diff --git a/t/harness b/t/harness
new file mode 100755 (executable)
index 0000000..f5c02f4
--- /dev/null
+++ b/t/harness
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use Getopt::Long ();
+use TAP::Harness::Archive;
+
+Getopt::Long::Parser->new(
+       config => [ qw/ pass_through / ],
+)->getoptions(
+       'jobs:1'    => \(my $jobs = $ENV{TEST_JOBS}),
+       'archive=s' => \my $archive,
+) or die "$0: Couldn't getoptions()";
+
+TAP::Harness::Archive->new({
+       jobs             => $jobs,
+       archive          => $archive,
+       ($ENV{GIT_TEST_OPTS}
+        ? (test_args    => [ split /\s+/, $ENV{GIT_TEST_OPTS} ])
+        : ()),
+       extra_properties => {},
+})->runtests(@ARGV);
index 648d1619c86bb676faadf62bd831683cb46d6be2..44263ade2533b848220c234441ab67813ca2b0b4 100644 (file)
@@ -3,9 +3,6 @@
 . ./test-lib.sh
 
 unset CVS_SERVER
-# for clean cvsps cache
-HOME=$(pwd)
-export HOME
 
 if ! type cvs >/dev/null 2>&1
 then
@@ -30,6 +27,12 @@ case "$cvsps_version" in
        ;;
 esac
 
+setup_cvs_test_repository () {
+       CVSROOT="$(pwd)/.cvsroot" &&
+       cp -r "$TEST_DIRECTORY/$1/cvsroot" "$CVSROOT" &&
+       export CVSROOT
+}
+
 test_cvs_co () {
        # Usage: test_cvs_co BRANCH_NAME
        rm -rf module-cvs-"$1"
index 92d6d319428223de9205d4d162e3806aedee855b..199f22c231b5ac1479929a89cdf988ac7aff4268 100644 (file)
@@ -68,41 +68,46 @@ svn_cmd () {
        svn "$orig_svncmd" --config-dir "$svnconf" "$@"
 }
 
-for d in \
-       "$SVN_HTTPD_PATH" \
-       /usr/sbin/apache2 \
-       /usr/sbin/httpd \
-; do
-       if test -f "$d"
+prepare_httpd () {
+       for d in \
+               "$SVN_HTTPD_PATH" \
+               /usr/sbin/apache2 \
+               /usr/sbin/httpd \
+       ; do
+               if test -f "$d"
+               then
+                       SVN_HTTPD_PATH="$d"
+                       break
+               fi
+       done
+       if test -z "$SVN_HTTPD_PATH"
        then
-               SVN_HTTPD_PATH="$d"
-               break
+               echo >&2 '*** error: Apache not found'
+               return 1
        fi
-done
-for d in \
-       "$SVN_HTTPD_MODULE_PATH" \
-       /usr/lib/apache2/modules \
-       /usr/libexec/apache2 \
-; do
-       if test -d "$d"
+       for d in \
+               "$SVN_HTTPD_MODULE_PATH" \
+               /usr/lib/apache2/modules \
+               /usr/libexec/apache2 \
+       ; do
+               if test -d "$d"
+               then
+                       SVN_HTTPD_MODULE_PATH="$d"
+                       break
+               fi
+       done
+       if test -z "$SVN_HTTPD_MODULE_PATH"
        then
-               SVN_HTTPD_MODULE_PATH="$d"
-               break
+               echo >&2 '*** error: Apache module dir not found'
+               return 1
        fi
-done
-
-start_httpd () {
-       repo_base_path="$1"
-       if test -z "$SVN_HTTPD_PORT"
+       if test ! -f "$SVN_HTTPD_MODULE_PATH/mod_dav_svn.so"
        then
-               echo >&2 'SVN_HTTPD_PORT is not defined!'
-               return
-       fi
-       if test -z "$repo_base_path"
-       then
-               repo_base_path=svn
+               echo >&2 '*** error: Apache module "mod_dav_svn" not found'
+               return 1
        fi
 
+       repo_base_path="${1-svn}"
        mkdir "$GIT_DIR"/logs
 
        cat > "$GIT_DIR/httpd.conf" <<EOF
@@ -119,12 +124,24 @@ LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
        SVNPath "$rawsvnrepo"
 </Location>
 EOF
+}
+
+start_httpd () {
+       if test -z "$SVN_HTTPD_PORT"
+       then
+               echo >&2 'SVN_HTTPD_PORT is not defined!'
+               return
+       fi
+
+       prepare_httpd "$1" || return 1
+
        "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
        svnrepo="http://127.0.0.1:$SVN_HTTPD_PORT/$repo_base_path"
 }
 
 stop_httpd () {
        test -z "$SVN_HTTPD_PORT" && return
+       test ! -f "$GIT_DIR/httpd.conf" && return
        "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
 }
 
index e733f6516fdf98e48d5c7cd36ecc89d36166eea1..3f24384371bf227126e38da61f3b7efd3961ede8 100644 (file)
@@ -75,12 +75,14 @@ fi
 
 prepare_httpd() {
        mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
+       cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
 
        ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
 
        if test -n "$LIB_HTTPD_SSL"
        then
                HTTPD_URL=https://127.0.0.1:$LIB_HTTPD_PORT
+               AUTH_HTTPD_URL=https://user%40host:user%40host@127.0.0.1:$LIB_HTTPD_PORT
 
                RANDFILE_PATH="$HTTPD_ROOT_PATH"/.rnd openssl req \
                        -config "$TEST_PATH/ssl.cnf" \
@@ -92,6 +94,7 @@ prepare_httpd() {
                HTTPD_PARA="$HTTPD_PARA -DSSL"
        else
                HTTPD_URL=http://127.0.0.1:$LIB_HTTPD_PORT
+               AUTH_HTTPD_URL=http://user%40host:user%40host@127.0.0.1:$LIB_HTTPD_PORT
        fi
 
        if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN"
index 4961505d1dd99da529b43abd42522e6a005961b1..0a4cdfa93ece7d8a4177835b5569583c22303564 100644 (file)
@@ -17,8 +17,33 @@ ErrorLog error.log
 <IfModule !mod_env.c>
        LoadModule env_module modules/mod_env.so
 </IfModule>
+<IfModule !mod_rewrite.c>
+       LoadModule rewrite_module modules/mod_rewrite.so
+</IFModule>
+<IfModule !mod_version.c>
+       LoadModule version_module modules/mod_version.so
+</IfModule>
+
+<IfVersion < 2.1>
+<IfModule !mod_auth.c>
+       LoadModule auth_module modules/mod_auth.so
+</IfModule>
+</IfVersion>
+
+<IfVersion >= 2.1>
+<IfModule !mod_auth_basic.c>
+       LoadModule auth_basic_module modules/mod_auth_basic.so
+</IfModule>
+<IfModule !mod_authn_file.c>
+       LoadModule authn_file_module modules/mod_authn_file.so
+</IfModule>
+<IfModule !mod_authz_user.c>
+       LoadModule authz_user_module modules/mod_authz_user.so
+</IfModule>
+</IfVersion>
 
 Alias /dumb/ www/
+Alias /auth/ www/auth/
 
 <Location /smart/>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
@@ -36,6 +61,10 @@ ScriptAlias /smart_noexport/ ${GIT_EXEC_PATH}/git-http-backend/
        Options ExecCGI
 </Files>
 
+RewriteEngine on
+RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
+RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
+
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
 
@@ -48,6 +77,13 @@ SSLMutex file:ssl_mutex
 SSLEngine On
 </IfDefine>
 
+<Location /auth/>
+       AuthType Basic
+       AuthName "git-auth"
+       AuthUserFile passwd
+       Require valid-user
+</Location>
+
 <IfDefine DAV>
        LoadModule dav_module modules/mod_dav.so
        LoadModule dav_fs_module modules/mod_dav_fs.so
diff --git a/t/lib-httpd/passwd b/t/lib-httpd/passwd
new file mode 100644 (file)
index 0000000..f2fbcad
--- /dev/null
@@ -0,0 +1 @@
+user@host:nKpa8pZUHx/ic
index 375e2486511656df4294f0a4b750fd8d3f16d273..06c3c9176207af6c27c924377f29cc2bd0c881e5 100644 (file)
@@ -2,11 +2,6 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq PERL; then
-       skip_all='skipping --patch tests, perl not available'
-       test_done
-fi
-
 set_state () {
        echo "$3" > "$1" &&
        git add "$1" &&
diff --git a/t/lib-prereq-FILEMODE.sh b/t/lib-prereq-FILEMODE.sh
new file mode 100644 (file)
index 0000000..bce5a4c
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
+#
+
+if test "$(git config --bool core.filemode)" = false
+then
+       say 'filemode disabled on the filesystem'
+else
+       test_set_prereq FILEMODE
+fi
index 6aefe27593e89d57f075bc0d3dbc5a1104d874b7..6ccf7970916b58748aedcce7e583eed2dee782d3 100644 (file)
@@ -47,6 +47,8 @@ for line in $FAKE_LINES; do
        case $line in
        squash|fixup|edit|reword)
                action="$line";;
+       exec*)
+               echo "$line" | sed 's/_/ /g' >> "$1";;
        "#")
                echo '# comment' >> "$1";;
        ">")
diff --git a/t/lib-terminal.sh b/t/lib-terminal.sh
new file mode 100644 (file)
index 0000000..c383b57
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_expect_success 'set up terminal for tests' '
+       if
+               test_have_prereq PERL &&
+               "$PERL_PATH" "$TEST_DIRECTORY"/test-terminal.perl \
+                       sh -c "test -t 1 && test -t 2"
+       then
+               test_set_prereq TTY &&
+               test_terminal () {
+                       if ! test_declared_prereq TTY
+                       then
+                               echo >&4 "test_terminal: need to declare TTY prerequisite"
+                               return 127
+                       fi
+                       "$PERL_PATH" "$TEST_DIRECTORY"/test-terminal.perl "$@"
+               }
+       fi
+'
index f2c73369a5b93544ee837229da965d7efbdc45bf..8deec75c3a0eef961986a0bb313a918ab532f58d 100755 (executable)
@@ -54,9 +54,40 @@ test_expect_success 'success is reported like this' '
 test_expect_failure 'pretend we have a known breakage' '
     false
 '
+
+test_expect_success 'pretend we have fixed a known breakage (run in sub test-lib)' "
+    mkdir passing-todo &&
+    (cd passing-todo &&
+    cat >passing-todo.sh <<EOF &&
+#!$SHELL_PATH
+
+test_description='A passing TODO test
+
+This is run in a sub test-lib so that we do not get incorrect passing
+metrics
+'
+
+# Point to the t/test-lib.sh, which isn't in ../ as usual
+TEST_DIRECTORY=\"$TEST_DIRECTORY\"
+. \"\$TEST_DIRECTORY\"/test-lib.sh
+
 test_expect_failure 'pretend we have fixed a known breakage' '
     :
 '
+
+test_done
+EOF
+    chmod +x passing-todo.sh &&
+    ./passing-todo.sh >out 2>err &&
+    ! test -s err &&
+sed -e 's/^> //' >expect <<EOF &&
+> ok 1 - pretend we have fixed a known breakage # TODO known breakage
+> # fixed 1 known breakage(s)
+> # passed all 1 test(s)
+> 1..1
+EOF
+    test_cmp expect out)
+"
 test_set_prereq HAVEIT
 haveit=no
 test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
@@ -73,27 +104,83 @@ then
        exit 1
 fi
 
+test_set_prereq HAVETHIS
+haveit=no
+test_expect_success HAVETHIS,HAVEIT 'test runs if prerequisites are satisfied' '
+    test_have_prereq HAVEIT &&
+    test_have_prereq HAVETHIS &&
+    haveit=yes
+'
+donthaveit=yes
+test_expect_success HAVEIT,DONTHAVEIT 'unmet prerequisites causes test to be skipped' '
+    donthaveit=no
+'
+donthaveiteither=yes
+test_expect_success DONTHAVEIT,HAVEIT 'unmet prerequisites causes test to be skipped' '
+    donthaveiteither=no
+'
+if test $haveit$donthaveit$donthaveiteither != yesyesyes
+then
+       say "bug in test framework: multiple prerequisite tags do not work reliably"
+       exit 1
+fi
+
 clean=no
 test_expect_success 'tests clean up after themselves' '
     test_when_finished clean=yes
 '
 
-cleaner=no
-test_expect_code 1 'tests clean up even after a failure' '
-    test_when_finished cleaner=yes &&
-    (exit 1)
-'
-
-if test $clean$cleaner != yesyes
+if test $clean != yes
 then
-       say "bug in test framework: cleanup commands do not work reliably"
+       say "bug in test framework: basic cleanup command does not work reliably"
        exit 1
 fi
 
-test_expect_code 2 'failure to clean up causes the test to fail' '
-    test_when_finished "(exit 2)"
+test_expect_success 'tests clean up even on failures' "
+    mkdir failing-cleanup &&
+    (cd failing-cleanup &&
+    cat >failing-cleanup.sh <<EOF &&
+#!$SHELL_PATH
+
+test_description='Failing tests with cleanup commands'
+
+# Point to the t/test-lib.sh, which isn't in ../ as usual
+TEST_DIRECTORY=\"$TEST_DIRECTORY\"
+. \"\$TEST_DIRECTORY\"/test-lib.sh
+
+test_expect_success 'tests clean up even after a failure' '
+    touch clean-after-failure &&
+    test_when_finished rm clean-after-failure &&
+    (exit 1)
+'
+
+test_expect_success 'failure to clean up causes the test to fail' '
+    test_when_finished \"(exit 2)\"
 '
 
+test_done
+EOF
+    chmod +x failing-cleanup.sh &&
+    test_must_fail ./failing-cleanup.sh >out 2>err &&
+    ! test -s err &&
+    ! test -f \"trash directory.failing-cleanup/clean-after-failure\" &&
+sed -e 's/Z$//' -e 's/^> //' >expect <<\EOF &&
+> not ok - 1 tests clean up even after a failure
+> #    Z
+> #        touch clean-after-failure &&
+> #        test_when_finished rm clean-after-failure &&
+> #        (exit 1)
+> #    Z
+> not ok - 2 failure to clean up causes the test to fail
+> #    Z
+> #        test_when_finished \"(exit 2)\"
+> #    Z
+> # failed 2 among 2 test(s)
+> 1..2
+EOF
+    test_cmp expect out)
+"
+
 ################################################################
 # Basics of the basics
 
index 7c0a698b92696ff2bbb91e65e0a42e87f9163a1d..f6849932112f1bbea1dafad09150857a707b64e4 100755 (executable)
@@ -25,7 +25,7 @@ check_config () {
 
 test_expect_success 'plain' '
        (
-               unset GIT_DIR GIT_WORK_TREE
+               sane_unset GIT_DIR GIT_WORK_TREE &&
                mkdir plain &&
                cd plain &&
                git init
@@ -33,9 +33,65 @@ test_expect_success 'plain' '
        check_config plain/.git false unset
 '
 
+test_expect_success 'plain nested in bare' '
+       (
+               sane_unset GIT_DIR GIT_WORK_TREE &&
+               git init --bare bare-ancestor.git &&
+               cd bare-ancestor.git &&
+               mkdir plain-nested &&
+               cd plain-nested &&
+               git init
+       ) &&
+       check_config bare-ancestor.git/plain-nested/.git false unset
+'
+
+test_expect_success 'plain through aliased command, outside any git repo' '
+       (
+               sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG_NOGLOBAL &&
+               HOME=$(pwd)/alias-config &&
+               export HOME &&
+               mkdir alias-config &&
+               echo "[alias] aliasedinit = init" >alias-config/.gitconfig &&
+
+               GIT_CEILING_DIRECTORIES=$(pwd) &&
+               export GIT_CEILING_DIRECTORIES &&
+
+               mkdir plain-aliased &&
+               cd plain-aliased &&
+               git aliasedinit
+       ) &&
+       check_config plain-aliased/.git false unset
+'
+
+test_expect_failure 'plain nested through aliased command' '
+       (
+               sane_unset GIT_DIR GIT_WORK_TREE &&
+               git init plain-ancestor-aliased &&
+               cd plain-ancestor-aliased &&
+               echo "[alias] aliasedinit = init" >>.git/config &&
+               mkdir plain-nested &&
+               cd plain-nested &&
+               git aliasedinit
+       ) &&
+       check_config plain-ancestor-aliased/plain-nested/.git false unset
+'
+
+test_expect_failure 'plain nested in bare through aliased command' '
+       (
+               sane_unset GIT_DIR GIT_WORK_TREE &&
+               git init --bare bare-ancestor-aliased.git &&
+               cd bare-ancestor-aliased.git &&
+               echo "[alias] aliasedinit = init" >>config &&
+               mkdir plain-nested &&
+               cd plain-nested &&
+               git aliasedinit
+       ) &&
+       check_config bare-ancestor-aliased.git/plain-nested/.git false unset
+'
+
 test_expect_success 'plain with GIT_WORK_TREE' '
        if (
-               unset GIT_DIR
+               sane_unset GIT_DIR &&
                mkdir plain-wt &&
                cd plain-wt &&
                GIT_WORK_TREE=$(pwd) git init
@@ -48,7 +104,7 @@ test_expect_success 'plain with GIT_WORK_TREE' '
 
 test_expect_success 'plain bare' '
        (
-               unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
+               sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
                mkdir plain-bare-1 &&
                cd plain-bare-1 &&
                git --bare init
@@ -58,7 +114,7 @@ test_expect_success 'plain bare' '
 
 test_expect_success 'plain bare with GIT_WORK_TREE' '
        if (
-               unset GIT_DIR GIT_CONFIG
+               sane_unset GIT_DIR GIT_CONFIG &&
                mkdir plain-bare-2 &&
                cd plain-bare-2 &&
                GIT_WORK_TREE=$(pwd) git --bare init
@@ -72,7 +128,7 @@ test_expect_success 'plain bare with GIT_WORK_TREE' '
 test_expect_success 'GIT_DIR bare' '
 
        (
-               unset GIT_CONFIG
+               sane_unset GIT_CONFIG &&
                mkdir git-dir-bare.git &&
                GIT_DIR=git-dir-bare.git git init
        ) &&
@@ -82,7 +138,7 @@ test_expect_success 'GIT_DIR bare' '
 test_expect_success 'init --bare' '
 
        (
-               unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
+               sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
                mkdir init-bare.git &&
                cd init-bare.git &&
                git init --bare
@@ -93,7 +149,7 @@ test_expect_success 'init --bare' '
 test_expect_success 'GIT_DIR non-bare' '
 
        (
-               unset GIT_CONFIG
+               sane_unset GIT_CONFIG &&
                mkdir non-bare &&
                cd non-bare &&
                GIT_DIR=.git git init
@@ -104,7 +160,7 @@ test_expect_success 'GIT_DIR non-bare' '
 test_expect_success 'GIT_DIR & GIT_WORK_TREE (1)' '
 
        (
-               unset GIT_CONFIG
+               sane_unset GIT_CONFIG &&
                mkdir git-dir-wt-1.git &&
                GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-1.git git init
        ) &&
@@ -114,7 +170,7 @@ test_expect_success 'GIT_DIR & GIT_WORK_TREE (1)' '
 test_expect_success 'GIT_DIR & GIT_WORK_TREE (2)' '
 
        if (
-               unset GIT_CONFIG
+               sane_unset GIT_CONFIG &&
                mkdir git-dir-wt-2.git &&
                GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-2.git git --bare init
        )
@@ -127,7 +183,7 @@ test_expect_success 'GIT_DIR & GIT_WORK_TREE (2)' '
 test_expect_success 'reinit' '
 
        (
-               unset GIT_CONFIG GIT_WORK_TREE GIT_CONFIG
+               sane_unset GIT_CONFIG GIT_WORK_TREE GIT_CONFIG &&
 
                mkdir again &&
                cd again &&
@@ -171,14 +227,12 @@ test_expect_success 'init with init.templatedir set' '
        mkdir templatedir-source &&
        echo Content >templatedir-source/file &&
        (
-               HOME="`pwd`" &&
-               export HOME &&
                test_config="${HOME}/.gitconfig" &&
                git config -f "$test_config"  init.templatedir "${HOME}/templatedir-source" &&
                mkdir templatedir-set &&
                cd templatedir-set &&
-               unset GIT_CONFIG_NOGLOBAL &&
-               unset GIT_TEMPLATE_DIR &&
+               sane_unset GIT_CONFIG_NOGLOBAL &&
+               sane_unset GIT_TEMPLATE_DIR &&
                NO_SET_GIT_TEMPLATE_DIR=t &&
                export NO_SET_GIT_TEMPLATE_DIR &&
                git init
@@ -188,10 +242,8 @@ test_expect_success 'init with init.templatedir set' '
 
 test_expect_success 'init --bare/--shared overrides system/global config' '
        (
-               HOME="`pwd`" &&
-               export HOME &&
                test_config="$HOME"/.gitconfig &&
-               unset GIT_CONFIG_NOGLOBAL &&
+               sane_unset GIT_CONFIG_NOGLOBAL &&
                git config -f "$test_config" core.bare false &&
                git config -f "$test_config" core.sharedRepository 0640 &&
                mkdir init-bare-shared-override &&
@@ -205,10 +257,8 @@ test_expect_success 'init --bare/--shared overrides system/global config' '
 
 test_expect_success 'init honors global core.sharedRepository' '
        (
-               HOME="`pwd`" &&
-               export HOME &&
                test_config="$HOME"/.gitconfig &&
-               unset GIT_CONFIG_NOGLOBAL &&
+               sane_unset GIT_CONFIG_NOGLOBAL &&
                git config -f "$test_config" core.sharedRepository 0666 &&
                mkdir shared-honor-global &&
                cd shared-honor-global &&
@@ -301,7 +351,7 @@ test_expect_success 'init notices EEXIST (2)' '
        )
 '
 
-test_expect_success POSIXPERM 'init notices EPERM' '
+test_expect_success POSIXPERM,SANITY 'init notices EPERM' '
        rm -fr newdir &&
        (
                mkdir newdir &&
index 53bd7fcc4abbf6cb7db73b43d5dde477f5115f90..ebbc7554a7d4dce4c2ed33a79f5d4feb3a520f05 100755 (executable)
@@ -36,6 +36,9 @@ test_expect_success 'setup' '
                echo "d/* test=a/b/d/*"
                echo "d/yes notest"
        ) >a/b/.gitattributes
+       (
+               echo "global test=global"
+       ) >"$HOME"/global-gitattributes
 
 '
 
@@ -48,18 +51,28 @@ test_expect_success 'attribute test' '
        attr_check a/b/g a/b/g &&
        attr_check b/g unspecified &&
        attr_check a/b/h a/b/h &&
-       attr_check a/b/d/g "a/b/d/*"
-       attr_check onoff unset
-       attr_check offon set
-       attr_check no unspecified
-       attr_check a/b/d/no "a/b/d/*"
+       attr_check a/b/d/g "a/b/d/*" &&
+       attr_check onoff unset &&
+       attr_check offon set &&
+       attr_check no unspecified &&
+       attr_check a/b/d/no "a/b/d/*" &&
        attr_check a/b/d/yes unspecified
 
 '
 
+test_expect_success 'core.attributesfile' '
+       attr_check global unspecified &&
+       git config core.attributesfile "$HOME/global-gitattributes" &&
+       attr_check global global &&
+       git config core.attributesfile "~/global-gitattributes" &&
+       attr_check global global &&
+       echo "global test=precedence" >> .gitattributes &&
+       attr_check global precedence
+'
+
 test_expect_success 'attribute test: read paths from stdin' '
 
-       cat <<EOF > expect
+       cat <<EOF > expect &&
 f: test: f
 a/f: test: f
 a/c/f: test: f
index 2342ac5788a9976b591cb78593279f092d1dc2f6..e3137d638ee5bb07b9278c1a9b90a207ad024a08 100755 (executable)
@@ -15,54 +15,30 @@ test_expect_success setup '
 
 '
 
-test_expect_success POSIXPERM 'write-tree should notice unwritable repository' '
-
-       (
-               chmod a-w .git/objects .git/objects/?? &&
-               test_must_fail git write-tree
-       )
-       status=$?
-       chmod 775 .git/objects .git/objects/??
-       (exit $status)
-
+test_expect_success POSIXPERM,SANITY 'write-tree should notice unwritable repository' '
+       test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+       chmod a-w .git/objects .git/objects/?? &&
+       test_must_fail git write-tree
 '
 
-test_expect_success POSIXPERM 'commit should notice unwritable repository' '
-
-       (
-               chmod a-w .git/objects .git/objects/?? &&
-               test_must_fail git commit -m second
-       )
-       status=$?
-       chmod 775 .git/objects .git/objects/??
-       (exit $status)
-
+test_expect_success POSIXPERM,SANITY 'commit should notice unwritable repository' '
+       test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+       chmod a-w .git/objects .git/objects/?? &&
+       test_must_fail git commit -m second
 '
 
-test_expect_success POSIXPERM 'update-index should notice unwritable repository' '
-
-       (
-               echo 6O >file &&
-               chmod a-w .git/objects .git/objects/?? &&
-               test_must_fail git update-index file
-       )
-       status=$?
-       chmod 775 .git/objects .git/objects/??
-       (exit $status)
-
+test_expect_success POSIXPERM,SANITY 'update-index should notice unwritable repository' '
+       test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+       echo 6O >file &&
+       chmod a-w .git/objects .git/objects/?? &&
+       test_must_fail git update-index file
 '
 
-test_expect_success POSIXPERM 'add should notice unwritable repository' '
-
-       (
-               echo b >file &&
-               chmod a-w .git/objects .git/objects/?? &&
-               test_must_fail git add file
-       )
-       status=$?
-       chmod 775 .git/objects .git/objects/??
-       (exit $status)
-
+test_expect_success POSIXPERM,SANITY 'add should notice unwritable repository' '
+       test_when_finished "chmod 775 .git/objects .git/objects/??" &&
+       echo b >file &&
+       chmod a-w .git/objects .git/objects/?? &&
+       test_must_fail git add file
 '
 
 test_done
index 234a94f3e6311c529b5bb476ee5a6a2ea705017f..1a8f44c44ca3cce70990ec56fec61894be9b3086 100755 (executable)
@@ -439,7 +439,7 @@ test_expect_success 'checkout when deleting .gitattributes' '
        git rm .gitattributes &&
        echo "contentsQ" | q_to_cr > .file2 &&
        git add .file2 &&
-       git commit -m third
+       git commit -m third &&
 
        git checkout master~1 &&
        git checkout master &&
index 828e35baf72d94908ad1f30dbd2e1aa6f9376e69..9078b84ae68b430285312749f62daee103dcc428 100755 (executable)
@@ -93,4 +93,47 @@ test_expect_success expanded_in_repo '
        cmp expanded-keywords expected-output
 '
 
+# The use of %f in a filter definition is expanded to the path to
+# the filename being smudged or cleaned.  It must be shell escaped.
+# First, set up some interesting file names and pet them in
+# .gitattributes.
+test_expect_success 'filter shell-escaped filenames' '
+       cat >argc.sh <<-EOF &&
+       #!$SHELL_PATH
+       cat >/dev/null
+       echo argc: \$# "\$@"
+       EOF
+       normal=name-no-magic &&
+       special="name  with '\''sq'\'' and \$x" &&
+       echo some test text >"$normal" &&
+       echo some test text >"$special" &&
+       git add "$normal" "$special" &&
+       git commit -q -m "add files" &&
+       echo "name* filter=argc" >.gitattributes &&
+
+       # delete the files and check them out again, using a smudge filter
+       # that will count the args and echo the command-line back to us
+       git config filter.argc.smudge "sh ./argc.sh %f" &&
+       rm "$normal" "$special" &&
+       git checkout -- "$normal" "$special" &&
+
+       # make sure argc.sh counted the right number of args
+       echo "argc: 1 $normal" >expect &&
+       test_cmp expect "$normal" &&
+       echo "argc: 1 $special" >expect &&
+       test_cmp expect "$special" &&
+
+       # do the same thing, but with more args in the filter expression
+       git config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" &&
+       rm "$normal" "$special" &&
+       git checkout -- "$normal" "$special" &&
+
+       # make sure argc.sh counted the right number of args
+       echo "argc: 2 $normal --my-extra-arg" >expect &&
+       test_cmp expect "$normal" &&
+       echo "argc: 2 $special --my-extra-arg" >expect &&
+       test_cmp expect "$special" &&
+       :
+'
+
 test_done
index c7d0324374e9df5131a58a9ae0cadf7ee4dc03e7..ec6c1b3f8a7eac0e1734883a0ad2f2d68f11bf96 100755 (executable)
@@ -7,7 +7,7 @@ UNZIP=${UNZIP:-unzip}
 
 test_expect_success setup '
 
-       git config core.autocrlf true
+       git config core.autocrlf true &&
 
        printf "CRLF line ending\r\nAnd another\r\n" > sample &&
        git add sample &&
@@ -20,7 +20,7 @@ test_expect_success setup '
 test_expect_success 'tar archive' '
 
        git archive --format=tar HEAD |
-       ( mkdir untarred && cd untarred && "$TAR" -xf - )
+       ( mkdir untarred && cd untarred && "$TAR" -xf - ) &&
 
        test_cmp sample untarred/sample
 
index f37ac8fa0b720d1a72536033c3334a6f5244d5d7..fe0164be62ad9ae7a2ea426c6e576cd1133f3d67 100755 (executable)
@@ -12,7 +12,7 @@ test_expect_success setup '
 
        git config core.autocrlf false &&
 
-       echo "one text" > .gitattributes
+       echo "one text" > .gitattributes &&
 
        for w in Hello world how are you; do echo $w; done >one &&
        for w in I am very very fine thank you; do echo $w; done >two &&
index 41df6bcf279a1abc4462e63866076094cfbdedd8..1542cf6a1313963fccdf42702c620be9e05f7143 100755 (executable)
@@ -4,22 +4,22 @@ test_description='Various filesystem issues'
 
 . ./test-lib.sh
 
-auml=`printf '\xc3\xa4'`
-aumlcdiar=`printf '\x61\xcc\x88'`
+auml=$(printf '\303\244')
+aumlcdiar=$(printf '\141\314\210')
 
 case_insensitive=
 unibad=
 no_symlinks=
 test_expect_success 'see what we expect' '
 
-       test_case=test_expect_success
-       test_unicode=test_expect_success
+       test_case=test_expect_success &&
+       test_unicode=test_expect_success &&
        mkdir junk &&
        echo good >junk/CamelCase &&
        echo bad >junk/camelcase &&
        if test "$(cat junk/CamelCase)" != good
        then
-               test_case=test_expect_failure
+               test_case=test_expect_failure &&
                case_insensitive=t
        fi &&
        rm -fr junk &&
@@ -27,7 +27,7 @@ test_expect_success 'see what we expect' '
        >junk/"$auml" &&
        case "$(cd junk && echo *)" in
        "$aumlcdiar")
-               test_unicode=test_expect_failure
+               test_unicode=test_expect_failure &&
                unibad=t
                ;;
        *)      ;;
@@ -36,7 +36,7 @@ test_expect_success 'see what we expect' '
        {
                ln -s x y 2> /dev/null &&
                test -h y 2> /dev/null ||
-               no_symlinks=1
+               no_symlinks=1 &&
                rm -f y
        }
 '
@@ -128,7 +128,7 @@ test_expect_success "setup unicode normalization tests" '
   cd unicode &&
   touch "$aumlcdiar" &&
   git add "$aumlcdiar" &&
-  git commit -m initial
+  git commit -m initial &&
   git tag initial &&
   git checkout -b topic &&
   git mv $aumlcdiar tmp &&
index 680d7d68612b168a2642ab64e7bc6c15c316d5b4..9bee8bfd2e063c40bae2d76930370bd9e8ba8fa5 100755 (executable)
@@ -12,4 +12,17 @@ test_expect_success 'character classes (isspace, isalpha etc.)' '
        test-ctype
 '
 
+test_expect_success 'mktemp to nonexistent directory prints filename' '
+       test_must_fail test-mktemp doesnotexist/testXXXXXX 2>err &&
+       grep "doesnotexist/test" err
+'
+
+test_expect_success POSIXPERM 'mktemp to unwritable directory prints filename' '
+       mkdir cannotwrite &&
+       chmod -w cannotwrite &&
+       test_when_finished "chmod +w cannotwrite" &&
+       test_must_fail test-mktemp cannotwrite/testXXXXXX 2>err &&
+       grep "cannotwrite/test" err
+'
+
 test_done
diff --git a/t/t0080-vcs-svn.sh b/t/t0080-vcs-svn.sh
new file mode 100755 (executable)
index 0000000..d3225ad
--- /dev/null
@@ -0,0 +1,171 @@
+#!/bin/sh
+
+test_description='check infrastructure for svn importer'
+
+. ./test-lib.sh
+uint32_max=4294967295
+
+test_expect_success 'obj pool: store data' '
+       cat <<-\EOF >expected &&
+       0
+       1
+       EOF
+
+       test-obj-pool <<-\EOF >actual &&
+       alloc one 16
+       set one 13
+       test one 13
+       reset one
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'obj pool: NULL is offset ~0' '
+       echo "$uint32_max" >expected &&
+       echo null one | test-obj-pool >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'obj pool: out-of-bounds access' '
+       cat <<-EOF >expected &&
+       0
+       0
+       $uint32_max
+       $uint32_max
+       16
+       20
+       $uint32_max
+       EOF
+
+       test-obj-pool <<-\EOF >actual &&
+       alloc one 16
+       alloc two 16
+       offset one 20
+       offset two 20
+       alloc one 5
+       offset one 20
+       free one 1
+       offset one 20
+       reset one
+       reset two
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'obj pool: high-water mark' '
+       cat <<-\EOF >expected &&
+       0
+       0
+       10
+       20
+       20
+       20
+       EOF
+
+       test-obj-pool <<-\EOF >actual &&
+       alloc one 10
+       committed one
+       alloc one 10
+       commit one
+       committed one
+       alloc one 10
+       free one 20
+       committed one
+       reset one
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'line buffer' '
+       echo HELLO >expected1 &&
+       printf "%s\n" "" HELLO >expected2 &&
+       echo >expected3 &&
+       printf "%s\n" "" Q | q_to_nul >expected4 &&
+       printf "%s\n" foo "" >expected5 &&
+       printf "%s\n" "" foo >expected6 &&
+
+       test-line-buffer <<-\EOF >actual1 &&
+       5
+       HELLO
+       EOF
+
+       test-line-buffer <<-\EOF >actual2 &&
+       0
+
+       5
+       HELLO
+       EOF
+
+       q_to_nul <<-\EOF |
+       1
+       Q
+       EOF
+       test-line-buffer >actual3 &&
+
+       q_to_nul <<-\EOF |
+       0
+
+       1
+       Q
+       EOF
+       test-line-buffer >actual4 &&
+
+       test-line-buffer <<-\EOF >actual5 &&
+       5
+       foo
+       EOF
+
+       test-line-buffer <<-\EOF >actual6 &&
+       0
+
+       5
+       foo
+       EOF
+
+       test_cmp expected1 actual1 &&
+       test_cmp expected2 actual2 &&
+       test_cmp expected3 actual3 &&
+       test_cmp expected4 actual4 &&
+       test_cmp expected5 actual5 &&
+       test_cmp expected6 actual6
+'
+
+test_expect_success 'string pool' '
+       echo a does not equal b >expected.differ &&
+       echo a equals a >expected.match &&
+       echo equals equals equals >expected.matchmore &&
+
+       test-string-pool "a,--b" >actual.differ &&
+       test-string-pool "a,a" >actual.match &&
+       test-string-pool "equals-equals" >actual.matchmore &&
+       test_must_fail test-string-pool a,a,a &&
+       test_must_fail test-string-pool a &&
+
+       test_cmp expected.differ actual.differ &&
+       test_cmp expected.match actual.match &&
+       test_cmp expected.matchmore actual.matchmore
+'
+
+test_expect_success 'treap sort' '
+       cat <<-\EOF >unsorted &&
+       68
+       12
+       13
+       13
+       68
+       13
+       13
+       21
+       10
+       11
+       12
+       13
+       13
+       EOF
+       sort unsorted >expected &&
+
+       test-treap <unsorted >actual &&
+       test_cmp expected actual
+'
+
+test_done
index 4f171722d9e1dda7668f54b4104267eb70d91c67..ca8a4098fa06afbdbe5c427983d3953550457bc2 100755 (executable)
@@ -309,7 +309,7 @@ test_expect_success \
 test_expect_success \
     '6 - must not exist in O && !A && !B case' "
      rm -f .git/index DD &&
-     echo DD >DD
+     echo DD >DD &&
      git update-index --add DD &&
      test_must_fail git read-tree -m $tree_O $tree_A $tree_B
 "
index 93ca84f9e6d3dfd859d8bd4941f4d2c5a354ab6c..680d992f229cda9f1904240f593d66dc1cf3e32d 100755 (executable)
@@ -98,8 +98,8 @@ test_expect_success \
      git checkout-index -u -f -q -a &&
      git update-index --add yomin &&
      read_tree_twoway $treeH $treeM &&
-     git ls-files --stage >4.out || return 1
-     git diff --no-index M.out 4.out >4diff.out
+     git ls-files --stage >4.out &&
+     test_must_fail git diff --no-index M.out 4.out >4diff.out &&
      compare_change 4diff.out expected &&
      check_cache_at yomin clean'
 
@@ -112,8 +112,8 @@ test_expect_success \
      git update-index --add yomin &&
      echo yomin yomin >yomin &&
      read_tree_twoway $treeH $treeM &&
-     git ls-files --stage >5.out || return 1
-     git diff --no-index M.out 5.out >5diff.out
+     git ls-files --stage >5.out &&
+     test_must_fail git diff --no-index M.out 5.out >5diff.out &&
      compare_change 5diff.out expected &&
      check_cache_at yomin dirty'
 
@@ -213,8 +213,8 @@ test_expect_success \
      echo nitfol nitfol >nitfol &&
      git update-index --add nitfol &&
      read_tree_twoway $treeH $treeM &&
-     git ls-files --stage >14.out || return 1
-     git diff --no-index M.out 14.out >14diff.out
+     git ls-files --stage >14.out &&
+     test_must_fail git diff --no-index M.out 14.out >14diff.out &&
      compare_change 14diff.out expected &&
      check_cache_at nitfol clean'
 
@@ -227,8 +227,8 @@ test_expect_success \
      git update-index --add nitfol &&
      echo nitfol nitfol nitfol >nitfol &&
      read_tree_twoway $treeH $treeM &&
-     git ls-files --stage >15.out || return 1
-     git diff --no-index M.out 15.out >15diff.out
+     git ls-files --stage >15.out &&
+     test_must_fail git diff --no-index M.out 15.out >15diff.out &&
      compare_change 15diff.out expected &&
      check_cache_at nitfol dirty'
 
@@ -377,7 +377,7 @@ test_expect_success \
      git ls-files --stage >treeM.out &&
 
      rm -f a &&
-     mkdir a
+     mkdir a &&
      : >a/b &&
      git update-index --add --remove a a/b &&
      treeH=`git write-tree` &&
@@ -394,7 +394,7 @@ test_expect_success '-m references the correct modified tree' '
        echo >file-a &&
        echo >file-b &&
        git add file-a file-b &&
-       git commit -a -m "test for correct modified tree"
+       git commit -a -m "test for correct modified tree" &&
        git branch initial-mod &&
        echo b >file-b &&
        git commit -a -m "B" &&
index 0241329a08af0be642131ffe4b619231d9ea3844..a4a17e001739a45fc7d53e76bd8bbb14426492f9 100755 (executable)
@@ -205,8 +205,8 @@ test_expect_success \
      echo nitfol nitfol >nitfol &&
      git update-index --add nitfol &&
      git read-tree -m -u $treeH $treeM &&
-     git ls-files --stage >14.out || return 1
-     git diff -U0 --no-index M.out 14.out >14diff.out
+     git ls-files --stage >14.out &&
+     test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
      compare_change 14diff.out expected &&
      sum bozbar frotz >actual14.sum &&
      grep -v nitfol M.sum > expected14.sum &&
@@ -226,8 +226,8 @@ test_expect_success \
      git update-index --add nitfol &&
      echo nitfol nitfol nitfol >nitfol &&
      git read-tree -m -u $treeH $treeM &&
-     git ls-files --stage >15.out || return 1
-     git diff -U0 --no-index M.out 15.out >15diff.out
+     git ls-files --stage >15.out &&
+     test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
      compare_change 15diff.out expected &&
      check_cache_at nitfol dirty &&
      sum bozbar frotz >actual15.sum &&
@@ -314,7 +314,7 @@ test_expect_success \
 # Also make sure we did not break DF vs DF/DF case.
 test_expect_success \
     'DF vs DF/DF case setup.' \
-    'rm -f .git/index
+    'rm -f .git/index &&
      echo DF >DF &&
      git update-index --add DF &&
      treeDF=`git write-tree` &&
index f19b4a2a4afa89fd1242d1acccb1e999e6a88c6d..eb8e3d447613ce7383f53a7a09039c01d72f35ef 100755 (executable)
@@ -177,7 +177,7 @@ test_expect_success SYMLINKS 'funny symlink in work tree' '
 
 '
 
-test_expect_success SYMLINKS 'funny symlink in work tree, un-unlink-able' '
+test_expect_success SYMLINKS,SANITY 'funny symlink in work tree, un-unlink-able' '
 
        rm -fr a b &&
        git reset --hard &&
index 62246dbf9546b9ee5ad4dbeb0b34de45a1671ac2..de84e35c4357c7329b3fae749228df28f31a9d3f 100755 (executable)
@@ -1,45 +1,57 @@
 #!/bin/sh
 
-test_description='sparse checkout tests'
+test_description='sparse checkout tests
+
+* (tag: removed, master) removed
+| D    sub/added
+* (HEAD, tag: top) modified and added
+| M    init.t
+| A    sub/added
+* (tag: init) init
+  A    init.t
+'
 
 . ./test-lib.sh
 
-cat >expected <<EOF
-100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0      init.t
-100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      sub/added
-EOF
 test_expect_success 'setup' '
+       cat >expected <<-\EOF &&
+       100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0       init.t
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       sub/added
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       subsub/added
+       EOF
+       cat >expected.swt <<-\EOF &&
+       H init.t
+       H sub/added
+       H subsub/added
+       EOF
+
        test_commit init &&
-       echo modified >> init.t &&
-       mkdir sub &&
-       touch sub/added &&
-       git add init.t sub/added &&
+       echo modified >>init.t &&
+       mkdir sub subsub &&
+       touch sub/added subsub/added &&
+       git add init.t sub/added subsub/added &&
        git commit -m "modified and added" &&
        git tag top &&
        git rm sub/added &&
        git commit -m removed &&
        git tag removed &&
        git checkout top &&
-       git ls-files --stage > result &&
+       git ls-files --stage >result &&
        test_cmp expected result
 '
 
-cat >expected.swt <<EOF
-H init.t
-H sub/added
-EOF
 test_expect_success 'read-tree without .git/info/sparse-checkout' '
        git read-tree -m -u HEAD &&
-       git ls-files --stage > result &&
+       git ls-files --stage >result &&
        test_cmp expected result &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result
 '
 
 test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
-       echo > .git/info/sparse-checkout
+       echo >.git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
        test -f sub/added
@@ -47,9 +59,9 @@ test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
 
 test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-checkout and enabled' '
        git config core.sparsecheckout true &&
-       echo > .git/info/sparse-checkout &&
+       echo >.git/info/sparse-checkout &&
        git read-tree --no-sparse-checkout -m -u HEAD &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
        test -f sub/added
@@ -57,94 +69,123 @@ test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-
 
 test_expect_success 'read-tree with empty .git/info/sparse-checkout' '
        git config core.sparsecheckout true &&
-       echo > .git/info/sparse-checkout &&
+       echo >.git/info/sparse-checkout &&
        test_must_fail git read-tree -m -u HEAD &&
-       git ls-files --stage > result &&
+       git ls-files --stage >result &&
        test_cmp expected result &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
        test -f sub/added
 '
 
-cat >expected.swt <<EOF
-S init.t
-H sub/added
-EOF
 test_expect_success 'match directories with trailing slash' '
+       cat >expected.swt-noinit <<-\EOF &&
+       S init.t
+       H sub/added
+       S subsub/added
+       EOF
+
        echo sub/ > .git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
        git ls-files -t > result &&
-       test_cmp expected.swt result &&
+       test_cmp expected.swt-noinit result &&
        test ! -f init.t &&
        test -f sub/added
 '
 
-cat >expected.swt <<EOF
-H init.t
-H sub/added
-EOF
-test_expect_failure 'match directories without trailing slash' '
-       echo init.t > .git/info/sparse-checkout &&
-       echo sub >> .git/info/sparse-checkout &&
+test_expect_success 'match directories without trailing slash' '
+       echo sub >>.git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
-       git ls-files -t > result &&
-       test_cmp expected.swt result &&
+       git ls-files -t >result &&
+       test_cmp expected.swt-noinit result &&
+       test ! -f init.t &&
+       test -f sub/added
+'
+
+test_expect_success 'match directory pattern' '
+       echo "s?b" >>.git/info/sparse-checkout &&
+       git read-tree -m -u HEAD &&
+       git ls-files -t >result &&
+       test_cmp expected.swt-noinit result &&
        test ! -f init.t &&
        test -f sub/added
 '
 
-cat >expected.swt <<EOF
-H init.t
-S sub/added
-EOF
 test_expect_success 'checkout area changes' '
-       echo init.t > .git/info/sparse-checkout &&
+       cat >expected.swt-nosub <<-\EOF &&
+       H init.t
+       S sub/added
+       S subsub/added
+       EOF
+
+       echo init.t >.git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
-       git ls-files -t > result &&
-       test_cmp expected.swt result &&
+       git ls-files -t >result &&
+       test_cmp expected.swt-nosub result &&
        test -f init.t &&
        test ! -f sub/added
 '
 
 test_expect_success 'read-tree updates worktree, absent case' '
-       echo sub/added > .git/info/sparse-checkout &&
+       echo sub/added >.git/info/sparse-checkout &&
        git checkout -f top &&
        git read-tree -m -u HEAD^ &&
        test ! -f init.t
 '
 
 test_expect_success 'read-tree updates worktree, dirty case' '
-       echo sub/added > .git/info/sparse-checkout &&
+       echo sub/added >.git/info/sparse-checkout &&
        git checkout -f top &&
-       echo dirty > init.t &&
+       echo dirty >init.t &&
        git read-tree -m -u HEAD^ &&
        grep -q dirty init.t &&
        rm init.t
 '
 
 test_expect_success 'read-tree removes worktree, dirty case' '
-       echo init.t > .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
        git checkout -f top &&
-       echo dirty > added &&
+       echo dirty >added &&
        git read-tree -m -u HEAD^ &&
        grep -q dirty added
 '
 
 test_expect_success 'read-tree adds to worktree, absent case' '
-       echo init.t > .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
        git checkout -f removed &&
        git read-tree -u -m HEAD^ &&
        test ! -f sub/added
 '
 
 test_expect_success 'read-tree adds to worktree, dirty case' '
-       echo init.t > .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
        git checkout -f removed &&
        mkdir sub &&
-       echo dirty > sub/added &&
+       echo dirty >sub/added &&
        git read-tree -u -m HEAD^ &&
        grep -q dirty sub/added
 '
 
+test_expect_success 'index removal and worktree narrowing at the same time' '
+       >empty &&
+       echo init.t >.git/info/sparse-checkout &&
+       echo sub/added >>.git/info/sparse-checkout &&
+       git checkout -f top &&
+       echo init.t >.git/info/sparse-checkout &&
+       git checkout removed &&
+       git ls-files sub/added >result &&
+       test ! -f sub/added &&
+       test_cmp empty result
+'
+
+test_expect_success 'read-tree --reset removes outside worktree' '
+       >empty &&
+       echo init.t >.git/info/sparse-checkout &&
+       git checkout -f top &&
+       git reset --hard removed &&
+       git ls-files sub/added >result &&
+       test_cmp empty result
+'
+
 test_done
index 56874996a6df278202c0bc476abec4bca3ce99c5..1fd187c5eb188671934f6afe85ca5003c529a942 100755 (executable)
@@ -16,24 +16,23 @@ test_expect_success setup '
        cp one original.one &&
        cp dir/two original.two
 '
-HERE=`pwd`
 LF='
 '
 
 test_expect_success 'update-index and ls-files' '
-       cd "$HERE" &&
        git update-index --add one &&
        case "`git ls-files`" in
        one) echo pass one ;;
        *) echo bad one; exit 1 ;;
        esac &&
-       cd dir &&
-       git update-index --add two &&
-       case "`git ls-files`" in
-       two) echo pass two ;;
-       *) echo bad two; exit 1 ;;
-       esac &&
-       cd .. &&
+       (
+               cd dir &&
+               git update-index --add two &&
+               case "`git ls-files`" in
+               two) echo pass two ;;
+               *) echo bad two; exit 1 ;;
+               esac
+       ) &&
        case "`git ls-files`" in
        dir/two"$LF"one) echo pass both ;;
        *) echo bad; exit 1 ;;
@@ -41,20 +40,20 @@ test_expect_success 'update-index and ls-files' '
 '
 
 test_expect_success 'cat-file' '
-       cd "$HERE" &&
        two=`git ls-files -s dir/two` &&
        two=`expr "$two" : "[0-7]* \\([0-9a-f]*\\)"` &&
        echo "$two" &&
        git cat-file -p "$two" >actual &&
        cmp dir/two actual &&
-       cd dir &&
-       git cat-file -p "$two" >actual &&
-       cmp two actual
+       (
+               cd dir &&
+               git cat-file -p "$two" >actual &&
+               cmp two actual
+       )
 '
 rm -f actual dir/actual
 
 test_expect_success 'diff-files' '
-       cd "$HERE" &&
        echo a >>one &&
        echo d >>dir/two &&
        case "`git diff-files --name-only`" in
@@ -62,77 +61,96 @@ test_expect_success 'diff-files' '
        *) echo bad top; exit 1 ;;
        esac &&
        # diff should not omit leading paths
-       cd dir &&
-       case "`git diff-files --name-only`" in
-       dir/two"$LF"one) echo pass subdir ;;
-       *) echo bad subdir; exit 1 ;;
-       esac &&
-       case "`git diff-files --name-only .`" in
-       dir/two) echo pass subdir limited ;;
-       *) echo bad subdir limited; exit 1 ;;
-       esac
+       (
+               cd dir &&
+               case "`git diff-files --name-only`" in
+               dir/two"$LF"one) echo pass subdir ;;
+               *) echo bad subdir; exit 1 ;;
+               esac &&
+               case "`git diff-files --name-only .`" in
+               dir/two) echo pass subdir limited ;;
+               *) echo bad subdir limited; exit 1 ;;
+               esac
+       )
 '
 
 test_expect_success 'write-tree' '
-       cd "$HERE" &&
        top=`git write-tree` &&
        echo $top &&
-       cd dir &&
-       sub=`git write-tree` &&
-       echo $sub &&
-       test "z$top" = "z$sub"
+       (
+               cd dir &&
+               sub=`git write-tree` &&
+               echo $sub &&
+               test "z$top" = "z$sub"
+       )
 '
 
 test_expect_success 'checkout-index' '
-       cd "$HERE" &&
        git checkout-index -f -u one &&
        cmp one original.one &&
-       cd dir &&
-       git checkout-index -f -u two &&
-       cmp two ../original.two
+       (
+               cd dir &&
+               git checkout-index -f -u two &&
+               cmp two ../original.two
+       )
 '
 
 test_expect_success 'read-tree' '
-       cd "$HERE" &&
        rm -f one dir/two &&
        tree=`git write-tree` &&
        git read-tree --reset -u "$tree" &&
        cmp one original.one &&
        cmp dir/two original.two &&
-       cd dir &&
-       rm -f two &&
-       git read-tree --reset -u "$tree" &&
-       cmp two ../original.two &&
-       cmp ../one ../original.one
+       (
+               cd dir &&
+               rm -f two &&
+               git read-tree --reset -u "$tree" &&
+               cmp two ../original.two &&
+               cmp ../one ../original.one
+       )
 '
 
+test_expect_success 'alias expansion' '
+       (
+               git config alias.ss status &&
+               cd dir &&
+               git status &&
+               git ss
+       )
+'
 test_expect_success 'no file/rev ambiguity check inside .git' '
-       cd "$HERE" &&
        git commit -a -m 1 &&
-       cd "$HERE"/.git &&
-       git show -s HEAD
+       (
+               cd .git &&
+               git show -s HEAD
+       )
 '
 
 test_expect_success 'no file/rev ambiguity check inside a bare repo' '
-       cd "$HERE" &&
        git clone -s --bare .git foo.git &&
-       cd foo.git && GIT_DIR=. git show -s HEAD
+       (
+               cd foo.git &&
+               GIT_DIR=. git show -s HEAD
+       )
 '
 
 # This still does not work as it should...
 : test_expect_success 'no file/rev ambiguity check inside a bare repo' '
-       cd "$HERE" &&
        git clone -s --bare .git foo.git &&
-       cd foo.git && git show -s HEAD
+       (
+               cd foo.git &&
+               git show -s HEAD
+       )
 '
 
 test_expect_success SYMLINKS 'detection should not be fooled by a symlink' '
-       cd "$HERE" &&
        rm -fr foo.git &&
        git clone -s .git another &&
        ln -s another yetanother &&
-       cd yetanother/.git &&
-       git show -s HEAD
+       (
+               cd yetanother/.git &&
+               git show -s HEAD
+       )
 '
 
 test_done
index ab55eda158bb5a6ecad77302aa2fe17bd6e92be3..bfa2c2190d0368eba533a8411df739eee77fa1be 100755 (executable)
@@ -42,7 +42,7 @@ test_expect_success 'git diff' '
 '
 
 test_expect_success 'tree' '
-       tree=$(git write-tree 2>/dev/null)
+       tree=$(git write-tree 2>/dev/null) &&
        test 8988da15d077d4829fc51d8544c097def6644dbb = $tree
 '
 
index 074f2f2e3e57f76b01f16502998e95b493676711..d0e55465ff08698376f5d9fa86357d51bd57458c 100755 (executable)
@@ -288,6 +288,14 @@ EOF
 test_expect_success 'working --list' \
        'git config --list > output && cmp output expect'
 
+cat > expect << EOF
+EOF
+
+test_expect_success '--list without repo produces empty output' '
+       git --git-dir=nonexistent config --list >output &&
+       test_cmp expect output
+'
+
 cat > expect << EOF
 beta.noindent sillyValue
 nextsection.nonewline wow2 for me
@@ -701,13 +709,13 @@ cat >expect <<\EOF
        trailingtilde = foo~
 EOF
 
-test_expect_success 'set --path' '
+test_expect_success NOT_MINGW 'set --path' '
        git config --path path.home "~/" &&
        git config --path path.normal "/dev/null" &&
        git config --path path.trailingtilde "foo~" &&
        test_cmp expect .git/config'
 
-if test "${HOME+set}"
+if test_have_prereq NOT_MINGW && test "${HOME+set}"
 then
        test_set_prereq HOMEVAR
 fi
@@ -730,7 +738,7 @@ cat >expect <<\EOF
 foo~
 EOF
 
-test_expect_success 'get --path copes with unset $HOME' '
+test_expect_success NOT_MINGW 'get --path copes with unset $HOME' '
        (
                unset HOME;
                test_must_fail git config --get --path path.home \
@@ -836,6 +844,27 @@ test_expect_success SYMLINKS 'symlinked configuration' '
 
 '
 
+test_expect_success 'nonexistent configuration' '
+       (
+               GIT_CONFIG=doesnotexist &&
+               export GIT_CONFIG &&
+               test_must_fail git config --list &&
+               test_must_fail git config test.xyzzy
+       )
+'
+
+test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
+       ln -s doesnotexist linktonada &&
+       ln -s linktonada linktolinktonada &&
+       (
+               GIT_CONFIG=linktonada &&
+               export GIT_CONFIG &&
+               test_must_fail git config --list &&
+               GIT_CONFIG=linktolinktonada &&
+               test_must_fail git config --list
+       )
+'
+
 test_expect_success 'check split_cmdline return' "
        git config alias.split-cmdline-fix 'echo \"' &&
        test_must_fail git split-cmdline-fix &&
index 8d305b43725f8cf60e7ee802df1923feb98eeae5..0e476624069a4ac4259f35b07f36bf0945bfe986 100755 (executable)
@@ -7,41 +7,64 @@ test_description='Test repository version check'
 
 . ./test-lib.sh
 
-cat >test.patch <<EOF
-diff --git a/test.txt b/test.txt
-new file mode 100644
---- /dev/null
-+++ b/test.txt
-@@ -0,0 +1 @@
-+123
-EOF
+test_expect_success 'setup' '
+       cat >test.patch <<-\EOF &&
+       diff --git a/test.txt b/test.txt
+       new file mode 100644
+       --- /dev/null
+       +++ b/test.txt
+       @@ -0,0 +1 @@
+       +123
+       EOF
 
-test_create_repo "test"
-test_create_repo "test2"
-
-GIT_CONFIG=test2/.git/config git config core.repositoryformatversion 99 || exit 1
+       test_create_repo "test" &&
+       test_create_repo "test2" &&
+       GIT_CONFIG=test2/.git/config git config core.repositoryformatversion 99
+'
 
 test_expect_success 'gitdir selection on normal repos' '
-       (test "$(git config core.repositoryformatversion)" = 0 &&
-       cd test &&
-       test "$(git config core.repositoryformatversion)" = 0)'
+       echo 0 >expect &&
+       git config core.repositoryformatversion >actual &&
+       (
+               cd test &&
+               git config core.repositoryformatversion >../actual2
+       ) &&
+       test_cmp expect actual &&
+       test_cmp expect actual2
+'
 
-# Make sure it would stop at test2, not trash
 test_expect_success 'gitdir selection on unsupported repo' '
-       (cd test2 &&
-       test "$(git config core.repositoryformatversion)" = 99)'
+       # Make sure it would stop at test2, not trash
+       echo 99 >expect &&
+       (
+               cd test2 &&
+               git config core.repositoryformatversion >../actual
+       ) &&
+       test_cmp expect actual
+'
 
 test_expect_success 'gitdir not required mode' '
-       (git apply --stat test.patch &&
-       cd test && git apply --stat ../test.patch &&
-       cd ../test2 && git apply --stat ../test.patch)'
-
-test_expect_success 'gitdir required mode on normal repos' '
-       (git apply --check --index test.patch &&
-       cd test && git apply --check --index ../test.patch)'
+       git apply --stat test.patch &&
+       (
+               cd test &&
+               git apply --stat ../test.patch
+       ) &&
+       (
+               cd test2 &&
+               git apply --stat ../test.patch
+       )
+'
 
-test_expect_success 'gitdir required mode on unsupported repo' '
-       (cd test2 && test_must_fail git apply --check --index ../test.patch)
+test_expect_success 'gitdir required mode' '
+       git apply --check --index test.patch &&
+       (
+               cd test &&
+               git apply --check --index ../test.patch
+       ) &&
+       (
+               cd test2 &&
+               test_must_fail git apply --check --index ../test.patch
+       )
 '
 
 test_done
index 97ab02aceb8d9448ca5d4b13b94a8e3d7221375a..b5d89a2250e41afe544b0d0f59f638e02d570cdd 100755 (executable)
@@ -18,9 +18,11 @@ umask 077
 setfacl_out="$(setfacl -m u:root:rwx . 2>&1)"
 setfacl_ret=$?
 
-if [ $setfacl_ret != 0 ]; then
-       skip_all="Skipping ACL tests: unable to use setfacl (output: '$setfacl_out'; return code: '$setfacl_ret')"
-       test_done
+if test $setfacl_ret != 0
+then
+       say "Unable to use setfacl (output: '$setfacl_out'; return code: '$setfacl_ret')"
+else
+       test_set_prereq SETFACL
 fi
 
 check_perms_and_acl () {
@@ -34,7 +36,7 @@ check_perms_and_acl () {
 
 dirs_to_set="./ .git/ .git/objects/ .git/objects/pack/"
 
-test_expect_success 'Setup test repo' '
+test_expect_success SETFACL 'Setup test repo' '
        setfacl -m d:u::rwx,d:g::---,d:o:---,d:m:rwx $dirs_to_set &&
        setfacl -m m:rwx               $dirs_to_set &&
        setfacl -m u:root:rwx          $dirs_to_set &&
@@ -46,12 +48,12 @@ test_expect_success 'Setup test repo' '
        git commit -m "init"
 '
 
-test_expect_success 'Objects creation does not break ACLs with restrictive umask' '
+test_expect_success SETFACL 'Objects creation does not break ACLs with restrictive umask' '
        # SHA1 for empty blob
        check_perms_and_acl .git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
 '
 
-test_expect_success 'git gc does not break ACLs with restrictive umask' '
+test_expect_success SETFACL 'git gc does not break ACLs with restrictive umask' '
        git gc &&
        check_perms_and_acl .git/objects/pack/*.pack
 '
index 54ba3df95f66ecc060adaec6846877c793016aa1..ff747f8229bb46df070ea9aad2702f4d1c703420 100755 (executable)
@@ -52,9 +52,8 @@ rm -f .git/$m
 
 test_expect_success \
        "fail to create $n" \
-       "touch .git/$n_dir
-        git update-ref $n $A >out 2>err"'
-        test $? != 0'
+       "touch .git/$n_dir &&
+        test_must_fail git update-ref $n $A >out 2>err"
 rm -f .git/$n_dir out err
 
 test_expect_success \
@@ -185,55 +184,55 @@ gd="Thu, 26 May 2005 18:33:00 -0500"
 ld="Thu, 26 May 2005 18:43:00 -0500"
 test_expect_success \
        'Query "master@{May 25 2005}" (before history)' \
-       'rm -f o e
+       'rm -f o e &&
         git rev-parse --verify "master@{May 25 2005}" >o 2>e &&
         test '"$C"' = $(cat o) &&
         test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
 test_expect_success \
        "Query master@{2005-05-25} (before history)" \
-       'rm -f o e
+       'rm -f o e &&
         git rev-parse --verify master@{2005-05-25} >o 2>e &&
         test '"$C"' = $(cat o) &&
         echo test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
 test_expect_success \
        'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \
-       'rm -f o e
+       'rm -f o e &&
         git rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
         test '"$C"' = $(cat o) &&
         test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"'
 test_expect_success \
        'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \
-       'rm -f o e
+       'rm -f o e &&
         git rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
         test '"$C"' = $(cat o) &&
         test "" = "$(cat e)"'
 test_expect_success \
        'Query "master@{May 26 2005 23:32:30}" (first non-creation change)' \
-       'rm -f o e
+       'rm -f o e &&
         git rev-parse --verify "master@{May 26 2005 23:32:30}" >o 2>e &&
         test '"$A"' = $(cat o) &&
         test "" = "$(cat e)"'
 test_expect_success \
        'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \
-       'rm -f o e
+       'rm -f o e &&
         git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
         test '"$B"' = $(cat o) &&
         test "warning: Log .git/logs/'"$m has gap after $gd"'." = "$(cat e)"'
 test_expect_success \
        'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
-       'rm -f o e
+       'rm -f o e &&
         git rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
         test '"$Z"' = $(cat o) &&
         test "" = "$(cat e)"'
 test_expect_success \
        'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \
-       'rm -f o e
+       'rm -f o e &&
         git rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
         test '"$E"' = $(cat o) &&
         test "" = "$(cat e)"'
 test_expect_success \
        'Query "master@{2005-05-28}" (past end of history)' \
-       'rm -f o e
+       'rm -f o e &&
         git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
         test '"$D"' = $(cat o) &&
         test "warning: Log .git/logs/'"$m unexpectedly ended on $ld"'." = "$(cat e)"'
@@ -247,7 +246,7 @@ test_expect_success \
      git add F &&
         GIT_AUTHOR_DATE="2005-05-26 23:30" \
         GIT_COMMITTER_DATE="2005-05-26 23:30" git commit -m add -a &&
-        h_TEST=$(git rev-parse --verify HEAD)
+        h_TEST=$(git rev-parse --verify HEAD) &&
         echo The other day this did not work. >M &&
         echo And then Bob told me how to fix it. >>M &&
         echo OTHER >F &&
index 7fa5f5b22a28f108b3063ff9920cffb530d950e6..2c96551ed0f2d3b95d661a0ce4d3b5b5d9ec0dfb 100755 (executable)
@@ -28,7 +28,7 @@ test_expect_success 'symbolic-ref refuses non-ref for HEAD' '
 reset_to_sane
 
 test_expect_success 'symbolic-ref refuses bare sha1' '
-       echo content >file && git add file && git commit -m one
+       echo content >file && git add file && git commit -m one &&
        test_must_fail git symbolic-ref HEAD `git rev-parse HEAD`
 '
 reset_to_sane
index 782e75d00091ebf1fda93ca5dc8e532289a15638..1b0f82fa4c7928fc4605ccf31a3ae45e6ac9f38e 100755 (executable)
@@ -32,7 +32,7 @@ test_expect_success "check-ref-format --branch @{-1}" '
        T=$(git write-tree) &&
        sha1=$(echo A | git commit-tree $T) &&
        git update-ref refs/heads/master $sha1 &&
-       git update-ref refs/remotes/origin/master $sha1
+       git update-ref refs/remotes/origin/master $sha1 &&
        git checkout master &&
        git checkout origin/master &&
        git checkout master &&
@@ -47,7 +47,7 @@ test_expect_success 'check-ref-format --branch from subdir' '
        T=$(git write-tree) &&
        sha1=$(echo A | git commit-tree $T) &&
        git update-ref refs/heads/master $sha1 &&
-       git update-ref refs/remotes/origin/master $sha1
+       git update-ref refs/remotes/origin/master $sha1 &&
        git checkout master &&
        git checkout origin/master &&
        git checkout master &&
index 25046c42081c8412b56c6b739dfd3a67ab700810..252fc828374583cfb4c2346853bb87560efdf01d 100755 (executable)
@@ -186,8 +186,8 @@ test_expect_success 'delete' '
        test_tick &&
        git commit -m tiger C &&
 
-       HEAD_entry_count=$(git reflog | wc -l)
-       master_entry_count=$(git reflog show master | wc -l)
+       HEAD_entry_count=$(git reflog | wc -l) &&
+       master_entry_count=$(git reflog show master | wc -l) &&
 
        test $HEAD_entry_count = 5 &&
        test $master_entry_count = 5 &&
@@ -199,13 +199,13 @@ test_expect_success 'delete' '
        test $HEAD_entry_count = $(git reflog | wc -l) &&
        ! grep ox < output &&
 
-       master_entry_count=$(wc -l < output)
+       master_entry_count=$(wc -l < output) &&
 
        git reflog delete HEAD@{1} &&
        test $(($HEAD_entry_count -1)) = $(git reflog | wc -l) &&
        test $master_entry_count = $(git reflog show master | wc -l) &&
 
-       HEAD_entry_count=$(git reflog | wc -l)
+       HEAD_entry_count=$(git reflog | wc -l) &&
 
        git reflog delete master@{07.04.2005.15:15:00.-0700} &&
        git reflog show master > output &&
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
new file mode 100755 (executable)
index 0000000..7f519e5
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='reflog walk shows repeated commits again'
+. ./test-lib.sh
+
+test_expect_success 'setup commits' '
+       test_tick &&
+       echo content >file && git add file && git commit -m one &&
+       git tag one &&
+       echo content >>file && git add file && git commit -m two &&
+       git tag two
+'
+
+test_expect_success 'setup reflog with alternating commits' '
+       git checkout -b topic &&
+       git reset one &&
+       git reset two &&
+       git reset one &&
+       git reset two
+'
+
+test_expect_success 'reflog shows all entries' '
+       cat >expect <<-\EOF
+               topic@{0} two: updating HEAD
+               topic@{1} one: updating HEAD
+               topic@{2} two: updating HEAD
+               topic@{3} one: updating HEAD
+               topic@{4} branch: Created from HEAD
+       EOF
+       git log -g --format="%gd %gs" topic >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 759cf12e16bcddc3dab9d6b3ad8abd5590245a94..bb01d5ab8f8ebcaf49ef238d48ae89e3845d5f4b 100755 (executable)
@@ -1,21 +1,23 @@
 #!/bin/sh
 
-test_description='git fsck random collection of tests'
+test_description='git fsck random collection of tests
+
+* (HEAD) B
+* (master) A
+'
 
 . ./test-lib.sh
 
 test_expect_success setup '
+       git config gc.auto 0 &&
        git config i18n.commitencoding ISO-8859-1 &&
        test_commit A fileA one &&
        git config --unset i18n.commitencoding &&
        git checkout HEAD^0 &&
        test_commit B fileB two &&
        git tag -d A B &&
-       git reflog expire --expire=now --all
-'
-
-test_expect_success 'HEAD is part of refs' '
-       test 0 = $(git fsck | wc -l)
+       git reflog expire --expire=now --all &&
+       >empty
 '
 
 test_expect_success 'loose objects borrowed from alternate are not missing' '
@@ -25,110 +27,132 @@ test_expect_success 'loose objects borrowed from alternate are not missing' '
                git init &&
                echo ../../../.git/objects >.git/objects/info/alternates &&
                test_commit C fileC one &&
-               git fsck >out &&
-               ! grep "missing blob" out
-       )
+               git fsck >../out 2>&1
+       ) &&
+       {
+               grep -v dangling out >actual ||
+               :
+       } &&
+       test_cmp empty actual
 '
 
-test_expect_success 'valid objects appear valid' '
-       { git fsck 2>out; true; } &&
-       ! grep error out &&
-       ! grep fatal out
+test_expect_success 'HEAD is part of refs, valid objects appear valid' '
+       git fsck >actual 2>&1 &&
+       test_cmp empty actual
 '
 
 # Corruption tests follow.  Make sure to remove all traces of the
 # specific corruption you test afterwards, lest a later test trip over
 # it.
 
+test_expect_success 'setup: helpers for corruption tests' '
+       sha1_file() {
+               echo "$*" | sed "s#..#.git/objects/&/#"
+       } &&
+
+       remove_object() {
+               file=$(sha1_file "$*") &&
+               test -e "$file" &&
+               rm -f "$file"
+       }
+'
+
 test_expect_success 'object with bad sha1' '
        sha=$(echo blob | git hash-object -w --stdin) &&
-       echo $sha &&
        old=$(echo $sha | sed "s+^..+&/+") &&
        new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&
-       sha="$(dirname $new)$(basename $new)"
+       sha="$(dirname $new)$(basename $new)" &&
        mv .git/objects/$old .git/objects/$new &&
+       test_when_finished "remove_object $sha" &&
        git update-index --add --cacheinfo 100644 $sha foo &&
+       test_when_finished "git read-tree -u --reset HEAD" &&
        tree=$(git write-tree) &&
+       test_when_finished "remove_object $tree" &&
        cmt=$(echo bogus | git commit-tree $tree) &&
+       test_when_finished "remove_object $cmt" &&
        git update-ref refs/heads/bogus $cmt &&
-       (git fsck 2>out; true) &&
-       grep "$sha.*corrupt" out &&
-       rm -f .git/objects/$new &&
-       git update-ref -d refs/heads/bogus &&
-       git read-tree -u --reset HEAD
+       test_when_finished "git update-ref -d refs/heads/bogus" &&
+
+       test_might_fail git fsck 2>out &&
+       cat out &&
+       grep "$sha.*corrupt" out
 '
 
 test_expect_success 'branch pointing to non-commit' '
-       git rev-parse HEAD^{tree} > .git/refs/heads/invalid &&
+       git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+       test_when_finished "git update-ref -d refs/heads/invalid" &&
        git fsck 2>out &&
-       grep "not a commit" out &&
-       git update-ref -d refs/heads/invalid
+       cat out &&
+       grep "not a commit" out
 '
 
-new=nothing
 test_expect_success 'email without @ is okay' '
        git cat-file commit HEAD >basis &&
        sed "s/@/AT/" basis >okay &&
        new=$(git hash-object -t commit -w --stdin <okay) &&
-       echo "$new" &&
+       test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
+       test_when_finished "git update-ref -d refs/heads/bogus" &&
        git fsck 2>out &&
        cat out &&
-       ! grep "error in commit $new" out
+       ! grep "commit $new" out
 '
-git update-ref -d refs/heads/bogus
-rm -f ".git/objects/$new"
 
-new=nothing
 test_expect_success 'email with embedded > is not okay' '
        git cat-file commit HEAD >basis &&
        sed "s/@[a-z]/&>/" basis >bad-email &&
        new=$(git hash-object -t commit -w --stdin <bad-email) &&
-       echo "$new" &&
+       test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
+       test_when_finished "git update-ref -d refs/heads/bogus" &&
        git fsck 2>out &&
        cat out &&
        grep "error in commit $new" out
 '
-git update-ref -d refs/heads/bogus
-rm -f ".git/objects/$new"
-
-cat > invalid-tag <<EOF
-object ffffffffffffffffffffffffffffffffffffffff
-type commit
-tag invalid
-tagger T A Gger <tagger@example.com> 1234567890 -0000
-
-This is an invalid tag.
-EOF
 
 test_expect_success 'tag pointing to nonexistent' '
-       tag=$(git hash-object -t tag -w --stdin < invalid-tag) &&
-       echo $tag > .git/refs/tags/invalid &&
+       cat >invalid-tag <<-\EOF &&
+       object ffffffffffffffffffffffffffffffffffffffff
+       type commit
+       tag invalid
+       tagger T A Gger <tagger@example.com> 1234567890 -0000
+
+       This is an invalid tag.
+       EOF
+
+       tag=$(git hash-object -t tag -w --stdin <invalid-tag) &&
+       test_when_finished "remove_object $tag" &&
+       echo $tag >.git/refs/tags/invalid &&
+       test_when_finished "git update-ref -d refs/tags/invalid" &&
        test_must_fail git fsck --tags >out &&
        cat out &&
-       grep "broken link" out &&
-       rm .git/refs/tags/invalid
+       grep "broken link" out
 '
 
-cat > wrong-tag <<EOF
-object $(echo blob | git hash-object -w --stdin)
-type commit
-tag wrong
-tagger T A Gger <tagger@example.com> 1234567890 -0000
-
-This is an invalid tag.
-EOF
-
 test_expect_success 'tag pointing to something else than its type' '
-       tag=$(git hash-object -t tag -w --stdin < wrong-tag) &&
-       echo $tag > .git/refs/tags/wrong &&
+       sha=$(echo blob | git hash-object -w --stdin) &&
+       test_when_finished "remove_object $sha" &&
+       cat >wrong-tag <<-EOF &&
+       object $sha
+       type commit
+       tag wrong
+       tagger T A Gger <tagger@example.com> 1234567890 -0000
+
+       This is an invalid tag.
+       EOF
+
+       tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+       test_when_finished "remove_object $tag" &&
+       echo $tag >.git/refs/tags/wrong &&
+       test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags 2>out &&
        cat out &&
-       grep "error in tag.*broken links" out &&
-       rm .git/refs/tags/wrong
+       grep "error in tag.*broken links" out
 '
 
-
+test_expect_success 'cleaned up' '
+       git fsck >actual 2>&1 &&
+       test_cmp empty actual
+'
 
 test_done
index bd8b60732b06365fa22585eb8e97a3b1e64fdd05..da6252b1179c47d39393c983d88c75d84a507cb7 100755 (executable)
 test_description='test separate work tree'
 . ./test-lib.sh
 
-test_rev_parse() {
-       name=$1
-       shift
-
-       test_expect_success "$name: is-bare-repository" \
-       "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
-       shift
-       [ $# -eq 0 ] && return
-
-       test_expect_success "$name: is-inside-git-dir" \
-       "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
-       shift
-       [ $# -eq 0 ] && return
-
-       test_expect_success "$name: is-inside-work-tree" \
-       "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
-       shift
-       [ $# -eq 0 ] && return
-
-       test_expect_success "$name: prefix" \
-       "test '$1' = \"\$(git rev-parse --show-prefix)\""
-       shift
-       [ $# -eq 0 ] && return
-}
-
-EMPTY_TREE=$(git write-tree)
-mkdir -p work/sub/dir || exit 1
-mkdir -p work2 || exit 1
-mv .git repo.git || exit 1
-
-say "core.worktree = relative path"
-GIT_DIR=repo.git
-GIT_CONFIG="$(pwd)"/$GIT_DIR/config
-export GIT_DIR GIT_CONFIG
-unset GIT_WORK_TREE
-git config core.worktree ../work
-test_rev_parse 'outside'      false false false
-cd work || exit 1
-GIT_DIR=../repo.git
-GIT_CONFIG="$(pwd)"/$GIT_DIR/config
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-GIT_DIR=../../../repo.git
-GIT_CONFIG="$(pwd)"/$GIT_DIR/config
-test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
-
-say "core.worktree = absolute path"
-GIT_DIR=$(pwd)/repo.git
-GIT_CONFIG=$GIT_DIR/config
-git config core.worktree "$(pwd)/work"
-test_rev_parse 'outside'      false false false
-cd work2
-test_rev_parse 'outside2'     false false false
-cd ../work || exit 1
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
-
-say "GIT_WORK_TREE=relative path (override core.worktree)"
-GIT_DIR=$(pwd)/repo.git
-GIT_CONFIG=$GIT_DIR/config
-git config core.worktree non-existent
-GIT_WORK_TREE=work
-export GIT_WORK_TREE
-test_rev_parse 'outside'      false false false
-cd work2
-test_rev_parse 'outside'      false false false
-cd ../work || exit 1
-GIT_WORK_TREE=.
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-GIT_WORK_TREE=../..
-test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
-
-mv work repo.git/work
-mv work2 repo.git/work2
-
-say "GIT_WORK_TREE=absolute path, work tree below git dir"
-GIT_DIR=$(pwd)/repo.git
-GIT_CONFIG=$GIT_DIR/config
-GIT_WORK_TREE=$(pwd)/repo.git/work
-test_rev_parse 'outside'              false false false
-cd repo.git || exit 1
-test_rev_parse 'in repo.git'              false true  false
-cd objects || exit 1
-test_rev_parse 'in repo.git/objects'      false true  false
-cd ../work2 || exit 1
-test_rev_parse 'in repo.git/work2'      false true  false
-cd ../work || exit 1
-test_rev_parse 'in repo.git/work'         false true true ''
-cd sub/dir || exit 1
-test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/
-cd ../../../.. || exit 1
-
-test_expect_success 'repo finds its work tree' '
-       (cd repo.git &&
-        : > work/sub/dir/untracked &&
-        test sub/dir/untracked = "$(git ls-files --others)")
-'
-
-test_expect_success 'repo finds its work tree from work tree, too' '
-       (cd repo.git/work/sub/dir &&
-        : > tracked &&
-        git --git-dir=../../.. add tracked &&
-        cd ../../.. &&
-        test sub/dir/tracked = "$(git ls-files)")
+test_expect_success 'setup' '
+       EMPTY_TREE=$(git write-tree) &&
+       EMPTY_BLOB=$(git hash-object -t blob --stdin </dev/null) &&
+       CHANGED_BLOB=$(echo changed | git hash-object -t blob --stdin) &&
+       ZEROES=0000000000000000000000000000000000000000 &&
+       EMPTY_BLOB7=$(echo $EMPTY_BLOB | sed "s/\(.......\).*/\1/") &&
+       CHANGED_BLOB7=$(echo $CHANGED_BLOB | sed "s/\(.......\).*/\1/") &&
+
+       mkdir -p work/sub/dir &&
+       mkdir -p work2 &&
+       mv .git repo.git
+'
+
+test_expect_success 'setup: helper for testing rev-parse' '
+       test_rev_parse() {
+               echo $1 >expected.bare &&
+               echo $2 >expected.inside-git &&
+               echo $3 >expected.inside-worktree &&
+               if test $# -ge 4
+               then
+                       echo $4 >expected.prefix
+               fi &&
+
+               git rev-parse --is-bare-repository >actual.bare &&
+               git rev-parse --is-inside-git-dir >actual.inside-git &&
+               git rev-parse --is-inside-work-tree >actual.inside-worktree &&
+               if test $# -ge 4
+               then
+                       git rev-parse --show-prefix >actual.prefix
+               fi &&
+
+               test_cmp expected.bare actual.bare &&
+               test_cmp expected.inside-git actual.inside-git &&
+               test_cmp expected.inside-worktree actual.inside-worktree &&
+               if test $# -ge 4
+               then
+                       # rev-parse --show-prefix should output
+                       # a single newline when at the top of the work tree,
+                       # but we test for that separately.
+                       test -z "$4" && ! test -s actual.prefix ||
+                       test_cmp expected.prefix actual.prefix
+               fi
+       }
+'
+
+test_expect_success 'setup: core.worktree = relative path' '
+       unset GIT_WORK_TREE;
+       GIT_DIR=repo.git &&
+       GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+       export GIT_DIR GIT_CONFIG &&
+       git config core.worktree ../work
+'
+
+test_expect_success 'outside' '
+       test_rev_parse false false false
+'
+
+test_expect_success 'inside work tree' '
+       (
+               cd work &&
+               GIT_DIR=../repo.git &&
+               GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+               test_rev_parse false false true ""
+       )
+'
+
+test_expect_failure 'empty prefix is actually written out' '
+       echo >expected &&
+       (
+               cd work &&
+               GIT_DIR=../repo.git &&
+               GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+               git rev-parse --show-prefix >../actual
+       ) &&
+       test_cmp expected actual
+'
+
+test_expect_success 'subdir of work tree' '
+       (
+               cd work/sub/dir &&
+               GIT_DIR=../../../repo.git &&
+               GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+               test_rev_parse false false true sub/dir/
+       )
+'
+
+test_expect_success 'setup: core.worktree = absolute path' '
+       unset GIT_WORK_TREE;
+       GIT_DIR=$(pwd)/repo.git &&
+       GIT_CONFIG=$GIT_DIR/config &&
+       export GIT_DIR GIT_CONFIG &&
+       git config core.worktree "$(pwd)/work"
+'
+
+test_expect_success 'outside' '
+       test_rev_parse false false false &&
+       (
+               cd work2 &&
+               test_rev_parse false false false
+       )
+'
+
+test_expect_success 'inside work tree' '
+       (
+               cd work &&
+               test_rev_parse false false true ""
+       )
+'
+
+test_expect_success 'subdir of work tree' '
+       (
+               cd work/sub/dir &&
+               test_rev_parse false false true sub/dir/
+       )
+'
+
+test_expect_success 'setup: GIT_WORK_TREE=relative (override core.worktree)' '
+       GIT_DIR=$(pwd)/repo.git &&
+       GIT_CONFIG=$GIT_DIR/config &&
+       git config core.worktree non-existent &&
+       GIT_WORK_TREE=work &&
+       export GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'outside' '
+       test_rev_parse false false false &&
+       (
+               cd work2 &&
+               test_rev_parse false false false
+       )
+'
+
+test_expect_success 'inside work tree' '
+       (
+               cd work &&
+               GIT_WORK_TREE=. &&
+               test_rev_parse false false true ""
+       )
+'
+
+test_expect_success 'subdir of work tree' '
+       (
+               cd work/sub/dir &&
+               GIT_WORK_TREE=../.. &&
+               test_rev_parse false false true sub/dir/
+       )
+'
+
+test_expect_success 'setup: GIT_WORK_TREE=absolute, below git dir' '
+       mv work repo.git/work &&
+       mv work2 repo.git/work2 &&
+       GIT_DIR=$(pwd)/repo.git &&
+       GIT_CONFIG=$GIT_DIR/config &&
+       GIT_WORK_TREE=$(pwd)/repo.git/work &&
+       export GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'outside' '
+       echo outside &&
+       test_rev_parse false false false
+'
+
+test_expect_success 'in repo.git' '
+       (
+               cd repo.git &&
+               test_rev_parse false true false
+       ) &&
+       (
+               cd repo.git/objects &&
+               test_rev_parse false true false
+       ) &&
+       (
+               cd repo.git/work2 &&
+               test_rev_parse false true false
+       )
+'
+
+test_expect_success 'inside work tree' '
+       (
+               cd repo.git/work &&
+               test_rev_parse false true true ""
+       )
+'
+
+test_expect_success 'subdir of work tree' '
+       (
+               cd repo.git/work/sub/dir &&
+               test_rev_parse false true true sub/dir/
+       )
+'
+
+test_expect_success 'find work tree from repo' '
+       echo sub/dir/untracked >expected &&
+       cat <<-\EOF >repo.git/work/.gitignore &&
+       expected.*
+       actual.*
+       .gitignore
+       EOF
+       >repo.git/work/sub/dir/untracked &&
+       (
+               cd repo.git &&
+               git ls-files --others --exclude-standard >../actual
+       ) &&
+       test_cmp expected actual
+'
+
+test_expect_success 'find work tree from work tree' '
+       echo sub/dir/tracked >expected &&
+       >repo.git/work/sub/dir/tracked &&
+       (
+               cd repo.git/work/sub/dir &&
+               git --git-dir=../../.. add tracked
+       ) &&
+       (
+               cd repo.git &&
+               git ls-files >../actual
+       ) &&
+       test_cmp expected actual
 '
 
 test_expect_success '_gently() groks relative GIT_DIR & GIT_WORK_TREE' '
-       (cd repo.git/work/sub/dir &&
-       GIT_DIR=../../.. GIT_WORK_TREE=../.. GIT_PAGER= \
+       (
+               cd repo.git/work/sub/dir &&
+               GIT_DIR=../../.. &&
+               GIT_WORK_TREE=../.. &&
+               GIT_PAGER= &&
+               export GIT_DIR GIT_WORK_TREE GIT_PAGER &&
+
                git diff --exit-code tracked &&
-       echo changed > tracked &&
-       ! GIT_DIR=../../.. GIT_WORK_TREE=../.. GIT_PAGER= \
-               git diff --exit-code tracked)
-'
-cat > diff-index-cached.expected <<\EOF
-:000000 100644 0000000000000000000000000000000000000000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 A     sub/dir/tracked
-EOF
-cat > diff-index.expected <<\EOF
-:000000 100644 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 A     sub/dir/tracked
-EOF
-
-
-test_expect_success 'git diff-index' '
-       GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff-index $EMPTY_TREE > result &&
-       test_cmp diff-index.expected result &&
-       GIT_DIR=repo.git git diff-index --cached $EMPTY_TREE > result &&
-       test_cmp diff-index-cached.expected result
-'
-cat >diff-files.expected <<\EOF
-:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M     sub/dir/tracked
-EOF
-
-test_expect_success 'git diff-files' '
-       GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff-files > result &&
-       test_cmp diff-files.expected result
-'
-
-cat >diff-TREE.expected <<\EOF
-diff --git a/sub/dir/tracked b/sub/dir/tracked
-new file mode 100644
-index 0000000..5ea2ed4
---- /dev/null
-+++ b/sub/dir/tracked
-@@ -0,0 +1 @@
-+changed
-EOF
-cat >diff-TREE-cached.expected <<\EOF
-diff --git a/sub/dir/tracked b/sub/dir/tracked
-new file mode 100644
-index 0000000..e69de29
-EOF
-cat >diff-FILES.expected <<\EOF
-diff --git a/sub/dir/tracked b/sub/dir/tracked
-index e69de29..5ea2ed4 100644
---- a/sub/dir/tracked
-+++ b/sub/dir/tracked
-@@ -0,0 +1 @@
-+changed
-EOF
-
-test_expect_success 'git diff' '
-       GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff $EMPTY_TREE > result &&
-       test_cmp diff-TREE.expected result &&
-       GIT_DIR=repo.git git diff --cached $EMPTY_TREE > result &&
-       test_cmp diff-TREE-cached.expected result &&
-       GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff > result &&
-       test_cmp diff-FILES.expected result
+               echo changed >tracked &&
+               test_must_fail git diff --exit-code tracked
+       )
+'
+
+test_expect_success 'diff-index respects work tree under .git dir' '
+       cat >diff-index-cached.expected <<-EOF &&
+       :000000 100644 $ZEROES $EMPTY_BLOB A    sub/dir/tracked
+       EOF
+       cat >diff-index.expected <<-EOF &&
+       :000000 100644 $ZEROES $ZEROES A        sub/dir/tracked
+       EOF
+
+       (
+               GIT_DIR=repo.git &&
+               GIT_WORK_TREE=repo.git/work &&
+               export GIT_DIR GIT_WORK_TREE &&
+               git diff-index $EMPTY_TREE >diff-index.actual &&
+               git diff-index --cached $EMPTY_TREE >diff-index-cached.actual
+       ) &&
+       test_cmp diff-index.expected diff-index.actual &&
+       test_cmp diff-index-cached.expected diff-index-cached.actual
+'
+
+test_expect_success 'diff-files respects work tree under .git dir' '
+       cat >diff-files.expected <<-EOF &&
+       :100644 100644 $EMPTY_BLOB $ZEROES M    sub/dir/tracked
+       EOF
+
+       (
+               GIT_DIR=repo.git &&
+               GIT_WORK_TREE=repo.git/work &&
+               export GIT_DIR GIT_WORK_TREE &&
+               git diff-files >diff-files.actual
+       ) &&
+       test_cmp diff-files.expected diff-files.actual
+'
+
+test_expect_success 'git diff respects work tree under .git dir' '
+       cat >diff-TREE.expected <<-EOF &&
+       diff --git a/sub/dir/tracked b/sub/dir/tracked
+       new file mode 100644
+       index 0000000..$CHANGED_BLOB7
+       --- /dev/null
+       +++ b/sub/dir/tracked
+       @@ -0,0 +1 @@
+       +changed
+       EOF
+       cat >diff-TREE-cached.expected <<-EOF &&
+       diff --git a/sub/dir/tracked b/sub/dir/tracked
+       new file mode 100644
+       index 0000000..$EMPTY_BLOB7
+       EOF
+       cat >diff-FILES.expected <<-EOF &&
+       diff --git a/sub/dir/tracked b/sub/dir/tracked
+       index $EMPTY_BLOB7..$CHANGED_BLOB7 100644
+       --- a/sub/dir/tracked
+       +++ b/sub/dir/tracked
+       @@ -0,0 +1 @@
+       +changed
+       EOF
+
+       (
+               GIT_DIR=repo.git &&
+               GIT_WORK_TREE=repo.git/work &&
+               export GIT_DIR GIT_WORK_TREE &&
+               git diff $EMPTY_TREE >diff-TREE.actual &&
+               git diff --cached $EMPTY_TREE >diff-TREE-cached.actual &&
+               git diff >diff-FILES.actual
+       ) &&
+       test_cmp diff-TREE.expected diff-TREE.actual &&
+       test_cmp diff-TREE-cached.expected diff-TREE-cached.actual &&
+       test_cmp diff-FILES.expected diff-FILES.actual
 '
 
 test_expect_success 'git grep' '
-       (cd repo.git/work/sub &&
-       GIT_DIR=../.. GIT_WORK_TREE=.. git grep -l changed | grep dir/tracked)
+       echo dir/tracked >expected.grep &&
+       (
+               cd repo.git/work/sub &&
+               GIT_DIR=../.. &&
+               GIT_WORK_TREE=.. &&
+               export GIT_DIR GIT_WORK_TREE &&
+               git grep -l changed >../../../actual.grep
+       ) &&
+       test_cmp expected.grep actual.grep
 '
 
 test_expect_success 'git commit' '
@@ -191,16 +328,23 @@ test_expect_success 'git commit' '
 
 test_expect_success 'absolute pathspec should fail gracefully' '
        (
-               cd repo.git || exit 1
-               git config --unset core.worktree
+               cd repo.git &&
+               test_might_fail git config --unset core.worktree &&
                test_must_fail git log HEAD -- /home
        )
 '
 
 test_expect_success 'make_relative_path handles double slashes in GIT_DIR' '
-       : > dummy_file
+       >dummy_file
        echo git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file &&
        git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file
 '
 
+test_expect_success 'relative $GIT_WORK_TREE and git subprocesses' '
+       GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work \
+       test-subprocess --setup-work-tree rev-parse --show-toplevel >actual &&
+       echo "$(pwd)/repo.git/work" >expected &&
+       test_cmp expected actual
+'
+
 test_done
index b3195c47070b35d555f8cfae90497a2d00d10ca7..1efd7f76ddea8dbd788032c5a9076270d1159825 100755 (executable)
@@ -40,7 +40,7 @@ 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 > output < optionspec
+       test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&
        test_cmp expect output
 '
 
index cc6539494737fff8a02460b6cdca3fccad8f770f..813cc1b3e29ec840deb63ad9c3c1dffd2187cdc5 100755 (executable)
@@ -104,4 +104,15 @@ test_expect_success 'use --default' '
        test_must_fail git rev-parse --verify --default bar
 '
 
+test_expect_success 'master@{n} for various n' '
+       N=$(git reflog | wc -l) &&
+       Nm1=$(($N-1)) &&
+       Np1=$(($N+1)) &&
+       git rev-parse --verify master@{0} &&
+       git rev-parse --verify master@{1} &&
+       git rev-parse --verify master@{$Nm1} &&
+       test_must_fail git rev-parse --verify master@{$N} &&
+       test_must_fail git rev-parse --verify master@{$Np1}
+'
+
 test_done
index df5ad8c686a959c9b03355c1ebc325f3c755ed46..cce87a5ab523d3a4232e9e11863aa3a6e8ca0211 100755 (executable)
@@ -9,8 +9,9 @@ test_prefix() {
 }
 
 test_fail() {
-       test_expect_code 128 "$1: prefix" \
-       "git rev-parse --show-prefix"
+       test_expect_success "$1: prefix" '
+               test_expect_code 128 git rev-parse --show-prefix
+       '
 }
 
 TRASH_ROOT="$PWD"
index af721f97192d70457b289d623c70f18569f5134a..9f8adb1f824a5bafe8829e5ab613e2ab83065f4f 100755 (executable)
@@ -31,6 +31,67 @@ test_expect_success 'correct file objects' '
         test $HASH_file = $(git rev-parse :0:file.txt) )
 '
 
+test_expect_success 'correct relative file objects (0)' '
+       git rev-parse :file.txt >expected &&
+       git rev-parse :./file.txt >result &&
+       test_cmp expected result &&
+       git rev-parse :0:./file.txt >result &&
+       test_cmp expected result
+'
+
+test_expect_success 'correct relative file objects (1)' '
+       git rev-parse HEAD:file.txt >expected &&
+       git rev-parse HEAD:./file.txt >result &&
+       test_cmp expected result
+'
+
+test_expect_success 'correct relative file objects (2)' '
+       (
+               cd subdir &&
+               git rev-parse HEAD:../file.txt >result &&
+               test_cmp ../expected result
+       )
+'
+
+test_expect_success 'correct relative file objects (3)' '
+       (
+               cd subdir &&
+               git rev-parse HEAD:../subdir/../file.txt >result &&
+               test_cmp ../expected result
+       )
+'
+
+test_expect_success 'correct relative file objects (4)' '
+       git rev-parse HEAD:subdir/file.txt >expected &&
+       (
+               cd subdir &&
+               git rev-parse HEAD:./file.txt >result &&
+               test_cmp ../expected result
+       )
+'
+
+test_expect_success 'correct relative file objects (5)' '
+       git rev-parse :subdir/file.txt >expected &&
+       (
+               cd subdir &&
+               git rev-parse :./file.txt >result &&
+               test_cmp ../expected result &&
+               git rev-parse :0:./file.txt >result &&
+               test_cmp ../expected result
+       )
+'
+
+test_expect_success 'correct relative file objects (6)' '
+       git rev-parse :file.txt >expected &&
+       (
+               cd subdir &&
+               git rev-parse :../file.txt >result &&
+               test_cmp ../expected result &&
+               git rev-parse :0:../file.txt >result &&
+               test_cmp ../expected result
+       )
+'
+
 test_expect_success 'incorrect revision id' '
        test_must_fail git rev-parse foobar:file.txt 2>error &&
        grep "Invalid object name '"'"'foobar'"'"'." error &&
@@ -66,4 +127,38 @@ test_expect_success 'incorrect file in :path and :N:path' '
        grep "fatal: Path '"'"'disk-only.txt'"'"' exists on disk, but not in the index." error
 '
 
+test_expect_success 'invalid @{n} reference' '
+       test_must_fail git rev-parse master@{99999} >output 2>error &&
+       test -z "$(cat output)" &&
+       grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error  &&
+       test_must_fail git rev-parse --verify master@{99999} >output 2>error &&
+       test -z "$(cat output)" &&
+       grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error
+'
+
+test_expect_success 'relative path not found' '
+       (
+               cd subdir &&
+               test_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&
+               grep subdir/nonexistent.txt error
+       )
+'
+
+test_expect_success 'relative path outside worktree' '
+       test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
+       test -z "$(cat output)" &&
+       grep "outside repository" error
+'
+
+test_expect_success 'relative path when cwd is outside worktree' '
+       test_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&
+       test -z "$(cat output)" &&
+       grep "relative path syntax can.t be used outside working tree." error
+'
+
+test_expect_success 'relative path when startup_info is NULL' '
+       test_must_fail test-match-trees HEAD:./file.txt HEAD:./file.txt 2>error &&
+       grep "BUG: startup_info struct is not initialized." error
+'
+
 test_done
index 8c8dfdaf9f037f370629f419af02aa7a9d75f613..a4555510c37276d36387d4c5c818503bb286dbd1 100755 (executable)
@@ -85,7 +85,7 @@ test_expect_success 'merge my-side@{u} records the correct name' '
        git branch -t new my-side@{u} &&
        git merge -s ours new@{u} &&
        git show -s --pretty=format:%s >actual &&
-       echo "Merge remote branch ${sq}origin/side${sq}" >expect &&
+       echo "Merge remote-tracking branch ${sq}origin/side${sq}" >expect &&
        test_cmp expect actual
 )
 '
index 7f60fd0b2e8904ec231975df3d65e66c9363ff45..335420fd87f87727f323156f461e520374b7da0b 100755 (executable)
@@ -134,8 +134,8 @@ cat >ls.expected <<EOF
 100644 $ONE_SHA1 0     me
 EOF
 
-export GIT_DIR="$TRASH_DIRECTORY/.git"
-export GIT_WORK_TREE=/
+GIT_DIR="$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && export GIT_WORK_TREE
 
 test_vars 'abs gitdir, root' "$GIT_DIR" "/" ""
 test_foobar_root
@@ -154,24 +154,24 @@ say "GIT_DIR relative, GIT_WORK_TREE set"
 
 test_expect_success 'go to /' 'cd /'
 
-export GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git"
-export GIT_WORK_TREE=/
+GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && export GIT_WORK_TREE
 
 test_vars 'rel gitdir, root' "$GIT_DIR" "/" ""
 test_foobar_root
 
 test_expect_success 'go to /foo' 'cd /foo'
 
-export GIT_DIR="../$TRASH_DIRECTORY/.git"
-export GIT_WORK_TREE=/
+GIT_DIR="../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && export GIT_WORK_TREE
 
 test_vars 'rel gitdir, foo' "$TRASH_DIRECTORY/.git" "/" "foo/"
 test_foobar_foo
 
 test_expect_success 'go to /foo/bar' 'cd /foo/bar'
 
-export GIT_DIR="../../$TRASH_DIRECTORY/.git"
-export GIT_WORK_TREE=/
+GIT_DIR="../../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=/ && export GIT_WORK_TREE
 
 test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
 test_foobar_foobar
@@ -180,24 +180,24 @@ say "GIT_DIR relative, GIT_WORK_TREE relative"
 
 test_expect_success 'go to /' 'cd /'
 
-export GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git"
-export GIT_WORK_TREE=.
+GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git" && export GIT_DIR
+GIT_WORK_TREE=. && export GIT_WORK_TREE
 
 test_vars 'rel gitdir, root' "$GIT_DIR" "/" ""
 test_foobar_root
 
 test_expect_success 'go to /' 'cd /foo'
 
-export GIT_DIR="../$TRASH_DIRECTORY/.git"
-export GIT_WORK_TREE=..
+GIT_DIR="../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=.. && export GIT_WORK_TREE
 
 test_vars 'rel gitdir, foo' "$TRASH_DIRECTORY/.git" "/" "foo/"
 test_foobar_foo
 
 test_expect_success 'go to /foo/bar' 'cd /foo/bar'
 
-export GIT_DIR="../../$TRASH_DIRECTORY/.git"
-export GIT_WORK_TREE=../..
+GIT_DIR="../../$TRASH_DIRECTORY/.git" && export GIT_DIR
+GIT_WORK_TREE=../.. && export GIT_WORK_TREE
 
 test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
 test_foobar_foobar
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh
new file mode 100755 (executable)
index 0000000..15101d5
--- /dev/null
@@ -0,0 +1,776 @@
+#!/bin/sh
+
+test_description="Tests of cwd/prefix/worktree/gitdir setup in all cases
+
+A few rules for repo setup:
+
+1. GIT_DIR is relative to user's cwd. --git-dir is equivalent to
+   GIT_DIR.
+
+2. .git file is relative to parent directory. .git file is basically
+   symlink in disguise. The directory where .git file points to will
+   become new git_dir.
+
+3. core.worktree is relative to git_dir.
+
+4. GIT_WORK_TREE is relative to user's cwd. --work-tree is
+   equivalent to GIT_WORK_TREE.
+
+5. GIT_WORK_TREE/core.worktree was originally meant to work only if
+   GIT_DIR is set, but earlier git didn't enforce it, and some scripts
+   depend on the implementation that happened to first discover .git by
+   going up from the users $cwd and then using the specified working tree
+   that may or may not have any relation to where .git was found in.  This
+   historical behaviour must be kept.
+
+6. Effective GIT_WORK_TREE overrides core.worktree and core.bare
+
+7. Effective core.worktree conflicts with core.bare
+
+8. If GIT_DIR is set but neither worktree nor bare setting is given,
+   original cwd becomes worktree.
+
+9. If .git discovery is done inside a repo, the repo becomes a bare
+   repo. .git discovery is performed if GIT_DIR is not set.
+
+10. If no worktree is available, cwd remains unchanged, prefix is
+    NULL.
+
+11. When user's cwd is outside worktree, cwd remains unchanged,
+    prefix is NULL.
+"
+. ./test-lib.sh
+
+here=$(pwd)
+
+test_repo () {
+       (
+               cd "$1" &&
+               if test -n "$2"
+               then
+                       GIT_DIR="$2" &&
+                       export GIT_DIR
+               fi &&
+               if test -n "$3"
+               then
+                       GIT_WORK_TREE="$3" &&
+                       export GIT_WORK_TREE
+               fi &&
+               rm -f trace &&
+               GIT_TRACE="$(pwd)/trace" git symbolic-ref HEAD >/dev/null &&
+               grep '^setup: ' trace >result &&
+               test_cmp expected result
+       )
+}
+
+maybe_config () {
+       file=$1 var=$2 value=$3 &&
+       if test "$value" != unset
+       then
+               git config --file="$file" "$var" "$value"
+       fi
+}
+
+setup_repo () {
+       name=$1 worktreecfg=$2 gitfile=$3 barecfg=$4 &&
+       sane_unset GIT_DIR GIT_WORK_TREE &&
+
+       git init "$name" &&
+       maybe_config "$name/.git/config" core.worktree "$worktreecfg" &&
+       maybe_config "$name/.git/config" core.bare "$barecfg" &&
+       mkdir -p "$name/sub/sub" &&
+
+       if test "${gitfile:+set}"
+       then
+               mv "$name/.git" "$name.git" &&
+               echo "gitdir: ../$name.git" >"$name/.git"
+       fi
+}
+
+maybe_set () {
+       var=$1 value=$2 &&
+       if test "$value" != unset
+       then
+               eval "$var=\$value" &&
+               export $var
+       fi
+}
+
+setup_env () {
+       worktreenv=$1 gitdirenv=$2 &&
+       sane_unset GIT_DIR GIT_WORK_TREE &&
+       maybe_set GIT_DIR "$gitdirenv" &&
+       maybe_set GIT_WORK_TREE "$worktreeenv"
+}
+
+expect () {
+       cat >"$1/expected" <<-EOF
+       setup: git_dir: $2
+       setup: worktree: $3
+       setup: cwd: $4
+       setup: prefix: $5
+       EOF
+}
+
+try_case () {
+       name=$1 worktreeenv=$2 gitdirenv=$3 &&
+       setup_env "$worktreeenv" "$gitdirenv" &&
+       expect "$name" "$4" "$5" "$6" "$7" &&
+       test_repo "$name"
+}
+
+run_wt_tests () {
+       N=$1 gitfile=$2
+
+       absgit="$here/$N/.git"
+       dotgit=.git
+       dotdotgit=../../.git
+
+       if test "$gitfile"
+       then
+               absgit="$here/$N.git"
+               dotgit=$absgit dotdotgit=$absgit
+       fi
+
+       test_expect_success "#$N: explicit GIT_WORK_TREE and GIT_DIR at toplevel" '
+               try_case $N "$here/$N" .git \
+                       "$dotgit" "$here/$N" "$here/$N" "(null)" &&
+               try_case $N . .git \
+                       "$dotgit" "$here/$N" "$here/$N" "(null)" &&
+               try_case $N "$here/$N" "$here/$N/.git" \
+                       "$absgit" "$here/$N" "$here/$N" "(null)" &&
+               try_case $N . "$here/$N/.git" \
+                       "$absgit" "$here/$N" "$here/$N" "(null)"
+       '
+
+       test_expect_success "#$N: explicit GIT_WORK_TREE and GIT_DIR in subdir" '
+               try_case $N/sub/sub "$here/$N" ../../.git \
+                       "$absgit" "$here/$N" "$here/$N" sub/sub/ &&
+               try_case $N/sub/sub ../.. ../../.git \
+                       "$absgit" "$here/$N" "$here/$N" sub/sub/ &&
+               try_case $N/sub/sub "$here/$N" "$here/$N/.git" \
+                       "$absgit" "$here/$N" "$here/$N" sub/sub/ &&
+               try_case $N/sub/sub ../.. "$here/$N/.git" \
+                       "$absgit" "$here/$N" "$here/$N" sub/sub/
+       '
+
+       test_expect_success "#$N: explicit GIT_WORK_TREE from parent of worktree" '
+               try_case $N "$here/$N/wt" .git \
+                       "$dotgit" "$here/$N/wt" "$here/$N" "(null)" &&
+               try_case $N wt .git \
+                       "$dotgit" "$here/$N/wt" "$here/$N" "(null)" &&
+               try_case $N wt "$here/$N/.git" \
+                       "$absgit" "$here/$N/wt" "$here/$N" "(null)" &&
+               try_case $N "$here/$N/wt" "$here/$N/.git" \
+                       "$absgit" "$here/$N/wt" "$here/$N" "(null)"
+       '
+
+       test_expect_success "#$N: explicit GIT_WORK_TREE from nephew of worktree" '
+               try_case $N/sub/sub "$here/$N/wt" ../../.git \
+                       "$dotdotgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" &&
+               try_case $N/sub/sub ../../wt ../../.git \
+                       "$dotdotgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" &&
+               try_case $N/sub/sub ../../wt "$here/$N/.git" \
+                       "$absgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" &&
+               try_case $N/sub/sub "$here/$N/wt" "$here/$N/.git" \
+                       "$absgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)"
+       '
+
+       test_expect_success "#$N: chdir_to_toplevel uses worktree, not git dir" '
+               try_case $N "$here" .git \
+                       "$absgit" "$here" "$here" $N/ &&
+               try_case $N .. .git \
+                       "$absgit" "$here" "$here" $N/ &&
+               try_case $N .. "$here/$N/.git" \
+                       "$absgit" "$here" "$here" $N/ &&
+               try_case $N "$here" "$here/$N/.git" \
+                       "$absgit" "$here" "$here" $N/
+       '
+
+       test_expect_success "#$N: chdir_to_toplevel uses worktree (from subdir)" '
+               try_case $N/sub/sub "$here" ../../.git \
+                       "$absgit" "$here" "$here" $N/sub/sub/ &&
+               try_case $N/sub/sub ../../.. ../../.git \
+                       "$absgit" "$here" "$here" $N/sub/sub/ &&
+               try_case $N/sub/sub ../../../ "$here/$N/.git" \
+                       "$absgit" "$here" "$here" $N/sub/sub/ &&
+               try_case $N/sub/sub "$here" "$here/$N/.git" \
+                       "$absgit" "$here" "$here" $N/sub/sub/
+       '
+}
+
+# try_repo #c GIT_WORK_TREE GIT_DIR core.worktree .gitfile? core.bare \
+#      (git dir) (work tree) (cwd) (prefix) \  <-- at toplevel
+#      (git dir) (work tree) (cwd) (prefix)    <-- from subdir
+try_repo () {
+       name=$1 worktreeenv=$2 gitdirenv=$3 &&
+       setup_repo "$name" "$4" "$5" "$6" &&
+       shift 6 &&
+       try_case "$name" "$worktreeenv" "$gitdirenv" \
+               "$1" "$2" "$3" "$4" &&
+       shift 4 &&
+       case "$gitdirenv" in
+       /* | ?:/* | unset) ;;
+       *)
+               gitdirenv=../$gitdirenv ;;
+       esac &&
+       try_case "$name/sub" "$worktreeenv" "$gitdirenv" \
+               "$1" "$2" "$3" "$4"
+}
+
+# Bit 0 = GIT_WORK_TREE
+# Bit 1 = GIT_DIR
+# Bit 2 = core.worktree
+# Bit 3 = .git is a file
+# Bit 4 = bare repo
+# Case# = encoding of the above 5 bits
+
+test_expect_success '#0: nonbare repo, no explicit configuration' '
+       try_repo 0 unset unset unset "" unset \
+               .git "$here/0" "$here/0" "(null)" \
+               .git "$here/0" "$here/0" sub/ 2>message &&
+       ! test -s message
+'
+
+test_expect_success '#1: GIT_WORK_TREE without explicit GIT_DIR is accepted' '
+       mkdir -p wt &&
+       try_repo 1 "$here" unset unset "" unset \
+               "$here/1/.git" "$here" "$here" 1/ \
+               "$here/1/.git" "$here" "$here" 1/sub/ 2>message &&
+       ! test -s message
+'
+
+test_expect_success '#2: worktree defaults to cwd with explicit GIT_DIR' '
+       try_repo 2 unset "$here/2/.git" unset "" unset \
+               "$here/2/.git" "$here/2" "$here/2" "(null)" \
+               "$here/2/.git" "$here/2/sub" "$here/2/sub" "(null)"
+'
+
+test_expect_success '#2b: relative GIT_DIR' '
+       try_repo 2b unset ".git" unset "" unset \
+               ".git" "$here/2b" "$here/2b" "(null)" \
+               "../.git" "$here/2b/sub" "$here/2b/sub" "(null)"
+'
+
+test_expect_success '#3: setup' '
+       setup_repo 3 unset "" unset &&
+       mkdir -p 3/sub/sub 3/wt/sub
+'
+run_wt_tests 3
+
+test_expect_success '#4: core.worktree without GIT_DIR set is accepted' '
+       setup_repo 4 ../sub "" unset &&
+       mkdir -p 4/sub sub &&
+       try_case 4 unset unset \
+               .git "$here/4/sub" "$here/4" "(null)" \
+               "$here/4/.git" "$here/4/sub" "$here/4/sub" "(null)" 2>message &&
+       ! test -s message
+'
+
+test_expect_success '#5: core.worktree + GIT_WORK_TREE is accepted' '
+       # or: you cannot intimidate away the lack of GIT_DIR setting
+       try_repo 5 "$here" unset "$here/5" "" unset \
+               "$here/5/.git" "$here" "$here" 5/ \
+               "$here/5/.git" "$here" "$here" 5/sub/ 2>message &&
+       try_repo 5a .. unset "$here/5a" "" unset \
+               "$here/5a/.git" "$here" "$here" 5a/ \
+               "$here/5a/.git" "$here/5a" "$here/5a" sub/ &&
+       ! test -s message
+'
+
+test_expect_success '#6: setting GIT_DIR brings core.worktree to life' '
+       setup_repo 6 "$here/6" "" unset &&
+       try_case 6 unset .git \
+               .git "$here/6" "$here/6" "(null)" &&
+       try_case 6 unset "$here/6/.git" \
+               "$here/6/.git" "$here/6" "$here/6" "(null)" &&
+       try_case 6/sub/sub unset ../../.git \
+               "$here/6/.git" "$here/6" "$here/6" sub/sub/ &&
+       try_case 6/sub/sub unset "$here/6/.git" \
+               "$here/6/.git" "$here/6" "$here/6" sub/sub/
+'
+
+test_expect_success '#6b: GIT_DIR set, core.worktree relative' '
+       setup_repo 6b .. "" unset &&
+       try_case 6b unset .git \
+               .git "$here/6b" "$here/6b" "(null)" &&
+       try_case 6b unset "$here/6b/.git" \
+               "$here/6b/.git" "$here/6b" "$here/6b" "(null)" &&
+       try_case 6b/sub/sub unset ../../.git \
+               "$here/6b/.git" "$here/6b" "$here/6b" sub/sub/ &&
+       try_case 6b/sub/sub unset "$here/6b/.git" \
+               "$here/6b/.git" "$here/6b" "$here/6b" sub/sub/
+'
+
+test_expect_success '#6c: GIT_DIR set, core.worktree=../wt (absolute)' '
+       setup_repo 6c "$here/6c/wt" "" unset &&
+       mkdir -p 6c/wt/sub &&
+
+       try_case 6c unset .git \
+               .git "$here/6c/wt" "$here/6c" "(null)" &&
+       try_case 6c unset "$here/6c/.git" \
+               "$here/6c/.git" "$here/6c/wt" "$here/6c" "(null)" &&
+       try_case 6c/sub/sub unset ../../.git \
+               ../../.git "$here/6c/wt" "$here/6c/sub/sub" "(null)" &&
+       try_case 6c/sub/sub unset "$here/6c/.git" \
+               "$here/6c/.git" "$here/6c/wt" "$here/6c/sub/sub" "(null)"
+'
+
+test_expect_success '#6d: GIT_DIR set, core.worktree=../wt (relative)' '
+       setup_repo 6d "$here/6d/wt" "" unset &&
+       mkdir -p 6d/wt/sub &&
+
+       try_case 6d unset .git \
+               .git "$here/6d/wt" "$here/6d" "(null)" &&
+       try_case 6d unset "$here/6d/.git" \
+               "$here/6d/.git" "$here/6d/wt" "$here/6d" "(null)" &&
+       try_case 6d/sub/sub unset ../../.git \
+               ../../.git "$here/6d/wt" "$here/6d/sub/sub" "(null)" &&
+       try_case 6d/sub/sub unset "$here/6d/.git" \
+               "$here/6d/.git" "$here/6d/wt" "$here/6d/sub/sub" "(null)"
+'
+
+test_expect_success '#6e: GIT_DIR set, core.worktree=../.. (absolute)' '
+       setup_repo 6e "$here" "" unset &&
+       try_case 6e unset .git \
+               "$here/6e/.git" "$here" "$here" 6e/ &&
+       try_case 6e unset "$here/6e/.git" \
+               "$here/6e/.git" "$here" "$here" 6e/ &&
+       try_case 6e/sub/sub unset ../../.git \
+               "$here/6e/.git" "$here" "$here" 6e/sub/sub/ &&
+       try_case 6e/sub/sub unset "$here/6e/.git" \
+               "$here/6e/.git" "$here" "$here" 6e/sub/sub/
+'
+
+test_expect_success '#6f: GIT_DIR set, core.worktree=../.. (relative)' '
+       setup_repo 6f ../../ "" unset &&
+       try_case 6f unset .git \
+               "$here/6f/.git" "$here" "$here" 6f/ &&
+       try_case 6f unset "$here/6f/.git" \
+               "$here/6f/.git" "$here" "$here" 6f/ &&
+       try_case 6f/sub/sub unset ../../.git \
+               "$here/6f/.git" "$here" "$here" 6f/sub/sub/ &&
+       try_case 6f/sub/sub unset "$here/6f/.git" \
+               "$here/6f/.git" "$here" "$here" 6f/sub/sub/
+'
+
+# case #7: GIT_WORK_TREE overrides core.worktree.
+test_expect_success '#7: setup' '
+       setup_repo 7 non-existent "" unset &&
+       mkdir -p 7/sub/sub 7/wt/sub
+'
+run_wt_tests 7
+
+test_expect_success '#8: gitfile, easy case' '
+       try_repo 8 unset unset unset gitfile unset \
+               "$here/8.git" "$here/8" "$here/8" "(null)" \
+               "$here/8.git" "$here/8" "$here/8" sub/
+'
+
+test_expect_success '#9: GIT_WORK_TREE accepted with gitfile' '
+       mkdir -p 9/wt &&
+       try_repo 9 wt unset unset gitfile unset \
+               "$here/9.git" "$here/9/wt" "$here/9" "(null)" \
+               "$here/9.git" "$here/9/sub/wt" "$here/9/sub" "(null)" 2>message &&
+       ! test -s message
+'
+
+test_expect_success '#10: GIT_DIR can point to gitfile' '
+       try_repo 10 unset "$here/10/.git" unset gitfile unset \
+               "$here/10.git" "$here/10" "$here/10" "(null)" \
+               "$here/10.git" "$here/10/sub" "$here/10/sub" "(null)"
+'
+
+test_expect_success '#10b: relative GIT_DIR can point to gitfile' '
+       try_repo 10b unset .git unset gitfile unset \
+               "$here/10b.git" "$here/10b" "$here/10b" "(null)" \
+               "$here/10b.git" "$here/10b/sub" "$here/10b/sub" "(null)"
+'
+
+# case #11: GIT_WORK_TREE works, gitfile case.
+test_expect_success '#11: setup' '
+       setup_repo 11 unset gitfile unset &&
+       mkdir -p 11/sub/sub 11/wt/sub
+'
+run_wt_tests 11 gitfile
+
+test_expect_success '#12: core.worktree with gitfile is accepted' '
+       try_repo 12 unset unset "$here/12" gitfile unset \
+               "$here/12.git" "$here/12" "$here/12" "(null)" \
+               "$here/12.git" "$here/12" "$here/12" sub/ 2>message &&
+       ! test -s message
+'
+
+test_expect_success '#13: core.worktree+GIT_WORK_TREE accepted (with gitfile)' '
+       # or: you cannot intimidate away the lack of GIT_DIR setting
+       try_repo 13 non-existent-too unset non-existent gitfile unset \
+               "$here/13.git" "$here/13/non-existent-too" "$here/13" "(null)" \
+               "$here/13.git" "$here/13/sub/non-existent-too" "$here/13/sub" "(null)" 2>message &&
+       ! test -s message
+'
+
+# case #14.
+# If this were more table-driven, it could share code with case #6.
+
+test_expect_success '#14: core.worktree with GIT_DIR pointing to gitfile' '
+       setup_repo 14 "$here/14" gitfile unset &&
+       try_case 14 unset .git \
+               "$here/14.git" "$here/14" "$here/14" "(null)" &&
+       try_case 14 unset "$here/14/.git" \
+               "$here/14.git" "$here/14" "$here/14" "(null)" &&
+       try_case 14/sub/sub unset ../../.git \
+               "$here/14.git" "$here/14" "$here/14" sub/sub/ &&
+       try_case 14/sub/sub unset "$here/14/.git" \
+               "$here/14.git" "$here/14" "$here/14" sub/sub/ &&
+
+       setup_repo 14c "$here/14c/wt" gitfile unset &&
+       mkdir -p 14c/wt/sub &&
+
+       try_case 14c unset .git \
+               "$here/14c.git" "$here/14c/wt" "$here/14c" "(null)" &&
+       try_case 14c unset "$here/14c/.git" \
+               "$here/14c.git" "$here/14c/wt" "$here/14c" "(null)" &&
+       try_case 14c/sub/sub unset ../../.git \
+               "$here/14c.git" "$here/14c/wt" "$here/14c/sub/sub" "(null)" &&
+       try_case 14c/sub/sub unset "$here/14c/.git" \
+               "$here/14c.git" "$here/14c/wt" "$here/14c/sub/sub" "(null)" &&
+
+       setup_repo 14d "$here/14d/wt" gitfile unset &&
+       mkdir -p 14d/wt/sub &&
+
+       try_case 14d unset .git \
+               "$here/14d.git" "$here/14d/wt" "$here/14d" "(null)" &&
+       try_case 14d unset "$here/14d/.git" \
+               "$here/14d.git" "$here/14d/wt" "$here/14d" "(null)" &&
+       try_case 14d/sub/sub unset ../../.git \
+               "$here/14d.git" "$here/14d/wt" "$here/14d/sub/sub" "(null)" &&
+       try_case 14d/sub/sub unset "$here/14d/.git" \
+               "$here/14d.git" "$here/14d/wt" "$here/14d/sub/sub" "(null)" &&
+
+       setup_repo 14e "$here" gitfile unset &&
+       try_case 14e unset .git \
+               "$here/14e.git" "$here" "$here" 14e/ &&
+       try_case 14e unset "$here/14e/.git" \
+               "$here/14e.git" "$here" "$here" 14e/ &&
+       try_case 14e/sub/sub unset ../../.git \
+               "$here/14e.git" "$here" "$here" 14e/sub/sub/ &&
+       try_case 14e/sub/sub unset "$here/14e/.git" \
+               "$here/14e.git" "$here" "$here" 14e/sub/sub/
+'
+
+test_expect_success '#14b: core.worktree is relative to actual git dir' '
+       setup_repo 14b ../14b gitfile unset &&
+       try_case 14b unset .git \
+               "$here/14b.git" "$here/14b" "$here/14b" "(null)" &&
+       try_case 14b unset "$here/14b/.git" \
+               "$here/14b.git" "$here/14b" "$here/14b" "(null)" &&
+       try_case 14b/sub/sub unset ../../.git \
+               "$here/14b.git" "$here/14b" "$here/14b" sub/sub/ &&
+       try_case 14b/sub/sub unset "$here/14b/.git" \
+               "$here/14b.git" "$here/14b" "$here/14b" sub/sub/ &&
+
+       setup_repo 14f ../ gitfile unset &&
+       try_case 14f unset .git \
+               "$here/14f.git" "$here" "$here" 14f/ &&
+       try_case 14f unset "$here/14f/.git" \
+               "$here/14f.git" "$here" "$here" 14f/ &&
+       try_case 14f/sub/sub unset ../../.git \
+               "$here/14f.git" "$here" "$here" 14f/sub/sub/ &&
+       try_case 14f/sub/sub unset "$here/14f/.git" \
+               "$here/14f.git" "$here" "$here" 14f/sub/sub/
+'
+
+# case #15: GIT_WORK_TREE overrides core.worktree (gitfile case).
+test_expect_success '#15: setup' '
+       setup_repo 15 non-existent gitfile unset &&
+       mkdir -p 15/sub/sub 15/wt/sub
+'
+run_wt_tests 15 gitfile
+
+test_expect_success '#16a: implicitly bare repo (cwd inside .git dir)' '
+       setup_repo 16a unset "" unset &&
+       mkdir -p 16a/.git/wt/sub &&
+
+       try_case 16a/.git unset unset \
+               . "(null)" "$here/16a/.git" "(null)" &&
+       try_case 16a/.git/wt unset unset \
+               "$here/16a/.git" "(null)" "$here/16a/.git/wt" "(null)" &&
+       try_case 16a/.git/wt/sub unset unset \
+               "$here/16a/.git" "(null)" "$here/16a/.git/wt/sub" "(null)"
+'
+
+test_expect_success '#16b: bare .git (cwd inside .git dir)' '
+       setup_repo 16b unset "" true &&
+       mkdir -p 16b/.git/wt/sub &&
+
+       try_case 16b/.git unset unset \
+               . "(null)" "$here/16b/.git" "(null)" &&
+       try_case 16b/.git/wt unset unset \
+               "$here/16b/.git" "(null)" "$here/16b/.git/wt" "(null)" &&
+       try_case 16b/.git/wt/sub unset unset \
+               "$here/16b/.git" "(null)" "$here/16b/.git/wt/sub" "(null)"
+'
+
+test_expect_success '#16c: bare .git has no worktree' '
+       try_repo 16c unset unset unset "" true \
+               .git "(null)" "$here/16c" "(null)" \
+               "$here/16c/.git" "(null)" "$here/16c/sub" "(null)"
+'
+
+test_expect_success '#17: GIT_WORK_TREE without explicit GIT_DIR is accepted (bare case)' '
+       # Just like #16.
+       setup_repo 17a unset "" true &&
+       setup_repo 17b unset "" true &&
+       mkdir -p 17a/.git/wt/sub &&
+       mkdir -p 17b/.git/wt/sub &&
+
+       try_case 17a/.git "$here/17a" unset \
+               "$here/17a/.git" "$here/17a" "$here/17a" .git/ \
+               2>message &&
+       try_case 17a/.git/wt "$here/17a" unset \
+               "$here/17a/.git" "$here/17a" "$here/17a" .git/wt/ &&
+       try_case 17a/.git/wt/sub "$here/17a" unset \
+               "$here/17a/.git" "$here/17a" "$here/17a" .git/wt/sub/ &&
+
+       try_case 17b/.git "$here/17b" unset \
+               "$here/17b/.git" "$here/17b" "$here/17b" .git/ &&
+       try_case 17b/.git/wt "$here/17b" unset \
+               "$here/17b/.git" "$here/17b" "$here/17b" .git/wt/ &&
+       try_case 17b/.git/wt/sub "$here/17b" unset \
+               "$here/17b/.git" "$here/17b" "$here/17b" .git/wt/sub/ &&
+
+       try_repo 17c "$here/17c" unset unset "" true \
+               .git "$here/17c" "$here/17c" "(null)" \
+               "$here/17c/.git" "$here/17c" "$here/17c" sub/ 2>message &&
+       ! test -s message
+'
+
+test_expect_success '#18: bare .git named by GIT_DIR has no worktree' '
+       try_repo 18 unset .git unset "" true \
+               .git "(null)" "$here/18" "(null)" \
+               ../.git "(null)" "$here/18/sub" "(null)" &&
+       try_repo 18b unset "$here/18b/.git" unset "" true \
+               "$here/18b/.git" "(null)" "$here/18b" "(null)" \
+               "$here/18b/.git" "(null)" "$here/18b/sub" "(null)"
+'
+
+# Case #19: GIT_DIR + GIT_WORK_TREE suppresses bareness.
+test_expect_success '#19: setup' '
+       setup_repo 19 unset "" true &&
+       mkdir -p 19/sub/sub 19/wt/sub
+'
+run_wt_tests 19
+
+test_expect_success '#20a: core.worktree without GIT_DIR accepted (inside .git)' '
+       # Unlike case #16a.
+       setup_repo 20a "$here/20a" "" unset &&
+       mkdir -p 20a/.git/wt/sub &&
+       try_case 20a/.git unset unset \
+               "$here/20a/.git" "$here/20a" "$here/20a" .git/ 2>message &&
+       try_case 20a/.git/wt unset unset \
+               "$here/20a/.git" "$here/20a" "$here/20a" .git/wt/ &&
+       try_case 20a/.git/wt/sub unset unset \
+               "$here/20a/.git" "$here/20a" "$here/20a" .git/wt/sub/ &&
+       ! test -s message
+'
+
+test_expect_success '#20b/c: core.worktree and core.bare conflict' '
+       setup_repo 20b non-existent "" true &&
+       mkdir -p 20b/.git/wt/sub &&
+       (
+               cd 20b/.git &&
+               test_must_fail git symbolic-ref HEAD >/dev/null
+       ) 2>message &&
+       grep "core.bare and core.worktree" message
+'
+
+# Case #21: core.worktree/GIT_WORK_TREE overrides core.bare' '
+test_expect_success '#21: setup, core.worktree warns before overriding core.bare' '
+       setup_repo 21 non-existent "" unset &&
+       mkdir -p 21/.git/wt/sub &&
+       (
+               cd 21/.git &&
+               GIT_WORK_TREE="$here/21" &&
+               export GIT_WORK_TREE &&
+               git symbolic-ref HEAD >/dev/null
+       ) 2>message &&
+       ! test -s message
+
+'
+run_wt_tests 21
+
+test_expect_success '#22a: core.worktree = GIT_DIR = .git dir' '
+       # like case #6.
+
+       setup_repo 22a "$here/22a/.git" "" unset &&
+       setup_repo 22ab . "" unset
+       mkdir -p 22a/.git/sub 22a/sub &&
+       mkdir -p 22ab/.git/sub 22ab/sub &&
+       try_case 22a/.git unset . \
+               . "$here/22a/.git" "$here/22a/.git" "(null)" &&
+       try_case 22a/.git unset "$here/22a/.git" \
+               "$here/22a/.git" "$here/22a/.git" "$here/22a/.git" "(null)" &&
+       try_case 22a/.git/sub unset .. \
+               "$here/22a/.git" "$here/22a/.git" "$here/22a/.git" sub/ &&
+       try_case 22a/.git/sub unset "$here/22a/.git" \
+               "$here/22a/.git" "$here/22a/.git" "$here/22a/.git" sub/ &&
+
+       try_case 22ab/.git unset . \
+               . "$here/22ab/.git" "$here/22ab/.git" "(null)" &&
+       try_case 22ab/.git unset "$here/22ab/.git" \
+               "$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" "(null)" &&
+       try_case 22ab/.git/sub unset .. \
+               "$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" sub/ &&
+       try_case 22ab/.git unset "$here/22ab/.git" \
+               "$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" "(null)"
+'
+
+test_expect_success '#22b: core.worktree child of .git, GIT_DIR=.git' '
+       setup_repo 22b "$here/22b/.git/wt" "" unset &&
+       setup_repo 22bb wt "" unset &&
+       mkdir -p 22b/.git/sub 22b/sub 22b/.git/wt/sub 22b/wt/sub &&
+       mkdir -p 22bb/.git/sub 22bb/sub 22bb/.git/wt 22bb/wt &&
+
+       try_case 22b/.git unset . \
+               . "$here/22b/.git/wt" "$here/22b/.git" "(null)" &&
+       try_case 22b/.git unset "$here/22b/.git" \
+               "$here/22b/.git" "$here/22b/.git/wt" "$here/22b/.git" "(null)" &&
+       try_case 22b/.git/sub unset .. \
+               .. "$here/22b/.git/wt" "$here/22b/.git/sub" "(null)" &&
+       try_case 22b/.git/sub unset "$here/22b/.git" \
+               "$here/22b/.git" "$here/22b/.git/wt" "$here/22b/.git/sub" "(null)" &&
+
+       try_case 22bb/.git unset . \
+               . "$here/22bb/.git/wt" "$here/22bb/.git" "(null)" &&
+       try_case 22bb/.git unset "$here/22bb/.git" \
+               "$here/22bb/.git" "$here/22bb/.git/wt" "$here/22bb/.git" "(null)" &&
+       try_case 22bb/.git/sub unset .. \
+               .. "$here/22bb/.git/wt" "$here/22bb/.git/sub" "(null)" &&
+       try_case 22bb/.git/sub unset "$here/22bb/.git" \
+               "$here/22bb/.git" "$here/22bb/.git/wt" "$here/22bb/.git/sub" "(null)"
+'
+
+test_expect_success '#22c: core.worktree = .git/.., GIT_DIR=.git' '
+       setup_repo 22c "$here/22c" "" unset &&
+       setup_repo 22cb .. "" unset &&
+       mkdir -p 22c/.git/sub 22c/sub &&
+       mkdir -p 22cb/.git/sub 22cb/sub &&
+
+       try_case 22c/.git unset . \
+               "$here/22c/.git" "$here/22c" "$here/22c" .git/ &&
+       try_case 22c/.git unset "$here/22c/.git" \
+               "$here/22c/.git" "$here/22c" "$here/22c" .git/ &&
+       try_case 22c/.git/sub unset .. \
+               "$here/22c/.git" "$here/22c" "$here/22c" .git/sub/ &&
+       try_case 22c/.git/sub unset "$here/22c/.git" \
+               "$here/22c/.git" "$here/22c" "$here/22c" .git/sub/ &&
+
+       try_case 22cb/.git unset . \
+               "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/ &&
+       try_case 22cb/.git unset "$here/22cb/.git" \
+               "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/ &&
+       try_case 22cb/.git/sub unset .. \
+               "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/sub/ &&
+       try_case 22cb/.git/sub unset "$here/22cb/.git" \
+               "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/sub/
+'
+
+test_expect_success '#22.2: core.worktree and core.bare conflict' '
+       setup_repo 22 "$here/22" "" true &&
+       (
+               cd 22/.git &&
+               GIT_DIR=. &&
+               export GIT_DIR &&
+               test_must_fail git symbolic-ref HEAD 2>result
+       ) &&
+       (
+               cd 22 &&
+               GIT_DIR=.git &&
+               export GIT_DIR &&
+               test_must_fail git symbolic-ref HEAD 2>result
+       ) &&
+       grep "core.bare and core.worktree" 22/.git/result &&
+       grep "core.bare and core.worktree" 22/result
+'
+
+# Case #23: GIT_DIR + GIT_WORK_TREE(+core.worktree) suppresses bareness.
+test_expect_success '#23: setup' '
+       setup_repo 23 non-existent "" true &&
+       mkdir -p 23/sub/sub 23/wt/sub
+'
+run_wt_tests 23
+
+test_expect_success '#24: bare repo has no worktree (gitfile case)' '
+       try_repo 24 unset unset unset gitfile true \
+               "$here/24.git" "(null)" "$here/24" "(null)" \
+               "$here/24.git" "(null)" "$here/24/sub" "(null)"
+'
+
+test_expect_success '#25: GIT_WORK_TREE accepted if GIT_DIR unset (bare gitfile case)' '
+       try_repo 25 "$here/25" unset unset gitfile true \
+               "$here/25.git" "$here/25" "$here/25" "(null)"  \
+               "$here/25.git" "$here/25" "$here/25" "sub/" 2>message &&
+       ! test -s message
+'
+
+test_expect_success '#26: bare repo has no worktree (GIT_DIR -> gitfile case)' '
+       try_repo 26 unset "$here/26/.git" unset gitfile true \
+               "$here/26.git" "(null)" "$here/26" "(null)" \
+               "$here/26.git" "(null)" "$here/26/sub" "(null)" &&
+       try_repo 26b unset .git unset gitfile true \
+               "$here/26b.git" "(null)" "$here/26b" "(null)" \
+               "$here/26b.git" "(null)" "$here/26b/sub" "(null)"
+'
+
+# Case #27: GIT_DIR + GIT_WORK_TREE suppresses bareness (with gitfile).
+test_expect_success '#27: setup' '
+       setup_repo 27 unset gitfile true &&
+       mkdir -p 27/sub/sub 27/wt/sub
+'
+run_wt_tests 27 gitfile
+
+test_expect_success '#28: core.worktree and core.bare conflict (gitfile case)' '
+       setup_repo 28 "$here/28" gitfile true &&
+       (
+               cd 28 &&
+               test_must_fail git symbolic-ref HEAD
+       ) 2>message &&
+       ! grep "^warning:" message &&
+       grep "core.bare and core.worktree" message
+'
+
+# Case #29: GIT_WORK_TREE(+core.worktree) overrides core.bare (gitfile case).
+test_expect_success '#29: setup' '
+       setup_repo 29 non-existent gitfile true &&
+       mkdir -p 29/sub/sub 29/wt/sub
+       (
+               cd 29 &&
+               GIT_WORK_TREE="$here/29" &&
+               export GIT_WORK_TREE &&
+               git symbolic-ref HEAD >/dev/null
+       ) 2>message &&
+       ! test -s message
+'
+run_wt_tests 29 gitfile
+
+test_expect_success '#30: core.worktree and core.bare conflict (gitfile version)' '
+       # Just like case #22.
+       setup_repo 30 "$here/30" gitfile true &&
+       (
+               cd 30 &&
+               GIT_DIR=.git &&
+               export GIT_DIR &&
+               test_must_fail git symbolic-ref HEAD 2>result
+       ) &&
+       grep "core.bare and core.worktree" 30/result
+'
+
+# Case #31: GIT_DIR + GIT_WORK_TREE(+core.worktree) suppresses
+# bareness (gitfile version).
+test_expect_success '#31: setup' '
+       setup_repo 31 non-existent gitfile true &&
+       mkdir -p 31/sub/sub 31/wt/sub
+'
+run_wt_tests 31 gitfile
+
+test_done
diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh
new file mode 100755 (executable)
index 0000000..e043cb7
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='tests for ref^{stuff}'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo blob >a-blob &&
+       git tag -a -m blob blob-tag `git hash-object -w a-blob`
+       mkdir a-tree &&
+       echo moreblobs >a-tree/another-blob &&
+       git add . &&
+       TREE_SHA1=`git write-tree` &&
+       git tag -a -m tree tree-tag "$TREE_SHA1" &&
+       git commit -m Initial &&
+       git tag -a -m commit commit-tag &&
+       git branch ref &&
+       git checkout master &&
+       echo modified >>a-blob &&
+       git add -u &&
+       git commit -m Modified
+'
+
+test_expect_success 'ref^{non-existent}' '
+       test_must_fail git rev-parse ref^{non-existent}
+'
+
+test_expect_success 'ref^{}' '
+       git rev-parse ref >expected &&
+       git rev-parse ref^{} >actual &&
+       test_cmp expected actual &&
+       git rev-parse commit-tag^{} >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'ref^{commit}' '
+       git rev-parse ref >expected &&
+       git rev-parse ref^{commit} >actual &&
+       test_cmp expected actual &&
+       git rev-parse commit-tag^{commit} >actual &&
+       test_cmp expected actual &&
+       test_must_fail git rev-parse tree-tag^{commit} &&
+       test_must_fail git rev-parse blob-tag^{commit}
+'
+
+test_expect_success 'ref^{tree}' '
+       echo $TREE_SHA1 >expected &&
+       git rev-parse ref^{tree} >actual &&
+       test_cmp expected actual &&
+       git rev-parse commit-tag^{tree} >actual &&
+       test_cmp expected actual &&
+       git rev-parse tree-tag^{tree} >actual &&
+       test_cmp expected actual &&
+       test_must_fail git rev-parse blob-tag^{tree}
+'
+
+test_expect_success 'ref^{/.}' '
+       git rev-parse master >expected &&
+       git rev-parse master^{/.} >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'ref^{/non-existent}' '
+       test_must_fail git rev-parse master^{/non-existent}
+'
+
+test_expect_success 'ref^{/Initial}' '
+       git rev-parse ref >expected &&
+       git rev-parse master^{/Initial} >actual &&
+       test_cmp expected actual
+'
+
+test_done
diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh
new file mode 100755 (executable)
index 0000000..b855983
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='basic checkout-index tests
+'
+
+. ./test-lib.sh
+
+test_expect_success 'checkout-index --gobbledegook' '
+       test_expect_code 129 git checkout-index --gobbledegook 2>err &&
+       grep "[Uu]sage" err
+'
+
+test_expect_success 'checkout-index -h in broken repository' '
+       mkdir broken &&
+       (
+               cd broken &&
+               git init &&
+               >.git/index &&
+               test_expect_code 129 git checkout-index -h >usage 2>&1
+       ) &&
+       grep "[Uu]sage" broken/usage
+'
+
+test_done
index 05cc8fdd0168d33d0fe6f2ca54aa4162a0c6cdd0..e6f59f1914667f0001fe990656a66bb76e14a41d 100755 (executable)
@@ -6,13 +6,7 @@ test_description='git checkout to switch between branches with symlink<->dir'
 
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-       skip_all="symbolic links not supported - skipping tests"
-       test_done
-fi
-
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
 
        mkdir frotz &&
        echo hello >frotz/filfre &&
@@ -23,7 +17,7 @@ test_expect_success setup '
        git branch side &&
 
        echo goodbye >nitfol &&
-       git add nitfol
+       git add nitfol &&
        test_tick &&
        git commit -m "master adds file nitfol" &&
 
@@ -38,18 +32,18 @@ test_expect_success setup '
 
 '
 
-test_expect_success 'switch from symlink to dir' '
+test_expect_success SYMLINKS 'switch from symlink to dir' '
 
        git checkout master
 
 '
 
-test_expect_success 'Remove temporary directories & switch to master' '
+test_expect_success SYMLINKS 'Remove temporary directories & switch to master' '
        rm -fr frotz xyzzy nitfol &&
        git checkout -f master
 '
 
-test_expect_success 'switch from dir to symlink' '
+test_expect_success SYMLINKS 'switch from dir to symlink' '
 
        git checkout side
 
index fda3f0af7eb0fcaf7d0d61ff10a22e2dedaa416b..70edbb33e26c0ee6d5cec81d2417c8a830d6fab1 100755 (executable)
@@ -39,4 +39,27 @@ test_expect_success '"checkout <submodule>" updates the index only' '
        git diff-files --quiet
 '
 
+test_expect_success '"checkout <submodule>" honors diff.ignoreSubmodules' '
+       git config diff.ignoreSubmodules dirty &&
+       echo x> submodule/untracked &&
+       git checkout HEAD >actual 2>&1 &&
+       ! test -s actual
+'
+
+test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .gitmodules' '
+       git config diff.ignoreSubmodules none &&
+       git config -f .gitmodules submodule.submodule.path submodule &&
+       git config -f .gitmodules submodule.submodule.ignore untracked &&
+       git checkout HEAD >actual 2>&1 &&
+       ! test -s actual
+'
+
+test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/config' '
+       git config -f .gitmodules submodule.submodule.ignore none &&
+       git config submodule.submodule.path submodule &&
+       git config submodule.submodule.ignore all &&
+       git checkout HEAD >actual 2>&1 &&
+       ! test -s actual
+'
+
 test_done
index 2144184d790055ab12dfbb53ec10f519de598f72..9cd0ac4ba3f14dc85b35fa14e20ceb16f191f724 100755 (executable)
@@ -4,7 +4,7 @@ test_description='git checkout --patch'
 
 . ./lib-patch-mode.sh
 
-test_expect_success 'setup' '
+test_expect_success PERL 'setup' '
        mkdir dir &&
        echo parent > dir/foo &&
        echo dummy > bar &&
@@ -18,55 +18,55 @@ test_expect_success 'setup' '
 
 # note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
 
-test_expect_success 'saying "n" does nothing' '
+test_expect_success PERL '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' '
+test_expect_success PERL '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
+test_expect_success PERL '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' '
+test_expect_success PERL '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' '
+test_expect_success PERL '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
+test_expect_success PERL '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^' '
+test_expect_success PERL '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
 '
 
-test_expect_success 'git checkout -p handles deletion' '
+test_expect_success PERL 'git checkout -p handles deletion' '
        set_state dir/foo work index &&
        rm dir/foo &&
        (echo n; echo y) | git checkout -p &&
@@ -79,28 +79,28 @@ test_expect_success 'git checkout -p handles deletion' '
 # 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' '
+test_expect_success PERL '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' '
+test_expect_success PERL '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' '
+test_expect_success PERL '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' '
+test_expect_success PERL '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) &&
@@ -108,7 +108,7 @@ test_expect_success 'path limiting works: foo inside dir' '
        verify_state dir/foo head head
 '
 
-test_expect_success 'none of this moved HEAD' '
+test_expect_success PERL 'none of this moved HEAD' '
        verify_saved_head
 '
 
index 2d2f63f22ee59d8dc24dafaa5224928ad62dea0c..0e3b8582f2a3edebb0d9eea7fcebaa88f305fd73 100755 (executable)
@@ -14,7 +14,7 @@ TEST_FILE=foo
 test_expect_success 'Setup' '
        echo "Initial" >"$TEST_FILE" &&
        git add "$TEST_FILE" &&
-       git commit -m "First Commit"
+       git commit -m "First Commit" &&
        test_tick &&
        echo "State 1" >>"$TEST_FILE" &&
        git add "$TEST_FILE" &&
diff --git a/t/t2019-checkout-amiguous-ref.sh b/t/t2019-checkout-amiguous-ref.sh
new file mode 100755 (executable)
index 0000000..943541d
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='checkout handling of ambiguous (branch/tag) refs'
+. ./test-lib.sh
+
+test_expect_success 'setup ambiguous refs' '
+       test_commit branch file &&
+       git branch ambiguity &&
+       git branch vagueness &&
+       test_commit tag file &&
+       git tag ambiguity &&
+       git tag vagueness HEAD:file &&
+       test_commit other file
+'
+
+test_expect_success 'checkout ambiguous ref succeeds' '
+       git checkout ambiguity >stdout 2>stderr
+'
+
+test_expect_success 'checkout produces ambiguity warning' '
+       grep "warning.*ambiguous" stderr
+'
+
+test_expect_success 'checkout chooses branch over tag' '
+       echo refs/heads/ambiguity >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
+       echo branch >expect &&
+       test_cmp expect file
+'
+
+test_expect_success 'checkout reports switch to branch' '
+       grep "Switched to branch" stderr &&
+       ! grep "^HEAD is now at" stderr
+'
+
+test_expect_success 'checkout vague ref succeeds' '
+       git checkout vagueness >stdout 2>stderr &&
+       test_set_prereq VAGUENESS_SUCCESS
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout produces ambiguity warning' '
+       grep "warning.*ambiguous" stderr
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' '
+       echo refs/heads/vagueness >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
+       echo branch >expect &&
+       test_cmp expect file
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' '
+       grep "Switched to branch" stderr &&
+       ! grep "^HEAD is now at" stderr
+'
+
+test_done
index b7131d8c08daf20f328dd7f9ce7a1118a734516b..21f4659a9d1c22fcc8c9eb3261315d67988dad2f 100755 (executable)
@@ -26,7 +26,7 @@ chmod +x .git/hooks/post-commit'
 
 test_expect_success 'post-commit hook used ordinarily' '
 echo initial >top &&
-git add top
+git add top &&
 git commit -m initial &&
 test -r "${COMMIT_FILE}"
 '
@@ -45,7 +45,7 @@ test -r "${COMMIT_FILE}"
 rm -rf "${COMMIT_FILE}"
 
 test_expect_success 'post-commit-hook from sub dir' '
-echo changed again >top
+echo changed again >top &&
 cd subdir &&
 git --git-dir .git --work-tree .. add ../top &&
 git --git-dir .git --work-tree .. commit -m subcommit &&
index 648184fd983512be57b46fb5903b42e4de5e4704..c8bce8c2e4314aaf466019438818293102c12c9c 100755 (executable)
@@ -51,7 +51,7 @@ test_expect_success 'update-index again' \
        echo hello world >dir1/file3 &&
        echo goodbye people >file2 &&
        git update-index --add file2 dir1/file3 &&
-       echo hello everybody >file2
+       echo hello everybody >file2 &&
        echo happy >dir1/file3 &&
        git update-index --again &&
        git ls-files -s >current &&
@@ -63,10 +63,10 @@ cat > expected <<\EOF
 EOF
 test_expect_success 'update-index --update from subdir' \
        'echo not so happy >file2 &&
-       cd dir1 &&
+       (cd dir1 &&
        cat ../file2 >file3 &&
-       git update-index --again &&
-       cd .. &&
+       git update-index --again
+       ) &&
        git ls-files -s >current &&
        cmp current expected'
 
index 641607d89ae400f5fbce9dba0f42ee185d3ecd6a..a7f3d47aec2591f9da19ce24b2796005ddf87096 100755 (executable)
@@ -13,7 +13,7 @@ test_expect_success 'submodule with absolute .git file' '
        (cd sub1 &&
         git init &&
         REAL="$(pwd)/.real" &&
-        mv .git "$REAL"
+        mv .git "$REAL" &&
         echo "gitdir: $REAL" >.git &&
         test_commit first)
 '
diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh
new file mode 100755 (executable)
index 0000000..809fafe
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='basic update-index tests
+
+Tests for command-line parsing and basic operation.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'update-index --nonsense fails' '
+       test_must_fail git update-index --nonsense 2>msg &&
+       cat msg &&
+       test -s msg
+'
+
+test_expect_success 'update-index --nonsense dumps usage' '
+       test_expect_code 129 git update-index --nonsense 2>err &&
+       grep "[Uu]sage: git update-index" err
+'
+
+test_expect_success 'update-index -h with corrupt index' '
+       mkdir broken &&
+       (
+               cd broken &&
+               git init &&
+               >.git/index &&
+               test_expect_code 129 git update-index -h >usage 2>&1
+       ) &&
+       grep "[Uu]sage: git update-index" broken/usage
+'
+
+test_done
index 2ad2819a345af53ff6ab0b7c28fa313f1a0a5956..0692427cb69c62327da52061de6238ca8e12169d 100755 (executable)
@@ -25,7 +25,7 @@ test_expect_success setup '
        echo initial >dir1/sub2 &&
        echo initial >dir2/sub3 &&
        git add check dir1 dir2 top foo &&
-       test_tick
+       test_tick &&
        git commit -m initial &&
 
        echo changed >check &&
index 6d2f2b67ee8d03e1f1dc4874da100cb2e179b6d1..c8fe9782672c0e6ba2f0fd0c1b708fc980263182 100755 (executable)
@@ -156,7 +156,7 @@ test_expect_success 'trailing slash in exclude allows directory match (2)' '
 
 test_expect_success 'trailing slash in exclude forces directory match (1)' '
 
-       >two
+       >two &&
        git ls-files --others --exclude=two/ >output &&
        grep "^two" output
 
diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh
new file mode 100755 (executable)
index 0000000..490e052
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='basic ls-files tests
+
+This test runs git ls-files with various unusual or malformed
+command-line arguments.
+'
+
+. ./test-lib.sh
+
+>empty
+
+test_expect_success 'ls-files in empty repository' '
+       git ls-files >actual &&
+       test_cmp empty actual
+'
+
+test_expect_success 'ls-files with nonexistent path' '
+       git ls-files doesnotexist >actual &&
+       test_cmp empty actual
+'
+
+test_expect_success 'ls-files with nonsense option' '
+       test_expect_code 129 git ls-files --nonsense 2>actual &&
+       grep "[Uu]sage: git ls-files" actual
+'
+
+test_expect_success 'ls-files -h in corrupt repository' '
+       mkdir broken &&
+       (
+               cd broken &&
+               git init &&
+               >.git/index &&
+               test_expect_code 129 git ls-files -h >usage 2>&1
+       ) &&
+       grep "[Uu]sage: git ls-files " broken/usage
+'
+
+test_done
index a7d8187169a36f708bd7dc39d97604dd9cc21565..ca01053bcc88806aae9abde5892b8c163b3936d0 100755 (executable)
@@ -26,4 +26,3 @@ test_expect_success \
     'git ls-files --error-unmatch foo bar'
 
 test_done
-1
index d541544537d47587abad8d27565cf9197387cfd6..34794f8a70e1c9b71384363e022d08da4ac8872e 100755 (executable)
@@ -23,6 +23,12 @@ test_expect_success 'setup 1' '
        git branch df-3 &&
        git branch remove &&
        git branch submod &&
+       git branch copy &&
+       git branch rename &&
+       if test_have_prereq SYMLINKS
+       then
+               git branch rename-ln
+       fi &&
 
        echo hello >>a &&
        cp a d/e &&
@@ -248,6 +254,31 @@ test_expect_success 'setup 7' '
        git commit -m "make d/ a submodule"
 '
 
+test_expect_success 'setup 8' '
+       git checkout rename &&
+       git mv a e &&
+       git add e &&
+       test_tick &&
+       git commit -m "rename a->e" &&
+       if test_have_prereq SYMLINKS
+       then
+               git checkout rename-ln &&
+               git mv a e &&
+               ln -s e a &&
+               git add a e &&
+               test_tick &&
+               git commit -m "rename a->e, symlink a->e"
+       fi
+'
+
+test_expect_success 'setup 9' '
+       git checkout copy &&
+       cp a e &&
+       git add e &&
+       test_tick &&
+       git commit -m "copy a->e"
+'
+
 test_expect_success 'merge-recursive simple' '
 
        rm -fr [abcd] &&
@@ -294,7 +325,7 @@ test_expect_success 'fail if the index has unresolved entries' '
        grep "You have not concluded your merge" out &&
        rm -f .git/MERGE_HEAD &&
        test_must_fail git merge "$c5" 2> out &&
-       grep "Your local changes to .* would be overwritten by merge." out
+       grep "Your local changes to the following files would be overwritten by merge:" out
 '
 
 test_expect_success 'merge-recursive remove conflict' '
@@ -526,7 +557,7 @@ test_expect_success 'reset and bind merge' '
                echo "100644 $o0 0      c"
                echo "100644 $o1 0      d/e"
        ) >expected &&
-       test_cmp expected actual
+       test_cmp expected actual &&
 
        git read-tree --prefix=z/ master &&
        git ls-files -s >actual &&
@@ -580,4 +611,43 @@ test_expect_failure 'merge-recursive simple w/submodule result' '
        test_cmp expected actual
 '
 
+test_expect_success 'merge-recursive copy vs. rename' '
+       git checkout -f copy &&
+       git merge rename &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
+       (
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 blob $o0   e"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+               echo "100644 $o0 0      e"
+       ) >expected &&
+       test_cmp expected actual
+'
+
+if test_have_prereq SYMLINKS
+then
+       test_expect_success 'merge-recursive rename vs. rename/symlink' '
+
+               git checkout -f rename &&
+               git merge rename-ln &&
+               ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+               (
+                       echo "100644 blob $o0   b"
+                       echo "100644 blob $o0   c"
+                       echo "100644 blob $o0   d/e"
+                       echo "100644 blob $o0   e"
+                       echo "100644 $o0 0      b"
+                       echo "100644 $o0 0      c"
+                       echo "100644 $o0 0      d/e"
+                       echo "100644 $o0 0      e"
+               ) >expected &&
+               test_cmp expected actual
+       '
+fi
+
+
 test_done
diff --git a/t/t3032-merge-recursive-options.sh b/t/t3032-merge-recursive-options.sh
new file mode 100755 (executable)
index 0000000..2b17311
--- /dev/null
@@ -0,0 +1,203 @@
+#!/bin/sh
+
+test_description='merge-recursive options
+
+* [master] Clarify
+ ! [remote] Remove cruft
+--
+ + [remote] Remove cruft
+*  [master] Clarify
+*+ [remote^] Initial revision
+*   ok 1: setup
+'
+
+. ./test-lib.sh
+
+test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
+test_have_prereq MINGW && export GREP_OPTIONS=-U
+
+test_expect_success 'setup' '
+       conflict_hunks () {
+               sed $SED_OPTIONS -n -e "
+                       /^<<<</ b conflict
+                       b
+                       : conflict
+                       p
+                       /^>>>>/ b
+                       n
+                       b conflict
+               " "$@"
+       } &&
+
+       cat <<-\EOF >text.txt &&
+           Hope, he says, cherishes the soul of him who lives in
+           justice and holiness and is the nurse of his age and the
+           companion of his journey;--hope which is mightiest to sway
+           the restless soul of man.
+
+       How admirable are his words!  And the great blessing of riches, I do
+       not say to every man, but to a good man, is, that he has had no
+       occasion to deceive or to defraud others, either intentionally or
+       unintentionally; and when he departs to the world below he is not in
+       any apprehension about offerings due to the gods or debts which he owes
+       to men.  Now to this peace of mind the possession of wealth greatly
+       contributes; and therefore I say, that, setting one thing against
+       another, of the many advantages which wealth has to give, to a man of
+       sense this is in my opinion the greatest.
+
+       Well said, Cephalus, I replied; but as concerning justice, what is
+       it?--to speak the truth and to pay your debts--no more than this?  And
+       even to this are there not exceptions?  Suppose that a friend when in
+       his right mind has deposited arms with me and he asks for them when he
+       is not in his right mind, ought I to give them back to him?  No one
+       would say that I ought or that I should be right in doing so, any more
+       than they would say that I ought always to speak the truth to one who
+       is in his condition.
+
+       You are quite right, he replied.
+
+       But then, I said, speaking the truth and paying your debts is not a
+       correct definition of justice.
+
+       CEPHALUS - SOCRATES - POLEMARCHUS
+
+       Quite correct, Socrates, if Simonides is to be believed, said
+       Polemarchus interposing.
+
+       I fear, said Cephalus, that I must go now, for I have to look after the
+       sacrifices, and I hand over the argument to Polemarchus and the company.
+       EOF
+       git add text.txt &&
+       test_tick &&
+       git commit -m "Initial revision" &&
+
+       git checkout -b remote &&
+       sed -e "
+                       s/\.  /\. /g
+                       s/[?]  /? /g
+                       s/    / /g
+                       s/--/---/g
+                       s/but as concerning/but as con cerning/
+                       /CEPHALUS - SOCRATES - POLEMARCHUS/ d
+               " text.txt >text.txt+ &&
+       mv text.txt+ text.txt &&
+       git commit -a -m "Remove cruft" &&
+
+       git checkout master &&
+       sed -e "
+                       s/\(not in his right mind\),\(.*\)/\1;\2Q/
+                       s/Quite correct\(.*\)/It is too correct\1Q/
+                       s/unintentionally/un intentionally/
+                       /un intentionally/ s/$/Q/
+                       s/Polemarchus interposing./Polemarchus, interposing.Q/
+                       /justice and holiness/ s/$/Q/
+                       /pay your debts/ s/$/Q/
+               " text.txt | q_to_cr >text.txt+ &&
+       mv text.txt+ text.txt &&
+       git commit -a -m "Clarify" &&
+       git show-branch --all
+'
+
+test_expect_success 'naive merge fails' '
+       git read-tree --reset -u HEAD &&
+       test_must_fail git merge-recursive HEAD^ -- HEAD remote &&
+       test_must_fail git update-index --refresh &&
+       grep "<<<<<<" text.txt
+'
+
+test_expect_success '--ignore-space-change makes merge succeed' '
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-space-change HEAD^ -- HEAD remote
+'
+
+test_expect_success 'naive cherry-pick fails' '
+       git read-tree --reset -u HEAD &&
+       test_must_fail git cherry-pick --no-commit remote &&
+       git read-tree --reset -u HEAD &&
+       test_must_fail git cherry-pick remote &&
+       test_must_fail git update-index --refresh &&
+       grep "<<<<<<" text.txt
+'
+
+test_expect_success '-Xignore-space-change makes cherry-pick succeed' '
+       git read-tree --reset -u HEAD &&
+       git cherry-pick --no-commit -Xignore-space-change remote
+'
+
+test_expect_success '--ignore-space-change: our w/s-only change wins' '
+       q_to_cr <<-\EOF >expected &&
+           justice and holiness and is the nurse of his age and theQ
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+       grep "justice and holiness" text.txt >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-change: their real change wins over w/s' '
+       cat <<-\EOF >expected &&
+       it?---to speak the truth and to pay your debts---no more than this? And
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+       grep "pay your debts" text.txt >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-change: does not ignore new spaces' '
+       cat <<-\EOF >expected1 &&
+       Well said, Cephalus, I replied; but as con cerning justice, what is
+       EOF
+       q_to_cr <<-\EOF >expected2 &&
+       un intentionally; and when he departs to the world below he is not inQ
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+       grep "Well said" text.txt >actual1 &&
+       grep "when he departs" text.txt >actual2 &&
+       test_cmp expected1 actual1 &&
+       test_cmp expected2 actual2
+'
+
+test_expect_success '--ignore-all-space drops their new spaces' '
+       cat <<-\EOF >expected &&
+       Well said, Cephalus, I replied; but as concerning justice, what is
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-all-space HEAD^ -- HEAD remote &&
+       grep "Well said" text.txt >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--ignore-all-space keeps our new spaces' '
+       q_to_cr <<-\EOF >expected &&
+       un intentionally; and when he departs to the world below he is not inQ
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-all-space HEAD^ -- HEAD remote &&
+       grep "when he departs" text.txt >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-at-eol' '
+       q_to_cr <<-\EOF >expected &&
+       <<<<<<< HEAD
+       is not in his right mind; ought I to give them back to him?  No oneQ
+       =======
+       is not in his right mind, ought I to give them back to him? No one
+       >>>>>>> remote
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       test_must_fail git merge-recursive --ignore-space-at-eol \
+                                                HEAD^ -- HEAD remote &&
+       conflict_hunks text.txt >actual &&
+       test_cmp expected actual
+'
+
+test_done
index 4261e9641e00fb3b543384b6a8dbbcc1a214b598..2f5f41a012b5f6e38f2b9d631a99648ac8937c2d 100755 (executable)
@@ -10,10 +10,10 @@ test_expect_success setup '
                cd sub &&
                git init &&
                >subfile &&
-               git add subfile
+               git add subfile &&
                git commit -m "subproject commit #1"
        ) &&
-       >mainfile
+       >mainfile &&
        git add sub mainfile &&
        test_tick &&
        git commit -m "superproject commit #1"
index 3ce501bb9794900b99fbbf2f2ccfbb330f7947a7..61c1f53d1b75302be60396d2e01ccd5da5b6e5b4 100755 (executable)
@@ -53,17 +53,15 @@ test_expect_success setup '
        git add .
 '
 
-# We have to run from a sub-directory to trigger prune_path
-# Then we finally get to run our --with-tree test
-cd sub
-
 test_expect_success 'git -ls-files --with-tree should succeed from subdir' '
-
-       git ls-files --with-tree=HEAD~1 >../output
-
+       # We have to run from a sub-directory to trigger prune_path
+       # Then we finally get to run our --with-tree test
+       (
+               cd sub &&
+               git ls-files --with-tree=HEAD~1 >../output
+       )
 '
 
-cd ..
 test_expect_success \
     'git -ls-files --with-tree should add entries from named tree.' \
     'test_cmp expected output'
index eee0d344d24623ad9928366729c8efaea699fcd7..81d90b66c50e75323a44aaf5e2e067d5a3569e6a 100755 (executable)
@@ -165,4 +165,13 @@ test_expect_success \
 EOF
      test_output'
 
+test_expect_success \
+    'ls-tree with one path a prefix of the other' \
+    'git ls-tree $tree path2/baz path2/bazbo >current &&
+     make_expected <<\EOF &&
+040000 tree X  path2/baz
+120000 blob X  path2/bazbo
+EOF
+     test_output'
+
 test_done
index 06654c6f82fb6cfe955b91bcbd4a5fa064c73e70..026f9f89d916fdd0547c58aed7bcba7f06850023 100755 (executable)
@@ -21,33 +21,32 @@ entries.  Also test odd filename and missing entries handling.
 '
 . ./test-lib.sh
 
-test_expect_success \
-    'setup' \
-    'echo 111 >1.txt &&
-     echo 222 >2.txt &&
-     mkdir path0 path0/a path0/a/b path0/a/b/c &&
-     echo 111 >path0/a/b/c/1.txt &&
-     mkdir path1 path1/b path1/b/c &&
-     echo 111 >path1/b/c/1.txt &&
-     mkdir path2 &&
-     echo 111 >path2/1.txt &&
-     mkdir path3 &&
-     echo 111 >path3/1.txt &&
-     echo 222 >path3/2.txt &&
-     find *.txt path* \( -type f -o -type l \) -print |
-     xargs git update-index --add &&
-     tree=`git write-tree` &&
-     echo $tree'
+test_expect_success 'setup' '
+       echo 111 >1.txt &&
+       echo 222 >2.txt &&
+       mkdir path0 path0/a path0/a/b path0/a/b/c &&
+       echo 111 >path0/a/b/c/1.txt &&
+       mkdir path1 path1/b path1/b/c &&
+       echo 111 >path1/b/c/1.txt &&
+       mkdir path2 &&
+       echo 111 >path2/1.txt &&
+       mkdir path3 &&
+       echo 111 >path3/1.txt &&
+       echo 222 >path3/2.txt &&
+       find *.txt path* \( -type f -o -type l \) -print |
+       xargs git update-index --add &&
+       tree=`git write-tree` &&
+       echo $tree
+'
 
 test_output () {
-    sed -e "s/ $_x40   / X     /" <current >check
-    test_cmp expected check
+       sed -e "s/ $_x40        / X     /" <current >check &&
+       test_cmp expected check
 }
 
-test_expect_success \
-    'ls-tree plain' \
-    'git ls-tree $tree >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree plain' '
+       git ls-tree $tree >current &&
+       cat >expected <<\EOF &&
 100644 blob X  1.txt
 100644 blob X  2.txt
 040000 tree X  path0
@@ -55,13 +54,13 @@ test_expect_success \
 040000 tree X  path2
 040000 tree X  path3
 EOF
-     test_output'
+       test_output
+'
 
 # Recursive does not show tree nodes anymore...
-test_expect_success \
-    'ls-tree recursive' \
-    'git ls-tree -r $tree >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree recursive' '
+       git ls-tree -r $tree >current &&
+       cat >expected <<\EOF &&
 100644 blob X  1.txt
 100644 blob X  2.txt
 100644 blob X  path0/a/b/c/1.txt
@@ -70,68 +69,71 @@ test_expect_success \
 100644 blob X  path3/1.txt
 100644 blob X  path3/2.txt
 EOF
-     test_output'
+       test_output
+'
 
-test_expect_success \
-    'ls-tree filter 1.txt' \
-    'git ls-tree $tree 1.txt >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree filter 1.txt' '
+       git ls-tree $tree 1.txt >current &&
+       cat >expected <<\EOF &&
 100644 blob X  1.txt
 EOF
-     test_output'
+       test_output
+'
 
-test_expect_success \
-    'ls-tree filter path1/b/c/1.txt' \
-    'git ls-tree $tree path1/b/c/1.txt >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree filter path1/b/c/1.txt' '
+       git ls-tree $tree path1/b/c/1.txt >current &&
+       cat >expected <<\EOF &&
 100644 blob X  path1/b/c/1.txt
 EOF
-     test_output'
+       test_output
+'
 
-test_expect_success \
-    'ls-tree filter all 1.txt files' \
-    'git ls-tree $tree 1.txt path0/a/b/c/1.txt path1/b/c/1.txt path2/1.txt path3/1.txt >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree filter all 1.txt files' '
+       git ls-tree $tree 1.txt path0/a/b/c/1.txt \
+               path1/b/c/1.txt path2/1.txt path3/1.txt >current &&
+       cat >expected <<\EOF &&
 100644 blob X  1.txt
 100644 blob X  path0/a/b/c/1.txt
 100644 blob X  path1/b/c/1.txt
 100644 blob X  path2/1.txt
 100644 blob X  path3/1.txt
 EOF
-     test_output'
+       test_output
+'
 
 # I am not so sure about this one after ls-tree doing pathspec match.
 # Having both path0/a and path0/a/b/c makes path0/a redundant, and
 # it behaves as if path0/a/b/c, path1/b/c, path2 and path3 are specified.
-test_expect_success \
-    'ls-tree filter directories' \
-    'git ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree filter directories' '
+       git ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
+       cat >expected <<\EOF &&
 040000 tree X  path0/a/b/c
 040000 tree X  path1/b/c
 040000 tree X  path2
 040000 tree X  path3
 EOF
-     test_output'
+       test_output
+'
 
 # Again, duplicates are filtered away so this is equivalent to
 # having 1.txt and path3
-test_expect_success \
-    'ls-tree filter odd names' \
-    'git ls-tree $tree 1.txt ./1.txt .//1.txt path3/1.txt path3/./1.txt path3 path3// >current &&
-     cat >expected <<\EOF &&
+test_expect_success 'ls-tree filter odd names' '
+       git ls-tree $tree 1.txt ./1.txt .//1.txt \
+               path3/1.txt path3/./1.txt path3 path3// >current &&
+       cat >expected <<\EOF &&
 100644 blob X  1.txt
 100644 blob X  path3/1.txt
 100644 blob X  path3/2.txt
 EOF
-     test_output'
+       test_output
+'
 
-test_expect_success \
-    'ls-tree filter missing files and extra slashes' \
-    'git ls-tree $tree 1.txt/ abc.txt path3//23.txt path3/2.txt/// >current &&
-     cat >expected <<\EOF &&
-EOF
-     test_output'
+test_expect_success 'ls-tree filter missing files and extra slashes' '
+       git ls-tree $tree 1.txt/ abc.txt \
+               path3//23.txt path3/2.txt/// >current &&
+       >expected &&
+       test_output
+'
 
 test_expect_success 'ls-tree filter is leading path match' '
        git ls-tree $tree pa path3/a >current &&
@@ -198,7 +200,7 @@ EOF
 '
 
 test_expect_success 'ls-tree --name-only' '
-       git ls-tree --name-only $tree >current
+       git ls-tree --name-only $tree >current &&
        cat >expected <<\EOF &&
 1.txt
 2.txt
@@ -211,7 +213,7 @@ EOF
 '
 
 test_expect_success 'ls-tree --name-only -r' '
-       git ls-tree --name-only -r $tree >current
+       git ls-tree --name-only -r $tree >current &&
        cat >expected <<\EOF &&
 1.txt
 2.txt
index f54a533456d74a3eb2f745dbc77c8ad5a5ae960f..f308235f5dd28da2c19d91dc63803312aacd2cda 100755 (executable)
@@ -26,6 +26,17 @@ test_expect_success \
      ! test -f .git/refs/heads/--help
 '
 
+test_expect_success 'branch -h in broken repository' '
+       mkdir broken &&
+       (
+               cd broken &&
+               git init &&
+               >.git/refs/heads/master &&
+               test_expect_code 129 git branch -h >usage 2>&1
+       ) &&
+       grep "[Uu]sage" broken/usage
+'
+
 test_expect_success \
     'git branch abc should create a branch' \
     'git branch abc && test -f .git/refs/heads/abc'
index 809d1c4ed49f06d06ba50cda6f52fe9201d2d625..6028748c6cecaedb087c35182de172ca0e93fe08 100755 (executable)
@@ -12,13 +12,13 @@ test_expect_success 'make commits' '
 '
 
 test_expect_success 'make branches' '
-       git branch branch-one
+       git branch branch-one &&
        git branch branch-two HEAD^
 '
 
 test_expect_success 'make remote branches' '
-       git update-ref refs/remotes/origin/branch-one branch-one
-       git update-ref refs/remotes/origin/branch-two branch-two
+       git update-ref refs/remotes/origin/branch-one branch-one &&
+       git update-ref refs/remotes/origin/branch-two branch-two &&
        git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/branch-one
 '
 
index a99e4d8b923cd0d85e5f7e875af4182d2238c9fb..5e29a052599bc28c0edaf56dfedb5f069b5ff01e 100755 (executable)
@@ -24,101 +24,131 @@ EOF
 cat 2>/dev/null >"$p1" "$p0"
 echo 'Foo Bar Baz' >"$p2"
 
-test -f "$p1" && cmp "$p0" "$p1" || {
+if test -f "$p1" && cmp "$p0" "$p1"
+then
+    test_set_prereq TABS_IN_FILENAMES
+else
        # since FAT/NTFS does not allow tabs in filenames, skip this test
-       skip_all='Your filesystem does not allow tabs in filenames, test skipped.'
-       test_done
-}
+       say 'Your filesystem does not allow tabs in filenames'
+fi
 
+test_expect_success TABS_IN_FILENAMES 'setup expect' "
 echo 'just space
 no-funny' >expected
-test_expect_success 'git ls-files no-funny' \
+"
+
+test_expect_success TABS_IN_FILENAMES 'git ls-files no-funny' \
        'git update-index --add "$p0" "$p2" &&
        git ls-files >current &&
        test_cmp expected current'
 
-t0=`git write-tree`
-echo "$t0" >t0
+test_expect_success TABS_IN_FILENAMES 'setup expect' '
+t0=`git write-tree` &&
+echo "$t0" >t0 &&
 
 cat > expected <<\EOF
 just space
 no-funny
 "tabs\t,\" (dq) and spaces"
 EOF
-test_expect_success 'git ls-files with-funny' \
+'
+
+test_expect_success TABS_IN_FILENAMES 'git ls-files with-funny' \
        'git update-index --add "$p1" &&
        git ls-files >current &&
        test_cmp expected current'
 
+test_expect_success TABS_IN_FILENAMES 'setup expect' "
 echo 'just space
 no-funny
-tabs   ," (dq) and spaces' >expected
-test_expect_success 'git ls-files -z with-funny' \
+tabs   ,\" (dq) and spaces' >expected
+"
+
+test_expect_success TABS_IN_FILENAMES 'git ls-files -z with-funny' \
        'git ls-files -z | perl -pe y/\\000/\\012/ >current &&
        test_cmp expected current'
 
-t1=`git write-tree`
-echo "$t1" >t1
+test_expect_success TABS_IN_FILENAMES 'setup expect' '
+t1=`git write-tree` &&
+echo "$t1" >t1 &&
 
 cat > expected <<\EOF
 just space
 no-funny
 "tabs\t,\" (dq) and spaces"
 EOF
-test_expect_success 'git ls-tree with funny' \
+'
+
+test_expect_success TABS_IN_FILENAMES 'git ls-tree with funny' \
        'git ls-tree -r $t1 | sed -e "s/^[^     ]*      //" >current &&
         test_cmp expected current'
 
+test_expect_success TABS_IN_FILENAMES 'setup expect' '
 cat > expected <<\EOF
 A      "tabs\t,\" (dq) and spaces"
 EOF
-test_expect_success 'git diff-index with-funny' \
+'
+
+test_expect_success TABS_IN_FILENAMES 'git diff-index with-funny' \
        'git diff-index --name-status $t0 >current &&
        test_cmp expected current'
 
-test_expect_success 'git diff-tree with-funny' \
+test_expect_success TABS_IN_FILENAMES 'git diff-tree with-funny' \
        'git diff-tree --name-status $t0 $t1 >current &&
        test_cmp expected current'
 
+test_expect_success TABS_IN_FILENAMES 'setup expect' "
 echo 'A
-tabs   ," (dq) and spaces' >expected
-test_expect_success 'git diff-index -z with-funny' \
+tabs   ,\" (dq) and spaces' >expected
+"
+
+test_expect_success TABS_IN_FILENAMES 'git diff-index -z with-funny' \
        'git diff-index -z --name-status $t0 | perl -pe y/\\000/\\012/ >current &&
        test_cmp expected current'
 
-test_expect_success 'git diff-tree -z with-funny' \
+test_expect_success TABS_IN_FILENAMES 'git diff-tree -z with-funny' \
        'git diff-tree -z --name-status $t0 $t1 | perl -pe y/\\000/\\012/ >current &&
        test_cmp expected current'
 
+test_expect_success TABS_IN_FILENAMES 'setup expect' '
 cat > expected <<\EOF
 CNUM   no-funny        "tabs\t,\" (dq) and spaces"
 EOF
-test_expect_success 'git diff-tree -C with-funny' \
+'
+
+test_expect_success TABS_IN_FILENAMES 'git diff-tree -C with-funny' \
        'git diff-tree -C --find-copies-harder --name-status \
                $t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current &&
        test_cmp expected current'
 
+test_expect_success TABS_IN_FILENAMES 'setup expect' '
 cat > expected <<\EOF
 RNUM   no-funny        "tabs\t,\" (dq) and spaces"
 EOF
-test_expect_success 'git diff-tree delete with-funny' \
+'
+
+test_expect_success TABS_IN_FILENAMES 'git diff-tree delete with-funny' \
        'git update-index --force-remove "$p0" &&
        git diff-index -M --name-status \
                $t0 | sed -e 's/^R[0-9]*/RNUM/' >current &&
        test_cmp expected current'
 
+test_expect_success TABS_IN_FILENAMES 'setup expect' '
 cat > expected <<\EOF
 diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
 similarity index NUM%
 rename from no-funny
 rename to "tabs\t,\" (dq) and spaces"
 EOF
-test_expect_success 'git diff-tree delete with-funny' \
+'
+
+test_expect_success TABS_IN_FILENAMES 'git diff-tree delete with-funny' \
        'git diff-index -M -p $t0 |
         sed -e "s/index [0-9]*%/index NUM%/" >current &&
         test_cmp expected current'
 
-chmod +x "$p1"
+test_expect_success TABS_IN_FILENAMES 'setup expect' '
+chmod +x "$p1" &&
 cat > expected <<\EOF
 diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
 old mode 100644
@@ -127,31 +157,39 @@ similarity index NUM%
 rename from no-funny
 rename to "tabs\t,\" (dq) and spaces"
 EOF
-test_expect_success 'git diff-tree delete with-funny' \
+'
+
+test_expect_success TABS_IN_FILENAMES 'git diff-tree delete with-funny' \
        'git diff-index -M -p $t0 |
         sed -e "s/index [0-9]*%/index NUM%/" >current &&
         test_cmp expected current'
 
+test_expect_success TABS_IN_FILENAMES 'setup expect' '
 cat >expected <<\EOF
  "tabs\t,\" (dq) and spaces"
  1 files changed, 0 insertions(+), 0 deletions(-)
 EOF
-test_expect_success 'git diff-tree rename with-funny applied' \
+'
+
+test_expect_success TABS_IN_FILENAMES 'git diff-tree rename with-funny applied' \
        'git diff-index -M -p $t0 |
         git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
         test_cmp expected current'
 
+test_expect_success TABS_IN_FILENAMES 'setup expect' '
 cat > expected <<\EOF
  no-funny
  "tabs\t,\" (dq) and spaces"
  2 files changed, 3 insertions(+), 3 deletions(-)
 EOF
-test_expect_success 'git diff-tree delete with-funny applied' \
+'
+
+test_expect_success TABS_IN_FILENAMES 'git diff-tree delete with-funny applied' \
        'git diff-index -p $t0 |
         git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
         test_cmp expected current'
 
-test_expect_success 'git apply non-git diff' \
+test_expect_success TABS_IN_FILENAMES 'git apply non-git diff' \
        'git diff-index -p $t0 |
         sed -ne "/^[-+@]/p" |
         git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
index 96b75813d73b3fd34a3c96aa9e977594319adb1d..1921ca3a73370d378e6aedd0820fd8d2a030da03 100755 (executable)
@@ -52,7 +52,7 @@ test_expect_success 'refusing to edit notes in refs/remotes/' '
 
 # 1 indicates caught gracefully by die, 128 means git-show barked
 test_expect_success 'handle empty notes gracefully' '
-       git notes show ; test 1 = $?
+       test_expect_code 1 git notes show
 '
 
 test_expect_success 'show non-existent notes entry with %N' '
@@ -365,6 +365,13 @@ c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd7
 c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
 EOF
 
+test_expect_success 'removing non-existing note should not create new commit' '
+       git rev-parse --verify refs/notes/commits > before_commit &&
+       test_must_fail git notes remove HEAD^ &&
+       git rev-parse --verify refs/notes/commits > after_commit &&
+       test_cmp before_commit after_commit
+'
+
 test_expect_success 'list notes with "git notes list"' '
        git notes list > output &&
        test_cmp expect output
@@ -620,16 +627,16 @@ test_expect_success '--show-notes=ref accumulates' '
 
 test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
        git config core.notesRef refs/notes/other &&
-       echo "Note on a tree" > expect
+       echo "Note on a tree" > expect &&
        git notes add -m "Note on a tree" HEAD: &&
        git notes show HEAD: > actual &&
        test_cmp expect actual &&
-       echo "Note on a blob" > expect
+       echo "Note on a blob" > expect &&
        filename=$(git ls-tree --name-only HEAD | head -n1) &&
        git notes add -m "Note on a blob" HEAD:$filename &&
        git notes show HEAD:$filename > actual &&
        test_cmp expect actual &&
-       echo "Note on a tag" > expect
+       echo "Note on a tag" > expect &&
        git tag -a -m "This is an annotated tag" foobar HEAD^ &&
        git notes add -m "Note on a tag" foobar &&
        git notes show foobar > actual &&
@@ -955,6 +962,7 @@ Date:   Thu Apr 7 15:27:13 2005 -0700
 
 Notes (other):
     a fresh note
+$whitespace
     another fresh note
 EOF
 
@@ -976,8 +984,11 @@ Date:   Thu Apr 7 15:27:13 2005 -0700
 
 Notes (other):
     a fresh note
+$whitespace
     another fresh note
+$whitespace
     append 1
+$whitespace
     append 2
 EOF
 
@@ -1054,4 +1065,23 @@ test_expect_success 'git notes copy diagnoses too many or too few parameters' '
        test_must_fail git notes copy one two three
 '
 
+test_expect_success 'git notes get-ref (no overrides)' '
+       git config --unset core.notesRef &&
+       sane_unset GIT_NOTES_REF &&
+       test "$(git notes get-ref)" = "refs/notes/commits"
+'
+
+test_expect_success 'git notes get-ref (core.notesRef)' '
+       git config core.notesRef refs/notes/foo &&
+       test "$(git notes get-ref)" = "refs/notes/foo"
+'
+
+test_expect_success 'git notes get-ref (GIT_NOTES_REF)' '
+       test "$(GIT_NOTES_REF=refs/notes/bar git notes get-ref)" = "refs/notes/bar"
+'
+
+test_expect_success 'git notes get-ref (--ref)' '
+       test "$(GIT_NOTES_REF=refs/notes/bar git notes --ref=baz get-ref)" = "refs/notes/baz"
+'
+
 test_done
index 361a10aeb17108049bf0e47b71153800727edd87..e35d7811acc60415e38e58866b5488938d0372a4 100755 (executable)
@@ -7,11 +7,9 @@ test_description='Test commit notes index (expensive!)'
 
 . ./test-lib.sh
 
-test -z "$GIT_NOTES_TIMING_TESTS" && {
-       skip_all="Skipping timing tests"
-       test_done
-       exit
-}
+test_set_prereq NOT_EXPENSIVE
+test -n "$GIT_NOTES_TIMING_TESTS" && test_set_prereq EXPENSIVE
+test -x /usr/bin/time && test_set_prereq USR_BIN_TIME
 
 create_repo () {
        number_of_commits=$1
@@ -98,21 +96,31 @@ time_notes () {
        for mode in no-notes notes
        do
                echo $mode
-               /usr/bin/time sh ../time_notes $mode $1
+               /usr/bin/time "$SHELL_PATH" ../time_notes $mode $1
        done
 }
 
-for count in 10 100 1000 10000; do
+do_tests () {
+       pr=$1
+       count=$2
+
+       test_expect_success $pr 'setup / mkdir' '
+               mkdir $count &&
+               cd $count
+       '
 
-       mkdir $count
-       (cd $count;
+       test_expect_success $pr "setup $count" "create_repo $count"
 
-       test_expect_success "setup $count" "create_repo $count"
+       test_expect_success $pr 'notes work' "test_notes $count"
 
-       test_expect_success 'notes work' "test_notes $count"
+       test_expect_success USR_BIN_TIME,$pr 'notes timing with /usr/bin/time' "time_notes 100"
+
+       test_expect_success $pr 'teardown / cd ..' 'cd ..'
+}
 
-       test_expect_success 'notes timing' "time_notes 100"
-       )
+do_tests NOT_EXPENSIVE 10
+for count in 100 1000 10000; do
+       do_tests EXPENSIVE $count
 done
 
 test_done
index 75ec18778e1be732593ae130aa257eca3290e36f..704aee81ef5618048bba5209629a0dfde136fdb3 100755 (executable)
@@ -168,15 +168,16 @@ INPUT_END
 }
 
 verify_concatenated_notes () {
-    git log | grep "^    " > output &&
-    i=$number_of_commits &&
-    while [ $i -gt 0 ]; do
-        echo "    commit #$i" &&
-        echo "    first note for commit #$i" &&
-        echo "    second note for commit #$i" &&
-        i=$(($i-1));
-    done > expect &&
-    test_cmp expect output
+       git log | grep "^    " > output &&
+       i=$number_of_commits &&
+       while [ $i -gt 0 ]; do
+               echo "    commit #$i" &&
+               echo "    first note for commit #$i" &&
+               echo "    " &&
+               echo "    second note for commit #$i" &&
+               i=$(($i-1));
+       done > expect &&
+       test_cmp expect output
 }
 
 test_expect_success 'test notes in no fanout concatenated with 2/38-fanout' 'test_concatenated_notes "s|^..|&/|" ""'
index 3269f2eebdbb71acd7c88edee2b6efe809637ade..2ea3be6546f43fd5c224aa8df6add96fa9df6ac2 100755 (executable)
@@ -26,7 +26,7 @@ test_expect_success 'example 1: notes to add an Acked-by line' '
 '
 
 test_expect_success 'example 2: binary notes' '
-       cp "$TEST_DIRECTORY"/test4012.png .
+       cp "$TEST_DIRECTORY"/test4012.png . &&
        git checkout B &&
        blob=$(git hash-object -w test4012.png) &&
        git notes --ref=logo add -C "$blob" &&
diff --git a/t/t3308-notes-merge.sh b/t/t3308-notes-merge.sh
new file mode 100755 (executable)
index 0000000..24d82b4
--- /dev/null
@@ -0,0 +1,368 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test merging of notes trees'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       test_commit 1st &&
+       test_commit 2nd &&
+       test_commit 3rd &&
+       test_commit 4th &&
+       test_commit 5th &&
+       # Create notes on 4 first commits
+       git config core.notesRef refs/notes/x &&
+       git notes add -m "Notes on 1st commit" 1st &&
+       git notes add -m "Notes on 2nd commit" 2nd &&
+       git notes add -m "Notes on 3rd commit" 3rd &&
+       git notes add -m "Notes on 4th commit" 4th
+'
+
+commit_sha1=$(git rev-parse 1st^{commit})
+commit_sha2=$(git rev-parse 2nd^{commit})
+commit_sha3=$(git rev-parse 3rd^{commit})
+commit_sha4=$(git rev-parse 4th^{commit})
+commit_sha5=$(git rev-parse 5th^{commit})
+
+verify_notes () {
+       notes_ref="$1"
+       git -c core.notesRef="refs/notes/$notes_ref" notes |
+               sort >"output_notes_$notes_ref" &&
+       test_cmp "expect_notes_$notes_ref" "output_notes_$notes_ref" &&
+       git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+               >"output_log_$notes_ref" &&
+       test_cmp "expect_log_$notes_ref" "output_log_$notes_ref"
+}
+
+cat <<EOF | sort >expect_notes_x
+5e93d24084d32e1cb61f7070505b9d2530cca987 $commit_sha4
+8366731eeee53787d2bdf8fc1eff7d94757e8da0 $commit_sha3
+eede89064cd42441590d6afec6c37b321ada3389 $commit_sha2
+daa55ffad6cb99bf64226532147ffcaf5ce8bdd1 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+Notes on 4th commit
+
+$commit_sha3 3rd
+Notes on 3rd commit
+
+$commit_sha2 2nd
+Notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+EOF
+
+test_expect_success 'verify initial notes (x)' '
+       verify_notes x
+'
+
+cp expect_notes_x expect_notes_y
+cp expect_log_x expect_log_y
+
+test_expect_success 'fail to merge empty notes ref into empty notes ref (z => y)' '
+       test_must_fail git -c "core.notesRef=refs/notes/y" notes merge z
+'
+
+test_expect_success 'fail to merge into various non-notes refs' '
+       test_must_fail git -c "core.notesRef=refs/notes" notes merge x &&
+       test_must_fail git -c "core.notesRef=refs/notes/" notes merge x &&
+       mkdir -p .git/refs/notes/dir &&
+       test_must_fail git -c "core.notesRef=refs/notes/dir" notes merge x &&
+       test_must_fail git -c "core.notesRef=refs/notes/dir/" notes merge x &&
+       test_must_fail git -c "core.notesRef=refs/heads/master" notes merge x &&
+       test_must_fail git -c "core.notesRef=refs/notes/y:" notes merge x &&
+       test_must_fail git -c "core.notesRef=refs/notes/y:foo" notes merge x &&
+       test_must_fail git -c "core.notesRef=refs/notes/foo^{bar" notes merge x
+'
+
+test_expect_success 'fail to merge various non-note-trees' '
+       git config core.notesRef refs/notes/y &&
+       test_must_fail git notes merge refs/notes &&
+       test_must_fail git notes merge refs/notes/ &&
+       test_must_fail git notes merge refs/notes/dir &&
+       test_must_fail git notes merge refs/notes/dir/ &&
+       test_must_fail git notes merge refs/heads/master &&
+       test_must_fail git notes merge x: &&
+       test_must_fail git notes merge x:foo &&
+       test_must_fail git notes merge foo^{bar
+'
+
+test_expect_success 'merge notes into empty notes ref (x => y)' '
+       git config core.notesRef refs/notes/y &&
+       git notes merge x &&
+       verify_notes y &&
+       # x and y should point to the same notes commit
+       test "$(git rev-parse refs/notes/x)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'merge empty notes ref (z => y)' '
+       git notes merge z &&
+       # y should not change (still == x)
+       test "$(git rev-parse refs/notes/x)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'change notes on other notes ref (y)' '
+       # Not touching notes to 1st commit
+       git notes remove 2nd &&
+       git notes append -m "More notes on 3rd commit" 3rd &&
+       git notes add -f -m "New notes on 4th commit" 4th &&
+       git notes add -m "Notes on 5th commit" 5th
+'
+
+test_expect_success 'merge previous notes commit (y^ => y) => No-op' '
+       pre_state="$(git rev-parse refs/notes/y)" &&
+       git notes merge y^ &&
+       # y should not move
+       test "$pre_state" = "$(git rev-parse refs/notes/y)"
+'
+
+cat <<EOF | sort >expect_notes_y
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+dec2502dac3ea161543f71930044deff93fa945c $commit_sha4
+4069cdb399fd45463ec6eef8e051a16a03592d91 $commit_sha3
+daa55ffad6cb99bf64226532147ffcaf5ce8bdd1 $commit_sha1
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+$commit_sha3 3rd
+Notes on 3rd commit
+
+More notes on 3rd commit
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+Notes on 1st commit
+
+EOF
+
+test_expect_success 'verify changed notes on other notes ref (y)' '
+       verify_notes y
+'
+
+test_expect_success 'verify unchanged notes on original notes ref (x)' '
+       verify_notes x
+'
+
+test_expect_success 'merge original notes (x) into changed notes (y) => No-op' '
+       git notes merge -vvv x &&
+       verify_notes y &&
+       verify_notes x
+'
+
+cp expect_notes_y expect_notes_x
+cp expect_log_y expect_log_x
+
+test_expect_success 'merge changed (y) into original (x) => Fast-forward' '
+       git config core.notesRef refs/notes/x &&
+       git notes merge y &&
+       verify_notes x &&
+       verify_notes y &&
+       # x and y should point to same the notes commit
+       test "$(git rev-parse refs/notes/x)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'merge empty notes ref (z => y)' '
+       # Prepare empty (but valid) notes ref (z)
+       git config core.notesRef refs/notes/z &&
+       git notes add -m "foo" &&
+       git notes remove &&
+       git notes >output_notes_z &&
+       test_cmp /dev/null output_notes_z &&
+       # Do the merge (z => y)
+       git config core.notesRef refs/notes/y &&
+       git notes merge z &&
+       verify_notes y &&
+       # y should no longer point to the same notes commit as x
+       test "$(git rev-parse refs/notes/x)" != "$(git rev-parse refs/notes/y)"
+'
+
+cat <<EOF | sort >expect_notes_y
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+dec2502dac3ea161543f71930044deff93fa945c $commit_sha4
+4069cdb399fd45463ec6eef8e051a16a03592d91 $commit_sha3
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+43add6bd0c8c0bc871ac7991e0f5573cfba27804 $commit_sha1
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+$commit_sha3 3rd
+Notes on 3rd commit
+
+More notes on 3rd commit
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+More notes on 1st commit
+
+EOF
+
+test_expect_success 'change notes on other notes ref (y)' '
+       # Append to 1st commit notes
+       git notes append -m "More notes on 1st commit" 1st &&
+       # Add new notes to 2nd commit
+       git notes add -m "New notes on 2nd commit" 2nd &&
+       verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_x
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+1f257a3a90328557c452f0817d6cc50c89d315d4 $commit_sha4
+daa55ffad6cb99bf64226532147ffcaf5ce8bdd1 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+More notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+Notes on 1st commit
+
+EOF
+
+test_expect_success 'change notes on notes ref (x)' '
+       git config core.notesRef refs/notes/x &&
+       git notes remove 3rd &&
+       git notes append -m "More notes on 4th commit" 4th &&
+       verify_notes x
+'
+
+cat <<EOF | sort >expect_notes_x
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+1f257a3a90328557c452f0817d6cc50c89d315d4 $commit_sha4
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+43add6bd0c8c0bc871ac7991e0f5573cfba27804 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+More notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+More notes on 1st commit
+
+EOF
+
+test_expect_success 'merge y into x => Non-conflicting 3-way merge' '
+       git notes merge y &&
+       verify_notes x &&
+       verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_w
+05a4927951bcef347f51486575b878b2b60137f2 $commit_sha3
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+EOF
+
+cat >expect_log_w <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+New notes on 3rd commit
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'create notes on new, separate notes ref (w)' '
+       git config core.notesRef refs/notes/w &&
+       # Add same note as refs/notes/y on 2nd commit
+       git notes add -m "New notes on 2nd commit" 2nd &&
+       # Add new note on 3rd commit (non-conflicting)
+       git notes add -m "New notes on 3rd commit" 3rd &&
+       # Verify state of notes on new, separate notes ref (w)
+       verify_notes w
+'
+
+cat <<EOF | sort >expect_notes_x
+0f2efbd00262f2fd41dfae33df8765618eeacd99 $commit_sha5
+1f257a3a90328557c452f0817d6cc50c89d315d4 $commit_sha4
+05a4927951bcef347f51486575b878b2b60137f2 $commit_sha3
+d000d30e6ddcfce3a8122c403226a2ce2fd04d9d $commit_sha2
+43add6bd0c8c0bc871ac7991e0f5573cfba27804 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+Notes on 5th commit
+
+$commit_sha4 4th
+New notes on 4th commit
+
+More notes on 4th commit
+
+$commit_sha3 3rd
+New notes on 3rd commit
+
+$commit_sha2 2nd
+New notes on 2nd commit
+
+$commit_sha1 1st
+Notes on 1st commit
+
+More notes on 1st commit
+
+EOF
+
+test_expect_success 'merge w into x => Non-conflicting history-less merge' '
+       git config core.notesRef refs/notes/x &&
+       git notes merge w &&
+       # Verify new state of notes on other notes ref (x)
+       verify_notes x &&
+       # Also verify that nothing changed on other notes refs (y and w)
+       verify_notes y &&
+       verify_notes w
+'
+
+test_done
diff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh
new file mode 100755 (executable)
index 0000000..461fd84
--- /dev/null
@@ -0,0 +1,647 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test notes merging with auto-resolving strategies'
+
+. ./test-lib.sh
+
+# Set up a notes merge scenario with all kinds of potential conflicts
+test_expect_success 'setup commits' '
+       test_commit 1st &&
+       test_commit 2nd &&
+       test_commit 3rd &&
+       test_commit 4th &&
+       test_commit 5th &&
+       test_commit 6th &&
+       test_commit 7th &&
+       test_commit 8th &&
+       test_commit 9th &&
+       test_commit 10th &&
+       test_commit 11th &&
+       test_commit 12th &&
+       test_commit 13th &&
+       test_commit 14th &&
+       test_commit 15th
+'
+
+commit_sha1=$(git rev-parse 1st^{commit})
+commit_sha2=$(git rev-parse 2nd^{commit})
+commit_sha3=$(git rev-parse 3rd^{commit})
+commit_sha4=$(git rev-parse 4th^{commit})
+commit_sha5=$(git rev-parse 5th^{commit})
+commit_sha6=$(git rev-parse 6th^{commit})
+commit_sha7=$(git rev-parse 7th^{commit})
+commit_sha8=$(git rev-parse 8th^{commit})
+commit_sha9=$(git rev-parse 9th^{commit})
+commit_sha10=$(git rev-parse 10th^{commit})
+commit_sha11=$(git rev-parse 11th^{commit})
+commit_sha12=$(git rev-parse 12th^{commit})
+commit_sha13=$(git rev-parse 13th^{commit})
+commit_sha14=$(git rev-parse 14th^{commit})
+commit_sha15=$(git rev-parse 15th^{commit})
+
+verify_notes () {
+       notes_ref="$1"
+       suffix="$2"
+       git -c core.notesRef="refs/notes/$notes_ref" notes |
+               sort >"output_notes_$suffix" &&
+       test_cmp "expect_notes_$suffix" "output_notes_$suffix" &&
+       git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+               >"output_log_$suffix" &&
+       test_cmp "expect_log_$suffix" "output_log_$suffix"
+}
+
+test_expect_success 'setup merge base (x)' '
+       git config core.notesRef refs/notes/x &&
+       git notes add -m "x notes on 6th commit" 6th &&
+       git notes add -m "x notes on 7th commit" 7th &&
+       git notes add -m "x notes on 8th commit" 8th &&
+       git notes add -m "x notes on 9th commit" 9th &&
+       git notes add -m "x notes on 10th commit" 10th &&
+       git notes add -m "x notes on 11th commit" 11th &&
+       git notes add -m "x notes on 12th commit" 12th &&
+       git notes add -m "x notes on 13th commit" 13th &&
+       git notes add -m "x notes on 14th commit" 14th &&
+       git notes add -m "x notes on 15th commit" 15th
+'
+
+cat <<EOF | sort >expect_notes_x
+457a85d6c814ea208550f15fcc48f804ac8dc023 $commit_sha15
+b0c95b954301d69da2bc3723f4cb1680d355937c $commit_sha14
+5d30216a129eeffa97d9694ffe8c74317a560315 $commit_sha13
+dd161bc149470fd890dd4ab52a4cbd79bbd18c36 $commit_sha12
+7abbc45126d680336fb24294f013a7cdfa3ed545 $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+20c613c835011c48a5abe29170a2402ca6354910 $commit_sha9
+a3daf8a1e4e5dc3409a303ad8481d57bfea7f5d6 $commit_sha8
+897003322b53bc6ca098e9324ee508362347e734 $commit_sha7
+11d97fdebfa5ceee540a3da07bce6fa0222bc082 $commit_sha6
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha15 15th
+x notes on 15th commit
+
+$commit_sha14 14th
+x notes on 14th commit
+
+$commit_sha13 13th
+x notes on 13th commit
+
+$commit_sha12 12th
+x notes on 12th commit
+
+$commit_sha11 11th
+x notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+x notes on 9th commit
+
+$commit_sha8 8th
+x notes on 8th commit
+
+$commit_sha7 7th
+x notes on 7th commit
+
+$commit_sha6 6th
+x notes on 6th commit
+
+$commit_sha5 5th
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'verify state of merge base (x)' 'verify_notes x x'
+
+test_expect_success 'setup local branch (y)' '
+       git update-ref refs/notes/y refs/notes/x &&
+       git config core.notesRef refs/notes/y &&
+       git notes add -f -m "y notes on 3rd commit" 3rd &&
+       git notes add -f -m "y notes on 4th commit" 4th &&
+       git notes add -f -m "y notes on 5th commit" 5th &&
+       git notes remove 6th &&
+       git notes remove 7th &&
+       git notes remove 8th &&
+       git notes add -f -m "y notes on 12th commit" 12th &&
+       git notes add -f -m "y notes on 13th commit" 13th &&
+       git notes add -f -m "y notes on 14th commit" 14th &&
+       git notes add -f -m "y notes on 15th commit" 15th
+'
+
+cat <<EOF | sort >expect_notes_y
+68b8630d25516028bed862719855b3d6768d7833 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7abbc45126d680336fb24294f013a7cdfa3ed545 $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+20c613c835011c48a5abe29170a2402ca6354910 $commit_sha9
+154508c7a0bcad82b6fe4b472bc4c26b3bf0825b $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+x notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+x notes on 9th commit
+
+$commit_sha8 8th
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'verify state of local branch (y)' 'verify_notes y y'
+
+test_expect_success 'setup remote branch (z)' '
+       git update-ref refs/notes/z refs/notes/x &&
+       git config core.notesRef refs/notes/z &&
+       git notes add -f -m "z notes on 2nd commit" 2nd &&
+       git notes add -f -m "y notes on 4th commit" 4th &&
+       git notes add -f -m "z notes on 5th commit" 5th &&
+       git notes remove 6th &&
+       git notes add -f -m "z notes on 8th commit" 8th &&
+       git notes remove 9th &&
+       git notes add -f -m "z notes on 11th commit" 11th &&
+       git notes remove 12th &&
+       git notes add -f -m "y notes on 14th commit" 14th &&
+       git notes add -f -m "z notes on 15th commit" 15th
+'
+
+cat <<EOF | sort >expect_notes_z
+9b4b2c61f0615412da3c10f98ff85b57c04ec765 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+5d30216a129eeffa97d9694ffe8c74317a560315 $commit_sha13
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+897003322b53bc6ca098e9324ee508362347e734 $commit_sha7
+99fc34adfc400b95c67b013115e37e31aa9a6d23 $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha15 15th
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+x notes on 13th commit
+
+$commit_sha12 12th
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+x notes on 7th commit
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'verify state of remote branch (z)' 'verify_notes z z'
+
+# At this point, before merging z into y, we have the following status:
+#
+# commit | base/x  | local/y | remote/z | diff from x to y/z         | result
+# -------|---------|---------|----------|----------------------------|-------
+# 1st    | [none]  | [none]  | [none]   | unchanged / unchanged      | [none]
+# 2nd    | [none]  | [none]  | 283b482  | unchanged / added          | 283b482
+# 3rd    | [none]  | 5772f42 | [none]   | added     / unchanged      | 5772f42
+# 4th    | [none]  | e2bfd06 | e2bfd06  | added     / added (same)   | e2bfd06
+# 5th    | [none]  | 154508c | 99fc34a  | added     / added (diff)   | ???
+# 6th    | 11d97fd | [none]  | [none]   | removed   / removed        | [none]
+# 7th    | 8970033 | [none]  | 8970033  | removed   / unchanged      | [none]
+# 8th    | a3daf8a | [none]  | 851e163  | removed   / changed        | ???
+# 9th    | 20c613c | 20c613c | [none]   | unchanged / removed        | [none]
+# 10th   | b8d03e1 | b8d03e1 | b8d03e1  | unchanged / unchanged      | b8d03e1
+# 11th   | 7abbc45 | 7abbc45 | 7e3c535  | unchanged / changed        | 7e3c535
+# 12th   | dd161bc | a66055f | [none]   | changed   / removed        | ???
+# 13th   | 5d30216 | 3a631fd | 5d30216  | changed   / unchanged      | 3a631fd
+# 14th   | b0c95b9 | 5de7ea7 | 5de7ea7  | changed   / changed (same) | 5de7ea7
+# 15th   | 457a85d | 68b8630 | 9b4b2c6  | changed   / changed (diff) | ???
+
+test_expect_success 'merge z into y with invalid strategy => Fail/No changes' '
+       git config core.notesRef refs/notes/y &&
+       test_must_fail git notes merge --strategy=foo z &&
+       # Verify no changes (y)
+       verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_ours
+68b8630d25516028bed862719855b3d6768d7833 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+154508c7a0bcad82b6fe4b472bc4c26b3bf0825b $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_ours <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge z into y with "ours" strategy => Non-conflicting 3-way merge' '
+       git notes merge --strategy=ours z &&
+       verify_notes y ours
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+       git update-ref refs/notes/y refs/notes/y^1 &&
+       # Verify pre-merge state
+       verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_theirs
+9b4b2c61f0615412da3c10f98ff85b57c04ec765 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+99fc34adfc400b95c67b013115e37e31aa9a6d23 $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_theirs <<EOF
+$commit_sha15 15th
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge z into y with "theirs" strategy => Non-conflicting 3-way merge' '
+       git notes merge --strategy=theirs z &&
+       verify_notes y theirs
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+       git update-ref refs/notes/y refs/notes/y^1 &&
+       # Verify pre-merge state
+       verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_union
+7c4e546efd0fe939f876beb262ece02797880b54 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+6c841cc36ea496027290967ca96bd2bef54dbb47 $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_union <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge z into y with "union" strategy => Non-conflicting 3-way merge' '
+       git notes merge --strategy=union z &&
+       verify_notes y union
+'
+
+test_expect_success 'reset to pre-merge state (y)' '
+       git update-ref refs/notes/y refs/notes/y^1 &&
+       # Verify pre-merge state
+       verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_union2
+d682107b8bf7a7aea1e537a8d5cb6a12b60135f1 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+357b6ca14c7afd59b7f8b8aaaa6b8b723771135b $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_union2 <<EOF
+$commit_sha15 15th
+z notes on 15th commit
+
+y notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+z notes on 5th commit
+
+y notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge y into z with "union" strategy => Non-conflicting 3-way merge' '
+       git config core.notesRef refs/notes/z &&
+       git notes merge --strategy=union y &&
+       verify_notes z union2
+'
+
+test_expect_success 'reset to pre-merge state (z)' '
+       git update-ref refs/notes/z refs/notes/z^1 &&
+       # Verify pre-merge state
+       verify_notes z z
+'
+
+cat <<EOF | sort >expect_notes_cat_sort_uniq
+6be90240b5f54594203e25d9f2f64b7567175aee $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+660311d7f78dc53db12ac373a43fca7465381a7e $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_cat_sort_uniq <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge y into z with "cat_sort_uniq" strategy => Non-conflicting 3-way merge' '
+       git notes merge --strategy=cat_sort_uniq y &&
+       verify_notes z cat_sort_uniq
+'
+
+test_done
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
new file mode 100755 (executable)
index 0000000..4ec4d11
--- /dev/null
@@ -0,0 +1,556 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test notes merging with manual conflict resolution'
+
+. ./test-lib.sh
+
+# Set up a notes merge scenario with different kinds of conflicts
+test_expect_success 'setup commits' '
+       test_commit 1st &&
+       test_commit 2nd &&
+       test_commit 3rd &&
+       test_commit 4th &&
+       test_commit 5th
+'
+
+commit_sha1=$(git rev-parse 1st^{commit})
+commit_sha2=$(git rev-parse 2nd^{commit})
+commit_sha3=$(git rev-parse 3rd^{commit})
+commit_sha4=$(git rev-parse 4th^{commit})
+commit_sha5=$(git rev-parse 5th^{commit})
+
+verify_notes () {
+       notes_ref="$1"
+       git -c core.notesRef="refs/notes/$notes_ref" notes |
+               sort >"output_notes_$notes_ref" &&
+       test_cmp "expect_notes_$notes_ref" "output_notes_$notes_ref" &&
+       git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+               >"output_log_$notes_ref" &&
+       test_cmp "expect_log_$notes_ref" "output_log_$notes_ref"
+}
+
+cat <<EOF | sort >expect_notes_x
+6e8e3febca3c2bb896704335cc4d0c34cb2f8715 $commit_sha4
+e5388c10860456ee60673025345fe2e153eb8cf8 $commit_sha3
+ceefa674873670e7ecd131814d909723cce2b669 $commit_sha2
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+x notes on 4th commit
+
+$commit_sha3 3rd
+x notes on 3rd commit
+
+$commit_sha2 2nd
+x notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'setup merge base (x)' '
+       git config core.notesRef refs/notes/x &&
+       git notes add -m "x notes on 2nd commit" 2nd &&
+       git notes add -m "x notes on 3rd commit" 3rd &&
+       git notes add -m "x notes on 4th commit" 4th &&
+       verify_notes x
+'
+
+cat <<EOF | sort >expect_notes_y
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+b0a6021ec006d07e80e9b20ec9b444cbd9d560d3 $commit_sha1
+EOF
+
+cat >expect_log_y <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+
+$commit_sha1 1st
+y notes on 1st commit
+
+EOF
+
+test_expect_success 'setup local branch (y)' '
+       git update-ref refs/notes/y refs/notes/x &&
+       git config core.notesRef refs/notes/y &&
+       git notes add -f -m "y notes on 1st commit" 1st &&
+       git notes remove 2nd &&
+       git notes add -f -m "y notes on 3rd commit" 3rd &&
+       git notes add -f -m "y notes on 4th commit" 4th &&
+       verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_z
+cff59c793c20bb49a4e01bc06fb06bad642e0d54 $commit_sha4
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a81da8956346e19bcb27a906f04af327e03e31b $commit_sha1
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+z notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+z notes on 1st commit
+
+EOF
+
+test_expect_success 'setup remote branch (z)' '
+       git update-ref refs/notes/z refs/notes/x &&
+       git config core.notesRef refs/notes/z &&
+       git notes add -f -m "z notes on 1st commit" 1st &&
+       git notes add -f -m "z notes on 2nd commit" 2nd &&
+       git notes remove 3rd &&
+       git notes add -f -m "z notes on 4th commit" 4th &&
+       verify_notes z
+'
+
+# At this point, before merging z into y, we have the following status:
+#
+# commit | base/x  | local/y | remote/z | diff from x to y/z
+# -------|---------|---------|----------|---------------------------
+# 1st    | [none]  | b0a6021 | 0a81da8  | added     / added (diff)
+# 2nd    | ceefa67 | [none]  | 283b482  | removed   / changed
+# 3rd    | e5388c1 | 5772f42 | [none]   | changed   / removed
+# 4th    | 6e8e3fe | e2bfd06 | cff59c7  | changed   / changed (diff)
+# 5th    | [none]  | [none]  | [none]   | [none]
+
+cat <<EOF | sort >expect_conflicts
+$commit_sha1
+$commit_sha2
+$commit_sha3
+$commit_sha4
+EOF
+
+cat >expect_conflict_$commit_sha1 <<EOF
+<<<<<<< refs/notes/m
+y notes on 1st commit
+=======
+z notes on 1st commit
+>>>>>>> refs/notes/z
+EOF
+
+cat >expect_conflict_$commit_sha2 <<EOF
+z notes on 2nd commit
+EOF
+
+cat >expect_conflict_$commit_sha3 <<EOF
+y notes on 3rd commit
+EOF
+
+cat >expect_conflict_$commit_sha4 <<EOF
+<<<<<<< refs/notes/m
+y notes on 4th commit
+=======
+z notes on 4th commit
+>>>>>>> refs/notes/z
+EOF
+
+cp expect_notes_y expect_notes_m
+cp expect_log_y expect_log_m
+
+git rev-parse refs/notes/y > pre_merge_y
+git rev-parse refs/notes/z > pre_merge_z
+
+test_expect_success 'merge z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+       git update-ref refs/notes/m refs/notes/y &&
+       git config core.notesRef refs/notes/m &&
+       test_must_fail git notes merge z >output &&
+       # Output should point to where to resolve conflicts
+       grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+       # Inspect merge conflicts
+       ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+       test_cmp expect_conflicts output_conflicts &&
+       ( for f in $(cat expect_conflicts); do
+               test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+               exit 1
+       done ) &&
+       # Verify that current notes tree (pre-merge) has not changed (m == y)
+       verify_notes y &&
+       verify_notes m &&
+       test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+cat <<EOF | sort >expect_notes_z
+00494adecf2d9635a02fa431308d67993f853968 $commit_sha4
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a81da8956346e19bcb27a906f04af327e03e31b $commit_sha1
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+z notes on 4th commit
+
+More z notes on 4th commit
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+z notes on 1st commit
+
+EOF
+
+test_expect_success 'change notes in z' '
+       git notes --ref z append -m "More z notes on 4th commit" 4th &&
+       verify_notes z
+'
+
+test_expect_success 'cannot do merge w/conflicts when previous merge is unfinished' '
+       test -d .git/NOTES_MERGE_WORKTREE &&
+       test_must_fail git notes merge z >output 2>&1 &&
+       # Output should indicate what is wrong
+       grep -q "\\.git/NOTES_MERGE_\\* exists" output
+'
+
+# Setup non-conflicting merge between x and new notes ref w
+
+cat <<EOF | sort >expect_notes_w
+ceefa674873670e7ecd131814d909723cce2b669 $commit_sha2
+f75d1df88cbfe4258d49852f26cfc83f2ad4494b $commit_sha1
+EOF
+
+cat >expect_log_w <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+x notes on 2nd commit
+
+$commit_sha1 1st
+w notes on 1st commit
+
+EOF
+
+test_expect_success 'setup unrelated notes ref (w)' '
+       git config core.notesRef refs/notes/w &&
+       git notes add -m "w notes on 1st commit" 1st &&
+       git notes add -m "x notes on 2nd commit" 2nd &&
+       verify_notes w
+'
+
+cat <<EOF | sort >expect_notes_w
+6e8e3febca3c2bb896704335cc4d0c34cb2f8715 $commit_sha4
+e5388c10860456ee60673025345fe2e153eb8cf8 $commit_sha3
+ceefa674873670e7ecd131814d909723cce2b669 $commit_sha2
+f75d1df88cbfe4258d49852f26cfc83f2ad4494b $commit_sha1
+EOF
+
+cat >expect_log_w <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+x notes on 4th commit
+
+$commit_sha3 3rd
+x notes on 3rd commit
+
+$commit_sha2 2nd
+x notes on 2nd commit
+
+$commit_sha1 1st
+w notes on 1st commit
+
+EOF
+
+test_expect_success 'can do merge without conflicts even if previous merge is unfinished (x => w)' '
+       test -d .git/NOTES_MERGE_WORKTREE &&
+       git notes merge x &&
+       verify_notes w &&
+       # Verify that other notes refs has not changed (x and y)
+       verify_notes x &&
+       verify_notes y
+'
+
+cat <<EOF | sort >expect_notes_m
+021faa20e931fb48986ffc6282b4bb05553ac946 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a59e787e6d688aa6309e56e8c1b89431a0fc1c1 $commit_sha1
+EOF
+
+cat >expect_log_m <<EOF
+$commit_sha5 5th
+
+$commit_sha4 4th
+y and z notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+y and z notes on 1st commit
+
+EOF
+
+test_expect_success 'finalize conflicting merge (z => m)' '
+       # Resolve conflicts and finalize merge
+       cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+y and z notes on 1st commit
+EOF
+       cat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&
+y and z notes on 4th commit
+EOF
+       git notes merge --commit &&
+       # No .git/NOTES_MERGE_* files left
+       test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+       test_cmp /dev/null output &&
+       # Merge commit has pre-merge y and pre-merge z as parents
+       test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
+       test "$(git rev-parse refs/notes/m^2)" = "$(cat pre_merge_z)" &&
+       # Merge commit mentions the notes refs merged
+       git log -1 --format=%B refs/notes/m > merge_commit_msg &&
+       grep -q refs/notes/m merge_commit_msg &&
+       grep -q refs/notes/z merge_commit_msg &&
+       # Merge commit mentions conflicting notes
+       grep -q "Conflicts" merge_commit_msg &&
+       ( for sha1 in $(cat expect_conflicts); do
+               grep -q "$sha1" merge_commit_msg ||
+               exit 1
+       done ) &&
+       # Verify contents of merge result
+       verify_notes m &&
+       # Verify that other notes refs has not changed (w, x, y and z)
+       verify_notes w &&
+       verify_notes x &&
+       verify_notes y &&
+       verify_notes z
+'
+
+cat >expect_conflict_$commit_sha4 <<EOF
+<<<<<<< refs/notes/m
+y notes on 4th commit
+=======
+z notes on 4th commit
+
+More z notes on 4th commit
+>>>>>>> refs/notes/z
+EOF
+
+cp expect_notes_y expect_notes_m
+cp expect_log_y expect_log_m
+
+git rev-parse refs/notes/y > pre_merge_y
+git rev-parse refs/notes/z > pre_merge_z
+
+test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+       git update-ref refs/notes/m refs/notes/y &&
+       git config core.notesRef refs/notes/m &&
+       test_must_fail git notes merge z >output &&
+       # Output should point to where to resolve conflicts
+       grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+       # Inspect merge conflicts
+       ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+       test_cmp expect_conflicts output_conflicts &&
+       ( for f in $(cat expect_conflicts); do
+               test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+               exit 1
+       done ) &&
+       # Verify that current notes tree (pre-merge) has not changed (m == y)
+       verify_notes y &&
+       verify_notes m &&
+       test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+test_expect_success 'abort notes merge' '
+       git notes merge --abort &&
+       # No .git/NOTES_MERGE_* files left
+       test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+       test_cmp /dev/null output &&
+       # m has not moved (still == y)
+       test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+       # Verify that other notes refs has not changed (w, x, y and z)
+       verify_notes w &&
+       verify_notes x &&
+       verify_notes y &&
+       verify_notes z
+'
+
+git rev-parse refs/notes/y > pre_merge_y
+git rev-parse refs/notes/z > pre_merge_z
+
+test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+       test_must_fail git notes merge z >output &&
+       # Output should point to where to resolve conflicts
+       grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+       # Inspect merge conflicts
+       ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+       test_cmp expect_conflicts output_conflicts &&
+       ( for f in $(cat expect_conflicts); do
+               test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+               exit 1
+       done ) &&
+       # Verify that current notes tree (pre-merge) has not changed (m == y)
+       verify_notes y &&
+       verify_notes m &&
+       test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+cat <<EOF | sort >expect_notes_m
+304dfb4325cf243025b9957486eb605a9b51c199 $commit_sha5
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+0a59e787e6d688aa6309e56e8c1b89431a0fc1c1 $commit_sha1
+EOF
+
+cat >expect_log_m <<EOF
+$commit_sha5 5th
+new note on 5th commit
+
+$commit_sha4 4th
+
+$commit_sha3 3rd
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+y and z notes on 1st commit
+
+EOF
+
+test_expect_success 'add + remove notes in finalized merge (z => m)' '
+       # Resolve one conflict
+       cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+y and z notes on 1st commit
+EOF
+       # Remove another conflict
+       rm .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&
+       # Remove a D/F conflict
+       rm .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
+       # Add a new note
+       echo "new note on 5th commit" > .git/NOTES_MERGE_WORKTREE/$commit_sha5 &&
+       # Finalize merge
+       git notes merge --commit &&
+       # No .git/NOTES_MERGE_* files left
+       test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+       test_cmp /dev/null output &&
+       # Merge commit has pre-merge y and pre-merge z as parents
+       test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
+       test "$(git rev-parse refs/notes/m^2)" = "$(cat pre_merge_z)" &&
+       # Merge commit mentions the notes refs merged
+       git log -1 --format=%B refs/notes/m > merge_commit_msg &&
+       grep -q refs/notes/m merge_commit_msg &&
+       grep -q refs/notes/z merge_commit_msg &&
+       # Merge commit mentions conflicting notes
+       grep -q "Conflicts" merge_commit_msg &&
+       ( for sha1 in $(cat expect_conflicts); do
+               grep -q "$sha1" merge_commit_msg ||
+               exit 1
+       done ) &&
+       # Verify contents of merge result
+       verify_notes m &&
+       # Verify that other notes refs has not changed (w, x, y and z)
+       verify_notes w &&
+       verify_notes x &&
+       verify_notes y &&
+       verify_notes z
+'
+
+cp expect_notes_y expect_notes_m
+cp expect_log_y expect_log_m
+
+test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
+       git update-ref refs/notes/m refs/notes/y &&
+       test_must_fail git notes merge z >output &&
+       # Output should point to where to resolve conflicts
+       grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+       # Inspect merge conflicts
+       ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+       test_cmp expect_conflicts output_conflicts &&
+       ( for f in $(cat expect_conflicts); do
+               test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+               exit 1
+       done ) &&
+       # Verify that current notes tree (pre-merge) has not changed (m == y)
+       verify_notes y &&
+       verify_notes m &&
+       test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+'
+
+cp expect_notes_w expect_notes_m
+cp expect_log_w expect_log_m
+
+test_expect_success 'reset notes ref m to somewhere else (w)' '
+       git update-ref refs/notes/m refs/notes/w &&
+       verify_notes m &&
+       test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
+'
+
+test_expect_success 'fail to finalize conflicting merge if underlying ref has moved in the meantime (m != NOTES_MERGE_PARTIAL^1)' '
+       # Resolve conflicts
+       cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+y and z notes on 1st commit
+EOF
+       cat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&
+y and z notes on 4th commit
+EOF
+       # Fail to finalize merge
+       test_must_fail git notes merge --commit >output 2>&1 &&
+       # .git/NOTES_MERGE_* must remain
+       test -f .git/NOTES_MERGE_PARTIAL &&
+       test -f .git/NOTES_MERGE_REF &&
+       test -f .git/NOTES_MERGE_WORKTREE/$commit_sha1 &&
+       test -f .git/NOTES_MERGE_WORKTREE/$commit_sha2 &&
+       test -f .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
+       test -f .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&
+       # Refs are unchanged
+       test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
+       test "$(git rev-parse refs/notes/y)" = "$(git rev-parse NOTES_MERGE_PARTIAL^1)"
+       test "$(git rev-parse refs/notes/m)" != "$(git rev-parse NOTES_MERGE_PARTIAL^1)"
+       # Mention refs/notes/m, and its current and expected value in output
+       grep -q "refs/notes/m" output &&
+       grep -q "$(git rev-parse refs/notes/m)" output &&
+       grep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output &&
+       # Verify that other notes refs has not changed (w, x, y and z)
+       verify_notes w &&
+       verify_notes x &&
+       verify_notes y &&
+       verify_notes z
+'
+
+test_expect_success 'resolve situation by aborting the notes merge' '
+       git notes merge --abort &&
+       # No .git/NOTES_MERGE_* files left
+       test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+       test_cmp /dev/null output &&
+       # m has not moved (still == w)
+       test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
+       # Verify that other notes refs has not changed (w, x, y and z)
+       verify_notes w &&
+       verify_notes x &&
+       verify_notes y &&
+       verify_notes z
+'
+
+test_done
diff --git a/t/t3311-notes-merge-fanout.sh b/t/t3311-notes-merge-fanout.sh
new file mode 100755 (executable)
index 0000000..93516ef
--- /dev/null
@@ -0,0 +1,436 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Johan Herland
+#
+
+test_description='Test notes merging at various fanout levels'
+
+. ./test-lib.sh
+
+verify_notes () {
+       notes_ref="$1"
+       commit="$2"
+       if test -f "expect_notes_$notes_ref"
+       then
+               git -c core.notesRef="refs/notes/$notes_ref" notes |
+                       sort >"output_notes_$notes_ref" &&
+               test_cmp "expect_notes_$notes_ref" "output_notes_$notes_ref" ||
+                       return 1
+       fi &&
+       git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
+               "$commit" >"output_log_$notes_ref" &&
+       test_cmp "expect_log_$notes_ref" "output_log_$notes_ref"
+}
+
+verify_fanout () {
+       notes_ref="$1"
+       # Expect entire notes tree to have a fanout == 1
+       git rev-parse --quiet --verify "refs/notes/$notes_ref" >/dev/null &&
+       git ls-tree -r --name-only "refs/notes/$notes_ref" |
+       while read path
+       do
+               case "$path" in
+               ??/??????????????????????????????????????)
+                       : true
+                       ;;
+               *)
+                       echo "Invalid path \"$path\"" &&
+                       return 1
+                       ;;
+               esac
+       done
+}
+
+verify_no_fanout () {
+       notes_ref="$1"
+       # Expect entire notes tree to have a fanout == 0
+       git rev-parse --quiet --verify "refs/notes/$notes_ref" >/dev/null &&
+       git ls-tree -r --name-only "refs/notes/$notes_ref" |
+       while read path
+       do
+               case "$path" in
+               ????????????????????????????????????????)
+                       : true
+                       ;;
+               *)
+                       echo "Invalid path \"$path\"" &&
+                       return 1
+                       ;;
+               esac
+       done
+}
+
+# Set up a notes merge scenario with different kinds of conflicts
+test_expect_success 'setup a few initial commits with notes (notes ref: x)' '
+       git config core.notesRef refs/notes/x &&
+       for i in 1 2 3 4 5
+       do
+               test_commit "commit$i" >/dev/null &&
+               git notes add -m "notes for commit$i" || return 1
+       done
+'
+
+commit_sha1=$(git rev-parse commit1^{commit})
+commit_sha2=$(git rev-parse commit2^{commit})
+commit_sha3=$(git rev-parse commit3^{commit})
+commit_sha4=$(git rev-parse commit4^{commit})
+commit_sha5=$(git rev-parse commit5^{commit})
+
+cat <<EOF | sort >expect_notes_x
+aed91155c7a72c2188e781fdf40e0f3761b299db $commit_sha5
+99fab268f9d7ee7b011e091a436c78def8eeee69 $commit_sha4
+953c20ae26c7aa0b428c20693fe38bc687f9d1a9 $commit_sha3
+6358796131b8916eaa2dde6902642942a1cb37e1 $commit_sha2
+b02d459c32f0e68f2fe0981033bb34f38776ba47 $commit_sha1
+EOF
+
+cat >expect_log_x <<EOF
+$commit_sha5 commit5
+notes for commit5
+
+$commit_sha4 commit4
+notes for commit4
+
+$commit_sha3 commit3
+notes for commit3
+
+$commit_sha2 commit2
+notes for commit2
+
+$commit_sha1 commit1
+notes for commit1
+
+EOF
+
+test_expect_success 'sanity check (x)' '
+       verify_notes x commit5 &&
+       verify_no_fanout x
+'
+
+num=300
+
+cp expect_log_x expect_log_y
+
+test_expect_success 'Add a few hundred commits w/notes to trigger fanout (x -> y)' '
+       git update-ref refs/notes/y refs/notes/x &&
+       git config core.notesRef refs/notes/y &&
+       i=5 &&
+       while test $i -lt $num
+       do
+               i=$(($i + 1)) &&
+               test_commit "commit$i" >/dev/null &&
+               git notes add -m "notes for commit$i" || return 1
+       done &&
+       test "$(git rev-parse refs/notes/y)" != "$(git rev-parse refs/notes/x)" &&
+       # Expected number of commits and notes
+       test $(git rev-list HEAD | wc -l) = $num &&
+       test $(git notes list | wc -l) = $num &&
+       # 5 first notes unchanged
+       verify_notes y commit5
+'
+
+test_expect_success 'notes tree has fanout (y)' 'verify_fanout y'
+
+test_expect_success 'No-op merge (already included) (x => y)' '
+       git update-ref refs/notes/m refs/notes/y &&
+       git config core.notesRef refs/notes/m &&
+       git notes merge x &&
+       test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/y)"
+'
+
+test_expect_success 'Fast-forward merge (y => x)' '
+       git update-ref refs/notes/m refs/notes/x &&
+       git notes merge y &&
+       test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/y)"
+'
+
+cat <<EOF | sort >expect_notes_z
+9f506ee70e20379d7f78204c77b334f43d77410d $commit_sha3
+23a47d6ea7d589895faf800752054818e1e7627b $commit_sha2
+b02d459c32f0e68f2fe0981033bb34f38776ba47 $commit_sha1
+EOF
+
+cat >expect_log_z <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+
+$commit_sha3 commit3
+notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+notes for commit1
+
+EOF
+
+test_expect_success 'change some of the initial 5 notes (x -> z)' '
+       git update-ref refs/notes/z refs/notes/x &&
+       git config core.notesRef refs/notes/z &&
+       git notes add -f -m "new notes for commit2" commit2 &&
+       git notes append -m "appended notes for commit3" commit3 &&
+       git notes remove commit4 &&
+       git notes remove commit5 &&
+       verify_notes z commit5
+'
+
+test_expect_success 'notes tree has no fanout (z)' 'verify_no_fanout z'
+
+cp expect_log_z expect_log_m
+
+test_expect_success 'successful merge without conflicts (y => z)' '
+       git update-ref refs/notes/m refs/notes/z &&
+       git config core.notesRef refs/notes/m &&
+       git notes merge y &&
+       verify_notes m commit5 &&
+       # x/y/z unchanged
+       verify_notes x commit5 &&
+       verify_notes y commit5 &&
+       verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_w <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+$commit_sha2 commit2
+notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'introduce conflicting changes (y -> w)' '
+       git update-ref refs/notes/w refs/notes/y &&
+       git config core.notesRef refs/notes/w &&
+       git notes add -f -m "other notes for commit1" commit1 &&
+       git notes add -f -m "other notes for commit3" commit3 &&
+       git notes add -f -m "other notes for commit4" commit4 &&
+       git notes remove commit5 &&
+       verify_notes w commit5
+'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "ours" strategy (z => w)' '
+       git update-ref refs/notes/m refs/notes/w &&
+       git config core.notesRef refs/notes/m &&
+       git notes merge -s ours z &&
+       verify_notes m commit5 &&
+       # w/x/y/z unchanged
+       verify_notes w commit5 &&
+       verify_notes x commit5 &&
+       verify_notes y commit5 &&
+       verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+
+$commit_sha3 commit3
+notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "theirs" strategy (z => w)' '
+       git update-ref refs/notes/m refs/notes/w &&
+       git notes merge -s theirs z &&
+       verify_notes m commit5 &&
+       # w/x/y/z unchanged
+       verify_notes w commit5 &&
+       verify_notes x commit5 &&
+       verify_notes y commit5 &&
+       verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "union" strategy (z => w)' '
+       git update-ref refs/notes/m refs/notes/w &&
+       git notes merge -s union z &&
+       verify_notes m commit5 &&
+       # w/x/y/z unchanged
+       verify_notes w commit5 &&
+       verify_notes x commit5 &&
+       verify_notes y commit5 &&
+       verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+appended notes for commit3
+notes for commit3
+other notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'successful merge using "cat_sort_uniq" strategy (z => w)' '
+       git update-ref refs/notes/m refs/notes/w &&
+       git notes merge -s cat_sort_uniq z &&
+       verify_notes m commit5 &&
+       # w/x/y/z unchanged
+       verify_notes w commit5 &&
+       verify_notes x commit5 &&
+       verify_notes y commit5 &&
+       verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+# We're merging z into w. Here are the conflicts we expect:
+#
+# commit | x -> w    | x -> z    | conflict?
+# -------|-----------|-----------|----------
+# 1      | changed   | unchanged | no, use w
+# 2      | unchanged | changed   | no, use z
+# 3      | changed   | changed   | yes (w, then z in conflict markers)
+# 4      | changed   | deleted   | yes (w)
+# 5      | deleted   | deleted   | no, deleted
+
+test_expect_success 'fails to merge using "manual" strategy (z => w)' '
+       git update-ref refs/notes/m refs/notes/w &&
+       test_must_fail git notes merge z
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+cat <<EOF | sort >expect_conflicts
+$commit_sha3
+$commit_sha4
+EOF
+
+cat >expect_conflict_$commit_sha3 <<EOF
+<<<<<<< refs/notes/m
+other notes for commit3
+=======
+notes for commit3
+
+appended notes for commit3
+>>>>>>> refs/notes/z
+EOF
+
+cat >expect_conflict_$commit_sha4 <<EOF
+other notes for commit4
+EOF
+
+test_expect_success 'verify conflict entries (with no fanout)' '
+       ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+       test_cmp expect_conflicts output_conflicts &&
+       ( for f in $(cat expect_conflicts); do
+               test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+               exit 1
+       done ) &&
+       # Verify that current notes tree (pre-merge) has not changed (m == w)
+       test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
+'
+
+cat >expect_log_m <<EOF
+$commit_sha5 commit5
+
+$commit_sha4 commit4
+other notes for commit4
+
+$commit_sha3 commit3
+other notes for commit3
+
+appended notes for commit3
+
+$commit_sha2 commit2
+new notes for commit2
+
+$commit_sha1 commit1
+other notes for commit1
+
+EOF
+
+test_expect_success 'resolve and finalize merge (z => w)' '
+       cat >.git/NOTES_MERGE_WORKTREE/$commit_sha3 <<EOF &&
+other notes for commit3
+
+appended notes for commit3
+EOF
+       git notes merge --commit &&
+       verify_notes m commit5 &&
+       # w/x/y/z unchanged
+       verify_notes w commit5 &&
+       verify_notes x commit5 &&
+       verify_notes y commit5 &&
+       verify_notes z commit5
+'
+
+test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
+
+test_done
index a19aeb6441cb29dc20478cf898b5f7767d770c94..349eebd54268c927249322dc22fc478869860f7c 100755 (executable)
@@ -153,7 +153,8 @@ test_expect_success 'setup: recover' '
 test_expect_success 'Show verbose error when HEAD could not be detached' '
        >B &&
        test_must_fail git rebase topic 2>output.err >output.out &&
-       grep "Untracked working tree file .B. would be overwritten" output.err
+       grep "The following untracked working tree files would be overwritten by checkout:" output.err &&
+       grep B output.err
 '
 rm -f B
 
index 2bea65634a1790ceecf824309c1fbb66250f58d5..be8c1d5ef9fd650c128a9fe7126cbcd8db6c5a95 100755 (executable)
@@ -117,4 +117,25 @@ test_expect_success 'picking rebase' '
        esac
 '
 
+test_expect_success 'rebase -s funny -Xopt' '
+       test_when_finished "rm -fr test-bin funny.was.run" &&
+       mkdir test-bin &&
+       cat >test-bin/git-merge-funny <<-EOF &&
+       #!$SHELL_PATH
+       case "\$1" in --opt) ;; *) exit 2 ;; esac
+       shift &&
+       >funny.was.run &&
+       exec git merge-recursive "\$@"
+       EOF
+       chmod +x test-bin/git-merge-funny &&
+       git reset --hard &&
+       git checkout -b test-funny master^ &&
+       test_commit funny &&
+       (
+               PATH=./test-bin:$PATH
+               git rebase -s funny -Xopt master
+       ) &&
+       test -f funny.was.run
+'
+
 test_done
index 9f03ce699e8f4c3c654065deae675d1a7b259c74..7d8147bb93df482cf7e11338ac96acdf43cf551e 100755 (executable)
@@ -7,34 +7,39 @@ test_description='git rebase interactive
 
 This test runs git rebase "interactively", by faking an edit, and verifies
 that the result still makes sense.
+
+Initial setup:
+
+     one - two - three - four (conflict-branch)
+   /
+ A - B - C - D - E            (master)
+ | \
+ |   F - G - H                (branch1)
+ |     \
+ |\      I                    (branch2)
+ | \
+ |   J - K - L - M            (no-conflict-branch)
+  \
+    N - O - P                 (no-ff-branch)
+
+ where A, B, D and G all touch file1, and one, two, three, four all
+ touch file "conflict".
 '
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
+test_cmp_rev () {
+       git rev-parse --verify "$1" >expect.rev &&
+       git rev-parse --verify "$2" >actual.rev &&
+       test_cmp expect.rev actual.rev
+}
+
 set_fake_editor
 
-# Set up the repository like this:
-#
-#     one - two - three - four (conflict-branch)
-#   /
-# A - B - C - D - E            (master)
-# | \
-# |   F - G - H                (branch1)
-# |     \
-# |\      I                    (branch2)
-# | \
-# |   J - K - L - M            (no-conflict-branch)
-#  \
-#    N - O - P                 (no-ff-branch)
-#
-# where A, B, D and G all touch file1, and one, two, three, four all
-# touch file "conflict".
-#
 # WARNING: Modifications to the initial repository can change the SHA ID used
 # in the expect2 file for the 'stop on conflicting pick' test.
 
-
 test_expect_success 'setup' '
        test_commit A file1 &&
        test_commit B file1 &&
@@ -46,22 +51,71 @@ test_expect_success 'setup' '
        test_commit G file1 &&
        test_commit H file5 &&
        git checkout -b branch2 F &&
-       test_commit I file6
+       test_commit I file6 &&
        git checkout -b conflict-branch A &&
-       for n in one two three four
-       do
-               test_commit $n conflict
-       done &&
+       test_commit one conflict &&
+       test_commit two conflict &&
+       test_commit three conflict &&
+       test_commit four conflict &&
        git checkout -b no-conflict-branch A &&
-       for n in J K L M
-       do
-               test_commit $n file$n
-       done &&
+       test_commit J fileJ &&
+       test_commit K fileK &&
+       test_commit L fileL &&
+       test_commit M fileM &&
        git checkout -b no-ff-branch A &&
-       for n in N O P
-       do
-               test_commit $n file$n
-       done
+       test_commit N fileN &&
+       test_commit O fileO &&
+       test_commit P fileP
+'
+
+# "exec" commands are ran with the user shell by default, but this may
+# be non-POSIX. For example, if SHELL=zsh then ">file" doesn't work
+# to create a file. Unseting SHELL avoids such non-portable behavior
+# in tests. It must be exported for it to take effect where needed.
+SHELL=
+export SHELL
+
+test_expect_success 'rebase -i with the exec command' '
+       git checkout master &&
+       (
+       FAKE_LINES="1 exec_>touch-one
+               2 exec_>touch-two exec_false exec_>touch-three
+               3 4 exec_>\"touch-file__name_with_spaces\";_>touch-after-semicolon 5" &&
+       export FAKE_LINES &&
+       test_must_fail git rebase -i A
+       ) &&
+       test_path_is_file touch-one &&
+       test_path_is_file touch-two &&
+       test_path_is_missing touch-three " (should have stopped before)" &&
+       test_cmp_rev C HEAD &&
+       git rebase --continue &&
+       test_path_is_file touch-three &&
+       test_path_is_file "touch-file  name with spaces" &&
+       test_path_is_file touch-after-semicolon &&
+       test_cmp_rev master HEAD &&
+       rm -f touch-*
+'
+
+test_expect_success 'rebase -i with the exec command runs from tree root' '
+       git checkout master &&
+       mkdir subdir && (cd subdir &&
+       FAKE_LINES="1 exec_>touch-subdir" \
+               git rebase -i HEAD^
+       ) &&
+       test_path_is_file touch-subdir &&
+       rm -fr subdir
+'
+
+test_expect_success 'rebase -i with the exec command checks tree cleanness' '
+       git checkout master &&
+       (
+       FAKE_LINES="exec_echo_foo_>file1 1" &&
+       export FAKE_LINES &&
+       test_must_fail git rebase -i HEAD^
+       ) &&
+       test_cmp_rev master^ HEAD &&
+       git reset --hard &&
+       git rebase --continue
 '
 
 test_expect_success 'no changes are a nop' '
@@ -143,16 +197,17 @@ test_expect_success 'abort' '
        git rebase --abort &&
        test $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
-       ! test -d .git/rebase-merge
+       test_path_is_missing .git/rebase-merge
 '
 
 test_expect_success 'abort with error when new base cannot be checked out' '
        git rm --cached file1 &&
        git commit -m "remove file in base" &&
        test_must_fail git rebase -i master > output 2>&1 &&
-       grep "Untracked working tree file .file1. would be overwritten" \
+       grep "The following untracked working tree files would be overwritten by checkout:" \
                output &&
-       ! test -d .git/rebase-merge &&
+       grep "file1" output &&
+       test_path_is_missing .git/rebase-merge &&
        git reset --hard HEAD^
 '
 
@@ -522,7 +577,7 @@ test_expect_success 'do "noop" when there is nothing to cherry-pick' '
 
        git checkout -b branch4 HEAD &&
        GIT_EDITOR=: git commit --amend \
-               --author="Somebody else <somebody@else.com>" 
+               --author="Somebody else <somebody@else.com>" &&
        test $(git rev-parse branch3) != $(git rev-parse branch4) &&
        git rebase -i branch3 &&
        test $(git rev-parse branch3) = $(git rev-parse branch4)
@@ -537,7 +592,7 @@ test_expect_success 'submodule rebase setup' '
                git add elif && git commit -m "submodule initial"
        ) &&
        echo 1 >file1 &&
-       git add file1 sub
+       git add file1 sub &&
        test_tick &&
        git commit -m "One" &&
        echo 2 >file1 &&
@@ -593,6 +648,7 @@ test_expect_success 'rebase -i can copy notes' '
 
 cat >expect <<EOF
 an earlier note
+
 a note
 EOF
 
index 85fc7c4af8cebdb50a7fa294b274bb2e7988997b..fe5f936988bc5ec9bbe6d9175ef6e085c422eadc 100755 (executable)
@@ -43,20 +43,20 @@ test_expect_success 'rebase -m' '
 '
 
 test_expect_success 'rebase --stat' '
-        git reset --hard start
+       git reset --hard start &&
         git rebase --stat master >diffstat.txt &&
         grep "^ fileX |  *1 +$" diffstat.txt
 '
 
 test_expect_success 'rebase w/config rebase.stat' '
-        git reset --hard start
+       git reset --hard start &&
         git config rebase.stat true &&
         git rebase master >diffstat.txt &&
         grep "^ fileX |  *1 +$" diffstat.txt
 '
 
 test_expect_success 'rebase -n overrides config rebase.stat config' '
-        git reset --hard start
+       git reset --hard start &&
         git config rebase.stat true &&
         git rebase -n master >diffstat.txt &&
         ! grep "^ fileX |  *1 +$" diffstat.txt
index 2999e78937f31a45e9e2ea925f69ac00f157503f..e573dc845b3d72004b2a96f344528c68977c52e1 100755 (executable)
@@ -38,7 +38,7 @@ testrebase() {
                # Clean up the state from the previous one
                git reset --hard pre-rebase &&
                test_must_fail git rebase$type master &&
-               test -d "$dotest" &&
+               test_path_is_dir "$dotest" &&
                git rebase --abort &&
                test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
                test ! -d "$dotest"
@@ -49,7 +49,7 @@ testrebase() {
                # Clean up the state from the previous one
                git reset --hard pre-rebase &&
                test_must_fail git rebase$type master &&
-               test -d "$dotest" &&
+               test_path_is_dir "$dotest" &&
                test_must_fail git rebase --skip &&
                test $(git rev-parse HEAD) = $(git rev-parse master) &&
                git rebase --abort &&
@@ -62,7 +62,7 @@ testrebase() {
                # Clean up the state from the previous one
                git reset --hard pre-rebase &&
                test_must_fail git rebase$type master &&
-               test -d "$dotest" &&
+               test_path_is_dir "$dotest" &&
                echo c > a &&
                echo d >> a &&
                git add a &&
@@ -72,6 +72,18 @@ testrebase() {
                test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
                test ! -d "$dotest"
        '
+
+       test_expect_success "rebase$type --abort does not update reflog" '
+               cd "$work_dir" &&
+               # Clean up the state from the previous one
+               git reset --hard pre-rebase &&
+               git reflog show to-rebase > reflog_before &&
+               test_must_fail git rebase$type master &&
+               git rebase --abort &&
+               git reflog show to-rebase > reflog_after &&
+               test_cmp reflog_before reflog_after &&
+               rm reflog_before reflog_after
+       '
 }
 
 testrebase "" .git/rebase-apply
index 2062b858bbcb63a715d87f3b12adfd0e9ceb3a67..6b84e6042a6fcc9cf850a53ad2a885597fb178fc 100755 (executable)
@@ -16,7 +16,7 @@ test_expect_success setup '
        git commit -a -m "A sample commit log message that has a long
 summary that spills over multiple lines.
 
-But otherwise with a sane description."
+But otherwise with a sane description." &&
 
        git branch side &&
 
index 8f785e7957519eaa3dd1ef946c905054c4fa0e6c..19341e5ca1c7f415afdf2c82f67930b8fed5478b 100755 (executable)
@@ -42,23 +42,24 @@ test_expect_success 'setup for merge-preserving rebase' \
        git commit -a -m "Modify A2" &&
 
        git clone ./. clone1 &&
-       cd clone1 &&
+       (cd clone1 &&
        git checkout -b topic origin/topic &&
-       git merge origin/master &&
-       cd .. &&
+       git merge origin/master
+       ) &&
 
        echo Fifth > B &&
        git add B &&
        git commit -m "Add different B" &&
 
        git clone ./. clone2 &&
-       cd clone2 &&
-       git checkout -b topic origin/topic &&
-       test_must_fail git merge origin/master &&
-       echo Resolved > B &&
-       git add B &&
-       git commit -m "Merge origin/master into topic" &&
-       cd .. &&
+       (
+               cd clone2 &&
+               git checkout -b topic origin/topic &&
+               test_must_fail git merge origin/master &&
+               echo Resolved >B &&
+               git add B &&
+               git commit -m "Merge origin/master into topic"
+       ) &&
 
        git checkout topic &&
        echo Fourth >> B &&
@@ -71,7 +72,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 remote branch " | wc -l)
+       test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l)
        )
 '
 
index 5869061c5bfdee4a84b156b8ec9d6e331a2c906c..086c91c7b47aa2fa7b593f4f9b2f84f6b7ba3724 100755 (executable)
@@ -173,14 +173,14 @@ EOF
 test_expect_success 'pre-rebase hook stops rebase' '
        git checkout -b stops1 other &&
        test_must_fail git rebase --root --onto master &&
-       test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1
+       test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1 &&
        test 0 = $(git rev-list other...stops1 | wc -l)
 '
 
 test_expect_success 'pre-rebase hook stops rebase -i' '
        git checkout -b stops2 other &&
        test_must_fail git rebase --root --onto master &&
-       test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2
+       test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2 &&
        test 0 = $(git rev-list other...stops2 | wc -l)
 '
 
index b63f4e2d677b6b4827d39a51a3c39719a8ab5b4f..b38be8e93723991d717b6b7fb690560efb58c36d 100755 (executable)
@@ -14,6 +14,7 @@ test_expect_success setup '
        git add . &&
        test_tick &&
        git commit -m "first commit" &&
+       git tag first-commit &&
        echo 3 >file3 &&
        git add . &&
        test_tick &&
@@ -21,38 +22,62 @@ test_expect_success setup '
        git tag base
 '
 
-test_expect_success 'auto fixup' '
+test_auto_fixup () {
        git reset --hard base &&
        echo 1 >file1 &&
        git add -u &&
        test_tick &&
-       git commit -m "fixup! first"
+       git commit -m "fixup! first" &&
 
-       git tag final-fixup &&
+       git tag $1 &&
        test_tick &&
-       git rebase --autosquash -i HEAD^^^ &&
+       git rebase $2 -i HEAD^^^ &&
        git log --oneline >actual &&
        test 3 = $(wc -l <actual) &&
-       git diff --exit-code final-fixup &&
+       git diff --exit-code $1 &&
        test 1 = "$(git cat-file blob HEAD^:file1)" &&
        test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'auto fixup (option)' '
+       test_auto_fixup final-fixup-option --autosquash
+'
+
+test_expect_success 'auto fixup (config)' '
+       git config rebase.autosquash true &&
+       test_auto_fixup final-fixup-config-true &&
+       test_must_fail test_auto_fixup fixup-config-true-no --no-autosquash &&
+       git config rebase.autosquash false &&
+       test_must_fail test_auto_fixup final-fixup-config-false
 '
 
-test_expect_success 'auto squash' '
+test_auto_squash () {
        git reset --hard base &&
        echo 1 >file1 &&
        git add -u &&
        test_tick &&
-       git commit -m "squash! first"
+       git commit -m "squash! first" &&
 
-       git tag final-squash &&
+       git tag $1 &&
        test_tick &&
-       git rebase --autosquash -i HEAD^^^ &&
+       git rebase $2 -i HEAD^^^ &&
        git log --oneline >actual &&
        test 3 = $(wc -l <actual) &&
-       git diff --exit-code final-squash &&
+       git diff --exit-code $1 &&
        test 1 = "$(git cat-file blob HEAD^:file1)" &&
        test 2 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'auto squash (option)' '
+       test_auto_squash final-squash --autosquash
+'
+
+test_expect_success 'auto squash (config)' '
+       git config rebase.autosquash true &&
+       test_auto_squash final-squash-config-true &&
+       test_must_fail test_auto_squash squash-config-true-no --no-autosquash &&
+       git config rebase.autosquash false &&
+       test_must_fail test_auto_squash final-squash-config-false
 '
 
 test_expect_success 'misspelled auto squash' '
@@ -60,7 +85,7 @@ test_expect_success 'misspelled auto squash' '
        echo 1 >file1 &&
        git add -u &&
        test_tick &&
-       git commit -m "squash! forst"
+       git commit -m "squash! forst" &&
        git tag final-missquash &&
        test_tick &&
        git rebase --autosquash -i HEAD^^^ &&
@@ -70,4 +95,102 @@ test_expect_success 'misspelled auto squash' '
        test 0 = $(git rev-list final-missquash...HEAD | wc -l)
 '
 
+test_expect_success 'auto squash that matches 2 commits' '
+       git reset --hard base &&
+       echo 4 >file4 &&
+       git add file4 &&
+       test_tick &&
+       git commit -m "first new commit" &&
+       echo 1 >file1 &&
+       git add -u &&
+       test_tick &&
+       git commit -m "squash! first" &&
+       git tag final-multisquash &&
+       test_tick &&
+       git rebase --autosquash -i HEAD~4 &&
+       git log --oneline >actual &&
+       test 4 = $(wc -l <actual) &&
+       git diff --exit-code final-multisquash &&
+       test 1 = "$(git cat-file blob HEAD^^:file1)" &&
+       test 2 = $(git cat-file commit HEAD^^ | grep first | wc -l) &&
+       test 1 = $(git cat-file commit HEAD | grep first | wc -l)
+'
+
+test_expect_success 'auto squash that matches a commit after the squash' '
+       git reset --hard base &&
+       echo 1 >file1 &&
+       git add -u &&
+       test_tick &&
+       git commit -m "squash! third" &&
+       echo 4 >file4 &&
+       git add file4 &&
+       test_tick &&
+       git commit -m "third commit" &&
+       git tag final-presquash &&
+       test_tick &&
+       git rebase --autosquash -i HEAD~4 &&
+       git log --oneline >actual &&
+       test 5 = $(wc -l <actual) &&
+       git diff --exit-code final-presquash &&
+       test 0 = "$(git cat-file blob HEAD^^:file1)" &&
+       test 1 = "$(git cat-file blob HEAD^:file1)" &&
+       test 1 = $(git cat-file commit HEAD | grep third | wc -l) &&
+       test 1 = $(git cat-file commit HEAD^ | grep third | wc -l)
+'
+test_expect_success 'auto squash that matches a sha1' '
+       git reset --hard base &&
+       echo 1 >file1 &&
+       git add -u &&
+       test_tick &&
+       git commit -m "squash! $(git rev-parse --short HEAD^)" &&
+       git tag final-shasquash &&
+       test_tick &&
+       git rebase --autosquash -i HEAD^^^ &&
+       git log --oneline >actual &&
+       test 3 = $(wc -l <actual) &&
+       git diff --exit-code final-shasquash &&
+       test 1 = "$(git cat-file blob HEAD^:file1)" &&
+       test 1 = $(git cat-file commit HEAD^ | grep squash | wc -l)
+'
+
+test_expect_success 'auto squash that matches longer sha1' '
+       git reset --hard base &&
+       echo 1 >file1 &&
+       git add -u &&
+       test_tick &&
+       git commit -m "squash! $(git rev-parse --short=11 HEAD^)" &&
+       git tag final-longshasquash &&
+       test_tick &&
+       git rebase --autosquash -i HEAD^^^ &&
+       git log --oneline >actual &&
+       test 3 = $(wc -l <actual) &&
+       git diff --exit-code final-longshasquash &&
+       test 1 = "$(git cat-file blob HEAD^:file1)" &&
+       test 1 = $(git cat-file commit HEAD^ | grep squash | wc -l)
+'
+
+test_auto_commit_flags () {
+       git reset --hard base &&
+       echo 1 >file1 &&
+       git add -u &&
+       test_tick &&
+       git commit --$1 first-commit &&
+       git tag final-commit-$1 &&
+       test_tick &&
+       git rebase --autosquash -i HEAD^^^ &&
+       git log --oneline >actual &&
+       test 3 = $(wc -l <actual) &&
+       git diff --exit-code final-commit-$1 &&
+       test 1 = "$(git cat-file blob HEAD^:file1)" &&
+       test $2 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'use commit --fixup' '
+       test_auto_commit_flags fixup 1
+'
+
+test_expect_success 'use commit --squash' '
+       test_auto_commit_flags squash 2
+'
+
 test_done
index 220a740ee89af325d04321a45aafc275ed130610..1fb3e499b4a044d935c090bfa567a7ace271c054 100755 (executable)
@@ -89,7 +89,7 @@ test_expect_success 'same, but do not remove trailing spaces' '
        git config core.whitespace "-blank-at-eol" &&
        git reset --hard HEAD^ &&
        cp third file && git add file && git commit -m third &&
-       git rebase --whitespace=fix HEAD^^
+       git rebase --whitespace=fix HEAD^^ &&
        git diff --exit-code HEAD^:file expect-second &&
        test_cmp file third
 '
diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh
new file mode 100755 (executable)
index 0000000..bd8efaf
--- /dev/null
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+test_description='git rebase - test patch id computation'
+
+. ./test-lib.sh
+
+test_set_prereq NOT_EXPENSIVE
+test -n "$GIT_PATCHID_TIMING_TESTS" && test_set_prereq EXPENSIVE
+test -x /usr/bin/time && test_set_prereq USR_BIN_TIME
+
+count()
+{
+       i=0
+       while test $i -lt $1
+       do
+               echo "$i"
+               i=$(($i+1))
+       done
+}
+
+scramble()
+{
+       i=0
+       while read x
+       do
+               if test $i -ne 0
+               then
+                       echo "$x"
+               fi
+               i=$((($i+1) % 10))
+       done < "$1" > "$1.new"
+       mv -f "$1.new" "$1"
+}
+
+run()
+{
+       echo \$ "$@"
+       /usr/bin/time "$@" >/dev/null
+}
+
+test_expect_success 'setup' '
+       git commit --allow-empty -m initial
+       git tag root
+'
+
+do_tests()
+{
+       pr=$1
+       nlines=$2
+
+       test_expect_success $pr "setup: $nlines lines" "
+               rm -f .gitattributes &&
+               git checkout -q -f master &&
+               git reset --hard root &&
+               count $nlines >file &&
+               git add file &&
+               git commit -q -m initial &&
+               git branch -f other &&
+
+               scramble file &&
+               git add file &&
+               git commit -q -m 'change big file' &&
+
+               git checkout -q other &&
+               : >newfile &&
+               git add newfile &&
+               git commit -q -m 'add small file' &&
+
+               git cherry-pick master >/dev/null 2>&1
+       "
+
+       test_debug "
+               run git diff master^\!
+       "
+
+       test_expect_success $pr 'setup attributes' "
+               echo 'file binary' >.gitattributes
+       "
+
+       test_debug "
+               run git format-patch --stdout master &&
+               run git format-patch --stdout --ignore-if-in-upstream master
+       "
+
+       test_expect_success $pr 'detect upstream patch' "
+               git checkout -q master &&
+               scramble file &&
+               git add file &&
+               git commit -q -m 'change big file again' &&
+               git checkout -q other^{} &&
+               git rebase master &&
+               test_must_fail test -n \"\$(git rev-list master...HEAD~)\"
+       "
+
+       test_expect_success $pr 'do not drop patch' "
+               git branch -f squashed master &&
+               git checkout -q -f squashed &&
+               git reset -q --soft HEAD~2 &&
+               git commit -q -m squashed &&
+               git checkout -q other^{} &&
+               test_must_fail git rebase squashed &&
+               rm -rf .git/rebase-apply
+       "
+}
+
+do_tests NOT_EXPENSIVE 500
+do_tests EXPENSIVE 50000
+
+test_done
index bc7aedd0484ed5ad1115cac0f22943445d658f47..043954422c82d9afbb318bcb7d1aa5685a391c85 100755 (executable)
@@ -81,6 +81,16 @@ test_expect_success 'revert after renaming branch' '
 
 '
 
+test_expect_success 'cherry-pick on stat-dirty working tree' '
+       git clone . copy &&
+       (
+               cd copy &&
+               git checkout initial &&
+               test-chmtime +40 oops &&
+               git cherry-pick added
+       )
+'
+
 test_expect_success 'revert forbidden on dirty working tree' '
 
        echo content >extra_file &&
index f7b3518a32763aa0fbad1a245dad8f0a5d866126..e6a64816efef0e53018c7a56784d1af62602e9d3 100755 (executable)
@@ -23,7 +23,7 @@ test_expect_success 'conflicting merge' '
 test_expect_success 'fixup' '
        echo foo-dev >foo &&
        git add foo && test_tick && git commit -q -m 4 &&
-       git reset --hard HEAD^
+       git reset --hard HEAD^ &&
        echo foo-dev >expect
 '
 
@@ -33,7 +33,7 @@ test_expect_success 'cherry-pick conflict' '
 '
 
 test_expect_success 'reconfigure' '
-       git config rerere.enabled false
+       git config rerere.enabled false &&
        git reset --hard
 '
 
index e17ae712b1078333fa191b97764177016bc01b84..51ca391e472441d2d503e721795d609bfaad1100 100755 (executable)
@@ -95,4 +95,14 @@ test_expect_success 'cherry pick a merge relative to nonexistent parent with --f
        test_must_fail git cherry-pick --ff -m 3 C
 '
 
+test_expect_success 'cherry pick a root commit with --ff' '
+       git reset --hard first -- &&
+       git rm file1 &&
+       echo first >file2 &&
+       git add file2 &&
+       git commit --amend -m "file2" &&
+       git cherry-pick --ff first &&
+       test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1"
+'
+
 test_done
index e25cf8039a310e325c23e85ce5fc845a0f681200..607bf25d8ff7720407c2b15e4808c575b5f36093 100755 (executable)
@@ -38,6 +38,26 @@ test_expect_success 'failed cherry-pick does not advance HEAD' '
        test "$head" = "$newhead"
 '
 
+test_expect_success 'advice from failed cherry-pick' "
+       git checkout -f initial^0 &&
+       git read-tree -u --reset HEAD &&
+       git clean -d -f -f -q -x &&
+
+       git update-index --refresh &&
+       git diff-index --exit-code HEAD &&
+
+       picked=\$(git rev-parse --short picked) &&
+       cat <<-EOF >expected &&
+       error: could not apply \$picked... picked
+       hint: after resolving the conflicts, mark the corrected paths
+       hint: with 'git add <paths>' or 'git rm <paths>'
+       hint: and commit the result with 'git commit -c \$picked'
+       EOF
+       test_must_fail git cherry-pick picked 2>actual &&
+
+       test_cmp expected actual
+"
+
 test_expect_success 'failed cherry-pick produces dirty index' '
 
        git checkout -f initial^0 &&
index f90ed3da3e0cb93b63d32d8002dc6d9bd3596a65..8e09fd0319c95cbd4d30c461f00fee5f52e27cbd 100755 (executable)
@@ -4,6 +4,18 @@ test_description='test cherry-picking many commits'
 
 . ./test-lib.sh
 
+check_head_differs_from() {
+       head=$(git rev-parse --verify HEAD) &&
+       arg=$(git rev-parse --verify "$1") &&
+       test "$head" != "$arg"
+}
+
+check_head_equals() {
+       head=$(git rev-parse --verify HEAD) &&
+       arg=$(git rev-parse --verify "$1") &&
+       test "$head" = "$arg"
+}
+
 test_expect_success setup '
        echo first > file1 &&
        git add file1 &&
@@ -23,13 +35,55 @@ test_expect_success setup '
 '
 
 test_expect_success 'cherry-pick first..fourth works' '
+       cat <<-\EOF >expected &&
+       [master OBJID] second
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       [master OBJID] third
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       [master OBJID] fourth
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       EOF
+
+       git checkout -f master &&
+       git reset --hard first &&
+       test_tick &&
+       git cherry-pick first..fourth >actual &&
+       git diff --quiet other &&
+       git diff --quiet HEAD other &&
+
+       sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
+       test_cmp expected actual.fuzzy &&
+       check_head_differs_from fourth
+'
+
+test_expect_success 'cherry-pick --strategy resolve first..fourth works' '
+       cat <<-\EOF >expected &&
+       Trying simple merge.
+       [master OBJID] second
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       Trying simple merge.
+       [master OBJID] third
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       Trying simple merge.
+       [master OBJID] fourth
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       EOF
+
        git checkout -f master &&
        git reset --hard first &&
        test_tick &&
-       git cherry-pick first..fourth &&
+       git cherry-pick --strategy resolve first..fourth >actual &&
        git diff --quiet other &&
        git diff --quiet HEAD other &&
-       test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify fourth)"
+       sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
+       test_cmp expected actual.fuzzy &&
+       check_head_differs_from fourth
 '
 
 test_expect_success 'cherry-pick --ff first..fourth works' '
@@ -39,7 +93,7 @@ test_expect_success 'cherry-pick --ff first..fourth works' '
        git cherry-pick --ff first..fourth &&
        git diff --quiet other &&
        git diff --quiet HEAD other &&
-       test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify fourth)"
+       check_head_equals fourth
 '
 
 test_expect_success 'cherry-pick -n first..fourth works' '
@@ -89,7 +143,7 @@ test_expect_success 'cherry-pick -3 fourth works' '
        git cherry-pick -3 fourth &&
        git diff --quiet other &&
        git diff --quiet HEAD other &&
-       test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify fourth)"
+       check_head_differs_from fourth
 '
 
 test_expect_success 'cherry-pick --stdin works' '
@@ -99,7 +153,7 @@ test_expect_success 'cherry-pick --stdin works' '
        git rev-list --reverse first..fourth | git cherry-pick --stdin &&
        git diff --quiet other &&
        git diff --quiet HEAD other &&
-       test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify fourth)"
+       check_head_differs_from fourth
 '
 
 test_done
diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh
new file mode 100755 (executable)
index 0000000..948ca1b
--- /dev/null
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+test_description='Test cherry-pick with directory/file conflicts'
+. ./test-lib.sh
+
+test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts' '
+       mkdir a &&
+       >a/f &&
+       git add a &&
+       git commit -m a &&
+
+       mkdir b &&
+       ln -s ../a b/a &&
+       git add b &&
+       git commit -m b &&
+
+       git checkout -b branch &&
+       rm b/a &&
+       mv a b/a &&
+       ln -s b/a a &&
+       git add . &&
+       git commit -m swap &&
+
+       >f1 &&
+       git add f1 &&
+       git commit -m f1
+'
+
+test_expect_success SYMLINKS 'Cherry-pick succeeds with rename across D/F conflicts' '
+       git reset --hard &&
+       git checkout master^0 &&
+       git cherry-pick branch
+'
+
+test_expect_success 'Setup rename with file on one side matching directory name on other' '
+       git checkout --orphan nick-testcase &&
+       git rm -rf . &&
+
+       >empty &&
+       git add empty &&
+       git commit -m "Empty file" &&
+
+       git checkout -b simple &&
+       mv empty file &&
+       mkdir empty &&
+       mv file empty &&
+       git add empty/file &&
+       git commit -m "Empty file under empty dir" &&
+
+       echo content >newfile &&
+       git add newfile &&
+       git commit -m "New file"
+'
+
+test_expect_success 'Cherry-pick succeeds with was_a_dir/file -> was_a_dir (resolve)' '
+       git reset --hard &&
+       git checkout -q nick-testcase^0 &&
+       git cherry-pick --strategy=resolve simple
+'
+
+test_expect_success 'Cherry-pick succeeds with was_a_dir/file -> was_a_dir (recursive)' '
+       git reset --hard &&
+       git checkout -q nick-testcase^0 &&
+       git cherry-pick --strategy=recursive simple
+'
+
+test_expect_success 'Setup rename with file on one side matching different dirname on other' '
+       git reset --hard &&
+       git checkout --orphan mergeme &&
+       git rm -rf . &&
+
+       mkdir sub &&
+       mkdir othersub &&
+       echo content > sub/file &&
+       echo foo > othersub/whatever &&
+       git add -A &&
+       git commit -m "Common commmit" &&
+
+       git rm -rf othersub &&
+       git mv sub/file othersub &&
+       git commit -m "Commit to merge" &&
+
+       git checkout -b newhead mergeme~1 &&
+       >independent-change &&
+       git add independent-change &&
+       git commit -m "Completely unrelated change"
+'
+
+test_expect_success 'Cherry-pick with rename to different D/F conflict succeeds (resolve)' '
+       git reset --hard &&
+       git checkout -q newhead^0 &&
+       git cherry-pick --strategy=resolve mergeme
+'
+
+test_expect_success 'Cherry-pick with rename to different D/F conflict succeeds (recursive)' '
+       git reset --hard &&
+       git checkout -q newhead^0 &&
+       git cherry-pick --strategy=recursive mergeme
+'
+
+test_done
index b514cbb60665fef9d5cd67b8f7bcd8e8a3c2d0a5..cd093bd34730a3dba6eec7719d3b0170e517fb5e 100755 (executable)
@@ -28,22 +28,6 @@ embedded' &&
      git commit -m 'add files with tabs and newlines'
 "
 
-# Determine rm behavior
-# Later we will try removing an unremovable path to make sure
-# git rm barfs, but if the test is run as root that cannot be
-# arranged.
-: >test-file
-chmod a-w .
-rm -f test-file 2>/dev/null
-if test -f test-file
-then
-       test_set_prereq RO_DIR
-else
-       skip_all='skipping removal failure test (perhaps running as root?)'
-fi
-chmod 775 .
-rm -f test-file
-
 test_expect_success \
     'Pre-check that foo exists and is in index before git rm foo' \
     '[ -f foo ] && git ls-files --error-unmatch foo'
@@ -112,7 +96,7 @@ test_expect_success FUNNYNAMES \
     "git rm -f 'space embedded' 'tab   embedded' 'newline
 embedded'"
 
-test_expect_success RO_DIR 'Test that "git rm -f" fails if its rm fails' '
+test_expect_success SANITY 'Test that "git rm -f" fails if its rm fails' '
        chmod a-w . &&
        test_must_fail git rm -f baz &&
        chmod 775 .
index 7d7140db380225bfffd3b64c125e2599b6be77c2..ec7108358e7b7f54f0b5e7530c8368e4a56576af 100755 (executable)
@@ -179,7 +179,7 @@ test_expect_success 'git add --refresh' '
        test -z "`git diff-index HEAD -- foo`"
 '
 
-test_expect_success POSIXPERM 'git add should fail atomically upon an unreadable file' '
+test_expect_success POSIXPERM,SANITY 'git add should fail atomically upon an unreadable file' '
        git reset --hard &&
        date >foo1 &&
        date >foo2 &&
@@ -190,7 +190,7 @@ test_expect_success POSIXPERM 'git add should fail atomically upon an unreadable
 
 rm -f foo2
 
-test_expect_success POSIXPERM 'git add --ignore-errors' '
+test_expect_success POSIXPERM,SANITY 'git add --ignore-errors' '
        git reset --hard &&
        date >foo1 &&
        date >foo2 &&
@@ -201,7 +201,7 @@ test_expect_success POSIXPERM 'git add --ignore-errors' '
 
 rm -f foo2
 
-test_expect_success POSIXPERM 'git add (add.ignore-errors)' '
+test_expect_success POSIXPERM,SANITY 'git add (add.ignore-errors)' '
        git config add.ignore-errors 1 &&
        git reset --hard &&
        date >foo1 &&
@@ -212,7 +212,7 @@ test_expect_success POSIXPERM 'git add (add.ignore-errors)' '
 '
 rm -f foo2
 
-test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' '
+test_expect_success POSIXPERM,SANITY 'git add (add.ignore-errors = false)' '
        git config add.ignore-errors 0 &&
        git reset --hard &&
        date >foo1 &&
@@ -223,7 +223,7 @@ test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' '
 '
 rm -f foo2
 
-test_expect_success POSIXPERM '--no-ignore-errors overrides config' '
+test_expect_success POSIXPERM,SANITY '--no-ignore-errors overrides config' '
        git config add.ignore-errors 1 &&
        git reset --hard &&
        date >foo1 &&
index 7ad8465f8f89656bf0ce57feb45e12bc9cea5d65..d6327e7c74cd0cd3d433a71b1e9bb89eaf088ed5 100755 (executable)
@@ -2,22 +2,20 @@
 
 test_description='add -i basic tests'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
-if ! test_have_prereq PERL; then
-       skip_all='skipping git add -i tests, perl not available'
-       test_done
-fi
-
-test_expect_success 'setup (initial)' '
+test_expect_success PERL 'setup (initial)' '
        echo content >file &&
        git add file &&
        echo more >>file &&
        echo lines >>file
 '
-test_expect_success 'status works (initial)' '
+test_expect_success PERL 'status works (initial)' '
        git add -i </dev/null >output &&
        grep "+1/-0 *+2/-0 file" output
 '
+
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 new file mode 100644
 index 0000000..d95f3ad
@@ -26,19 +24,21 @@ index 0000000..d95f3ad
 @@ -0,0 +1 @@
 +content
 EOF
-test_expect_success 'diff works (initial)' '
+'
+
+test_expect_success PERL 'diff works (initial)' '
        (echo d; echo 1) | git add -i >output &&
        sed -ne "/new file/,/content/p" <output >diff &&
        test_cmp expected diff
 '
-test_expect_success 'revert works (initial)' '
+test_expect_success PERL 'revert works (initial)' '
        git add file &&
        (echo r; echo 1) | git add -i &&
        git ls-files >output &&
        ! grep . output
 '
 
-test_expect_success 'setup (commit)' '
+test_expect_success PERL 'setup (commit)' '
        echo baseline >file &&
        git add file &&
        git commit -m commit &&
@@ -47,10 +47,12 @@ test_expect_success 'setup (commit)' '
        echo more >>file &&
        echo lines >>file
 '
-test_expect_success 'status works (commit)' '
+test_expect_success PERL 'status works (commit)' '
        git add -i </dev/null >output &&
        grep "+1/-0 *+2/-0 file" output
 '
+
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 index 180b47c..b6f2c08 100644
 --- a/file
@@ -59,60 +61,79 @@ index 180b47c..b6f2c08 100644
  baseline
 +content
 EOF
-test_expect_success 'diff works (commit)' '
+'
+
+test_expect_success PERL 'diff works (commit)' '
        (echo d; echo 1) | git add -i >output &&
        sed -ne "/^index/,/content/p" <output >diff &&
        test_cmp expected diff
 '
-test_expect_success 'revert works (commit)' '
+test_expect_success PERL 'revert works (commit)' '
        git add file &&
        (echo r; echo 1) | git add -i &&
        git add -i </dev/null >output &&
        grep "unchanged *+3/-0 file" output
 '
 
+
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 EOF
-cat >fake_editor.sh <<EOF
-EOF
-chmod a+x fake_editor.sh
-test_set_editor "$(pwd)/fake_editor.sh"
-test_expect_success 'dummy edit works' '
+'
+
+test_expect_success PERL 'setup fake editor' '
+       cat >fake_editor.sh <<EOF
+       EOF
+       chmod a+x fake_editor.sh &&
+       test_set_editor "$(pwd)/fake_editor.sh" &&
+'
+
+test_expect_success PERL 'dummy edit works' '
        (echo e; echo a) | git add -p &&
        git diff > diff &&
        test_cmp expected diff
 '
 
+test_expect_success PERL 'setup patch' '
 cat >patch <<EOF
 @@ -1,1 +1,4 @@
  this
 +patch
--doesn't
+-does not
  apply
 EOF
-echo "#!$SHELL_PATH" >fake_editor.sh
-cat >>fake_editor.sh <<\EOF
+'
+
+test_expect_success PERL 'setup fake editor' '
+       echo "#!$SHELL_PATH" >fake_editor.sh &&
+       cat >>fake_editor.sh <<\EOF &&
 mv -f "$1" oldpatch &&
 mv -f patch "$1"
 EOF
-chmod a+x fake_editor.sh
-test_set_editor "$(pwd)/fake_editor.sh"
-test_expect_success 'bad edit rejected' '
+       chmod a+x fake_editor.sh &&
+       test_set_editor "$(pwd)/fake_editor.sh"
+'
+
+test_expect_success PERL 'bad edit rejected' '
        git reset &&
        (echo e; echo n; echo d) | git add -p >output &&
        grep "hunk does not apply" output
 '
 
+test_expect_success PERL 'setup patch' '
 cat >patch <<EOF
 this patch
 is garbage
 EOF
-test_expect_success 'garbage edit rejected' '
+'
+
+test_expect_success PERL 'garbage edit rejected' '
        git reset &&
        (echo e; echo n; echo d) | git add -p >output &&
        grep "hunk does not apply" output
 '
 
+test_expect_success PERL 'setup patch' '
 cat >patch <<EOF
 @@ -1,0 +1,0 @@
  baseline
@@ -120,6 +141,9 @@ cat >patch <<EOF
 +newcontent
 +lines
 EOF
+'
+
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 diff --git a/file b/file
 index b5dd6c9..f910ae9 100644
@@ -132,13 +156,15 @@ index b5dd6c9..f910ae9 100644
 +more
  lines
 EOF
-test_expect_success 'real edit works' '
+'
+
+test_expect_success PERL 'real edit works' '
        (echo e; echo n; echo d) | git add -p &&
        git diff >output &&
        test_cmp expected output
 '
 
-test_expect_success 'skip files similarly as commit -a' '
+test_expect_success PERL 'skip files similarly as commit -a' '
        git reset &&
        echo file >.gitignore &&
        echo changed >file &&
@@ -152,14 +178,7 @@ test_expect_success 'skip files similarly as commit -a' '
 '
 rm -f .gitignore
 
-if test "$(git config --bool core.filemode)" = false
-then
-       say '# skipping filemode tests (filesystem does not properly support modes)'
-else
-       test_set_prereq FILEMODE
-fi
-
-test_expect_success FILEMODE 'patch does not affect mode' '
+test_expect_success PERL,FILEMODE 'patch does not affect mode' '
        git reset --hard &&
        echo content >>file &&
        chmod +x file &&
@@ -168,7 +187,7 @@ test_expect_success FILEMODE 'patch does not affect mode' '
        git diff file | grep "new mode"
 '
 
-test_expect_success FILEMODE 'stage mode but not hunk' '
+test_expect_success PERL,FILEMODE 'stage mode but not hunk' '
        git reset --hard &&
        echo content >>file &&
        chmod +x file &&
@@ -178,7 +197,7 @@ test_expect_success FILEMODE 'stage mode but not hunk' '
 '
 
 
-test_expect_success FILEMODE 'stage mode and hunk' '
+test_expect_success PERL,FILEMODE 'stage mode and hunk' '
        git reset --hard &&
        echo content >>file &&
        chmod +x file &&
@@ -190,13 +209,14 @@ test_expect_success FILEMODE 'stage mode and hunk' '
 
 # end of tests disabled when filemode is not usable
 
-test_expect_success 'setup again' '
+test_expect_success PERL '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
+test_expect_success PERL 'setup patch' '
 cat >patch <<EOF
 index 180b47c..b6f2c08 100644
 --- a/file
@@ -207,7 +227,10 @@ index 180b47c..b6f2c08 100644
  content
 +lastline
 EOF
+'
+
 # Expected output, similar to the patch but w/ diff at the top
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 diff --git a/file b/file
 index b6f2c08..61b9053 100755
@@ -219,8 +242,10 @@ index b6f2c08..61b9053 100755
  content
 +lastline
 EOF
+'
+
 # Test splitting the first patch, then adding both
-test_expect_success 'add first line works' '
+test_expect_success PERL 'add first line works' '
        git commit -am "clear local changes" &&
        git apply patch &&
        (echo s; echo y; echo y) | git add -p file &&
@@ -228,6 +253,7 @@ test_expect_success 'add first line works' '
        test_cmp expected diff
 '
 
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 diff --git a/non-empty b/non-empty
 deleted file mode 100644
@@ -237,7 +263,9 @@ index d95f3ad..0000000
 @@ -1 +0,0 @@
 -content
 EOF
-test_expect_success 'deleting a non-empty file' '
+'
+
+test_expect_success PERL 'deleting a non-empty file' '
        git reset --hard &&
        echo content >non-empty &&
        git add non-empty &&
@@ -248,13 +276,15 @@ test_expect_success 'deleting a non-empty file' '
        test_cmp expected diff
 '
 
+test_expect_success PERL 'setup expected' '
 cat >expected <<EOF
 diff --git a/empty b/empty
 deleted file mode 100644
 index e69de29..0000000
 EOF
+'
 
-test_expect_success 'deleting an empty file' '
+test_expect_success PERL 'deleting an empty file' '
        git reset --hard &&
        > empty &&
        git add empty &&
index 256c4c970145aa9f59e58ee1b0da4c6281b6d9e5..c06a5ee7660c3fd7ca15860cfb761b2c4d953e08 100755 (executable)
@@ -133,4 +133,33 @@ do
        '
 done
 
+test_commit_autosquash_flags () {
+       H=$1
+       flag=$2
+       test_expect_success "commit --$flag with $H encoding" '
+               git config i18n.commitencoding $H &&
+               git checkout -b $H-$flag C0 &&
+               echo $H >>F &&
+               git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt &&
+               test_tick &&
+               echo intermediate stuff >>G &&
+               git add G &&
+               git commit -a -m "intermediate commit" &&
+               test_tick &&
+               echo $H $flag >>F &&
+               git commit -a --$flag HEAD~1 $3 &&
+               E=$(git cat-file commit '$H-$flag' |
+                       sed -ne "s/^encoding //p") &&
+               test "z$E" = "z$H" &&
+               git config --unset-all i18n.commitencoding &&
+               git rebase --autosquash -i HEAD^^^ &&
+               git log --oneline >actual &&
+               test 3 = $(wc -l <actual)
+       '
+}
+
+test_commit_autosquash_flags eucJP fixup
+
+test_commit_autosquash_flags ISO-2022-JP squash '-m "squash message"'
+
 test_done
index 147e634cd65b616ea6c8059897f191b761996c6f..da82b655b3b33520b2c39d3992bc40368bf652be 100755 (executable)
@@ -15,11 +15,13 @@ LF='
 DQ='"'
 
 echo foo 2>/dev/null > "Name and an${HT}HT"
-test -f "Name and an${HT}HT" || {
-       # since FAT/NTFS does not allow tabs in filenames, skip this test
-       skip_all='Your filesystem does not allow tabs in filenames, test skipped.'
-       test_done
-}
+if ! test -f "Name and an${HT}HT"
+then
+       # FAT/NTFS does not allow tabs in filenames
+       say 'Your filesystem does not allow tabs in filenames'
+else
+       test_set_prereq TABS_IN_FILENAMES
+fi
 
 for_each_name () {
        for name in \
@@ -31,21 +33,22 @@ for_each_name () {
        done
 }
 
-test_expect_success setup '
+test_expect_success TABS_IN_FILENAMES 'setup' '
 
        mkdir "$FN" &&
-       for_each_name "echo initial >\"\$name\""
+       for_each_name "echo initial >\"\$name\"" &&
        git add . &&
        git commit -q -m Initial &&
 
        for_each_name "echo second >\"\$name\"" &&
-       git commit -a -m Second
+       git commit -a -m Second &&
 
        for_each_name "echo modified >\"\$name\""
 
 '
 
-cat >expect.quoted <<\EOF
+test_expect_success TABS_IN_FILENAMES 'setup expected files' '
+cat >expect.quoted <<\EOF &&
 Name
 "Name and a\nLF"
 "Name and an\tHT"
@@ -72,75 +75,76 @@ With SP in it
 濱野/file
 濱野純
 EOF
+'
 
-test_expect_success 'check fully quoted output from ls-files' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-files' '
 
        git ls-files >current && test_cmp expect.quoted current
 
 '
 
-test_expect_success 'check fully quoted output from diff-files' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-files' '
 
        git diff --name-only >current &&
        test_cmp expect.quoted current
 
 '
 
-test_expect_success 'check fully quoted output from diff-index' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-index' '
 
        git diff --name-only HEAD >current &&
        test_cmp expect.quoted current
 
 '
 
-test_expect_success 'check fully quoted output from diff-tree' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-tree' '
 
        git diff --name-only HEAD^ HEAD >current &&
        test_cmp expect.quoted current
 
 '
 
-test_expect_success 'check fully quoted output from ls-tree' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-tree' '
 
        git ls-tree --name-only -r HEAD >current &&
        test_cmp expect.quoted current
 
 '
 
-test_expect_success 'setting core.quotepath' '
+test_expect_success TABS_IN_FILENAMES 'setting core.quotepath' '
 
        git config --bool core.quotepath false
 
 '
 
-test_expect_success 'check fully quoted output from ls-files' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-files' '
 
        git ls-files >current && test_cmp expect.raw current
 
 '
 
-test_expect_success 'check fully quoted output from diff-files' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-files' '
 
        git diff --name-only >current &&
        test_cmp expect.raw current
 
 '
 
-test_expect_success 'check fully quoted output from diff-index' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-index' '
 
        git diff --name-only HEAD >current &&
        test_cmp expect.raw current
 
 '
 
-test_expect_success 'check fully quoted output from diff-tree' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-tree' '
 
        git diff --name-only HEAD^ HEAD >current &&
        test_cmp expect.raw current
 
 '
 
-test_expect_success 'check fully quoted output from ls-tree' '
+test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-tree' '
 
        git ls-tree --name-only -r HEAD >current &&
        test_cmp expect.raw current
index 62e208aadd592ddaa5c519cf328e636d948221c0..6fd560ccf10db5d016a4f1dde9e5eacca70e6f5b 100755 (executable)
@@ -69,9 +69,10 @@ test_expect_success 'apply stashed changes (including index)' '
 test_expect_success 'unstashing in a subdirectory' '
        git reset --hard HEAD &&
        mkdir subdir &&
-       cd subdir &&
-       git stash apply &&
-       cd ..
+       (
+               cd subdir &&
+               git stash apply
+       )
 '
 
 test_expect_success 'drop top stash' '
@@ -156,7 +157,7 @@ EOF
 
 test_expect_success 'stash branch' '
        echo foo > file &&
-       git commit file -m first
+       git commit file -m first &&
        echo bar > file &&
        echo bar2 > file2 &&
        git add file2 &&
@@ -254,7 +255,7 @@ test_expect_success 'stash rm and ignore' '
        echo file >.gitignore &&
        git stash save "rm and ignore" &&
        test bar = "$(cat file)" &&
-       test file = "$(cat .gitignore)"
+       test file = "$(cat .gitignore)" &&
        git stash apply &&
        ! test -r file &&
        test file = "$(cat .gitignore)"
@@ -267,7 +268,7 @@ test_expect_success 'stash rm and ignore (stage .gitignore)' '
        git add .gitignore &&
        git stash save "rm and ignore (stage .gitignore)" &&
        test bar = "$(cat file)" &&
-       ! test -r .gitignore
+       ! test -r .gitignore &&
        git stash apply &&
        ! test -r file &&
        test file = "$(cat .gitignore)"
@@ -378,4 +379,181 @@ test_expect_failure 'stash file to directory' '
        test foo = "$(cat file/file)"
 '
 
+test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD" &&
+       git reset --hard &&
+       echo foo >> file &&
+       STASH_ID=$(git stash create) &&
+       git reset --hard &&
+       git stash branch stash-branch ${STASH_ID} &&
+       test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" &&
+       test $(git ls-files --modified | wc -l) -eq 1
+'
+
+test_expect_success 'stash branch - stashes on stack, stash-like argument' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD" &&
+       git reset --hard &&
+       echo foo >> file &&
+       git stash &&
+       test_when_finished "git stash drop" &&
+       echo bar >> file &&
+       STASH_ID=$(git stash create) &&
+       git reset --hard &&
+       git stash branch stash-branch ${STASH_ID} &&
+       test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" &&
+       test $(git ls-files --modified | wc -l) -eq 1
+'
+
+test_expect_success 'stash show - stashes on stack, stash-like argument' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD" &&
+       git reset --hard &&
+       echo foo >> file &&
+       git stash &&
+       test_when_finished "git stash drop" &&
+       echo bar >> file &&
+       STASH_ID=$(git stash create) &&
+       git reset --hard &&
+       cat >expected <<-EOF &&
+        file |    1 +
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       EOF
+       git stash show ${STASH_ID} >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'stash show -p - stashes on stack, stash-like argument' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD" &&
+       git reset --hard &&
+       echo foo >> file &&
+       git stash &&
+       test_when_finished "git stash drop" &&
+       echo bar >> file &&
+       STASH_ID=$(git stash create) &&
+       git reset --hard &&
+       cat >expected <<-EOF &&
+       diff --git a/file b/file
+       index 7601807..935fbd3 100644
+       --- a/file
+       +++ b/file
+       @@ -1 +1,2 @@
+        baz
+       +bar
+       EOF
+       git stash show -p ${STASH_ID} >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'stash show - no stashes on stack, stash-like argument' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD" &&
+       git reset --hard &&
+       echo foo >> file &&
+       STASH_ID=$(git stash create) &&
+       git reset --hard &&
+       cat >expected <<-EOF &&
+        file |    1 +
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       EOF
+       git stash show ${STASH_ID} >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD" &&
+       git reset --hard &&
+       echo foo >> file &&
+       STASH_ID=$(git stash create) &&
+       git reset --hard &&
+       cat >expected <<-EOF &&
+       diff --git a/file b/file
+       index 7601807..71b52c4 100644
+       --- a/file
+       +++ b/file
+       @@ -1 +1,2 @@
+        baz
+       +foo
+       EOF
+       git stash show -p ${STASH_ID} >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'stash drop - fail early if specified stash is not a stash reference' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD && git stash clear" &&
+       git reset --hard &&
+       echo foo > file &&
+       git stash &&
+       echo bar > file &&
+       git stash &&
+       test_must_fail git stash drop $(git rev-parse stash@{0}) &&
+       git stash pop &&
+       test bar = "$(cat file)" &&
+       git reset --hard HEAD
+'
+
+test_expect_success 'stash pop - fail early if specified stash is not a stash reference' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD && git stash clear" &&
+       git reset --hard &&
+       echo foo > file &&
+       git stash &&
+       echo bar > file &&
+       git stash &&
+       test_must_fail git stash pop $(git rev-parse stash@{0}) &&
+       git stash pop &&
+       test bar = "$(cat file)" &&
+       git reset --hard HEAD
+'
+
+test_expect_success 'ref with non-existant reflog' '
+       git stash clear &&
+       echo bar5 > file &&
+       echo bar6 > file2 &&
+       git add file2 &&
+       git stash &&
+       ! "git rev-parse --quiet --verify does-not-exist" &&
+       test_must_fail git stash drop does-not-exist &&
+       test_must_fail git stash drop does-not-exist@{0} &&
+       test_must_fail git stash pop does-not-exist &&
+       test_must_fail git stash pop does-not-exist@{0} &&
+       test_must_fail git stash apply does-not-exist &&
+       test_must_fail git stash apply does-not-exist@{0} &&
+       test_must_fail git stash show does-not-exist &&
+       test_must_fail git stash show does-not-exist@{0} &&
+       test_must_fail git stash branch tmp does-not-exist &&
+       test_must_fail git stash branch tmp does-not-exist@{0} &&
+       git stash drop
+'
+
+test_expect_success 'invalid ref of the form stash@{n}, n >= N' '
+       git stash clear &&
+       test_must_fail git stash drop stash@{0} &&
+       echo bar5 > file &&
+       echo bar6 > file2 &&
+       git add file2 &&
+       git stash &&
+       test_must_fail git drop stash@{1} &&
+       test_must_fail git pop stash@{1} &&
+       test_must_fail git apply stash@{1} &&
+       test_must_fail git show stash@{1} &&
+       test_must_fail git branch tmp stash@{1} &&
+       git stash drop
+'
+
+test_expect_success 'stash branch should not drop the stash if the branch exists' '
+       git stash clear &&
+       echo foo >file &&
+       git add file &&
+       git commit -m initial &&
+       echo bar >file &&
+       git stash &&
+       test_must_fail git stash branch master stash@{0} &&
+       git rev-parse stash@{0} --
+'
+
 test_done
index f37e3bc6ec50e680af372a1c1065413aca063bcc..1e7193ac0bc650868f187d3103be32c3004a95b2 100755 (executable)
@@ -3,7 +3,7 @@
 test_description='git checkout --patch'
 . ./lib-patch-mode.sh
 
-test_expect_success 'setup' '
+test_expect_success PERL 'setup' '
        mkdir dir &&
        echo parent > dir/foo &&
        echo dummy > bar &&
@@ -19,14 +19,14 @@ test_expect_success 'setup' '
 
 # 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
+test_expect_success PERL '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' '
+test_expect_success PERL 'git stash -p' '
        (echo n; echo y) | git stash save -p &&
        verify_state dir/foo head index &&
        verify_saved_state bar &&
@@ -36,7 +36,7 @@ test_expect_success 'git stash -p' '
        verify_state bar dummy dummy
 '
 
-test_expect_success 'git stash -p --no-keep-index' '
+test_expect_success PERL '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 &&
@@ -48,7 +48,7 @@ test_expect_success 'git stash -p --no-keep-index' '
        verify_state bar dummy bar_index
 '
 
-test_expect_success 'none of this moved HEAD' '
+test_expect_success PERL 'none of this moved HEAD' '
        verify_saved_head
 '
 
index 73441a516572dcf826582c04f3fe9d3ad0c1a88c..9fb8ca06a84b3f3e60f466cc33bc2de786a9fc90 100755 (executable)
@@ -205,8 +205,8 @@ test_expect_success \
     'rm -fr Z [A-Z][A-Z] &&
      git read-tree $tree_A &&
      git checkout-index -f -a &&
-     git read-tree --reset $tree_O || return 1
-     git update-index --refresh >/dev/null ;# this can exit non-zero
+     git read-tree --reset $tree_O &&
+     test_must_fail git update-index --refresh -q &&
      git diff-files >.test-a &&
      cmp_diff_files_output .test-a .test-recursive-OA'
 
@@ -215,8 +215,8 @@ test_expect_success \
     'rm -fr Z [A-Z][A-Z] &&
      git read-tree $tree_B &&
      git checkout-index -f -a &&
-     git read-tree --reset $tree_O || return 1
-     git update-index --refresh >/dev/null ;# this can exit non-zero
+     git read-tree --reset $tree_O &&
+     test_must_fail git update-index --refresh -q &&
      git diff-files >.test-a &&
      cmp_diff_files_output .test-a .test-recursive-OB'
 
@@ -225,8 +225,8 @@ test_expect_success \
     'rm -fr Z [A-Z][A-Z] &&
      git read-tree $tree_B &&
      git checkout-index -f -a &&
-     git read-tree --reset $tree_A || return 1
-     git update-index --refresh >/dev/null ;# this can exit non-zero
+     git read-tree --reset $tree_A &&
+     test_must_fail git update-index --refresh -q &&
      git diff-files >.test-a &&
      cmp_diff_files_output .test-a .test-recursive-AB'
 
index 1a09e8db403adc9c65859b0a369ce9d06e00e6f4..92a65f4852b8eca089fb9de617e0cdc8b55e1475 100755 (executable)
@@ -12,13 +12,7 @@ by an edit for them.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-       skip_all='Symbolic links not supported, skipping tests.'
-       test_done
-fi
-
-test_expect_success \
+test_expect_success SYMLINKS \
     'prepare reference tree' \
     'echo xyzzy | tr -d '\\\\'012 >yomin &&
      ln -s xyzzy frotz &&
@@ -26,7 +20,7 @@ test_expect_success \
     tree=$(git write-tree) &&
     echo $tree'
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'prepare work tree' \
     'mv frotz rezrov &&
      rm -f yomin &&
@@ -40,8 +34,9 @@ test_expect_success \
 # rezrov and nitfol are rename/copy of frotz and bozbar should be
 # a new creation.
 
-GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current
-cat >expected <<\EOF
+test_expect_success SYMLINKS 'setup diff output' "
+    GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current &&
+    cat >expected <<\EOF
 diff --git a/bozbar b/bozbar
 new file mode 120000
 --- /dev/null
@@ -65,8 +60,9 @@ deleted file mode 100644
 -xyzzy
 \ No newline at end of file
 EOF
+"
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'validate diff output' \
     'compare_diff_patch current expected'
 
index e19ca65885a1e49916f68eee4abbe65e98227c50..d79d9e1e71ec38b4a82f1bf36dbbff5c3247a4d4 100755 (executable)
@@ -155,7 +155,7 @@ test_expect_success \
      git checkout-index -f -u -a &&
      sed -e "s/git/GIT/" file0 >file1 &&
      sed -e "s/git/GET/" file0 >file2 &&
-     rm -f file0
+     rm -f file0 &&
      git update-index --add --remove file0 file1 file2'
 
 test_expect_success \
index 94df7ae53a0ef47c0ef10ca6b3215ffdf38fa399..fbc8cd8f05f4debeb30b935c1b1db86e94e49f0e 100755 (executable)
@@ -70,4 +70,36 @@ test_expect_success 'diff-tree pathspec' '
        test_cmp expected current
 '
 
+EMPTY_TREE=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+
+test_expect_success 'diff-tree with wildcard shows dir also matches' '
+       git diff-tree --name-only $EMPTY_TREE $tree -- "f*" >result &&
+       echo file0 >expected &&
+       test_cmp expected result
+'
+
+test_expect_success 'diff-tree -r with wildcard' '
+       git diff-tree -r --name-only $EMPTY_TREE $tree -- "*file1" >result &&
+       echo path1/file1 >expected &&
+       test_cmp expected result
+'
+
+test_expect_success 'diff-tree with wildcard shows dir also matches' '
+       git diff-tree --name-only $tree $tree2 -- "path1/f*" >result &&
+       echo path1 >expected &&
+       test_cmp expected result
+'
+
+test_expect_success 'diff-tree -r with wildcard from beginning' '
+       git diff-tree -r --name-only $tree $tree2 -- "path1/*file1" >result &&
+       echo path1/file1 >expected &&
+       test_cmp expected result
+'
+
+test_expect_success 'diff-tree -r with wildcard' '
+       git diff-tree -r --name-only $tree $tree2 -- "path1/f*" >result &&
+       echo path1/file1 >expected &&
+       test_cmp expected result
+'
+
 test_done
index 918a21a2f41caf2e8ed6bb6120631ff0e4ccb0fc..408a19c4c258da9b7860edffea314b181e273edf 100755 (executable)
@@ -9,12 +9,6 @@ test_description='Test diff of symlinks.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-       skip_all='Symbolic links not supported, skipping tests.'
-       test_done
-fi
-
 cat > expected << EOF
 diff --git a/frotz b/frotz
 new file mode 120000
@@ -26,7 +20,7 @@ index 0000000..7c465af
 \ No newline at end of file
 EOF
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'diff new symlink' \
     'ln -s xyzzy frotz &&
     git update-index &&
@@ -35,7 +29,7 @@ test_expect_success \
     GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree > current &&
     compare_diff_patch current expected'
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'diff unchanged symlink' \
     'tree=$(git write-tree) &&
     git update-index frotz &&
@@ -52,7 +46,7 @@ index 7c465af..0000000
 \ No newline at end of file
 EOF
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'diff removed symlink' \
     'mv frotz frotz2 &&
     git diff-index -M -p $tree > current &&
@@ -62,7 +56,7 @@ cat > expected << EOF
 diff --git a/frotz b/frotz
 EOF
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'diff identical, but newly created symlink' \
     'ln -s xyzzy frotz &&
     git diff-index -M -p $tree > current &&
@@ -80,18 +74,44 @@ index 7c465af..df1db54 120000
 \ No newline at end of file
 EOF
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'diff different symlink' \
     'rm frotz &&
     ln -s yxyyz frotz &&
     git diff-index -M -p $tree > current &&
     compare_diff_patch current expected'
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'diff symlinks with non-existing targets' \
     'ln -s narf pinky &&
     ln -s take\ over brain &&
     test_must_fail git diff --no-index pinky brain > output 2> output.err &&
     grep narf output &&
     ! grep error output.err'
+
+test_expect_success SYMLINKS 'setup symlinks with attributes' '
+       echo "*.bin diff=bin" >>.gitattributes &&
+       echo content >file.bin &&
+       ln -s file.bin link.bin &&
+       git add -N file.bin link.bin
+'
+
+cat >expect <<'EOF'
+diff --git a/file.bin b/file.bin
+index e69de29..d95f3ad 100644
+Binary files a/file.bin and b/file.bin differ
+diff --git a/link.bin b/link.bin
+index e69de29..dce41ec 120000
+--- a/link.bin
++++ b/link.bin
+@@ -0,0 +1 @@
++file.bin
+\ No newline at end of file
+EOF
+test_expect_success SYMLINKS 'symlinks do not respect userdiff config by path' '
+       git config diff.bin.binary true &&
+       git diff file.bin link.bin >actual &&
+       test_cmp expect actual
+'
+
 test_done
index bc46563afc607e6fba9acbd2773d88207c878981..05ec0628322ccc103343515feb70d27a9a0e6c76 100755 (executable)
@@ -77,10 +77,6 @@ test_expect_success 'apply binary patch' \
         tree1=`git write-tree` &&
         test "$tree1" = "$tree0"'
 
-nul_to_q() {
-       perl -pe 'y/\000/Q/'
-}
-
 test_expect_success 'diff --no-index with binary creation' '
        echo Q | q_to_nul >binary &&
        (: hide error code from diff, which just indicates differences
index dae635851666a7f5ccf685b89d64f31d5dbdd2fd..9a665205884fcdb1c5888558d6edf960e335e013 100755 (executable)
@@ -208,7 +208,11 @@ log -p --first-parent master
 log -m -p --first-parent master
 log -m -p master
 log -SF master
+log -S F master
 log -SF -p master
+log -GF master
+log -GF -p master
+log -GF -p --pickaxe-all master
 log --decorate --all
 log --decorate=full --all
 
@@ -282,4 +286,8 @@ diff master master^ side
 diff --dirstat master~1 master~2
 EOF
 
+test_expect_success 'log -S requires an argument' '
+       test_must_fail git log -S
+'
+
 test_done
diff --git a/t/t4013/diff.log_-GF_-p_--pickaxe-all_master b/t/t4013/diff.log_-GF_-p_--pickaxe-all_master
new file mode 100644 (file)
index 0000000..d36f880
--- /dev/null
@@ -0,0 +1,27 @@
+$ git log -GF -p --pickaxe-all master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+$
diff --git a/t/t4013/diff.log_-GF_-p_master b/t/t4013/diff.log_-GF_-p_master
new file mode 100644 (file)
index 0000000..9d93f2c
--- /dev/null
@@ -0,0 +1,18 @@
+$ git log -GF -p master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+$
diff --git a/t/t4013/diff.log_-GF_master b/t/t4013/diff.log_-GF_master
new file mode 100644 (file)
index 0000000..4c6708d
--- /dev/null
@@ -0,0 +1,7 @@
+$ git log -GF master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+$
diff --git a/t/t4013/diff.log_-S_F_master b/t/t4013/diff.log_-S_F_master
new file mode 100644 (file)
index 0000000..978d2b4
--- /dev/null
@@ -0,0 +1,7 @@
+$ git log -S F master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+$
index f87434b9f8e0d520813389e6ea7c94f74222767b..027c13d52cd701ba28e3c5e29c5431acfccdad73 100755 (executable)
@@ -6,30 +6,36 @@
 test_description='various format-patch tests'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
 
 test_expect_success setup '
 
        for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file &&
        cat file >elif &&
        git add file elif &&
+       test_tick &&
        git commit -m Initial &&
        git checkout -b side &&
 
        for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
        test_chmod +x elif &&
+       test_tick &&
        git commit -m "Side changes #1" &&
 
        for i in D E F; do echo "$i"; done >>file &&
        git update-index file &&
+       test_tick &&
        git commit -m "Side changes #2" &&
        git tag C2 &&
 
        for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file &&
        git update-index file &&
+       test_tick &&
        git commit -m "Side changes #3 with \\n backslash-n in it." &&
 
        git checkout master &&
        git diff-tree -p C2 | git apply --index &&
+       test_tick &&
        git commit -m "Master accepts moral equivalent of #2"
 
 '
@@ -51,6 +57,22 @@ test_expect_success "format-patch --ignore-if-in-upstream" '
 
 '
 
+test_expect_success "format-patch doesn't consider merge commits" '
+
+       git checkout -b slave master &&
+       echo "Another line" >>file &&
+       test_tick &&
+       git commit -am "Slave change #1" &&
+       echo "Yet another line" >>file &&
+       test_tick &&
+       git commit -am "Slave change #2" &&
+       git checkout -b merger master &&
+       test_tick &&
+       git merge --no-ff slave &&
+       cnt=`git format-patch -3 --stdout | grep "^From " | wc -l` &&
+       test $cnt = 3
+'
+
 test_expect_success "format-patch result applies" '
 
        git checkout -b rebuild-0 master &&
@@ -665,4 +687,26 @@ test_expect_success 'format-patch --signature="" supresses signatures' '
        ! grep "^-- \$" output
 '
 
+test_expect_success TTY 'format-patch --stdout paginates' '
+       rm -f pager_used &&
+       (
+               GIT_PAGER="wc >pager_used" &&
+               export GIT_PAGER &&
+               test_terminal git format-patch --stdout --all
+       ) &&
+       test_path_is_file pager_used
+'
+
+ test_expect_success TTY 'format-patch --stdout pagination can be disabled' '
+       rm -f pager_used &&
+       (
+               GIT_PAGER="wc >pager_used" &&
+               export GIT_PAGER &&
+               test_terminal git --no-pager format-patch --stdout --all &&
+               test_terminal git -c "pager.format-patch=false" format-patch --stdout --all
+       ) &&
+       test_path_is_missing pager_used &&
+       test_path_is_missing .git/pager_used
+'
+
 test_done
index 935d101fe8d880ff426452ca6f611ce8d30b8bc1..9059bcd69eec7a718b6017356c82ec163875c042 100755 (executable)
@@ -330,7 +330,7 @@ test_expect_success 'check space before tab in indent (space-before-tab: on)' '
 
 test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '
 
-       git config core.whitespace "-indent-with-non-tab"
+       git config core.whitespace "-indent-with-non-tab" &&
        echo "        foo ();" > x &&
        git diff --check
 
@@ -344,6 +344,13 @@ test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
 
 '
 
+test_expect_success 'ditto, but tabwidth=9' '
+
+       git config core.whitespace "indent-with-non-tab,tabwidth=9" &&
+       git diff --check
+
+'
+
 test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
 
        git config core.whitespace "indent-with-non-tab" &&
@@ -352,6 +359,20 @@ test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab:
 
 '
 
+test_expect_success 'ditto, but tabwidth=10' '
+
+       git config core.whitespace "indent-with-non-tab,tabwidth=10" &&
+       test_must_fail git diff --check
+
+'
+
+test_expect_success 'ditto, but tabwidth=20' '
+
+       git config core.whitespace "indent-with-non-tab,tabwidth=20" &&
+       git diff --check
+
+'
+
 test_expect_success 'check tabs as indentation (tab-in-indent: off)' '
 
        git config core.whitespace "-tab-in-indent" &&
@@ -376,6 +397,13 @@ test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '
 
 '
 
+test_expect_success 'ditto, but tabwidth=1 (must be irrelevant)' '
+
+       git config core.whitespace "tab-in-indent,tabwidth=1" &&
+       test_must_fail git diff --check
+
+'
+
 test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '
 
        git config core.whitespace "tab-in-indent,indent-with-non-tab" &&
@@ -491,4 +519,41 @@ test_expect_success 'combined diff with autocrlf conversion' '
 
 '
 
+# Start testing the colored format for whitespace checks
+
+test_expect_success 'setup diff colors' '
+       git config color.diff always &&
+       git config color.diff.plain normal &&
+       git config color.diff.meta bold &&
+       git config color.diff.frag cyan &&
+       git config color.diff.func normal &&
+       git config color.diff.old red &&
+       git config color.diff.new green &&
+       git config color.diff.commit yellow &&
+       git config color.diff.whitespace "normal red" &&
+
+       git config core.autocrlf false
+'
+cat >expected <<\EOF
+<BOLD>diff --git a/x b/x<RESET>
+<BOLD>index 9daeafb..2874b91 100644<RESET>
+<BOLD>--- a/x<RESET>
+<BOLD>+++ b/x<RESET>
+<CYAN>@@ -1 +1,4 @@<RESET>
+ test<RESET>
+<GREEN>+<RESET><GREEN>{<RESET>
+<GREEN>+<RESET><BRED>  <RESET>
+<GREEN>+<RESET><GREEN>}<RESET>
+EOF
+
+test_expect_success 'diff that introduces a line with only tabs' '
+       git config core.whitespace blank-at-eol &&
+       git reset --hard &&
+       echo "test" > x &&
+       git commit -m "initial" x &&
+       echo "{NTN}" | tr "NT" "\n\t" >> x &&
+       git -c color.diff=always diff | test_decode_color >current &&
+       test_cmp expected current
+'
+
 test_done
index 34e5144eed8958a3f3341574ff8ecbcc6a4157a9..ab0c2f0574f915296b885a21c6151a6947bdae7e 100755 (executable)
@@ -13,12 +13,14 @@ P1='pathname        with HT'
 P2='pathname with SP'
 P3='pathname
 with LF'
-: 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1" || {
-       skip_all='Your filesystem does not allow tabs in filenames, test skipped.'
-       test_done
-}
+if : 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1"
+then
+       test_set_prereq TABS_IN_FILENAMES
+else
+       say 'Your filesystem does not allow tabs in filenames'
+fi
 
-test_expect_success setup '
+test_expect_success TABS_IN_FILENAMES setup '
        echo P0.0 >"$P0.0" &&
        echo P0.1 >"$P0.1" &&
        echo P0.2 >"$P0.2" &&
@@ -38,6 +40,7 @@ test_expect_success setup '
        :
 '
 
+test_expect_success TABS_IN_FILENAMES 'setup expected files' '
 cat >expect <<\EOF
  rename pathname.1 => "Rpathname\twith HT.0" (100%)
  rename pathname.3 => "Rpathname\nwith LF.0" (100%)
@@ -47,11 +50,14 @@ cat >expect <<\EOF
  rename pathname.0 => Rpathname.0 (100%)
  rename "pathname\twith HT.0" => Rpathname.1 (100%)
 EOF
-test_expect_success 'git diff --summary -M HEAD' '
+'
+
+test_expect_success TABS_IN_FILENAMES 'git diff --summary -M HEAD' '
        git diff --summary -M HEAD >actual &&
        test_cmp expect actual
 '
 
+test_expect_success TABS_IN_FILENAMES 'setup expected files' '
 cat >expect <<\EOF
  pathname.1 => "Rpathname\twith HT.0"            |    0
  pathname.3 => "Rpathname\nwith LF.0"            |    0
@@ -62,7 +68,9 @@ cat >expect <<\EOF
  "pathname\twith HT.0" => Rpathname.1            |    0
  7 files changed, 0 insertions(+), 0 deletions(-)
 EOF
-test_expect_success 'git diff --stat -M HEAD' '
+'
+
+test_expect_success TABS_IN_FILENAMES 'git diff --stat -M HEAD' '
        git diff --stat -M HEAD >actual &&
        test_cmp expect actual
 '
index 61589853df55e063fbe6489fc9c6effc4a9f33b6..95a7ca707045cad3362b92c18df73e82206a2844 100755 (executable)
@@ -29,66 +29,49 @@ test_expect_success 'git diff --quiet -w  HEAD^ HEAD' '
 '
 
 test_expect_success 'git diff-tree HEAD^ HEAD' '
-       git diff-tree --exit-code HEAD^ HEAD
-       test $? = 1
+       test_expect_code 1 git diff-tree --exit-code HEAD^ HEAD
 '
 test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
        git diff-tree --exit-code HEAD^ HEAD -- a
-       test $? = 0
 '
 test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
-       git diff-tree --exit-code HEAD^ HEAD -- b
-       test $? = 1
+       test_expect_code 1 git diff-tree --exit-code HEAD^ HEAD -- b
 '
 test_expect_success 'echo HEAD | git diff-tree --stdin' '
-       echo $(git rev-parse HEAD) | git diff-tree --exit-code --stdin
-       test $? = 1
+       echo $(git rev-parse HEAD) | test_expect_code 1 git diff-tree --exit-code --stdin
 '
 test_expect_success 'git diff-tree HEAD HEAD' '
        git diff-tree --exit-code HEAD HEAD
-       test $? = 0
 '
 test_expect_success 'git diff-files' '
        git diff-files --exit-code
-       test $? = 0
 '
 test_expect_success 'git diff-index --cached HEAD' '
        git diff-index --exit-code --cached HEAD
-       test $? = 0
 '
 test_expect_success 'git diff-index --cached HEAD^' '
-       git diff-index --exit-code --cached HEAD^
-       test $? = 1
+       test_expect_code 1 git diff-index --exit-code --cached HEAD^
 '
 test_expect_success 'git diff-index --cached HEAD^' '
        echo text >>b &&
        echo 3 >c &&
-       git add . && {
-               git diff-index --exit-code --cached HEAD^
-               test $? = 1
-       }
+       git add . &&
+       test_expect_code 1 git diff-index --exit-code --cached HEAD^
 '
 test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
-       git commit -m "text in b" && {
-               git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
-               test $? = 1
-       }
+       git commit -m "text in b" &&
+       test_expect_code 1 git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
 '
 test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
        git diff-tree -p --exit-code -Snot-found HEAD^ HEAD -- b
-       test $? = 0
 '
 test_expect_success 'git diff-files' '
-       echo 3 >>c && {
-               git diff-files --exit-code
-               test $? = 1
-       }
+       echo 3 >>c &&
+       test_expect_code 1 git diff-files --exit-code
 '
 test_expect_success 'git diff-index --cached HEAD' '
-       git update-index c && {
-               git diff-index --exit-code --cached HEAD
-               test $? = 1
-       }
+       git update-index c &&
+       test_expect_code 1 git diff-index --exit-code --cached HEAD
 '
 
 test_expect_success '--check --exit-code returns 0 for no difference' '
@@ -100,30 +83,26 @@ test_expect_success '--check --exit-code returns 0 for no difference' '
 test_expect_success '--check --exit-code returns 1 for a clean difference' '
 
        echo "good" > a &&
-       git diff --check --exit-code
-       test $? = 1
+       test_expect_code 1 git diff --check --exit-code
 
 '
 
 test_expect_success '--check --exit-code returns 3 for a dirty difference' '
 
        echo "bad   " >> a &&
-       git diff --check --exit-code
-       test $? = 3
+       test_expect_code 3 git diff --check --exit-code
 
 '
 
 test_expect_success '--check with --no-pager returns 2 for dirty difference' '
 
-       git --no-pager diff --check
-       test $? = 2
+       test_expect_code 2 git --no-pager diff --check
 
 '
 
 test_expect_success 'check should test not just the last line' '
        echo "" >>a &&
-       git --no-pager diff --check
-       test $? = 2
+       test_expect_code 2 git --no-pager diff --check
 
 '
 
@@ -133,10 +112,8 @@ test_expect_success 'check detects leftover conflict markers' '
        echo binary >>b &&
        git commit -m "side" b &&
        test_must_fail git merge master &&
-       git add b && (
-               git --no-pager diff --cached --check >test.out
-               test $? = 2
-       ) &&
+       git add b &&
+       test_expect_code 2 git --no-pager diff --cached --check >test.out &&
        test 3 = $(grep "conflict marker" test.out | wc -l) &&
        git reset --hard
 '
@@ -146,19 +123,13 @@ test_expect_success 'check honors conflict marker length' '
        echo ">>>>>>> boo" >>b &&
        echo "======" >>a &&
        git diff --check a &&
-       (
-               git diff --check b
-               test $? = 2
-       ) &&
+       test_expect_code 2 git diff --check b &&
        git reset --hard &&
        echo ">>>>>>>> boo" >>b &&
        echo "========" >>a &&
        git diff --check &&
        echo "b conflict-marker-size=8" >.gitattributes &&
-       (
-               git diff --check b
-               test $? = 2
-       ) &&
+       test_expect_code 2 git diff --check b &&
        git diff --check a &&
        git reset --hard
 '
index 5b10e976a3fd0c554ff2fa14004e69343a9ec634..364693062399228cd6a18b52a21db173519d8e94 100755 (executable)
@@ -32,13 +32,18 @@ EOF
 
 sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
 
-builtin_patterns="bibtex cpp html java objc pascal php python ruby tex"
+builtin_patterns="bibtex cpp csharp fortran html java objc pascal perl php python ruby tex"
 for p in $builtin_patterns
 do
        test_expect_success "builtin $p pattern compiles" '
                echo "*.java diff=$p" > .gitattributes &&
-               ! ( git diff --no-index Beer.java Beer-correct.java 2>&1 |
-                       grep "fatal" > /dev/null )
+               ! { git diff --no-index Beer.java Beer-correct.java 2>&1 |
+                       grep "fatal" > /dev/null; }
+       '
+       test_expect_success "builtin $p wordRegex pattern compiles" '
+               ! { git diff --no-index --word-diff \
+                       Beer.java Beer-correct.java 2>&1 |
+                       grep "fatal" > /dev/null; }
        '
 done
 
index f6d1f1ebab406fcd4f405178ec151149754500b0..a5019759bc7593cf8affd8625f61bbd7ab1b9655 100755 (executable)
@@ -36,11 +36,12 @@ prepare_output () {
        git diff --color >output
        $grep_a "$blue_grep" output >error
        $grep_a -v "$blue_grep" output >normal
+       return 0
 }
 
 test_expect_success default '
 
-       prepare_output
+       prepare_output &&
 
        grep Eight normal >/dev/null &&
        grep HT error >/dev/null &&
@@ -50,10 +51,67 @@ test_expect_success default '
 
 '
 
+test_expect_success 'default (attribute)' '
+
+       test_might_fail git config --unset core.whitespace &&
+       echo "F whitespace" >.gitattributes &&
+       prepare_output &&
+
+       grep Eight error >/dev/null &&
+       grep HT error >/dev/null &&
+       grep With error >/dev/null &&
+       grep Return error >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'default, tabwidth=10 (attribute)' '
+
+       git config core.whitespace "tabwidth=10" &&
+       echo "F whitespace" >.gitattributes &&
+       prepare_output &&
+
+       grep Eight normal >/dev/null &&
+       grep HT error >/dev/null &&
+       grep With error >/dev/null &&
+       grep Return error >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'no check (attribute)' '
+
+       test_might_fail git config --unset core.whitespace &&
+       echo "F -whitespace" >.gitattributes &&
+       prepare_output &&
+
+       grep Eight normal >/dev/null &&
+       grep HT normal >/dev/null &&
+       grep With normal >/dev/null &&
+       grep Return normal >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'no check, tabwidth=10 (attribute), must be irrelevant' '
+
+       git config core.whitespace "tabwidth=10" &&
+       echo "F -whitespace" >.gitattributes &&
+       prepare_output &&
+
+       grep Eight normal >/dev/null &&
+       grep HT normal >/dev/null &&
+       grep With normal >/dev/null &&
+       grep Return normal >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
 test_expect_success 'without -trail' '
 
-       git config core.whitespace -trail
-       prepare_output
+       rm -f .gitattributes &&
+       git config core.whitespace -trail &&
+       prepare_output &&
 
        grep Eight normal >/dev/null &&
        grep HT error >/dev/null &&
@@ -65,9 +123,9 @@ test_expect_success 'without -trail' '
 
 test_expect_success 'without -trail (attribute)' '
 
-       git config --unset core.whitespace
-       echo "F whitespace=-trail" >.gitattributes
-       prepare_output
+       test_might_fail git config --unset core.whitespace &&
+       echo "F whitespace=-trail" >.gitattributes &&
+       prepare_output &&
 
        grep Eight normal >/dev/null &&
        grep HT error >/dev/null &&
@@ -79,9 +137,9 @@ test_expect_success 'without -trail (attribute)' '
 
 test_expect_success 'without -space' '
 
-       rm -f .gitattributes
-       git config core.whitespace -space
-       prepare_output
+       rm -f .gitattributes &&
+       git config core.whitespace -space &&
+       prepare_output &&
 
        grep Eight normal >/dev/null &&
        grep HT normal >/dev/null &&
@@ -93,9 +151,9 @@ test_expect_success 'without -space' '
 
 test_expect_success 'without -space (attribute)' '
 
-       git config --unset core.whitespace
-       echo "F whitespace=-space" >.gitattributes
-       prepare_output
+       test_might_fail git config --unset core.whitespace &&
+       echo "F whitespace=-space" >.gitattributes &&
+       prepare_output &&
 
        grep Eight normal >/dev/null &&
        grep HT normal >/dev/null &&
@@ -107,9 +165,9 @@ test_expect_success 'without -space (attribute)' '
 
 test_expect_success 'with indent-non-tab only' '
 
-       rm -f .gitattributes
-       git config core.whitespace indent,-trailing,-space
-       prepare_output
+       rm -f .gitattributes &&
+       git config core.whitespace indent,-trailing,-space &&
+       prepare_output &&
 
        grep Eight error >/dev/null &&
        grep HT normal >/dev/null &&
@@ -121,9 +179,9 @@ test_expect_success 'with indent-non-tab only' '
 
 test_expect_success 'with indent-non-tab only (attribute)' '
 
-       git config --unset core.whitespace
-       echo "F whitespace=indent,-trailing,-space" >.gitattributes
-       prepare_output
+       test_might_fail git config --unset core.whitespace &&
+       echo "F whitespace=indent,-trailing,-space" >.gitattributes &&
+       prepare_output &&
 
        grep Eight error >/dev/null &&
        grep HT normal >/dev/null &&
@@ -133,11 +191,39 @@ test_expect_success 'with indent-non-tab only (attribute)' '
 
 '
 
+test_expect_success 'with indent-non-tab only, tabwidth=10' '
+
+       rm -f .gitattributes &&
+       git config core.whitespace indent,tabwidth=10,-trailing,-space &&
+       prepare_output &&
+
+       grep Eight normal >/dev/null &&
+       grep HT normal >/dev/null &&
+       grep With normal >/dev/null &&
+       grep Return normal >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'with indent-non-tab only, tabwidth=10 (attribute)' '
+
+       test_might_fail git config --unset core.whitespace &&
+       echo "F whitespace=indent,-trailing,-space,tabwidth=10" >.gitattributes &&
+       prepare_output &&
+
+       grep Eight normal >/dev/null &&
+       grep HT normal >/dev/null &&
+       grep With normal >/dev/null &&
+       grep Return normal >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
 test_expect_success 'with cr-at-eol' '
 
-       rm -f .gitattributes
-       git config core.whitespace cr-at-eol
-       prepare_output
+       rm -f .gitattributes &&
+       git config core.whitespace cr-at-eol &&
+       prepare_output &&
 
        grep Eight normal >/dev/null &&
        grep HT error >/dev/null &&
@@ -149,9 +235,9 @@ test_expect_success 'with cr-at-eol' '
 
 test_expect_success 'with cr-at-eol (attribute)' '
 
-       git config --unset core.whitespace
-       echo "F whitespace=trailing,cr-at-eol" >.gitattributes
-       prepare_output
+       test_might_fail git config --unset core.whitespace &&
+       echo "F whitespace=trailing,cr-at-eol" >.gitattributes &&
+       prepare_output &&
 
        grep Eight normal >/dev/null &&
        grep HT error >/dev/null &&
@@ -178,12 +264,21 @@ test_expect_success 'trailing empty lines (2)' '
 
 '
 
+test_expect_success 'checkdiff shows correct line number for trailing blank lines' '
+
+       printf "a\nb\n" > G &&
+       git add G &&
+       printf "x\nx\nx\na\nb\nc\n\n" > G &&
+       [ "$(git diff --check -- G)" = "G:7: new blank line at EOF." ]
+
+'
+
 test_expect_success 'do not color trailing cr in context' '
-       git config --unset core.whitespace
+       test_might_fail git config --unset core.whitespace &&
        rm -f .gitattributes &&
        echo AAAQ | tr Q "\015" >G &&
        git add G &&
-       echo BBBQ | tr Q "\015" >>G
+       echo BBBQ | tr Q "\015" >>G &&
        git diff --color G | tr "\015" Q >output &&
        grep "BBB.*${blue_grep}Q" output &&
        grep "AAA.*\[mQ" output
index 709b3231ca8d9da631727b4aadfb2f46049d37e9..886494b58f6739d40c2c34724064317b0d3be770 100755 (executable)
@@ -95,7 +95,7 @@ test_expect_success 'format.numbered && --keep-subject' '
 
 test_expect_success 'format.numbered = auto' '
 
-       git config format.numbered auto
+       git config format.numbered auto &&
        git format-patch --stdout HEAD~2 > patch5 &&
        test_numbered patch5
 
index 40a95a149e4e84924d961d43f03115f466a9acc1..5d20acf436558da6c11214f431beb503bc89459a 100755 (executable)
@@ -4,13 +4,7 @@ test_description='typechange rename detection'
 
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-       skip_all='Symbolic links not supported, skipping tests.'
-       test_done
-fi
-
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
 
        rm -f foo bar &&
        cat "$TEST_DIRECTORY"/../COPYING >foo &&
@@ -56,7 +50,7 @@ test_expect_success setup '
 
 '
 
-test_expect_success 'cross renames to be detected for regular files' '
+test_expect_success SYMLINKS 'cross renames to be detected for regular files' '
 
        git diff-tree five six -r --name-status -B -M | sort >actual &&
        {
@@ -67,7 +61,7 @@ test_expect_success 'cross renames to be detected for regular files' '
 
 '
 
-test_expect_success 'cross renames to be detected for typechange' '
+test_expect_success SYMLINKS 'cross renames to be detected for typechange' '
 
        git diff-tree one two -r --name-status -B -M | sort >actual &&
        {
@@ -78,7 +72,7 @@ test_expect_success 'cross renames to be detected for typechange' '
 
 '
 
-test_expect_success 'moves and renames' '
+test_expect_success SYMLINKS 'moves and renames' '
 
        git diff-tree three four -r --name-status -B -M | sort >actual &&
        {
index d5ccdd0cf8061e797e88185bfddb0864f73291dd..3726a0e2012e534623ddbdd71da04f177c32d4d0 100755 (executable)
@@ -74,7 +74,6 @@ test_expect_success 'extra character after attribute' '
 '
 
 test_expect_success 'unknown color slots are ignored (diff)' '
-       git config --unset diff.color.new
        git config color.diff.nosuchslotwilleverbedefined white &&
        git diff --color
 '
index d99814ac641c93383d7199f4419083bedcfc9ea8..241a74d2a20276d711944d3e203083b515486e77 100755 (executable)
@@ -316,11 +316,11 @@ test_expect_success 'git diff (empty submodule dir)' '
 test_expect_success 'conflicted submodule setup' '
 
        # 39 efs
-       c=fffffffffffffffffffffffffffffffffffffff
+       c=fffffffffffffffffffffffffffffffffffffff &&
        (
-               echo "000000 $_z40 0    sub"
-               echo "160000 1$c 1      sub"
-               echo "160000 2$c 2      sub"
+               echo "000000 $_z40 0    sub" &&
+               echo "160000 1$c 1      sub" &&
+               echo "160000 2$c 2      sub" &&
                echo "160000 3$c 3      sub"
        ) | git update-index --index-info &&
        echo >expect.nosub '\''diff --cc sub
index 6f7548c3a144ecada6642eccee6b0f687baafb2d..8096d8a337867b4afdc4b061fdc314fdd8eac185 100755 (executable)
@@ -6,8 +6,8 @@ test_description='word diff colors'
 
 test_expect_success setup '
 
-       git config diff.color.old red
-       git config diff.color.new green
+       git config diff.color.old red &&
+       git config diff.color.new green &&
        git config diff.color.func magenta
 
 '
@@ -35,10 +35,10 @@ aeff = aeff * ( aaa )
 EOF
 
 cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 330b04f..5ed8eff 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
 <CYAN>@@ -1,3 +1,7 @@<RESET>
 <RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
 
@@ -122,10 +122,10 @@ test_expect_success '--word-diff=plain --no-color' '
 '
 
 cat > expect <<EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 330b04f..5ed8eff 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
 <CYAN>@@ -1,3 +1,7 @@<RESET>
 <RED>[-h(4)-]<RESET><GREEN>{+h(4),hh[44]+}<RESET>
 
@@ -143,10 +143,10 @@ test_expect_success '--word-diff=plain --color' '
 '
 
 cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 330b04f..5ed8eff 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
 <CYAN>@@ -1 +1 @@<RESET>
 <RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
 <CYAN>@@ -3,0 +4,4 @@<RESET> <RESET><MAGENTA>a = b + c<RESET>
@@ -163,10 +163,10 @@ test_expect_success 'word diff without context' '
 '
 
 cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 330b04f..5ed8eff 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
 <CYAN>@@ -1,3 +1,7 @@<RESET>
 h(4),<GREEN>hh<RESET>[44]
 
@@ -199,10 +199,10 @@ test_expect_success 'option overrides .gitattributes' '
 '
 
 cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 330b04f..5ed8eff 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
 <CYAN>@@ -1,3 +1,7 @@<RESET>
 h(4)<GREEN>,hh[44]<RESET>
 
@@ -231,10 +231,10 @@ test_expect_success 'command-line overrides config' '
 '
 
 cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 330b04f..5ed8eff 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
 <CYAN>@@ -1,3 +1,7 @@<RESET>
 h(4),<GREEN>{+hh+}<RESET>[44]
 
@@ -260,10 +260,10 @@ test_expect_success 'remove diff driver regex' '
 '
 
 cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 330b04f..5ed8eff 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 330b04f..5ed8eff 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
 <CYAN>@@ -1,3 +1,7 @@<RESET>
 h(4),<GREEN>hh[44<RESET>]
 
@@ -282,10 +282,10 @@ echo 'aaa (aaa)' > pre
 echo 'aaa (aaa) aaa' > post
 
 cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index c29453b..be22f37 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index c29453b..be22f37 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
 <CYAN>@@ -1 +1 @@<RESET>
 aaa (aaa) <GREEN>aaa<RESET>
 EOF
@@ -301,10 +301,10 @@ echo '(:' > pre
 echo '(' > post
 
 cat > expect <<\EOF
-<WHITE>diff --git a/pre b/post<RESET>
-<WHITE>index 289cb9d..2d06f37 100644<RESET>
-<WHITE>--- a/pre<RESET>
-<WHITE>+++ b/post<RESET>
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 289cb9d..2d06f37 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
 <CYAN>@@ -1 +1 @@<RESET>
 (<RED>:<RESET>
 EOF
index 8e391cf9a72c6518217526155cbea93d74b9e626..bf9a7526bd38a17e0e991739db8c4a1f8541b2f6 100755 (executable)
@@ -37,9 +37,10 @@ head1=$(add_file sm1 foo1 foo2)
 test_expect_success 'added submodule' "
        git add sm1 &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 0000000...$head1 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -47,33 +48,36 @@ head2=$(add_file sm1 foo3)
 
 test_expect_success 'modified submodule(forward)' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head1..$head2:
   > Add foo3
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule(forward)' "
        git diff --submodule=log >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head1..$head2:
   > Add foo3
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule(forward) --submodule' "
        git diff --submodule >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head1..$head2:
   > Add foo3
 EOF
+       test_cmp expected actual
 "
 
 fullhead1=$(cd sm1; git rev-list --max-count=1 $head1)
 fullhead2=$(cd sm1; git rev-list --max-count=1 $head2)
 test_expect_success 'modified submodule(forward) --submodule=short' "
        git diff --submodule=short >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 diff --git a/sm1 b/sm1
 index $head1..$head2 160000
 --- a/sm1
@@ -82,34 +86,38 @@ index $head1..$head2 160000
 -Subproject commit $fullhead1
 +Subproject commit $fullhead2
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
-cd sm1 &&
-git reset --hard HEAD~2 >/dev/null &&
-head3=$(git rev-parse --verify HEAD | cut -c1-7) &&
-cd ..
+head3=$(
+       cd sm1 &&
+       git reset --hard HEAD~2 >/dev/null &&
+       git rev-parse --verify HEAD | cut -c1-7
+)
 
 test_expect_success 'modified submodule(backward)' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head2..$head3 (rewind):
   < Add foo3
   < Add foo2
 EOF
+       test_cmp expected actual
 "
 
 head4=$(add_file sm1 foo4 foo5) &&
 head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
 test_expect_success 'modified submodule(backward and forward)' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head2...$head4:
   > Add foo5
   > Add foo4
   < Add foo3
   < Add foo2
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -122,7 +130,7 @@ mv sm1-bak sm1
 
 test_expect_success 'typechanged submodule(submodule->blob), --cached' "
        git diff --submodule=log --cached >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 41fbea9...0000000 (submodule deleted)
 diff --git a/sm1 b/sm1
 new file mode 100644
@@ -132,11 +140,12 @@ index 0000000..9da5fb8
 @@ -0,0 +1 @@
 +sm1
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'typechanged submodule(submodule->blob)' "
        git diff --submodule=log >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 diff --git a/sm1 b/sm1
 deleted file mode 100644
 index 9da5fb8..0000000
@@ -146,13 +155,14 @@ index 9da5fb8..0000000
 -sm1
 Submodule sm1 0000000...$head4 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 rm -rf sm1 &&
 git checkout-index sm1
 test_expect_success 'typechanged submodule(submodule->blob)' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head4...0000000 (submodule deleted)
 diff --git a/sm1 b/sm1
 new file mode 100644
@@ -162,6 +172,7 @@ index 0000000..$head5
 @@ -0,0 +1 @@
 +sm1
 EOF
+       test_cmp expected actual
 "
 
 rm -f sm1 &&
@@ -170,15 +181,16 @@ head6=$(add_file sm1 foo6 foo7)
 fullhead6=$(cd sm1; git rev-list --max-count=1 $head6)
 test_expect_success 'nonexistent commit' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head4...$head6 (commits not present)
 EOF
+       test_cmp expected actual
 "
 
 commit_file
 test_expect_success 'typechanged submodule(blob->submodule)' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 diff --git a/sm1 b/sm1
 deleted file mode 100644
 index $head5..0000000
@@ -188,21 +200,24 @@ index $head5..0000000
 -sm1
 Submodule sm1 0000000...$head6 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
 test_expect_success 'submodule is up to date' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'submodule contains untracked content' "
        echo new > sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains untracked content
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'submodule contains untracked content (untracked ignored)' "
@@ -223,18 +238,20 @@ test_expect_success 'submodule contains untracked content (all ignored)' "
 test_expect_success 'submodule contains untracked and modifed content' "
        echo new > sm1/foo6 &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains untracked content
 Submodule sm1 contains modified content
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'submodule contains untracked and modifed content (untracked ignored)' "
        echo new > sm1/foo6 &&
        git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains modified content
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'submodule contains untracked and modifed content (dirty ignored)' "
@@ -252,45 +269,50 @@ test_expect_success 'submodule contains untracked and modifed content (all ignor
 test_expect_success 'submodule contains modifed content' "
        rm -f sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains modified content
 EOF
+       test_cmp expected actual
 "
 
 (cd sm1; git commit -mchange foo6 >/dev/null) &&
 head8=$(cd sm1; git rev-parse --verify HEAD | cut -c1-7) &&
 test_expect_success 'submodule is modified' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked content' "
        echo new > sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains untracked content
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked content (untracked ignored)' "
        git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked content (dirty ignored)' "
        git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked content (all ignored)' "
@@ -301,31 +323,34 @@ test_expect_success 'modified submodule contains untracked content (all ignored)
 test_expect_success 'modified submodule contains untracked and modifed content' "
        echo modification >> sm1/foo6 &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains untracked content
 Submodule sm1 contains modified content
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked and modifed content (untracked ignored)' "
        echo modification >> sm1/foo6 &&
        git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains modified content
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked and modifed content (dirty ignored)' "
        echo modification >> sm1/foo6 &&
        git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked and modifed content (all ignored)' "
@@ -337,19 +362,21 @@ test_expect_success 'modified submodule contains untracked and modifed content (
 test_expect_success 'modified submodule contains modifed content' "
        rm -f sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains modified content
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 rm -rf sm1
 test_expect_success 'deleted submodule' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6...0000000 (submodule deleted)
 EOF
+       test_cmp expected actual
 "
 
 test_create_repo sm2 &&
@@ -358,41 +385,45 @@ git add sm2
 
 test_expect_success 'multiple submodules' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6...0000000 (submodule deleted)
 Submodule sm2 0000000...$head7 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'path filter' "
        git diff-index -p --submodule=log HEAD sm2 >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm2 0000000...$head7 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm2
 test_expect_success 'given commit' "
        git diff-index -p --submodule=log HEAD^ >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6...0000000 (submodule deleted)
 Submodule sm2 0000000...$head7 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'given commit --submodule' "
        git diff-index -p --submodule HEAD^ >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6...0000000 (submodule deleted)
 Submodule sm2 0000000...$head7 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 fullhead7=$(cd sm2; git rev-list --max-count=1 $head7)
 
 test_expect_success 'given commit --submodule=short' "
        git diff-index -p --submodule=short HEAD^ >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 diff --git a/sm1 b/sm1
 deleted file mode 160000
 index $head6..0000000
@@ -408,6 +439,7 @@ index 0000000..$head7
 @@ -0,0 +1 @@
 +Subproject commit $fullhead7
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'setup .git file for sm2' '
@@ -419,10 +451,11 @@ test_expect_success 'setup .git file for sm2' '
 
 test_expect_success 'diff --submodule with .git file' '
        git diff --submodule HEAD^ >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6...0000000 (submodule deleted)
 Submodule sm2 0000000...$head7 (new submodule)
 EOF
+       test_cmp expected actual
 '
 
 test_done
index 1597965241c3566aa3a14e986fd5a630fb348479..e3ea3d5114b8f6a09b79c9a1323c563da0c7b170 100755 (executable)
@@ -7,6 +7,7 @@ test_description='git apply handling copy/rename patch.
 
 '
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 # setup
 
@@ -31,13 +32,6 @@ test_expect_success setup \
 test_expect_success apply \
     'git apply --index --stat --summary --apply test-patch'
 
-if test "$(git config --bool core.filemode)" = false
-then
-       say 'filemode disabled on the filesystem'
-else
-       test_set_prereq FILEMODE
-fi
-
 test_expect_success FILEMODE validate \
            'test -f bar && ls -l bar | grep "^-..x......"'
 
index 9692f16f3581f261c4c10a29f03751990adb7897..dbbf56cba9f5108f79d767ad48f3092dc821a232 100755 (executable)
@@ -37,15 +37,24 @@ test_expect_success 'setup' "
        git diff-tree -p -C master binary >C.diff &&
 
        git diff-tree -p --binary master binary >BF.diff &&
-       git diff-tree -p --binary -C master binary >CF.diff
+       git diff-tree -p --binary -C master binary >CF.diff &&
+
+       git diff-tree -p --full-index master binary >B-index.diff &&
+       git diff-tree -p -C --full-index master binary >C-index.diff &&
+
+       git init other-repo &&
+       (cd other-repo &&
+        git fetch .. master &&
+        git reset --hard FETCH_HEAD
+       )
 "
 
 test_expect_success 'stat binary diff -- should not fail.' \
-       'git checkout master
+       'git checkout master &&
         git apply --stat --summary B.diff'
 
 test_expect_success 'stat binary diff (copy) -- should not fail.' \
-       'git checkout master
+       'git checkout master &&
         git apply --stat --summary C.diff'
 
 test_expect_success 'check binary diff -- should fail.' \
@@ -69,11 +78,11 @@ test_expect_success \
 '
 
 test_expect_success 'check binary diff with replacement.' \
-       'git checkout master
+       'git checkout master &&
         git apply --check --allow-binary-replacement BF.diff'
 
 test_expect_success 'check binary diff with replacement (copy).' \
-       'git checkout master
+       'git checkout master &&
         git apply --check --allow-binary-replacement CF.diff'
 
 # Now we start applying them.
@@ -100,6 +109,22 @@ test_expect_success 'apply binary diff (copy) -- should fail.' \
        'do_reset &&
         test_must_fail git apply --index C.diff'
 
+test_expect_success 'apply binary diff with full-index' '
+       do_reset &&
+       git apply B-index.diff
+'
+
+test_expect_success 'apply binary diff with full-index (copy)' '
+       do_reset &&
+       git apply C-index.diff
+'
+
+test_expect_success 'apply full-index binary diff in new repo' '
+       (cd other-repo &&
+        do_reset &&
+        test_must_fail git apply ../B-index.diff)
+'
+
 test_expect_success 'apply binary diff without replacement.' \
        'do_reset &&
         git apply BF.diff'
diff --git a/t/t4111-apply-subdir.sh b/t/t4111-apply-subdir.sh
new file mode 100755 (executable)
index 0000000..7c39843
--- /dev/null
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+test_description='patching from inconvenient places'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       cat >patch <<-\EOF &&
+       diff file.orig file
+       --- a/file.orig
+       +++ b/file
+       @@ -1 +1,2 @@
+        1
+       +2
+       EOF
+       patch="$(pwd)/patch" &&
+
+       echo 1 >preimage &&
+       printf "%s\n" 1 2 >postimage &&
+       echo 3 >other &&
+
+       test_tick &&
+       git commit --allow-empty -m basis
+'
+
+test_expect_success 'setup: subdir' '
+       reset_subdir() {
+               git reset &&
+               mkdir -p sub/dir/b &&
+               mkdir -p objects &&
+               cp "$1" file &&
+               cp "$1" objects/file &&
+               cp "$1" sub/dir/file &&
+               cp "$1" sub/dir/b/file &&
+               git add file sub/dir/file sub/dir/b/file objects/file &&
+               cp "$2" file &&
+               cp "$2" sub/dir/file &&
+               cp "$2" sub/dir/b/file &&
+               cp "$2" objects/file &&
+               test_might_fail git update-index --refresh -q
+       }
+'
+
+test_expect_success 'apply from subdir of toplevel' '
+       cp postimage expected &&
+       reset_subdir other preimage &&
+       (
+               cd sub/dir &&
+               git apply "$patch"
+       ) &&
+       test_cmp expected sub/dir/file
+'
+
+test_expect_success 'apply --cached from subdir of toplevel' '
+       cp postimage expected &&
+       cp other expected.working &&
+       reset_subdir preimage other &&
+       (
+               cd sub/dir &&
+               git apply --cached "$patch"
+       ) &&
+       git show :sub/dir/file >actual &&
+       test_cmp expected actual &&
+       test_cmp expected.working sub/dir/file
+'
+
+test_expect_success 'apply --index from subdir of toplevel' '
+       cp postimage expected &&
+       reset_subdir preimage other &&
+       (
+               cd sub/dir &&
+               test_must_fail git apply --index "$patch"
+       ) &&
+       reset_subdir other preimage &&
+       (
+               cd sub/dir &&
+               test_must_fail git apply --index "$patch"
+       ) &&
+       reset_subdir preimage preimage &&
+       (
+               cd sub/dir &&
+               git apply --index "$patch"
+       ) &&
+       git show :sub/dir/file >actual &&
+       test_cmp expected actual &&
+       test_cmp expected sub/dir/file
+'
+
+test_expect_success 'apply from .git dir' '
+       cp postimage expected &&
+       cp preimage .git/file &&
+       cp preimage .git/objects/file &&
+       (
+               cd .git &&
+               git apply "$patch"
+       ) &&
+       test_cmp expected .git/file
+'
+
+test_expect_success 'apply from subdir of .git dir' '
+       cp postimage expected &&
+       cp preimage .git/file &&
+       cp preimage .git/objects/file &&
+       (
+               cd .git/objects &&
+               git apply "$patch"
+       ) &&
+       test_cmp expected .git/objects/file
+'
+
+test_expect_success 'apply --cached from .git dir' '
+       cp postimage expected &&
+       cp other expected.working &&
+       cp other .git/file &&
+       reset_subdir preimage other &&
+       (
+               cd .git &&
+               git apply --cached "$patch"
+       ) &&
+       git show :file >actual &&
+       test_cmp expected actual &&
+       test_cmp expected.working file &&
+       test_cmp expected.working .git/file
+'
+
+test_expect_success 'apply --cached from subdir of .git dir' '
+       cp postimage expected &&
+       cp preimage expected.subdir &&
+       cp other .git/file &&
+       cp other .git/objects/file &&
+       reset_subdir preimage other &&
+       (
+               cd .git/objects &&
+               git apply --cached "$patch"
+       ) &&
+       git show :file >actual &&
+       git show :objects/file >actual.subdir &&
+       test_cmp expected actual &&
+       test_cmp expected.subdir actual.subdir
+'
+
+test_done
index 164d58c222088bd42becf07c044f51bf23eda8b6..f12826fb09729d5753f3f6c9511dab5062f0aae1 100755 (executable)
@@ -9,13 +9,7 @@ test_description='git apply should not get confused with type changes.
 
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-       skip_all='Symbolic links not supported, skipping tests.'
-       test_done
-fi
-
-test_expect_success 'setup repository and commits' '
+test_expect_success SYMLINKS 'setup repository and commits' '
        echo "hello world" > foo &&
        echo "hi planet" > bar &&
        git update-index --add foo bar &&
@@ -48,7 +42,7 @@ test_expect_success 'setup repository and commits' '
        git branch foo-baz-renamed-from-foo
        '
 
-test_expect_success 'file renamed from foo to foo/baz' '
+test_expect_success SYMLINKS 'file renamed from foo to foo/baz' '
        git checkout -f initial &&
        git diff-tree -M -p HEAD foo-baz-renamed-from-foo > patch &&
        git apply --index < patch
@@ -56,7 +50,7 @@ test_expect_success 'file renamed from foo to foo/baz' '
 test_debug 'cat patch'
 
 
-test_expect_success 'file renamed from foo/baz to foo' '
+test_expect_success SYMLINKS 'file renamed from foo/baz to foo' '
        git checkout -f foo-baz-renamed-from-foo &&
        git diff-tree -M -p HEAD initial > patch &&
        git apply --index < patch
@@ -64,7 +58,7 @@ test_expect_success 'file renamed from foo/baz to foo' '
 test_debug 'cat patch'
 
 
-test_expect_success 'directory becomes file' '
+test_expect_success SYMLINKS 'directory becomes file' '
        git checkout -f foo-becomes-a-directory &&
        git diff-tree -p HEAD initial > patch &&
        git apply --index < patch
@@ -72,7 +66,7 @@ test_expect_success 'directory becomes file' '
 test_debug 'cat patch'
 
 
-test_expect_success 'file becomes directory' '
+test_expect_success SYMLINKS 'file becomes directory' '
        git checkout -f initial &&
        git diff-tree -p HEAD foo-becomes-a-directory > patch &&
        git apply --index < patch
@@ -80,7 +74,7 @@ test_expect_success 'file becomes directory' '
 test_debug 'cat patch'
 
 
-test_expect_success 'file becomes symlink' '
+test_expect_success SYMLINKS 'file becomes symlink' '
        git checkout -f initial &&
        git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
        git apply --index < patch
@@ -88,21 +82,21 @@ test_expect_success 'file becomes symlink' '
 test_debug 'cat patch'
 
 
-test_expect_success 'symlink becomes file' '
+test_expect_success SYMLINKS 'symlink becomes file' '
        git checkout -f foo-symlinked-to-bar &&
        git diff-tree -p HEAD foo-back-to-file > patch &&
        git apply --index < patch
        '
 test_debug 'cat patch'
 
-test_expect_success 'binary file becomes symlink' '
+test_expect_success SYMLINKS 'binary file becomes symlink' '
        git checkout -f foo-becomes-binary &&
        git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch &&
        git apply --index < patch
        '
 test_debug 'cat patch'
 
-test_expect_success 'symlink becomes binary file' '
+test_expect_success SYMLINKS 'symlink becomes binary file' '
        git checkout -f foo-symlinked-to-bar &&
        git diff-tree -p --binary HEAD foo-becomes-binary > patch &&
        git apply --index < patch
@@ -110,7 +104,7 @@ test_expect_success 'symlink becomes binary file' '
 test_debug 'cat patch'
 
 
-test_expect_success 'symlink becomes directory' '
+test_expect_success SYMLINKS 'symlink becomes directory' '
        git checkout -f foo-symlinked-to-bar &&
        git diff-tree -p HEAD foo-becomes-a-directory > patch &&
        git apply --index < patch
@@ -118,7 +112,7 @@ test_expect_success 'symlink becomes directory' '
 test_debug 'cat patch'
 
 
-test_expect_success 'directory becomes symlink' '
+test_expect_success SYMLINKS 'directory becomes symlink' '
        git checkout -f foo-becomes-a-directory &&
        git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
        git apply --index < patch
index aff4348034b21644818fb8f938ef006001b0b099..7674dd2ec9d6f14a0f2181ebf415378521f90fa4 100755 (executable)
@@ -9,13 +9,7 @@ test_description='git apply symlinks and partial files
 
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-       skip_all='Symbolic links not supported, skipping tests.'
-       test_done
-fi
-
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
 
        ln -s path1/path2/path3/path4/path5 link1 &&
        git add link? &&
@@ -34,7 +28,7 @@ test_expect_success setup '
 
 '
 
-test_expect_success 'apply symlink patch' '
+test_expect_success SYMLINKS 'apply symlink patch' '
 
        git checkout side &&
        git apply patch &&
@@ -43,7 +37,7 @@ test_expect_success 'apply symlink patch' '
 
 '
 
-test_expect_success 'apply --index symlink patch' '
+test_expect_success SYMLINKS 'apply --index symlink patch' '
 
        git checkout -f side &&
        git apply --index patch &&
index 3c73a783a7e908070308fb1f972f6b5d152e12a4..3d0384daa8a7b7369826b05bcb793cb445c3f9ee 100755 (executable)
@@ -73,7 +73,7 @@ D=`pwd`
 test_expect_success 'apply --whitespace=strip in subdir' '
 
        cd "$D" &&
-       git config --unset-all apply.whitespace
+       git config --unset-all apply.whitespace &&
        rm -f sub/file1 &&
        cp saved sub/file1 &&
        git update-index --refresh &&
index b463b4f05ce43ee345a0594528d684befe7c2ef3..579c9e61054521301464bfc31351040d45966836 100755 (executable)
@@ -10,21 +10,76 @@ test_description='git apply -p handling.'
 test_expect_success setup '
        mkdir sub &&
        echo A >sub/file1 &&
-       cp sub/file1 file1 &&
+       cp sub/file1 file1.saved &&
        git add sub/file1 &&
        echo B >sub/file1 &&
        git diff >patch.file &&
-       rm sub/file1 &&
-       rmdir sub
+       git checkout -- sub/file1 &&
+       git mv sub süb &&
+       echo B >süb/file1 &&
+       git diff >patch.escaped &&
+       grep "[\]" patch.escaped &&
+       rm süb/file1 &&
+       rmdir süb
 '
 
 test_expect_success 'apply git diff with -p2' '
+       cp file1.saved file1 &&
        git apply -p2 patch.file
 '
 
 test_expect_success 'apply with too large -p' '
+       cp file1.saved file1 &&
        test_must_fail git apply --stat -p3 patch.file 2>err &&
        grep "removing 3 leading" err
 '
 
+test_expect_success 'apply (-p2) traditional diff with funny filenames' '
+       cat >patch.quotes <<-\EOF &&
+       diff -u "a/"sub/file1 "b/"sub/file1
+       --- "a/"sub/file1
+       +++ "b/"sub/file1
+       @@ -1 +1 @@
+       -A
+       +B
+       EOF
+       echo B >expected &&
+
+       cp file1.saved file1 &&
+       git apply -p2 patch.quotes &&
+       test_cmp expected file1
+'
+
+test_expect_success 'apply with too large -p and fancy filename' '
+       cp file1.saved file1 &&
+       test_must_fail git apply --stat -p3 patch.escaped 2>err &&
+       grep "removing 3 leading" err
+'
+
+test_expect_success 'apply (-p2) diff, mode change only' '
+       cat >patch.chmod <<-\EOF &&
+       diff --git a/sub/file1 b/sub/file1
+       old mode 100644
+       new mode 100755
+       EOF
+       chmod 644 file1 &&
+       git apply -p2 patch.chmod &&
+       test -x file1
+'
+
+test_expect_success 'apply (-p2) diff, rename' '
+       cat >patch.rename <<-\EOF &&
+       diff --git a/sub/file1 b/sub/file2
+       similarity index 100%
+       rename from sub/file1
+       rename to sub/file2
+       EOF
+       echo A >expected &&
+
+       cp file1.saved file1 &&
+       rm -f file2 &&
+       git apply -p2 patch.rename &&
+       test_cmp expected file2
+'
+
 test_done
index 923fcab7f96a76ee7038bfcb20b1270565bc2230..39407376ba7da1cb8256bcd3041a83147cd9eee5 100755 (executable)
@@ -3,12 +3,6 @@
 test_description='apply to deeper directory without getting fooled with symlink'
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-       skip_all='Symbolic links not supported, skipping tests.'
-       test_done
-fi
-
 lecho () {
        for l_
        do
@@ -16,7 +10,7 @@ lecho () {
        done
 }
 
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
 
        mkdir -p arch/i386/boot arch/x86_64 &&
        lecho 1 2 3 4 5 >arch/i386/boot/Makefile &&
@@ -37,7 +31,7 @@ test_expect_success setup '
 
 '
 
-test_expect_success apply '
+test_expect_success SYMLINKS apply '
 
        git checkout test &&
        git diff --exit-code test &&
@@ -46,7 +40,7 @@ test_expect_success apply '
 
 '
 
-test_expect_success 'check result' '
+test_expect_success SYMLINKS 'check result' '
 
        git diff --exit-code master &&
        git diff --exit-code --cached master &&
index 8a676a5dcd113418c2bd4ea4aa885fddd5951a3a..6f6ee88b28bc5417035b45d87aaf4a9c974ab6c5 100755 (executable)
@@ -10,7 +10,8 @@ prepare_test_file () {
        #       X  RULE
        #       !  trailing-space
        #       @  space-before-tab
-       #       #  indent-with-non-tab
+       #       #  indent-with-non-tab (default tab width 8)
+       #       =  indent-with-non-tab,tabwidth=16
        #       %  tab-in-indent
        sed -e "s/_/ /g" -e "s/>/       /" <<-\EOF
                An_SP in an ordinary line>and a HT.
@@ -25,8 +26,8 @@ prepare_test_file () {
                ________>_Eight SP, a HT and a SP (@#%).
                _______________Fifteen SP (#).
                _______________>Fifteen SP and a HT (@#%).
-               ________________Sixteen SP (#).
-               ________________>Sixteen SP and a HT (@#%).
+               ________________Sixteen SP (#=).
+               ________________>Sixteen SP and a HT (@#%=).
                _____a__Five SP, a non WS, two SP.
                A line with a (!) trailing SP_
                A line with a (!) trailing HT>
@@ -121,6 +122,34 @@ test_expect_success 'whitespace=error-all, no rule (attribute)' '
 
 '
 
+test_expect_success 'spaces inserted by tab-in-indent' '
+
+       git config core.whitespace -trailing,-space,-indent,tab &&
+       rm -f .gitattributes &&
+       test_fix % &&
+       sed -e "s/_/ /g" -e "s/>/       /" <<-\EOF >expect &&
+               An_SP in an ordinary line>and a HT.
+               ________A HT (%).
+               ________A SP and a HT (@%).
+               _________A SP, a HT and a SP (@%).
+               _______Seven SP.
+               ________Eight SP (#).
+               ________Seven SP and a HT (@%).
+               ________________Eight SP and a HT (@#%).
+               _________Seven SP, a HT and a SP (@%).
+               _________________Eight SP, a HT and a SP (@#%).
+               _______________Fifteen SP (#).
+               ________________Fifteen SP and a HT (@#%).
+               ________________Sixteen SP (#=).
+               ________________________Sixteen SP and a HT (@#%=).
+               _____a__Five SP, a non WS, two SP.
+               A line with a (!) trailing SP_
+               A line with a (!) trailing HT>
+       EOF
+       test_cmp expect target
+
+'
+
 for t in - ''
 do
        case "$t" in '') tt='!' ;; *) tt= ;; esac
@@ -129,7 +158,7 @@ do
                case "$s" in '') ts='@' ;; *) ts= ;; esac
                for i in - ''
                do
-                       case "$i" in '') ti='#' ;; *) ti= ;; esac
+                       case "$i" in '') ti='#' ti16='=';; *) ti= ti16= ;; esac
                        for h in - ''
                        do
                                [ -z "$h$i" ] && continue
@@ -142,12 +171,22 @@ do
                                        test_fix "$tt$ts$ti$th"
                                '
 
+                               test_expect_success "rule=$rule,tabwidth=16" '
+                                       git config core.whitespace "$rule,tabwidth=16" &&
+                                       test_fix "$tt$ts$ti16$th"
+                               '
+
                                test_expect_success "rule=$rule (attributes)" '
                                        git config --unset core.whitespace &&
                                        echo "target whitespace=$rule" >.gitattributes &&
                                        test_fix "$tt$ts$ti$th"
                                '
 
+                               test_expect_success "rule=$rule,tabwidth=16 (attributes)" '
+                                       echo "target whitespace=$rule,tabwidth=16" >.gitattributes &&
+                                       test_fix "$tt$ts$ti16$th"
+                               '
+
                        done
                done
        done
@@ -176,9 +215,8 @@ test_expect_success 'trailing whitespace & no newline at the end of file' '
 '
 
 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
+       test_might_fail git config --unset core.whitespace &&
+       rm -f .gitattributes &&
 
        { echo a; echo b; echo c; } >one &&
        git add one &&
@@ -368,7 +406,7 @@ test_expect_success 'missing blanks at EOF must only match blank lines' '
        git diff -- one >patch &&
 
        echo a >one &&
-       test_must_fail git apply patch
+       test_must_fail git apply patch &&
        test_must_fail git apply --whitespace=fix patch &&
        test_must_fail git apply --ignore-space-change --whitespace=fix patch
 '
@@ -419,7 +457,7 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
        printf "b\r\n" >>one &&
        printf "c\r\n" >>one &&
        cp one save-one &&
-       printf "                 \r\n" >>one
+       printf "                 \r\n" >>one &&
        git add one &&
        printf "d\r\n" >>one &&
        cp one expect &&
@@ -436,7 +474,7 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
        printf "b\r\n" >>one &&
        printf "c\r\n" >>one &&
        cp one save-one &&
-       printf "                 \r\n" >>one
+       printf "                 \r\n" >>one &&
        git add one &&
        cp one expect &&
        printf "d\r\n" >>one &&
index 77200c0b2d969d621623f3be8621a3f9925d50a5..972946c174c18ee831d8595068c6ffa235c8a538 100755 (executable)
@@ -31,7 +31,7 @@ test_expect_success 'apply same filename with independent changes' '
 '
 
 test_expect_success 'apply same filename with overlapping changes' '
-       git reset --hard
+       git reset --hard &&
        modify "s/^d/z/" same_fn &&
        git diff > patch0 &&
        git add same_fn &&
@@ -44,8 +44,8 @@ test_expect_success 'apply same filename with overlapping changes' '
 '
 
 test_expect_success 'apply same new filename after rename' '
-       git reset --hard
-       git mv same_fn new_fn
+       git reset --hard &&
+       git mv same_fn new_fn &&
        modify "s/^d/z/" new_fn &&
        git add new_fn &&
        git diff -M --cached > patch1 &&
@@ -58,12 +58,12 @@ test_expect_success 'apply same new filename after rename' '
 '
 
 test_expect_success 'apply same old filename after rename -- should fail.' '
-       git reset --hard
-       git mv same_fn new_fn
+       git reset --hard &&
+       git mv same_fn new_fn &&
        modify "s/^d/z/" new_fn &&
        git add new_fn &&
        git diff -M --cached > patch1 &&
-       git mv new_fn same_fn
+       git mv new_fn same_fn &&
        modify "s/^e/y/" same_fn &&
        git diff >> patch1 &&
        git reset --hard &&
@@ -71,13 +71,13 @@ test_expect_success 'apply same old filename after rename -- should fail.' '
 '
 
 test_expect_success 'apply A->B (rename), C->A (rename), A->A -- should pass.' '
-       git reset --hard
-       git mv same_fn new_fn
+       git reset --hard &&
+       git mv same_fn new_fn &&
        modify "s/^d/z/" new_fn &&
        git add new_fn &&
        git diff -M --cached > patch1 &&
        git commit -m "a rename" &&
-       git mv other_fn same_fn
+       git mv other_fn same_fn &&
        modify "s/^e/y/" same_fn &&
        git add same_fn &&
        git diff -M --cached >> patch1 &&
index fc7af0493102f12438d59ac5ed6e3e96eda8c841..0d36ebdc8653d7d22ab5831c1f154db452e4558e 100755 (executable)
@@ -3,13 +3,7 @@
 test_description='applying patch with mode bits'
 
 . ./test-lib.sh
-
-if test "$(git config --bool core.filemode)" = false
-then
-       say 'filemode disabled on the filesystem'
-else
-       test_set_prereq FILEMODE
-fi
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 test_expect_success setup '
        echo original >file &&
index 7cfa2d6287ff7c6c0fc7fbc2e86fd622bbc901ce..d173acde0f2c44031003144fda9770f4b1e726b4 100755 (executable)
@@ -44,7 +44,7 @@ test_expect_success 'criss-cross rename' '
        git reset --hard &&
        mv file1 tmp &&
        mv file2 file1 &&
-       mv file3 file2
+       mv file3 file2 &&
        mv tmp file3 &&
        cp file1 file1-swapped &&
        cp file2 file2-swapped &&
index bb1ffe3b6cfe491137b34993fe2e3bed99908dff..a2bc1cd37d852a9cab79c946bf88ee9dc958b460 100755 (executable)
@@ -30,6 +30,7 @@ test_expect_success setup '
        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" &&
+       epocWest2="1969-12-31 16:00:00 -08:00" &&
 
        sed -e "s/TS0/$epocWest/" -e "s/TS1/$timeWest/" <c >createWest.patch &&
        sed -e "s/TS0/$epocEast/" -e "s/TS1/$timeEast/" <c >createEast.patch &&
@@ -46,6 +47,7 @@ test_expect_success setup '
        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 &&
+       sed -e "s/TS0/$timeWest/" -e "s/TS1/$epocWest2/" <d >removeWest2.patch &&
 
        echo something >something &&
        >empty
index 34218071b64600812acef5b5e3277346889eda7f..94da99075c55c790aae7a260ff6c4964f950c47c 100755 (executable)
@@ -8,7 +8,7 @@ test_description='git apply filename consistency check'
 . ./test-lib.sh
 
 test_expect_success setup '
-       cat > bad1.patch <<EOF
+       cat > bad1.patch <<EOF &&
 diff --git a/f b/f
 new file mode 100644
 index 0000000..d00491f
@@ -29,9 +29,9 @@ EOF
 '
 
 test_expect_success 'apply diff with inconsistent filenames in headers' '
-       test_must_fail git apply bad1.patch 2>err
-       grep "inconsistent new filename" err
-       test_must_fail git apply bad2.patch 2>err
+       test_must_fail git apply bad1.patch 2>err &&
+       grep "inconsistent new filename" err &&
+       test_must_fail git apply bad2.patch 2>err &&
        grep "inconsistent old filename" err
 '
 
index 1b82f93cffb1d4444a8c789f95931b86dd3d93b6..0043930ca6ab31f6cd6a0bf6464ceb281663f9e8 100755 (executable)
@@ -8,7 +8,7 @@ test_description='git apply submodule tests'
 . ./test-lib.sh
 
 test_expect_success setup '
-       cat > create-sm.patch <<EOF
+       cat > create-sm.patch <<EOF &&
 diff --git a/dir/sm b/dir/sm
 new file mode 160000
 index 0000000..0123456
diff --git a/t/t4135-apply-weird-filenames.sh b/t/t4135-apply-weird-filenames.sh
new file mode 100755 (executable)
index 0000000..bf5dc57
--- /dev/null
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+test_description='git apply with weird postimage filenames'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       vector=$TEST_DIRECTORY/t4135 &&
+
+       test_tick &&
+       git commit --allow-empty -m preimage &&
+       git tag preimage &&
+
+       reset_preimage() {
+               git checkout -f preimage^0 &&
+               git read-tree -u --reset HEAD &&
+               git update-index --refresh
+       } &&
+
+       test_when_finished "rm -f \"tab embedded.txt\"" &&
+       test_when_finished "rm -f '\''\"quoteembedded\".txt'\''" &&
+       if touch -- "tab        embedded.txt" '\''"quoteembedded".txt'\''
+       then
+               test_set_prereq FUNNYNAMES
+       fi
+'
+
+try_filename() {
+       desc=$1
+       postimage=$2
+       prereq=${3:-}
+       exp1=${4:-success}
+       exp2=${5:-success}
+       exp3=${6:-success}
+
+       test_expect_$exp1 $prereq "$desc, git-style file creation patch" "
+               echo postimage >expected &&
+               reset_preimage &&
+               rm -f '$postimage' &&
+               git apply -v \"\$vector\"/'git-$desc.diff' &&
+               test_cmp expected '$postimage'
+       "
+
+       test_expect_$exp2 $prereq "$desc, traditional patch" "
+               echo postimage >expected &&
+               reset_preimage &&
+               echo preimage >'$postimage' &&
+               git apply -v \"\$vector\"/'diff-$desc.diff' &&
+               test_cmp expected '$postimage'
+       "
+
+       test_expect_$exp3 $prereq "$desc, traditional file creation patch" "
+               echo postimage >expected &&
+               reset_preimage &&
+               rm -f '$postimage' &&
+               git apply -v \"\$vector\"/'add-$desc.diff' &&
+               test_cmp expected '$postimage'
+       "
+}
+
+try_filename 'plain'            'postimage.txt'
+try_filename 'with spaces'      'post image.txt'
+try_filename 'with tab'         'post  image.txt' FUNNYNAMES
+try_filename 'with backslash'   'post\image.txt' BSLASHPSPEC
+try_filename 'with quote'       '"postimage".txt' FUNNYNAMES success failure success
+
+test_expect_success 'whitespace-damaged traditional patch' '
+       echo postimage >expected &&
+       reset_preimage &&
+       rm -f postimage.txt &&
+       git apply -v "$vector/damaged.diff" &&
+       test_cmp expected postimage.txt
+'
+
+test_expect_success 'traditional patch with colon in timezone' '
+       echo postimage >expected &&
+       reset_preimage &&
+       rm -f "post image.txt" &&
+       git apply "$vector/funny-tz.diff" &&
+       test_cmp expected "post image.txt"
+'
+
+test_expect_success 'traditional, whitespace-damaged, colon in timezone' '
+       echo postimage >expected &&
+       reset_preimage &&
+       rm -f "post image.txt" &&
+       git apply "$vector/damaged-tz.diff" &&
+       test_cmp expected "post image.txt"
+'
+
+test_done
diff --git a/t/t4135/.gitignore b/t/t4135/.gitignore
new file mode 100644 (file)
index 0000000..3e58e65
--- /dev/null
@@ -0,0 +1,3 @@
+/file-creation/
+/trad-creation/
+/trad-modification/
diff --git a/t/t4135/add-plain.diff b/t/t4135/add-plain.diff
new file mode 100644 (file)
index 0000000..cf5970a
--- /dev/null
@@ -0,0 +1,5 @@
+diff -pruN a/postimage.txt b/postimage.txt
+--- a/postimage.txt    1969-12-31 18:00:00.000000000 -0600
++++ b/postimage.txt    2010-08-18 20:13:31.484002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with backslash.diff b/t/t4135/add-with backslash.diff
new file mode 100644 (file)
index 0000000..c6861e1
--- /dev/null
@@ -0,0 +1,5 @@
+diff -pruN a/post\image.txt b/post\image.txt
+--- a/post\image.txt   1969-12-31 18:00:00.000000000 -0600
++++ b/post\image.txt   2010-08-18 20:13:31.692002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with quote.diff b/t/t4135/add-with quote.diff
new file mode 100644 (file)
index 0000000..866de78
--- /dev/null
@@ -0,0 +1,5 @@
+diff -pruN a/"postimage".txt b/"postimage".txt
+--- a/"postimage".txt  1969-12-31 18:00:00.000000000 -0600
++++ b/"postimage".txt  2010-08-18 20:13:31.756002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with spaces.diff b/t/t4135/add-with spaces.diff
new file mode 100644 (file)
index 0000000..a9a1212
--- /dev/null
@@ -0,0 +1,5 @@
+diff -pruN a/post image.txt b/post image.txt
+--- a/post image.txt   1969-12-31 18:00:00.000000000 -0600
++++ b/post image.txt   2010-08-18 20:13:31.556002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/add-with tab.diff b/t/t4135/add-with tab.diff
new file mode 100644 (file)
index 0000000..bb67cb7
--- /dev/null
@@ -0,0 +1,5 @@
+diff -pruN a/post      image.txt b/post        image.txt
+--- a/post     image.txt       1969-12-31 18:00:00.000000000 -0600
++++ b/post     image.txt       2010-08-18 20:13:31.628002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/damaged-tz.diff b/t/t4135/damaged-tz.diff
new file mode 100644 (file)
index 0000000..07aaf08
--- /dev/null
@@ -0,0 +1,5 @@
+diff -urN -X /usr/people/jes/exclude-linux linux-2.6.12-rc2-mm3-vanilla/post image.txt linux-2.6.12-rc2-mm3/post image.txt
+--- linux-2.6.12-rc2-mm3-vanilla/post image.txt 1969-12-31 16:00:00 -08:00
++++ linux-2.6.12-rc2-mm3/post image.txt 2005-04-12 02:14:06 -07:00
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/damaged.diff b/t/t4135/damaged.diff
new file mode 100644 (file)
index 0000000..68f7ede
--- /dev/null
@@ -0,0 +1,5 @@
+diff -pruN a/postimage.txt b/postimage.txt
+--- a/postimage.txt     1969-12-31 18:00:00.000000000 -0600
++++ b/postimage.txt     2010-08-18 20:13:31.484002255 -0500
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/diff-plain.diff b/t/t4135/diff-plain.diff
new file mode 100644 (file)
index 0000000..acedcfa
--- /dev/null
@@ -0,0 +1,5 @@
+--- postimage.txt.orig 2010-08-18 20:13:31.432002255 -0500
++++ postimage.txt      2010-08-18 20:13:31.432002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with backslash.diff b/t/t4135/diff-with backslash.diff
new file mode 100644 (file)
index 0000000..9068a61
--- /dev/null
@@ -0,0 +1,5 @@
+--- post\image.txt.orig        2010-08-18 20:13:31.680002255 -0500
++++ post\image.txt     2010-08-18 20:13:31.680002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with quote.diff b/t/t4135/diff-with quote.diff
new file mode 100644 (file)
index 0000000..c8e8cc1
--- /dev/null
@@ -0,0 +1,5 @@
+--- "postimage".txt.orig       2010-08-18 20:13:31.744002255 -0500
++++ "postimage".txt    2010-08-18 20:13:31.744002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with spaces.diff b/t/t4135/diff-with spaces.diff
new file mode 100644 (file)
index 0000000..3512056
--- /dev/null
@@ -0,0 +1,5 @@
+--- post image.txt.orig        2010-08-18 20:13:31.544002255 -0500
++++ post image.txt     2010-08-18 20:13:31.544002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/diff-with tab.diff b/t/t4135/diff-with tab.diff
new file mode 100644 (file)
index 0000000..4e6d9b2
--- /dev/null
@@ -0,0 +1,5 @@
+--- post       image.txt.orig  2010-08-18 20:13:31.616002255 -0500
++++ post       image.txt       2010-08-18 20:13:31.616002255 -0500
+@@ -1 +1 @@
+-preimage
++postimage
diff --git a/t/t4135/funny-tz.diff b/t/t4135/funny-tz.diff
new file mode 100644 (file)
index 0000000..998e3a8
--- /dev/null
@@ -0,0 +1,5 @@
+diff -urN -X /usr/people/jes/exclude-linux linux-2.6.12-rc2-mm3-vanilla/post image.txt linux-2.6.12-rc2-mm3/post image.txt
+--- linux-2.6.12-rc2-mm3-vanilla/post image.txt        1969-12-31 16:00:00 -08:00
++++ linux-2.6.12-rc2-mm3/post image.txt        2005-04-12 02:14:06 -07:00
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-plain.diff b/t/t4135/git-plain.diff
new file mode 100644 (file)
index 0000000..db47d1a
--- /dev/null
@@ -0,0 +1,7 @@
+diff --git a/postimage.txt b/postimage.txt
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ b/postimage.txt
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with backslash.diff b/t/t4135/git-with backslash.diff
new file mode 100644 (file)
index 0000000..0e84a10
--- /dev/null
@@ -0,0 +1,7 @@
+diff --git "a/post\\image.txt" "b/post\\image.txt"
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ "b/post\\image.txt"
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with quote.diff b/t/t4135/git-with quote.diff
new file mode 100644 (file)
index 0000000..bdbea8a
--- /dev/null
@@ -0,0 +1,7 @@
+diff --git "a/\"postimage\".txt" "b/\"postimage\".txt"
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ "b/\"postimage\".txt"
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with spaces.diff b/t/t4135/git-with spaces.diff
new file mode 100644 (file)
index 0000000..baaa810
--- /dev/null
@@ -0,0 +1,7 @@
+diff --git a/post image.txt b/post image.txt
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ b/post image.txt   
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/git-with tab.diff b/t/t4135/git-with tab.diff
new file mode 100644 (file)
index 0000000..cca3c92
--- /dev/null
@@ -0,0 +1,7 @@
+diff --git "a/post\timage.txt" "b/post\timage.txt"
+new file mode 100644
+index 0000000..eff0c54
+--- /dev/null
++++ "b/post\timage.txt"
+@@ -0,0 +1 @@
++postimage
diff --git a/t/t4135/make-patches b/t/t4135/make-patches
new file mode 100755 (executable)
index 0000000..f5f45dd
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+do_filename() {
+       desc=$1
+       postimage=$2
+
+       rm -fr file-creation &&
+       git init file-creation &&
+       (
+               cd file-creation &&
+               git commit --allow-empty -m init &&
+               echo postimage >"$postimage" &&
+               git add -N "$postimage" &&
+               git diff HEAD >"../git-$desc.diff"
+       ) &&
+
+       rm -fr trad-modification &&
+       mkdir trad-modification &&
+       (
+               cd trad-modification &&
+               echo preimage >"$postimage.orig" &&
+               echo postimage >"$postimage" &&
+               ! diff -u "$postimage.orig" "$postimage" >"../diff-$desc.diff"
+       ) &&
+
+       rm -fr trad-creation &&
+       mkdir trad-creation &&
+       (
+               cd trad-creation &&
+               mkdir a b &&
+               echo postimage >"b/$postimage" &&
+               ! diff -pruN a b >"../add-$desc.diff"
+       )
+}
+
+do_filename plain postimage.txt &&
+do_filename 'with spaces' 'post image.txt' &&
+do_filename 'with tab' 'post   image.txt' &&
+do_filename 'with backslash' 'post\image.txt' &&
+do_filename 'with quote' '"postimage".txt' &&
+expand add-plain.diff >damaged.diff ||
+{
+       echo >&2 Failed. &&
+       exit 1
+}
index 1c3d8ed548e629689517661cd1fc6c21d98ccc80..850fc96d1f07b19310cb4672ab44374b37b82d67 100755 (executable)
@@ -219,7 +219,7 @@ test_expect_success 'am stays in branch' '
 
 test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
        git format-patch --stdout HEAD^ >patch3 &&
-       sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4
+       sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4 &&
        rm -fr .git/rebase-apply &&
        git reset --hard &&
        git checkout HEAD^ &&
index b55c4117884744db8eda17e42fe05e0e65216215..c95c4ccc393d0863ad53b6a2a684893282d7d9e6 100755 (executable)
@@ -62,4 +62,13 @@ do
 
 done
 
+test_expect_success 'am --abort will keep the local commits intact' '
+       test_must_fail git am 0004-*.patch &&
+       test_commit unrelated &&
+       git rev-parse HEAD >expect &&
+       git am --abort &&
+       git rev-parse HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 70856d07ed113b731d149bde73fe7b4eb25a72f2..36255d608a7af7d85f479986e302138401f25a8d 100755 (executable)
 #
 
 test_description='git rerere
+
+! [fifth] version1
+ ! [first] first
+  ! [fourth] version1
+   ! [master] initial
+    ! [second] prefer first over second
+     ! [third] version2
+------
+     + [third] version2
++      [fifth] version1
+  +    [fourth] version1
++ +  + [third^] third
+    -  [second] prefer first over second
+ +  +  [first] first
+    +  [second^] second
+++++++ [master] initial
 '
 
 . ./test-lib.sh
 
-test_expect_success 'setup' "
-       cat > a1 <<- EOF &&
+test_expect_success 'setup' '
+       cat >a1 <<-\EOF &&
        Some title
        ==========
-       Whether 'tis nobler in the mind to suffer
+       Whether '\''tis nobler in the mind to suffer
        The slings and arrows of outrageous fortune,
        Or to take arms against a sea of troubles,
        And by opposing end them? To die: to sleep;
        No more; and by a sleep to say we end
        The heart-ache and the thousand natural shocks
-       That flesh is heir to, 'tis a consummation
-       Devoutly to be wish'd.
+       That flesh is heir to, '\''tis a consummation
+       Devoutly to be wish'\''d.
        EOF
 
        git add a1 &&
+       test_tick &&
        git commit -q -a -m initial &&
 
-       git checkout -b first &&
-       cat >> a1 <<- EOF &&
+       cat >>a1 <<-\EOF &&
        Some title
        ==========
        To die, to sleep;
-       To sleep: perchance to dream: ay, there's the rub;
+       To sleep: perchance to dream: ay, there'\''s the rub;
        For in that sleep of death what dreams may come
        When we have shuffled off this mortal coil,
-       Must give us pause: there's the respect
+       Must give us pause: there'\''s the respect
        That makes calamity of so long life;
        EOF
+
+       git checkout -b first &&
+       test_tick &&
        git commit -q -a -m first &&
 
        git checkout -b second master &&
        git show first:a1 |
-       sed -e 's/To die, t/To die! T/' -e 's/Some title/Some Title/' > a1 &&
-       echo '* END *' >>a1 &&
+       sed -e "s/To die, t/To die! T/" -e "s/Some title/Some Title/" >a1 &&
+       echo "* END *" >>a1 &&
+       test_tick &&
        git commit -q -a -m second
-"
+'
 
 test_expect_success 'nothing recorded without rerere' '
-       (rm -rf .git/rr-cache; git config rerere.enabled false) &&
+       rm -rf .git/rr-cache &&
+       git config rerere.enabled false &&
        test_must_fail git merge first &&
        ! test -d .git/rr-cache
 '
 
-# activate rerere, old style
-test_expect_success 'conflicting merge' '
+test_expect_success 'activate rerere, old style (conflicting merge)' '
        git reset --hard &&
        mkdir .git/rr-cache &&
-       git config --unset rerere.enabled &&
-       test_must_fail git merge first
-'
+       test_might_fail git config --unset rerere.enabled &&
+       test_must_fail git merge first &&
 
-sha1=$(perl -pe 's/    .*//' .git/MERGE_RR)
-rr=.git/rr-cache/$sha1
-test_expect_success 'recorded preimage' "grep ^=======$ $rr/preimage"
+       sha1=$(perl -pe "s/     .*//" .git/MERGE_RR) &&
+       rr=.git/rr-cache/$sha1 &&
+       grep "^=======\$" $rr/preimage &&
+       ! test -f $rr/postimage &&
+       ! test -f $rr/thisimage
+'
 
 test_expect_success 'rerere.enabled works, too' '
        rm -rf .git/rr-cache &&
        git config rerere.enabled true &&
        git reset --hard &&
        test_must_fail git merge first &&
+
+       sha1=$(perl -pe "s/     .*//" .git/MERGE_RR) &&
+       rr=.git/rr-cache/$sha1 &&
        grep ^=======$ $rr/preimage
 '
 
-test_expect_success 'no postimage or thisimage yet' \
-       "test ! -f $rr/postimage -a ! -f $rr/thisimage"
+test_expect_success 'set up rr-cache' '
+       rm -rf .git/rr-cache &&
+       git config rerere.enabled true &&
+       git reset --hard &&
+       test_must_fail git merge first &&
+       sha1=$(perl -pe "s/     .*//" .git/MERGE_RR) &&
+       rr=.git/rr-cache/$sha1
+'
 
-test_expect_success 'preimage has right number of lines' '
+test_expect_success 'rr-cache looks sane' '
+       # no postimage or thisimage yet
+       ! test -f $rr/postimage &&
+       ! test -f $rr/thisimage &&
 
+       # preimage has right number of lines
        cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
+       echo $cnt &&
        test $cnt = 13
-
 '
 
-git show first:a1 > a1
-
-cat > expect << EOF
---- a/a1
-+++ b/a1
-@@ -1,4 +1,4 @@
--Some Title
-+Some title
- ==========
- Whether 'tis nobler in the mind to suffer
- The slings and arrows of outrageous fortune,
-@@ -8,21 +8,11 @@
- The heart-ache and the thousand natural shocks
- That flesh is heir to, 'tis a consummation
- Devoutly to be wish'd.
--<<<<<<<
--Some Title
--==========
--To die! To sleep;
--=======
- Some title
- ==========
- To die, to sleep;
-->>>>>>>
- To sleep: perchance to dream: ay, there's the rub;
- For in that sleep of death what dreams may come
- When we have shuffled off this mortal coil,
- Must give us pause: there's the respect
- That makes calamity of so long life;
--<<<<<<<
--=======
--* END *
-->>>>>>>
-EOF
-git rerere diff > out
-
-test_expect_success 'rerere diff' 'test_cmp expect out'
-
-cat > expect << EOF
-a1
-EOF
-
-git rerere status > out
-
-test_expect_success 'rerere status' 'test_cmp expect out'
-
-test_expect_success 'commit succeeds' \
-       "git commit -q -a -m 'prefer first over second'"
-
-test_expect_success 'recorded postimage' "test -f $rr/postimage"
-
-test_expect_success 'another conflicting merge' '
-       git checkout -b third master &&
-       git show second^:a1 | sed "s/To die: t/To die! T/" > a1 &&
-       git commit -q -a -m third &&
-       test_must_fail git pull . first
+test_expect_success 'rerere diff' '
+       git show first:a1 >a1 &&
+       cat >expect <<-\EOF &&
+       --- a/a1
+       +++ b/a1
+       @@ -1,4 +1,4 @@
+       -Some Title
+       +Some title
+        ==========
+        Whether '\''tis nobler in the mind to suffer
+        The slings and arrows of outrageous fortune,
+       @@ -8,21 +8,11 @@
+        The heart-ache and the thousand natural shocks
+        That flesh is heir to, '\''tis a consummation
+        Devoutly to be wish'\''d.
+       -<<<<<<<
+       -Some Title
+       -==========
+       -To die! To sleep;
+       -=======
+        Some title
+        ==========
+        To die, to sleep;
+       ->>>>>>>
+        To sleep: perchance to dream: ay, there'\''s the rub;
+        For in that sleep of death what dreams may come
+        When we have shuffled off this mortal coil,
+        Must give us pause: there'\''s the respect
+        That makes calamity of so long life;
+       -<<<<<<<
+       -=======
+       -* END *
+       ->>>>>>>
+       EOF
+       git rerere diff >out &&
+       test_cmp expect out
 '
 
-git show first:a1 | sed 's/To die: t/To die! T/' > expect
-test_expect_success 'rerere kicked in' "! grep ^=======$ a1"
+test_expect_success 'rerere status' '
+       echo a1 >expect &&
+       git rerere status >out &&
+       test_cmp expect out
+'
 
-test_expect_success 'rerere prefers first change' 'test_cmp a1 expect'
+test_expect_success 'first postimage wins' '
+       git show first:a1 | sed "s/To die: t/To die! T/" >expect &&
 
-rm $rr/postimage
-echo "$sha1    a1" | perl -pe 'y/\012/\000/' > .git/MERGE_RR
+       git commit -q -a -m "prefer first over second" &&
+       test -f $rr/postimage &&
 
-test_expect_success 'rerere clear' 'git rerere clear'
+       oldmtimepost=$(test-chmtime -v -60 $rr/postimage | cut -f 1) &&
 
-test_expect_success 'clear removed the directory' "test ! -d $rr"
+       git checkout -b third master &&
+       git show second^:a1 | sed "s/To die: t/To die! T/" >a1 &&
+       git commit -q -a -m third &&
 
-mkdir $rr
-echo Hello > $rr/preimage
-echo World > $rr/postimage
+       test_must_fail git pull . first &&
+       # rerere kicked in
+       ! grep "^=======\$" a1 &&
+       test_cmp expect a1
+'
 
-sha2=4000000000000000000000000000000000000000
-rr2=.git/rr-cache/$sha2
-mkdir $rr2
-echo Hello > $rr2/preimage
+test_expect_success 'rerere updates postimage timestamp' '
+       newmtimepost=$(test-chmtime -v +0 $rr/postimage | cut -f 1) &&
+       test $oldmtimepost -lt $newmtimepost
+'
 
-almost_15_days_ago=$((60-15*86400))
-just_over_15_days_ago=$((-1-15*86400))
-almost_60_days_ago=$((60-60*86400))
-just_over_60_days_ago=$((-1-60*86400))
+test_expect_success 'rerere clear' '
+       rm $rr/postimage &&
+       echo "$sha1     a1" | perl -pe "y/\012/\000/" >.git/MERGE_RR &&
+       git rerere clear &&
+       ! test -d $rr
+'
 
-test-chmtime =$almost_60_days_ago $rr/preimage
-test-chmtime =$almost_15_days_ago $rr2/preimage
+test_expect_success 'set up for garbage collection tests' '
+       mkdir -p $rr &&
+       echo Hello >$rr/preimage &&
+       echo World >$rr/postimage &&
 
-test_expect_success 'garbage collection (part1)' 'git rerere gc'
+       sha2=4000000000000000000000000000000000000000 &&
+       rr2=.git/rr-cache/$sha2 &&
+       mkdir $rr2 &&
+       echo Hello >$rr2/preimage &&
 
-test_expect_success 'young records still live' \
-       "test -f $rr/preimage && test -f $rr2/preimage"
+       almost_15_days_ago=$((60-15*86400)) &&
+       just_over_15_days_ago=$((-1-15*86400)) &&
+       almost_60_days_ago=$((60-60*86400)) &&
+       just_over_60_days_ago=$((-1-60*86400)) &&
 
-test-chmtime =$just_over_60_days_ago $rr/preimage
-test-chmtime =$just_over_15_days_ago $rr2/preimage
+       test-chmtime =$just_over_60_days_ago $rr/preimage &&
+       test-chmtime =$almost_60_days_ago $rr/postimage &&
+       test-chmtime =$almost_15_days_ago $rr2/preimage
+'
 
-test_expect_success 'garbage collection (part2)' 'git rerere gc'
+test_expect_success 'gc preserves young or recently used records' '
+       git rerere gc &&
+       test -f $rr/preimage &&
+       test -f $rr2/preimage
+'
 
-test_expect_success 'old records rest in peace' \
-       "test ! -f $rr/preimage && test ! -f $rr2/preimage"
+test_expect_success 'old records rest in peace' '
+       test-chmtime =$just_over_60_days_ago $rr/postimage &&
+       test-chmtime =$just_over_15_days_ago $rr2/preimage &&
+       git rerere gc &&
+       ! test -f $rr/preimage &&
+       ! test -f $rr2/preimage
+'
 
-test_expect_success 'file2 added differently in two branches' '
+test_expect_success 'setup: file2 added differently in two branches' '
        git reset --hard &&
+
        git checkout -b fourth &&
-       echo Hallo > file2 &&
+       echo Hallo >file2 &&
        git add file2 &&
+       test_tick &&
        git commit -m version1 &&
+
        git checkout third &&
-       echo Bello > file2 &&
+       echo Bello >file2 &&
        git add file2 &&
+       test_tick &&
        git commit -m version2 &&
+
        test_must_fail git merge fourth &&
-       echo Cello > file2 &&
+       echo Cello >file2 &&
        git add file2 &&
        git commit -m resolution
 '
 
 test_expect_success 'resolution was recorded properly' '
+       echo Cello >expected &&
+
        git reset --hard HEAD~2 &&
        git checkout -b fifth &&
-       echo Hallo > file3 &&
+
+       echo Hallo >file3 &&
        git add file3 &&
+       test_tick &&
        git commit -m version1 &&
+
        git checkout third &&
-       echo Bello > file3 &&
+       echo Bello >file3 &&
        git add file3 &&
+       test_tick &&
        git commit -m version2 &&
        git tag version2 &&
+
        test_must_fail git merge fifth &&
-       test Cello = "$(cat file3)" &&
-       test 0 != $(git ls-files -u | wc -l)
+       test_cmp expected file3 &&
+       test_must_fail git update-index --refresh
 '
 
 test_expect_success 'rerere.autoupdate' '
-       git config rerere.autoupdate true
+       git config rerere.autoupdate true &&
        git reset --hard &&
        git checkout version2 &&
        test_must_fail git merge fifth &&
-       test 0 = $(git ls-files -u | wc -l)
+       git update-index --refresh
 '
 
 test_expect_success 'merge --rerere-autoupdate' '
-       git config --unset rerere.autoupdate
+       test_might_fail git config --unset rerere.autoupdate &&
        git reset --hard &&
        git checkout version2 &&
        test_must_fail git merge --rerere-autoupdate fifth &&
-       test 0 = $(git ls-files -u | wc -l)
+       git update-index --refresh
 '
 
 test_expect_success 'merge --no-rerere-autoupdate' '
-       git config rerere.autoupdate true
+       headblob=$(git rev-parse version2:file3) &&
+       mergeblob=$(git rev-parse fifth:file3) &&
+       cat >expected <<-EOF &&
+       100644 $headblob 2      file3
+       100644 $mergeblob 3     file3
+       EOF
+
+       git config rerere.autoupdate true &&
        git reset --hard &&
        git checkout version2 &&
        test_must_fail git merge --no-rerere-autoupdate fifth &&
-       test 2 = $(git ls-files -u | wc -l)
+       git ls-files -u >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'set up an unresolved merge' '
+       headblob=$(git rev-parse version2:file3) &&
+       mergeblob=$(git rev-parse fifth:file3) &&
+       cat >expected.unresolved <<-EOF &&
+       100644 $headblob 2      file3
+       100644 $mergeblob 3     file3
+       EOF
+
+       test_might_fail git config --unset rerere.autoupdate &&
+       git reset --hard &&
+       git checkout version2 &&
+       fifth=$(git rev-parse fifth) &&
+       echo "$fifth            branch 'fifth' of ." |
+       git fmt-merge-msg >msg &&
+       ancestor=$(git merge-base version2 fifth) &&
+       test_must_fail git merge-recursive "$ancestor" -- HEAD fifth &&
+
+       git ls-files --stage >failedmerge &&
+       cp file3 file3.conflict &&
+
+       git ls-files -u >actual &&
+       test_cmp expected.unresolved actual
+'
+
+test_expect_success 'explicit rerere' '
+       test_might_fail git config --unset rerere.autoupdate &&
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       test_must_fail git update-index --refresh -q &&
+
+       git rerere &&
+       git ls-files -u >actual &&
+       test_cmp expected.unresolved actual
+'
+
+test_expect_success 'explicit rerere with autoupdate' '
+       git config rerere.autoupdate true &&
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       test_must_fail git update-index --refresh -q &&
+
+       git rerere &&
+       git update-index --refresh
+'
+
+test_expect_success 'explicit rerere --rerere-autoupdate overrides' '
+       git config rerere.autoupdate false &&
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       git rerere &&
+       git ls-files -u >actual1 &&
+
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       git rerere --rerere-autoupdate &&
+       git update-index --refresh &&
+
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       git rerere --rerere-autoupdate --no-rerere-autoupdate &&
+       git ls-files -u >actual2 &&
+
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       git rerere --rerere-autoupdate --no-rerere-autoupdate --rerere-autoupdate &&
+       git update-index --refresh &&
+
+       test_cmp expected.unresolved actual1 &&
+       test_cmp expected.unresolved actual2
+'
+
+test_expect_success 'rerere --no-no-rerere-autoupdate' '
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       test_must_fail git rerere --no-no-rerere-autoupdate 2>err &&
+       grep [Uu]sage err &&
+       test_must_fail git update-index --refresh
+'
+
+test_expect_success 'rerere -h' '
+       test_must_fail git rerere -h >help &&
+       grep [Uu]sage help
 '
 
 test_done
index cdb70b4b3356eeb45bb6e5ac62d1f82eb6b3ccdc..6872ba1a42ce289c0983b1ed62f05defb493bf49 100755 (executable)
@@ -35,7 +35,7 @@ test_expect_success 'setup' '
                tr 1234 "\370\235\204\236")" a1 &&
 
        echo 5 >a1 &&
-       git commit --quiet -m "a                                                                12      34      56      78" a1
+       git commit --quiet -m "a                                                                12      34      56      78" a1 &&
 
        echo 6 >a1 &&
        git commit --quiet -m "Commit by someone else" \
index ead204e5cb77804ee1b54cc1b2f31d35312fa758..2fcc31a6f3d346e533b697eecf853e219d174cd0 100755 (executable)
@@ -100,13 +100,11 @@ test_expect_success 'oneline' '
 
 test_expect_success 'diff-filter=A' '
 
-       actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) &&
-       expect=$(echo fifth ; echo fourth ; echo third ; echo initial) &&
-       test "$actual" = "$expect" || {
-               echo Oops
-               echo "Actual: $actual"
-               false
-       }
+       git log --pretty="format:%s" --diff-filter=A HEAD > actual &&
+       git log --pretty="format:%s" --diff-filter A HEAD > actual-separate &&
+       printf "fifth\nfourth\nthird\ninitial" > expect &&
+       test_cmp expect actual &&
+       test_cmp expect actual-separate
 
 '
 
@@ -193,7 +191,7 @@ test_expect_success 'git show <commits> leaves list of commits as given' '
 test_expect_success 'setup case sensitivity tests' '
        echo case >one &&
        test_tick &&
-       git add one
+       git add one &&
        git commit -a -m Second
 '
 
@@ -203,6 +201,13 @@ test_expect_success 'log --grep' '
        test_cmp expect actual
 '
 
+test_expect_success 'log --grep option parsing' '
+       echo second >expect &&
+       git log -1 --pretty="tformat:%s" --grep sec >actual &&
+       test_cmp expect actual &&
+       test_must_fail git log -1 --pretty="tformat:%s" --grep
+'
+
 test_expect_success 'log -i --grep' '
        echo Second >expect &&
        git log -1 --pretty="tformat:%s" -i --grep=sec >actual &&
@@ -336,7 +341,7 @@ test_expect_success 'set up more tangled history' '
        test_commit octopus-b &&
        git checkout master &&
        test_commit seventh &&
-       git merge octopus-a octopus-b
+       git merge octopus-a octopus-b &&
        git merge reach
 '
 
@@ -388,7 +393,7 @@ test_expect_success 'log --graph with merge' '
 '
 
 test_expect_success 'log.decorate configuration' '
-       git config --unset-all log.decorate || :
+       test_might_fail git config --unset-all log.decorate &&
 
        git log --oneline >expect.none &&
        git log --oneline --decorate >expect.short &&
@@ -416,6 +421,15 @@ test_expect_success 'log.decorate configuration' '
        git log --oneline --decorate=full >actual &&
        test_cmp expect.full actual &&
 
+       git config --unset-all log.decorate &&
+       git config log.decorate 1 &&
+       git log --oneline >actual &&
+       test_cmp expect.short actual &&
+       git log --oneline --decorate=full >actual &&
+       test_cmp expect.full actual &&
+       git log --oneline --decorate=no >actual &&
+       test_cmp expect.none actual &&
+
        git config --unset-all log.decorate &&
        git config log.decorate short &&
        git log --oneline >actual &&
index 9a7d1b446634382d91286a6c062cc9bcda0bd33f..e818de6ddd904378265cb11f2d48075cda474f5f 100755 (executable)
@@ -4,6 +4,14 @@ test_description='.mailmap configurations'
 
 . ./test-lib.sh
 
+fuzz_blame () {
+       sed "
+               s/$_x05[0-9a-f][0-9a-f][0-9a-f]/OBJID/g
+               s/$_x05[0-9a-f][0-9a-f]/OBJI/g
+               s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g
+       " "$@"
+}
+
 test_expect_success setup '
        echo one >one &&
        git add one &&
@@ -11,6 +19,7 @@ test_expect_success setup '
        git commit -m initial &&
        echo two >>one &&
        git add one &&
+       test_tick &&
        git commit --author "nick1 <bugs@company.xx>" -m second
 '
 
@@ -54,7 +63,7 @@ Repo Guy (1):
 
 EOF
 test_expect_success 'mailmap.file set' '
-       mkdir internal_mailmap &&
+       mkdir -p internal_mailmap &&
        echo "Internal Guy <bugs@company.xx>" > internal_mailmap/.mailmap &&
        git config mailmap.file internal_mailmap/.mailmap &&
        git shortlog HEAD >actual &&
@@ -92,6 +101,40 @@ test_expect_success 'mailmap.file non-existant' '
        test_cmp expect actual
 '
 
+cat >expect <<\EOF
+Internal Guy (1):
+      second
+
+Repo Guy (1):
+      initial
+
+EOF
+
+test_expect_success 'name entry after email entry' '
+       mkdir -p internal_mailmap &&
+       echo "<bugs@company.xy> <bugs@company.xx>" >internal_mailmap/.mailmap &&
+       echo "Internal Guy <bugs@company.xx>" >>internal_mailmap/.mailmap &&
+       git shortlog HEAD >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Internal Guy (1):
+      second
+
+Repo Guy (1):
+      initial
+
+EOF
+
+test_expect_success 'name entry after email entry, case-insensitive' '
+       mkdir -p internal_mailmap &&
+       echo "<bugs@company.xy> <bugs@company.xx>" >internal_mailmap/.mailmap &&
+       echo "Internal Guy <BUGS@Company.xx>" >>internal_mailmap/.mailmap &&
+       git shortlog HEAD >actual &&
+       test_cmp expect actual
+'
+
 cat >expect <<\EOF
 A U Thor (1):
       initial
@@ -101,7 +144,7 @@ nick1 (1):
 
 EOF
 test_expect_success 'No mailmap files, but configured' '
-       rm .mailmap &&
+       rm -f .mailmap internal_mailmap/.mailmap &&
        git shortlog HEAD >actual &&
        test_cmp expect actual
 '
@@ -153,7 +196,7 @@ test_expect_success 'Shortlog output (complex mapping)' '
        test_tick &&
        git commit --author "CTO <cto@coompany.xx>" -m seventh &&
 
-       mkdir internal_mailmap &&
+       mkdir -p internal_mailmap &&
        echo "Committed <committer@example.com>" > internal_mailmap/.mailmap &&
        echo "<cto@company.xx>                       <cto@coompany.xx>" >> internal_mailmap/.mailmap &&
        echo "Some Dude <some@dude.xx>         nick1 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
@@ -198,18 +241,18 @@ test_expect_success 'Log output (complex mapping)' '
 
 # git blame
 cat >expect <<\EOF
-^3a2fdcb (A U Thor     2005-04-07 15:13:13 -0700 1) one
-7de6f99b (Some Dude    2005-04-07 15:13:13 -0700 2) two
-5815879d (Other Author 2005-04-07 15:14:13 -0700 3) three
-ff859d96 (Other Author 2005-04-07 15:15:13 -0700 4) four
-5ab6d4fa (Santa Claus  2005-04-07 15:16:13 -0700 5) five
-38a42d8b (Santa Claus  2005-04-07 15:17:13 -0700 6) six
-8ddc0386 (CTO          2005-04-07 15:18:13 -0700 7) seven
+^OBJI (A U Thor     DATE 1) one
+OBJID (Some Dude    DATE 2) two
+OBJID (Other Author DATE 3) three
+OBJID (Other Author DATE 4) four
+OBJID (Santa Claus  DATE 5) five
+OBJID (Santa Claus  DATE 6) six
+OBJID (CTO          DATE 7) seven
 EOF
-
 test_expect_success 'Blame output (complex mapping)' '
        git blame one >actual &&
-       test_cmp expect actual
+       fuzz_blame actual >actual.fuzz &&
+       test_cmp expect actual.fuzz
 '
 
 test_done
index f603c1b1336c4a00889177376d9b51077c9cc2ac..e758e634a34762c6a2e085e0f6ea8fc5cc00af5e 100755 (executable)
@@ -59,7 +59,7 @@ test_expect_success 'interrupted am --directory="frotz nitfol"' '
 '
 
 test_expect_success 'apply to a funny path' '
-       with_sq="with'\''sq"
+       with_sq="with'\''sq" &&
        rm -fr .git/rebase-apply &&
        git reset --hard initial &&
        git am --directory="$with_sq" "$tm"/am-test-5-2 &&
index 27bfba55bd9631a666ee382b343c3c04af1ad26d..cff1b3e0502e96ff6232611ef70c24752a970a96 100755 (executable)
@@ -94,7 +94,7 @@ 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' \
+test_expect_success NOT_MINGW 'git archive --remote' \
     'git archive --remote=. HEAD >b5.tar &&
     test_cmp b.tar b5.tar'
 
index bbb9c1251d97bdaf5bca8f8260e481c3cac56dea..602806d09cda72c7bf0f407da35b2fb859404bd7 100755 (executable)
@@ -12,7 +12,7 @@ TRASH=`pwd`
 
 test_expect_success \
     'setup' \
-    'rm -f .git/index*
+    'rm -f .git/index* &&
      perl -e "print \"a\" x 4096;" > a &&
      perl -e "print \"b\" x 4096;" > b &&
      perl -e "print \"c\" x 4096;" > c &&
index 0a24e61ff942ee91dfb25fe490330a0272480ac2..2fc5af6007c75b7789cf2a91163939e24e00ac09 100755 (executable)
@@ -8,7 +8,7 @@ test_description='mmap sliding window tests'
 
 test_expect_success \
     'setup' \
-    'rm -f .git/index*
+    'rm -f .git/index* &&
      for i in a b c
      do
          echo $i >$i &&
@@ -48,7 +48,7 @@ test_expect_success \
      git repack -a -d &&
      test "`git count-objects`" = "0 objects, 0 kilobytes" &&
      pack2=`ls .git/objects/pack/*.pack` &&
-     test -f "$pack2"
+     test -f "$pack2" &&
      test "$pack1" \!= "$pack2"'
 
 test_expect_success \
index fb3a270822c9a4ac2e4f79bfa71c784291859d4f..b34ea93a8056a7ae0edf111ffe14c99b7c5b33c5 100755 (executable)
@@ -8,7 +8,7 @@ test_description='pack index with 64-bit offsets and object CRC'
 
 test_expect_success \
     'setup' \
-    'rm -rf .git
+    'rm -rf .git &&
      git init &&
      git config pack.threads 1 &&
      i=1 &&
index c718253673ec8b3baf635c4dc0b05fe127c6f4cd..b0b2684a1f879cc173bf2bc67902724ed8f25d48 100755 (executable)
@@ -94,6 +94,29 @@ test_expect_success 'refuse deleting push with denyDeletes' '
        test_must_fail git send-pack ./victim :extra master
 '
 
+test_expect_success 'cannot override denyDeletes with git -c send-pack' '
+       (
+               cd victim &&
+               test_might_fail git branch -D extra &&
+               git config receive.denyDeletes true &&
+               git branch extra master
+       ) &&
+       test_must_fail git -c receive.denyDeletes=false \
+                                       send-pack ./victim :extra master
+'
+
+test_expect_success 'override denyDeletes with git -c receive-pack' '
+       (
+               cd victim &&
+               test_might_fail git branch -D extra &&
+               git config receive.denyDeletes true &&
+               git branch extra master
+       ) &&
+       git send-pack \
+               --receive-pack="git -c receive.denyDeletes=false receive-pack" \
+               ./victim :extra master
+'
+
 test_expect_success 'denyNonFastforwards trumps --force' '
        (
            cd victim &&
@@ -106,7 +129,7 @@ test_expect_success 'denyNonFastforwards trumps --force' '
        test "$victim_orig" = "$victim_head"
 '
 
-test_expect_success 'push --all excludes remote tracking hierarchy' '
+test_expect_success 'push --all excludes remote-tracking hierarchy' '
        mkdir parent &&
        (
            cd parent &&
index 552da65a61e8f78d40f8e19cfc5b73696ebcf283..baa670cea5d8a9d8b4b4578de69917fe76f71d46 100755 (executable)
@@ -10,7 +10,11 @@ test_expect_success 'setup' '
        test_commit A foo A &&
        test_commit B foo B &&
        test_commit C foo C &&
-       test_commit D foo D
+       test_commit D foo D &&
+       git checkout A^0 &&
+       test_commit E bar E &&
+       test_commit F foo F &&
+       git checkout master
 '
 
 mkdir .git/hooks
@@ -79,6 +83,18 @@ EOF
        verify_hook_input
 '
 
+test_expect_success 'git rebase --skip the last one' '
+       git reset --hard F &&
+       clear_hook_input &&
+       test_must_fail git rebase --onto D A &&
+       git rebase --skip &&
+       echo rebase >expected.args &&
+       cat >expected.data <<EOF &&
+$(git rev-parse E) $(git rev-parse HEAD)
+EOF
+       verify_hook_input
+'
+
 test_expect_success 'git rebase -m' '
        git reset --hard D &&
        clear_hook_input &&
index 18376d66081759c6a4959a2d8bc47ca441364660..bafcca765e4fea92f430e7127506a2370e062ec7 100755 (executable)
@@ -91,7 +91,7 @@ test_expect_success 'setup' '
                prev=$cur &&
                cur=$(($cur+1))
        done &&
-       add B1 $A1
+       add B1 $A1 &&
        echo $ATIP > .git/refs/heads/A &&
        echo $BTIP > .git/refs/heads/B &&
        git symbolic-ref HEAD refs/heads/B
index 1037a723fe74756f241346a077f4f3682dbbf45d..7a46cbdbe687d080def03f41721fd0920ccd316f 100755 (executable)
@@ -57,7 +57,7 @@ test_expect_success 'copy commit and tree but not blob by hand' '
                cd cloned &&
                git count-objects | sed -e "s/ *objects,.*//"
        ) ) &&
-       test $cnt -eq 6
+       test $cnt -eq 6 &&
 
        blob=$(git rev-parse HEAD:file | sed -e "s|..|&/|") &&
        test -f "cloned/.git/objects/$blob" &&
index bab1a536f562c63db67035712588d3e9a5222649..60de2d6ede958e713aebe85d73ee65ddbc10201d 100755 (executable)
@@ -4,11 +4,9 @@ test_description='test automatic tag following'
 
 . ./test-lib.sh
 
-case $(uname -s) in
-*MINGW*)
-       skip_all="GIT_DEBUG_SEND_PACK not supported - skipping tests"
-       test_done
-esac
+if ! test_have_prereq NOT_MINGW; then
+       say "GIT_DEBUG_SEND_PACK not supported - skipping tests"
+fi
 
 # End state of the repository:
 #
@@ -19,7 +17,7 @@ esac
 #     \   C - origin/cat    \
 #      origin/master         master
 
-test_expect_success setup '
+test_expect_success NOT_MINGW setup '
        test_tick &&
        echo ichi >file &&
        git add file &&
@@ -42,13 +40,16 @@ test_expect_success setup '
 
 U=UPLOAD_LOG
 
+test_expect_success NOT_MINGW 'setup expect' '
 cat - <<EOF >expect
 #S
 want $A
 #E
 EOF
-test_expect_success 'fetch A (new commit : 1 connection)' '
-       rm -f $U
+'
+
+test_expect_success NOT_MINGW 'fetch A (new commit : 1 connection)' '
+       rm -f $U &&
        (
                cd cloned &&
                GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
@@ -59,7 +60,7 @@ test_expect_success 'fetch A (new commit : 1 connection)' '
        test_cmp expect actual
 '
 
-test_expect_success "create tag T on A, create C on branch cat" '
+test_expect_success NOT_MINGW "create tag T on A, create C on branch cat" '
        git tag -a -m tag1 tag1 $A &&
        T=$(git rev-parse --verify tag1) &&
 
@@ -71,14 +72,17 @@ test_expect_success "create tag T on A, create C on branch cat" '
        git checkout master
 '
 
+test_expect_success NOT_MINGW 'setup expect' '
 cat - <<EOF >expect
 #S
 want $C
 want $T
 #E
 EOF
-test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
-       rm -f $U
+'
+
+test_expect_success NOT_MINGW 'fetch C, T (new branch, tag : 1 connection)' '
+       rm -f $U &&
        (
                cd cloned &&
                GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
@@ -91,7 +95,7 @@ test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
        test_cmp expect actual
 '
 
-test_expect_success "create commits O, B, tag S on B" '
+test_expect_success NOT_MINGW "create commits O, B, tag S on B" '
        test_tick &&
        echo O >file &&
        git add file &&
@@ -107,14 +111,17 @@ test_expect_success "create commits O, B, tag S on B" '
        S=$(git rev-parse --verify tag2)
 '
 
+test_expect_success NOT_MINGW 'setup expect' '
 cat - <<EOF >expect
 #S
 want $B
 want $S
 #E
 EOF
-test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
-       rm -f $U
+'
+
+test_expect_success NOT_MINGW 'fetch B, S (commit and tag : 1 connection)' '
+       rm -f $U &&
        (
                cd cloned &&
                GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
@@ -127,13 +134,16 @@ test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
        test_cmp expect actual
 '
 
+test_expect_success NOT_MINGW 'setup expect' '
 cat - <<EOF >expect
 #S
 want $B
 want $S
 #E
 EOF
-test_expect_success 'new clone fetch master and tags' '
+'
+
+test_expect_success NOT_MINGW 'new clone fetch master and tags' '
        git branch -D cat
        rm -f $U
        (
index 4c498b1902e4b0c74ea8d6bfc039d583bcc4364e..d189add2d0afeea9ae29ca6b82a786b55b445217 100755 (executable)
@@ -107,16 +107,18 @@ test_expect_success 'remove remote' '
 )
 '
 
-test_expect_success 'remove remote protects non-remote branches' '
+test_expect_success 'remove remote protects local branches' '
 (
        cd test &&
        { cat >expect1 <<EOF
-Note: A non-remote branch was not removed; to delete it, use:
+Note: A branch outside the refs/remotes/ hierarchy was not removed;
+to delete it, use:
   git branch -d master
 EOF
        } &&
        { cat >expect2 <<EOF
-Note: Non-remote branches were not removed; to delete them, use:
+Note: Some branches outside the refs/remotes/ hierarchy were not removed;
+to delete them, use:
   git branch -d foobranch
   git branch -d master
 EOF
@@ -435,7 +437,7 @@ test_expect_success 'update --prune' '
         git branch -m side2 side3) &&
        (cd test &&
         git remote update --prune &&
-        (cd ../one && git branch -m side3 side2)
+        (cd ../one && git branch -m side3 side2) &&
         git rev-parse refs/remotes/origin/side3 &&
         test_must_fail git rev-parse refs/remotes/origin/side2)
 '
index 4eb10f602fdcba354c6d140ed6b910ee89641708..7e433b179f9fcb0b3ccdd0ec83c6ec850735e391 100755 (executable)
@@ -21,27 +21,30 @@ test_expect_success setup '
 
 test_expect_success "clone and setup child repos" '
        git clone . one &&
-       cd one &&
-       echo >file updated by one &&
-       git commit -a -m "updated by one" &&
-       cd .. &&
+       (
+               cd one &&
+               echo >file updated by one &&
+               git commit -a -m "updated by one"
+       ) &&
        git clone . two &&
-       cd two &&
-       git config branch.master.remote one &&
-       git config remote.one.url ../one/.git/ &&
-       git config remote.one.fetch refs/heads/master:refs/heads/one &&
-       cd .. &&
+       (
+               cd two &&
+               git config branch.master.remote one &&
+               git config remote.one.url ../one/.git/ &&
+               git config remote.one.fetch refs/heads/master:refs/heads/one
+       ) &&
        git clone . three &&
-       cd three &&
-       git config branch.master.remote two &&
-       git config branch.master.merge refs/heads/one &&
-       mkdir -p .git/remotes &&
-       {
-               echo "URL: ../two/.git/"
-               echo "Pull: refs/heads/master:refs/heads/two"
-               echo "Pull: refs/heads/one:refs/heads/one"
-       } >.git/remotes/two &&
-       cd .. &&
+       (
+               cd three &&
+               git config branch.master.remote two &&
+               git config branch.master.merge refs/heads/one &&
+               mkdir -p .git/remotes &&
+               {
+                       echo "URL: ../two/.git/"
+                       echo "Pull: refs/heads/master:refs/heads/two"
+                       echo "Pull: refs/heads/one:refs/heads/one"
+               } >.git/remotes/two
+       ) &&
        git clone . bundle &&
        git clone . seven
 '
@@ -116,7 +119,7 @@ test_expect_success 'fetch must not resolve short tag name' '
 test_expect_success 'fetch must not resolve short remote name' '
 
        cd "$D" &&
-       git update-ref refs/remotes/six/HEAD HEAD
+       git update-ref refs/remotes/six/HEAD HEAD &&
 
        mkdir six &&
        cd six &&
@@ -240,6 +243,38 @@ test_expect_success 'fetch with a non-applying branch.<name>.merge' '
        git fetch blub
 '
 
+# URL supplied to fetch does not match the url of the configured branch's remote
+test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [1]' '
+       one_head=$(cd one && git rev-parse HEAD) &&
+       this_head=$(git rev-parse HEAD) &&
+       git update-ref -d FETCH_HEAD &&
+       git fetch one &&
+       test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
+       test $this_head = "$(git rev-parse --verify HEAD)"
+'
+
+# URL supplied to fetch matches the url of the configured branch's remote and
+# the merge spec matches the branch the remote HEAD points to
+test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [2]' '
+       one_ref=$(cd one && git symbolic-ref HEAD) &&
+       git config branch.master.remote blub &&
+       git config branch.master.merge "$one_ref" &&
+       git update-ref -d FETCH_HEAD &&
+       git fetch one &&
+       test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
+       test $this_head = "$(git rev-parse --verify HEAD)"
+'
+
+# URL supplied to fetch matches the url of the configured branch's remote, but
+# the merge spec does not match the branch the remote HEAD points to
+test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [3]' '
+       git config branch.master.merge "${one_ref}_not" &&
+       git update-ref -d FETCH_HEAD &&
+       git fetch one &&
+       test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
+       test $this_head = "$(git rev-parse --verify HEAD)"
+'
+
 # the strange name is: a\!'b
 test_expect_success 'quoting of a strangely named repo' '
        test_must_fail git fetch "a\\!'\''b" > result 2>&1 &&
index 9e7486274b3f0079cb993acbd03e90afc5638e38..65d1e05bd62af9c2b6dfa6cdd841d03622bf94ac 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='fetch follows remote tracking branches correctly'
+test_description='fetch follows remote-tracking branches correctly'
 
 . ./test-lib.sh
 
index b73733219d62cabf02c59ed0bf08caec1158faef..227dd56137c469311209ebda43cb89f9734c6e68 100755 (executable)
@@ -27,7 +27,7 @@ test_expect_success setup '
        (
                cd two && git branch another
        ) &&
-       git clone --mirror two three
+       git clone --mirror two three &&
        git clone one test
 '
 
index b11da79c9cafebb5af572bd8e9f85dfc6f3c3f77..d73731e6446f71480db1ec7cceb73f27ad51ecd3 100755 (executable)
@@ -586,7 +586,7 @@ test_expect_success 'push --delete refuses src:dest refspecs' '
 '
 
 test_expect_success 'warn on push to HEAD of non-bare repository' '
-       mk_test heads/master
+       mk_test heads/master &&
        (
                cd testrepo &&
                git checkout master &&
@@ -597,7 +597,7 @@ test_expect_success 'warn on push to HEAD of non-bare repository' '
 '
 
 test_expect_success 'deny push to HEAD of non-bare repository' '
-       mk_test heads/master
+       mk_test heads/master &&
        (
                cd testrepo &&
                git checkout master &&
@@ -607,7 +607,7 @@ test_expect_success 'deny push to HEAD of non-bare repository' '
 '
 
 test_expect_success 'allow push to HEAD of bare repository (bare)' '
-       mk_test heads/master
+       mk_test heads/master &&
        (
                cd testrepo &&
                git checkout master &&
@@ -619,7 +619,7 @@ test_expect_success 'allow push to HEAD of bare repository (bare)' '
 '
 
 test_expect_success 'allow push to HEAD of non-bare repository (config)' '
-       mk_test heads/master
+       mk_test heads/master &&
        (
                cd testrepo &&
                git checkout master &&
index 96be5236a2faf7178edc3c60094ecc37da741a65..c00c9b071d696038f63e8d613e11beab68eb547e 100755 (executable)
@@ -123,7 +123,7 @@ test_expect_success 'bob works and pushes again' '
        (
                cd alice-pub &&
                git cat-file commit master >../bob-work/commit
-       )
+       ) &&
        (
                # This time Bob does not pull from Alice, and
                # the master branch at her public repository points
index 319e389ed0dbb9c920d8d619cdf94ab52145e9f4..0470a81be0edaf883788d313778ef6865ed34c6f 100755 (executable)
@@ -4,6 +4,11 @@ test_description='pulling into void'
 
 . ./test-lib.sh
 
+modify () {
+       sed -e "$1" <"$2" >"$2.x" &&
+       mv "$2.x" "$2"
+}
+
 D=`pwd`
 
 test_expect_success setup '
@@ -160,4 +165,68 @@ test_expect_success 'pull --rebase works on branch yet to be born' '
        test_cmp expect actual
 '
 
+test_expect_success 'setup for detecting upstreamed changes' '
+       mkdir src &&
+       (cd src &&
+        git init &&
+        printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" > stuff &&
+        git add stuff &&
+        git commit -m "Initial revision"
+       ) &&
+       git clone src dst &&
+       (cd src &&
+        modify s/5/43/ stuff &&
+        git commit -a -m "5->43" &&
+        modify s/6/42/ stuff &&
+        git commit -a -m "Make it bigger"
+       ) &&
+       (cd dst &&
+        modify s/5/43/ stuff &&
+        git commit -a -m "Independent discovery of 5->43"
+       )
+'
+
+test_expect_success 'git pull --rebase detects upstreamed changes' '
+       (cd dst &&
+        git pull --rebase &&
+        test -z "$(git ls-files -u)"
+       )
+'
+
+test_expect_success 'setup for avoiding reapplying old patches' '
+       (cd dst &&
+        test_might_fail git rebase --abort &&
+        git reset --hard origin/master
+       ) &&
+       git clone --bare src src-replace.git &&
+       rm -rf src &&
+       mv src-replace.git src &&
+       (cd dst &&
+        modify s/2/22/ stuff &&
+        git commit -a -m "Change 2" &&
+        modify s/3/33/ stuff &&
+        git commit -a -m "Change 3" &&
+        modify s/4/44/ stuff &&
+        git commit -a -m "Change 4" &&
+        git push &&
+
+        modify s/44/55/ stuff &&
+        git commit --amend -a -m "Modified Change 4"
+       )
+'
+
+test_expect_success 'git pull --rebase does not reapply old patches' '
+       (cd dst &&
+        test_must_fail git pull --rebase &&
+        test 1 = $(find .git/rebase-apply -name "000*" | wc -l)
+       )
+'
+
+test_expect_success 'git pull --rebase against local branch' '
+       git checkout -b copy2 to-rebase-orig &&
+       git pull --rebase . to-rebase &&
+       test "conflicting modification" = "$(cat file)" &&
+       test file = "$(cat file2)"
+'
+
 test_done
index 298200fa4cafc61b5f16f9337314a2b887593a36..8e9b204e02a3d204af08463d2229ca0f319cdc42 100755 (executable)
@@ -4,12 +4,6 @@ test_description='pulling from symlinked subdir'
 
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-       skip_all='Symbolic links not supported, skipping tests.'
-       test_done
-fi
-
 # The scenario we are building:
 #
 #   trash\ directory/
@@ -20,7 +14,7 @@ fi
 #
 # The working directory is subdir-link.
 
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
        mkdir subdir &&
        echo file >subdir/file &&
        git add subdir/file &&
@@ -36,7 +30,7 @@ test_expect_success setup '
 
 # Demonstrate that things work if we just avoid the symlink
 #
-test_expect_success 'pulling from real subdir' '
+test_expect_success SYMLINKS 'pulling from real subdir' '
        (
                echo real >subdir/file &&
                git commit -m real subdir/file &&
@@ -64,7 +58,7 @@ test_expect_success 'pulling from real subdir' '
 # directory.  A POSIX shell's "cd" works a little differently
 # than chdir() in C; "cd -P" is much closer to chdir().
 #
-test_expect_success 'pulling from symlinked subdir' '
+test_expect_success SYMLINKS 'pulling from symlinked subdir' '
        (
                echo link >subdir/file &&
                git commit -m link subdir/file &&
@@ -77,7 +71,7 @@ test_expect_success 'pulling from symlinked subdir' '
 # Prove that the remote end really is a repo, and other commands
 # work fine in this context.  It's just that "git pull" breaks.
 #
-test_expect_success 'pushing from symlinked subdir' '
+test_expect_success SYMLINKS 'pushing from symlinked subdir' '
        (
                cd subdir-link/ &&
                echo push >file &&
index 00da70763bc34fe05dcba90a48799e32880571ce..c229fe68f11007fbb96d7b3837c18fd2f306a0bf 100755 (executable)
@@ -2,9 +2,14 @@
 
 test_description='push with --set-upstream'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+ensure_fresh_upstream() {
+       rm -rf parent && git init --bare parent
+}
 
 test_expect_success 'setup bare parent' '
-       git init --bare parent &&
+       ensure_fresh_upstream &&
        git remote add upstream parent
 '
 
@@ -66,4 +71,41 @@ test_expect_success 'push -u HEAD' '
        check_config headbranch upstream refs/heads/headbranch
 '
 
+test_expect_success TTY 'progress messages go to tty' '
+       ensure_fresh_upstream &&
+
+       test_terminal git push -u upstream master >out 2>err &&
+       grep "Writing objects" err
+'
+
+test_expect_success 'progress messages do not go to non-tty' '
+       ensure_fresh_upstream &&
+
+       # skip progress messages, since stderr is non-tty
+       git push -u upstream master >out 2>err &&
+       ! grep "Writing objects" err
+'
+
+test_expect_success 'progress messages go to non-tty (forced)' '
+       ensure_fresh_upstream &&
+
+       # force progress messages to stderr, even though it is non-tty
+       git push -u --progress upstream master >out 2>err &&
+       grep "Writing objects" err
+'
+
+test_expect_success TTY 'push -q suppresses progress' '
+       ensure_fresh_upstream &&
+
+       test_terminal git push -u -q upstream master >out 2>err &&
+       ! grep "Writing objects" err
+'
+
+test_expect_failure TTY 'push --no-progress suppresses progress' '
+       ensure_fresh_upstream &&
+
+       test_terminal git push -u --no-progress upstream master >out 2>err &&
+       ! grep "Writing objects" err
+'
+
 test_done
diff --git a/t/t5525-fetch-tagopt.sh b/t/t5525-fetch-tagopt.sh
new file mode 100755 (executable)
index 0000000..4fbf7a1
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='tagopt variable affects "git fetch" and is overridden by commandline.'
+
+. ./test-lib.sh
+
+setup_clone () {
+       git clone --mirror . $1 &&
+       git remote add remote_$1 $1 &&
+       (cd $1 &&
+       git tag tag_$1)
+}
+
+test_expect_success setup '
+       test_commit test &&
+       setup_clone one &&
+       git config remote.remote_one.tagopt --no-tags &&
+       setup_clone two &&
+       git config remote.remote_two.tagopt --tags
+       '
+
+test_expect_success "fetch with tagopt=--no-tags does not get tag" '
+       git fetch remote_one &&
+       test_must_fail git show-ref tag_one
+       '
+
+test_expect_success "fetch --tags with tagopt=--no-tags gets tag" '
+       git fetch --tags remote_one &&
+       git show-ref tag_one
+       '
+
+test_expect_success "fetch --no-tags with tagopt=--tags does not get tag" '
+       git fetch --no-tags remote_two &&
+       test_must_fail git show-ref tag_two
+       '
+
+test_expect_success "fetch with tagopt=--tags gets tag" '
+       git fetch remote_two &&
+       git show-ref tag_two
+       '
+test_done
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
new file mode 100755 (executable)
index 0000000..884a5e5
--- /dev/null
@@ -0,0 +1,195 @@
+#!/bin/sh
+# Copyright (c) 2010, Jens Lehmann
+
+test_description='Recursive "git fetch" for submodules'
+
+. ./test-lib.sh
+
+pwd=$(pwd)
+
+add_upstream_commit() {
+       (
+               cd submodule &&
+               head1=$(git rev-parse --short HEAD) &&
+               echo new >> subfile &&
+               test_tick &&
+               git add subfile &&
+               git commit -m new subfile &&
+               head2=$(git rev-parse --short HEAD) &&
+               echo "From $pwd/submodule" > ../expect.err &&
+               echo "   $head1..$head2  master     -> origin/master" >> ../expect.err
+       ) &&
+       (
+               cd deepsubmodule &&
+               head1=$(git rev-parse --short HEAD) &&
+               echo new >> deepsubfile &&
+               test_tick &&
+               git add deepsubfile &&
+               git commit -m new deepsubfile &&
+               head2=$(git rev-parse --short HEAD) &&
+               echo "From $pwd/deepsubmodule" >> ../expect.err &&
+               echo "   $head1..$head2  master     -> origin/master" >> ../expect.err
+       )
+}
+
+test_expect_success setup '
+       mkdir deepsubmodule &&
+       (
+               cd deepsubmodule &&
+               git init &&
+               echo deepsubcontent > deepsubfile &&
+               git add deepsubfile &&
+               git commit -m new deepsubfile
+       ) &&
+       mkdir submodule &&
+       (
+               cd submodule &&
+               git init &&
+               echo subcontent > subfile &&
+               git add subfile &&
+               git submodule add "$pwd/deepsubmodule" deepsubmodule &&
+               git commit -a -m new
+       ) &&
+       git submodule add "$pwd/submodule" submodule &&
+       git commit -am initial &&
+       git clone . downstream &&
+       (
+               cd downstream &&
+               git submodule update --init --recursive
+       ) &&
+       echo "Fetching submodule submodule" > expect.out &&
+       echo "Fetching submodule submodule/deepsubmodule" >> expect.out
+'
+
+test_expect_success "fetch --recurse-submodules recurses into submodules" '
+       add_upstream_commit &&
+       (
+               cd downstream &&
+               git fetch --recurse-submodules >../actual.out 2>../actual.err
+       ) &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+'
+
+test_expect_success "fetch alone only fetches superproject" '
+       add_upstream_commit &&
+       (
+               cd downstream &&
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       ! test -s actual.out &&
+       ! test -s actual.err
+'
+
+test_expect_success "fetch --no-recurse-submodules only fetches superproject" '
+       (
+               cd downstream &&
+               git fetch --no-recurse-submodules >../actual.out 2>../actual.err
+       ) &&
+       ! test -s actual.out &&
+       ! test -s actual.err
+'
+
+test_expect_success "using fetchRecurseSubmodules=true in .gitmodules recurses into submodules" '
+       (
+               cd downstream &&
+               git config -f .gitmodules submodule.submodule.fetchRecurseSubmodules true &&
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+'
+
+test_expect_success "--no-recurse-submodules overrides .gitmodules config" '
+       add_upstream_commit &&
+       (
+               cd downstream &&
+               git fetch --no-recurse-submodules >../actual.out 2>../actual.err
+       ) &&
+       ! test -s actual.out &&
+       ! test -s actual.err
+'
+
+test_expect_success "using fetchRecurseSubmodules=false in .git/config overrides setting in .gitmodules" '
+       (
+               cd downstream &&
+               git config submodule.submodule.fetchRecurseSubmodules false &&
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       ! test -s actual.out &&
+       ! test -s actual.err
+'
+
+test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setting from .git/config" '
+       (
+               cd downstream &&
+               git fetch --recurse-submodules >../actual.out 2>../actual.err &&
+               git config -f --unset .gitmodules submodule.submodule.fetchRecurseSubmodules true &&
+               git config --unset submodule.submodule.fetchRecurseSubmodules
+       ) &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+'
+
+test_expect_success "--quiet propagates to submodules" '
+       (
+               cd downstream &&
+               git fetch --recurse-submodules --quiet >../actual.out 2>../actual.err
+       ) &&
+       ! test -s actual.out &&
+       ! test -s actual.err
+'
+
+test_expect_success "--dry-run propagates to submodules" '
+       add_upstream_commit &&
+       (
+               cd downstream &&
+               git fetch --recurse-submodules --dry-run >../actual.out 2>../actual.err
+       ) &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err &&
+       (
+               cd downstream &&
+               git fetch --recurse-submodules >../actual.out 2>../actual.err
+       ) &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+'
+
+test_expect_success "recurseSubmodules=true propagates into submodules" '
+       add_upstream_commit &&
+       (
+               cd downstream &&
+               git config fetch.recurseSubmodules true
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+'
+
+test_expect_success "--recurse-submodules overrides config in submodule" '
+       add_upstream_commit &&
+       (
+               cd downstream &&
+               (
+                       cd submodule &&
+                       git config fetch.recurseSubmodules false
+               ) &&
+               git fetch --recurse-submodules >../actual.out 2>../actual.err
+       ) &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+'
+
+test_expect_success "--no-recurse-submodules overrides config setting" '
+       add_upstream_commit &&
+       (
+               cd downstream &&
+               git config fetch.recurseSubmodules true
+               git fetch --no-recurse-submodules >../actual.out 2>../actual.err
+       ) &&
+       ! test -s actual.out &&
+       ! test -s actual.err
+'
+
+test_done
index 65d8d474bcc6aa5ab4a1c659e228ac2e61d71e04..faa2e9633783e96609f4a31beb4bcccedb876338 100755 (executable)
@@ -6,7 +6,7 @@ test_description='unpack-objects'
 
 test_expect_success setup '
        mkdir pub.git &&
-       GIT_DIR=pub.git git init --bare
+       GIT_DIR=pub.git git init --bare &&
        GIT_DIR=pub.git git config receive.fsckobjects true &&
        mkdir work &&
        (
index 2fb48d09edb47129d9cea8b9686e46a6d8f35615..a1883ca6b649a236e5e08a5e4b76d1479353dcac 100755 (executable)
@@ -30,18 +30,37 @@ test_expect_success 'create http-accessible bare repository' '
 '
 
 test_expect_success 'clone http repository' '
-       git clone $HTTPD_URL/dumb/repo.git clone &&
+       git clone $HTTPD_URL/dumb/repo.git clone-tmpl &&
+       cp -R clone-tmpl clone &&
        test_cmp file clone/file
 '
 
+test_expect_success 'clone http repository with authentication' '
+       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/auth/" &&
+       cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" "$HTTPD_DOCUMENT_ROOT_PATH/auth/repo.git" &&
+       git clone $AUTH_HTTPD_URL/auth/repo.git clone-auth &&
+       test_cmp file clone-auth/file
+'
+
 test_expect_success 'fetch changes via http' '
        echo content >>file &&
        git commit -a -m two &&
-       git push public
+       git push public &&
        (cd clone && git pull) &&
        test_cmp file clone/file
 '
 
+test_expect_success 'fetch changes via manual http-fetch' '
+       cp -R clone-tmpl clone2 &&
+
+       HEAD=$(git rev-parse --verify HEAD) &&
+       (cd clone2 &&
+        git http-fetch -a -w heads/master-new $HEAD $(git config remote.origin.url) &&
+        git checkout master-new &&
+        test $HEAD = $(git rev-parse --verify HEAD)) &&
+       test_cmp file clone2/file
+'
+
 test_expect_success 'http remote detects correct HEAD' '
        git push public master:other &&
        (cd clone &&
index fd19121372aecc0806e17e62d639855f391045d3..26d355725f5e8d317c71cb466ea091ec8f741d63 100755 (executable)
@@ -101,5 +101,13 @@ test_expect_success 'used upload-pack service' '
        test_cmp exp act
 '
 
+test_expect_success 'follow redirects (301)' '
+       git clone $HTTPD_URL/smart-redir-perm/repo.git --quiet repo-p
+'
+
+test_expect_success 'follow redirects (302)' '
+       git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
+'
+
 stop_httpd
 test_done
index 44885b850c4a1c3a09b8b078a643cf7cd8918213..0ad7ce07c4550ed28a22743a3c543e16f4d4c558 100755 (executable)
@@ -5,16 +5,17 @@ test_description='test git-http-backend-noserver'
 
 HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
 
+test_have_prereq MINGW && export GREP_OPTIONS=-U
+
 run_backend() {
        echo "$2" |
        QUERY_STRING="${1#*\?}" \
-       GIT_PROJECT_ROOT="$HTTPD_DOCUMENT_ROOT_PATH" \
-       PATH_INFO="${1%%\?*}" \
+       PATH_TRANSLATED="$HTTPD_DOCUMENT_ROOT_PATH/${1%%\?*}" \
        git http-backend >act.out 2>act.err
 }
 
 GET() {
-       export REQUEST_METHOD="GET" &&
+       REQUEST_METHOD="GET" && export REQUEST_METHOD &&
        run_backend "/repo.git/$1" &&
        unset REQUEST_METHOD &&
        if ! grep "Status" act.out >act
@@ -26,8 +27,8 @@ GET() {
 }
 
 POST() {
-       export REQUEST_METHOD="POST" &&
-       export CONTENT_TYPE="application/x-$1-request" &&
+       REQUEST_METHOD="POST" && export REQUEST_METHOD &&
+       CONTENT_TYPE="application/x-$1-request" && export CONTENT_TYPE &&
        run_backend "/repo.git/$1" "$2" &&
        unset REQUEST_METHOD &&
        unset CONTENT_TYPE &&
@@ -46,7 +47,7 @@ log_div() {
 . "$TEST_DIRECTORY"/t556x_common
 
 expect_aliased() {
-       export REQUEST_METHOD="GET" &&
+       REQUEST_METHOD="GET" && export REQUEST_METHOD &&
        if test $1 = 0; then
                run_backend "$2"
        else
index be024e551c977355d8611c2134a4eda97a2bcc7d..82926cfdb7b7c6a86ab7d73efdfa327a6be28c83 100755 (executable)
@@ -50,72 +50,72 @@ get_static_files() {
 }
 
 SMART=smart
-export GIT_HTTP_EXPORT_ALL=1
+GIT_HTTP_EXPORT_ALL=1 && export GIT_HTTP_EXPORT_ALL
 test_expect_success 'direct refs/heads/master not found' '
-       log_div "refs/heads/master"
+       log_div "refs/heads/master" &&
        GET refs/heads/master "404 Not Found"
 '
 test_expect_success 'static file is ok' '
-       log_div "getanyfile default"
+       log_div "getanyfile default" &&
        get_static_files "200 OK"
 '
 SMART=smart_noexport
 unset GIT_HTTP_EXPORT_ALL
 test_expect_success 'no export by default' '
-       log_div "no git-daemon-export-ok"
+       log_div "no git-daemon-export-ok" &&
        get_static_files "404 Not Found"
 '
 test_expect_success 'export if git-daemon-export-ok' '
-       log_div "git-daemon-export-ok"
+       log_div "git-daemon-export-ok" &&
         (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
         touch git-daemon-export-ok
        ) &&
         get_static_files "200 OK"
 '
 SMART=smart
-export GIT_HTTP_EXPORT_ALL=1
+GIT_HTTP_EXPORT_ALL=1 && export GIT_HTTP_EXPORT_ALL
 test_expect_success 'static file if http.getanyfile true is ok' '
-       log_div "getanyfile true"
+       log_div "getanyfile true" &&
        config http.getanyfile true &&
        get_static_files "200 OK"
 '
 test_expect_success 'static file if http.getanyfile false fails' '
-       log_div "getanyfile false"
+       log_div "getanyfile false" &&
        config http.getanyfile false &&
        get_static_files "403 Forbidden"
 '
 
 test_expect_success 'http.uploadpack default enabled' '
-       log_div "uploadpack default"
+       log_div "uploadpack default" &&
        GET info/refs?service=git-upload-pack "200 OK"  &&
        POST git-upload-pack 0000 "200 OK"
 '
 test_expect_success 'http.uploadpack true' '
-       log_div "uploadpack true"
+       log_div "uploadpack true" &&
        config http.uploadpack true &&
        GET info/refs?service=git-upload-pack "200 OK" &&
        POST git-upload-pack 0000 "200 OK"
 '
 test_expect_success 'http.uploadpack false' '
-       log_div "uploadpack false"
+       log_div "uploadpack false" &&
        config http.uploadpack false &&
        GET info/refs?service=git-upload-pack "403 Forbidden" &&
        POST git-upload-pack 0000 "403 Forbidden"
 '
 
 test_expect_success 'http.receivepack default disabled' '
-       log_div "receivepack default"
+       log_div "receivepack default" &&
        GET info/refs?service=git-receive-pack "403 Forbidden"  &&
        POST git-receive-pack 0000 "403 Forbidden"
 '
 test_expect_success 'http.receivepack true' '
-       log_div "receivepack true"
+       log_div "receivepack true" &&
        config http.receivepack true &&
        GET info/refs?service=git-receive-pack "200 OK" &&
        POST git-receive-pack 0000 "200 OK"
 '
 test_expect_success 'http.receivepack false' '
-       log_div "receivepack false"
+       log_div "receivepack false" &&
        config http.receivepack false &&
        GET info/refs?service=git-receive-pack "403 Forbidden" &&
        POST git-receive-pack 0000 "403 Forbidden"
index 8abb71afcd4d7389260baa6f82ecb9b53bb9524c..987e0c846309a8a49dbe31ce292edd55342217b3 100755 (executable)
@@ -163,8 +163,6 @@ test_expect_success 'clone a void' '
 
 test_expect_success 'clone respects global branch.autosetuprebase' '
        (
-               HOME=$(pwd) &&
-               export HOME &&
                test_config="$HOME/.gitconfig" &&
                unset GIT_CONFIG_NOGLOBAL &&
                git config -f "$test_config" branch.autosetuprebase remote &&
@@ -178,8 +176,14 @@ test_expect_success 'clone respects global branch.autosetuprebase' '
 
 test_expect_success 'respect url-encoding of file://' '
        git init x+y &&
-       test_must_fail git clone "file://$PWD/x+y" xy-url &&
-       git clone "file://$PWD/x%2By" xy-url
+       git clone "file://$PWD/x+y" xy-url-1 &&
+       git clone "file://$PWD/x%2By" xy-url-2
+'
+
+test_expect_success 'do not query-string-decode + in URLs' '
+       rm -rf x+y &&
+       git init "x y" &&
+       test_must_fail git clone "file://$PWD/x+y" xy-no-plus
 '
 
 test_expect_success 'do not respect url-encoding of non-url path' '
index deffdaee490d620c44baaee143f11be604171a42..3f353d99e8f4255b131b4d44d7ec2a2c73140f0f 100755 (executable)
@@ -5,21 +5,29 @@ test_description=clone
 . ./test-lib.sh
 
 test_expect_success setup '
-       echo "#!/bin/sh" > not_ssh
-       echo "echo \"\$*\" > not_ssh_output" >> not_ssh
-       echo "exit 1" >> not_ssh
+       echo "#!/bin/sh" > not_ssh &&
+       echo "echo \"\$*\" > not_ssh_output" >> not_ssh &&
+       echo "exit 1" >> not_ssh &&
        chmod +x not_ssh
 '
 
 test_expect_success 'clone calls git upload-pack unqualified with no -u option' '
-       GIT_SSH=./not_ssh git clone localhost:/path/to/repo junk
-       echo "localhost git-upload-pack '\''/path/to/repo'\''" >expected
+       (
+               GIT_SSH=./not_ssh &&
+               export GIT_SSH &&
+               test_must_fail git clone localhost:/path/to/repo junk
+       ) &&
+       echo "localhost git-upload-pack '\''/path/to/repo'\''" >expected &&
        test_cmp expected not_ssh_output
 '
 
 test_expect_success 'clone calls specified git upload-pack with -u option' '
-       GIT_SSH=./not_ssh git clone -u ./something/bin/git-upload-pack localhost:/path/to/repo junk
-       echo "localhost ./something/bin/git-upload-pack '\''/path/to/repo'\''" >expected
+       (
+               GIT_SSH=./not_ssh &&
+               export GIT_SSH &&
+               test_must_fail git clone -u ./something/bin/git-upload-pack localhost:/path/to/repo junk
+       ) &&
+       echo "localhost ./something/bin/git-upload-pack '\''/path/to/repo'\''" >expected &&
        test_cmp expected not_ssh_output
 '
 
index 8b4c356cd21846025d84a434077bfdc8ee2bab57..0f4d487be34d22820bcc63619e6e85b96f18c609 100755 (executable)
@@ -10,11 +10,11 @@ test_expect_success 'preparing origin repository' '
        git clone --bare . a.git &&
        git clone --bare . x &&
        test "$(GIT_CONFIG=a.git/config git config --bool core.bare)" = true &&
-       test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true
+       test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true &&
        git bundle create b1.bundle --all &&
        git bundle create b2.bundle master &&
        mkdir dir &&
-       cp b1.bundle dir/b3
+       cp b1.bundle dir/b3 &&
        cp b1.bundle b4
 '
 
@@ -112,7 +112,7 @@ test_expect_success 'bundle clone with nonexistent HEAD' '
        cd "$D" &&
        git clone b2.bundle b2 &&
        cd b2 &&
-       git fetch
+       git fetch &&
        test ! -e .git/refs/heads/master
 '
 
index ddc3dc52f497d05e20cf4034d544df6d08632935..728ccd88c3d69f06bff2c3593ddc325f9186402c 100755 (executable)
@@ -30,6 +30,13 @@ test_expect_success 'tags can be excluded by rev-list options' '
 
 '
 
+test_expect_success 'die if bundle file cannot be created' '
+
+       mkdir adir &&
+       test_must_fail git bundle create adir --all
+
+'
+
 test_expect_failure 'bundle --stdin' '
 
        echo master | git bundle create stdin-bundle.bdl --stdin &&
index e4d1b6a0fa39d82d048f4dde62155a059173c5b4..e9783c341a97afdd58aa2adddc8c6db4adb54bee 100755 (executable)
@@ -3,12 +3,14 @@
 test_description='Test cloning a repository larger than 2 gigabyte'
 . ./test-lib.sh
 
-test -z "$GIT_TEST_CLONE_2GB" &&
-skip_all="Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t" &&
-test_done &&
-exit
+if test -z "$GIT_TEST_CLONE_2GB"
+then
+       say 'Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t'
+else
+       test_set_prereq CLONE_2GB
+fi
 
-test_expect_success 'setup' '
+test_expect_success CLONE_2GB 'setup' '
 
        git config pack.compression 0 &&
        git config pack.depth 0 &&
@@ -36,13 +38,13 @@ test_expect_success 'setup' '
 
 '
 
-test_expect_success 'clone - bare' '
+test_expect_success CLONE_2GB 'clone - bare' '
 
        git clone --bare --no-hardlinks . clone-bare
 
 '
 
-test_expect_success 'clone - with worktree, file:// protocol' '
+test_expect_success CLONE_2GB 'clone - with worktree, file:// protocol' '
 
        git clone file://. clone-wt
 
index 637d8e97ac4d220437ac168dcce49dfd33aa7a24..1fb6380fceb294f1a7cc73359f5e39740260d8f0 100755 (executable)
@@ -13,13 +13,11 @@ if sys.hexversion < 0x02040000:
     sys.exit(1)
 '
 then
-       :
-else
-       skip_all='skipping git remote-testgit tests: requires Python 2.4 or newer'
-       test_done
+    # Requires Python 2.4 or newer
+       test_set_prereq PYTHON_24
 fi
 
-test_expect_success 'setup repository' '
+test_expect_success PYTHON_24 'setup repository' '
        git init --bare server/.git &&
        git clone server public &&
        (cd public &&
@@ -29,34 +27,34 @@ test_expect_success 'setup repository' '
         git push origin master)
 '
 
-test_expect_success 'cloning from local repo' '
+test_expect_success PYTHON_24 'cloning from local repo' '
        git clone "testgit::${PWD}/server" localclone &&
        test_cmp public/file localclone/file
 '
 
-test_expect_success 'cloning from remote repo' '
+test_expect_success PYTHON_24 'cloning from remote repo' '
        git clone "testgit::file://${PWD}/server" clone &&
        test_cmp public/file clone/file
 '
 
-test_expect_success 'create new commit on remote' '
+test_expect_success PYTHON_24 'create new commit on remote' '
        (cd public &&
         echo content >>file &&
         git commit -a -m two &&
         git push)
 '
 
-test_expect_success 'pulling from local repo' '
+test_expect_success PYTHON_24 'pulling from local repo' '
        (cd localclone && git pull) &&
        test_cmp public/file localclone/file
 '
 
-test_expect_success 'pulling from remote remote' '
+test_expect_success PYTHON_24 'pulling from remote remote' '
        (cd clone && git pull) &&
        test_cmp public/file clone/file
 '
 
-test_expect_success 'pushing to local repo' '
+test_expect_success PYTHON_24 'pushing to local repo' '
        (cd localclone &&
        echo content >>file &&
        git commit -a -m three &&
@@ -65,12 +63,12 @@ test_expect_success 'pushing to local repo' '
        test $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)
 '
 
-test_expect_success 'synch with changes from localclone' '
+test_expect_success PYTHON_24 'synch with changes from localclone' '
        (cd clone &&
         git pull)
 '
 
-test_expect_success 'pushing remote local repo' '
+test_expect_success PYTHON_24 'pushing remote local repo' '
        (cd clone &&
        echo content >>file &&
        git commit -a -m four &&
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
new file mode 100755 (executable)
index 0000000..b10685a
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='miscellaneous rev-list tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo content1 >wanted_file &&
+       echo content2 >unwanted_file &&
+       git add wanted_file unwanted_file &&
+       git commit -m one
+'
+
+test_expect_success 'rev-list --objects heeds pathspecs' '
+       git rev-list --objects HEAD -- wanted_file >output &&
+       grep wanted_file output &&
+       ! grep unwanted_file output
+'
+
+test_expect_success 'rev-list --objects with pathspecs and deeper paths' '
+       mkdir foo &&
+       >foo/file &&
+       git add foo/file &&
+       git commit -m two &&
+
+       git rev-list --objects HEAD -- foo >output &&
+       grep foo/file output &&
+
+       git rev-list --objects HEAD -- foo/file >output &&
+       grep foo/file output &&
+       ! grep unwanted_file output
+'
+
+test_expect_success 'rev-list --objects with pathspecs and copied files' '
+       git checkout --orphan junio-testcase &&
+       git rm -rf . &&
+
+       mkdir two &&
+       echo frotz >one &&
+       cp one two/three &&
+       git add one two/three &&
+       test_tick &&
+       git commit -m that &&
+
+       ONE=$(git rev-parse HEAD:one)
+       git rev-list --objects HEAD two >output &&
+       grep "$ONE two/three" output &&
+       ! grep one output
+'
+
+test_done
index fc57e7d3fd69c60144ee3fb3f66b252a67369b42..8efcd130795890c36dfe6c5c630d2be44c6e1699 100755 (executable)
@@ -90,22 +90,22 @@ check () {
 for type in basic parents parents-raw
 do
        test_expect_success 'without grafts' "
-               rm -f .git/info/grafts
+               rm -f .git/info/grafts &&
                check $type $B2 -- $B2 $B1 $B0
        "
 
        test_expect_success 'with grafts' "
-               echo '$B0 $A2' >.git/info/grafts
+               echo '$B0 $A2' >.git/info/grafts &&
                check $type $B2 -- $B2 $B1 $B0 $A2 $A1 $A0
        "
 
        test_expect_success 'without grafts, with pathlimit' "
-               rm -f .git/info/grafts
+               rm -f .git/info/grafts &&
                check $type $B2 subdir -- $B2 $B0
        "
 
        test_expect_success 'with grafts, with pathlimit' "
-               echo '$B0 $A2' >.git/info/grafts
+               echo '$B0 $A2' >.git/info/grafts &&
                check $type $B2 subdir -- $B2 $B0 $A2 $A0
        "
 
index cccacd4add48524abd4a3021f8048a6c45c245ea..d918cc02d090157485d404d34d49005e50cd9f1a 100755 (executable)
@@ -162,6 +162,14 @@ commit 131a310eb913d107dd3c09a65d1651175898735d
 commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
 EOF
 
+test_expect_success '%x00 shows NUL' '
+       echo  >expect commit f58db70b055c5718631e5c61528b28b12090cdea &&
+       echo >>expect fooQbar &&
+       git rev-list -1 --format=foo%x00bar HEAD >actual.nul &&
+       nul_to_q <actual.nul >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success '%ad respects --date=' '
        echo 2005-04-07 >expect.ad-short &&
        git log -1 --date=short --pretty=tformat:%ad >output.ad-short master &&
index c8a96a9a994badde602c8bf7a7decda048a00525..52f7b277ceae3ae183bc6082c01af9f76bd39a86 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success setup '
 
        commit one &&
 
-       test_tick=$(($test_tick - 2400))
+       test_tick=$(($test_tick - 2400)) &&
 
        commit two &&
        commit three &&
index 0144d9e858d2d8bf1720331c52f1809ed36e81b0..082032edc36268f1b8e26ca6f409080093ac0f2b 100755 (executable)
 # Copyright (c) 2005 Junio C Hamano
 #
 
-test_description='Merge base computation.
+test_description='Merge base and parent list computation.
 '
 
 . ./test-lib.sh
 
-T=$(git write-tree)
-
-M=1130000000
-Z=+0000
-
-GIT_COMMITTER_EMAIL=git@comm.iter.xz
-GIT_COMMITTER_NAME='C O Mmiter'
-GIT_AUTHOR_NAME='A U Thor'
-GIT_AUTHOR_EMAIL=git@au.thor.xz
-export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
-
-doit() {
-       OFFSET=$1; shift
-       NAME=$1; shift
-       PARENTS=
-       for P
-       do
-               PARENTS="${PARENTS}-p $P "
-       done
-       GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z"
-       GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE
-       export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
-       commit=$(echo $NAME | git commit-tree $T $PARENTS)
-       echo $commit >.git/refs/tags/$NAME
-       echo $commit
-}
-
-#  E---D---C---B---A
-#  \'-_         \   \
-#   \  `---------G   \
-#    \                \
-#     F----------------H
-
-# Setup...
-E=$(doit 5 E)
-D=$(doit 4 D $E)
-F=$(doit 6 F $E)
-C=$(doit 3 C $D)
-B=$(doit 2 B $C)
-A=$(doit 1 A $B)
-G=$(doit 7 G $B $E)
-H=$(doit 8 H $A $F)
-
-test_expect_success 'compute merge-base (single)' \
-    'MB=$(git merge-base G H) &&
-     expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
-
-test_expect_success 'compute merge-base (all)' \
-    'MB=$(git merge-base --all G H) &&
-     expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
-
-test_expect_success 'compute merge-base with show-branch' \
-    'MB=$(git show-branch --merge-base G H) &&
-     expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
-
-# Setup for second test to demonstrate that relying on timestamps in a
-# distributed SCM to provide a _consistent_ partial ordering of commits
-# leads to insanity.
-#
-#               Relative
-# Structure     timestamps
-#
-#   PL  PR        +4  +4
-#  /  \/  \      /  \/  \
-# L2  C2  R2    +3  -1  +3
-# |   |   |     |   |   |
-# L1  C1  R1    +2  -2  +2
-# |   |   |     |   |   |
-# L0  C0  R0    +1  -3  +1
-#   \ |  /        \ |  /
-#     S             0
-#
-# The left and right chains of commits can be of any length and complexity as
-# long as all of the timestamps are greater than that of S.
+test_expect_success 'setup' '
+       T=$(git write-tree) &&
 
-S=$(doit  0 S)
+       M=1130000000 &&
+       Z=+0000 &&
 
-C0=$(doit -3 C0 $S)
-C1=$(doit -2 C1 $C0)
-C2=$(doit -1 C2 $C1)
+       GIT_COMMITTER_EMAIL=git@comm.iter.xz &&
+       GIT_COMMITTER_NAME="C O Mmiter" &&
+       GIT_AUTHOR_NAME="A U Thor" &&
+       GIT_AUTHOR_EMAIL=git@au.thor.xz &&
+       export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
 
-L0=$(doit  1 L0 $S)
-L1=$(doit  2 L1 $L0)
-L2=$(doit  3 L2 $L1)
+       doit() {
+               OFFSET=$1 &&
+               NAME=$2 &&
+               shift 2 &&
 
-R0=$(doit  1 R0 $S)
-R1=$(doit  2 R1 $R0)
-R2=$(doit  3 R2 $R1)
+               PARENTS= &&
+               for P
+               do
+                       PARENTS="${PARENTS}-p $P "
+               done &&
 
-PL=$(doit  4 PL $L2 $C2)
-PR=$(doit  4 PR $C2 $R2)
+               GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z" &&
+               GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE &&
+               export GIT_COMMITTER_DATE GIT_AUTHOR_DATE &&
 
-test_expect_success 'compute merge-base (single)' \
-    'MB=$(git merge-base PL PR) &&
-     expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+               commit=$(echo $NAME | git commit-tree $T $PARENTS) &&
 
-test_expect_success 'compute merge-base (all)' \
-    'MB=$(git merge-base --all PL PR) &&
-     expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+               echo $commit >.git/refs/tags/$NAME &&
+               echo $commit
+       }
+'
 
-# 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 'set up G and H' '
+       # E---D---C---B---A
+       # \"-_         \   \
+       #  \  `---------G   \
+       #   \                \
+       #    F----------------H
+       E=$(doit 5 E) &&
+       D=$(doit 4 D $E) &&
+       F=$(doit 6 F $E) &&
+       C=$(doit 3 C $D) &&
+       B=$(doit 2 B $C) &&
+       A=$(doit 1 A $B) &&
+       G=$(doit 7 G $B $E) &&
+       H=$(doit 8 H $A $F)
+'
+
+test_expect_success 'merge-base G H' '
+       git name-rev $B >expected &&
+
+       MB=$(git merge-base G H) &&
+       git name-rev "$MB" >actual.single &&
+
+       MB=$(git merge-base --all G H) &&
+       git name-rev "$MB" >actual.all &&
+
+       MB=$(git show-branch --merge-base G H) &&
+       git name-rev "$MB" >actual.sb &&
+
+       test_cmp expected actual.single &&
+       test_cmp expected actual.all &&
+       test_cmp expected actual.sb
+'
 
+test_expect_success 'merge-base/show-branch --independent' '
+       git name-rev "$H" >expected1 &&
+       git name-rev "$H" "$G" >expected2 &&
+
+       parents=$(git merge-base --independent H) &&
+       git name-rev $parents >actual1.mb &&
+       parents=$(git merge-base --independent A H G) &&
+       git name-rev $parents >actual2.mb &&
+
+       parents=$(git show-branch --independent H) &&
+       git name-rev $parents >actual1.sb &&
+       parents=$(git show-branch --independent A H G) &&
+       git name-rev $parents >actual2.sb &&
+
+       test_cmp expected1 actual1.mb &&
+       test_cmp expected2 actual2.mb &&
+       test_cmp expected1 actual1.sb &&
+       test_cmp expected2 actual2.sb
+'
+
+test_expect_success 'unsynchronized clocks' '
+       # This test is to demonstrate that relying on timestamps in a distributed
+       # SCM to provide a _consistent_ partial ordering of commits leads to
+       # insanity.
+       #
+       #               Relative
+       # Structure     timestamps
+       #
+       #   PL  PR        +4  +4
+       #  /  \/  \      /  \/  \
+       # L2  C2  R2    +3  -1  +3
+       # |   |   |     |   |   |
+       # L1  C1  R1    +2  -2  +2
+       # |   |   |     |   |   |
+       # L0  C0  R0    +1  -3  +1
+       #   \ |  /        \ |  /
+       #     S             0
+       #
+       # The left and right chains of commits can be of any length and complexity as
+       # long as all of the timestamps are greater than that of S.
+
+       S=$(doit  0 S) &&
+
+       C0=$(doit -3 C0 $S) &&
+       C1=$(doit -2 C1 $C0) &&
+       C2=$(doit -1 C2 $C1) &&
+
+       L0=$(doit  1 L0 $S) &&
+       L1=$(doit  2 L1 $L0) &&
+       L2=$(doit  3 L2 $L1) &&
+
+       R0=$(doit  1 R0 $S) &&
+       R1=$(doit  2 R1 $R0) &&
+       R2=$(doit  3 R2 $R1) &&
+
+       PL=$(doit  4 PL $L2 $C2) &&
+       PR=$(doit  4 PR $C2 $R2) &&
+
+       git name-rev $C2 >expected &&
+
+       MB=$(git merge-base PL PR) &&
+       git name-rev "$MB" >actual.single &&
+
+       MB=$(git merge-base --all PL PR) &&
+       git name-rev "$MB" >actual.all &&
+
+       test_cmp expected actual.single &&
+       test_cmp expected actual.all
+'
+
+test_expect_success '--independent with unsynchronized clocks' '
+       IB=$(doit 0 IB) &&
+       I1=$(doit -10 I1 $IB) &&
+       I2=$(doit  -9 I2 $I1) &&
+       I3=$(doit  -8 I3 $I2) &&
+       I4=$(doit  -7 I4 $I3) &&
+       I5=$(doit  -6 I5 $I4) &&
+       I6=$(doit  -5 I6 $I5) &&
+       I7=$(doit  -4 I7 $I6) &&
+       I8=$(doit  -3 I8 $I7) &&
+       IH=$(doit  -2 IH $I8) &&
+
+       echo $IH >expected &&
+       git merge-base --independent IB IH >actual &&
+       test_cmp expected actual
+'
 
 test_expect_success 'merge-base for octopus-step (setup)' '
-       test_tick && git commit --allow-empty -m root && git tag MMR &&
-       test_tick && git commit --allow-empty -m 1 && git tag MM1 &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m A && git tag MMA &&
+       # 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_commit MMR &&
+       test_commit MM1 &&
+       test_commit MM-o &&
+       test_commit MM-p &&
+       test_commit MM-q &&
+       test_commit MMA &&
        git checkout MM1 &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m B && git tag MMB &&
+       test_commit MM-r &&
+       test_commit MM-s &&
+       test_commit MM-t &&
+       test_commit MMB &&
        git checkout MMR &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m C && git tag MMC
+       test_commit MM-u &&
+       test_commit MM-v &&
+       test_commit MM-w &&
+       test_commit MM-x &&
+       test_commit MMC
 '
 
 test_expect_success 'merge-base A B C' '
-       MB=$(git merge-base --all MMA MMB MMC) &&
-       MM1=$(git rev-parse --verify MM1) &&
-       test "$MM1" = "$MB"
-'
+       git rev-parse --verify MM1 >expected &&
+       git rev-parse --verify MMR >expected.sb &&
 
-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"
+       git merge-base --all MMA MMB MMC >actual &&
+       git merge-base --all --octopus MMA MMB MMC >actual.common &&
+       git show-branch --merge-base MMA MMB MMC >actual.sb &&
+
+       test_cmp expected actual &&
+       test_cmp expected.sb actual.common &&
+       test_cmp expected.sb actual.sb
 '
 
-test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
+test_expect_success 'criss-cross merge-base for octopus-step' '
        git reset --hard MMR &&
-       test_tick && git commit --allow-empty -m 1 && git tag CC1 &&
+       test_commit CC1 &&
        git reset --hard E &&
-       test_tick && git commit --allow-empty -m 2 && git tag CC2 &&
-       test_tick && git merge -s ours CC1 &&
-       test_tick && git commit --allow-empty -m o &&
-       test_tick && git commit --allow-empty -m B && git tag CCB &&
+       test_commit CC2 &&
+       test_tick &&
+       git merge -s ours CC1 &&
+       test_commit CC-o &&
+       test_commit CCB &&
        git reset --hard CC1 &&
-       test_tick && git merge -s ours CC2 &&
-       test_tick && git commit --allow-empty -m A && git tag CCA
-'
+       git merge -s ours CC2 &&
+       test_commit CCA &&
+
+       git rev-parse CC1 CC2 >expected &&
+       git merge-base --all CCB CCA^^ CCA^^2 >actual &&
 
-test_expect_success 'merge-base B A^^ A^^2' '
-       MB0=$(git merge-base --all CCB CCA^^ CCA^^2 | sort) &&
-       MB1=$(git rev-parse CC1 CC2 | sort) &&
-       test "$MB0" = "$MB1"
+       sort expected >expected.sorted &&
+       sort actual >actual.sorted &&
+       test_cmp expected.sorted actual.sorted
 '
 
 test_done
index 27fd52b7be8ee8a084bc96bd606813cc556c4bb0..f7181d1d6a143c60a5c4b26960bd42aa2c88035d 100755 (executable)
@@ -29,7 +29,7 @@ test_expect_success 'set up rev-list --graph test' '
        # Octopus merge B and C into branch A
        git checkout A &&
        git merge B C &&
-       git tag A4
+       git tag A4 &&
 
        test_commit A5 bar.txt &&
 
@@ -39,7 +39,7 @@ test_expect_success 'set up rev-list --graph test' '
        test_commit C4 bar.txt &&
        git checkout A &&
        git merge -s ours C &&
-       git tag A6
+       git tag A6 &&
 
        test_commit A7 bar.txt &&
 
@@ -90,7 +90,7 @@ test_expect_success '--graph --all' '
 # that undecorated merges are interesting, even with --simplify-by-decoration
 test_expect_success '--graph --simplify-by-decoration' '
        rm -f expected &&
-       git tag -d A4
+       git tag -d A4 &&
        echo "* $A7" >> expected &&
        echo "*   $A6" >> expected &&
        echo "|\\  " >> expected &&
@@ -116,12 +116,15 @@ test_expect_success '--graph --simplify-by-decoration' '
        test_cmp expected actual
        '
 
-# Get rid of all decorations on branch B, and graph with it simplified away
+test_expect_success 'setup: get rid of decorations on B' '
+       git tag -d B2 &&
+       git tag -d B1 &&
+       git branch -d B
+'
+
+# Graph with branch B 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 &&
@@ -143,9 +146,6 @@ test_expect_success '--graph --simplify-by-decoration prune branch B' '
 
 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 &&
@@ -163,9 +163,6 @@ test_expect_success '--graph --full-history -- bar.txt' '
 
 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 &&
@@ -181,9 +178,6 @@ test_expect_success '--graph --full-history --simplify-merges -- bar.txt' '
 
 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 &&
@@ -196,9 +190,6 @@ test_expect_success '--graph -- bar.txt' '
 
 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 &&
index 58428d9f5c3d079b0e842b5851b100f9e895a239..fb8291c812854608777dd8d35edfcda78df5069a 100755 (executable)
@@ -123,6 +123,12 @@ test_expect_success 'rev-list --glob=refs/heads/subspace/*' '
 
 '
 
+test_expect_success 'rev-list --glob refs/heads/subspace/*' '
+
+       compare rev-list "subspace/one subspace/two" "--glob refs/heads/subspace/*"
+
+'
+
 test_expect_success 'rev-list --glob=heads/subspace/*' '
 
        compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/*"
index e71c687f2b9473842684fae1a1b4b013c721497f..eec8f4e3edd85e350f5b8d8c86415317cce2aba1 100755 (executable)
@@ -6,23 +6,28 @@
 test_description='Test merge with directory/file conflicts'
 . ./test-lib.sh
 
-test_expect_success 'prepare repository' \
-'echo "Hello" > init &&
-git add init &&
-git commit -m "Initial commit" &&
-git branch B &&
-mkdir dir &&
-echo "foo" > dir/foo &&
-git add dir/foo &&
-git commit -m "File: dir/foo" &&
-git checkout B &&
-echo "file dir" > dir &&
-git add dir &&
-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' '
+test_expect_success 'prepare repository' '
+       echo Hello >init &&
+       git add init &&
+       git commit -m initial &&
+
+       git branch B &&
+       mkdir dir &&
+       echo foo >dir/foo &&
+       git add dir/foo &&
+       git commit -m "File: dir/foo" &&
+
+       git checkout B &&
+       echo file dir >dir &&
+       git add dir &&
+       git commit -m "File: dir"
+'
+
+test_expect_success 'Merge with d/f conflicts' '
+       test_expect_code 1 git merge "merge msg" B master
+'
+
+test_expect_success 'F/D conflict' '
        git reset --hard &&
        git checkout master &&
        rm .git/index &&
@@ -45,4 +50,51 @@ test_expect_failure 'F/D conflict' '
        git merge master
 '
 
+test_expect_success 'setup modify/delete + directory/file conflict' '
+       git checkout --orphan modify &&
+       git rm -rf . &&
+       git clean -fdqx &&
+
+       printf "a\nb\nc\nd\ne\nf\ng\nh\n" >letters &&
+       git add letters &&
+       git commit -m initial &&
+
+       echo i >>letters &&
+       git add letters &&
+       git commit -m modified &&
+
+       git checkout -b delete HEAD^ &&
+       git rm letters &&
+       mkdir letters &&
+       >letters/file &&
+       git add letters &&
+       git commit -m deleted
+'
+
+test_expect_success 'modify/delete + directory/file conflict' '
+       git checkout delete^0 &&
+       test_must_fail git merge modify &&
+
+       test 3 = $(git ls-files -s | wc -l) &&
+       test 2 = $(git ls-files -u | wc -l) &&
+       test 1 = $(git ls-files -o | wc -l) &&
+
+       test -f letters/file &&
+       test -f letters~modify
+'
+
+test_expect_success 'modify/delete + directory/file conflict; other way' '
+       git reset --hard &&
+       git clean -f &&
+       git checkout modify^0 &&
+       test_must_fail git merge delete &&
+
+       test 3 = $(git ls-files -s | wc -l) &&
+       test 2 = $(git ls-files -u | wc -l) &&
+       test 1 = $(git ls-files -o | wc -l) &&
+
+       test -f letters/file &&
+       test -f letters~HEAD
+'
+
 test_done
index b66544b76d545a396ef068438f3980b3f544efdd..1ed259d864b4ef4c8c7060fa5f22634a25c8e032 100755 (executable)
@@ -3,6 +3,11 @@
 test_description='Merge-recursive merging renames'
 . ./test-lib.sh
 
+modify () {
+       sed -e "$1" <"$2" >"$2.x" &&
+       mv "$2.x" "$2"
+}
+
 test_expect_success setup \
 '
 cat >A <<\EOF &&
@@ -94,245 +99,147 @@ git checkout master'
 
 test_expect_success 'pull renaming branch into unrenaming one' \
 '
-       git show-branch
-       git pull . white && {
-               echo "BAD: should have conflicted"
-               return 1
-       }
-       git ls-files -s
-       test "$(git ls-files -u B | wc -l)" -eq 3 || {
-               echo "BAD: should have left stages for B"
-               return 1
-       }
-       test "$(git ls-files -s N | wc -l)" -eq 1 || {
-               echo "BAD: should have merged N"
-               return 1
-       }
+       git show-branch &&
+       test_expect_code 1 git pull . white &&
+       git ls-files -s &&
+       git ls-files -u B >b.stages &&
+       test_line_count = 3 b.stages &&
+       git ls-files -s N >n.stages &&
+       test_line_count = 1 n.stages &&
        sed -ne "/^g/{
        p
        q
-       }" B | grep master || {
-               echo "BAD: should have listed our change first"
-               return 1
-       }
-       test "$(git diff white N | wc -l)" -eq 0 || {
-               echo "BAD: should have taken colored branch"
-               return 1
-       }
+       }" B | grep master &&
+       git diff --exit-code white N
 '
 
 test_expect_success 'pull renaming branch into another renaming one' \
 '
-       rm -f B
-       git reset --hard
-       git checkout red
-       git pull . white && {
-               echo "BAD: should have conflicted"
-               return 1
-       }
-       test "$(git ls-files -u B | wc -l)" -eq 3 || {
-               echo "BAD: should have left stages"
-               return 1
-       }
-       test "$(git ls-files -s N | wc -l)" -eq 1 || {
-               echo "BAD: should have merged N"
-               return 1
-       }
+       rm -f B &&
+       git reset --hard &&
+       git checkout red &&
+       test_expect_code 1 git pull . white &&
+       git ls-files -u B >b.stages &&
+       test_line_count = 3 b.stages &&
+       git ls-files -s N >n.stages &&
+       test_line_count = 1 n.stages &&
        sed -ne "/^g/{
        p
        q
-       }" B | grep red || {
-               echo "BAD: should have listed our change first"
-               return 1
-       }
-       test "$(git diff white N | wc -l)" -eq 0 || {
-               echo "BAD: should have taken colored branch"
-               return 1
-       }
+       }" B | grep red &&
+       git diff --exit-code white N
 '
 
 test_expect_success 'pull unrenaming branch into renaming one' \
 '
-       git reset --hard
-       git show-branch
-       git pull . master && {
-               echo "BAD: should have conflicted"
-               return 1
-       }
-       test "$(git ls-files -u B | wc -l)" -eq 3 || {
-               echo "BAD: should have left stages"
-               return 1
-       }
-       test "$(git ls-files -s N | wc -l)" -eq 1 || {
-               echo "BAD: should have merged N"
-               return 1
-       }
+       git reset --hard &&
+       git show-branch &&
+       test_expect_code 1 git pull . master &&
+       git ls-files -u B >b.stages &&
+       test_line_count = 3 b.stages &&
+       git ls-files -s N >n.stages &&
+       test_line_count = 1 n.stages &&
        sed -ne "/^g/{
        p
        q
-       }" B | grep red || {
-               echo "BAD: should have listed our change first"
-               return 1
-       }
-       test "$(git diff white N | wc -l)" -eq 0 || {
-               echo "BAD: should have taken colored branch"
-               return 1
-       }
+       }" B | grep red &&
+       git diff --exit-code white N
 '
 
 test_expect_success 'pull conflicting renames' \
 '
-       git reset --hard
-       git show-branch
-       git pull . blue && {
-               echo "BAD: should have conflicted"
-               return 1
-       }
-       test "$(git ls-files -u A | wc -l)" -eq 1 || {
-               echo "BAD: should have left a stage"
-               return 1
-       }
-       test "$(git ls-files -u B | wc -l)" -eq 1 || {
-               echo "BAD: should have left a stage"
-               return 1
-       }
-       test "$(git ls-files -u C | wc -l)" -eq 1 || {
-               echo "BAD: should have left a stage"
-               return 1
-       }
-       test "$(git ls-files -s N | wc -l)" -eq 1 || {
-               echo "BAD: should have merged N"
-               return 1
-       }
+       git reset --hard &&
+       git show-branch &&
+       test_expect_code 1 git pull . blue &&
+       git ls-files -u A >a.stages &&
+       test_line_count = 1 a.stages &&
+       git ls-files -u B >b.stages &&
+       test_line_count = 1 b.stages &&
+       git ls-files -u C >c.stages &&
+       test_line_count = 1 c.stages &&
+       git ls-files -s N >n.stages &&
+       test_line_count = 1 n.stages &&
        sed -ne "/^g/{
        p
        q
-       }" B | grep red || {
-               echo "BAD: should have listed our change first"
-               return 1
-       }
-       test "$(git diff white N | wc -l)" -eq 0 || {
-               echo "BAD: should have taken colored branch"
-               return 1
-       }
+       }" B | grep red &&
+       git diff --exit-code white N
 '
 
 test_expect_success 'interference with untracked working tree file' '
-
-       git reset --hard
-       git show-branch
-       echo >A this file should not matter
-       git pull . white && {
-               echo "BAD: should have conflicted"
-               return 1
-       }
-       test -f A || {
-               echo "BAD: should have left A intact"
-               return 1
-       }
+       git reset --hard &&
+       git show-branch &&
+       echo >A this file should not matter &&
+       test_expect_code 1 git pull . white &&
+       test_path_is_file A
 '
 
 test_expect_success 'interference with untracked working tree file' '
-
-       git reset --hard
-       git checkout white
-       git show-branch
-       rm -f A
-       echo >A this file should not matter
-       git pull . red && {
-               echo "BAD: should have conflicted"
-               return 1
-       }
-       test -f A || {
-               echo "BAD: should have left A intact"
-               return 1
-       }
+       git reset --hard &&
+       git checkout white &&
+       git show-branch &&
+       rm -f A &&
+       echo >A this file should not matter &&
+       test_expect_code 1 git pull . red &&
+       test_path_is_file A
 '
 
 test_expect_success 'interference with untracked working tree file' '
-
-       git reset --hard
-       rm -f A M
-       git checkout -f master
-       git tag -f anchor
-       git show-branch
-       git pull . yellow || {
-               echo "BAD: should have cleanly merged"
-               return 1
-       }
-       test -f M && {
-               echo "BAD: should have removed M"
-               return 1
-       }
+       git reset --hard &&
+       rm -f A M &&
+       git checkout -f master &&
+       git tag -f anchor &&
+       git show-branch &&
+       git pull . yellow &&
+       test_path_is_missing M &&
        git reset --hard anchor
 '
 
 test_expect_success 'updated working tree file should prevent the merge' '
-
-       git reset --hard
-       rm -f A M
-       git checkout -f master
-       git tag -f anchor
-       git show-branch
-       echo >>M one line addition
-       cat M >M.saved
-       git pull . yellow && {
-               echo "BAD: should have complained"
-               return 1
-       }
-       test_cmp M M.saved || {
-               echo "BAD: should have left M intact"
-               return 1
-       }
+       git reset --hard &&
+       rm -f A M &&
+       git checkout -f master &&
+       git tag -f anchor &&
+       git show-branch &&
+       echo >>M one line addition &&
+       cat M >M.saved &&
+       test_expect_code 128 git pull . yellow &&
+       test_cmp M M.saved &&
        rm -f M.saved
 '
 
 test_expect_success 'updated working tree file should prevent the merge' '
-
-       git reset --hard
-       rm -f A M
-       git checkout -f master
-       git tag -f anchor
-       git show-branch
-       echo >>M one line addition
-       cat M >M.saved
-       git update-index M
-       git pull . yellow && {
-               echo "BAD: should have complained"
-               return 1
-       }
-       test_cmp M M.saved || {
-               echo "BAD: should have left M intact"
-               return 1
-       }
+       git reset --hard &&
+       rm -f A M &&
+       git checkout -f master &&
+       git tag -f anchor &&
+       git show-branch &&
+       echo >>M one line addition &&
+       cat M >M.saved &&
+       git update-index M &&
+       test_expect_code 128 git pull . yellow &&
+       test_cmp M M.saved &&
        rm -f M.saved
 '
 
 test_expect_success 'interference with untracked working tree file' '
-
-       git reset --hard
-       rm -f A M
-       git checkout -f yellow
-       git tag -f anchor
-       git show-branch
-       echo >M this file should not matter
-       git pull . master || {
-               echo "BAD: should have cleanly merged"
-               return 1
-       }
-       test -f M || {
-               echo "BAD: should have left M intact"
-               return 1
-       }
-       git ls-files -s | grep M && {
-               echo "BAD: M must be untracked in the result"
-               return 1
-       }
+       git reset --hard &&
+       rm -f A M &&
+       git checkout -f yellow &&
+       git tag -f anchor &&
+       git show-branch &&
+       echo >M this file should not matter &&
+       git pull . master &&
+       test_path_is_file M &&
+       ! {
+               git ls-files -s |
+               grep M
+       } &&
        git reset --hard anchor
 '
 
 test_expect_success 'merge of identical changes in a renamed file' '
-       rm -f A M N
+       rm -f A M N &&
        git reset --hard &&
        git checkout change+rename &&
        GIT_MERGE_VERBOSITY=3 git merge change | grep "^Skipped B" &&
@@ -341,4 +248,365 @@ test_expect_success 'merge of identical changes in a renamed file' '
        GIT_MERGE_VERBOSITY=3 git merge change+rename | grep "^Skipped B"
 '
 
+test_expect_success 'setup for rename + d/f conflicts' '
+       git reset --hard &&
+       git checkout --orphan dir-in-way &&
+       git rm -rf . &&
+
+       mkdir sub &&
+       mkdir dir &&
+       printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >sub/file &&
+       echo foo >dir/file-in-the-way &&
+       git add -A &&
+       git commit -m "Common commmit" &&
+
+       echo 11 >>sub/file &&
+       echo more >>dir/file-in-the-way &&
+       git add -u &&
+       git commit -m "Commit to merge, with dir in the way" &&
+
+       git checkout -b dir-not-in-way &&
+       git reset --soft HEAD^ &&
+       git rm -rf dir &&
+       git commit -m "Commit to merge, with dir removed" -- dir sub/file &&
+
+       git checkout -b renamed-file-has-no-conflicts dir-in-way~1 &&
+       git rm -rf dir &&
+       git rm sub/file &&
+       printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n" >dir &&
+       git add dir &&
+       git commit -m "Independent change" &&
+
+       git checkout -b renamed-file-has-conflicts dir-in-way~1 &&
+       git rm -rf dir &&
+       git mv sub/file dir &&
+       echo 12 >>dir &&
+       git add dir &&
+       git commit -m "Conflicting change"
+'
+
+printf "1\n2\n3\n4\n5555\n6\n7\n8\n9\n10\n11\n" >expected
+
+test_expect_success 'Rename+D/F conflict; renamed file merges + dir not in way' '
+       git reset --hard &&
+       git checkout -q renamed-file-has-no-conflicts^0 &&
+       git merge --strategy=recursive dir-not-in-way &&
+       git diff --quiet &&
+       test -f dir &&
+       test_cmp expected dir
+'
+
+test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' '
+       git reset --hard &&
+       rm -rf dir~* &&
+       git checkout -q renamed-file-has-no-conflicts^0 &&
+       test_must_fail git merge --strategy=recursive dir-in-way >output &&
+
+       grep "CONFLICT (delete/modify): dir/file-in-the-way" output &&
+       grep "Auto-merging dir" output &&
+       grep "Adding as dir~HEAD instead" output &&
+
+       test 2 -eq "$(git ls-files -u | wc -l)" &&
+       test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+       test_must_fail git diff --quiet &&
+       test_must_fail git diff --cached --quiet &&
+
+       test -f dir/file-in-the-way &&
+       test -f dir~HEAD &&
+       test_cmp expected dir~HEAD
+'
+
+test_expect_success 'Same as previous, but merged other way' '
+       git reset --hard &&
+       rm -rf dir~* &&
+       git checkout -q dir-in-way^0 &&
+       test_must_fail git merge --strategy=recursive renamed-file-has-no-conflicts >output 2>errors &&
+
+       ! grep "error: refusing to lose untracked file at" errors &&
+       grep "CONFLICT (delete/modify): dir/file-in-the-way" output &&
+       grep "Auto-merging dir" output &&
+       grep "Adding as dir~renamed-file-has-no-conflicts instead" output &&
+
+       test 2 -eq "$(git ls-files -u | wc -l)" &&
+       test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+       test_must_fail git diff --quiet &&
+       test_must_fail git diff --cached --quiet &&
+
+       test -f dir/file-in-the-way &&
+       test -f dir~renamed-file-has-no-conflicts &&
+       test_cmp expected dir~renamed-file-has-no-conflicts
+'
+
+cat >expected <<\EOF &&
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+<<<<<<< HEAD
+12
+=======
+11
+>>>>>>> dir-not-in-way
+EOF
+
+test_expect_success 'Rename+D/F conflict; renamed file cannot merge, dir not in way' '
+       git reset --hard &&
+       rm -rf dir~* &&
+       git checkout -q renamed-file-has-conflicts^0 &&
+       test_must_fail git merge --strategy=recursive dir-not-in-way &&
+
+       test 3 -eq "$(git ls-files -u | wc -l)" &&
+       test 3 -eq "$(git ls-files -u dir | wc -l)" &&
+
+       test_must_fail git diff --quiet &&
+       test_must_fail git diff --cached --quiet &&
+
+       test -f dir &&
+       test_cmp expected dir
+'
+
+test_expect_success 'Rename+D/F conflict; renamed file cannot merge and dir in the way' '
+       modify s/dir-not-in-way/dir-in-way/ expected &&
+
+       git reset --hard &&
+       rm -rf dir~* &&
+       git checkout -q renamed-file-has-conflicts^0 &&
+       test_must_fail git merge --strategy=recursive dir-in-way &&
+
+       test 5 -eq "$(git ls-files -u | wc -l)" &&
+       test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)" &&
+       test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+       test_must_fail git diff --quiet &&
+       test_must_fail git diff --cached --quiet &&
+
+       test -f dir/file-in-the-way &&
+       test -f dir~HEAD &&
+       test_cmp expected dir~HEAD
+'
+
+cat >expected <<\EOF &&
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+<<<<<<< HEAD
+11
+=======
+12
+>>>>>>> renamed-file-has-conflicts
+EOF
+
+test_expect_success 'Same as previous, but merged other way' '
+       git reset --hard &&
+       rm -rf dir~* &&
+       git checkout -q dir-in-way^0 &&
+       test_must_fail git merge --strategy=recursive renamed-file-has-conflicts &&
+
+       test 5 -eq "$(git ls-files -u | wc -l)" &&
+       test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)" &&
+       test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+
+       test_must_fail git diff --quiet &&
+       test_must_fail git diff --cached --quiet &&
+
+       test -f dir/file-in-the-way &&
+       test -f dir~renamed-file-has-conflicts &&
+       test_cmp expected dir~renamed-file-has-conflicts
+'
+
+test_expect_success 'setup both rename source and destination involved in D/F conflict' '
+       git reset --hard &&
+       git checkout --orphan rename-dest &&
+       git rm -rf . &&
+       git clean -fdqx &&
+
+       mkdir one &&
+       echo stuff >one/file &&
+       git add -A &&
+       git commit -m "Common commmit" &&
+
+       git mv one/file destdir &&
+       git commit -m "Renamed to destdir" &&
+
+       git checkout -b source-conflict HEAD~1 &&
+       git rm -rf one &&
+       mkdir destdir &&
+       touch one destdir/foo &&
+       git add -A &&
+       git commit -m "Conflicts in the way"
+'
+
+test_expect_success 'both rename source and destination involved in D/F conflict' '
+       git reset --hard &&
+       rm -rf dir~* &&
+       git checkout -q rename-dest^0 &&
+       test_must_fail git merge --strategy=recursive source-conflict &&
+
+       test 1 -eq "$(git ls-files -u | wc -l)" &&
+
+       test_must_fail git diff --quiet &&
+
+       test -f destdir/foo &&
+       test -f one &&
+       test -f destdir~HEAD &&
+       test "stuff" = "$(cat destdir~HEAD)"
+'
+
+test_expect_success 'setup pair rename to parent of other (D/F conflicts)' '
+       git reset --hard &&
+       git checkout --orphan rename-two &&
+       git rm -rf . &&
+       git clean -fdqx &&
+
+       mkdir one &&
+       mkdir two &&
+       echo stuff >one/file &&
+       echo other >two/file &&
+       git add -A &&
+       git commit -m "Common commmit" &&
+
+       git rm -rf one &&
+       git mv two/file one &&
+       git commit -m "Rename two/file -> one" &&
+
+       git checkout -b rename-one HEAD~1 &&
+       git rm -rf two &&
+       git mv one/file two &&
+       rm -r one &&
+       git commit -m "Rename one/file -> two"
+'
+
+test_expect_success 'pair rename to parent of other (D/F conflicts) w/ untracked dir' '
+       git checkout -q rename-one^0 &&
+       mkdir one &&
+       test_must_fail git merge --strategy=recursive rename-two &&
+
+       test 2 -eq "$(git ls-files -u | wc -l)" &&
+       test 1 -eq "$(git ls-files -u one | wc -l)" &&
+       test 1 -eq "$(git ls-files -u two | wc -l)" &&
+
+       test_must_fail git diff --quiet &&
+
+       test 4 -eq $(find . | grep -v .git | wc -l) &&
+
+       test -d one &&
+       test -f one~rename-two &&
+       test -f two &&
+       test "other" = $(cat one~rename-two) &&
+       test "stuff" = $(cat two)
+'
+
+test_expect_success 'pair rename to parent of other (D/F conflicts) w/ clean start' '
+       git reset --hard &&
+       git clean -fdqx &&
+       test_must_fail git merge --strategy=recursive rename-two &&
+
+       test 2 -eq "$(git ls-files -u | wc -l)" &&
+       test 1 -eq "$(git ls-files -u one | wc -l)" &&
+       test 1 -eq "$(git ls-files -u two | wc -l)" &&
+
+       test_must_fail git diff --quiet &&
+
+       test 3 -eq $(find . | grep -v .git | wc -l) &&
+
+       test -f one &&
+       test -f two &&
+       test "other" = $(cat one) &&
+       test "stuff" = $(cat two)
+'
+
+test_expect_success 'setup rename of one file to two, with directories in the way' '
+       git reset --hard &&
+       git checkout --orphan first-rename &&
+       git rm -rf . &&
+       git clean -fdqx &&
+
+       echo stuff >original &&
+       git add -A &&
+       git commit -m "Common commmit" &&
+
+       mkdir two &&
+       >two/file &&
+       git add two/file &&
+       git mv original one &&
+       git commit -m "Put two/file in the way, rename to one" &&
+
+       git checkout -b second-rename HEAD~1 &&
+       mkdir one &&
+       >one/file &&
+       git add one/file &&
+       git mv original two &&
+       git commit -m "Put one/file in the way, rename to two"
+'
+
+test_expect_success 'check handling of differently renamed file with D/F conflicts' '
+       git checkout -q first-rename^0 &&
+       test_must_fail git merge --strategy=recursive second-rename &&
+
+       test 5 -eq "$(git ls-files -s | wc -l)" &&
+       test 3 -eq "$(git ls-files -u | wc -l)" &&
+       test 1 -eq "$(git ls-files -u one | wc -l)" &&
+       test 1 -eq "$(git ls-files -u two | wc -l)" &&
+       test 1 -eq "$(git ls-files -u original | wc -l)" &&
+       test 2 -eq "$(git ls-files -o | wc -l)" &&
+
+       test -f one/file &&
+       test -f two/file &&
+       test -f one~HEAD &&
+       test -f two~second-rename &&
+       ! test -f original
+'
+
+test_expect_success 'setup rename one file to two; directories moving out of the way' '
+       git reset --hard &&
+       git checkout --orphan first-rename-redo &&
+       git rm -rf . &&
+       git clean -fdqx &&
+
+       echo stuff >original &&
+       mkdir one two &&
+       touch one/file two/file &&
+       git add -A &&
+       git commit -m "Common commmit" &&
+
+       git rm -rf one &&
+       git mv original one &&
+       git commit -m "Rename to one" &&
+
+       git checkout -b second-rename-redo HEAD~1 &&
+       git rm -rf two &&
+       git mv original two &&
+       git commit -m "Rename to two"
+'
+
+test_expect_success 'check handling of differently renamed file with D/F conflicts' '
+       git checkout -q first-rename-redo^0 &&
+       test_must_fail git merge --strategy=recursive second-rename-redo &&
+
+       test 3 -eq "$(git ls-files -u | wc -l)" &&
+       test 1 -eq "$(git ls-files -u one | wc -l)" &&
+       test 1 -eq "$(git ls-files -u two | wc -l)" &&
+       test 1 -eq "$(git ls-files -u original | wc -l)" &&
+       test 0 -eq "$(git ls-files -o | wc -l)" &&
+
+       test -f one &&
+       test -f two &&
+       ! test -f original
+'
+
 test_done
index d486d73994cf563063b578ec5e212331e87bf67f..d9f343942c7eb9d1e80e98db2252cb29135544fa 100755 (executable)
@@ -64,6 +64,14 @@ cp new1.txt test.txt
 test_expect_success "merge without conflict" \
        "git merge-file test.txt orig.txt new2.txt"
 
+test_expect_success 'works in subdirectory' '
+       mkdir dir &&
+       cp new1.txt dir/a.txt &&
+       cp orig.txt dir/o.txt &&
+       cp new2.txt dir/b.txt &&
+       ( cd dir && git merge-file a.txt o.txt b.txt )
+'
+
 cp new1.txt test.txt
 test_expect_success "merge without conflict (--quiet)" \
        "git merge-file --quiet test.txt orig.txt new2.txt"
index b3fbf659c003acbed785558c21950046b8caced8..755d30ce2a5d1c5e34751d8906ad41b02d553b03 100755 (executable)
@@ -104,7 +104,7 @@ test_expect_success 'mark rename/delete as unmerged' '
        test_tick &&
        git commit -m delete &&
        git checkout -b rename HEAD^ &&
-       git mv a1 a2
+       git mv a1 a2 &&
        test_tick &&
        git commit -m rename &&
        test_must_fail git merge delete &&
index 3900d9f61f84a9eab907e87fca7bb9429ea36f04..73fc240e8548911c65c4dffc4da0c6ff8ab4f27b 100755 (executable)
@@ -6,7 +6,7 @@ test_description='subtree merge strategy'
 
 test_expect_success setup '
 
-       s="1 2 3 4 5 6 7 8"
+       s="1 2 3 4 5 6 7 8" &&
        for i in $s; do echo $i; done >hello &&
        git add hello &&
        git commit -m initial &&
index 3b042aacd63f77651fdaf3d10b65f4fc85669a75..b5063b6fe6c37f4b41a89b71c6d52ac0b5c07127 100755 (executable)
@@ -517,13 +517,13 @@ test_expect_success '"parallel" side branch creation' '
        add_line_into_file "2(para): line 2 on parallel branch" dir2/file2 &&
        PARA_HASH2=$(git rev-parse --verify HEAD) &&
        add_line_into_file "3(para): line 3 on parallel branch" dir2/file3 &&
-       PARA_HASH3=$(git rev-parse --verify HEAD)
+       PARA_HASH3=$(git rev-parse --verify HEAD) &&
        git merge -m "merge HASH4 and PARA_HASH3" "$HASH4" &&
-       PARA_HASH4=$(git rev-parse --verify HEAD)
+       PARA_HASH4=$(git rev-parse --verify HEAD) &&
        add_line_into_file "5(para): add line on parallel branch" dir1/file1 &&
-       PARA_HASH5=$(git rev-parse --verify HEAD)
+       PARA_HASH5=$(git rev-parse --verify HEAD) &&
        add_line_into_file "6(para): add line on parallel branch" dir2/file2 &&
-       PARA_HASH6=$(git rev-parse --verify HEAD)
+       PARA_HASH6=$(git rev-parse --verify HEAD) &&
        git merge -m "merge HASH7 and PARA_HASH6" "$HASH7" &&
        PARA_HASH7=$(git rev-parse --verify HEAD)
 '
index 8a3304fb0b5901fb02435d3b77c3d049404f4e25..1cd649e245e49735afa539ab0e982036154c4797 100755 (executable)
@@ -2,11 +2,7 @@
 
 test_description='merge-recursive: handle file mode'
 . ./test-lib.sh
-
-if ! test "$(git config --bool core.filemode)" = false
-then
-       test_set_prereq FILEMODE
-fi
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 test_expect_success 'mode change in one branch: keep changed version' '
        : >file1 &&
@@ -57,4 +53,35 @@ test_expect_success FILEMODE 'verify executable bit on file' '
        test -x file2
 '
 
+test_expect_success 'merging with triple rename across D/F conflict' '
+       git reset --hard HEAD &&
+       git checkout -b main &&
+       git rm -rf . &&
+
+       echo "just a file" >sub1 &&
+       mkdir -p sub2 &&
+       echo content1 >sub2/file1 &&
+       echo content2 >sub2/file2 &&
+       echo content3 >sub2/file3 &&
+       mkdir simple &&
+       echo base >simple/bar &&
+       git add -A &&
+       test_tick &&
+       git commit -m base &&
+
+       git checkout -b other &&
+       echo more >>simple/bar &&
+       test_tick &&
+       git commit -a -m changesimplefile &&
+
+       git checkout main &&
+       git rm sub1 &&
+       git mv sub2 sub1 &&
+       test_tick &&
+       git commit -m changefiletodir &&
+
+       test_tick &&
+       git merge other
+'
+
 test_done
index eac5ebac24a0174fa20625a19835861573147a26..fdb6c253718e3d4c8acfcd1fc197607aac04b4c4 100755 (executable)
@@ -70,4 +70,34 @@ test_expect_success 'set merge.renamelimit to 5' '
 test_rename 5 ok
 test_rename 6 fail
 
+test_expect_success 'setup large simple rename' '
+       git config --unset merge.renamelimit &&
+       git config --unset diff.renamelimit &&
+
+       git reset --hard initial &&
+       for i in $(count 200); do
+               make_text foo bar baz >$i
+       done &&
+       git add . &&
+       git commit -m create-files &&
+
+       git branch simple-change &&
+       git checkout -b simple-rename &&
+
+       mkdir builtin &&
+       git mv [0-9]* builtin/ &&
+       git commit -m renamed &&
+
+       git checkout simple-change &&
+       >unrelated-change &&
+       git add unrelated-change &&
+       git commit -m unrelated-change
+'
+
+test_expect_success 'massive simple rename does not spam added files' '
+       unset GIT_MERGE_VERBOSITY &&
+       git merge --no-stat simple-rename | grep -v Removing >output &&
+       test 5 -gt "$(wc -l < output)"
+'
+
 test_done
index cd3190c4a61f0404491b41a1b22f5143b63f4992..92e02d5d77501b2a3ff52069931a534fc7bb24fd 100755 (executable)
@@ -3,13 +3,7 @@
 test_description='merging when a directory was replaced with a symlink'
 . ./test-lib.sh
 
-if ! test_have_prereq SYMLINKS
-then
-       skip_all='Symbolic links not supported, skipping tests.'
-       test_done
-fi
-
-test_expect_success 'create a commit where dir a/b changed to symlink' '
+test_expect_success SYMLINKS '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 &&
@@ -23,7 +17,7 @@ test_expect_success 'create a commit where dir a/b changed to symlink' '
        git commit -m "dir to symlink"
 '
 
-test_expect_success 'keep a/b-2/c/d across checkout' '
+test_expect_success SYMLINKS 'keep a/b-2/c/d across checkout' '
        git checkout HEAD^0 &&
        git reset --hard master &&
        git rm --cached a/b &&
@@ -32,14 +26,14 @@ test_expect_success 'keep a/b-2/c/d across checkout' '
         test -f a/b-2/c/d
 '
 
-test_expect_success 'checkout should not have deleted a/b-2/c/d' '
+test_expect_success SYMLINKS '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' '
+test_expect_success SYMLINKS 'setup for merge test' '
        git reset --hard &&
        test -f a/b-2/c/d &&
        echo x > a/x &&
@@ -48,7 +42,7 @@ test_expect_success 'setup for merge test' '
        git tag baseline
 '
 
-test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '
+test_expect_success SYMLINKS 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s resolve master &&
@@ -56,7 +50,7 @@ test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '
        test -f a/b-2/c/d
 '
 
-test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' '
+test_expect_success SYMLINKS 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s recursive master &&
@@ -64,7 +58,55 @@ test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' '
        test -f a/b-2/c/d
 '
 
-test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
+test_expect_success SYMLINKS 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' '
+       git reset --hard &&
+       git checkout master^0 &&
+       git merge -s resolve baseline^0 &&
+       test -h a/b &&
+       test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '
+       git reset --hard &&
+       git checkout master^0 &&
+       git merge -s recursive baseline^0 &&
+       test -h a/b &&
+       test -f a/b-2/c/d
+'
+
+test_expect_failure SYMLINKS 'do not lose untracked in merge (resolve)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       >a/b/c/e &&
+       test_must_fail git merge -s resolve master &&
+       test -f a/b/c/e &&
+       test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'do not lose untracked in merge (recursive)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       >a/b/c/e &&
+       test_must_fail git merge -s recursive master &&
+       test -f a/b/c/e &&
+       test -f a/b-2/c/d
+'
+
+test_expect_success SYMLINKS 'do not lose modifications in merge (resolve)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       echo more content >>a/b/c/d &&
+       test_must_fail git merge -s resolve master
+'
+
+test_expect_success SYMLINKS 'do not lose modifications in merge (recursive)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       echo more content >>a/b/c/d &&
+       test_must_fail git merge -s recursive master
+'
+
+test_expect_success SYMLINKS 'setup a merge where dir a/b-2 changed to symlink' '
        git reset --hard &&
        git checkout start^0 &&
        rm -rf a/b-2 &&
@@ -74,7 +116,7 @@ test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
        git tag test2
 '
 
-test_expect_success 'merge should not have conflicts (resolve)' '
+test_expect_success SYMLINKS 'merge should not have D/F conflicts (resolve)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s resolve test2 &&
@@ -82,7 +124,7 @@ test_expect_success 'merge should not have conflicts (resolve)' '
        test -f a/b/c/d
 '
 
-test_expect_failure 'merge should not have conflicts (recursive)' '
+test_expect_success SYMLINKS 'merge should not have D/F conflicts (recursive)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s recursive test2 &&
@@ -90,4 +132,12 @@ test_expect_failure 'merge should not have conflicts (recursive)' '
        test -f a/b/c/d
 '
 
+test_expect_success SYMLINKS 'merge should not have F/D conflicts (recursive)' '
+       git reset --hard &&
+       git checkout -b foo test2 &&
+       git merge -s recursive baseline^0 &&
+       test -h a/b-2 &&
+       test -f a/b/c/d
+'
+
 test_done
index b8741416588e9f3d7de7c0a0359a0b8ea7cf8045..871577d90cc2817ffcc0cfe1ecf76aab6be61723 100755 (executable)
@@ -14,7 +14,80 @@ test_description='recursive merge corner cases'
 #  R1  R2
 #
 
-test_expect_success setup '
+test_expect_success 'setup basic criss-cross + rename with no modifications' '
+       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 simple rename+criss-cross with no modifications' '
+       git reset --hard &&
+       git checkout L2^0 &&
+
+       test_must_fail git merge -s recursive R2^0 &&
+
+       test 5 = $(git ls-files -s | wc -l) &&
+       test 3 = $(git ls-files -u | wc -l) &&
+       test 0 = $(git ls-files -o | wc -l) &&
+
+       test $(git rev-parse :0:one) = $(git rev-parse L2:one) &&
+       test $(git rev-parse :0:two) = $(git rev-parse R2:two) &&
+       test $(git rev-parse :2:three) = $(git rev-parse L2:three) &&
+       test $(git rev-parse :3:three) = $(git rev-parse R2:three) &&
+
+       cp two merged &&
+       >empty &&
+       test_must_fail git merge-file \
+               -L "Temporary merge branch 2" \
+               -L "" \
+               -L "Temporary merge branch 1" \
+               merged empty one &&
+       test $(git rev-parse :1:three) = $(git hash-object merged)
+'
+
+#
+# Same as before, but modify L1 slightly:
+#
+#  L1m L2
+#   o---o
+#  / \ / \
+# o   X   ?
+#  \ / \ /
+#   o---o
+#  R1  R2
+#
+
+test_expect_success 'setup criss-cross + rename merges with basic modification' '
+       git rm -rf . &&
+       git clean -fdqx &&
+       rm -rf .git &&
+       git init &&
+
        ten="0 1 2 3 4 5 6 7 8 9"
        for i in $ten
        do
@@ -30,6 +103,8 @@ test_expect_success setup '
        git branch L1 &&
        git checkout -b R1 &&
        git mv one three &&
+       echo more >>two &&
+       git add two &&
        test_tick && git commit -m R1 &&
 
        git checkout L1 &&
@@ -45,11 +120,115 @@ test_expect_success setup '
        git tag R2
 '
 
-test_expect_success merge '
+test_expect_success 'merge criss-cross + rename merges with basic modification' '
        git reset --hard &&
        git checkout L2^0 &&
 
-       test_must_fail git merge -s recursive R2^0
+       test_must_fail git merge -s recursive R2^0 &&
+
+       test 5 = $(git ls-files -s | wc -l) &&
+       test 3 = $(git ls-files -u | wc -l) &&
+       test 0 = $(git ls-files -o | wc -l) &&
+
+       test $(git rev-parse :0:one) = $(git rev-parse L2:one) &&
+       test $(git rev-parse :0:two) = $(git rev-parse R2:two) &&
+       test $(git rev-parse :2:three) = $(git rev-parse L2:three) &&
+       test $(git rev-parse :3:three) = $(git rev-parse R2:three) &&
+
+       head -n 10 two >merged &&
+       cp one merge-me &&
+       >empty &&
+       test_must_fail git merge-file \
+               -L "Temporary merge branch 2" \
+               -L "" \
+               -L "Temporary merge branch 1" \
+               merged empty merge-me &&
+       test $(git rev-parse :1:three) = $(git hash-object merged)
+'
+
+#
+# For the next test, we start with three commits in two lines of development
+# which setup a rename/add conflict:
+#   Commit A: File 'a' exists
+#   Commit B: Rename 'a' -> 'new_a'
+#   Commit C: Modify 'a', create different 'new_a'
+# Later, two different people merge and resolve differently:
+#   Commit D: Merge B & C, ignoring separately created 'new_a'
+#   Commit E: Merge B & C making use of some piece of secondary 'new_a'
+# Finally, someone goes to merge D & E.  Does git detect the conflict?
+#
+#      B   D
+#      o---o
+#     / \ / \
+#  A o   X   ? F
+#     \ / \ /
+#      o---o
+#      C   E
+#
+
+test_expect_success 'setup differently handled merges of rename/add conflict' '
+       git rm -rf . &&
+       git clean -fdqx &&
+       rm -rf .git &&
+       git init &&
+
+       printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
+       git add a &&
+       test_tick && git commit -m A &&
+
+       git branch B &&
+       git checkout -b C &&
+       echo 10 >>a &&
+       echo "other content" >>new_a &&
+       git add a new_a &&
+       test_tick && git commit -m C &&
+
+       git checkout B &&
+       git mv a new_a &&
+       test_tick && git commit -m B &&
+
+       git checkout B^0 &&
+       test_must_fail git merge C &&
+       git clean -f &&
+       test_tick && git commit -m D &&
+       git tag D &&
+
+       git checkout C^0 &&
+       test_must_fail git merge B &&
+       rm new_a~HEAD new_a &&
+       printf "Incorrectly merged content" >>new_a &&
+       git add -u &&
+       test_tick && git commit -m E &&
+       git tag E
+'
+
+test_expect_success 'git detects differently handled merges conflict' '
+       git reset --hard &&
+       git checkout D^0 &&
+
+       git merge -s recursive E^0 && {
+               echo "BAD: should have conflicted"
+               test "Incorrectly merged content" = "$(cat new_a)" &&
+                       echo "BAD: Silently accepted wrong content"
+               return 1
+       }
+
+       test 3 = $(git ls-files -s | wc -l) &&
+       test 3 = $(git ls-files -u | wc -l) &&
+       test 0 = $(git ls-files -o | wc -l) &&
+
+       test $(git rev-parse :2:new_a) = $(git rev-parse D:new_a) &&
+       test $(git rev-parse :3:new_a) = $(git rev-parse E:new_a) &&
+
+       git cat-file -p B:new_a >>merged &&
+       git cat-file -p C:new_a >>merge-me &&
+       >empty &&
+       test_must_fail git merge-file \
+               -L "Temporary merge branch 2" \
+               -L "" \
+               -L "Temporary merge branch 1" \
+               merged empty merge-me &&
+       test $(git rev-parse :1:new_a) = $(git hash-object merged)
 '
 
 test_done
diff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh
new file mode 100755 (executable)
index 0000000..d9c2d38
--- /dev/null
@@ -0,0 +1,191 @@
+#!/bin/sh
+
+test_description='CRLF merge conflict across text=auto change
+
+* [master] remove .gitattributes
+ ! [side] add line from b
+--
+ + [side] add line from b
+*  [master] remove .gitattributes
+*  [master^] add line from a
+*  [master~2] normalize file
+*+ [side^] Initial
+'
+
+. ./test-lib.sh
+
+test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
+
+test_expect_success setup '
+       git config core.autocrlf false &&
+
+       echo first line | append_cr >file &&
+       echo first line >control_file &&
+       echo only line >inert_file &&
+
+       git add file control_file inert_file &&
+       test_tick &&
+       git commit -m "Initial" &&
+       git tag initial &&
+       git branch side &&
+
+       echo "* text=auto" >.gitattributes &&
+       touch file &&
+       git add .gitattributes file &&
+       test_tick &&
+       git commit -m "normalize file" &&
+
+       echo same line | append_cr >>file &&
+       echo same line >>control_file &&
+       git add file control_file &&
+       test_tick &&
+       git commit -m "add line from a" &&
+       git tag a &&
+
+       git rm .gitattributes &&
+       rm file &&
+       git checkout file &&
+       test_tick &&
+       git commit -m "remove .gitattributes" &&
+       git tag c &&
+
+       git checkout side &&
+       echo same line | append_cr >>file &&
+       echo same line >>control_file &&
+       git add file control_file &&
+       test_tick &&
+       git commit -m "add line from b" &&
+       git tag b &&
+
+       git checkout master
+'
+
+test_expect_success 'set up fuzz_conflict() helper' '
+       fuzz_conflict() {
+               sed $SED_OPTIONS -e "s/^\([<>=]......\) .*/\1/" "$@"
+       }
+'
+
+test_expect_success 'Merge after setting text=auto' '
+       cat <<-\EOF >expected &&
+       first line
+       same line
+       EOF
+
+       git config merge.renormalize true &&
+       git rm -fr . &&
+       rm -f .gitattributes &&
+       git reset --hard a &&
+       git merge b &&
+       test_cmp expected file
+'
+
+test_expect_success 'Merge addition of text=auto' '
+       cat <<-\EOF >expected &&
+       first line
+       same line
+       EOF
+
+       git config merge.renormalize true &&
+       git rm -fr . &&
+       rm -f .gitattributes &&
+       git reset --hard b &&
+       git merge a &&
+       test_cmp expected file
+'
+
+test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '
+       q_to_cr <<-\EOF >expected &&
+       <<<<<<<
+       first line
+       same line
+       =======
+       first lineQ
+       same lineQ
+       >>>>>>>
+       EOF
+
+       git config merge.renormalize false &&
+       rm -f .gitattributes &&
+       git reset --hard a &&
+       test_must_fail git merge b &&
+       fuzz_conflict file >file.fuzzy &&
+       test_cmp expected file.fuzzy
+'
+
+test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' '
+       q_to_cr <<-\EOF >expected &&
+       <<<<<<<
+       first lineQ
+       same lineQ
+       =======
+       first line
+       same line
+       >>>>>>>
+       EOF
+
+       git config merge.renormalize false &&
+       rm -f .gitattributes &&
+       git reset --hard b &&
+       test_must_fail git merge a &&
+       fuzz_conflict file >file.fuzzy &&
+       test_cmp expected file.fuzzy
+'
+
+test_expect_failure 'checkout -m after setting text=auto' '
+       cat <<-\EOF >expected &&
+       first line
+       same line
+       EOF
+
+       git config merge.renormalize true &&
+       git rm -fr . &&
+       rm -f .gitattributes &&
+       git reset --hard initial &&
+       git checkout a -- . &&
+       git checkout -m b &&
+       test_cmp expected file
+'
+
+test_expect_failure 'checkout -m addition of text=auto' '
+       cat <<-\EOF >expected &&
+       first line
+       same line
+       EOF
+
+       git config merge.renormalize true &&
+       git rm -fr . &&
+       rm -f .gitattributes file &&
+       git reset --hard initial &&
+       git checkout b -- . &&
+       git checkout -m a &&
+       test_cmp expected file
+'
+
+test_expect_failure 'cherry-pick patch from after text=auto was added' '
+       append_cr <<-\EOF >expected &&
+       first line
+       same line
+       EOF
+
+       git config merge.renormalize true &&
+       git rm -fr . &&
+       git reset --hard b &&
+       test_must_fail git cherry-pick a >err 2>&1 &&
+       grep "[Nn]othing added" err &&
+       test_cmp expected file
+'
+
+test_expect_success 'Test delete/normalize conflict' '
+       git checkout -f side &&
+       git rm -fr . &&
+       rm -f .gitattributes &&
+       git reset --hard initial &&
+       git rm file &&
+       git commit -m "remove file" &&
+       git checkout master &&
+       git reset --hard a^ &&
+       git merge side
+'
+
+test_done
index 1785e178a4cb8fddd58d1b1db8062cf12825155e..1e0447f615c55ecf98ae341553ea60f10a956ae3 100755 (executable)
@@ -60,7 +60,7 @@ test_expect_success 'checkout' '
 
 test_expect_success 'checkout with local tracked branch' '
        git checkout master &&
-       git checkout follower >actual
+       git checkout follower >actual &&
        grep "is ahead of" actual
 '
 
index 4185b7ca1d3679b458d16753581fe78e3b68b376..ae2194e07d835aa088a1c5b5f5fa5001f41ea7d5 100755 (executable)
@@ -53,7 +53,7 @@ test_expect_success 'set up buggy branch' '
      echo "line 12" >> hello &&
      echo "line 13" >> hello &&
      add_and_commit_file hello "2 more lines" &&
-     HASH6=$(git rev-parse --verify HEAD)
+     HASH6=$(git rev-parse --verify HEAD) &&
      echo "line 14" >> hello &&
      echo "line 15" >> hello &&
      echo "line 16" >> hello &&
@@ -104,17 +104,18 @@ test_expect_success '"git fsck" works' '
 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 ..
+     (
+         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
+     )
 '
 
 test_expect_success '"git replace" listing and deleting' '
@@ -177,10 +178,11 @@ test_expect_success 'create parallel branch without the bug' '
 
 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 ..
+     (
+         cd clone_dir &&
+         git checkout parallel &&
+         git log --pretty=oneline | grep $PARA2
+     )
 '
 
 test_expect_success 'push branch with replacement' '
@@ -191,25 +193,34 @@ test_expect_success 'push branch with replacement' '
      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 ..
+     (
+         cd clone_dir &&
+         git checkout parallel2 &&
+         git log --pretty=oneline | grep $PARA3 &&
+         git show $PARA3 | grep "A U Thor"
+     )
 '
 
 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 ..
+     (
+         cd clone_dir &&
+         git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
+         git log --pretty=oneline parallel3 > output.txt &&
+         ! grep $PARA3 output.txt &&
+         git show $PARA3 > para3.txt &&
+         grep "A U Thor" para3.txt &&
+         git fetch origin "refs/replace/*:refs/replace/*" &&
+         git log --pretty=oneline parallel3 > output.txt &&
+         grep $PARA3 output.txt &&
+         git show $PARA3 > para3.txt &&
+         grep "O Thor" para3.txt
+     )
 '
 
 test_expect_success 'bisect and replacements' '
      git bisect start $HASH7 $HASH1 &&
-     test "$S" = "$(git rev-parse --verify HEAD)" &&
+     test "$PARA3" = "$(git rev-parse --verify HEAD)" &&
      git bisect reset &&
      GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
      test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
index 42f8ece0978f38e10200a52ce8cf1952cf13ecbd..9a168069217ef8d82173e563a04eaefe58d99f2a 100755 (executable)
@@ -70,14 +70,13 @@ test_expect_success setup '
                i=$(($i+1))
        done &&
 
-       git show-branch
-'
+       git show-branch &&
 
-cat >expected <<\EOF
-Merge branch 'left'
-EOF
+       apos="'\''"
+'
 
-test_expect_success 'merge-msg test #1' '
+test_expect_success 'message for merging local branch' '
+       echo "Merge branch ${apos}left${apos}" >expected &&
 
        git checkout master &&
        git fetch . left &&
@@ -86,11 +85,8 @@ test_expect_success 'merge-msg test #1' '
        test_cmp expected actual
 '
 
-cat >expected <<EOF
-Merge branch 'left' of $(pwd)
-EOF
-
-test_expect_success 'merge-msg test #2' '
+test_expect_success 'message for merging external branch' '
+       echo "Merge branch ${apos}left${apos} of $(pwd)" >expected &&
 
        git checkout master &&
        git fetch "$(pwd)" left &&
@@ -99,139 +95,231 @@ test_expect_success 'merge-msg test #2' '
        test_cmp expected actual
 '
 
-cat >expected <<\EOF
-Merge branch 'left'
+test_expect_success '[merge] summary/log configuration' '
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}
 
-* left:
-  Left #5
-  Left #4
-  Left #3
-  Common #2
-  Common #1
-EOF
+       * left:
+         Left #5
+         Left #4
+         Left #3
+         Common #2
+         Common #1
+       EOF
 
-test_expect_success 'merge-msg test #3-1' '
-
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
        git config merge.log true &&
+       test_might_fail git config --unset-all merge.summary &&
 
        git checkout master &&
        test_tick &&
        git fetch . left &&
 
-       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
-       test_cmp expected actual
-'
-
-test_expect_success 'merge-msg test #3-2' '
+       git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
 
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
+       test_might_fail git config --unset-all merge.log &&
        git config merge.summary true &&
 
        git checkout master &&
        test_tick &&
        git fetch . left &&
 
-       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
+
+       test_cmp expected actual1 &&
+       test_cmp expected actual2
+'
+
+test_expect_success 'setup: clear [merge] configuration' '
+       test_might_fail git config --unset-all merge.log &&
+       test_might_fail git config --unset-all merge.summary
+'
+
+test_expect_success 'setup FETCH_HEAD' '
+       git checkout master &&
+       test_tick &&
+       git fetch . left
+'
+
+test_expect_success 'merge.log=3 limits shortlog length' '
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}
+
+       * left: (5 commits)
+         Left #5
+         Left #4
+         Left #3
+         ...
+       EOF
+
+       git -c merge.log=3 fmt-merge-msg <.git/FETCH_HEAD >actual &&
        test_cmp expected actual
 '
 
-cat >expected <<\EOF
-Merge branches 'left' and 'right'
+test_expect_success 'merge.log=5 shows all 5 commits' '
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}
 
-* left:
-  Left #5
-  Left #4
-  Left #3
-  Common #2
-  Common #1
+       * left:
+         Left #5
+         Left #4
+         Left #3
+         Common #2
+         Common #1
+       EOF
 
-* right:
-  Right #5
-  Right #4
-  Right #3
-  Common #2
-  Common #1
-EOF
+       git -c merge.log=5 fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
 
-test_expect_success 'merge-msg test #4-1' '
+test_expect_success 'merge.log=0 disables shortlog' '
+       echo "Merge branch ${apos}left${apos}" >expected
+       git -c merge.log=0 fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
 
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
-       git config merge.log true &&
+test_expect_success '--log=3 limits shortlog length' '
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}
 
-       git checkout master &&
-       test_tick &&
-       git fetch . left right &&
+       * left: (5 commits)
+         Left #5
+         Left #4
+         Left #3
+         ...
+       EOF
 
-       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       git fmt-merge-msg --log=3 <.git/FETCH_HEAD >actual &&
        test_cmp expected actual
 '
 
-test_expect_success 'merge-msg test #4-2' '
+test_expect_success '--log=5 shows all 5 commits' '
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}
 
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
-       git config merge.summary true &&
+       * left:
+         Left #5
+         Left #4
+         Left #3
+         Common #2
+         Common #1
+       EOF
 
-       git checkout master &&
-       test_tick &&
-       git fetch . left right &&
+       git fmt-merge-msg --log=5 <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
 
-       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+test_expect_success '--no-log disables shortlog' '
+       echo "Merge branch ${apos}left${apos}" >expected &&
+       git fmt-merge-msg --no-log <.git/FETCH_HEAD >actual &&
        test_cmp expected actual
 '
 
-test_expect_success 'merge-msg test #5-1' '
+test_expect_success '--log=0 disables shortlog' '
+       echo "Merge branch ${apos}left${apos}" >expected &&
+       git fmt-merge-msg --no-log <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
 
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
-       git config merge.log yes &&
+test_expect_success 'fmt-merge-msg -m' '
+       echo "Sync with left" >expected &&
+       cat >expected.log <<-EOF &&
+       Sync with left
+
+       * ${apos}left${apos} of $(pwd):
+         Left #5
+         Left #4
+         Left #3
+         Common #2
+         Common #1
+       EOF
+
+       test_might_fail git config --unset merge.log &&
+       test_might_fail git config --unset merge.summary &&
+       git checkout master &&
+       git fetch "$(pwd)" left &&
+       git fmt-merge-msg -m "Sync with left" <.git/FETCH_HEAD >actual &&
+       git fmt-merge-msg --log -m "Sync with left" \
+                                       <.git/FETCH_HEAD >actual.log &&
+       git config merge.log true &&
+       git fmt-merge-msg -m "Sync with left" \
+                                       <.git/FETCH_HEAD >actual.log-config &&
+       git fmt-merge-msg --no-log -m "Sync with left" \
+                                       <.git/FETCH_HEAD >actual.nolog &&
+
+       test_cmp expected actual &&
+       test_cmp expected.log actual.log &&
+       test_cmp expected.log actual.log-config &&
+       test_cmp expected actual.nolog
+'
+
+test_expect_success 'setup: expected shortlog for two branches' '
+       cat >expected <<-EOF
+       Merge branches ${apos}left${apos} and ${apos}right${apos}
+
+       * left:
+         Left #5
+         Left #4
+         Left #3
+         Common #2
+         Common #1
+
+       * right:
+         Right #5
+         Right #4
+         Right #3
+         Common #2
+         Common #1
+       EOF
+'
 
+test_expect_success 'shortlog for two branches' '
+       git config merge.log true &&
+       test_might_fail git config --unset-all merge.summary &&
        git checkout master &&
        test_tick &&
        git fetch . left right &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
 
-       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
-       test_cmp expected actual
-'
+       test_might_fail git config --unset-all merge.log &&
+       git config merge.summary true &&
+       git checkout master &&
+       test_tick &&
+       git fetch . left right &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
 
-test_expect_success 'merge-msg test #5-2' '
+       git config merge.log yes &&
+       test_might_fail git config --unset-all merge.summary &&
+       git checkout master &&
+       test_tick &&
+       git fetch . left right &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual3 &&
 
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
+       test_might_fail git config --unset-all merge.log &&
        git config merge.summary yes &&
-
        git checkout master &&
        test_tick &&
        git fetch . left right &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual4 &&
 
-       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
-       test_cmp expected actual
+       test_cmp expected actual1 &&
+       test_cmp expected actual2 &&
+       test_cmp expected actual3 &&
+       test_cmp expected actual4
 '
 
 test_expect_success 'merge-msg -F' '
-
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
+       test_might_fail git config --unset-all merge.log &&
        git config merge.summary yes &&
-
        git checkout master &&
        test_tick &&
        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
+       test_might_fail git config --unset-all merge.log &&
        git config merge.summary yes &&
-
        git checkout master &&
        test_tick &&
        git fetch . left right &&
@@ -245,11 +333,11 @@ test_expect_success 'merge-msg -F in subdirectory' '
 '
 
 test_expect_success 'merge-msg with nothing to merge' '
-
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
+       test_might_fail git config --unset-all merge.log &&
        git config merge.summary yes &&
 
+       >empty &&
+
        (
                cd remote &&
                git checkout -b unrelated &&
@@ -258,22 +346,20 @@ test_expect_success 'merge-msg with nothing to merge' '
                git fmt-merge-msg <.git/FETCH_HEAD >../actual
        ) &&
 
-       test_cmp /dev/null actual
+       test_cmp empty actual
 '
 
-cat >expected <<\EOF
-Merge tag 'tag-r3'
-
-* tag 'tag-r3':
-  Right #3
-  Common #2
-  Common #1
-EOF
-
 test_expect_success 'merge-msg tag' '
+       cat >expected <<-EOF &&
+       Merge tag ${apos}tag-r3${apos}
+
+       * tag ${apos}tag-r3${apos}:
+         Right #3
+         Common #2
+         Common #1
+       EOF
 
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
+       test_might_fail git config --unset-all merge.log &&
        git config merge.summary yes &&
 
        git checkout master &&
@@ -284,26 +370,24 @@ test_expect_success 'merge-msg tag' '
        test_cmp expected actual
 '
 
-cat >expected <<\EOF
-Merge tags 'tag-r3' and 'tag-l5'
-
-* tag 'tag-r3':
-  Right #3
-  Common #2
-  Common #1
-
-* tag 'tag-l5':
-  Left #5
-  Left #4
-  Left #3
-  Common #2
-  Common #1
-EOF
-
 test_expect_success 'merge-msg two tags' '
-
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
+       cat >expected <<-EOF &&
+       Merge tags ${apos}tag-r3${apos} and ${apos}tag-l5${apos}
+
+       * tag ${apos}tag-r3${apos}:
+         Right #3
+         Common #2
+         Common #1
+
+       * tag ${apos}tag-l5${apos}:
+         Left #5
+         Left #4
+         Left #3
+         Common #2
+         Common #1
+       EOF
+
+       test_might_fail git config --unset-all merge.log &&
        git config merge.summary yes &&
 
        git checkout master &&
@@ -314,26 +398,24 @@ test_expect_success 'merge-msg two tags' '
        test_cmp expected actual
 '
 
-cat >expected <<\EOF
-Merge branch 'left', tag 'tag-r3'
-
-* tag 'tag-r3':
-  Right #3
-  Common #2
-  Common #1
-
-* left:
-  Left #5
-  Left #4
-  Left #3
-  Common #2
-  Common #1
-EOF
-
 test_expect_success 'merge-msg tag and branch' '
-
-       git config --unset-all merge.log
-       git config --unset-all merge.summary
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}, tag ${apos}tag-r3${apos}
+
+       * tag ${apos}tag-r3${apos}:
+         Right #3
+         Common #2
+         Common #1
+
+       * left:
+         Left #5
+         Left #4
+         Left #3
+         Common #2
+         Common #1
+       EOF
+
+       test_might_fail git config --unset-all merge.log &&
        git config merge.summary yes &&
 
        git checkout master &&
@@ -344,26 +426,27 @@ test_expect_success 'merge-msg tag and branch' '
        test_cmp expected actual
 '
 
-cat >expected <<\EOF
-Merge branch 'long'
-
-* long: (35 commits)
-EOF
-
 test_expect_success 'merge-msg lots of commits' '
+       {
+               cat <<-EOF &&
+               Merge branch ${apos}long${apos}
+
+               * long: (35 commits)
+               EOF
+
+               i=29 &&
+               while test $i -gt 9
+               do
+                       echo "  $i" &&
+                       i=$(($i-1))
+               done &&
+               echo "  ..."
+       } >expected &&
 
        git checkout master &&
        test_tick &&
        git fetch . long &&
 
-       i=29 &&
-       while test $i -gt 9
-       do
-               echo "  $i" &&
-               i=$(($i-1))
-       done >>expected &&
-       echo "  ..." >>expected
-
        git fmt-merge-msg <.git/FETCH_HEAD >actual &&
        test_cmp expected actual
 '
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
new file mode 100755 (executable)
index 0000000..82f3639
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+test_description='basic git gc tests
+'
+
+. ./test-lib.sh
+
+test_expect_success 'gc empty repository' '
+       git gc
+'
+
+test_expect_success 'gc --gobbledegook' '
+       test_expect_code 129 git gc --nonsense 2>err &&
+       grep "[Uu]sage: git gc" err
+'
+
+test_expect_success 'gc -h with invalid configuration' '
+       mkdir broken &&
+       (
+               cd broken &&
+               git init &&
+               echo "[gc] pruneexpire = CORRUPT" >>.git/config &&
+               test_expect_code 129 git gc -h >usage 2>&1
+       ) &&
+       grep "[Uu]sage" broken/usage
+'
+
+test_done
index 65a35d94a001b555ce9e4d6c528d588339a5300b..a845b154e4db50b52eb67eaefefcc42403c52c0e 100755 (executable)
@@ -61,7 +61,7 @@ test_expect_success \
 test_expect_success \
     'checking -f on untracked file with existing target' \
     'touch path0/untracked1 &&
-     git mv -f untracked1 path0
+     test_must_fail git mv -f untracked1 path0 &&
      test ! -f .git/index.lock &&
      test -f untracked1 &&
      test -f path0/untracked1'
@@ -207,7 +207,7 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' '
        git init &&
        echo 1 >dirty &&
        git add dirty &&
-       entry="$(git ls-files --stage dirty | cut -f 1)"
+       entry="$(git ls-files --stage dirty | cut -f 1)" &&
        git mv dirty dirty2 &&
        [ "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" ] &&
        echo 2 >dirty2 &&
index 2c55801ee8b88b2d97968a24706dcfdab824668b..e0227730deb88413301b37501fdb165a2c90460e 100755 (executable)
@@ -3,31 +3,34 @@
 test_description='git filter-branch'
 . ./test-lib.sh
 
-make_commit () {
-       lower=$(echo $1 | tr '[A-Z]' '[a-z]')
-       echo $lower > $lower
-       git add $lower
-       test_tick
-       git commit -m $1
-       git tag $1
-}
-
 test_expect_success 'setup' '
-       make_commit A
-       make_commit B
-       git checkout -b branch B
-       make_commit D
-       mkdir dir
-       make_commit dir/D
-       make_commit E
-       git checkout master
-       make_commit C
-       git checkout branch
-       git merge C
-       git tag F
-       make_commit G
-       make_commit H
-'
+       test_commit A &&
+       test_commit B &&
+       git checkout -b branch B &&
+       test_commit D &&
+       mkdir dir &&
+       test_commit dir/D &&
+       test_commit E &&
+       git checkout master &&
+       test_commit C &&
+       git checkout branch &&
+       git merge C &&
+       git tag F &&
+       test_commit G &&
+       test_commit H
+'
+# * (HEAD, branch) H
+# * G
+# *   Merge commit 'C' into branch
+# |\
+# | * (master) C
+# * | E
+# * | dir/D
+# * | D
+# |/
+# * B
+# * A
+
 
 H=$(git rev-parse H)
 
@@ -65,14 +68,14 @@ test_expect_success 'Fail if commit filter fails' '
 '
 
 test_expect_success 'rewrite, renaming a specific file' '
-       git filter-branch -f --tree-filter "mv d doh || :" HEAD
+       git filter-branch -f --tree-filter "mv D.t doh || :" HEAD
 '
 
 test_expect_success 'test that the file was renamed' '
-       test d = "$(git show HEAD:doh --)" &&
-       ! test -f d &&
+       test D = "$(git show HEAD:doh --)" &&
+       ! test -f D.t &&
        test -f doh &&
-       test d = "$(cat doh)"
+       test D = "$(cat doh)"
 '
 
 test_expect_success 'rewrite, renaming a specific directory' '
@@ -80,18 +83,18 @@ test_expect_success 'rewrite, renaming a specific directory' '
 '
 
 test_expect_success 'test that the directory was renamed' '
-       test dir/d = "$(git show HEAD:diroh/d --)" &&
+       test dir/D = "$(git show HEAD:diroh/D.t --)" &&
        ! test -d dir &&
        test -d diroh &&
        ! test -d diroh/dir &&
-       test -f diroh/d &&
-       test dir/d = "$(cat diroh/d)"
+       test -f diroh/D.t &&
+       test dir/D = "$(cat diroh/D.t)"
 '
 
 git tag oldD HEAD~4
 test_expect_success 'rewrite one branch, keeping a side branch' '
        git branch modD oldD &&
-       git filter-branch -f --tree-filter "mv b boh || :" D..modD
+       git filter-branch -f --tree-filter "mv B.t boh || :" D..modD
 '
 
 test_expect_success 'common ancestor is still common (unchanged)' '
@@ -104,13 +107,13 @@ test_expect_success 'filter subdirectory only' '
        git add subdir/new &&
        test_tick &&
        git commit -m "subdir" &&
-       echo H > a &&
+       echo H > A.t &&
        test_tick &&
-       git commit -m "not subdir" a &&
+       git commit -m "not subdir" A.t &&
        echo A > subdir/new &&
        test_tick &&
        git commit -m "again subdir" subdir/new &&
-       git rm a &&
+       git rm A.t &&
        test_tick &&
        git commit -m "again not subdir" &&
        git branch sub &&
@@ -134,7 +137,7 @@ test_expect_success 'more setup' '
        git add subdir/new &&
        test_tick &&
        git commit -m "subdir on master" subdir/new &&
-       git rm a &&
+       git rm A.t &&
        test_tick &&
        git commit -m "again subdir on master" &&
        git merge branch
@@ -283,8 +286,8 @@ test_expect_success 'Tag name filtering allows slashes in tag names' '
 
 test_expect_success 'Prune empty commits' '
        git rev-list HEAD > expect &&
-       make_commit to_remove &&
-       git filter-branch -f --index-filter "git update-index --remove to_remove" --prune-empty HEAD &&
+       test_commit to_remove &&
+       git filter-branch -f --index-filter "git update-index --remove to_remove.t" --prune-empty HEAD &&
        git rev-list HEAD > actual &&
        test_cmp expect actual
 '
@@ -307,6 +310,24 @@ test_expect_success '--remap-to-ancestor with filename filters' '
        test $orig_invariant = $(git rev-parse invariant)
 '
 
+test_expect_success 'automatic remapping to ancestor with filename filters' '
+       git checkout master &&
+       git reset --hard A &&
+       test_commit add-foo2 foo 1 &&
+       git branch moved-foo2 &&
+       test_commit add-bar2 bar a &&
+       git branch invariant2 &&
+       orig_invariant=$(git rev-parse invariant2) &&
+       git branch moved-bar2 &&
+       test_commit change-foo2 foo 2 &&
+       git filter-branch -f \
+               moved-foo2 moved-bar2 A..master \
+               -- -- foo &&
+       test $(git rev-parse moved-foo2) = $(git rev-parse moved-bar2) &&
+       test $(git rev-parse moved-foo2) = $(git rev-parse master^) &&
+       test $orig_invariant = $(git rev-parse invariant2)
+'
+
 test_expect_success 'setup submodule' '
        rm -fr ?* .git &&
        git init &&
index ac943f5eeecd17dd1edc5834265df9e16e5032f2..3e7baaf89f9bada46fc084e568544ce9df833594 100755 (executable)
@@ -1030,6 +1030,72 @@ test_expect_success GPG \
        test_cmp expect actual
 '
 
+# usage with rfc1991 signatures
+echo "rfc1991" > gpghome/gpg.conf
+get_tag_header rfc1991-signed-tag $commit commit $time >expect
+echo "RFC1991 signed tag" >>expect
+echo '-----BEGIN PGP MESSAGE-----' >>expect
+test_expect_success GPG \
+       'creating a signed tag with rfc1991' '
+       git tag -s -m "RFC1991 signed tag" rfc1991-signed-tag $commit &&
+       get_tag_msg rfc1991-signed-tag >actual &&
+       test_cmp expect actual
+'
+
+cat >fakeeditor <<'EOF'
+#!/bin/sh
+cp "$1" actual
+EOF
+chmod +x fakeeditor
+
+test_expect_success GPG \
+       'reediting a signed tag body omits signature' '
+       echo "RFC1991 signed tag" >expect &&
+       GIT_EDITOR=./fakeeditor git tag -f -s rfc1991-signed-tag $commit &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG \
+       'verifying rfc1991 signature' '
+       git tag -v rfc1991-signed-tag
+'
+
+test_expect_success GPG \
+       'list tag with rfc1991 signature' '
+       echo "rfc1991-signed-tag RFC1991 signed tag" >expect &&
+       git tag -l -n1 rfc1991-signed-tag >actual &&
+       test_cmp expect actual &&
+       git tag -l -n2 rfc1991-signed-tag >actual &&
+       test_cmp expect actual &&
+       git tag -l -n999 rfc1991-signed-tag >actual &&
+       test_cmp expect actual
+'
+
+rm -f gpghome/gpg.conf
+
+test_expect_success GPG \
+       'verifying rfc1991 signature without --rfc1991' '
+       git tag -v rfc1991-signed-tag
+'
+
+test_expect_success GPG \
+       'list tag with rfc1991 signature without --rfc1991' '
+       echo "rfc1991-signed-tag RFC1991 signed tag" >expect &&
+       git tag -l -n1 rfc1991-signed-tag >actual &&
+       test_cmp expect actual &&
+       git tag -l -n2 rfc1991-signed-tag >actual &&
+       test_cmp expect actual &&
+       git tag -l -n999 rfc1991-signed-tag >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG \
+       'reediting a signed tag body omits signature' '
+       echo "RFC1991 signed tag" >expect &&
+       GIT_EDITOR=./fakeeditor git tag -f -s rfc1991-signed-tag $commit &&
+       test_cmp expect actual
+'
+
 # try to sign with bad user.signingkey
 git config user.signingkey BobTheMouse
 test_expect_success GPG \
@@ -1051,13 +1117,23 @@ test_expect_success \
 
 test_expect_success \
        'message in editor has initial comment' '
-       GIT_EDITOR=cat git tag -a initial-comment > actual
+       ! (GIT_EDITOR=cat git tag -a initial-comment > actual)
+'
+
+test_expect_success \
+       'message in editor has initial comment: first line' '
        # check the first line --- should be empty
-       first=$(sed -e 1q <actual) &&
-       test -z "$first" &&
+       echo >first.expect &&
+       sed -e 1q <actual >first.actual &&
+       test_cmp first.expect first.actual
+'
+
+test_expect_success \
+       'message in editor has initial comment: remainder' '
        # remove commented lines from the remainder -- should be empty
-       rest=$(sed -e 1d -e '/^#/d' <actual) &&
-       test -z "$rest"
+       >rest.expect
+       sed -e 1d -e '/^#/d' <actual >rest.actual &&
+       test_cmp rest.expect rest.actual
 '
 
 get_tag_header reuse $commit commit $time >expect
@@ -1097,7 +1173,7 @@ hash1=$(git rev-parse HEAD)
 test_expect_success 'creating second commit and tag' '
        echo foo-2.0 >foo &&
        git add foo &&
-       git commit -m second
+       git commit -m second &&
        git tag v2.0
 '
 
@@ -1122,18 +1198,18 @@ v2.0
 EOF
 
 test_expect_success 'checking that first commit is in all tags (hash)' "
-       git tag -l --contains $hash1 v* >actual
+       git tag -l --contains $hash1 v* >actual &&
        test_cmp expected actual
 "
 
 # other ways of specifying the commit
 test_expect_success 'checking that first commit is in all tags (tag)' "
-       git tag -l --contains v1.0 v* >actual
+       git tag -l --contains v1.0 v* >actual &&
        test_cmp expected actual
 "
 
 test_expect_success 'checking that first commit is in all tags (relative)' "
-       git tag -l --contains HEAD~2 v* >actual
+       git tag -l --contains HEAD~2 v* >actual &&
        test_cmp expected actual
 "
 
@@ -1142,7 +1218,7 @@ v2.0
 EOF
 
 test_expect_success 'checking that second commit only has one tag' "
-       git tag -l --contains $hash2 v* >actual
+       git tag -l --contains $hash2 v* >actual &&
        test_cmp expected actual
 "
 
@@ -1151,7 +1227,7 @@ cat > expected <<EOF
 EOF
 
 test_expect_success 'checking that third commit has no tags' "
-       git tag -l --contains $hash3 v* >actual
+       git tag -l --contains $hash3 v* >actual &&
        test_cmp expected actual
 "
 
@@ -1161,7 +1237,7 @@ test_expect_success 'creating simple branch' '
        git branch stable v2.0 &&
         git checkout stable &&
        echo foo-3.0 > foo &&
-       git commit foo -m fourth
+       git commit foo -m fourth &&
        git tag v3.0
 '
 
@@ -1172,7 +1248,7 @@ v3.0
 EOF
 
 test_expect_success 'checking that branch head only has one tag' "
-       git tag -l --contains $hash4 v* >actual
+       git tag -l --contains $hash4 v* >actual &&
        test_cmp expected actual
 "
 
@@ -1186,7 +1262,7 @@ v4.0
 EOF
 
 test_expect_success 'checking that original branch head has one tag now' "
-       git tag -l --contains $hash3 v* >actual
+       git tag -l --contains $hash3 v* >actual &&
        test_cmp expected actual
 "
 
@@ -1201,18 +1277,18 @@ v4.0
 EOF
 
 test_expect_success 'checking that initial commit is in all tags' "
-       git tag -l --contains $hash1 v* >actual
+       git tag -l --contains $hash1 v* >actual &&
        test_cmp expected actual
 "
 
 # mixing modes and options:
 
 test_expect_success 'mixing incompatibles modes and options is forbidden' '
-       test_must_fail git tag -a
-       test_must_fail git tag -l -v
-       test_must_fail git tag -n 100
-       test_must_fail git tag -l -m msg
-       test_must_fail git tag -l -F some file
+       test_must_fail git tag -a &&
+       test_must_fail git tag -l -v &&
+       test_must_fail git tag -n 100 &&
+       test_must_fail git tag -l -m msg &&
+       test_must_fail git tag -l -F some file &&
        test_must_fail git tag -v -s
 '
 
index 26ddf9d496f6eacddbec603659be7fca3dff294b..1b530b5022fb58ce51ad8cd44c630acdca613311 100755 (executable)
@@ -111,13 +111,13 @@ do
        '
 done
 
-if echo 'echo space > "$1"' > "e space.sh"
+if echo 'echo space > "$1"' > "e space.sh"
 then
-       skip_all="Skipping; FS does not support spaces in filenames"
-       test_done
+       # FS supports spaces in filenames
+       test_set_prereq SPACES_IN_FILENAMES
 fi
 
-test_expect_success 'editor with a space' '
+test_expect_success SPACES_IN_FILENAMES 'editor with a space' '
 
        chmod a+x "e space.sh" &&
        GIT_EDITOR="./e\ space.sh" git commit --amend &&
@@ -126,7 +126,7 @@ test_expect_success 'editor with a space' '
 '
 
 unset GIT_EDITOR
-test_expect_success 'core.editor with a space' '
+test_expect_success SPACES_IN_FILENAMES 'core.editor with a space' '
 
        git config core.editor \"./e\ space.sh\" &&
        git commit --amend &&
index 71d3ceff8f06ad33aee5e48f85e97164df447a59..ed7575d0fdf9eebf65d808fa810ab4cc03095025 100755 (executable)
@@ -4,44 +4,15 @@ test_description='Test automatic use of a pager.'
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-pager.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
 
 cleanup_fail() {
        echo >&2 cleanup failed
        (exit 1)
 }
 
-test_expect_success 'set up terminal for tests' '
-       rm -f stdout_is_tty ||
-       cleanup_fail &&
-
-       if test -t 1
-       then
-               >stdout_is_tty
-       elif
-               test_have_prereq PERL &&
-               "$PERL_PATH" "$TEST_DIRECTORY"/t7006/test-terminal.perl \
-                       sh -c "test -t 1"
-       then
-               >test_terminal_works
-       fi
-'
-
-if test -e stdout_is_tty
-then
-       test_terminal() { "$@"; }
-       test_set_prereq TTY
-elif test -e test_terminal_works
-then
-       test_terminal() {
-               "$PERL_PATH" "$TEST_DIRECTORY"/t7006/test-terminal.perl "$@"
-       }
-       test_set_prereq TTY
-else
-       say "# no usable terminal, so skipping some tests"
-fi
-
 test_expect_success 'setup' '
-       unset GIT_PAGER GIT_PAGER_IN_USE;
+       sane_unset GIT_PAGER GIT_PAGER_IN_USE &&
        test_might_fail git config --unset core.pager &&
 
        PAGER="cat >paginated.out" &&
@@ -58,6 +29,21 @@ test_expect_success TTY 'some commands use a pager' '
        test -e paginated.out
 '
 
+test_expect_failure TTY 'pager runs from subdir' '
+       echo subdir/paginated.out >expected &&
+       mkdir -p subdir &&
+       rm -f paginated.out subdir/paginated.out &&
+       (
+               cd subdir &&
+               test_terminal git log
+       ) &&
+       {
+               ls paginated.out subdir/paginated.out ||
+               :
+       } >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success TTY 'some commands do not use a pager' '
        rm -f paginated.out ||
        cleanup_fail &&
@@ -106,6 +92,45 @@ test_expect_success TTY 'no pager with --no-pager' '
        ! test -e paginated.out
 '
 
+test_expect_success TTY 'configuration can disable pager' '
+       rm -f paginated.out &&
+       test_might_fail git config --unset pager.grep &&
+       test_terminal git grep initial &&
+       test -e paginated.out &&
+
+       rm -f paginated.out &&
+       git config pager.grep false &&
+       test_when_finished "git config --unset pager.grep" &&
+       test_terminal git grep initial &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git config uses a pager if configured to' '
+       rm -f paginated.out &&
+       git config pager.config true &&
+       test_when_finished "git config --unset pager.config" &&
+       test_terminal git config --list &&
+       test -e paginated.out
+'
+
+test_expect_success TTY 'configuration can enable pager (from subdir)' '
+       rm -f paginated.out &&
+       mkdir -p subdir &&
+       git config pager.bundle true &&
+       test_when_finished "git config --unset pager.bundle" &&
+
+       git bundle create test.bundle --all &&
+       rm -f paginated.out subdir/paginated.out &&
+       (
+               cd subdir &&
+               test_terminal git bundle unbundle ../test.bundle
+       ) &&
+       {
+               test -e paginated.out ||
+               test -e subdir/paginated.out
+       }
+'
+
 # A colored commit log will begin with an appropriate ANSI escape
 # for the first color; the text "commit" comes later.
 colorful() {
@@ -159,11 +184,6 @@ test_expect_success 'color when writing to a file intended for a pager' '
        colorful colorful.log
 '
 
-if test_have_prereq SIMPLEPAGER && test_have_prereq TTY
-then
-       test_set_prereq SIMPLEPAGERTTY
-fi
-
 # Use this helper to make it easy for the caller of your
 # terminal-using function to specify whether it should fail.
 # If you write
@@ -199,8 +219,8 @@ parse_args() {
 test_default_pager() {
        parse_args "$@"
 
-       $test_expectation SIMPLEPAGERTTY "$cmd - default pager is used by default" "
-               unset PAGER GIT_PAGER;
+       $test_expectation SIMPLEPAGER,TTY "$cmd - default pager is used by default" "
+               sane_unset PAGER GIT_PAGER &&
                test_might_fail git config --unset core.pager &&
                rm -f default_pager_used ||
                cleanup_fail &&
@@ -223,7 +243,7 @@ test_PAGER_overrides() {
        parse_args "$@"
 
        $test_expectation TTY "$cmd - PAGER overrides default pager" "
-               unset GIT_PAGER;
+               sane_unset GIT_PAGER &&
                test_might_fail git config --unset core.pager &&
                rm -f PAGER_used ||
                cleanup_fail &&
@@ -251,7 +271,7 @@ test_core_pager() {
        parse_args "$@"
 
        $test_expectation TTY "$cmd - repository-local core.pager setting $used_if_wanted" "
-               unset GIT_PAGER;
+               sane_unset GIT_PAGER &&
                rm -f core.pager_used ||
                cleanup_fail &&
 
@@ -279,7 +299,7 @@ test_pager_subdir_helper() {
        parse_args "$@"
 
        $test_expectation TTY "$cmd - core.pager $used_if_wanted from subdirectory" "
-               unset GIT_PAGER;
+               sane_unset GIT_PAGER &&
                rm -f core.pager_used &&
                rm -fr sub ||
                cleanup_fail &&
@@ -369,4 +389,45 @@ test_GIT_PAGER_overrides  expect_success test_must_fail 'git -p'
 
 test_doesnt_paginate      expect_failure test_must_fail 'git -p nonsense'
 
+test_pager_choices                       'git shortlog'
+test_expect_success 'setup: configure shortlog not to paginate' '
+       git config pager.shortlog false
+'
+test_doesnt_paginate      expect_success 'git shortlog'
+test_no_local_config_subdir expect_success 'git shortlog'
+test_default_pager        expect_success 'git -p shortlog'
+test_core_pager_subdir    expect_success 'git -p shortlog'
+
+test_core_pager_subdir    expect_success test_must_fail \
+                                        'git -p apply </dev/null'
+
+test_expect_success TTY 'command-specific pager' '
+       unset PAGER GIT_PAGER;
+       echo "foo:initial" >expect &&
+       >actual &&
+       git config --unset core.pager &&
+       git config pager.log "sed s/^/foo:/ >actual" &&
+       test_terminal git log --format=%s -1 &&
+       test_cmp expect actual
+'
+
+test_expect_success TTY 'command-specific pager overrides core.pager' '
+       unset PAGER GIT_PAGER;
+       echo "foo:initial" >expect &&
+       >actual &&
+       git config core.pager "exit 1"
+       git config pager.log "sed s/^/foo:/ >actual" &&
+       test_terminal git log --format=%s -1 &&
+       test_cmp expect actual
+'
+
+test_expect_success TTY 'command-specific pager overridden by environment' '
+       GIT_PAGER="sed s/^/foo:/ >actual" && export GIT_PAGER &&
+       >actual &&
+       echo "foo:initial" >expect &&
+       git config pager.log "exit 1" &&
+       test_terminal git log --format=%s -1 &&
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7006/test-terminal.perl b/t/t7006/test-terminal.perl
deleted file mode 100755 (executable)
index 73ff809..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/perl
-use strict;
-use warnings;
-use IO::Pty;
-use File::Copy;
-
-# Run @$argv in the background with stdout redirected to $out.
-sub start_child {
-       my ($argv, $out) = @_;
-       my $pid = fork;
-       if (not defined $pid) {
-               die "fork failed: $!"
-       } elsif ($pid == 0) {
-               open STDOUT, ">&", $out;
-               close $out;
-               exec(@$argv) or die "cannot exec '$argv->[0]': $!"
-       }
-       return $pid;
-}
-
-# Wait for $pid to finish.
-sub finish_child {
-       # Simplified from wait_or_whine() in run-command.c.
-       my ($pid) = @_;
-
-       my $waiting = waitpid($pid, 0);
-       if ($waiting < 0) {
-               die "waitpid failed: $!";
-       } elsif ($? & 127) {
-               my $code = $? & 127;
-               warn "died of signal $code";
-               return $code - 128;
-       } else {
-               return $? >> 8;
-       }
-}
-
-sub xsendfile {
-       my ($out, $in) = @_;
-
-       # Note: the real sendfile() cannot read from a terminal.
-
-       # It is unspecified by POSIX whether reads
-       # from a disconnected terminal will return
-       # EIO (as in AIX 4.x, IRIX, and Linux) or
-       # end-of-file.  Either is fine.
-       copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
-}
-
-if ($#ARGV < 1) {
-       die "usage: test-terminal program args";
-}
-my $master = new IO::Pty;
-my $slave = $master->slave;
-my $pid = start_child(\@ARGV, $slave);
-close $slave;
-xsendfile(\*STDOUT, $master);
-exit(finish_child($pid));
index eb8ca88cce59c234d8ee43cc94421024f6b1e0b5..e058d184d1c072bd3078fe17ad41f1026f093201 100755 (executable)
@@ -5,7 +5,7 @@ test_description='git grep in binary files'
 . ./test-lib.sh
 
 test_expect_success 'setup' "
-       printf 'binary\000file\n' >a &&
+       echo 'binaryQfile' | q_to_nul >a &&
        git add a &&
        git commit -m.
 "
@@ -61,7 +61,7 @@ test_expect_success 'git grep -Fi iLE a' '
 
 # This test actually passes on platforms where regexec() supports the
 # flag REG_STARTEND.
-test_expect_failure 'git grep ile a' '
+test_expect_success 'git grep ile a' '
        git grep ile a
 '
 
@@ -70,32 +70,32 @@ test_expect_failure 'git grep .fi a' '
 '
 
 test_expect_success 'git grep -F y<NUL>f a' "
-       printf 'y\000f' >f &&
+       printf 'yQf' | q_to_nul >f &&
        git grep -f f -F a
 "
 
 test_expect_success 'git grep -F y<NUL>x a' "
-       printf 'y\000x' >f &&
+       printf 'yQx' | q_to_nul >f &&
        test_must_fail git grep -f f -F a
 "
 
 test_expect_success 'git grep -Fi Y<NUL>f a' "
-       printf 'Y\000f' >f &&
+       printf 'YQf' | q_to_nul >f &&
        git grep -f f -Fi a
 "
 
 test_expect_failure 'git grep -Fi Y<NUL>x a' "
-       printf 'Y\000x' >f &&
+       printf 'YQx' | q_to_nul >f &&
        test_must_fail git grep -f f -Fi a
 "
 
 test_expect_success 'git grep y<NUL>f a' "
-       printf 'y\000f' >f &&
+       printf 'yQf' | q_to_nul >f &&
        git grep -f f a
 "
 
 test_expect_failure 'git grep y<NUL>x a' "
-       printf 'y\000x' >f &&
+       printf 'yQx' | q_to_nul >f &&
        test_must_fail git grep -f f a
 "
 
index c1f4fc3c6573b866497352ac9ed3f9e10d650992..95fab2036109c5a4aef8ff3d8100403fe335819f 100755 (executable)
@@ -3,7 +3,7 @@
 test_description='git reset --patch'
 . ./lib-patch-mode.sh
 
-test_expect_success 'setup' '
+test_expect_success PERL 'setup' '
        mkdir dir &&
        echo parent > dir/foo &&
        echo dummy > bar &&
@@ -17,20 +17,20 @@ test_expect_success 'setup' '
 
 # 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
+test_expect_success PERL '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' '
+test_expect_success PERL '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^' '
+test_expect_success PERL 'git reset -p HEAD^' '
        (echo n; echo y) | git reset -p HEAD^ &&
        verify_state dir/foo work parent &&
        verify_saved_state bar
@@ -41,27 +41,27 @@ test_expect_success 'git reset -p HEAD^' '
 # 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
+test_expect_success PERL '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
+test_expect_success PERL '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' '
+test_expect_success PERL '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' '
+test_expect_success PERL 'none of this moved HEAD' '
        verify_saved_head
 '
 
index 3a43571cab78bce546ca714c8d53b76fc94c6ee2..02f67b73b762850f3c7f0faf0886bf4011f57308 100755 (executable)
@@ -179,11 +179,11 @@ test_expect_success 'git clean -d with prefix and path' '
 
 '
 
-test_expect_success 'git clean symbolic link' '
+test_expect_success SYMLINKS 'git clean symbolic link' '
 
        mkdir -p build docs &&
        touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
-       ln -s docs/manual.txt src/part4.c
+       ln -s docs/manual.txt src/part4.c &&
        git clean &&
        test -f Makefile &&
        test -f README &&
@@ -388,16 +388,15 @@ test_expect_success 'core.excludesfile' '
 
 '
 
-test_expect_success 'removal failure' '
+test_expect_success SANITY 'removal failure' '
 
        mkdir foo &&
        touch foo/bar &&
        (exec <foo/bar &&
         chmod 0 foo &&
-        test_must_fail git clean -f -d)
-
+        test_must_fail git clean -f -d &&
+        chmod 755 foo)
 '
-chmod 755 foo
 
 test_expect_success 'nested git work tree' '
        rm -fr foo bar &&
@@ -444,7 +443,7 @@ test_expect_success 'git clean -e' '
        (
                cd repo &&
                git init &&
-               touch 1 2 3 known &&
+               touch known 1 2 3 &&
                git add known &&
                git clean -f -e 1 -e 2 &&
                test -e 1 &&
index 9bda97058499ecab88dc6de7551d7909e1ad5a2e..874279e32da98ac2c20137a207a0d115f073e444 100755 (executable)
@@ -413,18 +413,75 @@ test_expect_success 'submodule <invalid-path> warns' '
 
 test_expect_success 'add submodules without specifying an explicit path' '
        mkdir repo &&
-       cd repo &&
-       git init &&
-       echo r >r &&
-       git add r &&
-       git commit -m "repo commit 1" &&
-       cd .. &&
+       (
+               cd repo &&
+               git init &&
+               echo r >r &&
+               git add r &&
+               git commit -m "repo commit 1"
+       ) &&
        git clone --bare repo/ bare.git &&
-       cd addtest &&
-       git submodule add "$submodurl/repo" &&
-       git config -f .gitmodules submodule.repo.path repo &&
-       git submodule add "$submodurl/bare.git" &&
-       git config -f .gitmodules submodule.bare.path bare
+       (
+               cd addtest &&
+               git submodule add "$submodurl/repo" &&
+               git config -f .gitmodules submodule.repo.path repo &&
+               git submodule add "$submodurl/bare.git" &&
+               git config -f .gitmodules submodule.bare.path bare
+       )
+'
+
+test_expect_success 'add should fail when path is used by a file' '
+       (
+               cd addtest &&
+               touch file &&
+               test_must_fail  git submodule add "$submodurl/repo" file
+       )
+'
+
+test_expect_success 'add should fail when path is used by an existing directory' '
+       (
+               cd addtest &&
+               mkdir empty-dir &&
+               test_must_fail git submodule add "$submodurl/repo" empty-dir
+       )
+'
+
+test_expect_success 'set up for relative path tests' '
+       mkdir reltest &&
+       (
+               cd reltest &&
+               git init &&
+               mkdir sub &&
+               (
+                       cd sub &&
+                       git init &&
+                       test_commit foo
+               ) &&
+               git add sub &&
+               git config -f .gitmodules submodule.sub.path sub &&
+               git config -f .gitmodules submodule.sub.url ../subrepo &&
+               cp .git/config pristine-.git-config
+       )
+'
+
+test_expect_success 'relative path works with URL' '
+       (
+               cd reltest &&
+               cp pristine-.git-config .git/config &&
+               git config remote.origin.url ssh://hostname/repo &&
+               git submodule init &&
+               test "$(git config submodule.sub.url)" = ssh://hostname/subrepo
+       )
+'
+
+test_expect_success 'relative path works with user@host:path' '
+       (
+               cd reltest &&
+               cp pristine-.git-config .git/config &&
+               git config remote.origin.url user@host:repo &&
+               git submodule init &&
+               test "$(git config submodule.sub.url)" = user@host:subrepo
+       )
 '
 
 test_done
index cee319da0ae6ef9ec3b220adbb178e116713db2a..7d7fde057b04e4615e32b0ddc71fd3b07968a129 100755 (executable)
@@ -37,11 +37,12 @@ head1=$(add_file sm1 foo1 foo2)
 test_expect_success 'added submodule' "
        git add sm1 &&
        git submodule summary >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 * sm1 0000000...$head1 (2):
   > Add foo2
 
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -49,43 +50,47 @@ head2=$(add_file sm1 foo3)
 
 test_expect_success 'modified submodule(forward)' "
        git submodule summary >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 * sm1 $head1...$head2 (1):
   > Add foo3
 
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule(forward), --files' "
        git submodule summary --files >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 * sm1 $head1...$head2 (1):
   > Add foo3
 
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
-cd sm1 &&
-git reset --hard HEAD~2 >/dev/null &&
-head3=$(git rev-parse --verify HEAD | cut -c1-7) &&
-cd ..
+head3=$(
+       cd sm1 &&
+       git reset --hard HEAD~2 >/dev/null &&
+       git rev-parse --verify HEAD | cut -c1-7
+)
 
 test_expect_success 'modified submodule(backward)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head2...$head3 (2):
   < Add foo3
   < Add foo2
 
 EOF
+       test_cmp expected actual
 "
 
 head4=$(add_file sm1 foo4 foo5) &&
 head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
 test_expect_success 'modified submodule(backward and forward)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
@@ -93,17 +98,19 @@ test_expect_success 'modified submodule(backward and forward)' "
   < Add foo2
 
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success '--summary-limit' "
     git submodule summary -n 3 >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
   < Add foo3
 
 EOF
+    test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -116,30 +123,33 @@ mv sm1-bak sm1
 
 test_expect_success 'typechanged submodule(submodule->blob), --cached' "
     git submodule summary --cached >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob) (3):
   < Add foo5
 
 EOF
+       test_cmp actual expected
 "
 
 test_expect_success 'typechanged submodule(submodule->blob), --files' "
     git submodule summary --files >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head4(submodule) (3):
   > Add foo5
 
 EOF
+    test_cmp actual expected
 "
 
 rm -rf sm1 &&
 git checkout-index sm1
 test_expect_success 'typechanged submodule(submodule->blob)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob):
 
 EOF
+    test_cmp actual expected
 "
 
 rm -f sm1 &&
@@ -147,31 +157,34 @@ test_create_repo sm1 &&
 head6=$(add_file sm1 foo6 foo7)
 test_expect_success 'nonexistent commit' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head4...$head6:
   Warn: sm1 doesn't contain commit $head4_full
 
 EOF
+    test_cmp actual expected
 "
 
 commit_file
 test_expect_success 'typechanged submodule(blob->submodule)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head6(submodule) (2):
   > Add foo7
 
 EOF
+    test_cmp expected actual
 "
 
 commit_file sm1 &&
 rm -rf sm1
 test_expect_success 'deleted submodule' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 EOF
+    test_cmp expected actual
 "
 
 test_create_repo sm2 &&
@@ -180,34 +193,37 @@ git add sm2
 
 test_expect_success 'multiple submodules' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
+    test_cmp expected actual
 "
 
 test_expect_success 'path filter' "
     git submodule summary sm2 >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
+    test_cmp expected actual
 "
 
 commit_file sm2
 test_expect_success 'given commit' "
     git submodule summary HEAD^ >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
+    test_cmp expected actual
 "
 
 test_expect_success '--for-status' "
index 7538756487d9b9483143b338104cd6af13397a91..e5b19538b0192e5ab5f9081b0c10ac0dc8497cb6 100755 (executable)
@@ -14,7 +14,7 @@ test_expect_success setup '
        echo file > file &&
        git add file &&
        test_tick &&
-       git commit -m upstream
+       git commit -m upstream &&
        git clone . super &&
        git clone super submodule &&
        (cd super &&
@@ -23,7 +23,9 @@ test_expect_success setup '
         git commit -m "submodule"
        ) &&
        git clone super super-clone &&
-       (cd super-clone && git submodule update --init)
+       (cd super-clone && git submodule update --init) &&
+       git clone super empty-clone &&
+       (cd empty-clone && git submodule init)
 '
 
 test_expect_success 'change submodule' '
@@ -42,7 +44,7 @@ test_expect_success 'change submodule url' '
        ) &&
        mv submodule moved-submodule &&
        (cd super &&
-        git config -f .gitmodules submodule.submodule.url ../moved-submodule
+        git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
         test_tick &&
         git commit -a -m moved-submodule
        )
@@ -58,6 +60,17 @@ test_expect_success '"git submodule sync" should update submodule URLs' '
        (cd super-clone/submodule &&
         git checkout master &&
         git pull
+       ) &&
+       (cd super-clone &&
+        test -d "$(git config submodule.submodule.url)"
+       )
+'
+
+test_expect_success '"git submodule sync" should update submodule URLs if not yet cloned' '
+       (cd empty-clone &&
+        git pull &&
+        git submodule sync &&
+        test -d "$(git config submodule.submodule.url)"
        )
 '
 
index 4a7b8933f4bf1036ba1e044d6e2dc54d72084039..7e2e258950772c91dfc04aff72ed74e95b5df884 100755 (executable)
@@ -54,21 +54,132 @@ test_expect_success setup '
        git merge -s ours a
 '
 
-test_expect_success 'merging with modify/modify conflict' '
+# History setup
+#
+#      b
+#    /   \
+#   a     d
+#    \   /
+#      c
+#
+# a in the main repository records to sub-a in the submodule and
+# analogous b and c. d should be automatically found by merging c into
+# b in the main repository.
+test_expect_success 'setup for merge search' '
+       mkdir merge-search &&
+       (cd merge-search &&
+       git init &&
+       mkdir sub &&
+       (cd sub &&
+        git init &&
+        echo "file-a" > file-a &&
+        git add file-a &&
+        git commit -m "sub-a" &&
+        git branch sub-a) &&
+       git add sub &&
+       git commit -m "a" &&
+       git branch a &&
+
+       git checkout -b b &&
+       (cd sub &&
+        git checkout -b sub-b &&
+        echo "file-b" > file-b &&
+        git add file-b &&
+        git commit -m "sub-b") &&
+       git commit -a -m "b" &&
+
+       git checkout -b c a &&
+       (cd sub &&
+        git checkout -b sub-c sub-a &&
+        echo "file-c" > file-c &&
+        git add file-c &&
+        git commit -m "sub-c") &&
+       git commit -a -m "c" &&
 
-       git checkout -b test1 a &&
-       test_must_fail git merge b &&
-       test -f .git/MERGE_MSG &&
-       git diff &&
-       test -n "$(git ls-files -u)"
+       git checkout -b d a &&
+       (cd sub &&
+        git checkout -b sub-d sub-b &&
+        git merge sub-c) &&
+       git commit -a -m "d" &&
+       git branch test b)
 '
 
-test_expect_success 'merging with a modify/modify conflict between merge bases' '
+test_expect_success 'merge with one side as a fast-forward of the other' '
+       (cd merge-search &&
+        git checkout -b test-forward b &&
+        git merge d &&
+        git ls-tree test-forward sub | cut -f1 | cut -f3 -d" " > actual &&
+        (cd sub &&
+         git rev-parse sub-d > ../expect) &&
+        test_cmp actual expect)
+'
 
+test_expect_success 'merging should conflict for non fast-forward' '
+       (cd merge-search &&
+        git checkout -b test-nonforward b &&
+        (cd sub &&
+         git rev-parse sub-d > ../expect) &&
+        test_must_fail git merge c 2> actual  &&
+        grep $(cat expect) actual > /dev/null &&
+        git reset --hard)
+'
+
+test_expect_success 'merging should fail for ambiguous common parent' '
+       (cd merge-search &&
+       git checkout -b test-ambiguous b &&
+       (cd sub &&
+        git checkout -b ambiguous sub-b &&
+        git merge sub-c &&
+        git rev-parse sub-d > ../expect1 &&
+        git rev-parse ambiguous > ../expect2) &&
+       test_must_fail git merge c 2> actual &&
+       grep $(cat expect1) actual > /dev/null &&
+       grep $(cat expect2) actual > /dev/null &&
+       git reset --hard)
+'
+
+# in a situation like this
+#
+# submodule tree:
+#
+#    sub-a --- sub-b --- sub-d
+#
+# main tree:
+#
+#    e (sub-a)
+#   /
+#  bb (sub-b)
+#   \
+#    f (sub-d)
+#
+# A merge between e and f should fail because one of the submodule
+# commits (sub-a) does not descend from the submodule merge-base (sub-b).
+#
+test_expect_success 'merging should fail for changes that are backwards' '
+       (cd merge-search &&
+       git checkout -b bb a &&
+       (cd sub &&
+        git checkout sub-b) &&
+       git commit -a -m "bb" &&
+
+       git checkout -b e bb &&
+       (cd sub &&
+        git checkout sub-a) &&
+       git commit -a -m "e" &&
+
+       git checkout -b f bb &&
+       (cd sub &&
+        git checkout sub-d) &&
+       git commit -a -m "f" &&
+
+       git checkout -b test-backward e &&
+       test_must_fail git merge f)
+'
+
+test_expect_success 'merging with a modify/modify conflict between merge bases' '
        git reset --hard HEAD &&
        git checkout -b test2 c &&
        git merge d
-
 '
 
 test_done
index 1382a8e58a14c8fcc92889aad492d5883fd64f2b..bfb4975e940e29edf3b06e5c074b8e963cfbbdcf 100755 (executable)
@@ -25,7 +25,7 @@ test_expect_success 'setup a submodule tree' '
        echo file > file &&
        git add file &&
        test_tick &&
-       git commit -m upstream
+       git commit -m upstream &&
        git clone . super &&
        git clone super submodule &&
        git clone super rebasing &&
index db9365b6454b18eb07f5c0798e13e18d696498c6..d8ad25036f325ecdcc71257614e19b5e6ab2cdc5 100755 (executable)
@@ -16,7 +16,7 @@ test_expect_success 'setup a submodule tree' '
        echo file > file &&
        git add file &&
        test_tick &&
-       git commit -m upstream
+       git commit -m upstream &&
        git clone . super &&
        git clone super submodule &&
        (
@@ -30,7 +30,7 @@ test_expect_success 'setup a submodule tree' '
                        submodule.sub2 submodule.foo2 &&
                git config -f .gitmodules --rename-section \
                        submodule.sub3 submodule.foo3 &&
-               git add .gitmodules
+               git add .gitmodules &&
                test_tick &&
                git commit -m "submodules" &&
                git submodule init sub1 &&
@@ -226,6 +226,21 @@ test_expect_success 'test "status --recursive"' '
        test_cmp expect actual
 '
 
+sed -e "/nested1 /s/.*/+$nested1sha1 nested1 (file2~1)/;/sub[1-3]/d" < expect > expect2
+mv -f expect2 expect
+
+test_expect_success 'ensure "status --cached --recursive" preserves the --cached flag' '
+       (
+               cd clone3 &&
+               (
+                       cd nested1 &&
+                       test_commit file2
+               ) &&
+               git submodule status --cached --recursive -- nested1 > ../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 &&
@@ -238,4 +253,39 @@ test_expect_success 'use "git clone --recursive" to checkout all submodules' '
        test -d clone4/nested1/nested2/nested3/submodule/.git
 '
 
+test_expect_success 'test "update --recursive" with a flag with spaces' '
+       git clone super "common objects" &&
+       git clone super clone5 &&
+       (
+               cd clone5 &&
+               test ! -d nested1/.git &&
+               git submodule update --init --recursive --reference="$(dirname "$PWD")/common objects" &&
+               test -d nested1/.git &&
+               test -d nested1/nested2/.git &&
+               test -d nested1/nested2/nested3/.git &&
+               test -f nested1/.git/objects/info/alternates &&
+               test -f nested1/nested2/.git/objects/info/alternates &&
+               test -f nested1/nested2/nested3/.git/objects/info/alternates
+       )
+'
+
+test_expect_success 'use "update --recursive nested1" to checkout all submodules rooted in nested1' '
+       git clone super clone6 &&
+       (
+               cd clone6 &&
+               test ! -d sub1/.git &&
+               test ! -d sub2/.git &&
+               test ! -d sub3/.git &&
+               test ! -d nested1/.git &&
+               git submodule update --init --recursive -- nested1 &&
+               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
+       )
+'
+
 test_done
index aa9c577e9e306bce05cd0fe076c56017c1b97e41..162527c2114955df2c55db9bf37db59d05fe75f8 100755 (executable)
@@ -215,4 +215,84 @@ test_expect_success 'Commit a message with --allow-empty-message' '
        commit_msg_is "hello there"
 '
 
+commit_for_rebase_autosquash_setup () {
+       echo "first content line" >>foo &&
+       git add foo &&
+       cat >log <<EOF &&
+target message subject line
+
+target message body line 1
+target message body line 2
+EOF
+       git commit -F log &&
+       echo "second content line" >>foo &&
+       git add foo &&
+       git commit -m "intermediate commit" &&
+       echo "third content line" >>foo &&
+       git add foo
+}
+
+test_expect_success 'commit --fixup provides correct one-line commit message' '
+       commit_for_rebase_autosquash_setup &&
+       git commit --fixup HEAD~1 &&
+       commit_msg_is "fixup! target message subject line"
+'
+
+test_expect_success 'commit --squash works with -F' '
+       commit_for_rebase_autosquash_setup &&
+       echo "log message from file" >msgfile &&
+       git commit --squash HEAD~1 -F msgfile  &&
+       commit_msg_is "squash! target message subject linelog message from file"
+'
+
+test_expect_success 'commit --squash works with -m' '
+       commit_for_rebase_autosquash_setup &&
+       git commit --squash HEAD~1 -m "foo bar\nbaz" &&
+       commit_msg_is "squash! target message subject linefoo bar\nbaz"
+'
+
+test_expect_success 'commit --squash works with -C' '
+       commit_for_rebase_autosquash_setup &&
+       git commit --squash HEAD~1 -C HEAD &&
+       commit_msg_is "squash! target message subject lineintermediate commit"
+'
+
+test_expect_success 'commit --squash works with -c' '
+       commit_for_rebase_autosquash_setup &&
+       test_set_editor "$TEST_DIRECTORY"/t7500/edit-content &&
+       git commit --squash HEAD~1 -c HEAD &&
+       commit_msg_is "squash! target message subject lineedited commit"
+'
+
+test_expect_success 'commit --squash works with -C for same commit' '
+       commit_for_rebase_autosquash_setup &&
+       git commit --squash HEAD -C HEAD &&
+       commit_msg_is "squash! intermediate commit"
+'
+
+test_expect_success 'commit --squash works with -c for same commit' '
+       commit_for_rebase_autosquash_setup &&
+       test_set_editor "$TEST_DIRECTORY"/t7500/edit-content &&
+       git commit --squash HEAD -c HEAD &&
+       commit_msg_is "squash! edited commit"
+'
+
+test_expect_success 'commit --squash works with editor' '
+       commit_for_rebase_autosquash_setup &&
+       test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+       git commit --squash HEAD~1 &&
+       commit_msg_is "squash! target message subject linecommit message"
+'
+
+test_expect_success 'invalid message options when using --fixup' '
+       echo changes >>foo &&
+       echo "message" >log &&
+       git add foo &&
+       test_must_fail git commit --fixup HEAD~1 --squash HEAD~2 &&
+       test_must_fail git commit --fixup HEAD~1 -C HEAD~2 &&
+       test_must_fail git commit --fixup HEAD~1 -c HEAD~2 &&
+       test_must_fail git commit --fixup HEAD~1 -m "cmdline message" &&
+       test_must_fail git commit --fixup HEAD~1 -F log
+'
+
 test_done
diff --git a/t/t7500/edit-content b/t/t7500/edit-content
new file mode 100755 (executable)
index 0000000..08db9fd
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+sed -e "s/intermediate/edited/g" <"$1" >"$1-"
+mv "$1-" "$1"
+exit 0
index 8297cb4f1e6e2d903dfbf6fde825d2c787082e58..8980738c7540b79eaa566d6054f3e76b202bd27e 100755 (executable)
@@ -230,6 +230,10 @@ test_expect_success 'amend commit to fix date' '
 
 '
 
+test_expect_success 'commit complains about bogus date' '
+       test_must_fail git commit --amend --date=10.11.2010
+'
+
 test_expect_success 'sign off (1)' '
 
        echo 1 >positive &&
index ac2e187a5720d1ff947e58073dd6cc403ef40d5d..50da034cd3934d0509e67a6f20e514a18e5659d4 100755 (executable)
@@ -252,8 +252,8 @@ test_expect_success 'committer is automatic' '
 
        echo >>negative &&
        (
-               unset GIT_COMMITTER_EMAIL
-               unset GIT_COMMITTER_NAME
+               sane_unset GIT_COMMITTER_EMAIL &&
+               sane_unset GIT_COMMITTER_NAME &&
                # must fail because there is no change
                test_must_fail git commit -e -m "sample"
        ) &&
@@ -390,7 +390,7 @@ try_commit_status_combo () {
 
        test_expect_success 'commit --no-status' '
                clear_config commit.status &&
-               try_commit --no-status
+               try_commit --no-status &&
                ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
index 9c14b853c07f26722953c8a1f778a41af8808cf1..f1dc5c3b6a36507e664e624bf9cebb1634bce335 100755 (executable)
@@ -7,6 +7,30 @@ test_description='git status'
 
 . ./test-lib.sh
 
+test_expect_success 'status -h in broken repository' '
+       mkdir broken &&
+       test_when_finished "rm -fr broken" &&
+       (
+               cd broken &&
+               git init &&
+               echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
+               test_expect_code 129 git status -h >usage 2>&1
+       ) &&
+       grep "[Uu]sage" broken/usage
+'
+
+test_expect_success 'commit -h in broken repository' '
+       mkdir broken &&
+       test_when_finished "rm -fr broken" &&
+       (
+               cd broken &&
+               git init &&
+               echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
+               test_expect_code 129 git commit -h >usage 2>&1
+       ) &&
+       grep "[Uu]sage" broken/usage
+'
+
 test_expect_success 'setup' '
        : >tracked &&
        : >modified &&
@@ -44,7 +68,7 @@ cat >expect <<\EOF
 #
 #      new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -73,7 +97,7 @@ cat >expect <<\EOF
 # Changes to be committed:
 #      new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #      modified:   dir1/modified
 #
 # Untracked files:
@@ -140,7 +164,7 @@ cat >expect <<EOF
 #
 #      new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -167,7 +191,7 @@ cat >expect <<EOF
 # Changes to be committed:
 #      new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #      modified:   dir1/modified
 #
 # Untracked files not listed
@@ -202,7 +226,7 @@ cat >expect <<EOF
 #
 #      new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -260,7 +284,7 @@ cat >expect <<EOF
 #
 #      new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -320,7 +344,7 @@ cat >expect <<\EOF
 #
 #      new file:   ../dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -381,18 +405,19 @@ test_expect_success 'status --porcelain ignores relative paths setting' '
 
 test_expect_success 'setup unique colors' '
 
-       git config status.color.untracked blue
+       git config status.color.untracked blue &&
+       git config status.color.branch green
 
 '
 
 cat >expect <<\EOF
-# On branch master
+# On branch <GREEN>master<RESET>
 # Changes to be committed:
 #   (use "git reset HEAD <file>..." to unstage)
 #
 #      <GREEN>new file:   dir2/added<RESET>
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -521,7 +546,7 @@ cat >expect <<\EOF
 #
 #      new file:   dir2/added
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -614,7 +639,7 @@ cat >expect <<EOF
 #      new file:   dir2/added
 #      new file:   sm
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -673,7 +698,7 @@ cat >expect <<EOF
 #      new file:   dir2/added
 #      new file:   sm
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -718,7 +743,7 @@ test_expect_success 'status -s submodule summary' '
 
 cat >expect <<EOF
 # On branch master
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -766,7 +791,7 @@ cat >expect <<EOF
 #      new file:   dir2/added
 #      new file:   sm
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -793,7 +818,7 @@ test_expect_success 'commit --dry-run submodule summary (--amend)' '
        test_cmp expect output
 '
 
-test_expect_success POSIXPERM 'status succeeds in a read-only repository' '
+test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' '
        (
                chmod a-w .git &&
                # make dir1/tracked stat-dirty
@@ -808,7 +833,7 @@ test_expect_success POSIXPERM 'status succeeds in a read-only repository' '
        (exit $status)
 '
 
-(cd sm && echo > bar && git add bar && git commit -q -m 'Add bar' && cd .. && git add sm)
+(cd sm && echo > bar && git add bar && git commit -q -m 'Add bar') && git add sm
 new_head=$(cd sm && git rev-parse --short=7 --verify HEAD)
 touch .gitmodules
 
@@ -819,7 +844,7 @@ cat > expect << EOF
 #
 #      modified:   sm
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -931,7 +956,7 @@ cat > expect << EOF
 #
 #      modified:   sm
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #   (commit or discard the untracked or modified content in submodules)
@@ -989,7 +1014,7 @@ cat > expect << EOF
 #
 #      modified:   sm
 #
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
@@ -1067,7 +1092,7 @@ test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary
 
 cat > expect << EOF
 # On branch master
-# Changed but not updated:
+# Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
index 643ab03f992be8339b169b3bef0922ff3cdb04b9..77b69200297e222319b5701c62312bee27b62ce9 100755 (executable)
@@ -40,7 +40,7 @@ test_expect_success '-C option copies only the message with --reset-author' '
        test_tick &&
        git commit -a -C Initial --reset-author &&
        echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
-       author_header HEAD >actual
+       author_header HEAD >actual &&
        test_cmp expect actual &&
 
        message_body Initial >expect &&
index cde8390c1b9df6e6537a3538d8da11511b6644ec..b147a1bd69e96e48d3808028a3775a609471639a 100755 (executable)
 
 test_description='git merge
 
-Testing basic merge operations/option parsing.'
+Testing basic merge operations/option parsing.
+
+! [c0] commit 0
+ ! [c1] commit 1
+  ! [c2] commit 2
+   ! [c3] commit 3
+    ! [c4] c4
+     ! [c5] c5
+      ! [c6] c6
+       * [master] Merge commit 'c1'
+--------
+       - [master] Merge commit 'c1'
+ +     * [c1] commit 1
+      +  [c6] c6
+     +   [c5] c5
+    ++   [c4] c4
+   ++++  [c3] commit 3
+  +      [c2] commit 2
++++++++* [c0] commit 0
+'
 
 . ./test-lib.sh
 
-cat >file <<EOF
-1
-2
-3
-4
-5
-6
-7
-8
-9
-EOF
-
-cat >file.1 <<EOF
-1 X
-2
-3
-4
-5
-6
-7
-8
-9
-EOF
-
-cat >file.5 <<EOF
-1
-2
-3
-4
-5 X
-6
-7
-8
-9
-EOF
-
-cat >file.9 <<EOF
-1
-2
-3
-4
-5
-6
-7
-8
-9 X
-EOF
-
-cat  >result.1 <<EOF
-1 X
-2
-3
-4
-5
-6
-7
-8
-9
-EOF
-
-cat >result.1-5 <<EOF
-1 X
-2
-3
-4
-5 X
-6
-7
-8
-9
-EOF
-
-cat >result.1-5-9 <<EOF
-1 X
-2
-3
-4
-5 X
-6
-7
-8
-9 X
-EOF
-
-create_merge_msgs() {
-       echo "Merge commit 'c2'" >msg.1-5 &&
-       echo "Merge commit 'c2'; commit 'c3'" >msg.1-5-9 &&
-       echo "Squashed commit of the following:" >squash.1 &&
-       echo >>squash.1 &&
-       git log --no-merges ^HEAD c1 >>squash.1 &&
-       echo "Squashed commit of the following:" >squash.1-5 &&
-       echo >>squash.1-5 &&
-       git log --no-merges ^HEAD c2 >>squash.1-5 &&
-       echo "Squashed commit of the following:" >squash.1-5-9 &&
-       echo >>squash.1-5-9 &&
-       git log --no-merges ^HEAD c2 c3 >>squash.1-5-9 &&
-       echo > msg.nolog &&
-       echo "* commit 'c3':" >msg.log &&
-       echo "  commit 3" >>msg.log &&
-       echo >>msg.log
-}
-
-verify_diff() {
-       if ! test_cmp "$1" "$2"
-       then
-               echo "$3"
-               false
-       fi
-}
-
-verify_merge() {
-       verify_diff "$2" "$1" "[OOPS] bad merge result" &&
-       if test $(git ls-files -u | wc -l) -gt 0
-       then
-               echo "[OOPS] unmerged files"
-               false
-       fi &&
-       if test_must_fail git diff --exit-code
-       then
-               echo "[OOPS] working tree != index"
-               false
-       fi &&
-       if test -n "$3"
-       then
-               git show -s --pretty=format:%s HEAD >msg.act &&
-               verify_diff "$3" msg.act "[OOPS] bad merge message"
-       fi
-}
-
-verify_head() {
-       if test "$1" != "$(git rev-parse HEAD)"
-       then
-               echo "[OOPS] HEAD != $1"
-               false
-       fi
-}
-
-verify_parents() {
-       i=1
-       while test $# -gt 0
-       do
-               if test "$1" != "$(git rev-parse HEAD^$i)"
+test_expect_success 'set up test data and helpers' '
+       printf "%s\n" 1 2 3 4 5 6 7 8 9 >file &&
+       printf "%s\n" "1 X" 2 3 4 5 6 7 8 9 >file.1 &&
+       printf "%s\n" 1 2 3 4 "5 X" 6 7 8 9 >file.5 &&
+       printf "%s\n" 1 2 3 4 5 6 7 8 "9 X" >file.9 &&
+       printf "%s\n" "1 X" 2 3 4 5 6 7 8 9 >result.1 &&
+       printf "%s\n" "1 X" 2 3 4 "5 X" 6 7 8 9 >result.1-5 &&
+       printf "%s\n" "1 X" 2 3 4 "5 X" 6 7 8 "9 X" >result.1-5-9 &&
+
+       create_merge_msgs() {
+               echo "Merge commit '\''c2'\''" >msg.1-5 &&
+               echo "Merge commit '\''c2'\''; commit '\''c3'\''" >msg.1-5-9 &&
+               {
+                       echo "Squashed commit of the following:" &&
+                       echo &&
+                       git log --no-merges ^HEAD c1
+               } >squash.1 &&
+               {
+                       echo "Squashed commit of the following:" &&
+                       echo &&
+                       git log --no-merges ^HEAD c2
+               } >squash.1-5 &&
+               {
+                       echo "Squashed commit of the following:" &&
+                       echo &&
+                       git log --no-merges ^HEAD c2 c3
+               } >squash.1-5-9 &&
+               echo >msg.nolog &&
+               {
+                       echo "* commit '\''c3'\'':" &&
+                       echo "  commit 3" &&
+                       echo
+               } >msg.log
+       } &&
+
+       verify_merge() {
+               test_cmp "$2" "$1" &&
+               git update-index --refresh &&
+               git diff --exit-code &&
+               if test -n "$3"
                then
-                       echo "[OOPS] HEAD^$i != $1"
-                       return 1
+                       git show -s --pretty=format:%s HEAD >msg.act &&
+                       test_cmp "$3" msg.act
                fi
-               i=$(expr $i + 1)
-               shift
-       done
-}
-
-verify_mergeheads() {
-       i=1
-       if ! test -f .git/MERGE_HEAD
-       then
-               echo "[OOPS] MERGE_HEAD is missing"
-               false
-       fi &&
-       while test $# -gt 0
-       do
-               head=$(head -n $i .git/MERGE_HEAD | sed -ne \$p)
-               if test "$1" != "$head"
-               then
-                       echo "[OOPS] MERGE_HEAD $i != $1"
+       } &&
+
+       verify_head() {
+               echo "$1" >head.expected &&
+               git rev-parse HEAD >head.actual &&
+               test_cmp head.expected head.actual
+       } &&
+
+       verify_parents() {
+               printf "%s\n" "$@" >parents.expected &&
+               >parents.actual &&
+               i=1 &&
+               while test $i -le $#
+               do
+                       git rev-parse HEAD^$i >>parents.actual &&
+                       i=$(expr $i + 1) ||
                        return 1
-               fi
-               i=$(expr $i + 1)
-               shift
-       done
-}
+               done &&
+               test_cmp parents.expected parents.actual
+       } &&
 
-verify_no_mergehead() {
-       if test -f .git/MERGE_HEAD
-       then
-               echo "[OOPS] MERGE_HEAD exists"
-               false
-       fi
-}
+       verify_mergeheads() {
+               printf "%s\n" "$@" >mergehead.expected &&
+               test_cmp mergehead.expected .git/MERGE_HEAD
+       } &&
 
+       verify_no_mergehead() {
+               ! test -e .git/MERGE_HEAD
+       }
+'
 
 test_expect_success 'setup' '
        git add file &&
@@ -219,7 +133,7 @@ test_expect_success 'setup' '
        create_merge_msgs
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'test option parsing' '
        test_must_fail git merge -$ c1 &&
@@ -230,18 +144,35 @@ test_expect_success 'test option parsing' '
        test_must_fail git merge
 '
 
+test_expect_success 'merge -h with invalid index' '
+       mkdir broken &&
+       (
+               cd broken &&
+               git init &&
+               >.git/index &&
+               test_expect_code 129 git merge -h 2>usage
+       ) &&
+       grep "[Uu]sage: git merge" broken/usage
+'
+
 test_expect_success 'reject non-strategy with a git-merge-foo name' '
        test_must_fail git merge -s index c1
 '
 
 test_expect_success 'merge c0 with c1' '
+       echo "OBJID HEAD@{0}: merge c1: Fast-forward" >reflog.expected &&
+
        git reset --hard c0 &&
        git merge c1 &&
        verify_merge file result.1 &&
-       verify_head "$c1"
+       verify_head "$c1" &&
+
+       git reflog -1 >reflog.actual &&
+       sed "s/$_x05[0-9a-f]*/OBJID/g" reflog.actual >reflog.fuzzy &&
+       test_cmp reflog.expected reflog.fuzzy
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c0 with c1 with --ff-only' '
        git reset --hard c0 &&
@@ -251,7 +182,28 @@ test_expect_success 'merge c0 with c1 with --ff-only' '
        verify_head "$c1"
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
+
+test_expect_success 'merge from unborn branch' '
+       git checkout -f master &&
+       test_might_fail git branch -D kid &&
+
+       echo "OBJID HEAD@{0}: initial pull" >reflog.expected &&
+
+       git checkout --orphan kid &&
+       test_when_finished "git checkout -f master" &&
+       git rm -fr . &&
+       test_tick &&
+       git merge --ff-only c1 &&
+       verify_merge file result.1 &&
+       verify_head "$c1" &&
+
+       git reflog -1 >reflog.actual &&
+       sed "s/$_x05[0-9a-f][0-9a-f]/OBJID/g" reflog.actual >reflog.fuzzy &&
+       test_cmp reflog.expected reflog.fuzzy
+'
+
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2' '
        git reset --hard c1 &&
@@ -261,7 +213,7 @@ test_expect_success 'merge c1 with c2' '
        verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 and c3' '
        git reset --hard c1 &&
@@ -271,7 +223,7 @@ test_expect_success 'merge c1 with c2 and c3' '
        verify_parents $c1 $c2 $c3
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'failing merges with --ff-only' '
        git reset --hard c1 &&
@@ -288,7 +240,7 @@ test_expect_success 'merge c0 with c1 (no-commit)' '
        verify_head $c1
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (no-commit)' '
        git reset --hard c1 &&
@@ -298,7 +250,7 @@ test_expect_success 'merge c1 with c2 (no-commit)' '
        verify_mergeheads $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 and c3 (no-commit)' '
        git reset --hard c1 &&
@@ -308,7 +260,7 @@ test_expect_success 'merge c1 with c2 and c3 (no-commit)' '
        verify_mergeheads $c2 $c3
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c0 with c1 (squash)' '
        git reset --hard c0 &&
@@ -316,10 +268,10 @@ test_expect_success 'merge c0 with c1 (squash)' '
        verify_merge file result.1 &&
        verify_head $c0 &&
        verify_no_mergehead &&
-       verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
+       test_cmp squash.1 .git/SQUASH_MSG
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c0 with c1 (squash, ff-only)' '
        git reset --hard c0 &&
@@ -327,10 +279,10 @@ test_expect_success 'merge c0 with c1 (squash, ff-only)' '
        verify_merge file result.1 &&
        verify_head $c0 &&
        verify_no_mergehead &&
-       verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
+       test_cmp squash.1 .git/SQUASH_MSG
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (squash)' '
        git reset --hard c1 &&
@@ -338,17 +290,17 @@ test_expect_success 'merge c1 with c2 (squash)' '
        verify_merge file result.1-5 &&
        verify_head $c1 &&
        verify_no_mergehead &&
-       verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message"
+       test_cmp squash.1-5 .git/SQUASH_MSG
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
        git reset --hard c1 &&
        test_must_fail git merge --squash --ff-only c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 and c3 (squash)' '
        git reset --hard c1 &&
@@ -356,10 +308,10 @@ test_expect_success 'merge c1 with c2 and c3 (squash)' '
        verify_merge file result.1-5-9 &&
        verify_head $c1 &&
        verify_no_mergehead &&
-       verify_diff squash.1-5-9 .git/SQUASH_MSG "[OOPS] bad squash message"
+       test_cmp squash.1-5-9 .git/SQUASH_MSG
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (no-commit in config)' '
        git reset --hard c1 &&
@@ -370,7 +322,7 @@ test_expect_success 'merge c1 with c2 (no-commit in config)' '
        verify_mergeheads $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (squash in config)' '
        git reset --hard c1 &&
@@ -379,10 +331,10 @@ test_expect_success 'merge c1 with c2 (squash in config)' '
        verify_merge file result.1-5 &&
        verify_head $c1 &&
        verify_no_mergehead &&
-       verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message"
+       test_cmp squash.1-5 .git/SQUASH_MSG
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'override config option -n with --summary' '
        git reset --hard c1 &&
@@ -412,7 +364,7 @@ test_expect_success 'override config option -n with --stat' '
        fi
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'override config option --stat' '
        git reset --hard c1 &&
@@ -428,7 +380,7 @@ test_expect_success 'override config option --stat' '
        fi
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (override --no-commit)' '
        git reset --hard c1 &&
@@ -439,7 +391,7 @@ test_expect_success 'merge c1 with c2 (override --no-commit)' '
        verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (override --squash)' '
        git reset --hard c1 &&
@@ -450,7 +402,7 @@ test_expect_success 'merge c1 with c2 (override --squash)' '
        verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c0 with c1 (no-ff)' '
        git reset --hard c0 &&
@@ -461,7 +413,7 @@ test_expect_success 'merge c0 with c1 (no-ff)' '
        verify_parents $c0 $c1
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'combining --squash and --no-ff is refused' '
        test_must_fail git merge --squash --no-ff c1 &&
@@ -485,20 +437,20 @@ test_expect_success 'merge log message' '
        git reset --hard c0 &&
        git merge --no-log c2 &&
        git show -s --pretty=format:%b HEAD >msg.act &&
-       verify_diff msg.nolog msg.act "[OOPS] bad merge log message" &&
+       test_cmp msg.nolog msg.act &&
 
        git merge --log c3 &&
        git show -s --pretty=format:%b HEAD >msg.act &&
-       verify_diff msg.log msg.act "[OOPS] bad merge log message" &&
+       test_cmp msg.log msg.act &&
 
        git reset --hard HEAD^ &&
        git config merge.log yes &&
        git merge c3 &&
        git show -s --pretty=format:%b HEAD >msg.act &&
-       verify_diff msg.log msg.act "[OOPS] bad merge log message"
+       test_cmp msg.log msg.act
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c0, c2, c0, and c1' '
        git reset --hard c1 &&
@@ -509,7 +461,7 @@ test_expect_success 'merge c1 with c0, c2, c0, and c1' '
        verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c0, c2, c0, and c1' '
        git reset --hard c1 &&
@@ -520,7 +472,7 @@ test_expect_success 'merge c1 with c0, c2, c0, and c1' '
        verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c1 and c2' '
        git reset --hard c1 &&
@@ -531,7 +483,7 @@ test_expect_success 'merge c1 with c1 and c2' '
        verify_parents $c1 $c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge fast-forward in a dirty tree' '
        git reset --hard c0 &&
@@ -541,16 +493,16 @@ test_expect_success 'merge fast-forward in a dirty tree' '
        git merge c2
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'in-index merge' '
        git reset --hard c0 &&
-       git merge --no-ff -s resolve c1 > out &&
+       git merge --no-ff -s resolve c1 >out &&
        grep "Wonderful." out &&
        verify_parents $c0 $c1
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'refresh the index before merging' '
        git reset --hard c1 &&
@@ -558,31 +510,39 @@ test_expect_success 'refresh the index before merging' '
        git merge c3
 '
 
-cat >expected <<EOF
-Merge branch 'c5' (early part)
+cat >expected.branch <<\EOF
+Merge branch 'c5-branch' (early part)
+EOF
+cat >expected.tag <<\EOF
+Merge commit 'c5~1'
 EOF
 
 test_expect_success 'merge early part of c2' '
        git reset --hard c3 &&
-       echo c4 > c4.c &&
+       echo c4 >c4.c &&
        git add c4.c &&
        git commit -m c4 &&
        git tag c4 &&
-       echo c5 > c5.c &&
+       echo c5 >c5.c &&
        git add c5.c &&
        git commit -m c5 &&
        git tag c5 &&
        git reset --hard c3 &&
-       echo c6 > c6.c &&
+       echo c6 >c6.c &&
        git add c6.c &&
        git commit -m c6 &&
        git tag c6 &&
+       git branch -f c5-branch c5 &&
+       git merge c5-branch~1 &&
+       git show -s --pretty=format:%s HEAD >actual.branch &&
+       git reset --keep HEAD^ &&
        git merge c5~1 &&
-       git show -s --pretty=format:%s HEAD > actual &&
-       test_cmp actual expected
+       git show -s --pretty=format:%s HEAD >actual.tag &&
+       test_cmp expected.branch actual.branch &&
+       test_cmp expected.tag actual.tag
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge --no-ff --no-commit && commit' '
        git reset --hard c0 &&
@@ -591,13 +551,13 @@ test_expect_success 'merge --no-ff --no-commit && commit' '
        verify_parents $c0 $c1
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'amending no-ff merge commit' '
        EDITOR=: git commit --amend &&
        verify_parents $c0 $c1
 '
 
-test_debug 'gitk --all'
+test_debug 'git log --graph --decorate --oneline --all'
 
 test_done
index 7ba94ea99bc0785da5b398e494d23469ba44992d..b44b29395049308b30e61b00a55e2eb39f4355a1 100755 (executable)
@@ -114,13 +114,13 @@ test_expect_success 'setup conflicted merge' '
 test_expect_success 'merge picks up the best result' '
        git config --unset-all pull.twohead &&
        git reset --hard c5 &&
-       git merge -s resolve c6
+       test_must_fail git merge -s resolve c6 &&
        resolve_count=$(conflict_count) &&
        git reset --hard c5 &&
-       git merge -s recursive c6
+       test_must_fail git merge -s recursive c6 &&
        recursive_count=$(conflict_count) &&
        git reset --hard c5 &&
-       git merge -s recursive -s resolve c6
+       test_must_fail git merge -s recursive -s resolve c6 &&
        auto_count=$(conflict_count) &&
        test $auto_count = $recursive_count &&
        test $auto_count != $resolve_count
@@ -129,13 +129,13 @@ test_expect_success 'merge picks up the best result' '
 test_expect_success 'merge picks up the best result (from config)' '
        git config pull.twohead "recursive resolve" &&
        git reset --hard c5 &&
-       git merge -s resolve c6
+       test_must_fail git merge -s resolve c6 &&
        resolve_count=$(conflict_count) &&
        git reset --hard c5 &&
-       git merge -s recursive c6
+       test_must_fail git merge -s recursive c6 &&
        recursive_count=$(conflict_count) &&
        git reset --hard c5 &&
-       git merge c6
+       test_must_fail git merge c6 &&
        auto_count=$(conflict_count) &&
        test $auto_count = $recursive_count &&
        test $auto_count != $resolve_count
index 2746169514f9bd1629f05eed8f9ff2fcfdba8cb5..0a46795ae785fd4dfa0e565bfa0153ca107a904d 100755 (executable)
@@ -31,7 +31,7 @@ test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
        do
                refs="$refs c$i"
                i=`expr $i + 1`
-       done
+       done &&
        git merge $refs &&
        test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
        i=1 &&
index 52a451dd5782ab584ba34e73abaef32d5e332537..8e8c4d72464098e1c75805e48bb5907e34e9532c 100755 (executable)
@@ -1,49 +1,93 @@
 #!/bin/sh
 
-test_description='git merge
+test_description="git merge
 
-Testing a custom strategy.'
+Testing a custom strategy.
+
+*   (HEAD, master) Merge commit 'c3'
+|\
+| * (tag: c3) c3
+* | (tag: c1) c1
+|/
+| * tag: c2) c2
+|/
+* (tag: c0) c0
+"
 
 . ./test-lib.sh
 
-cat >git-merge-theirs <<EOF
-#!$SHELL_PATH
-eval git read-tree --reset -u \\\$\$#
-EOF
-chmod +x git-merge-theirs
-PATH=.:$PATH
-export PATH
+test_expect_success 'set up custom strategy' '
+       cat >git-merge-theirs <<-EOF &&
+       #!$SHELL_PATH
+       eval git read-tree --reset -u \\\$\$#
+       EOF
+
+       chmod +x git-merge-theirs &&
+       PATH=.:$PATH &&
+       export PATH
+'
 
 test_expect_success 'setup' '
-       echo c0 >c0.c &&
-       git add c0.c &&
-       git commit -m c0 &&
-       git tag c0 &&
-       echo c1 >c1.c &&
-       git add c1.c &&
-       git commit -m c1 &&
-       git tag c1 &&
-       git reset --hard c0 &&
+       test_commit c0 c0.c &&
+       test_commit c1 c1.c &&
+       git reset --keep c0 &&
        echo c1c1 >c1.c &&
-       echo c2 >c2.c &&
-       git add c1.c c2.c &&
-       git commit -m c2 &&
-       git tag c2
+       git add c1.c &&
+       test_commit c2 c2.c &&
+       git reset --keep c0 &&
+       test_commit c3 c3.c
 '
 
 test_expect_success 'merge c2 with a custom strategy' '
        git reset --hard c1 &&
+
+       git rev-parse c1 >head.old &&
+       git rev-parse c2 >second-parent.expected &&
+       git rev-parse c2^{tree} >tree.expected &&
        git merge -s theirs c2 &&
-       test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
-       test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
-       test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
-       test "$(git rev-parse c2^{tree})" = "$(git rev-parse HEAD^{tree})" &&
+
+       git rev-parse HEAD >head.new &&
+       git rev-parse HEAD^1 >first-parent &&
+       git rev-parse HEAD^2 >second-parent &&
+       git rev-parse HEAD^{tree} >tree &&
+       git update-index --refresh &&
        git diff --exit-code &&
        git diff --exit-code c2 HEAD &&
        git diff --exit-code c2 &&
+
+       ! test_cmp head.old head.new &&
+       test_cmp head.old first-parent &&
+       test_cmp second-parent.expected second-parent &&
+       test_cmp tree.expected tree &&
        test -f c0.c &&
        grep c1c1 c1.c &&
        test -f c2.c
 '
 
+test_expect_success 'trivial merge with custom strategy' '
+       git reset --hard c1 &&
+
+       git rev-parse c1 >head.old &&
+       git rev-parse c3 >second-parent.expected &&
+       git rev-parse c3^{tree} >tree.expected &&
+       git merge -s theirs c3 &&
+
+       git rev-parse HEAD >head.new &&
+       git rev-parse HEAD^1 >first-parent &&
+       git rev-parse HEAD^2 >second-parent &&
+       git rev-parse HEAD^{tree} >tree &&
+       git update-index --refresh &&
+       git diff --exit-code &&
+       git diff --exit-code c3 HEAD &&
+       git diff --exit-code c3 &&
+
+       ! test_cmp head.old head.new &&
+       test_cmp head.old first-parent &&
+       test_cmp second-parent.expected second-parent &&
+       test_cmp tree.expected tree &&
+       test -f c0.c &&
+       ! test -e c1.c &&
+       test -f c3.c
+'
+
 test_done
index d82349a6a8d6c12c3ca4398a18bd6c1dd42efdc9..5f731a1177267e76f19dbe1605a4d0f2e8e749bd 100755 (executable)
@@ -7,48 +7,54 @@ Do not overwrite changes.'
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-       echo c0 > c0.c &&
-       git add c0.c &&
-       git commit -m c0 &&
-       git tag c0 &&
-       echo c1 > c1.c &&
-       git add c1.c &&
-       git commit -m c1 &&
-       git tag c1 &&
+       test_commit c0 c0.c &&
+       test_commit c1 c1.c &&
+       test_commit c1a c1.c "c1 a" &&
        git reset --hard c0 &&
-       echo c2 > c2.c &&
-       git add c2.c &&
-       git commit -m c2 &&
-       git tag c2 &&
-       git reset --hard c1 &&
-       echo "c1 a" > c1.c &&
-       git add c1.c &&
-       git commit -m "c1 a" &&
-       git tag c1a &&
+       test_commit c2 c2.c &&
+       git reset --hard c0 &&
+       mkdir sub &&
+       echo "sub/f" > sub/f &&
+       mkdir sub2 &&
+       echo "sub2/f" > sub2/f &&
+       git add sub/f sub2/f &&
+       git commit -m sub &&
+       git tag sub &&
        echo "VERY IMPORTANT CHANGES" > important
 '
 
 test_expect_success 'will not overwrite untracked file' '
        git reset --hard c1 &&
-       cat important > c2.c &&
+       cp important c2.c &&
        test_must_fail git merge c2 &&
+       test_path_is_missing .git/MERGE_HEAD &&
        test_cmp important c2.c
 '
 
+test_expect_success 'will overwrite tracked file' '
+       git reset --hard c1 &&
+       cp important c2.c &&
+       git add c2.c &&
+       git commit -m important &&
+       git checkout c2
+'
+
 test_expect_success 'will not overwrite new file' '
        git reset --hard c1 &&
-       cat important > c2.c &&
+       cp important c2.c &&
        git add c2.c &&
        test_must_fail git merge c2 &&
+       test_path_is_missing .git/MERGE_HEAD &&
        test_cmp important c2.c
 '
 
 test_expect_success 'will not overwrite staged changes' '
        git reset --hard c1 &&
-       cat important > c2.c &&
+       cp important c2.c &&
        git add c2.c &&
        rm c2.c &&
        test_must_fail git merge c2 &&
+       test_path_is_missing .git/MERGE_HEAD &&
        git checkout c2.c &&
        test_cmp important c2.c
 '
@@ -57,7 +63,7 @@ test_expect_success 'will not overwrite removed file' '
        git reset --hard c1 &&
        git rm c1.c &&
        git commit -m "rm c1.c" &&
-       cat important > c1.c &&
+       cp important c1.c &&
        test_must_fail git merge c1a &&
        test_cmp important c1.c
 '
@@ -66,9 +72,10 @@ test_expect_success 'will not overwrite re-added file' '
        git reset --hard c1 &&
        git rm c1.c &&
        git commit -m "rm c1.c" &&
-       cat important > c1.c &&
+       cp important c1.c &&
        git add c1.c &&
        test_must_fail git merge c1a &&
+       test_path_is_missing .git/MERGE_HEAD &&
        test_cmp important c1.c
 '
 
@@ -76,12 +83,93 @@ test_expect_success 'will not overwrite removed file with staged changes' '
        git reset --hard c1 &&
        git rm c1.c &&
        git commit -m "rm c1.c" &&
-       cat important > c1.c &&
+       cp important c1.c &&
        git add c1.c &&
        rm c1.c &&
        test_must_fail git merge c1a &&
+       test_path_is_missing .git/MERGE_HEAD &&
        git checkout c1.c &&
        test_cmp important c1.c
 '
 
+test_expect_success 'will not overwrite untracked subtree' '
+       git reset --hard c0 &&
+       rm -rf sub &&
+       mkdir -p sub/f &&
+       cp important sub/f/important &&
+       test_must_fail git merge sub &&
+       test_path_is_missing .git/MERGE_HEAD &&
+       test_cmp important sub/f/important
+'
+
+cat >expect <<\EOF
+error: The following untracked working tree files would be overwritten by merge:
+       sub
+       sub2
+Please move or remove them before you can merge.
+EOF
+
+test_expect_success 'will not overwrite untracked file in leading path' '
+       git reset --hard c0 &&
+       rm -rf sub &&
+       cp important sub &&
+       cp important sub2 &&
+       test_must_fail git merge sub 2>out &&
+       test_cmp out expect &&
+       test_path_is_missing .git/MERGE_HEAD &&
+       test_cmp important sub &&
+       test_cmp important sub2 &&
+       rm -f sub sub2
+'
+
+test_expect_failure SYMLINKS 'will not overwrite untracked symlink in leading path' '
+       git reset --hard c0 &&
+       rm -rf sub &&
+       mkdir sub2 &&
+       ln -s sub2 sub &&
+       test_must_fail git merge sub &&
+       test_path_is_missing .git/MERGE_HEAD
+'
+
+test_expect_success SYMLINKS 'will not be confused by symlink in leading path' '
+       git reset --hard c0 &&
+       rm -rf sub &&
+       ln -s sub2 sub &&
+       git add sub &&
+       git commit -m ln &&
+       git checkout sub
+'
+
+cat >expect <<\EOF
+error: Untracked working tree file 'c0.c' would be overwritten by merge.
+fatal: read-tree failed
+EOF
+
+test_expect_success 'will not overwrite untracked file on unborn branch' '
+       git reset --hard c0 &&
+       git rm -fr . &&
+       git checkout --orphan new &&
+       cp important c0.c &&
+       test_must_fail git merge c0 2>out &&
+       test_cmp out expect &&
+       test_path_is_missing .git/MERGE_HEAD &&
+       test_cmp important c0.c
+'
+
+test_expect_success 'set up unborn branch and content' '
+       git symbolic-ref HEAD refs/heads/unborn &&
+       rm -f .git/index &&
+       echo foo > tracked-file &&
+       git add tracked-file &&
+       echo bar > untracked-file
+'
+
+test_expect_failure 'will not clobber WT/index when merging into unborn' '
+       git merge master &&
+       grep foo tracked-file &&
+       git show :tracked-file >expect &&
+       grep foo expect &&
+       grep bar untracked-file
+'
+
 test_done
index 28d56797b17d5b1426d6c7c36e37b956731f1dfd..9225fa6f025cd9586b46f30464a69ccb57ca6b2a 100755 (executable)
@@ -47,14 +47,14 @@ test_expect_success 'ambiguous tag' '
        check_oneline "Merge commit QambiguousQ"
 '
 
-test_expect_success 'remote branch' '
+test_expect_success 'remote-tracking 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"
+       check_oneline "Merge remote-tracking branch Qorigin/masterQ"
 '
 
 test_done
diff --git a/t/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh
new file mode 100755 (executable)
index 0000000..c994836
--- /dev/null
@@ -0,0 +1,133 @@
+#!/bin/sh
+
+test_description='unpack-trees error messages'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup' '
+       echo one >one &&
+       git add one &&
+       git commit -a -m First &&
+
+       git checkout -b branch &&
+       echo two >two &&
+       echo three >three &&
+       echo four >four &&
+       echo five >five &&
+       git add two three four five &&
+       git commit -m Second &&
+
+       git checkout master &&
+       echo other >two &&
+       echo other >three &&
+       echo other >four &&
+       echo other >five
+'
+
+cat >expect <<\EOF
+error: The following untracked working tree files would be overwritten by merge:
+       five
+       four
+       three
+       two
+Please move or remove them before you can merge.
+EOF
+
+test_expect_success 'untracked files overwritten by merge (fast and non-fast forward)' '
+       test_must_fail git merge branch 2>out &&
+       test_cmp out expect &&
+       git commit --allow-empty -m empty &&
+       (
+               GIT_MERGE_VERBOSITY=0 &&
+               export GIT_MERGE_VERBOSITY &&
+               test_must_fail git merge branch 2>out2
+       ) &&
+       test_cmp out2 expect &&
+       git reset --hard HEAD^
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by merge:
+       four
+       three
+       two
+Please, commit your changes or stash them before you can merge.
+error: The following untracked working tree files would be overwritten by merge:
+       five
+Please move or remove them before you can merge.
+EOF
+
+test_expect_success 'untracked files or local changes ovewritten by merge' '
+       git add two &&
+       git add three &&
+       git add four &&
+       test_must_fail git merge branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by checkout:
+       rep/one
+       rep/two
+Please, commit your changes or stash them before you can switch branches.
+EOF
+
+test_expect_success 'cannot switch branches because of local changes' '
+       git add five &&
+       mkdir rep &&
+       echo one >rep/one &&
+       echo two >rep/two &&
+       git add rep/one rep/two &&
+       git commit -m Fourth &&
+       git checkout master &&
+       echo uno >rep/one &&
+       echo dos >rep/two &&
+       test_must_fail git checkout branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by checkout:
+       rep/one
+       rep/two
+Please, commit your changes or stash them before you can switch branches.
+EOF
+
+test_expect_success 'not uptodate file porcelain checkout error' '
+       git add rep/one rep/two &&
+       test_must_fail git checkout branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Updating the following directories would lose untracked files in it:
+       rep
+       rep2
+
+EOF
+
+test_expect_success 'not_uptodate_dir porcelain checkout error' '
+       git init uptodate &&
+       cd uptodate &&
+       mkdir rep &&
+       mkdir rep2 &&
+       touch rep/foo &&
+       touch rep2/foo &&
+       git add rep/foo rep2/foo &&
+       git commit -m init &&
+       git checkout -b branch &&
+       git rm rep -r &&
+       git rm rep2 -r &&
+       >rep &&
+       >rep2 &&
+       git add rep rep2&&
+       git commit -m "added test as a file" &&
+       git checkout master &&
+       >rep/untracked-file &&
+       >rep2/untracked-file &&
+       test_must_fail git checkout branch 2>out &&
+       test_cmp out ../expect
+'
+
+test_done
index f5a7bf47e9c563139676dd0a4ce1ab7098d89011..d78bdec330cd8b9505aa92ff2e81e0f716b9a741 100755 (executable)
@@ -54,7 +54,7 @@ test_expect_success 'custom mergetool' '
 
 test_expect_success 'mergetool crlf' '
     git config core.autocrlf true &&
-    git checkout -b test2 branch1
+    git checkout -b test2 branch1 &&
     test_must_fail git merge master >/dev/null 2>&1 &&
     ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
@@ -68,22 +68,24 @@ test_expect_success 'mergetool crlf' '
 '
 
 test_expect_success 'mergetool in subdir' '
-    git checkout -b test3 branch1
-    cd subdir && (
-    test_must_fail git merge master >/dev/null 2>&1 &&
-    ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
-    test "$(cat file3)" = "master new sub") &&
-    cd ..
+    git checkout -b test3 branch1 &&
+    (
+       cd subdir &&
+       test_must_fail git merge master >/dev/null 2>&1 &&
+       ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
+       test "$(cat file3)" = "master new sub"
+    )
 '
 
 test_expect_success 'mergetool on file in parent dir' '
-    cd subdir && (
-    ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
-    ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
-    test "$(cat ../file1)" = "master updated" &&
-    test "$(cat ../file2)" = "master new" &&
-    git commit -m "branch1 resolved with mergetool - subdir") &&
-    cd ..
+    (
+       cd subdir &&
+       ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+       ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
+       test "$(cat ../file1)" = "master updated" &&
+       test "$(cat ../file2)" = "master new" &&
+       git commit -m "branch1 resolved with mergetool - subdir"
+    )
 '
 
 test_expect_success 'mergetool skips autoresolved' '
@@ -96,16 +98,17 @@ test_expect_success 'mergetool skips autoresolved' '
 '
 
 test_expect_success 'mergetool merges all from subdir' '
-    cd subdir && (
-    git config rerere.enabled false &&
-    test_must_fail git merge master &&
-    git mergetool --no-prompt &&
-    test "$(cat ../file1)" = "master updated" &&
-    test "$(cat ../file2)" = "master new" &&
-    test "$(cat file3)" = "master new sub" &&
-    git add ../file1 ../file2 file3 &&
-    git commit -m "branch2 resolved by mergetool from subdir") &&
-    cd ..
+    (
+       cd subdir &&
+       git config rerere.enabled false &&
+       test_must_fail git merge master &&
+       git mergetool --no-prompt &&
+       test "$(cat ../file1)" = "master updated" &&
+       test "$(cat ../file2)" = "master new" &&
+       test "$(cat file3)" = "master new sub" &&
+       git add ../file1 ../file2 file3 &&
+       git commit -m "branch2 resolved by mergetool from subdir"
+    )
 '
 
 test_done
diff --git a/t/t7611-merge-abort.sh b/t/t7611-merge-abort.sh
new file mode 100755 (executable)
index 0000000..61890bc
--- /dev/null
@@ -0,0 +1,313 @@
+#!/bin/sh
+
+test_description='test aborting in-progress merges
+
+Set up repo with conflicting and non-conflicting branches:
+
+There are three files foo/bar/baz, and the following graph illustrates the
+content of these files in each commit:
+
+# foo/bar/baz --- foo/bar/bazz     <-- master
+#             \
+#              --- foo/barf/bazf   <-- conflict_branch
+#               \
+#                --- foo/bart/baz  <-- clean_branch
+
+Next, test git merge --abort with the following variables:
+- before/after successful merge (should fail when not in merge context)
+- with/without conflicts
+- clean/dirty index before merge
+- clean/dirty worktree before merge
+- dirty index before merge matches contents on remote branch
+- changed/unchanged worktree after merge
+- changed/unchanged index after merge
+'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       # Create the above repo
+       echo foo > foo &&
+       echo bar > bar &&
+       echo baz > baz &&
+       git add foo bar baz &&
+       git commit -m initial &&
+       echo bazz > baz &&
+       git commit -a -m "second" &&
+       git checkout -b conflict_branch HEAD^ &&
+       echo barf > bar &&
+       echo bazf > baz &&
+       git commit -a -m "conflict" &&
+       git checkout -b clean_branch HEAD^ &&
+       echo bart > bar &&
+       git commit -a -m "clean" &&
+       git checkout master
+'
+
+pre_merge_head="$(git rev-parse HEAD)"
+
+test_expect_success 'fails without MERGE_HEAD (unstarted merge)' '
+       test_must_fail git merge --abort 2>output &&
+       grep -q MERGE_HEAD output &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)"
+'
+
+test_expect_success 'fails without MERGE_HEAD (completed merge)' '
+       git merge clean_branch &&
+       test ! -f .git/MERGE_HEAD &&
+       # Merge successfully completed
+       post_merge_head="$(git rev-parse HEAD)" &&
+       test_must_fail git merge --abort 2>output &&
+       grep -q MERGE_HEAD output &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$post_merge_head" = "$(git rev-parse HEAD)"
+'
+
+test_expect_success 'Forget previous merge' '
+       git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Abort after --no-commit' '
+       # Redo merge, but stop before creating merge commit
+       git merge --no-commit clean_branch &&
+       test -f .git/MERGE_HEAD &&
+       # Abort non-conflicting merge
+       git merge --abort &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       test -z "$(git diff)" &&
+       test -z "$(git diff --staged)"
+'
+
+test_expect_success 'Abort after conflicts' '
+       # Create conflicting merge
+       test_must_fail git merge conflict_branch &&
+       test -f .git/MERGE_HEAD &&
+       # Abort conflicting merge
+       git merge --abort &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       test -z "$(git diff)" &&
+       test -z "$(git diff --staged)"
+'
+
+test_expect_success 'Clean merge with dirty index fails' '
+       echo xyzzy >> foo &&
+       git add foo &&
+       git diff --staged > expect &&
+       test_must_fail git merge clean_branch &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       test -z "$(git diff)" &&
+       git diff --staged > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Conflicting merge with dirty index fails' '
+       test_must_fail git merge conflict_branch &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       test -z "$(git diff)" &&
+       git diff --staged > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Reset index (but preserve worktree changes)' '
+       git reset "$pre_merge_head" &&
+       git diff > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Abort clean merge with non-conflicting dirty worktree' '
+       git merge --no-commit clean_branch &&
+       test -f .git/MERGE_HEAD &&
+       # Abort merge
+       git merge --abort &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       test -z "$(git diff --staged)" &&
+       git diff > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Abort conflicting merge with non-conflicting dirty worktree' '
+       test_must_fail git merge conflict_branch &&
+       test -f .git/MERGE_HEAD &&
+       # Abort merge
+       git merge --abort &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       test -z "$(git diff --staged)" &&
+       git diff > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Reset worktree changes' '
+       git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Fail clean merge with conflicting dirty worktree' '
+       echo xyzzy >> bar &&
+       git diff > expect &&
+       test_must_fail git merge --no-commit clean_branch &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       test -z "$(git diff --staged)" &&
+       git diff > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Fail conflicting merge with conflicting dirty worktree' '
+       test_must_fail git merge conflict_branch &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       test -z "$(git diff --staged)" &&
+       git diff > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Reset worktree changes' '
+       git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Fail clean merge with matching dirty worktree' '
+       echo bart > bar &&
+       git diff > expect &&
+       test_must_fail git merge --no-commit clean_branch &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       test -z "$(git diff --staged)" &&
+       git diff > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Abort clean merge with matching dirty index' '
+       git add bar &&
+       git diff --staged > expect &&
+       git merge --no-commit clean_branch &&
+       test -f .git/MERGE_HEAD &&
+       ### When aborting the merge, git will discard all staged changes,
+       ### including those that were staged pre-merge. In other words,
+       ### --abort will LOSE any staged changes (the staged changes that
+       ### are lost must match the merge result, or the merge would not
+       ### have been allowed to start). Change expectations accordingly:
+       rm expect &&
+       touch expect &&
+       # Abort merge
+       git merge --abort &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       git diff --staged > actual &&
+       test_cmp expect actual &&
+       test -z "$(git diff)"
+'
+
+test_expect_success 'Reset worktree changes' '
+       git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Fail conflicting merge with matching dirty worktree' '
+       echo barf > bar &&
+       git diff > expect &&
+       test_must_fail git merge conflict_branch &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       test -z "$(git diff --staged)" &&
+       git diff > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Abort conflicting merge with matching dirty index' '
+       git add bar &&
+       git diff --staged > expect &&
+       test_must_fail git merge conflict_branch &&
+       test -f .git/MERGE_HEAD &&
+       ### When aborting the merge, git will discard all staged changes,
+       ### including those that were staged pre-merge. In other words,
+       ### --abort will LOSE any staged changes (the staged changes that
+       ### are lost must match the merge result, or the merge would not
+       ### have been allowed to start). Change expectations accordingly:
+       rm expect &&
+       touch expect &&
+       # Abort merge
+       git merge --abort &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       git diff --staged > actual &&
+       test_cmp expect actual &&
+       test -z "$(git diff)"
+'
+
+test_expect_success 'Reset worktree changes' '
+       git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Abort merge with pre- and post-merge worktree changes' '
+       # Pre-merge worktree changes
+       echo xyzzy > foo &&
+       echo barf > bar &&
+       git add bar &&
+       git diff > expect &&
+       git diff --staged > expect-staged &&
+       # Perform merge
+       test_must_fail git merge conflict_branch &&
+       test -f .git/MERGE_HEAD &&
+       # Post-merge worktree changes
+       echo yzxxz > foo &&
+       echo blech > baz &&
+       ### When aborting the merge, git will discard staged changes (bar)
+       ### and unmerged changes (baz). Other changes that are neither
+       ### staged nor marked as unmerged (foo), will be preserved. For
+       ### these changed, git cannot tell pre-merge changes apart from
+       ### post-merge changes, so the post-merge changes will be
+       ### preserved. Change expectations accordingly:
+       git diff -- foo > expect &&
+       rm expect-staged &&
+       touch expect-staged &&
+       # Abort merge
+       git merge --abort &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       git diff > actual &&
+       test_cmp expect actual &&
+       git diff --staged > actual-staged &&
+       test_cmp expect-staged actual-staged
+'
+
+test_expect_success 'Reset worktree changes' '
+       git reset --hard "$pre_merge_head"
+'
+
+test_expect_success 'Abort merge with pre- and post-merge index changes' '
+       # Pre-merge worktree changes
+       echo xyzzy > foo &&
+       echo barf > bar &&
+       git add bar &&
+       git diff > expect &&
+       git diff --staged > expect-staged &&
+       # Perform merge
+       test_must_fail git merge conflict_branch &&
+       test -f .git/MERGE_HEAD &&
+       # Post-merge worktree changes
+       echo yzxxz > foo &&
+       echo blech > baz &&
+       git add foo bar &&
+       ### When aborting the merge, git will discard all staged changes
+       ### (foo, bar and baz), and no changes will be preserved. Whether
+       ### the changes were staged pre- or post-merge does not matter
+       ### (except for not preventing starting the merge).
+       ### Change expectations accordingly:
+       rm expect expect-staged &&
+       touch expect &&
+       touch expect-staged &&
+       # Abort merge
+       git merge --abort &&
+       test ! -f .git/MERGE_HEAD &&
+       test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
+       git diff > actual &&
+       test_cmp expect actual &&
+       git diff --staged > actual-staged &&
+       test_cmp expect-staged actual-staged
+'
+
+test_done
index c2f66ff1703c35d5f5b92285e31e8df157e51642..d954b846a17d6374dd8c7dae7f901170ff3c8c24 100755 (executable)
@@ -56,7 +56,7 @@ test_expect_success 'loose objects in alternate ODB are not repacked' '
 '
 
 test_expect_success 'packed obs in alt ODB are repacked even when local repo is packless' '
-       mkdir alt_objects/pack
+       mkdir alt_objects/pack &&
        mv .git/objects/pack/* alt_objects/pack &&
        git repack -a &&
        myidx=$(ls -1 .git/objects/pack/*.idx) &&
@@ -95,14 +95,14 @@ test_expect_success 'packed obs in alternate ODB kept pack are repacked' '
        # swap the .keep so the commit object is in the pack with .keep
        for p in alt_objects/pack/*.pack
        do
-               base_name=$(basename $p .pack)
+               base_name=$(basename $p .pack) &&
                if test -f alt_objects/pack/$base_name.keep
                then
                        rm alt_objects/pack/$base_name.keep
                else
                        touch alt_objects/pack/$base_name.keep
                fi
-       done
+       done &&
        git repack -a -d &&
        myidx=$(ls -1 .git/objects/pack/*.idx) &&
        test -f "$myidx" &&
index 196827e7eacf0584379630f528fb7a4400f2b865..4048d106d4936ae1eef2ca3788a147e659bda65d 100755 (executable)
@@ -10,11 +10,6 @@ Testing basic diff tool invocation
 
 . ./test-lib.sh
 
-if ! test_have_prereq PERL; then
-       skip_all='skipping difftool tests, perl not available'
-       test_done
-fi
-
 LF='
 '
 
@@ -50,7 +45,7 @@ prompt_given()
 }
 
 # Create a file on master and change it on branch
-test_expect_success 'setup' '
+test_expect_success PERL 'setup' '
        echo master >file &&
        git add file &&
        git commit -m "added file" &&
@@ -62,7 +57,7 @@ test_expect_success 'setup' '
 '
 
 # Configure a custom difftool.<tool>.cmd and use it
-test_expect_success 'custom commands' '
+test_expect_success PERL 'custom commands' '
        restore_test_defaults &&
        git config difftool.test-tool.cmd "cat \$REMOTE" &&
 
@@ -75,13 +70,13 @@ test_expect_success 'custom commands' '
 '
 
 # Ensures that git-difftool ignores bogus --tool values
-test_expect_success 'difftool ignores bad --tool values' '
+test_expect_success PERL 'difftool ignores bad --tool values' '
        diff=$(git difftool --no-prompt --tool=bad-tool branch)
        test "$?" = 1 &&
        test "$diff" = ""
 '
 
-test_expect_success 'difftool honors --gui' '
+test_expect_success PERL 'difftool honors --gui' '
        git config merge.tool bogus-tool &&
        git config diff.tool bogus-tool &&
        git config diff.guitool test-tool &&
@@ -92,7 +87,7 @@ test_expect_success 'difftool honors --gui' '
        restore_test_defaults
 '
 
-test_expect_success 'difftool --gui works without configured diff.guitool' '
+test_expect_success PERL 'difftool --gui works without configured diff.guitool' '
        git config diff.tool test-tool &&
 
        diff=$(git difftool --no-prompt --gui branch) &&
@@ -102,8 +97,8 @@ test_expect_success 'difftool --gui works without configured diff.guitool' '
 '
 
 # Specify the diff tool using $GIT_DIFF_TOOL
-test_expect_success 'GIT_DIFF_TOOL variable' '
-       git config --unset diff.tool
+test_expect_success PERL 'GIT_DIFF_TOOL variable' '
+       test_might_fail git config --unset diff.tool &&
        GIT_DIFF_TOOL=test-tool &&
        export GIT_DIFF_TOOL &&
 
@@ -115,7 +110,7 @@ test_expect_success 'GIT_DIFF_TOOL variable' '
 
 # Test the $GIT_*_TOOL variables and ensure
 # that $GIT_DIFF_TOOL always wins unless --tool is specified
-test_expect_success 'GIT_DIFF_TOOL overrides' '
+test_expect_success PERL 'GIT_DIFF_TOOL overrides' '
        git config diff.tool bogus-tool &&
        git config merge.tool bogus-tool &&
 
@@ -136,7 +131,7 @@ test_expect_success 'GIT_DIFF_TOOL overrides' '
 
 # Test that we don't have to pass --no-prompt to difftool
 # when $GIT_DIFFTOOL_NO_PROMPT is true
-test_expect_success 'GIT_DIFFTOOL_NO_PROMPT variable' '
+test_expect_success PERL 'GIT_DIFFTOOL_NO_PROMPT variable' '
        GIT_DIFFTOOL_NO_PROMPT=true &&
        export GIT_DIFFTOOL_NO_PROMPT &&
 
@@ -148,7 +143,7 @@ test_expect_success 'GIT_DIFFTOOL_NO_PROMPT variable' '
 
 # git-difftool supports the difftool.prompt variable.
 # Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false
-test_expect_success 'GIT_DIFFTOOL_PROMPT variable' '
+test_expect_success PERL 'GIT_DIFFTOOL_PROMPT variable' '
        git config difftool.prompt false &&
        GIT_DIFFTOOL_PROMPT=true &&
        export GIT_DIFFTOOL_PROMPT &&
@@ -160,7 +155,7 @@ test_expect_success 'GIT_DIFFTOOL_PROMPT variable' '
 '
 
 # Test that we don't have to pass --no-prompt when difftool.prompt is false
-test_expect_success 'difftool.prompt config variable is false' '
+test_expect_success PERL 'difftool.prompt config variable is false' '
        git config difftool.prompt false &&
 
        diff=$(git difftool branch) &&
@@ -170,8 +165,8 @@ test_expect_success 'difftool.prompt config variable is false' '
 '
 
 # Test that we don't have to pass --no-prompt when mergetool.prompt is false
-test_expect_success 'difftool merge.prompt = false' '
-       git config --unset difftool.prompt
+test_expect_success PERL 'difftool merge.prompt = false' '
+       test_might_fail git config --unset difftool.prompt &&
        git config mergetool.prompt false &&
 
        diff=$(git difftool branch) &&
@@ -181,7 +176,7 @@ test_expect_success 'difftool merge.prompt = false' '
 '
 
 # Test that the -y flag can override difftool.prompt = true
-test_expect_success 'difftool.prompt can overridden with -y' '
+test_expect_success PERL 'difftool.prompt can overridden with -y' '
        git config difftool.prompt true &&
 
        diff=$(git difftool -y branch) &&
@@ -191,7 +186,7 @@ test_expect_success 'difftool.prompt can overridden with -y' '
 '
 
 # Test that the --prompt flag can override difftool.prompt = false
-test_expect_success 'difftool.prompt can overridden with --prompt' '
+test_expect_success PERL 'difftool.prompt can overridden with --prompt' '
        git config difftool.prompt false &&
 
        prompt=$(echo | git difftool --prompt branch | tail -1) &&
@@ -201,7 +196,7 @@ test_expect_success 'difftool.prompt can overridden with --prompt' '
 '
 
 # Test that the last flag passed on the command-line wins
-test_expect_success 'difftool last flag wins' '
+test_expect_success PERL 'difftool last flag wins' '
        diff=$(git difftool --prompt --no-prompt branch) &&
        test "$diff" = "branch" &&
 
@@ -215,8 +210,8 @@ test_expect_success 'difftool last flag wins' '
 
 # git-difftool falls back to git-mergetool config variables
 # so test that behavior here
-test_expect_success 'difftool + mergetool config variables' '
-       remove_config_vars
+test_expect_success PERL 'difftool + mergetool config variables' '
+       remove_config_vars &&
        git config merge.tool test-tool &&
        git config mergetool.test-tool.cmd "cat \$LOCAL" &&
 
@@ -233,7 +228,7 @@ test_expect_success 'difftool + mergetool config variables' '
        restore_test_defaults
 '
 
-test_expect_success 'difftool.<tool>.path' '
+test_expect_success PERL 'difftool.<tool>.path' '
        git config difftool.tkdiff.path echo &&
        diff=$(git difftool --tool=tkdiff --no-prompt branch) &&
        git config --unset difftool.tkdiff.path &&
@@ -243,33 +238,33 @@ test_expect_success 'difftool.<tool>.path' '
        restore_test_defaults
 '
 
-test_expect_success 'difftool --extcmd=cat' '
+test_expect_success PERL 'difftool --extcmd=cat' '
        diff=$(git difftool --no-prompt --extcmd=cat branch) &&
        test "$diff" = branch"$LF"master
 '
 
-test_expect_success 'difftool --extcmd cat' '
+test_expect_success PERL 'difftool --extcmd cat' '
        diff=$(git difftool --no-prompt --extcmd cat branch) &&
        test "$diff" = branch"$LF"master
 '
 
-test_expect_success 'difftool -x cat' '
+test_expect_success PERL 'difftool -x cat' '
        diff=$(git difftool --no-prompt -x cat branch) &&
        test "$diff" = branch"$LF"master
 '
 
-test_expect_success 'difftool --extcmd echo arg1' '
-       diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"echo\ \$1\" branch)
+test_expect_success PERL 'difftool --extcmd echo arg1' '
+       diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"echo\ \$1\" branch) &&
        test "$diff" = file
 '
 
-test_expect_success 'difftool --extcmd cat arg1' '
-       diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$1\" branch)
+test_expect_success PERL 'difftool --extcmd cat arg1' '
+       diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$1\" branch) &&
        test "$diff" = master
 '
 
-test_expect_success 'difftool --extcmd cat arg2' '
-       diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$2\" branch)
+test_expect_success PERL 'difftool --extcmd cat arg2' '
+       diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$2\" branch) &&
        test "$diff" = branch
 '
 
index 023f225a4b1f22c66fb291a76690564696d56035..8a7788dc39f236b15e60912c384d835fd1db5a28 100755 (executable)
@@ -182,6 +182,24 @@ do
                test_cmp expected actual
        '
 
+       test_expect_success "grep --max-depth 0 -- . t $L" '
+               {
+                       echo ${HC}t/v:1:vvv
+                       echo ${HC}v:1:vvv
+               } >expected &&
+               git grep --max-depth 0 -n -e vvv $H -- . t >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep --max-depth 0 -- t . $L" '
+               {
+                       echo ${HC}t/v:1:vvv
+                       echo ${HC}v:1:vvv
+               } >expected &&
+               git grep --max-depth 0 -n -e vvv $H -- t . >actual &&
+               test_cmp expected actual
+       '
+
 done
 
 cat >expected <<EOF
@@ -324,8 +342,13 @@ test_expect_success 'log grep setup' '
 
        echo a >>file &&
        test_tick &&
-       git commit -a -m "third"
+       git commit -a -m "third" &&
 
+       echo a >>file &&
+       test_tick &&
+       GIT_AUTHOR_NAME="Night Fall" \
+       GIT_AUTHOR_EMAIL="nitfol@frobozz.com" \
+       git commit -a -m "fourth"
 '
 
 test_expect_success 'log grep (1)' '
@@ -372,6 +395,28 @@ test_expect_success 'log --grep --author implicitly uses all-match' '
        test_cmp expect actual
 '
 
+test_expect_success 'log with multiple --author uses union' '
+       git log --author="Thor" --author="Aster" --format=%s >actual &&
+       {
+           echo third && echo second && echo initial
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log with --grep and multiple --author uses all-match' '
+       git log --author="Thor" --author="Night" --grep=i --format=%s >actual &&
+       {
+           echo third && echo initial
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log with --grep and multiple --author uses all-match' '
+       git log --author="Thor" --author="Night" --grep=q --format=%s >actual &&
+       >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'grep with CE_VALID file' '
        git update-index --assume-unchanged t/t &&
        rm t/t &&
@@ -452,7 +497,7 @@ test_expect_success 'outside of git repository' '
                echo file1:hello &&
                echo sub/file2:world
        } >non/expect.full &&
-       echo file2:world >non/expect.sub
+       echo file2:world >non/expect.sub &&
        (
                GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
                export GIT_CEILING_DIRECTORIES &&
@@ -478,7 +523,7 @@ test_expect_success 'inside git repository but with --no-index' '
                echo sub/file2:world
        } >is/expect.full &&
        : >is/expect.empty &&
-       echo file2:world >is/expect.sub
+       echo file2:world >is/expect.sub &&
        (
                cd is/git &&
                git init &&
index 597cf0486fbe1034594d3eec821f5278d9648d43..d3a51e12698bed50d4eef1474e75dbfd0be0d034 100755 (executable)
@@ -6,4 +6,9 @@ test_description='git blame'
 PROG='git blame -c'
 . "$TEST_DIRECTORY"/annotate-tests.sh
 
+PROG='git blame -c -e'
+test_expect_success 'Blame --show-email works' '
+    check_count "<A@test.git>" 1 "<B@test.git>" 1 "<B1@test.git>" 1 "<B2@test.git>" 1 "<author@example.com>" 1 "<C@test.git>" 1 "<D@test.git>" 1
+'
+
 test_done
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
new file mode 100755 (executable)
index 0000000..230143c
--- /dev/null
@@ -0,0 +1,188 @@
+#!/bin/sh
+
+test_description='git blame corner cases'
+. ./test-lib.sh
+
+pick_fc='s/^[0-9a-f^]* *\([^ ]*\) *(\([^ ]*\) .*/\1-\2/'
+
+test_expect_success setup '
+
+       echo A A A A A >one &&
+       echo B B B B B >two &&
+       echo C C C C C >tres &&
+       echo ABC >mouse &&
+       for i in 1 2 3 4 5 6 7 8 9
+       do
+               echo $i
+       done >nine_lines &&
+       for i in 1 2 3 4 5 6 7 8 9 a
+       do
+               echo $i
+       done >ten_lines &&
+       git add one two tres mouse nine_lines ten_lines &&
+       test_tick &&
+       GIT_AUTHOR_NAME=Initial git commit -m Initial &&
+
+       cat one >uno &&
+       mv two dos &&
+       cat one >>tres &&
+       echo DEF >>mouse
+       git add uno dos tres mouse &&
+       test_tick &&
+       GIT_AUTHOR_NAME=Second git commit -a -m Second &&
+
+       echo GHIJK >>mouse &&
+       git add mouse &&
+       test_tick &&
+       GIT_AUTHOR_NAME=Third git commit -m Third &&
+
+       cat mouse >cow &&
+       git add cow &&
+       test_tick &&
+       GIT_AUTHOR_NAME=Fourth git commit -m Fourth &&
+
+       {
+               echo ABC
+               echo DEF
+               echo XXXX
+               echo GHIJK
+       } >cow &&
+       git add cow &&
+       test_tick &&
+       GIT_AUTHOR_NAME=Fifth git commit -m Fifth
+'
+
+test_expect_success 'straight copy without -C' '
+
+       git blame uno | grep Second
+
+'
+
+test_expect_success 'straight move without -C' '
+
+       git blame dos | grep Initial
+
+'
+
+test_expect_success 'straight copy with -C' '
+
+       git blame -C1 uno | grep Second
+
+'
+
+test_expect_success 'straight move with -C' '
+
+       git blame -C1 dos | grep Initial
+
+'
+
+test_expect_success 'straight copy with -C -C' '
+
+       git blame -C -C1 uno | grep Initial
+
+'
+
+test_expect_success 'straight move with -C -C' '
+
+       git blame -C -C1 dos | grep Initial
+
+'
+
+test_expect_success 'append without -C' '
+
+       git blame -L2 tres | grep Second
+
+'
+
+test_expect_success 'append with -C' '
+
+       git blame -L2 -C1 tres | grep Second
+
+'
+
+test_expect_success 'append with -C -C' '
+
+       git blame -L2 -C -C1 tres | grep Second
+
+'
+
+test_expect_success 'append with -C -C -C' '
+
+       git blame -L2 -C -C -C1 tres | grep Initial
+
+'
+
+test_expect_success 'blame wholesale copy' '
+
+       git blame -f -C -C1 HEAD^ -- cow | sed -e "$pick_fc" >current &&
+       {
+               echo mouse-Initial
+               echo mouse-Second
+               echo mouse-Third
+       } >expected &&
+       test_cmp expected current
+
+'
+
+test_expect_success 'blame wholesale copy and more' '
+
+       git blame -f -C -C1 HEAD -- cow | sed -e "$pick_fc" >current &&
+       {
+               echo mouse-Initial
+               echo mouse-Second
+               echo cow-Fifth
+               echo mouse-Third
+       } >expected &&
+       test_cmp expected current
+
+'
+
+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_expect_success 'blame to a commit with no author name' '
+  TREE=`git rev-parse HEAD:`
+  cat >badcommit <<EOF
+tree $TREE
+author <noname> 1234567890 +0000
+committer David Reiss <dreiss@facebook.com> 1234567890 +0000
+
+some message
+EOF
+  COMMIT=`git hash-object -t commit -w badcommit`
+  git --no-pager blame $COMMIT -- uno >/dev/null
+'
+
+test_expect_success 'blame -L with invalid start' '
+       test_must_fail git blame -L5 tres 2>errors &&
+       grep "has only 2 lines" errors
+'
+
+test_expect_success 'blame -L with invalid end' '
+       test_must_fail git blame -L1,5 tres 2>errors &&
+       grep "has only 2 lines" errors
+'
+
+test_expect_success 'indent of line numbers, nine lines' '
+       git blame nine_lines >actual &&
+       test $(grep -c "  " actual) = 0
+'
+
+test_expect_success 'indent of line numbers, ten lines' '
+       git blame ten_lines >actual &&
+       test $(grep -c "  " actual) = 9
+'
+
+test_done
diff --git a/t/t8003-blame.sh b/t/t8003-blame.sh
deleted file mode 100755 (executable)
index 230143c..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/bin/sh
-
-test_description='git blame corner cases'
-. ./test-lib.sh
-
-pick_fc='s/^[0-9a-f^]* *\([^ ]*\) *(\([^ ]*\) .*/\1-\2/'
-
-test_expect_success setup '
-
-       echo A A A A A >one &&
-       echo B B B B B >two &&
-       echo C C C C C >tres &&
-       echo ABC >mouse &&
-       for i in 1 2 3 4 5 6 7 8 9
-       do
-               echo $i
-       done >nine_lines &&
-       for i in 1 2 3 4 5 6 7 8 9 a
-       do
-               echo $i
-       done >ten_lines &&
-       git add one two tres mouse nine_lines ten_lines &&
-       test_tick &&
-       GIT_AUTHOR_NAME=Initial git commit -m Initial &&
-
-       cat one >uno &&
-       mv two dos &&
-       cat one >>tres &&
-       echo DEF >>mouse
-       git add uno dos tres mouse &&
-       test_tick &&
-       GIT_AUTHOR_NAME=Second git commit -a -m Second &&
-
-       echo GHIJK >>mouse &&
-       git add mouse &&
-       test_tick &&
-       GIT_AUTHOR_NAME=Third git commit -m Third &&
-
-       cat mouse >cow &&
-       git add cow &&
-       test_tick &&
-       GIT_AUTHOR_NAME=Fourth git commit -m Fourth &&
-
-       {
-               echo ABC
-               echo DEF
-               echo XXXX
-               echo GHIJK
-       } >cow &&
-       git add cow &&
-       test_tick &&
-       GIT_AUTHOR_NAME=Fifth git commit -m Fifth
-'
-
-test_expect_success 'straight copy without -C' '
-
-       git blame uno | grep Second
-
-'
-
-test_expect_success 'straight move without -C' '
-
-       git blame dos | grep Initial
-
-'
-
-test_expect_success 'straight copy with -C' '
-
-       git blame -C1 uno | grep Second
-
-'
-
-test_expect_success 'straight move with -C' '
-
-       git blame -C1 dos | grep Initial
-
-'
-
-test_expect_success 'straight copy with -C -C' '
-
-       git blame -C -C1 uno | grep Initial
-
-'
-
-test_expect_success 'straight move with -C -C' '
-
-       git blame -C -C1 dos | grep Initial
-
-'
-
-test_expect_success 'append without -C' '
-
-       git blame -L2 tres | grep Second
-
-'
-
-test_expect_success 'append with -C' '
-
-       git blame -L2 -C1 tres | grep Second
-
-'
-
-test_expect_success 'append with -C -C' '
-
-       git blame -L2 -C -C1 tres | grep Second
-
-'
-
-test_expect_success 'append with -C -C -C' '
-
-       git blame -L2 -C -C -C1 tres | grep Initial
-
-'
-
-test_expect_success 'blame wholesale copy' '
-
-       git blame -f -C -C1 HEAD^ -- cow | sed -e "$pick_fc" >current &&
-       {
-               echo mouse-Initial
-               echo mouse-Second
-               echo mouse-Third
-       } >expected &&
-       test_cmp expected current
-
-'
-
-test_expect_success 'blame wholesale copy and more' '
-
-       git blame -f -C -C1 HEAD -- cow | sed -e "$pick_fc" >current &&
-       {
-               echo mouse-Initial
-               echo mouse-Second
-               echo cow-Fifth
-               echo mouse-Third
-       } >expected &&
-       test_cmp expected current
-
-'
-
-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_expect_success 'blame to a commit with no author name' '
-  TREE=`git rev-parse HEAD:`
-  cat >badcommit <<EOF
-tree $TREE
-author <noname> 1234567890 +0000
-committer David Reiss <dreiss@facebook.com> 1234567890 +0000
-
-some message
-EOF
-  COMMIT=`git hash-object -t commit -w badcommit`
-  git --no-pager blame $COMMIT -- uno >/dev/null
-'
-
-test_expect_success 'blame -L with invalid start' '
-       test_must_fail git blame -L5 tres 2>errors &&
-       grep "has only 2 lines" errors
-'
-
-test_expect_success 'blame -L with invalid end' '
-       test_must_fail git blame -L1,5 tres 2>errors &&
-       grep "has only 2 lines" errors
-'
-
-test_expect_success 'indent of line numbers, nine lines' '
-       git blame nine_lines >actual &&
-       test $(grep -c "  " actual) = 0
-'
-
-test_expect_success 'indent of line numbers, ten lines' '
-       git blame ten_lines >actual &&
-       test $(grep -c "  " actual) = 9
-'
-
-test_done
diff --git a/t/t8004-blame-with-conflicts.sh b/t/t8004-blame-with-conflicts.sh
new file mode 100755 (executable)
index 0000000..ba19ac1
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+# Based on a test case submitted by Björn Steinbrink.
+
+test_description='git blame on conflicted files'
+. ./test-lib.sh
+
+test_expect_success 'setup first case' '
+       # Create the old file
+       echo "Old line" > file1 &&
+       git add file1 &&
+       git commit --author "Old Line <ol@localhost>" -m file1.a &&
+
+       # Branch
+       git checkout -b foo &&
+
+       # Do an ugly move and change
+       git rm file1 &&
+       echo "New line ..."  > file2 &&
+       echo "... and more" >> file2 &&
+       git add file2 &&
+       git commit --author "U Gly <ug@localhost>" -m ugly &&
+
+       # Back to master and change something
+       git checkout master &&
+       echo "
+
+bla" >> file1 &&
+       git commit --author "Old Line <ol@localhost>" -a -m file1.b &&
+
+       # Back to foo and merge master
+       git checkout foo &&
+       if git merge master; then
+               echo needed conflict here
+               exit 1
+       else
+               echo merge failed - resolving automatically
+       fi &&
+       echo "New line ...
+... and more
+
+bla
+Even more" > file2 &&
+       git rm file1 &&
+       git commit --author "M Result <mr@localhost>" -a -m merged &&
+
+       # Back to master and change file1 again
+       git checkout master &&
+       sed s/bla/foo/ <file1 >X &&
+       rm file1 &&
+       mv X file1 &&
+       git commit --author "No Bla <nb@localhost>" -a -m replace &&
+
+       # Try to merge into foo again
+       git checkout foo &&
+       if git merge master; then
+               echo needed conflict here
+               exit 1
+       else
+               echo merge failed - test is setup
+       fi
+'
+
+test_expect_success \
+       'blame runs on unconflicted file while other file has conflicts' '
+       git blame file2
+'
+
+test_expect_success 'blame runs on conflicted file in stages 1,3' '
+       git blame file1
+'
+
+test_done
diff --git a/t/t8004-blame.sh b/t/t8004-blame.sh
deleted file mode 100755 (executable)
index ba19ac1..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/sh
-
-# Based on a test case submitted by Björn Steinbrink.
-
-test_description='git blame on conflicted files'
-. ./test-lib.sh
-
-test_expect_success 'setup first case' '
-       # Create the old file
-       echo "Old line" > file1 &&
-       git add file1 &&
-       git commit --author "Old Line <ol@localhost>" -m file1.a &&
-
-       # Branch
-       git checkout -b foo &&
-
-       # Do an ugly move and change
-       git rm file1 &&
-       echo "New line ..."  > file2 &&
-       echo "... and more" >> file2 &&
-       git add file2 &&
-       git commit --author "U Gly <ug@localhost>" -m ugly &&
-
-       # Back to master and change something
-       git checkout master &&
-       echo "
-
-bla" >> file1 &&
-       git commit --author "Old Line <ol@localhost>" -a -m file1.b &&
-
-       # Back to foo and merge master
-       git checkout foo &&
-       if git merge master; then
-               echo needed conflict here
-               exit 1
-       else
-               echo merge failed - resolving automatically
-       fi &&
-       echo "New line ...
-... and more
-
-bla
-Even more" > file2 &&
-       git rm file1 &&
-       git commit --author "M Result <mr@localhost>" -a -m merged &&
-
-       # Back to master and change file1 again
-       git checkout master &&
-       sed s/bla/foo/ <file1 >X &&
-       rm file1 &&
-       mv X file1 &&
-       git commit --author "No Bla <nb@localhost>" -a -m replace &&
-
-       # Try to merge into foo again
-       git checkout foo &&
-       if git merge master; then
-               echo needed conflict here
-               exit 1
-       else
-               echo merge failed - test is setup
-       fi
-'
-
-test_expect_success \
-       'blame runs on unconflicted file while other file has conflicts' '
-       git blame file2
-'
-
-test_expect_success 'blame runs on conflicted file in stages 1,3' '
-       git blame file1
-'
-
-test_done
index 9ad96d4d32390081ba97c336d80b2294cf69dc44..ea64cd8d0f02a3a08dca7742a29331c85e7da384 100755 (executable)
@@ -9,22 +9,29 @@ find_blame() {
 
 cat >helper <<'EOF'
 #!/bin/sh
-sed 's/^/converted: /' "$@"
+grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
+sed 's/^bin: /converted: /' "$1"
 EOF
 chmod +x helper
 
 test_expect_success 'setup ' '
-       echo test 1 >one.bin &&
-       echo test number 2 >two.bin &&
+       echo "bin: test 1" >one.bin &&
+       echo "bin: test number 2" >two.bin &&
+       if test_have_prereq SYMLINKS; then
+               ln -s one.bin symlink.bin
+       fi &&
        git add . &&
        GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
-       echo test 1 version 2 >one.bin &&
-       echo test number 2 version 2 >>two.bin &&
+       echo "bin: test 1 version 2" >one.bin &&
+       echo "bin: test number 2 version 2" >>two.bin &&
+       if test_have_prereq SYMLINKS; then
+               ln -sf two.bin symlink.bin
+       fi &&
        GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
 '
 
 cat >expected <<EOF
-(Number2 2010-01-01 20:00:00 +0000 1) test 1 version 2
+(Number2 2010-01-01 20:00:00 +0000 1) bin: test 1 version 2
 EOF
 
 test_expect_success 'no filter specified' '
@@ -66,8 +73,29 @@ test_expect_success 'blame --textconv going through revisions' '
        test_cmp expected result
 '
 
+test_expect_success 'setup +cachetextconv' '
+       git config diff.test.cachetextconv true
+'
+
+cat >expected_one <<EOF
+(Number2 2010-01-01 20:00:00 +0000 1) converted: test 1 version 2
+EOF
+
+test_expect_success 'blame --textconv works with textconvcache' '
+       git blame --textconv two.bin >blame &&
+       find_blame <blame >result &&
+       test_cmp expected result &&
+       git blame --textconv one.bin >blame &&
+       find_blame  <blame >result &&
+       test_cmp expected_one result
+'
+
+test_expect_success 'setup -cachetextconv' '
+       git config diff.test.cachetextconv false
+'
+
 test_expect_success 'make a new commit' '
-       echo "test number 2 version 3" >>two.bin &&
+       echo "bin: test number 2 version 3" >>two.bin &&
        GIT_AUTHOR_NAME=Number3 git commit -a -m Third --date="2010-01-01 22:00:00"
 '
 
@@ -77,4 +105,45 @@ test_expect_success 'blame from previous revision' '
        test_cmp expected result
 '
 
+cat >expected <<EOF
+(Number2 2010-01-01 20:00:00 +0000 1) two.bin
+EOF
+
+test_expect_success SYMLINKS 'blame with --no-textconv (on symlink)' '
+       git blame --no-textconv symlink.bin >blame &&
+       find_blame <blame >result &&
+       test_cmp expected result
+'
+
+test_expect_success SYMLINKS 'blame --textconv (on symlink)' '
+       git blame --textconv symlink.bin >blame &&
+       find_blame <blame >result &&
+       test_cmp expected result
+'
+
+# cp two.bin three.bin  and make small tweak
+# (this will direct blame -C -C three.bin to consider two.bin and symlink.bin)
+test_expect_success SYMLINKS 'make another new commit' '
+       cat >three.bin <<\EOF &&
+bin: test number 2
+bin: test number 2 version 2
+bin: test number 2 version 3
+bin: test number 3
+EOF
+       git add three.bin &&
+       GIT_AUTHOR_NAME=Number4 git commit -a -m Fourth --date="2010-01-01 23:00:00"
+'
+
+test_expect_success SYMLINKS 'blame on last commit (-C -C, symlink)' '
+       git blame -C -C three.bin >blame &&
+       find_blame <blame >result &&
+       cat >expected <<\EOF &&
+(Number1 2010-01-01 18:00:00 +0000 1) converted: test number 2
+(Number2 2010-01-01 20:00:00 +0000 2) converted: test number 2 version 2
+(Number3 2010-01-01 22:00:00 +0000 3) converted: test number 2 version 3
+(Number4 2010-01-01 23:00:00 +0000 4) converted: test number 3
+EOF
+       test_cmp expected result
+'
+
 test_done
index 38ac05e4a0b62e9105a400b4583d5504b42b3e7d..78a0085e648b8fa6773b47e86959853cf29ccdf9 100755 (executable)
@@ -5,15 +5,19 @@ test_description='git cat-file textconv support'
 
 cat >helper <<'EOF'
 #!/bin/sh
-sed 's/^/converted: /' "$@"
+grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
+sed 's/^bin: /converted: /' "$1"
 EOF
 chmod +x helper
 
 test_expect_success 'setup ' '
-       echo test >one.bin &&
+       echo "bin: test" >one.bin &&
+       if test_have_prereq SYMLINKS; then
+               ln -s one.bin symlink.bin
+       fi &&
        git add . &&
        GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
-       echo test version 2 >one.bin &&
+       echo "bin: test version 2" >one.bin &&
        GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
 '
 
@@ -33,7 +37,7 @@ test_expect_success 'setup textconv filters' '
 '
 
 cat >expected <<EOF
-test version 2
+bin: test version 2
 EOF
 
 test_expect_success 'cat-file without --textconv' '
@@ -42,7 +46,7 @@ test_expect_success 'cat-file without --textconv' '
 '
 
 cat >expected <<EOF
-test
+bin: test
 EOF
 
 test_expect_success 'cat-file without --textconv on previous commit' '
@@ -67,4 +71,28 @@ test_expect_success 'cat-file --textconv on previous commit' '
        git cat-file --textconv HEAD^:one.bin >result &&
        test_cmp expected result
 '
+
+test_expect_success SYMLINKS 'cat-file without --textconv (symlink)' '
+       git cat-file blob :symlink.bin >result &&
+       printf "%s" "one.bin" >expected
+       test_cmp expected result
+'
+
+
+test_expect_success SYMLINKS 'cat-file --textconv on index (symlink)' '
+       ! git cat-file --textconv :symlink.bin 2>result &&
+       cat >expected <<\EOF &&
+fatal: git cat-file --textconv: unable to run textconv on :symlink.bin
+EOF
+       test_cmp expected result
+'
+
+test_expect_success SYMLINKS 'cat-file --textconv on HEAD (symlink)' '
+       ! git cat-file --textconv HEAD:symlink.bin 2>result &&
+       cat >expected <<EOF &&
+fatal: git cat-file --textconv: unable to run textconv on HEAD:symlink.bin
+EOF
+       test_cmp expected result
+'
+
 test_done
index 23597cc40751addd1473acd2edf7563e0a517c9e..579ddb7572c27889060da315ddecb773b6fa1272 100755 (executable)
@@ -3,20 +3,17 @@
 test_description='git send-email'
 . ./test-lib.sh
 
-if ! test_have_prereq PERL; then
-       skip_all='skipping git send-email tests, perl not available'
-       test_done
-fi
+# May be altered later in the test
+PREREQ="PERL"
 
-PROG='git send-email'
-test_expect_success \
+test_expect_success $PREREQ \
     'prepare reference tree' \
     'echo "1A quick brown fox jumps over the" >file &&
      echo "lazy dog" >>file &&
      git add file &&
      GIT_AUTHOR_NAME="A" git commit -a -m "Initial."'
 
-test_expect_success \
+test_expect_success $PREREQ \
     'Setup helper tool' \
     '(echo "#!$SHELL_PATH"
       echo shift
@@ -36,7 +33,7 @@ clean_fake_sendmail() {
        rm -f commandline* msgtxt*
 }
 
-test_expect_success 'Extract patches' '
+test_expect_success $PREREQ 'Extract patches' '
     patches=`git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1`
 '
 
@@ -57,49 +54,56 @@ test_no_confirm () {
 
 # Exit immediately to prevent hang if a no-confirm test fails
 check_no_confirm () {
-       test -f no_confirm_okay || {
-               skip_all='confirm test failed; skipping remaining tests to prevent hanging'
-               test_done
-       }
+       if ! test -f no_confirm_okay
+       then
+               say 'confirm test failed; skipping remaining tests to prevent hanging'
+               PREREQ="$PREREQ,CHECK_NO_CONFIRM"
+       fi
+       return 0
 }
 
-test_expect_success 'No confirm with --suppress-cc' '
-       test_no_confirm --suppress-cc=sob
+test_expect_success $PREREQ 'No confirm with --suppress-cc' '
+       test_no_confirm --suppress-cc=sob &&
+       check_no_confirm
 '
-check_no_confirm
 
-test_expect_success 'No confirm with --confirm=never' '
-       test_no_confirm --confirm=never
+
+test_expect_success $PREREQ 'No confirm with --confirm=never' '
+       test_no_confirm --confirm=never &&
+       check_no_confirm
 '
-check_no_confirm
 
 # leave sendemail.confirm set to never after this so that none of the
 # remaining tests prompt unintentionally.
-test_expect_success 'No confirm with sendemail.confirm=never' '
+test_expect_success $PREREQ 'No confirm with sendemail.confirm=never' '
        git config sendemail.confirm never &&
-       test_no_confirm --compose --subject=foo
+       test_no_confirm --compose --subject=foo &&
+       check_no_confirm
 '
-check_no_confirm
 
-test_expect_success 'Send patches' '
+test_expect_success $PREREQ 'Send patches' '
      git send-email --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
 '
 
+test_expect_success $PREREQ 'setup expect' '
 cat >expected <<\EOF
 !nobody@example.com!
 !author@example.com!
 !one@example.com!
 !two@example.com!
 EOF
-test_expect_success \
+'
+
+test_expect_success $PREREQ \
     'Verify commandline' \
     'test_cmp expected commandline1'
 
-test_expect_success 'Send patches with --envelope-sender' '
+test_expect_success $PREREQ 'Send patches with --envelope-sender' '
     clean_fake_sendmail &&
      git send-email --envelope-sender="Patch Contributer <patch@example.com>" --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
 '
 
+test_expect_success $PREREQ 'setup expect' '
 cat >expected <<\EOF
 !patch@example.com!
 !-i!
@@ -108,15 +112,18 @@ cat >expected <<\EOF
 !one@example.com!
 !two@example.com!
 EOF
-test_expect_success \
+'
+
+test_expect_success $PREREQ \
     'Verify commandline' \
     'test_cmp expected commandline1'
 
-test_expect_success 'Send patches with --envelope-sender=auto' '
+test_expect_success $PREREQ 'Send patches with --envelope-sender=auto' '
     clean_fake_sendmail &&
      git send-email --envelope-sender=auto --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
 '
 
+test_expect_success $PREREQ 'setup expect' '
 cat >expected <<\EOF
 !nobody@example.com!
 !-i!
@@ -125,10 +132,13 @@ cat >expected <<\EOF
 !one@example.com!
 !two@example.com!
 EOF
-test_expect_success \
+'
+
+test_expect_success $PREREQ \
     'Verify commandline' \
     'test_cmp expected commandline1'
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-show-all-headers <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -158,8 +168,9 @@ References: <unique-message-id@example.com>
 
 Result: OK
 EOF
+"
 
-test_expect_success 'Show all headers' '
+test_expect_success $PREREQ 'Show all headers' '
        git send-email \
                --dry-run \
                --suppress-cc=sob \
@@ -177,7 +188,7 @@ test_expect_success 'Show all headers' '
        test_cmp expected-show-all-headers actual-show-all-headers
 '
 
-test_expect_success 'Prompting works' '
+test_expect_success $PREREQ 'Prompting works' '
        clean_fake_sendmail &&
        (echo "Example <from@example.com>"
         echo "to@example.com"
@@ -190,10 +201,28 @@ test_expect_success 'Prompting works' '
                grep "^To: to@example.com\$" msgtxt1
 '
 
-test_expect_success 'cccmd works' '
+test_expect_success $PREREQ 'tocmd works' '
+       clean_fake_sendmail &&
+       cp $patches tocmd.patch &&
+       echo tocmd--tocmd@example.com >>tocmd.patch &&
+       {
+         echo "#!$SHELL_PATH"
+         echo sed -n -e s/^tocmd--//p \"\$1\"
+       } > tocmd-sed &&
+       chmod +x tocmd-sed &&
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to-cmd=./tocmd-sed \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               tocmd.patch \
+               &&
+       grep "^To: tocmd@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ 'cccmd works' '
        clean_fake_sendmail &&
        cp $patches cccmd.patch &&
-       echo cccmd--cccmd@example.com >>cccmd.patch &&
+       echo "cccmd--  cccmd@example.com" >>cccmd.patch &&
        {
          echo "#!$SHELL_PATH"
          echo sed -n -e s/^cccmd--//p \"\$1\"
@@ -209,10 +238,10 @@ test_expect_success 'cccmd works' '
        grep "^ cccmd@example.com" msgtxt1
 '
 
-z8=zzzzzzzz
-z64=$z8$z8$z8$z8$z8$z8$z8$z8
-z512=$z64$z64$z64$z64$z64$z64$z64$z64
-test_expect_success 'reject long lines' '
+test_expect_success $PREREQ 'reject long lines' '
+       z8=zzzzzzzz &&
+       z64=$z8$z8$z8$z8$z8$z8$z8$z8 &&
+       z512=$z64$z64$z64$z64$z64$z64$z64$z64 &&
        clean_fake_sendmail &&
        cp $patches longline.patch &&
        echo $z512$z512 >>longline.patch &&
@@ -225,33 +254,33 @@ test_expect_success 'reject long lines' '
        grep longline.patch errors
 '
 
-test_expect_success 'no patch was sent' '
+test_expect_success $PREREQ 'no patch was sent' '
        ! test -e commandline1
 '
 
-test_expect_success 'Author From: in message body' '
+test_expect_success $PREREQ 'Author From: in message body' '
        clean_fake_sendmail &&
        git send-email \
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
                --smtp-server="$(pwd)/fake.sendmail" \
                $patches &&
-       sed "1,/^\$/d" < msgtxt1 > msgbody1
+       sed "1,/^\$/d" < msgtxt1 > msgbody1 &&
        grep "From: A <author@example.com>" msgbody1
 '
 
-test_expect_success 'Author From: not in message body' '
+test_expect_success $PREREQ 'Author From: not in message body' '
        clean_fake_sendmail &&
        git send-email \
                --from="A <author@example.com>" \
                --to=nobody@example.com \
                --smtp-server="$(pwd)/fake.sendmail" \
                $patches &&
-       sed "1,/^\$/d" < msgtxt1 > msgbody1
+       sed "1,/^\$/d" < msgtxt1 > msgbody1 &&
        ! grep "From: A <author@example.com>" msgbody1
 '
 
-test_expect_success 'allow long lines with --no-validate' '
+test_expect_success $PREREQ 'allow long lines with --no-validate' '
        git send-email \
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
@@ -261,19 +290,19 @@ test_expect_success 'allow long lines with --no-validate' '
                2>errors
 '
 
-test_expect_success 'Invalid In-Reply-To' '
+test_expect_success $PREREQ 'Invalid In-Reply-To' '
        clean_fake_sendmail &&
        git send-email \
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
                --in-reply-to=" " \
                --smtp-server="$(pwd)/fake.sendmail" \
-               $patches
-               2>errors
+               $patches \
+               2>errors &&
        ! grep "^In-Reply-To: < *>" msgtxt1
 '
 
-test_expect_success 'Valid In-Reply-To when prompting' '
+test_expect_success $PREREQ 'Valid In-Reply-To when prompting' '
        clean_fake_sendmail &&
        (echo "From Example <from@example.com>"
         echo "To Example <to@example.com>"
@@ -284,7 +313,50 @@ test_expect_success 'Valid In-Reply-To when prompting' '
        ! grep "^In-Reply-To: < *>" msgtxt1
 '
 
-test_expect_success 'setup fake editor' '
+test_expect_success $PREREQ 'In-Reply-To without --chain-reply-to' '
+       clean_fake_sendmail &&
+       echo "<unique-message-id@example.com>" >expect &&
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --nochain-reply-to \
+               --in-reply-to="$(cat expect)" \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               $patches $patches $patches \
+               2>errors &&
+       # The first message is a reply to --in-reply-to
+       sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
+       test_cmp expect actual &&
+       # Second and subsequent messages are replies to the first one
+       sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+       sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
+       test_cmp expect actual &&
+       sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success $PREREQ 'In-Reply-To with --chain-reply-to' '
+       clean_fake_sendmail &&
+       echo "<unique-message-id@example.com>" >expect &&
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --chain-reply-to \
+               --in-reply-to="$(cat expect)" \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               $patches $patches $patches \
+               2>errors &&
+       sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
+       test_cmp expect actual &&
+       sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+       sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
+       test_cmp expect actual &&
+       sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt2 >expect &&
+       sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success $PREREQ 'setup fake editor' '
        (echo "#!$SHELL_PATH" &&
         echo "echo fake edit >>\"\$1\""
        ) >fake-editor &&
@@ -293,7 +365,7 @@ test_expect_success 'setup fake editor' '
 
 test_set_editor "$(pwd)/fake-editor"
 
-test_expect_success '--compose works' '
+test_expect_success $PREREQ '--compose works' '
        clean_fake_sendmail &&
        git send-email \
        --compose --subject foo \
@@ -304,14 +376,15 @@ test_expect_success '--compose works' '
        2>errors
 '
 
-test_expect_success 'first message is compose text' '
+test_expect_success $PREREQ 'first message is compose text' '
        grep "^fake edit" msgtxt1
 '
 
-test_expect_success 'second message is patch' '
+test_expect_success $PREREQ 'second message is patch' '
        grep "Subject:.*Second" msgtxt2
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-sob <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -338,6 +411,7 @@ X-Mailer: X-MAILER-STRING
 
 Result: OK
 EOF
+"
 
 test_suppression () {
        git send-email \
@@ -354,11 +428,12 @@ test_suppression () {
        test_cmp expected-suppress-$1${2+"-$2"} actual-suppress-$1${2+"-$2"}
 }
 
-test_expect_success 'sendemail.cc set' '
+test_expect_success $PREREQ 'sendemail.cc set' '
        git config sendemail.cc cc@example.com &&
        test_suppression sob
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-sob <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -383,12 +458,14 @@ X-Mailer: X-MAILER-STRING
 
 Result: OK
 EOF
+"
 
-test_expect_success 'sendemail.cc unset' '
+test_expect_success $PREREQ 'sendemail.cc unset' '
        git config --unset sendemail.cc &&
        test_suppression sob
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-cccmd <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -416,14 +493,16 @@ X-Mailer: X-MAILER-STRING
 
 Result: OK
 EOF
+"
 
-test_expect_success 'sendemail.cccmd' '
+test_expect_success $PREREQ 'sendemail.cccmd' '
        echo echo cc-cmd@example.com > cccmd &&
        chmod +x cccmd &&
        git config sendemail.cccmd ./cccmd &&
        test_suppression cccmd
 '
 
+test_expect_success $PREREQ 'setup expect' '
 cat >expected-suppress-all <<\EOF
 0001-Second.patch
 Dry-OK. Log says:
@@ -439,11 +518,13 @@ X-Mailer: X-MAILER-STRING
 
 Result: OK
 EOF
+'
 
-test_expect_success '--suppress-cc=all' '
+test_expect_success $PREREQ '--suppress-cc=all' '
        test_suppression all
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-body <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -471,11 +552,13 @@ X-Mailer: X-MAILER-STRING
 
 Result: OK
 EOF
+"
 
-test_expect_success '--suppress-cc=body' '
+test_expect_success $PREREQ '--suppress-cc=body' '
        test_suppression body
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-body-cccmd <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -500,11 +583,13 @@ X-Mailer: X-MAILER-STRING
 
 Result: OK
 EOF
+"
 
-test_expect_success '--suppress-cc=body --suppress-cc=cccmd' '
+test_expect_success $PREREQ '--suppress-cc=body --suppress-cc=cccmd' '
        test_suppression body cccmd
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-sob <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -529,12 +614,14 @@ X-Mailer: X-MAILER-STRING
 
 Result: OK
 EOF
+"
 
-test_expect_success '--suppress-cc=sob' '
-       git config --unset sendemail.cccmd
+test_expect_success $PREREQ '--suppress-cc=sob' '
+       test_might_fail git config --unset sendemail.cccmd &&
        test_suppression sob
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-bodycc <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -562,11 +649,13 @@ X-Mailer: X-MAILER-STRING
 
 Result: OK
 EOF
+"
 
-test_expect_success '--suppress-cc=bodycc' '
+test_expect_success $PREREQ '--suppress-cc=bodycc' '
        test_suppression bodycc
 '
 
+test_expect_success $PREREQ 'setup expect' "
 cat >expected-suppress-cc <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -588,8 +677,9 @@ X-Mailer: X-MAILER-STRING
 
 Result: OK
 EOF
+"
 
-test_expect_success '--suppress-cc=cc' '
+test_expect_success $PREREQ '--suppress-cc=cc' '
        test_suppression cc
 '
 
@@ -604,23 +694,23 @@ test_confirm () {
        grep "Send this email" stdout
 }
 
-test_expect_success '--confirm=always' '
+test_expect_success $PREREQ '--confirm=always' '
        test_confirm --confirm=always --suppress-cc=all
 '
 
-test_expect_success '--confirm=auto' '
+test_expect_success $PREREQ '--confirm=auto' '
        test_confirm --confirm=auto
 '
 
-test_expect_success '--confirm=cc' '
+test_expect_success $PREREQ '--confirm=cc' '
        test_confirm --confirm=cc
 '
 
-test_expect_success '--confirm=compose' '
+test_expect_success $PREREQ '--confirm=compose' '
        test_confirm --confirm=compose --compose
 '
 
-test_expect_success 'confirm by default (due to cc)' '
+test_expect_success $PREREQ 'confirm by default (due to cc)' '
        CONFIRM=$(git config --get sendemail.confirm) &&
        git config --unset sendemail.confirm &&
        test_confirm
@@ -629,7 +719,7 @@ test_expect_success 'confirm by default (due to cc)' '
        test $ret = "0"
 '
 
-test_expect_success 'confirm by default (due to --compose)' '
+test_expect_success $PREREQ 'confirm by default (due to --compose)' '
        CONFIRM=$(git config --get sendemail.confirm) &&
        git config --unset sendemail.confirm &&
        test_confirm --suppress-cc=all --compose
@@ -638,7 +728,7 @@ test_expect_success 'confirm by default (due to --compose)' '
        test $ret = "0"
 '
 
-test_expect_success 'confirm detects EOF (inform assumes y)' '
+test_expect_success $PREREQ 'confirm detects EOF (inform assumes y)' '
        CONFIRM=$(git config --get sendemail.confirm) &&
        git config --unset sendemail.confirm &&
        rm -fr outdir &&
@@ -654,7 +744,7 @@ test_expect_success 'confirm detects EOF (inform assumes y)' '
        test $ret = "0"
 '
 
-test_expect_success 'confirm detects EOF (auto causes failure)' '
+test_expect_success $PREREQ 'confirm detects EOF (auto causes failure)' '
        CONFIRM=$(git config --get sendemail.confirm) &&
        git config sendemail.confirm auto &&
        GIT_SEND_EMAIL_NOTTY=1 &&
@@ -669,7 +759,7 @@ test_expect_success 'confirm detects EOF (auto causes failure)' '
        test $ret = "0"
 '
 
-test_expect_success 'confirm doesnt loop forever' '
+test_expect_success $PREREQ 'confirm doesnt loop forever' '
        CONFIRM=$(git config --get sendemail.confirm) &&
        git config sendemail.confirm auto &&
        GIT_SEND_EMAIL_NOTTY=1 &&
@@ -684,7 +774,7 @@ test_expect_success 'confirm doesnt loop forever' '
        test $ret = "0"
 '
 
-test_expect_success 'utf8 Cc is rfc2047 encoded' '
+test_expect_success $PREREQ 'utf8 Cc is rfc2047 encoded' '
        clean_fake_sendmail &&
        rm -fr outdir &&
        git format-patch -1 -o outdir --cc="àéìöú <utf8@example.com>" &&
@@ -697,7 +787,7 @@ test_expect_success 'utf8 Cc is rfc2047 encoded' '
        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' '
+test_expect_success $PREREQ '--compose adds MIME for utf8 body' '
        clean_fake_sendmail &&
        (echo "#!$SHELL_PATH" &&
         echo "echo utf8 body: àéìöú >>\"\$1\""
@@ -714,7 +804,7 @@ test_expect_success '--compose adds MIME for utf8 body' '
        grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1
 '
 
-test_expect_success '--compose respects user mime type' '
+test_expect_success $PREREQ '--compose respects user mime type' '
        clean_fake_sendmail &&
        (echo "#!$SHELL_PATH" &&
         echo "(echo MIME-Version: 1.0"
@@ -737,7 +827,7 @@ test_expect_success '--compose respects user mime type' '
        ! grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1
 '
 
-test_expect_success '--compose adds MIME for utf8 subject' '
+test_expect_success $PREREQ '--compose adds MIME for utf8 subject' '
        clean_fake_sendmail &&
          GIT_EDITOR="\"$(pwd)/fake-editor\"" \
          git send-email \
@@ -750,7 +840,7 @@ test_expect_success '--compose adds MIME for utf8 subject' '
        grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
 '
 
-test_expect_success 'detects ambiguous reference/file conflict' '
+test_expect_success $PREREQ 'detects ambiguous reference/file conflict' '
        echo master > master &&
        git add master &&
        git commit -m"add master" &&
@@ -758,7 +848,7 @@ test_expect_success 'detects ambiguous reference/file conflict' '
        grep disambiguate errors
 '
 
-test_expect_success 'feed two files' '
+test_expect_success $PREREQ 'feed two files' '
        rm -fr outdir &&
        git format-patch -2 -o outdir &&
        git send-email \
@@ -771,7 +861,7 @@ test_expect_success 'feed two files' '
        test "z$(sed -n -e 2p subjects)" = "zSubject: [PATCH 2/2] add master"
 '
 
-test_expect_success 'in-reply-to but no threading' '
+test_expect_success $PREREQ 'in-reply-to but no threading' '
        git send-email \
                --dry-run \
                --from="Example <nobody@example.com>" \
@@ -782,7 +872,7 @@ 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' '
+test_expect_success $PREREQ 'no in-reply-to and no threading' '
        git send-email \
                --dry-run \
                --from="Example <nobody@example.com>" \
@@ -792,7 +882,7 @@ test_expect_success 'no in-reply-to and no threading' '
        ! grep "In-Reply-To: " stdout
 '
 
-test_expect_success 'threading but no chain-reply-to' '
+test_expect_success $PREREQ 'threading but no chain-reply-to' '
        git send-email \
                --dry-run \
                --from="Example <nobody@example.com>" \
@@ -803,7 +893,7 @@ test_expect_success 'threading but no chain-reply-to' '
        grep "In-Reply-To: " stdout
 '
 
-test_expect_success 'warning with an implicit --chain-reply-to' '
+test_expect_success $PREREQ 'warning with an implicit --chain-reply-to' '
        git send-email \
        --dry-run \
        --from="Example <nobody@example.com>" \
@@ -812,7 +902,7 @@ test_expect_success 'warning with an implicit --chain-reply-to' '
        grep "no-chain-reply-to" errors
 '
 
-test_expect_success 'no warning with an explicit --chain-reply-to' '
+test_expect_success $PREREQ 'no warning with an explicit --chain-reply-to' '
        git send-email \
        --dry-run \
        --from="Example <nobody@example.com>" \
@@ -822,7 +912,7 @@ test_expect_success 'no warning with an explicit --chain-reply-to' '
        ! grep "no-chain-reply-to" errors
 '
 
-test_expect_success 'no warning with an explicit --no-chain-reply-to' '
+test_expect_success $PREREQ 'no warning with an explicit --no-chain-reply-to' '
        git send-email \
        --dry-run \
        --from="Example <nobody@example.com>" \
@@ -832,7 +922,7 @@ test_expect_success 'no warning with an explicit --no-chain-reply-to' '
        ! grep "no-chain-reply-to" errors
 '
 
-test_expect_success 'no warning with sendemail.chainreplyto = false' '
+test_expect_success $PREREQ 'no warning with sendemail.chainreplyto = false' '
        git config sendemail.chainreplyto false &&
        git send-email \
        --dry-run \
@@ -842,7 +932,7 @@ test_expect_success 'no warning with sendemail.chainreplyto = false' '
        ! grep "no-chain-reply-to" errors
 '
 
-test_expect_success 'no warning with sendemail.chainreplyto = true' '
+test_expect_success $PREREQ 'no warning with sendemail.chainreplyto = true' '
        git config sendemail.chainreplyto true &&
        git send-email \
        --dry-run \
@@ -852,7 +942,7 @@ test_expect_success 'no warning with sendemail.chainreplyto = true' '
        ! grep "no-chain-reply-to" errors
 '
 
-test_expect_success 'sendemail.to works' '
+test_expect_success $PREREQ 'sendemail.to works' '
        git config --replace-all sendemail.to "Somebody <somebody@ex.com>" &&
        git send-email \
                --dry-run \
@@ -861,7 +951,7 @@ test_expect_success 'sendemail.to works' '
        grep "To: Somebody <somebody@ex.com>" stdout
 '
 
-test_expect_success '--no-to overrides sendemail.to' '
+test_expect_success $PREREQ '--no-to overrides sendemail.to' '
        git send-email \
                --dry-run \
                --from="Example <nobody@example.com>" \
@@ -872,7 +962,7 @@ test_expect_success '--no-to overrides sendemail.to' '
        ! grep "To: Somebody <somebody@ex.com>" stdout
 '
 
-test_expect_success 'sendemail.cc works' '
+test_expect_success $PREREQ 'sendemail.cc works' '
        git config --replace-all sendemail.cc "Somebody <somebody@ex.com>" &&
        git send-email \
                --dry-run \
@@ -882,7 +972,7 @@ test_expect_success 'sendemail.cc works' '
        grep "Cc: Somebody <somebody@ex.com>" stdout
 '
 
-test_expect_success '--no-cc overrides sendemail.cc' '
+test_expect_success $PREREQ '--no-cc overrides sendemail.cc' '
        git send-email \
                --dry-run \
                --from="Example <nobody@example.com>" \
@@ -894,7 +984,7 @@ test_expect_success '--no-cc overrides sendemail.cc' '
        ! grep "Cc: Somebody <somebody@ex.com>" stdout
 '
 
-test_expect_success 'sendemail.bcc works' '
+test_expect_success $PREREQ 'sendemail.bcc works' '
        git config --replace-all sendemail.bcc "Other <other@ex.com>" &&
        git send-email \
                --dry-run \
@@ -905,7 +995,7 @@ test_expect_success 'sendemail.bcc works' '
        grep "RCPT TO:<other@ex.com>" stdout
 '
 
-test_expect_success '--no-bcc overrides sendemail.bcc' '
+test_expect_success $PREREQ '--no-bcc overrides sendemail.bcc' '
        git send-email \
                --dry-run \
                --from="Example <nobody@example.com>" \
@@ -918,6 +1008,46 @@ test_expect_success '--no-bcc overrides sendemail.bcc' '
        ! grep "RCPT TO:<other@ex.com>" stdout
 '
 
+test_expect_success $PREREQ 'patches To headers are used by default' '
+       patch=`git format-patch -1 --to="bodies@example.com"` &&
+       test_when_finished "rm $patch" &&
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --smtp-server relay.example.com \
+               $patch >stdout &&
+       grep "RCPT TO:<bodies@example.com>" stdout
+'
+
+test_expect_success $PREREQ 'patches To headers are appended to' '
+       patch=`git format-patch -1 --to="bodies@example.com"` &&
+       test_when_finished "rm $patch" &&
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --smtp-server relay.example.com \
+               $patch >stdout &&
+       grep "RCPT TO:<bodies@example.com>" stdout &&
+       grep "RCPT TO:<nobody@example.com>" stdout
+'
+
+test_expect_success $PREREQ 'To headers from files reset each patch' '
+       patch1=`git format-patch -1 --to="bodies@example.com"` &&
+       patch2=`git format-patch -1 --to="other@example.com" HEAD~` &&
+       test_when_finished "rm $patch1 && rm $patch2" &&
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --to="nobody@example.com" \
+               --smtp-server relay.example.com \
+               $patch1 $patch2 >stdout &&
+       test $(grep -c "RCPT TO:<bodies@example.com>" stdout) = 1 &&
+       test $(grep -c "RCPT TO:<nobody@example.com>" stdout) = 2 &&
+       test $(grep -c "RCPT TO:<other@example.com>" stdout) = 1
+'
+
+test_expect_success $PREREQ 'setup expect' '
 cat >email-using-8bit <<EOF
 From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
 Message-Id: <bogus-message-id@example.com>
@@ -927,14 +1057,17 @@ Subject: subject goes here
 
 Dieser deutsche Text enthält einen Umlaut!
 EOF
+'
 
+test_expect_success $PREREQ 'setup expect' '
 cat >content-type-decl <<EOF
 MIME-Version: 1.0
 Content-Type: text/plain; charset=UTF-8
 Content-Transfer-Encoding: 8bit
 EOF
+'
 
-test_expect_success 'asks about and fixes 8bit encodings' '
+test_expect_success $PREREQ 'asks about and fixes 8bit encodings' '
        clean_fake_sendmail &&
        echo |
        git send-email --from=author@example.com --to=nobody@example.com \
@@ -947,7 +1080,7 @@ test_expect_success 'asks about and fixes 8bit encodings' '
        test_cmp actual content-type-decl
 '
 
-test_expect_success 'sendemail.8bitEncoding works' '
+test_expect_success $PREREQ 'sendemail.8bitEncoding works' '
        clean_fake_sendmail &&
        git config sendemail.assume8bitEncoding UTF-8 &&
        echo bogus |
@@ -958,7 +1091,7 @@ test_expect_success 'sendemail.8bitEncoding works' '
        test_cmp actual content-type-decl
 '
 
-test_expect_success '--8bit-encoding overrides sendemail.8bitEncoding' '
+test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
        clean_fake_sendmail &&
        git config sendemail.assume8bitEncoding "bogus too" &&
        echo bogus |
@@ -970,6 +1103,7 @@ test_expect_success '--8bit-encoding overrides sendemail.8bitEncoding' '
        test_cmp actual content-type-decl
 '
 
+test_expect_success $PREREQ 'setup expect' '
 cat >email-using-8bit <<EOF
 From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
 Message-Id: <bogus-message-id@example.com>
@@ -979,12 +1113,15 @@ Subject: Dieser Betreff enthält auch einen Umlaut!
 
 Nothing to see here.
 EOF
+'
 
+test_expect_success $PREREQ 'setup expect' '
 cat >expected <<EOF
 Subject: =?UTF-8?q?Dieser=20Betreff=20enth=C3=A4lt=20auch=20einen=20Umlaut!?=
 EOF
+'
 
-test_expect_success '--8bit-encoding also treats subject' '
+test_expect_success $PREREQ '--8bit-encoding also treats subject' '
        clean_fake_sendmail &&
        echo bogus |
        git send-email --from=author@example.com --to=nobody@example.com \
@@ -995,4 +1132,40 @@ test_expect_success '--8bit-encoding also treats subject' '
        test_cmp expected actual
 '
 
+# Note that the patches in this test are deliberately out of order; we
+# want to make sure it works even if the cover-letter is not in the
+# first mail.
+test_expect_success $PREREQ 'refusing to send cover letter template' '
+       clean_fake_sendmail &&
+       rm -fr outdir &&
+       git format-patch --cover-letter -2 -o outdir &&
+       test_must_fail git send-email \
+         --from="Example <nobody@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         outdir/0002-*.patch \
+         outdir/0000-*.patch \
+         outdir/0001-*.patch \
+         2>errors >out &&
+       grep "SUBJECT HERE" errors &&
+       test -z "$(ls msgtxt*)"
+'
+
+test_expect_success $PREREQ '--force sends cover letter template anyway' '
+       clean_fake_sendmail &&
+       rm -fr outdir &&
+       git format-patch --cover-letter -2 -o outdir &&
+       git send-email \
+         --force \
+         --from="Example <nobody@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         outdir/0002-*.patch \
+         outdir/0000-*.patch \
+         outdir/0001-*.patch \
+         2>errors >out &&
+       ! grep "SUBJECT HERE" errors &&
+       test -n "$(ls msgtxt*)"
+'
+
 test_done
diff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh
new file mode 100755 (executable)
index 0000000..88a9751
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='check svn dumpfile importer'
+
+. ./test-lib.sh
+
+reinit_git () {
+       rm -fr .git &&
+       git init
+}
+
+>empty
+
+test_expect_success 'empty dump' '
+       reinit_git &&
+       echo "SVN-fs-dump-format-version: 2" >input &&
+       test-svn-fe input >stream &&
+       git fast-import <stream
+'
+
+test_expect_success 'v3 dumps not supported' '
+       reinit_git &&
+       echo "SVN-fs-dump-format-version: 3" >input &&
+       test_must_fail test-svn-fe input >stream &&
+       test_cmp empty stream
+'
+
+test_expect_success 'set up svn repo' '
+       svnconf=$PWD/svnconf &&
+       mkdir -p "$svnconf" &&
+
+       if
+               svnadmin -h >/dev/null 2>&1 &&
+               svnadmin create simple-svn &&
+               svnadmin load simple-svn <"$TEST_DIRECTORY/t9135/svn.dump" &&
+               svn export --config-dir "$svnconf" "file://$PWD/simple-svn" simple-svnco
+       then
+               test_set_prereq SVNREPO
+       fi
+'
+
+test_expect_success SVNREPO 't9135/svn.dump' '
+       git init simple-git &&
+       test-svn-fe "$TEST_DIRECTORY/t9135/svn.dump" >simple.fe &&
+       (
+               cd simple-git &&
+               git fast-import <../simple.fe
+       ) &&
+       (
+               cd simple-svnco &&
+               git init &&
+               git add . &&
+               git fetch ../simple-git master &&
+               git diff --exit-code FETCH_HEAD
+       )
+'
+
+test_done
index d5adae640b0b92e049de3c5d5e8996e1db06bcc4..b041516a1d6316dd36657b582c15100c0a7359d0 100755 (executable)
@@ -22,17 +22,18 @@ esac
 test_expect_success \
     'initialize git svn' '
        mkdir import &&
-       cd import &&
-       echo foo > foo &&
-       ln -s foo foo.link
-       mkdir -p dir/a/b/c/d/e &&
-       echo "deep dir" > dir/a/b/c/d/e/file &&
-       mkdir bar &&
-       echo "zzz" > bar/zzz &&
-       echo "#!/bin/sh" > exec.sh &&
-       chmod +x exec.sh &&
-       svn_cmd import -m "import for git svn" . "$svnrepo" >/dev/null &&
-       cd .. &&
+       (
+               cd import &&
+               echo foo >foo &&
+               ln -s foo foo.link
+               mkdir -p dir/a/b/c/d/e &&
+               echo "deep dir" >dir/a/b/c/d/e/file &&
+               mkdir bar &&
+               echo "zzz" >bar/zzz &&
+               echo "#!/bin/sh" >exec.sh &&
+               chmod +x exec.sh &&
+               svn_cmd import -m "import for git svn" . "$svnrepo" >/dev/null
+       ) &&
        rm -rf import &&
        git svn init "$svnrepo"'
 
@@ -142,7 +143,7 @@ test_expect_success "$name" '
        git svn set-tree --find-copies-harder --rmdir \
                ${remotes_git_svn}..mybranch5 &&
        svn_cmd up "$SVN_TREE" &&
-       test -L "$SVN_TREE"/exec.sh'
+       test -h "$SVN_TREE"/exec.sh'
 
 name='new symlink is added to a file that was also just made executable'
 
@@ -155,7 +156,7 @@ test_expect_success "$name" '
                ${remotes_git_svn}..mybranch5 &&
        svn_cmd up "$SVN_TREE" &&
        test -x "$SVN_TREE"/bar/zzz &&
-       test -L "$SVN_TREE"/exec-2.sh'
+       test -h "$SVN_TREE"/exec-2.sh'
 
 name='modify a symlink to become a file'
 test_expect_success "$name" '
@@ -168,7 +169,7 @@ test_expect_success "$name" '
                ${remotes_git_svn}..mybranch5 &&
        svn_cmd up "$SVN_TREE" &&
        test -f "$SVN_TREE"/exec-2.sh &&
-       test ! -L "$SVN_TREE"/exec-2.sh &&
+       test ! -h "$SVN_TREE"/exec-2.sh &&
        test_cmp help "$SVN_TREE"/exec-2.sh'
 
 name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL"
index 929499e996bc33d96ebab0b01ebcac8240e689b7..8869f5018ecd06636dc3e722cb518af88a6919d9 100755 (executable)
@@ -53,8 +53,9 @@ cd ..
 
 rm -rf import
 test_expect_success 'checkout working copy from svn' 'svn co "$svnrepo" test_wc'
-test_expect_success 'setup some commits to svn' \
-       'cd test_wc &&
+test_expect_success 'setup some commits to svn' '
+       (
+               cd test_wc &&
                echo Greetings >> kw.c &&
                poke kw.c &&
                svn_cmd commit -m "Not yet an Id" &&
@@ -63,8 +64,9 @@ test_expect_success 'setup some commits to svn' \
                svn_cmd commit -m "Modified file, but still not yet an Id" &&
                svn_cmd propset svn:keywords Id kw.c &&
                poke kw.c &&
-               svn_cmd commit -m "Propset Id" &&
-       cd ..'
+               svn_cmd commit -m "Propset Id"
+       )
+'
 
 test_expect_success 'initialize git svn' 'git svn init "$svnrepo"'
 test_expect_success 'fetch revisions from svn' 'git svn fetch'
@@ -81,13 +83,15 @@ expect='/* $Id$ */'
 got="`sed -ne 2p kw.c`"
 test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'"
 
-test_expect_success "propset CR on crlf files" \
-       'cd test_wc &&
+test_expect_success "propset CR on crlf files" '
+       (
+               cd test_wc &&
                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 ..'
+               svn_cmd commit -m "propset CR on crlf files"
+        )
+'
 
 test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
        'git svn fetch &&
@@ -137,19 +141,20 @@ cat > show-ignore.expect <<\EOF
 EOF
 
 test_expect_success 'test show-ignore' "
-       cd test_wc &&
-       mkdir -p deeply/nested/directory &&
-       touch deeply/nested/directory/.keep &&
-       svn_cmd add deeply &&
-       svn_cmd up &&
-       svn_cmd propset -R svn:ignore '
+       (
+               cd test_wc &&
+               mkdir -p deeply/nested/directory &&
+               touch deeply/nested/directory/.keep &&
+               svn_cmd add deeply &&
+               svn_cmd up &&
+               svn_cmd propset -R svn:ignore '
 no-such-file*
 ' .
-       svn_cmd commit -m 'propset svn:ignore'
-       cd .. &&
+               svn_cmd commit -m 'propset svn:ignore'
+       ) &&
        git svn show-ignore > show-ignore.got &&
        cmp show-ignore.expect show-ignore.got
-       "
+"
 
 cat >create-ignore.expect <<\EOF
 /no-such-file*
index 028fb19e09bbb9e2720e95a5e0b4ddb221f4a0e1..eb70f4839ccb065425b5ad8d40b270178dedef1b 100755 (executable)
@@ -4,13 +4,14 @@ test_description='git svn rmdir'
 
 test_expect_success 'initialize repo' '
        mkdir import &&
-       cd import &&
-       mkdir -p deeply/nested/directory/number/1 &&
-       mkdir -p deeply/nested/directory/number/2 &&
-       echo foo > deeply/nested/directory/number/1/file &&
-       echo foo > deeply/nested/directory/number/2/another &&
-       svn_cmd import -m "import for git svn" . "$svnrepo" &&
-       cd ..
+       (
+               cd import &&
+               mkdir -p deeply/nested/directory/number/1 &&
+               mkdir -p deeply/nested/directory/number/2 &&
+               echo foo >deeply/nested/directory/number/1/file &&
+               echo foo >deeply/nested/directory/number/2/another &&
+               svn_cmd import -m "import for git svn" . "$svnrepo"
+       )
        '
 
 test_expect_success 'mirror via git svn' '
index bbfd7f4793ab41118e7060bf7c0512309f81a546..13b179e721ef12993ce2bc8f0f59d9f49940447c 100755 (executable)
@@ -8,22 +8,24 @@ test_description='git svn fetching'
 
 test_expect_success 'initialize repo' '
        mkdir import &&
-       cd import &&
-       mkdir -p trunk &&
-       echo hello > trunk/readme &&
-       svn_cmd import -m "initial" . "$svnrepo" &&
-       cd .. &&
+       (
+               cd import &&
+               mkdir -p trunk &&
+               echo hello >trunk/readme &&
+               svn_cmd import -m "initial" . "$svnrepo"
+       ) &&
        svn_cmd co "$svnrepo" wc &&
-       cd wc &&
-       echo world >> trunk/readme &&
-       poke trunk/readme &&
-       svn_cmd commit -m "another commit" &&
-       svn_cmd up &&
-       svn_cmd mv trunk thunk &&
-       echo goodbye >> thunk/readme &&
-       poke thunk/readme &&
-       svn_cmd commit -m "bye now" &&
-       cd ..
+       (
+               cd wc &&
+               echo world >>trunk/readme &&
+               poke trunk/readme &&
+               svn_cmd commit -m "another commit" &&
+               svn_cmd up &&
+               svn_cmd mv trunk thunk &&
+               echo goodbye >>thunk/readme &&
+               poke thunk/readme &&
+               svn_cmd commit -m "bye now"
+       )
        '
 
 test_expect_success 'init and fetch a moved directory' '
@@ -83,16 +85,17 @@ test_expect_success 'follow larger parent' '
         '
 
 test_expect_success 'follow higher-level parent' '
-        svn mkdir -m "follow higher-level parent" "$svnrepo"/blob &&
-        svn co "$svnrepo"/blob blob &&
-        cd blob &&
-                echo hi > hi &&
-                svn add hi &&
-                svn commit -m "hihi" &&
-                cd ..
-        svn mkdir -m "new glob at top level" "$svnrepo"/glob &&
-        svn mv -m "move blob down a level" "$svnrepo"/blob "$svnrepo"/glob/blob &&
-        git svn init --minimize-url -i blob "$svnrepo"/glob/blob &&
+       svn mkdir -m "follow higher-level parent" "$svnrepo"/blob &&
+       svn co "$svnrepo"/blob blob &&
+       (
+               cd blob &&
+               echo hi > hi &&
+               svn add hi &&
+               svn commit -m "hihi"
+       ) &&
+       svn mkdir -m "new glob at top level" "$svnrepo"/glob &&
+       svn mv -m "move blob down a level" "$svnrepo"/blob "$svnrepo"/glob/blob &&
+       git svn init --minimize-url -i blob "$svnrepo"/glob/blob &&
         git svn fetch -i blob
         '
 
@@ -117,18 +120,23 @@ test_expect_success 'follow-parent avoids deleting relevant info' '
           import/trunk/subversion/bindings/swig/perl/t/larger-parent &&
          echo "bad delete test 2" > \
           import/trunk/subversion/bindings/swig/perl/another-larger &&
-       cd import &&
-         svn import -m "r9270 test" . "$svnrepo"/r9270 &&
-       cd .. &&
+       (
+               cd import &&
+               svn import -m "r9270 test" . "$svnrepo"/r9270
+       ) &&
        svn_cmd co "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl r9270 &&
-       cd r9270 &&
-         svn mkdir native &&
-         svn mv t native/t &&
-         for i in a b c; do svn mv $i.pm native/$i.pm; done &&
-         echo z >> native/t/c.t &&
-         poke native/t/c.t &&
-         svn commit -m "reorg test" &&
-       cd .. &&
+       (
+               cd r9270 &&
+               svn mkdir native &&
+               svn mv t native/t &&
+               for i in a b c
+               do
+                       svn mv $i.pm native/$i.pm
+               done &&
+               echo z >>native/t/c.t &&
+               poke native/t/c.t &&
+               svn commit -m "reorg test"
+       ) &&
        git svn init --minimize-url -i r9270-t \
          "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl/native/t &&
        git svn fetch -i r9270-t &&
@@ -182,7 +190,7 @@ test_expect_success "follow-parent is atomic" '
        git svn init --minimize-url -i stunk "$svnrepo"/stunk &&
        git svn fetch -i stunk &&
        git svn init --minimize-url -i flunked "$svnrepo"/flunked &&
-       git svn fetch -i flunked
+       git svn fetch -i flunked &&
        test "`git rev-parse --verify refs/remotes/flunk@18`" \
           = "`git rev-parse --verify refs/remotes/stunk`" &&
        test "`git rev-parse --verify refs/remotes/flunk~1`" \
index dd48e9cba832a06a34fd1e2c895bb1b19792168f..5d0afeae6caf03ca34d7ef883baed512bcbcec51 100755 (executable)
@@ -6,10 +6,11 @@ test_description='git svn commit-diff'
 
 test_expect_success 'initialize repo' '
        mkdir import &&
-       cd import &&
-       echo hello > readme &&
-       svn_cmd import -m "initial" . "$svnrepo" &&
-       cd .. &&
+       (
+               cd import &&
+               echo hello >readme &&
+               svn_cmd import -m "initial" . "$svnrepo"
+       ) &&
        echo hello > readme &&
        git update-index --add readme &&
        git commit -a -m "initial" &&
index 12f21b700ec5216b6aeaa886f862d3e3b1f6682f..f6d7ac7c5f67871ce332716561ea6aba7a6f920f 100755 (executable)
@@ -6,21 +6,23 @@ test_description='git svn commit-diff clobber'
 
 test_expect_success 'initialize repo' '
        mkdir import &&
-       cd import &&
-       echo initial > file &&
-       svn_cmd import -m "initial" . "$svnrepo" &&
-       cd .. &&
+       (
+               cd import &&
+               echo initial >file &&
+               svn_cmd import -m "initial" . "$svnrepo"
+       ) &&
        echo initial > file &&
        git update-index --add file &&
        git commit -a -m "initial"
        '
 test_expect_success 'commit change from svn side' '
        svn_cmd co "$svnrepo" t.svn &&
-       cd t.svn &&
-       echo second line from svn >> file &&
-       poke file &&
-       svn_cmd commit -m "second line from svn" &&
-       cd .. &&
+       (
+               cd t.svn &&
+               echo second line from svn >>file &&
+               poke file &&
+               svn_cmd commit -m "second line from svn"
+       ) &&
        rm -rf t.svn
        '
 
@@ -44,11 +46,12 @@ test_expect_success 'dcommit fails to commit because of conflict' '
        git svn fetch &&
        git reset --hard refs/${remotes_git_svn} &&
        svn_cmd co "$svnrepo" t.svn &&
-       cd t.svn &&
-       echo fourth line from svn >> file &&
-       poke file &&
-       svn_cmd commit -m "fourth line from svn" &&
-       cd .. &&
+       (
+               cd t.svn &&
+               echo fourth line from svn >>file &&
+               poke file &&
+               svn_cmd commit -m "fourth line from svn"
+       ) &&
        rm -rf t.svn &&
        echo "fourth line from git" >> file &&
        git commit -a -m "fourth line from git" &&
@@ -68,11 +71,12 @@ test_expect_success 'dcommit does the svn equivalent of an index merge' "
 
 test_expect_success 'commit another change from svn side' '
        svn_cmd co "$svnrepo" t.svn &&
-       cd t.svn &&
-               echo third line from svn >> file &&
+       (
+               cd t.svn &&
+               echo third line from svn >>file &&
                poke file &&
-               svn_cmd commit -m "third line from svn" &&
-       cd .. &&
+               svn_cmd commit -m "third line from svn"
+       ) &&
        rm -rf t.svn
        '
 
index 901b8e09fbc6660456311c04b4b88c20e108c313..289fc313fb737ac7895580fd8407cff758b085e7 100755 (executable)
@@ -6,14 +6,16 @@ test_description='git svn metadata migrations from previous versions'
 test_expect_success 'setup old-looking metadata' '
        cp "$GIT_DIR"/config "$GIT_DIR"/config-old-git-svn &&
        mkdir import &&
-       cd import &&
-               for i in trunk branches/a branches/b \
-                        tags/0.1 tags/0.2 tags/0.3; do
-                       mkdir -p $i && \
-                       echo hello >> $i/README || exit 1
-               done && \
+       (
+               cd import &&
+               for i in trunk branches/a branches/b tags/0.1 tags/0.2 tags/0.3
+               do
+                       mkdir -p $i &&
+                       echo hello >>$i/README ||
+                       exit 1
+               done &&
                svn_cmd import -m test . "$svnrepo"
-               cd .. &&
+       ) &&
        git svn init "$svnrepo" &&
        git svn fetch &&
        rm -rf "$GIT_DIR"/svn &&
index 84f7c9b4bb00931b57740be32ce7ee29bbb26694..3077851015879fc147ded9bb573d02c13967eec9 100755 (executable)
@@ -37,11 +37,12 @@ EOF
 test_expect_success 'setup svn repository' '
        svn_cmd co "$svnrepo" mysvnwork &&
        mkdir -p mysvnwork/trunk &&
-       cd mysvnwork &&
-               big_text_block >> trunk/README &&
+       (
+               cd mysvnwork &&
+               big_text_block >>trunk/README &&
                svn_cmd add trunk &&
-               svn_cmd ci -m "first commit" trunk &&
-               cd ..
+               svn_cmd ci -m "first commit" trunk
+       )
        '
 
 test_expect_success 'setup git mirror and merge' '
index 767799e7a70b91ef6f4e3f4007529f1cb0fd919c..6a48e4042996549e40843186783e2215beee9cca 100755 (executable)
@@ -61,11 +61,12 @@ test_expect_success 'add a file with plus signs' '
 
 test_expect_success 'clone the repository to test rebase' '
        git svn clone "$svnrepo" test-rebase &&
-       cd test-rebase &&
-               echo test-rebase > test-rebase &&
+       (
+               cd test-rebase &&
+               echo test-rebase >test-rebase &&
                git add test-rebase &&
-               git commit -m test-rebase &&
-               cd ..
+               git commit -m test-rebase
+       )
        '
 
 test_expect_success 'make a commit to test rebase' '
index 0374a7476bcc5b254aa1464d468434ae3205f114..5d477e4bdadd2168d0331e2ab809c5c86c8fa532 100755 (executable)
@@ -8,14 +8,16 @@ test_description='git svn log tests'
 
 test_expect_success 'setup repository and import' '
        mkdir import &&
-       cd import &&
-               for i in trunk branches/a branches/b \
-                        tags/0.1 tags/0.2 tags/0.3; do
-                       mkdir -p $i && \
-                       echo hello >> $i/README || exit 1
-               done && \
+       (
+               cd import &&
+               for i in trunk branches/a branches/b tags/0.1 tags/0.2 tags/0.3
+               do
+                       mkdir -p $i &&
+                       echo hello >>$i/README ||
+                       exit 1
+               done &&
                svn_cmd import -m test . "$svnrepo"
-               cd .. &&
+       ) &&
        git svn init "$svnrepo" -T trunk -b branches -t tags &&
        git svn fetch &&
        git reset --hard trunk &&
index 5fb94fb3d61c2e26dcd448ac5106ec0f9a3a2283..ff19695e776f803aef03a18e70067c89aa3be218 100755 (executable)
@@ -18,58 +18,50 @@ case $v in
        ;;
 esac
 
-ptouch() {
-       perl -w -e '
-               use strict;
-               use POSIX qw(mktime);
-               die "ptouch requires exactly 2 arguments" if @ARGV != 2;
-               my $text_last_updated = shift @ARGV;
-               my $git_file = shift @ARGV;
-               die "\"$git_file\" does not exist" if ! -e $git_file;
-               if ($text_last_updated
-                   =~ /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/) {
-                       my $mtime = mktime($6, $5, $4, $3, $2 - 1, $1 - 1900);
-                       my $atime = $mtime;
-                       utime $atime, $mtime, $git_file;
-               }
-       ' "`svn_cmd info $2 | grep '^Text Last Updated:'`" "$1"
+# On the "Text Last Updated" line, "git svn info" does not return the
+# same value as "svn info" (i.e. the commit timestamp that touched the
+# path most recently); do not expect that field to match.
+test_cmp_info () {
+       sed -e '/^Text Last Updated:/d' "$1" >tmp.expect
+       sed -e '/^Text Last Updated:/d' "$2" >tmp.actual
+       test_cmp tmp.expect tmp.actual &&
+       rm -f tmp.expect tmp.actual
 }
 
 quoted_svnrepo="$(echo $svnrepo | sed 's/ /%20/')"
 
 test_expect_success 'setup repository and import' '
        mkdir info &&
-       cd info &&
-               echo FIRST > A &&
-               echo one > file &&
+       (
+               cd info &&
+               echo FIRST >A &&
+               echo one >file &&
                ln -s file symlink-file &&
                mkdir directory &&
                touch directory/.placeholder &&
                ln -s directory symlink-directory &&
-               svn_cmd import -m "initial" . "$svnrepo" &&
-       cd .. &&
+               svn_cmd import -m "initial" . "$svnrepo"
+       ) &&
        svn_cmd co "$svnrepo" svnwc &&
-       cd svnwc &&
-               echo foo > foo &&
+       (
+               cd svnwc &&
+               echo foo >foo &&
                svn_cmd add foo &&
                svn_cmd commit -m "change outside directory" &&
-               svn_cmd update &&
-       cd .. &&
+               svn_cmd update
+       ) &&
        mkdir gitwc &&
-       cd gitwc &&
+       (
+               cd gitwc &&
                git svn init "$svnrepo" &&
-               git svn fetch &&
-       cd .. &&
-       ptouch gitwc/file svnwc/file &&
-       ptouch gitwc/directory svnwc/directory &&
-       ptouch gitwc/symlink-file svnwc/symlink-file &&
-       ptouch gitwc/symlink-directory svnwc/symlink-directory
+               git svn fetch
+       )
        '
 
 test_expect_success 'info' "
        (cd svnwc; svn info) > expected.info &&
        (cd gitwc; git svn info) > actual.info &&
-       test_cmp expected.info actual.info
+       test_cmp_info expected.info actual.info
        "
 
 test_expect_success 'info --url' '
@@ -79,7 +71,7 @@ test_expect_success 'info --url' '
 test_expect_success 'info .' "
        (cd svnwc; svn info .) > expected.info-dot &&
        (cd gitwc; git svn info .) > actual.info-dot &&
-       test_cmp expected.info-dot actual.info-dot
+       test_cmp_info expected.info-dot actual.info-dot
        "
 
 test_expect_success 'info --url .' '
@@ -89,7 +81,7 @@ test_expect_success 'info --url .' '
 test_expect_success 'info file' "
        (cd svnwc; svn info file) > expected.info-file &&
        (cd gitwc; git svn info file) > actual.info-file &&
-       test_cmp expected.info-file actual.info-file
+       test_cmp_info expected.info-file actual.info-file
        "
 
 test_expect_success 'info --url file' '
@@ -99,13 +91,13 @@ test_expect_success 'info --url file' '
 test_expect_success 'info directory' "
        (cd svnwc; svn info directory) > expected.info-directory &&
        (cd gitwc; git svn info directory) > actual.info-directory &&
-       test_cmp expected.info-directory actual.info-directory
+       test_cmp_info expected.info-directory actual.info-directory
        "
 
 test_expect_success 'info inside directory' "
        (cd svnwc/directory; svn info) > expected.info-inside-directory &&
        (cd gitwc/directory; git svn info) > actual.info-inside-directory &&
-       test_cmp expected.info-inside-directory actual.info-inside-directory
+       test_cmp_info expected.info-inside-directory actual.info-inside-directory
        "
 
 test_expect_success 'info --url directory' '
@@ -115,7 +107,7 @@ test_expect_success 'info --url directory' '
 test_expect_success 'info symlink-file' "
        (cd svnwc; svn info symlink-file) > expected.info-symlink-file &&
        (cd gitwc; git svn info symlink-file) > actual.info-symlink-file &&
-       test_cmp expected.info-symlink-file actual.info-symlink-file
+       test_cmp_info expected.info-symlink-file actual.info-symlink-file
        "
 
 test_expect_success 'info --url symlink-file' '
@@ -128,7 +120,7 @@ test_expect_success 'info symlink-directory' "
                > expected.info-symlink-directory &&
        (cd gitwc; git svn info symlink-directory) \
                > actual.info-symlink-directory &&
-       test_cmp expected.info-symlink-directory actual.info-symlink-directory
+       test_cmp_info expected.info-symlink-directory actual.info-symlink-directory
        "
 
 test_expect_success 'info --url symlink-directory' '
@@ -138,17 +130,18 @@ test_expect_success 'info --url symlink-directory' '
 
 test_expect_success 'info added-file' "
        echo two > gitwc/added-file &&
-       cd gitwc &&
-               git add added-file &&
-       cd .. &&
+       (
+               cd gitwc &&
+               git add added-file
+       ) &&
        cp gitwc/added-file svnwc/added-file &&
-       ptouch gitwc/added-file svnwc/added-file &&
-       cd svnwc &&
-               svn_cmd add added-file > /dev/null &&
-       cd .. &&
+       (
+               cd svnwc &&
+               svn_cmd add added-file > /dev/null
+       ) &&
        (cd svnwc; svn info added-file) > expected.info-added-file &&
        (cd gitwc; git svn info added-file) > actual.info-added-file &&
-       test_cmp expected.info-added-file actual.info-added-file
+       test_cmp_info expected.info-added-file actual.info-added-file
        "
 
 test_expect_success 'info --url added-file' '
@@ -158,19 +151,20 @@ test_expect_success 'info --url added-file' '
 
 test_expect_success 'info added-directory' "
        mkdir gitwc/added-directory svnwc/added-directory &&
-       ptouch gitwc/added-directory svnwc/added-directory &&
        touch gitwc/added-directory/.placeholder &&
-       cd svnwc &&
-               svn_cmd add added-directory > /dev/null &&
-       cd .. &&
-       cd gitwc &&
-               git add added-directory &&
-       cd .. &&
+       (
+               cd svnwc &&
+               svn_cmd add added-directory > /dev/null
+       ) &&
+       (
+               cd gitwc &&
+               git add added-directory
+       ) &&
        (cd svnwc; svn info added-directory) \
                > expected.info-added-directory &&
        (cd gitwc; git svn info added-directory) \
                > actual.info-added-directory &&
-       test_cmp expected.info-added-directory actual.info-added-directory
+       test_cmp_info expected.info-added-directory actual.info-added-directory
        "
 
 test_expect_success 'info --url added-directory' '
@@ -179,21 +173,22 @@ test_expect_success 'info --url added-directory' '
        '
 
 test_expect_success 'info added-symlink-file' "
-       cd gitwc &&
+       (
+               cd gitwc &&
                ln -s added-file added-symlink-file &&
-               git add added-symlink-file &&
-       cd .. &&
-       cd svnwc &&
+               git add added-symlink-file
+       ) &&
+       (
+               cd svnwc &&
                ln -s added-file added-symlink-file &&
-               svn_cmd add added-symlink-file > /dev/null &&
-       cd .. &&
-       ptouch gitwc/added-symlink-file svnwc/added-symlink-file &&
+               svn_cmd add added-symlink-file > /dev/null
+       ) &&
        (cd svnwc; svn info added-symlink-file) \
                > expected.info-added-symlink-file &&
        (cd gitwc; git svn info added-symlink-file) \
                > actual.info-added-symlink-file &&
-       test_cmp expected.info-added-symlink-file \
-                actual.info-added-symlink-file
+       test_cmp_info expected.info-added-symlink-file \
+               actual.info-added-symlink-file
        "
 
 test_expect_success 'info --url added-symlink-file' '
@@ -202,21 +197,22 @@ test_expect_success 'info --url added-symlink-file' '
        '
 
 test_expect_success 'info added-symlink-directory' "
-       cd gitwc &&
+       (
+               cd gitwc &&
                ln -s added-directory added-symlink-directory &&
-               git add added-symlink-directory &&
-       cd .. &&
-       cd svnwc &&
+               git add added-symlink-directory
+       ) &&
+       (
+               cd svnwc &&
                ln -s added-directory added-symlink-directory &&
-               svn_cmd add added-symlink-directory > /dev/null &&
-       cd .. &&
-       ptouch gitwc/added-symlink-directory svnwc/added-symlink-directory &&
+               svn_cmd add added-symlink-directory > /dev/null
+       ) &&
        (cd svnwc; svn info added-symlink-directory) \
                > expected.info-added-symlink-directory &&
        (cd gitwc; git svn info added-symlink-directory) \
                > actual.info-added-symlink-directory &&
-       test_cmp expected.info-added-symlink-directory \
-                actual.info-added-symlink-directory
+       test_cmp_info expected.info-added-symlink-directory \
+               actual.info-added-symlink-directory
        "
 
 test_expect_success 'info --url added-symlink-directory' '
@@ -224,25 +220,18 @@ test_expect_success 'info --url added-symlink-directory' '
             = "$quoted_svnrepo/added-symlink-directory"
        '
 
-# The next few tests replace the "Text Last Updated" value with a
-# placeholder since git doesn't have a way to know the date that a
-# now-deleted file was last checked out locally.  Internally it
-# simply reuses the Last Changed Date.
-
 test_expect_success 'info deleted-file' "
-       cd gitwc &&
-               git rm -f file > /dev/null &&
-       cd .. &&
-       cd svnwc &&
-               svn_cmd rm --force file > /dev/null &&
-       cd .. &&
-       (cd svnwc; svn info file) |
-       sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-               > expected.info-deleted-file &&
-       (cd gitwc; git svn info file) |
-       sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-               > actual.info-deleted-file &&
-       test_cmp expected.info-deleted-file actual.info-deleted-file
+       (
+               cd gitwc &&
+               git rm -f file > /dev/null
+       ) &&
+       (
+               cd svnwc &&
+               svn_cmd rm --force file > /dev/null
+       ) &&
+       (cd svnwc; svn info file) >expected.info-deleted-file &&
+       (cd gitwc; git svn info file) >actual.info-deleted-file &&
+       test_cmp_info expected.info-deleted-file actual.info-deleted-file
        "
 
 test_expect_success 'info --url file (deleted)' '
@@ -251,19 +240,17 @@ test_expect_success 'info --url file (deleted)' '
        '
 
 test_expect_success 'info deleted-directory' "
-       cd gitwc &&
-               git rm -r -f directory > /dev/null &&
-       cd .. &&
-       cd svnwc &&
-               svn_cmd rm --force directory > /dev/null &&
-       cd .. &&
-       (cd svnwc; svn info directory) |
-       sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-               > expected.info-deleted-directory &&
-       (cd gitwc; git svn info directory) |
-       sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-               > actual.info-deleted-directory &&
-       test_cmp expected.info-deleted-directory actual.info-deleted-directory
+       (
+               cd gitwc &&
+               git rm -r -f directory > /dev/null
+       ) &&
+       (
+               cd svnwc &&
+               svn_cmd rm --force directory > /dev/null
+       ) &&
+       (cd svnwc; svn info directory) >expected.info-deleted-directory &&
+       (cd gitwc; git svn info directory) >actual.info-deleted-directory &&
+       test_cmp_info expected.info-deleted-directory actual.info-deleted-directory
        "
 
 test_expect_success 'info --url directory (deleted)' '
@@ -272,20 +259,17 @@ test_expect_success 'info --url directory (deleted)' '
        '
 
 test_expect_success 'info deleted-symlink-file' "
-       cd gitwc &&
-               git rm -f symlink-file > /dev/null &&
-       cd .. &&
-       cd svnwc &&
-               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/' \
-               > expected.info-deleted-symlink-file &&
-       (cd gitwc; git svn info symlink-file) |
-       sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-               > actual.info-deleted-symlink-file &&
-       test_cmp expected.info-deleted-symlink-file \
-                actual.info-deleted-symlink-file
+       (
+               cd gitwc &&
+               git rm -f symlink-file > /dev/null
+       ) &&
+       (
+               cd svnwc &&
+               svn_cmd rm --force symlink-file > /dev/null
+       ) &&
+       (cd svnwc; svn info symlink-file) >expected.info-deleted-symlink-file &&
+       (cd gitwc; git svn info symlink-file) >actual.info-deleted-symlink-file &&
+       test_cmp_info expected.info-deleted-symlink-file actual.info-deleted-symlink-file
        "
 
 test_expect_success 'info --url symlink-file (deleted)' '
@@ -294,20 +278,17 @@ test_expect_success 'info --url symlink-file (deleted)' '
        '
 
 test_expect_success 'info deleted-symlink-directory' "
-       cd gitwc &&
-               git rm -f symlink-directory > /dev/null &&
-       cd .. &&
-       cd svnwc &&
-               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/' \
-                > expected.info-deleted-symlink-directory &&
-       (cd gitwc; git svn info symlink-directory) |
-       sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
-                > actual.info-deleted-symlink-directory &&
-       test_cmp expected.info-deleted-symlink-directory \
-                actual.info-deleted-symlink-directory
+       (
+               cd gitwc &&
+               git rm -f symlink-directory > /dev/null
+       ) &&
+       (
+               cd svnwc &&
+               svn_cmd rm --force symlink-directory > /dev/null
+       ) &&
+       (cd svnwc; svn info symlink-directory) >expected.info-deleted-symlink-directory &&
+       (cd gitwc; git svn info symlink-directory) >actual.info-deleted-symlink-directory &&
+       test_cmp_info expected.info-deleted-symlink-directory actual.info-deleted-symlink-directory
        "
 
 test_expect_success 'info --url symlink-directory (deleted)' '
@@ -346,9 +327,10 @@ test_expect_success 'info --url unknown-directory' '
        '
 
 test_expect_success 'info unknown-symlink-file' "
-       cd gitwc &&
-               ln -s unknown-file unknown-symlink-file &&
-       cd .. &&
+       (
+               cd gitwc &&
+               ln -s unknown-file unknown-symlink-file
+       ) &&
        (cd gitwc; test_must_fail git svn info unknown-symlink-file) \
                 2> actual.info-unknown-symlink-file &&
        grep unknown-symlink-file actual.info-unknown-symlink-file
@@ -361,9 +343,10 @@ test_expect_success 'info --url unknown-symlink-file' '
        '
 
 test_expect_success 'info unknown-symlink-directory' "
-       cd gitwc &&
-               ln -s unknown-directory unknown-symlink-directory &&
-       cd .. &&
+       (
+               cd gitwc &&
+               ln -s unknown-directory unknown-symlink-directory
+       ) &&
        (cd gitwc; test_must_fail git svn info unknown-symlink-directory) \
                 2> actual.info-unknown-symlink-directory &&
        grep unknown-symlink-directory actual.info-unknown-symlink-directory
index 9d9ebd533cbcbcfc1e23be51bad1e63f4f08c53b..1d92c05035f8fe68885a6338ceca4307f19e8663 100755 (executable)
@@ -20,9 +20,10 @@ test_expect_success 'setup svnrepo' '
 
 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 ..
+       (
+               cd clone &&
+               git rev-parse refs/${remotes_git_svn}
+       )
 '
 
 # SVN works either way, so should we...
index 045521615c64d1ae938524b0424e1e4260ca8c54..fd8184787fba13bbbf3f1ef377ad3f979ef8f47a 100755 (executable)
@@ -8,15 +8,15 @@ test_description='git svn respects rewriteRoot during rebuild'
 . ./lib-git-svn.sh
 
 mkdir import
-cd import
+(cd import
        touch foo
        svn_cmd import -m 'import for git svn' . "$svnrepo" >/dev/null
-cd ..
+)
 rm -rf import
 
 test_expect_success 'init, fetch and checkout repository' '
        git svn init --rewrite-root=http://invalid.invalid/ "$svnrepo" &&
-       git svn fetch
+       git svn fetch &&
        git checkout -b mybranch ${remotes_git_svn}
        '
 
index d6b076f6b7de148c8d548c7a977e4f09b4be8e3b..aa841e12996aad8cd7284eea56c47f0d89c79c56 100755 (executable)
@@ -24,7 +24,7 @@ test_expect_success 'initialize git svn' '
                svn_cmd import -m "import for git svn" . "$svnrepo"
        ) &&
        rm -rf import &&
-       git svn init "$svnrepo"
+       git svn init "$svnrepo" &&
        git svn fetch
 '
 
index c19418614fc8946b5be5f0014559e53fcc15c060..096abd1fe5e37444a442e698a5d5734823a45798 100755 (executable)
@@ -19,19 +19,19 @@ test_expect_success 'setup svnrepo' '
 test_expect_success 'test clone with multi-glob in branch names' '
        git svn clone -T trunk -b branches/*/* -t tags \
                      "$svnrepo/project" project &&
-       cd project &&
+       (cd project &&
                git rev-parse "refs/remotes/v14.1/beta" &&
-               git rev-parse "refs/remotes/v14.1/gold" &&
-       cd ..
+               git rev-parse "refs/remotes/v14.1/gold"
+       )
        '
 
 test_expect_success 'test dcommit to multi-globbed branch' "
-       cd project &&
+       (cd project &&
        git reset --hard 'refs/remotes/v14.1/gold' &&
        echo hello >> foo &&
        git commit -m 'hello' -- foo &&
-       git svn dcommit &&
-       cd ..
+       git svn dcommit
+       )
        "
 
 test_done
index 4aab8ecc142d9719fd83b064445fa297cc10a520..2e4789d061fe084024b621708ee27e629c2a004a 100755 (executable)
@@ -9,27 +9,27 @@ test_description='git svn partial-rebuild tests'
 test_expect_success 'initialize svnrepo' '
        mkdir import &&
        (
-               cd import &&
+               (cd import &&
                mkdir trunk branches tags &&
-               cd trunk &&
-               echo foo > foo &&
-               cd .. &&
+               (cd trunk &&
+               echo foo > foo
+               ) &&
                svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null &&
                svn_cmd copy "$svnrepo"/trunk "$svnrepo"/branches/a \
-                       -m "created branch a" &&
-               cd .. &&
+                       -m "created branch a"
+               ) &&
                rm -rf import &&
                svn_cmd co "$svnrepo"/trunk trunk &&
-               cd trunk &&
+               (cd trunk &&
                echo bar >> foo &&
-               svn_cmd ci -m "updated trunk" &&
-               cd .. &&
+               svn_cmd ci -m "updated trunk"
+               ) &&
                svn_cmd co "$svnrepo"/branches/a a &&
-               cd a &&
+               (cd a &&
                echo baz >> a &&
                svn_cmd add a &&
-               svn_cmd ci -m "updated a" &&
-               cd .. &&
+               svn_cmd ci -m "updated a"
+               ) &&
                git svn init --stdlayout "$svnrepo"
        )
 '
@@ -41,11 +41,11 @@ test_expect_success 'import an early SVN revision into git' '
 test_expect_success 'make full git mirror of SVN' '
        mkdir mirror &&
        (
-               cd mirror &&
+               (cd mirror &&
                git init &&
                git svn init --stdlayout "$svnrepo" &&
-               git svn fetch &&
-               cd ..
+               git svn fetch
+               )
        )
 '
 
index 807e494a3af4eb889711cbd7bb8d20c160557301..4b034a67f305622f146e66914139e58045e2457e 100755 (executable)
@@ -9,19 +9,19 @@ test_description='git svn partial-rebuild tests'
 test_expect_success 'initialize svnrepo' '
        mkdir import &&
        (
-               cd import &&
+               (cd import &&
                mkdir trunk branches tags &&
-               cd trunk &&
-               echo foo > foo &&
-               cd .. &&
-               svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null &&
-               cd .. &&
+               (cd trunk &&
+               echo foo > foo
+               ) &&
+               svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null
+               ) &&
                rm -rf import &&
                svn_cmd co "$svnrepo"/trunk trunk &&
-               cd trunk &&
+               (cd trunk &&
                echo bar >> foo &&
-               svn_cmd ci -m "updated trunk" &&
-               cd .. &&
+               svn_cmd ci -m "updated trunk"
+               ) &&
                rm -rf trunk
        )
 '
index 3c4f31925fe0498717b56ff95cf42a9a7450a236..ec0a10661409d24c77bf2061bd8a554bfb565d74 100755 (executable)
@@ -95,8 +95,6 @@ test_expect_success 'fresh clone with svn.authors-file in config' '
        (
                rm -r "$GIT_DIR" &&
                test x = x"$(git config svn.authorsfile)" &&
-               HOME="`pwd`" &&
-               export HOME &&
                test_config="$HOME"/.gitconfig &&
                unset GIT_CONFIG_NOGLOBAL &&
                unset GIT_DIR &&
index 9a24a65b64111b4540db28315fedd4efd3b19ae7..f762038f0ea95ce0f9d411ff6f494088ee1fc22c 100755 (executable)
@@ -88,7 +88,7 @@ test_expect_success 'enable broken symlink workaround' \
 test_expect_success '"bar" is an empty file' 'test -f x/bar && ! test -s x/bar'
 test_expect_success 'get "bar" => symlink fix from svn' \
                '(cd x && git svn rebase)'
-test_expect_success SYMLINKS '"bar" becomes a symlink' 'test -L x/bar'
+test_expect_success SYMLINKS '"bar" becomes a symlink' 'test -h x/bar'
 
 
 test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" y'
index 636ca0abb965ad2462a2e1e1ebea2a4183aa19e6..d60da63f7aced3e13a411eee661509ae3790e68f 100755 (executable)
@@ -6,10 +6,10 @@ test_description='git svn dcommit clobber series'
 
 test_expect_success 'initialize repo' '
        mkdir import &&
-       cd import &&
+       (cd import &&
        awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
-       svn_cmd import -m "initial" . "$svnrepo" &&
-       cd .. &&
+       svn_cmd import -m "initial" . "$svnrepo"
+       ) &&
        git svn init "$svnrepo" &&
        git svn fetch &&
        test -e file
@@ -19,14 +19,14 @@ 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_cmd co "$svnrepo" tmp &&
-       cd 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_cmd commit -m "58 => 5588, 61 => 6611" &&
-               cd ..
+               svn_cmd commit -m "58 => 5588, 61 => 6611"
+       )
        '
 
 test_expect_success 'some unrelated changes to git' "
index 1236accd993d5bc1a80fbda720ce229f8e93940c..e21ee5f663ce8333625c5d9d483c42dde3394675 100755 (executable)
@@ -17,11 +17,10 @@ test_expect_success 'setup test repository' '
                > foo &&
                svn_cmd add foo &&
                svn_cmd commit -m "add foo"
-       )
+       ) &&
+       start_httpd
 '
 
-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 )
index 337ea59711ac7aca78a27afb92693f88e98aed9d..4594e1ae2f36b5a5582cdb1f2421fdfbd2f8c996 100755 (executable)
@@ -37,13 +37,11 @@ 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
+if test -r .git/svn/refs/remotes/git-svn/unhandled.log.gz
 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' '
index 565365cbd3ff80d816dd02b2072045cd25ae4b93..158c8e33ef3381f3310ac39a99932d185290d685 100755 (executable)
@@ -33,7 +33,7 @@ test_expect_success 'more emptiness' '
 '
 
 test_expect_success 'git svn rebase creates empty directory' '
-       ( cd cloned && git svn rebase )
+       ( cd cloned && git svn rebase ) &&
        test -d cloned/"! !"
 '
 
index 250c651eaecf60103ee442bcfa2a6c65250320ec..4f6c06ecb2bc8671949326955acf3ff39a364c5d 100755 (executable)
@@ -18,39 +18,39 @@ test_expect_success 'load svn dump' "
 
 test_expect_success 'all svn merges became git merge commits' '
        unmarked=$(git rev-list --parents --all --grep=Merge |
-               grep -v " .* " | cut -f1 -d" ")
+               grep -v " .* " | cut -f1 -d" ") &&
        [ -z "$unmarked" ]
        '
 
 test_expect_success 'cherry picks did not become git merge commits' '
        bad_cherries=$(git rev-list --parents --all --grep=Cherry |
-               grep " .* " | cut -f1 -d" ")
+               grep " .* " | cut -f1 -d" ") &&
        [ -z "$bad_cherries" ]
        '
 
 test_expect_success 'svn non-merge merge commits did not become git merge commits' '
        bad_non_merges=$(git rev-list --parents --all --grep=non-merge |
-               grep " .* " | cut -f1 -d" ")
+               grep " .* " | cut -f1 -d" ") &&
        [ -z "$bad_non_merges" ]
        '
 
 test_expect_success 'commit made to merged branch is reachable from the merge' '
-       before_commit=$(git rev-list --all --grep="trunk commit before merging trunk to b2")
-       merge_commit=$(git rev-list --all --grep="Merge trunk to b2")
-       not_reachable=$(git rev-list -1 $before_commit --not $merge_commit)
+       before_commit=$(git rev-list --all --grep="trunk commit before merging trunk to b2") &&
+       merge_commit=$(git rev-list --all --grep="Merge trunk to b2") &&
+       not_reachable=$(git rev-list -1 $before_commit --not $merge_commit) &&
        [ -z "$not_reachable" ]
        '
 
 test_expect_success 'merging two branches in one commit is detected correctly' '
-       f1_commit=$(git rev-list --all --grep="make f1 branch from trunk")
-       f2_commit=$(git rev-list --all --grep="make f2 branch from trunk")
-       merge_commit=$(git rev-list --all --grep="Merge f1 and f2 to trunk")
-       not_reachable=$(git rev-list -1 $f1_commit $f2_commit --not $merge_commit)
+       f1_commit=$(git rev-list --all --grep="make f1 branch from trunk") &&
+       f2_commit=$(git rev-list --all --grep="make f2 branch from trunk") &&
+       merge_commit=$(git rev-list --all --grep="Merge f1 and f2 to trunk") &&
+       not_reachable=$(git rev-list -1 $f1_commit $f2_commit --not $merge_commit) &&
        [ -z "$not_reachable" ]
        '
 
 test_expect_failure 'everything got merged in the end' '
-       unmerged=$(git rev-list --all --not master)
+       unmerged=$(git rev-list --all --not master) &&
        [ -z "$unmerged" ]
        '
 
diff --git a/t/t9157-git-svn-fetch-merge.sh b/t/t9157-git-svn-fetch-merge.sh
new file mode 100755 (executable)
index 0000000..991d2aa
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Steven Walter
+#
+
+test_description='git svn merge detection'
+. ./lib-git-svn.sh
+
+svn_ver="$(svn --version --quiet)"
+case $svn_ver in
+0.* | 1.[0-4].*)
+       skip_all="skipping git-svn test - SVN too old ($svn_ver)"
+       test_done
+       ;;
+esac
+
+test_expect_success 'initialize source svn repo' '
+       svn_cmd mkdir -m x "$svnrepo"/trunk &&
+       svn_cmd mkdir -m x "$svnrepo"/branches &&
+       svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+       (
+               cd "$SVN_TREE" &&
+               touch foo &&
+               svn add foo &&
+               svn commit -m "initial commit" &&
+               svn cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
+               touch bar &&
+               svn add bar &&
+               svn commit -m x &&
+               svn cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch2 &&
+               svn switch "$svnrepo"/branches/branch1 &&
+               touch baz &&
+               svn add baz &&
+               svn commit -m x &&
+               svn switch "$svnrepo"/trunk &&
+               svn merge "$svnrepo"/branches/branch1 &&
+               svn commit -m "merge" &&
+               svn switch "$svnrepo"/branches/branch1 &&
+               svn commit -m x &&
+               svn switch "$svnrepo"/branches/branch2 &&
+               svn merge "$svnrepo"/branches/branch1 &&
+               svn commit -m "merge branch1" &&
+               svn switch "$svnrepo"/trunk &&
+               svn merge "$svnrepo"/branches/branch2 &&
+               svn resolved baz &&
+               svn commit -m "merge branch2"
+       ) &&
+       rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'clone svn repo' '
+       git svn init -s "$svnrepo" &&
+       git svn fetch
+'
+
+test_expect_success 'verify merge commit' 'git rev-parse HEAD^2'
+
+test_done
diff --git a/t/t9158-git-svn-mergeinfo.sh b/t/t9158-git-svn-mergeinfo.sh
new file mode 100755 (executable)
index 0000000..3ab4390
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Steven Walter
+#
+
+test_description='git svn mergeinfo propagation'
+
+. ./lib-git-svn.sh
+
+say 'define NO_SVN_TESTS to skip git svn tests'
+
+test_expect_success 'initialize source svn repo' '
+       svn_cmd mkdir -m x "$svnrepo"/trunk &&
+       svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+       (
+               cd "$SVN_TREE" &&
+               touch foo &&
+               svn_cmd add foo &&
+               svn_cmd commit -m "initial commit"
+       ) &&
+       rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'clone svn repo' '
+       git svn init "$svnrepo"/trunk &&
+       git svn fetch
+'
+
+test_expect_success 'change svn:mergeinfo' '
+       touch bar &&
+       git add bar &&
+       git commit -m "bar" &&
+       git svn dcommit --mergeinfo="/branches/foo:1-10"
+'
+
+test_expect_success 'verify svn:mergeinfo' '
+       mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/trunk)
+       test "$mergeinfo" = "/branches/foo:1-10"
+'
+
+test_done
index ee39b36d78a9d5602be5d48ae34b1e5ce4927c79..e5da65b99fa1459836e516772fe8d13d038f502e 100755 (executable)
@@ -5,6 +5,7 @@
 test_description='Test export of commits to CVS'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 if ! test_have_prereq PERL; then
        skip_all='skipping git cvsexportcommit tests, perl not available'
@@ -229,11 +230,6 @@ test_expect_success \
       test_must_fail git cvsexportcommit -c $id
       )'
 
-if ! test "$(git config --bool core.filemode)" = false
-then
-       test_set_prereq FILEMODE
-fi
-
 test_expect_success FILEMODE \
      'Retain execute bit' \
      'mkdir G &&
index 96d07f183377f8822257cb3a1dad29023b391462..986bc14d58c30201e75f3d95d9b8245e997b99e9 100755 (executable)
@@ -7,6 +7,23 @@ test_description='test git fast-import utility'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
+# Print $1 bytes from stdin to stdout.
+#
+# This could be written as "head -c $1", but IRIX "head" does not
+# support the -c option.
+head_c () {
+       perl -e '
+               my $len = $ARGV[1];
+               while ($len > 0) {
+                       my $s;
+                       my $nread = sysread(STDIN, $s, $len);
+                       die "cannot read: $!" unless defined($nread);
+                       print $s;
+                       $len -= $nread;
+               }
+       ' - "$1"
+}
+
 file2_data='file2
 second line of EOF'
 
@@ -23,11 +40,18 @@ file5_data='an inline file.
 file6_data='#!/bin/sh
 echo "$@"'
 
+>empty
+
 ###
 ### series A
 ###
 
 test_tick
+
+test_expect_success 'empty stream succeeds' '
+       git fast-import </dev/null
+'
+
 cat >input <<INPUT_END
 blob
 mark :2
@@ -321,7 +345,7 @@ test_expect_success \
        'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
 test_expect_success \
        'C: validate reuse existing blob' \
-       'test $newf = `git rev-parse --verify branch:file2/newf`
+       'test $newf = `git rev-parse --verify branch:file2/newf` &&
         test $oldf = `git rev-parse --verify branch:file2/oldf`'
 
 cat >expect <<EOF
@@ -874,6 +898,69 @@ test_expect_success \
         git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
         compare_diff_raw expect actual'
 
+test_expect_success \
+       'N: copy root directory by tree hash' \
+       'cat >expect <<-\EOF &&
+       :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D      file3/newf
+       :100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D      file3/oldf
+       EOF
+        root=$(git rev-parse refs/heads/branch^0^{tree}) &&
+        cat >input <<-INPUT_END &&
+       commit refs/heads/N6
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy root directory by tree hash
+       COMMIT
+
+       from refs/heads/branch^0
+       M 040000 $root ""
+       INPUT_END
+        git fast-import <input &&
+        git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
+        compare_diff_raw expect actual'
+
+test_expect_success \
+       'N: delete directory by copying' \
+       'cat >expect <<-\EOF &&
+       OBJID
+       :100644 000000 OBJID OBJID D    foo/bar/qux
+       OBJID
+       :000000 100644 OBJID OBJID A    foo/bar/baz
+       :000000 100644 OBJID OBJID A    foo/bar/qux
+       EOF
+        empty_tree=$(git mktree </dev/null) &&
+        cat >input <<-INPUT_END &&
+       commit refs/heads/N-delete
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       collect data to be deleted
+       COMMIT
+
+       deleteall
+       M 100644 inline foo/bar/baz
+       data <<DATA_END
+       hello
+       DATA_END
+       C "foo/bar/baz" "foo/bar/qux"
+       C "foo/bar/baz" "foo/bar/quux/1"
+       C "foo/bar/baz" "foo/bar/quuux"
+       M 040000 $empty_tree foo/bar/quux
+       M 040000 $empty_tree foo/bar/quuux
+
+       commit refs/heads/N-delete
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       delete subdirectory
+       COMMIT
+
+       M 040000 $empty_tree foo/bar/qux
+       INPUT_END
+        git fast-import <input &&
+        git rev-list N-delete |
+               git diff-tree -r --stdin --root --always |
+               sed -e "s/$_x40/OBJID/g" >actual &&
+        test_cmp expect actual'
+
 test_expect_success \
        'N: modify copied tree' \
        'cat >expect <<-\EOF &&
@@ -907,6 +994,114 @@ test_expect_success \
         git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
         compare_diff_raw expect actual'
 
+test_expect_success \
+       'N: reject foo/ syntax' \
+       'subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+        test_must_fail git fast-import <<-INPUT_END
+       commit refs/heads/N5B
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy with invalid syntax
+       COMMIT
+
+       from refs/heads/branch^0
+       M 040000 $subdir file3/
+       INPUT_END'
+
+test_expect_success \
+       'N: copy to root by id and modify' \
+       'echo "hello, world" >expect.foo &&
+        echo hello >expect.bar &&
+        git fast-import <<-SETUP_END &&
+       commit refs/heads/N7
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       hello, tree
+       COMMIT
+
+       deleteall
+       M 644 inline foo/bar
+       data <<EOF
+       hello
+       EOF
+       SETUP_END
+
+        tree=$(git rev-parse --verify N7:) &&
+        git fast-import <<-INPUT_END &&
+       commit refs/heads/N8
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy to root by id and modify
+       COMMIT
+
+       M 040000 $tree ""
+       M 644 inline foo/foo
+       data <<EOF
+       hello, world
+       EOF
+       INPUT_END
+        git show N8:foo/foo >actual.foo &&
+        git show N8:foo/bar >actual.bar &&
+        test_cmp expect.foo actual.foo &&
+        test_cmp expect.bar actual.bar'
+
+test_expect_success \
+       'N: extract subtree' \
+       'branch=$(git rev-parse --verify refs/heads/branch^{tree}) &&
+        cat >input <<-INPUT_END &&
+       commit refs/heads/N9
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       extract subtree branch:newdir
+       COMMIT
+
+       M 040000 $branch ""
+       C "newdir" ""
+       INPUT_END
+        git fast-import <input &&
+        git diff --exit-code branch:newdir N9'
+
+test_expect_success \
+       'N: modify subtree, extract it, and modify again' \
+       'echo hello >expect.baz &&
+        echo hello, world >expect.qux &&
+        git fast-import <<-SETUP_END &&
+       commit refs/heads/N10
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       hello, tree
+       COMMIT
+
+       deleteall
+       M 644 inline foo/bar/baz
+       data <<EOF
+       hello
+       EOF
+       SETUP_END
+
+        tree=$(git rev-parse --verify N10:) &&
+        git fast-import <<-INPUT_END &&
+       commit refs/heads/N11
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy to root by id and modify
+       COMMIT
+
+       M 040000 $tree ""
+       M 100644 inline foo/bar/qux
+       data <<EOF
+       hello, world
+       EOF
+       R "foo" ""
+       C "bar/qux" "bar/quux"
+       INPUT_END
+        git show N11:bar/baz >actual.baz &&
+        git show N11:bar/qux >actual.qux &&
+        git show N11:bar/quux >actual.quux &&
+        test_cmp expect.baz actual.baz &&
+        test_cmp expect.qux actual.qux &&
+        test_cmp expect.qux actual.quux'
+
 ###
 ### series O
 ###
@@ -1110,11 +1305,10 @@ test_expect_success \
        'P: supermodule & submodule mix' \
        'git fast-import <input &&
         git checkout subuse1 &&
-        rm -rf sub && mkdir sub && cd sub &&
+        rm -rf sub && mkdir sub && (cd sub &&
         git init &&
         git fetch --update-head-ok .. refs/heads/sub:refs/heads/master &&
-        git checkout master &&
-        cd .. &&
+        git checkout master) &&
         git submodule init &&
         git submodule update'
 
@@ -1612,6 +1806,253 @@ test_expect_success 'R: feature no-relative-marks should be honoured' '
     test_cmp marks.new non-relative.out
 '
 
+test_expect_success 'R: feature cat-blob supported' '
+       echo "feature cat-blob" |
+       git fast-import
+'
+
+test_expect_success 'R: cat-blob-fd must be a nonnegative integer' '
+       test_must_fail git fast-import --cat-blob-fd=-1 </dev/null
+'
+
+test_expect_success 'R: print old blob' '
+       blob=$(echo "yes it can" | git hash-object -w --stdin) &&
+       cat >expect <<-EOF &&
+       ${blob} blob 11
+       yes it can
+
+       EOF
+       echo "cat-blob $blob" |
+       git fast-import --cat-blob-fd=6 6>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'R: in-stream cat-blob-fd not respected' '
+       echo hello >greeting &&
+       blob=$(git hash-object -w greeting) &&
+       cat >expect <<-EOF &&
+       ${blob} blob 6
+       hello
+
+       EOF
+       git fast-import --cat-blob-fd=3 3>actual.3 >actual.1 <<-EOF &&
+       cat-blob $blob
+       EOF
+       test_cmp expect actual.3 &&
+       test_cmp empty actual.1 &&
+       git fast-import 3>actual.3 >actual.1 <<-EOF &&
+       option cat-blob-fd=3
+       cat-blob $blob
+       EOF
+       test_cmp empty actual.3 &&
+       test_cmp expect actual.1
+'
+
+test_expect_success 'R: print new blob' '
+       blob=$(echo "yep yep yep" | git hash-object --stdin) &&
+       cat >expect <<-EOF &&
+       ${blob} blob 12
+       yep yep yep
+
+       EOF
+       git fast-import --cat-blob-fd=6 6>actual <<-\EOF &&
+       blob
+       mark :1
+       data <<BLOB_END
+       yep yep yep
+       BLOB_END
+       cat-blob :1
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'R: print new blob by sha1' '
+       blob=$(echo "a new blob named by sha1" | git hash-object --stdin) &&
+       cat >expect <<-EOF &&
+       ${blob} blob 25
+       a new blob named by sha1
+
+       EOF
+       git fast-import --cat-blob-fd=6 6>actual <<-EOF &&
+       blob
+       data <<BLOB_END
+       a new blob named by sha1
+       BLOB_END
+       cat-blob $blob
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'setup: big file' '
+       (
+               echo "the quick brown fox jumps over the lazy dog" >big &&
+               for i in 1 2 3
+               do
+                       cat big big big big >bigger &&
+                       cat bigger bigger bigger bigger >big ||
+                       exit
+               done
+       )
+'
+
+test_expect_success 'R: print two blobs to stdout' '
+       blob1=$(git hash-object big) &&
+       blob1_len=$(wc -c <big) &&
+       blob2=$(echo hello | git hash-object --stdin) &&
+       {
+               echo ${blob1} blob $blob1_len &&
+               cat big &&
+               cat <<-EOF
+
+               ${blob2} blob 6
+               hello
+
+               EOF
+       } >expect &&
+       {
+               cat <<-\END_PART1 &&
+                       blob
+                       mark :1
+                       data <<data_end
+               END_PART1
+               cat big &&
+               cat <<-\EOF
+                       data_end
+                       blob
+                       mark :2
+                       data <<data_end
+                       hello
+                       data_end
+                       cat-blob :1
+                       cat-blob :2
+               EOF
+       } |
+       git fast-import >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'setup: have pipes?' '
+       rm -f frob &&
+       if mkfifo frob
+       then
+               test_set_prereq PIPE
+       fi
+'
+
+test_expect_success PIPE 'R: copy using cat-file' '
+       expect_id=$(git hash-object big) &&
+       expect_len=$(wc -c <big) &&
+       echo $expect_id blob $expect_len >expect.response &&
+
+       rm -f blobs &&
+       cat >frontend <<-\FRONTEND_END &&
+       #!/bin/sh
+       FRONTEND_END
+
+       mkfifo blobs &&
+       (
+               export GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_COMMITTER_DATE &&
+               cat <<-\EOF &&
+               feature cat-blob
+               blob
+               mark :1
+               data <<BLOB
+               EOF
+               cat big &&
+               cat <<-\EOF &&
+               BLOB
+               cat-blob :1
+               EOF
+
+               read blob_id type size <&3 &&
+               echo "$blob_id $type $size" >response &&
+               head_c $size >blob <&3 &&
+               read newline <&3 &&
+
+               cat <<-EOF &&
+               commit refs/heads/copied
+               committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+               data <<COMMIT
+               copy big file as file3
+               COMMIT
+               M 644 inline file3
+               data <<BLOB
+               EOF
+               cat blob &&
+               echo BLOB
+       ) 3<blobs |
+       git fast-import --cat-blob-fd=3 3>blobs &&
+       git show copied:file3 >actual &&
+       test_cmp expect.response response &&
+       test_cmp big actual
+'
+
+test_expect_success PIPE 'R: print blob mid-commit' '
+       rm -f blobs &&
+       echo "A blob from _before_ the commit." >expect &&
+       mkfifo blobs &&
+       (
+               exec 3<blobs &&
+               cat <<-EOF &&
+               feature cat-blob
+               blob
+               mark :1
+               data <<BLOB
+               A blob from _before_ the commit.
+               BLOB
+               commit refs/heads/temporary
+               committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+               data <<COMMIT
+               Empty commit
+               COMMIT
+               cat-blob :1
+               EOF
+
+               read blob_id type size <&3 &&
+               head_c $size >actual <&3 &&
+               read newline <&3 &&
+
+               echo
+       ) |
+       git fast-import --cat-blob-fd=3 3>blobs &&
+       test_cmp expect actual
+'
+
+test_expect_success PIPE 'R: print staged blob within commit' '
+       rm -f blobs &&
+       echo "A blob from _within_ the commit." >expect &&
+       mkfifo blobs &&
+       (
+               exec 3<blobs &&
+               cat <<-EOF &&
+               feature cat-blob
+               commit refs/heads/within
+               committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+               data <<COMMIT
+               Empty commit
+               COMMIT
+               M 644 inline within
+               data <<BLOB
+               A blob from _within_ the commit.
+               BLOB
+               EOF
+
+               to_get=$(
+                       echo "A blob from _within_ the commit." |
+                       git hash-object --stdin
+               ) &&
+               echo "cat-blob $to_get" &&
+
+               read blob_id type size <&3 &&
+               head_c $size >actual <&3 &&
+               read newline <&3 &&
+
+               echo deleteall
+       ) |
+       git fast-import --cat-blob-fd=3 3>blobs &&
+       test_cmp expect actual
+'
+
 cat >input << EOF
 option git quiet
 blob
@@ -1620,8 +2061,6 @@ hi
 
 EOF
 
-touch empty
-
 test_expect_success 'R: quiet option results in no stats being output' '
     cat input | git fast-import 2> output &&
     test_cmp empty output
@@ -1639,6 +2078,14 @@ test_expect_success 'R: unknown commandline options are rejected' '\
     test_must_fail git fast-import --non-existing-option < /dev/null
 '
 
+test_expect_success 'R: die on invalid option argument' '
+       echo "option git active-branches=-5" |
+       test_must_fail git fast-import &&
+       echo "option git depth=" |
+       test_must_fail git fast-import &&
+       test_must_fail git fast-import --depth="5 elephants" </dev/null
+'
+
 cat >input <<EOF
 option non-existing-vcs non-existing-option
 EOF
index a5c99d85074a3e04d699afd9b0990a89afe33f54..7cf8cd8a2fed2191b31082c92a95598a3e06513d 100755 (executable)
@@ -255,13 +255,18 @@ EOF
 
 INPUT_END
 
+whitespace="    "
+
 cat >expect <<EXPECT_END
     fourth commit
     pre-prefix of note for fourth commit
+$whitespace
     prefix of note for fourth commit
+$whitespace
     third note for fourth commit
     third commit
     prefix of note for third commit
+$whitespace
     third note for third commit
     second commit
     third note for second commit
index d43f37ccafb4ecf6f649a5504d2626f652d48d02..f823c05305e5021a8cca98d49256af3b1c008fb9 100755 (executable)
@@ -26,7 +26,7 @@ test_expect_success 'setup' '
        test_tick &&
        git tag rein &&
        git checkout -b wer HEAD^ &&
-       echo lange > file2
+       echo lange > file2 &&
        test_tick &&
        git commit -m sitzt file2 &&
        test_tick &&
@@ -355,6 +355,20 @@ test_expect_failure 'no exact-ref revisions included' '
        )
 '
 
+test_expect_success 'path limiting with import-marks does not lose unmodified files'        '
+       git checkout -b simple marks~2 &&
+       git fast-export --export-marks=marks simple -- file > /dev/null &&
+       echo more content >> file &&
+       test_tick &&
+       git commit -mnext file &&
+       git fast-export --import-marks=marks simple -- file file0 | grep file0
+'
+
+test_expect_success 'full-tree re-shows unmodified files'        '
+       git checkout -f simple &&
+       test $(git fast-export --full-tree simple | grep -c file0) -eq 3
+'
+
 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 //"` &&
@@ -376,4 +390,28 @@ 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'
 
+test_expect_success SYMLINKS 'directory becomes symlink'        '
+       git init dirtosymlink &&
+       git init result &&
+       (
+               cd dirtosymlink &&
+               mkdir foo &&
+               mkdir bar &&
+               echo hello > foo/world &&
+               echo hello > bar/world &&
+               git add foo/world bar/world &&
+               git commit -q -mone &&
+               git rm -r foo &&
+               ln -s bar foo &&
+               git add foo &&
+               git commit -q -mtwo
+       ) &&
+       (
+               cd dirtosymlink &&
+               git fast-export master -- foo |
+               (cd ../result && git fast-import --quiet)
+       ) &&
+       (cd result && git show master:foo)
+'
+
 test_done
index 36c457e7f2312774223f853aab0e3b055659e916..9199550ef4ffa39e4ce8bdb36badfd723e95e55f 100755 (executable)
@@ -57,7 +57,7 @@ test_expect_success 'setup' '
 # as argument to co -d
 test_expect_success 'basic checkout' \
   'GIT_CONFIG="$git_config" cvs -Q co -d cvswork master &&
-   test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/"
+   test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/" &&
    test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"'
 
 #------------------------
index 925bd0fbedf9865cef834460969c0a9fd2262437..ff6d6fb473fe0b00a4e2135395cdf89c43250a1a 100755 (executable)
@@ -70,7 +70,7 @@ test_expect_success 'setup' '
     mkdir subdir &&
     echo "Another text file" > subdir/file.h &&
     echo "Another binary: Q (this time CR)" | q_to_cr > subdir/withCr.bin &&
-    echo "Mixed up NUL, but marked text: Q <- there" | q_to_nul > mixedUp.c
+    echo "Mixed up NUL, but marked text: Q <- there" | q_to_nul > mixedUp.c &&
     echo "Unspecified" > subdir/unspecified.other &&
     echo "/*.bin -crlf" > .gitattributes &&
     echo "/*.c crlf" >> .gitattributes &&
@@ -129,21 +129,22 @@ test_expect_success 'cvs co (use attributes)' '
 '
 
 test_expect_success 'adding files' '
-    cd cvswork/subdir &&
+    (cd cvswork &&
+    (cd subdir &&
     echo "more text" > src.c &&
     GIT_CONFIG="$git_config" cvs -Q add src.c >cvs.log 2>&1 &&
     marked_as . src.c "" &&
-    echo "psuedo-binary" > temp.bin &&
-    cd .. &&
+    echo "psuedo-binary" > temp.bin
+    ) &&
     GIT_CONFIG="$git_config" cvs -Q add subdir/temp.bin >cvs.log 2>&1 &&
     marked_as subdir temp.bin "-kb" &&
     cd subdir &&
     GIT_CONFIG="$git_config" cvs -Q ci -m "adding files" >cvs.log 2>&1 &&
     marked_as . temp.bin "-kb" &&
     marked_as . src.c ""
+    )
 '
 
-cd "$WORKDIR"
 test_expect_success 'updating' '
     git pull gitcvs.git &&
     echo 'hi' > subdir/newfile.bin &&
@@ -153,9 +154,9 @@ test_expect_success 'updating' '
     git add subdir/newfile.bin subdir/file.h subdir/newfile.c binfile.bin &&
     git commit -q -m "Add and change some files" &&
     git push gitcvs.git >/dev/null &&
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs -Q update &&
-    cd .. &&
+    (cd cvswork &&
+    GIT_CONFIG="$git_config" cvs -Q update
+    ) &&
     marked_as cvswork textfile.c "" &&
     marked_as cvswork binfile.bin -kb &&
     marked_as cvswork .gitattributes "" &&
@@ -233,35 +234,35 @@ test_expect_success 'cvs co another copy (guess)' '
 '
 
 test_expect_success 'add text (guess)' '
-    cd cvswork &&
+    (cd cvswork &&
     echo "simpleText" > simpleText.c &&
-    GIT_CONFIG="$git_config" cvs -Q add simpleText.c &&
-    cd .. &&
+    GIT_CONFIG="$git_config" cvs -Q add simpleText.c
+    ) &&
     marked_as cvswork simpleText.c ""
 '
 
 test_expect_success 'add bin (guess)' '
-    cd cvswork &&
+    (cd cvswork &&
     echo "simpleBin: NUL: Q <- there" | q_to_nul > simpleBin.bin &&
-    GIT_CONFIG="$git_config" cvs -Q add simpleBin.bin &&
-    cd .. &&
+    GIT_CONFIG="$git_config" cvs -Q add simpleBin.bin
+    ) &&
     marked_as cvswork simpleBin.bin -kb
 '
 
 test_expect_success 'remove files (guess)' '
-    cd cvswork &&
+    (cd cvswork &&
     GIT_CONFIG="$git_config" cvs -Q rm -f subdir/file.h &&
-    cd subdir &&
-    GIT_CONFIG="$git_config" cvs -Q rm -f withCr.bin &&
-    cd ../.. &&
+    (cd subdir &&
+    GIT_CONFIG="$git_config" cvs -Q rm -f withCr.bin
+    )) &&
     marked_as cvswork/subdir withCr.bin -kb &&
     marked_as cvswork/subdir file.h ""
 '
 
 test_expect_success 'cvs ci (guess)' '
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs -Q ci -m "add/rm files" >cvs.log 2>&1 &&
-    cd .. &&
+    (cd cvswork &&
+    GIT_CONFIG="$git_config" cvs -Q ci -m "add/rm files" >cvs.log 2>&1
+    ) &&
     marked_as cvswork textfile.c "" &&
     marked_as cvswork binfile.bin -kb &&
     marked_as cvswork .gitattributes "" &&
@@ -278,9 +279,9 @@ test_expect_success 'cvs ci (guess)' '
 '
 
 test_expect_success 'update subdir of other copy (guess)' '
-    cd cvswork2/subdir &&
-    GIT_CONFIG="$git_config" cvs -Q update &&
-    cd ../.. &&
+    (cd cvswork2/subdir &&
+    GIT_CONFIG="$git_config" cvs -Q update
+    ) &&
     marked_as cvswork2 textfile.c "" &&
     marked_as cvswork2 binfile.bin -kb &&
     marked_as cvswork2 .gitattributes "" &&
@@ -304,11 +305,11 @@ test_expect_success 'update/merge full other copy (guess)' '
     git add multilineTxt.c &&
     git commit -q -m "modify multiline file" >> "${WORKDIR}/marked.log" &&
     git push gitcvs.git >/dev/null &&
-    cd cvswork2 &&
+    (cd cvswork2 &&
     sed "s/1/replaced_1/" < multilineTxt.c > ml.temp &&
     mv ml.temp multilineTxt.c &&
-    GIT_CONFIG="$git_config" cvs update > cvs.log 2>&1 &&
-    cd .. &&
+    GIT_CONFIG="$git_config" cvs update > cvs.log 2>&1
+    ) &&
     marked_as cvswork2 textfile.c "" &&
     marked_as cvswork2 binfile.bin -kb &&
     marked_as cvswork2 .gitattributes "" &&
index 4f2b9b062b08302aedab42dcdd0a33776559c05e..21cd286bb7abf33c944a8758ce8d702e83ca2022 100755 (executable)
@@ -650,25 +650,26 @@ test_debug 'cat gitweb.log'
 # ----------------------------------------------------------------------
 # syntax highlighting
 
-cat >>gitweb_config.perl <<\EOF
-$feature{'highlight'}{'override'} = 1;
-EOF
 
 highlight --version >/dev/null 2>&1
 if [ $? -eq 127 ]; then
        say "Skipping syntax highlighting test, because 'highlight' was not found"
 else
        test_set_prereq HIGHLIGHT
+       cat >>gitweb_config.perl <<-\EOF
+       our $highlight_bin = "highlight";
+       $feature{'highlight'}{'override'} = 1;
+       EOF
 fi
 
 test_expect_success HIGHLIGHT \
-       'syntax highlighting (no highlight)' \
+       'syntax highlighting (no highlight, unknown syntax)' \
        'git config gitweb.highlight yes &&
         gitweb_run "p=.git;a=blob;f=file"'
 test_debug 'cat gitweb.log'
 
 test_expect_success HIGHLIGHT \
-       'syntax highlighting (highlighted)' \
+       'syntax highlighting (highlighted, shell script)' \
        'git config gitweb.highlight yes &&
         echo "#!/usr/bin/sh" > test.sh &&
         git add test.sh &&
index 2487da1296da1a2508526f58a2e45fa750c49c8e..18825aff89f5699afaabc34df0fd7baab5863747 100755 (executable)
@@ -16,7 +16,7 @@ code and message.'
 # snapshot settings
 
 test_expect_success 'setup' "
-       test_commit 'SnapshotTests' 'i can has snapshot?'
+       test_commit 'SnapshotTests' 'i can has snapshot'
 "
 
 
index 2eff9cd68ce2f51618159df402bfb46382d44434..4c384ff02333e1413cc67b1b4a6bed47ae3d00db 100755 (executable)
@@ -3,21 +3,18 @@
 test_description='git cvsimport basic tests'
 . ./lib-cvs.sh
 
-if ! test_have_prereq PERL; then
-       skip_all='skipping git cvsimport tests, perl not available'
-       test_done
-fi
-
-CVSROOT=$(pwd)/cvsroot
-export CVSROOT
+test_expect_success PERL 'setup cvsroot environment' '
+       CVSROOT=$(pwd)/cvsroot &&
+       export CVSROOT
+'
 
-test_expect_success 'setup cvsroot' '$CVS init'
+test_expect_success PERL 'setup cvsroot' '$CVS init'
 
-test_expect_success 'setup a cvs module' '
+test_expect_success PERL 'setup a cvs module' '
 
        mkdir "$CVSROOT/module" &&
        $CVS co -d module-cvs module &&
-       cd module-cvs &&
+       (cd module-cvs &&
        cat <<EOF >o_fortuna &&
 O Fortuna
 velut luna
@@ -41,29 +38,28 @@ add "O Fortuna" lyrics
 
 These public domain lyrics make an excellent sample text.
 EOF
-       $CVS commit -F message &&
-       cd ..
+       $CVS commit -F message
+       )
 '
 
-test_expect_success 'import a trivial module' '
+test_expect_success PERL 'import a trivial module' '
 
        git cvsimport -a -R -z 0 -C module-git module &&
        test_cmp module-cvs/o_fortuna module-git/o_fortuna
 
 '
 
-test_expect_success 'pack refs' 'cd module-git && git gc && cd ..'
+test_expect_success PERL 'pack refs' '(cd module-git && git gc)'
 
-test_expect_success 'initial import has correct .git/cvs-revisions' '
+test_expect_success PERL 'initial import has correct .git/cvs-revisions' '
 
        (cd module-git &&
         git log --format="o_fortuna 1.1 %H" -1) > expected &&
        test_cmp expected module-git/.git/cvs-revisions
 '
 
-test_expect_success 'update cvs module' '
-
-       cd module-cvs &&
+test_expect_success PERL 'update cvs module' '
+       (cd module-cvs &&
        cat <<EOF >o_fortuna &&
 O Fortune,
 like the moon
@@ -86,21 +82,22 @@ translate to English
 
 My Latin is terrible.
 EOF
-       $CVS commit -F message &&
-       cd ..
+       $CVS commit -F message
+       )
 '
 
-test_expect_success 'update git module' '
+test_expect_success PERL 'update git module' '
 
-       cd module-git &&
-       git cvsimport -a -R -z 0 module &&
-       git merge origin &&
-       cd .. &&
+       (cd module-git &&
+       git config cvsimport.trackRevisions true &&
+       git cvsimport -a -z 0 module &&
+       git merge origin
+       ) &&
        test_cmp module-cvs/o_fortuna module-git/o_fortuna
 
 '
 
-test_expect_success 'update has correct .git/cvs-revisions' '
+test_expect_success PERL 'update has correct .git/cvs-revisions' '
 
        (cd module-git &&
         git log --format="o_fortuna 1.1 %H" -1 HEAD^ &&
@@ -108,28 +105,28 @@ test_expect_success 'update has correct .git/cvs-revisions' '
        test_cmp expected module-git/.git/cvs-revisions
 '
 
-test_expect_success 'update cvs module' '
+test_expect_success PERL 'update cvs module' '
 
-       cd module-cvs &&
+       (cd module-cvs &&
                echo 1 >tick &&
                $CVS add tick &&
                $CVS commit -m 1
-       cd ..
-
+       )
 '
 
-test_expect_success 'cvsimport.module config works' '
+test_expect_success PERL 'cvsimport.module config works' '
 
-       cd module-git &&
+       (cd module-git &&
                git config cvsimport.module module &&
-               git cvsimport -a -R -z0 &&
-               git merge origin &&
-       cd .. &&
+               git config cvsimport.trackRevisions true &&
+               git cvsimport -a -z0 &&
+               git merge origin
+       ) &&
        test_cmp module-cvs/tick module-git/tick
 
 '
 
-test_expect_success 'second update has correct .git/cvs-revisions' '
+test_expect_success PERL 'second update has correct .git/cvs-revisions' '
 
        (cd module-git &&
         git log --format="o_fortuna 1.1 %H" -1 HEAD^^ &&
@@ -138,24 +135,25 @@ test_expect_success 'second update has correct .git/cvs-revisions' '
        test_cmp expected module-git/.git/cvs-revisions
 '
 
-test_expect_success 'import from a CVS working tree' '
+test_expect_success PERL 'import from a CVS working tree' '
 
        $CVS co -d import-from-wt module &&
-       cd import-from-wt &&
+       (cd import-from-wt &&
+               git config cvsimport.trackRevisions false &&
                git cvsimport -a -z0 &&
                echo 1 >expect &&
                git log -1 --pretty=format:%s%n >actual &&
-               test_cmp actual expect &&
-       cd ..
+               test_cmp actual expect
+       )
 
 '
 
-test_expect_success 'no .git/cvs-revisions created by default' '
+test_expect_success PERL 'no .git/cvs-revisions created by default' '
 
        ! test -e import-from-wt/.git/cvs-revisions
 
 '
 
-test_expect_success 'test entire HEAD' 'test_cmp_branch_tree master'
+test_expect_success PERL 'test entire HEAD' 'test_cmp_branch_tree master'
 
 test_done
index 3afaf565267eaa3281d5957775e76cf6a0d5f1b0..827d39f5bf28652cc3d9fdf2f331592ed0b46ecc 100755 (executable)
 test_description='git cvsimport handling of vendor branches'
 . ./lib-cvs.sh
 
-CVSROOT="$TEST_DIRECTORY"/t9601/cvsroot
-export CVSROOT
+setup_cvs_test_repository t9601
 
-test_expect_success 'import a module with a vendor branch' '
+test_expect_success PERL '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 PERL '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 PERL 'check master out of git repository' 'test_git_co master'
 
-test_expect_success 'check a file that was imported once' '
+test_expect_success PERL '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_expect_failure PERL '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_expect_success PERL '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_expect_success PERL '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_expect_success PERL '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_expect_success PERL 'a vendor branch whose tag has been removed' '
 
        test_cmp_branch_file master imported-anonymously.txt
 
index 67878b2d0ce2003e761dbd3e72a699f6e4fa56c7..e1db323f545fccfdadbf2bc1df1060e006674298 100755 (executable)
@@ -6,70 +6,69 @@
 test_description='git cvsimport handling of branches and tags'
 . ./lib-cvs.sh
 
-CVSROOT="$TEST_DIRECTORY"/t9602/cvsroot
-export CVSROOT
+setup_cvs_test_repository t9602
 
-test_expect_success 'import module' '
+test_expect_success PERL 'import module' '
 
        git cvsimport -C module-git module
 
 '
 
-test_expect_success 'test branch master' '
+test_expect_success PERL 'test branch master' '
 
        test_cmp_branch_tree master
 
 '
 
-test_expect_success 'test branch vendorbranch' '
+test_expect_success PERL 'test branch vendorbranch' '
 
        test_cmp_branch_tree vendorbranch
 
 '
 
-test_expect_failure 'test branch B_FROM_INITIALS' '
+test_expect_failure PERL 'test branch B_FROM_INITIALS' '
 
        test_cmp_branch_tree B_FROM_INITIALS
 
 '
 
-test_expect_failure 'test branch B_FROM_INITIALS_BUT_ONE' '
+test_expect_failure PERL 'test branch B_FROM_INITIALS_BUT_ONE' '
 
        test_cmp_branch_tree B_FROM_INITIALS_BUT_ONE
 
 '
 
-test_expect_failure 'test branch B_MIXED' '
+test_expect_failure PERL 'test branch B_MIXED' '
 
        test_cmp_branch_tree B_MIXED
 
 '
 
-test_expect_success 'test branch B_SPLIT' '
+test_expect_success PERL 'test branch B_SPLIT' '
 
        test_cmp_branch_tree B_SPLIT
 
 '
 
-test_expect_failure 'test tag vendortag' '
+test_expect_failure PERL 'test tag vendortag' '
 
        test_cmp_branch_tree vendortag
 
 '
 
-test_expect_success 'test tag T_ALL_INITIAL_FILES' '
+test_expect_success PERL '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_expect_failure PERL '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_expect_failure PERL 'test tag T_MIXED' '
 
        test_cmp_branch_tree T_MIXED
 
index 958bdce4dd8891fa648ba2bf554b18cae91de712..52034c8f77abd2eb0079866331c49a2f5f34e20d 100755 (executable)
 test_description='git cvsimport testing for correct patchset estimation'
 . ./lib-cvs.sh
 
-CVSROOT="$TEST_DIRECTORY"/t9603/cvsroot
-export CVSROOT
+setup_cvs_test_repository t9603
 
 test_expect_failure 'import with criss cross times on revisions' '
 
     git cvsimport -p"-x" -C module-git module &&
-    cd module-git &&
+    (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 "" >> ../actual-A
+    ) &&
     echo "Rev 4
 Rev 3
 Rev 2
index 671f38db2bb78c076b281602ca5b87f10fdf4d00..c15ca2d6479a814d7649a38a671a554b6ed8b68f 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 use lib (split(/:/, $ENV{GITPERLLIB}));
 
-use 5.006002;
+use 5.008;
 use warnings;
 use strict;
 
index 29fd7209cf1aff851f0f1f95ccecd0d949b3da6c..0fdc541a7cd7d6b694c4f4a3fd06b6cf21dc7380 100644 (file)
@@ -70,6 +70,9 @@ unset GIT_NOTES_REF
 unset GIT_NOTES_DISPLAY_REF
 unset GIT_NOTES_REWRITE_REF
 unset GIT_NOTES_REWRITE_MODE
+unset GIT_REFLOG_ACTION
+unset GIT_CHERRY_PICK_HELP
+unset GIT_QUIET
 GIT_MERGE_VERBOSITY=5
 export GIT_MERGE_VERBOSITY
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
@@ -238,14 +241,51 @@ test_set_editor () {
 }
 
 test_decode_color () {
-       sed     -e 's/.\[1m/<WHITE>/g' \
-               -e 's/.\[31m/<RED>/g' \
-               -e 's/.\[32m/<GREEN>/g' \
-               -e 's/.\[33m/<YELLOW>/g' \
-               -e 's/.\[34m/<BLUE>/g' \
-               -e 's/.\[35m/<MAGENTA>/g' \
-               -e 's/.\[36m/<CYAN>/g' \
-               -e 's/.\[m/<RESET>/g'
+       awk '
+               function name(n) {
+                       if (n == 0) return "RESET";
+                       if (n == 1) return "BOLD";
+                       if (n == 30) return "BLACK";
+                       if (n == 31) return "RED";
+                       if (n == 32) return "GREEN";
+                       if (n == 33) return "YELLOW";
+                       if (n == 34) return "BLUE";
+                       if (n == 35) return "MAGENTA";
+                       if (n == 36) return "CYAN";
+                       if (n == 37) return "WHITE";
+                       if (n == 40) return "BLACK";
+                       if (n == 41) return "BRED";
+                       if (n == 42) return "BGREEN";
+                       if (n == 43) return "BYELLOW";
+                       if (n == 44) return "BBLUE";
+                       if (n == 45) return "BMAGENTA";
+                       if (n == 46) return "BCYAN";
+                       if (n == 47) return "BWHITE";
+               }
+               {
+                       while (match($0, /\033\[[0-9;]*m/) != 0) {
+                               printf "%s<", substr($0, 1, RSTART-1);
+                               codes = substr($0, RSTART+2, RLENGTH-3);
+                               if (length(codes) == 0)
+                                       printf "%s", name(0)
+                               else {
+                                       n = split(codes, ary, ";");
+                                       sep = "";
+                                       for (i = 1; i <= n; i++) {
+                                               printf "%s%s", sep, name(ary[i]);
+                                               sep = ";"
+                                       }
+                               }
+                               printf ">";
+                               $0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);
+                       }
+                       print
+               }
+       '
+}
+
+nul_to_q () {
+       perl -pe 'y/\000/Q/'
 }
 
 q_to_nul () {
@@ -268,6 +308,17 @@ remove_cr () {
        tr '\015' Q | sed -e 's/Q$//'
 }
 
+# In some bourne shell implementations, the "unset" builtin returns
+# nonzero status when a variable to be unset was not set in the first
+# place.
+#
+# Use sane_unset when that should not be considered an error.
+
+sane_unset () {
+       unset "$@"
+       return 0
+}
+
 test_tick () {
        if test -z "${test_tick+set}"
        then
@@ -331,12 +382,44 @@ test_set_prereq () {
 satisfied=" "
 
 test_have_prereq () {
-       case $satisfied in
-       *" $1 "*)
-               : yes, have it ;;
-       *)
-               ! : nope ;;
+       # prerequisites can be concatenated with ','
+       save_IFS=$IFS
+       IFS=,
+       set -- $*
+       IFS=$save_IFS
+
+       total_prereq=0
+       ok_prereq=0
+       missing_prereq=
+
+       for prerequisite
+       do
+               total_prereq=$(($total_prereq + 1))
+               case $satisfied in
+               *" $prerequisite "*)
+                       ok_prereq=$(($ok_prereq + 1))
+                       ;;
+               *)
+                       # Keep a list of missing prerequisites
+                       if test -z "$missing_prereq"
+                       then
+                               missing_prereq=$prerequisite
+                       else
+                               missing_prereq="$prerequisite,$missing_prereq"
+                       fi
+               esac
+       done
+
+       test $total_prereq = $ok_prereq
+}
+
+test_declared_prereq () {
+       case ",$test_prereq," in
+       *,$1,*)
+               return 0
+               ;;
        esac
+       return 1
 }
 
 # You are not expected to call test_ok_ and test_failure_ directly, use
@@ -391,15 +474,21 @@ test_skip () {
                        break
                esac
        done
-       if test -z "$to_skip" && test -n "$prereq" &&
-          ! test_have_prereq "$prereq"
+       if test -z "$to_skip" && test -n "$test_prereq" &&
+          ! test_have_prereq "$test_prereq"
        then
                to_skip=t
        fi
        case "$to_skip" in
        t)
+               of_prereq=
+               if test "$missing_prereq" != "$test_prereq"
+               then
+                       of_prereq=" of $test_prereq"
+               fi
+
                say_color skip >&3 "skipping test: $@"
-               say_color skip "ok $test_count # skip $1"
+               say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
                : true
                ;;
        *)
@@ -409,9 +498,10 @@ test_skip () {
 }
 
 test_expect_failure () {
-       test "$#" = 3 && { prereq=$1; shift; } || prereq=
+       test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
+       export test_prereq
        if ! test_skip "$@"
        then
                say >&3 "checking known breakage: $2"
@@ -427,9 +517,10 @@ test_expect_failure () {
 }
 
 test_expect_success () {
-       test "$#" = 3 && { prereq=$1; shift; } || prereq=
+       test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+       export test_prereq
        if ! test_skip "$@"
        then
                say >&3 "expecting success: $2"
@@ -444,24 +535,6 @@ test_expect_success () {
        echo >&3 ""
 }
 
-test_expect_code () {
-       test "$#" = 4 && { prereq=$1; shift; } || prereq=
-       test "$#" = 3 ||
-       error "bug in the test script: not 3 or 4 parameters to test-expect-code"
-       if ! test_skip "$@"
-       then
-               say >&3 "expecting exit code $1: $3"
-               test_run_ "$3"
-               if [ "$?" = 0 -a "$eval_ret" = "$1" ]
-               then
-                       test_ok_ "$2"
-               else
-                       test_failure_ "$@"
-               fi
-       fi
-       echo >&3 ""
-}
-
 # test_external runs external test scripts that provide continuous
 # test output about their progress, and succeeds/fails on
 # zero/non-zero exit code.  It outputs the test output on stdout even
@@ -471,11 +544,12 @@ test_expect_code () {
 # Usage: test_external description command arguments...
 # Example: test_external 'Perl API' perl ../path/to/test.pl
 test_external () {
-       test "$#" = 4 && { prereq=$1; shift; } || prereq=
+       test "$#" = 4 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 3 ||
        error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
        descr="$1"
        shift
+       export test_prereq
        if ! test_skip "$descr" "$@"
        then
                # Announce the script to reduce confusion about the
@@ -545,6 +619,60 @@ test_external_without_stderr () {
        fi
 }
 
+# debugging-friendly alternatives to "test [-f|-d|-e]"
+# The commands test the existence or non-existence of $1. $2 can be
+# given to provide a more precise diagnosis.
+test_path_is_file () {
+       if ! [ -f "$1" ]
+       then
+               echo "File $1 doesn't exist. $*"
+               false
+       fi
+}
+
+test_path_is_dir () {
+       if ! [ -d "$1" ]
+       then
+               echo "Directory $1 doesn't exist. $*"
+               false
+       fi
+}
+
+test_path_is_missing () {
+       if [ -e "$1" ]
+       then
+               echo "Path exists:"
+               ls -ld "$1"
+               if [ $# -ge 1 ]; then
+                       echo "$*"
+               fi
+               false
+       fi
+}
+
+# test_line_count checks that a file has the number of lines it
+# ought to. For example:
+#
+#      test_expect_success 'produce exactly one line of output' '
+#              do something >output &&
+#              test_line_count = 1 output
+#      '
+#
+# is like "test $(wc -l <output) = 1" except that it passes the
+# output through when the number of lines is wrong.
+
+test_line_count () {
+       if test $# != 3
+       then
+               error "bug in the test script: not 3 parameters to test_line_count"
+       elif ! test $(wc -l <"$3") "$1" "$2"
+       then
+               echo "test_line_count: line count for $3 !$1 $2"
+               cat "$3"
+               return 1
+       fi
+}
+
 # This is not among top-level (test_expect_success | test_expect_failure)
 # but is a prefix that can be used in the test script, like:
 #
@@ -559,7 +687,18 @@ test_external_without_stderr () {
 
 test_must_fail () {
        "$@"
-       test $? -gt 0 -a $? -le 129 -o $? -gt 192
+       exit_code=$?
+       if test $exit_code = 0; then
+               echo >&2 "test_must_fail: command succeeded: $*"
+               return 1
+       elif test $exit_code -gt 129 -a $exit_code -le 192; then
+               echo >&2 "test_must_fail: died by signal: $*"
+               return 1
+       elif test $exit_code = 127; then
+               echo >&2 "test_must_fail: command not found: $*"
+               return 1
+       fi
+       return 0
 }
 
 # Similar to test_must_fail, but tolerates success, too.  This is
@@ -575,7 +714,37 @@ test_must_fail () {
 
 test_might_fail () {
        "$@"
-       test $? -ge 0 -a $? -le 129 -o $? -gt 192
+       exit_code=$?
+       if test $exit_code -gt 129 -a $exit_code -le 192; then
+               echo >&2 "test_might_fail: died by signal: $*"
+               return 1
+       elif test $exit_code = 127; then
+               echo >&2 "test_might_fail: command not found: $*"
+               return 1
+       fi
+       return 0
+}
+
+# Similar to test_must_fail and test_might_fail, but check that a
+# given command exited with a given exit code. Meant to be used as:
+#
+#      test_expect_success 'Merge with d/f conflicts' '
+#              test_expect_code 1 git merge "merge msg" B master
+#      '
+
+test_expect_code () {
+       want_code=$1
+       shift
+       "$@"
+       exit_code=$?
+       if test $exit_code = $want_code
+       then
+               echo >&2 "test_expect_code: command exited with $exit_code: $*"
+               return 0
+       else
+               echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
+               return 1
+       fi
 }
 
 # test_cmp is a helper function to compare actual and expected output.
@@ -625,28 +794,31 @@ test_when_finished () {
 test_create_repo () {
        test "$#" = 1 ||
        error "bug in the test script: not 1 parameter to test-create-repo"
-       owd=`pwd`
        repo="$1"
        mkdir -p "$repo"
-       cd "$repo" || error "Cannot setup test environment"
-       "$GIT_EXEC_PATH/git-init" "--template=$TEST_DIRECTORY/../templates/blt/" >&3 2>&4 ||
-       error "cannot run git init -- have you built things yet?"
-       mv .git/hooks .git/hooks-disabled
-       cd "$owd"
+       (
+               cd "$repo" || error "Cannot setup test environment"
+               "$GIT_EXEC_PATH/git-init" "--template=$GIT_BUILD_DIR/templates/blt/" >&3 2>&4 ||
+               error "cannot run git init -- have you built things yet?"
+               mv .git/hooks .git/hooks-disabled
+       ) || exit
 }
 
 test_done () {
        GIT_EXIT_OK=t
-       test_results_dir="$TEST_DIRECTORY/test-results"
-       mkdir -p "$test_results_dir"
-       test_results_path="$test_results_dir/${0%.sh}-$$.counts"
 
-       echo "total $test_count" >> $test_results_path
-       echo "success $test_success" >> $test_results_path
-       echo "fixed $test_fixed" >> $test_results_path
-       echo "broken $test_broken" >> $test_results_path
-       echo "failed $test_failure" >> $test_results_path
-       echo "" >> $test_results_path
+       if test -z "$HARNESS_ACTIVE"; then
+               test_results_dir="$TEST_DIRECTORY/test-results"
+               mkdir -p "$test_results_dir"
+               test_results_path="$test_results_dir/${0%.sh}-$$.counts"
+
+               echo "total $test_count" >> $test_results_path
+               echo "success $test_success" >> $test_results_path
+               echo "fixed $test_fixed" >> $test_results_path
+               echo "broken $test_broken" >> $test_results_path
+               echo "failed $test_failure" >> $test_results_path
+               echo "" >> $test_results_path
+       fi
 
        if test "$test_fixed" != 0
        then
@@ -688,7 +860,15 @@ test_done () {
 
 # Test the binaries we have just built.  The tests are kept in
 # t/ subdirectory and are run in 'trash directory' subdirectory.
-TEST_DIRECTORY=$(pwd)
+if test -z "$TEST_DIRECTORY"
+then
+       # We allow tests to override this, in case they want to run tests
+       # outside of t/, e.g. for running tests on the test library
+       # itself.
+       TEST_DIRECTORY=$(pwd)
+fi
+GIT_BUILD_DIR="$TEST_DIRECTORY"/..
+
 if test -n "$valgrind"
 then
        make_symlink () {
@@ -715,7 +895,7 @@ then
                test -x "$1" || return
 
                base=$(basename "$1")
-               symlink_target=$TEST_DIRECTORY/../$base
+               symlink_target=$GIT_BUILD_DIR/$base
                # do not override scripts
                if test -x "$symlink_target" &&
                    test ! -d "$symlink_target" &&
@@ -734,7 +914,7 @@ then
        # override all git executables in TEST_DIRECTORY/..
        GIT_VALGRIND=$TEST_DIRECTORY/valgrind
        mkdir -p "$GIT_VALGRIND"/bin
-       for file in $TEST_DIRECTORY/../git* $TEST_DIRECTORY/../test-*
+       for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
        do
                make_valgrind_symlink $file
        done
@@ -755,10 +935,10 @@ then
 elif test -n "$GIT_TEST_INSTALLED" ; then
        GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
        error "Cannot run git from $GIT_TEST_INSTALLED."
-       PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH
+       PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR:$PATH
        GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
 else # normal case, use ../bin-wrappers only unless $with_dashes:
-       git_bin_dir="$TEST_DIRECTORY/../bin-wrappers"
+       git_bin_dir="$GIT_BUILD_DIR/bin-wrappers"
        if ! test -x "$git_bin_dir/git" ; then
                if test -z "$with_dashes" ; then
                        say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH"
@@ -766,18 +946,18 @@ else # normal case, use ../bin-wrappers only unless $with_dashes:
                with_dashes=t
        fi
        PATH="$git_bin_dir:$PATH"
-       GIT_EXEC_PATH=$TEST_DIRECTORY/..
+       GIT_EXEC_PATH=$GIT_BUILD_DIR
        if test -n "$with_dashes" ; then
-               PATH="$TEST_DIRECTORY/..:$PATH"
+               PATH="$GIT_BUILD_DIR:$PATH"
        fi
 fi
-GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
+GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
 unset GIT_CONFIG
 GIT_CONFIG_NOSYSTEM=1
 GIT_CONFIG_NOGLOBAL=1
 export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL
 
-. ../GIT-BUILD-OPTIONS
+. "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
 
 if test -z "$GIT_TEST_CMP"
 then
@@ -789,22 +969,22 @@ then
        fi
 fi
 
-GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
+GITPERLLIB="$GIT_BUILD_DIR"/perl/blib/lib:"$GIT_BUILD_DIR"/perl/blib/arch/auto/Git
 export GITPERLLIB
-test -d ../templates/blt || {
+test -d "$GIT_BUILD_DIR"/templates/blt || {
        error "You haven't built things yet, have you?"
 }
 
 if test -z "$GIT_TEST_INSTALLED" && test -z "$NO_PYTHON"
 then
-       GITPYTHONLIB="$(pwd)/../git_remote_helpers/build/lib"
+       GITPYTHONLIB="$GIT_BUILD_DIR/git_remote_helpers/build/lib"
        export GITPYTHONLIB
-       test -d ../git_remote_helpers/build || {
+       test -d "$GIT_BUILD_DIR"/git_remote_helpers/build || {
                error "You haven't built git_remote_helpers yet, have you?"
        }
 fi
 
-if ! test -x ../test-chmtime; then
+if ! test -x "$GIT_BUILD_DIR"/test-chmtime; then
        echo >&2 'You need to build test-chmtime:'
        echo >&2 'Run "make test-chmtime" in the source (toplevel) directory'
        exit 1
@@ -829,6 +1009,9 @@ test_create_repo "$test"
 # in subprocesses like git equals our $PWD (for pathname comparisons).
 cd -P "$test" || exit 1
 
+HOME=$(pwd)
+export HOME
+
 this_test=${0##*/}
 this_test=${this_test%%-*}
 for skp in $GIT_SKIP_TESTS
@@ -876,11 +1059,20 @@ case $(uname -s) in
        # no POSIX permissions
        # backslashes in pathspec are converted to '/'
        # exec does not inherit the PID
+       test_set_prereq MINGW
+       test_set_prereq SED_STRIPS_CR
+       ;;
+*CYGWIN*)
+       test_set_prereq POSIXPERM
+       test_set_prereq EXECKEEPSPID
+       test_set_prereq NOT_MINGW
+       test_set_prereq SED_STRIPS_CR
        ;;
 *)
        test_set_prereq POSIXPERM
        test_set_prereq BSLASHPSPEC
        test_set_prereq EXECKEEPSPID
+       test_set_prereq NOT_MINGW
        ;;
 esac
 
@@ -890,3 +1082,7 @@ test -z "$NO_PYTHON" && test_set_prereq PYTHON
 # test whether the filesystem supports symbolic links
 ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
 rm -f y
+
+# When the tests are run as root, permission tests will report that
+# things are writable when they shouldn't be.
+test -w / || test_set_prereq SANITY
diff --git a/t/test-terminal.perl b/t/test-terminal.perl
new file mode 100755 (executable)
index 0000000..ee01eb9
--- /dev/null
@@ -0,0 +1,76 @@
+#!/usr/bin/perl
+use 5.008;
+use strict;
+use warnings;
+use IO::Pty;
+use File::Copy;
+
+# Run @$argv in the background with stdio redirected to $out and $err.
+sub start_child {
+       my ($argv, $out, $err) = @_;
+       my $pid = fork;
+       if (not defined $pid) {
+               die "fork failed: $!"
+       } elsif ($pid == 0) {
+               open STDOUT, ">&", $out;
+               open STDERR, ">&", $err;
+               close $out;
+               exec(@$argv) or die "cannot exec '$argv->[0]': $!"
+       }
+       return $pid;
+}
+
+# Wait for $pid to finish.
+sub finish_child {
+       # Simplified from wait_or_whine() in run-command.c.
+       my ($pid) = @_;
+
+       my $waiting = waitpid($pid, 0);
+       if ($waiting < 0) {
+               die "waitpid failed: $!";
+       } elsif ($? & 127) {
+               my $code = $? & 127;
+               warn "died of signal $code";
+               return $code - 128;
+       } else {
+               return $? >> 8;
+       }
+}
+
+sub xsendfile {
+       my ($out, $in) = @_;
+
+       # Note: the real sendfile() cannot read from a terminal.
+
+       # It is unspecified by POSIX whether reads
+       # from a disconnected terminal will return
+       # EIO (as in AIX 4.x, IRIX, and Linux) or
+       # end-of-file.  Either is fine.
+       copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
+}
+
+sub copy_stdio {
+       my ($out, $err) = @_;
+       my $pid = fork;
+       defined $pid or die "fork failed: $!";
+       if (!$pid) {
+               close($out);
+               xsendfile(\*STDERR, $err);
+               exit 0;
+       }
+       close($err);
+       xsendfile(\*STDOUT, $out);
+       finish_child($pid) == 0
+               or exit 1;
+}
+
+if ($#ARGV < 1) {
+       die "usage: test-terminal program args";
+}
+my $master_out = new IO::Pty;
+my $master_err = new IO::Pty;
+my $pid = start_child(\@ARGV, $master_out->slave, $master_err->slave);
+close $master_out->slave;
+close $master_err->slave;
+copy_stdio($master_out, $master_err);
+exit(finish_child($pid));
diff --git a/tag.c b/tag.c
index 85607c219e25d63b0d1f3344649104c60f5b96e2..f789744ccaf5d5fb6f63ba0911a869492affc162 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -4,6 +4,9 @@
 #include "tree.h"
 #include "blob.h"
 
+#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
+#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----"
+
 const char *tag_type = "tag";
 
 struct object *deref_tag(struct object *o, const char *warn, int warnlen)
@@ -28,12 +31,12 @@ struct tag *lookup_tag(const unsigned char *sha1)
                return create_object(sha1, OBJ_TAG, alloc_tag_node());
        if (!obj->type)
                obj->type = OBJ_TAG;
-        if (obj->type != OBJ_TAG) {
-                error("Object %s is a %s, not a tag",
-                      sha1_to_hex(sha1), typename(obj->type));
-                return NULL;
-        }
-        return (struct tag *) obj;
+       if (obj->type != OBJ_TAG) {
+               error("Object %s is a %s, not a tag",
+                     sha1_to_hex(sha1), typename(obj->type));
+               return NULL;
+       }
+       return (struct tag *) obj;
 }
 
 static unsigned long parse_tag_date(const char *buf, const char *tail)
@@ -133,3 +136,15 @@ int parse_tag(struct tag *item)
        free(data);
        return ret;
 }
+
+size_t parse_signature(const char *buf, unsigned long size)
+{
+       char *eol;
+       size_t len = 0;
+       while (len < size && prefixcmp(buf + len, PGP_SIGNATURE) &&
+                       prefixcmp(buf + len, PGP_MESSAGE)) {
+               eol = memchr(buf + len, '\n', size - len);
+               len += eol ? eol - (buf + len) + 1 : size - len;
+       }
+       return len;
+}
diff --git a/tag.h b/tag.h
index 47662724a6d7d07eeeacd5c8528d94d750ecf878..85223700396f5ddb00c046a1a9fdb63c92aae40e 100644 (file)
--- a/tag.h
+++ b/tag.h
@@ -16,5 +16,6 @@ extern struct tag *lookup_tag(const unsigned char *sha1);
 extern int parse_tag_buffer(struct tag *item, void *data, unsigned long size);
 extern int parse_tag(struct tag *item);
 extern struct object *deref_tag(struct object *, const char *, int);
+extern size_t parse_signature(const char *buf, unsigned long size);
 
 #endif /* TAG_H */
diff --git a/test-line-buffer.c b/test-line-buffer.c
new file mode 100644 (file)
index 0000000..c11bf7f
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * test-line-buffer.c: code to exercise the svn importer's input helper
+ *
+ * Input format:
+ *     number NL
+ *     (number bytes) NL
+ *     number NL
+ *     ...
+ */
+
+#include "git-compat-util.h"
+#include "vcs-svn/line_buffer.h"
+
+static uint32_t strtouint32(const char *s)
+{
+       char *end;
+       uintmax_t n = strtoumax(s, &end, 10);
+       if (*s == '\0' || *end != '\0')
+               die("invalid count: %s", s);
+       return (uint32_t) n;
+}
+
+int main(int argc, char *argv[])
+{
+       char *s;
+
+       if (argc != 1)
+               usage("test-line-buffer < input.txt");
+       if (buffer_init(NULL))
+               die_errno("open error");
+       while ((s = buffer_read_line())) {
+               s = buffer_read_string(strtouint32(s));
+               fputs(s, stdout);
+               fputc('\n', stdout);
+               buffer_skip_bytes(1);
+               if (!(s = buffer_read_line()))
+                       break;
+               buffer_copy_bytes(strtouint32(s) + 1);
+       }
+       if (buffer_deinit())
+               die("input error");
+       if (ferror(stdout))
+               die("output error");
+       buffer_reset();
+       return 0;
+}
diff --git a/test-mktemp.c b/test-mktemp.c
new file mode 100644 (file)
index 0000000..c8c5421
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * test-mktemp.c: code to exercise the creation of temporary files
+ */
+#include "git-compat-util.h"
+
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               usage("Expected 1 parameter defining the temporary file template");
+
+       xmkstemp(xstrdup(argv[1]));
+
+       return 0;
+}
diff --git a/test-obj-pool.c b/test-obj-pool.c
new file mode 100644 (file)
index 0000000..5018863
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * test-obj-pool.c: code to exercise the svn importer's object pool
+ */
+
+#include "cache.h"
+#include "vcs-svn/obj_pool.h"
+
+enum pool { POOL_ONE, POOL_TWO };
+obj_pool_gen(one, int, 1)
+obj_pool_gen(two, int, 4096)
+
+static uint32_t strtouint32(const char *s)
+{
+       char *end;
+       uintmax_t n = strtoumax(s, &end, 10);
+       if (*s == '\0' || (*end != '\n' && *end != '\0'))
+               die("invalid offset: %s", s);
+       return (uint32_t) n;
+}
+
+static void handle_command(const char *command, enum pool pool, const char *arg)
+{
+       switch (*command) {
+       case 'a':
+               if (!prefixcmp(command, "alloc ")) {
+                       uint32_t n = strtouint32(arg);
+                       printf("%"PRIu32"\n",
+                               pool == POOL_ONE ?
+                               one_alloc(n) : two_alloc(n));
+                       return;
+               }
+       case 'c':
+               if (!prefixcmp(command, "commit ")) {
+                       pool == POOL_ONE ? one_commit() : two_commit();
+                       return;
+               }
+               if (!prefixcmp(command, "committed ")) {
+                       printf("%"PRIu32"\n",
+                               pool == POOL_ONE ?
+                               one_pool.committed : two_pool.committed);
+                       return;
+               }
+       case 'f':
+               if (!prefixcmp(command, "free ")) {
+                       uint32_t n = strtouint32(arg);
+                       pool == POOL_ONE ? one_free(n) : two_free(n);
+                       return;
+               }
+       case 'n':
+               if (!prefixcmp(command, "null ")) {
+                       printf("%"PRIu32"\n",
+                               pool == POOL_ONE ?
+                               one_offset(NULL) : two_offset(NULL));
+                       return;
+               }
+       case 'o':
+               if (!prefixcmp(command, "offset ")) {
+                       uint32_t n = strtouint32(arg);
+                       printf("%"PRIu32"\n",
+                               pool == POOL_ONE ?
+                               one_offset(one_pointer(n)) :
+                               two_offset(two_pointer(n)));
+                       return;
+               }
+       case 'r':
+               if (!prefixcmp(command, "reset ")) {
+                       pool == POOL_ONE ? one_reset() : two_reset();
+                       return;
+               }
+       case 's':
+               if (!prefixcmp(command, "set ")) {
+                       uint32_t n = strtouint32(arg);
+                       if (pool == POOL_ONE)
+                               *one_pointer(n) = 1;
+                       else
+                               *two_pointer(n) = 1;
+                       return;
+               }
+       case 't':
+               if (!prefixcmp(command, "test ")) {
+                       uint32_t n = strtouint32(arg);
+                       printf("%d\n", pool == POOL_ONE ?
+                               *one_pointer(n) : *two_pointer(n));
+                       return;
+               }
+       default:
+               die("unrecognized command: %s", command);
+       }
+}
+
+static void handle_line(const char *line)
+{
+       const char *arg = strchr(line, ' ');
+       enum pool pool;
+
+       if (arg && !prefixcmp(arg + 1, "one"))
+               pool = POOL_ONE;
+       else if (arg && !prefixcmp(arg + 1, "two"))
+               pool = POOL_TWO;
+       else
+               die("no pool specified: %s", line);
+
+       handle_command(line, pool, arg + strlen("one "));
+}
+
+int main(int argc, char *argv[])
+{
+       struct strbuf sb = STRBUF_INIT;
+       if (argc != 1)
+               usage("test-obj-str < script");
+
+       while (strbuf_getline(&sb, stdin, '\n') != EOF)
+               handle_line(sb.buf);
+       strbuf_release(&sb);
+       return 0;
+}
index acd1a2ba70fc2ffb38cd8fb0784aa6e5e693183f..0828592162cc7c61f0d025ba4d83fc29bc4c2067 100644 (file)
@@ -66,9 +66,9 @@ int main(int argc, const char **argv)
                  "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG },
                OPT_GROUP("Standard options"),
                OPT__ABBREV(&abbrev),
-               OPT__VERBOSE(&verbose),
-               OPT__DRY_RUN(&dry_run),
-               OPT__QUIET(&quiet),
+               OPT__VERBOSE(&verbose, "be verbose"),
+               OPT__DRY_RUN(&dry_run, "dry run"),
+               OPT__QUIET(&quiet, "be quiet"),
                OPT_END(),
        };
        int i;
diff --git a/test-string-pool.c b/test-string-pool.c
new file mode 100644 (file)
index 0000000..c5782e6
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * test-string-pool.c: code to exercise the svn importer's string pool
+ */
+
+#include "git-compat-util.h"
+#include "vcs-svn/string_pool.h"
+
+int main(int argc, char *argv[])
+{
+       const uint32_t unequal = pool_intern("does not equal");
+       const uint32_t equal = pool_intern("equals");
+       uint32_t buf[3];
+       uint32_t n;
+
+       if (argc != 2)
+               usage("test-string-pool <string>,<string>");
+
+       n = pool_tok_seq(3, buf, ",-", argv[1]);
+       if (n >= 3)
+               die("too many strings");
+       if (n <= 1)
+               die("too few strings");
+
+       buf[2] = buf[1];
+       buf[1] = (buf[0] == buf[2]) ? equal : unequal;
+       pool_print_seq(3, buf, ' ', stdout);
+       fputc('\n', stdout);
+
+       pool_reset();
+       return 0;
+}
diff --git a/test-subprocess.c b/test-subprocess.c
new file mode 100644 (file)
index 0000000..667d3e5
--- /dev/null
@@ -0,0 +1,21 @@
+#include "cache.h"
+#include "run-command.h"
+
+int main(int argc, char **argv)
+{
+       const char *prefix;
+       struct child_process cp;
+       int nogit = 0;
+
+       prefix = setup_git_directory_gently(&nogit);
+       if (nogit)
+               die("No git repo found");
+       if (!strcmp(argv[1], "--setup-work-tree")) {
+               setup_work_tree();
+               argv++;
+       }
+       memset(&cp, 0, sizeof(cp));
+       cp.git_cmd = 1;
+       cp.argv = (const char **)argv+1;
+       return run_command(&cp);
+}
diff --git a/test-svn-fe.c b/test-svn-fe.c
new file mode 100644 (file)
index 0000000..77cf78a
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * test-svn-fe: Code to exercise the svn import lib
+ */
+
+#include "git-compat-util.h"
+#include "vcs-svn/svndump.h"
+
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               usage("test-svn-fe <file>");
+       svndump_init(argv[1]);
+       svndump_read(NULL);
+       svndump_deinit();
+       svndump_reset();
+       return 0;
+}
diff --git a/test-treap.c b/test-treap.c
new file mode 100644 (file)
index 0000000..ab8c951
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * test-treap.c: code to exercise the svn importer's treap structure
+ */
+
+#include "cache.h"
+#include "vcs-svn/obj_pool.h"
+#include "vcs-svn/trp.h"
+
+struct int_node {
+       uintmax_t n;
+       struct trp_node children;
+};
+
+obj_pool_gen(node, struct int_node, 3)
+
+static int node_cmp(struct int_node *a, struct int_node *b)
+{
+       return (a->n > b->n) - (a->n < b->n);
+}
+
+trp_gen(static, treap_, struct int_node, children, node, node_cmp)
+
+static void strtonode(struct int_node *item, const char *s)
+{
+       char *end;
+       item->n = strtoumax(s, &end, 10);
+       if (*s == '\0' || (*end != '\n' && *end != '\0'))
+               die("invalid integer: %s", s);
+}
+
+int main(int argc, char *argv[])
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct trp_root root = { ~0 };
+       uint32_t item;
+
+       if (argc != 1)
+               usage("test-treap < ints");
+
+       while (strbuf_getline(&sb, stdin, '\n') != EOF) {
+               struct int_node *node = node_pointer(node_alloc(1));
+
+               item = node_offset(node);
+               strtonode(node, sb.buf);
+               node = treap_insert(&root, node_pointer(item));
+               if (node_offset(node) != item)
+                       die("inserted %"PRIu32" in place of %"PRIu32"",
+                               node_offset(node), item);
+       }
+
+       item = node_offset(treap_first(&root));
+       while (~item) {
+               uint32_t next;
+               struct int_node *tmp = node_pointer(node_alloc(1));
+
+               tmp->n = node_pointer(item)->n;
+               next = node_offset(treap_next(&root, node_pointer(item)));
+
+               treap_remove(&root, node_pointer(item));
+               item = node_offset(treap_nsearch(&root, tmp));
+
+               if (item != next && (!~item || node_pointer(item)->n != tmp->n))
+                       die("found %"PRIuMAX" in place of %"PRIuMAX"",
+                               ~item ? node_pointer(item)->n : ~(uintmax_t) 0,
+                               ~next ? node_pointer(next)->n : ~(uintmax_t) 0);
+               printf("%"PRIuMAX"\n", tmp->n);
+       }
+       node_reset();
+       return 0;
+}
index 1727a03333b80f2a01ca029162a4b511480a86e8..6fb98c333c22a67512516bbf047c2cac270a66ae 100644 (file)
@@ -1,7 +1,11 @@
 #ifndef THREAD_COMPAT_H
 #define THREAD_COMPAT_H
 
+#ifndef NO_PTHREADS
+#include <pthread.h>
+
 extern int online_cpus(void);
 extern int init_recursive_mutex(pthread_mutex_t*);
 
+#endif
 #endif /* THREAD_COMPAT_H */
diff --git a/trace.c b/trace.c
index 1e560cb0b977e19beeb095497c8e4f6280b7f0d6..35d388dce44a4a8e3d6053dfd6abcb652771818a 100644 (file)
--- a/trace.c
+++ b/trace.c
 #include "cache.h"
 #include "quote.h"
 
-void do_nothing(size_t unused)
-{
-}
-
 /* Get a trace file descriptor from GIT_TRACE env variable. */
 static int get_trace_fd(int *need_close)
 {
@@ -76,7 +72,7 @@ void trace_printf(const char *fmt, ...)
        if (!fd)
                return;
 
-       set_try_to_free_routine(do_nothing);    /* is never reset */
+       set_try_to_free_routine(NULL);  /* is never reset */
        strbuf_init(&buf, 64);
        va_start(ap, fmt);
        len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
@@ -108,7 +104,7 @@ void trace_argv_printf(const char **argv, const char *fmt, ...)
        if (!fd)
                return;
 
-       set_try_to_free_routine(do_nothing);    /* is never reset */
+       set_try_to_free_routine(NULL);  /* is never reset */
        strbuf_init(&buf, 64);
        va_start(ap, fmt);
        len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
@@ -131,3 +127,52 @@ void trace_argv_printf(const char **argv, const char *fmt, ...)
        if (need_close)
                close(fd);
 }
+
+static const char *quote_crnl(const char *path)
+{
+       static char new_path[PATH_MAX];
+       const char *p2 = path;
+       char *p1 = new_path;
+
+       if (!path)
+               return NULL;
+
+       while (*p2) {
+               switch (*p2) {
+               case '\\': *p1++ = '\\'; *p1++ = '\\'; break;
+               case '\n': *p1++ = '\\'; *p1++ = 'n'; break;
+               case '\r': *p1++ = '\\'; *p1++ = 'r'; break;
+               default:
+                       *p1++ = *p2;
+               }
+               p2++;
+       }
+       *p1 = '\0';
+       return new_path;
+}
+
+/* FIXME: move prefix to startup_info struct and get rid of this arg */
+void trace_repo_setup(const char *prefix)
+{
+       const char *git_work_tree;
+       char cwd[PATH_MAX];
+       char *trace = getenv("GIT_TRACE");
+
+       if (!trace || !strcmp(trace, "") ||
+           !strcmp(trace, "0") || !strcasecmp(trace, "false"))
+               return;
+
+       if (!getcwd(cwd, PATH_MAX))
+               die("Unable to get current working directory");
+
+       if (!(git_work_tree = get_git_work_tree()))
+               git_work_tree = "(null)";
+
+       if (!prefix)
+               prefix = "(null)";
+
+       trace_printf("setup: git_dir: %s\n", quote_crnl(get_git_dir()));
+       trace_printf("setup: worktree: %s\n", quote_crnl(git_work_tree));
+       trace_printf("setup: cwd: %s\n", quote_crnl(cwd));
+       trace_printf("setup: prefix: %s\n", quote_crnl(prefix));
+}
index acfc88e3f1f0e99863512bf2c6ea82f1283cc35d..4e4754c32bd53f28f9335c2a4529f5804f3086f2 100644 (file)
@@ -8,6 +8,7 @@
 #include "quote.h"
 #include "remote.h"
 #include "string-list.h"
+#include "thread-utils.h"
 
 static int debug;
 
@@ -862,3 +863,314 @@ int transport_helper_init(struct transport *transport, const char *name)
        transport->smart_options = &(data->transport_options);
        return 0;
 }
+
+/*
+ * Linux pipes can buffer 65536 bytes at once (and most platforms can
+ * buffer less), so attempt reads and writes with up to that size.
+ */
+#define BUFFERSIZE 65536
+/* This should be enough to hold debugging message. */
+#define PBUFFERSIZE 8192
+
+/* Print bidirectional transfer loop debug message. */
+static void transfer_debug(const char *fmt, ...)
+{
+       va_list args;
+       char msgbuf[PBUFFERSIZE];
+       static int debug_enabled = -1;
+
+       if (debug_enabled < 0)
+               debug_enabled = getenv("GIT_TRANSLOOP_DEBUG") ? 1 : 0;
+       if (!debug_enabled)
+               return;
+
+       va_start(args, fmt);
+       vsnprintf(msgbuf, PBUFFERSIZE, fmt, args);
+       va_end(args);
+       fprintf(stderr, "Transfer loop debugging: %s\n", msgbuf);
+}
+
+/* Stream state: More data may be coming in this direction. */
+#define SSTATE_TRANSFERING 0
+/*
+ * Stream state: No more data coming in this direction, flushing rest of
+ * data.
+ */
+#define SSTATE_FLUSHING 1
+/* Stream state: Transfer in this direction finished. */
+#define SSTATE_FINISHED 2
+
+#define STATE_NEEDS_READING(state) ((state) <= SSTATE_TRANSFERING)
+#define STATE_NEEDS_WRITING(state) ((state) <= SSTATE_FLUSHING)
+#define STATE_NEEDS_CLOSING(state) ((state) == SSTATE_FLUSHING)
+
+/* Unidirectional transfer. */
+struct unidirectional_transfer {
+       /* Source */
+       int src;
+       /* Destination */
+       int dest;
+       /* Is source socket? */
+       int src_is_sock;
+       /* Is destination socket? */
+       int dest_is_sock;
+       /* Transfer state (TRANSFERING/FLUSHING/FINISHED) */
+       int state;
+       /* Buffer. */
+       char buf[BUFFERSIZE];
+       /* Buffer used. */
+       size_t bufuse;
+       /* Name of source. */
+       const char *src_name;
+       /* Name of destination. */
+       const char *dest_name;
+};
+
+/* Closes the target (for writing) if transfer has finished. */
+static void udt_close_if_finished(struct unidirectional_transfer *t)
+{
+       if (STATE_NEEDS_CLOSING(t->state) && !t->bufuse) {
+               t->state = SSTATE_FINISHED;
+               if (t->dest_is_sock)
+                       shutdown(t->dest, SHUT_WR);
+               else
+                       close(t->dest);
+               transfer_debug("Closed %s.", t->dest_name);
+       }
+}
+
+/*
+ * Tries to read read data from source into buffer. If buffer is full,
+ * no data is read. Returns 0 on success, -1 on error.
+ */
+static int udt_do_read(struct unidirectional_transfer *t)
+{
+       ssize_t bytes;
+
+       if (t->bufuse == BUFFERSIZE)
+               return 0;       /* No space for more. */
+
+       transfer_debug("%s is readable", t->src_name);
+       bytes = read(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse);
+       if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
+               errno != EINTR) {
+               error("read(%s) failed: %s", t->src_name, strerror(errno));
+               return -1;
+       } else if (bytes == 0) {
+               transfer_debug("%s EOF (with %i bytes in buffer)",
+                       t->src_name, t->bufuse);
+               t->state = SSTATE_FLUSHING;
+       } else if (bytes > 0) {
+               t->bufuse += bytes;
+               transfer_debug("Read %i bytes from %s (buffer now at %i)",
+                       (int)bytes, t->src_name, (int)t->bufuse);
+       }
+       return 0;
+}
+
+/* Tries to write data from buffer into destination. If buffer is empty,
+ * no data is written. Returns 0 on success, -1 on error.
+ */
+static int udt_do_write(struct unidirectional_transfer *t)
+{
+       size_t bytes;
+
+       if (t->bufuse == 0)
+               return 0;       /* Nothing to write. */
+
+       transfer_debug("%s is writable", t->dest_name);
+       bytes = write(t->dest, t->buf, t->bufuse);
+       if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
+               errno != EINTR) {
+               error("write(%s) failed: %s", t->dest_name, strerror(errno));
+               return -1;
+       } else if (bytes > 0) {
+               t->bufuse -= bytes;
+               if (t->bufuse)
+                       memmove(t->buf, t->buf + bytes, t->bufuse);
+               transfer_debug("Wrote %i bytes to %s (buffer now at %i)",
+                       (int)bytes, t->dest_name, (int)t->bufuse);
+       }
+       return 0;
+}
+
+
+/* State of bidirectional transfer loop. */
+struct bidirectional_transfer_state {
+       /* Direction from program to git. */
+       struct unidirectional_transfer ptg;
+       /* Direction from git to program. */
+       struct unidirectional_transfer gtp;
+};
+
+static void *udt_copy_task_routine(void *udt)
+{
+       struct unidirectional_transfer *t = (struct unidirectional_transfer *)udt;
+       while (t->state != SSTATE_FINISHED) {
+               if (STATE_NEEDS_READING(t->state))
+                       if (udt_do_read(t))
+                               return NULL;
+               if (STATE_NEEDS_WRITING(t->state))
+                       if (udt_do_write(t))
+                               return NULL;
+               if (STATE_NEEDS_CLOSING(t->state))
+                       udt_close_if_finished(t);
+       }
+       return udt;     /* Just some non-NULL value. */
+}
+
+#ifndef NO_PTHREADS
+
+/*
+ * Join thread, with apporiate errors on failure. Name is name for the
+ * thread (for error messages). Returns 0 on success, 1 on failure.
+ */
+static int tloop_join(pthread_t thread, const char *name)
+{
+       int err;
+       void *tret;
+       err = pthread_join(thread, &tret);
+       if (!tret) {
+               error("%s thread failed", name);
+               return 1;
+       }
+       if (err) {
+               error("%s thread failed to join: %s", name, strerror(err));
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Spawn the transfer tasks and then wait for them. Returns 0 on success,
+ * -1 on failure.
+ */
+static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s)
+{
+       pthread_t gtp_thread;
+       pthread_t ptg_thread;
+       int err;
+       int ret = 0;
+       err = pthread_create(&gtp_thread, NULL, udt_copy_task_routine,
+               &s->gtp);
+       if (err)
+               die("Can't start thread for copying data: %s", strerror(err));
+       err = pthread_create(&ptg_thread, NULL, udt_copy_task_routine,
+               &s->ptg);
+       if (err)
+               die("Can't start thread for copying data: %s", strerror(err));
+
+       ret |= tloop_join(gtp_thread, "Git to program copy");
+       ret |= tloop_join(ptg_thread, "Program to git copy");
+       return ret;
+}
+#else
+
+/* Close the source and target (for writing) for transfer. */
+static void udt_kill_transfer(struct unidirectional_transfer *t)
+{
+       t->state = SSTATE_FINISHED;
+       /*
+        * Socket read end left open isn't a disaster if nobody
+        * attempts to read from it (mingw compat headers do not
+        * have SHUT_RD)...
+        *
+        * We can't fully close the socket since otherwise gtp
+        * task would first close the socket it sends data to
+        * while closing the ptg file descriptors.
+        */
+       if (!t->src_is_sock)
+               close(t->src);
+       if (t->dest_is_sock)
+               shutdown(t->dest, SHUT_WR);
+       else
+               close(t->dest);
+}
+
+/*
+ * Join process, with apporiate errors on failure. Name is name for the
+ * process (for error messages). Returns 0 on success, 1 on failure.
+ */
+static int tloop_join(pid_t pid, const char *name)
+{
+       int tret;
+       if (waitpid(pid, &tret, 0) < 0) {
+               error("%s process failed to wait: %s", name, strerror(errno));
+               return 1;
+       }
+       if (!WIFEXITED(tret) || WEXITSTATUS(tret)) {
+               error("%s process failed", name);
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Spawn the transfer tasks and then wait for them. Returns 0 on success,
+ * -1 on failure.
+ */
+static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s)
+{
+       pid_t pid1, pid2;
+       int ret = 0;
+
+       /* Fork thread #1: git to program. */
+       pid1 = fork();
+       if (pid1 < 0)
+               die_errno("Can't start thread for copying data");
+       else if (pid1 == 0) {
+               udt_kill_transfer(&s->ptg);
+               exit(udt_copy_task_routine(&s->gtp) ? 0 : 1);
+       }
+
+       /* Fork thread #2: program to git. */
+       pid2 = fork();
+       if (pid2 < 0)
+               die_errno("Can't start thread for copying data");
+       else if (pid2 == 0) {
+               udt_kill_transfer(&s->gtp);
+               exit(udt_copy_task_routine(&s->ptg) ? 0 : 1);
+       }
+
+       /*
+        * Close both streams in parent as to not interfere with
+        * end of file detection and wait for both tasks to finish.
+        */
+       udt_kill_transfer(&s->gtp);
+       udt_kill_transfer(&s->ptg);
+       ret |= tloop_join(pid1, "Git to program copy");
+       ret |= tloop_join(pid2, "Program to git copy");
+       return ret;
+}
+#endif
+
+/*
+ * Copies data from stdin to output and from input to stdout simultaneously.
+ * Additionally filtering through given filter. If filter is NULL, uses
+ * identity filter.
+ */
+int bidirectional_transfer_loop(int input, int output)
+{
+       struct bidirectional_transfer_state state;
+
+       /* Fill the state fields. */
+       state.ptg.src = input;
+       state.ptg.dest = 1;
+       state.ptg.src_is_sock = (input == output);
+       state.ptg.dest_is_sock = 0;
+       state.ptg.state = SSTATE_TRANSFERING;
+       state.ptg.bufuse = 0;
+       state.ptg.src_name = "remote input";
+       state.ptg.dest_name = "stdout";
+
+       state.gtp.src = 0;
+       state.gtp.dest = output;
+       state.gtp.src_is_sock = 0;
+       state.gtp.dest_is_sock = (input == output);
+       state.gtp.state = SSTATE_TRANSFERING;
+       state.gtp.bufuse = 0;
+       state.gtp.src_name = "stdin";
+       state.gtp.dest_name = "remote output";
+
+       return tloop_spawnwait_tasks(&state);
+}
index 4dba6f8815a80093a8ac9edc226d99bba1bb6394..00786606117feea4b33b7e632b8ff8a3c26de4e4 100644 (file)
@@ -789,6 +789,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        args.use_thin_pack = data->options.thin;
        args.verbose = (transport->verbose > 0);
        args.quiet = (transport->verbose < 0);
+       args.progress = transport->progress;
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
        args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
 
index c59d97388e6fdb1dccd4dca0d41dccc3d129f8fb..e803c0e7baf3785fac35fca64072e05e4f668a26 100644 (file)
@@ -154,6 +154,7 @@ int transport_connect(struct transport *transport, const char *name,
 
 /* Transport methods defined outside transport.c */
 int transport_helper_init(struct transport *transport, const char *name);
+int bidirectional_transfer_loop(int input, int output);
 
 /* common methods used by transport.c and builtin-send-pack.c */
 void transport_verify_remote_names(int nr_heads, const char **heads);
index cd659c6fe447b6fcdedee2843113feb358f23849..3954281f509bbed9a9b095ed92d24b67275fed82 100644 (file)
@@ -6,34 +6,18 @@
 #include "diffcore.h"
 #include "tree.h"
 
-static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)
-{
-       char *newbase = xmalloc(baselen + pathlen + 2);
-       memcpy(newbase, base, baselen);
-       memcpy(newbase + baselen, path, pathlen);
-       memcpy(newbase + baselen + pathlen, "/", 2);
-       return newbase;
-}
-
-static char *malloc_fullname(const char *base, int baselen, const char *path, int pathlen)
-{
-       char *fullname = xmalloc(baselen + pathlen + 1);
-       memcpy(fullname, base, baselen);
-       memcpy(fullname + baselen, path, pathlen);
-       fullname[baselen + pathlen] = 0;
-       return fullname;
-}
+static void show_entry(struct diff_options *opt, const char *prefix,
+                      struct tree_desc *desc, struct strbuf *base);
 
-static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
-                      const char *base, int baselen);
-
-static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, int baselen, struct diff_options *opt)
+static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
+                             struct strbuf *base, struct diff_options *opt)
 {
        unsigned mode1, mode2;
        const char *path1, *path2;
        const unsigned char *sha1, *sha2;
        int cmp, pathlen1, pathlen2;
-       char *fullname;
+       int old_baselen = base->len;
+       int retval = 0;
 
        sha1 = tree_entry_extract(t1, &path1, &mode1);
        sha2 = tree_entry_extract(t2, &path2, &mode2);
@@ -42,11 +26,11 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
        pathlen2 = tree_entry_len(path2, sha2);
        cmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);
        if (cmp < 0) {
-               show_entry(opt, "-", t1, base, baselen);
+               show_entry(opt, "-", t1, base);
                return -1;
        }
        if (cmp > 0) {
-               show_entry(opt, "+", t2, base, baselen);
+               show_entry(opt, "+", t2, base);
                return 1;
        }
        if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER) && !hashcmp(sha1, sha2) && mode1 == mode2)
@@ -57,147 +41,29 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
         * file, we need to consider it a remove and an add.
         */
        if (S_ISDIR(mode1) != S_ISDIR(mode2)) {
-               show_entry(opt, "-", t1, base, baselen);
-               show_entry(opt, "+", t2, base, baselen);
+               show_entry(opt, "-", t1, base);
+               show_entry(opt, "+", t2, base);
                return 0;
        }
 
+       strbuf_add(base, path1, pathlen1);
        if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode1)) {
-               int retval;
-               char *newbase = malloc_base(base, baselen, path1, pathlen1);
                if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
-                       newbase[baselen + pathlen1] = 0;
                        opt->change(opt, mode1, mode2,
-                                   sha1, sha2, newbase, 0, 0);
-                       newbase[baselen + pathlen1] = '/';
+                                   sha1, sha2, base->buf, 0, 0);
                }
-               retval = diff_tree_sha1(sha1, sha2, newbase, opt);
-               free(newbase);
-               return retval;
+               strbuf_addch(base, '/');
+               retval = diff_tree_sha1(sha1, sha2, base->buf, opt);
+       } else {
+               opt->change(opt, mode1, mode2, sha1, sha2, base->buf, 0, 0);
        }
-
-       fullname = malloc_fullname(base, baselen, path1, pathlen1);
-       opt->change(opt, mode1, mode2, sha1, sha2, fullname, 0, 0);
-       free(fullname);
+       strbuf_setlen(base, old_baselen);
        return 0;
 }
 
-/*
- * Is a tree entry interesting given the pathspec we have?
- *
- * Return:
- *  - 2 for "yes, and all subsequent entries will be"
- *  - 1 for yes
- *  - zero for no
- *  - negative for "no, and no subsequent entries will be either"
- */
-static int tree_entry_interesting(struct tree_desc *desc, const char *base, int baselen, struct diff_options *opt)
-{
-       const char *path;
-       const unsigned char *sha1;
-       unsigned mode;
-       int i;
-       int pathlen;
-       int never_interesting = -1;
-
-       if (!opt->nr_paths)
-               return 1;
-
-       sha1 = tree_entry_extract(desc, &path, &mode);
-
-       pathlen = tree_entry_len(path, sha1);
-
-       for (i = 0; i < opt->nr_paths; i++) {
-               const char *match = opt->paths[i];
-               int matchlen = opt->pathlens[i];
-               int m = -1; /* signals that we haven't called strncmp() */
-
-               if (baselen >= matchlen) {
-                       /* If it doesn't match, move along... */
-                       if (strncmp(base, match, matchlen))
-                               continue;
-
-                       /*
-                        * If the base is a subdirectory of a path which
-                        * was specified, all of them are interesting.
-                        */
-                       if (!matchlen ||
-                           base[matchlen] == '/' ||
-                           match[matchlen - 1] == '/')
-                               return 2;
-
-                       /* Just a random prefix match */
-                       continue;
-               }
-
-               /* Does the base match? */
-               if (strncmp(base, match, baselen))
-                       continue;
-
-               match += baselen;
-               matchlen -= baselen;
-
-               if (never_interesting) {
-                       /*
-                        * We have not seen any match that sorts later
-                        * than the current path.
-                        */
-
-                       /*
-                        * Does match sort strictly earlier than path
-                        * with their common parts?
-                        */
-                       m = strncmp(match, path,
-                                   (matchlen < pathlen) ? matchlen : pathlen);
-                       if (m < 0)
-                               continue;
-
-                       /*
-                        * If we come here even once, that means there is at
-                        * least one pathspec that would sort equal to or
-                        * later than the path we are currently looking at.
-                        * In other words, if we have never reached this point
-                        * after iterating all pathspecs, it means all
-                        * pathspecs are either outside of base, or inside the
-                        * base but sorts strictly earlier than the current
-                        * one.  In either case, they will never match the
-                        * subsequent entries.  In such a case, we initialized
-                        * the variable to -1 and that is what will be
-                        * returned, allowing the caller to terminate early.
-                        */
-                       never_interesting = 0;
-               }
-
-               if (pathlen > matchlen)
-                       continue;
-
-               if (matchlen > pathlen) {
-                       if (match[pathlen] != '/')
-                               continue;
-                       if (!S_ISDIR(mode))
-                               continue;
-               }
-
-               if (m == -1)
-                       /*
-                        * we cheated and did not do strncmp(), so we do
-                        * that here.
-                        */
-                       m = strncmp(match, path, pathlen);
-
-               /*
-                * If common part matched earlier then it is a hit,
-                * because we rejected the case where path is not a
-                * leading directory and is shorter than match.
-                */
-               if (!m)
-                       return 1;
-       }
-       return never_interesting; /* No matches */
-}
-
 /* A whole sub-tree went away or appeared */
-static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)
+static void show_tree(struct diff_options *opt, const char *prefix,
+                     struct tree_desc *desc, struct strbuf *base)
 {
        int all_interesting = 0;
        while (desc->size) {
@@ -206,31 +72,32 @@ static void show_tree(struct diff_options *opt, const char *prefix, struct tree_
                if (all_interesting)
                        show = 1;
                else {
-                       show = tree_entry_interesting(desc, base, baselen,
-                                                     opt);
+                       show = tree_entry_interesting(&desc->entry, base, 0,
+                                                     &opt->pathspec);
                        if (show == 2)
                                all_interesting = 1;
                }
                if (show < 0)
                        break;
                if (show)
-                       show_entry(opt, prefix, desc, base, baselen);
+                       show_entry(opt, prefix, desc, base);
                update_tree_entry(desc);
        }
 }
 
 /* A file entry went away or appeared */
-static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
-                      const char *base, int baselen)
+static void show_entry(struct diff_options *opt, const char *prefix,
+                      struct tree_desc *desc, struct strbuf *base)
 {
        unsigned mode;
        const char *path;
        const unsigned char *sha1 = tree_entry_extract(desc, &path, &mode);
        int pathlen = tree_entry_len(path, sha1);
+       int old_baselen = base->len;
 
+       strbuf_add(base, path, pathlen);
        if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode)) {
                enum object_type type;
-               char *newbase = malloc_base(base, baselen, path, pathlen);
                struct tree_desc inner;
                void *tree;
                unsigned long size;
@@ -239,37 +106,27 @@ 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, 0);
-                       newbase[baselen + pathlen] = '/';
-               }
+               if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE))
+                       opt->add_remove(opt, *prefix, mode, sha1, base->buf, 0);
 
-               init_tree_desc(&inner, tree, size);
-               show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
+               strbuf_addch(base, '/');
 
+               init_tree_desc(&inner, tree, size);
+               show_tree(opt, prefix, &inner, base);
                free(tree);
-               free(newbase);
-       } else {
-               char *fullname = malloc_fullname(base, baselen, path, pathlen);
-               opt->add_remove(opt, prefix[0], mode, sha1, fullname, 0);
-               free(fullname);
-       }
+       } else
+               opt->add_remove(opt, prefix[0], mode, sha1, base->buf, 0);
+
+       strbuf_setlen(base, old_baselen);
 }
 
-static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt)
+static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
+                              struct diff_options *opt, int *all_interesting)
 {
-       int all_interesting = 0;
        while (t->size) {
-               int show;
-
-               if (all_interesting)
-                       show = 1;
-               else {
-                       show = tree_entry_interesting(t, base, baselen, opt);
-                       if (show == 2)
-                               all_interesting = 1;
-               }
+               int show = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec);
+               if (show == 2)
+                       *all_interesting = 1;
                if (!show) {
                        update_tree_entry(t);
                        continue;
@@ -281,31 +138,44 @@ static void skip_uninteresting(struct tree_desc *t, const char *base, int basele
        }
 }
 
-int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
+int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
+             const char *base_str, struct diff_options *opt)
 {
-       int baselen = strlen(base);
+       struct strbuf base;
+       int baselen = strlen(base_str);
+       int all_t1_interesting = 0;
+       int all_t2_interesting = 0;
+
+       /* Enable recursion indefinitely */
+       opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
+       opt->pathspec.max_depth = -1;
+
+       strbuf_init(&base, PATH_MAX);
+       strbuf_add(&base, base_str, baselen);
 
        for (;;) {
                if (DIFF_OPT_TST(opt, QUICK) &&
                    DIFF_OPT_TST(opt, HAS_CHANGES))
                        break;
-               if (opt->nr_paths) {
-                       skip_uninteresting(t1, base, baselen, opt);
-                       skip_uninteresting(t2, base, baselen, opt);
+               if (opt->pathspec.nr) {
+                       if (!all_t1_interesting)
+                               skip_uninteresting(t1, &base, opt, &all_t1_interesting);
+                       if (!all_t2_interesting)
+                               skip_uninteresting(t2, &base, opt, &all_t2_interesting);
                }
                if (!t1->size) {
                        if (!t2->size)
                                break;
-                       show_entry(opt, "+", t2, base, baselen);
+                       show_entry(opt, "+", t2, &base);
                        update_tree_entry(t2);
                        continue;
                }
                if (!t2->size) {
-                       show_entry(opt, "-", t1, base, baselen);
+                       show_entry(opt, "-", t1, &base);
                        update_tree_entry(t1);
                        continue;
                }
-               switch (compare_tree_entry(t1, t2, base, baselen, opt)) {
+               switch (compare_tree_entry(t1, t2, &base, opt)) {
                case -1:
                        update_tree_entry(t1);
                        continue;
@@ -318,6 +188,8 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru
                }
                die("git diff-tree: internal error");
        }
+
+       strbuf_release(&base);
        return 0;
 }
 
@@ -348,7 +220,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
        DIFF_OPT_SET(&diff_opts, RECURSIVE);
        DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-       diff_opts.single_follow = opt->paths[0];
+       diff_opts.single_follow = opt->pathspec.raw[0];
        diff_opts.break_opt = opt->break_opt;
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
@@ -368,15 +240,16 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
                 * diff_queued_diff, we will also use that as the path in
                 * the future!
                 */
-               if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->paths[0])) {
+               if ((p->status == 'R' || p->status == 'C') &&
+                   !strcmp(p->two->path, opt->pathspec.raw[0])) {
                        /* Switch the file-pairs around */
                        q->queue[i] = choice;
                        choice = p;
 
                        /* Update the path we use from now on.. */
                        diff_tree_release_paths(opt);
-                       opt->paths[0] = xstrdup(p->one->path);
-                       diff_tree_setup_paths(opt->paths, opt);
+                       opt->pathspec.raw[0] = xstrdup(p->one->path);
+                       diff_tree_setup_paths(opt->pathspec.raw, opt);
 
                        /*
                         * The caller expects us to return a set of vanilla
@@ -451,36 +324,12 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_
        return retval;
 }
 
-static int count_paths(const char **paths)
-{
-       int i = 0;
-       while (*paths++)
-               i++;
-       return i;
-}
-
 void diff_tree_release_paths(struct diff_options *opt)
 {
-       free(opt->pathlens);
+       free_pathspec(&opt->pathspec);
 }
 
 void diff_tree_setup_paths(const char **p, struct diff_options *opt)
 {
-       opt->nr_paths = 0;
-       opt->pathlens = NULL;
-       opt->paths = NULL;
-
-       if (p) {
-               int i;
-
-               opt->paths = p;
-               opt->nr_paths = count_paths(p);
-               if (opt->nr_paths == 0) {
-                       opt->pathlens = NULL;
-                       return;
-               }
-               opt->pathlens = xmalloc(opt->nr_paths * sizeof(int));
-               for (i=0; i < opt->nr_paths; i++)
-                       opt->pathlens[i] = strlen(p[i]);
-       }
+       init_pathspec(&opt->pathspec, p);
 }
index 67a9a0c5a5bf7d7125765679318cfcd68c160da7..ddcf50c30a25e5c3695b41363c050d1c1f6580bf 100644 (file)
@@ -1,5 +1,7 @@
 #include "cache.h"
 #include "tree-walk.h"
+#include "unpack-trees.h"
+#include "dir.h"
 #include "tree.h"
 
 static const char *get_mode(const char *str, unsigned int *modep)
@@ -310,6 +312,7 @@ static void free_extended_entry(struct tree_desc_x *t)
 int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
 {
        int ret = 0;
+       int error = 0;
        struct name_entry *entry = xmalloc(n*sizeof(*entry));
        int i;
        struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
@@ -377,8 +380,11 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
                if (!mask)
                        break;
                ret = info->fn(n, mask, dirmask, entry, info);
-               if (ret < 0)
-                       break;
+               if (ret < 0) {
+                       error = ret;
+                       if (!info->show_all_errors)
+                               break;
+               }
                mask &= ret;
                ret = 0;
                for (i = 0; i < n; i++)
@@ -389,7 +395,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
        for (i = 0; i < n; i++)
                free_extended_entry(tx + i);
        free(tx);
-       return ret;
+       return error;
 }
 
 static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
@@ -450,3 +456,185 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
        free(tree);
        return retval;
 }
+
+static int match_entry(const struct name_entry *entry, int pathlen,
+                      const char *match, int matchlen,
+                      int *never_interesting)
+{
+       int m = -1; /* signals that we haven't called strncmp() */
+
+       if (*never_interesting) {
+               /*
+                * We have not seen any match that sorts later
+                * than the current path.
+                */
+
+               /*
+                * Does match sort strictly earlier than path
+                * with their common parts?
+                */
+               m = strncmp(match, entry->path,
+                           (matchlen < pathlen) ? matchlen : pathlen);
+               if (m < 0)
+                       return 0;
+
+               /*
+                * If we come here even once, that means there is at
+                * least one pathspec that would sort equal to or
+                * later than the path we are currently looking at.
+                * In other words, if we have never reached this point
+                * after iterating all pathspecs, it means all
+                * pathspecs are either outside of base, or inside the
+                * base but sorts strictly earlier than the current
+                * one.  In either case, they will never match the
+                * subsequent entries.  In such a case, we initialized
+                * the variable to -1 and that is what will be
+                * returned, allowing the caller to terminate early.
+                */
+               *never_interesting = 0;
+       }
+
+       if (pathlen > matchlen)
+               return 0;
+
+       if (matchlen > pathlen) {
+               if (match[pathlen] != '/')
+                       return 0;
+               if (!S_ISDIR(entry->mode))
+                       return 0;
+       }
+
+       if (m == -1)
+               /*
+                * we cheated and did not do strncmp(), so we do
+                * that here.
+                */
+               m = strncmp(match, entry->path, pathlen);
+
+       /*
+        * If common part matched earlier then it is a hit,
+        * because we rejected the case where path is not a
+        * leading directory and is shorter than match.
+        */
+       if (!m)
+               return 1;
+
+       return 0;
+}
+
+static int match_dir_prefix(const char *base, int baselen,
+                           const char *match, int matchlen)
+{
+       if (strncmp(base, match, matchlen))
+               return 0;
+
+       /*
+        * If the base is a subdirectory of a path which
+        * was specified, all of them are interesting.
+        */
+       if (!matchlen ||
+           base[matchlen] == '/' ||
+           match[matchlen - 1] == '/')
+               return 1;
+
+       /* Just a random prefix match */
+       return 0;
+}
+
+/*
+ * Is a tree entry interesting given the pathspec we have?
+ *
+ * Pre-condition: baselen == 0 || base[baselen-1] == '/'
+ *
+ * Return:
+ *  - 2 for "yes, and all subsequent entries will be"
+ *  - 1 for yes
+ *  - zero for no
+ *  - negative for "no, and no subsequent entries will be either"
+ */
+int tree_entry_interesting(const struct name_entry *entry,
+                          struct strbuf *base, int base_offset,
+                          const struct pathspec *ps)
+{
+       int i;
+       int pathlen, baselen = base->len - base_offset;
+       int never_interesting = ps->has_wildcard ? 0 : -1;
+
+       if (!ps->nr) {
+               if (!ps->recursive || ps->max_depth == -1)
+                       return 1;
+               return !!within_depth(base->buf + base_offset, baselen,
+                                     !!S_ISDIR(entry->mode),
+                                     ps->max_depth);
+       }
+
+       pathlen = tree_entry_len(entry->path, entry->sha1);
+
+       for (i = ps->nr-1; i >= 0; i--) {
+               const struct pathspec_item *item = ps->items+i;
+               const char *match = item->match;
+               const char *base_str = base->buf + base_offset;
+               int matchlen = item->len;
+
+               if (baselen >= matchlen) {
+                       /* If it doesn't match, move along... */
+                       if (!match_dir_prefix(base_str, baselen, match, matchlen))
+                               goto match_wildcards;
+
+                       if (!ps->recursive || ps->max_depth == -1)
+                               return 2;
+
+                       return !!within_depth(base_str + matchlen + 1,
+                                             baselen - matchlen - 1,
+                                             !!S_ISDIR(entry->mode),
+                                             ps->max_depth);
+               }
+
+               /* Does the base match? */
+               if (!strncmp(base_str, match, baselen)) {
+                       if (match_entry(entry, pathlen,
+                                       match + baselen, matchlen - baselen,
+                                       &never_interesting))
+                               return 1;
+
+                       if (ps->items[i].has_wildcard) {
+                               if (!fnmatch(match + baselen, entry->path, 0))
+                                       return 1;
+
+                               /*
+                                * Match all directories. We'll try to
+                                * match files later on.
+                                */
+                               if (ps->recursive && S_ISDIR(entry->mode))
+                                       return 1;
+                       }
+
+                       continue;
+               }
+
+match_wildcards:
+               if (!ps->items[i].has_wildcard)
+                       continue;
+
+               /*
+                * Concatenate base and entry->path into one and do
+                * fnmatch() on it.
+                */
+
+               strbuf_add(base, entry->path, pathlen);
+
+               if (!fnmatch(match, base->buf + base_offset, 0)) {
+                       strbuf_setlen(base, base_offset + baselen);
+                       return 1;
+               }
+               strbuf_setlen(base, base_offset + baselen);
+
+               /*
+                * Match all directories. We'll try to match files
+                * later on.
+                */
+               if (ps->recursive && S_ISDIR(entry->mode))
+                       return 1;
+       }
+       return never_interesting; /* No matches */
+}
index 42110a465f9a8c91d1bc643dfae7a9b9c32e3719..39524b7dba6a1d0b63c4cd2b42db59a27a030b21 100644 (file)
@@ -28,7 +28,10 @@ static inline int tree_entry_len(const char *name, const unsigned char *sha1)
 void update_tree_entry(struct tree_desc *);
 void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
 
-/* Helper function that does both of the above and returns true for success */
+/*
+ * Helper function that does both tree_entry_extract() and update_tree_entry()
+ * and returns true for success
+ */
 int tree_entry(struct tree_desc *, struct name_entry *);
 
 void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1);
@@ -45,6 +48,7 @@ struct traverse_info {
        unsigned long conflicts;
        traverse_callback_t fn;
        void *data;
+       int show_all_errors;
 };
 
 int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);
@@ -56,4 +60,6 @@ static inline int traverse_path_len(const struct traverse_info *info, const stru
        return info->pathlen + tree_entry_len(n->path, n->sha1);
 }
 
+extern int tree_entry_interesting(const struct name_entry *, struct strbuf *, int, const struct pathspec *ps);
+
 #endif
index f561d88156b827b15bad7dd4e00f944718149bd9..bf204b921040f411a22d0f17592e940517672c66 100644 (file)
  * Error messages expected by scripts out of plumbing commands such as
  * read-tree.  Non-scripted Porcelain is not required to use these messages
  * and in fact are encouraged to reword them to better suit their particular
- * situation better.  See how "git checkout" replaces not_uptodate_file to
- * explain why it does not allow switching between branches when you have
- * local changes, for example.
+ * situation better.  See how "git checkout" and "git merge" replaces
+ * them using setup_unpack_trees_porcelain(), for example.
  */
-static struct unpack_trees_error_msgs unpack_plumbing_errors = {
-       /* would_overwrite */
+const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
+       /* ERROR_WOULD_OVERWRITE */
        "Entry '%s' would be overwritten by merge. Cannot merge.",
 
-       /* not_uptodate_file */
+       /* ERROR_NOT_UPTODATE_FILE */
        "Entry '%s' not uptodate. Cannot merge.",
 
-       /* not_uptodate_dir */
+       /* ERROR_NOT_UPTODATE_DIR */
        "Updating '%s' would lose untracked files in it",
 
-       /* would_lose_untracked */
-       "Untracked working tree file '%s' would be %s by merge.",
+       /* ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN */
+       "Untracked working tree file '%s' would be overwritten by merge.",
 
-       /* bind_overlap */
+       /* ERROR_WOULD_LOSE_UNTRACKED_REMOVED */
+       "Untracked working tree file '%s' would be removed by merge.",
+
+       /* ERROR_BIND_OVERLAP */
        "Entry '%s' overlaps with '%s'.  Cannot bind.",
 
-       /* sparse_not_uptodate_file */
+       /* ERROR_SPARSE_NOT_UPTODATE_FILE */
        "Entry '%s' not uptodate. Cannot update sparse checkout.",
 
-       /* would_lose_orphaned */
-       "Working tree file '%s' would be %s by sparse checkout update.",
+       /* ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN */
+       "Working tree file '%s' would be overwritten by sparse checkout update.",
+
+       /* ERROR_WOULD_LOSE_ORPHANED_REMOVED */
+       "Working tree file '%s' would be removed by sparse checkout update.",
 };
 
-#define ERRORMSG(o,fld) \
-       ( ((o) && (o)->msgs.fld) \
-       ? ((o)->msgs.fld) \
-       : (unpack_plumbing_errors.fld) )
+#define ERRORMSG(o,type) \
+       ( ((o) && (o)->msgs[(type)]) \
+         ? ((o)->msgs[(type)])      \
+         : (unpack_plumbing_errors[(type)]) )
+
+void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
+                                 const char *cmd)
+{
+       int i;
+       const char **msgs = opts->msgs;
+       const char *msg;
+       char *tmp;
+       const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
+       if (advice_commit_before_merge)
+               msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
+                       "Please, commit your changes or stash them before you can %s.";
+       else
+               msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
+       sprintf(tmp, msg, cmd, cmd2);
+       msgs[ERROR_WOULD_OVERWRITE] = tmp;
+       msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
+
+       msgs[ERROR_NOT_UPTODATE_DIR] =
+               "Updating the following directories would lose untracked files in it:\n%s";
+
+       if (advice_commit_before_merge)
+               msg = "The following untracked working tree files would be %s by %s:\n%%s"
+                       "Please move or remove them before you can %s.";
+       else
+               msg = "The following untracked working tree files would be %s by %s:\n%%s";
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
+       sprintf(tmp, msg, "removed", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
+       sprintf(tmp, msg, "overwritten", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
+
+       /*
+        * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
+        * cannot easily display it as a list.
+        */
+       msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'.  Cannot bind.";
+
+       msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
+               "Cannot update sparse checkout: the following entries are not up-to-date:\n%s";
+       msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
+               "The following Working tree files would be overwritten by sparse checkout update:\n%s";
+       msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
+               "The following Working tree files would be removed by sparse checkout update:\n%s";
+
+       opts->show_all_errors = 1;
+       /* rejected paths may not have a static buffer */
+       for (i = 0; i < ARRAY_SIZE(opts->unpack_rejects); i++)
+               opts->unpack_rejects[i].strdup_strings = 1;
+}
 
 static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
        unsigned int set, unsigned int clear)
@@ -53,19 +110,65 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 
        clear |= CE_HASHED | CE_UNHASHED;
 
+       if (set & CE_REMOVE)
+               set |= CE_WT_REMOVE;
+
        memcpy(new, ce, size);
        new->next = NULL;
        new->ce_flags = (new->ce_flags & ~clear) | set;
        add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
+/*
+ * add error messages on path <path>
+ * corresponding to the type <e> with the message <msg>
+ * indicating if it should be display in porcelain or not
+ */
+static int add_rejected_path(struct unpack_trees_options *o,
+                            enum unpack_trees_error_types e,
+                            const char *path)
+{
+       if (!o->show_all_errors)
+               return error(ERRORMSG(o, e), path);
+
+       /*
+        * Otherwise, insert in a list for future display by
+        * display_error_msgs()
+        */
+       string_list_append(&o->unpack_rejects[e], path);
+       return -1;
+}
+
+/*
+ * display all the error messages stored in a nice way
+ */
+static void display_error_msgs(struct unpack_trees_options *o)
+{
+       int e, i;
+       int something_displayed = 0;
+       for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {
+               struct string_list *rejects = &o->unpack_rejects[e];
+               if (rejects->nr > 0) {
+                       struct strbuf path = STRBUF_INIT;
+                       something_displayed = 1;
+                       for (i = 0; i < rejects->nr; i++)
+                               strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
+                       error(ERRORMSG(o, e), path.buf);
+                       strbuf_release(&path);
+               }
+               string_list_clear(rejects, 0);
+       }
+       if (something_displayed)
+               printf("Aborting\n");
+}
+
 /*
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
  */
 static void unlink_entry(struct cache_entry *ce)
 {
-       if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
+       if (!check_leading_path(ce->name, ce_namelen(ce)))
                return;
        if (remove_or_warn(ce->ce_mode, ce->name))
                return;
@@ -84,7 +187,7 @@ static int check_updates(struct unpack_trees_options *o)
        if (o->update && o->verbose_update) {
                for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
                        struct cache_entry *ce = index->cache[cnt];
-                       if (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE))
+                       if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
                                total++;
                }
 
@@ -104,12 +207,6 @@ static int check_updates(struct unpack_trees_options *o)
                                unlink_entry(ce);
                        continue;
                }
-
-               if (ce->ce_flags & CE_REMOVE) {
-                       display_progress(progress, ++cnt);
-                       if (o->update)
-                               unlink_entry(ce);
-               }
        }
        remove_marked_cache_entries(&o->result);
        remove_scheduled_dirs();
@@ -132,37 +229,42 @@ static int check_updates(struct unpack_trees_options *o)
 }
 
 static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);
-static int verify_absent_sparse(struct cache_entry *ce, const char *action, struct unpack_trees_options *o);
-
-static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_trees_options *o)
-{
-       const char *basename;
-
-       if (ce_stage(ce))
-               return 0;
-
-       basename = strrchr(ce->name, '/');
-       basename = basename ? basename+1 : ce->name;
-       return excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0;
-}
+static int verify_absent_sparse(struct cache_entry *ce, enum unpack_trees_error_types, struct unpack_trees_options *o);
 
 static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
 {
        int was_skip_worktree = ce_skip_worktree(ce);
 
-       if (will_have_skip_worktree(ce, o))
+       if (ce->ce_flags & CE_NEW_SKIP_WORKTREE)
                ce->ce_flags |= CE_SKIP_WORKTREE;
        else
                ce->ce_flags &= ~CE_SKIP_WORKTREE;
 
        /*
-        * We only care about files getting into the checkout area
-        * If merge strategies want to remove some, go ahead, this
-        * flag will be removed eventually in unpack_trees() if it's
-        * outside checkout area.
+        * if (!was_skip_worktree && !ce_skip_worktree()) {
+        *      This is perfectly normal. Move on;
+        * }
         */
-       if (ce->ce_flags & CE_REMOVE)
-               return 0;
+
+       /*
+        * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
+        * area as a result of ce_skip_worktree() shortcuts in
+        * verify_absent() and verify_uptodate().
+        * Make sure they don't modify worktree if they are already
+        * outside checkout area
+        */
+       if (was_skip_worktree && ce_skip_worktree(ce)) {
+               ce->ce_flags &= ~CE_UPDATE;
+
+               /*
+                * By default, when CE_REMOVE is on, CE_WT_REMOVE is also
+                * on to get that file removed from both index and worktree.
+                * If that file is already outside worktree area, don't
+                * bother remove it.
+                */
+               if (ce->ce_flags & CE_REMOVE)
+                       ce->ce_flags &= ~CE_WT_REMOVE;
+       }
 
        if (!was_skip_worktree && ce_skip_worktree(ce)) {
                /*
@@ -175,7 +277,7 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
                ce->ce_flags |= CE_WT_REMOVE;
        }
        if (was_skip_worktree && !ce_skip_worktree(ce)) {
-               if (verify_absent_sparse(ce, "overwritten", o))
+               if (verify_absent_sparse(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
                        return -1;
                ce->ce_flags |= CE_UPDATE;
        }
@@ -208,7 +310,7 @@ static void mark_all_ce_unused(struct index_state *index)
 {
        int i;
        for (i = 0; i < index->cache_nr; i++)
-               index->cache[i]->ce_flags &= ~CE_UNPACKED;
+               index->cache[i]->ce_flags &= ~(CE_UNPACKED | CE_ADDED | CE_NEW_SKIP_WORKTREE);
 }
 
 static int locate_in_src_index(struct cache_entry *ce,
@@ -709,9 +811,177 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
        return mask;
 }
 
+/* Whole directory matching */
+static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
+                             char *prefix, int prefix_len,
+                             char *basename,
+                             int select_mask, int clear_mask,
+                             struct exclude_list *el)
+{
+       struct cache_entry **cache_end = cache + nr;
+       int dtype = DT_DIR;
+       int ret = excluded_from_list(prefix, prefix_len, basename, &dtype, el);
+
+       prefix[prefix_len++] = '/';
+
+       /* included, no clearing for any entries under this directory */
+       if (!ret) {
+               for (; cache != cache_end; cache++) {
+                       struct cache_entry *ce = *cache;
+                       if (strncmp(ce->name, prefix, prefix_len))
+                               break;
+               }
+               return nr - (cache_end - cache);
+       }
+
+       /* excluded, clear all selected entries under this directory. */
+       if (ret == 1) {
+               for (; cache != cache_end; cache++) {
+                       struct cache_entry *ce = *cache;
+                       if (select_mask && !(ce->ce_flags & select_mask))
+                               continue;
+                       if (strncmp(ce->name, prefix, prefix_len))
+                               break;
+                       ce->ce_flags &= ~clear_mask;
+               }
+               return nr - (cache_end - cache);
+       }
+
+       return 0;
+}
+
+/*
+ * Traverse the index, find every entry that matches according to
+ * o->el. Do "ce_flags &= ~clear_mask" on those entries. Return the
+ * number of traversed entries.
+ *
+ * If select_mask is non-zero, only entries whose ce_flags has on of
+ * those bits enabled are traversed.
+ *
+ * cache       : pointer to an index entry
+ * prefix_len  : an offset to its path
+ *
+ * The current path ("prefix") including the trailing '/' is
+ *   cache[0]->name[0..(prefix_len-1)]
+ * Top level path has prefix_len zero.
+ */
+static int clear_ce_flags_1(struct cache_entry **cache, int nr,
+                           char *prefix, int prefix_len,
+                           int select_mask, int clear_mask,
+                           struct exclude_list *el)
+{
+       struct cache_entry **cache_end = cache + nr;
+
+       /*
+        * Process all entries that have the given prefix and meet
+        * select_mask condition
+        */
+       while(cache != cache_end) {
+               struct cache_entry *ce = *cache;
+               const char *name, *slash;
+               int len, dtype;
+
+               if (select_mask && !(ce->ce_flags & select_mask)) {
+                       cache++;
+                       continue;
+               }
+
+               if (prefix_len && strncmp(ce->name, prefix, prefix_len))
+                       break;
+
+               name = ce->name + prefix_len;
+               slash = strchr(name, '/');
+
+               /* If it's a directory, try whole directory match first */
+               if (slash) {
+                       int processed;
+
+                       len = slash - name;
+                       memcpy(prefix + prefix_len, name, len);
+
+                       /*
+                        * terminate the string (no trailing slash),
+                        * clear_c_f_dir needs it
+                        */
+                       prefix[prefix_len + len] = '\0';
+                       processed = clear_ce_flags_dir(cache, cache_end - cache,
+                                                      prefix, prefix_len + len,
+                                                      prefix + prefix_len,
+                                                      select_mask, clear_mask,
+                                                      el);
+
+                       /* clear_c_f_dir eats a whole dir already? */
+                       if (processed) {
+                               cache += processed;
+                               continue;
+                       }
+
+                       prefix[prefix_len + len++] = '/';
+                       cache += clear_ce_flags_1(cache, cache_end - cache,
+                                                 prefix, prefix_len + len,
+                                                 select_mask, clear_mask, el);
+                       continue;
+               }
+
+               /* Non-directory */
+               dtype = ce_to_dtype(ce);
+               if (excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el) > 0)
+                       ce->ce_flags &= ~clear_mask;
+               cache++;
+       }
+       return nr - (cache_end - cache);
+}
+
+static int clear_ce_flags(struct cache_entry **cache, int nr,
+                           int select_mask, int clear_mask,
+                           struct exclude_list *el)
+{
+       char prefix[PATH_MAX];
+       return clear_ce_flags_1(cache, nr,
+                               prefix, 0,
+                               select_mask, clear_mask,
+                               el);
+}
+
+/*
+ * Set/Clear CE_NEW_SKIP_WORKTREE according to $GIT_DIR/info/sparse-checkout
+ */
+static void mark_new_skip_worktree(struct exclude_list *el,
+                                  struct index_state *the_index,
+                                  int select_flag, int skip_wt_flag)
+{
+       int i;
+
+       /*
+        * 1. Pretend the narrowest worktree: only unmerged entries
+        * are checked out
+        */
+       for (i = 0; i < the_index->cache_nr; i++) {
+               struct cache_entry *ce = the_index->cache[i];
+
+               if (select_flag && !(ce->ce_flags & select_flag))
+                       continue;
+
+               if (!ce_stage(ce))
+                       ce->ce_flags |= skip_wt_flag;
+               else
+                       ce->ce_flags &= ~skip_wt_flag;
+       }
+
+       /*
+        * 2. Widen worktree according to sparse-checkout file.
+        * Matched entries will have skip_wt_flag cleared (i.e. "in")
+        */
+       clear_ce_flags(the_index->cache, the_index->cache_nr,
+                      select_flag, skip_wt_flag, el);
+}
+
+static int verify_absent(struct cache_entry *, enum unpack_trees_error_types, struct unpack_trees_options *);
 /*
  * N-way merge "len" trees.  Returns 0 on success, -1 on failure to manipulate the
  * resulting index, -2 on failure to reflect the changes to the work tree.
+ *
+ * CE_ADDED, CE_UNPACKED and CE_NEW_SKIP_WORKTREE are used internally
  */
 int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
 {
@@ -744,6 +1014,12 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        o->merge_size = len;
        mark_all_ce_unused(o->src_index);
 
+       /*
+        * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
+        */
+       if (!o->skip_sparse_checkout)
+               mark_new_skip_worktree(o->el, o->src_index, 0, CE_NEW_SKIP_WORKTREE);
+
        if (!dfc)
                dfc = xcalloc(1, cache_entry_size(0));
        o->df_conflict_entry = dfc;
@@ -755,6 +1031,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                setup_traverse_info(&info, prefix);
                info.fn = unpack_callback;
                info.data = o;
+               info.show_all_errors = o->show_all_errors;
 
                if (o->prefix) {
                        /*
@@ -796,21 +1073,34 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 
        if (!o->skip_sparse_checkout) {
                int empty_worktree = 1;
-               for (i = 0;i < o->result.cache_nr;i++) {
+
+               /*
+                * Sparse checkout loop #2: set NEW_SKIP_WORKTREE on entries not in loop #1
+                * If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
+                * so apply_sparse_checkout() won't attempt to remove it from worktree
+                */
+               mark_new_skip_worktree(o->el, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
+
+               for (i = 0; i < o->result.cache_nr; i++) {
                        struct cache_entry *ce = o->result.cache[i];
 
+                       /*
+                        * Entries marked with CE_ADDED in merged_entry() do not have
+                        * verify_absent() check (the check is effectively disabled
+                        * because CE_NEW_SKIP_WORKTREE is set unconditionally).
+                        *
+                        * Do the real check now because we have had
+                        * correct CE_NEW_SKIP_WORKTREE
+                        */
+                       if (ce->ce_flags & CE_ADDED &&
+                           verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
+                                       return -1;
+
                        if (apply_sparse_checkout(ce, o)) {
                                ret = -1;
                                goto done;
                        }
-                       /*
-                        * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
-                        * area as a result of ce_skip_worktree() shortcuts in
-                        * verify_absent() and verify_uptodate(). Clear them.
-                        */
-                       if (ce_skip_worktree(ce))
-                               ce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);
-                       else
+                       if (!ce_skip_worktree(ce))
                                empty_worktree = 0;
 
                }
@@ -826,14 +1116,12 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                *o->dst_index = o->result;
 
 done:
-       for (i = 0;i < el.nr;i++)
-               free(el.excludes[i]);
-       if (el.excludes)
-               free(el.excludes);
-
+       free_excludes(&el);
        return ret;
 
 return_failed:
+       if (o->show_all_errors)
+               display_error_msgs(o);
        mark_all_ce_unused(o->src_index);
        ret = unpack_failed(o, NULL);
        goto done;
@@ -843,7 +1131,7 @@ return_failed:
 
 static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-       return error(ERRORMSG(o, would_overwrite), ce->name);
+       return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
 }
 
 static int same(struct cache_entry *a, struct cache_entry *b)
@@ -865,7 +1153,7 @@ static int same(struct cache_entry *a, struct cache_entry *b)
  */
 static int verify_uptodate_1(struct cache_entry *ce,
                                   struct unpack_trees_options *o,
-                                  const char *error_msg)
+                                  enum unpack_trees_error_types error_type)
 {
        struct stat st;
 
@@ -890,21 +1178,21 @@ static int verify_uptodate_1(struct cache_entry *ce,
        if (errno == ENOENT)
                return 0;
        return o->gently ? -1 :
-               error(error_msg, ce->name);
+               add_rejected_path(o, error_type, ce->name);
 }
 
 static int verify_uptodate(struct cache_entry *ce,
                           struct unpack_trees_options *o)
 {
-       if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
+       if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
                return 0;
-       return verify_uptodate_1(ce, o, ERRORMSG(o, not_uptodate_file));
+       return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
 }
 
 static int verify_uptodate_sparse(struct cache_entry *ce,
                                  struct unpack_trees_options *o)
 {
-       return verify_uptodate_1(ce, o, ERRORMSG(o, sparse_not_uptodate_file));
+       return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
 }
 
 static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
@@ -920,13 +1208,15 @@ static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_optio
  * Currently, git does not checkout subprojects during a superproject
  * checkout, so it is not going to overwrite anything.
  */
-static int verify_clean_submodule(struct cache_entry *ce, const char *action,
+static int verify_clean_submodule(struct cache_entry *ce,
+                                     enum unpack_trees_error_types error_type,
                                      struct unpack_trees_options *o)
 {
        return 0;
 }
 
-static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
+static int verify_clean_subdirectory(struct cache_entry *ce,
+                                     enum unpack_trees_error_types error_type,
                                      struct unpack_trees_options *o)
 {
        /*
@@ -947,7 +1237,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
                 */
                if (!hashcmp(sha1, ce->sha1))
                        return 0;
-               return verify_clean_submodule(ce, action, o);
+               return verify_clean_submodule(ce, error_type, o);
        }
 
        /*
@@ -991,7 +1281,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
        i = read_directory(&d, pathbuf, namelen+1, NULL);
        if (i)
                return o->gently ? -1 :
-                       error(ERRORMSG(o, not_uptodate_dir), ce->name);
+                       add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
        free(pathbuf);
        return cnt;
 }
@@ -1004,91 +1294,122 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
  * See if we can find a case-insensitive match in the index that also
  * matches the stat information, and assume it's that other file!
  */
-static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst, struct stat *st)
+static int icase_exists(struct unpack_trees_options *o, const char *name, int len, struct stat *st)
 {
        struct cache_entry *src;
 
-       src = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1);
+       src = index_name_exists(o->src_index, name, len, 1);
        return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
 }
 
-/*
- * We do not want to remove or overwrite a working tree file that
- * is not tracked, unless it is ignored.
- */
-static int verify_absent_1(struct cache_entry *ce, const char *action,
-                                struct unpack_trees_options *o,
-                                const char *error_msg)
+static int check_ok_to_remove(const char *name, int len, int dtype,
+                             struct cache_entry *ce, struct stat *st,
+                             enum unpack_trees_error_types error_type,
+                             struct unpack_trees_options *o)
 {
-       struct stat st;
+       struct cache_entry *result;
 
-       if (o->index_only || o->reset || !o->update)
+       /*
+        * It may be that the 'lstat()' succeeded even though
+        * target 'ce' was absent, because there is an old
+        * entry that is different only in case..
+        *
+        * Ignore that lstat() if it matches.
+        */
+       if (ignore_case && icase_exists(o, name, len, st))
                return 0;
 
-       if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
+       if (o->dir && excluded(o->dir, name, &dtype))
+               /*
+                * ce->name is explicitly excluded, so it is Ok to
+                * overwrite it.
+                */
                return 0;
-
-       if (!lstat(ce->name, &st)) {
-               int dtype = ce_to_dtype(ce);
-               struct cache_entry *result;
-
+       if (S_ISDIR(st->st_mode)) {
                /*
-                * It may be that the 'lstat()' succeeded even though
-                * target 'ce' was absent, because there is an old
-                * entry that is different only in case..
-                *
-                * Ignore that lstat() if it matches.
+                * We are checking out path "foo" and
+                * found "foo/." in the working tree.
+                * This is tricky -- if we have modified
+                * files that are in "foo/" we would lose
+                * them.
                 */
-               if (ignore_case && icase_exists(o, ce, &st))
-                       return 0;
+               if (verify_clean_subdirectory(ce, error_type, o) < 0)
+                       return -1;
+               return 0;
+       }
 
-               if (o->dir && excluded(o->dir, ce->name, &dtype))
-                       /*
-                        * ce->name is explicitly excluded, so it is Ok to
-                        * overwrite it.
-                        */
-                       return 0;
-               if (S_ISDIR(st.st_mode)) {
-                       /*
-                        * We are checking out path "foo" and
-                        * found "foo/." in the working tree.
-                        * This is tricky -- if we have modified
-                        * files that are in "foo/" we would lose
-                        * them.
-                        */
-                       if (verify_clean_subdirectory(ce, action, o) < 0)
-                               return -1;
+       /*
+        * The previous round may already have decided to
+        * delete this path, which is in a subdirectory that
+        * is being replaced with a blob.
+        */
+       result = index_name_exists(&o->result, name, len, 0);
+       if (result) {
+               if (result->ce_flags & CE_REMOVE)
                        return 0;
-               }
+       }
 
-               /*
-                * The previous round may already have decided to
-                * delete this path, which is in a subdirectory that
-                * is being replaced with a blob.
-                */
-               result = index_name_exists(&o->result, ce->name, ce_namelen(ce), 0);
-               if (result) {
-                       if (result->ce_flags & CE_REMOVE)
-                               return 0;
-               }
+       return o->gently ? -1 :
+               add_rejected_path(o, error_type, name);
+}
 
-               return o->gently ? -1 :
-                       error(ERRORMSG(o, would_lose_untracked), ce->name, action);
+/*
+ * We do not want to remove or overwrite a working tree file that
+ * is not tracked, unless it is ignored.
+ */
+static int verify_absent_1(struct cache_entry *ce,
+                                enum unpack_trees_error_types error_type,
+                                struct unpack_trees_options *o)
+{
+       int len;
+       struct stat st;
+
+       if (o->index_only || o->reset || !o->update)
+               return 0;
+
+       len = check_leading_path(ce->name, ce_namelen(ce));
+       if (!len)
+               return 0;
+       else if (len > 0) {
+               char path[PATH_MAX + 1];
+               memcpy(path, ce->name, len);
+               path[len] = 0;
+               if (lstat(path, &st))
+                       return error("cannot stat '%s': %s", path,
+                                       strerror(errno));
+
+               return check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st,
+                               error_type, o);
+       } else if (lstat(ce->name, &st)) {
+               if (errno != ENOENT)
+                       return error("cannot stat '%s': %s", ce->name,
+                                    strerror(errno));
+               return 0;
+       } else {
+               return check_ok_to_remove(ce->name, ce_namelen(ce),
+                                         ce_to_dtype(ce), ce, &st,
+                                         error_type, o);
        }
-       return 0;
 }
-static int verify_absent(struct cache_entry *ce, const char *action,
+
+static int verify_absent(struct cache_entry *ce,
+                        enum unpack_trees_error_types error_type,
                         struct unpack_trees_options *o)
 {
-       if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
+       if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
                return 0;
-       return verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked));
+       return verify_absent_1(ce, error_type, o);
 }
 
-static int verify_absent_sparse(struct cache_entry *ce, const char *action,
+static int verify_absent_sparse(struct cache_entry *ce,
+                        enum unpack_trees_error_types error_type,
                         struct unpack_trees_options *o)
 {
-       return verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_orphaned));
+       enum unpack_trees_error_types orphaned_error = error_type;
+       if (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN)
+               orphaned_error = ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN;
+
+       return verify_absent_1(ce, orphaned_error, o);
 }
 
 static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
@@ -1097,7 +1418,22 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
        int update = CE_UPDATE;
 
        if (!old) {
-               if (verify_absent(merge, "overwritten", o))
+               /*
+                * New index entries. In sparse checkout, the following
+                * verify_absent() will be delayed until after
+                * traverse_trees() finishes in unpack_trees(), then:
+                *
+                *  - CE_NEW_SKIP_WORKTREE will be computed correctly
+                *  - verify_absent() be called again, this time with
+                *    correct CE_NEW_SKIP_WORKTREE
+                *
+                * verify_absent() call here does nothing in sparse
+                * checkout (i.e. o->skip_sparse_checkout == 0)
+                */
+               update |= CE_ADDED;
+               merge->ce_flags |= CE_NEW_SKIP_WORKTREE;
+
+               if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
                        return -1;
                invalidate_ce_path(merge, o);
        } else if (!(old->ce_flags & CE_CONFLICTED)) {
@@ -1114,8 +1450,8 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                } else {
                        if (verify_uptodate(old, o))
                                return -1;
-                       if (ce_skip_worktree(old))
-                               update |= CE_SKIP_WORKTREE;
+                       /* Migrate old flags over */
+                       update |= old->ce_flags & (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
                        invalidate_ce_path(old, o);
                }
        } else {
@@ -1135,7 +1471,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
 {
        /* Did it exist in the index? */
        if (!old) {
-               if (verify_absent(ce, "removed", o))
+               if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))
                        return -1;
                return 0;
        }
@@ -1284,7 +1620,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
                        if (index)
                                return deleted_entry(index, index, o);
                        if (ce && !head_deleted) {
-                               if (verify_absent(ce, "removed", o))
+                               if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))
                                        return -1;
                        }
                        return 0;
@@ -1417,7 +1753,7 @@ int bind_merge(struct cache_entry **src,
                             o->merge_size);
        if (a && old)
                return o->gently ? -1 :
-                       error(ERRORMSG(o, bind_overlap), a->name, old->name);
+                       error(ERRORMSG(o, ERROR_BIND_OVERLAP), a->name, old->name);
        if (!a)
                return keep_entry(old, o);
        else
index ef70eab39025fcdaccda059692ae447a13fa0aeb..cd11a08365ab3e27b1321b3df87bcab6b9278f90 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef UNPACK_TREES_H
 #define UNPACK_TREES_H
 
+#include "string-list.h"
+
 #define MAX_UNPACK_TREES 8
 
 struct unpack_trees_options;
@@ -9,16 +11,26 @@ struct exclude_list;
 typedef int (*merge_fn_t)(struct cache_entry **src,
                struct unpack_trees_options *options);
 
-struct unpack_trees_error_msgs {
-       const char *would_overwrite;
-       const char *not_uptodate_file;
-       const char *not_uptodate_dir;
-       const char *would_lose_untracked;
-       const char *bind_overlap;
-       const char *sparse_not_uptodate_file;
-       const char *would_lose_orphaned;
+enum unpack_trees_error_types {
+       ERROR_WOULD_OVERWRITE = 0,
+       ERROR_NOT_UPTODATE_FILE,
+       ERROR_NOT_UPTODATE_DIR,
+       ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN,
+       ERROR_WOULD_LOSE_UNTRACKED_REMOVED,
+       ERROR_BIND_OVERLAP,
+       ERROR_SPARSE_NOT_UPTODATE_FILE,
+       ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN,
+       ERROR_WOULD_LOSE_ORPHANED_REMOVED,
+       NB_UNPACK_TREES_ERROR_TYPES
 };
 
+/*
+ * Sets the list of user-friendly error messages to be used by the
+ * command "cmd" (either merge or checkout), and show_all_errors to 1.
+ */
+void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
+                                 const char *cmd);
+
 struct unpack_trees_options {
        unsigned int reset,
                     merge,
@@ -33,12 +45,18 @@ struct unpack_trees_options {
                     diff_index_cached,
                     debug_unpack,
                     skip_sparse_checkout,
-                    gently;
+                    gently,
+                    show_all_errors;
        const char *prefix;
        int cache_bottom;
        struct dir_struct *dir;
        merge_fn_t fn;
-       struct unpack_trees_error_msgs msgs;
+       const char *msgs[NB_UNPACK_TREES_ERROR_TYPES];
+       /*
+        * Store error messages in an array, each case
+        * corresponding to a error message type
+        */
+       struct string_list unpack_rejects[NB_UNPACK_TREES_ERROR_TYPES];
 
        int head_idx;
        int merge_size;
index fc79ddef255a49176acab52c6492c1086b4e6e44..b40a43f27d0e16f0305153f984741938b3a6ab89 100644 (file)
@@ -11,7 +11,7 @@
 #include "list-objects.h"
 #include "run-command.h"
 
-static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=nn] <dir>";
+static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
 
 /* bits #0..7 in revision.h, #8..10 in commit.c */
 #define THEY_HAVE      (1u << 11)
@@ -366,7 +366,7 @@ static int reachable(struct commit *want)
 {
        struct commit_list *work = NULL;
 
-       insert_by_date(want, &work);
+       commit_list_insert_by_date(want, &work);
        while (work) {
                struct commit_list *list = work->next;
                struct commit *commit = work->item;
@@ -387,7 +387,7 @@ static int reachable(struct commit *want)
                for (list = commit->parents; list; list = list->next) {
                        struct commit *parent = list->item;
                        if (!(parent->object.flags & REACHABLE))
-                               insert_by_date(parent, &work);
+                               commit_list_insert_by_date(parent, &work);
                }
        }
        want->object.flags |= REACHABLE;
@@ -482,7 +482,7 @@ static int get_common_commits(void)
 
 static void receive_needs(void)
 {
-       struct object_array shallows = {0, 0, NULL};
+       struct object_array shallows = OBJECT_ARRAY_INIT;
        static char line[1000];
        int len, depth = 0;
 
diff --git a/url.c b/url.c
index 230623657a0e06975c4721a9fddb117ba84883b1..6a5495960f03ded65f0f5f8b8bd5c7cd98c0b05e 100644 (file)
--- a/url.c
+++ b/url.c
@@ -67,7 +67,8 @@ static int url_decode_char(const char *q)
        return val;
 }
 
-static char *url_decode_internal(const char **query, const char *stop_at, struct strbuf *out)
+static char *url_decode_internal(const char **query, const char *stop_at,
+                                struct strbuf *out, int decode_plus)
 {
        const char *q = *query;
 
@@ -90,7 +91,7 @@ static char *url_decode_internal(const char **query, const char *stop_at, struct
                        }
                }
 
-               if (c == '+')
+               if (decode_plus && c == '+')
                        strbuf_addch(out, ' ');
                else
                        strbuf_addch(out, c);
@@ -110,17 +111,31 @@ char *url_decode(const char *url)
                strbuf_add(&out, url, colon - url);
                url = colon;
        }
-       return url_decode_internal(&url, NULL, &out);
+       return url_decode_internal(&url, NULL, &out, 0);
 }
 
 char *url_decode_parameter_name(const char **query)
 {
        struct strbuf out = STRBUF_INIT;
-       return url_decode_internal(query, "&=", &out);
+       return url_decode_internal(query, "&=", &out, 1);
 }
 
 char *url_decode_parameter_value(const char **query)
 {
        struct strbuf out = STRBUF_INIT;
-       return url_decode_internal(query, "&", &out);
+       return url_decode_internal(query, "&", &out, 1);
+}
+
+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, "/");
+}
+
+void str_end_url_with_slash(const char *url, char **dest) {
+       struct strbuf buf = STRBUF_INIT;
+       end_url_with_slash(&buf, url);
+       free(*dest);
+       *dest = strbuf_detach(&buf, NULL);
 }
diff --git a/url.h b/url.h
index 15817f8f9385c217b22706df64117fcc53c8a87f..7100e3215a97b234c8ab93b76f7f86518f7c3382 100644 (file)
--- a/url.h
+++ b/url.h
@@ -7,4 +7,7 @@ extern char *url_decode(const char *url);
 extern char *url_decode_parameter_name(const char **query);
 extern char *url_decode_parameter_value(const char **query);
 
+extern void end_url_with_slash(struct strbuf *buf, const char *url);
+extern void str_end_url_with_slash(const char *url, char **dest);
+
 #endif /* URL_H */
index c49cc1b67e1164c561fb20839272bd8b1f7dde6d..9ebf231ea5ee2e7fd1f71c9557a6537d29428d8d 100644 (file)
@@ -9,7 +9,23 @@ static int drivers_alloc;
 
 #define PATTERNS(name, pattern, word_regex)                    \
        { name, NULL, -1, { pattern, REG_EXTENDED }, word_regex }
+#define IPATTERN(name, pattern, word_regex)                    \
+       { name, NULL, -1, { pattern, REG_EXTENDED | REG_ICASE }, word_regex }
 static struct userdiff_driver builtin_drivers[] = {
+IPATTERN("fortran",
+        "!^([C*]|[ \t]*!)\n"
+        "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n"
+        "^[ \t]*((END[ \t]+)?(PROGRAM|MODULE|BLOCK[ \t]+DATA"
+               "|([^'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$",
+        /* -- */
+        "[a-zA-Z][a-zA-Z0-9_]*"
+        "|\\.([Ee][Qq]|[Nn][Ee]|[Gg][TtEe]|[Ll][TtEe]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Aa][Nn][Dd]|[Oo][Rr]|[Nn]?[Ee][Qq][Vv]|[Nn][Oo][Tt])\\."
+        /* numbers and format statements like 2E14.4, or ES12.6, 9X.
+         * Don't worry about format statements without leading digits since
+         * they would have been matched above as a variable anyway. */
+        "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?"
+        "|//|\\*\\*|::|[/<>=]="
+        "|[^[:space:]]|[\x80-\xff]+"),
 PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
         "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"),
 PATTERNS("java",
@@ -36,7 +52,7 @@ PATTERNS("objc",
         "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
         "|[^[:space:]]|[\x80-\xff]+"),
 PATTERNS("pascal",
-        "^((procedure|function|constructor|destructor|interface|"
+        "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|"
                "implementation|initialization|finalization)[ \t]*.*)$"
         "\n"
         "^(.*=[ \t]*(class|record).*)$",
@@ -45,6 +61,23 @@ PATTERNS("pascal",
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
         "|<>|<=|>=|:=|\\.\\."
         "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("perl",
+        "^[ \t]*package .*;\n"
+        "^[ \t]*sub .* \\{\n"
+        "^[A-Z]+ \\{\n"        /* BEGIN, END, ... */
+        "^=head[0-9] ",        /* POD */
+        /* -- */
+        "[[:alpha:]_'][[:alnum:]_']*"
+        "|0[xb]?[0-9a-fA-F_]*"
+        /* taking care not to interpret 3..5 as (3.)(.5) */
+        "|[0-9a-fA-F_]+(\\.[0-9a-fA-F_]+)?([eE][-+]?[0-9_]+)?"
+        "|=>|-[rwxoRWXOezsfdlpSugkbctTBMAC>]|~~|::"
+        "|&&=|\\|\\|=|//=|\\*\\*="
+        "|&&|\\|\\||//|\\+\\+|--|\\*\\*|\\.\\.\\.?"
+        "|[-+*/%.^&<>=!|]="
+        "|=~|!~"
+        "|<<|<>|<=>|>>"
+        "|[^[:space:]]"),
 PATTERNS("php",
         "^[\t ]*(((public|protected|private|static)[\t ]+)*function.*)$\n"
         "^[\t ]*(class.*)$",
@@ -58,14 +91,14 @@ PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$",
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
         "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"
-        "|[^[:space:]|[\x80-\xff]+"),
+        "|[^[:space:]]|[\x80-\xff]+"),
         /* -- */
 PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
         /* -- */
         "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
         "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"
-        "|[^[:space:]|[\x80-\xff]+"),
+        "|[^[:space:]]|[\x80-\xff]+"),
 PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
         "[={}\"]|[^={}\" \t]+"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
@@ -82,9 +115,26 @@ PATTERNS("cpp",
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
         "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
         "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("csharp",
+        /* Keywords */
+        "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
+        /* Methods and constructors */
+        "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
+        /* Properties */
+        "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+        /* Type definitions */
+        "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
+        /* Namespace */
+        "^[ \t]*(namespace[ \t]+.*)$",
+        /* -- */
+        "[a-zA-Z_][a-zA-Z0-9_]*"
+        "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
+        "|[^[:space:]]|[\x80-\xff]+"),
 { "default", NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
+#undef IPATTERN
 
 static struct userdiff_driver driver_true = {
        "diff=true",
diff --git a/vcs-svn/LICENSE b/vcs-svn/LICENSE
new file mode 100644 (file)
index 0000000..0a5e3c4
--- /dev/null
@@ -0,0 +1,33 @@
+Copyright (C) 2010 David Barr <david.barr@cordelta.com>.
+All rights reserved.
+
+Copyright (C) 2008 Jason Evans <jasone@canonware.com>.
+All rights reserved.
+
+Copyright (C) 2005 Stefan Hegny, hydrografix Consulting GmbH,
+Frankfurt/Main, Germany
+and others, see http://svn2cc.sarovar.org
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice(s), this list of conditions and the following disclaimer
+   unmodified other than the allowable addition of one or more
+   copyright notices.
+2. Redistributions in binary form must reproduce the above copyright
+   notice(s), this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
new file mode 100644 (file)
index 0000000..6cfa256
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "fast_export.h"
+#include "line_buffer.h"
+#include "repo_tree.h"
+#include "string_pool.h"
+
+#define MAX_GITSVN_LINE_LEN 4096
+
+static uint32_t first_commit_done;
+
+void fast_export_delete(uint32_t depth, uint32_t *path)
+{
+       putchar('D');
+       putchar(' ');
+       pool_print_seq(depth, path, '/', stdout);
+       putchar('\n');
+}
+
+void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,
+                       uint32_t mark)
+{
+       /* Mode must be 100644, 100755, 120000, or 160000. */
+       printf("M %06"PRIo32" :%"PRIu32" ", mode, mark);
+       pool_print_seq(depth, path, '/', stdout);
+       putchar('\n');
+}
+
+static char gitsvnline[MAX_GITSVN_LINE_LEN];
+void fast_export_commit(uint32_t revision, uint32_t author, char *log,
+                       uint32_t uuid, uint32_t url,
+                       unsigned long timestamp)
+{
+       if (!log)
+               log = "";
+       if (~uuid && ~url) {
+               snprintf(gitsvnline, MAX_GITSVN_LINE_LEN,
+                               "\n\ngit-svn-id: %s@%"PRIu32" %s\n",
+                                pool_fetch(url), revision, pool_fetch(uuid));
+       } else {
+               *gitsvnline = '\0';
+       }
+       printf("commit refs/heads/master\n");
+       printf("committer %s <%s@%s> %ld +0000\n",
+                  ~author ? pool_fetch(author) : "nobody",
+                  ~author ? pool_fetch(author) : "nobody",
+                  ~uuid ? pool_fetch(uuid) : "local", timestamp);
+       printf("data %"PRIu32"\n%s%s\n",
+                  (uint32_t) (strlen(log) + strlen(gitsvnline)),
+                  log, gitsvnline);
+       if (!first_commit_done) {
+               if (revision > 1)
+                       printf("from refs/heads/master^0\n");
+               first_commit_done = 1;
+       }
+       repo_diff(revision - 1, revision);
+       fputc('\n', stdout);
+
+       printf("progress Imported commit %"PRIu32".\n\n", revision);
+}
+
+void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len)
+{
+       if (mode == REPO_MODE_LNK) {
+               /* svn symlink blobs start with "link " */
+               buffer_skip_bytes(5);
+               len -= 5;
+       }
+       printf("blob\nmark :%"PRIu32"\ndata %"PRIu32"\n", mark, len);
+       buffer_copy_bytes(len);
+       fputc('\n', stdout);
+}
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
new file mode 100644 (file)
index 0000000..2aaaea5
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef FAST_EXPORT_H_
+#define FAST_EXPORT_H_
+
+void fast_export_delete(uint32_t depth, uint32_t *path);
+void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,
+                       uint32_t mark);
+void fast_export_commit(uint32_t revision, uint32_t author, char *log,
+                       uint32_t uuid, uint32_t url, unsigned long timestamp);
+void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len);
+
+#endif
diff --git a/vcs-svn/line_buffer.c b/vcs-svn/line_buffer.c
new file mode 100644 (file)
index 0000000..1543567
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "line_buffer.h"
+#include "obj_pool.h"
+
+#define LINE_BUFFER_LEN 10000
+#define COPY_BUFFER_LEN 4096
+
+/* Create memory pool for char sequence of known length */
+obj_pool_gen(blob, char, 4096)
+
+static char line_buffer[LINE_BUFFER_LEN];
+static char byte_buffer[COPY_BUFFER_LEN];
+static FILE *infile;
+
+int buffer_init(const char *filename)
+{
+       infile = filename ? fopen(filename, "r") : stdin;
+       if (!infile)
+               return -1;
+       return 0;
+}
+
+int buffer_deinit(void)
+{
+       int err;
+       if (infile == stdin)
+               return ferror(infile);
+       err = ferror(infile);
+       err |= fclose(infile);
+       return err;
+}
+
+/* Read a line without trailing newline. */
+char *buffer_read_line(void)
+{
+       char *end;
+       if (!fgets(line_buffer, sizeof(line_buffer), infile))
+               /* Error or data exhausted. */
+               return NULL;
+       end = line_buffer + strlen(line_buffer);
+       if (end[-1] == '\n')
+               end[-1] = '\0';
+       else if (feof(infile))
+               ; /* No newline at end of file.  That's fine. */
+       else
+               /*
+                * Line was too long.
+                * There is probably a saner way to deal with this,
+                * but for now let's return an error.
+                */
+               return NULL;
+       return line_buffer;
+}
+
+char *buffer_read_string(uint32_t len)
+{
+       char *s;
+       blob_free(blob_pool.size);
+       s = blob_pointer(blob_alloc(len + 1));
+       s[fread(s, 1, len, infile)] = '\0';
+       return ferror(infile) ? NULL : s;
+}
+
+void buffer_copy_bytes(uint32_t len)
+{
+       uint32_t in;
+       while (len > 0 && !feof(infile) && !ferror(infile)) {
+               in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
+               in = fread(byte_buffer, 1, in, infile);
+               len -= in;
+               fwrite(byte_buffer, 1, in, stdout);
+               if (ferror(stdout)) {
+                       buffer_skip_bytes(len);
+                       return;
+               }
+       }
+}
+
+void buffer_skip_bytes(uint32_t len)
+{
+       uint32_t in;
+       while (len > 0 && !feof(infile) && !ferror(infile)) {
+               in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
+               in = fread(byte_buffer, 1, in, infile);
+               len -= in;
+       }
+}
+
+void buffer_reset(void)
+{
+       blob_reset();
+}
diff --git a/vcs-svn/line_buffer.h b/vcs-svn/line_buffer.h
new file mode 100644 (file)
index 0000000..9c78ae1
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef LINE_BUFFER_H_
+#define LINE_BUFFER_H_
+
+int buffer_init(const char *filename);
+int buffer_deinit(void);
+char *buffer_read_line(void);
+char *buffer_read_string(uint32_t len);
+void buffer_copy_bytes(uint32_t len);
+void buffer_skip_bytes(uint32_t len);
+void buffer_reset(void);
+
+#endif
diff --git a/vcs-svn/line_buffer.txt b/vcs-svn/line_buffer.txt
new file mode 100644 (file)
index 0000000..8906fb1
--- /dev/null
@@ -0,0 +1,58 @@
+line_buffer API
+===============
+
+The line_buffer library provides a convenient interface for
+mostly-line-oriented input.
+
+Each line is not permitted to exceed 10000 bytes.  The provided
+functions are not thread-safe or async-signal-safe, and like
+`fgets()`, they generally do not function correctly if interrupted
+by a signal without SA_RESTART set.
+
+Calling sequence
+----------------
+
+The calling program:
+
+ - specifies a file to read with `buffer_init`
+ - processes input with `buffer_read_line`, `buffer_read_string`,
+   `buffer_skip_bytes`, and `buffer_copy_bytes`
+ - closes the file with `buffer_deinit`, perhaps to start over and
+   read another file.
+
+Before exiting, the caller can use `buffer_reset` to deallocate
+resources for the benefit of profiling tools.
+
+Functions
+---------
+
+`buffer_init`::
+       Open the named file for input.  If filename is NULL,
+       start reading from stdin.  On failure, returns -1 (with
+       errno indicating the nature of the failure).
+
+`buffer_deinit`::
+       Stop reading from the current file (closing it unless
+       it was stdin).  Returns nonzero if `fclose` fails or
+       the error indicator was set.
+
+`buffer_read_line`::
+       Read a line and strip off the trailing newline.
+       On failure or end of file, returns NULL.
+
+`buffer_read_string`::
+       Read `len` characters of input or up to the end of the
+       file, whichever comes first.  Returns NULL on error.
+       Returns whatever characters were read (possibly "")
+       for end of file.
+
+`buffer_copy_bytes`::
+       Read `len` bytes of input and dump them to the standard output
+       stream.  Returns early for error or end of file.
+
+`buffer_skip_bytes`::
+       Discards `len` bytes from the input stream (stopping early
+       if necessary because of an error or eof).
+
+`buffer_reset`::
+       Deallocates non-static buffers.
diff --git a/vcs-svn/obj_pool.h b/vcs-svn/obj_pool.h
new file mode 100644 (file)
index 0000000..deb6eb8
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#ifndef OBJ_POOL_H_
+#define OBJ_POOL_H_
+
+#include "git-compat-util.h"
+
+#define MAYBE_UNUSED __attribute__((__unused__))
+
+#define obj_pool_gen(pre, obj_t, initial_capacity) \
+static struct { \
+       uint32_t committed; \
+       uint32_t size; \
+       uint32_t capacity; \
+       obj_t *base; \
+} pre##_pool = {0, 0, 0, NULL}; \
+static MAYBE_UNUSED uint32_t pre##_alloc(uint32_t count) \
+{ \
+       uint32_t offset; \
+       if (pre##_pool.size + count > pre##_pool.capacity) { \
+               while (pre##_pool.size + count > pre##_pool.capacity) \
+                       if (pre##_pool.capacity) \
+                               pre##_pool.capacity *= 2; \
+                       else \
+                               pre##_pool.capacity = initial_capacity; \
+               pre##_pool.base = realloc(pre##_pool.base, \
+                                       pre##_pool.capacity * sizeof(obj_t)); \
+       } \
+       offset = pre##_pool.size; \
+       pre##_pool.size += count; \
+       return offset; \
+} \
+static MAYBE_UNUSED void pre##_free(uint32_t count) \
+{ \
+       pre##_pool.size -= count; \
+} \
+static MAYBE_UNUSED uint32_t pre##_offset(obj_t *obj) \
+{ \
+       return obj == NULL ? ~0 : obj - pre##_pool.base; \
+} \
+static MAYBE_UNUSED obj_t *pre##_pointer(uint32_t offset) \
+{ \
+       return offset >= pre##_pool.size ? NULL : &pre##_pool.base[offset]; \
+} \
+static MAYBE_UNUSED void pre##_commit(void) \
+{ \
+       pre##_pool.committed = pre##_pool.size; \
+} \
+static MAYBE_UNUSED void pre##_reset(void) \
+{ \
+       free(pre##_pool.base); \
+       pre##_pool.base = NULL; \
+       pre##_pool.size = 0; \
+       pre##_pool.capacity = 0; \
+       pre##_pool.committed = 0; \
+}
+
+#endif
diff --git a/vcs-svn/repo_tree.c b/vcs-svn/repo_tree.c
new file mode 100644 (file)
index 0000000..e3d1fa3
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+
+#include "string_pool.h"
+#include "repo_tree.h"
+#include "obj_pool.h"
+#include "fast_export.h"
+
+#include "trp.h"
+
+struct repo_dirent {
+       uint32_t name_offset;
+       struct trp_node children;
+       uint32_t mode;
+       uint32_t content_offset;
+};
+
+struct repo_dir {
+       struct trp_root entries;
+};
+
+struct repo_commit {
+       uint32_t root_dir_offset;
+};
+
+/* Memory pools for commit, dir and dirent */
+obj_pool_gen(commit, struct repo_commit, 4096)
+obj_pool_gen(dir, struct repo_dir, 4096)
+obj_pool_gen(dent, struct repo_dirent, 4096)
+
+static uint32_t active_commit;
+static uint32_t mark;
+
+static int repo_dirent_name_cmp(const void *a, const void *b);
+
+/* Treap for directory entries */
+trp_gen(static, dent_, struct repo_dirent, children, dent, repo_dirent_name_cmp);
+
+uint32_t next_blob_mark(void)
+{
+       return mark++;
+}
+
+static struct repo_dir *repo_commit_root_dir(struct repo_commit *commit)
+{
+       return dir_pointer(commit->root_dir_offset);
+}
+
+static struct repo_dirent *repo_first_dirent(struct repo_dir *dir)
+{
+       return dent_first(&dir->entries);
+}
+
+static int repo_dirent_name_cmp(const void *a, const void *b)
+{
+       const struct repo_dirent *dent1 = a, *dent2 = b;
+       uint32_t a_offset = dent1->name_offset;
+       uint32_t b_offset = dent2->name_offset;
+       return (a_offset > b_offset) - (a_offset < b_offset);
+}
+
+static int repo_dirent_is_dir(struct repo_dirent *dent)
+{
+       return dent != NULL && dent->mode == REPO_MODE_DIR;
+}
+
+static struct repo_dir *repo_dir_from_dirent(struct repo_dirent *dent)
+{
+       if (!repo_dirent_is_dir(dent))
+               return NULL;
+       return dir_pointer(dent->content_offset);
+}
+
+static struct repo_dir *repo_clone_dir(struct repo_dir *orig_dir)
+{
+       uint32_t orig_o, new_o;
+       orig_o = dir_offset(orig_dir);
+       if (orig_o >= dir_pool.committed)
+               return orig_dir;
+       new_o = dir_alloc(1);
+       orig_dir = dir_pointer(orig_o);
+       *dir_pointer(new_o) = *orig_dir;
+       return dir_pointer(new_o);
+}
+
+static struct repo_dirent *repo_read_dirent(uint32_t revision, uint32_t *path)
+{
+       uint32_t name = 0;
+       struct repo_dirent *key = dent_pointer(dent_alloc(1));
+       struct repo_dir *dir = NULL;
+       struct repo_dirent *dent = NULL;
+       dir = repo_commit_root_dir(commit_pointer(revision));
+       while (~(name = *path++)) {
+               key->name_offset = name;
+               dent = dent_search(&dir->entries, key);
+               if (dent == NULL || !repo_dirent_is_dir(dent))
+                       break;
+               dir = repo_dir_from_dirent(dent);
+       }
+       dent_free(1);
+       return dent;
+}
+
+static void repo_write_dirent(uint32_t *path, uint32_t mode,
+                             uint32_t content_offset, uint32_t del)
+{
+       uint32_t name, revision, dir_o = ~0, parent_dir_o = ~0;
+       struct repo_dir *dir;
+       struct repo_dirent *key;
+       struct repo_dirent *dent = NULL;
+       revision = active_commit;
+       dir = repo_commit_root_dir(commit_pointer(revision));
+       dir = repo_clone_dir(dir);
+       commit_pointer(revision)->root_dir_offset = dir_offset(dir);
+       while (~(name = *path++)) {
+               parent_dir_o = dir_offset(dir);
+
+               key = dent_pointer(dent_alloc(1));
+               key->name_offset = name;
+
+               dent = dent_search(&dir->entries, key);
+               if (dent == NULL)
+                       dent = key;
+               else
+                       dent_free(1);
+
+               if (dent == key) {
+                       dent->mode = REPO_MODE_DIR;
+                       dent->content_offset = 0;
+                       dent = dent_insert(&dir->entries, dent);
+               }
+
+               if (dent_offset(dent) < dent_pool.committed) {
+                       dir_o = repo_dirent_is_dir(dent) ?
+                                       dent->content_offset : ~0;
+                       dent_remove(&dir->entries, dent);
+                       dent = dent_pointer(dent_alloc(1));
+                       dent->name_offset = name;
+                       dent->mode = REPO_MODE_DIR;
+                       dent->content_offset = dir_o;
+                       dent = dent_insert(&dir->entries, dent);
+               }
+
+               dir = repo_dir_from_dirent(dent);
+               dir = repo_clone_dir(dir);
+               dent->content_offset = dir_offset(dir);
+       }
+       if (dent == NULL)
+               return;
+       dent->mode = mode;
+       dent->content_offset = content_offset;
+       if (del && ~parent_dir_o)
+               dent_remove(&dir_pointer(parent_dir_o)->entries, dent);
+}
+
+uint32_t repo_copy(uint32_t revision, uint32_t *src, uint32_t *dst)
+{
+       uint32_t mode = 0, content_offset = 0;
+       struct repo_dirent *src_dent;
+       src_dent = repo_read_dirent(revision, src);
+       if (src_dent != NULL) {
+               mode = src_dent->mode;
+               content_offset = src_dent->content_offset;
+               repo_write_dirent(dst, mode, content_offset, 0);
+       }
+       return mode;
+}
+
+void repo_add(uint32_t *path, uint32_t mode, uint32_t blob_mark)
+{
+       repo_write_dirent(path, mode, blob_mark, 0);
+}
+
+uint32_t repo_replace(uint32_t *path, uint32_t blob_mark)
+{
+       uint32_t mode = 0;
+       struct repo_dirent *src_dent;
+       src_dent = repo_read_dirent(active_commit, path);
+       if (src_dent != NULL) {
+               mode = src_dent->mode;
+               repo_write_dirent(path, mode, blob_mark, 0);
+       }
+       return mode;
+}
+
+void repo_modify(uint32_t *path, uint32_t mode, uint32_t blob_mark)
+{
+       struct repo_dirent *src_dent;
+       src_dent = repo_read_dirent(active_commit, path);
+       if (src_dent != NULL && blob_mark == 0)
+               blob_mark = src_dent->content_offset;
+       repo_write_dirent(path, mode, blob_mark, 0);
+}
+
+void repo_delete(uint32_t *path)
+{
+       repo_write_dirent(path, 0, 0, 1);
+}
+
+static void repo_git_add_r(uint32_t depth, uint32_t *path, struct repo_dir *dir);
+
+static void repo_git_add(uint32_t depth, uint32_t *path, struct repo_dirent *dent)
+{
+       if (repo_dirent_is_dir(dent))
+               repo_git_add_r(depth, path, repo_dir_from_dirent(dent));
+       else
+               fast_export_modify(depth, path,
+                                  dent->mode, dent->content_offset);
+}
+
+static void repo_git_add_r(uint32_t depth, uint32_t *path, struct repo_dir *dir)
+{
+       struct repo_dirent *de = repo_first_dirent(dir);
+       while (de) {
+               path[depth] = de->name_offset;
+               repo_git_add(depth + 1, path, de);
+               de = dent_next(&dir->entries, de);
+       }
+}
+
+static void repo_diff_r(uint32_t depth, uint32_t *path, struct repo_dir *dir1,
+                       struct repo_dir *dir2)
+{
+       struct repo_dirent *de1, *de2;
+       de1 = repo_first_dirent(dir1);
+       de2 = repo_first_dirent(dir2);
+
+       while (de1 && de2) {
+               if (de1->name_offset < de2->name_offset) {
+                       path[depth] = de1->name_offset;
+                       fast_export_delete(depth + 1, path);
+                       de1 = dent_next(&dir1->entries, de1);
+                       continue;
+               }
+               if (de1->name_offset > de2->name_offset) {
+                       path[depth] = de2->name_offset;
+                       repo_git_add(depth + 1, path, de2);
+                       de2 = dent_next(&dir2->entries, de2);
+                       continue;
+               }
+               path[depth] = de1->name_offset;
+
+               if (de1->mode == de2->mode &&
+                   de1->content_offset == de2->content_offset) {
+                       ; /* No change. */
+               } else if (repo_dirent_is_dir(de1) && repo_dirent_is_dir(de2)) {
+                       repo_diff_r(depth + 1, path,
+                                   repo_dir_from_dirent(de1),
+                                   repo_dir_from_dirent(de2));
+               } else if (!repo_dirent_is_dir(de1) && !repo_dirent_is_dir(de2)) {
+                       repo_git_add(depth + 1, path, de2);
+               } else {
+                       fast_export_delete(depth + 1, path);
+                       repo_git_add(depth + 1, path, de2);
+               }
+               de1 = dent_next(&dir1->entries, de1);
+               de2 = dent_next(&dir2->entries, de2);
+       }
+       while (de1) {
+               path[depth] = de1->name_offset;
+               fast_export_delete(depth + 1, path);
+               de1 = dent_next(&dir1->entries, de1);
+       }
+       while (de2) {
+               path[depth] = de2->name_offset;
+               repo_git_add(depth + 1, path, de2);
+               de2 = dent_next(&dir2->entries, de2);
+       }
+}
+
+static uint32_t path_stack[REPO_MAX_PATH_DEPTH];
+
+void repo_diff(uint32_t r1, uint32_t r2)
+{
+       repo_diff_r(0,
+                   path_stack,
+                   repo_commit_root_dir(commit_pointer(r1)),
+                   repo_commit_root_dir(commit_pointer(r2)));
+}
+
+void repo_commit(uint32_t revision, uint32_t author, char *log, uint32_t uuid,
+                uint32_t url, unsigned long timestamp)
+{
+       fast_export_commit(revision, author, log, uuid, url, timestamp);
+       dent_commit();
+       dir_commit();
+       active_commit = commit_alloc(1);
+       commit_pointer(active_commit)->root_dir_offset =
+               commit_pointer(active_commit - 1)->root_dir_offset;
+}
+
+static void mark_init(void)
+{
+       uint32_t i;
+       mark = 0;
+       for (i = 0; i < dent_pool.size; i++)
+               if (!repo_dirent_is_dir(dent_pointer(i)) &&
+                   dent_pointer(i)->content_offset > mark)
+                       mark = dent_pointer(i)->content_offset;
+       mark++;
+}
+
+void repo_init(void)
+{
+       mark_init();
+       if (commit_pool.size == 0) {
+               /* Create empty tree for commit 0. */
+               commit_alloc(1);
+               commit_pointer(0)->root_dir_offset = dir_alloc(1);
+               dir_pointer(0)->entries.trp_root = ~0;
+               dir_commit();
+       }
+       /* Preallocate next commit, ready for changes. */
+       active_commit = commit_alloc(1);
+       commit_pointer(active_commit)->root_dir_offset =
+               commit_pointer(active_commit - 1)->root_dir_offset;
+}
+
+void repo_reset(void)
+{
+       pool_reset();
+       commit_reset();
+       dir_reset();
+       dent_reset();
+}
diff --git a/vcs-svn/repo_tree.h b/vcs-svn/repo_tree.h
new file mode 100644 (file)
index 0000000..5476175
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef REPO_TREE_H_
+#define REPO_TREE_H_
+
+#include "git-compat-util.h"
+
+#define REPO_MODE_DIR 0040000
+#define REPO_MODE_BLB 0100644
+#define REPO_MODE_EXE 0100755
+#define REPO_MODE_LNK 0120000
+
+#define REPO_MAX_PATH_LEN 4096
+#define REPO_MAX_PATH_DEPTH 1000
+
+uint32_t next_blob_mark(void);
+uint32_t repo_copy(uint32_t revision, uint32_t *src, uint32_t *dst);
+void repo_add(uint32_t *path, uint32_t mode, uint32_t blob_mark);
+uint32_t repo_replace(uint32_t *path, uint32_t blob_mark);
+void repo_modify(uint32_t *path, uint32_t mode, uint32_t blob_mark);
+void repo_delete(uint32_t *path);
+void repo_commit(uint32_t revision, uint32_t author, char *log, uint32_t uuid,
+                uint32_t url, long unsigned timestamp);
+void repo_diff(uint32_t r1, uint32_t r2);
+void repo_init(void);
+void repo_reset(void);
+
+#endif
diff --git a/vcs-svn/string_pool.c b/vcs-svn/string_pool.c
new file mode 100644 (file)
index 0000000..f5b1da8
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "trp.h"
+#include "obj_pool.h"
+#include "string_pool.h"
+
+static struct trp_root tree = { ~0 };
+
+struct node {
+       uint32_t offset;
+       struct trp_node children;
+};
+
+/* Two memory pools: one for struct node, and another for strings */
+obj_pool_gen(node, struct node, 4096)
+obj_pool_gen(string, char, 4096)
+
+static char *node_value(struct node *node)
+{
+       return node ? string_pointer(node->offset) : NULL;
+}
+
+static int node_cmp(struct node *a, struct node *b)
+{
+       return strcmp(node_value(a), node_value(b));
+}
+
+/* Build a Treap from the node structure (a trp_node w/ offset) */
+trp_gen(static, tree_, struct node, children, node, node_cmp);
+
+const char *pool_fetch(uint32_t entry)
+{
+       return node_value(node_pointer(entry));
+}
+
+uint32_t pool_intern(const char *key)
+{
+       /* Canonicalize key */
+       struct node *match = NULL, *node;
+       uint32_t key_len;
+       if (key == NULL)
+               return ~0;
+       key_len = strlen(key) + 1;
+       node = node_pointer(node_alloc(1));
+       node->offset = string_alloc(key_len);
+       strcpy(node_value(node), key);
+       match = tree_search(&tree, node);
+       if (!match) {
+               tree_insert(&tree, node);
+       } else {
+               node_free(1);
+               string_free(key_len);
+               node = match;
+       }
+       return node_offset(node);
+}
+
+uint32_t pool_tok_r(char *str, const char *delim, char **saveptr)
+{
+       char *token = strtok_r(str, delim, saveptr);
+       return token ? pool_intern(token) : ~0;
+}
+
+void pool_print_seq(uint32_t len, uint32_t *seq, char delim, FILE *stream)
+{
+       uint32_t i;
+       for (i = 0; i < len && ~seq[i]; i++) {
+               fputs(pool_fetch(seq[i]), stream);
+               if (i < len - 1 && ~seq[i + 1])
+                       fputc(delim, stream);
+       }
+}
+
+uint32_t pool_tok_seq(uint32_t sz, uint32_t *seq, const char *delim, char *str)
+{
+       char *context = NULL;
+       uint32_t token = ~0;
+       uint32_t length;
+
+       if (sz == 0)
+               return ~0;
+       if (str)
+               token = pool_tok_r(str, delim, &context);
+       for (length = 0; length < sz; length++) {
+               seq[length] = token;
+               if (token == ~0)
+                       return length;
+               token = pool_tok_r(NULL, delim, &context);
+       }
+       seq[sz - 1] = ~0;
+       return sz;
+}
+
+void pool_reset(void)
+{
+       node_reset();
+       string_reset();
+}
diff --git a/vcs-svn/string_pool.h b/vcs-svn/string_pool.h
new file mode 100644 (file)
index 0000000..222fb66
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef STRING_POOL_H_
+#define STRING_POOL_H_
+
+uint32_t pool_intern(const char *key);
+const char *pool_fetch(uint32_t entry);
+uint32_t pool_tok_r(char *str, const char *delim, char **saveptr);
+void pool_print_seq(uint32_t len, uint32_t *seq, char delim, FILE *stream);
+uint32_t pool_tok_seq(uint32_t sz, uint32_t *seq, const char *delim, char *str);
+void pool_reset(void);
+
+#endif
diff --git a/vcs-svn/string_pool.txt b/vcs-svn/string_pool.txt
new file mode 100644 (file)
index 0000000..1b41f15
--- /dev/null
@@ -0,0 +1,43 @@
+string_pool API
+===============
+
+The string_pool API provides facilities for replacing strings
+with integer keys that can be more easily compared and stored.
+The facilities are designed so that one could teach Git without
+too much trouble to store the information needed for these keys to
+remain valid over multiple executions.
+
+Functions
+---------
+
+pool_intern::
+       Include a string in the string pool and get its key.
+       If that string is already in the pool, retrieves its
+       existing key.
+
+pool_fetch::
+       Retrieve the string associated to a given key.
+
+pool_tok_r::
+       Extract the key of the next token from a string.
+       Interface mimics strtok_r.
+
+pool_print_seq::
+       Print a sequence of strings named by key to a file, using the
+       specified delimiter to separate them.
+
+       If NULL (key ~0) appears in the sequence, the sequence ends
+       early.
+
+pool_tok_seq::
+       Split a string into tokens, storing the keys of segments
+       into a caller-provided array.
+
+       Unless sz is 0, the array will always be ~0-terminated.
+       If there is not enough room for all the tokens, the
+       array holds as many tokens as fit in the entries before
+       the terminating ~0.  Return value is the index after the
+       last token, or sz if the tokens did not fit.
+
+pool_reset::
+       Deallocate storage for the string pool.
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
new file mode 100644 (file)
index 0000000..2ad2c30
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * Parse and rearrange a svnadmin dump.
+ * Create the dump with:
+ * svnadmin dump --incremental -r<startrev>:<endrev> <repository> >outfile
+ *
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "cache.h"
+#include "repo_tree.h"
+#include "fast_export.h"
+#include "line_buffer.h"
+#include "obj_pool.h"
+#include "string_pool.h"
+
+#define NODEACT_REPLACE 4
+#define NODEACT_DELETE 3
+#define NODEACT_ADD 2
+#define NODEACT_CHANGE 1
+#define NODEACT_UNKNOWN 0
+
+#define DUMP_CTX 0
+#define REV_CTX  1
+#define NODE_CTX 2
+
+#define LENGTH_UNKNOWN (~0)
+#define DATE_RFC2822_LEN 31
+
+/* Create memory pool for log messages */
+obj_pool_gen(log, char, 4096)
+
+static char* log_copy(uint32_t length, char *log)
+{
+       char *buffer;
+       log_free(log_pool.size);
+       buffer = log_pointer(log_alloc(length));
+       strncpy(buffer, log, length);
+       return buffer;
+}
+
+static struct {
+       uint32_t action, propLength, textLength, srcRev, srcMode, mark, type;
+       uint32_t src[REPO_MAX_PATH_DEPTH], dst[REPO_MAX_PATH_DEPTH];
+} node_ctx;
+
+static struct {
+       uint32_t revision, author;
+       unsigned long timestamp;
+       char *log;
+} rev_ctx;
+
+static struct {
+       uint32_t version, uuid, url;
+} dump_ctx;
+
+static struct {
+       uint32_t svn_log, svn_author, svn_date, svn_executable, svn_special, uuid,
+               revision_number, node_path, node_kind, node_action,
+               node_copyfrom_path, node_copyfrom_rev, text_content_length,
+               prop_content_length, content_length, svn_fs_dump_format_version;
+} keys;
+
+static void reset_node_ctx(char *fname)
+{
+       node_ctx.type = 0;
+       node_ctx.action = NODEACT_UNKNOWN;
+       node_ctx.propLength = LENGTH_UNKNOWN;
+       node_ctx.textLength = LENGTH_UNKNOWN;
+       node_ctx.src[0] = ~0;
+       node_ctx.srcRev = 0;
+       node_ctx.srcMode = 0;
+       pool_tok_seq(REPO_MAX_PATH_DEPTH, node_ctx.dst, "/", fname);
+       node_ctx.mark = 0;
+}
+
+static void reset_rev_ctx(uint32_t revision)
+{
+       rev_ctx.revision = revision;
+       rev_ctx.timestamp = 0;
+       rev_ctx.log = NULL;
+       rev_ctx.author = ~0;
+}
+
+static void reset_dump_ctx(uint32_t url)
+{
+       dump_ctx.url = url;
+       dump_ctx.version = 1;
+       dump_ctx.uuid = ~0;
+}
+
+static void init_keys(void)
+{
+       keys.svn_log = pool_intern("svn:log");
+       keys.svn_author = pool_intern("svn:author");
+       keys.svn_date = pool_intern("svn:date");
+       keys.svn_executable = pool_intern("svn:executable");
+       keys.svn_special = pool_intern("svn:special");
+       keys.uuid = pool_intern("UUID");
+       keys.revision_number = pool_intern("Revision-number");
+       keys.node_path = pool_intern("Node-path");
+       keys.node_kind = pool_intern("Node-kind");
+       keys.node_action = pool_intern("Node-action");
+       keys.node_copyfrom_path = pool_intern("Node-copyfrom-path");
+       keys.node_copyfrom_rev = pool_intern("Node-copyfrom-rev");
+       keys.text_content_length = pool_intern("Text-content-length");
+       keys.prop_content_length = pool_intern("Prop-content-length");
+       keys.content_length = pool_intern("Content-length");
+       keys.svn_fs_dump_format_version = pool_intern("SVN-fs-dump-format-version");
+}
+
+static void read_props(void)
+{
+       uint32_t len;
+       uint32_t key = ~0;
+       char *val = NULL;
+       char *t;
+       while ((t = buffer_read_line()) && strcmp(t, "PROPS-END")) {
+               if (!strncmp(t, "K ", 2)) {
+                       len = atoi(&t[2]);
+                       key = pool_intern(buffer_read_string(len));
+                       buffer_read_line();
+               } else if (!strncmp(t, "V ", 2)) {
+                       len = atoi(&t[2]);
+                       val = buffer_read_string(len);
+                       if (key == keys.svn_log) {
+                               /* Value length excludes terminating nul. */
+                               rev_ctx.log = log_copy(len + 1, val);
+                       } else if (key == keys.svn_author) {
+                               rev_ctx.author = pool_intern(val);
+                       } else if (key == keys.svn_date) {
+                               if (parse_date_basic(val, &rev_ctx.timestamp, NULL))
+                                       fprintf(stderr, "Invalid timestamp: %s\n", val);
+                       } else if (key == keys.svn_executable) {
+                               node_ctx.type = REPO_MODE_EXE;
+                       } else if (key == keys.svn_special) {
+                               node_ctx.type = REPO_MODE_LNK;
+                       }
+                       key = ~0;
+                       buffer_read_line();
+               }
+       }
+}
+
+static void handle_node(void)
+{
+       if (node_ctx.propLength != LENGTH_UNKNOWN && node_ctx.propLength)
+               read_props();
+
+       if (node_ctx.srcRev)
+               node_ctx.srcMode = repo_copy(node_ctx.srcRev, node_ctx.src, node_ctx.dst);
+
+       if (node_ctx.textLength != LENGTH_UNKNOWN &&
+           node_ctx.type != REPO_MODE_DIR)
+               node_ctx.mark = next_blob_mark();
+
+       if (node_ctx.action == NODEACT_DELETE) {
+               repo_delete(node_ctx.dst);
+       } else if (node_ctx.action == NODEACT_CHANGE ||
+                          node_ctx.action == NODEACT_REPLACE) {
+               if (node_ctx.action == NODEACT_REPLACE &&
+                   node_ctx.type == REPO_MODE_DIR)
+                       repo_replace(node_ctx.dst, node_ctx.mark);
+               else if (node_ctx.propLength != LENGTH_UNKNOWN)
+                       repo_modify(node_ctx.dst, node_ctx.type, node_ctx.mark);
+               else if (node_ctx.textLength != LENGTH_UNKNOWN)
+                       node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark);
+       } else if (node_ctx.action == NODEACT_ADD) {
+               if (node_ctx.srcRev && node_ctx.propLength != LENGTH_UNKNOWN)
+                       repo_modify(node_ctx.dst, node_ctx.type, node_ctx.mark);
+               else if (node_ctx.srcRev && node_ctx.textLength != LENGTH_UNKNOWN)
+                       node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark);
+               else if ((node_ctx.type == REPO_MODE_DIR && !node_ctx.srcRev) ||
+                        node_ctx.textLength != LENGTH_UNKNOWN)
+                       repo_add(node_ctx.dst, node_ctx.type, node_ctx.mark);
+       }
+
+       if (node_ctx.propLength == LENGTH_UNKNOWN && node_ctx.srcMode)
+               node_ctx.type = node_ctx.srcMode;
+
+       if (node_ctx.mark)
+               fast_export_blob(node_ctx.type, node_ctx.mark, node_ctx.textLength);
+       else if (node_ctx.textLength != LENGTH_UNKNOWN)
+               buffer_skip_bytes(node_ctx.textLength);
+}
+
+static void handle_revision(void)
+{
+       if (rev_ctx.revision)
+               repo_commit(rev_ctx.revision, rev_ctx.author, rev_ctx.log,
+                       dump_ctx.uuid, dump_ctx.url, rev_ctx.timestamp);
+}
+
+void svndump_read(const char *url)
+{
+       char *val;
+       char *t;
+       uint32_t active_ctx = DUMP_CTX;
+       uint32_t len;
+       uint32_t key;
+
+       reset_dump_ctx(pool_intern(url));
+       while ((t = buffer_read_line())) {
+               val = strstr(t, ": ");
+               if (!val)
+                       continue;
+               *val++ = '\0';
+               *val++ = '\0';
+               key = pool_intern(t);
+
+               if (key == keys.svn_fs_dump_format_version) {
+                       dump_ctx.version = atoi(val);
+                       if (dump_ctx.version > 2)
+                               die("expected svn dump format version <= 2, found %"PRIu32,
+                                   dump_ctx.version);
+               } else if (key == keys.uuid) {
+                       dump_ctx.uuid = pool_intern(val);
+               } else if (key == keys.revision_number) {
+                       if (active_ctx == NODE_CTX)
+                               handle_node();
+                       if (active_ctx != DUMP_CTX)
+                               handle_revision();
+                       active_ctx = REV_CTX;
+                       reset_rev_ctx(atoi(val));
+               } else if (key == keys.node_path) {
+                       if (active_ctx == NODE_CTX)
+                               handle_node();
+                       active_ctx = NODE_CTX;
+                       reset_node_ctx(val);
+               } else if (key == keys.node_kind) {
+                       if (!strcmp(val, "dir"))
+                               node_ctx.type = REPO_MODE_DIR;
+                       else if (!strcmp(val, "file"))
+                               node_ctx.type = REPO_MODE_BLB;
+                       else
+                               fprintf(stderr, "Unknown node-kind: %s\n", val);
+               } else if (key == keys.node_action) {
+                       if (!strcmp(val, "delete")) {
+                               node_ctx.action = NODEACT_DELETE;
+                       } else if (!strcmp(val, "add")) {
+                               node_ctx.action = NODEACT_ADD;
+                       } else if (!strcmp(val, "change")) {
+                               node_ctx.action = NODEACT_CHANGE;
+                       } else if (!strcmp(val, "replace")) {
+                               node_ctx.action = NODEACT_REPLACE;
+                       } else {
+                               fprintf(stderr, "Unknown node-action: %s\n", val);
+                               node_ctx.action = NODEACT_UNKNOWN;
+                       }
+               } else if (key == keys.node_copyfrom_path) {
+                       pool_tok_seq(REPO_MAX_PATH_DEPTH, node_ctx.src, "/", val);
+               } else if (key == keys.node_copyfrom_rev) {
+                       node_ctx.srcRev = atoi(val);
+               } else if (key == keys.text_content_length) {
+                       node_ctx.textLength = atoi(val);
+               } else if (key == keys.prop_content_length) {
+                       node_ctx.propLength = atoi(val);
+               } else if (key == keys.content_length) {
+                       len = atoi(val);
+                       buffer_read_line();
+                       if (active_ctx == REV_CTX) {
+                               read_props();
+                       } else if (active_ctx == NODE_CTX) {
+                               handle_node();
+                               active_ctx = REV_CTX;
+                       } else {
+                               fprintf(stderr, "Unexpected content length header: %"PRIu32"\n", len);
+                               buffer_skip_bytes(len);
+                       }
+               }
+       }
+       if (active_ctx == NODE_CTX)
+               handle_node();
+       if (active_ctx != DUMP_CTX)
+               handle_revision();
+}
+
+void svndump_init(const char *filename)
+{
+       buffer_init(filename);
+       repo_init();
+       reset_dump_ctx(~0);
+       reset_rev_ctx(0);
+       reset_node_ctx(NULL);
+       init_keys();
+}
+
+void svndump_deinit(void)
+{
+       log_reset();
+       repo_reset();
+       reset_dump_ctx(~0);
+       reset_rev_ctx(0);
+       reset_node_ctx(NULL);
+       if (buffer_deinit())
+               fprintf(stderr, "Input error\n");
+       if (ferror(stdout))
+               fprintf(stderr, "Output error\n");
+}
+
+void svndump_reset(void)
+{
+       log_reset();
+       buffer_reset();
+       repo_reset();
+       reset_dump_ctx(~0);
+       reset_rev_ctx(0);
+       reset_node_ctx(NULL);
+}
diff --git a/vcs-svn/svndump.h b/vcs-svn/svndump.h
new file mode 100644 (file)
index 0000000..93c412f
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef SVNDUMP_H_
+#define SVNDUMP_H_
+
+void svndump_init(const char *filename);
+void svndump_read(const char *url);
+void svndump_deinit(void);
+void svndump_reset(void);
+
+#endif
diff --git a/vcs-svn/trp.h b/vcs-svn/trp.h
new file mode 100644 (file)
index 0000000..c32b918
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * C macro implementation of treaps.
+ *
+ * Usage:
+ *   #include <stdint.h>
+ *   #include "trp.h"
+ *   trp_gen(...)
+ *
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#ifndef TRP_H_
+#define TRP_H_
+
+#define MAYBE_UNUSED __attribute__((__unused__))
+
+/* Node structure. */
+struct trp_node {
+       uint32_t trpn_left;
+       uint32_t trpn_right;
+};
+
+/* Root structure. */
+struct trp_root {
+       uint32_t trp_root;
+};
+
+/* Pointer/Offset conversion. */
+#define trpn_pointer(a_base, a_offset) (a_base##_pointer(a_offset))
+#define trpn_offset(a_base, a_pointer) (a_base##_offset(a_pointer))
+#define trpn_modify(a_base, a_offset) \
+       do { \
+               if ((a_offset) < a_base##_pool.committed) { \
+                       uint32_t old_offset = (a_offset);\
+                       (a_offset) = a_base##_alloc(1); \
+                       *trpn_pointer(a_base, a_offset) = \
+                               *trpn_pointer(a_base, old_offset); \
+               } \
+       } while (0)
+
+/* Left accessors. */
+#define trp_left_get(a_base, a_field, a_node) \
+       (trpn_pointer(a_base, a_node)->a_field.trpn_left)
+#define trp_left_set(a_base, a_field, a_node, a_left) \
+       do { \
+               trpn_modify(a_base, a_node); \
+               trp_left_get(a_base, a_field, a_node) = (a_left); \
+       } while (0)
+
+/* Right accessors. */
+#define trp_right_get(a_base, a_field, a_node) \
+       (trpn_pointer(a_base, a_node)->a_field.trpn_right)
+#define trp_right_set(a_base, a_field, a_node, a_right) \
+       do { \
+               trpn_modify(a_base, a_node); \
+               trp_right_get(a_base, a_field, a_node) = (a_right); \
+       } while (0)
+
+/*
+ * Fibonacci hash function.
+ * The multiplier is the nearest prime to (2^32 times (√5 - 1)/2).
+ * See Knuth §6.4: volume 3, 3rd ed, p518.
+ */
+#define trpn_hash(a_node) (uint32_t) (2654435761u * (a_node))
+
+/* Priority accessors. */
+#define trp_prio_get(a_node) trpn_hash(a_node)
+
+/* Node initializer. */
+#define trp_node_new(a_base, a_field, a_node) \
+       do { \
+               trp_left_set(a_base, a_field, (a_node), ~0); \
+               trp_right_set(a_base, a_field, (a_node), ~0); \
+       } while (0)
+
+/* Internal utility macros. */
+#define trpn_first(a_base, a_field, a_root, r_node) \
+       do { \
+               (r_node) = (a_root); \
+               if ((r_node) == ~0) \
+                       return NULL; \
+               while (~trp_left_get(a_base, a_field, (r_node))) \
+                       (r_node) = trp_left_get(a_base, a_field, (r_node)); \
+       } while (0)
+
+#define trpn_rotate_left(a_base, a_field, a_node, r_node) \
+       do { \
+               (r_node) = trp_right_get(a_base, a_field, (a_node)); \
+               trp_right_set(a_base, a_field, (a_node), \
+                       trp_left_get(a_base, a_field, (r_node))); \
+               trp_left_set(a_base, a_field, (r_node), (a_node)); \
+       } while (0)
+
+#define trpn_rotate_right(a_base, a_field, a_node, r_node) \
+       do { \
+               (r_node) = trp_left_get(a_base, a_field, (a_node)); \
+               trp_left_set(a_base, a_field, (a_node), \
+                       trp_right_get(a_base, a_field, (r_node))); \
+               trp_right_set(a_base, a_field, (r_node), (a_node)); \
+       } while (0)
+
+#define trp_gen(a_attr, a_pre, a_type, a_field, a_base, a_cmp) \
+a_attr a_type MAYBE_UNUSED *a_pre##first(struct trp_root *treap) \
+{ \
+       uint32_t ret; \
+       trpn_first(a_base, a_field, treap->trp_root, ret); \
+       return trpn_pointer(a_base, ret); \
+} \
+a_attr a_type MAYBE_UNUSED *a_pre##next(struct trp_root *treap, a_type *node) \
+{ \
+       uint32_t ret; \
+       uint32_t offset = trpn_offset(a_base, node); \
+       if (~trp_right_get(a_base, a_field, offset)) { \
+               trpn_first(a_base, a_field, \
+                       trp_right_get(a_base, a_field, offset), ret); \
+       } else { \
+               uint32_t tnode = treap->trp_root; \
+               ret = ~0; \
+               while (1) { \
+                       int cmp = (a_cmp)(trpn_pointer(a_base, offset), \
+                               trpn_pointer(a_base, tnode)); \
+                       if (cmp < 0) { \
+                               ret = tnode; \
+                               tnode = trp_left_get(a_base, a_field, tnode); \
+                       } else if (cmp > 0) { \
+                               tnode = trp_right_get(a_base, a_field, tnode); \
+                       } else { \
+                               break; \
+                       } \
+               } \
+       } \
+       return trpn_pointer(a_base, ret); \
+} \
+a_attr a_type MAYBE_UNUSED *a_pre##search(struct trp_root *treap, a_type *key) \
+{ \
+       int cmp; \
+       uint32_t ret = treap->trp_root; \
+       while (~ret && (cmp = (a_cmp)(key, trpn_pointer(a_base, ret)))) { \
+               if (cmp < 0) { \
+                       ret = trp_left_get(a_base, a_field, ret); \
+               } else { \
+                       ret = trp_right_get(a_base, a_field, ret); \
+               } \
+       } \
+       return trpn_pointer(a_base, ret); \
+} \
+a_attr a_type MAYBE_UNUSED *a_pre##nsearch(struct trp_root *treap, a_type *key) \
+{ \
+       int cmp; \
+       uint32_t ret = treap->trp_root; \
+       while (~ret && (cmp = (a_cmp)(key, trpn_pointer(a_base, ret)))) { \
+               if (cmp < 0) { \
+                       if (!~trp_left_get(a_base, a_field, ret)) \
+                               break; \
+                       ret = trp_left_get(a_base, a_field, ret); \
+               } else { \
+                       ret = trp_right_get(a_base, a_field, ret); \
+               } \
+       } \
+       return trpn_pointer(a_base, ret); \
+} \
+a_attr uint32_t MAYBE_UNUSED a_pre##insert_recurse(uint32_t cur_node, uint32_t ins_node) \
+{ \
+       if (cur_node == ~0) { \
+               return ins_node; \
+       } else { \
+               uint32_t ret; \
+               int cmp = (a_cmp)(trpn_pointer(a_base, ins_node), \
+                                       trpn_pointer(a_base, cur_node)); \
+               if (cmp < 0) { \
+                       uint32_t left = a_pre##insert_recurse( \
+                               trp_left_get(a_base, a_field, cur_node), ins_node); \
+                       trp_left_set(a_base, a_field, cur_node, left); \
+                       if (trp_prio_get(left) < trp_prio_get(cur_node)) \
+                               trpn_rotate_right(a_base, a_field, cur_node, ret); \
+                       else \
+                               ret = cur_node; \
+               } else { \
+                       uint32_t right = a_pre##insert_recurse( \
+                               trp_right_get(a_base, a_field, cur_node), ins_node); \
+                       trp_right_set(a_base, a_field, cur_node, right); \
+                       if (trp_prio_get(right) < trp_prio_get(cur_node)) \
+                               trpn_rotate_left(a_base, a_field, cur_node, ret); \
+                       else \
+                               ret = cur_node; \
+               } \
+               return ret; \
+       } \
+} \
+a_attr a_type *MAYBE_UNUSED a_pre##insert(struct trp_root *treap, a_type *node) \
+{ \
+       uint32_t offset = trpn_offset(a_base, node); \
+       trp_node_new(a_base, a_field, offset); \
+       treap->trp_root = a_pre##insert_recurse(treap->trp_root, offset); \
+       return trpn_pointer(a_base, offset); \
+} \
+a_attr uint32_t MAYBE_UNUSED a_pre##remove_recurse(uint32_t cur_node, uint32_t rem_node) \
+{ \
+       int cmp = a_cmp(trpn_pointer(a_base, rem_node), \
+                       trpn_pointer(a_base, cur_node)); \
+       if (cmp == 0) { \
+               uint32_t ret; \
+               uint32_t left = trp_left_get(a_base, a_field, cur_node); \
+               uint32_t right = trp_right_get(a_base, a_field, cur_node); \
+               if (left == ~0) { \
+                       if (right == ~0) \
+                               return ~0; \
+               } else if (right == ~0 || trp_prio_get(left) < trp_prio_get(right)) { \
+                       trpn_rotate_right(a_base, a_field, cur_node, ret); \
+                       right = a_pre##remove_recurse(cur_node, rem_node); \
+                       trp_right_set(a_base, a_field, ret, right); \
+                       return ret; \
+               } \
+               trpn_rotate_left(a_base, a_field, cur_node, ret); \
+               left = a_pre##remove_recurse(cur_node, rem_node); \
+               trp_left_set(a_base, a_field, ret, left); \
+               return ret; \
+       } else if (cmp < 0) { \
+               uint32_t left = a_pre##remove_recurse( \
+                       trp_left_get(a_base, a_field, cur_node), rem_node); \
+               trp_left_set(a_base, a_field, cur_node, left); \
+               return cur_node; \
+       } else { \
+               uint32_t right = a_pre##remove_recurse( \
+                       trp_right_get(a_base, a_field, cur_node), rem_node); \
+               trp_right_set(a_base, a_field, cur_node, right); \
+               return cur_node; \
+       } \
+} \
+a_attr void MAYBE_UNUSED a_pre##remove(struct trp_root *treap, a_type *node) \
+{ \
+       treap->trp_root = a_pre##remove_recurse(treap->trp_root, \
+               trpn_offset(a_base, node)); \
+} \
+
+#endif
diff --git a/vcs-svn/trp.txt b/vcs-svn/trp.txt
new file mode 100644 (file)
index 0000000..5ca6b42
--- /dev/null
@@ -0,0 +1,109 @@
+Motivation
+==========
+
+Treaps provide a memory-efficient binary search tree structure.
+Insertion/deletion/search are about as about as fast in the average
+case as red-black trees and the chances of worst-case behavior are
+vanishingly small, thanks to (pseudo-)randomness.  The bad worst-case
+behavior is a small price to pay, given that treaps are much simpler
+to implement.
+
+API
+===
+
+The trp API generates a data structure and functions to handle a
+large growing set of objects stored in a pool.
+
+The caller:
+
+. Specifies parameters for the generated functions with the
+  trp_gen(static, foo_, ...) macro.
+
+. Allocates a `struct trp_root` variable and sets it to {~0}.
+
+. Adds new nodes to the set using `foo_insert`.  Any pointers
+  to existing nodes cannot be relied upon any more, so the caller
+  might retrieve them anew with `foo_pointer`.
+
+. Can find a specific item in the set using `foo_search`.
+
+. Can iterate over items in the set using `foo_first` and `foo_next`.
+
+. Can remove an item from the set using `foo_remove`.
+
+Example:
+
+----
+struct ex_node {
+       const char *s;
+       struct trp_node ex_link;
+};
+static struct trp_root ex_base = {~0};
+obj_pool_gen(ex, struct ex_node, 4096);
+trp_gen(static, ex_, struct ex_node, ex_link, ex, strcmp)
+struct ex_node *item;
+
+item = ex_pointer(ex_alloc(1));
+item->s = "hello";
+ex_insert(&ex_base, item);
+item = ex_pointer(ex_alloc(1));
+item->s = "goodbye";
+ex_insert(&ex_base, item);
+for (item = ex_first(&ex_base); item; item = ex_next(&ex_base, item))
+       printf("%s\n", item->s);
+----
+
+Functions
+---------
+
+trp_gen(attr, foo_, node_type, link_field, pool, cmp)::
+
+       Generate a type-specific treap implementation.
++
+. The storage class for generated functions will be 'attr' (e.g., `static`).
+. Generated function names are prefixed with 'foo_' (e.g., `treap_`).
+. Treap nodes will be of type 'node_type' (e.g., `struct treap_node`).
+  This type must be a struct with at least one `struct trp_node` field
+  to point to its children.
+. The field used to access child nodes will be 'link_field'.
+. All treap nodes must lie in the 'pool' object pool.
+. Treap nodes must be totally ordered by the 'cmp' relation, with the
+  following prototype:
++
+int (*cmp)(node_type \*a, node_type \*b)
++
+and returning a value less than, equal to, or greater than zero
+according to the result of comparison.
+
+node_type {asterisk}foo_insert(struct trp_root *treap, node_type \*node)::
+
+       Insert node into treap.  If inserted multiple times,
+       a node will appear in the treap multiple times.
++
+The return value is the address of the node within the treap,
+which might differ from `node` if `pool_alloc` had to call
+`realloc` to expand the pool.
+
+void foo_remove(struct trp_root *treap, node_type \*node)::
+
+       Remove node from treap.  Caller must ensure node is
+       present in treap before using this function.
+
+node_type *foo_search(struct trp_root \*treap, node_type \*key)::
+
+       Search for a node that matches key.  If no match is found,
+       result is NULL.
+
+node_type *foo_nsearch(struct trp_root \*treap, node_type \*key)::
+
+       Like `foo_search`, but if if the key is missing return what
+       would be key's successor, were key in treap (NULL if no
+       successor).
+
+node_type *foo_first(struct trp_root \*treap)::
+
+       Find the first item from the treap, in sorted order.
+
+node_type *foo_next(struct trp_root \*treap, node_type \*node)::
+
+       Find the next item.
index 11d9052ed86ab17b56ab86ef21b7c98095e410dc..dce7128daf8487a61e8d2f35cf15fca618964f86 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -207,7 +207,7 @@ static int mark_complete(const char *path, const unsigned char *sha1, int flag,
        struct commit *commit = lookup_commit_reference_gently(sha1, 1);
        if (commit) {
                commit->object.flags |= COMPLETE;
-               insert_by_date(commit, &complete);
+               commit_list_insert_by_date(commit, &complete);
        }
        return 0;
 }
index fd8ead33ed72c37e690ee1fc5b8568f629c95145..55b074ec46b0a222b03f8bea42f4d5cdd9811293 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -3,16 +3,17 @@
  */
 #include "cache.h"
 
-static void try_to_free_builtin(size_t size)
+static void do_nothing(size_t size)
 {
-       release_pack_memory(size, -1);
 }
 
-static void (*try_to_free_routine)(size_t size) = try_to_free_builtin;
+static void (*try_to_free_routine)(size_t size) = do_nothing;
 
 try_to_free_t set_try_to_free_routine(try_to_free_t routine)
 {
        try_to_free_t old = try_to_free_routine;
+       if (!routine)
+               routine = do_nothing;
        try_to_free_routine = routine;
        return old;
 }
@@ -108,21 +109,6 @@ void *xcalloc(size_t nmemb, size_t size)
        return ret;
 }
 
-void *xmmap(void *start, size_t length,
-       int prot, int flags, int fd, off_t offset)
-{
-       void *ret = mmap(start, length, prot, flags, fd, offset);
-       if (ret == MAP_FAILED) {
-               if (!length)
-                       return NULL;
-               release_pack_memory(length, fd);
-               ret = mmap(start, length, prot, flags, fd, offset);
-               if (ret == MAP_FAILED)
-                       die_errno("Out of memory? mmap failed");
-       }
-       return ret;
-}
-
 /*
  * xread() is the same a read(), but it automatically restarts read()
  * operations with a recoverable error (EAGAIN and EINTR). xread()
@@ -212,118 +198,158 @@ FILE *xfdopen(int fd, const char *mode)
 int xmkstemp(char *template)
 {
        int fd;
+       char origtemplate[PATH_MAX];
+       strlcpy(origtemplate, template, sizeof(origtemplate));
 
        fd = mkstemp(template);
-       if (fd < 0)
-               die_errno("Unable to create temporary file");
-       return fd;
-}
+       if (fd < 0) {
+               int saved_errno = errno;
+               const char *nonrelative_template;
 
-int xmkstemp_mode(char *template, int mode)
-{
-       int fd;
+               if (!template[0])
+                       template = origtemplate;
 
-       fd = git_mkstemp_mode(template, mode);
-       if (fd < 0)
-               die_errno("Unable to create temporary file");
+               nonrelative_template = make_nonrelative_path(template);
+               errno = saved_errno;
+               die_errno("Unable to create temporary file '%s'",
+                       nonrelative_template);
+       }
        return fd;
 }
 
-/*
- * zlib wrappers to make sure we don't silently miss errors
- * at init time.
- */
-void git_inflate_init(z_streamp strm)
+/* git_mkstemp() - create tmp file honoring TMPDIR variable */
+int git_mkstemp(char *path, size_t len, const char *template)
 {
-       const char *err;
-
-       switch (inflateInit(strm)) {
-       case Z_OK:
-               return;
-
-       case Z_MEM_ERROR:
-               err = "out of memory";
-               break;
-       case Z_VERSION_ERROR:
-               err = "wrong version";
-               break;
-       default:
-               err = "error";
+       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;
        }
-       die("inflateInit: %s (%s)", err, strm->msg ? strm->msg : "no message");
+       return mkstemp(path);
 }
 
-void git_inflate_end(z_streamp strm)
+/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */
+int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
 {
-       if (inflateEnd(strm) != Z_OK)
-               error("inflateEnd: %s", strm->msg ? strm->msg : "failed");
+       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 git_inflate(z_streamp strm, int flush)
+/* Adapted from libiberty's mkstemp.c. */
+
+#undef TMP_MAX
+#define TMP_MAX 16384
+
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
 {
-       int ret = inflate(strm, flush);
-       const char *err;
-
-       switch (ret) {
-       /* Out of memory is fatal. */
-       case Z_MEM_ERROR:
-               die("inflate: out of memory");
-
-       /* Data corruption errors: we may want to recover from them (fsck) */
-       case Z_NEED_DICT:
-               err = "needs dictionary"; break;
-       case Z_DATA_ERROR:
-               err = "data stream error"; break;
-       case Z_STREAM_ERROR:
-               err = "stream consistency error"; break;
-       default:
-               err = "unknown error"; break;
-
-       /* Z_BUF_ERROR: normal, needs more space in the output buffer */
-       case Z_BUF_ERROR:
-       case Z_OK:
-       case Z_STREAM_END:
-               return ret;
+       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;
        }
-       error("inflate: %s (%s)", err, strm->msg ? strm->msg : "no message");
-       return ret;
-}
 
-int odb_mkstemp(char *template, size_t limit, const char *pattern)
-{
-       int fd;
        /*
-        * we let the umask do its job, don't try to be more
-        * restrictive except to remove write permission.
+        * Replace pattern's XXXXXX characters with randomness.
+        * Try TMP_MAX different filenames.
         */
-       int mode = 0444;
-       snprintf(template, limit, "%s/%s",
-                get_object_directory(), pattern);
-       fd = git_mkstemp_mode(template, mode);
-       if (0 <= fd)
-               return fd;
-
-       /* slow path */
-       /* some mkstemp implementations erase template on failure */
-       snprintf(template, limit, "%s/%s",
-                get_object_directory(), pattern);
-       safe_create_leading_directories(template);
-       return xmkstemp_mode(template, mode);
+       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, mode);
+               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';
+       return -1;
+}
+
+int git_mkstemp_mode(char *pattern, int mode)
+{
+       /* mkstemp is just mkstemps with no suffix */
+       return git_mkstemps_mode(pattern, 0, mode);
 }
 
-int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
+int gitmkstemps(char *pattern, int suffix_len)
+{
+       return git_mkstemps_mode(pattern, suffix_len, 0600);
+}
+
+int xmkstemp_mode(char *template, int mode)
 {
        int fd;
+       char origtemplate[PATH_MAX];
+       strlcpy(origtemplate, template, sizeof(origtemplate));
 
-       snprintf(name, namesz, "%s/pack/pack-%s.keep",
-                get_object_directory(), sha1_to_hex(sha1));
-       fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
-       if (0 <= fd)
-               return fd;
+       fd = git_mkstemp_mode(template, mode);
+       if (fd < 0) {
+               int saved_errno = errno;
+               const char *nonrelative_template;
+
+               if (!template[0])
+                       template = origtemplate;
 
-       /* slow path */
-       safe_create_leading_directories(name);
-       return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+               nonrelative_template = make_nonrelative_path(template);
+               errno = saved_errno;
+               die_errno("Unable to create temporary file '%s'",
+                       nonrelative_template);
+       }
+       return fd;
 }
 
 static int warn_if_unremovable(const char *op, const char *file, int rc)
diff --git a/ws.c b/ws.c
index d7b8c33f14195ba7ebe86bdbca2fea8f1b22ad37..9fb9b14760b100972a1adb5198bff9d6a9941808 100644 (file)
--- a/ws.c
+++ b/ws.c
@@ -56,6 +56,16 @@ unsigned parse_whitespace_rule(const char *string)
                                rule |= whitespace_rule_names[i].rule_bits;
                        break;
                }
+               if (strncmp(string, "tabwidth=", 9) == 0) {
+                       unsigned tabwidth = atoi(string + 9);
+                       if (0 < tabwidth && tabwidth < 0100) {
+                               rule &= ~WS_TAB_WIDTH_MASK;
+                               rule |= tabwidth;
+                       }
+                       else
+                               warning("tabwidth %.*s out of range",
+                                       (int)(len - 9), string + 9);
+               }
                string = ep;
        }
 
@@ -84,7 +94,7 @@ unsigned whitespace_rule(const char *pathname)
                value = attr_whitespace_rule.value;
                if (ATTR_TRUE(value)) {
                        /* true (whitespace) */
-                       unsigned all_rule = 0;
+                       unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
                        int i;
                        for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
                                if (!whitespace_rule_names[i].loosens_error &&
@@ -93,7 +103,7 @@ unsigned whitespace_rule(const char *pathname)
                        return all_rule;
                } else if (ATTR_FALSE(value)) {
                        /* false (-whitespace) */
-                       return 0;
+                       return ws_tab_width(whitespace_rule_cfg);
                } else if (ATTR_UNSET(value)) {
                        /* reset to default (!whitespace) */
                        return whitespace_rule_cfg;
@@ -174,8 +184,11 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
                }
        }
 
+       if (trailing_whitespace == -1)
+               trailing_whitespace = len;
+
        /* Check indentation */
-       for (i = 0; i < len; i++) {
+       for (i = 0; i < trailing_whitespace; i++) {
                if (line[i] == ' ')
                        continue;
                if (line[i] != '\t')
@@ -203,7 +216,7 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
        }
 
        /* Check for indent using non-tab. */
-       if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8) {
+       if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= ws_tab_width(ws_rule)) {
                result |= WS_INDENT_WITH_NON_TAB;
                if (stream) {
                        fputs(ws, stream);
@@ -218,8 +231,6 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
                 * Now the rest of the line starts at "written".
                 * The non-highlighted part ends at "trailing_whitespace".
                 */
-               if (trailing_whitespace == -1)
-                       trailing_whitespace = len;
 
                /* Emit non-highlighted (middle) segment. */
                if (trailing_whitespace - written > 0) {
@@ -319,7 +330,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,
                } else if (ch == ' ') {
                        last_space_in_indent = i;
                        if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
-                           8 <= i - last_tab_in_indent)
+                           ws_tab_width(ws_rule) <= i - last_tab_in_indent)
                                need_fix_leading_space = 1;
                } else
                        break;
@@ -349,7 +360,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,
                                strbuf_addch(dst, ch);
                        } else {
                                consecutive_spaces++;
-                               if (consecutive_spaces == 8) {
+                               if (consecutive_spaces == ws_tab_width(ws_rule)) {
                                        strbuf_addch(dst, '\t');
                                        consecutive_spaces = 0;
                                }
@@ -362,12 +373,13 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,
                fixed = 1;
        } else if ((ws_rule & WS_TAB_IN_INDENT) && last_tab_in_indent >= 0) {
                /* Expand tabs into spaces */
+               int start = dst->len;
                int last = last_tab_in_indent + 1;
                for (i = 0; i < last; i++) {
                        if (src[i] == '\t')
                                do {
                                        strbuf_addch(dst, ' ');
-                               } while (dst->len % 8);
+                               } while ((dst->len - start) % ws_tab_width(ws_rule));
                        else
                                strbuf_addch(dst, src[i]);
                }
index 54b6b03b9cc4e168e6b6429679b61dc2a1d5197b..a82b11d341c9dddb541cc72a5568769d9f538780 100644 (file)
@@ -21,11 +21,15 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RED,    /* WT_STATUS_UNMERGED */
        GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */
        GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */
+       GIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */
 };
 
 static const char *color(int slot, struct wt_status *s)
 {
-       return s->use_color > 0 ? s->color_palette[slot] : "";
+       const char *c = s->use_color > 0 ? s->color_palette[slot] : "";
+       if (slot == WT_STATUS_ONBRANCH && color_is_nil(c))
+               c = s->color_palette[WT_STATUS_HEADER];
+       return c;
 }
 
 void wt_status_prepare(struct wt_status *s)
@@ -88,7 +92,7 @@ static void wt_status_print_dirty_header(struct wt_status *s,
 {
        const char *c = color(WT_STATUS_HEADER, s);
 
-       color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+       color_fprintf_ln(s->fp, c, "# Changes not staged for commit:");
        if (!advice_status_hints)
                return;
        if (!has_deleted)
@@ -319,7 +323,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
     }
        rev.diffopt.format_callback = wt_status_collect_changed_cb;
        rev.diffopt.format_callback_data = s;
-       rev.prune_data = s->pathspec;
+       init_pathspec(&rev.prune_data, s->pathspec);
        run_diff_files(&rev, 0);
 }
 
@@ -344,20 +348,22 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 200;
        rev.diffopt.break_opt = 0;
-       rev.prune_data = s->pathspec;
+       init_pathspec(&rev.prune_data, s->pathspec);
        run_diff_index(&rev, 1);
 }
 
 static void wt_status_collect_changes_initial(struct wt_status *s)
 {
+       struct pathspec pathspec;
        int i;
 
+       init_pathspec(&pathspec, s->pathspec);
        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];
 
-               if (!ce_path_match(ce, s->pathspec))
+               if (!ce_path_match(ce, &pathspec))
                        continue;
                it = string_list_insert(&s->change, ce->name);
                d = it->util;
@@ -372,6 +378,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
                else
                        d->index_status = DIFF_STATUS_ADDED;
        }
+       free_pathspec(&pathspec);
 }
 
 static void wt_status_collect_untracked(struct wt_status *s)
@@ -390,11 +397,9 @@ static void wt_status_collect_untracked(struct wt_status *s)
        fill_directory(&dir, s->pathspec);
        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 (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
-                       continue;
-               string_list_insert(&s->untracked, ent->name);
+               if (cache_name_is_other(ent->name, ent->len) &&
+                   match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
+                       string_list_insert(&s->untracked, ent->name);
                free(ent);
        }
 
@@ -404,11 +409,9 @@ static void wt_status_collect_untracked(struct wt_status *s)
                fill_directory(&dir, s->pathspec);
                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 (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
-                               continue;
-                       string_list_insert(&s->ignored, ent->name);
+                       if (cache_name_is_other(ent->name, ent->len) &&
+                           match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
+                               string_list_insert(&s->ignored, ent->name);
                        free(ent);
                }
        }
@@ -629,7 +632,8 @@ static void wt_status_print_tracking(struct wt_status *s)
 
 void wt_status_print(struct wt_status *s)
 {
-       const char *branch_color = color(WT_STATUS_HEADER, s);
+       const char *branch_color = color(WT_STATUS_ONBRANCH, s);
+       const char *branch_status_color = color(WT_STATUS_HEADER, s);
 
        if (s->branch) {
                const char *on_what = "On branch ";
@@ -638,11 +642,12 @@ 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, s);
+                       branch_status_color = color(WT_STATUS_NOBRANCH, s);
                        on_what = "Not currently on any branch.";
                }
                color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
-               color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
+               color_fprintf(s->fp, branch_status_color, "%s", on_what);
+               color_fprintf_ln(s->fp, branch_color, "%s", branch_name);
                if (!s->is_initial)
                        wt_status_print_tracking(s);
        }
@@ -748,10 +753,20 @@ static void wt_shortstatus_status(int null_termination, struct string_list_item
                const char *one;
                if (d->head_path) {
                        one = quote_path(d->head_path, -1, &onebuf, s->prefix);
+                       if (*one != '"' && strchr(one, ' ') != NULL) {
+                               putchar('"');
+                               strbuf_addch(&onebuf, '"');
+                               one = onebuf.buf;
+                       }
                        printf("%s -> ", one);
                        strbuf_release(&onebuf);
                }
                one = quote_path(it->string, -1, &onebuf, s->prefix);
+               if (*one != '"' && strchr(one, ' ') != NULL) {
+                       putchar('"');
+                       strbuf_addch(&onebuf, '"');
+                       one = onebuf.buf;
+               }
                printf("%s\n", one);
                strbuf_release(&onebuf);
        }
index 9df9c9fad2512d7c1d7d6456cdd645d641b5a089..20b17cf4393b8f9acce93320fb97998ad7cd609b 100644 (file)
@@ -13,7 +13,9 @@ enum color_wt_status {
        WT_STATUS_NOBRANCH,
        WT_STATUS_UNMERGED,
        WT_STATUS_LOCAL_BRANCH,
-       WT_STATUS_REMOTE_BRANCH
+       WT_STATUS_REMOTE_BRANCH,
+       WT_STATUS_ONBRANCH,
+       WT_STATUS_MAXSLOT
 };
 
 enum untracked_status_type {
@@ -46,7 +48,7 @@ struct wt_status {
        int show_ignored_files;
        enum untracked_status_type show_untracked_files;
        const char *ignore_submodule_arg;
-       char color_palette[WT_STATUS_REMOTE_BRANCH+1][COLOR_MAXLEN];
+       char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
 
        /* These are computed during processing of the individual sections */
        int commitable;
index cd2285de1cb1faa9f7c6c97dd22210f20bb046a3..164581f87f49935f0d1b1885420960a4d11dea56 100644 (file)
@@ -212,8 +212,10 @@ int read_mmfile(mmfile_t *ptr, const char *filename)
                return error("Could not open %s", filename);
        sz = xsize_t(st.st_size);
        ptr->ptr = xmalloc(sz ? sz : 1);
-       if (sz && fread(ptr->ptr, sz, 1, f) != 1)
+       if (sz && fread(ptr->ptr, sz, 1, f) != 1) {
+               fclose(f);
                return error("Could not read %s", filename);
+       }
        fclose(f);
        ptr->size = sz;
        return 0;
@@ -286,9 +288,8 @@ static long ff_regexp(const char *line, long len,
        result = pmatch[i].rm_eo - pmatch[i].rm_so;
        if (result > buffer_size)
                result = buffer_size;
-       else
-               while (result > 0 && (isspace(line[result - 1])))
-                       result--;
+       while (result > 0 && (isspace(line[result - 1])))
+               result--;
        memcpy(buffer, line, result);
  fail:
        free(line_buffer);
index c4bedf0d1ce1252563d7f36da7d846f5943343b0..277e2eec5b4cced5882f37ac42af7f22efe92902 100644 (file)
@@ -85,27 +85,6 @@ static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
        return -1;
 }
 
-static void xdl_find_func(xdfile_t *xf, long i, char *buf, long sz, long *ll,
-               find_func_t ff, void *ff_priv) {
-
-       /*
-        * Be quite stupid about this for now.  Find a line in the old file
-        * before the start of the hunk (and context) which starts with a
-        * plausible character.
-        */
-
-       const char *rec;
-       long len;
-
-       while (i-- > 0) {
-               len = xdl_get_rec(xf, i, &rec);
-               if ((*ll = ff(rec, len, buf, sz, ff_priv)) >= 0)
-                       return;
-       }
-       *ll = 0;
-}
-
-
 static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                            xdemitconf_t const *xecfg) {
        xdfile_t *xdf = &xe->xdf1;
@@ -127,6 +106,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
        xdchange_t *xch, *xche;
        char funcbuf[80];
        long funclen = 0;
+       long funclineprev = -1;
        find_func_t ff = xecfg->find_func ?  xecfg->find_func : def_ff;
 
        if (xecfg->flags & XDL_EMIT_COMMON)
@@ -150,9 +130,19 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                 */
 
                if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
-                       xdl_find_func(&xe->xdf1, s1, funcbuf,
-                                     sizeof(funcbuf), &funclen,
-                                     ff, xecfg->find_func_priv);
+                       long l;
+                       for (l = s1 - 1; l >= 0 && l > funclineprev; l--) {
+                               const char *rec;
+                               long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
+                               long newfunclen = ff(rec, reclen, funcbuf,
+                                                    sizeof(funcbuf),
+                                                    xecfg->find_func_priv);
+                               if (newfunclen >= 0) {
+                                       funclen = newfunclen;
+                                       break;
+                               }
+                       }
+                       funclineprev = s1 - 1;
                }
                if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
                                      funcbuf, funclen, ecb) < 0)
index 8ef232cfad12d706aaafe705bf16c546f3597182..165a895a93e04b33ca7c8f3839ee85e0eccb4a07 100644 (file)
@@ -30,6 +30,7 @@
 #define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
 #define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
 #define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+#define XDL_ISSPACE(c) (isspace((unsigned char)(c)))
 #define XDL_ADDBITS(v,b)       ((v) + ((v) >> (b)))
 #define XDL_MASKBITS(b)                ((1UL << (b)) - 1)
 #define XDL_HASHLONG(v,b)      (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
index 6d6fc1bc5e01be7305ec5101f43645a1dce69bd3..9e13b25abc90350de4488276074dc282edc216b1 100644 (file)
@@ -336,7 +336,7 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
 static int line_contains_alnum(const char *ptr, long size)
 {
        while (size--)
-               if (isalnum(*(ptr++)))
+               if (isalnum((unsigned char)*(ptr++)))
                        return 1;
        return 0;
 }
index 22f9bd692c2706136b4938bbcb2e0fc3dab212b8..ab6503460f760356a393a3c7b3b8438213e1ee94 100644 (file)
@@ -211,18 +211,18 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
                        if (l1[i1++] != l2[i2++])
                                return 0;
                skip_ws:
-                       while (i1 < s1 && isspace(l1[i1]))
+                       while (i1 < s1 && XDL_ISSPACE(l1[i1]))
                                i1++;
-                       while (i2 < s2 && isspace(l2[i2]))
+                       while (i2 < s2 && XDL_ISSPACE(l2[i2]))
                                i2++;
                }
        } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
                while (i1 < s1 && i2 < s2) {
-                       if (isspace(l1[i1]) && isspace(l2[i2])) {
+                       if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
                                /* Skip matching spaces and try again */
-                               while (i1 < s1 && isspace(l1[i1]))
+                               while (i1 < s1 && XDL_ISSPACE(l1[i1]))
                                        i1++;
-                               while (i2 < s2 && isspace(l2[i2]))
+                               while (i2 < s2 && XDL_ISSPACE(l2[i2]))
                                        i2++;
                                continue;
                        }
@@ -241,13 +241,13 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
         * while there still are characters remaining on both lines.
         */
        if (i1 < s1) {
-               while (i1 < s1 && isspace(l1[i1]))
+               while (i1 < s1 && XDL_ISSPACE(l1[i1]))
                        i1++;
                if (s1 != i1)
                        return 0;
        }
        if (i2 < s2) {
-               while (i2 < s2 && isspace(l2[i2]))
+               while (i2 < s2 && XDL_ISSPACE(l2[i2]))
                        i2++;
                return (s2 == i2);
        }
@@ -260,10 +260,10 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data,
        char const *ptr = *data;
 
        for (; ptr < top && *ptr != '\n'; ptr++) {
-               if (isspace(*ptr)) {
+               if (XDL_ISSPACE(*ptr)) {
                        const char *ptr2 = ptr;
                        int at_eol;
-                       while (ptr + 1 < top && isspace(ptr[1])
+                       while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
                                        && ptr[1] != '\n')
                                ptr++;
                        at_eol = (top <= ptr + 1 || ptr[1] == '\n');
diff --git a/zlib.c b/zlib.c
new file mode 100644 (file)
index 0000000..c4d58da
--- /dev/null
+++ b/zlib.c
@@ -0,0 +1,61 @@
+/*
+ * zlib wrappers to make sure we don't silently miss errors
+ * at init time.
+ */
+#include "cache.h"
+
+void git_inflate_init(z_streamp strm)
+{
+       const char *err;
+
+       switch (inflateInit(strm)) {
+       case Z_OK:
+               return;
+
+       case Z_MEM_ERROR:
+               err = "out of memory";
+               break;
+       case Z_VERSION_ERROR:
+               err = "wrong version";
+               break;
+       default:
+               err = "error";
+       }
+       die("inflateInit: %s (%s)", err, strm->msg ? strm->msg : "no message");
+}
+
+void git_inflate_end(z_streamp strm)
+{
+       if (inflateEnd(strm) != Z_OK)
+               error("inflateEnd: %s", strm->msg ? strm->msg : "failed");
+}
+
+int git_inflate(z_streamp strm, int flush)
+{
+       int ret = inflate(strm, flush);
+       const char *err;
+
+       switch (ret) {
+       /* Out of memory is fatal. */
+       case Z_MEM_ERROR:
+               die("inflate: out of memory");
+
+       /* Data corruption errors: we may want to recover from them (fsck) */
+       case Z_NEED_DICT:
+               err = "needs dictionary"; break;
+       case Z_DATA_ERROR:
+               err = "data stream error"; break;
+       case Z_STREAM_ERROR:
+               err = "stream consistency error"; break;
+       default:
+               err = "unknown error"; break;
+
+       /* Z_BUF_ERROR: normal, needs more space in the output buffer */
+       case Z_BUF_ERROR:
+       case Z_OK:
+       case Z_STREAM_END:
+               return ret;
+       }
+       error("inflate: %s (%s)", err, strm->msg ? strm->msg : "no message");
+       return ret;
+}