Code

Merge branch 'maint-1.6.2' into maint-1.6.3
authorJunio C Hamano <gitster@pobox.com>
Thu, 3 Sep 2009 16:42:38 +0000 (09:42 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 3 Sep 2009 16:42:38 +0000 (09:42 -0700)
* maint-1.6.2:
  git-clone: add missing comma in --reference documentation
  clone: disconnect transport after fetching

460 files changed:
.gitattributes
.gitignore
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/RelNotes-1.6.3.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.3.2.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.3.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.3.4.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.3.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/asciidoc.conf
Documentation/blame-options.txt
Documentation/callouts.xsl [deleted file]
Documentation/config.txt
Documentation/docbook-xsl.css
Documentation/fetch-options.txt
Documentation/git-archive.txt
Documentation/git-bisect.txt
Documentation/git-branch.txt
Documentation/git-cat-file.txt
Documentation/git-check-ref-format.txt
Documentation/git-checkout.txt
Documentation/git-clean.txt
Documentation/git-clone.txt
Documentation/git-config.txt
Documentation/git-cvsimport.txt
Documentation/git-difftool.txt [new file with mode: 0644]
Documentation/git-filter-branch.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-grep.txt
Documentation/git-imap-send.txt
Documentation/git-merge.txt
Documentation/git-mergetool--lib.txt [new file with mode: 0644]
Documentation/git-mergetool.txt
Documentation/git-patch-id.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-remote.txt
Documentation/git-rev-list.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-shell.txt
Documentation/git-show-branch.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-symbolic-ref.txt
Documentation/git-update-server-info.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitcli.txt
Documentation/gitcore-tutorial.txt
Documentation/githooks.txt
Documentation/gittutorial.txt
Documentation/glossary-content.txt
Documentation/manpage-1.72.xsl
Documentation/manpage-base.xsl [new file with mode: 0644]
Documentation/manpage-bold-literal.xsl [new file with mode: 0644]
Documentation/manpage-normal.xsl [new file with mode: 0644]
Documentation/manpage-suppress-sp.xsl [new file with mode: 0644]
Documentation/merge-config.txt
Documentation/merge-options.txt
Documentation/merge-strategies.txt
Documentation/pretty-formats.txt
Documentation/pretty-options.txt
Documentation/rev-list-options.txt
Documentation/technical/api-parse-options.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
Makefile
RelNotes
alias.c
alloc.c
archive-tar.c
archive.c
archive.h
attr.c
attr.h
base85.c
bisect.c [new file with mode: 0644]
bisect.h [new file with mode: 0644]
branch.c
branch.h
builtin-add.c
builtin-apply.c
builtin-archive.c
builtin-bisect--helper.c [new file with mode: 0644]
builtin-blame.c
builtin-branch.c
builtin-cat-file.c
builtin-check-ref-format.c
builtin-checkout-index.c
builtin-checkout.c
builtin-clean.c
builtin-clone.c
builtin-commit.c
builtin-config.c
builtin-describe.c
builtin-diff-tree.c
builtin-fast-export.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-for-each-ref.c
builtin-gc.c
builtin-grep.c
builtin-help.c
builtin-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-ls-tree.c
builtin-mailinfo.c
builtin-merge.c
builtin-pack-objects.c
builtin-prune-packed.c
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-receive-pack.c
builtin-reflog.c
builtin-remote.c
builtin-rerere.c
builtin-reset.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-rm.c
builtin-send-pack.c
builtin-shortlog.c
builtin-show-branch.c
builtin-show-ref.c
builtin-tag.c
builtin-tar-tree.c
builtin-unpack-objects.c
builtin-update-index.c
builtin-upload-archive.c
builtin-verify-tag.c
builtin.h
cache.h
color.c
color.h
combine-diff.c
command-list.txt
compat/cygwin.c
compat/fnmatch/fnmatch.c
compat/mingw.c
compat/mingw.h
compat/regex/regex.c
compat/win32mmap.c [new file with mode: 0644]
compat/winansi.c
config.c
configure.ac
connect.c
contrib/completion/git-completion.bash
contrib/convert-objects/convert-objects.c
contrib/difftool/git-difftool [deleted file]
contrib/difftool/git-difftool-helper [deleted file]
contrib/difftool/git-difftool.txt [deleted file]
contrib/fast-import/git-p4
contrib/fast-import/import-tars.perl
contrib/hg-to-git/hg-to-git.py
contrib/thunderbird-patch-inline/README
ctype.c
daemon.c
decorate.c
delta.h
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-break.c
diffcore-rename.c
dir.c
dir.h
entry.c
environment.c
fast-import.c
fsck.c
fsck.h
git-add--interactive.perl
git-am.sh
git-bisect.sh
git-compat-util.h
git-cvsserver.perl
git-difftool--helper.sh [new file with mode: 0755]
git-difftool.perl [new file with mode: 0755]
git-filter-branch.sh
git-gui/Makefile
git-gui/git-gui.sh
git-gui/lib/branch_delete.tcl
git-gui/lib/checkout_op.tcl
git-gui/lib/choose_repository.tcl
git-gui/lib/commit.tcl
git-gui/lib/diff.tcl
git-gui/lib/mergetool.tcl
git-gui/lib/remote_branch_delete.tcl
git-gui/lib/shortcut.tcl
git-gui/lib/tools.tcl
git-gui/po/de.po
git-gui/po/fr.po
git-gui/po/git-gui.pot
git-gui/po/hu.po
git-gui/po/it.po
git-gui/po/ja.po
git-gui/po/nb.po
git-gui/po/ru.po
git-gui/po/sv.po
git-gui/po/zh_cn.po
git-gui/windows/git-gui.sh
git-instaweb.sh
git-merge-one-file.sh
git-merge-resolve.sh
git-mergetool--lib.sh [new file with mode: 0644]
git-mergetool.sh
git-parse-remote.sh
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-request-pull.sh
git-send-email.perl
git-submodule.sh
git-svn.perl
git.c
gitk-git/gitk
gitk-git/po/ru.po [new file with mode: 0644]
gitweb/README
gitweb/gitweb.perl
graph.c
grep.c
grep.h
help.c
http-push.c
http-walker.c
http.c
http.h
imap-send.c
index-pack.c
levenshtein.c
list-objects.c
list-objects.h
ll-merge.c
lockfile.c
log-tree.c
log-tree.h
mailmap.c
merge-recursive.c
object.c
pack-refs.c
parse-options.c
parse-options.h
patch-delta.c
patch-ids.c
perl/Git.pm
pretty.c
progress.c
quote.c
quote.h
read-cache.c
reflog-walk.c
reflog-walk.h
refs.c
refs.h
remote.c
remote.h
rerere.c
rerere.h
revision.c
revision.h
run-command.c
send-pack.h
server-info.c
sha1-lookup.c
sha1-lookup.h
sha1_file.c
sha1_name.c
shell.c
sideband.c
sideband.h
strbuf.c
strbuf.h
string-list.c
string-list.h
symlinks.c
t/Makefile
t/README
t/annotate-tests.sh
t/lib-git-svn.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/t0000-basic.sh
t/t0004-unwritable.sh
t/t0020-crlf.sh
t/t0024-crlf-archive.sh
t/t0050-filesystem.sh
t/t0055-beyond-symlinks.sh
t/t0060-path-utils.sh
t/t1004-read-tree-m-u-wf.sh
t/t1020-subdirectory.sh
t/t1100-commit-tree-options.sh
t/t1300-repo-config.sh
t/t1301-shared-repo.sh
t/t1400-update-ref.sh
t/t1410-reflog.sh
t/t1411-reflog-show.sh [new file with mode: 0755]
t/t1450-fsck.sh
t/t1504-ceiling-dirs.sh
t/t2001-checkout-cache-clash.sh
t/t2003-checkout-cache-mkdir.sh
t/t2004-checkout-cache-temp.sh
t/t2007-checkout-symlink.sh
t/t2100-update-cache-badpath.sh
t/t2200-add-update.sh
t/t2201-add-update-typechange.sh
t/t2300-cd-to-toplevel.sh
t/t3000-ls-files-others.sh
t/t3010-ls-files-killed-modified.sh
t/t3030-merge-recursive.sh
t/t3031-merge-criscross.sh [new file with mode: 0755]
t/t3100-ls-tree-restrict.sh
t/t3200-branch.sh
t/t3203-branch-output.sh [new file with mode: 0755]
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3406-rebase-message.sh
t/t3409-rebase-hook.sh [deleted file]
t/t3413-rebase-hook.sh [new file with mode: 0755]
t/t3505-cherry-pick-empty.sh
t/t3600-rm.sh
t/t3700-add.sh
t/t3701-add-interactive.sh
t/t4002-diff-basic.sh
t/t4004-diff-rename-symlink.sh
t/t4006-diff-mode.sh
t/t4008-diff-break-rewrite.sh
t/t4011-diff-symlink.sh
t/t4012-diff-binary.sh
t/t4013-diff-various.sh
t/t4013/diff.diff_--dirstat_master~1_master~2 [new file with mode: 0644]
t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side [new file with mode: 0644]
t/t4013/diff.format-patch_--attach_--stdout_initial..master
t/t4013/diff.format-patch_--attach_--stdout_initial..master^
t/t4013/diff.format-patch_--attach_--stdout_initial..side
t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master [new file with mode: 0644]
t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
t/t4013/diff.format-patch_--inline_--stdout_initial..master
t/t4013/diff.format-patch_--inline_--stdout_initial..master^
t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
t/t4013/diff.format-patch_--inline_--stdout_initial..side
t/t4013/diff.log_--decorate_--all [new file with mode: 0644]
t/t4013/diff.rev-list_--children_HEAD [new file with mode: 0644]
t/t4013/diff.rev-list_--parents_HEAD [new file with mode: 0644]
t/t4014-format-patch.sh
t/t4017-quiet.sh [deleted file]
t/t4021-format-patch-numbered.sh
t/t4021-format-patch-signer-mime.sh [deleted file]
t/t4023-diff-rename-typechange.sh
t/t4029-diff-trailing-space.sh
t/t4035-diff-quiet.sh [new file with mode: 0755]
t/t4036-format-patch-signer-mime.sh [new file with mode: 0755]
t/t4038-diff-combined.sh [new file with mode: 0755]
t/t4102-apply-rename.sh
t/t4114-apply-typechange.sh
t/t4115-apply-symlink.sh
t/t4118-apply-empty-context.sh
t/t4122-apply-symlink-inside.sh
t/t4124-apply-ws-rule.sh
t/t4129-apply-samemode.sh
t/t4131-apply-fake-ancestor.sh [new file with mode: 0755]
t/t4150-am.sh
t/t4200-rerere.sh
t/t4202-log.sh
t/t4204-patch-id.sh [new file with mode: 0755]
t/t5000-tar-tree.sh
t/t5001-archive-attr.sh [new file with mode: 0755]
t/t5300-pack-object.sh
t/t5302-pack-index.sh
t/t5303-pack-corruption-resilience.sh
t/t5403-post-checkout-hook.sh
t/t5503-tagfollow.sh
t/t5505-remote.sh
t/t5506-remote-groups.sh [new file with mode: 0755]
t/t5511-refspec.sh
t/t5515-fetch-merge-logic.sh
t/t5521-pull-symlink.sh [deleted file]
t/t5522-pull-symlink.sh [new file with mode: 0755]
t/t5540-http-push.sh
t/t5550-http-fetch.sh [new file with mode: 0755]
t/t5601-clone.sh
t/t5602-clone-remote-exec.sh
t/t5701-clone-local.sh
t/t5705-clone-2gb.sh [new file with mode: 0755]
t/t6023-merge-file.sh
t/t6023-merge-rename-nocruft.sh [deleted file]
t/t6030-bisect-porcelain.sh
t/t6031-merge-recursive.sh
t/t6034-merge-rename-nocruft.sh [new file with mode: 0755]
t/t6040-tracking-info.sh
t/t6200-fmt-merge-msg.sh
t/t6300-for-each-ref.sh
t/t7001-mv.sh
t/t7002-grep.sh
t/t7004-tag.sh
t/t7005-editor.sh
t/t7201-co.sh
t/t7300-clean.sh
t/t7400-submodule-basic.sh
t/t7405-submodule-merge.sh
t/t7500-commit.sh
t/t7501-commit.sh
t/t7502-commit.sh
t/t7502-status.sh [deleted file]
t/t7503-pre-commit-hook.sh
t/t7504-commit-msg-hook.sh
t/t7508-status.sh [new file with mode: 0755]
t/t7700-repack.sh
t/t7800-difftool.sh [new file with mode: 0755]
t/t8003-blame.sh
t/t8005-blame-i18n.sh
t/t8005/euc-japan.txt [new file with mode: 0644]
t/t8005/sjis.txt
t/t8005/utf8.txt
t/t9001-send-email.sh
t/t9100-git-svn-basic.sh
t/t9106-git-svn-dcommit-clobber-series.sh [deleted file]
t/t9108-git-svn-multi-glob.sh [deleted file]
t/t9109-git-svn-multi-glob.sh [new file with mode: 0755]
t/t9129-git-svn-i18n-commitencoding.sh
t/t9131-git-svn-empty-symlink.sh
t/t9132-git-svn-broken-symlink.sh
t/t9134-git-svn-ignore-paths.sh
t/t9137-git-svn-dcommit-clobber-series.sh [new file with mode: 0755]
t/t9200-git-cvsexportcommit.sh
t/t9301-fast-export.sh
t/t9400-git-cvsserver-server.sh
t/t9401-git-cvsserver-crlf.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9600-cvsimport.sh
t/t9700-perl-git.sh
t/test-lib.sh
t/valgrind/.gitignore [new file with mode: 0644]
t/valgrind/analyze.sh [new file with mode: 0755]
t/valgrind/default.supp [new file with mode: 0644]
t/valgrind/valgrind.sh [new file with mode: 0755]
templates/hooks--pre-commit.sample
templates/hooks--update.sample
templates/this--description
transport.c
transport.h
tree-diff.c
unimplemented.sh [new file with mode: 0644]
unpack-trees.c
upload-pack.c
userdiff.c
wrapper.c
ws.c
wt-status.c
xdiff-interface.c
xdiff/xdiffi.c
xdiff/xemit.c
xdiff/xmerge.c

index 6b9c715d21d5486e59083fb6071566aa6ecd4d42..0636deea9357d2f1e9331119f02fb75fb6b15393 100644 (file)
@@ -1,2 +1,2 @@
 * whitespace=!indent,trail,space
-*.[ch] whitespace
+*.[ch] whitespace=indent,trail,space
index 1c57d4c958bc5e8ff539c5f5ddb1c784d923271b..41c0b20a76ce0fd47c7cafc51777336ce99ce1b0 100644 (file)
@@ -11,6 +11,7 @@ git-apply
 git-archimport
 git-archive
 git-bisect
+git-bisect--helper
 git-blame
 git-branch
 git-bundle
@@ -35,6 +36,8 @@ git-diff
 git-diff-files
 git-diff-index
 git-diff-tree
+git-difftool
+git-difftool--helper
 git-describe
 git-fast-export
 git-fast-import
@@ -78,6 +81,7 @@ git-merge-recursive
 git-merge-resolve
 git-merge-subtree
 git-mergetool
+git-mergetool--lib
 git-mktag
 git-mktree
 git-name-rev
index 0d7fa9cca9e5c3ec8a11cd2c878f5a7afe353abe..b8bf618a30fd32a014e41e1ba9914f5e652bdefd 100644 (file)
@@ -129,3 +129,6 @@ For C programs:
    used in the git core command set (unless your command is clearly
    separate from it, such as an importer to convert random-scm-X
    repositories to git).
+
+ - When we pass <string, length> pair to functions, we should try to
+   pass them in that order.
index 144ec32f12a7950746dd71e2ddde5b3f0cab57b0..7a8037f586773c00004e34079b48bb514db2515f 100644 (file)
@@ -41,7 +41,8 @@ man7dir=$(mandir)/man7
 
 ASCIIDOC=asciidoc
 ASCIIDOC_EXTRA =
-MANPAGE_XSL = callouts.xsl
+MANPAGE_XSL = manpage-normal.xsl
+XMLTO_EXTRA =
 INSTALL?=install
 RM ?= rm -f
 DOC_REF = origin/man
@@ -59,13 +60,52 @@ endif
 -include ../config.mak.autogen
 -include ../config.mak
 
+#
+# For asciidoc ...
+#      -7.1.2, no extra settings are needed.
+#      8.0-,   set ASCIIDOC8.
+#
+
+#
+# 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.69.1-1.71.0,  set DOCBOOK_SUPPRESS_SP?
+#      1.71.1,         no extra settings are needed?
+#      1.72.0,         set DOCBOOK_XSL_172.
+#      1.73.0-,        set ASCIIDOC_NO_ROFF
+#
+
+#
+# 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
 ASCIIDOC_EXTRA += -a asciidoc7compatible
 endif
 ifdef DOCBOOK_XSL_172
-ASCIIDOC_EXTRA += -a docbook-xsl-172
+ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
 MANPAGE_XSL = manpage-1.72.xsl
+else
+       ifdef ASCIIDOC_NO_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
+       endif
 endif
+ifdef MAN_BOLD_LITERAL
+XMLTO_EXTRA += -m manpage-bold-literal.xsl
+endif
+ifdef DOCBOOK_SUPPRESS_SP
+XMLTO_EXTRA += -m manpage-suppress-sp.xsl
+endif
+
+SHELL_PATH ?= $(SHELL)
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 
 #
 # Please note that there is a minor bug in asciidoc.
@@ -76,6 +116,32 @@ endif
 # yourself - yes, all 6 characters of it!
 #
 
+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1  =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+       QUIET_ASCIIDOC  = @echo '   ' ASCIIDOC $@;
+       QUIET_XMLTO     = @echo '   ' XMLTO $@;
+       QUIET_DB2TEXI   = @echo '   ' DB2TEXI $@;
+       QUIET_MAKEINFO  = @echo '   ' MAKEINFO $@;
+       QUIET_DBLATEX   = @echo '   ' DBLATEX $@;
+       QUIET_XSLTPROC  = @echo '   ' XSLTPROC $@;
+       QUIET_GEN       = @echo '   ' GEN $@;
+       QUIET_STDERR    = 2> /dev/null
+       QUIET_SUBDIR0   = +@subdir=
+       QUIET_SUBDIR1   = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
+                         $(MAKE) $(PRINT_DIR) -C $$subdir
+       export V
+endif
+endif
+
 all: html man
 
 html: $(DOC_HTML)
@@ -116,10 +182,10 @@ install-pdf: pdf
        $(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir)
 
 install-html: html
-       sh ./install-webdoc.sh $(DESTDIR)$(htmldir)
+       '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
 
 ../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
-       $(MAKE) -C ../ GIT-VERSION-FILE
+       $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
 
 -include ../GIT-VERSION-FILE
 
@@ -127,8 +193,8 @@ install-html: html
 # Determine "include::" file references in asciidoc files.
 #
 doc.dep : $(wildcard *.txt) build-docdep.perl
-       $(RM) $@+ $@
-       $(PERL_PATH) ./build-docdep.perl >$@+
+       $(QUIET_GEN)$(RM) $@+ $@ && \
+       $(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \
        mv $@+ $@
 
 -include doc.dep
@@ -146,102 +212,105 @@ cmds_txt = cmds-ancillaryinterrogators.txt \
 $(cmds_txt): cmd-list.made
 
 cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
-       $(RM) $@
-       $(PERL_PATH) ./cmd-list.perl ../command-list.txt
+       $(QUIET_GEN)$(RM) $@ && \
+       $(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \
        date >$@
 
 clean:
        $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
-       $(RM) *.texi *.texi+ git.info gitman.info
+       $(RM) *.texi *.texi+ *.texi++ git.info gitman.info
        $(RM) howto-index.txt howto/*.html doc.dep
        $(RM) technical/api-*.html technical/api-index.txt
        $(RM) $(cmds_txt) *.made
 
 $(MAN_HTML): %.html : %.txt
-       $(RM) $@+ $@
+       $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
        $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \
-               $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $<
+               $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \
        mv $@+ $@
 
 %.1 %.5 %.7 : %.xml
-       $(RM) $@
-       xmlto -m $(MANPAGE_XSL) man $<
+       $(QUIET_XMLTO)$(RM) $@ && \
+       xmlto -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
 
 %.xml : %.txt
-       $(RM) $@+ $@
+       $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
        $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
-               $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $<
+               $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \
        mv $@+ $@
 
 user-manual.xml: user-manual.txt user-manual.conf
-       $(ASCIIDOC) -b docbook -d book $<
+       $(QUIET_ASCIIDOC)$(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d book $<
 
 technical/api-index.txt: technical/api-index-skel.txt \
        technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS))
-       cd technical && sh ./api-index.sh
+       $(QUIET_GEN)cd technical && '$(SHELL_PATH_SQ)' ./api-index.sh
 
 $(patsubst %,%.html,$(API_DOCS) technical/api-index): %.html : %.txt
-       $(ASCIIDOC) -b xhtml11 -f asciidoc.conf \
+       $(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 -f asciidoc.conf \
                $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) $*.txt
 
 XSLT = docbook.xsl
 XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
 
 user-manual.html: user-manual.xml
-       xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
+       $(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
 
 git.info: user-manual.texi
-       $(MAKEINFO) --no-split -o $@ user-manual.texi
+       $(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ user-manual.texi
 
 user-manual.texi: user-manual.xml
-       $(RM) $@+ $@
-       $(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout | \
-               $(PERL_PATH) fix-texi.perl >$@+
+       $(QUIET_DB2TEXI)$(RM) $@+ $@ && \
+       $(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \
+       $(PERL_PATH) fix-texi.perl <$@++ >$@+ && \
+       rm $@++ && \
        mv $@+ $@
 
 user-manual.pdf: user-manual.xml
-       $(RM) $@+ $@
-       $(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $<
+       $(QUIET_DBLATEX)$(RM) $@+ $@ && \
+       $(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< && \
        mv $@+ $@
 
 gitman.texi: $(MAN_XML) cat-texi.perl
-       $(RM) $@+ $@
+       $(QUIET_DB2TEXI)$(RM) $@+ $@ && \
        ($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \
-               --to-stdout $(xml);)) | $(PERL_PATH) cat-texi.perl $@ >$@+
+               --to-stdout $(xml) &&) true) > $@++ && \
+       $(PERL_PATH) cat-texi.perl $@ <$@++ >$@+ && \
+       rm $@++ && \
        mv $@+ $@
 
 gitman.info: gitman.texi
-       $(MAKEINFO) --no-split --no-validate $*.texi
+       $(QUIET_MAKEINFO)$(MAKEINFO) --no-split --no-validate $*.texi
 
 $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
-       $(RM) $@+ $@
-       $(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+
+       $(QUIET_DB2TEXI)$(RM) $@+ $@ && \
+       $(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+ && \
        mv $@+ $@
 
 howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
-       $(RM) $@+ $@
-       sh ./howto-index.sh $(wildcard howto/*.txt) >$@+
+       $(QUIET_GEN)$(RM) $@+ $@ && \
+       '$(SHELL_PATH_SQ)' ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \
        mv $@+ $@
 
 $(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
-       $(ASCIIDOC) -b xhtml11 $*.txt
+       $(QUIET_ASCIIDOC)$(ASCIIDOC) $(ASCIIDOC_EXTRA) -b xhtml11 $*.txt
 
 WEBDOC_DEST = /pub/software/scm/git/docs
 
 $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
-       $(RM) $@+ $@
-       sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+
+       $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+       sed -e '1,/^$$/d' $< | $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b xhtml11 - >$@+ && \
        mv $@+ $@
 
 install-webdoc : html
-       sh ./install-webdoc.sh $(WEBDOC_DEST)
+       '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST)
 
 quick-install: quick-install-man
 
 quick-install-man:
-       sh ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
+       '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
 
 quick-install-html:
-       sh ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
+       '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
 
 .PHONY: .FORCE-GIT-VERSION-FILE
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).
index 9b559adefce5c9a89e58d30ae79820a9ef2e9072..76fc84d8780762e083cd4ca584b9d783b8c0cd81 100644 (file)
@@ -6,9 +6,13 @@ Checklist (and a short version for the impatient):
        - check for unnecessary whitespace with "git diff --check"
          before committing
        - do not check in commented out code or unneeded files
-       - provide a meaningful commit message
        - the first line of the commit message should be a short
          description and should skip the full stop
+       - the body should provide a meaningful commit message, which:
+               - uses the imperative, present tense: "change",
+                 not "changed" or "changes".
+               - includes motivation for the change, and contrasts
+                 its implementation with previous behaviour
        - if you want your work included in git.git, add a
          "Signed-off-by: Your Name <you@example.com>" line to the
          commit message (or just use the option "-s" when
@@ -62,6 +66,14 @@ Describe the technical detail of the change(s).
 
 If your description starts to get too long, that's a sign that you
 probably need to split up your commit to finer grained pieces.
+That being said, patches which plainly describe the things that
+help reviewers check the patch, and future maintainers understand
+the code, are the most beautiful patches.  Descriptions that summarise
+the point in the subject well, and describe the motivation for the
+change, the approach taken by the change, and if relevant how this
+differs substantially from the prior version, can be found on Usenet
+archives back into the late 80's.  Consider it like good Netiquette,
+but for code.
 
 Oh, another thing.  I am picky about whitespaces.  Make sure your
 changes do not trigger errors with the sample pre-commit hook shipped
@@ -491,6 +503,12 @@ message, complete the addressing and subject fields, and press send.
 Gmail
 -----
 
+GMail does not appear to have any way to turn off line wrapping in the web
+interface, so this will mangle any emails that you send.  You can however
+use any IMAP email client to connect to the google imap server, and forward
+the emails through that.  Just make sure to disable line wrapping in that
+email client.  Alternatively, use "git send-email" instead.
+
 Submitting properly formatted patches via Gmail is simple now that
 IMAP support is available. First, edit your ~/.gitconfig to specify your
 account settings:
@@ -503,6 +521,9 @@ account settings:
        port = 993
        sslverify = false
 
+You might need to instead use: folder = "[Google Mail]/Drafts" if you get an error
+that the "Folder doesn't exist".
+
 Next, ensure that your Gmail settings are correct. In "Settings" the
 "Use Unicode (UTF-8) encoding for outgoing messages" should be checked.
 
@@ -513,3 +534,4 @@ command to send the patch emails to your Gmail Drafts folder.
 
 Go to your Gmail account, open the Drafts folder, find the patch email, fill
 in the To: and CC: fields and send away!
+
index 1e735df3bb5ba15b7f088442439e48a99ca85dbd..87a90f2c3fd48bbb593a2cef135310f88d87e4b4 100644 (file)
@@ -17,6 +17,7 @@ caret=&#94;
 startsb=&#91;
 endsb=&#93;
 tilde=&#126;
+backtick=&#96;
 
 ifdef::backend-docbook[]
 [linkgit-inlinemacro]
@@ -27,7 +28,7 @@ ifdef::backend-docbook[]
 endif::backend-docbook[]
 
 ifdef::backend-docbook[]
-ifndef::docbook-xsl-172[]
+ifndef::git-asciidoc-no-roff[]
 # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
 # v1.72 breaks with this because it replaces dots not in roff requests.
 [listingblock]
@@ -42,16 +43,16 @@ ifdef::doctype-manpage[]
 endif::doctype-manpage[]
 </literallayout>
 {title#}</example>
-endif::docbook-xsl-172[]
+endif::git-asciidoc-no-roff[]
 
-ifdef::docbook-xsl-172[]
+ifdef::git-asciidoc-no-roff[]
 ifdef::doctype-manpage[]
 # The following two small workarounds insert a simple paragraph after screen
 [listingblock]
 <example><title>{title}</title>
-<screen>
+<literallayout>
 |
-</screen><simpara></simpara>
+</literallayout><simpara></simpara>
 {title#}</example>
 
 [verseblock]
@@ -59,10 +60,11 @@ ifdef::doctype-manpage[]
 {title%}<literallayout{id? id="{id}"}>
 {title#}<literallayout>
 |
-</literallayout><simpara></simpara>
+</literallayout>
 {title#}</para></formalpara>
+{title%}<simpara></simpara>
 endif::doctype-manpage[]
-endif::docbook-xsl-172[]
+endif::git-asciidoc-no-roff[]
 endif::backend-docbook[]
 
 ifdef::doctype-manpage[]
index 26cfb6219591e982a360f3303889bd594e5d3293..1625ffce6a1910c879447705fea4e3301039debd 100644 (file)
@@ -70,6 +70,14 @@ of lines before or after the line given by <start>.
        tree copy has the contents of the named file (specify
        `-` to make the command read from the standard input).
 
+--date <format>::
+       The value is one of the following alternatives:
+       {relative,local,default,iso,rfc,short}. If --date is not
+       provided, the value of the blame.date config variable is
+       used. If the blame.date config variable is also not set, the
+       iso format is used. For more information, See the discussion
+       of the --date option at linkgit:git-log[1].
+
 -M|<num>|::
        Detect moving lines in the file as well.  When a commit
        moves a block of lines in a file (e.g. the original file
diff --git a/Documentation/callouts.xsl b/Documentation/callouts.xsl
deleted file mode 100644 (file)
index 6a361a2..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<!-- callout.xsl: converts asciidoc callouts to man page format -->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
-<xsl:template match="co">
-       <xsl:value-of select="concat('\fB(',substring-after(@id,'-'),')\fR')"/>
-</xsl:template>
-<xsl:template match="calloutlist">
-       <xsl:text>.sp&#10;</xsl:text>
-       <xsl:apply-templates/>
-       <xsl:text>&#10;</xsl:text>
-</xsl:template>
-<xsl:template match="callout">
-       <xsl:value-of select="concat('\fB',substring-after(@arearefs,'-'),'. \fR')"/>
-       <xsl:apply-templates/>
-       <xsl:text>.br&#10;</xsl:text>
-</xsl:template>
-
-<!-- sorry, this is not about callouts, but attempts to work around
- spurious .sp at the tail of the line docbook stylesheets seem to add -->
-<xsl:template match="simpara">
-  <xsl:variable name="content">
-    <xsl:apply-templates/>
-  </xsl:variable>
-  <xsl:value-of select="normalize-space($content)"/>
-  <xsl:if test="not(ancestor::authorblurb) and
-                not(ancestor::personblurb)">
-    <xsl:text>&#10;&#10;</xsl:text>
-  </xsl:if>
-</xsl:template>
-
-</xsl:stylesheet>
index 6af58ff3aa9766a545079740978d85da82b3c3cf..181c2f9bdeb9a453a0e61c69f1806c33fc7832fc 100644 (file)
@@ -2,15 +2,15 @@ CONFIGURATION FILE
 ------------------
 
 The git configuration file contains a number of variables that affect
-the git command's behavior. `.git/config` file for each repository
-is used to store the information for that repository, and
-`$HOME/.gitconfig` is used to store per user information to give
-fallback values for `.git/config` file. The file `/etc/gitconfig`
-can be used to store system-wide defaults.
-
-They can be used by both the git plumbing
-and the porcelains. The variables are divided into sections, where
-in the fully qualified variable name the variable itself is the last
+the git command's behavior. The `.git/config` file in each repository
+is used to store the configuration for that repository, and
+`$HOME/.gitconfig` is used to store a per-user configuration as
+fallback values for the `.git/config` file. The file `/etc/gitconfig`
+can be used to store a system-wide default configuration.
+
+The configuration variables are used by both the git plumbing
+and the porcelains. The variables are divided into sections, wherein
+the fully qualified variable name of the variable itself is the last
 dot-separated segment and the section name is everything before the last
 dot. The variable names are case-insensitive and only alphanumeric
 characters are allowed. Some variables may appear multiple times.
@@ -25,35 +25,35 @@ blank lines are ignored.
 The file consists of sections and variables.  A section begins with
 the name of the section in square brackets and continues until the next
 section begins.  Section names are not case sensitive.  Only alphanumeric
-characters, '`-`' and '`.`' are allowed in section names.  Each variable
-must belong to some section, which means that there must be section
-header before first setting of a variable.
+characters, `-` and `.` are allowed in section names.  Each variable
+must belong to some section, which means that there must be section
+header before the first setting of a variable.
 
 Sections can be further divided into subsections.  To begin a subsection
 put its name in double quotes, separated by space from the section name,
-in the section header, like in example below:
+in the section header, like in the example below:
 
 --------
        [section "subsection"]
 
 --------
 
-Subsection names can contain any characters except newline (doublequote
-'`"`' and backslash have to be escaped as '`\"`' and '`\\`',
-respectively) and are case sensitive.  Section header cannot span multiple
+Subsection names are case sensitive and can contain any characters except
+newline (doublequote `"` and backslash have to be escaped as `\"` and `\\`,
+respectively).  Section headers cannot span multiple
 lines.  Variables may belong directly to a section or to a given subsection.
 You can have `[section]` if you have `[section "subsection"]`, but you
 don't need to.
 
-There is also (case insensitive) alternative `[section.subsection]` syntax.
-In this syntax subsection names follow the same restrictions as for section
-name.
+There is also a case insensitive alternative `[section.subsection]` syntax.
+In this syntax, subsection names follow the same restrictions as for section
+names.
 
 All the other lines are recognized as setting variables, in the form
 'name = value'.  If there is no equal sign on the line, the entire line
 is taken as 'name' and the variable is recognized as boolean "true".
 The variable names are case-insensitive and only alphanumeric
-characters and '`-`' are allowed.  There can be more than one value
+characters and `-` are allowed.  There can be more than one value
 for a given variable; we say then that variable is multivalued.
 
 Leading and trailing whitespace in a variable value is discarded.
@@ -61,26 +61,26 @@ Internal whitespace within a variable value is retained verbatim.
 
 The values following the equals sign in variable assign are all either
 a string, an integer, or a boolean.  Boolean values may be given as yes/no,
-0/1 or true/false.  Case is not significant in boolean values, when
+0/1, true/false or on/off.  Case is not significant in boolean values, when
 converting value to the canonical form using '--bool' type specifier;
 'git-config' will ensure that the output is "true" or "false".
 
 String values may be entirely or partially enclosed in double quotes.
-You need to enclose variable value in double quotes if you want to
-preserve leading or trailing whitespace, or if variable value contains
-beginning of comment characters (if it contains '#' or ';').
-Double quote '`"`' and backslash '`\`' characters in variable value must
-be escaped: use '`\"`' for '`"`' and '`\\`' for '`\`'.
-
-The following escape sequences (beside '`\"`' and '`\\`') are recognized:
-'`\n`' for newline character (NL), '`\t`' for horizontal tabulation (HT, TAB)
-and '`\b`' for backspace (BS).  No other char escape sequence, nor octal
+You need to enclose variable values in double quotes if you want to
+preserve leading or trailing whitespace, or if the variable value contains
+comment characters (i.e. it contains '#' or ';').
+Double quote `"` and backslash `\` characters in variable values must
+be escaped: use `\"` for `"` and `\\` for `\`.
+
+The following escape sequences (beside `\"` and `\\`) are recognized:
+`\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB)
+and `\b` for backspace (BS).  No other char escape sequence, nor octal
 char sequences are valid.
 
-Variable value ending in a '`\`' is continued on the next line in the
+Variable values ending in a `\` are continued on the next line in the
 customary UNIX fashion.
 
-Some variables may require special value format.
+Some variables may require special value format.
 
 Example
 ~~~~~~~
@@ -221,6 +221,11 @@ core.gitProxy::
 Can be overridden by the 'GIT_PROXY_COMMAND' environment variable
 (which always applies universally, without the special "for"
 handling).
++
+The special string `none` can be used as the proxy command to
+specify that no proxy be used for a given domain pattern.
+This is useful for excluding servers inside a firewall from
+proxy use, while defaulting to a common proxy for external domains.
 
 core.ignoreStat::
        If true, commands which modify both the working tree and the index
@@ -384,9 +389,9 @@ core.pager::
        to override git's default settings this way, you need
        to be explicit.  For example, to disable the S option
        in a backward compatible manner, set `core.pager`
-       to "`less -+$LESS -FRX`".  This will be passed to the
+       to `less -+$LESS -FRX`.  This will be passed to the
        shell by git, which will translate the final command to
-       "`LESS=FRSX less -+FRSX -FRX`".
+       `LESS=FRSX less -+FRSX -FRX`.
 
 core.whitespace::
        A comma separated list of common whitespace problems to
@@ -424,6 +429,20 @@ relatively high IO latencies.  With this set to 'true', git will do the
 index comparison to the filesystem data in parallel, allowing
 overlapping IO's.
 
+core.createObject::
+       You can set this to 'link', in which case a hardlink followed by
+       a delete of the source are used to make sure that object creation
+       will not overwrite existing objects.
++
+On some file system/operating system combinations, this is unreliable.
+Set this config setting to 'rename' there; However, This will remove the
+check that makes sure that existing object files will not get overwritten.
+
+add.ignore-errors::
+       Tells 'git-add' to continue adding files when some files cannot be
+       added due to indexing errors. Equivalent to the '--ignore-errors'
+       option of linkgit:git-add[1].
+
 alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
        after defining "alias.last = cat-file commit HEAD", the invocation
@@ -437,7 +456,9 @@ If the alias expansion is prefixed with an exclamation point,
 it will be treated as a shell command.  For example, defining
 "alias.new = !gitk --all --not ORIG_HEAD", the invocation
 "git new" is equivalent to running the shell command
-"gitk --all --not ORIG_HEAD".
+"gitk --all --not ORIG_HEAD".  Note that shell commands will be
+executed from the top-level directory of a repository, which may
+not necessarily be the current directory.
 
 apply.whitespace::
        Tells 'git-apply' how to handle whitespaces, in the same way
@@ -470,10 +491,14 @@ branch.autosetuprebase::
        This option defaults to never.
 
 branch.<name>.remote::
-       When in branch <name>, it tells 'git-fetch' which remote to fetch.
-       If this option is not given, 'git-fetch' defaults to remote "origin".
+       When in branch <name>, it tells 'git-fetch' and 'git-push' which
+       remote to fetch from/push to.  It defaults to `origin` if no remote is
+       configured. `origin` is also used if you are not on any branch.
 
 branch.<name>.merge::
+       Defines, together with branch.<name>.remote, the upstream branch
+       for the given branch. It tells 'git-fetch'/'git-pull' which
+       branch to merge and can also affect 'git-push' (see push.default).
        When in branch <name>, it tells 'git-fetch' the default
        refspec to be marked for merging in FETCH_HEAD. The value is
        handled like the remote part of a refspec, and must match a
@@ -550,6 +575,25 @@ color.diff.<slot>::
        whitespace errors). The values of these variables may be specified as
        in color.branch.<slot>.
 
+color.grep::
+       When set to `always`, always highlight matches.  When `false` (or
+       `never`), never.  When set to `true` or `auto`, use color only
+       when the output is written to the terminal.  Defaults to `false`.
+
+color.grep.external::
+       The string value of this variable is passed to an external 'grep'
+       command as a command line option if match highlighting is turned
+       on.  If set to an empty string, no option is passed at all,
+       turning off coloring for external 'grep' calls; this is the default.
+       For GNU grep, set it to `--color=always` to highlight matches even
+       when a pager is used.
+
+color.grep.match::
+       Use customized color for matches.  The value of this variable
+       may be specified as in color.branch.<slot>.  It is passed using
+       the environment variables 'GREP_COLOR' and 'GREP_COLORS' when
+       calling an external 'grep'.
+
 color.interactive::
        When set to `always`, always use colors for interactive prompts
        and displays (such as those used by "git-add --interactive").
@@ -560,7 +604,7 @@ color.interactive.<slot>::
        Use customized color for 'git-add --interactive'
        output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
        four distinct types of normal output from interactive
-       programs.  The values of these variables may be specified as
+       commands.  The values of these variables may be specified as
        in color.branch.<slot>.
 
 color.pager::
@@ -641,6 +685,27 @@ diff.suppressBlankEmpty::
        A boolean to inhibit the standard behavior of printing a space
        before each empty output line. Defaults to false.
 
+diff.tool::
+       Controls which diff tool is used.  `diff.tool` overrides
+       `merge.tool` when used by linkgit:git-difftool[1] and has
+       the same valid values as `merge.tool` minus "tortoisemerge"
+       and plus "kompare".
+
+difftool.<tool>.path::
+       Override the path for the given tool.  This is useful in case
+       your tool is not in the PATH.
+
+difftool.<tool>.cmd::
+       Specify the command to invoke the specified diff tool.
+       The specified command is evaluated in shell 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.
+
+difftool.prompt::
+       Prompt before each invocation of the diff tool.
+
 diff.wordRegex::
        A POSIX Extended Regular Expression used to determine what is a "word"
        when performing word-by-word difference calculations.  Character
@@ -658,6 +723,13 @@ fetch.unpackLimit::
        especially on slow filesystems.  If not set, the value of
        `transfer.unpackLimit` is used instead.
 
+format.attach::
+       Enable multipart/mixed attachments as the default for
+       'format-patch'.  The value can also be a double quoted string
+       which will enable attachments as the default and set the
+       value as the boundary.  See the --attach option in
+       linkgit:git-format-patch[1].
+
 format.numbered::
        A boolean which can enable or disable sequence numbers in patch
        subjects.  It defaults to "auto" which enables it only if there
@@ -669,6 +741,14 @@ format.headers::
        Additional email headers to include in a patch to be submitted
        by mail.  See linkgit:git-format-patch[1].
 
+format.cc::
+       Additional "Cc:" headers to include in a patch to be submitted
+       by mail.  See the --cc option in linkgit:git-format-patch[1].
+
+format.subjectprefix::
+       The default for format-patch is to output files with the '[PATCH]'
+       subject prefix. Use this variable to change that prefix.
+
 format.suffix::
        The default for format-patch is to output files with the suffix
        `.patch`. Use this variable to change that suffix (make sure to
@@ -679,6 +759,23 @@ format.pretty::
        See linkgit:git-log[1], linkgit:git-show[1],
        linkgit:git-whatchanged[1].
 
+format.thread::
+       The default threading style for 'git-format-patch'.  Can be
+       either a boolean value, `shallow` or `deep`.  `shallow`
+       threading makes every mail a reply to the head of the series,
+       where the head is chosen from the cover letter, the
+       `\--in-reply-to`, and the first patch mail, in this order.
+       `deep` threading makes every mail a reply to the previous one.
+       A true boolean value is the same as `shallow`, and a false
+       value disables threading.
+
+format.signoff::
+    A boolean value which lets you enable the `-s/--signoff` option of
+    format-patch by default. *Note:* Adding the Signed-off-by: line to a
+    patch should be a conscious act and means that you certify you have
+    the rights to submit this work under the same open source license.
+    Please see the 'SubmittingPatches' document for further discussion.
+
 gc.aggressiveWindow::
        The window size parameter used in the delta compression
        algorithm used by 'git-gc --aggressive'.  This defaults
@@ -1003,7 +1100,7 @@ instaweb.port::
        linkgit:git-instaweb[1].
 
 interactive.singlekey::
-       In interactive programs, allow the user to provide one-letter
+       In interactive commands, allow the user to provide one-letter
        input with a single key (i.e., without hitting enter).
        Currently this is used only by the `\--patch` mode of
        linkgit:git-add[1].  Note that this setting is silently
@@ -1153,7 +1250,7 @@ pager.<cmd>::
        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`".
+       all commands, set `core.pager` or `GIT_PAGER` to `cat`.
 
 pull.octopus::
        The default merge strategy to use when pulling multiple branches
@@ -1162,6 +1259,23 @@ pull.octopus::
 pull.twohead::
        The default merge strategy to use when pulling a single branch.
 
+push.default::
+       Defines the action git push should take if no refspec is given
+       on the command line, no refspec is configured in the remote, and
+       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.
+  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.
+
+rebase.stat::
+       Whether to show a diffstat of what changed upstream since the last
+       rebase. False by default.
+
 receive.fsckObjects::
        If it is set to true, git-receive-pack will check all received
        objects. It will abort in the case of a malformed object or a
index b878b385c6967f4c64ba30bdfe8f9bd24bef91e3..e11c8f053ad753f218a0a79ddacfe62862603bb9 100644 (file)
@@ -16,6 +16,7 @@ body blockquote {
 html body {
   margin: 1em 5% 1em 5%;
   line-height: 1.2;
+  font-family: sans-serif;
 }
 
 body div {
@@ -128,6 +129,15 @@ body pre {
 
 tt.literal, code.literal {
   color: navy;
+  font-family: sans-serif;
+}
+
+code.literal:before { content: "'"; }
+code.literal:after { content: "'"; }
+
+em {
+  font-style: italic;
+  color: #064;
 }
 
 div.literallayout p {
@@ -137,7 +147,6 @@ div.literallayout p {
 
 div.literallayout {
   font-family: monospace;
-#  margin: 0.5em 10% 0.5em 1em;
   margin: 0em;
   color: navy;
   border: 1px solid silver;
@@ -187,7 +196,8 @@ dt {
 }
 
 dt span.term {
-  font-style: italic;
+  font-style: normal;
+  color: navy;
 }
 
 div.variablelist dd p {
index d313795fdbc420e3395adc42aebe82fabda037d4..ea3b1bc19f753ca80bc133ae614d2c79810a1cb0 100644 (file)
@@ -1,7 +1,7 @@
 -q::
 --quiet::
        Pass --quiet to git-fetch-pack and silence any other internally
-       used programs.
+       used git commands.
 
 -v::
 --verbose::
index 105a31a088c7846c0844d8f844eaba46061a3172..92444ddf10081ee5faccd28491b47ef1cb45deee 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git archive' [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>]
+             [--output=<file>] [--worktree-attributes]
              [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
              [path...]
 
@@ -47,6 +48,12 @@ OPTIONS
 --prefix=<prefix>/::
        Prepend <prefix>/ to each filename in the archive.
 
+--output=<file>::
+       Write the archive to <file> instead of stdout.
+
+--worktree-attributes::
+       Look for attributes in .gitattributes in working directory too.
+
 <extra>::
        This can be any options that the archiver backend understands.
        See next section.
index e5862b9dbbbd69fcd57e8ec85fb0b08f5bf221d0..ffc02c737cf0a8d2bdb3cd812e0a23d43ee27fd7 100644 (file)
@@ -217,7 +217,7 @@ If you have a script that can tell if the current source code is good
 or bad, you can bisect by issuing the command:
 
 ------------
-$ git bisect run my_script
+$ git bisect run my_script arguments
 ------------
 
 Note that the script (`my_script` in the above example) should
@@ -257,6 +257,13 @@ $ git bisect start HEAD v1.2 --      # HEAD is bad, v1.2 is good
 $ git bisect run make                # "make" builds the app
 ------------
 
+* Automatically bisect a test failure between origin and HEAD:
++
+------------
+$ git bisect start HEAD origin --    # HEAD is bad, origin is good
+$ git bisect run make test           # "make test" builds and tests
+------------
+
 * Automatically bisect a broken test suite:
 +
 ------------
@@ -296,6 +303,15 @@ It is safer if both "test.sh" and "check_test_case.sh" scripts are
 outside the repository to prevent interactions between the bisect,
 make and test processes and the scripts.
 
+* Automatically bisect a broken test suite:
++
+------------
+$ git bisect start HEAD HEAD~10 --   # culprit is among the last 10
+$ git bisect run sh -c "make || exit 125; ~/check_test_case.sh"
+------------
++
+Does the same as the previous example, but on a single line.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
index 7f7b781f24cd916da71b2481a122fa1d59762cae..cbd427587188d81726445183f772f02982736e6a 100644 (file)
@@ -76,8 +76,8 @@ OPTIONS
        based sha1 expressions such as "<branchname>@\{yesterday}".
 
 -f::
-       Force the creation of a new branch even if it means deleting
-       a branch that already exists with the same name.
+       Reset <branchname> to <startpoint> if <branchname> exists
+       already. Without `-f` 'git-branch' refuses to change an existing branch.
 
 -m::
        Move/rename a branch and the corresponding reflog.
@@ -100,7 +100,9 @@ OPTIONS
 
 -v::
 --verbose::
-       Show sha1 and commit subject line for each head.
+       Show sha1 and commit subject line for each head, along with
+       relationship to upstream branch (if any). If given twice, print
+       the name of the upstream branch, as well.
 
 --abbrev=<length>::
        Alter the sha1's minimum display length in the output listing.
@@ -110,19 +112,22 @@ OPTIONS
        Display the full sha1s in the output listing rather than abbreviating them.
 
 --track::
-       When creating a new branch, set up the configuration so that 'git-pull'
-       will automatically retrieve data from the start point, which must be
-       a branch. Use this if you always pull from the same upstream branch
-       into the new branch, and if you do not want to use "git pull
-       <repository> <refspec>" explicitly. This behavior is the default
-       when the start point is a remote 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.
+       When creating a new branch, set up configuration to mark the
+       start-point branch as "upstream" from the new branch. This
+       configuration will tell git to show the relationship between the
+       two branches in `git status` and `git branch -v`. Furthermore,
+       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.
+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.
 
 --no-track::
-       Ignore the branch.autosetupmerge configuration variable.
+       Do not set up "upstream" configuration, even if the
+       branch.autosetupmerge configuration variable is true.
 
 --contains <commit>::
        Only list branches which contain the specified commit.
index b191276d7a44511bfce60febe6f5cb9a025ec9a3..58c8d65772af4ef20ad573af9dd28691f5357437 100644 (file)
@@ -9,8 +9,8 @@ git-cat-file - Provide content or type and size information for repository objec
 SYNOPSIS
 --------
 [verse]
-'git cat-file' [-t | -s | -e | -p | <type>] <object>
-'git cat-file' [--batch | --batch-check] < <list-of-objects>
+'git cat-file' (-t | -s | -e | -p | <type>) <object>
+'git cat-file' (--batch | --batch-check) < <list-of-objects>
 
 DESCRIPTION
 -----------
index 171b68377d6604167fde4f5e4266432fa9048345..c1ce26884e73f1599602472ba5032988486cc465 100644 (file)
@@ -7,7 +7,9 @@ git-check-ref-format - Ensures that a reference name is well formed
 
 SYNOPSIS
 --------
+[verse]
 'git check-ref-format' <refname>
+'git check-ref-format' [--branch] <branchname-shorthand>
 
 DESCRIPTION
 -----------
@@ -30,7 +32,11 @@ imposes the following rules on how references are named:
   caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
   or open bracket `[` anywhere.
 
-. They cannot end with a slash `/`.
+. They cannot end with a slash `/` nor a dot `.`.
+
+. They cannot end with the sequence `.lock`.
+
+. They cannot contain a sequence `@{`.
 
 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
@@ -49,6 +55,18 @@ reference name expressions (see linkgit:git-rev-parse[1]):
   It may also be used to select a specific object such as with
   'git-cat-file': "git cat-file blob v1.3.3:refs.c".
 
+. at-open-brace `@{` is used as a notation to access a reflog entry.
+
+With the `--branch` option, it expands a branch name shorthand and
+prints the name of the branch the shorthand refers to.
+
+EXAMPLE
+-------
+
+git check-ref-format --branch @{-1}::
+
+Print the name of the previous branch.
+
 
 GIT
 ---
index 132fc4faa5efab2a07b592e3567340b5eeb3f910..ad4b31e89218e857001fce69f32acbe85fc7539c 100644 (file)
@@ -8,28 +8,28 @@ git-checkout - Checkout a branch or paths to the working tree
 SYNOPSIS
 --------
 [verse]
-'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>]
+'git checkout' [-q] [-f] [-m] [<branch>]
+'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
 
 DESCRIPTION
 -----------
 
 When <paths> are not given, this command switches branches by
-updating the index and working tree to reflect the specified
-branch, <branch>, and updating HEAD to be <branch> or, if
-specified, <new_branch>.  Using -b will cause <new_branch> to
-be created; in this case you can use the --track or --no-track
-options, which will be passed to `git branch`.
+updating the index, working tree, and HEAD to reflect the specified
+branch.
 
-As a convenience, --track will default to create a branch whose
-name is constructed from the specified branch name by stripping
-the first namespace level.
+If `-b` is given, a new branch is created and checked out, as if
+linkgit:git-branch[1] were called; in this case you can
+use the --track or --no-track options, which will be passed to `git
+branch`.  As a convenience, --track without `-b` implies branch
+creation; see the description of --track below.
 
 When <paths> are given, this command does *not* switch
 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` options is meaningless and giving
-either of them results in an error.  <tree-ish> argument can be
+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.
@@ -62,27 +62,16 @@ entries; instead, unmerged entries are ignored.
 
 -b::
        Create a new branch named <new_branch> and start it at
-       <branch>.  The new branch name must pass all checks defined
-       by linkgit:git-check-ref-format[1].  Some of these checks
-       may restrict the characters allowed in a branch name.
+       <start_point>; see linkgit:git-branch[1] for details.
 
 -t::
 --track::
-       When creating a new branch, set up configuration so that 'git-pull'
-       will automatically retrieve data from the start point, which must be
-       a branch. Use this if you always pull from the same upstream branch
-       into the new branch, and if you don't want to use "git pull
-       <repository> <refspec>" explicitly. This behavior is the default
-       when the start point is a remote 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.
+       When creating a new branch, set up "upstream" configuration. See
+       "--track" in linkgit:git-branch[1] for details.
 +
-If no '-b' option was given, the name of the new branch will be
-derived from the remote branch, by attempting to guess the name
-of the branch on remote system.  If "remotes/" or "refs/remotes/"
-are prefixed, it is stripped away, and then the part up to the
+If no '-b' option is given, the name of the new branch will be
+derived from the remote 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
 off of "origin/hack" (or "remotes/origin/hack", or even
@@ -91,12 +80,12 @@ guessing results in an empty name, the guessing is aborted.  You can
 explicitly give a name with '-b' in such a case.
 
 --no-track::
-       Ignore the branch.autosetupmerge configuration variable.
+       Do not set up "upstream" configuration, even if the
+       branch.autosetupmerge configuration variable is true.
 
 -l::
-       Create the new branch's reflog.  This activates recording of
-       all changes made to the branch ref, enabling use of date
-       based sha1 expressions such as "<branchname>@\{yesterday}".
+       Create the new branch's reflog; see linkgit:git-branch[1] for
+       details.
 
 -m::
 --merge::
@@ -124,23 +113,28 @@ the conflicted merge in the specified paths.
        "merge" (default) and "diff3" (in addition to what is shown by
        "merge" style, shows the original contents).
 
+<branch>::
+       Branch to checkout; if it refers to a branch (i.e., a name that,
+       when prepended with "refs/heads/", is a valid ref), then that
+       branch is checked out. Otherwise, if it refers to a valid
+       commit, your HEAD becomes "detached" and you are no longer on
+       any branch (see below for details).
++
+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\}"`.
+
 <new_branch>::
        Name for the new branch.
 
+<start_point>::
+       The name of a commit at which to start the new branch; see
+       linkgit:git-branch[1] for details. Defaults to HEAD.
+
 <tree-ish>::
        Tree to checkout from (when paths are given). If not specified,
        the index will be used.
 
-<branch>::
-       Branch to checkout (when no paths are given); may be any object
-       ID that resolves to a commit.  Defaults to HEAD.
-+
-When this parameter names a non-branch (but still a valid commit object),
-your HEAD becomes 'detached'.
-+
-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\}`".
 
 
 Detached HEAD
@@ -156,12 +150,12 @@ $ git checkout v2.6.18
 ------------
 
 Earlier versions of git did not allow this and asked you to
-create a temporary branch using `-b` option, but starting from
+create a temporary branch using the `-b` option, but starting from
 version 1.5.0, the above command 'detaches' your HEAD from the
-current branch and directly point at the commit named by the tag
-(`v2.6.18` in the above example).
+current branch and directly points at the commit named by the tag
+(`v2.6.18` in the example above).
 
-You can use usual git commands while in this state.  You can use
+You can use all git commands while in this state.  You can use
 `git reset --hard $othercommit` to further move around, for
 example.  You can make changes and create a new commit on top of
 a detached HEAD.  You can even create a merge by using `git
@@ -206,7 +200,7 @@ You should instead write:
 $ git checkout -- hello.c
 ------------
 
-. After working in a wrong branch, switching to the correct
+. After working in the wrong branch, switching to the correct
 branch would be done using:
 +
 ------------
@@ -214,7 +208,7 @@ $ git checkout mytopic
 ------------
 +
 However, your "wrong" branch and correct "mytopic" branch may
-differ in files that you have locally modified, in which case,
+differ in files that you have modified locally, in which case
 the above checkout would fail like this:
 +
 ------------
index 8a114509f4a19b4fa6c6d8de8afdc94938d24b82..be894af39ff6559affbf0617e4c5d8fe68c33fe8 100644 (file)
@@ -12,14 +12,17 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Removes files unknown to git.  This allows to clean the working tree
-from files that are not under version control.  If the '-x' option is
-specified, ignored files are also removed, allowing to remove all
-build products.
+
+Cleans the working tree by recursively removing files that are not
+under version control, starting from the current directory.
+
+Normally, only files unknown to git are removed, but if the '-x'
+option is specified, ignored files are also removed. This can, for
+example, be useful to remove all build products.
+
 If any optional `<path>...` arguments are given, only those paths
 are affected.
 
-
 OPTIONS
 -------
 -d::
index 0c7486d782c6cda30a26090239fa849f0a773e76..87c13ab13eb05e67312bf45de3e31ca30f7a01ab 100644 (file)
@@ -149,7 +149,7 @@ then the cloned repository will become corrupt.
        part of the source repository is used if no directory is
        explicitly given ("repo" for "/path/to/repo.git" and "foo"
        for "host.xz:foo/.git").  Cloning into an existing directory
-       is not allowed.
+       is only allowed if the directory is empty.
 
 :git-clone: 1
 include::urls.txt[]
index 6ab2af4b61e2188297e15f2e3c1d9c5f05f680b8..f68b198205d3a4d808ce58e6b0ac03e70bb160a4 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git config' [<file-option>] [type] [-z|--null] name [value [value_regex]]
 'git config' [<file-option>] [type] --add name value
-'git config' [<file-option>] [type] --replace-all name [value [value_regex]]
+'git config' [<file-option>] [type] --replace-all name value [value_regex]
 'git config' [<file-option>] [type] [-z|--null] --get name [value_regex]
 'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex]
 'git config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex]
@@ -22,6 +22,7 @@ SYNOPSIS
 'git config' [<file-option>] [-z|--null] -l | --list
 'git config' [<file-option>] --get-color name [default]
 'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
+'git config' [<file-option>] -e | --edit
 
 DESCRIPTION
 -----------
@@ -68,7 +69,8 @@ OPTIONS
 
 --add::
        Adds a new line to the option without altering any existing
-       values.  This is the same as providing '^$' as the value_regex.
+       values.  This is the same as providing '^$' as the value_regex
+       in `--replace-all`.
 
 --get::
        Get the value for a given key (optionally filtered by a regex
@@ -154,13 +156,18 @@ See also <<FILES>>.
        When the color setting for `name` is undefined, the command uses
        `color.ui` as fallback.
 
---get-color name default::
+--get-color name [default]::
 
        Find the color configured for `name` (e.g. `color.diff.new`) and
        output it as the ANSI color escape sequence to the standard
        output.  The optional `default` parameter is used instead, if
        there is no color configured for `name`.
 
+-e::
+--edit::
+       Opens an editor to modify the specified config file; either
+       '--system', '--global', or repository (default).
+
 [[FILES]]
 FILES
 -----
index 8f9ba74c8bc95c36e8586e921f4df6617e999d82..614e769f4e5351d2820ebb9bf0dd1ae519c965ba 100644 (file)
@@ -24,6 +24,9 @@ repository, or incrementally import into an existing one.
 Splitting the CVS log into patch sets is done by 'cvsps'.
 At least version 2.1 is required.
 
+*WARNING:* for certain situations the import leads to incorrect results.
+Please see the section <<issues,ISSUES>> for further reference.
+
 You should *never* do any work of your own on the branches that are
 created by 'git-cvsimport'.  By default initial import will create and populate a
 "master" branch from the CVS repository's main branch which you're free
@@ -164,6 +167,39 @@ If '-v' is specified, the script reports what it is doing.
 Otherwise, success is indicated the Unix way, i.e. by simply exiting with
 a zero exit status.
 
+[[issues]]
+ISSUES
+------
+Problems related to timestamps:
+
+ * If timestamps of commits in the cvs repository are not stable enough
+   to be used for ordering commits changes may show up in the wrong
+   order.
+ * If any files were ever "cvs import"ed more than once (e.g., import of
+   more than one vendor release) the HEAD contains the wrong content.
+ * If the timestamp order of different files cross the revision order
+   within the commit matching time window the order of commits may be
+   wrong.
+
+Problems related to branches:
+
+ * Branches on which no commits have been made are not imported.
+ * All files from the branching point are added to a branch even if
+   never added in cvs.
+ * This applies to files added to the source branch *after* a daughter
+   branch was created: if previously no commit was made on the daughter
+   branch they will erroneously be added to the daughter branch in git.
+
+Problems related to tags:
+
+* Multiple tags on the same revision are not imported.
+
+If you suspect that any of these issues may apply to the repository you
+want to import consider using these alternative tools which proved to be
+more stable in practice:
+
+* cvs2git (part of cvs2svn), `http://cvs2svn.tigris.org`
+* parsecvs, `http://cgit.freedesktop.org/~keithp/parsecvs`
 
 Author
 ------
diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
new file mode 100644 (file)
index 0000000..15b247b
--- /dev/null
@@ -0,0 +1,105 @@
+git-difftool(1)
+===============
+
+NAME
+----
+git-difftool - Show changes using common diff tools
+
+SYNOPSIS
+--------
+'git difftool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<'git diff' options>]
+
+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.
+
+OPTIONS
+-------
+-y::
+--no-prompt::
+       Do not prompt before launching a diff tool.
+
+--prompt::
+       Prompt before each invocation of the diff tool.
+       This is the default behaviour; the option is provided to
+       override any configuration settings.
+
+-t <tool>::
+--tool=<tool>::
+       Use the diff tool specified by <tool>.
+       Valid merge tools are:
+       kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff,
+       ecmerge, diffuse and opendiff
++
+If a diff tool is not specified, 'git-difftool'
+will use the configuration variable `diff.tool`.  If the
+configuration variable `diff.tool` is not set, 'git-difftool'
+will pick a suitable default.
++
+You can explicitly provide a full path to the tool by setting the
+configuration variable `difftool.<tool>.path`. For example, you
+can configure the absolute path to kdiff3 by setting
+`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the
+tool is available in PATH.
++
+Instead of running one of the known diff tools,
+'git-difftool' can be customized to run an alternative program
+by specifying the command line to invoke in a configuration
+variable `difftool.<tool>.cmd`.
++
+When 'git-difftool' is invoked with this tool (either through the
+`-t` or `--tool` option or the `diff.tool` configuration variable)
+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`.
+
+See linkgit:git-diff[1] for the full list of supported options.
+
+CONFIG VARIABLES
+----------------
+'git-difftool' falls back to 'git-mergetool' config variables when the
+difftool equivalents have not been defined.
+
+diff.tool::
+       The default diff tool to use.
+
+difftool.<tool>.path::
+       Override the path for the given tool.  This is useful in case
+       your tool is not in the PATH.
+
+difftool.<tool>.cmd::
+       Specify the command to invoke the specified diff tool.
++
+See the `--tool=<tool>` option above for more details.
+
+difftool.prompt::
+       Prompt before each invocation of the diff tool.
+
+SEE ALSO
+--------
+linkgit:git-diff[1]::
+        Show changes between commits, commit and working tree, etc
+
+linkgit:git-mergetool[1]::
+       Run merge conflict resolution tools to resolve merge conflicts
+
+linkgit:git-config[1]::
+        Get and set repository or global options
+
+
+AUTHOR
+------
+Written by David Aguilar <davvid@gmail.com>.
+
+Documentation
+--------------
+Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
index c1193953a128201be821538176c1fd666950afb3..ab527b5b316a81eddd8a0eefb501448a3097182f 100644 (file)
@@ -94,7 +94,9 @@ OPTIONS
 --index-filter <command>::
        This is the filter for rewriting the index.  It is similar to the
        tree filter but does not check out the tree, which makes it much
-       faster.  For hairy cases, see linkgit:git-update-index[1].
+       faster.  Frequently used with `git rm \--cached
+       \--ignore-unmatch ...`, see EXAMPLES below.  For hairy
+       cases, see linkgit:git-update-index[1].
 
 --parent-filter <command>::
        This is the filter for rewriting the commit's parent list.
@@ -207,19 +209,18 @@ However, if the file is absent from the tree of some commit,
 a simple `rm filename` will fail for that tree and commit.
 Thus you may instead want to use `rm -f filename` as the script.
 
-A significantly faster version:
+Using `\--index-filter` with 'git-rm' yields a significantly faster
+version.  Like with using `rm filename`, `git rm --cached filename`
+will fail if the file is absent from the tree of a commit.  If you
+want to "completely forget" a file, it does not matter when it entered
+history, so we also add `\--ignore-unmatch`:
 
 --------------------------------------------------------------------------
-git filter-branch --index-filter 'git rm --cached filename' HEAD
+git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
 --------------------------------------------------------------------------
 
 Now, you will get the rewritten history saved in HEAD.
 
-As with using `rm filename`, `git rm --cached filename` will fail
-if the file is absent from the tree of a commit.  If it is not important
-whether the file is already absent from the tree, you can use
-`git rm --cached --ignore-unmatch filename` instead.
-
 To rewrite the repository to look as if `foodir/` had been its project
 root, and discard all other history:
 
index 5061d3e4e7b8a888093c5e8c7b4cb03509391756..8dc873fd4465879ec3224732c690f1173a6e2dc2 100644 (file)
@@ -75,6 +75,8 @@ For all objects, the following names can be used:
 refname::
        The name of the ref (the part after $GIT_DIR/).
        For a non-ambiguous short name of the ref append `:short`.
+       The option core.warnAmbiguousRefs is used to select the strict
+       abbreviation mode.
 
 objecttype::
        The type of the object (`blob`, `tree`, `commit`, `tag`).
@@ -85,6 +87,11 @@ objectsize::
 objectname::
        The object name (aka SHA-1).
 
+upstream::
+       The name of a local ref which can be considered ``upstream''
+       from the displayed ref. Respects `:short` in the same way as
+       `refname` above.
+
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
 be used to specify the value in the header field.
index 3c29655d753902facf7f356dd30b1783ec96660e..6f1fc80119600c419d2e3b98bac0c44f9cb09026 100644 (file)
@@ -9,9 +9,10 @@ git-format-patch - Prepare patches for e-mail submission
 SYNOPSIS
 --------
 [verse]
-'git format-patch' [-k] [-o <dir> | --stdout] [--thread]
-                  [--attach[=<boundary>] | --inline[=<boundary>]]
-                  [-s | --signoff] [<common diff options>]
+'git format-patch' [-k] [(-o|--output-directory) <dir> | --stdout]
+                  [--thread[=<style>]]
+                  [(--attach|--inline)[=<boundary>] | --no-attach]
+                  [-s | --signoff]
                   [-n | --numbered | -N | --no-numbered]
                   [--start-number <n>] [--numbered-files]
                   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
@@ -19,6 +20,7 @@ SYNOPSIS
                   [--subject-prefix=Subject-Prefix]
                   [--cc=<email>]
                   [--cover-letter]
+                  [<common diff options>]
                   [ <since> | <revision range> ]
 
 DESCRIPTION
@@ -112,15 +114,27 @@ include::diff-options.txt[]
        which is the commit message and the patch itself in the
        second part, with "Content-Disposition: attachment".
 
+--no-attach::
+       Disable the creation of an attachment, overriding the
+       configuration setting.
+
 --inline[=<boundary>]::
        Create multipart/mixed attachment, the first part of
        which is the commit message and the patch itself in the
        second part, with "Content-Disposition: inline".
 
---thread::
+--thread[=<style>]::
        Add In-Reply-To and References headers to make the second and
        subsequent mails appear as replies to the first.  Also generates
        the Message-Id header to reference.
++
+The optional <style> argument can be either `shallow` or `deep`.
+'shallow' threading makes every mail a reply to the head of the
+series, where the head is chosen from the cover letter, the
+`\--in-reply-to`, and the first patch mail, in this order.  'deep'
+threading makes every mail a reply to the previous one.  If not
+specified, defaults to the 'format.thread' configuration, or `shallow`
+if that is not set.
 
 --in-reply-to=Message-Id::
        Make the first mail (or all the mails with --no-thread) appear as a
@@ -144,6 +158,11 @@ include::diff-options.txt[]
        Add a "Cc:" header to the email headers. This is in addition
        to any configured headers, and may be used multiple times.
 
+--add-header=<header>::
+       Add an arbitrary header to the email headers.  This is in addition
+       to any configured headers, and may be used multiple times.
+       For example, --add-header="Organization: git-foo"
+
 --cover-letter::
        In addition to the patches, generate a cover letter file
        containing the shortlog and the overall diffstat.  You can
@@ -152,18 +171,17 @@ include::diff-options.txt[]
 --suffix=.<sfx>::
        Instead of using `.patch` as the suffix for generated
        filenames, use specified suffix.  A common alternative is
-       `--suffix=.txt`.
+       `--suffix=.txt`.  Leaving this empty will remove the `.patch`
+       suffix.
 +
-Note that you would need to include the leading dot `.` if you
-want a filename like `0001-description-of-my-change.patch`, and
-the first letter does not have to be a dot.  Leaving it empty would
-not add any suffix.
+Note that the leading character does not have to be a dot; for example,
+you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
 
 --no-binary::
-       Don't output contents of changes in binary files, just take note
-       that they differ.  Note that this disable the patch to be properly
-       applied.  By default the contents of changes in those files are
-       encoded in the patch.
+       Do not output contents of changes in binary files, instead
+       display a notice that those files changed.  Patches generated
+       using this option cannot be applied properly, but they are
+       still useful for code review.
 
 --root::
        Treat the revision argument as a <revision range>, even if it
@@ -174,9 +192,10 @@ not add any suffix.
 
 CONFIGURATION
 -------------
-You can specify extra mail header lines to be added to each message
-in the repository configuration, new defaults for the subject prefix
-and file suffix, and number patches when outputting more than one.
+You can specify extra mail header lines to be added to each message,
+defaults for the subject prefix and file suffix, number patches when
+outputting more than one patch, add "Cc:" headers, configure attachments,
+and sign off patches with configuration variables.
 
 ------------
 [format]
@@ -185,6 +204,8 @@ and file suffix, and number patches when outputting more than one.
        suffix = .txt
        numbered = auto
        cc = <email>
+       attach [ = mime-boundary-string ]
+       signoff = true
 ------------
 
 
@@ -222,8 +243,8 @@ $ git format-patch -M -B origin
 +
 Additionally, it detects and handles renames and complete rewrites
 intelligently to produce a renaming patch.  A renaming patch reduces
-the amount of text output, and generally makes it easier to review it.
-Note that the "patch" program does not understand renaming patches, so
+the amount of text output, and generally makes it easier to review.
+Note that non-git "patch" programs won't understand renaming patches, so
 use it only when you know the recipient uses git to apply your patch.
 
 * Extract three topmost commits from the current branch and format them
index 553da6cbb1777a94cb3d6b48dc77c445e18ca2b0..fccb82deb48ec75d25f948e7caa69a44e09f5832 100644 (file)
@@ -17,6 +17,7 @@ SYNOPSIS
           [-l | --files-with-matches] [-L | --files-without-match]
           [-z | --null]
           [-c | --count] [--all-match]
+          [--color | --no-color]
           [-A <post-context>] [-B <pre-context>] [-C <context>]
           [-f <file>] [-e] <pattern>
           [--and|--or|--not|(|)|-e <pattern>...] [<tree>...]
@@ -105,6 +106,13 @@ OPTIONS
        Instead of showing every matched line, show the number of
        lines that match.
 
+--color::
+       Show colored matches.
+
+--no-color::
+       Turn off match highlighting, even when the configuration file
+       gives the default to color output.
+
 -[ABC] <context>::
        Show `context` trailing (`A` -- after), or leading (`B`
        -- before), or both (`C` -- context) lines, and place a
index eed50572e08e10266907811a40d69cbf1cc7a532..d016dafd491de5a4635e30b92607dadbaf7bf46f 100644 (file)
@@ -64,6 +64,13 @@ imap.sslverify::
        used by the SSL/TLS connection. Default is `true`. Ignored when
        imap.tunnel is set.
 
+imap.preformattedHTML::
+       A boolean to enable/disable the use of html encoding when sending
+       a patch.  An html encoded patch will be bracketed with <pre>
+       and have a content type of text/html.  Ironically, enabling this
+       option causes Thunderbird to send the patch as a plain/text,
+       format=fixed email.  Default is `false`.
+
 Examples
 ~~~~~~~~
 
index f7be5846a67de1a3215bad3d7b9c263705cd1bba..af68d694a0ad8066b05fcc02bae8e57a7c137034 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]...
-       [-m <msg>] <remote> <remote>...
+       [-m <msg>] <remote>...
 'git merge' <msg> HEAD <remote>...
 
 DESCRIPTION
@@ -40,8 +40,8 @@ include::merge-options.txt[]
 include::merge-strategies.txt[]
 
 
-If you tried a merge which resulted in complex conflicts and
-would want to start over, you can recover with 'git-reset'.
+If you tried a merge which resulted in complex conflicts and
+want to start over, you can recover with 'git-reset'.
 
 CONFIGURATION
 -------------
@@ -146,7 +146,7 @@ And here is another line that is cleanly resolved or unmodified.
 ------------
 
 The area where a pair of conflicting changes happened is marked with markers
-"`<<<<<<<`", "`=======`", and "`>>>>>>>`".  The part before the "`=======`"
+`<<<<<<<`, `=======`, and `>>>>>>>`.  The part before the `=======`
 is typically your side, and the part afterwards is typically their side.
 
 The default format does not show what the original said in the conflicting
@@ -173,8 +173,8 @@ Git makes conflict resolution easy.
 And here is another line that is cleanly resolved or unmodified.
 ------------
 
-In addition to the "`<<<<<<<`", "`=======`", and "`>>>>>>>`" markers, it uses
-another "`|||||||`" marker that is followed by the original text.  You can
+In addition to the `<<<<<<<`, `=======`, and `>>>>>>>` markers, it uses
+another `|||||||` marker that is followed by the original text.  You can
 tell that the original just stated a fact, and your side simply gave in to
 that statement and gave up, while the other side tried to have a more
 positive attitude.  You can sometimes come up with a better resolution by
diff --git a/Documentation/git-mergetool--lib.txt b/Documentation/git-mergetool--lib.txt
new file mode 100644 (file)
index 0000000..78eb03f
--- /dev/null
@@ -0,0 +1,54 @@
+git-mergetool--lib(1)
+=====================
+
+NAME
+----
+git-mergetool--lib - Common git merge tool shell scriptlets
+
+SYNOPSIS
+--------
+'TOOL_MODE=(diff|merge) . "$(git --exec-path)/git-mergetool--lib"'
+
+DESCRIPTION
+-----------
+
+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
+`.`) 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`
+to define the operation mode for the functions listed below.
+'diff' and 'merge' are valid values.
+
+FUNCTIONS
+---------
+get_merge_tool::
+       returns a merge tool.
+
+get_merge_tool_cmd::
+       returns the custom command for a merge tool.
+
+get_merge_tool_path::
+       returns the custom path for a merge tool.
+
+run_merge_tool::
+       launches a merge tool given the tool name and a true/false
+       flag to indicate whether a merge base is present.
+       '$MERGED', '$LOCAL', '$REMOTE', and '$BASE' must be defined
+       for use by the merge tool.
+
+Author
+------
+Written by David Aguilar <davvid@gmail.com>
+
+Documentation
+--------------
+Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
index 5d3c6328726ba1af829026dd97840fc43335cfe0..ff9700d17a333f50d6509bc14a0bd81fad876d3b 100644 (file)
@@ -26,7 +26,8 @@ OPTIONS
 --tool=<tool>::
        Use the merge resolution program specified by <tool>.
        Valid merge tools are:
-       kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
+       kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge,
+       diffuse, tortoisemerge and opendiff
 +
 If a merge resolution program is not specified, 'git-mergetool'
 will use the configuration variable `merge.tool`.  If the
index 477785e13418e1971156f5210015da4ab9d77cab..253fc0fc255a0f82778d120647031eaa66eb647a 100644 (file)
@@ -20,7 +20,7 @@ IOW, you can use this thing to look for likely duplicate commits.
 
 When dealing with 'git-diff-tree' output, it takes advantage of
 the fact that the patch is prefixed with the object name of the
-commit, and outputs two 40-byte hexadecimal string.  The first
+commit, and outputs two 40-byte hexadecimal strings.  The first
 string is the patch ID, and the second string is the commit ID.
 This can be used to make a mapping from patch ID to commit ID.
 
index 4e7e5a719a4b0447159213466d46aa2360b7408e..fd53c49fb886b196ba79e40c4c9c4efe0dae5432 100644 (file)
@@ -24,8 +24,8 @@ every time you push into it, by setting up 'hooks' there.  See
 documentation for linkgit:git-receive-pack[1].
 
 
-OPTIONS
--------
+OPTIONS[[OPTIONS]]
+------------------
 <repository>::
        The "remote" repository that is destination of a push
        operation.  This parameter can be either a URL
@@ -187,6 +187,28 @@ reason::
 Examples
 --------
 
+git push::
+       Works like `git push <remote>`, where <remote> is the
+       current branch's remote (or `origin`, if no remote is
+       configured for the current branch).
+
+git push origin::
+       Without additional configuration, works like
+       `git push origin :`.
++
+The default behavior of this command when no <refspec> is given can be
+configured by setting the `push` option of the remote.
++
+For example, to default to pushing only the current branch to `origin`
+use `git config remote.origin.push HEAD`.  Any valid <refspec> (like
+the ones in the examples below) can be configured as the default for
+`git push origin`.
+
+git push origin :::
+       Push "matching" branches to `origin`. See
+       <refspec> in the <<OPTIONS,OPTIONS>> section above for a
+       description of "matching" branches.
+
 git push origin master::
        Find a ref that matches `master` in the source repository
        (most likely, it would find `refs/heads/master`), and update
index da3c38cd60dc0d143fe91417b742335cfff27809..3d5a066c31675e502eb027dde824d1966c9c0f09 100644 (file)
@@ -192,6 +192,13 @@ Alternatively, you can undo the 'git-rebase' with
 
     git rebase --abort
 
+CONFIGURATION
+-------------
+
+rebase.stat::
+       Whether to show a diffstat of what changed upstream since the last
+       rebase. False by default.
+
 OPTIONS
 -------
 <newbase>::
@@ -232,7 +239,15 @@ OPTIONS
 
 -v::
 --verbose::
-       Display a diffstat of what changed upstream since the last rebase.
+       Be verbose. Implies --stat.
+
+--stat::
+       Show a diffstat of what changed upstream since the last rebase. The
+       diffstat is also controlled by the configuration option rebase.stat.
+
+-n::
+--no-stat::
+       Do not show a diffstat as part of the rebase process.
 
 --no-verify::
        This option bypasses the pre-rebase hook.  See also linkgit:githooks[5].
@@ -243,11 +258,23 @@ OPTIONS
        context exist they all must match.  By default no context is
        ever ignored.
 
+-f::
+--force-rebase::
+       Force the rebase even if the current branch is a descendant
+       of the commit you are rebasing onto.  Normally the command will
+       exit with the message "Current branch is up to date" in such a
+       situation.
+
 --whitespace=<option>::
        This flag is passed to the 'git-apply' program
        (see linkgit:git-apply[1]) that applies the patch.
        Incompatible with the --interactive option.
 
+--committer-date-is-author-date::
+--ignore-date::
+       These flags are passed to 'git-am' to easily change the dates
+       of the rebased commits (see linkgit:git-am[1]).
+
 -i::
 --interactive::
        Make a list of the commits which are about to be rebased.  Let the
index fad983e297514da7e847196b0375e3254fdf235d..82a3d296736e515d4f99732e31369c8cdccc2cfd 100644 (file)
@@ -13,9 +13,10 @@ SYNOPSIS
 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
 'git remote rename' <old> <new>
 'git remote rm' <name>
+'git remote set-head' <name> [-a | -d | <branch>]
 'git remote show' [-n] <name>
 'git remote prune' [-n | --dry-run] <name>
-'git remote update' [group]
+'git remote update' [-p | --prune] [group | remote]...
 
 DESCRIPTION
 -----------
@@ -53,8 +54,7 @@ is created.  You can give more than one `-t <branch>` to track
 multiple branches without grabbing all branches.
 +
 With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
-up to point at remote's `<master>` branch instead of whatever
-branch the `HEAD` at the remote repository actually points at.
+up to point at remote's `<master>` branch. See also the set-head command.
 +
 In mirror mode, enabled with `\--mirror`, the refs will not be stored
 in the 'refs/remotes/' namespace, but in 'refs/heads/'.  This option
@@ -76,6 +76,30 @@ the configuration file format.
 Remove the remote named <name>. All remote tracking branches and
 configuration settings for the remote are removed.
 
+'set-head'::
+
+Sets or deletes the default branch (`$GIT_DIR/remotes/<name>/HEAD`) for
+the named remote. Having a default branch for a remote is not required,
+but allows the name of the remote to be specified in lieu of a specific
+branch. For example, if the default branch for `origin` is set to
+`master`, then `origin` may be specified wherever you would normally
+specify `origin/master`.
++
+With `-d`, `$GIT_DIR/remotes/<name>/HEAD` is deleted.
++
+With `-a`, the remote is queried to determine its `HEAD`, then
+`$GIT_DIR/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote
+`HEAD` is pointed at `next`, "`git remote set-head origin -a`" will set
+`$GIT_DIR/refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will
+only work if `refs/remotes/origin/next` already exists; if not it must be
+fetched first.
++
+Use `<branch>` to set `$GIT_DIR/remotes/<name>/HEAD` explicitly. e.g., "git
+remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to
+`refs/remotes/origin/master`. This will only work if
+`refs/remotes/origin/master` already exists; if not it must be fetched first.
++
+
 'show'::
 
 Gives some information about the remote <name>.
@@ -90,17 +114,19 @@ These stale branches have already been removed from the remote repository
 referenced by <name>, but are still locally available in
 "remotes/<name>".
 +
-With `--dry-run` option, report what branches will be pruned, but do no
+With `--dry-run` option, report what branches will be pruned, but do not
 actually prune them.
 
 'update'::
 
 Fetch updates for a named set of remotes in the repository as defined by
 remotes.<group>.  If a named group is not specified on the command line,
-the configuration parameter remotes.default will get used; if
+the configuration parameter remotes.default will be used; if
 remotes.default is not defined, all remotes which do not have the
 configuration parameter remote.<name>.skipDefaultUpdate set to true will
 be updated.  (See linkgit:git-config[1]).
++
+With `--prune` option, prune all the remotes that are updated.
 
 
 DISCUSSION
index 1c9cc28895a6ea3fcfd978f940e3fa327219de0a..ae1186e3402eb9c428001d32071dc3998b04bb3c 100644 (file)
@@ -83,7 +83,7 @@ between the two operands.  The following two commands are equivalent:
        $ git rev-list A...B
 -----------------------------------------------------------------------
 
-'git-rev-list' is a very essential git program, since it
+'rev-list' is a very essential git command, since it
 provides the ability to build and traverse commit ancestry graphs. For
 this reason, it has a lot of different options that enables it to be
 used by commands as different as 'git-bisect' and
index 3ccef2f2b3295c6d5e14b8f3a9d354d7b57598da..52c353e674761bf4897484a261c702e5cc02f18a 100644 (file)
@@ -26,7 +26,7 @@ OPTIONS
 --parseopt::
        Use 'git-rev-parse' in option parsing mode (see PARSEOPT section below).
 
---keep-dash-dash::
+--keep-dashdash::
        Only meaningful in `--parseopt` mode. Tells the option parser to echo
        out the first `--` met instead of skipping it.
 
@@ -84,6 +84,11 @@ OPTIONS
        unfortunately named tag "master"), and show them as full
        refnames (e.g. "refs/heads/master").
 
+--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.
+
 --all::
        Show all refs found in `$GIT_DIR/refs`.
 
@@ -299,18 +304,18 @@ previous section means the set of commits reachable from that
 commit, following the commit ancestry chain.
 
 To exclude commits reachable from a commit, a prefix `{caret}`
-notation is used.  E.g. "`{caret}r1 r2`" means commits reachable
+notation is used.  E.g. `{caret}r1 r2` means commits reachable
 from `r2` but exclude the ones reachable from `r1`.
 
 This set operation appears so often that there is a shorthand
 for it.  When you have two commits `r1` and `r2` (named according
 to the syntax explained in SPECIFYING REVISIONS above), you can ask
 for commits that are reachable from r2 excluding those that are reachable
-from r1 by "`{caret}r1 r2`" and it can be written as "`r1..r2`".
+from r1 by `{caret}r1 r2` and it can be written as `r1..r2`.
 
-A similar notation "`r1\...r2`" is called symmetric difference
+A similar notation `r1\...r2` is called symmetric difference
 of `r1` and `r2` and is defined as
-"`r1 r2 --not $(git merge-base --all r1 r2)`".
+`r1 r2 --not $(git merge-base --all r1 r2)`.
 It is the set of commits that are reachable from either one of
 `r1` or `r2` but not from both.
 
index fc0a4ab441d0ee55f44450d2722187e975f84f44..a2821907c7800909ff32ea6f413c7d27dee38f12 100644 (file)
@@ -14,6 +14,10 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Takes the patches given on the command line and emails them out.
+Patches can be specified as files, directories (which will send all
+files in the directory), or directly as a revision list.  In the
+last case, any format accepted by linkgit:git-format-patch[1] can
+be passed to git send-email.
 
 The header of the email is configurable by command line options.  If not
 specified on the command line, the user will be prompted with a ReadLine
@@ -39,13 +43,13 @@ OPTIONS
 Composing
 ~~~~~~~~~
 
---bcc::
+--bcc=<address>::
        Specify a "Bcc:" value for each email. Default is the value of
        'sendemail.bcc'.
 +
 The --bcc option must be repeated for each user you want on the bcc list.
 
---cc::
+--cc=<address>::
        Specify a starting "Cc:" value for each email.
        Default is the value of 'sendemail.cc'.
 +
@@ -60,33 +64,32 @@ The --cc option must be repeated for each user you want on the cc list.
        Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
        introductory message for the patch series.
 +
-When '--compose' is used, git send-email gets less interactive will use the
-values of the headers you set there. If the body of the email (what you type
-after the headers and a blank line) only contains blank (or GIT: prefixed)
-lines, the summary won't be sent, but git-send-email will still use the
-Headers values if you don't removed them.
+When '--compose' is used, git send-email will use the From, Subject, and
+In-Reply-To headers specified in the message. If the body of the message
+(what you type after the headers and a blank line) only contains blank
+(or GIT: prefixed) lines the summary won't be sent, but From, Subject,
+and In-Reply-To headers will be used unless they are removed.
 +
-If it wasn't able to see a header in the summary it will ask you about it
-interactively after quitting your editor.
+Missing From or In-Reply-To headers will be prompted for.
 
---from::
+--from=<address>::
        Specify the sender of the emails.  This will default to
        the value GIT_COMMITTER_IDENT, as returned by "git var -l".
        The user will still be prompted to confirm this entry.
 
---in-reply-to::
+--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 (the default)
        Only necessary if --compose is also set.  If --compose
        is not set, this will be prompted for.
 
---subject::
+--subject=<string>::
        Specify the initial subject of the email thread.
        Only necessary if --compose is also set.  If --compose
        is not set, this will be prompted for.
 
---to::
+--to=<address>::
        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,
@@ -98,7 +101,7 @@ The --to option must be repeated for each user you want on the to list.
 Sending
 ~~~~~~~
 
---envelope-sender::
+--envelope-sender=<address>::
        Specify the envelope sender used to send the emails.
        This is useful if your default address is not the address that is
        subscribed to a list. If you use the sendmail binary, you must have
@@ -106,12 +109,12 @@ Sending
        the 'sendemail.envelopesender' configuration variable; if that is
        unspecified, choosing the envelope sender is left to your MTA.
 
---smtp-encryption::
+--smtp-encryption=<encryption>::
        Specify the encryption to use, either 'ssl' or 'tls'.  Any other
        value reverts to plain SMTP.  Default is the value of
        'sendemail.smtpencryption'.
 
---smtp-pass::
+--smtp-pass[=<password>]::
        Password for SMTP-AUTH. The argument is optional: If no
        argument is specified, then the empty string is used as
        the password. Default is the value of 'sendemail.smtppass',
@@ -123,7 +126,7 @@ or on the command line. If a username has been specified (with
 specified (with '--smtp-pass' or 'sendemail.smtppass'), then the
 user is prompted for a password while the input is masked for privacy.
 
---smtp-server::
+--smtp-server=<host>::
        If set, specifies the outgoing SMTP server to use (e.g.
        `smtp.example.com` or a raw IP address).  Alternatively it can
        specify a full pathname of a sendmail-like program instead;
@@ -133,7 +136,7 @@ user is prompted for a password while the input is masked for privacy.
        `/usr/lib/sendmail` if such program is available, or
        `localhost` otherwise.
 
---smtp-server-port::
+--smtp-server-port=<port>::
        Specifies a port different from the default port (SMTP
        servers typically listen to smtp port 25 and ssmtp port
        465). This can be set with 'sendemail.smtpserverport'.
@@ -141,7 +144,7 @@ user is prompted for a password while the input is masked for privacy.
 --smtp-ssl::
        Legacy alias for '--smtp-encryption ssl'.
 
---smtp-user::
+--smtp-user=<user>::
        Username for SMTP-AUTH. Default is the value of 'sendemail.smtpuser';
        if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'),
        then authentication is not attempted.
@@ -150,13 +153,13 @@ user is prompted for a password while the input is masked for privacy.
 Automating
 ~~~~~~~~~~
 
---cc-cmd::
+--cc-cmd=<command>::
        Specify a command to execute once per patch file which
        should generate patch file specific "Cc:" entries.
        Output of this command must be single email address per line.
        Default is the value of 'sendemail.cccmd' configuration value.
 
---[no-]chain-reply-to::
+--[no-]chain-reply-to=<identifier>::
        If this is set, each email will be sent as a reply to the previous
        email sent.  If disabled with "--no-chain-reply-to", all emails after
        the first will be sent as replies to the first email sent.  When using
@@ -164,7 +167,7 @@ Automating
        entire patch series. Default is the value of the 'sendemail.chainreplyto'
        configuration value; if that is unspecified, default to --chain-reply-to.
 
---identity::
+--identity=<identity>::
        A configuration identity. When given, causes values in the
        'sendemail.<identity>' subsection to take precedence over
        values in the 'sendemail' section. The default identity is
@@ -175,7 +178,7 @@ Automating
        cc list. Default is the value of 'sendemail.signedoffbycc' configuration
        value; if that is unspecified, default to --signed-off-by-cc.
 
---suppress-cc::
+--suppress-cc=<category>::
        Specify an additional category of recipients to suppress the
        auto-cc of:
 +
@@ -212,6 +215,22 @@ specified, as well as 'body' if --no-signed-off-cc is specified.
 Administering
 ~~~~~~~~~~~~~
 
+--confirm=<mode>::
+       Confirm just before sending:
++
+--
+- 'always' will always confirm before sending
+- 'never' will never confirm before sending
+- 'cc' will confirm before sending when send-email has automatically
+  added addresses from the patch to the Cc list
+- 'compose' will confirm before sending the first message when using --compose.
+- 'auto' is equivalent to 'cc' + 'compose'
+--
++
+Default is the value of 'sendemail.confirm' configuration value; if that
+is unspecified, default to 'auto' unless any of the suppress options
+have been specified, in which case default to 'compose'.
+
 --dry-run::
        Do everything except actually send the emails.
 
@@ -247,7 +266,7 @@ sendemail.aliasesfile::
 
 sendemail.aliasfiletype::
        Format of the file(s) specified in sendemail.aliasesfile. Must be
-       one of 'mutt', 'mailrc', 'pine', or 'gnus'.
+       one of 'mutt', 'mailrc', 'pine', 'elm', or 'gnus'.
 
 sendemail.multiedit::
        If true (default), a single editor instance will be spawned to edit
@@ -255,6 +274,11 @@ sendemail.multiedit::
        summary when '--compose' is used). If false, files will be edited one
        after the other, spawning a new editor each time.
 
+sendemail.confirm::
+       Sets the default for whether to confirm before sending. Must be
+       one of 'always', 'never', 'cc', 'compose', or 'auto'. See '--confirm'
+       in the previous section for the meaning of these values.
+
 
 Author
 ------
index 3f8d973af1c9b8aead3f831eb94a42b2e461ec8b..0f3ad811cfa41e65a3d807a5eb766ce2a66a7831 100644 (file)
@@ -18,9 +18,9 @@ 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 three commands are permitted to be called, 'git-receive-pack'
-'git-upload-pack' with a single required argument or 'cvs server' (to invoke
-'git-cvsserver').
+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').
 
 Author
 ------
index 7e9ff3762b111d8740a99b676ea516435018f3a8..51a4e9d6d767f34205f418ec86a6281dbf4c7b2c 100644 (file)
@@ -148,9 +148,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'.  "fixes" branch
-adds one commit 'Introduce "reset type"'.  "mhf" branch has many
-other commits.  The current branch is "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".
 
 
 EXAMPLE
index 051f94d26f9f057cbd4db0d6886ca1a4d33c912c..1cc24cc47e3b9845c9d1006ee58faed1bc731c49 100644 (file)
@@ -75,14 +75,22 @@ show [<stash>]::
        it will accept any format known to 'git-diff' (e.g., `git stash show
        -p stash@\{1}` to view the second most recent stash in patch form).
 
-apply [--index] [<stash>]::
+pop [<stash>]::
 
-       Restore the changes recorded in the stash on top of the current
-       working tree state.  When no `<stash>` is given, applies the latest
-       one.  The working directory must match the index.
+       Remove a single stashed state from the stash list and apply it
+       on top of the current working tree state, i.e., do the inverse
+       operation of `git stash save`. The working directory must
+       match the index.
 +
-This operation can fail with conflicts; you need to resolve them
-by hand in the working tree.
+Applying the state can fail with conflicts; in this case, it is not
+removed from the stash list. You need to resolve the conflicts by hand
+and call `git stash drop` manually afterwards.
++
+When no `<stash>` is given, `stash@\{0}` is assumed. See also `apply`.
+
+apply [--index] [<stash>]::
+
+       Like `pop`, but do not remove the state from the stash list.
 +
 If the `--index` option is used, then tries to reinstate not only the working
 tree's changes, but also the index's ones. However, this can fail, when you
@@ -112,12 +120,6 @@ drop [<stash>]::
        Remove a single stashed state from the stash list. When no `<stash>`
        is given, it removes the latest one. i.e. `stash@\{0}`
 
-pop [<stash>]::
-
-       Remove a single stashed state from the stash list and apply on top
-       of the current working tree state. When no `<stash>` is given,
-       `stash@\{0}` is assumed. See also `apply`.
-
 create::
 
        Create a stash (which is a regular commit object) and return its
@@ -163,7 +165,7 @@ $ git pull
 file foobar not up to date, cannot merge.
 $ git stash
 $ git pull
-$ git stash apply
+$ git stash pop
 ----------------------------------------------------------------
 
 Interrupted workflow::
@@ -192,7 +194,7 @@ You can use 'git-stash' to simplify the above, like this:
 $ git stash
 $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
-$ git stash apply
+$ git stash pop
 # ... continue hacking ...
 ----------------------------------------------------------------
 
index 3b8df4467377d73d613f76875c725cbf8544ee77..15b34d3760820655c76da3d3b8508db81c8e8dae 100644 (file)
@@ -137,8 +137,9 @@ foreach::
        the processing to terminate. This can be overridden by adding '|| :'
        to the end of the command.
 +
-As an example, "git submodule foreach 'echo $path `git rev-parse HEAD`' will
-show the path and currently checked out commit for each submodule.
+As an example, +git submodule foreach \'echo $path {backtick}git
+rev-parse HEAD{backtick}'+ will show the path and currently checked out
+commit for each submodule.
 
 sync::
        Synchronizes submodules' remote URL configuration setting
index d8bf7bfc303af3549c30e199dc0575f8e87e5b9f..74be8435cccf402dce2c95338b9553101b431b88 100644 (file)
@@ -85,6 +85,10 @@ COMMANDS
        specified, the prefix must include a trailing slash.
        Setting a prefix is useful if you wish to track multiple
        projects that share a common repository.
+--ignore-paths=<regex>;;
+       When passed to 'init' or 'clone' this regular expression will
+       be preserved as a config key.  See 'fetch' for a description
+       of '--ignore-paths'.
 
 'fetch'::
        Fetch unfetched revisions from the Subversion remote we are
@@ -97,6 +101,9 @@ COMMANDS
        makes 'git-log' (even without --date=local) show the same times
        that `svn log` would in the local timezone.
 
+--parent;;
+       Fetch only from the SVN parent of the current HEAD.
+
 This doesn't interfere with interoperating with the Subversion
 repository you cloned from, but if you wish for your local Git
 repository to be able to interoperate with someone else's local Git
@@ -104,17 +111,25 @@ repository, either don't use this option or you should both use it in
 the same local timezone.
 
 --ignore-paths=<regex>;;
-       This allows one to specify Perl regular expression that will
+       This allows one to specify Perl regular expression that will
        cause skipping of all matching paths from checkout from SVN.
-       Examples:
+       The '--ignore-paths' option should match for every 'fetch'
+       (including automatic fetches due to 'clone', 'dcommit',
+       'rebase', etc) on a given repository.
+
+config key: svn-remote.<name>.ignore-paths
+
+       If the ignore-paths config key is set and the command
+       line option is also given, both regular expressions
+       will be used.
 
-       --ignore-paths="^doc" - skip "doc*" directory for every fetch.
+Examples:
 
-       --ignore-paths="^[^/]+/(?:branches|tags)" - skip "branches"
-           and "tags" of first level directories.
+       --ignore-paths="^doc" - skip "doc*" directory for every
+           fetch.
 
-       Regular expression is not persistent, you should specify
-       it every time when fetching.
+       --ignore-paths="^[^/]+/(?:branches|tags)" - skip
+           "branches" and "tags" of first level directories.
 
 'clone'::
        Runs 'init' and 'fetch'.  It will automatically create a
@@ -385,7 +400,8 @@ config key: svn.authorsfile
 
 -q::
 --quiet::
-       Make 'git-svn' less verbose.
+       Make 'git-svn' less verbose. Specify a second time to make it
+       even less verbose.
 
 --repack[=<n>]::
 --repack-flags=<flags>::
@@ -672,14 +688,14 @@ listed below are allowed:
 ------------------------------------------------------------------------
 [svn-remote "project-a"]
        url = http://server.org/svn
+       fetch = trunk/project-a:refs/remotes/project-a/trunk
        branches = branches/*/project-a:refs/remotes/project-a/branches/*
        tags = tags/*/project-a:refs/remotes/project-a/tags/*
-       trunk = trunk/project-a:refs/remotes/project-a/trunk
 ------------------------------------------------------------------------
 
-Keep in mind that the '*' (asterisk) wildcard of the local ref
+Keep in mind that the '\*' (asterisk) wildcard of the local ref
 (right of the ':') *must* be the farthest right path component;
-however the remote wildcard may be anywhere as long as it's own
+however the remote wildcard may be anywhere as long as it's an
 independent path component (surrounded by '/' or EOL).   This
 type of configuration is not automatically created by 'init' and
 should be manually entered with a text-editor or using 'git-config'.
index 210fde03a12cd757769f81754e789a2a5934f02c..63925388073b17c3087d32813979513580adfa2a 100644 (file)
@@ -14,9 +14,9 @@ DESCRIPTION
 Given one argument, reads which branch head the given symbolic
 ref refers to and outputs its path, relative to the `.git/`
 directory.  Typically you would give `HEAD` as the <name>
-argument to see on which branch your working tree is on.
+argument to see which branch your working tree is on.
 
-Give two arguments, create or update a symbolic ref <name> to
+Given two arguments, creates or updates a symbolic ref <name> to
 point at the given branch <ref>.
 
 A symbolic ref is a regular file that stores a string that
index 35d27b0c7f0e4b7a1d0851140958e71fabb0e6bc..035cc3018f22a9e2669c94f10475624d02f4098a 100644 (file)
@@ -39,12 +39,6 @@ what they are for:
 * info/refs
 
 
-BUGS
-----
-When you remove an existing ref, the command fails to update
-info/refs file unless `--force` flag is given.
-
-
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com>
index 9a26bde73e695effce9741558994e7799e81489a..8fbe187fb33d8bfe3e6f3cbf87e186dbd93bb066 100644 (file)
@@ -9,7 +9,7 @@ git - the stupid content tracker
 SYNOPSIS
 --------
 [verse]
-'git' [--version] [--exec-path[=GIT_EXEC_PATH]]
+'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
     [-p|--paginate|--no-pager]
     [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
     [--help] COMMAND [ARGS]
@@ -43,9 +43,14 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.6.2/git.html[documentation for release 1.6.2]
+* link:v1.6.3/git.html[documentation for release 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:v1.6.1.3/git.html[documentation for release 1.6.1.3]
@@ -177,6 +182,10 @@ help ...`.
        environment variable. If no path is given, 'git' will print
        the current setting and then exit.
 
+--html-path::
+       Print the path to wherever your git HTML documentation is installed
+       and exit.
+
 -p::
 --paginate::
        Pipe all output into 'less' (or if set, $PAGER).
@@ -218,6 +227,8 @@ The link:user-manual.html#git-concepts[git concepts chapter of the
 user-manual] and linkgit:gitcore-tutorial[7] both provide
 introductions to the underlying git architecture.
 
+See linkgit:gitworkflows[7] for an overview of recommended workflows.
+
 See also the link:howto-index.html[howto] documents for some useful
 examples.
 
@@ -304,7 +315,7 @@ Synching repositories
 
 include::cmds-synchingrepositories.txt[]
 
-The following are helper programs used by the above; end users
+The following are helper commands used by the above; end users
 typically do not use them directly.
 
 include::cmds-synchelpers.txt[]
@@ -635,7 +646,8 @@ SEE ALSO
 linkgit:gittutorial[7], linkgit:gittutorial-2[7],
 link:everyday.html[Everyday Git], linkgit:gitcvs-migration[7],
 linkgit:gitglossary[7], linkgit:gitcore-tutorial[7],
-linkgit:gitcli[7], link:user-manual.html[The Git User's Manual]
+linkgit:gitcli[7], link:user-manual.html[The Git User's Manual],
+linkgit:gitworkflows[7]
 
 GIT
 ---
index aaa073efc80522a649f17d60127aae8cc85b0b3b..1195e83b6e65b5cf7926c973c730ffe19ff92202 100644 (file)
@@ -404,7 +404,7 @@ Performing a three-way merge
 
 The attribute `merge` affects how three versions of a file is
 merged when a file-level merge is necessary during `git merge`,
-and other programs such as `git revert` and `git cherry-pick`.
+and other commands such as `git revert` and `git cherry-pick`.
 
 Set::
 
index 29e5929db22257346a2bed16cbd5ca6531698676..be39ed7c1509158867998ce8fb77341242ecdf79 100644 (file)
@@ -46,20 +46,20 @@ Here are the rules regarding the "flags" that you should follow when you are
 scripting git:
 
  * it's preferred to use the non dashed form of git commands, which means that
-   you should prefer `"git foo"` to `"git-foo"`.
+   you should prefer `git foo` to `git-foo`.
 
- * splitting short options to separate words (prefer `"git foo -a -b"`
-   to `"git foo -ab"`, the latter may not even work).
+ * splitting short options to separate words (prefer `git foo -a -b`
+   to `git foo -ab`, the latter may not even work).
 
  * when a command line option takes an argument, use the 'sticked' form.  In
-   other words, write `"git foo -oArg"` instead of `"git foo -o Arg"` for short
-   options, and `"git foo --long-opt=Arg"` instead of `"git foo --long-opt Arg"`
+   other words, write `git foo -oArg` instead of `git foo -o Arg` for short
+   options, and `git foo --long-opt=Arg` instead of `git foo --long-opt Arg`
    for long options.  An option that takes optional option-argument must be
    written in the 'sticked' form.
 
  * when you give a revision parameter to a command, make sure the parameter is
    not ambiguous with a name of a file in the work tree.  E.g. do not write
-   `"git log -1 HEAD"` but write `"git log -1 HEAD --"`; the former will not work
+   `git log -1 HEAD` but write `git log -1 HEAD --`; the former will not work
    if you happen to have a file called `HEAD` in the work tree.
 
 
@@ -99,17 +99,17 @@ usage: git-describe [options] <committish>*
 
 Negating options
 ~~~~~~~~~~~~~~~~
-Options with long option names can be negated by prefixing `"--no-"`. For
-example, `"git branch"` has the option `"--track"` which is 'on' by default. You
-can use `"--no-track"` to override that behaviour. The same goes for `"--color"`
-and `"--no-color"`.
+Options with long option names can be negated by prefixing `--no-`. For
+example, `git branch` has the option `--track` which is 'on' by default. You
+can use `--no-track` to override that behaviour. The same goes for `--color`
+and `--no-color`.
 
 
 Aggregating short options
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 Commands that support the enhanced option parser allow you to aggregate short
-options. This means that you can for example use `"git rm -rf"` or
-`"git clean -fdx"`.
+options. This means that you can for example use `git rm -rf` or
+`git clean -fdx`.
 
 
 Separating argument from the option
index 7ba5e589d7e824c526482c9707a5c26ac730cc9e..b3640c4e64494689841e598cf17741ea8620af12 100644 (file)
@@ -12,7 +12,7 @@ git *
 DESCRIPTION
 -----------
 
-This tutorial explains how to use the "core" git programs to set up and
+This tutorial explains how to use the "core" git commands to set up and
 work with a git repository.
 
 If you just need to use git as a revision control system you may prefer
@@ -1328,7 +1328,7 @@ into it later. Obviously, this repository creation needs to be
 done only once.
 
 [NOTE]
-'git-push' uses a pair of programs,
+'git-push' uses a pair of commands,
 'git-send-pack' on your local machine, and 'git-receive-pack'
 on the remote machine. The communication between the two over
 the network internally uses an SSH connection.
index 1fd512bca2e12f5eb0abac0edb0dfb2a86949cec..1c736738ccc91b6929226a27e02716221ec3ef05 100644 (file)
@@ -151,6 +151,10 @@ indicating whether the checkout was a branch checkout (changing branches,
 flag=1) or a file checkout (retrieving a file from the index, flag=0).
 This hook cannot affect the outcome of 'git-checkout'.
 
+It is also run after 'git-clone', unless the --no-checkout (-n) option is
+used. The first parameter given to the hook is the null-ref, the second the
+ref of the new HEAD and the flag is always 1.
+
 This hook can be used to perform repository validity checks, auto-display
 differences from the previous HEAD if different, or set working dir metadata
 properties.
index c5d5596d895755d69c8060bc38a800d7c70eda26..cf0689cfeb8b5122f8fa20345d9f5307fee1cd6c 100644 (file)
@@ -332,11 +332,11 @@ alice$ git log -p HEAD..FETCH_HEAD
 ------------------------------------------------
 
 This operation is safe even if Alice has uncommitted local changes.
-The range notation HEAD..FETCH_HEAD" means "show everything that is reachable
-from the FETCH_HEAD but exclude anything that is reachable from HEAD.
+The range notation "HEAD..FETCH_HEAD" means "show everything that is reachable
+from the FETCH_HEAD but exclude anything that is reachable from HEAD".
 Alice already knows everything that leads to her current state (HEAD),
-and reviewing what Bob has in his state (FETCH_HEAD) that she has not
-seen with this command
+and reviews what Bob has in his state (FETCH_HEAD) that she has not
+seen with this command.
 
 If Alice wants to visualize what Bob did since their histories forked
 she can issue the following command:
@@ -375,9 +375,9 @@ it easier:
 alice$ git remote add bob /home/bob/myrepo
 ------------------------------------------------
 
-With this, Alice can perform the first part of the "pull" operation alone using the
-'git-fetch' command without merging them with her own branch,
-using:
+With this, Alice can perform the first part of the "pull" operation
+alone using the 'git-fetch' command without merging them with her own
+branch, using:
 
 -------------------------------------
 alice$ git fetch bob
@@ -566,22 +566,22 @@ $ git log v2.5.. Makefile       # commits since v2.5 which modify
 
 You can also give 'git-log' a "range" of commits where the first is not
 necessarily an ancestor of the second; for example, if the tips of
-the branches "stable-release" and "master" diverged from a common
+the branches "stable" and "master" diverged from a common
 commit some time ago, then
 
 -------------------------------------
-$ git log stable..experimental
+$ git log stable..master
 -------------------------------------
 
-will list commits made in the experimental branch but not in the
+will list commits made in the master branch but not in the
 stable branch, while
 
 -------------------------------------
-$ git log experimental..stable
+$ git log master..stable
 -------------------------------------
 
 will show the list of commits made on the stable branch but not
-the experimental branch.
+the master branch.
 
 The 'git-log' command has a weakness: it must present commits in a
 list.  When the history has lines of development that diverged and
@@ -650,6 +650,9 @@ digressions that may be interesting at this point are:
     smart enough to perform a close-to-optimal search even in the
     case of complex non-linear history with lots of merged branches.
 
+  * linkgit:gitworkflows[7]: Gives an overview of recommended
+    workflows.
+
   * link:everyday.html[Everyday GIT with 20 Commands Or So]
 
   * linkgit:gitcvs-migration[7]: Git for CVS users.
@@ -661,6 +664,7 @@ linkgit:gitcvs-migration[7],
 linkgit:gitcore-tutorial[7],
 linkgit:gitglossary[7],
 linkgit:git-help[1],
+linkgit:gitworkflows[7],
 link:everyday.html[Everyday git],
 link:user-manual.html[The Git User's Manual]
 
index 9afca755ed309b5bdf3cd293d6120f676f1053cd..572374f7a6ff17dd40ad687a2fda24d53f7d730b 100644 (file)
@@ -262,7 +262,7 @@ This commit is referred to as a "merge commit", or sometimes just a
        'origin' is used for that purpose. New upstream updates
        will be fetched into remote <<def_tracking_branch,tracking branches>> named
        origin/name-of-upstream-branch, which you can see using
-       "`git branch -r`".
+       `git branch -r`.
 
 [[def_pack]]pack::
        A set of objects which have been compressed into one file (to save space
@@ -449,6 +449,12 @@ This commit is referred to as a "merge commit", or sometimes just a
        An <<def_object,object>> which is not <<def_reachable,reachable>> from a
        <<def_branch,branch>>, <<def_tag,tag>>, or any other reference.
 
+[[def_upstream_branch]]upstream branch::
+       The default <<def_branch,branch>> that is merged into the branch in
+       question (or the branch in question is rebased onto). It is configured
+       via branch.<name>.remote and branch.<name>.merge. If the upstream branch
+       of 'A' is 'origin/B' sometimes we say "'A' is tracking 'origin/B'".
+
 [[def_working_tree]]working tree::
        The tree of actual checked out files.  The working tree is
        normally equal to the <<def_HEAD,HEAD>> plus any local changes
index 4065a3a27a38be73132b9f509e1d63546b3fddef..b4d315cb8c4792c25cfad7892e056356543e85e1 100644 (file)
@@ -1,21 +1,14 @@
-<!-- Based on callouts.xsl. Fixes man page callouts for DocBook 1.72 XSL -->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<!-- manpage-1.72.xsl:
+     special settings for manpages rendered from asciidoc+docbook
+     handles peculiarities in docbook-xsl 1.72.0 -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+               version="1.0">
 
-<xsl:param name="man.output.quietly" select="1"/>
-<xsl:param name="refentry.meta.get.quietly" select="1"/>
+<xsl:import href="manpage-base.xsl"/>
 
-<xsl:template match="co">
-       <xsl:value-of select="concat('&#x2593;fB(',substring-after(@id,'-'),')&#x2593;fR')"/>
-</xsl:template>
-<xsl:template match="calloutlist">
-       <xsl:text>&#x2302;sp&#10;</xsl:text>
-       <xsl:apply-templates/>
-       <xsl:text>&#10;</xsl:text>
-</xsl:template>
-<xsl:template match="callout">
-       <xsl:value-of select="concat('&#x2593;fB',substring-after(@arearefs,'-'),'. &#x2593;fR')"/>
-       <xsl:apply-templates/>
-       <xsl:text>&#x2302;br&#10;</xsl:text>
-</xsl:template>
+<!-- these are the special values for the roff control characters
+     needed for docbook-xsl 1.72.0 -->
+<xsl:param name="git.docbook.backslash">&#x2593;</xsl:param>
+<xsl:param name="git.docbook.dot"      >&#x2302;</xsl:param>
 
 </xsl:stylesheet>
diff --git a/Documentation/manpage-base.xsl b/Documentation/manpage-base.xsl
new file mode 100644 (file)
index 0000000..a264fa6
--- /dev/null
@@ -0,0 +1,35 @@
+<!-- manpage-base.xsl:
+     special formatting for manpages rendered from asciidoc+docbook -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+               version="1.0">
+
+<!-- these params silence some output from xmlto -->
+<xsl:param name="man.output.quietly" select="1"/>
+<xsl:param name="refentry.meta.get.quietly" select="1"/>
+
+<!-- convert asciidoc callouts to man page format;
+     git.docbook.backslash and git.docbook.dot params
+     must be supplied by another XSL file or other means -->
+<xsl:template match="co">
+       <xsl:value-of select="concat(
+                             $git.docbook.backslash,'fB(',
+                             substring-after(@id,'-'),')',
+                             $git.docbook.backslash,'fR')"/>
+</xsl:template>
+<xsl:template match="calloutlist">
+       <xsl:value-of select="$git.docbook.dot"/>
+       <xsl:text>sp&#10;</xsl:text>
+       <xsl:apply-templates/>
+       <xsl:text>&#10;</xsl:text>
+</xsl:template>
+<xsl:template match="callout">
+       <xsl:value-of select="concat(
+                             $git.docbook.backslash,'fB',
+                             substring-after(@arearefs,'-'),
+                             '. ',$git.docbook.backslash,'fR')"/>
+       <xsl:apply-templates/>
+       <xsl:value-of select="$git.docbook.dot"/>
+       <xsl:text>br&#10;</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-bold-literal.xsl b/Documentation/manpage-bold-literal.xsl
new file mode 100644 (file)
index 0000000..608eb5d
--- /dev/null
@@ -0,0 +1,17 @@
+<!-- manpage-bold-literal.xsl:
+     special formatting for manpages rendered from asciidoc+docbook -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+               version="1.0">
+
+<!-- render literal text as bold (instead of plain or monospace);
+     this makes literal text easier to distinguish in manpages
+     viewed on a tty -->
+<xsl:template match="literal">
+       <xsl:value-of select="$git.docbook.backslash"/>
+       <xsl:text>fB</xsl:text>
+       <xsl:apply-templates/>
+       <xsl:value-of select="$git.docbook.backslash"/>
+       <xsl:text>fR</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-normal.xsl b/Documentation/manpage-normal.xsl
new file mode 100644 (file)
index 0000000..a48f5b1
--- /dev/null
@@ -0,0 +1,13 @@
+<!-- manpage-normal.xsl:
+     special settings for manpages rendered from asciidoc+docbook
+     handles anything we want to keep away from docbook-xsl 1.72.0 -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+               version="1.0">
+
+<xsl:import href="manpage-base.xsl"/>
+
+<!-- these are the normal values for the roff control characters -->
+<xsl:param name="git.docbook.backslash">\</xsl:param>
+<xsl:param name="git.docbook.dot"      >.</xsl:param>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-suppress-sp.xsl b/Documentation/manpage-suppress-sp.xsl
new file mode 100644 (file)
index 0000000..a63c763
--- /dev/null
@@ -0,0 +1,21 @@
+<!-- manpage-suppress-sp.xsl:
+     special settings for manpages rendered from asciidoc+docbook
+     handles erroneous, inline .sp in manpage output of some
+     versions of docbook-xsl -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+               version="1.0">
+
+<!-- attempt to work around spurious .sp at the tail of the line
+     that some versions of docbook stylesheets seem to add -->
+<xsl:template match="simpara">
+  <xsl:variable name="content">
+    <xsl:apply-templates/>
+  </xsl:variable>
+  <xsl:value-of select="normalize-space($content)"/>
+  <xsl:if test="not(ancestor::authorblurb) and
+                not(ancestor::personblurb)">
+    <xsl:text>&#10;&#10;</xsl:text>
+  </xsl:if>
+</xsl:template>
+
+</xsl:stylesheet>
index 1ff08ff2ccc6e56542f49713867698b66dab673f..4832bc75e2604c997e004018023e3b93cb394b87 100644 (file)
@@ -22,7 +22,8 @@ merge.stat::
 merge.tool::
        Controls which merge resolution program is used by
        linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
-       "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
+       "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff",
+       "diffuse", "ecmerge", "tortoisemerge", and
        "opendiff".  Any other value is treated is custom merge tool
        and there must be a corresponding mergetool.<tool>.cmd option.
 
index 637b53f898829af98153d60953434fa30e3df179..adadf8e4bf309a2fd2ec8efbeff09b410ca7b041 100644 (file)
@@ -39,7 +39,8 @@
 
 --squash::
        Produce the working tree and index state as if a real
-       merge happened, but do not actually make a commit or
+       merge happened (except for the merge information),
+       but do not actually make a commit or
        move the `HEAD`, nor record `$GIT_DIR/MERGE_HEAD` to
        cause the next `git commit` command to create a merge
        commit.  This allows you to create a single commit on
index 1276f858ade29bec40716d19cf56fe6e3882fc25..4365b7e8420fa96d6cbfa14a5aa49d956ba2de16 100644 (file)
@@ -3,15 +3,15 @@ MERGE STRATEGIES
 
 resolve::
        This can only resolve two heads (i.e. the current branch
-       and another branch you pulled from) using 3-way merge
+       and another branch you pulled from) using 3-way merge
        algorithm.  It tries to carefully detect criss-cross
        merge ambiguities and is considered generally safe and
        fast.
 
 recursive::
-       This can only resolve two heads using 3-way merge
-       algorithm.  When there are more than one common
-       ancestors that can be used for 3-way merge, it creates a
+       This can only resolve two heads using 3-way merge
+       algorithm.  When there is more than one common
+       ancestor that can be used for 3-way merge, it creates a
        merged tree of the common ancestors and uses that as
        the reference tree for the 3-way merge.  This has been
        reported to result in fewer merge conflicts without
@@ -22,11 +22,11 @@ recursive::
        pulling or merging one branch.
 
 octopus::
-       This resolves more than two-head case, but refuses to do
-       complex merge that needs manual resolution.  It is
+       This resolves cases with more than two heads, but refuses to do
+       complex merge that needs manual resolution.  It is
        primarily meant to be used for bundling topic branch
        heads together.  This is the default merge strategy when
-       pulling or merging more than one branches.
+       pulling or merging more than one branch.
 
 ours::
        This resolves any number of heads, but the result of the
index 159390c35a9413590b83f512c3f9f59e6a275e77..2a845b1e57590a6f18f05fd3efa858b736c1071a 100644 (file)
@@ -121,6 +121,7 @@ The placeholders are:
 - '%d': ref names, like the --decorate option of linkgit:git-log[1]
 - '%e': encoding
 - '%s': subject
+- '%f': sanitized subject line, suitable for a filename
 - '%b': body
 - '%Cred': switch color to red
 - '%Cgreen': switch color to green
@@ -152,3 +153,12 @@ $ git log -2 --pretty=tformat:%h 4da45bef \
 4da45be
 7134973
 ---------------------
++
+In addition, any unrecognized string that has a `%` in it is interpreted
+as if it has `tformat:` in front of it.  For example, these two are
+equivalent:
++
+---------------------
+$ git log -2 --pretty=tformat:%h 4da45bef
+$ git log -2 --pretty=%h 4da45bef
+---------------------
index 5f21efe40745a98eafff7df67a916b1f1193fae4..bff94991b68aaca5a81eae4e6681f3431aa6b9ac 100644 (file)
@@ -1,4 +1,5 @@
 --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',
@@ -17,6 +18,10 @@ configuration (see linkgit:git-config[1]).
 This should make "--pretty=oneline" a whole lot more readable for
 people using 80-column terminals.
 
+--oneline::
+       This is a shorthand for "--pretty=oneline --abbrev-commit"
+       used together.
+
 --encoding[=<encoding>]::
        The commit objects record the encoding used for the log message
        in their encoding header; this option can be used to tell the
index 7dd237c2f66d81aebf59add2787aa822c94d9b92..11eec941dff7302ffcd68eca3d6437d31c022ca9 100644 (file)
@@ -140,38 +140,38 @@ limiting may be applied.
 --
 
 -n 'number'::
---max-count='number'::
+--max-count=<number>::
 
        Limit the number of commits output.
 
---skip='number'::
+--skip=<number>::
 
        Skip 'number' commits before starting to show the commit output.
 
---since='date'::
---after='date'::
+--since=<date>::
+--after=<date>::
 
        Show commits more recent than a specific date.
 
---until='date'::
---before='date'::
+--until=<date>::
+--before=<date>::
 
        Show commits older than a specific date.
 
 ifdef::git-rev-list[]
---max-age='timestamp'::
---min-age='timestamp'::
+--max-age=<timestamp>::
+--min-age=<timestamp>::
 
        Limit the commits output to specified time range.
 endif::git-rev-list[]
 
---author='pattern'::
---committer='pattern'::
+--author=<pattern>::
+--committer=<pattern>::
 
        Limit the commits output to ones with author/committer
        header lines that match the specified pattern (regular expression).
 
---grep='pattern'::
+--grep=<pattern>::
 
        Limit the commits output to ones with log message that
        matches the specified pattern (regular expression).
index 539863b1f920f8f34ad9272907cbacbd35a7fcbd..e30c602f476da2da632d2cd04396d168c80ab233 100644 (file)
@@ -66,6 +66,12 @@ Steps to parse options
 non-option arguments in `argv[]`.
 `argc` is updated appropriately because of the assignment.
 +
+You can also pass NULL instead of a usage array as fourth parameter of
+parse_options(), to avoid displaying a help screen with usage info and
+option list.  This should only be done if necessary, e.g. to implement
+a limited parser for only a subset of the options that needs to be run
+before the full parser, which in turn shows the full help message.
++
 Flags are the bitwise-or of:
 
 `PARSE_OPT_KEEP_DASHDASH`::
@@ -77,6 +83,28 @@ Flags are the bitwise-or of:
        Using this flag, processing is stopped at the first non-option
        argument.
 
+`PARSE_OPT_KEEP_ARGV0`::
+       Keep the first argument, which contains the program name.  It's
+       removed from argv[] by default.
+
+`PARSE_OPT_KEEP_UNKNOWN`::
+       Keep unknown arguments instead of erroring out.  This doesn't
+       work for all combinations of arguments as users might expect
+       it to do.  E.g. if the first argument in `--unknown --known`
+       takes a value (which we can't know), the second one is
+       mistakenly interpreted as a known option.  Similarly, if
+       `PARSE_OPT_STOP_AT_NON_OPTION` is set, the second argument in
+       `--unknown value` will be mistakenly interpreted as a
+       non-option, not as a value belonging to the unknown option,
+       the parser early.  That's why parse_options() errors out if
+       both options are set.
+
+`PARSE_OPT_NO_INTERNAL_HELP`::
+       By default, parse_options() handles `-h`, `--help` and
+       `--help-all` internally, by showing a help screen.  This option
+       turns it off and allows one to add custom handlers for these
+       options, or to just leave them unknown.
+
 Data Structure
 --------------
 
@@ -170,7 +198,7 @@ The function must be defined in this form:
 
 The callback mechanism is as follows:
 
-* Inside `funct`, the only interesting member of the structure
+* Inside `func`, the only interesting member of the structure
   given by `opt` is the void pointer `opt->value`.
   `\*opt->value` will be the value that is saved into `var`, if you
   use `OPT_CALLBACK()`.
index 96af8977f6cae5382728f13116ea24ba2d130bef..67ebffa568fd684afd08496281a530c658a217d9 100644 (file)
@@ -188,7 +188,7 @@ As you can see, a commit shows who made the latest change, what they
 did, and why.
 
 Every commit has a 40-hexdigit id, sometimes called the "object name" or the
-"SHA1 id", shown on the first line of the "git-show" output.  You can usually
+"SHA-1 id", shown on the first line of the "git show" output.  You can usually
 refer to a commit by a shorter name, such as a tag or a branch name, but this
 longer name can also be useful.  Most importantly, it is a globally unique
 name for this commit: so if you tell somebody else the object name (for
@@ -307,7 +307,7 @@ ref: refs/heads/master
 Examining an old version without creating a new branch
 ------------------------------------------------------
 
-The git-checkout command normally expects a branch head, but will also
+The `git checkout` command normally expects a branch head, but will also
 accept an arbitrary commit; for example, you can check out the commit
 referenced by a tag:
 
@@ -320,7 +320,7 @@ If you want to create a new branch from this checkout, you may do so
 HEAD is now at 427abfa... Linux v2.6.17
 ------------------------------------------------
 
-The HEAD then refers to the SHA1 of the commit instead of to a branch,
+The HEAD then refers to the SHA-1 of the commit instead of to a branch,
 and git branch shows that you are no longer on a branch:
 
 ------------------------------------------------
@@ -400,7 +400,7 @@ references with the same shorthand name, see the "SPECIFYING
 REVISIONS" section of linkgit:git-rev-parse[1].
 
 [[Updating-a-repository-With-git-fetch]]
-Updating a repository with git-fetch
+Updating a repository with git fetch
 ------------------------------------
 
 Eventually the developer cloned from will do additional work in her
@@ -427,7 +427,7 @@ $ git fetch linux-nfs
 -------------------------------------------------
 
 New remote-tracking branches will be stored under the shorthand name
-that you gave "git-remote add", in this case linux-nfs:
+that you gave "git remote add", in this case linux-nfs:
 
 -------------------------------------------------
 $ git branch -r
@@ -516,7 +516,7 @@ $ git bisect reset
 
 to return you to the branch you were on before.
 
-Note that the version which git-bisect checks out for you at each
+Note that the version which `git bisect` checks out for you at each
 point is just a suggestion, and you're free to try a different
 version if you think it would be a good idea.  For example,
 occasionally you may land on a commit that broke something unrelated;
@@ -592,11 +592,11 @@ In addition to HEAD, there are several other special names for
 commits:
 
 Merges (to be discussed later), as well as operations such as
-git-reset, which change the currently checked-out commit, generally
+`git reset`, which change the currently checked-out commit, generally
 set ORIG_HEAD to the value HEAD had before the current operation.
 
-The git-fetch operation always stores the head of the last fetched
-branch in FETCH_HEAD.  For example, if you run git fetch without
+The `git fetch` operation always stores the head of the last fetched
+branch in FETCH_HEAD.  For example, if you run `git fetch` without
 specifying a local branch as the target of the operation
 
 -------------------------------------------------
@@ -739,7 +739,7 @@ $ git log --pretty=oneline origin..mybranch | wc -l
 -------------------------------------------------
 
 Alternatively, you may often see this sort of thing done with the
-lower-level command linkgit:git-rev-list[1], which just lists the SHA1's
+lower-level command linkgit:git-rev-list[1], which just lists the SHA-1's
 of all the given commits:
 
 -------------------------------------------------
@@ -1073,9 +1073,9 @@ $ git diff
 
 shows the difference between the working tree and the index file.
 
-Note that "git-add" always adds just the current contents of a file
+Note that "git add" always adds just the current contents of a file
 to the index; further changes to the same file will be ignored unless
-you run git-add on the file again.
+you run `git add` on the file again.
 
 When you're ready, just run
 
@@ -1136,10 +1136,10 @@ Ignoring files
 A project will often generate files that you do 'not' want to track with git.
 This typically includes files generated by a build process or temporary
 backup files made by your editor. Of course, 'not' tracking files with git
-is just a matter of 'not' calling "`git-add`" on them. But it quickly becomes
+is just a matter of 'not' calling `git add` on them. But it quickly becomes
 annoying to have these untracked files lying around; e.g. they make
-"`git add .`" practically useless, and they keep showing up in the output of
-"`git status`".
+`git add .` practically useless, and they keep showing up in the output of
+`git status`.
 
 You can tell git to ignore certain files by creating a file called .gitignore
 in the top level of your working directory, with contents such as:
@@ -1349,7 +1349,7 @@ $ git add file.txt
 -------------------------------------------------
 
 the different stages of that file will be "collapsed", after which
-git-diff will (by default) no longer show diffs for that file.
+`git diff` will (by default) no longer show diffs for that file.
 
 [[undoing-a-merge]]
 Undoing a merge
@@ -1446,7 +1446,7 @@ Fixing a mistake by rewriting history
 
 If the problematic commit is the most recent commit, and you have not
 yet made that commit public, then you may just
-<<undoing-a-merge,destroy it using git-reset>>.
+<<undoing-a-merge,destroy it using `git reset`>>.
 
 Alternatively, you
 can edit the working directory and update the index to fix your
@@ -1474,7 +1474,7 @@ Checking out an old version of a file
 
 In the process of undoing a previous bad change, you may find it
 useful to check out an older version of a particular file using
-linkgit:git-checkout[1].  We've used git-checkout before to switch
+linkgit:git-checkout[1].  We've used `git checkout` before to switch
 branches, but it has quite different behavior if it is given a path
 name: the command
 
@@ -1520,10 +1520,10 @@ $ git commit -a -m "blorpl: typofix"
 ------------------------------------------------
 
 After that, you can go back to what you were working on with
-`git stash apply`:
+`git stash pop`:
 
 ------------------------------------------------
-$ git stash apply
+$ git stash pop
 ------------------------------------------------
 
 
@@ -1542,7 +1542,7 @@ $ git gc
 -------------------------------------------------
 
 to recompress the archive.  This can be very time-consuming, so
-you may prefer to run git-gc when you are not doing other work.
+you may prefer to run `git gc` when you are not doing other work.
 
 
 [[ensuring-reliability]]
@@ -1634,7 +1634,7 @@ In some situations the reflog may not be able to save you.  For example,
 suppose you delete a branch, then realize you need the history it
 contained.  The reflog is also deleted; however, if you have not yet
 pruned the repository, then you may still be able to find the lost
-commits in the dangling objects that git-fsck reports.  See
+commits in the dangling objects that `git fsck` reports.  See
 <<dangling-objects>> for the details.
 
 -------------------------------------------------
@@ -1676,7 +1676,7 @@ Sharing development with others
 ===============================
 
 [[getting-updates-With-git-pull]]
-Getting updates with git-pull
+Getting updates with git pull
 -----------------------------
 
 After you clone a repository and make a few changes of your own, you
@@ -1722,7 +1722,7 @@ repository that you pulled from.
 <<fast-forwards,fast forward>>; instead, your branch will just be
 updated to point to the latest commit from the upstream branch.)
 
-The git-pull command can also be given "." as the "remote" repository,
+The `git pull` command can also be given "." as the "remote" repository,
 in which case it just merges in a branch from the current repository; so
 the commands
 
@@ -1795,7 +1795,7 @@ Public git repositories
 Another way to submit changes to a project is to tell the maintainer
 of that project to pull the changes from your repository using
 linkgit:git-pull[1].  In the section "<<getting-updates-With-git-pull,
-Getting updates with git-pull>>" we described this as a way to get
+Getting updates with `git pull`>>" we described this as a way to get
 updates from the "main" repository, but it works just as well in the
 other direction.
 
@@ -1847,7 +1847,7 @@ Setting up a public repository
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Assume your personal repository is in the directory ~/proj.  We
-first create a new clone of the repository and tell git-daemon that it
+first create a new clone of the repository and tell `git daemon` that it
 is meant to be public:
 
 -------------------------------------------------
@@ -1878,10 +1878,10 @@ repository>>", below.
 Otherwise, all you need to do is start linkgit:git-daemon[1]; it will
 listen on port 9418.  By default, it will allow access to any directory
 that looks like a git directory and contains the magic file
-git-daemon-export-ok.  Passing some directory paths as git-daemon
+git-daemon-export-ok.  Passing some directory paths as `git daemon`
 arguments will further restrict the exports to those paths.
 
-You can also run git-daemon as an inetd service; see the
+You can also run `git daemon` as an inetd service; see the
 linkgit:git-daemon[1] man page for details.  (See especially the
 examples section.)
 
@@ -1942,7 +1942,7 @@ or just
 $ git push ssh://yourserver.com/~you/proj.git master
 -------------------------------------------------
 
-As with git-fetch, git-push will complain if this does not result in a
+As with `git fetch`, `git push` will complain if this does not result in a
 <<fast-forwards,fast forward>>; see the following section for details on
 handling this case.
 
@@ -1952,7 +1952,7 @@ repository that has a checked-out working tree, but the working tree
 will not be updated by the push.  This may lead to unexpected results if
 the branch you push to is the currently checked-out branch!
 
-As with git-fetch, you may also set up configuration options to
+As with `git fetch`, you may also set up configuration options to
 save typing; so, for example, after
 
 -------------------------------------------------
@@ -1988,13 +1988,13 @@ error: failed to push to 'ssh://yourserver.com/~you/proj.git'
 
 This can happen, for example, if you:
 
-       - use `git-reset --hard` to remove already-published commits, or
-       - use `git-commit --amend` to replace already-published commits
+       - use `git reset --hard` to remove already-published commits, or
+       - use `git commit --amend` to replace already-published commits
          (as in <<fixing-a-mistake-by-rewriting-history>>), or
-       - use `git-rebase` to rebase any already-published commits (as
+       - use `git rebase` to rebase any already-published commits (as
          in <<using-git-rebase>>).
 
-You may force git-push to perform the update anyway by preceding the
+You may force `git push` to perform the update anyway by preceding the
 branch name with a plus sign:
 
 -------------------------------------------------
@@ -2036,7 +2036,7 @@ advantages over the central shared repository:
 
        - Git's ability to quickly import and merge patches allows a
          single maintainer to process incoming changes even at very
-         high rates.  And when that becomes too much, git-pull provides
+         high rates.  And when that becomes too much, `git pull` provides
          an easy way for that maintainer to delegate this job to other
          maintainers while still allowing optional review of incoming
          changes.
@@ -2404,7 +2404,7 @@ use them, and then explain some of the problems that can arise because
 you are rewriting history.
 
 [[using-git-rebase]]
-Keeping a patch series up to date using git-rebase
+Keeping a patch series up to date using git rebase
 --------------------------------------------------
 
 Suppose that you create a branch "mywork" on a remote-tracking branch
@@ -2468,9 +2468,9 @@ patches to the new mywork.  The result will look like:
 ................................................
 
 In the process, it may discover conflicts.  In that case it will stop
-and allow you to fix the conflicts; after fixing conflicts, use "git-add"
+and allow you to fix the conflicts; after fixing conflicts, use `git add`
 to update the index with those contents, and then, instead of
-running git-commit, just run
+running `git commit`, just run
 
 -------------------------------------------------
 $ git rebase --continue
@@ -2508,7 +2508,7 @@ with
 $ git tag bad mywork~5
 -------------------------------------------------
 
-(Either gitk or git-log may be useful for finding the commit.)
+(Either gitk or `git log` may be useful for finding the commit.)
 
 Then check out that commit, edit it, and rebase the rest of the series
 on top of it (note that we could check out the commit on a temporary
@@ -2549,12 +2549,12 @@ $ gitk origin..mywork &
 
 and browse through the list of patches in the mywork branch using gitk,
 applying them (possibly in a different order) to mywork-new using
-cherry-pick, and possibly modifying them as you go using `commit --amend`.
+cherry-pick, and possibly modifying them as you go using `git commit --amend`.
 The linkgit:git-gui[1] command may also help as it allows you to
 individually select diff hunks for inclusion in the index (by
 right-clicking on the diff hunk and choosing "Stage Hunk for Commit").
 
-Another technique is to use git-format-patch to create a series of
+Another technique is to use `git format-patch` to create a series of
 patches, then reset the state to before the patches:
 
 -------------------------------------------------
@@ -2662,7 +2662,7 @@ you know is that D is bad, that Z is good, and that
 linkgit:git-bisect[1] identifies C as the culprit, how will you
 figure out that the problem is due to this change in semantics?
 
-When the result of a git-bisect is a non-merge commit, you should
+When the result of a `git bisect` is a non-merge commit, you should
 normally be able to discover the problem by examining just that commit.
 Developers can make this easy by breaking their changes into small
 self-contained commits.  That won't help in the case above, however,
@@ -2725,7 +2725,7 @@ master branch.  In more detail:
 git fetch and fast-forwards
 ---------------------------
 
-In the previous example, when updating an existing branch, "git-fetch"
+In the previous example, when updating an existing branch, "git fetch"
 checks to make sure that the most recent commit on the remote
 branch is a descendant of the most recent commit on your copy of the
 branch before updating your copy of the branch to point at the new
@@ -2751,7 +2751,7 @@ resulting in a situation like:
             o--o--o <-- new head of the branch
 ................................................
 
-In this case, "git-fetch" will fail, and print out a warning.
+In this case, "git fetch" will fail, and print out a warning.
 
 In that case, you can still force git to update to the new head, as
 described in the following section.  However, note that in the
@@ -2760,7 +2760,7 @@ unless you've already created a reference of your own pointing to
 them.
 
 [[forcing-fetch]]
-Forcing git-fetch to do non-fast-forward updates
+Forcing git fetch to do non-fast-forward updates
 ------------------------------------------------
 
 If git fetch fails because the new head of a branch is not a
@@ -2865,8 +2865,8 @@ The Object Database
 We already saw in <<understanding-commits>> that all commits are stored
 under a 40-digit "object name".  In fact, all the information needed to
 represent the history of a project is stored in objects with such names.
-In each case the name is calculated by taking the SHA1 hash of the
-contents of the object.  The SHA1 hash is a cryptographic hash function.
+In each case the name is calculated by taking the SHA-1 hash of the
+contents of the object.  The SHA-1 hash is a cryptographic hash function.
 What that means to us is that it is impossible to find two different
 objects with the same name.  This has a number of advantages; among
 others:
@@ -2877,10 +2877,10 @@ others:
   same content stored in two repositories will always be stored under
   the same name.
 - Git can detect errors when it reads an object, by checking that the
-  object's name is still the SHA1 hash of its contents.
+  object's name is still the SHA-1 hash of its contents.
 
 (See <<object-details>> for the details of the object formatting and
-SHA1 calculation.)
+SHA-1 calculation.)
 
 There are four different types of objects: "blob", "tree", "commit", and
 "tag".
@@ -2926,9 +2926,9 @@ committer Junio C Hamano <gitster@pobox.com> 1187591163 -0700
 
 As you can see, a commit is defined by:
 
-- a tree: The SHA1 name of a tree object (as defined below), representing
+- a tree: The SHA-1 name of a tree object (as defined below), representing
   the contents of a directory at a certain point in time.
-- parent(s): The SHA1 name of some number of commits which represent the
+- parent(s): The SHA-1 name of some number of commits which represent the
   immediately previous step(s) in the history of the project.  The
   example above has one parent; merge commits may have more than
   one.  A commit with no parents is called a "root" commit, and
@@ -2977,13 +2977,13 @@ $ git ls-tree fb3a8bdd0ce
 ------------------------------------------------
 
 As you can see, a tree object contains a list of entries, each with a
-mode, object type, SHA1 name, and name, sorted by name.  It represents
+mode, object type, SHA-1 name, and name, sorted by name.  It represents
 the contents of a single directory tree.
 
 The object type may be a blob, representing the contents of a file, or
 another tree, representing the contents of a subdirectory.  Since trees
-and blobs, like all other objects, are named by the SHA1 hash of their
-contents, two trees have the same SHA1 name if and only if their
+and blobs, like all other objects, are named by the SHA-1 hash of their
+contents, two trees have the same SHA-1 name if and only if their
 contents (including, recursively, the contents of all subdirectories)
 are identical.  This allows git to quickly determine the differences
 between two related tree objects, since it can ignore any entries with
@@ -3029,15 +3029,15 @@ currently checked out.
 Trust
 ~~~~~
 
-If you receive the SHA1 name of a blob from one source, and its contents
+If you receive the SHA-1 name of a blob from one source, and its contents
 from another (possibly untrusted) source, you can still trust that those
-contents are correct as long as the SHA1 name agrees.  This is because
-the SHA1 is designed so that it is infeasible to find different contents
+contents are correct as long as the SHA-1 name agrees.  This is because
+the SHA-1 is designed so that it is infeasible to find different contents
 that produce the same hash.
 
-Similarly, you need only trust the SHA1 name of a top-level tree object
+Similarly, you need only trust the SHA-1 name of a top-level tree object
 to trust the contents of the entire directory that it refers to, and if
-you receive the SHA1 name of a commit from a trusted source, then you
+you receive the SHA-1 name of a commit from a trusted source, then you
 can easily verify the entire history of commits reachable through
 parents of that commit, and all of those contents of the trees referred
 to by those commits.
@@ -3049,7 +3049,7 @@ that you trust that commit, and the immutability of the history of
 commits tells others that they can trust the whole history.
 
 In other words, you can easily validate a whole archive by just
-sending out a single email that tells the people the name (SHA1 hash)
+sending out a single email that tells the people the name (SHA-1 hash)
 of the top commit, and digitally sign that email using something
 like GPG/PGP.
 
@@ -3090,7 +3090,7 @@ How git stores objects efficiently: pack files
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Newly created objects are initially created in a file named after the
-object's SHA1 hash (stored in .git/objects).
+object's SHA-1 hash (stored in .git/objects).
 
 Unfortunately this system becomes inefficient once a project has a
 lot of objects.  Try this on an old project:
@@ -3131,7 +3131,7 @@ $ git prune
 
 to remove any of the "loose" objects that are now contained in the
 pack.  This will also remove any unreferenced objects (which may be
-created when, for example, you use "git-reset" to remove a commit).
+created when, for example, you use "git reset" to remove a commit).
 You can verify that the loose objects are gone by looking at the
 .git/objects directory or by running
 
@@ -3160,7 +3160,7 @@ branch still exists, as does everything it pointed to. The branch
 pointer itself just doesn't, since you replaced it with another one.
 
 There are also other situations that cause dangling objects. For
-example, a "dangling blob" may arise because you did a "git-add" of a
+example, a "dangling blob" may arise because you did a "git add" of a
 file, but then, before you actually committed it and made it part of the
 bigger picture, you changed something else in that file and committed
 that *updated* thing--the old state that you added originally ends up
@@ -3210,7 +3210,7 @@ Usually, dangling blobs and trees aren't very interesting. They're
 almost always the result of either being a half-way mergebase (the blob
 will often even have the conflict markers from a merge in it, if you
 have had conflicting merges that you fixed up by hand), or simply
-because you interrupted a "git-fetch" with ^C or something like that,
+because you interrupted a "git fetch" with ^C or something like that,
 leaving _some_ of the new objects in the object database, but just
 dangling and useless.
 
@@ -3225,9 +3225,9 @@ and they'll be gone. But you should only run "git prune" on a quiescent
 repository--it's kind of like doing a filesystem fsck recovery: you
 don't want to do that while the filesystem is mounted.
 
-(The same is true of "git-fsck" itself, btw, but since
-git-fsck never actually *changes* the repository, it just reports
-on what it found, git-fsck itself is never "dangerous" to run.
+(The same is true of "git fsck" itself, btw, but since
+`git fsck` never actually *changes* the repository, it just reports
+on what it found, `git fsck` itself is never 'dangerous' to run.
 Running it while somebody is actually changing the repository can cause
 confusing and scary messages, but it won't actually do anything bad. In
 contrast, running "git prune" while somebody is actively changing the
@@ -3297,7 +3297,7 @@ $ git hash-object -w somedirectory/myfile
 ------------------------------------------------
 
 which will create and store a blob object with the contents of
-somedirectory/myfile, and output the sha1 of that object.  if you're
+somedirectory/myfile, and output the SHA-1 of that object.  if you're
 extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in
 which case you've guessed right, and the corruption is fixed!
 
@@ -3359,7 +3359,7 @@ The index
 -----------
 
 The index is a binary file (generally kept in .git/index) containing a
-sorted list of path names, each with permissions and the SHA1 of a blob
+sorted list of path names, each with permissions and the SHA-1 of a blob
 object; linkgit:git-ls-files[1] can show you the contents of the index:
 
 -------------------------------------------------
@@ -3489,14 +3489,14 @@ done
 
 NOTE: Do not use local URLs here if you plan to publish your superproject!
 
-See what files `git-submodule` created:
+See what files `git submodule` created:
 
 -------------------------------------------------
 $ ls -a
 .  ..  .git  .gitmodules  a  b  c  d
 -------------------------------------------------
 
-The `git-submodule add <repo> <path>` command does a couple of things:
+The `git submodule add <repo> <path>` command does a couple of things:
 
 - It clones the submodule from <repo> to the given <path> under the
   current directory and by default checks out the master branch.
@@ -3542,7 +3542,7 @@ init` to add the submodule repository URLs to `.git/config`:
 $ git submodule init
 -------------------------------------------------
 
-Now use `git-submodule update` to clone the repositories and check out the
+Now use `git submodule update` to clone the repositories and check out the
 commits specified in the superproject:
 
 -------------------------------------------------
@@ -3552,8 +3552,8 @@ $ ls -a
 .  ..  .git  a.txt
 -------------------------------------------------
 
-One major difference between `git-submodule update` and `git-submodule add` is
-that `git-submodule update` checks out a specific commit, rather than the tip
+One major difference between `git submodule update` and `git submodule add` is
+that `git submodule update` checks out a specific commit, rather than the tip
 of a branch. It's like checking out a tag: the head is detached, so you're not
 working on a branch.
 
@@ -3754,7 +3754,7 @@ unsaved state that you might want to restore later!) your current
 index.  Normal operation is just
 
 -------------------------------------------------
-$ git read-tree <sha1 of tree>
+$ git read-tree <SHA-1 of tree>
 -------------------------------------------------
 
 and your index file will now be equivalent to the tree that you saved
@@ -3769,7 +3769,7 @@ You update your working directory from the index by "checking out"
 files. This is not a very common operation, since normally you'd just
 keep your files updated, and rather than write to your working
 directory, you'd tell the index files about the changes in your
-working directory (i.e. `git-update-index`).
+working directory (i.e. `git update-index`).
 
 However, if you decide to jump to a new version, or check out somebody
 else's version, or just restore a previous tree, you'd populate your
@@ -3782,7 +3782,7 @@ $ git checkout-index filename
 
 or, if you want to check out all of the index, use `-a`.
 
-NOTE! git-checkout-index normally refuses to overwrite old files, so
+NOTE! `git checkout-index` normally refuses to overwrite old files, so
 if you have an old version of the tree already checked out, you will
 need to use the "-f" flag ('before' the "-a" flag or the filename) to
 'force' the checkout.
@@ -3820,7 +3820,7 @@ $ git commit-tree <tree> -p <parent> [-p <parent2> ..]
 and then giving the reason for the commit on stdin (either through
 redirection from a pipe or file, or by just typing it at the tty).
 
-git-commit-tree will return the name of the object that represents
+`git commit-tree` will return the name of the object that represents
 that commit, and you should save it away for later use. Normally,
 you'd commit a new `HEAD` state, and while git doesn't care where you
 save the note about that state, in practice we tend to just write the
@@ -3889,7 +3889,7 @@ $ git cat-file blob|tree|commit|tag <objectname>
 
 to show its contents. NOTE! Trees have binary content, and as a result
 there is a special helper for showing that content, called
-`git-ls-tree`, which turns the binary content into a more easily
+`git ls-tree`, which turns the binary content into a more easily
 readable form.
 
 It's especially instructive to look at "commit" objects, since those
@@ -3978,13 +3978,13 @@ $ git ls-files --unmerged
 ------------------------------------------------
 
 Each line of the `git ls-files --unmerged` output begins with
-the blob mode bits, blob SHA1, 'stage number', and the
+the blob mode bits, blob SHA-1, 'stage number', and the
 filename.  The 'stage number' is git's way to say which tree it
 came from: stage 1 corresponds to `$orig` tree, stage 2 `HEAD`
 tree, and stage3 `$target` tree.
 
 Earlier we said that trivial merges are done inside
-`git-read-tree -m`.  For example, if the file did not change
+`git read-tree -m`.  For example, if the file did not change
 from `$orig` to `HEAD` nor `$target`, or if the file changed
 from `$orig` to `HEAD` and `$orig` to `$target` the same way,
 obviously the final outcome is what is in `HEAD`.  What the
@@ -4011,20 +4011,20 @@ $ mv -f hello.c~2 hello.c
 $ git update-index hello.c
 -------------------------------------------------
 
-When a path is in the "unmerged" state, running `git-update-index` for
+When a path is in the "unmerged" state, running `git update-index` for
 that path tells git to mark the path resolved.
 
 The above is the description of a git merge at the lowest level,
 to help you understand what conceptually happens under the hood.
-In practice, nobody, not even git itself, runs `git-cat-file` three times
-for this.  There is a `git-merge-index` program that extracts the
+In practice, nobody, not even git itself, runs `git cat-file` three times
+for this.  There is a `git merge-index` program that extracts the
 stages to temporary files and calls a "merge" script on it:
 
 -------------------------------------------------
 $ git merge-index git-merge-one-file hello.c
 -------------------------------------------------
 
-and that is what higher level `git-merge -s resolve` is implemented with.
+and that is what higher level `git merge -s resolve` is implemented with.
 
 [[hacking-git]]
 Hacking git
@@ -4045,12 +4045,12 @@ objects).  There are currently four different object types: "blob",
 Regardless of object type, all objects share the following
 characteristics: they are all deflated with zlib, and have a header
 that not only specifies their type, but also provides size information
-about the data in the object.  It's worth noting that the SHA1 hash
+about the data in the object.  It's worth noting that the SHA-1 hash
 that is used to name the object is the hash of the original data
 plus this header, so `sha1sum` 'file' does not match the object name
 for 'file'.
 (Historical note: in the dawn of the age of git the hash
-was the sha1 of the 'compressed' object.)
+was the SHA-1 of the 'compressed' object.)
 
 As a result, the general consistency of an object can always be tested
 independently of the contents or the type of the object: all objects can
@@ -4061,7 +4061,7 @@ size> {plus} <byte\0> {plus} <binary object data>.
 
 The structured objects can further have their structure and
 connectivity to other objects verified. This is generally done with
-the `git-fsck` program, which generates a full dependency graph
+the `git fsck` program, which generates a full dependency graph
 of all objects, and verifies their internal consistency (in addition
 to just verifying their superficial consistency through the hash).
 
@@ -4120,7 +4120,7 @@ functions like `get_sha1_basic()` or the likes.
 This is just to get you into the groove for the most libified part of Git:
 the revision walker.
 
-Basically, the initial version of `git-log` was a shell script:
+Basically, the initial version of `git log` was a shell script:
 
 ----------------------------------------------------------------
 $ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \
@@ -4129,20 +4129,20 @@ $ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \
 
 What does this mean?
 
-`git-rev-list` is the original version of the revision walker, which
+`git rev-list` is the original version of the revision walker, which
 _always_ printed a list of revisions to stdout.  It is still functional,
-and needs to, since most new Git programs start out as scripts using
-`git-rev-list`.
+and needs to, since most new Git commands start out as scripts using
+`git rev-list`.
 
-`git-rev-parse` is not as important any more; it was only used to filter out
+`git rev-parse` is not as important any more; it was only used to filter out
 options that were relevant for the different plumbing commands that were
 called by the script.
 
-Most of what `git-rev-list` did is contained in `revision.c` and
+Most of what `git rev-list` did is contained in `revision.c` and
 `revision.h`.  It wraps the options in a struct named `rev_info`, which
 controls how and what revisions are walked, and more.
 
-The original job of `git-rev-parse` is now taken by the function
+The original job of `git rev-parse` is now taken by the function
 `setup_revisions()`, which parses the revisions and the common command line
 options for the revision walker. This information is stored in the struct
 `rev_info` for later consumption. You can do your own command line option
@@ -4155,7 +4155,7 @@ just have a look at the first implementation of `cmd_log()`; call
 `git show v1.3.0{tilde}155^2{tilde}4` and scroll down to that function (note that you
 no longer need to call `setup_pager()` directly).
 
-Nowadays, `git-log` is a builtin, which means that it is _contained_ in the
+Nowadays, `git log` is a builtin, which means that it is _contained_ in the
 command `git`.  The source side of a builtin is
 
 - a function called `cmd_<bla>`, typically defined in `builtin-<bla>.c`,
@@ -4171,7 +4171,7 @@ since they share quite a bit of code.  In that case, the commands which are
 _not_ named like the `.c` file in which they live have to be listed in
 `BUILT_INS` in the `Makefile`.
 
-`git-log` looks more complicated in C than it does in the original script,
+`git log` looks more complicated in C than it does in the original script,
 but that allows for a much greater flexibility and performance.
 
 Here again it is a good point to take a pause.
@@ -4182,9 +4182,9 @@ the organization of Git (after you know the basic concepts).
 So, think about something which you are interested in, say, "how can I
 access a blob just knowing the object name of it?".  The first step is to
 find a Git command with which you can do it.  In this example, it is either
-`git-show` or `git-cat-file`.
+`git show` or `git cat-file`.
 
-For the sake of clarity, let's stay with `git-cat-file`, because it
+For the sake of clarity, let's stay with `git cat-file`, because it
 
 - is plumbing, and
 
@@ -4198,7 +4198,7 @@ it does.
 ------------------------------------------------------------------
         git_config(git_default_config);
         if (argc != 3)
-                usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>");
+               usage("git cat-file [-t|-s|-e|-p|<type>] <sha1>");
         if (get_sha1(argv[2], sha1))
                 die("Not a valid object name %s", argv[2]);
 ------------------------------------------------------------------
@@ -4243,10 +4243,10 @@ To find out how the result can be used, just read on in `cmd_cat_file()`:
 -----------------------------------
 
 Sometimes, you do not know where to look for a feature.  In many such cases,
-it helps to search through the output of `git log`, and then `git-show` the
+it helps to search through the output of `git log`, and then `git show` the
 corresponding commit.
 
-Example: If you know that there was some test case for `git-bundle`, but
+Example: If you know that there was some test case for `git bundle`, but
 do not remember where it was (yes, you _could_ `git grep bundle t/`, but that
 does not illustrate the point!):
 
@@ -4530,7 +4530,7 @@ The basic requirements:
 - Whenever possible, section headings should clearly describe the task
   they explain how to do, in language that requires no more knowledge
   than necessary: for example, "importing patches into a project" rather
-  than "the git-am command"
+  than "the `git am` command"
 
 Think about how to create a clear chapter dependency graph that will
 allow people to get to important topics without necessarily reading
index 9d65aed4177f610d9e03f4087ee3ed847c58194e..a56dcb26e7f344effe59ecbbeb46f7081bbccdc4 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.2.5
+DEF_VER=v1.6.3.4
 
 LF='
 '
index a23e27013999ce900913456ff980af2103c7974f..f88ed3e14d2421fdd730705d2aac0c317d6f94e0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -126,6 +126,12 @@ all::
 # randomly break unless your underlying filesystem supports those sub-second
 # times (my ext3 doesn't).
 #
+# Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
+# "st_ctim"
+#
+# Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec"
+# available.  This automatically turns USE_NSEC off.
+#
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-index perspective.
 #
@@ -139,6 +145,8 @@ all::
 # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
 # MakeMaker (e.g. using ActiveState under Cygwin).
 #
+# Define NO_PERL if you do not want Perl scripts or libraries at all.
+#
 # Define NO_TCLTK if you do not want Tcl/Tk GUI.
 #
 # The TCL_PATH variable governs the location of the Tcl interpreter
@@ -159,6 +167,17 @@ all::
 # Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call
 # your external grep (e.g., if your system lacks grep, if its grep is
 # broken, or spawning external process is slower than built-in grep git has).
+#
+# Define UNRELIABLE_FSTAT if your system's fstat does not return the same
+# information on a not yet closed file that lstat would return for the same
+# file after it was closed.
+#
+# Define OBJECT_CREATION_USES_RENAMES if your operating systems has problems
+# when hardlinking a file to another name and unlinking the original file right
+# away (some NTFS drivers seem to zero the contents in that scenario).
+#
+# Define NO_CROSS_DIRECTORY_HARDLINKS if you plan to distribute the installed
+# programs as a tar, where bin/ and libexec/ might be on different file systems.
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -257,14 +276,28 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
 BASIC_CFLAGS =
 BASIC_LDFLAGS =
 
+# Guard against environment variables
+BUILTIN_OBJS =
+BUILT_INS =
+COMPAT_CFLAGS =
+COMPAT_OBJS =
+LIB_H =
+LIB_OBJS =
+PROGRAMS =
+SCRIPT_PERL =
+SCRIPT_SH =
+TEST_PROGRAMS =
+
 SCRIPT_SH += git-am.sh
 SCRIPT_SH += git-bisect.sh
+SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
 SCRIPT_SH += git-lost-found.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
+SCRIPT_SH += git-mergetool--lib.sh
 SCRIPT_SH += git-parse-remote.sh
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
@@ -278,6 +311,7 @@ SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
 
 SCRIPT_PERL += git-add--interactive.perl
+SCRIPT_PERL += git-difftool.perl
 SCRIPT_PERL += git-archimport.perl
 SCRIPT_PERL += git-cvsexportcommit.perl
 SCRIPT_PERL += git-cvsimport.perl
@@ -333,7 +367,7 @@ BUILT_INS += git-whatchanged$X
 ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
 
 # what 'all' will build but not install in gitexecdir
-OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
+OTHER_PROGRAMS = git$X
 
 # Set paths to tools early so that they can be used for version tests.
 ifndef SHELL_PATH
@@ -412,6 +446,7 @@ LIB_OBJS += archive-tar.o
 LIB_OBJS += archive-zip.o
 LIB_OBJS += attr.o
 LIB_OBJS += base85.o
+LIB_OBJS += bisect.o
 LIB_OBJS += blob.o
 LIB_OBJS += branch.o
 LIB_OBJS += bundle.o
@@ -512,6 +547,7 @@ BUILTIN_OBJS += builtin-add.o
 BUILTIN_OBJS += builtin-annotate.o
 BUILTIN_OBJS += builtin-apply.o
 BUILTIN_OBJS += builtin-archive.o
+BUILTIN_OBJS += builtin-bisect--helper.o
 BUILTIN_OBJS += builtin-blame.o
 BUILTIN_OBJS += builtin-branch.o
 BUILTIN_OBJS += builtin-bundle.o
@@ -655,6 +691,7 @@ ifeq ($(uname_S),Darwin)
        endif
        NO_MEMMEM = YesPlease
        THREADED_DELTA_SEARCH = YesPlease
+       USE_ST_TIMESPEC = YesPlease
 endif
 ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
@@ -665,6 +702,7 @@ ifeq ($(uname_S),SunOS)
        NO_HSTRERROR = YesPlease
        NO_MKDTEMP = YesPlease
        OLD_ICONV = UnfortunatelyYes
+       NO_EXTERNAL_GREP = YesPlease
        ifeq ($(uname_R),5.8)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
@@ -704,6 +742,7 @@ ifeq ($(uname_S),FreeBSD)
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
        DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
+       USE_ST_TIMESPEC = YesPlease
        THREADED_DELTA_SEARCH = YesPlease
        ifeq ($(shell expr "$(uname_R)" : '4\.'),2)
                PTHREAD_LIBS = -pthread
@@ -714,6 +753,7 @@ endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
        NO_MEMMEM = YesPlease
+       USE_ST_TIMESPEC = YesPlease
        NEEDS_LIBICONV = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
@@ -726,12 +766,14 @@ ifeq ($(uname_S),NetBSD)
        BASIC_CFLAGS += -I/usr/pkg/include
        BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
        THREADED_DELTA_SEARCH = YesPlease
+       USE_ST_TIMESPEC = YesPlease
 endif
 ifeq ($(uname_S),AIX)
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
        NO_MKDTEMP = YesPlease
        NO_STRLCPY = YesPlease
+       NO_NSEC = YesPlease
        FREAD_READS_DIRECTORIES = UnfortunatelyYes
        INTERNAL_QSORT = UnfortunatelyYes
        NEEDS_LIBICONV=YesPlease
@@ -772,9 +814,9 @@ ifeq ($(uname_S),HP-UX)
 endif
 ifneq (,$(findstring CYGWIN,$(uname_S)))
        COMPAT_OBJS += compat/cygwin.o
+       UNRELIABLE_FSTAT = UnfortunatelyYes
 endif
 ifneq (,$(findstring MINGW,$(uname_S)))
-       NO_MMAP = YesPlease
        NO_PREAD = YesPlease
        NO_OPENSSL = YesPlease
        NO_CURL = YesPlease
@@ -797,6 +839,10 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        RUNTIME_PREFIX = YesPlease
        NO_POSIX_ONLY_PROGRAMS = YesPlease
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
+       NO_NSEC = YesPlease
+       USE_WIN32_MMAP = YesPlease
+       UNRELIABLE_FSTAT = UnfortunatelyYes
+       OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
        COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
@@ -918,6 +964,15 @@ endif
 ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
        BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
 endif
+ifdef USE_NSEC
+       BASIC_CFLAGS += -DUSE_NSEC
+endif
+ifdef USE_ST_TIMESPEC
+       BASIC_CFLAGS += -DUSE_ST_TIMESPEC
+endif
+ifdef NO_NSEC
+       BASIC_CFLAGS += -DNO_NSEC
+endif
 ifdef NO_C99_FORMAT
        BASIC_CFLAGS += -DNO_C99_FORMAT
 endif
@@ -965,6 +1020,14 @@ endif
 ifdef NO_MMAP
        COMPAT_CFLAGS += -DNO_MMAP
        COMPAT_OBJS += compat/mmap.o
+else
+       ifdef USE_WIN32_MMAP
+               COMPAT_CFLAGS += -DUSE_WIN32_MMAP
+               COMPAT_OBJS += compat/win32mmap.o
+       endif
+endif
+ifdef OBJECT_CREATION_USES_RENAMES
+       COMPAT_CFLAGS += -DOBJECT_CREATION_MODE=1
 endif
 ifdef NO_PREAD
        COMPAT_CFLAGS += -DNO_PREAD
@@ -1061,11 +1124,18 @@ endif
 ifdef NO_EXTERNAL_GREP
        BASIC_CFLAGS += -DNO_EXTERNAL_GREP
 endif
+ifdef UNRELIABLE_FSTAT
+       BASIC_CFLAGS += -DUNRELIABLE_FSTAT
+endif
 
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
 
+ifeq ($(PERL_PATH),)
+NO_PERL=NoThanks
+endif
+
 QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
 QUIET_SUBDIR1  =
 
@@ -1140,7 +1210,9 @@ ifndef NO_TCLTK
        $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all
        $(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
 endif
+ifndef NO_PERL
        $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
+endif
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
 
 please_set_SHELL_PATH_to_a_more_modern_shell:
@@ -1153,6 +1225,7 @@ strip: $(PROGRAMS) git$X
 
 git.o: git.c common-cmds.h GIT-CFLAGS
        $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
+               '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
                $(ALL_CFLAGS) -c $(filter %.c,$^)
 
 git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
@@ -1187,6 +1260,7 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
        chmod +x $@+ && \
        mv $@+ $@
 
+ifndef NO_PERL
 $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
 
 perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL
@@ -1208,6 +1282,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
        chmod +x $@+ && \
        mv $@+ $@
 
+OTHER_PROGRAMS += gitweb/gitweb.cgi
 gitweb/gitweb.cgi: gitweb/gitweb.perl
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
@@ -1246,6 +1321,15 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
            $@.sh > $@+ && \
        chmod +x $@+ && \
        mv $@+ $@
+else # NO_PERL
+$(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
+       $(QUIET_GEN)$(RM) $@ $@+ && \
+       sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+           -e 's|@@REASON@@|NO_PERL=$(NO_PERL)|g' \
+           unimplemented.sh >$@+ && \
+       chmod +x $@+ && \
+       mv $@+ $@
+endif # NO_PERL
 
 configure: configure.ac
        $(QUIET_GEN)$(RM) $@ $<+ && \
@@ -1302,7 +1386,7 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
 $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
-$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
+$(patsubst git-%$X,%.o,$(PROGRAMS)) git.o: $(LIB_H) $(wildcard */*.h)
 builtin-revert.o wt-status.o: wt-status.h
 
 $(LIB_FILE): $(LIB_OBJS)
@@ -1361,6 +1445,8 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
 GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
+       @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
+       @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
 
 ### Detect Tck/Tk interpreter path changes
 ifndef NO_TCLTK
@@ -1455,17 +1541,20 @@ install: all
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X git-cvsserver '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
+ifndef NO_PERL
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
+endif
 ifndef NO_TCLTK
        $(MAKE) -C gitk-git install
        $(MAKE) -C git-gui gitexecdir='$(gitexec_instdir_SQ)' install
 endif
 ifneq (,$X)
-       $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';)
+       $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p' -ef '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p$X' || $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';)
 endif
        bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
        execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
        { $(RM) "$$execdir/git-add$X" && \
+               test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
                ln "$$bindir/git$X" "$$execdir/git-add$X" 2>/dev/null || \
                cp "$$bindir/git$X" "$$execdir/git-add$X"; } && \
        { for p in $(filter-out git-add$X,$(BUILT_INS)); do \
@@ -1473,7 +1562,7 @@ endif
                ln "$$execdir/git-add$X" "$$execdir/$$p" 2>/dev/null || \
                ln -s "git-add$X" "$$execdir/$$p" 2>/dev/null || \
                cp "$$execdir/git-add$X" "$$execdir/$$p" || exit; \
-         done } && \
+         done; } && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
 install-doc:
@@ -1563,9 +1652,11 @@ clean:
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
        $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
-       $(RM) gitweb/gitweb.cgi
        $(MAKE) -C Documentation/ clean
+ifndef NO_PERL
+       $(RM) gitweb/gitweb.cgi
        $(MAKE) -C perl clean
+endif
        $(MAKE) -C templates/ clean
        $(MAKE) -C t/ clean
 ifndef NO_TCLTK
@@ -1638,3 +1729,27 @@ check-docs::
 check-builtins::
        ./check-builtins.sh
 
+### Test suite coverage testing
+#
+.PHONY: coverage coverage-clean coverage-build coverage-report
+
+coverage:
+       $(MAKE) coverage-build
+       $(MAKE) coverage-report
+
+coverage-clean:
+       rm -f *.gcda *.gcno
+
+COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs
+COVERAGE_LDFLAGS = $(CFLAGS)  -O0 -lgcov
+
+coverage-build: coverage-clean
+       $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all
+       $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \
+               -j1 test
+
+coverage-report:
+       gcov -b *.c
+       grep '^function.*called 0 ' *.c.gcov \
+               | sed -e 's/\([^:]*\)\.gcov: *function \([^ ]*\) called.*/\1: \2/' \
+               | tee coverage-untested-functions
index c02134157a066664882768c69449919fb896bc68..03c996d22ac5e007157998aba981ab113cf1ad7c 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.2.5.txt
\ No newline at end of file
+Documentation/RelNotes-1.6.3.4.txt
\ No newline at end of file
diff --git a/alias.c b/alias.c
index ccb1108c94436035d0da8b1d6f08f859b68294a3..372b7d809301f9e3e936459e405cd6f2627bd4a9 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -27,7 +27,7 @@ int split_cmdline(char *cmdline, const char ***argv)
        int src, dst, count = 0, size = 16;
        char quoted = 0;
 
-       *argv = xmalloc(sizeof(char*) * size);
+       *argv = xmalloc(sizeof(char *) * size);
 
        /* split alias_string */
        (*argv)[count++] = cmdline;
@@ -38,10 +38,7 @@ int split_cmdline(char *cmdline, const char ***argv)
                        while (cmdline[++src]
                                        && isspace(cmdline[src]))
                                ; /* skip */
-                       if (count >= size) {
-                               size += 16;
-                               *argv = xrealloc(*argv, sizeof(char*) * size);
-                       }
+                       ALLOC_GROW(*argv, count+1, size);
                        (*argv)[count++] = cmdline + dst;
                } else if (!quoted && (c == '\'' || c == '"')) {
                        quoted = c;
@@ -72,6 +69,9 @@ int split_cmdline(char *cmdline, const char ***argv)
                return error("unclosed quote");
        }
 
+       ALLOC_GROW(*argv, count+1, size);
+       (*argv)[count] = NULL;
+
        return count;
 }
 
diff --git a/alloc.c b/alloc.c
index 216c23a6f854c614d38c743cd7687a37f304161b..6ef6753d180afad29bc335854150c824b9de8a18 100644 (file)
--- a/alloc.c
+++ b/alloc.c
@@ -57,7 +57,7 @@ DEFINE_ALLOCATOR(object, union any_object)
 #define SZ_FMT "%zu"
 #endif
 
-static void report(const charname, unsigned int count, size_t size)
+static void report(const char *name, unsigned int count, size_t size)
 {
     fprintf(stderr, "%10s: %8u (" SZ_FMT " kB)\n", name, count, size);
 }
index ba890ebdecd8672aeb32757605c8a2976fa21161..cee06ce3cbc1819008337633ed0753638c423ac5 100644 (file)
@@ -180,7 +180,7 @@ static int write_tar_entry(struct archiver_args *args,
 
        sprintf(header.mode, "%07o", mode & 07777);
        sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
-       sprintf(header.mtime, "%011lo", args->time);
+       sprintf(header.mtime, "%011lo", (unsigned long) args->time);
 
        sprintf(header.uid, "%07o", 0);
        sprintf(header.gid, "%07o", 0);
index e6de0397cc82ae97018cbf4fc82d5697ec4d915d..b2b90d31700d3aa50a3ac0626c1a8561c37c49b7 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -4,6 +4,7 @@
 #include "attr.h"
 #include "archive.h"
 #include "parse-options.h"
+#include "unpack-trees.h"
 
 static char const * const archive_usage[] = {
        "git archive [options] <tree-ish> [path...]",
@@ -150,6 +151,8 @@ int write_archive_entries(struct archiver_args *args,
                write_archive_entry_fn_t write_entry)
 {
        struct archiver_context context;
+       struct unpack_trees_options opts;
+       struct tree_desc t;
        int err;
 
        if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
@@ -168,6 +171,22 @@ int write_archive_entries(struct archiver_args *args,
        context.args = args;
        context.write_entry = write_entry;
 
+       /*
+        * Setup index and instruct attr to read index only
+        */
+       if (!args->worktree_attributes) {
+               memset(&opts, 0, sizeof(opts));
+               opts.index_only = 1;
+               opts.head_idx = -1;
+               opts.src_index = &the_index;
+               opts.dst_index = &the_index;
+               opts.fn = oneway_merge;
+               init_tree_desc(&t, args->tree->buffer, args->tree->size);
+               if (unpack_trees(1, &t, &opts))
+                       return -1;
+               git_attr_set_direction(GIT_ATTR_INDEX, &the_index);
+       }
+
        err =  read_tree_recursive(args->tree, args->base, args->baselen, 0,
                        args->pathspec, write_archive_entry, &context);
        if (err == READ_TREE_RECURSIVE)
@@ -253,15 +272,21 @@ static int parse_archive_args(int argc, const char **argv,
        const char *base = NULL;
        const char *remote = NULL;
        const char *exec = NULL;
+       const char *output = NULL;
        int compression_level = -1;
        int verbose = 0;
        int i;
        int list = 0;
+       int worktree_attributes = 0;
        struct option opts[] = {
                OPT_GROUP(""),
                OPT_STRING(0, "format", &format, "fmt", "archive format"),
                OPT_STRING(0, "prefix", &base, "prefix",
                        "prepend prefix to each pathname in the archive"),
+               OPT_STRING(0, "output", &output, "file",
+                       "write the archive to this file"),
+               OPT_BOOLEAN(0, "worktree-attributes", &worktree_attributes,
+                       "read .gitattributes in working directory"),
                OPT__VERBOSE(&verbose),
                OPT__COMPR('0', &compression_level, "store only", 0),
                OPT__COMPR('1', &compression_level, "compress faster", 1),
@@ -290,6 +315,8 @@ static int parse_archive_args(int argc, const char **argv,
                die("Unexpected option --remote");
        if (exec)
                die("Option --exec can only be used together with --remote");
+       if (output)
+               die("Unexpected option --output");
 
        if (!base)
                base = "";
@@ -319,6 +346,7 @@ static int parse_archive_args(int argc, const char **argv,
        args->verbose = verbose;
        args->base = base;
        args->baselen = strlen(base);
+       args->worktree_attributes = worktree_attributes;
 
        return argc;
 }
index 0b15b35143fffcc13764e4e668ee452b191cc609..038ac353d40567029403e3e7a2933af73a130720 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -10,6 +10,7 @@ struct archiver_args {
        time_t time;
        const char **pathspec;
        unsigned int verbose : 1;
+       unsigned int worktree_attributes : 1;
        int compression_level;
 };
 
diff --git a/attr.c b/attr.c
index 17f6a4dca521d9690377f2e93a0192d8a874d2ad..55bdb7cdebea7f7ea551231fe7801f272d128d69 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -1,3 +1,4 @@
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
 #include "attr.h"
 
@@ -34,8 +35,7 @@ static struct git_attr *(git_attr_hash[HASHSIZE]);
 
 static unsigned hash_name(const char *name, int namelen)
 {
-       unsigned val = 0;
-       unsigned char c;
+       unsigned val = 0, c;
 
        while (namelen--) {
                c = *name++;
@@ -223,7 +223,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
                if (is_macro)
                        res->u.attr = git_attr(name, namelen);
                else {
-                       res->u.pattern = (char*)&(res->state[num_attr]);
+                       res->u.pattern = (char *)&(res->state[num_attr]);
                        memcpy(res->u.pattern, name, namelen);
                        res->u.pattern[namelen] = 0;
                }
@@ -274,7 +274,7 @@ static void free_attr_elem(struct attr_stack *e)
                            setto == ATTR__UNKNOWN)
                                ;
                        else
-                               free((char*) setto);
+                               free((char *) setto);
                }
                free(a);
        }
@@ -318,6 +318,9 @@ static struct attr_stack *read_attr_from_array(const char **list)
        return res;
 }
 
+static enum git_attr_direction direction;
+static struct index_state *use_index;
+
 static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
 {
        FILE *fp = fopen(path, "r");
@@ -340,9 +343,10 @@ static void *read_index_data(const char *path)
        unsigned long sz;
        enum object_type type;
        void *data;
+       struct index_state *istate = use_index ? use_index : &the_index;
 
        len = strlen(path);
-       pos = cache_name_pos(path, len);
+       pos = index_name_pos(istate, path, len);
        if (pos < 0) {
                /*
                 * We might be in the middle of a merge, in which
@@ -350,15 +354,15 @@ static void *read_index_data(const char *path)
                 */
                int i;
                for (i = -pos - 1;
-                    (pos < 0 && i < active_nr &&
-                     !strcmp(active_cache[i]->name, path));
+                    (pos < 0 && i < istate->cache_nr &&
+                     !strcmp(istate->cache[i]->name, path));
                     i++)
-                       if (ce_stage(active_cache[i]) == 2)
+                       if (ce_stage(istate->cache[i]) == 2)
                                pos = i;
        }
        if (pos < 0)
                return NULL;
-       data = read_sha1_file(active_cache[pos]->sha1, &type, &sz);
+       data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
        if (!data || type != OBJ_BLOB) {
                free(data);
                return NULL;
@@ -366,27 +370,17 @@ static void *read_index_data(const char *path)
        return data;
 }
 
-static struct attr_stack *read_attr(const char *path, int macro_ok)
+static struct attr_stack *read_attr_from_index(const char *path, int macro_ok)
 {
        struct attr_stack *res;
        char *buf, *sp;
        int lineno = 0;
 
-       res = read_attr_from_file(path, macro_ok);
-       if (res)
-               return res;
-
-       res = xcalloc(1, sizeof(*res));
-
-       /*
-        * There is no checked out .gitattributes file there, but
-        * we might have it in the index.  We allow operation in a
-        * sparsely checked out work tree, so read from it.
-        */
        buf = read_index_data(path);
        if (!buf)
-               return res;
+               return NULL;
 
+       res = xcalloc(1, sizeof(*res));
        for (sp = buf; *sp; ) {
                char *ep;
                int more;
@@ -401,6 +395,32 @@ static struct attr_stack *read_attr(const char *path, int macro_ok)
        return res;
 }
 
+static struct attr_stack *read_attr(const char *path, int macro_ok)
+{
+       struct attr_stack *res;
+
+       if (direction == GIT_ATTR_CHECKOUT) {
+               res = read_attr_from_index(path, macro_ok);
+               if (!res)
+                       res = read_attr_from_file(path, macro_ok);
+       }
+       else if (direction == GIT_ATTR_CHECKIN) {
+               res = read_attr_from_file(path, macro_ok);
+               if (!res)
+                       /*
+                        * There is no checked out .gitattributes file there, but
+                        * we might have it in the index.  We allow operation in a
+                        * sparsely checked out work tree, so read from it.
+                        */
+                       res = read_attr_from_index(path, macro_ok);
+       }
+       else
+               res = read_attr_from_index(path, macro_ok);
+       if (!res)
+               res = xcalloc(1, sizeof(*res));
+       return res;
+}
+
 #if DEBUG_ATTR
 static void debug_info(const char *what, struct attr_stack *elem)
 {
@@ -428,6 +448,15 @@ static void debug_set(const char *what, const char *match, struct git_attr *attr
 #define debug_set(a,b,c,d) do { ; } while (0)
 #endif
 
+static void drop_attr_stack(void)
+{
+       while (attr_stack) {
+               struct attr_stack *elem = attr_stack;
+               attr_stack = elem->prev;
+               free_attr_elem(elem);
+       }
+}
+
 static void bootstrap_attr_stack(void)
 {
        if (!attr_stack) {
@@ -438,7 +467,7 @@ static void bootstrap_attr_stack(void)
                elem->prev = attr_stack;
                attr_stack = elem;
 
-               if (!is_bare_repository()) {
+               if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
                        elem = read_attr(GITATTRIBUTES_FILE, 1);
                        elem->origin = strdup("");
                        elem->prev = attr_stack;
@@ -505,7 +534,7 @@ static void prepare_attr_stack(const char *path, int dirlen)
        /*
         * Read from parent directories and push them down
         */
-       if (!is_bare_repository()) {
+       if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
                while (1) {
                        char *cp;
 
@@ -526,6 +555,8 @@ static void prepare_attr_stack(const char *path, int dirlen)
                }
        }
 
+       strbuf_release(&pathbuf);
+
        /*
         * Finally push the "info" one at the top of the stack.
         */
@@ -642,3 +673,16 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check)
 
        return 0;
 }
+
+void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate)
+{
+       enum git_attr_direction old = direction;
+
+       if (is_bare_repository() && new != GIT_ATTR_INDEX)
+               die("BUG: non-INDEX attr direction in a bare repo");
+
+       direction = new;
+       if (new != old)
+               drop_attr_stack();
+       use_index = istate;
+}
diff --git a/attr.h b/attr.h
index f1c2038b0923d3130937eef965667204a8634e6d..69b5767ebc2189a8bf9d98ff88c1885ec8fcdb7d 100644 (file)
--- a/attr.h
+++ b/attr.h
@@ -31,4 +31,11 @@ struct git_attr_check {
 
 int git_checkattr(const char *path, int, struct git_attr_check *);
 
+enum git_attr_direction {
+       GIT_ATTR_CHECKIN,
+       GIT_ATTR_CHECKOUT,
+       GIT_ATTR_INDEX,
+};
+void git_attr_set_direction(enum git_attr_direction, struct index_state *);
+
 #endif /* ATTR_H */
index b88270f90844095b3d352cc4213cbebd95a7f420..b417a15bbc83fff7180078a4cf9f73603477a295 100644 (file)
--- a/base85.c
+++ b/base85.c
@@ -91,7 +91,7 @@ void encode_85(char *buf, const unsigned char *data, int bytes)
                unsigned acc = 0;
                int cnt;
                for (cnt = 24; cnt >= 0; cnt -= 8) {
-                       int ch = *data++;
+                       unsigned ch = *data++;
                        acc |= ch << cnt;
                        if (--bytes == 0)
                                break;
diff --git a/bisect.c b/bisect.c
new file mode 100644 (file)
index 0000000..58f7e6f
--- /dev/null
+++ b/bisect.c
@@ -0,0 +1,556 @@
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "refs.h"
+#include "list-objects.h"
+#include "quote.h"
+#include "sha1-lookup.h"
+#include "bisect.h"
+
+static unsigned char (*skipped_sha1)[20];
+static int skipped_sha1_nr;
+static int skipped_sha1_alloc;
+
+static const char **rev_argv;
+static int rev_argv_nr;
+static int rev_argv_alloc;
+
+/* bits #0-15 in revision.h */
+
+#define COUNTED                (1u<<16)
+
+/*
+ * This is a truly stupid algorithm, but it's only
+ * used for bisection, and we just don't care enough.
+ *
+ * We care just barely enough to avoid recursing for
+ * non-merge entries.
+ */
+static int count_distance(struct commit_list *entry)
+{
+       int nr = 0;
+
+       while (entry) {
+               struct commit *commit = entry->item;
+               struct commit_list *p;
+
+               if (commit->object.flags & (UNINTERESTING | COUNTED))
+                       break;
+               if (!(commit->object.flags & TREESAME))
+                       nr++;
+               commit->object.flags |= COUNTED;
+               p = commit->parents;
+               entry = p;
+               if (p) {
+                       p = p->next;
+                       while (p) {
+                               nr += count_distance(p);
+                               p = p->next;
+                       }
+               }
+       }
+
+       return nr;
+}
+
+static void clear_distance(struct commit_list *list)
+{
+       while (list) {
+               struct commit *commit = list->item;
+               commit->object.flags &= ~COUNTED;
+               list = list->next;
+       }
+}
+
+#define DEBUG_BISECT 0
+
+static inline int weight(struct commit_list *elem)
+{
+       return *((int*)(elem->item->util));
+}
+
+static inline void weight_set(struct commit_list *elem, int weight)
+{
+       *((int*)(elem->item->util)) = weight;
+}
+
+static int count_interesting_parents(struct commit *commit)
+{
+       struct commit_list *p;
+       int count;
+
+       for (count = 0, p = commit->parents; p; p = p->next) {
+               if (p->item->object.flags & UNINTERESTING)
+                       continue;
+               count++;
+       }
+       return count;
+}
+
+static inline int halfway(struct commit_list *p, int nr)
+{
+       /*
+        * Don't short-cut something we are not going to return!
+        */
+       if (p->item->object.flags & TREESAME)
+               return 0;
+       if (DEBUG_BISECT)
+               return 0;
+       /*
+        * 2 and 3 are halfway of 5.
+        * 3 is halfway of 6 but 2 and 4 are not.
+        */
+       switch (2 * weight(p) - nr) {
+       case -1: case 0: case 1:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+#if !DEBUG_BISECT
+#define show_list(a,b,c,d) do { ; } while (0)
+#else
+static void show_list(const char *debug, int counted, int nr,
+                     struct commit_list *list)
+{
+       struct commit_list *p;
+
+       fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
+
+       for (p = list; p; p = p->next) {
+               struct commit_list *pp;
+               struct commit *commit = p->item;
+               unsigned flags = commit->object.flags;
+               enum object_type type;
+               unsigned long size;
+               char *buf = read_sha1_file(commit->object.sha1, &type, &size);
+               char *ep, *sp;
+
+               fprintf(stderr, "%c%c%c ",
+                       (flags & TREESAME) ? ' ' : 'T',
+                       (flags & UNINTERESTING) ? 'U' : ' ',
+                       (flags & COUNTED) ? 'C' : ' ');
+               if (commit->util)
+                       fprintf(stderr, "%3d", weight(p));
+               else
+                       fprintf(stderr, "---");
+               fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
+               for (pp = commit->parents; pp; pp = pp->next)
+                       fprintf(stderr, " %.*s", 8,
+                               sha1_to_hex(pp->item->object.sha1));
+
+               sp = strstr(buf, "\n\n");
+               if (sp) {
+                       sp += 2;
+                       for (ep = sp; *ep && *ep != '\n'; ep++)
+                               ;
+                       fprintf(stderr, " %.*s", (int)(ep - sp), sp);
+               }
+               fprintf(stderr, "\n");
+       }
+}
+#endif /* DEBUG_BISECT */
+
+static struct commit_list *best_bisection(struct commit_list *list, int nr)
+{
+       struct commit_list *p, *best;
+       int best_distance = -1;
+
+       best = list;
+       for (p = list; p; p = p->next) {
+               int distance;
+               unsigned flags = p->item->object.flags;
+
+               if (flags & TREESAME)
+                       continue;
+               distance = weight(p);
+               if (nr - distance < distance)
+                       distance = nr - distance;
+               if (distance > best_distance) {
+                       best = p;
+                       best_distance = distance;
+               }
+       }
+
+       return best;
+}
+
+struct commit_dist {
+       struct commit *commit;
+       int distance;
+};
+
+static int compare_commit_dist(const void *a_, const void *b_)
+{
+       struct commit_dist *a, *b;
+
+       a = (struct commit_dist *)a_;
+       b = (struct commit_dist *)b_;
+       if (a->distance != b->distance)
+               return b->distance - a->distance; /* desc sort */
+       return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
+}
+
+static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
+{
+       struct commit_list *p;
+       struct commit_dist *array = xcalloc(nr, sizeof(*array));
+       int cnt, i;
+
+       for (p = list, cnt = 0; p; p = p->next) {
+               int distance;
+               unsigned flags = p->item->object.flags;
+
+               if (flags & TREESAME)
+                       continue;
+               distance = weight(p);
+               if (nr - distance < distance)
+                       distance = nr - distance;
+               array[cnt].commit = p->item;
+               array[cnt].distance = distance;
+               cnt++;
+       }
+       qsort(array, cnt, sizeof(*array), compare_commit_dist);
+       for (p = list, i = 0; i < cnt; i++) {
+               struct name_decoration *r = xmalloc(sizeof(*r) + 100);
+               struct object *obj = &(array[i].commit->object);
+
+               sprintf(r->name, "dist=%d", array[i].distance);
+               r->next = add_decoration(&name_decoration, obj, r);
+               p->item = array[i].commit;
+               p = p->next;
+       }
+       if (p)
+               p->next = NULL;
+       free(array);
+       return list;
+}
+
+/*
+ * zero or positive weight is the number of interesting commits it can
+ * reach, including itself.  Especially, weight = 0 means it does not
+ * reach any tree-changing commits (e.g. just above uninteresting one
+ * but traversal is with pathspec).
+ *
+ * weight = -1 means it has one parent and its distance is yet to
+ * be computed.
+ *
+ * weight = -2 means it has more than one parent and its distance is
+ * unknown.  After running count_distance() first, they will get zero
+ * or positive distance.
+ */
+static struct commit_list *do_find_bisection(struct commit_list *list,
+                                            int nr, int *weights,
+                                            int find_all)
+{
+       int n, counted;
+       struct commit_list *p;
+
+       counted = 0;
+
+       for (n = 0, p = list; p; p = p->next) {
+               struct commit *commit = p->item;
+               unsigned flags = commit->object.flags;
+
+               p->item->util = &weights[n++];
+               switch (count_interesting_parents(commit)) {
+               case 0:
+                       if (!(flags & TREESAME)) {
+                               weight_set(p, 1);
+                               counted++;
+                               show_list("bisection 2 count one",
+                                         counted, nr, list);
+                       }
+                       /*
+                        * otherwise, it is known not to reach any
+                        * tree-changing commit and gets weight 0.
+                        */
+                       break;
+               case 1:
+                       weight_set(p, -1);
+                       break;
+               default:
+                       weight_set(p, -2);
+                       break;
+               }
+       }
+
+       show_list("bisection 2 initialize", counted, nr, list);
+
+       /*
+        * If you have only one parent in the resulting set
+        * then you can reach one commit more than that parent
+        * can reach.  So we do not have to run the expensive
+        * count_distance() for single strand of pearls.
+        *
+        * However, if you have more than one parents, you cannot
+        * just add their distance and one for yourself, since
+        * they usually reach the same ancestor and you would
+        * end up counting them twice that way.
+        *
+        * So we will first count distance of merges the usual
+        * way, and then fill the blanks using cheaper algorithm.
+        */
+       for (p = list; p; p = p->next) {
+               if (p->item->object.flags & UNINTERESTING)
+                       continue;
+               if (weight(p) != -2)
+                       continue;
+               weight_set(p, count_distance(p));
+               clear_distance(list);
+
+               /* Does it happen to be at exactly half-way? */
+               if (!find_all && halfway(p, nr))
+                       return p;
+               counted++;
+       }
+
+       show_list("bisection 2 count_distance", counted, nr, list);
+
+       while (counted < nr) {
+               for (p = list; p; p = p->next) {
+                       struct commit_list *q;
+                       unsigned flags = p->item->object.flags;
+
+                       if (0 <= weight(p))
+                               continue;
+                       for (q = p->item->parents; q; q = q->next) {
+                               if (q->item->object.flags & UNINTERESTING)
+                                       continue;
+                               if (0 <= weight(q))
+                                       break;
+                       }
+                       if (!q)
+                               continue;
+
+                       /*
+                        * weight for p is unknown but q is known.
+                        * add one for p itself if p is to be counted,
+                        * otherwise inherit it from q directly.
+                        */
+                       if (!(flags & TREESAME)) {
+                               weight_set(p, weight(q)+1);
+                               counted++;
+                               show_list("bisection 2 count one",
+                                         counted, nr, list);
+                       }
+                       else
+                               weight_set(p, weight(q));
+
+                       /* Does it happen to be at exactly half-way? */
+                       if (!find_all && halfway(p, nr))
+                               return p;
+               }
+       }
+
+       show_list("bisection 2 counted all", counted, nr, list);
+
+       if (!find_all)
+               return best_bisection(list, nr);
+       else
+               return best_bisection_sorted(list, nr);
+}
+
+struct commit_list *find_bisection(struct commit_list *list,
+                                         int *reaches, int *all,
+                                         int find_all)
+{
+       int nr, on_list;
+       struct commit_list *p, *best, *next, *last;
+       int *weights;
+
+       show_list("bisection 2 entry", 0, 0, list);
+
+       /*
+        * Count the number of total and tree-changing items on the
+        * list, while reversing the list.
+        */
+       for (nr = on_list = 0, last = NULL, p = list;
+            p;
+            p = next) {
+               unsigned flags = p->item->object.flags;
+
+               next = p->next;
+               if (flags & UNINTERESTING)
+                       continue;
+               p->next = last;
+               last = p;
+               if (!(flags & TREESAME))
+                       nr++;
+               on_list++;
+       }
+       list = last;
+       show_list("bisection 2 sorted", 0, nr, list);
+
+       *all = nr;
+       weights = xcalloc(on_list, sizeof(*weights));
+
+       /* Do the real work of finding bisection commit. */
+       best = do_find_bisection(list, nr, weights, find_all);
+       if (best) {
+               if (!find_all)
+                       best->next = NULL;
+               *reaches = weight(best);
+       }
+       free(weights);
+       return best;
+}
+
+static int register_ref(const char *refname, const unsigned char *sha1,
+                       int flags, void *cb_data)
+{
+       if (!strcmp(refname, "bad")) {
+               ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+               rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1));
+       } else if (!prefixcmp(refname, "good-")) {
+               const char *hex = sha1_to_hex(sha1);
+               char *good = xmalloc(strlen(hex) + 2);
+               *good = '^';
+               memcpy(good + 1, hex, strlen(hex) + 1);
+               ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+               rev_argv[rev_argv_nr++] = good;
+       } else if (!prefixcmp(refname, "skip-")) {
+               ALLOC_GROW(skipped_sha1, skipped_sha1_nr + 1,
+                          skipped_sha1_alloc);
+               hashcpy(skipped_sha1[skipped_sha1_nr++], sha1);
+       }
+
+       return 0;
+}
+
+static int read_bisect_refs(void)
+{
+       return for_each_ref_in("refs/bisect/", register_ref, NULL);
+}
+
+void read_bisect_paths(void)
+{
+       struct strbuf str = STRBUF_INIT;
+       const char *filename = git_path("BISECT_NAMES");
+       FILE *fp = fopen(filename, "r");
+
+       if (!fp)
+               die("Could not open file '%s': %s", filename, strerror(errno));
+
+       while (strbuf_getline(&str, fp, '\n') != EOF) {
+               char *quoted;
+               int res;
+
+               strbuf_trim(&str);
+               quoted = strbuf_detach(&str, NULL);
+               res = sq_dequote_to_argv(quoted, &rev_argv,
+                                        &rev_argv_nr, &rev_argv_alloc);
+               if (res)
+                       die("Badly quoted content in file '%s': %s",
+                           filename, quoted);
+       }
+
+       strbuf_release(&str);
+       fclose(fp);
+}
+
+static int skipcmp(const void *a, const void *b)
+{
+       return hashcmp(a, b);
+}
+
+static void prepare_skipped(void)
+{
+       qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp);
+}
+
+static const unsigned char *skipped_sha1_access(size_t index, void *table)
+{
+       unsigned char (*skipped)[20] = table;
+       return skipped[index];
+}
+
+static int lookup_skipped(unsigned char *sha1)
+{
+       return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr,
+                       skipped_sha1_access);
+}
+
+struct commit_list *filter_skipped(struct commit_list *list,
+                                  struct commit_list **tried,
+                                  int show_all)
+{
+       struct commit_list *filtered = NULL, **f = &filtered;
+
+       *tried = NULL;
+
+       if (!skipped_sha1_nr)
+               return list;
+
+       prepare_skipped();
+
+       while (list) {
+               struct commit_list *next = list->next;
+               list->next = NULL;
+               if (0 <= lookup_skipped(list->item->object.sha1)) {
+                       /* Move current to tried list */
+                       *tried = list;
+                       tried = &list->next;
+               } else {
+                       if (!show_all)
+                               return list;
+                       /* Move current to filtered list */
+                       *f = list;
+                       f = &list->next;
+               }
+               list = next;
+       }
+
+       return filtered;
+}
+
+static void bisect_rev_setup(struct rev_info *revs, const char *prefix)
+{
+       init_revisions(revs, prefix);
+       revs->abbrev = 0;
+       revs->commit_format = CMIT_FMT_UNSPECIFIED;
+
+       /* argv[0] will be ignored by setup_revisions */
+       ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+       rev_argv[rev_argv_nr++] = xstrdup("bisect_rev_setup");
+
+       if (read_bisect_refs())
+               die("reading bisect refs failed");
+
+       ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+       rev_argv[rev_argv_nr++] = xstrdup("--");
+
+       read_bisect_paths();
+
+       ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+       rev_argv[rev_argv_nr++] = NULL;
+
+       setup_revisions(rev_argv_nr, rev_argv, revs, NULL);
+
+       revs->limited = 1;
+}
+
+int bisect_next_vars(const char *prefix)
+{
+       struct rev_info revs;
+       struct rev_list_info info;
+       int reaches = 0, all = 0;
+
+       memset(&info, 0, sizeof(info));
+       info.revs = &revs;
+       info.bisect_show_flags = BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED;
+
+       bisect_rev_setup(&revs, prefix);
+
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
+       if (revs.tree_objects)
+               mark_edges_uninteresting(revs.commits, &revs, NULL);
+
+       revs.commits = find_bisection(revs.commits, &reaches, &all,
+                                     !!skipped_sha1_nr);
+
+       return show_bisect_vars(&info, reaches, all);
+}
diff --git a/bisect.h b/bisect.h
new file mode 100644 (file)
index 0000000..fdba913
--- /dev/null
+++ b/bisect.h
@@ -0,0 +1,29 @@
+#ifndef BISECT_H
+#define BISECT_H
+
+extern struct commit_list *find_bisection(struct commit_list *list,
+                                         int *reaches, int *all,
+                                         int find_all);
+
+extern struct commit_list *filter_skipped(struct commit_list *list,
+                                         struct commit_list **tried,
+                                         int show_all);
+
+/* bisect_show_flags flags in struct rev_list_info */
+#define BISECT_SHOW_ALL                (1<<0)
+#define BISECT_SHOW_TRIED      (1<<1)
+#define BISECT_SHOW_STRINGED   (1<<2)
+
+struct rev_list_info {
+       struct rev_info *revs;
+       int bisect_show_flags;
+       int show_timestamp;
+       int hdr_termination;
+       const char *header_prefix;
+};
+
+extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all);
+
+extern int bisect_next_vars(const char *prefix);
+
+#endif
index 1f00e44deb28afe74ae4f0b85b23039476032fe5..62030af4b51abbfb9c488dbf018770f3b3606789 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -32,21 +32,59 @@ static int find_tracked_branch(struct remote *remote, void *priv)
        return 0;
 }
 
-static int should_setup_rebase(const struct tracking *tracking)
+static int should_setup_rebase(const char *origin)
 {
        switch (autorebase) {
        case AUTOREBASE_NEVER:
                return 0;
        case AUTOREBASE_LOCAL:
-               return tracking->remote == NULL;
+               return origin == NULL;
        case AUTOREBASE_REMOTE:
-               return tracking->remote != NULL;
+               return origin != NULL;
        case AUTOREBASE_ALWAYS:
                return 1;
        }
        return 0;
 }
 
+void install_branch_config(int flag, const char *local, const char *origin, const char *remote)
+{
+       struct strbuf key = STRBUF_INIT;
+       int rebasing = should_setup_rebase(origin);
+
+       strbuf_addf(&key, "branch.%s.remote", local);
+       git_config_set(key.buf, origin ? origin : ".");
+
+       strbuf_reset(&key);
+       strbuf_addf(&key, "branch.%s.merge", local);
+       git_config_set(key.buf, remote);
+
+       if (rebasing) {
+               strbuf_reset(&key);
+               strbuf_addf(&key, "branch.%s.rebase", local);
+               git_config_set(key.buf, "true");
+       }
+
+       if (flag & BRANCH_CONFIG_VERBOSE) {
+               strbuf_reset(&key);
+
+               strbuf_addstr(&key, origin ? "remote" : "local");
+
+               /* Are we tracking a proper "branch"? */
+               if (!prefixcmp(remote, "refs/heads/")) {
+                       strbuf_addf(&key, " branch %s", remote + 11);
+                       if (origin)
+                               strbuf_addf(&key, " from %s", origin);
+               }
+               else
+                       strbuf_addf(&key, " ref %s", remote);
+               printf("Branch %s set up to track %s%s.\n",
+                      local, key.buf,
+                      rebasing ? " by rebasing" : "");
+       }
+       strbuf_release(&key);
+}
+
 /*
  * This is called when new_ref is branched off of orig_ref, and tries
  * to infer the settings for branch.<new_ref>.{remote,merge} from the
@@ -55,7 +93,6 @@ static int should_setup_rebase(const struct tracking *tracking)
 static int setup_tracking(const char *new_ref, const char *orig_ref,
                           enum branch_track track)
 {
-       char key[1024];
        struct tracking tracking;
 
        if (strlen(new_ref) > 1024 - 7 - 7 - 1)
@@ -80,19 +117,10 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
                return error("Not tracking: ambiguous information for ref %s",
                                orig_ref);
 
-       sprintf(key, "branch.%s.remote", new_ref);
-       git_config_set(key, tracking.remote ?  tracking.remote : ".");
-       sprintf(key, "branch.%s.merge", new_ref);
-       git_config_set(key, tracking.src ? tracking.src : orig_ref);
-       printf("Branch %s set up to track %s branch %s.\n", new_ref,
-               tracking.remote ? "remote" : "local", orig_ref);
-       if (should_setup_rebase(&tracking)) {
-               sprintf(key, "branch.%s.rebase", new_ref);
-               git_config_set(key, "true");
-               printf("This branch will rebase on pull.\n");
-       }
-       free(tracking.src);
+       install_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote,
+                             tracking.src ? tracking.src : orig_ref);
 
+       free(tracking.src);
        return 0;
 }
 
@@ -106,16 +134,8 @@ void create_branch(const char *head,
        char *real_ref, msg[PATH_MAX + 20];
        struct strbuf ref = STRBUF_INIT;
        int forcing = 0;
-       int len;
-
-       len = strlen(name);
-       if (interpret_nth_last_branch(name, &ref) != len) {
-               strbuf_reset(&ref);
-               strbuf_add(&ref, name, len);
-       }
-       strbuf_splice(&ref, 0, 0, "refs/heads/", 11);
 
-       if (check_ref_format(ref.buf))
+       if (strbuf_check_branch_ref(&ref, name))
                die("'%s' is not a valid branch name.", name);
 
        if (resolve_ref(ref.buf, sha1, 1, NULL)) {
index 9f0c2a2c1fab9a312f436880956da0973c68ead8..eed817a64c7620bfe67f395e39b4eef2f85a4ab3 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -21,4 +21,11 @@ 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".
+ */
+#define BRANCH_CONFIG_VERBOSE 01
+extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote);
+
 #endif
index 08443f2f1ecf7d9edd21cec11fa74548c3326df5..d6ad57bfc2841959a2017d0723c1f9d57a920fac 100644 (file)
@@ -61,7 +61,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
        fill_pathspec_matches(pathspec, seen, specs);
 
        for (i = 0; i < specs; i++) {
-               if (!seen[i] && !file_exists(pathspec[i]))
+               if (!seen[i] && pathspec[i][0] && !file_exists(pathspec[i]))
                        die("pathspec '%s' did not match any files",
                                        pathspec[i]);
        }
@@ -104,7 +104,7 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
        /* Set up the default git porcelain excludes */
        memset(dir, 0, sizeof(*dir));
        if (!ignored_too) {
-               dir->collect_ignored = 1;
+               dir->flags |= DIR_COLLECT_IGNORED;
                setup_standard_excludes(dir);
        }
 
@@ -148,7 +148,7 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p
        if (pathspec) {
                const char **p;
                for (p = pathspec; *p; p++) {
-                       if (has_symlink_leading_path(strlen(*p), *p)) {
+                       if (has_symlink_leading_path(*p, strlen(*p))) {
                                int len = prefix ? strlen(prefix) : 0;
                                die("'%s' is beyond a symbolic link", *p + len);
                        }
@@ -250,6 +250,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        int add_new_files;
        int require_pathspec;
 
+       git_config(add_config, NULL);
+
        argc = parse_options(argc, argv, builtin_add_options,
                          builtin_add_usage, 0);
        if (patch_interactive)
@@ -257,8 +259,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        if (add_interactive)
                exit(interactive_add(argc, argv, prefix));
 
-       git_config(add_config, NULL);
-
        if (addremove && take_worktree_changes)
                die("-A and -u are mutually incompatible");
        if ((addremove || take_worktree_changes) && !argc) {
index c6feaf5ca86f320ff3c512c2fb4e1e473936a0de..a40b9822425e25272cadf6f4170ba967eacf11bf 100644 (file)
@@ -2394,7 +2394,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
                 * In such a case, path "new_name" does not exist as
                 * far as git is concerned.
                 */
-               if (has_symlink_leading_path(strlen(new_name), new_name))
+               if (has_symlink_leading_path(new_name, strlen(new_name)))
                        return 0;
 
                return error("%s: already exists in working directory", new_name);
@@ -2487,7 +2487,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
        if ((st_mode ^ patch->old_mode) & S_IFMT)
                return error("%s: wrong type", old_name);
        if (st_mode != patch->old_mode)
-               fprintf(stderr, "warning: %s has type %o, expected %o\n",
+               warning("%s has type %o, expected %o",
                        old_name, st_mode, patch->old_mode);
        if (!patch->new_mode && !patch->is_delete)
                patch->new_mode = st_mode;
@@ -2781,7 +2781,7 @@ static void remove_file(struct patch *patch, int rmdir_empty)
                        if (rmdir(patch->old_name))
                                warning("unable to remove submodule %s",
                                        patch->old_name);
-               } else if (!unlink(patch->old_name) && rmdir_empty) {
+               } else if (!unlink_or_warn(patch->old_name) && rmdir_empty) {
                        remove_path(patch->old_name);
                }
        }
@@ -2891,7 +2891,7 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                        if (!try_create_file(newpath, mode, buf, size)) {
                                if (!rename(newpath, path))
                                        return;
-                               unlink(newpath);
+                               unlink_or_warn(newpath);
                                break;
                        }
                        if (errno != EEXIST)
@@ -2971,8 +2971,7 @@ static int write_out_one_reject(struct patch *patch)
        cnt = strlen(patch->new_name);
        if (ARRAY_SIZE(namebuf) <= cnt + 5) {
                cnt = ARRAY_SIZE(namebuf) - 5;
-               fprintf(stderr,
-                       "warning: truncating .rej filename to %.*s.rej",
+               warning("truncating .rej filename to %.*s.rej",
                        cnt - 1, patch->new_name);
        }
        memcpy(namebuf, patch->new_name, cnt);
@@ -3263,10 +3262,10 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                        "ignore additions made by the patch"),
                OPT_BOOLEAN(0, "stat", &diffstat,
                        "instead of applying the patch, output diffstat for the input"),
-               OPT_BOOLEAN(0, "allow-binary-replacement", &binary,
-                       "now no-op"),
-               OPT_BOOLEAN(0, "binary", &binary,
-                       "now no-op"),
+               { OPTION_BOOLEAN, 0, "allow-binary-replacement", &binary,
+                 NULL, "old option, now no-op", PARSE_OPT_HIDDEN },
+               { OPTION_BOOLEAN, 0, "binary", &binary,
+                 NULL, "old option, now no-op", PARSE_OPT_HIDDEN },
                OPT_BOOLEAN(0, "numstat", &numstat,
                        "shows number of added and deleted lines in decimal notation"),
                OPT_BOOLEAN(0, "summary", &summary,
@@ -3316,6 +3315,10 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
 
        argc = parse_options(argc, argv, builtin_apply_options,
                        apply_usage, 0);
+       fake_ancestor = parse_options_fix_filename(prefix, fake_ancestor);
+       if (fake_ancestor)
+               fake_ancestor = xstrdup(fake_ancestor);
+
        if (apply_with_reject)
                apply = apply_verbosely = 1;
        if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
@@ -3354,8 +3357,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                    squelch_whitespace_errors < whitespace_error) {
                        int squelched =
                                whitespace_error - squelch_whitespace_errors;
-                       fprintf(stderr, "warning: squelched %d "
-                               "whitespace error%s\n",
+                       warning("squelched %d "
+                               "whitespace error%s",
                                squelched,
                                squelched == 1 ? "" : "s");
                }
@@ -3365,12 +3368,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                            whitespace_error == 1 ? "" : "s",
                            whitespace_error == 1 ? "s" : "");
                if (applied_after_fixing_ws && apply)
-                       fprintf(stderr, "warning: %d line%s applied after"
-                               " fixing whitespace errors.\n",
+                       warning("%d line%s applied after"
+                               " fixing whitespace errors.",
                                applied_after_fixing_ws,
                                applied_after_fixing_ws == 1 ? "" : "s");
                else if (whitespace_error)
-                       fprintf(stderr, "warning: %d line%s add%s whitespace errors.\n",
+                       warning("%d line%s add%s whitespace errors.",
                                whitespace_error,
                                whitespace_error == 1 ? "" : "s",
                                whitespace_error == 1 ? "s" : "");
index 5ceec433fd590e8bf6a51700ea69c37f9af30fa7..ab50cebba0e6798996cc007ced9079c3f0b94a29 100644 (file)
@@ -5,44 +5,35 @@
 #include "cache.h"
 #include "builtin.h"
 #include "archive.h"
+#include "parse-options.h"
 #include "pkt-line.h"
 #include "sideband.h"
 
-static int run_remote_archiver(const char *remote, int argc,
-                              const char **argv)
+static void create_output_file(const char *output_file)
+{
+       int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+       if (output_fd < 0)
+               die("could not create archive file: %s ", output_file);
+       if (output_fd != 1) {
+               if (dup2(output_fd, 1) < 0)
+                       die("could not redirect output");
+               else
+                       close(output_fd);
+       }
+}
+
+static int run_remote_archiver(int argc, const char **argv,
+                              const char *remote, const char *exec)
 {
        char *url, buf[LARGE_PACKET_MAX];
        int fd[2], i, len, rv;
        struct child_process *conn;
-       const char *exec = "git-upload-archive";
-       int exec_at = 0, exec_value_at = 0;
-
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (!prefixcmp(arg, "--exec=")) {
-                       if (exec_at)
-                               die("multiple --exec specified");
-                       exec = arg + 7;
-                       exec_at = i;
-               } else if (!strcmp(arg, "--exec")) {
-                       if (exec_at)
-                               die("multiple --exec specified");
-                       if (i + 1 >= argc)
-                               die("option --exec requires a value");
-                       exec = argv[i + 1];
-                       exec_at = i;
-                       exec_value_at = ++i;
-               }
-       }
 
        url = xstrdup(remote);
        conn = git_connect(fd, url, exec, 0);
 
-       for (i = 1; i < argc; i++) {
-               if (i == exec_at || i == exec_value_at)
-                       continue;
+       for (i = 1; i < argc; i++)
                packet_write(fd[1], "argument %s\n", argv[i]);
-       }
        packet_flush(fd[1]);
 
        len = packet_read_line(fd[0], buf, sizeof(buf));
@@ -61,7 +52,7 @@ static int run_remote_archiver(const char *remote, int argc,
                die("git archive: expected a flush");
 
        /* Now, start reading from fd[0] and spit it out to stdout */
-       rv = recv_sideband("archive", fd[0], 1, 2);
+       rv = recv_sideband("archive", fd[0], 1);
        close(fd[0]);
        close(fd[1]);
        rv |= finish_connect(conn);
@@ -69,51 +60,33 @@ static int run_remote_archiver(const char *remote, int argc,
        return !!rv;
 }
 
-static const char *extract_remote_arg(int *ac, const char **av)
-{
-       int ix, iy, cnt = *ac;
-       int no_more_options = 0;
-       const char *remote = NULL;
-
-       for (ix = iy = 1; ix < cnt; ix++) {
-               const char *arg = av[ix];
-               if (!strcmp(arg, "--"))
-                       no_more_options = 1;
-               if (!no_more_options) {
-                       if (!prefixcmp(arg, "--remote=")) {
-                               if (remote)
-                                       die("Multiple --remote specified");
-                               remote = arg + 9;
-                               continue;
-                       } else if (!strcmp(arg, "--remote")) {
-                               if (remote)
-                                       die("Multiple --remote specified");
-                               if (++ix >= cnt)
-                                       die("option --remote requires a value");
-                               remote = av[ix];
-                               continue;
-                       }
-                       if (arg[0] != '-')
-                               no_more_options = 1;
-               }
-               if (ix != iy)
-                       av[iy] = arg;
-               iy++;
-       }
-       if (remote) {
-               av[--cnt] = NULL;
-               *ac = cnt;
-       }
-       return remote;
-}
+#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH |         \
+                            PARSE_OPT_KEEP_ARGV0 |     \
+                            PARSE_OPT_KEEP_UNKNOWN |   \
+                            PARSE_OPT_NO_INTERNAL_HELP )
 
 int cmd_archive(int argc, const char **argv, const char *prefix)
 {
+       const char *exec = "git-upload-archive";
+       const char *output = NULL;
        const char *remote = NULL;
+       struct option local_opts[] = {
+               OPT_STRING(0, "output", &output, "file",
+                       "write the archive to this file"),
+               OPT_STRING(0, "remote", &remote, "repo",
+                       "retrieve the archive from remote repository <repo>"),
+               OPT_STRING(0, "exec", &exec, "cmd",
+                       "path to the remote git-upload-archive command"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, local_opts, NULL, PARSE_OPT_KEEP_ALL);
+
+       if (output)
+               create_output_file(output);
 
-       remote = extract_remote_arg(&argc, argv);
        if (remote)
-               return run_remote_archiver(remote, argc, argv);
+               return run_remote_archiver(argc, argv, remote, exec);
 
        setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
 
diff --git a/builtin-bisect--helper.c b/builtin-bisect--helper.c
new file mode 100644 (file)
index 0000000..8fe7787
--- /dev/null
@@ -0,0 +1,27 @@
+#include "builtin.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "bisect.h"
+
+static const char * const git_bisect_helper_usage[] = {
+       "git bisect--helper --next-vars",
+       NULL
+};
+
+int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
+{
+       int next_vars = 0;
+       struct option options[] = {
+               OPT_BOOLEAN(0, "next-vars", &next_vars,
+                           "output next bisect step variables"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0);
+
+       if (!next_vars)
+               usage_with_options(git_bisect_helper_usage, options);
+
+       /* next-vars */
+       return bisect_next_vars(prefix);
+}
index 1ead9b48308feacfe253be060c32ec0210475a3d..0afdb16cb0e14367994fac99b60f36c292aeaac9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Pickaxe
+ * Blame
  *
  * Copyright (c) 2006, Junio C Hamano
  */
@@ -40,6 +40,10 @@ static int reverse;
 static int blank_boundary;
 static int incremental;
 static int xdl_opts = XDF_NEED_MINIMAL;
+
+static enum date_mode blame_date_mode = DATE_ISO8601;
+static size_t blame_date_width;
+
 static struct string_list mailmap;
 
 #ifndef DEBUG
@@ -74,6 +78,7 @@ static unsigned blame_copy_score;
  */
 struct origin {
        int refcnt;
+       struct origin *previous;
        struct commit *commit;
        mmfile_t file;
        unsigned char blob_sha1[20];
@@ -115,6 +120,8 @@ static inline struct origin *origin_incref(struct origin *o)
 static void origin_decref(struct origin *o)
 {
        if (o && --o->refcnt <= 0) {
+               if (o->previous)
+                       origin_decref(o->previous);
                free(o->file.ptr);
                free(o);
        }
@@ -355,18 +362,28 @@ static struct origin *find_origin(struct scoreboard *sb,
                               "", &diff_opts);
        diffcore_std(&diff_opts);
 
-       /* It is either one entry that says "modified", or "created",
-        * or nothing.
-        */
        if (!diff_queued_diff.nr) {
                /* The path is the same as parent */
                porigin = get_origin(sb, parent, origin->path);
                hashcpy(porigin->blob_sha1, origin->blob_sha1);
-       }
-       else if (diff_queued_diff.nr != 1)
-               die("internal error in blame::find_origin");
-       else {
-               struct diff_filepair *p = diff_queued_diff.queue[0];
+       } else {
+               /*
+                * Since origin->path is a pathspec, if the parent
+                * commit had it as a directory, we will see a whole
+                * bunch of deletion of files in the directory that we
+                * do not care about.
+                */
+               int i;
+               struct diff_filepair *p = NULL;
+               for (i = 0; i < diff_queued_diff.nr; i++) {
+                       const char *name;
+                       p = diff_queued_diff.queue[i];
+                       name = p->one->path ? p->one->path : p->two->path;
+                       if (!strcmp(name, origin->path))
+                               break;
+               }
+               if (!p)
+                       die("internal error in blame::find_origin");
                switch (p->status) {
                default:
                        die("internal error in blame::find_origin (%c)",
@@ -866,7 +883,7 @@ static void find_copy_in_blob(struct scoreboard *sb,
         * Prepare mmfile that contains only the lines in ent.
         */
        cp = nth_line(sb, ent->lno);
-       file_o.ptr = (char*) cp;
+       file_o.ptr = (char *) cp;
        cnt = ent->num_lines;
 
        while (cnt && cp < sb->final_buf + sb->final_buf_size) {
@@ -1198,6 +1215,10 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
                struct origin *porigin = sg_origin[i];
                if (!porigin)
                        continue;
+               if (!origin->previous) {
+                       origin_incref(porigin);
+                       origin->previous = porigin;
+               }
                if (pass_blame_to_parent(sb, origin, porigin))
                        goto finish;
        }
@@ -1414,6 +1435,39 @@ static void write_filename_info(const char *path)
        write_name_quoted(path, stdout, '\n');
 }
 
+/*
+ * Porcelain/Incremental format wants to show a lot of details per
+ * commit.  Instead of repeating this every line, emit it only once,
+ * the first time each commit appears in the output.
+ */
+static int emit_one_suspect_detail(struct origin *suspect)
+{
+       struct commit_info ci;
+
+       if (suspect->commit->object.flags & METAINFO_SHOWN)
+               return 0;
+
+       suspect->commit->object.flags |= METAINFO_SHOWN;
+       get_commit_info(suspect->commit, &ci, 1);
+       printf("author %s\n", ci.author);
+       printf("author-mail %s\n", ci.author_mail);
+       printf("author-time %lu\n", ci.author_time);
+       printf("author-tz %s\n", ci.author_tz);
+       printf("committer %s\n", ci.committer);
+       printf("committer-mail %s\n", ci.committer_mail);
+       printf("committer-time %lu\n", ci.committer_time);
+       printf("committer-tz %s\n", ci.committer_tz);
+       printf("summary %s\n", ci.summary);
+       if (suspect->commit->object.flags & UNINTERESTING)
+               printf("boundary\n");
+       if (suspect->previous) {
+               struct origin *prev = suspect->previous;
+               printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
+               write_name_quoted(prev->path, stdout, '\n');
+       }
+       return 1;
+}
+
 /*
  * The blame_entry is found to be guilty for the range.  Mark it
  * as such, and show it in incremental output.
@@ -1429,22 +1483,7 @@ static void found_guilty_entry(struct blame_entry *ent)
                printf("%s %d %d %d\n",
                       sha1_to_hex(suspect->commit->object.sha1),
                       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
-               if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
-                       struct commit_info ci;
-                       suspect->commit->object.flags |= METAINFO_SHOWN;
-                       get_commit_info(suspect->commit, &ci, 1);
-                       printf("author %s\n", ci.author);
-                       printf("author-mail %s\n", ci.author_mail);
-                       printf("author-time %lu\n", ci.author_time);
-                       printf("author-tz %s\n", ci.author_tz);
-                       printf("committer %s\n", ci.committer);
-                       printf("committer-mail %s\n", ci.committer_mail);
-                       printf("committer-time %lu\n", ci.committer_time);
-                       printf("committer-tz %s\n", ci.committer_tz);
-                       printf("summary %s\n", ci.summary);
-                       if (suspect->commit->object.flags & UNINTERESTING)
-                               printf("boundary\n");
-               }
+               emit_one_suspect_detail(suspect);
                write_filename_info(suspect->path);
                maybe_flush_or_die(stdout, "stdout");
        }
@@ -1507,24 +1546,20 @@ static const char *format_time(unsigned long time, const char *tz_str,
                               int show_raw_time)
 {
        static char time_buf[128];
-       time_t t = time;
-       int minutes, tz;
-       struct tm *tm;
+       const char *time_str;
+       int time_len;
+       int tz;
 
        if (show_raw_time) {
                sprintf(time_buf, "%lu %s", time, tz_str);
-               return time_buf;
        }
-
-       tz = atoi(tz_str);
-       minutes = tz < 0 ? -tz : tz;
-       minutes = (minutes / 100)*60 + (minutes % 100);
-       minutes = tz < 0 ? -minutes : minutes;
-       t = time + minutes * 60;
-       tm = gmtime(&t);
-
-       strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", tm);
-       strcat(time_buf, tz_str);
+       else {
+               tz = atoi(tz_str);
+               time_str = show_date(time, tz, blame_date_mode);
+               time_len = strlen(time_str);
+               memcpy(time_buf, time_str, time_len);
+               memset(time_buf + time_len, ' ', blame_date_width - time_len);
+       }
        return time_buf;
 }
 
@@ -1551,24 +1586,8 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
               ent->s_lno + 1,
               ent->lno + 1,
               ent->num_lines);
-       if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
-               struct commit_info ci;
-               suspect->commit->object.flags |= METAINFO_SHOWN;
-               get_commit_info(suspect->commit, &ci, 1);
-               printf("author %s\n", ci.author);
-               printf("author-mail %s\n", ci.author_mail);
-               printf("author-time %lu\n", ci.author_time);
-               printf("author-tz %s\n", ci.author_tz);
-               printf("committer %s\n", ci.committer);
-               printf("committer-mail %s\n", ci.committer_mail);
-               printf("committer-time %lu\n", ci.committer_time);
-               printf("committer-tz %s\n", ci.committer_tz);
-               write_filename_info(suspect->path);
-               printf("summary %s\n", ci.summary);
-               if (suspect->commit->object.flags & UNINTERESTING)
-                       printf("boundary\n");
-       }
-       else if (suspect->commit->object.flags & MORE_THAN_ONE_PATH)
+       if (emit_one_suspect_detail(suspect) ||
+           (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
                write_filename_info(suspect->path);
 
        cp = nth_line(sb, ent->lno);
@@ -1695,7 +1714,7 @@ static int prepare_lines(struct scoreboard *sb)
        while (len--) {
                if (bol) {
                        sb->lineno = xrealloc(sb->lineno,
-                                             sizeof(int) * (num + 1));
+                                             sizeof(int *) * (num + 1));
                        sb->lineno[num] = buf - sb->final_buf;
                        bol = 0;
                }
@@ -1705,7 +1724,7 @@ static int prepare_lines(struct scoreboard *sb)
                }
        }
        sb->lineno = xrealloc(sb->lineno,
-                             sizeof(int) * (num + incomplete + 1));
+                             sizeof(int *) * (num + incomplete + 1));
        sb->lineno[num + incomplete] = buf - sb->final_buf;
        sb->num_lines = num + incomplete;
        return sb->num_lines;
@@ -1806,36 +1825,6 @@ static void sanity_check_refcnt(struct scoreboard *sb)
                        baa = 1;
                }
        }
-       for (ent = sb->ent; ent; ent = ent->next) {
-               /* Mark the ones that haven't been checked */
-               if (0 < ent->suspect->refcnt)
-                       ent->suspect->refcnt = -ent->suspect->refcnt;
-       }
-       for (ent = sb->ent; ent; ent = ent->next) {
-               /*
-                * ... then pick each and see if they have the the
-                * correct refcnt.
-                */
-               int found;
-               struct blame_entry *e;
-               struct origin *suspect = ent->suspect;
-
-               if (0 < suspect->refcnt)
-                       continue;
-               suspect->refcnt = -suspect->refcnt; /* Unmark */
-               for (found = 0, e = sb->ent; e; e = e->next) {
-                       if (e->suspect != suspect)
-                               continue;
-                       found++;
-               }
-               if (suspect->refcnt != found) {
-                       fprintf(stderr, "%s in %s has refcnt %d, not %d\n",
-                               ent->suspect->path,
-                               sha1_to_hex(ent->suspect->commit->object.sha1),
-                               ent->suspect->refcnt, found);
-                       baa = 2;
-               }
-       }
        if (baa) {
                int opt = 0160;
                find_alignment(sb, &opt);
@@ -1910,7 +1899,7 @@ static const char *parse_loc(const char *spec,
                return spec;
 
        /* it could be a regexp of form /.../ */
-       for (term = (char*) spec + 1; *term && *term != '/'; term++) {
+       for (term = (char *) spec + 1; *term && *term != '/'; term++) {
                if (*term == '\\')
                        term++;
        }
@@ -1975,6 +1964,12 @@ static int git_blame_config(const char *var, const char *value, void *cb)
                blank_boundary = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "blame.date")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               blame_date_mode = parse_date_format(value);
+               return 0;
+       }
        return git_default_config(var, value, cb);
 }
 
@@ -2239,6 +2234,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
        git_config(git_blame_config, NULL);
        init_revisions(&revs, NULL);
+       revs.date_mode = blame_date_mode;
+
        save_commit_buffer = 0;
        dashdash_pos = 0;
 
@@ -2267,8 +2264,35 @@ parse_done:
                die("reading graft file %s failed: %s",
                    revs_file, strerror(errno));
 
-       if (cmd_is_annotate)
+       if (cmd_is_annotate) {
                output_option |= OUTPUT_ANNOTATE_COMPAT;
+               blame_date_mode = DATE_ISO8601;
+       } else {
+               blame_date_mode = revs.date_mode;
+       }
+
+       /* The maximum width used to show the dates */
+       switch (blame_date_mode) {
+       case DATE_RFC2822:
+               blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
+               break;
+       case DATE_ISO8601:
+               blame_date_width = sizeof("2006-10-19 16:00:04 -0700");
+               break;
+       case DATE_RAW:
+               blame_date_width = sizeof("1161298804 -0700");
+               break;
+       case DATE_SHORT:
+               blame_date_width = sizeof("2006-10-19");
+               break;
+       case DATE_RELATIVE:
+               /* "normal" is used as the fallback for "relative" */
+       case DATE_LOCAL:
+       case DATE_NORMAL:
+               blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
+               break;
+       }
+       blame_date_width -= 1; /* strip the null */
 
        if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
                opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
index 07166b8856b2a2637a55b20221a929d6dbea1e0c..887fa60fa54139eb5e25f801e5b964978db846ab 100644 (file)
@@ -32,18 +32,18 @@ static unsigned char head_sha1[20];
 
 static int branch_use_color = -1;
 static char branch_colors[][COLOR_MAXLEN] = {
-       "\033[m",       /* reset */
-       "",             /* PLAIN (normal) */
-       "\033[31m",     /* REMOTE (red) */
-       "",             /* LOCAL (normal) */
-       "\033[32m",     /* CURRENT (green) */
+       GIT_COLOR_RESET,
+       GIT_COLOR_NORMAL,       /* PLAIN */
+       GIT_COLOR_RED,          /* REMOTE */
+       GIT_COLOR_NORMAL,       /* LOCAL */
+       GIT_COLOR_GREEN,        /* CURRENT */
 };
 enum color_branch {
-       COLOR_BRANCH_RESET = 0,
-       COLOR_BRANCH_PLAIN = 1,
-       COLOR_BRANCH_REMOTE = 2,
-       COLOR_BRANCH_LOCAL = 3,
-       COLOR_BRANCH_CURRENT = 4,
+       BRANCH_COLOR_RESET = 0,
+       BRANCH_COLOR_PLAIN = 1,
+       BRANCH_COLOR_REMOTE = 2,
+       BRANCH_COLOR_LOCAL = 3,
+       BRANCH_COLOR_CURRENT = 4,
 };
 
 static enum merge_filter {
@@ -56,15 +56,15 @@ static unsigned char merge_filter_ref[20];
 static int parse_branch_color_slot(const char *var, int ofs)
 {
        if (!strcasecmp(var+ofs, "plain"))
-               return COLOR_BRANCH_PLAIN;
+               return BRANCH_COLOR_PLAIN;
        if (!strcasecmp(var+ofs, "reset"))
-               return COLOR_BRANCH_RESET;
+               return BRANCH_COLOR_RESET;
        if (!strcasecmp(var+ofs, "remote"))
-               return COLOR_BRANCH_REMOTE;
+               return BRANCH_COLOR_REMOTE;
        if (!strcasecmp(var+ofs, "local"))
-               return COLOR_BRANCH_LOCAL;
+               return BRANCH_COLOR_LOCAL;
        if (!strcasecmp(var+ofs, "current"))
-               return COLOR_BRANCH_CURRENT;
+               return BRANCH_COLOR_CURRENT;
        die("bad config variable '%s'", var);
 }
 
@@ -121,11 +121,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
                        die("Couldn't look up commit object for HEAD");
        }
        for (i = 0; i < argc; i++, strbuf_release(&bname)) {
-               int len = strlen(argv[i]);
-
-               if (interpret_nth_last_branch(argv[i], &bname) != len)
-                       strbuf_add(&bname, argv[i], len);
-
+               strbuf_branchname(&bname, argv[i]);
                if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
                        error("Cannot delete the branch '%s' "
                              "which you are currently on.", bname.buf);
@@ -188,51 +184,80 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
 
 struct ref_item {
        char *name;
-       unsigned int kind;
+       char *dest;
+       unsigned int kind, len;
        struct commit *commit;
 };
 
 struct ref_list {
        struct rev_info revs;
-       int index, alloc, maxwidth;
+       int index, alloc, maxwidth, verbose, abbrev;
        struct ref_item *list;
        struct commit_list *with_commit;
        int kinds;
 };
 
+static char *resolve_symref(const char *src, const char *prefix)
+{
+       unsigned char sha1[20];
+       int flag;
+       const char *dst, *cp;
+
+       dst = resolve_ref(src, sha1, 0, &flag);
+       if (!(dst && (flag & REF_ISSYMREF)))
+               return NULL;
+       if (prefix && (cp = skip_prefix(dst, prefix)))
+               dst = cp;
+       return xstrdup(dst);
+}
+
 static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 {
        struct ref_list *ref_list = (struct ref_list*)(cb_data);
        struct ref_item *newitem;
        struct commit *commit;
-       int kind;
-       int len;
+       int kind, i;
+       const char *prefix, *orig_refname = refname;
+
+       static struct {
+               int kind;
+               const char *prefix;
+               int pfxlen;
+       } ref_kind[] = {
+               { REF_LOCAL_BRANCH, "refs/heads/", 11 },
+               { REF_REMOTE_BRANCH, "refs/remotes/", 13 },
+       };
 
        /* Detect kind */
-       if (!prefixcmp(refname, "refs/heads/")) {
-               kind = REF_LOCAL_BRANCH;
-               refname += 11;
-       } else if (!prefixcmp(refname, "refs/remotes/")) {
-               kind = REF_REMOTE_BRANCH;
-               refname += 13;
-       } else
-               return 0;
-
-       commit = lookup_commit_reference_gently(sha1, 1);
-       if (!commit)
-               return error("branch '%s' does not point at a commit", refname);
-
-       /* Filter with with_commit if specified */
-       if (!is_descendant_of(commit, ref_list->with_commit))
+       for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
+               prefix = ref_kind[i].prefix;
+               if (strncmp(refname, prefix, ref_kind[i].pfxlen))
+                       continue;
+               kind = ref_kind[i].kind;
+               refname += ref_kind[i].pfxlen;
+               break;
+       }
+       if (ARRAY_SIZE(ref_kind) <= i)
                return 0;
 
        /* Don't add types the caller doesn't want */
        if ((kind & ref_list->kinds) == 0)
                return 0;
 
-       if (merge_filter != NO_FILTER)
-               add_pending_object(&ref_list->revs,
-                                  (struct object *)commit, refname);
+       commit = NULL;
+       if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
+               commit = lookup_commit_reference_gently(sha1, 1);
+               if (!commit)
+                       return error("branch '%s' does not point at a commit", refname);
+
+               /* Filter with with_commit if specified */
+               if (!is_descendant_of(commit, ref_list->with_commit))
+                       return 0;
+
+               if (merge_filter != NO_FILTER)
+                       add_pending_object(&ref_list->revs,
+                                          (struct object *)commit, refname);
+       }
 
        /* Resize buffer */
        if (ref_list->index >= ref_list->alloc) {
@@ -246,9 +271,14 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        newitem->name = xstrdup(refname);
        newitem->kind = kind;
        newitem->commit = commit;
-       len = strlen(newitem->name);
-       if (len > ref_list->maxwidth)
-               ref_list->maxwidth = len;
+       newitem->len = strlen(refname);
+       newitem->dest = resolve_symref(orig_refname, prefix);
+       /* adjust for "remotes/" */
+       if (newitem->kind == REF_REMOTE_BRANCH &&
+           ref_list->kinds != REF_REMOTE_BRANCH)
+               newitem->len += 8;
+       if (newitem->len > ref_list->maxwidth)
+               ref_list->maxwidth = newitem->len;
 
        return 0;
 }
@@ -257,8 +287,10 @@ static void free_ref_list(struct ref_list *ref_list)
 {
        int i;
 
-       for (i = 0; i < ref_list->index; i++)
+       for (i = 0; i < ref_list->index; i++) {
                free(ref_list->list[i].name);
+               free(ref_list->list[i].dest);
+       }
        free(ref_list->list);
 }
 
@@ -272,19 +304,30 @@ static int ref_cmp(const void *r1, const void *r2)
        return strcmp(c1->name, c2->name);
 }
 
-static void fill_tracking_info(struct strbuf *stat, const char *branch_name)
+static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
+               int show_upstream_ref)
 {
        int ours, theirs;
        struct branch *branch = branch_get(branch_name);
 
-       if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs))
+       if (!stat_tracking_info(branch, &ours, &theirs)) {
+               if (branch && branch->merge && branch->merge[0]->dst &&
+                   show_upstream_ref)
+                       strbuf_addf(stat, "[%s] ",
+                           shorten_unambiguous_ref(branch->merge[0]->dst, 0));
                return;
+       }
+
+       strbuf_addch(stat, '[');
+       if (show_upstream_ref)
+               strbuf_addf(stat, "%s: ",
+                       shorten_unambiguous_ref(branch->merge[0]->dst, 0));
        if (!ours)
-               strbuf_addf(stat, "[behind %d] ", theirs);
+               strbuf_addf(stat, "behind %d] ", theirs);
        else if (!theirs)
-               strbuf_addf(stat, "[ahead %d] ", ours);
+               strbuf_addf(stat, "ahead %d] ", ours);
        else
-               strbuf_addf(stat, "[ahead %d, behind %d] ", ours, theirs);
+               strbuf_addf(stat, "ahead %d, behind %d] ", ours, theirs);
 }
 
 static int matches_merge_filter(struct commit *commit)
@@ -299,34 +342,46 @@ static int matches_merge_filter(struct commit *commit)
 }
 
 static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
-                          int abbrev, int current)
+                          int abbrev, int current, char *prefix)
 {
        char c;
        int color;
        struct commit *commit = item->commit;
+       struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
 
        if (!matches_merge_filter(commit))
                return;
 
        switch (item->kind) {
        case REF_LOCAL_BRANCH:
-               color = COLOR_BRANCH_LOCAL;
+               color = BRANCH_COLOR_LOCAL;
                break;
        case REF_REMOTE_BRANCH:
-               color = COLOR_BRANCH_REMOTE;
+               color = BRANCH_COLOR_REMOTE;
                break;
        default:
-               color = COLOR_BRANCH_PLAIN;
+               color = BRANCH_COLOR_PLAIN;
                break;
        }
 
        c = ' ';
        if (current) {
                c = '*';
-               color = COLOR_BRANCH_CURRENT;
+               color = BRANCH_COLOR_CURRENT;
        }
 
-       if (verbose) {
+       strbuf_addf(&name, "%s%s", prefix, item->name);
+       if (verbose)
+               strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
+                           maxwidth, name.buf,
+                           branch_get_color(BRANCH_COLOR_RESET));
+       else
+               strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
+                           name.buf, branch_get_color(BRANCH_COLOR_RESET));
+
+       if (item->dest)
+               strbuf_addf(&out, " -> %s", item->dest);
+       else if (verbose) {
                struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
                const char *sub = " **** invalid ref ****";
 
@@ -338,46 +393,63 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
                }
 
                if (item->kind == REF_LOCAL_BRANCH)
-                       fill_tracking_info(&stat, item->name);
+                       fill_tracking_info(&stat, item->name, verbose > 1);
 
-               printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color),
-                      maxwidth, item->name,
-                      branch_get_color(COLOR_BRANCH_RESET),
-                      find_unique_abbrev(item->commit->object.sha1, abbrev),
-                      stat.buf, sub);
+               strbuf_addf(&out, " %s %s%s",
+                       find_unique_abbrev(item->commit->object.sha1, abbrev),
+                       stat.buf, sub);
                strbuf_release(&stat);
                strbuf_release(&subject);
-       } else {
-               printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
-                      branch_get_color(COLOR_BRANCH_RESET));
        }
+       printf("%s\n", out.buf);
+       strbuf_release(&name);
+       strbuf_release(&out);
 }
 
 static int calc_maxwidth(struct ref_list *refs)
 {
-       int i, l, w = 0;
+       int i, w = 0;
        for (i = 0; i < refs->index; i++) {
                if (!matches_merge_filter(refs->list[i].commit))
                        continue;
-               l = strlen(refs->list[i].name);
-               if (l > w)
-                       w = l;
+               if (refs->list[i].len > w)
+                       w = refs->list[i].len;
        }
        return w;
 }
 
+
+static void show_detached(struct ref_list *ref_list)
+{
+       struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
+
+       if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
+               struct ref_item item;
+               item.name = xstrdup("(no branch)");
+               item.len = strlen(item.name);
+               item.kind = REF_LOCAL_BRANCH;
+               item.dest = NULL;
+               item.commit = head_commit;
+               if (item.len > ref_list->maxwidth)
+                       ref_list->maxwidth = item.len;
+               print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
+               free(item.name);
+       }
+}
+
 static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
 {
        int i;
        struct ref_list ref_list;
-       struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
 
        memset(&ref_list, 0, sizeof(ref_list));
        ref_list.kinds = kinds;
+       ref_list.verbose = verbose;
+       ref_list.abbrev = abbrev;
        ref_list.with_commit = with_commit;
        if (merge_filter != NO_FILTER)
                init_revisions(&ref_list.revs, NULL);
-       for_each_ref(append_ref, &ref_list);
+       for_each_rawref(append_ref, &ref_list);
        if (merge_filter != NO_FILTER) {
                struct commit *filter;
                filter = lookup_commit_reference_gently(merge_filter_ref, 0);
@@ -393,24 +465,18 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
        qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
        detached = (detached && (kinds & REF_LOCAL_BRANCH));
-       if (detached && head_commit &&
-           is_descendant_of(head_commit, with_commit)) {
-               struct ref_item item;
-               item.name = xstrdup("(no branch)");
-               item.kind = REF_LOCAL_BRANCH;
-               item.commit = head_commit;
-               if (strlen(item.name) > ref_list.maxwidth)
-                       ref_list.maxwidth = strlen(item.name);
-               print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
-               free(item.name);
-       }
+       if (detached)
+               show_detached(&ref_list);
 
        for (i = 0; i < ref_list.index; i++) {
                int current = !detached &&
                        (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
                        !strcmp(ref_list.list[i].name, head);
+               char *prefix = (kinds != REF_REMOTE_BRANCH &&
+                               ref_list.list[i].kind == REF_REMOTE_BRANCH)
+                               ? "remotes/" : "";
                print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
-                              abbrev, current);
+                              abbrev, current, prefix);
        }
 
        free_ref_list(&ref_list);
@@ -421,22 +487,27 @@ static void rename_branch(const char *oldname, const char *newname, int force)
        struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
        unsigned char sha1[20];
        struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
+       int recovery = 0;
 
        if (!oldname)
                die("cannot rename the current branch while not on any.");
 
-       strbuf_addf(&oldref, "refs/heads/%s", oldname);
-
-       if (check_ref_format(oldref.buf))
-               die("Invalid branch name: %s", oldref.buf);
-
-       strbuf_addf(&newref, "refs/heads/%s", newname);
+       if (strbuf_check_branch_ref(&oldref, oldname)) {
+               /*
+                * Bad name --- this could be an attempt to rename a
+                * ref that we used to allow to be created by accident.
+                */
+               if (resolve_ref(oldref.buf, sha1, 1, NULL))
+                       recovery = 1;
+               else
+                       die("Invalid branch name: '%s'", oldname);
+       }
 
-       if (check_ref_format(newref.buf))
-               die("Invalid branch name: %s", newref.buf);
+       if (strbuf_check_branch_ref(&newref, newname))
+               die("Invalid branch name: '%s'", newname);
 
        if (resolve_ref(newref.buf, sha1, 1, NULL) && !force)
-               die("A branch named '%s' already exists.", newname);
+               die("A branch named '%s' already exists.", newref.buf + 11);
 
        strbuf_addf(&logmsg, "Branch: renamed %s to %s",
                 oldref.buf, newref.buf);
@@ -445,6 +516,9 @@ static void rename_branch(const char *oldname, const char *newname, int force)
                die("Branch rename failed");
        strbuf_release(&logmsg);
 
+       if (recovery)
+               warning("Renamed a misnamed branch '%s' away", oldref.buf + 11);
+
        /* no need to pass logmsg here as HEAD didn't really move */
        if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
                die("Branch renamed to %s, but HEAD is not updated!", newname);
index 8fad19daedef8a38674ee35cd543983bad610857..43ffe7ffae90322d757e110d8693a936209236f0 100644 (file)
@@ -201,8 +201,8 @@ static int batch_objects(int print_contents)
 }
 
 static const char * const cat_file_usage[] = {
-       "git cat-file [-t|-s|-e|-p|<type>] <sha1>",
-       "git cat-file [--batch|--batch-check] < <list_of_sha1s>",
+       "git cat-file (-t|-s|-e|-p|<type>) <object>",
+       "git cat-file (--batch|--batch-check) < <list_of_objects>",
        NULL
 };
 
index 701de439ae0f508f3a8ab41559230357be60637e..f9381e07eaeda673e91ef6250eca4027c5ba0278 100644 (file)
@@ -5,9 +5,18 @@
 #include "cache.h"
 #include "refs.h"
 #include "builtin.h"
+#include "strbuf.h"
 
 int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
 {
+       if (argc == 3 && !strcmp(argv[1], "--branch")) {
+               struct strbuf sb = STRBUF_INIT;
+
+               if (strbuf_check_branch_ref(&sb, argv[2]))
+                       die("'%s' is not a valid branch name", argv[2]);
+               printf("%s\n", sb.buf + 11);
+               exit(0);
+       }
        if (argc != 2)
                usage("git check-ref-format refname");
        return !!check_ref_format(argv[1]);
index 0d534bc023a1a3367bdf19f6450ce17e627959f0..afe35e246c5ab2b262683308def787c6abce2a83 100644 (file)
@@ -124,7 +124,7 @@ static int checkout_file(const char *name, int prefix_length)
 static void checkout_all(const char *prefix, int prefix_length)
 {
        int i, errs = 0;
-       struct cache_entrylast_ce = NULL;
+       struct cache_entry *last_ce = NULL;
 
        for (i = 0; i < active_nr ; i++) {
                struct cache_entry *ce = active_cache[i];
@@ -278,7 +278,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                p = prefix_path(prefix, prefix_length, arg);
                checkout_file(p, prefix_length);
                if (p < arg || p > arg + strlen(arg))
-                       free((char*)p);
+                       free((char *)p);
        }
 
        if (read_from_stdin) {
index ffdb33aef596740890362a0e237d33131a906f45..b8a4b0139b3b9c2f731315413a73db30edaf9231 100644 (file)
@@ -179,7 +179,7 @@ static int checkout_merged(int pos, struct checkout *state)
        /*
         * NEEDSWORK:
         * There is absolutely no reason to write this as a blob object
-        * and create a phoney cache entry just to leak.  This hack is
+        * and create a phony cache entry just to leak.  This hack is
         * primarily to get to the write_entry() machinery that massages
         * the contents to work-tree format and writes out which only
         * allows it for a cache entry.  The code in write_entry() needs
@@ -216,7 +216,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
        newfd = hold_locked_index(lock_file, 1);
-       if (read_cache() < 0)
+       if (read_cache_preload(pathspec) < 0)
                return error("corrupt index file");
 
        if (source_tree)
@@ -293,6 +293,8 @@ static void show_local_changes(struct object *head)
        init_revisions(&rev, NULL);
        rev.abbrev = 0;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
+       if (diff_setup_done(&rev.diffopt) < 0)
+               die("diff_setup_done failed");
        add_pending_object(&rev, head, NULL);
        run_diff_index(&rev, 0);
 }
@@ -349,16 +351,11 @@ struct branch_info {
 static void setup_branch_path(struct branch_info *branch)
 {
        struct strbuf buf = STRBUF_INIT;
-       int ret;
 
-       if ((ret = interpret_nth_last_branch(branch->name, &buf))
-           && ret == strlen(branch->name)) {
+       strbuf_branchname(&buf, branch->name);
+       if (strcmp(buf.buf, branch->name))
                branch->name = xstrdup(buf.buf);
-               strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
-       } else {
-               strbuf_addstr(&buf, "refs/heads/");
-               strbuf_addstr(&buf, branch->name);
-       }
+       strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
        branch->path = strbuf_detach(&buf, NULL);
 }
 
@@ -369,7 +366,7 @@ static int merge_working_tree(struct checkout_opts *opts,
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
        int newfd = hold_locked_index(lock_file, 1);
 
-       if (read_cache() < 0)
+       if (read_cache_preload(NULL) < 0)
                return error("corrupt index file");
 
        if (opts->force) {
@@ -403,7 +400,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                topts.verbose_update = !opts->quiet;
                topts.fn = twoway_merge;
                topts.dir = xcalloc(1, sizeof(*topts.dir));
-               topts.dir->show_ignored = 1;
+               topts.dir->flags |= DIR_SHOW_IGNORED;
                topts.dir->exclude_per_dir = ".gitignore";
                tree = parse_tree_indirect(old->commit->object.sha1);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
@@ -544,18 +541,10 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
                parse_commit(new->commit);
        }
 
-       /*
-        * If we were on a detached HEAD, but we are now moving to
-        * a new commit, we want to mention the old commit once more
-        * to remind the user that it might be lost.
-        */
-       if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
-               describe_detached_head("Previous HEAD position was", old.commit);
-
        if (!old.commit && !opts->force) {
                if (!opts->quiet) {
-                       fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n");
-                       fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name);
+                       warning("You appear to be on a branch yet to be born.");
+                       warning("Forcing checkout of %s.", new->name);
                }
                opts->force = 1;
        }
@@ -564,6 +553,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
        if (ret)
                return ret;
 
+       /*
+        * If we were on a detached HEAD, but have now moved to
+        * a new commit, we want to mention the old commit once more
+        * to remind the user that it might be lost.
+        */
+       if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
+               describe_detached_head("Previous HEAD position was", old.commit);
+
        update_refs_for_switch(opts, &old, new);
 
        ret = post_checkout_hook(old.commit, new->commit, 1);
@@ -734,12 +731,11 @@ no_reference:
 
        if (opts.new_branch) {
                struct strbuf buf = STRBUF_INIT;
-               strbuf_addstr(&buf, "refs/heads/");
-               strbuf_addstr(&buf, opts.new_branch);
+               if (strbuf_check_branch_ref(&buf, opts.new_branch))
+                       die("git checkout: we do not like '%s' as a branch name.",
+                           opts.new_branch);
                if (!get_sha1(buf.buf, rev))
                        die("git checkout: branch %s already exists", opts.new_branch);
-               if (check_ref_format(buf.buf))
-                       die("git checkout: we do not like '%s' as a branch name.", opts.new_branch);
                strbuf_release(&buf);
        }
 
index f78c2fb108bc667079290f9b2fa82f47da5eb34c..c5ad33d3e665f6d1613df2dfba235b03765565ac 100644 (file)
@@ -60,7 +60,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
        memset(&dir, 0, sizeof(dir));
        if (ignored_only)
-               dir.show_ignored = 1;
+               dir.flags |= DIR_SHOW_IGNORED;
 
        if (ignored && ignored_only)
                die("-x and -X cannot be used together");
@@ -69,7 +69,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                die("clean.requireForce%s set and -n or -f not given; "
                    "refusing to clean", config_set ? "" : " not");
 
-       dir.show_other_directories = 1;
+       dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
 
        if (!ignored)
                setup_standard_excludes(&dir);
index 1c1d72911757c116b0cd19dd4b50c10ebbd68214..6605e83407ce70dbb77a4be51cfadec60b3db6ad 100644 (file)
@@ -20,6 +20,9 @@
 #include "dir.h"
 #include "pack-refs.h"
 #include "sigchain.h"
+#include "branch.h"
+#include "remote.h"
+#include "run-command.h"
 
 /*
  * Overall FIXMEs:
@@ -225,7 +228,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
                }
 
                if (unlink(dest->buf) && errno != ENOENT)
-                       die("failed to unlink %s", dest->buf);
+                       die("failed to unlink %s: %s",
+                           dest->buf, strerror(errno));
                if (!option_no_hardlinks) {
                        if (!link(src->buf, dest->buf))
                                continue;
@@ -267,7 +271,7 @@ static const struct ref *clone_local(const char *src_repo,
 
 static const char *junk_work_tree;
 static const char *junk_git_dir;
-pid_t junk_pid;
+static pid_t junk_pid;
 
 static void remove_junk(void)
 {
@@ -293,43 +297,6 @@ static void remove_junk_on_signal(int signo)
        raise(signo);
 }
 
-static const struct ref *locate_head(const struct ref *refs,
-                                    const struct ref *mapped_refs,
-                                    const struct ref **remote_head_p)
-{
-       const struct ref *remote_head = NULL;
-       const struct ref *remote_master = NULL;
-       const struct ref *r;
-       for (r = refs; r; r = r->next)
-               if (!strcmp(r->name, "HEAD"))
-                       remote_head = r;
-
-       for (r = mapped_refs; r; r = r->next)
-               if (!strcmp(r->name, "refs/heads/master"))
-                       remote_master = r;
-
-       if (remote_head_p)
-               *remote_head_p = remote_head;
-
-       /* If there's no HEAD value at all, never mind. */
-       if (!remote_head)
-               return NULL;
-
-       /* If refs/heads/master could be right, it is. */
-       if (remote_master && !hashcmp(remote_master->old_sha1,
-                                     remote_head->old_sha1))
-               return remote_master;
-
-       /* Look for another ref that points there */
-       for (r = mapped_refs; r; r = r->next)
-               if (r != remote_head &&
-                   !hashcmp(r->old_sha1, remote_head->old_sha1))
-                       return r;
-
-       /* Nothing is the same */
-       return NULL;
-}
-
 static struct ref *write_remote_refs(const struct ref *refs,
                struct refspec *refspec, const char *reflog)
 {
@@ -350,23 +317,8 @@ static struct ref *write_remote_refs(const struct ref *refs,
        return local_refs;
 }
 
-static void install_branch_config(const char *local,
-                                 const char *origin,
-                                 const char *remote)
-{
-       struct strbuf key = STRBUF_INIT;
-       strbuf_addf(&key, "branch.%s.remote", local);
-       git_config_set(key.buf, origin);
-       strbuf_reset(&key);
-       strbuf_addf(&key, "branch.%s.merge", local);
-       git_config_set(key.buf, remote);
-       strbuf_release(&key);
-}
-
 int cmd_clone(int argc, const char **argv, const char *prefix)
 {
-       int use_local_hardlinks = 1;
-       int use_separate_remote = 1;
        int is_bundle = 0;
        struct stat buf;
        const char *repo_name, *repo, *work_tree, *git_dir;
@@ -377,8 +329,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
        struct transport *transport = NULL;
        char *src_ref_prefix = "refs/heads/";
+       int err = 0;
 
-       struct refspec refspec;
+       struct refspec *refspec;
+       const char *fetch_pattern;
 
        junk_pid = getpid();
 
@@ -388,9 +342,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (argc == 0)
                die("You must specify a repository to clone.");
 
-       if (option_no_hardlinks)
-               use_local_hardlinks = 0;
-
        if (option_mirror)
                option_bare = 1;
 
@@ -399,7 +350,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        die("--bare and --origin %s options are incompatible.",
                            option_origin);
                option_no_checkout = 1;
-               use_separate_remote = 0;
        }
 
        if (!option_origin)
@@ -457,7 +407,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        atexit(remove_junk);
        sigchain_push_common(remove_junk_on_signal);
 
-       setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1);
+       setenv(CONFIG_ENVIRONMENT, mkpath("%s/config", git_dir), 1);
 
        if (safe_create_leading_directories_const(git_dir) < 0)
                die("could not create leading directories of '%s'", git_dir);
@@ -487,8 +437,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
        }
 
+       strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);
+
        if (option_mirror || !option_bare) {
                /* Configure the remote */
+               strbuf_addf(&key, "remote.%s.fetch", option_origin);
+               git_config_set_multivar(key.buf, value.buf, "^$", 0);
+               strbuf_reset(&key);
+
                if (option_mirror) {
                        strbuf_addf(&key, "remote.%s.mirror", option_origin);
                        git_config_set(key.buf, "true");
@@ -497,19 +453,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
                strbuf_addf(&key, "remote.%s.url", option_origin);
                git_config_set(key.buf, repo);
-                       strbuf_reset(&key);
-
-               strbuf_addf(&key, "remote.%s.fetch", option_origin);
-               strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);
-               git_config_set_multivar(key.buf, value.buf, "^$", 0);
                strbuf_reset(&key);
-               strbuf_reset(&value);
        }
 
-       refspec.force = 0;
-       refspec.pattern = 1;
-       refspec.src = src_ref_prefix;
-       refspec.dst = branch_top.buf;
+       fetch_pattern = value.buf;
+       refspec = parse_fetch_refspec(1, &fetch_pattern);
+
+       strbuf_reset(&value);
 
        if (path && !is_bundle)
                refs = clone_local(path, git_dir);
@@ -543,9 +493,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (refs) {
                clear_extra_refs();
 
-               mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);
+               mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
 
-               head_points_at = locate_head(refs, mapped_refs, &remote_head);
+               remote_head = find_ref_by_name(refs, "HEAD");
+               head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
        }
        else {
                warning("You appear to have cloned an empty repository.");
@@ -553,7 +504,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                remote_head = NULL;
                option_no_checkout = 1;
                if (!option_bare)
-                       install_branch_config("master", option_origin,
+                       install_branch_config(0, "master", option_origin,
                                              "refs/heads/master");
        }
 
@@ -583,7 +534,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                                      head_points_at->peer_ref->name,
                                      reflog_msg.buf);
 
-                       install_branch_config(head, option_origin,
+                       install_branch_config(0, head, option_origin,
                                              head_points_at->name);
                }
        } else if (remote_head) {
@@ -633,6 +584,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                if (write_cache(fd, active_cache, active_nr) ||
                    commit_locked_index(lock_file))
                        die("unable to write new index file");
+
+               err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
+                               sha1_to_hex(remote_head->old_sha1), "1", NULL);
        }
 
        strbuf_release(&reflog_msg);
@@ -640,5 +594,5 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        strbuf_release(&key);
        strbuf_release(&value);
        junk_pid = 0;
-       return 0;
+       return err;
 }
index 81371b1d2698a48dba36046d7ff9d849f830a762..baaa75cf908d57e6d8540b6483684400c0b2494b 100644 (file)
@@ -699,7 +699,11 @@ static int parse_and_validate_options(int argc, const char *argv[],
 
        argc = parse_options(argc, argv, builtin_commit_options, usage, 0);
        logfile = parse_options_fix_filename(prefix, logfile);
+       if (logfile)
+               logfile = xstrdup(logfile);
        template_file = parse_options_fix_filename(prefix, template_file);
+       if (template_file)
+               template_file = xstrdup(template_file);
 
        if (force_author && !strchr(force_author, '>'))
                force_author = find_author_by_nickname(force_author);
index f71016204b540d0d935323c909a0ffccb1abdbe2..a81bc8bbf033fac83ad6deecbf4a67ba44e06a0b 100644 (file)
@@ -1,9 +1,12 @@
 #include "builtin.h"
 #include "cache.h"
 #include "color.h"
+#include "parse-options.h"
 
-static const char git_config_set_usage[] =
-"git config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]";
+static const char *const builtin_config_usage[] = {
+       "git config [options]",
+       NULL
+};
 
 static char *key;
 static regex_t *key_regexp;
@@ -16,7 +19,67 @@ static int seen;
 static char delim = '=';
 static char key_delim = ' ';
 static char term = '\n';
-static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW;
+
+static int use_global_config, use_system_config;
+static const char *given_config_file;
+static int actions, types;
+static const char *get_color_slot, *get_colorbool_slot;
+static int end_null;
+
+#define ACTION_GET (1<<0)
+#define ACTION_GET_ALL (1<<1)
+#define ACTION_GET_REGEXP (1<<2)
+#define ACTION_REPLACE_ALL (1<<3)
+#define ACTION_ADD (1<<4)
+#define ACTION_UNSET (1<<5)
+#define ACTION_UNSET_ALL (1<<6)
+#define ACTION_RENAME_SECTION (1<<7)
+#define ACTION_REMOVE_SECTION (1<<8)
+#define ACTION_LIST (1<<9)
+#define ACTION_EDIT (1<<10)
+#define ACTION_SET (1<<11)
+#define ACTION_SET_ALL (1<<12)
+#define ACTION_GET_COLOR (1<<13)
+#define ACTION_GET_COLORBOOL (1<<14)
+
+#define TYPE_BOOL (1<<0)
+#define TYPE_INT (1<<1)
+#define TYPE_BOOL_OR_INT (1<<2)
+
+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_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),
+       OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL),
+       OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP),
+       OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name value [value_regex]", ACTION_REPLACE_ALL),
+       OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD),
+       OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET),
+       OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL),
+       OPT_BIT(0, "rename-section", &actions, "rename section: old-name new-name", ACTION_RENAME_SECTION),
+       OPT_BIT(0, "remove-section", &actions, "remove a section: name", ACTION_REMOVE_SECTION),
+       OPT_BIT('l', "list", &actions, "list all", ACTION_LIST),
+       OPT_BIT('e', "edit", &actions, "opens an editor", ACTION_EDIT),
+       OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"),
+       OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"),
+       OPT_GROUP("Type"),
+       OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL),
+       OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT),
+       OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT),
+       OPT_GROUP("Other"),
+       OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"),
+       OPT_END(),
+};
+
+static void check_argc(int argc, int min, int max) {
+       if (argc >= min && argc <= max)
+               return;
+       error("wrong number of arguments");
+       usage_with_options(builtin_config_usage, builtin_config_options);
+}
 
 static int show_all_config(const char *key_, const char *value_, void *cb)
 {
@@ -27,7 +90,7 @@ static int show_all_config(const char *key_, const char *value_, void *cb)
        return 0;
 }
 
-static int show_config(const char* key_, const char* value_, void *cb)
+static int show_config(const char *key_, const char *value_, void *cb)
 {
        char value[256];
        const char *vptr = value;
@@ -49,11 +112,11 @@ static int show_config(const char* key_, const char* value_, void *cb)
        }
        if (seen && !do_all)
                dup_error = 1;
-       if (type == T_INT)
+       if (types == TYPE_INT)
                sprintf(value, "%d", git_config_int(key_, value_?value_:""));
-       else if (type == T_BOOL)
+       else if (types == TYPE_BOOL)
                vptr = git_config_bool(key_, value_) ? "true" : "false";
-       else if (type == T_BOOL_OR_INT) {
+       else if (types == TYPE_BOOL_OR_INT) {
                int is_bool, v;
                v = git_config_bool_or_int(key_, value_, &is_bool);
                if (is_bool)
@@ -74,7 +137,7 @@ static int show_config(const char* key_, const char* value_, void *cb)
        return 0;
 }
 
-static int get_value(const char* key_, const char* regex_)
+static int get_value(const char *key_, const char *regex_)
 {
        int ret = -1;
        char *tl;
@@ -152,18 +215,18 @@ static char *normalize_value(const char *key, const char *value)
        if (!value)
                return NULL;
 
-       if (type == T_RAW)
+       if (types == 0)
                normalized = xstrdup(value);
        else {
                normalized = xmalloc(64);
-               if (type == T_INT) {
+               if (types == TYPE_INT) {
                        int v = git_config_int(key, value);
                        sprintf(normalized, "%d", v);
                }
-               else if (type == T_BOOL)
+               else if (types == TYPE_BOOL)
                        sprintf(normalized, "%s",
                                git_config_bool(key, value) ? "true" : "false");
-               else if (type == T_BOOL_OR_INT) {
+               else if (types == TYPE_BOOL_OR_INT) {
                        int is_bool, v;
                        v = git_config_bool_or_int(key, value, &is_bool);
                        if (!is_bool)
@@ -178,6 +241,7 @@ static char *normalize_value(const char *key, const char *value)
 
 static int get_color_found;
 static const char *get_color_slot;
+static const char *get_colorbool_slot;
 static char parsed_color[COLOR_MAXLEN];
 
 static int git_get_color_config(const char *var, const char *value, void *cb)
@@ -191,29 +255,8 @@ static int git_get_color_config(const char *var, const char *value, void *cb)
        return 0;
 }
 
-static int get_color(int argc, const char **argv)
+static void get_color(const char *def_color)
 {
-       /*
-        * grab the color setting for the given slot from the configuration,
-        * or parse the default value if missing, and return ANSI color
-        * escape sequence.
-        *
-        * e.g.
-        * git config --get-color color.diff.whitespace "blue reverse"
-        */
-       const char *def_color = NULL;
-
-       switch (argc) {
-       default:
-               usage(git_config_set_usage);
-       case 2:
-               def_color = argv[1];
-               /* fallthru */
-       case 1:
-               get_color_slot = argv[0];
-               break;
-       }
-
        get_color_found = 0;
        parsed_color[0] = '\0';
        git_config(git_get_color_config, NULL);
@@ -222,7 +265,6 @@ static int get_color(int argc, const char **argv)
                color_parse(def_color, "command line", parsed_color);
 
        fputs(parsed_color, stdout);
-       return 0;
 }
 
 static int stdout_is_tty;
@@ -231,7 +273,7 @@ static int get_diff_color_found;
 static int git_get_colorbool_config(const char *var, const char *value,
                void *cb)
 {
-       if (!strcmp(var, get_color_slot)) {
+       if (!strcmp(var, get_colorbool_slot)) {
                get_colorbool_found =
                        git_config_colorbool(var, value, stdout_is_tty);
        }
@@ -246,183 +288,190 @@ static int git_get_colorbool_config(const char *var, const char *value,
        return 0;
 }
 
-static int get_colorbool(int argc, const char **argv)
+static int get_colorbool(int print)
 {
-       /*
-        * git config --get-colorbool <slot> [<stdout-is-tty>]
-        *
-        * returns "true" or "false" depending on how <slot>
-        * is configured.
-        */
-
-       if (argc == 2)
-               stdout_is_tty = git_config_bool("command line", argv[1]);
-       else if (argc == 1)
-               stdout_is_tty = isatty(1);
-       else
-               usage(git_config_set_usage);
        get_colorbool_found = -1;
        get_diff_color_found = -1;
-       get_color_slot = argv[0];
        git_config(git_get_colorbool_config, NULL);
 
        if (get_colorbool_found < 0) {
-               if (!strcmp(get_color_slot, "color.diff"))
+               if (!strcmp(get_colorbool_slot, "color.diff"))
                        get_colorbool_found = get_diff_color_found;
                if (get_colorbool_found < 0)
                        get_colorbool_found = git_use_color_default;
        }
 
-       if (argc == 1) {
-               return get_colorbool_found ? 0 : 1;
-       } else {
+       if (print) {
                printf("%s\n", get_colorbool_found ? "true" : "false");
                return 0;
-       }
+       } else
+               return get_colorbool_found ? 0 : 1;
 }
 
-int cmd_config(int argc, const char **argv, const char *prefix)
+int cmd_config(int argc, const char **argv, const char *unused_prefix)
 {
        int nongit;
-       charvalue;
-       const char *file = setup_git_directory_gently(&nongit);
+       char *value;
+       const char *prefix = setup_git_directory_gently(&nongit);
 
        config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);
 
-       while (1 < argc) {
-               if (!strcmp(argv[1], "--int"))
-                       type = T_INT;
-               else if (!strcmp(argv[1], "--bool"))
-                       type = T_BOOL;
-               else if (!strcmp(argv[1], "--bool-or-int"))
-                       type = T_BOOL_OR_INT;
-               else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) {
-                       if (argc != 2)
-                               usage(git_config_set_usage);
-                       if (git_config(show_all_config, NULL) < 0 &&
-                                       file && errno)
-                               die("unable to read config file %s: %s", file,
-                                   strerror(errno));
-                       return 0;
-               }
-               else if (!strcmp(argv[1], "--global")) {
-                       char *home = getenv("HOME");
-                       if (home) {
-                               char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
-                               config_exclusive_filename = user_config;
-                       } else {
-                               die("$HOME not set");
-                       }
-               }
-               else if (!strcmp(argv[1], "--system"))
-                       config_exclusive_filename = git_etc_gitconfig();
-               else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) {
-                       if (argc < 3)
-                               usage(git_config_set_usage);
-                       if (!is_absolute_path(argv[2]) && file)
-                               file = prefix_filename(file, strlen(file),
-                                                      argv[2]);
-                       else
-                               file = argv[2];
-                       config_exclusive_filename = file;
-                       argc--;
-                       argv++;
-               }
-               else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) {
-                       term = '\0';
-                       delim = '\n';
-                       key_delim = '\n';
-               }
-               else if (!strcmp(argv[1], "--rename-section")) {
-                       int ret;
-                       if (argc != 4)
-                               usage(git_config_set_usage);
-                       ret = git_config_rename_section(argv[2], argv[3]);
-                       if (ret < 0)
-                               return ret;
-                       if (ret == 0) {
-                               fprintf(stderr, "No such section!\n");
-                               return 1;
-                       }
-                       return 0;
-               }
-               else if (!strcmp(argv[1], "--remove-section")) {
-                       int ret;
-                       if (argc != 3)
-                               usage(git_config_set_usage);
-                       ret = git_config_rename_section(argv[2], NULL);
-                       if (ret < 0)
-                               return ret;
-                       if (ret == 0) {
-                               fprintf(stderr, "No such section!\n");
-                               return 1;
-                       }
-                       return 0;
-               } else if (!strcmp(argv[1], "--get-color")) {
-                       return get_color(argc-2, argv+2);
-               } else if (!strcmp(argv[1], "--get-colorbool")) {
-                       return get_colorbool(argc-2, argv+2);
-               } else
-                       break;
-               argc--;
-               argv++;
-       }
-
-       switch (argc) {
-       case 2:
-               return get_value(argv[1], NULL);
-       case 3:
-               if (!strcmp(argv[1], "--unset"))
-                       return git_config_set(argv[2], NULL);
-               else if (!strcmp(argv[1], "--unset-all"))
-                       return git_config_set_multivar(argv[2], NULL, NULL, 1);
-               else if (!strcmp(argv[1], "--get"))
-                       return get_value(argv[2], NULL);
-               else if (!strcmp(argv[1], "--get-all")) {
-                       do_all = 1;
-                       return get_value(argv[2], NULL);
-               } else if (!strcmp(argv[1], "--get-regexp")) {
-                       show_keys = 1;
-                       use_key_regexp = 1;
-                       do_all = 1;
-                       return get_value(argv[2], NULL);
+       argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage,
+                            PARSE_OPT_STOP_AT_NON_OPTION);
+
+       if (use_global_config + use_system_config + !!given_config_file > 1) {
+               error("only one config file at a time.");
+               usage_with_options(builtin_config_usage, builtin_config_options);
+       }
+
+       if (use_global_config) {
+               char *home = getenv("HOME");
+               if (home) {
+                       char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
+                       config_exclusive_filename = user_config;
                } else {
-                       value = normalize_value(argv[1], argv[2]);
-                       return git_config_set(argv[1], value);
+                       die("$HOME not set");
                }
-       case 4:
-               if (!strcmp(argv[1], "--unset"))
-                       return git_config_set_multivar(argv[2], NULL, argv[3], 0);
-               else if (!strcmp(argv[1], "--unset-all"))
-                       return git_config_set_multivar(argv[2], NULL, argv[3], 1);
-               else if (!strcmp(argv[1], "--get"))
-                       return get_value(argv[2], argv[3]);
-               else if (!strcmp(argv[1], "--get-all")) {
-                       do_all = 1;
-                       return get_value(argv[2], argv[3]);
-               } else if (!strcmp(argv[1], "--get-regexp")) {
-                       show_keys = 1;
-                       use_key_regexp = 1;
-                       do_all = 1;
-                       return get_value(argv[2], argv[3]);
-               } else if (!strcmp(argv[1], "--add")) {
-                       value = normalize_value(argv[2], argv[3]);
-                       return git_config_set_multivar(argv[2], value, "^$", 0);
-               } else if (!strcmp(argv[1], "--replace-all")) {
-                       value = normalize_value(argv[2], argv[3]);
-                       return git_config_set_multivar(argv[2], value, NULL, 1);
-               } else {
-                       value = normalize_value(argv[1], argv[2]);
-                       return git_config_set_multivar(argv[1], value, argv[3], 0);
+       }
+       else if (use_system_config)
+               config_exclusive_filename = git_etc_gitconfig();
+       else if (given_config_file) {
+               if (!is_absolute_path(given_config_file) && prefix)
+                       config_exclusive_filename = prefix_filename(prefix,
+                                                                   strlen(prefix),
+                                                                   argv[2]);
+               else
+                       config_exclusive_filename = given_config_file;
+       }
+
+       if (end_null) {
+               term = '\0';
+               delim = '\n';
+               key_delim = '\n';
+       }
+
+       if (HAS_MULTI_BITS(types)) {
+               error("only one type at a time.");
+               usage_with_options(builtin_config_usage, builtin_config_options);
+       }
+
+       if (get_color_slot)
+           actions |= ACTION_GET_COLOR;
+       if (get_colorbool_slot)
+           actions |= ACTION_GET_COLORBOOL;
+
+       if ((get_color_slot || get_colorbool_slot) && types) {
+               error("--get-color and variable type are incoherent");
+               usage_with_options(builtin_config_usage, builtin_config_options);
+       }
+
+       if (HAS_MULTI_BITS(actions)) {
+               error("only one action at a time.");
+               usage_with_options(builtin_config_usage, builtin_config_options);
+       }
+       if (actions == 0)
+               switch (argc) {
+               case 1: actions = ACTION_GET; break;
+               case 2: actions = ACTION_SET; break;
+               case 3: actions = ACTION_SET_ALL; break;
+               default:
+                       usage_with_options(builtin_config_usage, builtin_config_options);
                }
-       case 5:
-               if (!strcmp(argv[1], "--replace-all")) {
-                       value = normalize_value(argv[2], argv[3]);
-                       return git_config_set_multivar(argv[2], value, argv[4], 1);
+
+       if (actions == ACTION_LIST) {
+               check_argc(argc, 0, 0);
+               if (git_config(show_all_config, NULL) < 0) {
+                       if (config_exclusive_filename)
+                               die("unable to read config file %s: %s",
+                                   config_exclusive_filename, strerror(errno));
+                       else
+                               die("error processing config file(s)");
                }
-       case 1:
-       default:
-               usage(git_config_set_usage);
        }
+       else if (actions == ACTION_EDIT) {
+               check_argc(argc, 0, 0);
+               if (!config_exclusive_filename && nongit)
+                       die("not in a git directory");
+               git_config(git_default_config, NULL);
+               launch_editor(config_exclusive_filename ?
+                             config_exclusive_filename : git_path("config"),
+                             NULL, NULL);
+       }
+       else if (actions == ACTION_SET) {
+               check_argc(argc, 2, 2);
+               value = normalize_value(argv[0], argv[1]);
+               return git_config_set(argv[0], value);
+       }
+       else if (actions == ACTION_SET_ALL) {
+               check_argc(argc, 2, 3);
+               value = normalize_value(argv[0], argv[1]);
+               return git_config_set_multivar(argv[0], value, argv[2], 0);
+       }
+       else if (actions == ACTION_ADD) {
+               check_argc(argc, 2, 2);
+               value = normalize_value(argv[0], argv[1]);
+               return git_config_set_multivar(argv[0], value, "^$", 0);
+       }
+       else if (actions == ACTION_REPLACE_ALL) {
+               check_argc(argc, 2, 3);
+               value = normalize_value(argv[0], argv[1]);
+               return git_config_set_multivar(argv[0], value, argv[2], 1);
+       }
+       else if (actions == ACTION_GET) {
+               check_argc(argc, 1, 2);
+               return get_value(argv[0], argv[1]);
+       }
+       else if (actions == ACTION_GET_ALL) {
+               do_all = 1;
+               check_argc(argc, 1, 2);
+               return get_value(argv[0], argv[1]);
+       }
+       else if (actions == ACTION_GET_REGEXP) {
+               show_keys = 1;
+               use_key_regexp = 1;
+               do_all = 1;
+               check_argc(argc, 1, 2);
+               return get_value(argv[0], argv[1]);
+       }
+       else if (actions == ACTION_UNSET) {
+               check_argc(argc, 1, 2);
+               if (argc == 2)
+                       return git_config_set_multivar(argv[0], NULL, argv[1], 0);
+               else
+                       return git_config_set(argv[0], NULL);
+       }
+       else if (actions == ACTION_UNSET_ALL) {
+               check_argc(argc, 1, 2);
+               return git_config_set_multivar(argv[0], NULL, argv[1], 1);
+       }
+       else if (actions == ACTION_RENAME_SECTION) {
+               int ret;
+               check_argc(argc, 2, 2);
+               ret = git_config_rename_section(argv[0], argv[1]);
+               if (ret < 0)
+                       return ret;
+               if (ret == 0)
+                       die("No such section!");
+       }
+       else if (actions == ACTION_REMOVE_SECTION) {
+               int ret;
+               check_argc(argc, 1, 1);
+               ret = git_config_rename_section(argv[0], NULL);
+               if (ret < 0)
+                       return ret;
+               if (ret == 0)
+                       die("No such section!");
+       }
+       else if (actions == ACTION_GET_COLOR) {
+               get_color(argv[0]);
+       }
+       else if (actions == ACTION_GET_COLORBOOL) {
+               if (argc == 1)
+                       stdout_is_tty = git_config_bool("command line", argv[0]);
+               else if (argc == 0)
+                       stdout_is_tty = isatty(1);
+               return get_colorbool(argc != 0);
+       }
+
        return 0;
 }
index 3a007ed1cafcab82f466d355b63e416b06eb8864..63c6a19da5b38bc7c00c624c080ba0afbb10ff8a 100644 (file)
@@ -334,7 +334,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                die("--long is incompatible with --abbrev=0");
 
        if (contains) {
-               const char **args = xmalloc((7 + argc) * sizeof(char*));
+               const char **args = xmalloc((7 + argc) * sizeof(char *));
                int i = 0;
                args[i++] = "name-rev";
                args[i++] = "--name-only";
@@ -349,7 +349,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                                args[i++] = s;
                        }
                }
-               memcpy(args + i, argv, argc * sizeof(char*));
+               memcpy(args + i, argv, argc * sizeof(char *));
                args[i + argc] = NULL;
                return cmd_name_rev(i + argc, args, prefix);
        }
index 8ecefd4f0f99117534deb9ca7d4446ade5c00223..79cedb72c44dca3edf400d86e401a93531b54407 100644 (file)
@@ -102,7 +102,6 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
 
        init_revisions(opt, prefix);
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
-       nr_sha1 = 0;
        opt->abbrev = 0;
        opt->diff = 1;
        argc = setup_revisions(argc, argv, opt, NULL);
index f171ee4a2beb3cfd6eec203f422799249508ee13..9091481fd537c7bc855183232a75dfa6e386b8c3 100644 (file)
@@ -221,7 +221,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        if (message)
                message += 2;
 
-       if (commit->parents) {
+       if (commit->parents &&
+           get_object_mark(&commit->parents->item->object) != 0) {
                parse_commit(commit->parents->item);
                diff_tree_sha1(commit->parents->item->tree->object.sha1,
                               commit->tree->object.sha1, "", &rev->diffopt);
@@ -427,21 +428,27 @@ static void export_marks(char *file)
        uint32_t mark;
        struct object_decoration *deco = idnums.hash;
        FILE *f;
+       int e = 0;
 
        f = fopen(file, "w");
        if (!f)
-               error("Unable to open marks file %s for writing", file);
+               error("Unable to open marks file %s for writing.", file);
 
        for (i = 0; i < idnums.size; i++) {
                if (deco->base && deco->base->type == 1) {
                        mark = ptr_to_mark(deco->decoration);
-                       fprintf(f, ":%"PRIu32" %s\n", mark,
-                               sha1_to_hex(deco->base->sha1));
+                       if (fprintf(f, ":%"PRIu32" %s\n", mark,
+                               sha1_to_hex(deco->base->sha1)) < 0) {
+                           e = 1;
+                           break;
+                       }
                }
                deco++;
        }
 
-       if (ferror(f) || fclose(f))
+       e |= ferror(f);
+       e |= fclose(f);
+       if (e)
                error("Unable to write marks file %s.", file);
 }
 
index 1effdc0d6b88c2a69ea36f18593556fc83a14fbf..629735f54723a3bc3b24f8c91e9a6226ae28d742 100644 (file)
@@ -112,7 +112,7 @@ static void mark_common(struct commit *commit,
   Get the next rev to send, ignoring the common.
 */
 
-static const unsigned charget_rev(void)
+static const unsigned char *get_rev(void)
 {
        struct commit *commit = NULL;
 
@@ -217,9 +217,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        if (args.depth > 0) {
                char line[1024];
                unsigned char sha1[20];
-               int len;
 
-               while ((len = packet_read_line(fd[0], line, sizeof(line)))) {
+               while (packet_read_line(fd[0], line, sizeof(line))) {
                        if (!prefixcmp(line, "shallow ")) {
                                if (get_sha1_hex(line + 8, sha1))
                                        die("invalid shallow line: %s", line);
@@ -484,7 +483,9 @@ static int sideband_demux(int fd, void *data)
 {
        int *xd = data;
 
-       return recv_sideband("fetch-pack", xd[0], fd, 2);
+       int ret = recv_sideband("fetch-pack", xd[0], fd);
+       close(fd);
+       return ret;
 }
 
 static int get_pack(int xd[2], char **pack_lockfile)
@@ -612,7 +613,7 @@ static struct ref *do_fetch_pack(int fd[2],
                        /* When cloning, it is not unusual to have
                         * no common commit.
                         */
-                       fprintf(stderr, "warning: no common commits\n");
+                       warning("no common commits");
 
        if (get_pack(fd, pack_lockfile))
                die("git fetch-pack: fetch failed.");
@@ -812,15 +813,13 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                int fd;
 
                mtime.sec = st.st_mtime;
-#ifdef USE_NSEC
-               mtime.usec = st.st_mtim.usec;
-#endif
+               mtime.nsec = ST_MTIME_NSEC(st);
                if (stat(shallow, &st)) {
                        if (mtime.sec)
                                die("shallow file was removed during fetch");
                } else if (st.st_mtime != mtime.sec
 #ifdef USE_NSEC
-                               || st.st_mtim.usec != mtime.usec
+                               || ST_MTIME_NSEC(st) != mtime.nsec
 #endif
                          )
                        die("shallow file was changed during fetch");
@@ -828,7 +827,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                fd = hold_lock_file_for_update(&lock, shallow,
                                               LOCK_DIE_ON_ERROR);
                if (!write_shallow_commits(fd, 0)) {
-                       unlink(shallow);
+                       unlink_or_warn(shallow);
                        rollback_lock_file(&lock);
                } else {
                        commit_lock_file(&lock);
index 7fb35fca9d1b57dacaebfd3cb9b2af4d035e750c..1f7a3f1ce66434a84ff96ef352245862fe088a70 100644 (file)
@@ -167,6 +167,9 @@ static struct ref *get_ref_map(struct transport *transport,
        return ref_map;
 }
 
+#define STORE_REF_ERROR_OTHER 1
+#define STORE_REF_ERROR_DF_CONFLICT 2
+
 static int s_update_ref(const char *action,
                        struct ref *ref,
                        int check_old)
@@ -181,9 +184,11 @@ static int s_update_ref(const char *action,
        lock = lock_any_ref_for_update(ref->name,
                                       check_old ? ref->old_sha1 : NULL, 0);
        if (!lock)
-               return 2;
+               return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
+                                         STORE_REF_ERROR_OTHER;
        if (write_ref_sha1(lock, ref->new_sha1, msg) < 0)
-               return 2;
+               return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
+                                         STORE_REF_ERROR_OTHER;
        return 0;
 }
 
@@ -197,11 +202,7 @@ static int update_local_ref(struct ref *ref,
        struct commit *current = NULL, *updated;
        enum object_type type;
        struct branch *current_branch = branch_get(NULL);
-       const char *pretty_ref = ref->name + (
-               !prefixcmp(ref->name, "refs/heads/") ? 11 :
-               !prefixcmp(ref->name, "refs/tags/") ? 10 :
-               !prefixcmp(ref->name, "refs/remotes/") ? 13 :
-               0);
+       const char *pretty_ref = prettify_ref(ref);
 
        *display = 0;
        type = sha1_object_info(ref->new_sha1, NULL);
@@ -381,7 +382,7 @@ static int store_updated_refs(const char *url, const char *remote_name,
                }
        }
        fclose(fp);
-       if (rc & 2)
+       if (rc & STORE_REF_ERROR_DF_CONFLICT)
                error("some local refs could not be updated; try running\n"
                      " 'git remote prune %s' to remove any old, conflicting "
                      "branches", remote_name);
@@ -544,7 +545,8 @@ static void check_not_current_branch(struct ref *ref_map)
        for (; ref_map; ref_map = ref_map->next)
                if (ref_map->peer_ref && !strcmp(current_branch->refname,
                                        ref_map->peer_ref->name))
-                       die("Refusing to fetch into current branch");
+                       die("Refusing to fetch into current branch %s "
+                           "of non-bare repository", current_branch->refname);
 }
 
 static int do_fetch(struct transport *transport,
index df18f4070f3877ed907476f2179590001ec972b7..fae1482ba91937232f427a3f5dd03c587c3fba57 100644 (file)
@@ -256,8 +256,7 @@ static void shortlog(const char *name, unsigned char *sha1,
 
 int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
        int limit = 20, i = 0, pos = 0;
-       char line[1024];
-       char *p = line, *sep = "";
+       char *sep = "";
        unsigned char head_sha1[20];
        const char *current_branch;
 
@@ -271,9 +270,8 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
        /* get a line */
        while (pos < in->len) {
                int len;
-               char *newline;
+               char *newline, *p = in->buf + pos;
 
-               p = in->buf + pos;
                newline = strchr(p, '\n');
                len = newline ? newline - p : strlen(p);
                pos += len + !!newline;
@@ -365,6 +363,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, options, fmt_merge_msg_usage, 0);
        if (argc > 0)
                usage_with_options(fmt_merge_msg_usage, options);
+       inpath = parse_options_fix_filename(prefix, inpath);
 
        if (inpath && strcmp(inpath, "-")) {
                in = fopen(inpath, "r");
index e46b7adc9719e147536398e8e365d6d3e65a4ba7..1911cda1c29ef4cb6f0785226efcb1d3cb07ef3e 100644 (file)
@@ -8,6 +8,7 @@
 #include "blob.h"
 #include "quote.h"
 #include "parse-options.h"
+#include "remote.h"
 
 /* Quoting styles */
 #define QUOTE_NONE 0
@@ -66,6 +67,7 @@ static struct {
        { "subject" },
        { "body" },
        { "contents" },
+       { "upstream" },
 };
 
 /*
@@ -337,8 +339,11 @@ static const char *copy_name(const char *buf)
 static const char *copy_email(const char *buf)
 {
        const char *email = strchr(buf, '<');
-       const char *eoemail = strchr(email, '>');
-       if (!email || !eoemail)
+       const char *eoemail;
+       if (!email)
+               return "";
+       eoemail = strchr(email, '>');
+       if (!eoemail)
                return "";
        return xmemdupz(email, eoemail + 1 - email);
 }
@@ -543,109 +548,6 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
        }
 }
 
-/*
- * generate a format suitable for scanf from a ref_rev_parse_rules
- * rule, that is replace the "%.*s" spec with a "%s" spec
- */
-static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
-{
-       char *spec;
-
-       spec = strstr(rule, "%.*s");
-       if (!spec || strstr(spec + 4, "%.*s"))
-               die("invalid rule in ref_rev_parse_rules: %s", rule);
-
-       /* copy all until spec */
-       strncpy(scanf_fmt, rule, spec - rule);
-       scanf_fmt[spec - rule] = '\0';
-       /* copy new spec */
-       strcat(scanf_fmt, "%s");
-       /* copy remaining rule */
-       strcat(scanf_fmt, spec + 4);
-
-       return;
-}
-
-/*
- * Shorten the refname to an non-ambiguous form
- */
-static char *get_short_ref(struct refinfo *ref)
-{
-       int i;
-       static char **scanf_fmts;
-       static int nr_rules;
-       char *short_name;
-
-       /* pre generate scanf formats from ref_rev_parse_rules[] */
-       if (!nr_rules) {
-               size_t total_len = 0;
-
-               /* the rule list is NULL terminated, count them first */
-               for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
-                       /* no +1 because strlen("%s") < strlen("%.*s") */
-                       total_len += strlen(ref_rev_parse_rules[nr_rules]);
-
-               scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
-
-               total_len = 0;
-               for (i = 0; i < nr_rules; i++) {
-                       scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
-                                       + total_len;
-                       gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
-                       total_len += strlen(ref_rev_parse_rules[i]);
-               }
-       }
-
-       /* bail out if there are no rules */
-       if (!nr_rules)
-               return ref->refname;
-
-       /* buffer for scanf result, at most ref->refname must fit */
-       short_name = xstrdup(ref->refname);
-
-       /* skip first rule, it will always match */
-       for (i = nr_rules - 1; i > 0 ; --i) {
-               int j;
-               int short_name_len;
-
-               if (1 != sscanf(ref->refname, scanf_fmts[i], short_name))
-                       continue;
-
-               short_name_len = strlen(short_name);
-
-               /*
-                * check if the short name resolves to a valid ref,
-                * but use only rules prior to the matched one
-                */
-               for (j = 0; j < i; j++) {
-                       const char *rule = ref_rev_parse_rules[j];
-                       unsigned char short_objectname[20];
-                       char refname[PATH_MAX];
-
-                       /*
-                        * the short name is ambiguous, if it resolves
-                        * (with this previous rule) to a valid ref
-                        * read_ref() returns 0 on success
-                        */
-                       mksnpath(refname, sizeof(refname),
-                                rule, short_name_len, short_name);
-                       if (!read_ref(refname, short_objectname))
-                               break;
-               }
-
-               /*
-                * short name is non-ambiguous if all previous rules
-                * haven't resolved to a valid ref
-                */
-               if (j == i)
-                       return short_name;
-       }
-
-       free(short_name);
-       return ref->refname;
-}
-
-
 /*
  * Parse the object referred by ref, and grab needed value.
  */
@@ -659,48 +561,74 @@ static void populate_value(struct refinfo *ref)
 
        ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
 
-       buf = get_obj(ref->objectname, &obj, &size, &eaten);
-       if (!buf)
-               die("missing object %s for %s",
-                   sha1_to_hex(ref->objectname), ref->refname);
-       if (!obj)
-               die("parse_object_buffer failed on %s for %s",
-                   sha1_to_hex(ref->objectname), ref->refname);
-
        /* Fill in specials first */
        for (i = 0; i < used_atom_cnt; i++) {
                const char *name = used_atom[i];
                struct atom_value *v = &ref->value[i];
                int deref = 0;
+               const char *refname;
+               const char *formatp;
+
                if (*name == '*') {
                        deref = 1;
                        name++;
                }
-               if (!prefixcmp(name, "refname")) {
-                       const char *formatp = strchr(name, ':');
-                       const char *refname = ref->refname;
-
-                       /* look for "short" refname format */
-                       if (formatp) {
-                               formatp++;
-                               if (!strcmp(formatp, "short"))
-                                       refname = get_short_ref(ref);
-                               else
-                                       die("unknown refname format %s",
-                                           formatp);
-                       }
 
-                       if (!deref)
-                               v->s = refname;
-                       else {
-                               int len = strlen(refname);
-                               char *s = xmalloc(len + 4);
-                               sprintf(s, "%s^{}", refname);
-                               v->s = s;
-                       }
+               if (!prefixcmp(name, "refname"))
+                       refname = ref->refname;
+               else if(!prefixcmp(name, "upstream")) {
+                       struct branch *branch;
+                       /* only local branches may have an upstream */
+                       if (prefixcmp(ref->refname, "refs/heads/"))
+                               continue;
+                       branch = branch_get(ref->refname + 11);
+
+                       if (!branch || !branch->merge || !branch->merge[0] ||
+                           !branch->merge[0]->dst)
+                               continue;
+                       refname = branch->merge[0]->dst;
+               }
+               else
+                       continue;
+
+               formatp = strchr(name, ':');
+               /* look for "short" refname format */
+               if (formatp) {
+                       formatp++;
+                       if (!strcmp(formatp, "short"))
+                               refname = shorten_unambiguous_ref(refname,
+                                                     warn_ambiguous_refs);
+                       else
+                               die("unknown %.*s format %s",
+                                   (int)(formatp - name), name, formatp);
+               }
+
+               if (!deref)
+                       v->s = refname;
+               else {
+                       int len = strlen(refname);
+                       char *s = xmalloc(len + 4);
+                       sprintf(s, "%s^{}", refname);
+                       v->s = s;
                }
        }
 
+       for (i = 0; i < used_atom_cnt; i++) {
+               struct atom_value *v = &ref->value[i];
+               if (v->s == NULL)
+                       goto need_obj;
+       }
+       return;
+
+ need_obj:
+       buf = get_obj(ref->objectname, &obj, &size, &eaten);
+       if (!buf)
+               die("missing object %s for %s",
+                   sha1_to_hex(ref->objectname), ref->refname);
+       if (!obj)
+               die("parse_object_buffer failed on %s for %s",
+                   sha1_to_hex(ref->objectname), ref->refname);
+
        grab_values(ref->value, 0, obj, buf, size);
        if (!eaten)
                free(buf);
@@ -943,7 +871,6 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
                return -1;
 
        *sort_tail = s = xcalloc(1, sizeof(*s));
-       sort_tail = &s->next;
 
        if (*arg == '-') {
                s->reverse = 1;
@@ -1002,9 +929,12 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                sort = default_sort();
        sort_atom_limit = used_atom_cnt;
 
+       /* for warn_ambiguous_refs */
+       git_config(git_default_config, NULL);
+
        memset(&cbdata, 0, sizeof(cbdata));
        cbdata.grab_pattern = argv;
-       for_each_ref(grab_single_ref, &cbdata);
+       for_each_rawref(grab_single_ref, &cbdata);
        refs = cbdata.grab_array;
        num_refs = cbdata.grab_cnt;
 
index 8d990ed4935804591a17c864e280c0c9b4b7597f..fc556ed7f3fb68734fd783e5b38e6b050bed9c5b 100644 (file)
@@ -23,7 +23,7 @@ static const char * const builtin_gc_usage[] = {
 };
 
 static int pack_refs = 1;
-static int aggressive_window = -1;
+static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
 static const char *prune_expire = "2.weeks.ago";
@@ -200,6 +200,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
 
        if (aggressive) {
                append_option(argv_repack, "-f", MAX_ADD);
+               append_option(argv_repack, "--depth=250", MAX_ADD);
                if (aggressive_window > 0) {
                        sprintf(buf, "--window=%d", aggressive_window);
                        append_option(argv_repack, buf, MAX_ADD);
index 3f12ba382690699d96580c3ddb1a61c79520e694..f88a912ace9195c387566770432a904e2d7adcb7 100644 (file)
 
 static int builtin_grep;
 
+static int grep_config(const char *var, const char *value, void *cb)
+{
+       struct grep_opt *opt = cb;
+
+       if (!strcmp(var, "color.grep")) {
+               opt->color = git_config_colorbool(var, value, -1);
+               return 0;
+       }
+       if (!strcmp(var, "color.grep.external"))
+               return git_config_string(&(opt->color_external), var, value);
+       if (!strcmp(var, "color.grep.match")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               color_parse(value, var, opt->color_match);
+               return 0;
+       }
+       return git_color_default_config(var, value, cb);
+}
+
 /*
  * git grep pathspecs are somewhat different from diff-tree pathspecs;
  * pathname wildcards are allowed.
@@ -269,6 +288,21 @@ static int flush_grep(struct grep_opt *opt,
        return status;
 }
 
+static void grep_add_color(struct strbuf *sb, const char *escape_seq)
+{
+       size_t orig_len = sb->len;
+
+       while (*escape_seq) {
+               if (*escape_seq == 'm')
+                       strbuf_addch(sb, ';');
+               else if (*escape_seq != '\033' && *escape_seq  != '[')
+                       strbuf_addch(sb, *escape_seq);
+               escape_seq++;
+       }
+       if (sb->len > orig_len && sb->buf[sb->len - 1] == ';')
+               strbuf_setlen(sb, sb->len - 1);
+}
+
 static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 {
        int i, nr, argc, hit, len, status;
@@ -339,6 +373,23 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
                push_arg("-e");
                push_arg(p->pattern);
        }
+       if (opt->color) {
+               struct strbuf sb = STRBUF_INIT;
+
+               grep_add_color(&sb, opt->color_match);
+               setenv("GREP_COLOR", sb.buf, 1);
+
+               strbuf_reset(&sb);
+               strbuf_addstr(&sb, "mt=");
+               grep_add_color(&sb, opt->color_match);
+               strbuf_addstr(&sb, ":sl=:cx=:fn=:ln=:bn=:se=");
+               setenv("GREP_COLORS", sb.buf, 1);
+
+               strbuf_release(&sb);
+
+               if (opt->color_external && strlen(opt->color_external) > 0)
+                       push_arg(opt->color_external);
+       }
 
        hit = 0;
        argc = nr;
@@ -536,6 +587,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        opt.pattern_tail = &opt.pattern_list;
        opt.regflags = REG_NEWLINE;
 
+       strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
+       opt.color = -1;
+       git_config(grep_config, &opt);
+       if (opt.color == -1)
+               opt.color = git_use_color_default;
+
        /*
         * If there is no -- then the paths must exist in the working
         * tree.  If there is no explicit pattern specified with -e or
@@ -732,6 +789,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        opt.relative = 0;
                        continue;
                }
+               if (!strcmp("--color", arg)) {
+                       opt.color = 1;
+                       continue;
+               }
+               if (!strcmp("--no-color", arg)) {
+                       opt.color = 0;
+                       continue;
+               }
                if (!strcmp("--", arg)) {
                        /* later processing wants to have this at argv[1] */
                        argv--;
@@ -757,6 +822,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (opt.color && !opt.color_external)
+               builtin_grep = 1;
        if (!opt.pattern_list)
                die("no pattern given.");
        if ((opt.regflags != REG_NEWLINE) && opt.fixed)
index 9b57a746185719eb43c962c4adb96283657692e9..67dda3e6e63c9130a6f1e1c94cb35e519d765a95 100644 (file)
@@ -114,7 +114,7 @@ static int check_emacsclient_version(void)
        return 0;
 }
 
-static void exec_woman_emacs(const charpath, const char *page)
+static void exec_woman_emacs(const char *path, const char *page)
 {
        if (!check_emacsclient_version()) {
                /* This works only with emacsclient version >= 22. */
@@ -128,7 +128,7 @@ static void exec_woman_emacs(const char* path, const char *page)
        }
 }
 
-static void exec_man_konqueror(const charpath, const char *page)
+static void exec_man_konqueror(const char *path, const char *page)
 {
        const char *display = getenv("DISPLAY");
        if (display && *display) {
@@ -156,7 +156,7 @@ static void exec_man_konqueror(const char* path, const char *page)
        }
 }
 
-static void exec_man_man(const charpath, const char *page)
+static void exec_man_man(const char *path, const char *page)
 {
        if (!path)
                path = "man";
@@ -236,7 +236,7 @@ static int add_man_viewer_info(const char *var, const char *value)
        const char *subkey = strrchr(name, '.');
 
        if (!subkey)
-               return error("Config with no key for man viewer: %s", name);
+               return 0;
 
        if (!strcmp(subkey, ".path")) {
                if (!value)
@@ -249,7 +249,6 @@ static int add_man_viewer_info(const char *var, const char *value)
                return add_man_viewer_cmd(name, subkey - name, value);
        }
 
-       warning("'%s': unsupported man viewer sub key.", subkey);
        return 0;
 }
 
index bfdfc7b411fb463f06ba98f58c07f5e707813310..d1fa12a59efb34256b2cc80b03c637cc844d84ff 100644 (file)
@@ -132,8 +132,7 @@ static void copy_templates(const char *template_dir)
        }
        dir = opendir(template_path);
        if (!dir) {
-               fprintf(stderr, "warning: templates not found %s\n",
-                       template_dir);
+               warning("templates not found %s", template_dir);
                return;
        }
 
@@ -146,8 +145,8 @@ static void copy_templates(const char *template_dir)
 
        if (repository_format_version &&
            repository_format_version != GIT_REPO_VERSION) {
-               fprintf(stderr, "warning: not copying templates of "
-                       "a wrong format version %d from '%s'\n",
+               warning("not copying templates of "
+                       "a wrong format version %d from '%s'",
                        repository_format_version,
                        template_dir);
                closedir(dir);
index 0f0adf2bab69328f5b6320c1baca58ad226570df..f10cfebdbbabac67dea41639289e7ad968d5bbe3 100644 (file)
@@ -17,6 +17,7 @@
 #include "run-command.h"
 #include "shortlog.h"
 #include "remote.h"
+#include "string-list.h"
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -416,18 +417,13 @@ int cmd_log(int argc, const char **argv, const char *prefix)
 }
 
 /* format-patch */
-#define FORMAT_PATCH_NAME_MAX 64
-
-static int istitlechar(char c)
-{
-       return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
-               (c >= '0' && c <= '9') || c == '.' || c == '_';
-}
 
 static const char *fmt_patch_suffix = ".patch";
 static int numbered = 0;
 static int auto_number = 1;
 
+static char *default_attach = NULL;
+
 static char **extra_hdr;
 static int extra_hdr_nr;
 static int extra_hdr_alloc;
@@ -459,6 +455,11 @@ static void add_header(const char *value)
        extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
 }
 
+#define THREAD_SHALLOW 1
+#define THREAD_DEEP 2
+static int thread = 0;
+static int do_signoff = 0;
+
 static int git_format_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "format.headers")) {
@@ -488,94 +489,60 @@ static int git_format_config(const char *var, const char *value, void *cb)
                auto_number = auto_number && numbered;
                return 0;
        }
-
-       return git_log_config(var, value, cb);
-}
-
-
-static const char *get_oneline_for_filename(struct commit *commit,
-                                           int keep_subject)
-{
-       static char filename[PATH_MAX];
-       char *sol;
-       int len = 0;
-       int suffix_len = strlen(fmt_patch_suffix) + 1;
-
-       sol = strstr(commit->buffer, "\n\n");
-       if (!sol)
-               filename[0] = '\0';
-       else {
-               int j, space = 0;
-
-               sol += 2;
-               /* strip [PATCH] or [PATCH blabla] */
-               if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
-                       char *eos = strchr(sol + 6, ']');
-                       if (eos) {
-                               while (isspace(*eos))
-                                       eos++;
-                               sol = eos;
-                       }
+       if (!strcmp(var, "format.attach")) {
+               if (value && *value)
+                       default_attach = xstrdup(value);
+               else
+                       default_attach = xstrdup(git_version_string);
+               return 0;
+       }
+       if (!strcmp(var, "format.thread")) {
+               if (value && !strcasecmp(value, "deep")) {
+                       thread = THREAD_DEEP;
+                       return 0;
                }
-
-               for (j = 0;
-                    j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
-                            len < sizeof(filename) - suffix_len &&
-                            sol[j] && sol[j] != '\n';
-                    j++) {
-                       if (istitlechar(sol[j])) {
-                               if (space) {
-                                       filename[len++] = '-';
-                                       space = 0;
-                               }
-                               filename[len++] = sol[j];
-                               if (sol[j] == '.')
-                                       while (sol[j + 1] == '.')
-                                               j++;
-                       } else
-                               space = 1;
+               if (value && !strcasecmp(value, "shallow")) {
+                       thread = THREAD_SHALLOW;
+                       return 0;
                }
-               while (filename[len - 1] == '.'
-                      || filename[len - 1] == '-')
-                       len--;
-               filename[len] = '\0';
+               thread = git_config_bool(var, value) && THREAD_SHALLOW;
+               return 0;
+       }
+       if (!strcmp(var, "format.signoff")) {
+               do_signoff = git_config_bool(var, value);
+               return 0;
        }
-       return filename;
+
+       return git_log_config(var, value, cb);
 }
 
 static FILE *realstdout = NULL;
 static const char *output_directory = NULL;
 static int outdir_offset;
 
-static int reopen_stdout(const char *oneline, int nr, int total)
+static int reopen_stdout(struct commit *commit, struct rev_info *rev)
 {
-       char filename[PATH_MAX];
-       int len = 0;
+       struct strbuf filename = STRBUF_INIT;
        int suffix_len = strlen(fmt_patch_suffix) + 1;
 
        if (output_directory) {
-               len = snprintf(filename, sizeof(filename), "%s",
-                               output_directory);
-               if (len >=
-                   sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
+               strbuf_addstr(&filename, output_directory);
+               if (filename.len >=
+                   PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
                        return error("name of output directory is too long");
-               if (filename[len - 1] != '/')
-                       filename[len++] = '/';
+               if (filename.buf[filename.len - 1] != '/')
+                       strbuf_addch(&filename, '/');
        }
 
-       if (!oneline)
-               len += sprintf(filename + len, "%d", nr);
-       else {
-               len += sprintf(filename + len, "%04d-", nr);
-               len += snprintf(filename + len, sizeof(filename) - len - 1
-                               - suffix_len, "%s", oneline);
-               strcpy(filename + len, fmt_patch_suffix);
-       }
+       get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
 
-       fprintf(realstdout, "%s\n", filename + outdir_offset);
-       if (freopen(filename, "w", stdout) == NULL)
-               return error("Cannot open patch file %s",filename);
+       if (!DIFF_OPT_TST(&rev->diffopt, QUIET))
+               fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
 
+       if (freopen(filename.buf, "w", stdout) == NULL)
+               return error("Cannot open patch file %s", filename.buf);
+
+       strbuf_release(&filename);
        return 0;
 }
 
@@ -645,7 +612,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              int nr, struct commit **list, struct commit *head)
 {
        const char *committer;
-       char *head_sha1;
        const char *subject_start = NULL;
        const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
        const char *msg;
@@ -656,20 +622,40 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        const char *encoding = "utf-8";
        struct diff_options opts;
        int need_8bit_cte = 0;
+       struct commit *commit = NULL;
 
        if (rev->commit_format != CMIT_FMT_EMAIL)
                die("Cover letter needs email format");
 
-       if (!use_stdout && reopen_stdout(numbered_files ?
-                               NULL : "cover-letter", 0, rev->total))
+       committer = git_committer_info(0);
+
+       if (!numbered_files) {
+               /*
+                * We fake a commit for the cover letter so we get the filename
+                * desired.
+                */
+               commit = xcalloc(1, sizeof(*commit));
+               commit->buffer = xmalloc(400);
+               snprintf(commit->buffer, 400,
+                       "tree 0000000000000000000000000000000000000000\n"
+                       "parent %s\n"
+                       "author %s\n"
+                       "committer %s\n\n"
+                       "cover letter\n",
+                       sha1_to_hex(head->object.sha1), committer, committer);
+       }
+
+       if (!use_stdout && reopen_stdout(commit, rev))
                return;
 
-       head_sha1 = sha1_to_hex(head->object.sha1);
+       if (commit) {
 
-       log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers,
-                               &need_8bit_cte);
+               free(commit->buffer);
+               free(commit);
+       }
 
-       committer = git_committer_info(0);
+       log_write_email_headers(rev, head, &subject_start, &extra_headers,
+                               &need_8bit_cte);
 
        msg = body;
        pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
@@ -766,10 +752,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int numbered_files = 0;         /* _just_ numbers */
        int subject_prefix = 0;
        int ignore_if_in_upstream = 0;
-       int thread = 0;
        int cover_letter = 0;
        int boundary_count = 0;
        int no_binary_diff = 0;
+       int numbered_cmdline_opt = 0;
        struct commit *origin = NULL, *head = NULL;
        const char *in_reply_to = NULL;
        struct patch_ids ids;
@@ -787,6 +773,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
        rev.subject_prefix = fmt_patch_subject_prefix;
 
+       if (default_attach) {
+               rev.mime_boundary = default_attach;
+               rev.no_inline = 1;
+       }
+
        /*
         * Parse the arguments before setup_revisions(), or something
         * like "git format-patch -o a123 HEAD^.." may fail; a123 is
@@ -796,8 +787,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                if (!strcmp(argv[i], "--stdout"))
                        use_stdout = 1;
                else if (!strcmp(argv[i], "-n") ||
-                               !strcmp(argv[i], "--numbered"))
+                               !strcmp(argv[i], "--numbered")) {
                        numbered = 1;
+                       numbered_cmdline_opt = 1;
+               }
                else if (!strcmp(argv[i], "-N") ||
                                !strcmp(argv[i], "--no-numbered")) {
                        numbered = 0;
@@ -833,13 +826,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                }
                else if (!strcmp(argv[i], "--signoff") ||
                         !strcmp(argv[i], "-s")) {
-                       const char *committer;
-                       const char *endpos;
-                       committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
-                       endpos = strchr(committer, '>');
-                       if (!endpos)
-                               die("bogus committer info %s", committer);
-                       add_signoff = xmemdupz(committer, endpos - committer + 1);
+                       do_signoff = 1;
                }
                else if (!strcmp(argv[i], "--attach")) {
                        rev.mime_boundary = git_version_string;
@@ -849,6 +836,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        rev.mime_boundary = argv[i] + 9;
                        rev.no_inline = 1;
                }
+               else if (!strcmp(argv[i], "--no-attach")) {
+                       rev.mime_boundary = NULL;
+                       rev.no_inline = 0;
+               }
                else if (!strcmp(argv[i], "--inline")) {
                        rev.mime_boundary = git_version_string;
                        rev.no_inline = 0;
@@ -859,8 +850,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                }
                else if (!strcmp(argv[i], "--ignore-if-in-upstream"))
                        ignore_if_in_upstream = 1;
-               else if (!strcmp(argv[i], "--thread"))
-                       thread = 1;
+               else if (!strcmp(argv[i], "--thread")
+                       || !strcmp(argv[i], "--thread=shallow"))
+                       thread = THREAD_SHALLOW;
+               else if (!strcmp(argv[i], "--thread=deep"))
+                       thread = THREAD_DEEP;
+               else if (!strcmp(argv[i], "--no-thread"))
+                       thread = 0;
                else if (!prefixcmp(argv[i], "--in-reply-to="))
                        in_reply_to = argv[i] + 14;
                else if (!strcmp(argv[i], "--in-reply-to")) {
@@ -877,11 +873,23 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        cover_letter = 1;
                else if (!strcmp(argv[i], "--no-binary"))
                        no_binary_diff = 1;
+               else if (!prefixcmp(argv[i], "--add-header="))
+                       add_header(argv[i] + 13);
                else
                        argv[j++] = argv[i];
        }
        argc = j;
 
+       if (do_signoff) {
+               const char *committer;
+               const char *endpos;
+               committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
+               endpos = strchr(committer, '>');
+               if (!endpos)
+                       die("bogus committer info %s", committer);
+               add_signoff = xmemdupz(committer, endpos - committer + 1);
+       }
+
        for (i = 0; i < extra_hdr_nr; i++) {
                strbuf_addstr(&buf, extra_hdr[i]);
                strbuf_addch(&buf, '\n');
@@ -913,6 +921,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
        if (start_number < 0)
                start_number = 1;
+
+       /*
+        * If numbered is set solely due to format.numbered in config,
+        * and it would conflict with --keep-subject (-k) from the
+        * command line, reset "numbered".
+        */
+       if (numbered && keep_subject && !numbered_cmdline_opt)
+               numbered = 0;
+
        if (numbered && keep_subject)
                die ("-n and -k are mutually exclusive.");
        if (keep_subject && subject_prefix)
@@ -1009,8 +1026,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                numbered = 1;
        if (numbered)
                rev.total = total + start_number - 1;
-       if (in_reply_to)
-               rev.ref_message_id = clean_message_id(in_reply_to);
+       if (in_reply_to || thread || cover_letter)
+               rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+       if (in_reply_to) {
+               const char *msgid = clean_message_id(in_reply_to);
+               string_list_append(msgid, rev.ref_message_ids);
+       }
+       rev.numbered_files = numbered_files;
+       rev.patch_suffix = fmt_patch_suffix;
        if (cover_letter) {
                if (thread)
                        gen_message_id(&rev, "cover");
@@ -1029,21 +1052,39 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        /* Have we already had a message ID? */
                        if (rev.message_id) {
                                /*
-                                * If we've got the ID to be a reply
-                                * to, discard the current ID;
-                                * otherwise, make everything a reply
-                                * to that.
+                                * For deep threading: make every mail
+                                * a reply to the previous one, no
+                                * matter what other options are set.
+                                *
+                                * For shallow threading:
+                                *
+                                * Without --cover-letter and
+                                * --in-reply-to, make every mail a
+                                * reply to the one before.
+                                *
+                                * With --in-reply-to but no
+                                * --cover-letter, make every mail a
+                                * reply to the <reply-to>.
+                                *
+                                * With --cover-letter, make every
+                                * mail but the cover letter a reply
+                                * to the cover letter.  The cover
+                                * letter is a reply to the
+                                * --in-reply-to, if specified.
                                 */
-                               if (rev.ref_message_id)
+                               if (thread == THREAD_SHALLOW
+                                   && rev.ref_message_ids->nr > 0
+                                   && (!cover_letter || rev.nr > 1))
                                        free(rev.message_id);
                                else
-                                       rev.ref_message_id = rev.message_id;
+                                       string_list_append(rev.message_id,
+                                                          rev.ref_message_ids);
                        }
                        gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
                }
-               if (!use_stdout && reopen_stdout(numbered_files ? NULL :
-                               get_oneline_for_filename(commit, keep_subject),
-                               rev.nr, rev.total))
+
+               if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
+                                                &rev))
                        die("Failed to create output files");
                shown = log_tree_commit(&rev, commit);
                free(commit->buffer);
index ca6f33d0466572863db7dbd4c3079dcb06caa4e0..da2daf45acd2154b7f00da34d41a5ab8acaf0c38 100644 (file)
@@ -10,6 +10,7 @@
 #include "dir.h"
 #include "builtin.h"
 #include "tree.h"
+#include "parse-options.h"
 
 static int abbrev;
 static int show_deleted;
@@ -28,6 +29,7 @@ static const char **pathspec;
 static int error_unmatch;
 static char *ps_matched;
 static const char *with_tree;
+static int exc_given;
 
 static const char *tag_cached = "";
 static const char *tag_unmerged = "";
@@ -174,7 +176,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
                for (i = 0; i < active_nr; i++) {
                        struct cache_entry *ce = active_cache[i];
                        int dtype = ce_to_dtype(ce);
-                       if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
+                       if (excluded(dir, ce->name, &dtype) !=
+                                       !!(dir->flags & DIR_SHOW_IGNORED))
                                continue;
                        if (show_unmerged && !ce_stage(ce))
                                continue;
@@ -189,7 +192,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
                        struct stat st;
                        int err;
                        int dtype = ce_to_dtype(ce);
-                       if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
+                       if (excluded(dir, ce->name, &dtype) !=
+                                       !!(dir->flags & DIR_SHOW_IGNORED))
                                continue;
                        if (ce->ce_flags & CE_UPDATE)
                                continue;
@@ -374,159 +378,141 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
        return errors;
 }
 
-static const char ls_files_usage[] =
-       "git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
-       "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
-       "[ --exclude-per-directory=<filename> ] [--exclude-standard] "
-       "[--full-name] [--abbrev] [--] [<file>]*";
+static const char * const ls_files_usage[] = {
+       "git ls-files [options] [<file>]*",
+       NULL
+};
+
+static int option_parse_z(const struct option *opt,
+                         const char *arg, int unset)
+{
+       line_terminator = unset ? '\n' : '\0';
+
+       return 0;
+}
+
+static int option_parse_exclude(const struct option *opt,
+                               const char *arg, int unset)
+{
+       struct exclude_list *list = opt->value;
+
+       exc_given = 1;
+       add_exclude(arg, "", 0, list);
+
+       return 0;
+}
+
+static int option_parse_exclude_from(const struct option *opt,
+                                    const char *arg, int unset)
+{
+       struct dir_struct *dir = opt->value;
+
+       exc_given = 1;
+       add_excludes_from_file(dir, arg);
+
+       return 0;
+}
+
+static int option_parse_exclude_standard(const struct option *opt,
+                                        const char *arg, int unset)
+{
+       struct dir_struct *dir = opt->value;
+
+       exc_given = 1;
+       setup_standard_excludes(dir);
+
+       return 0;
+}
 
 int cmd_ls_files(int argc, const char **argv, const char *prefix)
 {
-       int i;
-       int exc_given = 0, require_work_tree = 0;
+       int require_work_tree = 0, show_tag = 0;
        struct dir_struct dir;
+       struct option builtin_ls_files_options[] = {
+               { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
+                       "paths are separated with NUL character",
+                       PARSE_OPT_NOARG, option_parse_z },
+               OPT_BOOLEAN('t', NULL, &show_tag,
+                       "identify the file status with tags"),
+               OPT_BOOLEAN('v', NULL, &show_valid_bit,
+                       "use lowercase letters for 'assume unchanged' files"),
+               OPT_BOOLEAN('c', "cached", &show_cached,
+                       "show cached files in the output (default)"),
+               OPT_BOOLEAN('d', "deleted", &show_deleted,
+                       "show deleted files in the output"),
+               OPT_BOOLEAN('m', "modified", &show_modified,
+                       "show modified files in the output"),
+               OPT_BOOLEAN('o', "others", &show_others,
+                       "show other files in the output"),
+               OPT_BIT('i', "ignored", &dir.flags,
+                       "show ignored files in the output",
+                       DIR_SHOW_IGNORED),
+               OPT_BOOLEAN('s', "stage", &show_stage,
+                       "show staged contents' object name in the output"),
+               OPT_BOOLEAN('k', "killed", &show_killed,
+                       "show files on the filesystem that need to be removed"),
+               OPT_BIT(0, "directory", &dir.flags,
+                       "show 'other' directories' name only",
+                       DIR_SHOW_OTHER_DIRECTORIES),
+               OPT_BIT(0, "no-empty-directory", &dir.flags,
+                       "don't show empty directories",
+                       DIR_HIDE_EMPTY_DIRECTORIES),
+               OPT_BOOLEAN('u', "unmerged", &show_unmerged,
+                       "show unmerged files in the output"),
+               { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern",
+                       "skip files matching pattern",
+                       0, option_parse_exclude },
+               { OPTION_CALLBACK, 'X', "exclude-from", &dir, "file",
+                       "exclude patterns are read from <file>",
+                       0, option_parse_exclude_from },
+               OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, "file",
+                       "read additional per-directory exclude patterns in <file>"),
+               { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
+                       "add the standard git exclusions",
+                       PARSE_OPT_NOARG, option_parse_exclude_standard },
+               { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
+                       "make the output relative to the project top directory",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
+               OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
+                       "if any <file> is not in the index, treat this as an error"),
+               OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
+                       "pretend that paths removed since <tree-ish> are still present"),
+               OPT__ABBREV(&abbrev),
+               OPT_END()
+       };
 
        memset(&dir, 0, sizeof(dir));
        if (prefix)
                prefix_offset = strlen(prefix);
        git_config(git_default_config, NULL);
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(arg, "-z")) {
-                       line_terminator = 0;
-                       continue;
-               }
-               if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
-                       tag_cached = "H ";
-                       tag_unmerged = "M ";
-                       tag_removed = "R ";
-                       tag_modified = "C ";
-                       tag_other = "? ";
-                       tag_killed = "K ";
-                       if (arg[1] == 'v')
-                               show_valid_bit = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
-                       show_cached = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
-                       show_deleted = 1;
-                       require_work_tree = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
-                       show_modified = 1;
-                       require_work_tree = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
-                       show_others = 1;
-                       require_work_tree = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
-                       dir.show_ignored = 1;
-                       require_work_tree = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
-                       show_stage = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
-                       show_killed = 1;
-                       require_work_tree = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--directory")) {
-                       dir.show_other_directories = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--no-empty-directory")) {
-                       dir.hide_empty_directories = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
-                       /* There's no point in showing unmerged unless
-                        * you also show the stage information.
-                        */
-                       show_stage = 1;
-                       show_unmerged = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-x") && i+1 < argc) {
-                       exc_given = 1;
-                       add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
-                       continue;
-               }
-               if (!prefixcmp(arg, "--exclude=")) {
-                       exc_given = 1;
-                       add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
-                       continue;
-               }
-               if (!strcmp(arg, "-X") && i+1 < argc) {
-                       exc_given = 1;
-                       add_excludes_from_file(&dir, argv[++i]);
-                       continue;
-               }
-               if (!prefixcmp(arg, "--exclude-from=")) {
-                       exc_given = 1;
-                       add_excludes_from_file(&dir, arg+15);
-                       continue;
-               }
-               if (!prefixcmp(arg, "--exclude-per-directory=")) {
-                       exc_given = 1;
-                       dir.exclude_per_dir = arg + 24;
-                       continue;
-               }
-               if (!strcmp(arg, "--exclude-standard")) {
-                       exc_given = 1;
-                       setup_standard_excludes(&dir);
-                       continue;
-               }
-               if (!strcmp(arg, "--full-name")) {
-                       prefix_offset = 0;
-                       continue;
-               }
-               if (!strcmp(arg, "--error-unmatch")) {
-                       error_unmatch = 1;
-                       continue;
-               }
-               if (!prefixcmp(arg, "--with-tree=")) {
-                       with_tree = arg + 12;
-                       continue;
-               }
-               if (!prefixcmp(arg, "--abbrev=")) {
-                       abbrev = strtoul(arg+9, NULL, 10);
-                       if (abbrev && abbrev < MINIMUM_ABBREV)
-                               abbrev = MINIMUM_ABBREV;
-                       else if (abbrev > 40)
-                               abbrev = 40;
-                       continue;
-               }
-               if (!strcmp(arg, "--abbrev")) {
-                       abbrev = DEFAULT_ABBREV;
-                       continue;
-               }
-               if (*arg == '-')
-                       usage(ls_files_usage);
-               break;
+       argc = parse_options(argc, argv, builtin_ls_files_options,
+                       ls_files_usage, 0);
+       if (show_tag || show_valid_bit) {
+               tag_cached = "H ";
+               tag_unmerged = "M ";
+               tag_removed = "R ";
+               tag_modified = "C ";
+               tag_other = "? ";
+               tag_killed = "K ";
        }
+       if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)
+               require_work_tree = 1;
+       if (show_unmerged)
+               /*
+                * There's no point in showing unmerged unless
+                * you also show the stage information.
+                */
+               show_stage = 1;
+       if (dir.exclude_per_dir)
+               exc_given = 1;
 
        if (require_work_tree && !is_inside_work_tree())
                setup_work_tree();
 
-       pathspec = get_pathspec(prefix, argv + i);
+       pathspec = get_pathspec(prefix, argv);
 
-       /* be nice with submodule patsh ending in a slash */
+       /* be nice with submodule paths ending in a slash */
        read_cache();
        if (pathspec)
                strip_trailing_slash_from_submodules();
@@ -543,7 +529,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                ps_matched = xcalloc(1, num);
        }
 
-       if (dir.show_ignored && !exc_given) {
+       if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given) {
                fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
                        argv[0]);
                exit(1);
index fca46312f681caf077565fb4bd5b59a70008eb59..22008dfa8fb94d971b6fb1bd7342ede02a8d3fd0 100644 (file)
@@ -60,7 +60,6 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
 {
        int retval = 0;
        const char *type = blob_type;
-       unsigned long size;
 
        if (S_ISGITLINK(mode)) {
                /*
@@ -90,17 +89,20 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
 
        if (!(ls_options & LS_NAME_ONLY)) {
                if (ls_options & LS_SHOW_SIZE) {
+                       char size_text[24];
                        if (!strcmp(type, blob_type)) {
-                               sha1_object_info(sha1, &size);
-                               printf("%06o %s %s %7lu\t", mode, type,
-                                      abbrev ? find_unique_abbrev(sha1, abbrev)
-                                             : sha1_to_hex(sha1),
-                                      size);
+                               unsigned long size;
+                               if (sha1_object_info(sha1, &size) == OBJ_BAD)
+                                       strcpy(size_text, "BAD");
+                               else
+                                       snprintf(size_text, sizeof(size_text),
+                                                "%lu", size);
                        } else
-                               printf("%06o %s %s %7c\t", mode, type,
-                                      abbrev ? find_unique_abbrev(sha1, abbrev)
-                                             : sha1_to_hex(sha1),
-                                      '-');
+                               strcpy(size_text, "-");
+                       printf("%06o %s %s %7s\t", mode, type,
+                              abbrev ? find_unique_abbrev(sha1, abbrev)
+                                     : sha1_to_hex(sha1),
+                              size_text);
                } else
                        printf("%06o %s %s\t", mode, type,
                               abbrev ? find_unique_abbrev(sha1, abbrev)
index 2789ccdf7dd43a1170a1ca28a3e4d4802422e719..1eeeb4de6d0d54e3fd753b7f057351094e10a24e 100644 (file)
@@ -537,7 +537,6 @@ static int decode_header_bq(struct strbuf *it)
                                 */
                                strbuf_add(&outbuf, in, ep - in);
                        }
-                       in = ep;
                }
                /* E.g.
                 * ep : "=?iso-2022-jp?B?GyR...?= foo"
index c339380cf3a94c5c157e7b3fae082ea2321320fc..9e9bd526c39e25b257733a575e8c690ed494f214 100644 (file)
@@ -360,9 +360,8 @@ static void merge_name(const char *remote, struct strbuf *msg)
        const char *ptr;
        int len, early;
 
-       len = strlen(remote);
-       if (interpret_nth_last_branch(remote, &bname) == len)
-               remote = bname.buf;
+       strbuf_branchname(&bname, remote);
+       remote = bname.buf;
 
        memset(branch_head, 0, sizeof(branch_head));
        remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
@@ -636,7 +635,7 @@ static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
        memset(&opts, 0, sizeof(opts));
        memset(&t, 0, sizeof(t));
        memset(&dir, 0, sizeof(dir));
-       dir.show_ignored = 1;
+       dir.flags |= DIR_SHOW_IGNORED;
        dir.exclude_per_dir = ".gitignore";
        opts.dir = &dir;
 
@@ -837,8 +836,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        struct commit_list **remotes = &remoteheads;
 
        setup_work_tree();
+       if (file_exists(git_path("MERGE_HEAD")))
+               die("You have not concluded your merge. (MERGE_HEAD exists)");
        if (read_cache_unmerged())
-               die("You are in the middle of a conflicted merge.");
+               die("You are in the middle of a conflicted merge."
+                               " (index unmerged)");
 
        /*
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
index b859cb147c707d76685e999284d8cc647977e6a0..941cc2d73cf5ee6791a0cee8c409dcdb9756b448 100644 (file)
@@ -293,7 +293,7 @@ static unsigned long write_object(struct sha1file *f,
                                die("unable to read %s", sha1_to_hex(entry->idx.sha1));
                        /*
                         * make sure no cached delta data remains from a
-                        * previous attempt before a pack split occured.
+                        * previous attempt before a pack split occurred.
                         */
                        free(entry->delta_data);
                        entry->delta_data = NULL;
@@ -653,8 +653,7 @@ static void rehash_objects(void)
 
 static unsigned name_hash(const char *name)
 {
-       unsigned char c;
-       unsigned hash = 0;
+       unsigned c, hash = 0;
 
        if (!name)
                return 0;
@@ -1612,7 +1611,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
                return;
        }
        if (progress > pack_to_stdout)
-               fprintf(stderr, "Delta compression using %d threads.\n",
+               fprintf(stderr, "Delta compression using up to %d threads.\n",
                                delta_search_threads);
 
        /* Partition the work amongst work threads. */
@@ -1901,7 +1900,7 @@ static void read_object_list_from_stdin(void)
 
 #define OBJECT_ADDED (1u<<20)
 
-static void show_commit(struct commit *commit)
+static void show_commit(struct commit *commit, void *data)
 {
        add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
        commit->object.flags |= OBJECT_ADDED;
@@ -2079,7 +2078,7 @@ static void get_object_list(int ac, const char **av)
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
-       traverse_commit_list(&revs, show_commit, show_object);
+       traverse_commit_list(&revs, show_commit, show_object, NULL);
 
        if (keep_unreachable)
                add_objects_in_unpacked_packs(&revs);
index 2d5b2cd353a5e80a74e3828aae8e0c7dfa0e1997..00590b1c3c2cceda7a75537c8680a96cd2ef84d4 100644 (file)
@@ -28,8 +28,8 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
                memcpy(pathname + len, de->d_name, 38);
                if (opts & DRY_RUN)
                        printf("rm -f %s\n", pathname);
-               else if (unlink(pathname) < 0)
-                       error("unable to unlink %s", pathname);
+               else
+                       unlink_or_warn(pathname);
                display_progress(progress, i + 1);
        }
        pathname[len] = 0;
@@ -55,6 +55,7 @@ void prune_packed_objects(int opts)
        for (i = 0; i < 256; i++) {
                DIR *d;
 
+               display_progress(progress, i + 1);
                sprintf(pathname + len, "%02x/", i);
                d = opendir(pathname);
                if (!d)
index 545e9c1f9458ed936e2a99a208f75b70f368294c..145ba83651e9c8560e20d6ab449ce64783bc65f5 100644 (file)
@@ -27,7 +27,7 @@ static int prune_tmp_object(const char *path, const char *filename)
        }
        printf("Removing stale temporary file %s\n", fullpath);
        if (!show_only)
-               unlink(fullpath);
+               unlink_or_warn(fullpath);
        return 0;
 }
 
@@ -47,7 +47,7 @@ static int prune_object(char *path, const char *filename, const unsigned char *s
                       (type > 0) ? typename(type) : "unknown");
        }
        if (!show_only)
-               unlink(fullpath);
+               unlink_or_warn(fullpath);
        return 0;
 }
 
index ca36fb1e5834fc581bc7bf8ed54184bbecdc2389..2eabcd3bdfb3f5d5705125a8f74d21d4ab1deafc 100644 (file)
@@ -48,6 +48,71 @@ static void set_refspecs(const char **refs, int nr)
        }
 }
 
+static void setup_push_tracking(void)
+{
+       struct strbuf refspec = STRBUF_INIT;
+       struct branch *branch = branch_get(NULL);
+       if (!branch)
+               die("You are not currently on a branch.");
+       if (!branch->merge_nr)
+               die("The current branch %s is not tracking anything.",
+                   branch->name);
+       if (branch->merge_nr != 1)
+               die("The current branch %s is tracking multiple branches, "
+                   "refusing to push.", branch->name);
+       strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
+       add_refspec(refspec.buf);
+}
+
+static const char *warn_unconfigured_push_msg[] = {
+       "You did not specify any refspecs to push, and the current remote",
+       "has not configured any push refspecs. The default action in this",
+       "case is to push all matching refspecs, that is, all branches",
+       "that exist both locally and remotely will be updated.  This may",
+       "not necessarily be what you want to happen.",
+       "",
+       "You can specify what action you want to take in this case, and",
+       "avoid seeing this message again, by configuring 'push.default' to:",
+       "  'nothing'  : Do not push anything",
+       "  'matching' : Push all matching branches (default)",
+       "  'tracking' : Push the current branch to whatever it is tracking",
+       "  'current'  : Push the current branch"
+};
+
+static void warn_unconfigured_push(void)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(warn_unconfigured_push_msg); i++)
+               warning("%s", warn_unconfigured_push_msg[i]);
+}
+
+static void setup_default_push_refspecs(void)
+{
+       git_config(git_default_config, NULL);
+       switch (push_default) {
+       case PUSH_DEFAULT_UNSPECIFIED:
+               warn_unconfigured_push();
+               /* fallthrough */
+
+       case PUSH_DEFAULT_MATCHING:
+               add_refspec(":");
+               break;
+
+       case PUSH_DEFAULT_TRACKING:
+               setup_push_tracking();
+               break;
+
+       case PUSH_DEFAULT_CURRENT:
+               add_refspec("HEAD");
+               break;
+
+       case PUSH_DEFAULT_NOTHING:
+               die("You didn't specify any refspecs to push, and "
+                   "push.default is \"nothing\".");
+               break;
+       }
+}
+
 static int do_push(const char *repo, int flags)
 {
        int i, errs;
@@ -79,11 +144,12 @@ static int do_push(const char *repo, int flags)
                return error("--all and --mirror are incompatible");
        }
 
-       if (!refspec
-               && !(flags & TRANSPORT_PUSH_ALL)
-               && remote->push_refspec_nr) {
-               refspec = remote->push_refspec;
-               refspec_nr = remote->push_refspec_nr;
+       if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {
+               if (remote->push_refspec_nr) {
+                       refspec = remote->push_refspec;
+                       refspec_nr = remote->push_refspec_nr;
+               } else if (!(flags & TRANSPORT_PUSH_MIRROR))
+                       setup_default_push_refspecs();
        }
        errs = 0;
        for (i = 0; i < remote->url_nr; i++) {
index 391d709704320dba7a918fdd79b68c6c604d33b2..82e25eaa0758d8b9584592f4072f81eeccb0bf1d 100644 (file)
@@ -135,7 +135,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                                die("more than one --exclude-per-directory are given.");
 
                        dir = xcalloc(1, sizeof(*opts.dir));
-                       dir->show_ignored = 1;
+                       dir->flags |= DIR_SHOW_IGNORED;
                        dir->exclude_per_dir = arg + 24;
                        opts.dir = dir;
                        /* We do not need to nor want to do read-directory
index 849f1fe6f9c703bd7717e54548300dfe6e495061..0b08da9b595432efa231eb59d88ecd2b422eb56a 100644 (file)
@@ -27,10 +27,9 @@ static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
 static int unpack_limit = 100;
 static int report_status;
+static int prefer_ofs_delta = 1;
 static const char *head_name;
-
-static char capabilities[] = " report-status delete-refs ";
-static int capabilities_sent;
+static char *capabilities_to_send;
 
 static enum deny_action parse_deny_action(const char *var, const char *value)
 {
@@ -84,24 +83,29 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
+               prefer_ofs_delta = git_config_bool(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
 static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-       if (capabilities_sent)
+       if (!capabilities_to_send)
                packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
        else
                packet_write(1, "%s %s%c%s\n",
-                            sha1_to_hex(sha1), path, 0, capabilities);
-       capabilities_sent = 1;
+                            sha1_to_hex(sha1), path, 0, capabilities_to_send);
+       capabilities_to_send = NULL;
        return 0;
 }
 
 static void write_head_info(void)
 {
        for_each_ref(show_ref, NULL);
-       if (!capabilities_sent)
+       if (capabilities_to_send)
                show_ref("capabilities^{}", null_sha1, 0, NULL);
 
 }
@@ -675,7 +679,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        setup_path();
 
        if (!enter_repo(dir, 0))
-               die("'%s': unable to chdir or not a git archive", dir);
+               die("'%s' does not appear to be a git repository", dir);
 
        if (is_repository_shallow())
                die("attempt to push into a shallow repository");
@@ -687,6 +691,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        else if (0 <= receive_unpack_limit)
                unpack_limit = receive_unpack_limit;
 
+       capabilities_to_send = (prefer_ofs_delta) ?
+               " report-status delete-refs ofs-delta " :
+               " report-status delete-refs ";
+
        add_alternate_refs();
        write_head_info();
        clear_extra_refs();
@@ -702,7 +710,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        unpack_status = unpack();
                execute_commands(unpack_status);
                if (pack_lockfile)
-                       unlink(pack_lockfile);
+                       unlink_or_warn(pack_lockfile);
                if (report_status)
                        report(unpack_status);
                run_receive_hook(post_receive_hook);
index 249ad2a311f2e9680618870aed3ac053c9fcf7a1..95198c5de41072bfb8adf7f29d9bbb44eec665ac 100644 (file)
@@ -240,10 +240,10 @@ static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsig
 static void mark_reachable(struct commit *commit, unsigned long expire_limit)
 {
        /*
-        * We need to compute if commit on either side of an reflog
+        * We need to compute whether the commit on either side of a reflog
         * entry is reachable from the tip of the ref for all entries.
         * Mark commits that are reachable from the tip down to the
-        * time threashold first; we know a commit marked thusly is
+        * time threshold first; we know a commit marked thusly is
         * reachable from the tip without running in_merge_bases()
         * at all.
         */
@@ -694,7 +694,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
  */
 
 static const char reflog_usage[] =
-"git reflog (expire | ...)";
+"git reflog [ show | expire | delete ]";
 
 int cmd_reflog(int argc, const char **argv, const char *prefix)
 {
index ac69d37c8af415a64cba88a888e9b80db95ef97a..7685cfcaf08bef280576ea5f39e62dc1b92ae22c 100644 (file)
@@ -12,15 +12,21 @@ static const char * const builtin_remote_usage[] = {
        "git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
        "git remote rename <old> <new>",
        "git remote rm <name>",
+       "git remote set-head <name> [-a | -d | <branch>]",
        "git remote show [-n] <name>",
        "git remote prune [-n | --dry-run] <name>",
-       "git remote [-v | --verbose] update [group]",
+       "git remote [-v | --verbose] update [-p | --prune] [group]",
        NULL
 };
 
+#define GET_REF_STATES (1<<0)
+#define GET_HEAD_NAMES (1<<1)
+#define GET_PUSH_REF_STATES (1<<2)
+
 static int verbose;
 
 static int show_all(void);
+static int prune_remote(const char *remote, int dry_run);
 
 static inline int postfixcmp(const char *string, const char *postfix)
 {
@@ -143,8 +149,9 @@ static int add(int argc, const char **argv)
 }
 
 struct branch_info {
-       char *remote;
+       char *remote_name;
        struct string_list merge;
+       int rebase;
 };
 
 static struct string_list branch_list;
@@ -161,10 +168,11 @@ static const char *abbrev_ref(const char *name, const char *prefix)
 static int config_read_branches(const char *key, const char *value, void *cb)
 {
        if (!prefixcmp(key, "branch.")) {
+               const char *orig_key = key;
                char *name;
                struct string_list_item *item;
                struct branch_info *info;
-               enum { REMOTE, MERGE } type;
+               enum { REMOTE, MERGE, REBASE } type;
 
                key += 7;
                if (!postfixcmp(key, ".remote")) {
@@ -173,6 +181,9 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                } else if (!postfixcmp(key, ".merge")) {
                        name = xstrndup(key, strlen(key) - 6);
                        type = MERGE;
+               } else if (!postfixcmp(key, ".rebase")) {
+                       name = xstrndup(key, strlen(key) - 7);
+                       type = REBASE;
                } else
                        return 0;
 
@@ -182,10 +193,10 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                        item->util = xcalloc(sizeof(struct branch_info), 1);
                info = item->util;
                if (type == REMOTE) {
-                       if (info->remote)
-                               warning("more than one branch.%s", key);
-                       info->remote = xstrdup(value);
-               } else {
+                       if (info->remote_name)
+                               warning("more than one %s", orig_key);
+                       info->remote_name = xstrdup(value);
+               } else if (type == MERGE) {
                        char *space = strchr(value, ' ');
                        value = abbrev_branch(value);
                        while (space) {
@@ -196,7 +207,8 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                                space = strchr(value, ' ');
                        }
                        string_list_append(xstrdup(value), &info->merge);
-               }
+               } else
+                       info->rebase = git_config_bool(orig_key, value);
        }
        return 0;
 }
@@ -206,12 +218,12 @@ static void read_branches(void)
        if (branch_list.nr)
                return;
        git_config(config_read_branches, NULL);
-       sort_string_list(&branch_list);
 }
 
 struct ref_states {
        struct remote *remote;
-       struct string_list new, stale, tracked;
+       struct string_list new, stale, tracked, heads, push;
+       int queried;
 };
 
 static int handle_one_branch(const char *refname,
@@ -227,10 +239,8 @@ static int handle_one_branch(const char *refname,
                const char *name = abbrev_branch(refspec.src);
                /* symbolic refs pointing nowhere were handled already */
                if ((flags & REF_ISSYMREF) ||
-                               unsorted_string_list_has_string(&states->tracked,
-                                       name) ||
-                               unsorted_string_list_has_string(&states->new,
-                                       name))
+                   string_list_has_string(&states->tracked, name) ||
+                   string_list_has_string(&states->new, name))
                        return 0;
                item = string_list_append(name, &states->stale);
                item->util = xstrdup(refname);
@@ -238,39 +248,154 @@ static int handle_one_branch(const char *refname,
        return 0;
 }
 
-static int get_ref_states(const struct ref *ref, struct ref_states *states)
+static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
 {
        struct ref *fetch_map = NULL, **tail = &fetch_map;
+       struct ref *ref;
        int i;
 
        for (i = 0; i < states->remote->fetch_refspec_nr; i++)
-               if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1))
+               if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
                        die("Could not get fetch map for refspec %s",
                                states->remote->fetch_refspec[i]);
 
        states->new.strdup_strings = states->tracked.strdup_strings = 1;
        for (ref = fetch_map; ref; ref = ref->next) {
-               struct string_list *target = &states->tracked;
                unsigned char sha1[20];
-               void *util = NULL;
-
                if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
-                       target = &states->new;
-               else {
-                       target = &states->tracked;
-                       if (hashcmp(sha1, ref->new_sha1))
-                               util = &states;
-               }
-               string_list_append(abbrev_branch(ref->name), target)->util = util;
+                       string_list_append(abbrev_branch(ref->name), &states->new);
+               else
+                       string_list_append(abbrev_branch(ref->name), &states->tracked);
        }
        free_refs(fetch_map);
 
+       sort_string_list(&states->new);
+       sort_string_list(&states->tracked);
        for_each_ref(handle_one_branch, states);
        sort_string_list(&states->stale);
 
        return 0;
 }
 
+struct push_info {
+       char *dest;
+       int forced;
+       enum {
+               PUSH_STATUS_CREATE = 0,
+               PUSH_STATUS_DELETE,
+               PUSH_STATUS_UPTODATE,
+               PUSH_STATUS_FASTFORWARD,
+               PUSH_STATUS_OUTOFDATE,
+               PUSH_STATUS_NOTQUERIED,
+       } status;
+};
+
+static int get_push_ref_states(const struct ref *remote_refs,
+       struct ref_states *states)
+{
+       struct remote *remote = states->remote;
+       struct ref *ref, *local_refs, *push_map, **push_tail;
+       if (remote->mirror)
+               return 0;
+
+       local_refs = get_local_heads();
+       push_map = copy_ref_list(remote_refs);
+
+       push_tail = &push_map;
+       while (*push_tail)
+               push_tail = &((*push_tail)->next);
+       match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr,
+                  remote->push_refspec, MATCH_REFS_NONE);
+
+       states->push.strdup_strings = 1;
+       for (ref = push_map; ref; ref = ref->next) {
+               struct string_list_item *item;
+               struct push_info *info;
+
+               if (!ref->peer_ref)
+                       continue;
+               hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+
+               item = string_list_append(abbrev_branch(ref->peer_ref->name),
+                                         &states->push);
+               item->util = xcalloc(sizeof(struct push_info), 1);
+               info = item->util;
+               info->forced = ref->force;
+               info->dest = xstrdup(abbrev_branch(ref->name));
+
+               if (is_null_sha1(ref->new_sha1)) {
+                       info->status = PUSH_STATUS_DELETE;
+               } else if (!hashcmp(ref->old_sha1, ref->new_sha1))
+                       info->status = PUSH_STATUS_UPTODATE;
+               else if (is_null_sha1(ref->old_sha1))
+                       info->status = PUSH_STATUS_CREATE;
+               else if (has_sha1_file(ref->old_sha1) &&
+                        ref_newer(ref->new_sha1, ref->old_sha1))
+                       info->status = PUSH_STATUS_FASTFORWARD;
+               else
+                       info->status = PUSH_STATUS_OUTOFDATE;
+       }
+       free_refs(local_refs);
+       free_refs(push_map);
+       return 0;
+}
+
+static int get_push_ref_states_noquery(struct ref_states *states)
+{
+       int i;
+       struct remote *remote = states->remote;
+       struct string_list_item *item;
+       struct push_info *info;
+
+       if (remote->mirror)
+               return 0;
+
+       states->push.strdup_strings = 1;
+       if (!remote->push_refspec_nr) {
+               item = string_list_append("(matching)", &states->push);
+               info = item->util = xcalloc(sizeof(struct push_info), 1);
+               info->status = PUSH_STATUS_NOTQUERIED;
+               info->dest = xstrdup(item->string);
+       }
+       for (i = 0; i < remote->push_refspec_nr; i++) {
+               struct refspec *spec = remote->push + i;
+               if (spec->matching)
+                       item = string_list_append("(matching)", &states->push);
+               else if (strlen(spec->src))
+                       item = string_list_append(spec->src, &states->push);
+               else
+                       item = string_list_append("(delete)", &states->push);
+
+               info = item->util = xcalloc(sizeof(struct push_info), 1);
+               info->forced = spec->force;
+               info->status = PUSH_STATUS_NOTQUERIED;
+               info->dest = xstrdup(spec->dst ? spec->dst : item->string);
+       }
+       return 0;
+}
+
+static int get_head_names(const struct ref *remote_refs, struct ref_states *states)
+{
+       struct ref *ref, *matches;
+       struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
+       struct refspec refspec;
+
+       refspec.force = 0;
+       refspec.pattern = 1;
+       refspec.src = refspec.dst = "refs/heads/*";
+       states->heads.strdup_strings = 1;
+       get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
+       matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
+                                   fetch_map, 1);
+       for(ref = matches; ref; ref = ref->next)
+               string_list_append(abbrev_branch(ref->name), &states->heads);
+
+       free_refs(fetch_map);
+       free_refs(matches);
+
+       return 0;
+}
+
 struct known_remote {
        struct known_remote *next;
        struct remote *remote;
@@ -400,8 +525,8 @@ static int migrate_file(struct remote *remote)
                path = git_path("remotes/%s", remote->name);
        else if (remote->origin == REMOTE_BRANCHES)
                path = git_path("branches/%s", remote->name);
-       if (path && unlink(path))
-               warning("failed to remove '%s'", path);
+       if (path)
+               unlink_or_warn(path);
        return 0;
 }
 
@@ -466,7 +591,7 @@ static int mv(int argc, const char **argv)
        for (i = 0; i < branch_list.nr; i++) {
                struct string_list_item *item = branch_list.items + i;
                struct branch_info *info = item->util;
-               if (info->remote && !strcmp(info->remote, rename.old)) {
+               if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
                        strbuf_reset(&buf);
                        strbuf_addf(&buf, "branch.%s.remote", item->string);
                        if (git_config_set(buf.buf, rename.new)) {
@@ -484,9 +609,8 @@ static int mv(int argc, const char **argv)
                struct string_list_item *item = remote_branches.items + i;
                int flag = 0;
                unsigned char sha1[20];
-               const char *symref;
 
-               symref = resolve_ref(item->string, sha1, 1, &flag);
+               resolve_ref(item->string, sha1, 1, &flag);
                if (!(flag & REF_ISSYMREF))
                        continue;
                if (delete_ref(item->string, NULL, REF_NODEREF))
@@ -576,7 +700,7 @@ static int rm(int argc, const char **argv)
        for (i = 0; i < branch_list.nr; i++) {
                struct string_list_item *item = branch_list.items + i;
                struct branch_info *info = item->util;
-               if (info->remote && !strcmp(info->remote, remote->name)) {
+               if (info->remote_name && !strcmp(info->remote_name, remote->name)) {
                        const char *keys[] = { "remote", "merge", NULL }, **k;
                        for (k = keys; *k; k++) {
                                strbuf_reset(&buf);
@@ -618,18 +742,37 @@ static int rm(int argc, const char **argv)
        return result;
 }
 
-static void show_list(const char *title, struct string_list *list,
-                     const char *extra_arg)
+void clear_push_info(void *util, const char *string)
 {
-       int i;
+       struct push_info *info = util;
+       free(info->dest);
+       free(info);
+}
 
-       if (!list->nr)
-               return;
+static void free_remote_ref_states(struct ref_states *states)
+{
+       string_list_clear(&states->new, 0);
+       string_list_clear(&states->stale, 0);
+       string_list_clear(&states->tracked, 0);
+       string_list_clear(&states->heads, 0);
+       string_list_clear_func(&states->push, clear_push_info);
+}
 
-       printf(title, list->nr > 1 ? "es" : "", extra_arg);
-       printf("\n");
-       for (i = 0; i < list->nr; i++)
-               printf("    %s\n", list->items[i].string);
+static int append_ref_to_tracked_list(const char *refname,
+       const unsigned char *sha1, int flags, void *cb_data)
+{
+       struct ref_states *states = cb_data;
+       struct refspec refspec;
+
+       if (flags & REF_ISSYMREF)
+               return 0;
+
+       memset(&refspec, 0, sizeof(refspec));
+       refspec.dst = (char *)refname;
+       if (!remote_find_tracking(states->remote, &refspec))
+               string_list_append(abbrev_branch(refspec.src), &states->tracked);
+
+       return 0;
 }
 
 static int get_remote_ref_states(const char *name,
@@ -637,7 +780,7 @@ static int get_remote_ref_states(const char *name,
                                 int query)
 {
        struct transport *transport;
-       const struct ref *ref;
+       const struct ref *remote_refs;
 
        states->remote = remote_get(name);
        if (!states->remote)
@@ -646,107 +789,341 @@ static int get_remote_ref_states(const char *name,
        read_branches();
 
        if (query) {
-               transport = transport_get(NULL, states->remote->url_nr > 0 ?
+               transport = transport_get(states->remote, states->remote->url_nr > 0 ?
                        states->remote->url[0] : NULL);
-               ref = transport_get_remote_refs(transport);
+               remote_refs = transport_get_remote_refs(transport);
                transport_disconnect(transport);
 
-               get_ref_states(ref, states);
+               states->queried = 1;
+               if (query & GET_REF_STATES)
+                       get_ref_states(remote_refs, states);
+               if (query & GET_HEAD_NAMES)
+                       get_head_names(remote_refs, states);
+               if (query & GET_PUSH_REF_STATES)
+                       get_push_ref_states(remote_refs, states);
+       } else {
+               for_each_ref(append_ref_to_tracked_list, states);
+               sort_string_list(&states->tracked);
+               get_push_ref_states_noquery(states);
        }
 
        return 0;
 }
 
-static int append_ref_to_tracked_list(const char *refname,
-       const unsigned char *sha1, int flags, void *cb_data)
+struct show_info {
+       struct string_list *list;
+       struct ref_states *states;
+       int width, width2;
+       int any_rebase;
+};
+
+int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
 {
-       struct ref_states *states = cb_data;
-       struct refspec refspec;
+       struct show_info *info = cb_data;
+       int n = strlen(item->string);
+       if (n > info->width)
+               info->width = n;
+       string_list_insert(item->string, info->list);
+       return 0;
+}
 
-       memset(&refspec, 0, sizeof(refspec));
-       refspec.dst = (char *)refname;
-       if (!remote_find_tracking(states->remote, &refspec))
-               string_list_append(abbrev_branch(refspec.src), &states->tracked);
+int show_remote_info_item(struct string_list_item *item, void *cb_data)
+{
+       struct show_info *info = cb_data;
+       struct ref_states *states = info->states;
+       const char *name = item->string;
+
+       if (states->queried) {
+               const char *fmt = "%s";
+               const char *arg = "";
+               if (string_list_has_string(&states->new, name)) {
+                       fmt = " new (next fetch will store in remotes/%s)";
+                       arg = states->remote->name;
+               } else if (string_list_has_string(&states->tracked, name))
+                       arg = " tracked";
+               else if (string_list_has_string(&states->stale, name))
+                       arg = " stale (use 'git remote prune' to remove)";
+               else
+                       arg = " ???";
+               printf("    %-*s", info->width, name);
+               printf(fmt, arg);
+               printf("\n");
+       } else
+               printf("    %s\n", name);
 
        return 0;
 }
 
+int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
+{
+       struct show_info *show_info = cb_data;
+       struct ref_states *states = show_info->states;
+       struct branch_info *branch_info = branch_item->util;
+       struct string_list_item *item;
+       int n;
+
+       if (!branch_info->merge.nr || !branch_info->remote_name ||
+           strcmp(states->remote->name, branch_info->remote_name))
+               return 0;
+       if ((n = strlen(branch_item->string)) > show_info->width)
+               show_info->width = n;
+       if (branch_info->rebase)
+               show_info->any_rebase = 1;
+
+       item = string_list_insert(branch_item->string, show_info->list);
+       item->util = branch_info;
+
+       return 0;
+}
+
+int show_local_info_item(struct string_list_item *item, void *cb_data)
+{
+       struct show_info *show_info = cb_data;
+       struct branch_info *branch_info = item->util;
+       struct string_list *merge = &branch_info->merge;
+       const char *also;
+       int i;
+
+       if (branch_info->rebase && branch_info->merge.nr > 1) {
+               error("invalid branch.%s.merge; cannot rebase onto > 1 branch",
+                       item->string);
+               return 0;
+       }
+
+       printf("    %-*s ", show_info->width, item->string);
+       if (branch_info->rebase) {
+               printf("rebases onto remote %s\n", merge->items[0].string);
+               return 0;
+       } else if (show_info->any_rebase) {
+               printf(" merges with remote %s\n", merge->items[0].string);
+               also = "    and with remote";
+       } else {
+               printf("merges with remote %s\n", merge->items[0].string);
+               also = "   and with remote";
+       }
+       for (i = 1; i < merge->nr; i++)
+               printf("    %-*s %s %s\n", show_info->width, "", also,
+                      merge->items[i].string);
+
+       return 0;
+}
+
+int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
+{
+       struct show_info *show_info = cb_data;
+       struct push_info *push_info = push_item->util;
+       struct string_list_item *item;
+       int n;
+       if ((n = strlen(push_item->string)) > show_info->width)
+               show_info->width = n;
+       if ((n = strlen(push_info->dest)) > show_info->width2)
+               show_info->width2 = n;
+       item = string_list_append(push_item->string, show_info->list);
+       item->util = push_item->util;
+       return 0;
+}
+
+/*
+ * Sorting comparison for a string list that has push_info
+ * structs in its util field
+ */
+static int cmp_string_with_push(const void *va, const void *vb)
+{
+       const struct string_list_item *a = va;
+       const struct string_list_item *b = vb;
+       const struct push_info *a_push = a->util;
+       const struct push_info *b_push = b->util;
+       int cmp = strcmp(a->string, b->string);
+       return cmp ? cmp : strcmp(a_push->dest, b_push->dest);
+}
+
+int show_push_info_item(struct string_list_item *item, void *cb_data)
+{
+       struct show_info *show_info = cb_data;
+       struct push_info *push_info = item->util;
+       char *src = item->string, *status = NULL;
+
+       switch (push_info->status) {
+       case PUSH_STATUS_CREATE:
+               status = "create";
+               break;
+       case PUSH_STATUS_DELETE:
+               status = "delete";
+               src = "(none)";
+               break;
+       case PUSH_STATUS_UPTODATE:
+               status = "up to date";
+               break;
+       case PUSH_STATUS_FASTFORWARD:
+               status = "fast forwardable";
+               break;
+       case PUSH_STATUS_OUTOFDATE:
+               status = "local out of date";
+               break;
+       case PUSH_STATUS_NOTQUERIED:
+               break;
+       }
+       if (status)
+               printf("    %-*s %s to %-*s (%s)\n", show_info->width, src,
+                       push_info->forced ? "forces" : "pushes",
+                       show_info->width2, push_info->dest, status);
+       else
+               printf("    %-*s %s to %s\n", show_info->width, src,
+                       push_info->forced ? "forces" : "pushes",
+                       push_info->dest);
+       return 0;
+}
+
 static int show(int argc, const char **argv)
 {
-       int no_query = 0, result = 0;
+       int no_query = 0, result = 0, query_flag = 0;
        struct option options[] = {
                OPT_GROUP("show specific options"),
                OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"),
                OPT_END()
        };
        struct ref_states states;
+       struct string_list info_list = { NULL, 0, 0, 0 };
+       struct show_info info;
 
        argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
 
        if (argc < 1)
                return show_all();
 
+       if (!no_query)
+               query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
+
        memset(&states, 0, sizeof(states));
+       memset(&info, 0, sizeof(info));
+       info.states = &states;
+       info.list = &info_list;
        for (; argc; argc--, argv++) {
                int i;
 
-               get_remote_ref_states(*argv, &states, !no_query);
-
-               printf("* remote %s\n  URL: %s\n", *argv,
-                       states.remote->url_nr > 0 ?
-                               states.remote->url[0] : "(no URL)");
-
-               for (i = 0; i < branch_list.nr; i++) {
-                       struct string_list_item *branch = branch_list.items + i;
-                       struct branch_info *info = branch->util;
-                       int j;
-
-                       if (!info->merge.nr || strcmp(*argv, info->remote))
-                               continue;
-                       printf("  Remote branch%s merged with 'git pull' "
-                               "while on branch %s\n   ",
-                               info->merge.nr > 1 ? "es" : "",
-                               branch->string);
-                       for (j = 0; j < info->merge.nr; j++)
-                               printf(" %s", info->merge.items[j].string);
-                       printf("\n");
-               }
-
-               if (!no_query) {
-                       show_list("  New remote branch%s (next fetch "
-                               "will store in remotes/%s)",
-                               &states.new, states.remote->name);
-                       show_list("  Stale tracking branch%s (use 'git remote "
-                               "prune')", &states.stale, "");
-               }
+               get_remote_ref_states(*argv, &states, query_flag);
 
+               printf("* remote %s\n", *argv);
+               if (states.remote->url_nr) {
+                       for (i=0; i < states.remote->url_nr; i++)
+                               printf("  URL: %s\n", states.remote->url[i]);
+               } else
+                       printf("  URL: %s\n", "(no URL)");
                if (no_query)
-                       for_each_ref(append_ref_to_tracked_list, &states);
-               show_list("  Tracked remote branch%s", &states.tracked, "");
-
-               if (states.remote->push_refspec_nr) {
-                       printf("  Local branch%s pushed with 'git push'\n",
-                               states.remote->push_refspec_nr > 1 ?
-                                       "es" : "");
-                       for (i = 0; i < states.remote->push_refspec_nr; i++) {
-                               struct refspec *spec = states.remote->push + i;
-                               printf("    %s%s%s%s\n",
-                                      spec->force ? "+" : "",
-                                      abbrev_branch(spec->src),
-                                      spec->dst ? ":" : "",
-                                      spec->dst ? abbrev_branch(spec->dst) : "");
-                       }
+                       printf("  HEAD branch: (not queried)\n");
+               else if (!states.heads.nr)
+                       printf("  HEAD branch: (unknown)\n");
+               else if (states.heads.nr == 1)
+                       printf("  HEAD branch: %s\n", states.heads.items[0].string);
+               else {
+                       printf("  HEAD branch (remote HEAD is ambiguous,"
+                              " may be one of the following):\n");
+                       for (i = 0; i < states.heads.nr; i++)
+                               printf("    %s\n", states.heads.items[i].string);
                }
 
-               /* NEEDSWORK: free remote */
-               string_list_clear(&states.new, 0);
-               string_list_clear(&states.stale, 0);
-               string_list_clear(&states.tracked, 0);
+               /* remote branch info */
+               info.width = 0;
+               for_each_string_list(add_remote_to_show_info, &states.new, &info);
+               for_each_string_list(add_remote_to_show_info, &states.tracked, &info);
+               for_each_string_list(add_remote_to_show_info, &states.stale, &info);
+               if (info.list->nr)
+                       printf("  Remote branch%s:%s\n",
+                              info.list->nr > 1 ? "es" : "",
+                               no_query ? " (status not queried)" : "");
+               for_each_string_list(show_remote_info_item, info.list, &info);
+               string_list_clear(info.list, 0);
+
+               /* git pull info */
+               info.width = 0;
+               info.any_rebase = 0;
+               for_each_string_list(add_local_to_show_info, &branch_list, &info);
+               if (info.list->nr)
+                       printf("  Local branch%s configured for 'git pull':\n",
+                              info.list->nr > 1 ? "es" : "");
+               for_each_string_list(show_local_info_item, info.list, &info);
+               string_list_clear(info.list, 0);
+
+               /* git push info */
+               if (states.remote->mirror)
+                       printf("  Local refs will be mirrored by 'git push'\n");
+
+               info.width = info.width2 = 0;
+               for_each_string_list(add_push_to_show_info, &states.push, &info);
+               qsort(info.list->items, info.list->nr,
+                       sizeof(*info.list->items), cmp_string_with_push);
+               if (info.list->nr)
+                       printf("  Local ref%s configured for 'git push'%s:\n",
+                               info.list->nr > 1 ? "s" : "",
+                               no_query ? " (status not queried)" : "");
+               for_each_string_list(show_push_info_item, info.list, &info);
+               string_list_clear(info.list, 0);
+
+               free_remote_ref_states(&states);
        }
 
        return result;
 }
 
+static int set_head(int argc, const char **argv)
+{
+       int i, opt_a = 0, opt_d = 0, result = 0;
+       struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
+       char *head_name = NULL;
+
+       struct option options[] = {
+               OPT_GROUP("set-head specific options"),
+               OPT_BOOLEAN('a', "auto", &opt_a,
+                           "set refs/remotes/<name>/HEAD according to remote"),
+               OPT_BOOLEAN('d', "delete", &opt_d,
+                           "delete refs/remotes/<name>/HEAD"),
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+       if (argc)
+               strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
+
+       if (!opt_a && !opt_d && argc == 2) {
+               head_name = xstrdup(argv[1]);
+       } else if (opt_a && !opt_d && argc == 1) {
+               struct ref_states states;
+               memset(&states, 0, sizeof(states));
+               get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
+               if (!states.heads.nr)
+                       result |= error("Cannot determine remote HEAD");
+               else if (states.heads.nr > 1) {
+                       result |= error("Multiple remote HEAD branches. "
+                                       "Please choose one explicitly with:");
+                       for (i = 0; i < states.heads.nr; i++)
+                               fprintf(stderr, "  git remote set-head %s %s\n",
+                                       argv[0], states.heads.items[i].string);
+               } else
+                       head_name = xstrdup(states.heads.items[0].string);
+               free_remote_ref_states(&states);
+       } else if (opt_d && !opt_a && argc == 1) {
+               if (delete_ref(buf.buf, NULL, REF_NODEREF))
+                       result |= error("Could not delete %s", buf.buf);
+       } else
+               usage_with_options(builtin_remote_usage, options);
+
+       if (head_name) {
+               unsigned char sha1[20];
+               strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
+               /* make sure it's valid */
+               if (!resolve_ref(buf2.buf, sha1, 1, NULL))
+                       result |= error("Not a valid ref: %s", buf2.buf);
+               else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
+                       result |= error("Could not setup %s", buf.buf);
+               if (opt_a)
+                       printf("%s/HEAD set to %s\n", argv[0], head_name);
+               free(head_name);
+       }
+
+       strbuf_release(&buf);
+       strbuf_release(&buf2);
+       return result;
+}
+
 static int prune(int argc, const char **argv)
 {
        int dry_run = 0, result = 0;
@@ -755,49 +1132,49 @@ static int prune(int argc, const char **argv)
                OPT__DRY_RUN(&dry_run),
                OPT_END()
        };
-       struct ref_states states;
-       const char *dangling_msg;
 
        argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
 
        if (argc < 1)
                usage_with_options(builtin_remote_usage, options);
 
-       dangling_msg = (dry_run
-                       ? " %s will become dangling!\n"
-                       : " %s has become dangling!\n");
+       for (; argc; argc--, argv++)
+               result |= prune_remote(*argv, dry_run);
 
-       memset(&states, 0, sizeof(states));
-       for (; argc; argc--, argv++) {
-               int i;
-
-               get_remote_ref_states(*argv, &states, 1);
+       return result;
+}
 
-               if (states.stale.nr) {
-                       printf("Pruning %s\n", *argv);
-                       printf("URL: %s\n",
-                              states.remote->url_nr
-                              ? states.remote->url[0]
-                              : "(no URL)");
-               }
+static int prune_remote(const char *remote, int dry_run)
+{
+       int result = 0, i;
+       struct ref_states states;
+       const char *dangling_msg = dry_run
+               ? " %s will become dangling!\n"
+               : " %s has become dangling!\n";
 
-               for (i = 0; i < states.stale.nr; i++) {
-                       const char *refname = states.stale.items[i].util;
+       memset(&states, 0, sizeof(states));
+       get_remote_ref_states(remote, &states, GET_REF_STATES);
+
+       if (states.stale.nr) {
+               printf("Pruning %s\n", remote);
+               printf("URL: %s\n",
+                      states.remote->url_nr
+                      ? states.remote->url[0]
+                      : "(no URL)");
+       }
 
-                       if (!dry_run)
-                               result |= delete_ref(refname, NULL, 0);
+       for (i = 0; i < states.stale.nr; i++) {
+               const char *refname = states.stale.items[i].util;
 
-                       printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
-                              abbrev_ref(refname, "refs/remotes/"));
-                       warn_dangling_symref(dangling_msg, refname);
-               }
+               if (!dry_run)
+                       result |= delete_ref(refname, NULL, 0);
 
-               /* NEEDSWORK: free remote */
-               string_list_clear(&states.new, 0);
-               string_list_clear(&states.stale, 0);
-               string_list_clear(&states.tracked, 0);
+               printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
+                      abbrev_ref(refname, "refs/remotes/"));
+               warn_dangling_symref(dangling_msg, refname);
        }
 
+       free_remote_ref_states(&states);
        return result;
 }
 
@@ -814,16 +1191,18 @@ struct remote_group {
        struct string_list *list;
 } remote_group;
 
-static int get_remote_group(const char *key, const char *value, void *cb)
+static int get_remote_group(const char *key, const char *value, void *num_hits)
 {
        if (!prefixcmp(key, "remotes.") &&
                        !strcmp(key + 8, remote_group.name)) {
                /* split list by white space */
                int space = strcspn(value, " \t\n");
                while (*value) {
-                       if (space > 1)
+                       if (space > 1) {
                                string_list_append(xstrndup(value, space),
                                                remote_group.list);
+                               ++*((int *)num_hits);
+                       }
                        value += space + (value[space] != '\0');
                        space = strcspn(value, " \t\n");
                }
@@ -834,10 +1213,18 @@ static int get_remote_group(const char *key, const char *value, void *cb)
 
 static int update(int argc, const char **argv)
 {
-       int i, result = 0;
+       int i, result = 0, prune = 0;
        struct string_list list = { NULL, 0, 0, 0 };
        static const char *default_argv[] = { NULL, "default", NULL };
+       struct option options[] = {
+               OPT_GROUP("update specific options"),
+               OPT_BOOLEAN('p', "prune", &prune,
+                           "prune remotes after fetching"),
+               OPT_END()
+       };
 
+       argc = parse_options(argc, argv, options, builtin_remote_usage,
+                            PARSE_OPT_KEEP_ARGV0);
        if (argc < 2) {
                argc = 2;
                argv = default_argv;
@@ -845,15 +1232,28 @@ static int update(int argc, const char **argv)
 
        remote_group.list = &list;
        for (i = 1; i < argc; i++) {
+               int groups_found = 0;
                remote_group.name = argv[i];
-               result = git_config(get_remote_group, NULL);
+               result = git_config(get_remote_group, &groups_found);
+               if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) {
+                       struct remote *remote;
+                       if (!remote_is_configured(argv[i]))
+                               die("No such remote or remote group: %s",
+                                   argv[i]);
+                       remote = remote_get(argv[i]);
+                       string_list_append(remote->name, remote_group.list);
+               }
        }
 
        if (!result && !list.nr  && argc == 2 && !strcmp(argv[1], "default"))
                result = for_each_remote(get_one_remote_for_update, &list);
 
-       for (i = 0; i < list.nr; i++)
-               result |= fetch_remote(list.items[i].string);
+       for (i = 0; i < list.nr; i++) {
+               int err = fetch_remote(list.items[i].string);
+               result |= err;
+               if (!err && prune)
+                       result |= prune_remote(list.items[i].string, 0);
+       }
 
        /* all names were strdup()ed or strndup()ed */
        list.strdup_strings = 1;
@@ -920,6 +1320,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
                result = mv(argc, argv);
        else if (!strcmp(argv[0], "rm"))
                result = rm(argc, argv);
+       else if (!strcmp(argv[0], "set-head"))
+               result = set_head(argc, argv);
        else if (!strcmp(argv[0], "show"))
                result = show(argc, argv);
        else if (!strcmp(argv[0], "prune"))
index bd8fc77a7a65a21a401f917b75d36883cda634ed..adfb7b5f48597c19c23235b74cf1a0c779985dc6 100644 (file)
@@ -13,28 +13,17 @@ static const char git_rerere_usage[] =
 static int cutoff_noresolve = 15;
 static int cutoff_resolve = 60;
 
-static const char *rr_path(const char *name, const char *file)
-{
-       return git_path("rr-cache/%s/%s", name, file);
-}
-
 static time_t rerere_created_at(const char *name)
 {
        struct stat st;
-       return stat(rr_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
-}
-
-static int has_resolution(const char *name)
-{
-       struct stat st;
-       return !stat(rr_path(name, "postimage"), &st);
+       return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
 }
 
 static void unlink_rr_item(const char *name)
 {
-       unlink(rr_path(name, "thisimage"));
-       unlink(rr_path(name, "preimage"));
-       unlink(rr_path(name, "postimage"));
+       unlink(rerere_path(name, "thisimage"));
+       unlink(rerere_path(name, "preimage"));
+       unlink(rerere_path(name, "postimage"));
        rmdir(git_path("rr-cache/%s", name));
 }
 
@@ -65,7 +54,7 @@ static void garbage_collect(struct string_list *rr)
                then = rerere_created_at(e->d_name);
                if (!then)
                        continue;
-               cutoff = (has_resolution(e->d_name)
+               cutoff = (has_rerere_resolution(e->d_name)
                          ? cutoff_resolve : cutoff_noresolve);
                if (then < now - cutoff * 86400)
                        string_list_append(e->d_name, &to_remove);
@@ -124,10 +113,10 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
        if (!strcmp(argv[1], "clear")) {
                for (i = 0; i < merge_rr.nr; i++) {
                        const char *name = (const char *)merge_rr.items[i].util;
-                       if (!has_resolution(name))
+                       if (!has_rerere_resolution(name))
                                unlink_rr_item(name);
                }
-               unlink(git_path("rr-cache/MERGE_RR"));
+               unlink_or_warn(git_path("rr-cache/MERGE_RR"));
        } else if (!strcmp(argv[1], "gc"))
                garbage_collect(&merge_rr);
        else if (!strcmp(argv[1], "status"))
@@ -137,7 +126,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
                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(rr_path(name, "preimage"), path, path, path);
+                       diff_two(rerere_path(name, "preimage"), path, path, path);
                }
        else
                usage(git_rerere_usage);
index c0cb915c26b56a905807cefa4e246d99d199b297..7e7ebabaa865ebd749502774638efe7b6d26bec1 100644 (file)
@@ -228,7 +228,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                }
                /*
                 * Otherwise, argv[i] could be either <rev> or <paths> and
-                * has to be unambigous.
+                * has to be unambiguous.
                 */
                else if (!get_sha1(argv[i], sha1)) {
                        /*
index 0af7cd94f99728e08379441061098e1966e36b73..38a8f234de8120d15eaff9ee0d342e334ee006ca 100644 (file)
@@ -1,20 +1,12 @@
 #include "cache.h"
-#include "refs.h"
-#include "tag.h"
 #include "commit.h"
-#include "tree.h"
-#include "blob.h"
-#include "tree-walk.h"
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
 #include "builtin.h"
 #include "log-tree.h"
 #include "graph.h"
-
-/* bits #0-15 in revision.h */
-
-#define COUNTED                (1u<<16)
+#include "bisect.h"
 
 static const char rev_list_usage[] =
 "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -50,73 +42,69 @@ static const char rev_list_usage[] =
 "    --bisect-all"
 ;
 
-static struct rev_info revs;
-
-static int bisect_list;
-static int show_timestamp;
-static int hdr_termination;
-static const char *header_prefix;
-
-static void finish_commit(struct commit *commit);
-static void show_commit(struct commit *commit)
+static void finish_commit(struct commit *commit, void *data);
+static void show_commit(struct commit *commit, void *data)
 {
-       graph_show_commit(revs.graph);
+       struct rev_list_info *info = data;
+       struct rev_info *revs = info->revs;
+
+       graph_show_commit(revs->graph);
 
-       if (show_timestamp)
+       if (info->show_timestamp)
                printf("%lu ", commit->date);
-       if (header_prefix)
-               fputs(header_prefix, stdout);
+       if (info->header_prefix)
+               fputs(info->header_prefix, stdout);
 
-       if (!revs.graph) {
+       if (!revs->graph) {
                if (commit->object.flags & BOUNDARY)
                        putchar('-');
                else if (commit->object.flags & UNINTERESTING)
                        putchar('^');
-               else if (revs.left_right) {
+               else if (revs->left_right) {
                        if (commit->object.flags & SYMMETRIC_LEFT)
                                putchar('<');
                        else
                                putchar('>');
                }
        }
-       if (revs.abbrev_commit && revs.abbrev)
-               fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
+       if (revs->abbrev_commit && revs->abbrev)
+               fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev),
                      stdout);
        else
                fputs(sha1_to_hex(commit->object.sha1), stdout);
-       if (revs.print_parents) {
+       if (revs->print_parents) {
                struct commit_list *parents = commit->parents;
                while (parents) {
                        printf(" %s", sha1_to_hex(parents->item->object.sha1));
                        parents = parents->next;
                }
        }
-       if (revs.children.name) {
+       if (revs->children.name) {
                struct commit_list *children;
 
-               children = lookup_decoration(&revs.children, &commit->object);
+               children = lookup_decoration(&revs->children, &commit->object);
                while (children) {
                        printf(" %s", sha1_to_hex(children->item->object.sha1));
                        children = children->next;
                }
        }
-       show_decorations(&revs, commit);
-       if (revs.commit_format == CMIT_FMT_ONELINE)
+       show_decorations(revs, commit);
+       if (revs->commit_format == CMIT_FMT_ONELINE)
                putchar(' ');
        else
                putchar('\n');
 
-       if (revs.verbose_header && commit->buffer) {
+       if (revs->verbose_header && commit->buffer) {
                struct strbuf buf = STRBUF_INIT;
-               pretty_print_commit(revs.commit_format, commit,
-                                   &buf, revs.abbrev, NULL, NULL,
-                                   revs.date_mode, 0);
-               if (revs.graph) {
+               pretty_print_commit(revs->commit_format, commit,
+                                   &buf, revs->abbrev, NULL, NULL,
+                                   revs->date_mode, 0);
+               if (revs->graph) {
                        if (buf.len) {
-                               if (revs.commit_format != CMIT_FMT_ONELINE)
-                                       graph_show_oneline(revs.graph);
+                               if (revs->commit_format != CMIT_FMT_ONELINE)
+                                       graph_show_oneline(revs->graph);
 
-                               graph_show_commit_msg(revs.graph, &buf);
+                               graph_show_commit_msg(revs->graph, &buf);
 
                                /*
                                 * Add a newline after the commit message.
@@ -134,7 +122,7 @@ static void show_commit(struct commit *commit)
                                 * format doesn't explicitly end in a newline.)
                                 */
                                if (buf.len && buf.buf[buf.len - 1] == '\n')
-                                       graph_show_padding(revs.graph);
+                                       graph_show_padding(revs->graph);
                                putchar('\n');
                        } else {
                                /*
@@ -142,23 +130,23 @@ static void show_commit(struct commit *commit)
                                 * the rest of the graph output for this
                                 * commit.
                                 */
-                               if (graph_show_remainder(revs.graph))
+                               if (graph_show_remainder(revs->graph))
                                        putchar('\n');
                        }
                } else {
                        if (buf.len)
-                               printf("%s%c", buf.buf, hdr_termination);
+                               printf("%s%c", buf.buf, info->hdr_termination);
                }
                strbuf_release(&buf);
        } else {
-               if (graph_show_remainder(revs.graph))
+               if (graph_show_remainder(revs->graph))
                        putchar('\n');
        }
        maybe_flush_or_die(stdout, "stdout");
-       finish_commit(commit);
+       finish_commit(commit, data);
 }
 
-static void finish_commit(struct commit *commit)
+static void finish_commit(struct commit *commit, void *data)
 {
        if (commit->parents) {
                free_commit_list(commit->parents);
@@ -198,389 +186,122 @@ static void show_edge(struct commit *commit)
        printf("-%s\n", sha1_to_hex(commit->object.sha1));
 }
 
-/*
- * This is a truly stupid algorithm, but it's only
- * used for bisection, and we just don't care enough.
- *
- * We care just barely enough to avoid recursing for
- * non-merge entries.
- */
-static int count_distance(struct commit_list *entry)
-{
-       int nr = 0;
-
-       while (entry) {
-               struct commit *commit = entry->item;
-               struct commit_list *p;
-
-               if (commit->object.flags & (UNINTERESTING | COUNTED))
-                       break;
-               if (!(commit->object.flags & TREESAME))
-                       nr++;
-               commit->object.flags |= COUNTED;
-               p = commit->parents;
-               entry = p;
-               if (p) {
-                       p = p->next;
-                       while (p) {
-                               nr += count_distance(p);
-                               p = p->next;
-                       }
-               }
-       }
-
-       return nr;
-}
-
-static void clear_distance(struct commit_list *list)
+static inline int log2i(int n)
 {
-       while (list) {
-               struct commit *commit = list->item;
-               commit->object.flags &= ~COUNTED;
-               list = list->next;
-       }
-}
+       int log2 = 0;
 
-#define DEBUG_BISECT 0
+       for (; n > 1; n >>= 1)
+               log2++;
 
-static inline int weight(struct commit_list *elem)
-{
-       return *((int*)(elem->item->util));
+       return log2;
 }
 
-static inline void weight_set(struct commit_list *elem, int weight)
+static inline int exp2i(int n)
 {
-       *((int*)(elem->item->util)) = weight;
+       return 1 << n;
 }
 
-static int count_interesting_parents(struct commit *commit)
+/*
+ * Estimate the number of bisect steps left (after the current step)
+ *
+ * For any x between 0 included and 2^n excluded, the probability for
+ * n - 1 steps left looks like:
+ *
+ * P(2^n + x) == (2^n - x) / (2^n + x)
+ *
+ * and P(2^n + x) < 0.5 means 2^n < 3x
+ */
+static int estimate_bisect_steps(int all)
 {
-       struct commit_list *p;
-       int count;
+       int n, x, e;
 
-       for (count = 0, p = commit->parents; p; p = p->next) {
-               if (p->item->object.flags & UNINTERESTING)
-                       continue;
-               count++;
-       }
-       return count;
-}
-
-static inline int halfway(struct commit_list *p, int nr)
-{
-       /*
-        * Don't short-cut something we are not going to return!
-        */
-       if (p->item->object.flags & TREESAME)
+       if (all < 3)
                return 0;
-       if (DEBUG_BISECT)
-               return 0;
-       /*
-        * 2 and 3 are halfway of 5.
-        * 3 is halfway of 6 but 2 and 4 are not.
-        */
-       switch (2 * weight(p) - nr) {
-       case -1: case 0: case 1:
-               return 1;
-       default:
-               return 0;
-       }
-}
-
-#if !DEBUG_BISECT
-#define show_list(a,b,c,d) do { ; } while (0)
-#else
-static void show_list(const char *debug, int counted, int nr,
-                     struct commit_list *list)
-{
-       struct commit_list *p;
-
-       fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
-
-       for (p = list; p; p = p->next) {
-               struct commit_list *pp;
-               struct commit *commit = p->item;
-               unsigned flags = commit->object.flags;
-               enum object_type type;
-               unsigned long size;
-               char *buf = read_sha1_file(commit->object.sha1, &type, &size);
-               char *ep, *sp;
-
-               fprintf(stderr, "%c%c%c ",
-                       (flags & TREESAME) ? ' ' : 'T',
-                       (flags & UNINTERESTING) ? 'U' : ' ',
-                       (flags & COUNTED) ? 'C' : ' ');
-               if (commit->util)
-                       fprintf(stderr, "%3d", weight(p));
-               else
-                       fprintf(stderr, "---");
-               fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
-               for (pp = commit->parents; pp; pp = pp->next)
-                       fprintf(stderr, " %.*s", 8,
-                               sha1_to_hex(pp->item->object.sha1));
-
-               sp = strstr(buf, "\n\n");
-               if (sp) {
-                       sp += 2;
-                       for (ep = sp; *ep && *ep != '\n'; ep++)
-                               ;
-                       fprintf(stderr, " %.*s", (int)(ep - sp), sp);
-               }
-               fprintf(stderr, "\n");
-       }
-}
-#endif /* DEBUG_BISECT */
-
-static struct commit_list *best_bisection(struct commit_list *list, int nr)
-{
-       struct commit_list *p, *best;
-       int best_distance = -1;
-
-       best = list;
-       for (p = list; p; p = p->next) {
-               int distance;
-               unsigned flags = p->item->object.flags;
-
-               if (flags & TREESAME)
-                       continue;
-               distance = weight(p);
-               if (nr - distance < distance)
-                       distance = nr - distance;
-               if (distance > best_distance) {
-                       best = p;
-                       best_distance = distance;
-               }
-       }
-
-       return best;
-}
-
-struct commit_dist {
-       struct commit *commit;
-       int distance;
-};
 
-static int compare_commit_dist(const void *a_, const void *b_)
-{
-       struct commit_dist *a, *b;
+       n = log2i(all);
+       e = exp2i(n);
+       x = all - e;
 
-       a = (struct commit_dist *)a_;
-       b = (struct commit_dist *)b_;
-       if (a->distance != b->distance)
-               return b->distance - a->distance; /* desc sort */
-       return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
+       return (e < 3 * x) ? n : n - 1;
 }
 
-static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
+static void show_tried_revs(struct commit_list *tried, int stringed)
 {
-       struct commit_list *p;
-       struct commit_dist *array = xcalloc(nr, sizeof(*array));
-       int cnt, i;
-
-       for (p = list, cnt = 0; p; p = p->next) {
-               int distance;
-               unsigned flags = p->item->object.flags;
-
-               if (flags & TREESAME)
-                       continue;
-               distance = weight(p);
-               if (nr - distance < distance)
-                       distance = nr - distance;
-               array[cnt].commit = p->item;
-               array[cnt].distance = distance;
-               cnt++;
-       }
-       qsort(array, cnt, sizeof(*array), compare_commit_dist);
-       for (p = list, i = 0; i < cnt; i++) {
-               struct name_decoration *r = xmalloc(sizeof(*r) + 100);
-               struct object *obj = &(array[i].commit->object);
-
-               sprintf(r->name, "dist=%d", array[i].distance);
-               r->next = add_decoration(&name_decoration, obj, r);
-               p->item = array[i].commit;
-               p = p->next;
+       printf("bisect_tried='");
+       for (;tried; tried = tried->next) {
+               char *format = tried->next ? "%s|" : "%s";
+               printf(format, sha1_to_hex(tried->item->object.sha1));
        }
-       if (p)
-               p->next = NULL;
-       free(array);
-       return list;
+       printf(stringed ? "' &&\n" : "'\n");
 }
 
-/*
- * zero or positive weight is the number of interesting commits it can
- * reach, including itself.  Especially, weight = 0 means it does not
- * reach any tree-changing commits (e.g. just above uninteresting one
- * but traversal is with pathspec).
- *
- * weight = -1 means it has one parent and its distance is yet to
- * be computed.
- *
- * weight = -2 means it has more than one parent and its distance is
- * unknown.  After running count_distance() first, they will get zero
- * or positive distance.
- */
-static struct commit_list *do_find_bisection(struct commit_list *list,
-                                            int nr, int *weights,
-                                            int find_all)
+int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
 {
-       int n, counted;
-       struct commit_list *p;
-
-       counted = 0;
-
-       for (n = 0, p = list; p; p = p->next) {
-               struct commit *commit = p->item;
-               unsigned flags = commit->object.flags;
-
-               p->item->util = &weights[n++];
-               switch (count_interesting_parents(commit)) {
-               case 0:
-                       if (!(flags & TREESAME)) {
-                               weight_set(p, 1);
-                               counted++;
-                               show_list("bisection 2 count one",
-                                         counted, nr, list);
-                       }
-                       /*
-                        * otherwise, it is known not to reach any
-                        * tree-changing commit and gets weight 0.
-                        */
-                       break;
-               case 1:
-                       weight_set(p, -1);
-                       break;
-               default:
-                       weight_set(p, -2);
-                       break;
-               }
-       }
+       int cnt, flags = info->bisect_show_flags;
+       char hex[41] = "", *format;
+       struct commit_list *tried;
+       struct rev_info *revs = info->revs;
 
-       show_list("bisection 2 initialize", counted, nr, list);
+       if (!revs->commits && !(flags & BISECT_SHOW_TRIED))
+               return 1;
+
+       revs->commits = filter_skipped(revs->commits, &tried, flags & BISECT_SHOW_ALL);
 
        /*
-        * If you have only one parent in the resulting set
-        * then you can reach one commit more than that parent
-        * can reach.  So we do not have to run the expensive
-        * count_distance() for single strand of pearls.
-        *
-        * However, if you have more than one parents, you cannot
-        * just add their distance and one for yourself, since
-        * they usually reach the same ancestor and you would
-        * end up counting them twice that way.
-        *
-        * So we will first count distance of merges the usual
-        * way, and then fill the blanks using cheaper algorithm.
+        * revs->commits can reach "reaches" commits among
+        * "all" commits.  If it is good, then there are
+        * (all-reaches) commits left to be bisected.
+        * On the other hand, if it is bad, then the set
+        * to bisect is "reaches".
+        * A bisect set of size N has (N-1) commits further
+        * to test, as we already know one bad one.
         */
-       for (p = list; p; p = p->next) {
-               if (p->item->object.flags & UNINTERESTING)
-                       continue;
-               if (weight(p) != -2)
-                       continue;
-               weight_set(p, count_distance(p));
-               clear_distance(list);
-
-               /* Does it happen to be at exactly half-way? */
-               if (!find_all && halfway(p, nr))
-                       return p;
-               counted++;
-       }
+       cnt = all - reaches;
+       if (cnt < reaches)
+               cnt = reaches;
 
-       show_list("bisection 2 count_distance", counted, nr, list);
+       if (revs->commits)
+               strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1));
 
-       while (counted < nr) {
-               for (p = list; p; p = p->next) {
-                       struct commit_list *q;
-                       unsigned flags = p->item->object.flags;
-
-                       if (0 <= weight(p))
-                               continue;
-                       for (q = p->item->parents; q; q = q->next) {
-                               if (q->item->object.flags & UNINTERESTING)
-                                       continue;
-                               if (0 <= weight(q))
-                                       break;
-                       }
-                       if (!q)
-                               continue;
-
-                       /*
-                        * weight for p is unknown but q is known.
-                        * add one for p itself if p is to be counted,
-                        * otherwise inherit it from q directly.
-                        */
-                       if (!(flags & TREESAME)) {
-                               weight_set(p, weight(q)+1);
-                               counted++;
-                               show_list("bisection 2 count one",
-                                         counted, nr, list);
-                       }
-                       else
-                               weight_set(p, weight(q));
-
-                       /* Does it happen to be at exactly half-way? */
-                       if (!find_all && halfway(p, nr))
-                               return p;
-               }
+       if (flags & BISECT_SHOW_ALL) {
+               traverse_commit_list(revs, show_commit, show_object, info);
+               printf("------\n");
        }
 
-       show_list("bisection 2 counted all", counted, nr, list);
-
-       if (!find_all)
-               return best_bisection(list, nr);
-       else
-               return best_bisection_sorted(list, nr);
-}
-
-static struct commit_list *find_bisection(struct commit_list *list,
-                                         int *reaches, int *all,
-                                         int find_all)
-{
-       int nr, on_list;
-       struct commit_list *p, *best, *next, *last;
-       int *weights;
-
-       show_list("bisection 2 entry", 0, 0, list);
+       if (flags & BISECT_SHOW_TRIED)
+               show_tried_revs(tried, flags & BISECT_SHOW_STRINGED);
+       format = (flags & BISECT_SHOW_STRINGED) ?
+               "bisect_rev=%s &&\n"
+               "bisect_nr=%d &&\n"
+               "bisect_good=%d &&\n"
+               "bisect_bad=%d &&\n"
+               "bisect_all=%d &&\n"
+               "bisect_steps=%d\n"
+               :
+               "bisect_rev=%s\n"
+               "bisect_nr=%d\n"
+               "bisect_good=%d\n"
+               "bisect_bad=%d\n"
+               "bisect_all=%d\n"
+               "bisect_steps=%d\n";
+       printf(format,
+              hex,
+              cnt - 1,
+              all - reaches - 1,
+              reaches - 1,
+              all,
+              estimate_bisect_steps(all));
 
-       /*
-        * Count the number of total and tree-changing items on the
-        * list, while reversing the list.
-        */
-       for (nr = on_list = 0, last = NULL, p = list;
-            p;
-            p = next) {
-               unsigned flags = p->item->object.flags;
-
-               next = p->next;
-               if (flags & UNINTERESTING)
-                       continue;
-               p->next = last;
-               last = p;
-               if (!(flags & TREESAME))
-                       nr++;
-               on_list++;
-       }
-       list = last;
-       show_list("bisection 2 sorted", 0, nr, list);
-
-       *all = nr;
-       weights = xcalloc(on_list, sizeof(*weights));
-
-       /* Do the real work of finding bisection commit. */
-       best = do_find_bisection(list, nr, weights, find_all);
-       if (best) {
-               if (!find_all)
-                       best->next = NULL;
-               *reaches = weight(best);
-       }
-       free(weights);
-       return best;
+       return 0;
 }
 
 int cmd_rev_list(int argc, const char **argv, const char *prefix)
 {
-       struct commit_list *list;
+       struct rev_info revs;
+       struct rev_list_info info;
        int i;
        int read_from_stdin = 0;
+       int bisect_list = 0;
        int bisect_show_vars = 0;
        int bisect_find_all = 0;
        int quiet = 0;
@@ -591,6 +312,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
        argc = setup_revisions(argc, argv, &revs, NULL);
 
+       memset(&info, 0, sizeof(info));
+       info.revs = &revs;
+
        quiet = DIFF_OPT_TST(&revs.diffopt, QUIET);
        for (i = 1 ; i < argc; i++) {
                const char *arg = argv[i];
@@ -600,7 +324,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp(arg, "--timestamp")) {
-                       show_timestamp = 1;
+                       info.show_timestamp = 1;
                        continue;
                }
                if (!strcmp(arg, "--bisect")) {
@@ -610,6 +334,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                if (!strcmp(arg, "--bisect-all")) {
                        bisect_list = 1;
                        bisect_find_all = 1;
+                       info.bisect_show_flags = BISECT_SHOW_ALL;
                        revs.show_decorations = 1;
                        continue;
                }
@@ -629,19 +354,17 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        }
        if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
                /* The command line has a --pretty  */
-               hdr_termination = '\n';
+               info.hdr_termination = '\n';
                if (revs.commit_format == CMIT_FMT_ONELINE)
-                       header_prefix = "";
+                       info.header_prefix = "";
                else
-                       header_prefix = "commit ";
+                       info.header_prefix = "commit ";
        }
        else if (revs.verbose_header)
                /* Only --header was specified */
                revs.commit_format = CMIT_FMT_RAW;
 
-       list = revs.commits;
-
-       if ((!list &&
+       if ((!revs.commits &&
             (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
              !revs.pending.nr)) ||
            revs.diff)
@@ -662,47 +385,15 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
                revs.commits = find_bisection(revs.commits, &reaches, &all,
                                              bisect_find_all);
-               if (bisect_show_vars) {
-                       int cnt;
-                       char hex[41];
-                       if (!revs.commits)
-                               return 1;
-                       /*
-                        * revs.commits can reach "reaches" commits among
-                        * "all" commits.  If it is good, then there are
-                        * (all-reaches) commits left to be bisected.
-                        * On the other hand, if it is bad, then the set
-                        * to bisect is "reaches".
-                        * A bisect set of size N has (N-1) commits further
-                        * to test, as we already know one bad one.
-                        */
-                       cnt = all - reaches;
-                       if (cnt < reaches)
-                               cnt = reaches;
-                       strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1));
-
-                       if (bisect_find_all) {
-                               traverse_commit_list(&revs, show_commit, show_object);
-                               printf("------\n");
-                       }
 
-                       printf("bisect_rev=%s\n"
-                              "bisect_nr=%d\n"
-                              "bisect_good=%d\n"
-                              "bisect_bad=%d\n"
-                              "bisect_all=%d\n",
-                              hex,
-                              cnt - 1,
-                              all - reaches - 1,
-                              reaches - 1,
-                              all);
-                       return 0;
-               }
+               if (bisect_show_vars)
+                       return show_bisect_vars(&info, reaches, all);
        }
 
        traverse_commit_list(&revs,
-               quiet ? finish_commit : show_commit,
-               quiet ? finish_object : show_object);
+                            quiet ? finish_commit : show_commit,
+                            quiet ? finish_object : show_object,
+                            &info);
 
        return 0;
 }
index 81d5a6ffc9ff1c149bfb68f976a9e66c307cae1d..22c6d6ad161f0de7dee88336a81a8f1f873c0bb0 100644 (file)
@@ -26,6 +26,8 @@ static int show_type = NORMAL;
 #define SHOW_SYMBOLIC_FULL 2
 static int symbolic;
 static int abbrev;
+static int abbrev_ref;
+static int abbrev_ref_strict;
 static int output_sq;
 
 /*
@@ -109,8 +111,8 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
                return;
        def = NULL;
 
-       if (symbolic && name) {
-               if (symbolic == SHOW_SYMBOLIC_FULL) {
+       if ((symbolic || abbrev_ref) && name) {
+               if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) {
                        unsigned char discard[20];
                        char *full;
 
@@ -125,6 +127,9 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
                                 */
                                break;
                        case 1: /* happy */
+                               if (abbrev_ref)
+                                       full = shorten_unambiguous_ref(full,
+                                               abbrev_ref_strict);
                                show_with_type(type, full);
                                break;
                        default: /* ambiguous */
@@ -506,6 +511,20 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                symbolic = SHOW_SYMBOLIC_FULL;
                                continue;
                        }
+                       if (!prefixcmp(arg, "--abbrev-ref") &&
+                           (!arg[12] || arg[12] == '=')) {
+                               abbrev_ref = 1;
+                               abbrev_ref_strict = warn_ambiguous_refs;
+                               if (arg[12] == '=') {
+                                       if (!strcmp(arg + 13, "strict"))
+                                               abbrev_ref_strict = 1;
+                                       else if (!strcmp(arg + 13, "loose"))
+                                               abbrev_ref_strict = 0;
+                                       else
+                                               die("unknown mode for %s", arg);
+                               }
+                               continue;
+                       }
                        if (!strcmp(arg, "--all")) {
                                for_each_ref(show_reference, NULL);
                                continue;
index c11f45585825962e61bdee4bdd6df206f0a141c6..269d60890ac6732f05b835e88bdb60a10bb3d441 100644 (file)
@@ -59,8 +59,7 @@ static int check_local_mod(unsigned char *head, int index_only)
 
                if (lstat(ce->name, &st) < 0) {
                        if (errno != ENOENT)
-                               fprintf(stderr, "warning: '%s': %s",
-                                       ce->name, strerror(errno));
+                               warning("'%s': %s", ce->name, strerror(errno));
                        /* It already vanished from the working tree */
                        continue;
                }
index d65d01969252332eeee12b0419e4ba3a806952b1..473a3de40c4cc9aab5c1355d0602646234e7bc1c 100644 (file)
@@ -1,6 +1,5 @@
 #include "cache.h"
 #include "commit.h"
-#include "tag.h"
 #include "refs.h"
 #include "pkt-line.h"
 #include "run-command.h"
@@ -11,9 +10,7 @@ static const char send_pack_usage[] =
 "git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
 "  --all and explicit <ref> specification are mutually exclusive.";
 
-static struct send_pack_args args = {
-       /* .receivepack = */ "git-receive-pack",
-};
+static struct send_pack_args args;
 
 static int feed_object(const unsigned char *sha1, int fd, int negative)
 {
@@ -32,7 +29,7 @@ static int feed_object(const unsigned char *sha1, int fd, int negative)
 /*
  * Make a pack stream and spit it out into file descriptor fd
  */
-static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra)
+static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args)
 {
        /*
         * The child becomes pack-objects --revs; we feed
@@ -46,12 +43,16 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
                "--stdout",
                NULL,
                NULL,
+               NULL,
        };
        struct child_process po;
        int i;
 
-       if (args.use_thin_pack)
-               argv[4] = "--thin";
+       i = 4;
+       if (args->use_thin_pack)
+               argv[i++] = "--thin";
+       if (args->use_ofs_delta)
+               argv[i++] = "--delta-base-offset";
        memset(&po, 0, sizeof(po));
        po.argv = argv;
        po.in = -1;
@@ -84,82 +85,6 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
        return 0;
 }
 
-static void unmark_and_free(struct commit_list *list, unsigned int mark)
-{
-       while (list) {
-               struct commit_list *temp = list;
-               temp->item->object.flags &= ~mark;
-               list = temp->next;
-               free(temp);
-       }
-}
-
-static int ref_newer(const unsigned char *new_sha1,
-                    const unsigned char *old_sha1)
-{
-       struct object *o;
-       struct commit *old, *new;
-       struct commit_list *list, *used;
-       int found = 0;
-
-       /* Both new and old must be commit-ish and new is descendant of
-        * old.  Otherwise we require --force.
-        */
-       o = deref_tag(parse_object(old_sha1), NULL, 0);
-       if (!o || o->type != OBJ_COMMIT)
-               return 0;
-       old = (struct commit *) o;
-
-       o = deref_tag(parse_object(new_sha1), NULL, 0);
-       if (!o || o->type != OBJ_COMMIT)
-               return 0;
-       new = (struct commit *) o;
-
-       if (parse_commit(new) < 0)
-               return 0;
-
-       used = list = NULL;
-       commit_list_insert(new, &list);
-       while (list) {
-               new = pop_most_recent_commit(&list, 1);
-               commit_list_insert(new, &used);
-               if (new == old) {
-                       found = 1;
-                       break;
-               }
-       }
-       unmark_and_free(list, 1);
-       unmark_and_free(used, 1);
-       return found;
-}
-
-static struct ref *local_refs, **local_tail;
-static struct ref *remote_refs, **remote_tail;
-
-static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-       struct ref *ref;
-       int len;
-
-       /* we already know it starts with refs/ to get here */
-       if (check_ref_format(refname + 5))
-               return 0;
-
-       len = strlen(refname) + 1;
-       ref = xcalloc(1, sizeof(*ref) + len);
-       hashcpy(ref->new_sha1, sha1);
-       memcpy(ref->name, refname, len);
-       *local_tail = ref;
-       local_tail = &ref->next;
-       return 0;
-}
-
-static void get_local_heads(void)
-{
-       local_tail = &local_refs;
-       for_each_ref(one_local_ref, NULL);
-}
-
 static int receive_status(int in, struct ref *refs)
 {
        struct ref *hint;
@@ -247,16 +172,6 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
        }
 }
 
-static const char *prettify_ref(const struct ref *ref)
-{
-       const char *name = ref->name;
-       return name + (
-               !prefixcmp(name, "refs/heads/") ? 11 :
-               !prefixcmp(name, "refs/tags/") ? 10 :
-               !prefixcmp(name, "refs/remotes/") ? 13 :
-               0);
-}
-
 #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 
 static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
@@ -385,47 +300,31 @@ static int refs_pushed(struct ref *ref)
        return 0;
 }
 
-static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)
+int send_pack(struct send_pack_args *args,
+             int fd[], struct child_process *conn,
+             struct ref *remote_refs,
+             struct extra_have_objects *extra_have)
 {
+       int in = fd[0];
+       int out = fd[1];
        struct ref *ref;
        int new_refs;
        int ask_for_status_report = 0;
        int allow_deleting_refs = 0;
        int expect_status_report = 0;
-       int flags = MATCH_REFS_NONE;
        int ret;
-       struct extra_have_objects extra_have;
-
-       memset(&extra_have, 0, sizeof(extra_have));
-       if (args.send_all)
-               flags |= MATCH_REFS_ALL;
-       if (args.send_mirror)
-               flags |= MATCH_REFS_MIRROR;
-
-       /* No funny business with the matcher */
-       remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL,
-                                      &extra_have);
-       get_local_heads();
 
        /* Does the other end support the reporting? */
        if (server_supports("report-status"))
                ask_for_status_report = 1;
        if (server_supports("delete-refs"))
                allow_deleting_refs = 1;
-
-       /* match them up */
-       if (!remote_tail)
-               remote_tail = &remote_refs;
-       if (match_refs(local_refs, remote_refs, &remote_tail,
-                      nr_refspec, refspec, flags)) {
-               close(out);
-               return -1;
-       }
+       if (server_supports("ofs-delta"))
+               args->use_ofs_delta = 1;
 
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
                        "Perhaps you should specify a branch such as 'master'.\n");
-               close(out);
                return 0;
        }
 
@@ -437,7 +336,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 
                if (ref->peer_ref)
                        hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-               else if (!args.send_mirror)
+               else if (!args->send_mirror)
                        continue;
 
                ref->deletion = is_null_sha1(ref->new_sha1);
@@ -476,7 +375,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
                    (!has_sha1_file(ref->old_sha1)
                      || !ref_newer(ref->new_sha1, ref->old_sha1));
 
-               if (ref->nonfastforward && !ref->force && !args.force_update) {
+               if (ref->nonfastforward && !ref->force && !args->force_update) {
                        ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
                        continue;
                }
@@ -484,7 +383,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
                if (!ref->deletion)
                        new_refs++;
 
-               if (!args.dry_run) {
+               if (!args->dry_run) {
                        char *old_hex = sha1_to_hex(ref->old_sha1);
                        char *new_hex = sha1_to_hex(ref->new_sha1);
 
@@ -505,27 +404,19 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
        }
 
        packet_flush(out);
-       if (new_refs && !args.dry_run) {
-               if (pack_objects(out, remote_refs, &extra_have) < 0)
+       if (new_refs && !args->dry_run) {
+               if (pack_objects(out, remote_refs, extra_have, args) < 0) {
+                       for (ref = remote_refs; ref; ref = ref->next)
+                               ref->status = REF_STATUS_NONE;
                        return -1;
+               }
        }
-       else
-               close(out);
 
        if (expect_status_report)
                ret = receive_status(in, remote_refs);
        else
                ret = 0;
 
-       print_push_status(dest, remote_refs);
-
-       if (!args.dry_run && remote) {
-               for (ref = remote_refs; ref; ref = ref->next)
-                       update_tracking_ref(remote, ref);
-       }
-
-       if (!refs_pushed(remote_refs))
-               fprintf(stderr, "Everything up-to-date\n");
        if (ret < 0)
                return ret;
        for (ref = remote_refs; ref; ref = ref->next) {
@@ -574,11 +465,19 @@ static void verify_remote_names(int nr_heads, const char **heads)
 
 int cmd_send_pack(int argc, const char **argv, const char *prefix)
 {
-       int i, nr_heads = 0;
-       const char **heads = NULL;
+       int i, nr_refspecs = 0;
+       const char **refspecs = NULL;
        const char *remote_name = NULL;
        struct remote *remote = NULL;
        const char *dest = NULL;
+       int fd[2];
+       struct child_process *conn;
+       struct extra_have_objects extra_have;
+       struct ref *remote_refs, **remote_tail, *local_refs;
+       int ret;
+       int send_all = 0;
+       const char *receivepack = "git-receive-pack";
+       int flags;
 
        argv++;
        for (i = 1; i < argc; i++, argv++) {
@@ -586,11 +485,11 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 
                if (*arg == '-') {
                        if (!prefixcmp(arg, "--receive-pack=")) {
-                               args.receivepack = arg + 15;
+                               receivepack = arg + 15;
                                continue;
                        }
                        if (!prefixcmp(arg, "--exec=")) {
-                               args.receivepack = arg + 7;
+                               receivepack = arg + 7;
                                continue;
                        }
                        if (!prefixcmp(arg, "--remote=")) {
@@ -598,7 +497,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--all")) {
-                               args.send_all = 1;
+                               send_all = 1;
                                continue;
                        }
                        if (!strcmp(arg, "--dry-run")) {
@@ -627,8 +526,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                        dest = arg;
                        continue;
                }
-               heads = (const char **) argv;
-               nr_heads = argc - i;
+               refspecs = (const char **) argv;
+               nr_refspecs = argc - i;
                break;
        }
        if (!dest)
@@ -637,8 +536,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
         * --all and --mirror are incompatible; neither makes sense
         * with any refspecs.
         */
-       if ((heads && (args.send_all || args.send_mirror)) ||
-                                       (args.send_all && args.send_mirror))
+       if ((refspecs && (send_all || args.send_mirror)) ||
+           (send_all && args.send_mirror))
                usage(send_pack_usage);
 
        if (remote_name) {
@@ -649,24 +548,50 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                }
        }
 
-       return send_pack(&args, dest, remote, nr_heads, heads);
-}
+       conn = git_connect(fd, dest, receivepack, args.verbose ? CONNECT_VERBOSE : 0);
 
-int send_pack(struct send_pack_args *my_args,
-             const char *dest, struct remote *remote,
-             int nr_heads, const char **heads)
-{
-       int fd[2], ret;
-       struct child_process *conn;
+       memset(&extra_have, 0, sizeof(extra_have));
+
+       get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL,
+                        &extra_have);
 
-       memcpy(&args, my_args, sizeof(args));
+       verify_remote_names(nr_refspecs, refspecs);
 
-       verify_remote_names(nr_heads, heads);
+       local_refs = get_local_heads();
 
-       conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
-       ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
+       flags = MATCH_REFS_NONE;
+
+       if (send_all)
+               flags |= MATCH_REFS_ALL;
+       if (args.send_mirror)
+               flags |= MATCH_REFS_MIRROR;
+
+       /* match them up */
+       remote_tail = &remote_refs;
+       while (*remote_tail)
+               remote_tail = &((*remote_tail)->next);
+       if (match_refs(local_refs, remote_refs, &remote_tail,
+                      nr_refspecs, refspecs, flags)) {
+               return -1;
+       }
+
+       ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
+
+       close(fd[1]);
        close(fd[0]);
-       /* do_send_pack always closes fd[1] */
+
        ret |= finish_connect(conn);
-       return !!ret;
+
+       print_push_status(dest, remote_refs);
+
+       if (!args.dry_run && remote) {
+               struct ref *ref;
+               for (ref = remote_refs; ref; ref = ref->next)
+                       update_tracking_ref(remote, ref);
+       }
+
+       if (!ret && !refs_pushed(remote_refs))
+               fprintf(stderr, "Everything up-to-date\n");
+
+       return ret;
 }
index badd9120388569c79b137f679505b495cc1dbbe9..b28091b4455db15e80841f2779ce0686d4f18826 100644 (file)
@@ -101,7 +101,6 @@ static void insert_one_record(struct shortlog *log,
        }
        while (*oneline && isspace(*oneline) && *oneline != '\n')
                oneline++;
-       len = eol - oneline;
        format_subject(&subject, oneline, " ");
        buffer = strbuf_detach(&subject, NULL);
 
index 306b850c720ecef7030bd7139f7c7d2758125ac4..c3afabbe914d699a8e0c80bdb5063ca51506167e 100644 (file)
@@ -365,8 +365,7 @@ static int append_ref(const char *refname, const unsigned char *sha1,
                                return 0;
        }
        if (MAX_REVS <= ref_name_cnt) {
-               fprintf(stderr, "warning: ignoring %s; "
-                       "cannot handle more than %d refs\n",
+               warning("ignoring %s; cannot handle more than %d refs",
                        refname, MAX_REVS);
                return 0;
        }
@@ -577,7 +576,7 @@ static void parse_reflog_param(const char *arg, int *cnt, const char **base)
        if (*ep == ',')
                *base = ep + 1;
        else if (*ep)
-               die("unrecognized reflog param '%s'", arg + 9);
+               die("unrecognized reflog param '%s'", arg);
        else
                *base = NULL;
        if (*cnt <= 0)
index 572b114119db15f5f42dd79e3bc15e6d219f71db..dc76c5090f2367426201b37c5b13c2a5cbf00de2 100644 (file)
@@ -140,7 +140,7 @@ static int exclude_existing(const char *match)
                                continue;
                }
                if (check_ref_format(ref)) {
-                       fprintf(stderr, "warning: ref '%s' ignored\n", ref);
+                       warning("ref '%s' ignored", ref);
                        continue;
                }
                if (!string_list_has_string(&existing_refs, ref)) {
index 01e73747d02f384c5e31b846340a4b586c84aab3..e54443009420d98dc281285e997b32f2a263b996 100644 (file)
@@ -338,7 +338,7 @@ static void create_tag(const unsigned char *object, const char *tag,
                exit(128);
        }
        if (path) {
-               unlink(path);
+               unlink_or_warn(path);
                free(path);
        }
 }
index 0713bca778e7be18b58ec5d207dc7c8cd7e982ed..f88e7219367c309dbc3d3e7ce2db32666703a91d 100644 (file)
@@ -24,7 +24,7 @@ int cmd_tar_tree(int argc, const char **argv, const char *prefix)
         *      git archive --format-tar --prefix=basedir tree-ish
         */
        int i;
-       const char **nargv = xcalloc(sizeof(*nargv), argc + 2);
+       const char **nargv = xcalloc(sizeof(*nargv), argc + 3);
        char *basedir_arg;
        int nargc = 0;
 
@@ -36,6 +36,13 @@ int cmd_tar_tree(int argc, const char **argv, const char *prefix)
                argv++;
                argc--;
        }
+
+       /*
+        * Because it's just a compatibility wrapper, tar-tree supports only
+        * the old behaviour of reading attributes from the work tree.
+        */
+       nargv[nargc++] = "--worktree-attributes";
+
        switch (argc) {
        default:
                usage(tar_tree_usage);
index 9a773239cabab9998bcea829c0fb2abea9bdb8e8..8e831be476b71eed151cee1b5f2fc81c4edcb5ff 100644 (file)
@@ -422,8 +422,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
 static void unpack_one(unsigned nr)
 {
        unsigned shift;
-       unsigned char *pack, c;
-       unsigned long size;
+       unsigned char *pack;
+       unsigned long size, c;
        enum object_type type;
 
        obj_list[nr].offset = consumed_bytes;
index dd43d5bef425af318a884bfc235a4aff40931633..92beaaf4b3ca5520a9af27bde7f15dda46d80197 100644 (file)
@@ -195,7 +195,7 @@ static int process_path(const char *path)
        struct stat st;
 
        len = strlen(path);
-       if (has_symlink_leading_path(len, path))
+       if (has_symlink_leading_path(path, len))
                return error("'%s' is beyond a symbolic link", path);
 
        /*
@@ -292,7 +292,7 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
        report("add '%s'", path);
  free_return:
        if (p < path || p > path + strlen(path))
-               free((char*)p);
+               free((char *)p);
 }
 
 static void read_index_info(int line_termination)
@@ -509,7 +509,7 @@ static int do_unresolve(int ac, const char **av,
                const char *p = prefix_path(prefix, prefix_length, arg);
                err |= unresolve_one(p);
                if (p < arg || p > arg + strlen(arg))
-                       free((char*)p);
+                       free((char *)p);
        }
        return err;
 }
@@ -712,7 +712,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                if (set_executable_bit)
                        chmod_path(set_executable_bit, p);
                if (p < path || p > path + strlen(path))
-                       free((char*)p);
+                       free((char *)p);
        }
        if (read_from_stdin) {
                struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
index a9b02fa32f372a6810867c10560a20d58b5b2a91..c4cd1e1327f18ae27b74fac6dfd41938cd9dfe5d 100644 (file)
@@ -35,7 +35,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix)
        strcpy(buf, argv[1]); /* enter-repo smudges its argument */
 
        if (!enter_repo(buf, 0))
-               die("not a git archive");
+               die("'%s' does not appear to be a git repository", buf);
 
        /* put received options in sent_argv[] */
        sent_argc = 1;
@@ -80,16 +80,17 @@ static void error_clnt(const char *fmt, ...)
        die("sent error to the client: %s", buf);
 }
 
-static void process_input(int child_fd, int band)
+static ssize_t process_input(int child_fd, int band)
 {
        char buf[16384];
        ssize_t sz = read(child_fd, buf, sizeof(buf));
        if (sz < 0) {
                if (errno != EAGAIN && errno != EINTR)
                        error_clnt("read error: %s\n", strerror(errno));
-               return;
+               return sz;
        }
        send_sideband(1, band, buf, sz, LARGE_PACKET_MAX);
+       return sz;
 }
 
 int cmd_upload_archive(int argc, const char **argv, const char *prefix)
@@ -131,6 +132,7 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
 
        while (1) {
                struct pollfd pfd[2];
+               ssize_t processed[2] = { 0, 0 };
                int status;
 
                pfd[0].fd = fd1[0];
@@ -147,12 +149,12 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
                }
                if (pfd[0].revents & POLLIN)
                        /* Data stream ready */
-                       process_input(pfd[0].fd, 1);
+                       processed[0] = process_input(pfd[0].fd, 1);
                if (pfd[1].revents & POLLIN)
                        /* Status stream ready */
-                       process_input(pfd[1].fd, 2);
+                       processed[1] = process_input(pfd[1].fd, 2);
                /* Always finish to read data when available */
-               if ((pfd[0].revents | pfd[1].revents) & POLLIN)
+               if (processed[0] || processed[1])
                        continue;
 
                if (waitpid(writer, &status, 0) < 0)
index 729a1593e61d87ad4596f07e7faedac81de64e81..7f7fda42f9b7ab272ff2b3fa4ad1984a79f11ce0 100644 (file)
@@ -55,7 +55,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
        close(gpg.in);
        ret = finish_command(&gpg);
 
-       unlink(path);
+       unlink_or_warn(path);
 
        return ret;
 }
index 1495cf6a20128ccffb981c3ac4d1da5469da1940..425ff8e89b361c34b3336cda58794682c66b57f3 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -25,6 +25,7 @@ extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
 extern int cmd_apply(int argc, const char **argv, const char *prefix);
 extern int cmd_archive(int argc, const char **argv, const char *prefix);
+extern int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
 extern int cmd_blame(int argc, const char **argv, const char *prefix);
 extern int cmd_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_bundle(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index dfeb1e9c860ed1407e5a2d7a5f5a7d822f460045..b8503ad91c3b13ccaf87a6f596d13918f7ae114b 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -140,8 +140,8 @@ struct ondisk_cache_entry_extended {
 };
 
 struct cache_entry {
-       unsigned int ce_ctime;
-       unsigned int ce_mtime;
+       struct cache_time ce_ctime;
+       struct cache_time ce_mtime;
        unsigned int ce_dev;
        unsigned int ce_ino;
        unsigned int ce_mode;
@@ -282,7 +282,7 @@ struct index_state {
        struct cache_entry **cache;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct cache_tree *cache_tree;
-       time_t timestamp;
+       struct cache_time timestamp;
        void *alloc;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
@@ -428,7 +428,7 @@ extern int read_index_preload(struct index_state *, const char **pathspec);
 extern int read_index_from(struct index_state *, const char *path);
 extern int is_index_unborn(struct index_state *);
 extern int read_index_unmerged(struct index_state *);
-extern int write_index(const struct index_state *, int newfd);
+extern int write_index(struct index_state *, int newfd);
 extern int discard_index(struct index_state *);
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
@@ -443,6 +443,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt
 extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 extern int remove_index_entry_at(struct index_state *, int pos);
+extern void remove_marked_cache_entries(struct index_state *istate);
 extern int remove_file_from_index(struct index_state *, const char *path);
 #define ADD_CACHE_VERBOSE 1
 #define ADD_CACHE_PRETEND 2
@@ -541,8 +542,24 @@ enum rebase_setup_type {
        AUTOREBASE_ALWAYS,
 };
 
+enum push_default_type {
+       PUSH_DEFAULT_UNSPECIFIED = -1,
+       PUSH_DEFAULT_NOTHING = 0,
+       PUSH_DEFAULT_MATCHING,
+       PUSH_DEFAULT_TRACKING,
+       PUSH_DEFAULT_CURRENT,
+};
+
 extern enum branch_track git_branch_track;
 extern enum rebase_setup_type autorebase;
+extern enum push_default_type push_default;
+
+enum object_creation_mode {
+       OBJECT_CREATION_USES_HARDLINKS = 0,
+       OBJECT_CREATION_USES_RENAMES = 1,
+};
+
+extern enum object_creation_mode object_creation_mode;
 
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
@@ -670,7 +687,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
 extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
-extern int interpret_nth_last_branch(const char *str, struct strbuf *);
+extern int interpret_branch_name(const char *str, struct strbuf *);
 
 extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
 extern const char *ref_rev_parse_rules[];
@@ -725,11 +742,13 @@ struct checkout {
 };
 
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
-extern int has_symlink_leading_path(int len, const char *name);
-extern int has_symlink_or_noent_leading_path(int len, const char *name);
-extern int has_dirs_only_path(int len, const char *name, int prefix_len);
-extern void invalidate_lstat_cache(int len, const char *name);
+extern int has_symlink_leading_path(const char *name, int len);
+extern int has_symlink_or_noent_leading_path(const char *name, int len);
+extern int has_dirs_only_path(const char *name, int len, int prefix_len);
+extern void invalidate_lstat_cache(const char *name, int len);
 extern void clear_lstat_cache(void);
+extern void schedule_dir_for_removal(const char *name, int len);
+extern void remove_scheduled_dirs(void);
 
 extern struct alternate_object_database {
        struct alternate_object_database *next;
@@ -802,7 +821,7 @@ struct ref {
 #define REF_HEADS      (1u << 1)
 #define REF_TAGS       (1u << 2)
 
-extern struct ref *find_ref_by_name(struct ref *list, const char *name);
+extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
 
 #define CONNECT_VERBOSE       (1u << 0)
 extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
@@ -827,7 +846,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 
 extern void pack_report(void);
 extern int open_pack_index(struct packed_git *);
-extern unsigned charuse_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
+extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
 extern void close_pack_windows(struct packed_git *);
 extern void unuse_pack(struct pack_window **);
 extern void free_pack_by_name(const char *);
diff --git a/color.c b/color.c
index db4dccfb77d80c9bd8981719fe2d0dc17c6772b6..62977f4808ae339fdfe797e16b4eb28dc6abb85d 100644 (file)
--- a/color.c
+++ b/color.c
@@ -1,8 +1,6 @@
 #include "cache.h"
 #include "color.h"
 
-#define COLOR_RESET "\033[m"
-
 int git_use_color_default = 0;
 
 static int parse_color(const char *name, int len)
@@ -54,7 +52,7 @@ void color_parse_mem(const char *value, int value_len, const char *var,
        int bg = -2;
 
        if (!strncasecmp(value, "reset", len)) {
-               strcpy(dst, "\033[m");
+               strcpy(dst, GIT_COLOR_RESET);
                return;
        }
 
@@ -175,7 +173,7 @@ static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
                r += fprintf(fp, "%s", color);
        r += vfprintf(fp, fmt, args);
        if (*color)
-               r += fprintf(fp, "%s", COLOR_RESET);
+               r += fprintf(fp, "%s", GIT_COLOR_RESET);
        if (trail)
                r += fprintf(fp, "%s", trail);
        return r;
@@ -217,7 +215,7 @@ int color_fwrite_lines(FILE *fp, const char *color,
                char *p = memchr(buf, '\n', count);
                if (p != buf && (fputs(color, fp) < 0 ||
                                fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
-                               fputs(COLOR_RESET, fp) < 0))
+                               fputs(GIT_COLOR_RESET, fp) < 0))
                        return -1;
                if (!p)
                        return 0;
diff --git a/color.h b/color.h
index 5019df82f79f1888b3aa57b9752a6a55b13f475a..18abeb7c7dd47794d4f5fea4049b5719f03383fe 100644 (file)
--- a/color.h
+++ b/color.h
@@ -4,6 +4,17 @@
 /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
 #define COLOR_MAXLEN 24
 
+#define GIT_COLOR_NORMAL       ""
+#define GIT_COLOR_RESET                "\033[m"
+#define GIT_COLOR_BOLD         "\033[1m"
+#define GIT_COLOR_RED          "\033[31m"
+#define GIT_COLOR_GREEN                "\033[32m"
+#define GIT_COLOR_YELLOW       "\033[33m"
+#define GIT_COLOR_BLUE         "\033[34m"
+#define GIT_COLOR_MAGENTA      "\033[35m"
+#define GIT_COLOR_CYAN         "\033[36m"
+#define GIT_COLOR_BG_RED       "\033[41m"
+
 /*
  * This variable stores the value of color.ui
  */
index 0b071b6e256fa655b51f9e484f0633a7f055dd25..1a6f9d1df5b7dcafbdaa2d64c45554e129972be2 100644 (file)
@@ -24,7 +24,7 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                        path = q->queue[i]->two->path;
                        len = strlen(path);
                        p = xmalloc(combine_diff_path_size(num_parent, len));
-                       p->path = (char*) &(p->parent[num_parent]);
+                       p->path = (char *) &(p->parent[num_parent]);
                        memcpy(p->path, path, len);
                        p->path[len] = 0;
                        p->len = len;
@@ -80,6 +80,7 @@ struct lline {
 /* Lines surviving in the merge result */
 struct sline {
        struct lline *lost_head, **lost_tail;
+       struct lline *next_lost;
        char *bol;
        int len;
        /* bit 0 up to (N-1) are on if the parent has this line (i.e.
@@ -121,18 +122,12 @@ static void append_lost(struct sline *sline, int n, const char *line, int len)
 
        /* Check to see if we can squash things */
        if (sline->lost_head) {
-               struct lline *last_one = NULL;
-               /* We cannot squash it with earlier one */
-               for (lline = sline->lost_head;
-                    lline;
-                    lline = lline->next)
-                       if (lline->parent_map & this_mask)
-                               last_one = lline;
-               lline = last_one ? last_one->next : sline->lost_head;
+               lline = sline->next_lost;
                while (lline) {
                        if (lline->len == len &&
                            !memcmp(lline->line, line, len)) {
                                lline->parent_map |= this_mask;
+                               sline->next_lost = lline->next;
                                return;
                        }
                        lline = lline->next;
@@ -147,6 +142,7 @@ static void append_lost(struct sline *sline, int n, const char *line, int len)
        lline->line[len] = 0;
        *sline->lost_tail = lline;
        sline->lost_tail = &lline->next;
+       sline->next_lost = NULL;
 }
 
 struct combine_diff_state {
@@ -168,25 +164,28 @@ static void consume_line(void *state_, char *line, unsigned long len)
                                      &state->nb, &state->nn))
                        return;
                state->lno = state->nb;
-               if (!state->nb)
-                       /* @@ -1,2 +0,0 @@ to remove the
-                        * first two lines...
-                        */
-                       state->nb = 1;
-               if (state->nn == 0)
+               if (state->nn == 0) {
                        /* @@ -X,Y +N,0 @@ removed Y lines
                         * that would have come *after* line N
                         * in the result.  Our lost buckets hang
                         * to the line after the removed lines,
+                        *
+                        * Note that this is correct even when N == 0,
+                        * in which case the hunk removes the first
+                        * line in the file.
                         */
                        state->lost_bucket = &state->sline[state->nb];
-               else
+                       if (!state->nb)
+                               state->nb = 1;
+               } else {
                        state->lost_bucket = &state->sline[state->nb-1];
+               }
                if (!state->sline[state->nb-1].p_lno)
                        state->sline[state->nb-1].p_lno =
                                xcalloc(state->num_parent,
                                        sizeof(unsigned long));
                state->sline[state->nb-1].p_lno[state->n] = state->ob;
+               state->lost_bucket->next_lost = state->lost_bucket->lost_head;
                return;
        }
        if (!state->lost_bucket)
@@ -534,7 +533,6 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
                return; /* result deleted */
 
        while (1) {
-               struct sline *sl = &sline[lno];
                unsigned long hunk_end;
                unsigned long rlines;
                const char *hunk_comment = NULL;
@@ -600,7 +598,7 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
                        struct lline *ll;
                        int j;
                        unsigned long p_mask;
-                       sl = &sline[lno++];
+                       struct sline *sl = &sline[lno++];
                        ll = (sl->flag & no_pre_delete) ? NULL : sl->lost_head;
                        while (ll) {
                                fputs(c_old, stdout);
@@ -1064,7 +1062,7 @@ void diff_tree_combined_merge(const unsigned char *sha1,
        for (parents = commit->parents, num_parent = 0;
             parents;
             parents = parents->next, num_parent++)
-               hashcpy((unsigned char*)(parent + num_parent),
+               hashcpy((unsigned char *)(parent + num_parent),
                        parents->item->object.sha1);
        diff_tree_combined(sha1, parent, num_parent, dense, rev);
 }
index 3583a33ee90647d8e6ded02643eb75753760d94f..fb03a2ebb5d51f46d00fad3b3f6b1794d4fdad2b 100644 (file)
@@ -33,6 +33,7 @@ git-diff                                mainporcelain common
 git-diff-files                          plumbinginterrogators
 git-diff-index                          plumbinginterrogators
 git-diff-tree                           plumbinginterrogators
+git-difftool                            ancillaryinterrogators
 git-fast-export                                ancillarymanipulators
 git-fast-import                                ancillarymanipulators
 git-fetch                               mainporcelain common
index ebac1483929c798905e6558e0013e3de3d6abeb2..b4a51b958c5651d3b509013aaef0c4e30b68a816 100644 (file)
@@ -89,10 +89,10 @@ static int cygwin_stat(const char *path, struct stat *buf)
 /*
  * At start up, we are trying to determine whether Win32 API or cygwin stat
  * functions should be used. The choice is determined by core.ignorecygwinfstricks.
- * Reading this option is not always possible immediately as git_dir may be
+ * Reading this option is not always possible immediately as git_dir may
  * not be set yet. So until it is set, use cygwin lstat/stat functions.
  * However, if core.filemode is set, we must use the Cygwin posix
- * stat/lstat as the Windows stat fuctions do not determine posix filemode.
+ * stat/lstat as the Windows stat functions do not determine posix filemode.
  *
  * Note that git_cygwin_config() does NOT call git_default_config() and this
  * is deliberate.  Many commands read from config to establish initial
index 1f4ead5f981688ee9e29ae2ee281e3904c9131f6..14feac7fe179069908df6dbc084b2adcc78c9242 100644 (file)
@@ -39,7 +39,7 @@
 # include <stdlib.h>
 #endif
 
-/* For platform which support the ISO C amendement 1 functionality we
+/* For platforms which support the ISO C amendment 1 functionality we
    support user defined character classes.  */
 #if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
 /* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
@@ -90,7 +90,7 @@
 
 # if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
 /* The GNU C library provides support for user-defined character classes
-   and the functions from ISO C amendement 1.  */
+   and the functions from ISO C amendment 1.  */
 #  ifdef CHARCLASS_NAME_MAX
 #   define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
 #  else
index 3dbe6a77ffa4675b19e7183fd49e11212cb2cda0..cdeda1d9859fd545950e0c39ee7de3c9dc09bb07 100644 (file)
@@ -4,6 +4,119 @@
 
 unsigned int _CRT_fmode = _O_BINARY;
 
+static int err_win_to_posix(DWORD winerr)
+{
+       int error = ENOSYS;
+       switch(winerr) {
+       case ERROR_ACCESS_DENIED: error = EACCES; break;
+       case ERROR_ACCOUNT_DISABLED: error = EACCES; break;
+       case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break;
+       case ERROR_ALREADY_ASSIGNED: error = EBUSY; break;
+       case ERROR_ALREADY_EXISTS: error = EEXIST; break;
+       case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break;
+       case ERROR_BAD_COMMAND: error = EIO; break;
+       case ERROR_BAD_DEVICE: error = ENODEV; break;
+       case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break;
+       case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break;
+       case ERROR_BAD_FORMAT: error = ENOEXEC; break;
+       case ERROR_BAD_LENGTH: error = EINVAL; break;
+       case ERROR_BAD_PATHNAME: error = ENOENT; break;
+       case ERROR_BAD_PIPE: error = EPIPE; break;
+       case ERROR_BAD_UNIT: error = ENODEV; break;
+       case ERROR_BAD_USERNAME: error = EINVAL; break;
+       case ERROR_BROKEN_PIPE: error = EPIPE; break;
+       case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break;
+       case ERROR_BUSY: error = EBUSY; break;
+       case ERROR_BUSY_DRIVE: error = EBUSY; break;
+       case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break;
+       case ERROR_CANNOT_MAKE: error = EACCES; break;
+       case ERROR_CANTOPEN: error = EIO; break;
+       case ERROR_CANTREAD: error = EIO; break;
+       case ERROR_CANTWRITE: error = EIO; break;
+       case ERROR_CRC: error = EIO; break;
+       case ERROR_CURRENT_DIRECTORY: error = EACCES; break;
+       case ERROR_DEVICE_IN_USE: error = EBUSY; break;
+       case ERROR_DEV_NOT_EXIST: error = ENODEV; break;
+       case ERROR_DIRECTORY: error = EINVAL; break;
+       case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break;
+       case ERROR_DISK_CHANGE: error = EIO; break;
+       case ERROR_DISK_FULL: error = ENOSPC; break;
+       case ERROR_DRIVE_LOCKED: error = EBUSY; break;
+       case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break;
+       case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break;
+       case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break;
+       case ERROR_FILE_EXISTS: error = EEXIST; break;
+       case ERROR_FILE_INVALID: error = ENODEV; break;
+       case ERROR_FILE_NOT_FOUND: error = ENOENT; break;
+       case ERROR_GEN_FAILURE: error = EIO; break;
+       case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break;
+       case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break;
+       case ERROR_INVALID_ACCESS: error = EACCES; break;
+       case ERROR_INVALID_ADDRESS: error = EFAULT; break;
+       case ERROR_INVALID_BLOCK: error = EFAULT; break;
+       case ERROR_INVALID_DATA: error = EINVAL; break;
+       case ERROR_INVALID_DRIVE: error = ENODEV; break;
+       case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break;
+       case ERROR_INVALID_FLAGS: error = EINVAL; break;
+       case ERROR_INVALID_FUNCTION: error = ENOSYS; break;
+       case ERROR_INVALID_HANDLE: error = EBADF; break;
+       case ERROR_INVALID_LOGON_HOURS: error = EACCES; break;
+       case ERROR_INVALID_NAME: error = EINVAL; break;
+       case ERROR_INVALID_OWNER: error = EINVAL; break;
+       case ERROR_INVALID_PARAMETER: error = EINVAL; break;
+       case ERROR_INVALID_PASSWORD: error = EPERM; break;
+       case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
+       case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
+       case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
+       case ERROR_INVALID_WORKSTATION: error = EACCES; break;
+       case ERROR_IO_DEVICE: error = EIO; break;
+       case ERROR_IO_INCOMPLETE: error = EINTR; break;
+       case ERROR_LOCKED: error = EBUSY; break;
+       case ERROR_LOCK_VIOLATION: error = EACCES; break;
+       case ERROR_LOGON_FAILURE: error = EACCES; break;
+       case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break;
+       case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break;
+       case ERROR_MORE_DATA: error = EPIPE; break;
+       case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
+       case ERROR_NOACCESS: error = EFAULT; break;
+       case ERROR_NONE_MAPPED: error = EINVAL; break;
+       case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
+       case ERROR_NOT_READY: error = EAGAIN; break;
+       case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
+       case ERROR_NO_DATA: error = EPIPE; break;
+       case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break;
+       case ERROR_NO_PROC_SLOTS: error = EAGAIN; break;
+       case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break;
+       case ERROR_OPEN_FAILED: error = EIO; break;
+       case ERROR_OPEN_FILES: error = EBUSY; break;
+       case ERROR_OPERATION_ABORTED: error = EINTR; break;
+       case ERROR_OUTOFMEMORY: error = ENOMEM; break;
+       case ERROR_PASSWORD_EXPIRED: error = EACCES; break;
+       case ERROR_PATH_BUSY: error = EBUSY; break;
+       case ERROR_PATH_NOT_FOUND: error = ENOENT; break;
+       case ERROR_PIPE_BUSY: error = EBUSY; break;
+       case ERROR_PIPE_CONNECTED: error = EPIPE; break;
+       case ERROR_PIPE_LISTENING: error = EPIPE; break;
+       case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
+       case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
+       case ERROR_READ_FAULT: error = EIO; break;
+       case ERROR_SEEK: error = EIO; break;
+       case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
+       case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
+       case ERROR_SHARING_VIOLATION: error = EACCES; break;
+       case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
+       case ERROR_SWAPERROR: error = ENOENT; break;
+       case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
+       case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
+       case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break;
+       case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break;
+       case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
+       case ERROR_WRITE_FAULT: error = EIO; break;
+       case ERROR_WRITE_PROTECT: error = EROFS; break;
+       }
+       return error;
+}
+
 #undef open
 int mingw_open (const char *filename, int oflags, ...)
 {
@@ -46,7 +159,8 @@ static int do_lstat(const char *file_name, struct stat *buf)
                buf->st_uid = 0;
                buf->st_nlink = 1;
                buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
-               buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
+               buf->st_size = fdata.nFileSizeLow |
+                       (((off_t)fdata.nFileSizeHigh)<<32);
                buf->st_dev = buf->st_rdev = 0; /* not used by Git */
                buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
                buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
@@ -101,7 +215,7 @@ int mingw_fstat(int fd, struct stat *buf)
        }
        /* direct non-file handles to MS's fstat() */
        if (GetFileType(fh) != FILE_TYPE_DISK)
-               return fstat(fd, buf);
+               return _fstati64(fd, buf);
 
        if (GetFileInformationByHandle(fh, &fdata)) {
                buf->st_ino = 0;
@@ -109,7 +223,8 @@ int mingw_fstat(int fd, struct stat *buf)
                buf->st_uid = 0;
                buf->st_nlink = 1;
                buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
-               buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
+               buf->st_size = fdata.nFileSizeLow |
+                       (((off_t)fdata.nFileSizeHigh)<<32);
                buf->st_dev = buf->st_rdev = 0; /* not used by Git */
                buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
                buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
@@ -281,7 +396,7 @@ repeat:
                 * 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
-                * relinguishing here.
+                * relinquishing here.
                 */
                Sleep(0);
                goto repeat;
@@ -342,7 +457,7 @@ static const char *quote_arg(const char *arg)
        const char *p = arg;
        if (!*p) force_quotes = 1;
        while (*p) {
-               if (isspace(*p) || *p == '*' || *p == '?' || *p == '{')
+               if (isspace(*p) || *p == '*' || *p == '?' || *p == '{' || *p == '\'')
                        force_quotes = 1;
                else if (*p == '"')
                        n++;
@@ -447,7 +562,7 @@ static char **get_path_split(void)
        if (!n)
                return NULL;
 
-       path = xmalloc((n+1)*sizeof(char*));
+       path = xmalloc((n+1)*sizeof(char *));
        p = envpath;
        i = 0;
        do {
@@ -819,7 +934,9 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz)
 #undef rename
 int mingw_rename(const char *pold, const char *pnew)
 {
-       DWORD attrs;
+       DWORD attrs, gle;
+       int tries = 0;
+       static const int delay[] = { 0, 1, 10, 20, 40 };
 
        /*
         * Try native rename() first to get errno right.
@@ -829,10 +946,12 @@ int mingw_rename(const char *pold, const char *pnew)
                return 0;
        if (errno != EEXIST)
                return -1;
+repeat:
        if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
                return 0;
        /* TODO: translate more errors */
-       if (GetLastError() == ERROR_ACCESS_DENIED &&
+       gle = GetLastError();
+       if (gle == ERROR_ACCESS_DENIED &&
            (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
                if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
                        errno = EISDIR;
@@ -842,10 +961,23 @@ int mingw_rename(const char *pold, const char *pnew)
                    SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
                        if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
                                return 0;
+                       gle = GetLastError();
                        /* revert file attributes on failure */
                        SetFileAttributes(pnew, attrs);
                }
        }
+       if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
+               /*
+                * We assume that some other process had the source or
+                * destination file open at the wrong moment and retry.
+                * In order to give the other process a higher chance to
+                * complete its operation, we give up our time slice now.
+                * If we have to retry again, we do sleep a bit.
+                */
+               Sleep(delay[tries]);
+               tries++;
+               goto repeat;
+       }
        errno = EACCES;
        return -1;
 }
@@ -1003,3 +1135,24 @@ void mingw_open_html(const char *unixpath)
        printf("Launching default browser to display HTML ...\n");
        ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0);
 }
+
+int link(const char *oldpath, const char *newpath)
+{
+       typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
+       static T create_hard_link = NULL;
+       if (!create_hard_link) {
+               create_hard_link = (T) GetProcAddress(
+                       GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
+               if (!create_hard_link)
+                       create_hard_link = (T)-1;
+       }
+       if (create_hard_link == (T)-1) {
+               errno = ENOSYS;
+               return -1;
+       }
+       if (!create_hard_link(newpath, oldpath, NULL)) {
+               errno = err_win_to_posix(GetLastError());
+               return -1;
+       }
+       return 0;
+}
index a25589880130f2232aaf626cddcd739ac80dd378..762eb143a7654480d8831a6258a0767c0f33900b 100644 (file)
@@ -67,8 +67,6 @@ static inline int readlink(const char *path, char *buf, size_t bufsiz)
 { errno = ENOSYS; return -1; }
 static inline int symlink(const char *oldpath, const char *newpath)
 { errno = ENOSYS; return -1; }
-static inline int link(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)
@@ -134,6 +132,7 @@ int getpagesize(void);      /* defined in MinGW's libgcc.a */
 struct passwd *getpwuid(int 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);
 
 /*
  * replacements of existing functions
@@ -160,14 +159,22 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
 int mingw_rename(const char*, const char*);
 #define rename mingw_rename
 
+#ifdef USE_WIN32_MMAP
+int mingw_getpagesize(void);
+#define getpagesize mingw_getpagesize
+#endif
+
 /* Use mingw_lstat() instead of lstat()/stat() and
  * mingw_fstat() instead of fstat() on Windows.
  */
+#define off_t off64_t
+#define stat _stati64
+#define lseek _lseeki64
 int mingw_lstat(const char *file_name, struct stat *buf);
 int mingw_fstat(int fd, struct stat *buf);
 #define fstat mingw_fstat
 #define lstat mingw_lstat
-#define stat(x,y) mingw_lstat(x,y)
+#define _stati64(x,y) mingw_lstat(x,y)
 
 int mingw_utime(const char *file_name, const struct utimbuf *times);
 #define utime mingw_utime
index 87b33e46697b9a5dd9a9e8391ca3607f7e2ff569..5ea007567d15c229685f571628d4afcce6783e8d 100644 (file)
@@ -1043,7 +1043,7 @@ regex_compile (pattern, size, syntax, bufp)
      they can be reliably used as array indices.  */
   register unsigned char c, c1;
 
-  /* A random tempory spot in PATTERN.  */
+  /* A random temporary spot in PATTERN.  */
   const char *p1;
 
   /* Points to the end of the buffer, where we should append.  */
@@ -1796,7 +1796,7 @@ regex_compile (pattern, size, syntax, bufp)
                    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> <succed_n count>
+                     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
diff --git a/compat/win32mmap.c b/compat/win32mmap.c
new file mode 100644 (file)
index 0000000..779d796
--- /dev/null
@@ -0,0 +1,53 @@
+#include "../git-compat-util.h"
+
+/*
+ * Note that this doesn't return the actual pagesize, but
+ * the allocation granularity. If future Windows specific git code
+ * needs the real getpagesize function, we need to find another solution.
+ */
+int mingw_getpagesize(void)
+{
+       SYSTEM_INFO si;
+       GetSystemInfo(&si);
+       return si.dwAllocationGranularity;
+}
+
+void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
+{
+       HANDLE hmap;
+       void *temp;
+       size_t len;
+       struct stat st;
+       uint64_t o = offset;
+       uint32_t l = o & 0xFFFFFFFF;
+       uint32_t h = (o >> 32) & 0xFFFFFFFF;
+
+       if (!fstat(fd, &st))
+               len = xsize_t(st.st_size);
+       else
+               die("mmap: could not determine filesize");
+
+       if ((length + offset) > len)
+               length = len - offset;
+
+       if (!(flags & MAP_PRIVATE))
+               die("Invalid usage of mmap when built with USE_WIN32_MMAP");
+
+       hmap = CreateFileMapping((HANDLE)_get_osfhandle(fd), 0, PAGE_WRITECOPY,
+               0, 0, 0);
+
+       if (!hmap)
+               return MAP_FAILED;
+
+       temp = MapViewOfFileEx(hmap, FILE_MAP_COPY, h, l, length, start);
+
+       if (!CloseHandle(hmap))
+               warning("unable to close file mapping handle\n");
+
+       return temp ? temp : MAP_FAILED;
+}
+
+int git_munmap(void *start, size_t length)
+{
+       return !UnmapViewOfFile(start);
+}
index e2d96dfe6f75213de567174261d9aeba3e663d9d..44dc293ad314379d7835d9d96a5c3fd12ad2b27f 100644 (file)
@@ -18,8 +18,6 @@
 
  This file is git-specific. Therefore, this file does not attempt
  to implement any codes that are not used by git.
-
- TODO: K
 */
 
 static HANDLE console;
@@ -79,6 +77,20 @@ static void set_console_attr(void)
        SetConsoleTextAttribute(console, attributes);
 }
 
+static void erase_in_line(void)
+{
+       CONSOLE_SCREEN_BUFFER_INFO sbi;
+
+       if (!console)
+               return;
+
+       GetConsoleScreenBufferInfo(console, &sbi);
+       FillConsoleOutputCharacterA(console, ' ',
+               sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
+               NULL);
+}
+
+
 static const char *set_attr(const char *str)
 {
        const char *func;
@@ -218,7 +230,7 @@ static const char *set_attr(const char *str)
                set_console_attr();
                break;
        case 'K':
-               /* TODO */
+               erase_in_line();
                break;
        default:
                /* Unsupported code */
index 5d9072c1b937783f0c7759157d03d3ffb5897c14..7e5594b65e4e62514fde378f4f3abb1ee3d1cbdb 100644 (file)
--- a/config.c
+++ b/config.c
@@ -62,7 +62,8 @@ static char *parse_value(void)
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
-                       space = 1;
+                       if (len)
+                               space++;
                        continue;
                }
                if (!quote) {
@@ -71,11 +72,8 @@ static char *parse_value(void)
                                continue;
                        }
                }
-               if (space) {
-                       if (len)
-                               value[len++] = ' ';
-                       space = 0;
-               }
+               for (; space; space--)
+                       value[len++] = ' ';
                if (c == '\\') {
                        c = get_next_char();
                        switch (c) {
@@ -331,9 +329,9 @@ int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
                return 1;
        if (!*value)
                return 0;
-       if (!strcasecmp(value, "true") || !strcasecmp(value, "yes"))
+       if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
                return 1;
-       if (!strcasecmp(value, "false") || !strcasecmp(value, "no"))
+       if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
                return 0;
        *is_bool = 0;
        return git_config_int(name, value);
@@ -495,6 +493,16 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.createobject")) {
+               if (!strcmp(value, "rename"))
+                       object_creation_mode = OBJECT_CREATION_USES_RENAMES;
+               else if (!strcmp(value, "link"))
+                       object_creation_mode = OBJECT_CREATION_USES_HARDLINKS;
+               else
+                       die("Invalid mode for object creation: %s", value);
+               return 0;
+       }
+
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
 }
@@ -565,6 +573,31 @@ static int git_default_branch_config(const char *var, const char *value)
        return 0;
 }
 
+static int git_default_push_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "push.default")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               else if (!strcmp(value, "nothing"))
+                       push_default = PUSH_DEFAULT_NOTHING;
+               else if (!strcmp(value, "matching"))
+                       push_default = PUSH_DEFAULT_MATCHING;
+               else if (!strcmp(value, "tracking"))
+                       push_default = PUSH_DEFAULT_TRACKING;
+               else if (!strcmp(value, "current"))
+                       push_default = PUSH_DEFAULT_CURRENT;
+               else {
+                       error("Malformed value for %s: %s", var, value);
+                       return error("Must be one of nothing, matching, "
+                                    "tracking or current.");
+               }
+               return 0;
+       }
+
+       /* Add other config variables here and to Documentation/config.txt. */
+       return 0;
+}
+
 static int git_default_mailmap_config(const char *var, const char *value)
 {
        if (!strcmp(var, "mailmap.file"))
@@ -588,6 +621,9 @@ int git_default_config(const char *var, const char *value, void *dummy)
        if (!prefixcmp(var, "branch."))
                return git_default_branch_config(var, value);
 
+       if (!prefixcmp(var, "push."))
+               return git_default_push_config(var, value);
+
        if (!prefixcmp(var, "mailmap."))
                return git_default_mailmap_config(var, value);
 
@@ -644,28 +680,37 @@ int git_config_global(void)
 
 int git_config(config_fn_t fn, void *data)
 {
-       int ret = 0;
+       int ret = 0, found = 0;
        char *repo_config = NULL;
        const char *home = NULL;
 
        /* Setting $GIT_CONFIG makes git read _only_ the given config file. */
        if (config_exclusive_filename)
                return git_config_from_file(fn, config_exclusive_filename, data);
-       if (git_config_system() && !access(git_etc_gitconfig(), R_OK))
+       if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
+               found += 1;
+       }
 
        home = getenv("HOME");
        if (git_config_global() && home) {
                char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
-               if (!access(user_config, R_OK))
+               if (!access(user_config, R_OK)) {
                        ret += git_config_from_file(fn, user_config, data);
+                       found += 1;
+               }
                free(user_config);
        }
 
        repo_config = git_pathdup("config");
-       ret += git_config_from_file(fn, repo_config, data);
+       if (!access(repo_config, R_OK)) {
+               ret += git_config_from_file(fn, repo_config, data);
+               found += 1;
+       }
        free(repo_config);
+       if (found == 0)
+               return -1;
        return ret;
 }
 
@@ -677,16 +722,16 @@ int git_config(config_fn_t fn, void *data)
 
 static struct {
        int baselen;
-       charkey;
+       char *key;
        int do_not_match;
-       regex_tvalue_regex;
+       regex_t *value_regex;
        int multi_replace;
        size_t offset[MAX_MATCHES];
        enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
        int seen;
 } store;
 
-static int matches(const char* key, const char* value)
+static int matches(const char *key, const char *value)
 {
        return !strcmp(key, store.key) &&
                (store.value_regex == NULL ||
@@ -694,7 +739,7 @@ static int matches(const char* key, const char* value)
                  !regexec(store.value_regex, value, 0, NULL, 0)));
 }
 
-static int store_aux(const char* key, const char* value, void *cb)
+static int store_aux(const char *key, const char *value, void *cb)
 {
        const char *ep;
        size_t section_len;
@@ -763,7 +808,7 @@ static int write_error(const char *filename)
        return 4;
 }
 
-static int store_write_section(int fd, const charkey)
+static int store_write_section(int fd, const char *key)
 {
        const char *dot;
        int i, success;
@@ -788,7 +833,7 @@ static int store_write_section(int fd, const char* key)
        return success;
 }
 
-static int store_write_pair(int fd, const char* key, const char* value)
+static int store_write_pair(int fd, const char *key, const char *value)
 {
        int i, success;
        int length = strlen(key + store.baselen + 1);
@@ -836,8 +881,8 @@ static int store_write_pair(int fd, const char* key, const char* value)
        return success;
 }
 
-static ssize_t find_beginning_of_line(const charcontents, size_t size,
-       size_t offset_, intfound_bracket)
+static ssize_t find_beginning_of_line(const char *contents, size_t size,
+       size_t offset_, int *found_bracket)
 {
        size_t equal_offset = size, bracket_offset = size;
        ssize_t offset;
@@ -862,7 +907,7 @@ contline:
        return offset;
 }
 
-int git_config_set(const char* key, const char* value)
+int git_config_set(const char *key, const char *value)
 {
        return git_config_set_multivar(key, value, NULL, 0);
 }
@@ -890,15 +935,15 @@ int git_config_set(const char* key, const char* value)
  * - the config file is removed and the lock file rename()d to it.
  *
  */
-int git_config_set_multivar(const char* key, const char* value,
-       const charvalue_regex, int multi_replace)
+int git_config_set_multivar(const char *key, const char *value,
+       const char *value_regex, int multi_replace)
 {
        int i, dot;
        int fd = -1, in_fd;
        int ret;
-       charconfig_filename;
+       char *config_filename;
        struct lock_file *lock = NULL;
-       const charlast_dot = strrchr(key, '.');
+       const char *last_dot = strrchr(key, '.');
 
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
@@ -979,13 +1024,13 @@ int git_config_set_multivar(const char* key, const char* value,
                        goto out_free;
                }
 
-               store.key = (char*)key;
+               store.key = (char *)key;
                if (!store_write_section(fd, key) ||
                    !store_write_pair(fd, key, value))
                        goto write_err_out;
        } else {
                struct stat st;
-               charcontents;
+               char *contents;
                size_t contents_sz, copy_begin, copy_end;
                int i, new_line = 0;
 
index 082a03d3cf6e9416c80b5d486c3191d3ac64cca4..25fbe3bb6be2b29501965409670b066ae616c570 100644 (file)
@@ -42,6 +42,8 @@ else \
        if test "$withval" = "yes"; then \
                AC_MSG_WARN([You should provide path for --with-$1=PATH]); \
        else \
+               m4_toupper($1)_PATH=$withval; \
+               AC_MSG_NOTICE([Setting m4_toupper($1)_PATH to $withval]); \
                GIT_CONF_APPEND_LINE(${PROGRAM}_PATH=$withval); \
        fi; \
 fi; \
@@ -61,6 +63,8 @@ elif test "$withval" = "yes"; then \
        m4_toupper(NO_$1)=; \
 else \
        m4_toupper(NO_$1)=; \
+       m4_toupper($1)DIR=$withval; \
+       AC_MSG_NOTICE([Setting m4_toupper($1)DIR to $withval]); \
        GIT_CONF_APPEND_LINE(${PACKAGE}DIR=$withval); \
 fi \
 ])# GIT_PARSE_WITH
@@ -76,6 +80,32 @@ AC_DEFUN([GIT_CHECK_FUNC],[AC_CHECK_FUNC([$1],[
   AC_SEARCH_LIBS([$1],,
   [$2],[$3])
 ],[$3])])
+
+dnl
+dnl GIT_STASH_FLAGS(BASEPATH_VAR)
+dnl -----------------------------
+dnl Allow for easy stashing of LDFLAGS and CPPFLAGS before running
+dnl tests that may want to take user settings into account.
+AC_DEFUN([GIT_STASH_FLAGS],[
+if test -n "$1"; then
+   old_CPPFLAGS="$CPPFLAGS"
+   old_LDFLAGS="$LDFLAGS"
+   CPPFLAGS="-I$1/include $CPPFLAGS"
+   LDFLAGS="-L$1/$lib $LDFLAGS"
+fi
+])
+
+dnl
+dnl GIT_UNSTASH_FLAGS(BASEPATH_VAR)
+dnl -----------------------------
+dnl Restore the stashed *FLAGS values.
+AC_DEFUN([GIT_UNSTASH_FLAGS],[
+if test -n "$1"; then
+   CPPFLAGS="$old_CPPFLAGS"
+   LDFLAGS="$old_LDFLAGS"
+fi
+])
+
 ## Site configuration related to programs (before tests)
 ## --with-PACKAGE[=ARG] and --without-PACKAGE
 #
@@ -86,9 +116,124 @@ AC_ARG_WITH([lib],
  [if test "$withval" = "no" || test "$withval" = "yes"; then \
        AC_MSG_WARN([You should provide name for --with-lib=ARG]); \
 else \
+       lib=$withval; \
+       AC_MSG_NOTICE([Setting lib to '$lib']); \
        GIT_CONF_APPEND_LINE(lib=$withval); \
 fi; \
 ],[])
+
+if test -z "$lib"; then
+   AC_MSG_NOTICE([Setting lib to 'lib' (the default)])
+   lib=lib
+fi
+
+AC_ARG_ENABLE([pthreads],
+ [AS_HELP_STRING([--enable-pthreads=FLAGS],
+  [FLAGS is the value to pass to the compiler to enable POSIX Threads.]
+  [The default if FLAGS is not specified is to try first -pthread]
+  [and then -lpthread.]
+  [--without-pthreads will disable threading.])],
+[
+if test "x$enableval" = "xyes"; then
+   AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads])
+elif test "x$enableval" != "xno"; then
+   PTHREAD_CFLAGS=$enableval
+   AC_MSG_NOTICE([Setting '$PTHREAD_CFLAGS' as the FLAGS to enable POSIX Threads])
+else
+   AC_MSG_NOTICE([POSIX Threads will be disabled.])
+   NO_PTHREADS=YesPlease
+   USER_NOPTHREAD=1
+fi],
+[
+   AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads.])
+])
+
+## Site configuration (override autodetection)
+## --with-PACKAGE[=ARG] and --without-PACKAGE
+AC_MSG_NOTICE([CHECKS for site configuration])
+#
+# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability
+# tests.  These tests take up a significant amount of the total test time
+# but are not needed unless you plan to talk to SVN repos.
+#
+# Define MOZILLA_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
+# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
+# choice) has very fast version optimized for i586.
+#
+# Define PPC_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for PowerPC.
+#
+# Define ARM_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for ARM.
+#
+# Define NO_OPENSSL environment variable if you do not have OpenSSL.
+# This also implies MOZILLA_SHA1.
+#
+# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+AC_ARG_WITH(openssl,
+AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)])
+AS_HELP_STRING([],              [ARG can be prefix for openssl library and headers]),\
+GIT_PARSE_WITH(openssl))
+#
+# Define NO_CURL if you do not have curl installed.  git-http-pull and
+# git-http-push are not built, and you cannot use http:// and https://
+# transports.
+#
+# Define CURLDIR=/foo/bar if your curl header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+AC_ARG_WITH(curl,
+AS_HELP_STRING([--with-curl],[support http(s):// transports (default is YES)])
+AS_HELP_STRING([],           [ARG can be also prefix for curl library and headers]),
+GIT_PARSE_WITH(curl))
+#
+# Define NO_EXPAT if you do not have expat installed.  git-http-push is
+# not built, and you cannot push using http:// and https:// transports.
+#
+# Define EXPATDIR=/foo/bar if your expat header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+AC_ARG_WITH(expat,
+AS_HELP_STRING([--with-expat],
+[support git-push using http:// and https:// transports via WebDAV (default is YES)])
+AS_HELP_STRING([],            [ARG can be also prefix for expat library and headers]),
+GIT_PARSE_WITH(expat))
+#
+# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink
+# installed in /sw, but don't want GIT to link against any libraries
+# installed there.  If defined you may specify your own (or Fink's)
+# include directories and library directories by defining CFLAGS
+# and LDFLAGS appropriately.
+#
+# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
+# have DarwinPorts installed in /opt/local, but don't want GIT to
+# link against any libraries installed there.  If defined you may
+# specify your own (or DarwinPort's) include directories and
+# library directories by defining CFLAGS and LDFLAGS appropriately.
+#
+# Define NO_MMAP if you want to avoid mmap.
+#
+# Define NO_ICONV if your libc does not properly support iconv.
+AC_ARG_WITH(iconv,
+AS_HELP_STRING([--without-iconv],
+[if your architecture doesn't properly support iconv])
+AS_HELP_STRING([--with-iconv=PATH],
+[PATH is prefix for libiconv library and headers])
+AS_HELP_STRING([],
+[used only if you need linking with libiconv]),
+GIT_PARSE_WITH(iconv))
+
+## --enable-FEATURE[=ARG] and --disable-FEATURE
+#
+# Define USE_NSEC below if you want git to care about sub-second file mtimes
+# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
+# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
+# randomly break unless your underlying filesystem supports those sub-second
+# times (my ext3 doesn't).
+#
+# Define USE_STDEV below if you want git to care about the underlying device
+# change being considered an inode change from the update-index perspective.
+
 #
 # Define SHELL_PATH to provide path to shell.
 GIT_ARG_SET_PATH(shell)
@@ -167,7 +312,7 @@ fi
 AC_CHECK_PROGS(ASCIIDOC, [asciidoc])
 if test -n "$ASCIIDOC"; then
        AC_MSG_CHECKING([for asciidoc version])
-       asciidoc_version=`$ASCIIDOC --version 2>&1`
+       asciidoc_version=`$ASCIIDOC --version 2>/dev/null`
        case "${asciidoc_version}" in
        asciidoc' '8*)
                ASCIIDOC8=YesPlease
@@ -191,33 +336,59 @@ AC_MSG_NOTICE([CHECKS for libraries])
 #
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 # Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
+
+GIT_STASH_FLAGS($OPENSSLDIR)
+
 AC_CHECK_LIB([crypto], [SHA1_Init],
 [NEEDS_SSL_WITH_CRYPTO=],
 [AC_CHECK_LIB([ssl], [SHA1_Init],
  [NEEDS_SSL_WITH_CRYPTO=YesPlease
   NEEDS_SSL_WITH_CRYPTO=],
  [NO_OPENSSL=YesPlease])])
+
+GIT_UNSTASH_FLAGS($OPENSSLDIR)
+
 AC_SUBST(NEEDS_SSL_WITH_CRYPTO)
 AC_SUBST(NO_OPENSSL)
+
 #
 # Define NO_CURL if you do not have libcurl installed.  git-http-pull and
 # git-http-push are not built, and you cannot use http:// and https://
 # transports.
+
+GIT_STASH_FLAGS($CURLDIR)
+
 AC_CHECK_LIB([curl], [curl_global_init],
 [NO_CURL=],
 [NO_CURL=YesPlease])
+
+GIT_UNSTASH_FLAGS($CURLDIR)
+
 AC_SUBST(NO_CURL)
+
 #
 # Define NO_EXPAT if you do not have expat installed.  git-http-push is
 # not built, and you cannot push using http:// and https:// transports.
+
+GIT_STASH_FLAGS($EXPATDIR)
+
 AC_CHECK_LIB([expat], [XML_ParserCreate],
 [NO_EXPAT=],
 [NO_EXPAT=YesPlease])
+
+GIT_UNSTASH_FLAGS($EXPATDIR)
+
 AC_SUBST(NO_EXPAT)
+
 #
 # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin and
 # some Solaris installations).
 # Define NO_ICONV if neither libc nor libiconv support iconv.
+
+if test -z "$NO_ICONV"; then
+
+GIT_STASH_FLAGS($ICONVDIR)
+
 AC_DEFUN([ICONVTEST_SRC], [
 #include <iconv.h>
 
@@ -227,25 +398,52 @@ int main(void)
        return 0;
 }
 ])
-AC_MSG_CHECKING([for iconv in -lc])
-AC_LINK_IFELSE(ICONVTEST_SRC,
+
+if test -n "$ICONVDIR"; then
+   lib_order="-liconv -lc"
+else
+   lib_order="-lc -liconv"
+fi
+
+NO_ICONV=YesPlease
+
+for l in $lib_order; do
+    if test "$l" = "-liconv"; then
+       NEEDS_LIBICONV=YesPlease
+    else
+       NEEDS_LIBICONV=
+    fi
+
+    old_LIBS="$LIBS"
+    LIBS="$LIBS $l"
+    AC_MSG_CHECKING([for iconv in $l])
+    AC_LINK_IFELSE(ICONVTEST_SRC,
        [AC_MSG_RESULT([yes])
-       NEEDS_LIBICONV=],
-       [AC_MSG_RESULT([no])
-       old_LIBS="$LIBS"
-       LIBS="$LIBS -liconv"
-       AC_MSG_CHECKING([for iconv in -liconv])
-       AC_LINK_IFELSE(ICONVTEST_SRC,
-               [AC_MSG_RESULT([yes])
-               NEEDS_LIBICONV=YesPlease],
-               [AC_MSG_RESULT([no])
-               NO_ICONV=YesPlease])
-       LIBS="$old_LIBS"])
+       NO_ICONV=
+       break],
+       [AC_MSG_RESULT([no])])
+    LIBS="$old_LIBS"
+done
+
+#in case of break
+LIBS="$old_LIBS"
+
+GIT_UNSTASH_FLAGS($ICONVDIR)
+
 AC_SUBST(NEEDS_LIBICONV)
 AC_SUBST(NO_ICONV)
-test -n "$NEEDS_LIBICONV" && LIBS="$LIBS -liconv"
+
+if test -n "$NO_ICONV"; then
+    NEEDS_LIBICONV=
+fi
+
+fi
+
 #
 # Define NO_DEFLATE_BOUND if deflateBound is missing from zlib.
+
+GIT_STASH_FLAGS($ZLIB_PATH)
+
 AC_DEFUN([ZLIBTEST_SRC], [
 #include <zlib.h>
 
@@ -263,7 +461,11 @@ AC_LINK_IFELSE(ZLIBTEST_SRC,
        [AC_MSG_RESULT([no])
        NO_DEFLATE_BOUND=yes])
 LIBS="$old_LIBS"
+
+GIT_UNSTASH_FLAGS($ZLIB_PATH)
+
 AC_SUBST(NO_DEFLATE_BOUND)
+
 #
 # Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
 # Patrick Mauritz).
@@ -297,13 +499,18 @@ int main(void)
        return 0;
 }
 ]])
+
+GIT_STASH_FLAGS($ICONVDIR)
+
 AC_MSG_CHECKING([for old iconv()])
 AC_COMPILE_IFELSE(OLDICONVTEST_SRC,
        [AC_MSG_RESULT([no])],
        [AC_MSG_RESULT([yes])
        OLD_ICONV=UnfortunatelyYes])
-AC_SUBST(OLD_ICONV)
 
+GIT_UNSTASH_FLAGS($ICONVDIR)
+
+AC_SUBST(OLD_ICONV)
 
 ## Checks for typedefs, structures, and compiler characteristics.
 AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
@@ -494,114 +701,65 @@ AC_SUBST(NO_MKDTEMP)
 #
 # Define PTHREAD_LIBS to the linker flag used for Pthread support and define
 # THREADED_DELTA_SEARCH if Pthreads are available.
-AC_LANG_CONFTEST([AC_LANG_PROGRAM(
-  [[#include <pthread.h>]],
-  [[pthread_mutex_t test_mutex;]]
-)])
-${CC} -pthread conftest.c -o conftest.o > /dev/null 2>&1
-if test $? -eq 0;then
- PTHREAD_LIBS="-pthread"
- THREADED_DELTA_SEARCH=YesPlease
+AC_DEFUN([PTHREADTEST_SRC], [
+#include <pthread.h>
+
+int main(void)
+{
+       pthread_mutex_t test_mutex;
+       return (0);
+}
+])
+
+dnl AC_LANG_CONFTEST([AC_LANG_PROGRAM(
+dnl   [[#include <pthread.h>]],
+dnl   [[pthread_mutex_t test_mutex;]]
+dnl )])
+
+NO_PTHREADS=UnfortunatelyYes
+THREADED_DELTA_SEARCH=
+PTHREAD_LIBS=
+
+if test -n "$USER_NOPTHREAD"; then
+   AC_MSG_NOTICE([Skipping POSIX Threads at user request.])
+# handle these separately since PTHREAD_CFLAGS could be '-lpthreads
+# -D_REENTRANT' or some such.
+elif test -z "$PTHREAD_CFLAGS"; then
+  for opt in -pthread -lpthread; do
+     old_CFLAGS="$CFLAGS"
+     CFLAGS="$opt $CFLAGS"
+     AC_MSG_CHECKING([Checking for POSIX Threads with '$opt'])
+     AC_LINK_IFELSE(PTHREADTEST_SRC,
+       [AC_MSG_RESULT([yes])
+               NO_PTHREADS=
+               PTHREAD_LIBS="$opt"
+               THREADED_DELTA_SEARCH=YesPlease
+               break
+       ],
+       [AC_MSG_RESULT([no])])
+      CFLAGS="$old_CFLAGS"
+  done
 else
- ${CC} -lpthread conftest.c -o conftest.o > /dev/null 2>&1
- if test $? -eq 0;then
-  PTHREAD_LIBS="-lpthread"
-  THREADED_DELTA_SEARCH=YesPlease
- else
-  NO_PTHREADS=UnfortunatelyYes
- fi
+  old_CFLAGS="$CFLAGS"
+  CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
+  AC_MSG_CHECKING([Checking for POSIX Threads with '$PTHREAD_CFLAGS'])
+  AC_LINK_IFELSE(PTHREADTEST_SRC,
+       [AC_MSG_RESULT([yes])
+               NO_PTHREADS=
+               PTHREAD_LIBS="$PTHREAD_CFLAGS"
+               THREADED_DELTA_SEARCH=YesPlease
+       ],
+       [AC_MSG_RESULT([no])])
+
+  CFLAGS="$old_CFLAGS"
 fi
+
+CFLAGS="$old_CFLAGS"
+
 AC_SUBST(PTHREAD_LIBS)
 AC_SUBST(NO_PTHREADS)
 AC_SUBST(THREADED_DELTA_SEARCH)
 
-## Site configuration (override autodetection)
-## --with-PACKAGE[=ARG] and --without-PACKAGE
-AC_MSG_NOTICE([CHECKS for site configuration])
-#
-# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability
-# tests.  These tests take up a significant amount of the total test time
-# but are not needed unless you plan to talk to SVN repos.
-#
-# Define MOZILLA_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
-# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
-# choice) has very fast version optimized for i586.
-#
-# Define PPC_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for PowerPC.
-#
-# Define ARM_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for ARM.
-#
-# Define NO_OPENSSL environment variable if you do not have OpenSSL.
-# This also implies MOZILLA_SHA1.
-#
-# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in
-# /foo/bar/include and /foo/bar/lib directories.
-AC_ARG_WITH(openssl,
-AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)])
-AS_HELP_STRING([],              [ARG can be prefix for openssl library and headers]),\
-GIT_PARSE_WITH(openssl))
-#
-# Define NO_CURL if you do not have curl installed.  git-http-pull and
-# git-http-push are not built, and you cannot use http:// and https://
-# transports.
-#
-# Define CURLDIR=/foo/bar if your curl header and library files are in
-# /foo/bar/include and /foo/bar/lib directories.
-AC_ARG_WITH(curl,
-AS_HELP_STRING([--with-curl],[support http(s):// transports (default is YES)])
-AS_HELP_STRING([],           [ARG can be also prefix for curl library and headers]),
-GIT_PARSE_WITH(curl))
-#
-# Define NO_EXPAT if you do not have expat installed.  git-http-push is
-# not built, and you cannot push using http:// and https:// transports.
-#
-# Define EXPATDIR=/foo/bar if your expat header and library files are in
-# /foo/bar/include and /foo/bar/lib directories.
-AC_ARG_WITH(expat,
-AS_HELP_STRING([--with-expat],
-[support git-push using http:// and https:// transports via WebDAV (default is YES)])
-AS_HELP_STRING([],            [ARG can be also prefix for expat library and headers]),
-GIT_PARSE_WITH(expat))
-#
-# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink
-# installed in /sw, but don't want GIT to link against any libraries
-# installed there.  If defined you may specify your own (or Fink's)
-# include directories and library directories by defining CFLAGS
-# and LDFLAGS appropriately.
-#
-# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
-# have DarwinPorts installed in /opt/local, but don't want GIT to
-# link against any libraries installed there.  If defined you may
-# specify your own (or DarwinPort's) include directories and
-# library directories by defining CFLAGS and LDFLAGS appropriately.
-#
-# Define NO_MMAP if you want to avoid mmap.
-#
-# Define NO_ICONV if your libc does not properly support iconv.
-AC_ARG_WITH(iconv,
-AS_HELP_STRING([--without-iconv],
-[if your architecture doesn't properly support iconv])
-AS_HELP_STRING([--with-iconv=PATH],
-[PATH is prefix for libiconv library and headers])
-AS_HELP_STRING([],
-[used only if you need linking with libiconv]),
-GIT_PARSE_WITH(iconv))
-
-## --enable-FEATURE[=ARG] and --disable-FEATURE
-#
-# Define USE_NSEC below if you want git to care about sub-second file mtimes
-# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
-# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
-# randomly break unless your underlying filesystem supports those sub-second
-# times (my ext3 doesn't).
-#
-# Define USE_STDEV below if you want git to care about the underlying device
-# change being considered an inode change from the update-index perspective.
-
-
 ## Output files
 AC_CONFIG_FILES(["${config_file}":"${config_in}":"${config_append}"])
 AC_OUTPUT
index 2f23ab3b87e500137fe0af957901c30e61434564..958c831e430340435c31b640bd757d56b7cb8b71 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -177,18 +177,11 @@ static enum protocol get_protocol(const char *name)
 
 static const char *ai_name(const struct addrinfo *ai)
 {
-       static char addr[INET_ADDRSTRLEN];
-       if ( AF_INET == ai->ai_family ) {
-               struct sockaddr_in *in;
-               in = (struct sockaddr_in *)ai->ai_addr;
-               inet_ntop(ai->ai_family, &in->sin_addr, addr, sizeof(addr));
-       } else if ( AF_INET6 == ai->ai_family ) {
-               struct sockaddr_in6 *in;
-               in = (struct sockaddr_in6 *)ai->ai_addr;
-               inet_ntop(ai->ai_family, &in->sin6_addr, addr, sizeof(addr));
-       } else {
+       static char addr[NI_MAXHOST];
+       if (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), NULL, 0,
+                       NI_NUMERICHOST) != 0)
                strcpy(addr, "(unknown)");
-       }
+
        return addr;
 }
 
@@ -373,8 +366,6 @@ static void git_tcp_connect(int fd[2], char *host, int flags)
 
 
 static char *git_proxy_command;
-static const char *rhost_name;
-static int rhost_len;
 
 static int git_proxy_command_options(const char *var, const char *value,
                void *cb)
@@ -383,6 +374,8 @@ static int git_proxy_command_options(const char *var, const char *value,
                const char *for_pos;
                int matchlen = -1;
                int hostlen;
+               const char *rhost_name = cb;
+               int rhost_len = strlen(rhost_name);
 
                if (git_proxy_command)
                        return 0;
@@ -426,11 +419,8 @@ static int git_proxy_command_options(const char *var, const char *value,
 
 static int git_use_proxy(const char *host)
 {
-       rhost_name = host;
-       rhost_len = strlen(host);
        git_proxy_command = getenv("GIT_PROXY_COMMAND");
-       git_config(git_proxy_command_options, NULL);
-       rhost_name = NULL;
+       git_config(git_proxy_command_options, (void*)host);
        return (git_proxy_command && *git_proxy_command);
 }
 
@@ -507,7 +497,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                                  const char *prog, int flags)
 {
        char *url = xstrdup(url_orig);
-       char *host, *path = url;
+       char *host, *path;
        char *end;
        int c;
        struct child_process *conn;
@@ -589,7 +579,10 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                        git_tcp_connect(fd, host, flags);
                /*
                 * Separate original protocol components prog and path
-                * from extended components with a NUL byte.
+                * from extended host header with a NUL byte.
+                *
+                * Note: Do not add any other headers here!  Doing so
+                * will cause older git-daemon servers to crash.
                 */
                packet_write(fd[1],
                             "%s %s%chost=%s%c",
index 8431837f9705d9a940819db4fcd51283f433ba1a..f44152c4331bb6a2de764e6dcc98c1ac39e517e5 100755 (executable)
@@ -62,7 +62,7 @@ esac
 __gitdir ()
 {
        if [ -z "${1-}" ]; then
-               if [ -n "$__git_dir" ]; then
+               if [ -n "${__git_dir-}" ]; then
                        echo "$__git_dir"
                elif [ -d .git ]; then
                        echo .git
@@ -80,68 +80,84 @@ __gitdir ()
 # returns text to add to bash PS1 prompt (includes branch name)
 __git_ps1 ()
 {
-       local g="$(git rev-parse --git-dir 2>/dev/null)"
+       local g="$(__gitdir)"
        if [ -n "$g" ]; then
                local r
                local b
-               if [ -d "$g/rebase-apply" ]
-               then
-                       if test -f "$g/rebase-apply/rebasing"
-                       then
+               if [ -d "$g/rebase-apply" ]; then
+                       if [ -f "$g/rebase-apply/rebasing" ]; then
                                r="|REBASE"
-                       elif test -f "$g/rebase-apply/applying"
-                       then
+               elif [ -f "$g/rebase-apply/applying" ]; then
                                r="|AM"
                        else
                                r="|AM/REBASE"
                        fi
                        b="$(git symbolic-ref HEAD 2>/dev/null)"
-               elif [ -f "$g/rebase-merge/interactive" ]
-               then
+               elif [ -f "$g/rebase-merge/interactive" ]; then
                        r="|REBASE-i"
                        b="$(cat "$g/rebase-merge/head-name")"
-               elif [ -d "$g/rebase-merge" ]
-               then
+               elif [ -d "$g/rebase-merge" ]; then
                        r="|REBASE-m"
                        b="$(cat "$g/rebase-merge/head-name")"
-               elif [ -f "$g/MERGE_HEAD" ]
-               then
-                       r="|MERGING"
-                       b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
-                       if [ -f "$g/BISECT_LOG" ]
-                       then
-                               r="|BISECTING"
+                       if [ -f "$g/MERGE_HEAD" ]; then
+                               r="|MERGING"
                        fi
-                       if ! b="$(git symbolic-ref HEAD 2>/dev/null)"
-                       then
-                               if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
-                               then
-                                       b="$(cut -c1-7 "$g/HEAD")..."
-                               fi
+                       if [ -f "$g/BISECT_LOG" ]; then
+                               r="|BISECTING"
                        fi
+
+                       b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+
+                               b="$(
+                               case "${GIT_PS1_DESCRIBE_STYLE-}" in
+                               (contains)
+                                       git describe --contains HEAD ;;
+                               (branch)
+                                       git describe --contains --all HEAD ;;
+                               (describe)
+                                       git describe HEAD ;;
+                               (* | default)
+                                       git describe --exact-match HEAD ;;
+                               esac 2>/dev/null)" ||
+
+                               b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
+                               b="unknown"
+                               b="($b)"
+                       }
                fi
 
                local w
                local i
+               local c
 
-               if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then
-                       if test "$(git config --bool bash.showDirtyState)" != "false"; then
-                               git diff --no-ext-diff --ignore-submodules \
-                                       --quiet --exit-code || w="*"
-                               if git rev-parse --quiet --verify HEAD >/dev/null; then
-                                       git diff-index --cached --quiet \
-                                               --ignore-submodules HEAD -- || i="+"
-                               else
-                                       i="#"
+               if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
+                       if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
+                               c="BARE:"
+                       else
+                               b="GIT_DIR!"
+                       fi
+               elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
+                       if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
+                               if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
+                                       git diff --no-ext-diff --ignore-submodules \
+                                               --quiet --exit-code || w="*"
+                                       if git rev-parse --quiet --verify HEAD >/dev/null; then
+                                               git diff-index --cached --quiet \
+                                                       --ignore-submodules HEAD -- || i="+"
+                                       else
+                                               i="#"
+                                       fi
                                fi
                        fi
                fi
 
-               if [ -n "${1-}" ]; then
-                       printf "$1" "${b##refs/heads/}$w$i$r"
-               else
-                       printf " (%s)" "${b##refs/heads/}$w$i$r"
+               if [ -n "$b" ]; then
+                       if [ -n "${1-}" ]; then
+                               printf "$1" "$c${b##refs/heads/}$w$i$r"
+                       else
+                               printf " (%s)" "$c${b##refs/heads/}$w$i$r"
+                       fi
                fi
        fi
 }
@@ -299,7 +315,7 @@ __git_remotes ()
 
 __git_merge_strategies ()
 {
-       if [ -n "$__git_merge_strategylist" ]; then
+       if [ -n "${__git_merge_strategylist-}" ]; then
                echo "$__git_merge_strategylist"
                return
        fi
@@ -383,9 +399,88 @@ __git_complete_revlist ()
        esac
 }
 
+__git_complete_remote_or_refspec ()
+{
+       local cmd="${COMP_WORDS[1]}"
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
+       while [ $c -lt $COMP_CWORD ]; do
+               i="${COMP_WORDS[c]}"
+               case "$i" in
+               --all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
+               -*) ;;
+               *) remote="$i"; break ;;
+               esac
+               c=$((++c))
+       done
+       if [ -z "$remote" ]; then
+               __gitcomp "$(__git_remotes)"
+               return
+       fi
+       if [ $no_complete_refspec = 1 ]; then
+               COMPREPLY=()
+               return
+       fi
+       [ "$remote" = "." ] && remote=
+       case "$cur" in
+       *:*)
+               case "$COMP_WORDBREAKS" in
+               *:*) : great ;;
+               *)   pfx="${cur%%:*}:" ;;
+               esac
+               cur="${cur#*:}"
+               lhs=0
+               ;;
+       +*)
+               pfx="+"
+               cur="${cur#+}"
+               ;;
+       esac
+       case "$cmd" in
+       fetch)
+               if [ $lhs = 1 ]; then
+                       __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur"
+               else
+                       __gitcomp "$(__git_refs)" "$pfx" "$cur"
+               fi
+               ;;
+       pull)
+               if [ $lhs = 1 ]; then
+                       __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+               else
+                       __gitcomp "$(__git_refs)" "$pfx" "$cur"
+               fi
+               ;;
+       push)
+               if [ $lhs = 1 ]; then
+                       __gitcomp "$(__git_refs)" "$pfx" "$cur"
+               else
+                       __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+               fi
+               ;;
+       esac
+}
+
+__git_complete_strategy ()
+{
+       case "${COMP_WORDS[COMP_CWORD-1]}" 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=}"
+               return 0
+               ;;
+       esac
+       return 1
+}
+
 __git_all_commands ()
 {
-       if [ -n "$__git_all_commandlist" ]; then
+       if [ -n "${__git_all_commandlist-}" ]; then
                echo "$__git_all_commandlist"
                return
        fi
@@ -403,7 +498,7 @@ __git_all_commandlist="$(__git_all_commands 2>/dev/null)"
 
 __git_porcelain_commands ()
 {
-       if [ -n "$__git_porcelain_commandlist" ]; then
+       if [ -n "${__git_porcelain_commandlist-}" ]; then
                echo "$__git_porcelain_commandlist"
                return
        fi
@@ -563,7 +658,8 @@ _git_am ()
                ;;
        --*)
                __gitcomp "
-                       --signoff --utf8 --binary --3way --interactive
+                       --3way --committer-date-is-author-date --ignore-date
+                       --interactive --keep --no-utf8 --signoff --utf8
                        --whitespace=
                        "
                return
@@ -816,7 +912,7 @@ _git_diff ()
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --*)
-               __gitcomp "--cached --pickaxe-all --pickaxe-regex
+               __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
                        --base --ours --theirs
                        $__git_diff_common_options
                        "
@@ -826,42 +922,62 @@ _git_diff ()
        __git_complete_file
 }
 
-_git_fetch ()
+__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
+                       tkdiff vimdiff gvimdiff xxdiff
+"
+
+_git_difftool ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --tool=*)
+               __gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
+               return
+               ;;
+       --*)
+               __gitcomp "--tool="
+               return
+               ;;
+       esac
+       COMPREPLY=()
+}
 
-       if [ "$COMP_CWORD" = 2 ]; then
-               __gitcomp "$(__git_remotes)"
-       else
-               case "$cur" in
-               *:*)
-                       local pfx=""
-                       case "$COMP_WORDBREAKS" in
-                       *:*) : great ;;
-                       *)   pfx="${cur%%:*}:" ;;
-                       esac
-                       __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
-                       ;;
-               *)
-                       __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")"
-                       ;;
-               esac
-       fi
+__git_fetch_options="
+       --quiet --verbose --append --upload-pack --force --keep --depth=
+       --tags --no-tags
+"
+
+_git_fetch ()
+{
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --*)
+               __gitcomp "$__git_fetch_options"
+               return
+               ;;
+       esac
+       __git_complete_remote_or_refspec
 }
 
 _git_format_patch ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
+       --thread=*)
+               __gitcomp "
+                       deep shallow
+                       " "" "${cur##--thread=}"
+               return
+               ;;
        --*)
                __gitcomp "
-                       --stdout --attach --thread
+                       --stdout --attach --no-attach --thread --thread=
                        --output-directory
                        --numbered --start-number
                        --numbered-files
                        --keep-subject
                        --signoff
-                       --in-reply-to=
+                       --in-reply-to= --cc=
                        --full-index --binary
                        --not --all
                        --cover-letter
@@ -875,6 +991,21 @@ _git_format_patch ()
        __git_complete_revlist
 }
 
+_git_fsck ()
+{
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --*)
+               __gitcomp "
+                       --tags --root --unreachable --cache --no-reflogs --full
+                       --strict --verbose --lost-found
+                       "
+               return
+               ;;
+       esac
+       COMPREPLY=()
+}
+
 _git_gc ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -997,6 +1128,7 @@ __git_log_shortlog_options="
 "
 
 __git_log_pretty_formats="oneline short medium full fuller email raw format:"
+__git_log_date_formats="relative iso8601 rfc2822 short local default raw"
 
 _git_log ()
 {
@@ -1014,10 +1146,13 @@ _git_log ()
                        " "" "${cur##--pretty=}"
                return
                ;;
+       --format=*)
+               __gitcomp "$__git_log_pretty_formats
+                       " "" "${cur##--format=}"
+               return
+               ;;
        --date=*)
-               __gitcomp "
-                       relative iso8601 rfc2822 short local default
-               " "" "${cur##--date=}"
+               __gitcomp "$__git_log_date_formats" "" "${cur##--date=}"
                return
                ;;
        --*)
@@ -1029,7 +1164,7 @@ _git_log ()
                        --follow
                        --abbrev-commit --abbrev=
                        --relative-date --date=
-                       --pretty=
+                       --pretty= --format= --oneline
                        --cherry-pick
                        --graph
                        --decorate
@@ -1045,24 +1180,19 @@ _git_log ()
        __git_complete_revlist
 }
 
+__git_merge_options="
+       --no-commit --no-stat --log --no-log --squash --strategy
+       --commit --stat --no-squash --ff --no-ff
+"
+
 _git_merge ()
 {
+       __git_complete_strategy && return
+
        local cur="${COMP_WORDS[COMP_CWORD]}"
-       case "${COMP_WORDS[COMP_CWORD-1]}" in
-       -s|--strategy)
-               __gitcomp "$(__git_merge_strategies)"
-               return
-       esac
        case "$cur" in
-       --strategy=*)
-               __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
-               return
-               ;;
        --*)
-               __gitcomp "
-                       --no-commit --no-stat --log --no-log --squash --strategy
-                       --commit --stat --no-squash --ff --no-ff
-                       "
+               __gitcomp "$__git_merge_options"
                return
        esac
        __gitcomp "$(__git_refs)"
@@ -1073,10 +1203,7 @@ _git_mergetool ()
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --tool=*)
-               __gitcomp "
-                       kdiff3 tkdiff meld xxdiff emerge
-                       vimdiff gvimdiff ecmerge opendiff
-                       " "" "${cur##--tool=}"
+               __gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
                return
                ;;
        --*)
@@ -1111,40 +1238,44 @@ _git_name_rev ()
 
 _git_pull ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       __git_complete_strategy && return
 
-       if [ "$COMP_CWORD" = 2 ]; then
-               __gitcomp "$(__git_remotes)"
-       else
-               __gitcomp "$(__git_refs "${COMP_WORDS[2]}")"
-       fi
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --*)
+               __gitcomp "
+                       --rebase --no-rebase
+                       $__git_merge_options
+                       $__git_fetch_options
+               "
+               return
+               ;;
+       esac
+       __git_complete_remote_or_refspec
 }
 
 _git_push ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
-
-       if [ "$COMP_CWORD" = 2 ]; then
+       case "${COMP_WORDS[COMP_CWORD-1]}" in
+       --repo)
                __gitcomp "$(__git_remotes)"
-       else
-               case "$cur" in
-               *:*)
-                       local pfx=""
-                       case "$COMP_WORDBREAKS" in
-                       *:*) : great ;;
-                       *)   pfx="${cur%%:*}:" ;;
-                       esac
-
-                       __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}"
-                       ;;
-               +*)
-                       __gitcomp "$(__git_refs)" + "${cur#+}"
-                       ;;
-               *)
-                       __gitcomp "$(__git_refs)"
-                       ;;
-               esac
-       fi
+               return
+       esac
+       case "$cur" in
+       --repo=*)
+               __gitcomp "$(__git_remotes)" "" "${cur##--repo=}"
+               return
+               ;;
+       --*)
+               __gitcomp "
+                       --all --mirror --tags --dry-run --force --verbose
+                       --receive-pack= --repo=
+               "
+               return
+               ;;
+       esac
+       __git_complete_remote_or_refspec
 }
 
 _git_rebase ()
@@ -1154,16 +1285,8 @@ _git_rebase ()
                __gitcomp "--continue --skip --abort"
                return
        fi
-       case "${COMP_WORDS[COMP_CWORD-1]}" in
-       -s|--strategy)
-               __gitcomp "$(__git_merge_strategies)"
-               return
-       esac
+       __git_complete_strategy && return
        case "$cur" in
-       --strategy=*)
-               __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
-               return
-               ;;
        --*)
                __gitcomp "--onto --merge --strategy --interactive"
                return
@@ -1171,18 +1294,39 @@ _git_rebase ()
        __gitcomp "$(__git_refs)"
 }
 
+__git_send_email_confirm_options="always never auto cc compose"
+__git_send_email_suppresscc_options="author self cc ccbody sob cccmd body all"
+
 _git_send_email ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
+       --confirm=*)
+               __gitcomp "
+                       $__git_send_email_confirm_options
+                       " "" "${cur##--confirm=}"
+               return
+               ;;
+       --suppress-cc=*)
+               __gitcomp "
+                       $__git_send_email_suppresscc_options
+                       " "" "${cur##--suppress-cc=}"
+
+               return
+               ;;
+       --smtp-encryption=*)
+               __gitcomp "ssl tls" "" "${cur##--smtp-encryption=}"
+               return
+               ;;
        --*)
-               __gitcomp "--bcc --cc --cc-cmd --chain-reply-to --compose
-                       --dry-run --envelope-sender --from --identity
+               __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to
+                       --compose --confirm= --dry-run --envelope-sender
+                       --from --identity
                        --in-reply-to --no-chain-reply-to --no-signed-off-by-cc
                        --no-suppress-from --no-thread --quiet
                        --signed-off-by-cc --smtp-pass --smtp-server
-                       --smtp-server-port --smtp-ssl --smtp-user --subject
-                       --suppress-cc --suppress-from --thread --to
+                       --smtp-server-port --smtp-encryption= --smtp-user
+                       --subject --suppress-cc= --suppress-from --thread --to
                        --validate --no-validate"
                return
                ;;
@@ -1236,6 +1380,26 @@ _git_config ()
                        "
                return
                ;;
+       help.format)
+               __gitcomp "man info web html"
+               return
+               ;;
+       log.date)
+               __gitcomp "$__git_log_date_formats"
+               return
+               ;;
+       sendemail.aliasesfiletype)
+               __gitcomp "mutt mailrc pine elm gnus"
+               return
+               ;;
+       sendemail.confirm)
+               __gitcomp "$__git_send_email_confirm_options"
+               return
+               ;;
+       sendemail.suppresscc)
+               __gitcomp "$__git_send_email_suppresscc_options"
+               return
+               ;;
        *.*)
                COMPREPLY=()
                return
@@ -1264,6 +1428,39 @@ _git_config ()
                __gitcomp "$(__git_heads)" "$pfx" "$cur" "."
                return
                ;;
+       guitool.*.*)
+               local pfx="${cur%.*}."
+               cur="${cur##*.}"
+               __gitcomp "
+                       argprompt cmd confirm needsfile noconsole norescan
+                       prompt revprompt revunmerged title
+                       " "$pfx" "$cur"
+               return
+               ;;
+       difftool.*.*)
+               local pfx="${cur%.*}."
+               cur="${cur##*.}"
+               __gitcomp "cmd path" "$pfx" "$cur"
+               return
+               ;;
+       man.*.*)
+               local pfx="${cur%.*}."
+               cur="${cur##*.}"
+               __gitcomp "cmd path" "$pfx" "$cur"
+               return
+               ;;
+       mergetool.*.*)
+               local pfx="${cur%.*}."
+               cur="${cur##*.}"
+               __gitcomp "cmd path trustExitCode" "$pfx" "$cur"
+               return
+               ;;
+       pager.*)
+               local pfx="${cur%.*}."
+               cur="${cur#*.}"
+               __gitcomp "$(__git_all_commands)" "$pfx" "$cur"
+               return
+               ;;
        remote.*.*)
                local pfx="${cur%.*}."
                cur="${cur##*.}"
@@ -1279,8 +1476,15 @@ _git_config ()
                __gitcomp "$(__git_remotes)" "$pfx" "$cur" "."
                return
                ;;
+       url.*.*)
+               local pfx="${cur%.*}."
+               cur="${cur##*.}"
+               __gitcomp "insteadof" "$pfx" "$cur"
+               return
+               ;;
        esac
        __gitcomp "
+               alias.
                apply.whitespace
                branch.autosetupmerge
                branch.autosetuprebase
@@ -1298,6 +1502,9 @@ _git_config ()
                color.diff.old
                color.diff.plain
                color.diff.whitespace
+               color.grep
+               color.grep.external
+               color.grep.match
                color.interactive
                color.interactive.header
                color.interactive.help
@@ -1315,6 +1522,7 @@ _git_config ()
                core.autocrlf
                core.bare
                core.compression
+               core.createObject
                core.deltaBaseCacheLimit
                core.editor
                core.excludesfile
@@ -1345,11 +1553,21 @@ _git_config ()
                diff.renameLimit
                diff.renameLimit.
                diff.renames
+               diff.suppressBlankEmpty
+               diff.tool
+               diff.wordRegex
+               difftool.
+               difftool.prompt
                fetch.unpackLimit
+               format.attach
+               format.cc
                format.headers
                format.numbered
                format.pretty
+               format.signoff
+               format.subjectprefix
                format.suffix
+               format.thread
                gc.aggressiveWindow
                gc.auto
                gc.autopacklimit
@@ -1360,6 +1578,7 @@ _git_config ()
                gc.rerereresolved
                gc.rerereunresolved
                gitcvs.allbinary
+               gitcvs.commitmsgannotation
                gitcvs.dbTableNamePrefix
                gitcvs.dbdriver
                gitcvs.dbname
@@ -1368,6 +1587,7 @@ _git_config ()
                gitcvs.enabled
                gitcvs.logfile
                gitcvs.usecrlfattr
+               guitool.
                gui.blamehistoryctx
                gui.commitmsgwidth
                gui.copyblamethreshold
@@ -1394,13 +1614,24 @@ _git_config ()
                http.sslVerify
                i18n.commitEncoding
                i18n.logOutputEncoding
+               imap.folder
+               imap.host
+               imap.pass
+               imap.port
+               imap.preformattedHTML
+               imap.sslverify
+               imap.tunnel
+               imap.user
                instaweb.browser
                instaweb.httpd
                instaweb.local
                instaweb.modulepath
                instaweb.port
+               interactive.singlekey
                log.date
                log.showroot
+               mailmap.file
+               man.
                man.viewer
                merge.conflictstyle
                merge.log
@@ -1408,7 +1639,9 @@ _git_config ()
                merge.stat
                merge.tool
                merge.verbosity
+               mergetool.
                mergetool.keepBackup
+               mergetool.prompt
                pack.compression
                pack.deltaCacheLimit
                pack.deltaCacheSize
@@ -1418,8 +1651,11 @@ _git_config ()
                pack.threads
                pack.window
                pack.windowMemory
+               pager.
                pull.octopus
                pull.twohead
+               push.default
+               rebase.stat
                receive.denyCurrentBranch
                receive.denyDeletes
                receive.denyNonFastForwards
@@ -1428,11 +1664,32 @@ _git_config ()
                repack.usedeltabaseoffset
                rerere.autoupdate
                rerere.enabled
+               sendemail.aliasesfile
+               sendemail.aliasesfiletype
+               sendemail.bcc
+               sendemail.cc
+               sendemail.cccmd
+               sendemail.chainreplyto
+               sendemail.confirm
+               sendemail.envelopesender
+               sendemail.multiedit
+               sendemail.signedoffbycc
+               sendemail.smtpencryption
+               sendemail.smtppass
+               sendemail.smtpserver
+               sendemail.smtpserverport
+               sendemail.smtpuser
+               sendemail.suppresscc
+               sendemail.suppressfrom
+               sendemail.thread
+               sendemail.to
+               sendemail.validate
                showbranch.default
                status.relativePaths
                status.showUntrackedFiles
                tar.umask
                transfer.unpackLimit
+               url.
                user.email
                user.name
                user.signingkey
@@ -1443,7 +1700,7 @@ _git_config ()
 
 _git_remote ()
 {
-       local subcommands="add rename rm show prune update"
+       local subcommands="add rename rm show prune update set-head"
        local subcommand="$(__git_find_subcommand "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
@@ -1541,8 +1798,13 @@ _git_show ()
                        " "" "${cur##--pretty=}"
                return
                ;;
+       --format=*)
+               __gitcomp "$__git_log_pretty_formats
+                       " "" "${cur##--format=}"
+               return
+               ;;
        --*)
-               __gitcomp "--pretty=
+               __gitcomp "--pretty= --format= --abbrev-commit --oneline
                        $__git_diff_common_options
                        "
                return
@@ -1559,7 +1821,7 @@ _git_show_branch ()
                __gitcomp "
                        --all --remotes --topo-order --current --more=
                        --list --independent --merge-base --no-name
-                       --sha1-name --topics --reflog
+                       --sha1-name --sparse --topics --reflog
                        "
                return
                ;;
@@ -1771,6 +2033,7 @@ _git ()
                        --bare
                        --version
                        --exec-path
+                       --html-path
                        --work-tree=
                        --help
                        "
@@ -1800,8 +2063,10 @@ _git ()
        config)      _git_config ;;
        describe)    _git_describe ;;
        diff)        _git_diff ;;
+       difftool)    _git_difftool ;;
        fetch)       _git_fetch ;;
        format-patch) _git_format_patch ;;
+       fsck)        _git_fsck ;;
        gc)          _git_gc ;;
        grep)        _git_grep ;;
        help)        _git_help ;;
@@ -1841,7 +2106,7 @@ _gitk ()
        __git_has_doubledash && return
 
        local cur="${COMP_WORDS[COMP_CWORD]}"
-       local g="$(git rev-parse --git-dir 2>/dev/null)"
+       local g="$(__gitdir)"
        local merge=""
        if [ -f "$g/MERGE_HEAD" ]; then
                merge="--merge"
index 90e7900e6d7aff2fadf9ba04f8d982733493411c..f3b57bf1d21e8256bf084076e50cbc9ff84a1ee8 100644 (file)
@@ -59,7 +59,7 @@ static void convert_ascii_sha1(void *buffer)
        struct entry *entry;
 
        if (get_sha1_hex(buffer, sha1))
-               die("expected sha1, got '%s'", (char*) buffer);
+               die("expected sha1, got '%s'", (char *) buffer);
        entry = convert_entry(sha1);
        memcpy(buffer, sha1_to_hex(entry->new_sha1), 40);
 }
@@ -100,7 +100,7 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base
                if (!slash) {
                        newlen += sprintf(new + newlen, "%o %s", mode, path);
                        new[newlen++] = '\0';
-                       hashcpy((unsigned char*)new + newlen, (unsigned char *) buffer + len - 20);
+                       hashcpy((unsigned char *)new + newlen, (unsigned char *) buffer + len - 20);
                        newlen += 20;
 
                        used += len;
@@ -271,7 +271,7 @@ static void convert_commit(void *buffer, unsigned long size, unsigned char *resu
        unsigned long orig_size = size;
 
        if (memcmp(buffer, "tree ", 5))
-               die("Bad commit '%s'", (char*) buffer);
+               die("Bad commit '%s'", (char *) buffer);
        convert_ascii_sha1((char *) buffer + 5);
        buffer = (char *) buffer + 46;    /* "tree " + "hex sha1" + "\n" */
        while (!memcmp(buffer, "parent ", 7)) {
diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool
deleted file mode 100755 (executable)
index 0cda3d2..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env perl
-# Copyright (c) 2009 David Aguilar
-#
-# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
-# git-difftool-helper script.  This script exports
-# GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and
-# GIT_DIFFTOOL_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper.
-# Any arguments that are unknown to this script are forwarded to 'git diff'.
-
-use strict;
-use warnings;
-use Cwd qw(abs_path);
-use File::Basename qw(dirname);
-
-my $DIR = abs_path(dirname($0));
-
-
-sub usage
-{
-       print << 'USAGE';
-usage: git difftool [--tool=<tool>] [--no-prompt] ["git diff" options]
-USAGE
-       exit 1;
-}
-
-sub setup_environment
-{
-       $ENV{PATH} = "$DIR:$ENV{PATH}";
-       $ENV{GIT_PAGER} = '';
-       $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper';
-}
-
-sub exe
-{
-       my $exe = shift;
-       return defined $ENV{COMSPEC} ? "$exe.exe" : $exe;
-}
-
-sub generate_command
-{
-       my @command = (exe('git'), 'diff');
-       my $skip_next = 0;
-       my $idx = -1;
-       for my $arg (@ARGV) {
-               $idx++;
-               if ($skip_next) {
-                       $skip_next = 0;
-                       next;
-               }
-               if ($arg eq '-t' or $arg eq '--tool') {
-                       usage() if $#ARGV <= $idx;
-                       $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1];
-                       $skip_next = 1;
-                       next;
-               }
-               if ($arg =~ /^--tool=/) {
-                       $ENV{GIT_MERGE_TOOL} = substr($arg, 7);
-                       next;
-               }
-               if ($arg eq '--no-prompt') {
-                       $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
-                       next;
-               }
-               if ($arg eq '-h' or $arg eq '--help') {
-                       usage();
-               }
-               push @command, $arg;
-       }
-       return @command
-}
-
-setup_environment();
-exec(generate_command());
diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper
deleted file mode 100755 (executable)
index db3af6a..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-#!/bin/sh
-# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
-# It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff,
-# emerge, ecmerge, vimdiff, gvimdiff, and custom user-configurable tools.
-# This script is typically launched by using the 'git difftool'
-# convenience command.
-#
-# Copyright (c) 2009 David Aguilar
-
-# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt.
-should_prompt () {
-       ! test -n "$GIT_DIFFTOOL_NO_PROMPT"
-}
-
-# Should we keep the backup .orig file?
-keep_backup_mode="$(git config --bool merge.keepBackup || echo true)"
-keep_backup () {
-       test "$keep_backup_mode" = "true"
-}
-
-# This function manages the backup .orig file.
-# A backup $MERGED.orig file is created if changes are detected.
-cleanup_temp_files () {
-       if test -n "$MERGED"; then
-               if keep_backup && test "$MERGED" -nt "$BACKUP"; then
-                       test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
-               else
-                       rm -f -- "$BACKUP"
-               fi
-       fi
-}
-
-# This is called when users Ctrl-C out of git-difftool-helper
-sigint_handler () {
-       cleanup_temp_files
-       exit 1
-}
-
-# This function prepares temporary files and launches the appropriate
-# merge tool.
-launch_merge_tool () {
-       # Merged is the filename as it appears in the work tree
-       # Local is the contents of a/filename
-       # Remote is the contents of b/filename
-       # Custom merge tool commands might use $BASE so we provide it
-       MERGED="$1"
-       LOCAL="$2"
-       REMOTE="$3"
-       BASE="$1"
-       ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
-       BACKUP="$MERGED.BACKUP.$ext"
-
-       # Create and ensure that we clean up $BACKUP
-       test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
-       trap sigint_handler INT
-
-       # $LOCAL and $REMOTE are temporary files so prompt
-       # the user with the real $MERGED name before launching $merge_tool.
-       if should_prompt; then
-               printf "\nViewing: '$MERGED'\n"
-               printf "Hit return to launch '%s': " "$merge_tool"
-               read ans
-       fi
-
-       # Run the appropriate merge tool command
-       case "$merge_tool" in
-       kdiff3)
-               basename=$(basename "$MERGED")
-               "$merge_tool_path" --auto \
-                       --L1 "$basename (A)" \
-                       --L2 "$basename (B)" \
-                       -o "$MERGED" "$LOCAL" "$REMOTE" \
-                       > /dev/null 2>&1
-               ;;
-
-       kompare)
-               "$merge_tool_path" "$LOCAL" "$REMOTE"
-               ;;
-
-       tkdiff)
-               "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
-               ;;
-
-       meld)
-               "$merge_tool_path" "$LOCAL" "$REMOTE"
-               ;;
-
-       vimdiff)
-               "$merge_tool_path" -c "wincmd l" "$LOCAL" "$REMOTE"
-               ;;
-
-       gvimdiff)
-               "$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$REMOTE"
-               ;;
-
-       xxdiff)
-               "$merge_tool_path" \
-                       -X \
-                       -R 'Accel.SaveAsMerged: "Ctrl-S"' \
-                       -R 'Accel.Search: "Ctrl+F"' \
-                       -R 'Accel.SearchForward: "Ctrl-G"' \
-                       --merged-file "$MERGED" \
-                       "$LOCAL" "$REMOTE"
-               ;;
-
-       opendiff)
-               "$merge_tool_path" "$LOCAL" "$REMOTE" \
-                       -merge "$MERGED" | cat
-               ;;
-
-       ecmerge)
-               "$merge_tool_path" "$LOCAL" "$REMOTE" \
-                       --default --mode=merge2 --to="$MERGED"
-               ;;
-
-       emerge)
-               "$merge_tool_path" -f emerge-files-command \
-                       "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
-               ;;
-
-       *)
-               if test -n "$merge_tool_cmd"; then
-                       ( eval $merge_tool_cmd )
-               fi
-               ;;
-       esac
-
-       cleanup_temp_files
-}
-
-# Verifies that mergetool.<tool>.cmd exists
-valid_custom_tool() {
-       merge_tool_cmd="$(git config mergetool.$1.cmd)"
-       test -n "$merge_tool_cmd"
-}
-
-# Verifies that the chosen merge tool is properly setup.
-# Built-in merge tools are always valid.
-valid_tool() {
-       case "$1" in
-       kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
-               ;; # happy
-       *)
-               if ! valid_custom_tool "$1"
-               then
-                       return 1
-               fi
-               ;;
-       esac
-}
-
-# Sets up the merge_tool_path variable.
-# This handles the mergetool.<tool>.path configuration.
-init_merge_tool_path() {
-       merge_tool_path=$(git config mergetool."$1".path)
-       if test -z "$merge_tool_path"; then
-               case "$1" in
-               emerge)
-                       merge_tool_path=emacs
-                       ;;
-               *)
-                       merge_tool_path="$1"
-                       ;;
-               esac
-       fi
-}
-
-# Allow the GIT_MERGE_TOOL variable to provide a default value
-test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
-
-# If not merge tool was specified then use the merge.tool
-# configuration variable.  If that's invalid then reset merge_tool.
-if test -z "$merge_tool"; then
-       merge_tool=$(git config merge.tool)
-       if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
-               echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
-               echo >&2 "Resetting to default..."
-               unset merge_tool
-       fi
-fi
-
-# Try to guess an appropriate merge tool if no tool has been set.
-if test -z "$merge_tool"; then
-       # We have a $DISPLAY so try some common UNIX merge tools
-       if test -n "$DISPLAY"; then
-               # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare
-               if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
-                       merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff"
-               else
-                       merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff"
-               fi
-       fi
-       if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
-               # $EDITOR is emacs so add emerge as a candidate
-               merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
-       elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
-               # $EDITOR is vim so add vimdiff as a candidate
-               merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
-       else
-               merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
-       fi
-       echo "merge tool candidates: $merge_tool_candidates"
-
-       # Loop over each candidate and stop when a valid merge tool is found.
-       for i in $merge_tool_candidates
-       do
-               init_merge_tool_path $i
-               if type "$merge_tool_path" > /dev/null 2>&1; then
-                       merge_tool=$i
-                       break
-               fi
-       done
-
-       if test -z "$merge_tool" ; then
-               echo "No known merge resolution program available."
-               exit 1
-       fi
-
-else
-       # A merge tool has been set, so verify that it's valid.
-       if ! valid_tool "$merge_tool"; then
-               echo >&2 "Unknown merge tool $merge_tool"
-               exit 1
-       fi
-
-       init_merge_tool_path "$merge_tool"
-
-       if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
-               echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
-               exit 1
-       fi
-fi
-
-
-# Launch the merge tool on each path provided by 'git diff'
-while test $# -gt 6
-do
-       launch_merge_tool "$1" "$2" "$5"
-       shift 7
-done
diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt
deleted file mode 100644 (file)
index 6e2610c..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-git-difftool(1)
-===============
-
-NAME
-----
-git-difftool - compare changes using common merge tools
-
-SYNOPSIS
---------
-'git difftool' [--tool=<tool>] [--no-prompt] ['git diff' options]
-
-DESCRIPTION
------------
-'git-difftool' is a git command that allows you to compare and edit files
-between revisions using common merge tools.  At its most basic level,
-'git-difftool' does what 'git-mergetool' does but its use is for non-merge
-situations such as when preparing commits or comparing changes against
-the index.
-
-'git difftool' is a frontend to 'git diff' and accepts the same
-arguments and options.
-
-See linkgit:git-diff[1] for the full list of supported options.
-
-OPTIONS
--------
--t <tool>::
---tool=<tool>::
-       Use the merge resolution program specified by <tool>.
-       Valid merge tools are:
-       kdiff3, kompare, tkdiff, meld, xxdiff, emerge,
-       vimdiff, gvimdiff, ecmerge, and opendiff
-+
-If a merge resolution program is not specified, 'git-difftool'
-will use the configuration variable `merge.tool`.  If the
-configuration variable `merge.tool` is not set, 'git difftool'
-will pick a suitable default.
-+
-You can explicitly provide a full path to the tool by setting the
-configuration variable `mergetool.<tool>.path`. For example, you
-can configure the absolute path to kdiff3 by setting
-`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the
-tool is available in PATH.
-+
-Instead of running one of the known merge tool programs,
-'git-difftool' can be customized to run an alternative program
-by specifying the command line to invoke in a configuration
-variable `mergetool.<tool>.cmd`.
-+
-When 'git-difftool' is invoked with this tool (either through the
-`-t` or `--tool` option or the `merge.tool` configuration variable)
-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`.
-
---no-prompt::
-       Do not prompt before launching a diff tool.
-
-CONFIG VARIABLES
-----------------
-merge.tool::
-       The default merge tool to use.
-+
-See the `--tool=<tool>` option above for more details.
-
-merge.keepBackup::
-       The original, unedited file content can be saved to a file with
-       a `.orig` extension.  Defaults to `true` (i.e. keep the backup files).
-
-mergetool.<tool>.path::
-       Override the path for the given tool.  This is useful in case
-       your tool is not in the PATH.
-
-mergetool.<tool>.cmd::
-       Specify the command to invoke the specified merge tool.
-+
-See the `--tool=<tool>` option above for more details.
-
-
-SEE ALSO
---------
-linkgit:git-diff[1]::
-        Show changes between commits, commit and working tree, etc
-
-linkgit:git-mergetool[1]::
-       Run merge conflict resolution tools to resolve merge conflicts
-
-linkgit:git-config[1]::
-        Get and set repository or global options
-
-
-AUTHOR
-------
-Written by David Aguilar <davvid@gmail.com>.
-
-Documentation
---------------
-Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the linkgit:git[1] suite
index 3832f602253fbe793ddf81c61b61e5a2757ce89d..342529db309821f461e8f77d05bc5e01c76802ec 100755 (executable)
@@ -1142,7 +1142,7 @@ class P4Sync(Command):
 
         s = ''
         for (key, val) in self.users.items():
-            s += "%s\t%s\n" % (key, val)
+           s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1))
 
         open(self.getUserCacheFilename(), "wb").write(s)
         self.userMapFromPerforceServer = True
index 23aeb257b9557cb146586868084ce1b20d7e7ac8..78e40d2a13e5c754b10a8e68641236bf58394e05 100755 (executable)
@@ -14,13 +14,18 @@ die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV;
 
 my $branch_name = 'import-tars';
 my $branch_ref = "refs/heads/$branch_name";
-my $committer_name = 'T Ar Creator';
-my $committer_email = 'tar@example.com';
+my $author_name = $ENV{'GIT_AUTHOR_NAME'} || 'T Ar Creator';
+my $author_email = $ENV{'GIT_AUTHOR_EMAIL'} || 'tar@example.com';
+my $committer_name = $ENV{'GIT_COMMITTER_NAME'} || `git config --get user.name`;
+my $committer_email = $ENV{'GIT_COMMITTER_EMAIL'} || `git config --get user.email`;
+
+chomp($committer_name, $committer_email);
 
 open(FI, '|-', 'git', 'fast-import', '--quiet')
        or die "Unable to start git fast-import: $!\n";
 foreach my $tar_file (@ARGV)
 {
+       my $commit_time = time;
        $tar_file =~ m,([^/]+)$,;
        my $tar_name = $1;
 
@@ -39,7 +44,7 @@ foreach my $tar_file (@ARGV)
                die "Unrecognized compression format: $tar_file\n";
        }
 
-       my $commit_time = 0;
+       my $author_time = 0;
        my $next_mark = 1;
        my $have_top_dir = 1;
        my ($top_dir, %files);
@@ -77,10 +82,16 @@ foreach my $tar_file (@ARGV)
                $mtime = oct $mtime;
                next if $typeflag == 5; # directory
 
-               print FI "blob\n", "mark :$next_mark\n", "data $size\n";
-               while ($size > 0 && read(I, $_, 512) == 512) {
-                       print FI substr($_, 0, $size);
-                       $size -= 512;
+               print FI "blob\n", "mark :$next_mark\n";
+               if ($typeflag == 2) { # symbolic link
+                       print FI "data ", length($linkname), "\n", $linkname;
+                       $mode = 0120000;
+               } else {
+                       print FI "data $size\n";
+                       while ($size > 0 && read(I, $_, 512) == 512) {
+                               print FI substr($_, 0, $size);
+                               $size -= 512;
+                       }
                }
                print FI "\n";
 
@@ -92,7 +103,7 @@ foreach my $tar_file (@ARGV)
                }
                $files{$path} = [$next_mark++, $mode];
 
-               $commit_time = $mtime if $mtime > $commit_time;
+               $author_time = $mtime if $mtime > $author_time;
                $path =~ m,^([^/]+)/,;
                $top_dir = $1 unless $top_dir;
                $have_top_dir = 0 if $top_dir ne $1;
@@ -100,6 +111,7 @@ foreach my $tar_file (@ARGV)
 
        print FI <<EOF;
 commit $branch_ref
+author $author_name <$author_email> $author_time +0000
 committer $committer_name <$committer_email> $commit_time +0000
 data <<END_OF_COMMIT_MESSAGE
 Imported from $tar_file.
@@ -112,14 +124,15 @@ EOF
        {
                my ($mark, $mode) = @{$files{$path}};
                $path =~ s,^([^/]+)/,, if $have_top_dir;
-               printf FI "M %o :%i %s\n", $mode & 0111 ? 0755 : 0644, $mark, $path;
+               $mode = $mode & 0111 ? 0755 : 0644 unless $mode == 0120000;
+               printf FI "M %o :%i %s\n", $mode, $mark, $path;
        }
        print FI "\n";
 
        print FI <<EOF;
 tag $tar_name
 from $branch_ref
-tagger $committer_name <$committer_email> $commit_time +0000
+tagger $author_name <$author_email> $author_time +0000
 data <<END_OF_TAG_MESSAGE
 Package $tar_name
 END_OF_TAG_MESSAGE
index 7b03204ed18500756ba55818f0808b52db68d048..2a6839d81ee970cb799859c7a833f8f8cb780864 100755 (executable)
@@ -20,7 +20,7 @@
 """
 
 import os, os.path, sys
-import tempfile, popen2, pickle, getopt
+import tempfile, pickle, getopt
 import re
 
 # Maps hg version -> git version
index 39f96aa115e0a20024d2f41138db6b2b8c6d5320..000147bbe4a00525d68efb1358c013812e10dcca 100644 (file)
@@ -1,12 +1,12 @@
 appp.sh is a script that is supposed to be used together with ExternalEditor
-for Mozilla Thundebird. It will let you include patches inline in e-mails
+for Mozilla Thunderbird. It will let you include patches inline in e-mails
 in an easy way.
 
 Usage:
 - Generate the patch with git format-patch.
 - Start writing a new e-mail in Thunderbird.
 - Press the external editor button (or Ctrl-E) to run appp.sh
-- Select the previosly generated patch file.
+- Select the previously generated patch file.
 - Finish editing the e-mail.
 
 Any text that is entered into the message editor before appp.sh is called
diff --git a/ctype.c b/ctype.c
index b90ec004f29c30c4b6a6ea5339599d7a8db0fb8c..7ee64c7d77dd4a5665f70d80ffba1bcdecb9a408 100644 (file)
--- a/ctype.c
+++ b/ctype.c
@@ -10,7 +10,7 @@ enum {
        A = GIT_ALPHA,
        D = GIT_DIGIT,
        G = GIT_GLOB_SPECIAL,   /* *, ?, [, \\ */
-       R = GIT_REGEX_SPECIAL,  /* $, (, ), +, ., ^, {, | * */
+       R = GIT_REGEX_SPECIAL,  /* $, (, ), +, ., ^, {, | */
 };
 
 unsigned char sane_ctype[256] = {
index d93cf960f9eaf05eec11b67746142e6e94d719cb..b2babcc076de65b53671157115e63e74fec83a3e 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -229,7 +229,7 @@ static char *path_ok(char *directory)
        }
 
        if (!path) {
-               logerror("'%s': unable to chdir or not a git archive", dir);
+               logerror("'%s' does not appear to be a git repository", dir);
                return NULL;
        }
 
@@ -406,15 +406,15 @@ static char *xstrdup_tolower(const char *str)
 }
 
 /*
- * Separate the "extra args" information as supplied by the client connection.
+ * Read the host as supplied by the client connection.
  */
-static void parse_extra_args(char *extra_args, int buflen)
+static void parse_host_arg(char *extra_args, int buflen)
 {
        char *val;
        int vallen;
        char *end = extra_args + buflen;
 
-       while (extra_args < end && *extra_args) {
+       if (extra_args < end && *extra_args) {
                saw_extended_args = 1;
                if (strncasecmp("host=", extra_args, 5) == 0) {
                        val = extra_args + 5;
@@ -436,6 +436,8 @@ static void parse_extra_args(char *extra_args, int buflen)
                        /* On to the next one */
                        extra_args = val + vallen;
                }
+               if (extra_args < end && *extra_args)
+                       die("Invalid request");
        }
 
        /*
@@ -444,27 +446,27 @@ static void parse_extra_args(char *extra_args, int buflen)
        if (hostname) {
 #ifndef NO_IPV6
                struct addrinfo hints;
-               struct addrinfo *ai, *ai0;
+               struct addrinfo *ai;
                int gai;
                static char addrbuf[HOST_NAME_MAX + 1];
 
                memset(&hints, 0, sizeof(hints));
                hints.ai_flags = AI_CANONNAME;
 
-               gai = getaddrinfo(hostname, 0, &hints, &ai0);
+               gai = getaddrinfo(hostname, 0, &hints, &ai);
                if (!gai) {
-                       for (ai = ai0; ai; ai = ai->ai_next) {
-                               struct sockaddr_in *sin_addr = (void *)ai->ai_addr;
-
-                               inet_ntop(AF_INET, &sin_addr->sin_addr,
-                                         addrbuf, sizeof(addrbuf));
-                               free(canon_hostname);
-                               canon_hostname = xstrdup(ai->ai_canonname);
-                               free(ip_address);
-                               ip_address = xstrdup(addrbuf);
-                               break;
-                       }
-                       freeaddrinfo(ai0);
+                       struct sockaddr_in *sin_addr = (void *)ai->ai_addr;
+
+                       inet_ntop(AF_INET, &sin_addr->sin_addr,
+                                 addrbuf, sizeof(addrbuf));
+                       free(ip_address);
+                       ip_address = xstrdup(addrbuf);
+
+                       free(canon_hostname);
+                       canon_hostname = xstrdup(ai->ai_canonname ?
+                                                ai->ai_canonname : ip_address);
+
+                       freeaddrinfo(ai);
                }
 #else
                struct hostent *hent;
@@ -545,7 +547,7 @@ static int execute(struct sockaddr *addr)
        hostname = canon_hostname = ip_address = tcp_port = NULL;
 
        if (len != pktlen)
-               parse_extra_args(line + len + 1, pktlen - len - 1);
+               parse_host_arg(line + len + 1, pktlen - len - 1);
 
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                struct daemon_service *s = &(daemon_service[i]);
index 82d9e221eabab53acc418d0db6327e480836a5ed..e6fd8a7441a7ac6753d93e7156b9f71fe248262d 100644 (file)
@@ -8,7 +8,9 @@
 
 static unsigned int hash_obj(const struct object *obj, unsigned int n)
 {
-       unsigned int hash = *(unsigned int *)obj->sha1;
+       unsigned int hash;
+
+       memcpy(&hash, obj->sha1, sizeof(unsigned int));
        return hash % n;
 }
 
diff --git a/delta.h b/delta.h
index 40ccf5a1e95f62d840a006274f7024fa43208b1c..b9d333dd5a1c64ab35159ed608cf942951504f84 100644 (file)
--- a/delta.h
+++ b/delta.h
@@ -90,12 +90,11 @@ static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
                                               const unsigned char *top)
 {
        const unsigned char *data = *datap;
-       unsigned char cmd;
-       unsigned long size = 0;
+       unsigned long cmd, size = 0;
        int i = 0;
        do {
                cmd = *data++;
-               size |= (cmd & ~0x80) << i;
+               size |= (cmd & 0x7f) << i;
                i += 7;
        } while (cmd & 0x80 && data < top);
        *datap = data;
index 79d06068344f5a602f6c8799f6671ccbf98cf49c..0aba6cda3c01e17b07bb7235b0395ba50256aedd 100644 (file)
@@ -31,7 +31,7 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
                        return -1;
                return 1;
        }
-       if (has_symlink_leading_path(ce_namelen(ce), ce->name))
+       if (has_symlink_leading_path(ce->name, ce_namelen(ce)))
                return 1;
        if (S_ISDIR(st->st_mode)) {
                unsigned char sub[20];
@@ -214,7 +214,7 @@ static int get_stat_data(struct cache_entry *ce,
        const unsigned char *sha1 = ce->sha1;
        unsigned int mode = ce->ce_mode;
 
-       if (!cached) {
+       if (!cached && !ce_uptodate(ce)) {
                int changed;
                struct stat st;
                changed = check_removed(ce, &st);
index 7273a7a0b648cc8cbd6dce2043a6e544b87d28b6..4ebc1dbd87d9b90ad2aaa5c7d4601761e2d1d724 100644 (file)
@@ -38,6 +38,10 @@ static int get_mode(const char *path, int *mode)
 
        if (!path || !strcmp(path, "/dev/null"))
                *mode = 0;
+#ifdef _WIN32
+       else if (!strcasecmp(path, "nul"))
+               *mode = 0;
+#endif
        else if (!strcmp(path, "-"))
                *mode = create_ce_mode(0666);
        else if (lstat(path, &st))
@@ -229,7 +233,7 @@ void diff_no_index(struct rev_info *revs,
        if (prefix) {
                int len = strlen(prefix);
 
-               revs->diffopt.paths = xcalloc(2, sizeof(char*));
+               revs->diffopt.paths = xcalloc(2, sizeof(char *));
                for (i = 0; i < 2; i++) {
                        const char *p = argv[argc - 2 + i];
                        /*
diff --git a/diff.c b/diff.c
index 4f980aded6aa0f61b0a6354b0157b8711a2c4627..f0b580c1503147d093d928915ead2c5f88a0042b 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -30,14 +30,14 @@ int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 
 static char diff_colors[][COLOR_MAXLEN] = {
-       "\033[m",       /* reset */
-       "",             /* PLAIN (normal) */
-       "\033[1m",      /* METAINFO (bold) */
-       "\033[36m",     /* FRAGINFO (cyan) */
-       "\033[31m",     /* OLD (red) */
-       "\033[32m",     /* NEW (green) */
-       "\033[33m",     /* COMMIT (yellow) */
-       "\033[41m",     /* WHITESPACE (red background) */
+       GIT_COLOR_RESET,
+       GIT_COLOR_NORMAL,       /* PLAIN */
+       GIT_COLOR_BOLD,         /* METAINFO */
+       GIT_COLOR_CYAN,         /* FRAGINFO */
+       GIT_COLOR_RED,          /* OLD */
+       GIT_COLOR_GREEN,        /* NEW */
+       GIT_COLOR_YELLOW,       /* COMMIT */
+       GIT_COLOR_BG_RED,       /* WHITESPACE */
 };
 
 static void diff_filespec_load_driver(struct diff_filespec *one);
@@ -62,6 +62,15 @@ static int parse_diff_color_slot(const char *var, int ofs)
        die("bad config variable '%s'", var);
 }
 
+static int git_config_rename(const char *var, const char *value)
+{
+       if (!value)
+               return DIFF_DETECT_RENAME;
+       if (!strcasecmp(value, "copies") || !strcasecmp(value, "copy"))
+               return  DIFF_DETECT_COPY;
+       return git_config_bool(var,value) ? DIFF_DETECT_RENAME : 0;
+}
+
 /*
  * These are to give UI layer defaults.
  * The core-level commands such as git-diff-files should
@@ -75,13 +84,7 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                return 0;
        }
        if (!strcmp(var, "diff.renames")) {
-               if (!value)
-                       diff_detect_rename_default = DIFF_DETECT_RENAME;
-               else if (!strcasecmp(value, "copies") ||
-                        !strcasecmp(value, "copy"))
-                       diff_detect_rename_default = DIFF_DETECT_COPY;
-               else if (git_config_bool(var,value))
-                       diff_detect_rename_default = DIFF_DETECT_RENAME;
+               diff_detect_rename_default = git_config_rename(var, value);
                return 0;
        }
        if (!strcmp(var, "diff.autorefreshindex")) {
@@ -186,7 +189,7 @@ static void remove_tempfile(void)
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
                if (diff_temp[i].name == diff_temp[i].tmp_path)
-                       unlink(diff_temp[i].name);
+                       unlink_or_warn(diff_temp[i].name);
                diff_temp[i].name = NULL;
        }
 }
@@ -873,9 +876,9 @@ static void fill_print_name(struct diffstat_file *file)
        file->print_name = pname;
 }
 
-static void show_stats(struct diffstat_tdata, struct diff_options *options)
+static void show_stats(struct diffstat_t *data, struct diff_options *options)
 {
-       int i, len, add, del, total, adds = 0, dels = 0;
+       int i, len, add, del, adds = 0, dels = 0;
        int max_change = 0, max_len = 0;
        int total_files = data->nr;
        int width, name_width;
@@ -978,14 +981,12 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
                 */
                add = added;
                del = deleted;
-               total = add + del;
                adds += add;
                dels += del;
 
                if (width <= max_change) {
                        add = scale_linear(add, width, max_change);
                        del = scale_linear(del, width, max_change);
-                       total = add + del;
                }
                show_name(options->file, prefix, name, len, reset, set);
                fprintf(options->file, "%5d%s", added + deleted,
@@ -1024,7 +1025,7 @@ static void show_shortstats(struct diffstat_t* data, struct diff_options *option
               total_files, adds, dels);
 }
 
-static void show_numstat(struct diffstat_tdata, struct diff_options *options)
+static void show_numstat(struct diffstat_t *data, struct diff_options *options)
 {
        int i;
 
@@ -2582,13 +2583,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 
        /* xdiff options */
        else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
-               options->xdl_opts |= XDF_IGNORE_WHITESPACE;
+               DIFF_XDL_SET(options, IGNORE_WHITESPACE);
        else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
-               options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
+               DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);
        else if (!strcmp(arg, "--ignore-space-at-eol"))
-               options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL;
+               DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(arg, "--patience"))
-               options->xdl_opts |= XDF_PATIENCE_DIFF;
+               DIFF_XDL_SET(options, PATIENCE_DIFF);
 
        /* flags options */
        else if (!strcmp(arg, "--binary")) {
@@ -2609,10 +2610,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_SET(options, COLOR_DIFF);
        else if (!strcmp(arg, "--no-color"))
                DIFF_OPT_CLR(options, COLOR_DIFF);
-       else if (!strcmp(arg, "--color-words"))
-               options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
+       else if (!strcmp(arg, "--color-words")) {
+               DIFF_OPT_SET(options, COLOR_DIFF);
+               DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
+       }
        else if (!prefixcmp(arg, "--color-words=")) {
-               options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
+               DIFF_OPT_SET(options, COLOR_DIFF);
+               DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
                options->word_regex = arg + 14;
        }
        else if (!strcmp(arg, "--exit-code"))
@@ -3586,6 +3590,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
        if (start_command(&child) != 0 ||
            strbuf_read(&buf, child.out, 0) < 0 ||
            finish_command(&child) != 0) {
+               strbuf_release(&buf);
                remove_tempfile();
                error("error running textconv command '%s'", pgm);
                return NULL;
diff --git a/diff.h b/diff.h
index 6703a4fb4f0302f4adf1065e91cd1bb27e5c973a..6616877ee5d15a8101cbdea0271cc18f8837d2f1 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -69,6 +69,9 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
 #define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)
+#define DIFF_XDL_TST(opts, flag)    ((opts)->xdl_opts & XDF_##flag)
+#define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)
+#define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)
 
 struct diff_options {
        const char *filter;
index 31cdcfe8bcdae7df65b0387071846299a14bb7be..d7097bb576cef8900e8e0218ba82e1cc7a87a567 100644 (file)
@@ -45,7 +45,7 @@ static int should_break(struct diff_filespec *src,
         * The value we return is 1 if we want the pair to be broken,
         * or 0 if we do not.
         */
-       unsigned long delta_size, base_size, max_size;
+       unsigned long delta_size, max_size;
        unsigned long src_copied, literal_added, src_removed;
 
        *merge_score_p = 0; /* assume no deletion --- "do not break"
@@ -64,7 +64,6 @@ static int should_break(struct diff_filespec *src,
        if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
                return 0; /* error but caught downstream */
 
-       base_size = ((src->size < dst->size) ? src->size : dst->size);
        max_size = ((src->size > dst->size) ? src->size : dst->size);
        if (max_size < MINIMUM_BREAK_SIZE)
                return 0; /* we do not break too small filepair */
index 0b0d6b8c8c2ab8833bb5d929ef0d3cb7891ec582..63ac998bfaf64da807bec0ae0bd57c391fb651fe 100644 (file)
@@ -267,7 +267,7 @@ static int find_identical_files(struct file_similarity *src,
                        int score;
                        struct diff_filespec *source = p->filespec;
 
-                       /* False hash collission? */
+                       /* False hash collision? */
                        if (hashcmp(source->sha1, target->sha1))
                                continue;
                        /* Non-regular files? If so, the modes must match! */
diff --git a/dir.c b/dir.c
index 8b6c1f4755318375878e3cb7290441fcdf6a4eaa..0e6b752cd5833bfb970619983b4efa880aaaf134 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -156,7 +156,7 @@ void add_exclude(const char *string, const char *base,
        if (len && string[len - 1] == '/') {
                char *s;
                x = xmalloc(sizeof(*x) + len);
-               s = (char*)(x+1);
+               s = (char *)(x+1);
                memcpy(s, string, len - 1);
                s[len - 1] = '\0';
                string = s;
@@ -487,14 +487,14 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
                return recurse_into_directory;
 
        case index_gitdir:
-               if (dir->show_other_directories)
+               if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
                        return ignore_directory;
                return show_directory;
 
        case index_nonexistent:
-               if (dir->show_other_directories)
+               if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
                        break;
-               if (!dir->no_gitlinks) {
+               if (!(dir->flags & DIR_NO_GITLINKS)) {
                        unsigned char sha1[20];
                        if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
                                return show_directory;
@@ -503,7 +503,7 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
        }
 
        /* This is the "show_other_directories" case */
-       if (!dir->hide_empty_directories)
+       if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
                return show_directory;
        if (!read_directory_recursive(dir, dirname, dirname, len, 1, simplify))
                return ignore_directory;
@@ -576,7 +576,7 @@ static int get_dtype(struct dirent *de, const char *path)
  */
 static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only, const struct path_simplify *simplify)
 {
-       DIR *fdir = opendir(path);
+       DIR *fdir = opendir(*path ? path : ".");
        int contents = 0;
 
        if (fdir) {
@@ -601,7 +601,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
 
                        dtype = DTYPE(de);
                        exclude = excluded(dir, fullname, &dtype);
-                       if (exclude && dir->collect_ignored
+                       if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
                            && in_pathspec(fullname, baselen + len, simplify))
                                dir_add_ignored(dir, fullname, baselen + len);
 
@@ -609,7 +609,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                         * Excluded? If we don't explicitly want to show
                         * ignored files, ignore it
                         */
-                       if (exclude && !dir->show_ignored)
+                       if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
                                continue;
 
                        if (dtype == DT_UNKNOWN)
@@ -621,7 +621,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                         * even if we don't ignore them, since the
                         * directory may contain files that we do..
                         */
-                       if (!exclude && dir->show_ignored) {
+                       if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) {
                                if (dtype != DT_DIR)
                                        continue;
                        }
@@ -634,7 +634,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                                len++;
                                switch (treat_directory(dir, fullname, baselen + len, simplify)) {
                                case show_directory:
-                                       if (exclude != dir->show_ignored)
+                                       if (exclude != !!(dir->flags
+                                                       & DIR_SHOW_IGNORED))
                                                continue;
                                        break;
                                case recurse_into_directory:
@@ -720,7 +721,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
 {
        struct path_simplify *simplify;
 
-       if (has_symlink_leading_path(strlen(path), path))
+       if (has_symlink_leading_path(path, strlen(path)))
                return dir->nr;
 
        simplify = create_simplify(pathspec);
diff --git a/dir.h b/dir.h
index bdc2d47447c2ca406aac41d7a8382bf5928fbda8..541286ad1daeb66a9963288d275534ee963bd5cd 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -34,11 +34,13 @@ struct exclude_stack {
 struct dir_struct {
        int nr, alloc;
        int ignored_nr, ignored_alloc;
-       unsigned int show_ignored:1,
-                    show_other_directories:1,
-                    hide_empty_directories:1,
-                    no_gitlinks:1,
-                    collect_ignored:1;
+       enum {
+               DIR_SHOW_IGNORED = 1<<0,
+               DIR_SHOW_OTHER_DIRECTORIES = 1<<1,
+               DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
+               DIR_NO_GITLINKS = 1<<3,
+               DIR_COLLECT_IGNORED = 1<<4
+       } flags;
        struct dir_entry **entries;
        struct dir_entry **ignored;
 
diff --git a/entry.c b/entry.c
index 05aa58d34823258789ec9e32abc897b8e6777412..cc841edf9051460b3a382ea25c0097f245ec8884 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -2,15 +2,19 @@
 #include "blob.h"
 #include "dir.h"
 
-static void create_directories(const char *path, const struct checkout *state)
+static void create_directories(const char *path, int path_len,
+                              const struct checkout *state)
 {
-       int len = strlen(path);
-       char *buf = xmalloc(len + 1);
-       const char *slash = path;
-
-       while ((slash = strchr(slash+1, '/')) != NULL) {
-               len = slash - path;
-               memcpy(buf, path, len);
+       char *buf = xmalloc(path_len + 1);
+       int len = 0;
+
+       while (len < path_len) {
+               do {
+                       buf[len] = path[len];
+                       len++;
+               } while (len < path_len && path[len] != '/');
+               if (len >= path_len)
+                       break;
                buf[len] = 0;
 
                /*
@@ -20,7 +24,7 @@ static void create_directories(const char *path, const struct checkout *state)
                 * we test the path components of the prefix with the
                 * stat() function instead of the lstat() function.
                 */
-               if (has_dirs_only_path(len, buf, state->base_dir_len))
+               if (has_dirs_only_path(buf, len, state->base_dir_len))
                        continue; /* ok, it is already a directory. */
 
                /*
@@ -31,7 +35,7 @@ static void create_directories(const char *path, const struct checkout *state)
                 */
                if (mkdir(buf, 0777)) {
                        if (errno == EEXIST && state->force &&
-                           !unlink(buf) && !mkdir(buf, 0777))
+                           !unlink_or_warn(buf) && !mkdir(buf, 0777))
                                continue;
                        die("cannot create directory at %s", buf);
                }
@@ -74,7 +78,7 @@ static int create_file(const char *path, unsigned int mode)
        return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
 }
 
-static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size)
+static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
 {
        enum object_type type;
        void *new = read_sha1_file(ce->sha1, &type, size);
@@ -89,36 +93,52 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned
 
 static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
 {
-       int fd;
-       long wrote;
-
-       switch (ce->ce_mode & S_IFMT) {
-               char *new;
-               struct strbuf buf;
-               unsigned long size;
+       unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
+       int fd, ret, fstat_done = 0;
+       char *new;
+       struct strbuf buf = STRBUF_INIT;
+       unsigned long size;
+       size_t wrote, newsize = 0;
+       struct stat st;
 
+       switch (ce_mode_s_ifmt) {
        case S_IFREG:
-               new = read_blob_entry(ce, path, &size);
+       case S_IFLNK:
+               new = read_blob_entry(ce, &size);
                if (!new)
                        return error("git checkout-index: 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)",
+                                            path, strerror(errno));
+                       break;
+               }
+
                /*
                 * Convert from git internal format to working tree format
                 */
-               strbuf_init(&buf, 0);
-               if (convert_to_working_tree(ce->name, new, size, &buf)) {
-                       size_t newsize = 0;
+               if (ce_mode_s_ifmt == S_IFREG &&
+                   convert_to_working_tree(ce->name, new, size, &buf)) {
                        free(new);
                        new = strbuf_detach(&buf, &newsize);
                        size = newsize;
                }
 
                if (to_tempfile) {
-                       strcpy(path, ".merge_file_XXXXXX");
+                       if (ce_mode_s_ifmt == S_IFREG)
+                               strcpy(path, ".merge_file_XXXXXX");
+                       else
+                               strcpy(path, ".merge_link_XXXXXX");
                        fd = mkstemp(path);
-               } else
+               } else if (ce_mode_s_ifmt == S_IFREG) {
                        fd = create_file(path, ce->ce_mode);
+               } else {
+                       fd = create_file(path, 0666);
+               }
                if (fd < 0) {
                        free(new);
                        return error("git checkout-index: unable to create file %s (%s)",
@@ -126,41 +146,17 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                }
 
                wrote = write_in_full(fd, new, size);
+               /* use fstat() only when path == ce->name */
+               if (fstat_is_reliable() &&
+                   state->refresh_cache && !to_tempfile && !state->base_dir_len) {
+                       fstat(fd, &st);
+                       fstat_done = 1;
+               }
                close(fd);
                free(new);
                if (wrote != size)
                        return error("git checkout-index: unable to write file %s", path);
                break;
-       case S_IFLNK:
-               new = read_blob_entry(ce, path, &size);
-               if (!new)
-                       return error("git checkout-index: unable to read sha1 file of %s (%s)",
-                               path, sha1_to_hex(ce->sha1));
-               if (to_tempfile || !has_symlinks) {
-                       if (to_tempfile) {
-                               strcpy(path, ".merge_link_XXXXXX");
-                               fd = mkstemp(path);
-                       } else
-                               fd = create_file(path, 0666);
-                       if (fd < 0) {
-                               free(new);
-                               return error("git checkout-index: unable to create "
-                                                "file %s (%s)", path, strerror(errno));
-                       }
-                       wrote = write_in_full(fd, new, size);
-                       close(fd);
-                       free(new);
-                       if (wrote != size)
-                               return error("git checkout-index: unable to write file %s",
-                                       path);
-               } else {
-                       wrote = symlink(new, path);
-                       free(new);
-                       if (wrote)
-                               return error("git checkout-index: unable to create "
-                                                "symlink %s (%s)", path, strerror(errno));
-               }
-               break;
        case S_IFGITLINK:
                if (to_tempfile)
                        return error("git checkout-index: cannot create temporary subproject %s", path);
@@ -172,8 +168,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
        }
 
        if (state->refresh_cache) {
-               struct stat st;
-               lstat(ce->name, &st);
+               if (!fstat_done)
+                       lstat(ce->name, &st);
                fill_stat_cache_info(ce, &st);
        }
        return 0;
@@ -190,6 +186,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
 
        memcpy(path, state->base_dir, len);
        strcpy(path + len, ce->name);
+       len += ce_namelen(ce);
 
        if (!lstat(path, &st)) {
                unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
@@ -218,6 +215,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
                        return error("unable to unlink old '%s' (%s)", path, strerror(errno));
        } else if (state->not_new)
                return 0;
-       create_directories(path, state);
+       create_directories(path, len, state);
        return write_entry(ce, path, state, 0);
 }
index e278bce0ea5f1ddda2dab9012663c6d1d4c6bd89..801a005ef1b23ef13cfa9ece676c550fe35dedc0 100644 (file)
@@ -42,6 +42,11 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
+enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
+#ifndef OBJECT_CREATION_MODE
+#define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS
+#endif
+enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
 
 /* Parallel index stat data preload? */
 int core_preload_index = 0;
index d5fc042bbfe97080df3e275f069055c63bad32eb..a2a24588a99957d5af759ca6d8858366430ab3a3 100644 (file)
@@ -1,4 +1,5 @@
 /*
+(See Documentation/git-fast-import.txt for maintained documentation.)
 Format of STDIN stream:
 
   stream ::= cmd*;
@@ -18,8 +19,8 @@ Format of STDIN stream:
 
   new_commit ::= 'commit' sp ref_str lf
     mark?
-    ('author' sp name '<' email '>' when lf)?
-    'committer' sp name '<' email '>' when lf
+    ('author' sp name sp '<' email '>' sp when lf)?
+    'committer' sp name sp '<' email '>' sp when lf
     commit_msg
     ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
     ('merge' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)*
@@ -43,7 +44,7 @@ Format of STDIN stream:
 
   new_tag ::= 'tag' sp tag_str lf
     'from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf
-    ('tagger' sp name '<' email '>' when lf)?
+    ('tagger' sp name sp '<' email '>' sp when lf)?
     tag_msg;
   tag_msg ::= data;
 
@@ -75,7 +76,7 @@ Format of STDIN stream:
     delim lf;
 
      # note: declen indicates the length of binary_data in bytes.
-     # declen does not include the lf preceeding the binary data.
+     # declen does not include the lf preceding the binary data.
      #
   exact_data ::= 'data' sp declen lf
     binary_data;
@@ -132,8 +133,8 @@ Format of STDIN stream:
      # 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 the line (an lf have
-     # preceeded it).
+     # must be the first character on that line (an lf
+     # preceded it).
      #
   comment ::= '#' not_lf* lf;
   not_lf  ::= # Any byte that is not ASCII newline (LF);
@@ -211,7 +212,7 @@ struct tree_content;
 struct tree_entry
 {
        struct tree_content *tree;
-       struct atom_strname;
+       struct atom_str *name;
        struct tree_entry_ms
        {
                uint16_t mode;
@@ -312,7 +313,7 @@ static unsigned int object_entry_alloc = 5000;
 static struct object_entry_pool *blocks;
 static struct object_entry *object_table[1 << 16];
 static struct mark_set *marks;
-static const charmark_file;
+static const char *mark_file;
 
 /* Our last blob */
 static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 };
@@ -671,7 +672,7 @@ static struct branch *lookup_branch(const char *name)
 static struct branch *new_branch(const char *name)
 {
        unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz;
-       struct branchb = lookup_branch(name);
+       struct branch *b = lookup_branch(name);
 
        if (b)
                die("Invalid attempt to create duplicate branch: %s", name);
@@ -930,7 +931,7 @@ static void unkeep_all_packs(void)
                struct packed_git *p = all_packs[k];
                snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
                         get_object_directory(), sha1_to_hex(p->sha1));
-               unlink(name);
+               unlink_or_warn(name);
        }
 }
 
@@ -952,7 +953,7 @@ static void end_packfile(void)
                close(pack_data->pack_fd);
                idx_name = keep_pack(create_index());
 
-               /* Register the packfile with core git's machinary. */
+               /* Register the packfile with core git's machinery. */
                new_p = add_packed_git(idx_name, strlen(idx_name), 1);
                if (!new_p)
                        die("core git rejected index %s", idx_name);
@@ -980,7 +981,7 @@ static void end_packfile(void)
        }
        else {
                close(old_p->pack_fd);
-               unlink(old_p->pack_name);
+               unlink_or_warn(old_p->pack_name);
        }
        free(old_p);
 
@@ -1034,7 +1035,7 @@ static int store_object(
        git_SHA_CTX c;
        z_stream s;
 
-       hdrlen = sprintf((char*)hdr,"%s %lu", typename(type),
+       hdrlen = sprintf((char *)hdr,"%s %lu", typename(type),
                (unsigned long)dat->len) + 1;
        git_SHA1_Init(&c);
        git_SHA1_Update(&c, hdr, hdrlen);
@@ -1216,7 +1217,7 @@ static const char *get_mode(const char *str, uint16_t *modep)
 
 static void load_tree(struct tree_entry *root)
 {
-       unsigned charsha1 = root->versions[1].sha1;
+       unsigned char *sha1 = root->versions[1].sha1;
        struct object_entry *myoe;
        struct tree_content *t;
        unsigned long size;
@@ -1257,8 +1258,8 @@ static void load_tree(struct tree_entry *root)
                e->versions[0].mode = e->versions[1].mode;
                e->name = to_atom(c, strlen(c));
                c += e->name->str_len + 1;
-               hashcpy(e->versions[0].sha1, (unsigned char*)c);
-               hashcpy(e->versions[1].sha1, (unsigned char*)c);
+               hashcpy(e->versions[0].sha1, (unsigned char *)c);
+               hashcpy(e->versions[1].sha1, (unsigned char *)c);
                c += 20;
        }
        free(buf);
@@ -1742,21 +1743,19 @@ static void parse_data(struct strbuf *sb)
 static int validate_raw_date(const char *src, char *result, int maxlen)
 {
        const char *orig_src = src;
-       char *endp, sign;
-       unsigned long date;
+       char *endp;
 
        errno = 0;
 
-       date = strtoul(src, &endp, 10);
+       strtoul(src, &endp, 10);
        if (errno || endp == src || *endp != ' ')
                return -1;
 
        src = endp + 1;
        if (*src != '-' && *src != '+')
                return -1;
-       sign = *src;
 
-       date = strtoul(src + 1, &endp, 10);
+       strtoul(src + 1, &endp, 10);
        if (errno || endp == src || *endp || (endp - orig_src) >= maxlen)
                return -1;
 
diff --git a/fsck.c b/fsck.c
index 97f76c58155249412d7c59e965b564dfc0f75181..511b82cba9977c14d6dcba5efbd6d38591d3aacc 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -148,20 +148,17 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
        struct tree_desc desc;
        unsigned o_mode;
        const char *o_name;
-       const unsigned char *o_sha1;
 
        init_tree_desc(&desc, item->buffer, item->size);
 
        o_mode = 0;
        o_name = NULL;
-       o_sha1 = NULL;
 
        while (desc.size) {
                unsigned mode;
                const char *name;
-               const unsigned char *sha1;
 
-               sha1 = tree_entry_extract(&desc, &name, &mode);
+               tree_entry_extract(&desc, &name, &mode);
 
                if (strchr(name, '/'))
                        has_full_path = 1;
@@ -207,7 +204,6 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
 
                o_mode = mode;
                o_name = name;
-               o_sha1 = sha1;
        }
 
        retval = 0;
diff --git a/fsck.h b/fsck.h
index 990ee02335a2e2693e32baa82b259c23843f2aa0..008456b675b6e5c8a5a07e0b8628a41e3acc733a 100644 (file)
--- a/fsck.h
+++ b/fsck.h
@@ -23,7 +23,7 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...);
  * the return value is:
  *    -1       error in processing the object
  *    <0       return value of the callback, which lead to an abort
- *    >0       return value of the first sigaled error >0 (in the case of no other errors)
+ *    >0       return value of the first signaled error >0 (in the case of no other errors)
  *    0                everything OK
  */
 int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);
index 5407b2e1b88cef5b9124ef9dca79cd1f95018bae..06f70602cc619e9e13bdc609a43780ce8bc6570d 100755 (executable)
@@ -767,6 +767,100 @@ sub split_hunk {
        return @split;
 }
 
+sub find_last_o_ctx {
+       my ($it) = @_;
+       my $text = $it->{TEXT};
+       my ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]);
+       my $i = @{$text};
+       my $last_o_ctx = $o_ofs + $o_cnt;
+       while (0 < --$i) {
+               my $line = $text->[$i];
+               if ($line =~ /^ /) {
+                       $last_o_ctx--;
+                       next;
+               }
+               last;
+       }
+       return $last_o_ctx;
+}
+
+sub merge_hunk {
+       my ($prev, $this) = @_;
+       my ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) =
+           parse_hunk_header($prev->{TEXT}[0]);
+       my ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) =
+           parse_hunk_header($this->{TEXT}[0]);
+
+       my (@line, $i, $ofs, $o_cnt, $n_cnt);
+       $ofs = $o0_ofs;
+       $o_cnt = $n_cnt = 0;
+       for ($i = 1; $i < @{$prev->{TEXT}}; $i++) {
+               my $line = $prev->{TEXT}[$i];
+               if ($line =~ /^\+/) {
+                       $n_cnt++;
+                       push @line, $line;
+                       next;
+               }
+
+               last if ($o1_ofs <= $ofs);
+
+               $o_cnt++;
+               $ofs++;
+               if ($line =~ /^ /) {
+                       $n_cnt++;
+               }
+               push @line, $line;
+       }
+
+       for ($i = 1; $i < @{$this->{TEXT}}; $i++) {
+               my $line = $this->{TEXT}[$i];
+               if ($line =~ /^\+/) {
+                       $n_cnt++;
+                       push @line, $line;
+                       next;
+               }
+               $ofs++;
+               $o_cnt++;
+               if ($line =~ /^ /) {
+                       $n_cnt++;
+               }
+               push @line, $line;
+       }
+       my $head = ("@@ -$o0_ofs" .
+                   (($o_cnt != 1) ? ",$o_cnt" : '') .
+                   " +$n0_ofs" .
+                   (($n_cnt != 1) ? ",$n_cnt" : '') .
+                   " @@\n");
+       @{$prev->{TEXT}} = ($head, @line);
+}
+
+sub coalesce_overlapping_hunks {
+       my (@in) = @_;
+       my @out = ();
+
+       my ($last_o_ctx, $last_was_dirty);
+
+       for (grep { $_->{USE} } @in) {
+               if ($_->{TYPE} ne 'hunk') {
+                       push @out, $_;
+                       next;
+               }
+               my $text = $_->{TEXT};
+               my ($o_ofs) = parse_hunk_header($text->[0]);
+               if (defined $last_o_ctx &&
+                   $o_ofs <= $last_o_ctx &&
+                   !$_->{DIRTY} &&
+                   !$last_was_dirty) {
+                       merge_hunk($out[-1], $_);
+               }
+               else {
+                       push @out, $_;
+               }
+               $last_o_ctx = find_last_o_ctx($out[-1]);
+               $last_was_dirty = $_->{DIRTY};
+       }
+       return @out;
+}
 
 sub color_diff {
        return map {
@@ -804,6 +898,10 @@ EOF
                || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
        system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
 
+       if ($? != 0) {
+               return undef;
+       }
+
        open $fh, '<', $hunkfile
                or die "failed to open hunk edit file for reading: " . $!;
        my @newtext = grep { !/^#/ } <$fh>;
@@ -874,7 +972,8 @@ sub edit_hunk_loop {
                my $newhunk = {
                        TEXT => $text,
                        TYPE => $hunk->[$ix]->{TYPE},
-                       USE => 1
+                       USE => 1,
+                       DIRTY => 1,
                };
                if (diff_applies($head,
                                 @{$hunk}[0..$ix-1],
@@ -1206,6 +1305,8 @@ sub patch_update_file {
                }
        }
 
+       @hunk = coalesce_overlapping_hunks(@hunk);
+
        my $n_lofs = 0;
        my @result = ();
        for (@hunk) {
index d3390755fc687a611e89320a7bbfb4ead512c863..6d1848b6cce89e4953a3ca6e1b2e6e1611277a4a 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -36,6 +36,13 @@ cd_to_toplevel
 git var GIT_COMMITTER_IDENT >/dev/null ||
        die "You need to set your committer info first"
 
+if git rev-parse --verify -q HEAD >/dev/null
+then
+       HAS_HEAD=yes
+else
+       HAS_HEAD=
+fi
+
 sq () {
        for sqarg
        do
@@ -290,16 +297,26 @@ else
                : >"$dotest/rebasing"
        else
                : >"$dotest/applying"
-               git update-ref ORIG_HEAD HEAD
+               if test -n "$HAS_HEAD"
+               then
+                       git update-ref ORIG_HEAD HEAD
+               else
+                       git update-ref -d ORIG_HEAD >/dev/null 2>&1
+               fi
        fi
 fi
 
 case "$resolved" in
 '')
-       files=$(git diff-index --cached --name-only HEAD --) || exit
+       case "$HAS_HEAD" in
+       '')
+               files=$(git ls-files) ;;
+       ?*)
+               files=$(git diff-index --cached --name-only HEAD --) ;;
+       esac || exit
        if test "$files"
        then
-               : >"$dotest/dirtyindex"
+               test -n "$HAS_HEAD" && : >"$dotest/dirtyindex"
                die "Dirty index: cannot apply patches (dirty: $files)"
        fi
 esac
@@ -541,18 +558,20 @@ do
        fi
 
        tree=$(git write-tree) &&
-       parent=$(git rev-parse --verify HEAD) &&
        commit=$(
                if test -n "$ignore_date"
                then
                        GIT_AUTHOR_DATE=
                fi
+               parent=$(git rev-parse --verify -q HEAD) ||
+               echo >&2 "applying to an empty history"
+
                if test -n "$committer_date_is_author_date"
                then
                        GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
                        export GIT_COMMITTER_DATE
                fi &&
-               git commit-tree $tree -p $parent <"$dotest/final-commit"
+               git commit-tree $tree ${parent:+-p} $parent <"$dotest/final-commit"
        ) &&
        git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
        stop_here $this
index 10ad340920efb7177df53cb3a209d1a3edd5a039..24712ff304af89317793fa4c54d39f4c579bb345 100755 (executable)
@@ -77,7 +77,7 @@ bisect_start() {
        then
                # Reset to the rev from where we started.
                start_head=$(cat "$GIT_DIR/BISECT_START")
-               git checkout "$start_head" || exit
+               git checkout "$start_head" -- || exit
        else
                # Get rev from where we start.
                case "$head" in
@@ -279,87 +279,14 @@ bisect_auto_next() {
        bisect_next_check && bisect_next || :
 }
 
-filter_skipped() {
-       _eval="$1"
-       _skip="$2"
-
-       if [ -z "$_skip" ]; then
-               eval "$_eval" | {
-                       while read line
-                       do
-                               echo "$line &&"
-                       done
-                       echo ':'
-               }
-               return
-       fi
-
-       # Let's parse the output of:
-       # "git rev-list --bisect-vars --bisect-all ..."
-       eval "$_eval" | {
-               VARS= FOUND= TRIED=
-               while read hash line
-               do
-                       case "$VARS,$FOUND,$TRIED,$hash" in
-                       1,*,*,*)
-                               # "bisect_foo=bar" read from rev-list output.
-                               echo "$hash &&"
-                               ;;
-                       ,*,*,---*)
-                               # Separator
-                               ;;
-                       ,,,bisect_rev*)
-                               # We had nothing to search.
-                               echo "bisect_rev= &&"
-                               VARS=1
-                               ;;
-                       ,,*,bisect_rev*)
-                               # We did not find a good bisect rev.
-                               # This should happen only if the "bad"
-                               # commit is also a "skip" commit.
-                               echo "bisect_rev='$TRIED' &&"
-                               VARS=1
-                               ;;
-                       ,,*,*)
-                               # We are searching.
-                               TRIED="${TRIED:+$TRIED|}$hash"
-                               case "$_skip" in
-                               *$hash*) ;;
-                               *)
-                                       echo "bisect_rev=$hash &&"
-                                       echo "bisect_tried='$TRIED' &&"
-                                       FOUND=1
-                                       ;;
-                               esac
-                               ;;
-                       ,1,*,bisect_rev*)
-                               # We have already found a rev to be tested.
-                               VARS=1
-                               ;;
-                       ,1,*,*)
-                               ;;
-                       *)
-                               # Unexpected input
-                               echo "die 'filter_skipped error'"
-                               die "filter_skipped error " \
-                                   "VARS: '$VARS' " \
-                                   "FOUND: '$FOUND' " \
-                                   "TRIED: '$TRIED' " \
-                                   "hash: '$hash' " \
-                                   "line: '$line'"
-                               ;;
-                       esac
-               done
-               echo ':'
-       }
-}
-
 exit_if_skipped_commits () {
        _tried=$1
-       if expr "$_tried" : ".*[|].*" > /dev/null ; then
+       _bad=$2
+       if test -n "$_tried" ; then
                echo "There are only 'skip'ped commit left to test."
                echo "The first bad commit could be any of:"
                echo "$_tried" | tr '[|]' '[\012]'
+               test -n "$_bad" && echo "$_bad"
                echo "We cannot bisect more!"
                exit 2
        fi
@@ -370,7 +297,7 @@ bisect_checkout() {
        _msg="$2"
        echo "Bisecting: $_msg"
        mark_expected_rev "$_rev"
-       git checkout -q "$_rev" || exit
+       git checkout -q "$_rev" -- || exit
        git show-branch "$_rev"
 }
 
@@ -490,29 +417,24 @@ bisect_next() {
        test "$?" -eq "1" && return
 
        # Get bisection information
-       BISECT_OPT=''
-       test -n "$skip" && BISECT_OPT='--bisect-all'
-       eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
-       eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
-       eval=$(filter_skipped "$eval" "$skip") &&
+       eval=$(eval "git bisect--helper --next-vars") &&
        eval "$eval" || exit
 
        if [ -z "$bisect_rev" ]; then
+               # We should exit here only if the "bad"
+               # commit is also a "skip" commit (see above).
+               exit_if_skipped_commits "$bisect_tried"
                echo "$bad was both good and bad"
                exit 1
        fi
        if [ "$bisect_rev" = "$bad" ]; then
-               exit_if_skipped_commits "$bisect_tried"
+               exit_if_skipped_commits "$bisect_tried" "$bad"
                echo "$bisect_rev is first bad commit"
                git diff-tree --pretty $bisect_rev
                exit 0
        fi
 
-       # We should exit here only if the "bad"
-       # commit is also a "skip" commit (see above).
-       exit_if_skipped_commits "$bisect_rev"
-
-       bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this"
+       bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)"
 }
 
 bisect_visualize() {
@@ -549,7 +471,7 @@ bisect_reset() {
        *)
            usage ;;
        esac
-       git checkout "$branch" && bisect_clean_state
+       git checkout "$branch" -- && bisect_clean_state
 }
 
 bisect_clean_state() {
index 878d83dd0863570042af80919899b4d6aa57e35b..c7cf2d5d9cdbfd4ed20c8b8ea49a36af7c138a4e 100644 (file)
@@ -46,6 +46,7 @@
 #define _ALL_SOURCE 1
 #define _GNU_SOURCE 1
 #define _BSD_SOURCE 1
+#define _NETBSD_SOURCE 1
 
 #include <unistd.h>
 #include <stdio.h>
@@ -166,7 +167,7 @@ static inline const char *skip_prefix(const char *str, const char *prefix)
        return strncmp(str, prefix, len) ? NULL : str + len;
 }
 
-#ifdef NO_MMAP
+#if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
 
 #ifndef PROT_READ
 #define PROT_READ 1
@@ -180,13 +181,19 @@ static inline const char *skip_prefix(const char *str, const char *prefix)
 extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
 extern int git_munmap(void *start, size_t length);
 
+#else /* NO_MMAP || USE_WIN32_MMAP */
+
+#include <sys/mman.h>
+
+#endif /* NO_MMAP || USE_WIN32_MMAP */
+
+#ifdef NO_MMAP
+
 /* This value must be multiple of (pagesize * 2) */
 #define DEFAULT_PACKED_GIT_WINDOW_SIZE (1 * 1024 * 1024)
 
 #else /* NO_MMAP */
 
-#include <sys/mman.h>
-
 /* This value must be multiple of (pagesize * 2) */
 #define DEFAULT_PACKED_GIT_WINDOW_SIZE \
        (sizeof(void*) >= 8 \
@@ -388,4 +395,30 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 # define FORCE_DIR_SET_GID 0
 #endif
 
+#ifdef NO_NSEC
+#undef USE_NSEC
+#define ST_CTIME_NSEC(st) 0
+#define ST_MTIME_NSEC(st) 0
+#else
+#ifdef USE_ST_TIMESPEC
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
+#else
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
+#endif
+#endif
+
+#ifdef UNRELIABLE_FSTAT
+#define fstat_is_reliable() 0
+#else
+#define fstat_is_reliable() 1
+#endif
+
+/*
+ * Preserves errno, prints a message, but gives no warning for ENOENT.
+ * Always returns the return value of unlink(2).
+ */
+int unlink_or_warn(const char *path);
+
 #endif
index ab6cea3e538047c30a5d728c7a16ee3c935c070b..6dc45f5d45a44c9bbe31987c1a10e4554f037d6c 100755 (executable)
@@ -285,7 +285,7 @@ sub req_Root
        return 0;
     }
 
-    my @gitvars = `git-config -l`;
+    my @gitvars = `git config -l`;
     if ($?) {
        print "E problems executing git-config on the server -- this is not a git repository or the PATH is not set correctly.\n";
         print "E \n";
@@ -702,7 +702,7 @@ sub req_Modified
     # Save the file data in $state
     $state->{entries}{$state->{directory}.$data}{modified_filename} = $filename;
     $state->{entries}{$state->{directory}.$data}{modified_mode} = $mode;
-    $state->{entries}{$state->{directory}.$data}{modified_hash} = `git-hash-object $filename`;
+    $state->{entries}{$state->{directory}.$data}{modified_hash} = `git hash-object $filename`;
     $state->{entries}{$state->{directory}.$data}{modified_hash} =~ s/\s.*$//s;
 
     #$log->debug("req_Modified : file=$data mode=$mode size=$size");
@@ -1289,7 +1289,7 @@ sub req_ci
 
        # do a checkout of the file if it is part of this tree
         if ($wrev) {
-            system('git-checkout-index', '-f', '-u', $filename);
+            system('git', 'checkout-index', '-f', '-u', $filename);
             unless ($? == 0) {
                 die "Error running git-checkout-index -f -u $filename : $!";
             }
@@ -1331,15 +1331,15 @@ sub req_ci
         {
             $log->info("Removing file '$filename'");
             unlink($filename);
-            system("git-update-index", "--remove", $filename);
+            system("git", "update-index", "--remove", $filename);
         }
         elsif ( $addflag )
         {
             $log->info("Adding file '$filename'");
-            system("git-update-index", "--add", $filename);
+            system("git", "update-index", "--add", $filename);
         } else {
             $log->info("Updating file '$filename'");
-            system("git-update-index", $filename);
+            system("git", "update-index", $filename);
         }
     }
 
@@ -1351,7 +1351,7 @@ sub req_ci
         return;
     }
 
-    my $treehash = `git-write-tree`;
+    my $treehash = `git write-tree`;
     chomp $treehash;
 
     $log->debug("Treehash : $treehash, Parenthash : $parenthash");
@@ -1368,7 +1368,7 @@ sub req_ci
     }
     close $msg_fh;
 
-    my $commithash = `git-commit-tree $treehash -p $parenthash < $msg_filename`;
+    my $commithash = `git commit-tree $treehash -p $parenthash < $msg_filename`;
     chomp($commithash);
     $log->info("Commit hash : $commithash");
 
@@ -1821,7 +1821,7 @@ sub req_annotate
        # TODO: if we got a revision from the client, use that instead
        # to look up the commithash in sqlite (still good to default to
        # the current head as we do now)
-       system("git-read-tree", $lastseenin);
+       system("git", "read-tree", $lastseenin);
        unless ($? == 0)
        {
            print "E error running git-read-tree $lastseenin $ENV{GIT_INDEX_FILE} $!\n";
@@ -1830,7 +1830,7 @@ sub req_annotate
        $log->info("Created index '$ENV{GIT_INDEX_FILE}' with commit $lastseenin - exit status $?");
 
         # do a checkout of the file
-        system('git-checkout-index', '-f', '-u', $filename);
+        system('git', 'checkout-index', '-f', '-u', $filename);
         unless ($? == 0) {
             print "E error running git-checkout-index -f -u $filename : $!\n";
             return;
@@ -1861,7 +1861,7 @@ sub req_annotate
         close ANNOTATEHINTS
             or (print "E failed to write $a_hints: $!\n"), return;
 
-        my @cmd = (qw(git-annotate -l -S), $a_hints, $filename);
+        my @cmd = (qw(git annotate -l -S), $a_hints, $filename);
         if (!open(ANNOTATE, "-|", @cmd)) {
             print "E error invoking ". join(' ',@cmd) .": $!\n";
             return;
@@ -2078,17 +2078,17 @@ sub transmitfile
 
     die "Need filehash" unless ( defined ( $filehash ) and $filehash =~ /^[a-zA-Z0-9]{40}$/ );
 
-    my $type = `git-cat-file -t $filehash`;
+    my $type = `git cat-file -t $filehash`;
     chomp $type;
 
     die ( "Invalid type '$type' (expected 'blob')" ) unless ( defined ( $type ) and $type eq "blob" );
 
-    my $size = `git-cat-file -s $filehash`;
+    my $size = `git cat-file -s $filehash`;
     chomp $size;
 
     $log->debug("transmitfile($filehash) size=$size, type=$type");
 
-    if ( open my $fh, '-|', "git-cat-file", "blob", $filehash )
+    if ( open my $fh, '-|', "git", "cat-file", "blob", $filehash )
     {
         if ( defined ( $options->{targetfile} ) )
         {
@@ -2935,7 +2935,7 @@ sub update
         push @git_log_params, $self->{module};
     }
     # git-rev-list is the backend / plumbing version of git-log
-    open(GITLOG, '-|', 'git-rev-list', @git_log_params) or die "Cannot call git-rev-list: $!";
+    open(GITLOG, '-|', 'git', 'rev-list', @git_log_params) or die "Cannot call git-rev-list: $!";
 
     my @commits;
 
@@ -3021,7 +3021,7 @@ sub update
                         next;
                     }
                    my $base = eval {
-                           safe_pipe_capture('git-merge-base',
+                           safe_pipe_capture('git', 'merge-base',
                                                 $lastpicked, $parent);
                    };
                    # The two branches may not be related at all,
@@ -3033,7 +3033,7 @@ sub update
                     if ($base) {
                         my @merged;
                         # print "want to log between  $base $parent \n";
-                        open(GITLOG, '-|', 'git-log', '--pretty=medium', "$base..$parent")
+                        open(GITLOG, '-|', 'git', 'log', '--pretty=medium', "$base..$parent")
                          or die "Cannot call git-log: $!";
                         my $mergedhash;
                         while (<GITLOG>) {
@@ -3075,7 +3075,7 @@ sub update
 
         if ( defined ( $lastpicked ) )
         {
-            my $filepipe = open(FILELIST, '-|', 'git-diff-tree', '-z', '-r', $lastpicked, $commit->{hash}) or die("Cannot call git-diff-tree : $!");
+            my $filepipe = open(FILELIST, '-|', 'git', 'diff-tree', '-z', '-r', $lastpicked, $commit->{hash}) or die("Cannot call git-diff-tree : $!");
            local ($/) = "\0";
             while ( <FILELIST> )
             {
@@ -3149,7 +3149,7 @@ sub update
             # this is used to detect files removed from the repo
             my $seen_files = {};
 
-            my $filepipe = open(FILELIST, '-|', 'git-ls-tree', '-z', '-r', $commit->{hash}) or die("Cannot call git-ls-tree : $!");
+            my $filepipe = open(FILELIST, '-|', 'git', 'ls-tree', '-z', '-r', $commit->{hash}) or die("Cannot call git-ls-tree : $!");
            local $/ = "\0";
             while ( <FILELIST> )
             {
@@ -3451,7 +3451,7 @@ sub commitmessage
         return $message;
     }
 
-    my @lines = safe_pipe_capture("git-cat-file", "commit", $commithash);
+    my @lines = safe_pipe_capture("git", "cat-file", "commit", $commithash);
     shift @lines while ( $lines[0] =~ /\S/ );
     $message = join("",@lines);
     $message .= " " if ( $message =~ /\n$/ );
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
new file mode 100755 (executable)
index 0000000..57e8e32
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+# git-difftool--helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
+# This script is typically launched by using the 'git difftool'
+# convenience command.
+#
+# Copyright (c) 2009 David Aguilar
+
+# Load common functions from git-mergetool--lib
+TOOL_MODE=diff
+. git-mergetool--lib
+
+# difftool.prompt controls the default prompt/no-prompt behavior
+# and is overridden with $GIT_DIFFTOOL*_PROMPT.
+should_prompt () {
+       prompt=$(git config --bool difftool.prompt || echo true)
+       if test "$prompt" = true; then
+               test -z "$GIT_DIFFTOOL_NO_PROMPT"
+       else
+               test -n "$GIT_DIFFTOOL_PROMPT"
+       fi
+}
+
+# Sets up shell variables and runs a merge tool
+launch_merge_tool () {
+       # Merged is the filename as it appears in the work tree
+       # Local is the contents of a/filename
+       # Remote is the contents of b/filename
+       # Custom merge tool commands might use $BASE so we provide it
+       MERGED="$1"
+       LOCAL="$2"
+       REMOTE="$3"
+       BASE="$1"
+
+       # $LOCAL and $REMOTE are temporary files so prompt
+       # the user with the real $MERGED name before launching $merge_tool.
+       if should_prompt; then
+               printf "\nViewing: '$MERGED'\n"
+               printf "Hit return to launch '%s': " "$merge_tool"
+               read ans
+       fi
+
+       # Run the appropriate merge tool command
+       run_merge_tool "$merge_tool"
+}
+
+# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values
+test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
+test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL"
+
+if test -z "$merge_tool"; then
+       merge_tool="$(get_merge_tool)" || exit
+fi
+
+# Launch the merge tool on each path provided by 'git diff'
+while test $# -gt 6
+do
+       launch_merge_tool "$1" "$2" "$5"
+       shift 7
+done
diff --git a/git-difftool.perl b/git-difftool.perl
new file mode 100755 (executable)
index 0000000..ba5e60a
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/env perl
+# Copyright (c) 2009 David Aguilar
+#
+# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
+# git-difftool--helper script.
+#
+# This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.
+# GIT_DIFFTOOL_NO_PROMPT, GIT_DIFFTOOL_PROMPT, and GIT_DIFF_TOOL
+# are exported for use by git-difftool--helper.
+#
+# Any arguments that are unknown to this script are forwarded to 'git diff'.
+
+use strict;
+use warnings;
+use Cwd qw(abs_path);
+use File::Basename qw(dirname);
+
+my $DIR = abs_path(dirname($0));
+
+
+sub usage
+{
+       print << 'USAGE';
+usage: git difftool [--tool=<tool>] [-y|--no-prompt] ["git diff" options]
+USAGE
+       exit 1;
+}
+
+sub setup_environment
+{
+       $ENV{PATH} = "$DIR:$ENV{PATH}";
+       $ENV{GIT_PAGER} = '';
+       $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
+}
+
+sub exe
+{
+       my $exe = shift;
+       if ($^O eq 'MSWin32' || $^O eq 'msys') {
+               return "$exe.exe";
+       }
+       return $exe;
+}
+
+sub generate_command
+{
+       my @command = (exe('git'), 'diff');
+       my $skip_next = 0;
+       my $idx = -1;
+       for my $arg (@ARGV) {
+               $idx++;
+               if ($skip_next) {
+                       $skip_next = 0;
+                       next;
+               }
+               if ($arg eq '-t' || $arg eq '--tool') {
+                       usage() if $#ARGV <= $idx;
+                       $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];
+                       $skip_next = 1;
+                       next;
+               }
+               if ($arg =~ /^--tool=/) {
+                       $ENV{GIT_DIFF_TOOL} = substr($arg, 7);
+                       next;
+               }
+               if ($arg eq '-y' || $arg eq '--no-prompt') {
+                       $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
+                       delete $ENV{GIT_DIFFTOOL_PROMPT};
+                       next;
+               }
+               if ($arg eq '--prompt') {
+                       $ENV{GIT_DIFFTOOL_PROMPT} = 'true';
+                       delete $ENV{GIT_DIFFTOOL_NO_PROMPT};
+                       next;
+               }
+               if ($arg eq '-h' || $arg eq '--help') {
+                       usage();
+               }
+               push @command, $arg;
+       }
+       return @command
+}
+
+setup_environment();
+
+# ActiveState Perl for Win32 does not implement POSIX semantics of
+# exec* system call. It just spawns the given executable and finishes
+# the starting program, exiting with code 0.
+# system will at least catch the errors returned by git diff,
+# allowing the caller of git difftool better handling of failures.
+my $rc = system(generate_command());
+exit($rc | ($rc >> 8));
index 9a09ba138244b1665de78e94ce12fb8fdb878758..37e044db40bb89470bb64a4108c86d5173e03b5a 100755 (executable)
@@ -232,7 +232,9 @@ while read sha1 type name
 do
        case "$force,$name" in
        ,$orig_namespace*)
-               die "Namespace $orig_namespace not empty"
+               die "Cannot create a new backup.
+A previous backup already exists in $orig_namespace
+Force overwriting the backup with -f"
        ;;
        t,$orig_namespace*)
                git update-ref -d "$name" $sha1
@@ -270,10 +272,10 @@ test $commits -eq 0 && die "Found nothing to rewrite"
 
 # Rewrite the commits
 
-i=0
+git_filter_branch__commit_count=0
 while read commit parents; do
-       i=$(($i+1))
-       printf "\rRewrite $commit ($i/$commits)"
+       git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1))
+       printf "\rRewrite $commit ($git_filter_branch__commit_count/$commits)"
 
        case "$filter_subdir" in
        "")
@@ -428,7 +430,7 @@ if [ "$filter_tag_name" ]; then
                if [ "$type" = "tag" ]; then
                        # Dereference to a commit
                        sha1t="$sha1"
-                       sha1="$(git rev-parse "$sha1"^{commit} 2>/dev/null)" || continue
+                       sha1="$(git rev-parse -q "$sha1"^{commit})" || continue
                fi
 
                [ -f "../map/$sha1" ] || continue
index 3ad8a21b30128ce21b6b6c28094d9af5e5866faf..b3580e9e48b6ac5d1a7fbd010f032445702f140f 100644 (file)
@@ -105,8 +105,11 @@ endif
 
 ifeq ($(uname_S),Darwin)
        TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app
-       ifeq ($(shell expr "$(uname_R)" : '9\.'),2)
-               TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish\ Shell.app
+       ifeq ($(shell echo "$(uname_R)" | awk -F. '{if ($$1 >= 9) print "y"}')_$(shell test -d $(TKFRAMEWORK) || echo n),y_n)
+               TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish.app
+               ifeq ($(shell test -d $(TKFRAMEWORK) || echo n),n)
+                       TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish\ Shell.app
+               endif
        endif
        TKEXECUTABLE = $(shell basename "$(TKFRAMEWORK)" .app)
 endif
index e018e076f8a1b927ac07f612cf097992643a5db7..14b92ba786554f4e714c89191e5584a825964ab2 100755 (executable)
@@ -122,6 +122,7 @@ unset oguimsg
 set _appname {Git Gui}
 set _gitdir {}
 set _gitexec {}
+set _githtmldir {}
 set _reponame {}
 set _iscygwin {}
 set _search_path {}
@@ -168,6 +169,28 @@ proc gitexec {args} {
        return [eval [list file join $_gitexec] $args]
 }
 
+proc githtmldir {args} {
+       global _githtmldir
+       if {$_githtmldir eq {}} {
+               if {[catch {set _githtmldir [git --html-path]}]} {
+                       # Git not installed or option not yet supported
+                       return {}
+               }
+               if {[is_Cygwin]} {
+                       set _githtmldir [exec cygpath \
+                               --windows \
+                               --absolute \
+                               $_githtmldir]
+               } else {
+                       set _githtmldir [file normalize $_githtmldir]
+               }
+       }
+       if {$args eq {}} {
+               return $_githtmldir
+       }
+       return [eval [list file join $_githtmldir] $args]
+}
+
 proc reponame {} {
        return $::_reponame
 }
@@ -640,10 +663,13 @@ font create font_diffbold
 font create font_diffitalic
 
 foreach class {Button Checkbutton Entry Label
-               Labelframe Listbox Menu Message
+               Labelframe Listbox Message
                Radiobutton Spinbox Text} {
        option add *$class.font font_ui
 }
+if {![is_MacOSX]} {
+       option add *Menu.font font_ui
+}
 unset class
 
 if {[is_Windows] || [is_MacOSX]} {
@@ -699,7 +725,7 @@ proc apply_config {} {
 
 set default_config(branch.autosetupmerge) true
 set default_config(merge.tool) {}
-set default_config(merge.keepbackup) true
+set default_config(mergetool.keepbackup) true
 set default_config(merge.diffstat) true
 set default_config(merge.summary) false
 set default_config(merge.verbosity) 2
@@ -769,9 +795,9 @@ if {![regsub {^git version } $_git_version {} _git_version]} {
 set _real_git_version $_git_version
 regsub -- {[\-\.]dirty$} $_git_version {} _git_version
 regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
-regsub {\.rc[0-9]+$} $_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
+regsub {\.[a-zA-Z]+\.?[0-9]+$} $_git_version {} _git_version
 
 if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
        catch {wm withdraw .}
@@ -1108,6 +1134,7 @@ set current_diff_path {}
 set is_3way_diff 0
 set is_conflict_diff 0
 set selected_commit_type new
+set diff_empty_count 0
 
 set nullid "0000000000000000000000000000000000000000"
 set nullid2 "0000000000000000000000000000000000000001"
@@ -1924,7 +1951,7 @@ proc do_explore {} {
                # freedesktop.org-conforming system is our best shot
                set explorer "xdg-open"
        }
-       eval exec $explorer [file dirname [gitdir]] &
+       eval exec $explorer [list [file nativename [file dirname [gitdir]]]] &
 }
 
 set is_quitting 0
@@ -2277,6 +2304,12 @@ set ui_comm {}
 # -- Menu Bar
 #
 menu .mbar -tearoff 0
+if {[is_MacOSX]} {
+       # -- Apple Menu (Mac OS X only)
+       #
+       .mbar add cascade -label Apple -menu .mbar.apple
+       menu .mbar.apple
+}
 .mbar add cascade -label [mc Repository] -menu .mbar.repository
 .mbar add cascade -label [mc Edit] -menu .mbar.edit
 if {[is_enabled branch]} {
@@ -2292,7 +2325,6 @@ if {[is_enabled transport]} {
 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
        .mbar add cascade -label [mc Tools] -menu .mbar.tools
 }
-. configure -menu .mbar
 
 # -- Repository Menu
 #
@@ -2545,19 +2577,7 @@ if {[is_enabled transport]} {
 }
 
 if {[is_MacOSX]} {
-       # -- Apple Menu (Mac OS X only)
-       #
-       .mbar add cascade -label Apple -menu .mbar.apple
-       menu .mbar.apple
-
-       .mbar.apple add command -label [mc "About %s" [appname]] \
-               -command do_about
-       .mbar.apple add separator
-       .mbar.apple add command \
-               -label [mc "Preferences..."] \
-               -command do_options \
-               -accelerator $M1T-,
-       bind . <$M1B-,> do_options
+       proc ::tk::mac::ShowPreferences {} {do_options}
 } else {
        # -- Edit Menu
        #
@@ -2585,17 +2605,23 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
 .mbar add cascade -label [mc Help] -menu .mbar.help
 menu .mbar.help
 
-if {![is_MacOSX]} {
+if {[is_MacOSX]} {
+       .mbar.apple add command -label [mc "About %s" [appname]] \
+               -command do_about
+       .mbar.apple add separator
+} else {
        .mbar.help add command -label [mc "About %s" [appname]] \
                -command do_about
 }
+. configure -menu .mbar
 
+set doc_path [githtmldir]
+if {$doc_path ne {}} {
+       set doc_path [file join $doc_path index.html]
 
-set doc_path [file dirname [gitexec]]
-set doc_path [file join $doc_path Documentation index.html]
-
-if {[is_Cygwin]} {
-       set doc_path [exec cygpath --mixed $doc_path]
+       if {[is_Cygwin]} {
+               set doc_path [exec cygpath --mixed $doc_path]
+       }
 }
 
 if {[file isfile $doc_path]} {
@@ -2944,7 +2970,7 @@ $ctxm add command \
        -command {tk_textPaste $ui_comm}
 $ctxm add command \
        -label [mc Delete] \
-       -command {$ui_comm delete sel.first sel.last}
+       -command {catch {$ui_comm delete sel.first sel.last}}
 $ctxm add separator
 $ctxm add command \
        -label [mc "Select All"] \
index ef1930b4911591566be4561b8c17c24e1cfbfaad..20d5e42307c70a976e56f922f787610cde5bac9f 100644 (file)
@@ -51,7 +51,7 @@ constructor dialog {} {
                $w.check \
                [mc "Delete Only If Merged Into"] \
                ]
-       $w_check none [mc "Always (Do not perform merge test.)"]
+       $w_check none [mc "Always (Do not perform merge checks)"]
        pack $w.check -anchor nw -fill x -pady 5 -padx 5
 
        foreach h [load_all_heads] {
@@ -112,7 +112,7 @@ method _delete {} {
        }
        if {$to_delete eq {}} return
        if {$check_cmt eq {}} {
-               set msg [mc "Recovering deleted branches is difficult. \n\n Delete the selected branches?"]
+               set msg [mc "Recovering deleted branches is difficult.\n\nDelete the selected branches?"]
                if {[tk_messageBox \
                        -icon warning \
                        -type yesno \
index caca88831b7066c573917542d24a52e832dcff34..9e7412c446f715588b9cfe21654a3447e8680e12 100644 (file)
@@ -9,6 +9,7 @@ field w_cons   {}; # embedded console window object
 field new_expr   ; # expression the user saw/thinks this is
 field new_hash   ; # commit SHA-1 we are switching to
 field new_ref    ; # ref we are updating/creating
+field old_hash   ; # commit SHA-1 that was checked out when we started
 
 field parent_w      .; # window that started us
 field merge_type none; # type of merge to apply to existing branch
@@ -280,11 +281,11 @@ method _start_checkout {} {
 
        # -- Our in memory state should match the repository.
        #
-       repository_state curType curHEAD curMERGE_HEAD
+       repository_state curType old_hash curMERGE_HEAD
        if {[string match amend* $commit_type]
                && $curType eq {normal}
-               && $curHEAD eq $HEAD} {
-       } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
+               && $old_hash eq $HEAD} {
+       } elseif {$commit_type ne $curType || $HEAD ne $old_hash} {
                info_popup [mc "Last scanned state does not match repository state.
 
 Another Git program has modified this repository since the last scan.  A rescan must be performed before the current branch can be changed.
@@ -297,7 +298,7 @@ The rescan will be automatically started now.
                return
        }
 
-       if {$curHEAD eq $new_hash} {
+       if {$old_hash eq $new_hash} {
                _after_readtree $this
        } elseif {[is_config_true gui.trustmtime]} {
                _readtree $this
@@ -453,13 +454,47 @@ method _after_readtree {} {
 If you wanted to be on a branch, create one now starting from 'This Detached Checkout'."]
        }
 
+       # -- Run the post-checkout hook.
+       #
+       set fd_ph [githook_read post-checkout $old_hash $new_hash 1]
+       if {$fd_ph ne {}} {
+               global pch_error
+               set pch_error {}
+               fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
+               fileevent $fd_ph readable [cb _postcheckout_wait $fd_ph]
+       } else {
+               _update_repo_state $this
+       }
+}
+
+method _postcheckout_wait {fd_ph} {
+       global pch_error
+
+       append pch_error [read $fd_ph]
+       fconfigure $fd_ph -blocking 1
+       if {[eof $fd_ph]} {
+               if {[catch {close $fd_ph}]} {
+                       hook_failed_popup post-checkout $pch_error 0
+               }
+               unset pch_error
+               _update_repo_state $this
+               return
+       }
+       fconfigure $fd_ph -blocking 0
+}
+
+method _update_repo_state {} {
        # -- Update our repository state.  If we were previously in
        #    amend mode we need to toss the current buffer and do a
        #    full rescan to update our file lists.  If we weren't in
        #    amend mode our file lists are accurate and we can avoid
        #    the rescan.
        #
+       global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
+       global ui_comm
+
        unlock_index
+       set name [_name $this]
        set selected_commit_type new
        if {[string match amend* $commit_type]} {
                $ui_comm delete 0.0 end
index f9ff62a3b22fcc481c88dc35268d06d2fc4a5795..633cc572bbd076ff957c0b8194b6645e899e53ad 100644 (file)
@@ -398,6 +398,8 @@ method _do_new {} {
        grid $w_body.where.l $w_body.where.t $w_body.where.b -sticky ew
        pack $w_body.where -fill x
 
+       grid columnconfigure $w_body.where 1 -weight 1
+
        trace add variable @local_path write [cb _write_local_path]
        bind $w_body.h <Destroy> [list trace remove variable @local_path write [cb _write_local_path]]
        update
@@ -964,7 +966,34 @@ method _readtree_wait {fd} {
                return
        }
 
-       set done 1
+       # -- Run the post-checkout hook.
+       #
+       set fd_ph [githook_read post-checkout [string repeat 0 40] \
+               [git rev-parse HEAD] 1]
+       if {$fd_ph ne {}} {
+               global pch_error
+               set pch_error {}
+               fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
+               fileevent $fd_ph readable [cb _postcheckout_wait $fd_ph]
+       } else {
+               set done 1
+       }
+}
+
+method _postcheckout_wait {fd_ph} {
+       global pch_error
+
+       append pch_error [read $fd_ph]
+       fconfigure $fd_ph -blocking 1
+       if {[eof $fd_ph]} {
+               if {[catch {close $fd_ph}]} {
+                       hook_failed_popup post-checkout $pch_error 0
+               }
+               unset pch_error
+               set done 1
+               return
+       }
+       fconfigure $fd_ph -blocking 0
 }
 
 ######################################################################
@@ -998,6 +1027,8 @@ method _do_open {} {
        grid $w_body.where.l $w_body.where.t $w_body.where.b -sticky ew
        pack $w_body.where -fill x
 
+       grid columnconfigure $w_body.where 1 -weight 1
+
        trace add variable @local_path write [cb _write_local_path]
        bind $w_body.h <Destroy> [list trace remove variable @local_path write [cb _write_local_path]]
        update
index 9cc84105952d02f0171be0e23f0a82373bae0545..7f459cd5647f690a5e48ed7c29fc0d75eef89d0c 100644 (file)
@@ -115,6 +115,23 @@ proc create_new_commit {} {
        rescan ui_ready
 }
 
+proc setup_commit_encoding {msg_wt {quiet 0}} {
+       global repo_config
+
+       if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
+               set enc utf-8
+       }
+       set use_enc [tcl_encoding $enc]
+       if {$use_enc ne {}} {
+               fconfigure $msg_wt -encoding $use_enc
+       } else {
+               if {!$quiet} {
+                       error_popup [mc "warning: Tcl does not support encoding '%s'." $enc]
+               }
+               fconfigure $msg_wt -encoding utf-8
+       }
+}
+
 proc commit_tree {} {
        global HEAD commit_type file_states ui_comm repo_config
        global pch_error
@@ -200,16 +217,7 @@ A good commit message has the following format:
        set msg_p [gitdir GITGUI_EDITMSG]
        set msg_wt [open $msg_p w]
        fconfigure $msg_wt -translation lf
-       if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
-               set enc utf-8
-       }
-       set use_enc [tcl_encoding $enc]
-       if {$use_enc ne {}} {
-               fconfigure $msg_wt -encoding $use_enc
-       } else {
-               error_popup [mc "warning: Tcl does not support encoding '%s'." $enc]
-               fconfigure $msg_wt -encoding utf-8
-       }
+       setup_commit_encoding $msg_wt
        puts $msg_wt $msg
        close $msg_wt
 
@@ -362,6 +370,7 @@ A rescan will be automatically started now.
                append reflogm " ($commit_type)"
        }
        set msg_fd [open $msg_p r]
+       setup_commit_encoding $msg_fd 1
        gets $msg_fd subject
        close $msg_fd
        append reflogm {: } $subject
@@ -398,8 +407,8 @@ A rescan will be automatically started now.
        #
        set fd_ph [githook_read post-commit]
        if {$fd_ph ne {}} {
-               upvar #0 pch_error$cmt_id pc_err
-               set pc_err {}
+               global pch_error
+               set pch_error {}
                fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
                fileevent $fd_ph readable \
                        [list commit_postcommit_wait $fd_ph $cmt_id]
@@ -461,7 +470,7 @@ A rescan will be automatically started now.
 }
 
 proc commit_postcommit_wait {fd_ph cmt_id} {
-       upvar #0 pch_error$cmt_id pch_error
+       global pch_error
 
        append pch_error [read $fd_ph]
        fconfigure $fd_ph -blocking 1
index bbbf15c875f437f486f652d204bcebf1e5f1a8e4..925b3f56c1c23cc5e0c2b270b9f7e160013c9009 100644 (file)
@@ -51,11 +51,16 @@ proc force_diff_encoding {enc} {
 
 proc handle_empty_diff {} {
        global current_diff_path file_states file_lists
+       global diff_empty_count
 
        set path $current_diff_path
        set s $file_states($path)
        if {[lindex $s 0] ne {_M}} return
 
+       # Prevent infinite rescan loops
+       incr diff_empty_count
+       if {$diff_empty_count > 1} return
+
        info_popup [mc "No differences detected.
 
 %s has no changes.
@@ -310,6 +315,7 @@ proc read_diff {fd cont_info} {
        global ui_diff diff_active
        global is_3way_diff is_conflict_diff current_diff_header
        global current_diff_queue
+       global diff_empty_count
 
        $ui_diff conf -state normal
        while {[gets $fd line] >= 0} {
@@ -415,7 +421,10 @@ proc read_diff {fd cont_info} {
 
                if {[$ui_diff index end] eq {2.0}} {
                        handle_empty_diff
+               } else {
+                       set diff_empty_count 0
                }
+
                set callback [lindex $cont_info 1]
                if {$callback ne {}} {
                        eval $callback
index eb2b4b56a4db4c20727432c7a71d5192d580ce9e..3fe90e697002baaa1c5fa8df4c3d3eae199b062d 100644 (file)
@@ -88,7 +88,7 @@ proc merge_load_stages {path cont} {
        set merge_stages(3) {}
        set merge_stages_buf {}
 
-       set merge_stages_fd [eval git_read ls-files -u -z -- $path]
+       set merge_stages_fd [eval git_read ls-files -u -z -- {$path}]
 
        fconfigure $merge_stages_fd -blocking 0 -translation binary -encoding binary
        fileevent $merge_stages_fd readable [list read_merge_stages $merge_stages_fd $cont]
@@ -382,7 +382,7 @@ proc merge_tool_finish {fd} {
                delete_temp_files $mtool_tmpfiles
                ui_status [mc "Merge tool failed."]
        } else {
-               if {[is_config_true merge.keepbackup]} {
+               if {[is_config_true mergetool.keepbackup]} {
                        file rename -force -- $backup "$mtool_target.orig"
                }
 
index 89eb0f70f289e48e2b875e2cd49eb026a266ca0e..4e02fc0d393ff9fe8d2983fec99a03db7f36273a 100644 (file)
@@ -213,9 +213,7 @@ method _delete {} {
                -type yesno \
                -title [wm title $w] \
                -parent $w \
-               -message [mc "Recovering deleted branches is difficult.
-
-Delete the selected branches?"]] ne yes} {
+               -message [mc "Recovering deleted branches is difficult.\n\nDelete the selected branches?"]] ne yes} {
                return
        }
 
index 38c3151b05c732d919943e44629bfc0a8c9fb617..2f20eb39c0e25d68e4e6b46fe3b14a46b84ae83e 100644 (file)
@@ -54,7 +54,7 @@ proc do_cygwin_shortcut {} {
                                        $argv0]
                                win32_create_lnk $fn [list \
                                        $sh -c \
-                                       "CHERE_INVOKING=1 source /etc/profile;[sq $me]" \
+                                       "CHERE_INVOKING=1 source /etc/profile;[sq $me] &" \
                                        ] \
                                        [file dirname [file normalize [gitdir]]]
                        } err]} {
index 6ae63b6c7c61cb8f773ba5b9367c948820a0873a..95e6e5553ea86482d0fe9be77b07e805e01e3393 100644 (file)
@@ -146,7 +146,7 @@ proc tools_complete {fullname w {ok 1}} {
        }
 
        if {$ok} {
-               set msg [mc "Tool completed succesfully: %s" $fullname]
+               set msg [mc "Tool completed successfully: %s" $fullname]
        } else {
                set msg [mc "Tool failed: %s" $fullname]
        }
index a6f730b4eb38365b5156b715e9bb2fd8772c672c..51abb50bb627d05d702de2d794c47b874c9667ca 100644 (file)
@@ -773,16 +773,6 @@ msgstr "Immer (ohne Zusammenführungstest)"
 msgid "The following branches are not completely merged into %s:"
 msgstr "Folgende Zweige sind noch nicht mit »%s« zusammengeführt:"
 
-#: lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult. \n"
-"\n"
-" Delete the selected branches?"
-msgstr ""
-"Gelöschte Zweige können nur mit größerem Aufwand wiederhergestellt werden.\n"
-"\n"
-"Gewählte Zweige jetzt löschen?"
-
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -2506,7 +2496,7 @@ msgstr "Starten: %s"
 
 #: lib/tools.tcl:149
 #, tcl-format
-msgid "Tool completed succesfully: %s"
+msgid "Tool completed successfully: %s"
 msgstr "Werkzeug erfolgreich abgeschlossen: %s"
 
 #: lib/tools.tcl:151
index 45773ab3d89f84757cf1f7dc0a7d0626ce88f1ec..a944ace6ced4c68b55b7b25023d546521a8ecd68 100644 (file)
@@ -62,7 +62,7 @@ msgstr ""
 "\n"
 "%s nécessite au moins Git 1.5.0.\n"
 "\n"
-"Peut'on considérer que '%s' est en version 1.5.0 ?\n"
+"Peut-on considérer que '%s' est en version 1.5.0 ?\n"
 
 #: git-gui.sh:1062
 msgid "Git directory not found:"
@@ -82,7 +82,7 @@ msgstr "Aucun répertoire de travail"
 
 #: git-gui.sh:1247 lib/checkout_op.tcl:305
 msgid "Refreshing file status..."
-msgstr "Rafraichissement du status des fichiers..."
+msgstr "Rafraîchissement du statut des fichiers..."
 
 #: git-gui.sh:1303
 msgid "Scanning for modified files ..."
@@ -163,7 +163,7 @@ msgstr "Dépôt"
 
 #: git-gui.sh:2281
 msgid "Edit"
-msgstr "Edition"
+msgstr "Édition"
 
 #: git-gui.sh:2283 lib/choose_rev.tcl:561
 msgid "Branch"
@@ -199,7 +199,7 @@ msgstr "Naviguer dans la branche..."
 
 #: git-gui.sh:2316
 msgid "Visualize Current Branch's History"
-msgstr "Visualiser historique branche courante"
+msgstr "Visualiser l'historique de la branche courante"
 
 #: git-gui.sh:2320
 msgid "Visualize All Branch History"
@@ -208,12 +208,12 @@ msgstr "Voir l'historique de toutes les branches"
 #: git-gui.sh:2327
 #, tcl-format
 msgid "Browse %s's Files"
-msgstr "Naviguer l'arborescence de %s"
+msgstr "Parcourir l'arborescence de %s"
 
 #: git-gui.sh:2329
 #, tcl-format
 msgid "Visualize %s's History"
-msgstr "Voir l'historique de la branche: %s"
+msgstr "Voir l'historique de la branche : %s"
 
 #: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
@@ -230,7 +230,7 @@ msgstr "Vérifier le dépôt"
 #: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
-msgstr "Créer icône sur bureau"
+msgstr "Créer une icône sur le bureau"
 
 #: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
@@ -320,7 +320,7 @@ msgstr "Désindexer"
 
 #: git-gui.sh:2484 lib/index.tcl:410
 msgid "Revert Changes"
-msgstr "Annuler les modifications (revert)"
+msgstr "Annuler les modifications"
 
 #: git-gui.sh:2491 git-gui.sh:3069
 msgid "Show Less Context"
@@ -382,7 +382,7 @@ msgstr "Documentation en ligne"
 
 #: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
 msgid "Show SSH Key"
-msgstr "Montrer clé SSH"
+msgstr "Montrer la clé SSH"
 
 #: git-gui.sh:2707
 #, tcl-format
@@ -445,7 +445,7 @@ msgstr "Fichier :"
 
 #: git-gui.sh:3078
 msgid "Refresh"
-msgstr "Rafraichir"
+msgstr "Rafraîchir"
 
 #: git-gui.sh:3099
 msgid "Decrease Font Size"
@@ -457,7 +457,7 @@ msgstr "Agrandir la police"
 
 #: git-gui.sh:3111 lib/blame.tcl:281
 msgid "Encoding"
-msgstr "Encodage"
+msgstr "Codage des caractères"
 
 #: git-gui.sh:3122
 msgid "Apply/Reverse Hunk"
@@ -469,7 +469,7 @@ msgstr "Appliquer/Inverser la ligne"
 
 #: git-gui.sh:3137
 msgid "Run Merge Tool"
-msgstr "Lancer outil de merge"
+msgstr "Lancer l'outil de fusion"
 
 #: git-gui.sh:3142
 msgid "Use Remote Version"
@@ -527,7 +527,7 @@ msgid ""
 "Tcl binary distributed by Cygwin."
 msgstr ""
 "\n"
-"Ceci est du à un problème connu avec\n"
+"Ceci est dû à un problème connu avec\n"
 "le binaire Tcl distribué par Cygwin."
 
 #: git-gui.sh:3336
@@ -630,11 +630,11 @@ msgstr "Fichier original :"
 
 #: lib/blame.tcl:1021
 msgid "Cannot find HEAD commit:"
-msgstr "Impossible de trouver le commit HEAD:"
+msgstr "Impossible de trouver le commit HEAD :"
 
 #: lib/blame.tcl:1076
 msgid "Cannot find parent commit:"
-msgstr "Impossible de trouver le commit parent:"
+msgstr "Impossible de trouver le commit parent :"
 
 #: lib/blame.tcl:1091
 msgid "Unable to display parent"
@@ -646,7 +646,7 @@ msgstr "Erreur lors du chargement des différences :"
 
 #: lib/blame.tcl:1232
 msgid "Originally By:"
-msgstr "A l'origine par :"
+msgstr "À l'origine par :"
 
 #: lib/blame.tcl:1238
 msgid "In File:"
@@ -691,11 +691,11 @@ msgstr "Détacher de la branche locale"
 
 #: lib/branch_create.tcl:22
 msgid "Create Branch"
-msgstr "Créer branche"
+msgstr "Créer une branche"
 
 #: lib/branch_create.tcl:27
 msgid "Create New Branch"
-msgstr "Créer nouvelle branche"
+msgstr "Créer une nouvelle branche"
 
 #: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
 msgid "Create"
@@ -719,7 +719,7 @@ msgstr "Révision initiale"
 
 #: lib/branch_create.tcl:72
 msgid "Update Existing Branch:"
-msgstr "Mettre à jour branche existante :"
+msgstr "Mettre à jour une branche existante :"
 
 #: lib/branch_create.tcl:75
 msgid "No"
@@ -727,7 +727,7 @@ msgstr "Non"
 
 #: lib/branch_create.tcl:80
 msgid "Fast Forward Only"
-msgstr "Mise-à-jour rectiligne seulement (fast-forward)"
+msgstr "Mise à jour rectiligne seulement (fast-forward)"
 
 #: lib/branch_create.tcl:85 lib/checkout_op.tcl:536
 msgid "Reset"
@@ -769,7 +769,7 @@ msgstr "Branches locales"
 
 #: lib/branch_delete.tcl:52
 msgid "Delete Only If Merged Into"
-msgstr "Supprimer seulement si fusionnée dans:"
+msgstr "Supprimer seulement si fusionnée dans :"
 
 #: lib/branch_delete.tcl:54
 msgid "Always (Do not perform merge test.)"
@@ -780,23 +780,13 @@ msgstr "Toujours (Ne pas faire de test de fusion.)"
 msgid "The following branches are not completely merged into %s:"
 msgstr "Les branches suivantes ne sont pas complètement fusionnées dans %s :"
 
-#: lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult. \n"
-"\n"
-" Delete the selected branches?"
-msgstr ""
-"Récupérer des branches supprimées est difficile.\n"
-"\n"
-"Supprimer les branches sélectionnées ?"
-
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
 "Failed to delete branches:\n"
 "%s"
 msgstr ""
-"La suppression des branches suivantes a échouée :\n"
+"La suppression des branches suivantes a échoué :\n"
 "%s"
 
 #: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22
@@ -902,11 +892,11 @@ msgstr "La stratégie de fusion '%s' n'est pas supportée."
 #: lib/checkout_op.tcl:261
 #, tcl-format
 msgid "Failed to update '%s'."
-msgstr "La mise à jour de '%s' a échouée."
+msgstr "La mise à jour de '%s' a échoué."
 
 #: lib/checkout_op.tcl:273
 msgid "Staging area (index) is already locked."
-msgstr "L'index (staging area) est déjà vérouillé"
+msgstr "L'index (staging area) est déjà verrouillé."
 
 #: lib/checkout_op.tcl:288
 msgid ""
@@ -918,7 +908,7 @@ msgid ""
 "The rescan will be automatically started now.\n"
 msgstr ""
 "L'état lors de la dernière synchronisation ne correspond plus à l'état du "
-"dépôt\n"
+"dépôt.\n"
 "\n"
 "Un autre programme Git a modifié ce dépôt depuis la dernière "
 "synchronisation. Une resynchronisation doit être effectuée avant de pouvoir "
@@ -956,9 +946,9 @@ msgid ""
 "If you wanted to be on a branch, create one now starting from 'This Detached "
 "Checkout'."
 msgstr ""
-"Vous n'êtes plus ur une branche locale.\n"
+"Vous n'êtes plus sur une branche locale.\n"
 "\n"
-"Si vous vouliez être sur une branche, créez en une maintenant en partant de "
+"Si vous vouliez être sur une branche, créez-en une maintenant en partant de "
 "'Cet emprunt détaché'."
 
 #: lib/checkout_op.tcl:468 lib/checkout_op.tcl:472
@@ -1000,7 +990,7 @@ msgstr ""
 "mis à jour avec succès, mais la mise à jour d'un fichier interne à Git a "
 "échouée.\n"
 "\n"
-"Cela n'aurait pas du se produire. %s va abandonner et se terminer."
+"Cela n'aurait pas dû se produire. %s va abandonner et se terminer."
 
 #: lib/choose_font.tcl:39
 msgid "Select"
@@ -1023,8 +1013,8 @@ msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
 msgstr ""
-"C'est un texte d'exemple.\n"
-"Si vous aimez ce texte, vous pouvez choisir cette police"
+"Ceci est un texte d'exemple.\n"
+"Si vous aimez ce texte, vous pouvez choisir cette police."
 
 #: lib/choose_repository.tcl:28
 msgid "Git Gui"
@@ -1040,7 +1030,7 @@ msgstr "Nouveau..."
 
 #: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
 msgid "Clone Existing Repository"
-msgstr "Cloner dépôt existant"
+msgstr "Cloner un dépôt existant"
 
 #: lib/choose_repository.tcl:106
 msgid "Clone..."
@@ -1048,7 +1038,7 @@ msgstr "Cloner..."
 
 #: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
 msgid "Open Existing Repository"
-msgstr "Ouvrir dépôt existant"
+msgstr "Ouvrir un dépôt existant"
 
 #: lib/choose_repository.tcl:119
 msgid "Open..."
@@ -1056,17 +1046,17 @@ msgstr "Ouvrir..."
 
 #: lib/choose_repository.tcl:132
 msgid "Recent Repositories"
-msgstr "Dépôt récemment utilisés"
+msgstr "Dépôts récemment utilisés"
 
 #: lib/choose_repository.tcl:138
 msgid "Open Recent Repository:"
-msgstr "Ouvrir dépôt récent :"
+msgstr "Ouvrir un dépôt récent :"
 
 #: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
 #: lib/choose_repository.tcl:316
 #, tcl-format
 msgid "Failed to create repository %s:"
-msgstr "La création du dépôt %s a échouée :"
+msgstr "La création du dépôt %s a échoué :"
 
 #: lib/choose_repository.tcl:387
 msgid "Directory:"
@@ -1093,11 +1083,11 @@ msgstr "Cloner"
 
 #: lib/choose_repository.tcl:473
 msgid "Source Location:"
-msgstr "Emplacement source:"
+msgstr "Emplacement source :"
 
 #: lib/choose_repository.tcl:484
 msgid "Target Directory:"
-msgstr "Répertoire cible:"
+msgstr "Répertoire cible :"
 
 #: lib/choose_repository.tcl:496
 msgid "Clone Type:"
@@ -1137,7 +1127,7 @@ msgstr "L'emplacement %s existe déjà."
 
 #: lib/choose_repository.tcl:622
 msgid "Failed to configure origin"
-msgstr "La configuration de l'origine a échouée."
+msgstr "La configuration de l'origine a échoué."
 
 #: lib/choose_repository.tcl:634
 msgid "Counting objects"
@@ -1242,7 +1232,7 @@ msgstr "fichiers"
 
 #: lib/choose_repository.tcl:962
 msgid "Initial file checkout failed."
-msgstr "Chargement initial du fichier échoué."
+msgstr "Le chargement initial du fichier a échoué."
 
 #: lib/choose_repository.tcl:978
 msgid "Open"
@@ -1284,7 +1274,7 @@ msgstr "Révision invalide : %s"
 
 #: lib/choose_rev.tcl:338
 msgid "No revision selected."
-msgstr "Pas de révision selectionnée."
+msgstr "Pas de révision sélectionnée."
 
 #: lib/choose_rev.tcl:346
 msgid "Revision expression is empty."
@@ -1292,7 +1282,7 @@ msgstr "L'expression de révision est vide."
 
 #: lib/choose_rev.tcl:531
 msgid "Updated"
-msgstr "Mise-à-jour:"
+msgstr "Mise à jour:"
 
 #: lib/choose_rev.tcl:559
 msgid "URL"
@@ -1320,8 +1310,8 @@ msgid ""
 msgstr ""
 "Impossible de corriger pendant une fusion.\n"
 "\n"
-"Vous êtes actuellement au milieu d'une fusion qui n'a pas été completement "
-"terminée. Vous ne pouvez pas corriger le commit précédant sauf si vous "
+"Vous êtes actuellement au milieu d'une fusion qui n'a pas été complètement "
+"terminée. Vous ne pouvez pas corriger le commit précédent sauf si vous "
 "abandonnez la fusion courante.\n"
 
 #: lib/commit.tcl:49
@@ -1409,7 +1399,7 @@ msgstr ""
 #: lib/commit.tcl:211
 #, tcl-format
 msgid "warning: Tcl does not support encoding '%s'."
-msgstr "attention : Tcl ne supporte pas l'encodage '%s'."
+msgstr "attention : Tcl ne supporte pas lcodage '%s'."
 
 #: lib/commit.tcl:227
 msgid "Calling pre-commit hook..."
@@ -1469,12 +1459,12 @@ msgstr "commit-tree a échoué :"
 
 #: lib/commit.tcl:373
 msgid "update-ref failed:"
-msgstr "update-ref a échoué"
+msgstr "update-ref a échoué :"
 
 #: lib/commit.tcl:461
 #, tcl-format
 msgid "Created commit %s: %s"
-msgstr "Commit créé %s : %s"
+msgstr "Commit %s créé : %s"
 
 #: lib/console.tcl:59
 msgid "Working... please wait..."
@@ -1581,24 +1571,24 @@ msgid ""
 "LOCAL: deleted\n"
 "REMOTE:\n"
 msgstr ""
-"LOCAL: supprimé\n"
-"DISTANT:\n"
+"LOCAL : supprimé\n"
+"DISTANT :\n"
 
 #: lib/diff.tcl:125
 msgid ""
 "REMOTE: deleted\n"
 "LOCAL:\n"
 msgstr ""
-"DISTANT: supprimé\n"
-"LOCAL:\n"
+"DISTANT : supprimé\n"
+"LOCAL :\n"
 
 #: lib/diff.tcl:132
 msgid "LOCAL:\n"
-msgstr "LOCAL:\n"
+msgstr "LOCAL :\n"
 
 #: lib/diff.tcl:135
 msgid "REMOTE:\n"
-msgstr "DISTANT:\n"
+msgstr "DISTANT :\n"
 
 #: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
@@ -1624,7 +1614,7 @@ msgid ""
 "* Showing only first %d bytes.\n"
 msgstr ""
 "* Le fichier non suivi fait %d octets.\n"
-"* On montre seulement les premiers %d octets.\n"
+"* Seuls les %d premiers octets sont montrés.\n"
 
 #: lib/diff.tcl:228
 #, tcl-format
@@ -1635,7 +1625,7 @@ msgid ""
 msgstr ""
 "\n"
 "* Fichier suivi raccourcis ici de %s.\n"
-"* Pour voir le fichier entier, utiliser un éditeur externe.\n"
+"* Pour voir le fichier entier, utilisez un éditeur externe.\n"
 
 #: lib/diff.tcl:436
 msgid "Failed to unstage selected hunk."
@@ -1680,7 +1670,7 @@ msgstr "Vous devez corriger les erreurs suivantes avant de pouvoir commiter."
 
 #: lib/index.tcl:6
 msgid "Unable to unlock the index."
-msgstr "Impossible de dévérouiller l'index."
+msgstr "Impossible de déverrouiller l'index."
 
 #: lib/index.tcl:15
 msgid "Index Error"
@@ -1700,12 +1690,12 @@ msgstr "Continuer"
 
 #: lib/index.tcl:31
 msgid "Unlock Index"
-msgstr "Déverouiller l'index"
+msgstr "Déverrouiller l'index"
 
 #: lib/index.tcl:287
 #, tcl-format
 msgid "Unstaging %s from commit"
-msgstr "Désindexation de: %s"
+msgstr "Désindexation de : %s"
 
 #: lib/index.tcl:326
 msgid "Ready to commit."
@@ -1804,11 +1794,11 @@ msgid ""
 msgstr ""
 "Vous êtes au milieu d'une modification.\n"
 "\n"
-"Le fichier %s est modifié.\n"
+"Le fichier %s a été modifié.\n"
 "\n"
 "Vous devriez terminer le commit courant avant de lancer une fusion. En "
 "faisait comme cela, vous éviterez de devoir éventuellement abandonner une "
-"fusion ayant échouée.\n"
+"fusion ayant échoué.\n"
 
 #: lib/merge.tcl:107
 #, tcl-format
@@ -1826,7 +1816,7 @@ msgstr "La fusion s'est faite avec succès."
 
 #: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
-msgstr "La fusion a echouée. Il est nécessaire de résoudre les conflicts."
+msgstr "La fusion a echoué. Il est nécessaire de résoudre les conflits."
 
 #: lib/merge.tcl:158
 #, tcl-format
@@ -1914,16 +1904,16 @@ msgid ""
 "\n"
 "This operation can be undone only by restarting the merge."
 msgstr ""
-"Noter que le diff ne montre que les modifications en conflict.\n"
+"Noter que le diff ne montre que les modifications en conflit.\n"
 "\n"
 "%s sera écrasé.\n"
 "\n"
-"Cette opération ne peut être défaite qu'en relançant la fusion."
+"Cette opération ne peut être inversée qu'en relançant la fusion."
 
 #: lib/mergetool.tcl:45
 #, tcl-format
 msgid "File %s seems to have unresolved conflicts, still stage?"
-msgstr "Le fichier %s semble avoir des conflicts non résolus, indéxer quand même ?"
+msgstr "Le fichier %s semble avoir des conflits non résolus, indexer quand même ?"
 
 #: lib/mergetool.tcl:60
 #, tcl-format
@@ -1932,11 +1922,11 @@ msgstr "Ajouter une résolution pour %s"
 
 #: lib/mergetool.tcl:141
 msgid "Cannot resolve deletion or link conflicts using a tool"
-msgstr "Impossible de résoudre la suppression ou de relier des conflicts en utilisant un outil"
+msgstr "Impossible de résoudre la suppression ou de relier des conflits en utilisant un outil"
 
 #: lib/mergetool.tcl:146
 msgid "Conflict file does not exist"
-msgstr "Le fichier en conflict n'existe pas."
+msgstr "Le fichier en conflit n'existe pas."
 
 #: lib/mergetool.tcl:264
 #, tcl-format
@@ -1958,7 +1948,7 @@ msgid ""
 "Error retrieving versions:\n"
 "%s"
 msgstr ""
-"Erreur lors de la récupération des versions:\n"
+"Erreur lors de la récupération des versions :\n"
 "%s"
 
 #: lib/mergetool.tcl:343
@@ -1968,7 +1958,7 @@ msgid ""
 "\n"
 "%s"
 msgstr ""
-"Impossible de lancer l'outil de fusion:\n"
+"Impossible de lancer l'outil de fusion :\n"
 "\n"
 "%s"
 
@@ -1983,12 +1973,12 @@ msgstr "L'outil de fusion a échoué."
 #: lib/option.tcl:11
 #, tcl-format
 msgid "Invalid global encoding '%s'"
-msgstr "Encodage global invalide '%s'"
+msgstr "Codage global '%s' invalide"
 
 #: lib/option.tcl:19
 #, tcl-format
 msgid "Invalid repo encoding '%s'"
-msgstr "Encodage de dépôt invalide '%s'"
+msgstr "Codage de dépôt '%s' invalide"
 
 #: lib/option.tcl:117
 msgid "Restore Defaults"
@@ -2001,7 +1991,7 @@ msgstr "Sauvegarder"
 #: lib/option.tcl:131
 #, tcl-format
 msgid "%s Repository"
-msgstr "Dépôt: %s"
+msgstr "Dépôt : %s"
 
 #: lib/option.tcl:132
 msgid "Global (All Repositories)"
@@ -2069,7 +2059,7 @@ msgstr "Nouveau modèle de nom de branche"
 
 #: lib/option.tcl:155
 msgid "Default File Contents Encoding"
-msgstr "Encodage du contenu des fichiers par défaut"
+msgstr "Codage du contenu des fichiers par défaut"
 
 #: lib/option.tcl:203
 msgid "Change"
@@ -2098,11 +2088,11 @@ msgstr "Préférences"
 
 #: lib/option.tcl:314
 msgid "Failed to completely save options:"
-msgstr "La sauvegarde complète des options a échouée :"
+msgstr "La sauvegarde complète des options a échoué :"
 
 #: lib/remote.tcl:163
 msgid "Remove Remote"
-msgstr "Supprimer dépôt distant"
+msgstr "Supprimer un dépôt distant"
 
 #: lib/remote.tcl:168
 msgid "Prune from"
@@ -2118,11 +2108,11 @@ msgstr "Pousser vers"
 
 #: lib/remote_add.tcl:19
 msgid "Add Remote"
-msgstr "Ajouter dépôt distant"
+msgstr "Ajouter un dépôt distant"
 
 #: lib/remote_add.tcl:24
 msgid "Add New Remote"
-msgstr "Ajouter nouveau dépôt distant"
+msgstr "Ajouter un nouveau dépôt distant"
 
 #: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
 msgid "Add"
@@ -2134,7 +2124,7 @@ msgstr "Détails des dépôts distants"
 
 #: lib/remote_add.tcl:50
 msgid "Location:"
-msgstr "Emplacement:"
+msgstr "Emplacement :"
 
 #: lib/remote_add.tcl:62
 msgid "Further Action"
@@ -2146,7 +2136,7 @@ msgstr "Récupérer immédiatement"
 
 #: lib/remote_add.tcl:71
 msgid "Initialize Remote Repository and Push"
-msgstr "Initialiser dépôt distant et pousser"
+msgstr "Initialiser un dépôt distant et pousser"
 
 #: lib/remote_add.tcl:77
 msgid "Do Nothing Else Now"
@@ -2193,7 +2183,7 @@ msgstr "Mise en place de %s (à %s)"
 
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
 msgid "Delete Branch Remotely"
-msgstr "Supprimer branche à distance"
+msgstr "Supprimer une branche à distance"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
@@ -2244,8 +2234,8 @@ msgid ""
 "One or more of the merge tests failed because you have not fetched the "
 "necessary commits.  Try fetching from %s first."
 msgstr ""
-"Une ou plusieurs des tests de fusion ont échoués parce que vous n'avez pas "
-"récupéré les commits nécessaires. Essayez de récupéré à partir de %s d'abord."
+"Un ou plusieurs des tests de fusion ont échoué parce que vous n'avez pas "
+"récupéré les commits nécessaires. Essayez de récupérer à partir de %s d'abord."
 
 #: lib/remote_branch_delete.tcl:207
 msgid "Please select one or more branches to delete."
@@ -2257,14 +2247,14 @@ msgid ""
 "\n"
 "Delete the selected branches?"
 msgstr ""
-"Récupérer des branches supprimées est difficile.\n"
+"Il est difficile de récupérer des branches supprimées.\n"
 "\n"
-"Souhaitez vous supprimer les branches sélectionnées ?"
+"Supprimer les branches sélectionnées ?"
 
 #: lib/remote_branch_delete.tcl:226
 #, tcl-format
 msgid "Deleting branches from %s"
-msgstr "Supprimer les branches de %s"
+msgstr "Suppression des branches de %s"
 
 #: lib/remote_branch_delete.tcl:286
 msgid "No repository selected."
@@ -2285,7 +2275,7 @@ msgstr "Suivant"
 
 #: lib/search.tcl:24
 msgid "Prev"
-msgstr "Précédant"
+msgstr "Précédent"
 
 #: lib/search.tcl:25
 msgid "Case-Sensitive"
@@ -2293,7 +2283,7 @@ msgstr "Sensible à la casse"
 
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
-msgstr "Impossible d'écrire le raccourcis :"
+msgstr "Impossible d'écrire le raccourci :"
 
 #: lib/shortcut.tcl:136
 msgid "Cannot write icon:"
@@ -2318,7 +2308,7 @@ msgstr "Réinitialisation du dictionnaire à %s."
 
 #: lib/spellcheck.tcl:73
 msgid "Spell checker silently failed on startup"
-msgstr "La vérification d'orthographe a échouée silentieusement au démarrage"
+msgstr "La vérification d'orthographe a échoué silencieusement au démarrage"
 
 #: lib/spellcheck.tcl:80
 msgid "Unrecognized spell checker"
@@ -2351,11 +2341,11 @@ msgstr "Générer une clé"
 
 #: lib/sshkey.tcl:56
 msgid "Copy To Clipboard"
-msgstr "Copier dans le presse papier"
+msgstr "Copier dans le presse-papier"
 
 #: lib/sshkey.tcl:70
 msgid "Your OpenSSH Public Key"
-msgstr "Votre clé publique Open SSH"
+msgstr "Votre clé publique OpenSSH"
 
 #: lib/sshkey.tcl:78
 msgid "Generating..."
@@ -2368,7 +2358,7 @@ msgid ""
 "\n"
 "%s"
 msgstr ""
-"Impossible de lancer ssh-keygen:\n"
+"Impossible de lancer ssh-keygen :\n"
 "\n"
 "%s"
 
@@ -2398,7 +2388,7 @@ msgstr "Lancer %s nécessite qu'un fichier soit sélectionné."
 #: lib/tools.tcl:90
 #, tcl-format
 msgid "Are you sure you want to run %s?"
-msgstr "Êtes vous sûr de vouloir lancer %s ?"
+msgstr "Êtes-vous sûr de vouloir lancer %s ?"
 
 #: lib/tools.tcl:110
 #, tcl-format
@@ -2412,7 +2402,7 @@ msgstr "Lancement de : %s"
 
 #: lib/tools.tcl:149
 #, tcl-format
-msgid "Tool completed succesfully: %s"
+msgid "Tool completed successfully: %s"
 msgstr "L'outil a terminé avec succès : %s"
 
 #: lib/tools.tcl:151
@@ -2422,11 +2412,11 @@ msgstr "L'outil a échoué : %s"
 
 #: lib/tools_dlg.tcl:22
 msgid "Add Tool"
-msgstr "Ajouter outil"
+msgstr "Ajouter un outil"
 
 #: lib/tools_dlg.tcl:28
 msgid "Add New Tool Command"
-msgstr "Ajouter nouvelle commande d'outil"
+msgstr "Ajouter une nouvelle commande d'outil"
 
 #: lib/tools_dlg.tcl:33
 msgid "Add globally"
@@ -2438,7 +2428,7 @@ msgstr "Détails sur l'outil"
 
 #: lib/tools_dlg.tcl:48
 msgid "Use '/' separators to create a submenu tree:"
-msgstr "Utiliser les séparateurs '/' pour créer un arbre de sous menus :"
+msgstr "Utiliser les séparateurs '/' pour créer un arbre de sous-menus :"
 
 #: lib/tools_dlg.tcl:61
 msgid "Command:"
@@ -2462,7 +2452,7 @@ msgstr "Ne pas montrer la fenêtre de sortie des commandes"
 
 #: lib/tools_dlg.tcl:97
 msgid "Run only if a diff is selected ($FILENAME not empty)"
-msgstr "Lancer seulement si un diff est selectionné ($FILENAME non vide)"
+msgstr "Lancer seulement si un diff est sélectionné ($FILENAME non vide)"
 
 #: lib/tools_dlg.tcl:121
 msgid "Please supply a name for the tool."
@@ -2479,7 +2469,7 @@ msgid ""
 "Could not add tool:\n"
 "%s"
 msgstr ""
-"Impossible d'ajouter l'outil:\n"
+"Impossible d'ajouter l'outil :\n"
 "%s"
 
 #: lib/tools_dlg.tcl:190
index 15aea0dc64fd9711bf7246d347ceaafc773d874b..53b7d3634d842a43f8043e3f825d4232e17588aa 100644 (file)
@@ -753,13 +753,6 @@ msgstr ""
 msgid "The following branches are not completely merged into %s:"
 msgstr ""
 
-#: lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult. \n"
-"\n"
-" Delete the selected branches?"
-msgstr ""
-
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -2220,7 +2213,7 @@ msgstr ""
 
 #: lib/tools.tcl:149
 #, tcl-format
-msgid "Tool completed succesfully: %s"
+msgid "Tool completed successfully: %s"
 msgstr ""
 
 #: lib/tools.tcl:151
index f761b6415298809897f4f1cccdd0ea325ccbb1dc..0f87bc1cbeedca8d9040777a5484500d1071c2e6 100644 (file)
@@ -776,16 +776,6 @@ msgstr "Mindig (Ne legyen merge teszt.)"
 msgid "The following branches are not completely merged into %s:"
 msgstr "A következő branchek nem teljesen lettek merge-ölve ebbe: %s:"
 
-#: lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult. \n"
-"\n"
-" Delete the selected branches?"
-msgstr ""
-"A törölt branchek visszaállítása bonyolult. \n"
-"\n"
-" Biztosan törli a kiválasztott brancheket?"
-
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -2399,7 +2389,7 @@ msgstr "Futtatás: %s..."
 
 #: lib/tools.tcl:149
 #, tcl-format
-msgid "Tool completed succesfully: %s"
+msgid "Tool completed successfully: %s"
 msgstr "Az eszköz sikeresen befejeződött: %s"
 
 #: lib/tools.tcl:151
index 294e5958874f41c52071313cd107124b38c710a3..762632c22f9e5567bf4603e2958401e6156f1872 100644 (file)
@@ -778,16 +778,6 @@ msgstr "Sempre (Non effettuare verifiche di fusione)."
 msgid "The following branches are not completely merged into %s:"
 msgstr "I rami seguenti non sono stati fusi completamente in %s:"
 
-#: lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult. \n"
-"\n"
-" Delete the selected branches?"
-msgstr ""
-"Ricomporre rami cancellati può essere complicato. \n"
-"\n"
-" Eliminare i rami selezionati?"
-
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -2418,7 +2408,7 @@ msgstr "Eseguo: %s"
 
 #: lib/tools.tcl:149
 #, tcl-format
-msgid "Tool completed succesfully: %s"
+msgid "Tool completed successfully: %s"
 msgstr "Il programma esterno è terminato con successo: %s"
 
 #: lib/tools.tcl:151
index 09d60bef74990e43a9515437d9b4de53e5df0b98..63c4695103a764acbf116df1946fd0bc311c47f0 100644 (file)
@@ -773,16 +773,6 @@ msgstr "無条件(マージテストしない)"
 msgid "The following branches are not completely merged into %s:"
 msgstr "以下のブランチは %s に完全にマージされていません:"
 
-#: lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult. \n"
-"\n"
-" Delete the selected branches?"
-msgstr ""
-"ブランチを削除すると元に戻すのは困難です。 \n"
-"\n"
-" 選択したブランチを削除しますか?"
-
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -2382,7 +2372,7 @@ msgstr "実行中: %s"
 
 #: lib/tools.tcl:149
 #, tcl-format
-msgid "Tool completed succesfully: %s"
+msgid "Tool completed successfully: %s"
 msgstr "ツールが完了しました: %s"
 
 #: lib/tools.tcl:151
index 1c5137d84ca8797238dbcf1cbc6fd80218fbca5f..6de93c28c2e2b7cb0413521c987161c4a4fcb59d 100644 (file)
@@ -761,16 +761,6 @@ msgstr "Alltid (Ikke utfør sammenslåingstest.)"
 msgid "The following branches are not completely merged into %s:"
 msgstr "Følgende grener er ikke fullstendig slått sammen med %s:"
 
-#: lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult. \n"
-"\n"
-" Delete the selected branches?"
-msgstr ""
-"Gjenoppretting av fjernede grener er vanskelig. \n"
-"\n"
-" Fjern valgte grener?"
-
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -2331,7 +2321,7 @@ msgstr "Kjører: %s"
 
 #: lib/tools.tcl:149
 #, tcl-format
-msgid "Tool completed succesfully: %s"
+msgid "Tool completed successfully: %s"
 msgstr "Verktøyet ble fullført med suksess: %s"
 
 #: lib/tools.tcl:151
index db55b3e0a69813cba16932212ee1b2ce0f5b2b9a..0ffc4a418fccb4453c1601ca87668e2d185e14db 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-03-14 07:18+0100\n"
+"POT-Creation-Date: 2008-12-08 08:31-0800\n"
 "PO-Revision-Date: 2007-10-22 22:30-0200\n"
 "Last-Translator: Alex Riesen <raa.lkml@gmail.com>\n"
 "Language-Team: Russian Translation <git@vger.kernel.org>\n"
@@ -15,33 +15,33 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
-#: git-gui.sh:763
+#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
+#: git-gui.sh:866
 msgid "git-gui: fatal error"
 msgstr "git-gui: критическая ошибка"
 
-#: git-gui.sh:593
+#: git-gui.sh:689
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "В %s установлен неверный шрифт:"
 
-#: git-gui.sh:620
+#: git-gui.sh:723
 msgid "Main Font"
 msgstr "Шрифт интерфейса"
 
-#: git-gui.sh:621
+#: git-gui.sh:724
 msgid "Diff/Console Font"
 msgstr "Шрифт консоли и изменений (diff)"
 
-#: git-gui.sh:635
+#: git-gui.sh:738
 msgid "Cannot find git in PATH."
 msgstr "git не найден в PATH."
 
-#: git-gui.sh:662
+#: git-gui.sh:765
 msgid "Cannot parse Git version string:"
 msgstr "Невозможно распознать строку версии Git: "
 
-#: git-gui.sh:680
+#: git-gui.sh:783
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -53,384 +53,451 @@ msgid ""
 "Assume '%s' is version 1.5.0?\n"
 msgstr ""
 "Невозможно определить версию Git\n"
+"\n"
 "%s указывает на версию '%s'.\n"
 "\n"
 "для %s требуется версия Git, начиная с 1.5.0\n"
 "\n"
 "Принять '%s' как версию 1.5.0?\n"
 
-#: git-gui.sh:918
+#: git-gui.sh:1062
 msgid "Git directory not found:"
 msgstr "Каталог Git не найден:"
 
-#: git-gui.sh:925
+#: git-gui.sh:1069
 msgid "Cannot move to top of working directory:"
 msgstr "Невозможно перейти к корню рабочего каталога репозитория: "
 
-#: git-gui.sh:932
+#: git-gui.sh:1076
 msgid "Cannot use funny .git directory:"
-msgstr "Каталог.git испорчен: "
+msgstr "Каталог .git испорчен: "
 
-#: git-gui.sh:937
+#: git-gui.sh:1081
 msgid "No working directory"
 msgstr "Отсутствует рабочий каталог"
 
-#: git-gui.sh:1084 lib/checkout_op.tcl:283
+#: git-gui.sh:1247 lib/checkout_op.tcl:305
 msgid "Refreshing file status..."
 msgstr "Обновление информации о состоянии файлов..."
 
-#: git-gui.sh:1149
+#: git-gui.sh:1303
 msgid "Scanning for modified files ..."
 msgstr "Поиск измененных файлов..."
 
-#: git-gui.sh:1324 lib/browser.tcl:246
+#: git-gui.sh:1367
+msgid "Calling prepare-commit-msg hook..."
+msgstr "Вызов программы поддержки репозитория prepare-commit-msg..."
+
+#: git-gui.sh:1384
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr "Сохранение прервано программой поддержки репозитория prepare-commit-msg"
+
+#: git-gui.sh:1542 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Готово."
 
-#: git-gui.sh:1590
+#: git-gui.sh:1819
 msgid "Unmodified"
 msgstr "Не изменено"
 
-#: git-gui.sh:1592
+#: git-gui.sh:1821
 msgid "Modified, not staged"
 msgstr "Изменено, не подготовлено"
 
-#: git-gui.sh:1593 git-gui.sh:1598
+#: git-gui.sh:1822 git-gui.sh:1830
 msgid "Staged for commit"
 msgstr "Подготовлено для сохранения"
 
-#: git-gui.sh:1594 git-gui.sh:1599
+#: git-gui.sh:1823 git-gui.sh:1831
 msgid "Portions staged for commit"
 msgstr "Части, подготовленные для сохранения"
 
-#: git-gui.sh:1595 git-gui.sh:1600
+#: git-gui.sh:1824 git-gui.sh:1832
 msgid "Staged for commit, missing"
 msgstr "Подготовлено для сохранения, отсутствует"
 
-#: git-gui.sh:1597
+#: git-gui.sh:1826
+msgid "File type changed, not staged"
+msgstr "Тип файла изменён, не подготовлено"
+
+#: git-gui.sh:1827
+msgid "File type changed, staged"
+msgstr "Тип файла изменён, подготовлено"
+
+#: git-gui.sh:1829
 msgid "Untracked, not staged"
 msgstr "Не отслеживается, не подготовлено"
 
-#: git-gui.sh:1602
+#: git-gui.sh:1834
 msgid "Missing"
 msgstr "Отсутствует"
 
-#: git-gui.sh:1603
+#: git-gui.sh:1835
 msgid "Staged for removal"
 msgstr "Подготовлено для удаления"
 
-#: git-gui.sh:1604
+#: git-gui.sh:1836
 msgid "Staged for removal, still present"
 msgstr "Подготовлено для удаления, еще не удалено"
 
-#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
+#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
+#: git-gui.sh:1842 git-gui.sh:1843
 msgid "Requires merge resolution"
-msgstr "Требуется разрешение конфликта при объединении"
+msgstr "Требуется разрешение конфликта при слиянии"
 
-#: git-gui.sh:1644
+#: git-gui.sh:1878
 msgid "Starting gitk... please wait..."
-msgstr "Ð\97апÑ\83Ñ\81каеÑ\82Ñ\81Ñ\8f gitk... Ð¿Ð¾Ð¶Ð°Ð»Ñ\83йÑ\81Ñ\82а, Ð¶Ð´Ð¸Ñ\82е..."
+msgstr "Ð\97апÑ\83Ñ\81каеÑ\82Ñ\81Ñ\8f gitk... Ð\9fодождиÑ\82е, Ð¿Ð¾Ð¶Ð°Ð»Ñ\83йÑ\81Ñ\82а..."
 
-#: git-gui.sh:1653
-#, tcl-format
-msgid ""
-"Unable to start gitk:\n"
-"\n"
-"%s does not exist"
-msgstr ""
-"Не удалось запустить gitk:\n"
-"\n"
-"%s не существует"
+#: git-gui.sh:1887
+msgid "Couldn't find gitk in PATH"
+msgstr "gitk не найден в PATH."
 
-#: git-gui.sh:1860 lib/choose_repository.tcl:36
+#: git-gui.sh:2280 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Репозиторий"
 
-#: git-gui.sh:1861
+#: git-gui.sh:2281
 msgid "Edit"
 msgstr "Редактировать"
 
-#: git-gui.sh:1863 lib/choose_rev.tcl:561
+#: git-gui.sh:2283 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Ветвь"
 
-#: git-gui.sh:1866 lib/choose_rev.tcl:548
+#: git-gui.sh:2286 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Состояние"
 
-#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
+#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
-msgstr "Ð\9eбÑ\8aединиÑ\82Ñ\8c"
+msgstr "СлиÑ\8fние"
 
-#: git-gui.sh:1870 lib/choose_rev.tcl:557
+#: git-gui.sh:2290 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Внешние репозитории"
 
-#: git-gui.sh:1879
+#: git-gui.sh:2293
+msgid "Tools"
+msgstr "Вспомогательные операции"
+
+#: git-gui.sh:2302
+msgid "Explore Working Copy"
+msgstr "Просмотр рабочего каталога"
+
+#: git-gui.sh:2307
 msgid "Browse Current Branch's Files"
 msgstr "Просмотреть файлы текущей ветви"
 
-#: git-gui.sh:1883
+#: git-gui.sh:2311
 msgid "Browse Branch Files..."
 msgstr "Показать файлы ветви..."
 
-#: git-gui.sh:1888
+#: git-gui.sh:2316
 msgid "Visualize Current Branch's History"
-msgstr "Ð\98Ñ\81Ñ\82оÑ\80иÑ\8f Ñ\82екÑ\83Ñ\89ей Ð²ÐµÑ\82ви Ð½Ð°Ð³Ð»Ñ\8fдно"
+msgstr "Ð\9fоказаÑ\82Ñ\8c Ð¸Ñ\81Ñ\82оÑ\80иÑ\8e Ñ\82екÑ\83Ñ\89ей Ð²ÐµÑ\82ви"
 
-#: git-gui.sh:1892
+#: git-gui.sh:2320
 msgid "Visualize All Branch History"
-msgstr "Ð\98Ñ\81Ñ\82оÑ\80иÑ\8f Ð²Ñ\81еÑ\85 Ð²ÐµÑ\82вей Ð½Ð°Ð³Ð»Ñ\8fдно"
+msgstr "Ð\9fоказаÑ\82Ñ\8c Ð¸Ñ\81Ñ\82оÑ\80иÑ\8e Ð²Ñ\81еÑ\85 Ð²ÐµÑ\82вей"
 
-#: git-gui.sh:1899
+#: git-gui.sh:2327
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Показать файлы ветви %s"
 
-#: git-gui.sh:1901
+#: git-gui.sh:2329
 #, tcl-format
 msgid "Visualize %s's History"
-msgstr "Ð\98Ñ\81Ñ\82оÑ\80иÑ\8f Ð²ÐµÑ\82ви %s Ð½Ð°Ð³Ð»Ñ\8fдно"
+msgstr "Ð\9fоказаÑ\82Ñ\8c Ð¸Ñ\81Ñ\82оÑ\80иÑ\8e Ð²ÐµÑ\82ви %s"
 
-#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Статистика базы данных"
 
-#: git-gui.sh:1909 lib/database.tcl:34
+#: git-gui.sh:2337 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Сжать базу данных"
 
-#: git-gui.sh:1912
+#: git-gui.sh:2340
 msgid "Verify Database"
 msgstr "Проверить базу данных"
 
-#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
+#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Создать ярлык на рабочем столе"
 
-#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
+#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "Выход"
 
-#: git-gui.sh:1939
+#: git-gui.sh:2371
 msgid "Undo"
 msgstr "Отменить"
 
-#: git-gui.sh:1942
+#: git-gui.sh:2374
 msgid "Redo"
 msgstr "Повторить"
 
-#: git-gui.sh:1946 git-gui.sh:2443
+#: git-gui.sh:2378 git-gui.sh:2937
 msgid "Cut"
 msgstr "Вырезать"
 
-#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: git-gui.sh:2381 git-gui.sh:2940 git-gui.sh:3014 git-gui.sh:3096
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Копировать"
 
-#: git-gui.sh:1952 git-gui.sh:2449
+#: git-gui.sh:2384 git-gui.sh:2943
 msgid "Paste"
 msgstr "Вставить"
 
-#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
+#: git-gui.sh:2387 git-gui.sh:2946 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Удалить"
 
-#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
+#: git-gui.sh:2391 git-gui.sh:2950 git-gui.sh:3100 lib/console.tcl:71
 msgid "Select All"
 msgstr "Выделить все"
 
-#: git-gui.sh:1968
+#: git-gui.sh:2400
 msgid "Create..."
 msgstr "Создать..."
 
-#: git-gui.sh:1974
+#: git-gui.sh:2406
 msgid "Checkout..."
 msgstr "Перейти..."
 
-#: git-gui.sh:1980
+#: git-gui.sh:2412
 msgid "Rename..."
 msgstr "Переименовать..."
 
-#: git-gui.sh:1985 git-gui.sh:2085
+#: git-gui.sh:2417
 msgid "Delete..."
 msgstr "Удалить..."
 
-#: git-gui.sh:1990
+#: git-gui.sh:2422
 msgid "Reset..."
 msgstr "Сбросить..."
 
-#: git-gui.sh:2002 git-gui.sh:2389
+#: git-gui.sh:2432
+msgid "Done"
+msgstr "Завершено"
+
+#: git-gui.sh:2434
+msgid "Commit@@verb"
+msgstr "Сохранить"
+
+#: git-gui.sh:2443 git-gui.sh:2878
 msgid "New Commit"
 msgstr "Новое состояние"
 
-#: git-gui.sh:2010 git-gui.sh:2396
+#: git-gui.sh:2451 git-gui.sh:2885
 msgid "Amend Last Commit"
 msgstr "Исправить последнее состояние"
 
-#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2461 git-gui.sh:2839 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Перечитать"
 
-#: git-gui.sh:2025
+#: git-gui.sh:2467
 msgid "Stage To Commit"
 msgstr "Подготовить для сохранения"
 
-#: git-gui.sh:2031
+#: git-gui.sh:2473
 msgid "Stage Changed Files To Commit"
 msgstr "Подготовить измененные файлы для сохранения"
 
-#: git-gui.sh:2037
+#: git-gui.sh:2479
 msgid "Unstage From Commit"
 msgstr "Убрать из подготовленного"
 
-#: git-gui.sh:2042 lib/index.tcl:395
+#: git-gui.sh:2484 lib/index.tcl:410
 msgid "Revert Changes"
 msgstr "Отменить изменения"
 
-#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
-msgid "Sign Off"
-msgstr "Ð\9fодпиÑ\81аÑ\82Ñ\8c"
+#: git-gui.sh:2491 git-gui.sh:3083
+msgid "Show Less Context"
+msgstr "Ð\9cенÑ\8cÑ\88е ÐºÐ¾Ð½Ñ\82екÑ\81Ñ\82а"
 
-#: git-gui.sh:2053 git-gui.sh:2372
-msgid "Commit@@verb"
-msgstr "Сохранить"
+#: git-gui.sh:2495 git-gui.sh:3087
+msgid "Show More Context"
+msgstr "Больше контекста"
+
+#: git-gui.sh:2502 git-gui.sh:2852 git-gui.sh:2961
+msgid "Sign Off"
+msgstr "Вставить Signed-off-by"
 
-#: git-gui.sh:2064
+#: git-gui.sh:2518
 msgid "Local Merge..."
-msgstr "Локальное объединение..."
+msgstr "Локальное слияние..."
 
-#: git-gui.sh:2069
+#: git-gui.sh:2523
 msgid "Abort Merge..."
-msgstr "Прервать объединение..."
+msgstr "Прервать слияние..."
+
+#: git-gui.sh:2535 git-gui.sh:2575
+msgid "Add..."
+msgstr "Добавить..."
 
-#: git-gui.sh:2081
+#: git-gui.sh:2539
 msgid "Push..."
 msgstr "Отправить..."
 
-#: git-gui.sh:2092 lib/choose_repository.tcl:41
-msgid "Apple"
-msgstr ""
+#: git-gui.sh:2543
+msgid "Delete Branch..."
+msgstr "Удалить ветвь..."
 
-#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "О %s"
 
-#: git-gui.sh:2099
+#: git-gui.sh:2557
 msgid "Preferences..."
 msgstr "Настройки..."
 
-#: git-gui.sh:2107 git-gui.sh:2639
+#: git-gui.sh:2565 git-gui.sh:3129
 msgid "Options..."
 msgstr "Настройки..."
 
-#: git-gui.sh:2113 lib/choose_repository.tcl:47
+#: git-gui.sh:2576
+msgid "Remove..."
+msgstr "Удалить..."
+
+#: git-gui.sh:2585 lib/choose_repository.tcl:50
 msgid "Help"
 msgstr "Помощь"
 
-#: git-gui.sh:2154
+#: git-gui.sh:2611
 msgid "Online Documentation"
 msgstr "Документация в интернете"
 
-#: git-gui.sh:2238
+#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr "Показать ключ SSH"
+
+#: git-gui.sh:2721
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr "критическая ошибка: %s: нет такого файла или каталога"
 
-#: git-gui.sh:2271
+#: git-gui.sh:2754
 msgid "Current Branch:"
 msgstr "Текущая ветвь:"
 
-#: git-gui.sh:2292
+#: git-gui.sh:2775
 msgid "Staged Changes (Will Commit)"
 msgstr "Подготовлено (будет сохранено)"
 
-#: git-gui.sh:2312
+#: git-gui.sh:2795
 msgid "Unstaged Changes"
 msgstr "Изменено (не будет сохранено)"
 
-#: git-gui.sh:2362
+#: git-gui.sh:2845
 msgid "Stage Changed"
 msgstr "Подготовить все"
 
-#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2864 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr "Отправить"
 
-#: git-gui.sh:2408
+#: git-gui.sh:2899
 msgid "Initial Commit Message:"
 msgstr "Комментарий к первому состоянию:"
 
-#: git-gui.sh:2409
+#: git-gui.sh:2900
 msgid "Amended Commit Message:"
 msgstr "Комментарий к исправленному состоянию:"
 
-#: git-gui.sh:2410
+#: git-gui.sh:2901
 msgid "Amended Initial Commit Message:"
 msgstr "Комментарий к исправленному первоначальному состоянию:"
 
-#: git-gui.sh:2411
+#: git-gui.sh:2902
 msgid "Amended Merge Commit Message:"
-msgstr "Комментарий к исправленному объединению:"
+msgstr "Комментарий к исправленному слиянию:"
 
-#: git-gui.sh:2412
+#: git-gui.sh:2903
 msgid "Merge Commit Message:"
-msgstr "Комментарий к объединению:"
+msgstr "Комментарий к слиянию:"
 
-#: git-gui.sh:2413
+#: git-gui.sh:2904
 msgid "Commit Message:"
 msgstr "Комментарий к состоянию:"
 
-#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
+#: git-gui.sh:2953 git-gui.sh:3104 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Копировать все"
 
-#: git-gui.sh:2483 lib/blame.tcl:107
+#: git-gui.sh:2977 lib/blame.tcl:104
 msgid "File:"
 msgstr "Файл:"
 
-#: git-gui.sh:2589
-msgid "Apply/Reverse Hunk"
-msgstr "Применить/Убрать изменение"
-
-#: git-gui.sh:2595
-msgid "Show Less Context"
-msgstr "Меньше контекста"
-
-#: git-gui.sh:2602
-msgid "Show More Context"
-msgstr "Больше контекста"
-
-#: git-gui.sh:2610
+#: git-gui.sh:3092
 msgid "Refresh"
 msgstr "Обновить"
 
-#: git-gui.sh:2631
+#: git-gui.sh:3113
 msgid "Decrease Font Size"
 msgstr "Уменьшить размер шрифта"
 
-#: git-gui.sh:2635
+#: git-gui.sh:3117
 msgid "Increase Font Size"
 msgstr "Увеличить размер шрифта"
 
-#: git-gui.sh:2646
+#: git-gui.sh:3125 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "Кодировка"
+
+#: git-gui.sh:3136
+msgid "Apply/Reverse Hunk"
+msgstr "Применить/Убрать изменение"
+
+#: git-gui.sh:3141
+msgid "Apply/Reverse Line"
+msgstr "Применить/Убрать строку"
+
+#: git-gui.sh:3151
+msgid "Run Merge Tool"
+msgstr "Запустить программу слияния"
+
+#: git-gui.sh:3156
+msgid "Use Remote Version"
+msgstr "Взять внешнюю версию"
+
+#: git-gui.sh:3160
+msgid "Use Local Version"
+msgstr "Взять локальную версию"
+
+#: git-gui.sh:3164
+msgid "Revert To Base"
+msgstr "Отменить изменения"
+
+#: git-gui.sh:3183
 msgid "Unstage Hunk From Commit"
 msgstr "Не сохранять часть"
 
-#: git-gui.sh:2648
+#: git-gui.sh:3184
+msgid "Unstage Line From Commit"
+msgstr "Убрать строку из подготовленного"
+
+#: git-gui.sh:3186
 msgid "Stage Hunk For Commit"
 msgstr "Подготовить часть для сохранения"
 
-#: git-gui.sh:2667
+#: git-gui.sh:3187
+msgid "Stage Line For Commit"
+msgstr "Подготовить строку для сохранения"
+
+#: git-gui.sh:3210
 msgid "Initializing..."
 msgstr "Инициализация..."
 
-#: git-gui.sh:2762
+#: git-gui.sh:3315
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -447,7 +514,7 @@ msgstr ""
 "запущенными из %s\n"
 "\n"
 
-#: git-gui.sh:2792
+#: git-gui.sh:3345
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -457,7 +524,7 @@ msgstr ""
 "Это известная проблема с Tcl,\n"
 "распространяемым Cygwin."
 
-#: git-gui.sh:2797
+#: git-gui.sh:3350
 #, tcl-format
 msgid ""
 "\n"
@@ -478,64 +545,108 @@ msgstr ""
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - графический пользовательский интерфейс к Git."
 
-#: lib/blame.tcl:77
+#: lib/blame.tcl:72
 msgid "File Viewer"
 msgstr "Просмотр файла"
 
-#: lib/blame.tcl:81
+#: lib/blame.tcl:78
 msgid "Commit:"
 msgstr "Сохраненное состояние:"
 
-#: lib/blame.tcl:264
+#: lib/blame.tcl:271
 msgid "Copy Commit"
 msgstr "Скопировать SHA-1"
 
-#: lib/blame.tcl:384
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr "Найти текст..."
+
+#: lib/blame.tcl:284
+msgid "Do Full Copy Detection"
+msgstr "Провести полный поиск копий"
+
+#: lib/blame.tcl:288
+msgid "Show History Context"
+msgstr "Показать исторический контекст"
+
+#: lib/blame.tcl:291
+msgid "Blame Parent Commit"
+msgstr "Рассмотреть состояние предка"
+
+#: lib/blame.tcl:450
 #, tcl-format
 msgid "Reading %s..."
 msgstr "Чтение %s..."
 
-#: lib/blame.tcl:488
+#: lib/blame.tcl:557
 msgid "Loading copy/move tracking annotations..."
 msgstr "Загрузка аннотации копирований/переименований..."
 
-#: lib/blame.tcl:508
+#: lib/blame.tcl:577
 msgid "lines annotated"
 msgstr "строк прокомментировано"
 
-#: lib/blame.tcl:689
+#: lib/blame.tcl:769
 msgid "Loading original location annotations..."
 msgstr "Загрузка аннотаций первоначального положения объекта..."
 
-#: lib/blame.tcl:692
+#: lib/blame.tcl:772
 msgid "Annotation complete."
 msgstr "Аннотация завершена."
 
-#: lib/blame.tcl:746
+#: lib/blame.tcl:802
+msgid "Busy"
+msgstr "Занят"
+
+#: lib/blame.tcl:803
+msgid "Annotation process is already running."
+msgstr "Аннотация уже запущена"
+
+#: lib/blame.tcl:842
+msgid "Running thorough copy detection..."
+msgstr "Выполнение полного поиска копий..."
+
+#: lib/blame.tcl:910
 msgid "Loading annotation..."
 msgstr "Загрузка аннотации..."
 
-#: lib/blame.tcl:802
+#: lib/blame.tcl:963
 msgid "Author:"
 msgstr "Автор:"
 
-#: lib/blame.tcl:806
+#: lib/blame.tcl:967
 msgid "Committer:"
 msgstr "Сохранил:"
 
-#: lib/blame.tcl:811
+#: lib/blame.tcl:972
 msgid "Original File:"
 msgstr "Исходный файл:"
 
-#: lib/blame.tcl:925
+#: lib/blame.tcl:1020
+msgid "Cannot find HEAD commit:"
+msgstr "Невозможно найти текущее состояние:"
+
+#: lib/blame.tcl:1075
+msgid "Cannot find parent commit:"
+msgstr "Невозможно найти состояние предка:"
+
+#: lib/blame.tcl:1090
+msgid "Unable to display parent"
+msgstr "Не могу показать предка"
+
+#: lib/blame.tcl:1091 lib/diff.tcl:297
+msgid "Error loading diff:"
+msgstr "Ошибка загрузки изменений:"
+
+#: lib/blame.tcl:1231
 msgid "Originally By:"
 msgstr "Источник:"
 
-#: lib/blame.tcl:931
+#: lib/blame.tcl:1237
 msgid "In File:"
 msgstr "Файл:"
 
-#: lib/blame.tcl:936
+#: lib/blame.tcl:1242
 msgid "Copied Or Moved Here By:"
 msgstr "Скопировано/перемещено в:"
 
@@ -549,16 +660,18 @@ msgstr "Перейти"
 
 #: 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:522 lib/choose_font.tcl:43 lib/merge.tcl:171
-#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/checkout_op.tcl:544 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
 msgid "Cancel"
-msgstr "Ð\9eÑ\82мениÑ\82Ñ\8c"
+msgstr "Ð\9eÑ\82мена"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
 msgid "Revision"
 msgstr "Версия"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
 msgid "Options"
 msgstr "Настройки"
 
@@ -578,7 +691,7 @@ msgstr "Создание ветви"
 msgid "Create New Branch"
 msgstr "Создать новую ветвь"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
 msgid "Create"
 msgstr "Создать"
 
@@ -586,7 +699,7 @@ msgstr "Создать"
 msgid "Branch Name"
 msgstr "Название ветви"
 
-#: lib/branch_create.tcl:43
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
 msgid "Name:"
 msgstr "Название:"
 
@@ -610,7 +723,7 @@ msgstr "Нет"
 msgid "Fast Forward Only"
 msgstr "Только Fast Forward"
 
-#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:536
 msgid "Reset"
 msgstr "Сброс"
 
@@ -650,26 +763,16 @@ msgstr "Локальные ветви"
 
 #: lib/branch_delete.tcl:52
 msgid "Delete Only If Merged Into"
-msgstr "Удалить только в случае, если было объединение с"
+msgstr "Удалить только в случае, если было слияние с"
 
 #: lib/branch_delete.tcl:54
 msgid "Always (Do not perform merge test.)"
-msgstr "Всегда (не выполнять проверку на объединение)"
+msgstr "Всегда (не выполнять проверку на слияние)"
 
 #: lib/branch_delete.tcl:103
 #, tcl-format
 msgid "The following branches are not completely merged into %s:"
-msgstr "Следующие ветви объединены с %s не полностью:"
-
-#: lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult. \n"
-"\n"
-" Delete the selected branches?"
-msgstr ""
-"Восстанавливать удаленные ветви сложно. \n"
-"\n"
-" Удалить выбранные ветви?"
+msgstr "Ветви, которые не полностью сливаются с %s:"
 
 #: lib/branch_delete.tcl:141
 #, tcl-format
@@ -700,7 +803,7 @@ msgstr "Новое название:"
 msgid "Please select a branch to rename."
 msgstr "Укажите ветвь для переименования."
 
-#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:201
 #, tcl-format
 msgid "Branch '%s' already exists."
 msgstr "Ветвь '%s' уже существует."
@@ -731,32 +834,38 @@ msgstr "[На уровень выше]"
 msgid "Browse Branch Files"
 msgstr "Показать файлы ветви"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:387
-#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
-#: lib/choose_repository.tcl:987
+#: lib/browser.tcl:278 lib/choose_repository.tcl:394
+#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
+#: lib/choose_repository.tcl:995
 msgid "Browse"
 msgstr "Показать"
 
-#: lib/checkout_op.tcl:79
+#: lib/checkout_op.tcl:84
 #, tcl-format
 msgid "Fetching %s from %s"
 msgstr "Получение %s из %s "
 
-#: lib/checkout_op.tcl:127
+#: lib/checkout_op.tcl:132
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
 msgstr "критическая ошибка: невозможно разрешить %s"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
+#: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "Закрыть"
 
-#: lib/checkout_op.tcl:169
+#: lib/checkout_op.tcl:174
 #, tcl-format
 msgid "Branch '%s' does not exist."
 msgstr "Ветвь '%s' не существует "
 
-#: lib/checkout_op.tcl:206
+#: lib/checkout_op.tcl:193
+#, tcl-format
+msgid "Failed to configure simplified git-pull for '%s'."
+msgstr "Ошибка создания упрощённой конфигурации git pull для '%s'."
+
+#: lib/checkout_op.tcl:228
 #, tcl-format
 msgid ""
 "Branch '%s' already exists.\n"
@@ -767,23 +876,23 @@ msgstr ""
 "Ветвь '%s' уже существует.\n"
 "\n"
 "Она не может быть прокручена(fast-forward) к %s.\n"
-"Требуется объединение."
+"Требуется слияние."
 
-#: lib/checkout_op.tcl:220
+#: lib/checkout_op.tcl:242
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
-msgstr "СÑ\82Ñ\80аÑ\82егиÑ\8f Ð¾Ð±Ñ\8aединениÑ\8f '%s' Ð½Ðµ Ð¿Ð¾Ð´Ð´ÐµÑ\80живаеÑ\82Ñ\81Ñ\8f."
+msgstr "Ð\9dеизвеÑ\81Ñ\82наÑ\8f Ñ\81Ñ\82Ñ\80аÑ\82егиÑ\8f Ñ\81лиÑ\8fниÑ\8f: '%s'."
 
-#: lib/checkout_op.tcl:239
+#: lib/checkout_op.tcl:261
 #, tcl-format
 msgid "Failed to update '%s'."
 msgstr "Не удалось обновить '%s'."
 
-#: lib/checkout_op.tcl:251
+#: lib/checkout_op.tcl:273
 msgid "Staging area (index) is already locked."
 msgstr "Рабочая область заблокирована другим процессом."
 
-#: lib/checkout_op.tcl:266
+#: lib/checkout_op.tcl:288
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -799,30 +908,30 @@ msgstr ""
 "\n"
 "Это будет сделано сейчас автоматически.\n"
 
-#: lib/checkout_op.tcl:322
+#: lib/checkout_op.tcl:344
 #, tcl-format
 msgid "Updating working directory to '%s'..."
 msgstr "Обновление рабочего каталога из '%s'..."
 
-#: lib/checkout_op.tcl:323
+#: lib/checkout_op.tcl:345
 msgid "files checked out"
 msgstr "файлы извлечены"
 
-#: lib/checkout_op.tcl:353
+#: lib/checkout_op.tcl:375
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
-msgstr "Прерван переход на '%s' (требуется объединение на уровне файлов)"
+msgstr "Прерван переход на '%s' (требуется слияние содержания файлов)"
 
-#: lib/checkout_op.tcl:354
+#: lib/checkout_op.tcl:376
 msgid "File level merge required."
-msgstr "Требуется объединение на уровне файлов."
+msgstr "Требуется слияние содержания файлов."
 
-#: lib/checkout_op.tcl:358
+#: lib/checkout_op.tcl:380
 #, tcl-format
 msgid "Staying on branch '%s'."
 msgstr "Ветвь '%s' остается текущей."
 
-#: lib/checkout_op.tcl:429
+#: lib/checkout_op.tcl:451
 msgid ""
 "You are no longer on a local branch.\n"
 "\n"
@@ -834,30 +943,30 @@ msgstr ""
 "Если вы хотите снова вернуться к какой-нибудь ветви, создайте ее сейчас, "
 "начиная с 'Текущего отсоединенного состояния'."
 
-#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
+#: lib/checkout_op.tcl:468 lib/checkout_op.tcl:472
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "Ветвь '%s' сделана текущей."
 
-#: lib/checkout_op.tcl:478
+#: lib/checkout_op.tcl:500
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
 msgstr "Сброс '%s' в '%s' приведет к потере следующих сохраненных состояний: "
 
-#: lib/checkout_op.tcl:500
+#: lib/checkout_op.tcl:522
 msgid "Recovering lost commits may not be easy."
 msgstr "Восстановить потерянные сохраненные состояния будет сложно."
 
-#: lib/checkout_op.tcl:505
+#: lib/checkout_op.tcl:527
 #, tcl-format
 msgid "Reset '%s'?"
 msgstr "Сбросить '%s'?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:163
+#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "Наглядно"
 
-#: lib/checkout_op.tcl:578
+#: lib/checkout_op.tcl:600
 #, tcl-format
 msgid ""
 "Failed to set current branch.\n"
@@ -900,224 +1009,228 @@ msgstr ""
 
 #: lib/choose_repository.tcl:28
 msgid "Git Gui"
-msgstr ""
+msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
 msgid "Create New Repository"
 msgstr "Создать новый репозиторий"
 
-#: lib/choose_repository.tcl:87
+#: lib/choose_repository.tcl:93
 msgid "New..."
 msgstr "Новый..."
 
-#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
 msgid "Clone Existing Repository"
 msgstr "Склонировать существующий репозиторий"
 
-#: lib/choose_repository.tcl:100
+#: lib/choose_repository.tcl:106
 msgid "Clone..."
 msgstr "Склонировать..."
 
-#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
 msgid "Open Existing Repository"
 msgstr "Выбрать существующий репозиторий"
 
-#: lib/choose_repository.tcl:113
+#: lib/choose_repository.tcl:119
 msgid "Open..."
 msgstr "Открыть..."
 
-#: lib/choose_repository.tcl:126
+#: lib/choose_repository.tcl:132
 msgid "Recent Repositories"
 msgstr "Недавние репозитории"
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:138
 msgid "Open Recent Repository:"
 msgstr "Открыть последний репозиторий"
 
-#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
-#: lib/choose_repository.tcl:310
+#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:316
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Не удалось создать репозиторий %s:"
 
-#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
+#: lib/choose_repository.tcl:387
 msgid "Directory:"
 msgstr "Каталог:"
 
-#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
-#: lib/choose_repository.tcl:1011
+#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1017
 msgid "Git Repository"
 msgstr "Репозиторий"
 
-#: lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:442
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Каталог '%s' уже существует."
 
-#: lib/choose_repository.tcl:441
+#: lib/choose_repository.tcl:446
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Файл '%s' уже существует."
 
-#: lib/choose_repository.tcl:455
+#: lib/choose_repository.tcl:460
 msgid "Clone"
 msgstr "Склонировать"
 
-#: lib/choose_repository.tcl:468
-msgid "URL:"
-msgstr "Ссылка:"
+#: lib/choose_repository.tcl:473
+msgid "Source Location:"
+msgstr "Исходное положение:"
+
+#: lib/choose_repository.tcl:484
+msgid "Target Directory:"
+msgstr "Каталог назначения:"
 
-#: lib/choose_repository.tcl:489
+#: lib/choose_repository.tcl:496
 msgid "Clone Type:"
 msgstr "Тип клона:"
 
-#: lib/choose_repository.tcl:495
+#: lib/choose_repository.tcl:502
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Стандартный (Быстрый, полуизбыточный, \"жесткие\" ссылки)"
 
-#: lib/choose_repository.tcl:501
+#: lib/choose_repository.tcl:508
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Полная копия (Медленный, создает резервную копию)"
 
-#: lib/choose_repository.tcl:507
+#: lib/choose_repository.tcl:514
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Общий (Самый быстрый, не рекомендуется, без резервной копии)"
 
-#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
-#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
-#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
+#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Каталог не является репозиторием: %s"
 
-#: lib/choose_repository.tcl:579
+#: lib/choose_repository.tcl:586
 msgid "Standard only available for local repository."
 msgstr "Стандартный клон возможен только для локального репозитория."
 
-#: lib/choose_repository.tcl:583
+#: lib/choose_repository.tcl:590
 msgid "Shared only available for local repository."
 msgstr "Общий клон возможен только для локального репозитория."
 
-#: lib/choose_repository.tcl:604
+#: lib/choose_repository.tcl:611
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "Путь '%s' уже существует."
 
-#: lib/choose_repository.tcl:615
+#: lib/choose_repository.tcl:622
 msgid "Failed to configure origin"
 msgstr "Не могу сконфигурировать исходный репозиторий."
 
-#: lib/choose_repository.tcl:627
+#: lib/choose_repository.tcl:634
 msgid "Counting objects"
 msgstr "Считаю объекты"
 
-#: lib/choose_repository.tcl:628
+#: lib/choose_repository.tcl:635
 msgid "buckets"
 msgstr ""
 
-#: lib/choose_repository.tcl:652
+#: lib/choose_repository.tcl:659
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Не могу скопировать objects/info/alternates: %s"
 
-#: lib/choose_repository.tcl:688
+#: lib/choose_repository.tcl:695
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Нечего клонировать с %s."
 
-#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
-#: lib/choose_repository.tcl:916
+#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:923
 msgid "The 'master' branch has not been initialized."
 msgstr "Не инициализирована ветвь 'master'."
 
-#: lib/choose_repository.tcl:703
+#: lib/choose_repository.tcl:710
 msgid "Hardlinks are unavailable.  Falling back to copying."
-msgstr "\"Жесткие ссылки\" не доступны. Буду использовать копирование."
+msgstr "\"Жесткие ссылки\" недоступны. Будет использовано копирование."
 
-#: lib/choose_repository.tcl:715
+#: lib/choose_repository.tcl:722
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Клонирование %s"
 
-#: lib/choose_repository.tcl:746
+#: lib/choose_repository.tcl:753
 msgid "Copying objects"
 msgstr "Копирование objects"
 
-#: lib/choose_repository.tcl:747
+#: lib/choose_repository.tcl:754
 msgid "KiB"
 msgstr "КБ"
 
-#: lib/choose_repository.tcl:771
+#: lib/choose_repository.tcl:778
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Не могу скопировать объект: %s"
 
-#: lib/choose_repository.tcl:781
+#: lib/choose_repository.tcl:788
 msgid "Linking objects"
 msgstr "Создание ссылок на objects"
 
-#: lib/choose_repository.tcl:782
+#: lib/choose_repository.tcl:789
 msgid "objects"
 msgstr "объекты"
 
-#: lib/choose_repository.tcl:790
+#: lib/choose_repository.tcl:797
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Не могу \"жестко связать\" объект: %s"
 
-#: lib/choose_repository.tcl:845
+#: lib/choose_repository.tcl:852
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 "Не могу получить ветви и объекты. Дополнительная информация на консоли."
 
-#: lib/choose_repository.tcl:856
+#: lib/choose_repository.tcl:863
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "Не могу получить метки. Дополнительная информация на консоли."
 
-#: lib/choose_repository.tcl:880
+#: lib/choose_repository.tcl:887
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "Не могу определить HEAD. Дополнительная информация на консоли."
 
-#: lib/choose_repository.tcl:889
+#: lib/choose_repository.tcl:896
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Не могу очистить %s"
 
-#: lib/choose_repository.tcl:895
+#: lib/choose_repository.tcl:902
 msgid "Clone failed."
 msgstr "Клонирование не удалось."
 
-#: lib/choose_repository.tcl:902
+#: lib/choose_repository.tcl:909
 msgid "No default branch obtained."
 msgstr "Не было получено ветви по умолчанию."
 
-#: lib/choose_repository.tcl:913
+#: lib/choose_repository.tcl:920
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Не могу распознать %s как состояние."
 
-#: lib/choose_repository.tcl:925
+#: lib/choose_repository.tcl:932
 msgid "Creating working directory"
 msgstr "Создаю рабочий каталог"
 
-#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
-#: lib/index.tcl:193
+#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
+#: lib/index.tcl:196
 msgid "files"
 msgstr "файлов"
 
-#: lib/choose_repository.tcl:955
+#: lib/choose_repository.tcl:962
 msgid "Initial file checkout failed."
 msgstr "Не удалось получить начальное состояние файлов репозитория."
 
-#: lib/choose_repository.tcl:971
+#: lib/choose_repository.tcl:978
 msgid "Open"
 msgstr "Открыть"
 
-#: lib/choose_repository.tcl:981
+#: lib/choose_repository.tcl:988
 msgid "Repository:"
 msgstr "Репозиторий:"
 
-#: lib/choose_repository.tcl:1031
+#: lib/choose_repository.tcl:1037
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Не удалось открыть репозиторий %s:"
@@ -1140,7 +1253,7 @@ msgstr "Ветвь слежения"
 
 #: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
 msgid "Tag"
-msgstr "Таг"
+msgstr "Ð\9cеÑ\82ка"
 
 #: lib/choose_rev.tcl:317
 #, tcl-format
@@ -1182,24 +1295,24 @@ msgid ""
 "completed.  You cannot amend the prior commit unless you first abort the "
 "current merge activity.\n"
 msgstr ""
-"Ð\9dевозможно Ð¸Ñ\81пÑ\80авиÑ\82Ñ\8c Ñ\81оÑ\81Ñ\82оÑ\8fние Ð²Ð¾ Ð²Ñ\80емÑ\8f Ð¾Ð±Ñ\8aединения.\n"
+"Ð\9dевозможно Ð¸Ñ\81пÑ\80авиÑ\82Ñ\8c Ñ\81оÑ\81Ñ\82оÑ\8fние Ð²Ð¾ Ð²Ñ\80емÑ\8f Ð¾Ð¿ÐµÑ\80аÑ\86ии Ñ\81лиÑ\8fния.\n"
 "\n"
-"Текущее объединение не завершено. Невозможно исправить предыдущее "
-"сохраненное состояние не прерывая текущее объединение.\n"
+"Текущее слияние не завершено. Невозможно исправить предыдущее "
+"сохраненное состояние, не прерывая эту операцию.\n"
 
-#: lib/commit.tcl:49
+#: lib/commit.tcl:48
 msgid "Error loading commit data for amend:"
 msgstr "Ошибка при загрузке данных для исправления сохраненного состояния:"
 
-#: lib/commit.tcl:76
+#: lib/commit.tcl:75
 msgid "Unable to obtain your identity:"
 msgstr "Невозможно получить информацию об авторстве:"
 
-#: lib/commit.tcl:81
+#: lib/commit.tcl:80
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr "Неверный GIT_COMMITTER_IDENT:"
 
-#: lib/commit.tcl:133
+#: lib/commit.tcl:132
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1215,7 +1328,7 @@ msgstr ""
 "\n"
 "Это будет сделано сейчас автоматически.\n"
 
-#: lib/commit.tcl:154
+#: lib/commit.tcl:155
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1223,12 +1336,12 @@ msgid ""
 "File %s has merge conflicts.  You must resolve them and stage the file "
 "before committing.\n"
 msgstr ""
-"Нельзя сохранить необъединенные файлы.\n"
+"Нельзя сохранить файлы с незавершённой операцей слияния.\n"
 "\n"
-"Для файла %s возник конфликт объединения. Разрешите конфликт и добавьте к "
+"Для файла %s возник конфликт слияния. Разрешите конфликт и добавьте к "
 "подготовленным файлам перед сохранением.\n"
 
-#: lib/commit.tcl:162
+#: lib/commit.tcl:163
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1239,7 +1352,7 @@ msgstr ""
 "\n"
 "Файл %s не может быть сохранен данной программой.\n"
 
-#: lib/commit.tcl:170
+#: lib/commit.tcl:171
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1249,7 +1362,7 @@ msgstr ""
 "\n"
 "Подготовьте хотя бы один файл до создания сохраненного состояния.\n"
 
-#: lib/commit.tcl:183
+#: lib/commit.tcl:186
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1267,45 +1380,45 @@ msgstr ""
 "- вторая строка пустая\n"
 "- оставшиеся строки: опишите, что дают ваши изменения.\n"
 
-#: lib/commit.tcl:207
+#: lib/commit.tcl:210
 #, tcl-format
 msgid "warning: Tcl does not support encoding '%s'."
 msgstr "предупреждение: Tcl не поддерживает кодировку '%s'."
 
-#: lib/commit.tcl:221
+#: lib/commit.tcl:226
 msgid "Calling pre-commit hook..."
 msgstr "Вызов программы поддержки репозитория pre-commit..."
 
-#: lib/commit.tcl:236
+#: lib/commit.tcl:241
 msgid "Commit declined by pre-commit hook."
 msgstr "Сохранение прервано программой поддержки репозитория pre-commit"
 
-#: lib/commit.tcl:259
+#: lib/commit.tcl:264
 msgid "Calling commit-msg hook..."
 msgstr "Вызов программы поддержки репозитория commit-msg..."
 
-#: lib/commit.tcl:274
+#: lib/commit.tcl:279
 msgid "Commit declined by commit-msg hook."
 msgstr "Сохранение прервано программой поддержки репозитория commit-msg"
 
-#: lib/commit.tcl:287
+#: lib/commit.tcl:292
 msgid "Committing changes..."
 msgstr "Сохранение изменений..."
 
-#: lib/commit.tcl:303
+#: lib/commit.tcl:308
 msgid "write-tree failed:"
 msgstr "Программа write-tree завершилась с ошибкой:"
 
-#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+#: lib/commit.tcl:309 lib/commit.tcl:353 lib/commit.tcl:373
 msgid "Commit failed."
 msgstr "Сохранить состояние не удалось."
 
-#: lib/commit.tcl:321
+#: lib/commit.tcl:326
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Состояние %s выглядит поврежденным"
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:331
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1315,23 +1428,23 @@ msgid ""
 msgstr ""
 "Отсутствуют изменения для сохранения.\n"
 "\n"
-"Ни один файл не был изменен и не было объединения.\n"
+"Ни один файл не был изменен и не было слияния.\n"
 "\n"
 "Сейчас автоматически запустится перечитывание репозитория.\n"
 
-#: lib/commit.tcl:333
+#: lib/commit.tcl:338
 msgid "No changes to commit."
 msgstr "Отуствуют измения для сохранения."
 
-#: lib/commit.tcl:347
+#: lib/commit.tcl:352
 msgid "commit-tree failed:"
 msgstr "Программа commit-tree завершилась с ошибкой:"
 
-#: lib/commit.tcl:367
+#: lib/commit.tcl:372
 msgid "update-ref failed:"
 msgstr "Программа update-ref завершилась с ошибкой:"
 
-#: lib/commit.tcl:454
+#: lib/commit.tcl:460
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Создано состояние %s: %s "
@@ -1406,7 +1519,7 @@ msgstr ""
 msgid "Invalid date from Git: %s"
 msgstr "Неправильная дата в репозитории: %s"
 
-#: lib/diff.tcl:42
+#: lib/diff.tcl:59
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1428,40 +1541,101 @@ msgstr ""
 "\n"
 "Сейчас будет запущено перечитывание репозитория, чтобы найти подобные файлы."
 
-#: lib/diff.tcl:81
+#: lib/diff.tcl:99
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "Загрузка изменений в %s..."
 
-#: lib/diff.tcl:114 lib/diff.tcl:184
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+"ЛОКАЛЬНО: удалён\n"
+"ВНЕШНИЙ:\n"
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+"ВНЕШНИЙ: удалён\n"
+"ЛОКАЛЬНО:\n"
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr "ЛОКАЛЬНО:\n"
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr "ВНЕШНИЙ:\n"
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Не могу показать %s"
 
-#: lib/diff.tcl:115
+#: lib/diff.tcl:198
 msgid "Error loading file:"
 msgstr "Ошибка загрузки файла:"
 
-#: lib/diff.tcl:122
+#: lib/diff.tcl:205
 msgid "Git Repository (subproject)"
 msgstr "Репозиторий Git (подпроект)"
 
-#: lib/diff.tcl:134
+#: lib/diff.tcl:217
 msgid "* Binary file (not showing content)."
 msgstr "* Двоичный файл (содержимое не показано)"
 
-#: lib/diff.tcl:185
-msgid "Error loading diff:"
-msgstr "Ошибка загрузки diff:"
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"* Размер неподготовленого файла %d байт.\n"
+"* Показано первых %d байт.\n"
+
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"* Неподготовленый файл обрезан: %s.\n"
+"* Чтобы увидеть весь файл, используйте программу-редактор.\n"
 
-#: lib/diff.tcl:303
+#: lib/diff.tcl:436
 msgid "Failed to unstage selected hunk."
 msgstr "Не удалось исключить выбранную часть."
 
-#: lib/diff.tcl:310
+#: lib/diff.tcl:443
 msgid "Failed to stage selected hunk."
 msgstr "Не удалось подготовить к сохранению выбранную часть."
 
+#: lib/diff.tcl:509
+msgid "Failed to unstage selected line."
+msgstr "Не удалось исключить выбранную строку."
+
+#: lib/diff.tcl:517
+msgid "Failed to stage selected line."
+msgstr "Не удалось подготовить к сохранению выбранную строку."
+
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "По умолчанию"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Системная (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Другая"
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "ошибка"
@@ -1480,7 +1654,7 @@ msgstr "Не удалось разблокировать индекс"
 
 #: lib/index.tcl:15
 msgid "Index Error"
-msgstr "Ð\9eÑ\88ибка Ð¸Ð½Ð´ÐµÐºÑ\81а"
+msgstr "Ð\9eÑ\88ибка Ð² Ð¸Ð½Ð´ÐµÐºÑ\81е"
 
 #: lib/index.tcl:21
 msgid ""
@@ -1498,50 +1672,59 @@ msgstr "Продолжить"
 msgid "Unlock Index"
 msgstr "Разблокировать индекс"
 
-#: lib/index.tcl:282
+#: lib/index.tcl:287
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "Удаление %s из подготовленного"
 
-#: lib/index.tcl:313
+#: lib/index.tcl:326
 msgid "Ready to commit."
 msgstr "Подготовлено для сохранения"
 
-#: lib/index.tcl:326
+#: lib/index.tcl:339
 #, tcl-format
 msgid "Adding %s"
 msgstr "Добавление %s..."
 
-#: lib/index.tcl:381
+#: lib/index.tcl:396
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Отменить изменения в файле %s?"
 
-#: lib/index.tcl:383
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Отменить изменения в %i файле(-ах)?"
 
-#: lib/index.tcl:391
+#: lib/index.tcl:406
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Любые изменения, не подготовленные к сохранению, будут потеряны при данной "
 "операции."
 
-#: lib/index.tcl:394
+#: lib/index.tcl:409
 msgid "Do Nothing"
 msgstr "Ничего не делать"
 
+#: lib/index.tcl:427
+msgid "Reverting selected files"
+msgstr "Удаление изменений в выбраных файлах"
+
+#: lib/index.tcl:431
+#, tcl-format
+msgid "Reverting %s"
+msgstr "Отмена изменений в %s"
+
 #: lib/merge.tcl:13
 msgid ""
 "Cannot merge while amending.\n"
 "\n"
 "You must finish amending this commit before starting any type of merge.\n"
 msgstr ""
-"Невозможно выполнить объединение во время исправления.\n"
+"Невозможно выполнить слияние во время исправления.\n"
 "\n"
 "Завершите исправление данного состояния перед выполнением операции "
-"объединения.\n"
+"слияния.\n"
 
 #: lib/merge.tcl:27
 msgid ""
@@ -1559,7 +1742,7 @@ msgstr ""
 "\n"
 "Это будет сделано сейчас автоматически.\n"
 
-#: lib/merge.tcl:44
+#: lib/merge.tcl:45
 #, tcl-format
 msgid ""
 "You are in the middle of a conflicted merge.\n"
@@ -1569,14 +1752,14 @@ msgid ""
 "You must resolve them, stage the file, and commit to complete the current "
 "merge.  Only then can you begin another merge.\n"
 msgstr ""
-"Предыдущее объединение не завершено из-за конфликта.\n"
+"Предыдущее слияние не завершено из-за конфликта.\n"
 "\n"
-"Для файла %s возник конфликт объединения.\n"
+"Для файла %s возник конфликт слияния.\n"
 "\n"
 "Разрешите конфликт, подготовьте файл и сохраните. Только после этого можно "
-"начать следующее объединение.\n"
+"начать следующее слияние.\n"
 
-#: lib/merge.tcl:54
+#: lib/merge.tcl:55
 #, tcl-format
 msgid ""
 "You are in the middle of a change.\n"
@@ -1590,36 +1773,37 @@ msgstr ""
 "\n"
 "Файл %s изменен.\n"
 "\n"
-"Подготовьте и сохраните измения перед началом объединения. В случае "
-"необходимости это позволит прервать операцию объединения.\n"
+"Подготовьте и сохраните измения перед началом слияния. В случае "
+"необходимости это позволит прервать операцию слияния.\n"
 
-#: lib/merge.tcl:106
+#: lib/merge.tcl:107
 #, tcl-format
 msgid "%s of %s"
 msgstr "%s из %s"
 
-#: lib/merge.tcl:119
+#: lib/merge.tcl:120
+#, tcl-format
 msgid "Merging %s and %s..."
-msgstr "Ð\9eбÑ\8aединение %s и %s..."
+msgstr "СлиÑ\8fние %s и %s..."
 
-#: lib/merge.tcl:130
+#: lib/merge.tcl:131
 msgid "Merge completed successfully."
-msgstr "Ð\9eбÑ\8aединение успешно завершено."
+msgstr "СлиÑ\8fние успешно завершено."
 
-#: lib/merge.tcl:132
+#: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
-msgstr "Не удалось завершить объединение. Требуется разрешение конфликта."
+msgstr "Не удалось завершить слияние. Требуется разрешение конфликта."
 
-#: lib/merge.tcl:157
+#: lib/merge.tcl:158
 #, tcl-format
 msgid "Merge Into %s"
-msgstr "Ð\9eбÑ\8aединиÑ\82Ñ\8c с %s"
+msgstr "СлиÑ\8fние с %s"
 
-#: lib/merge.tcl:176
+#: lib/merge.tcl:177
 msgid "Revision To Merge"
-msgstr "Версия для объединения"
+msgstr "Версия, с которой провести слияние"
 
-#: lib/merge.tcl:211
+#: lib/merge.tcl:212
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1629,7 +1813,7 @@ msgstr ""
 "\n"
 "Завершите текущее исправление сохраненного состояния.\n"
 
-#: lib/merge.tcl:221
+#: lib/merge.tcl:222
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1637,13 +1821,13 @@ msgid ""
 "\n"
 "Continue with aborting the current merge?"
 msgstr ""
-"Ð\9fÑ\80еÑ\80ваÑ\82Ñ\8c Ð¾Ð±Ñ\8aединение?\n"
+"Ð\9fÑ\80еÑ\80ваÑ\82Ñ\8c Ð¾Ð¿ÐµÑ\80аÑ\86иÑ\8e Ñ\81лиÑ\8fниÑ\8f?\n"
 "\n"
-"Прерывание объединения приведет к потере *ВСЕХ* несохраненных изменений.\n"
+"Прерывание этой операции приведет к потере *ВСЕХ* несохраненных изменений.\n"
 "\n"
 "Продолжить?"
 
-#: lib/merge.tcl:227
+#: lib/merge.tcl:228
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1651,130 +1835,346 @@ msgid ""
 "\n"
 "Continue with resetting the current changes?"
 msgstr ""
-"Ð\9fÑ\80еÑ\80ваÑ\82Ñ\8c Ð¾Ð±Ñ\8aединение?\n"
+"Ð\9fÑ\80еÑ\80ваÑ\82Ñ\8c Ð¾Ð¿ÐµÑ\80аÑ\86иÑ\8e Ñ\81лиÑ\8fниÑ\8f?\n"
 "\n"
-"Прерывание объединения приведет к потере *ВСЕХ* несохраненных изменений.\n"
+"Прерывание этой операции приведет к потере *ВСЕХ* несохраненных изменений.\n"
 "\n"
 "Продолжить?"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "Aborting"
 msgstr "Прерываю"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "files reset"
 msgstr "изменения в файлах отменены"
 
-#: lib/merge.tcl:265
+#: lib/merge.tcl:267
 msgid "Abort failed."
 msgstr "Прервать не удалось."
 
-#: lib/merge.tcl:267
+#: lib/merge.tcl:269
 msgid "Abort completed.  Ready."
 msgstr "Прервано."
 
-#: lib/option.tcl:95
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Использовать базовую версию для разрешения конфликта?"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Использовать версию этой ветви для разрешения конфликта?"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Использовать версию другой ветви для разрешения конфликта?"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Внимание! Список изменений показывает только конфликтующие отличия.\n"
+"\n"
+"%s будет переписан.\n"
+"\n"
+"Это действие можно отменить только перезапуском операции слияния."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr ""
+"Файл %s кажется содержит необработаные конфликты. "
+"Продолжить подготовку к сохранению?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "Добавляю результат разрешения для %s"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr ""
+"Программа слияния не обрабатывает конфликты с удалением или участием ссылок"
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "Конфликтующий файл не существует"
+
+#: lib/mergetool.tcl:264
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "'%s' не является программой слияния"
+
+#: lib/mergetool.tcl:268
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "Неизвестная программа слияния '%s'"
+
+#: lib/mergetool.tcl:303
+msgid "Merge tool is already running, terminate it?"
+msgstr "Программа слияния уже работает. Прервать?"
+
+#: lib/mergetool.tcl:323
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Ошибка получения версий:\n"
+"%s"
+
+#: lib/mergetool.tcl:343
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"Ошибка запуска программы слияния:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:347
+msgid "Running merge tool..."
+msgstr "Запуск программы слияния..."
+
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
+msgid "Merge tool failed."
+msgstr "Ошибка выполнения программы слияния."
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "Ошибка в глобальной установке кодировки '%s'"
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr "Неверная кодировка репозитория: '%s'"
+
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr "Восстановить настройки по умолчанию"
 
-#: lib/option.tcl:99
+#: lib/option.tcl:121
 msgid "Save"
 msgstr "Сохранить"
 
-#: lib/option.tcl:109
+#: lib/option.tcl:131
 #, tcl-format
 msgid "%s Repository"
-msgstr "для репозитория %s"
+msgstr "Ð\94ля репозитория %s"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:132
 msgid "Global (All Repositories)"
 msgstr "Общие (для всех репозиториев)"
 
-#: lib/option.tcl:116
+#: lib/option.tcl:138
 msgid "User Name"
 msgstr "Имя пользователя"
 
-#: lib/option.tcl:117
+#: lib/option.tcl:139
 msgid "Email Address"
 msgstr "Адрес электронной почты"
 
-#: lib/option.tcl:119
+#: lib/option.tcl:141
 msgid "Summarize Merge Commits"
-msgstr "Суммарный комментарий при объединении"
+msgstr "Суммарный комментарий при слиянии"
 
-#: lib/option.tcl:120
+#: lib/option.tcl:142
 msgid "Merge Verbosity"
-msgstr "Уровень детальности сообщений при объединении"
+msgstr "Уровень детальности сообщений при слиянии"
 
-#: lib/option.tcl:121
+#: lib/option.tcl:143
 msgid "Show Diffstat After Merge"
-msgstr "Показать отчет об изменениях после объединения"
+msgstr "Показать отчет об изменениях после слияния"
+
+#: lib/option.tcl:144
+msgid "Use Merge Tool"
+msgstr "Использовать для слияния программу"
 
-#: lib/option.tcl:123
+#: lib/option.tcl:146
 msgid "Trust File Modification Timestamps"
 msgstr "Доверять времени модификации файла"
 
-#: lib/option.tcl:124
+#: lib/option.tcl:147
 msgid "Prune Tracking Branches During Fetch"
 msgstr "Чистка ветвей слежения при получении изменений"
 
-#: lib/option.tcl:125
+#: lib/option.tcl:148
 msgid "Match Tracking Branches"
 msgstr "Имя новой ветви взять из имен ветвей слежения"
 
-#: lib/option.tcl:126
+#: lib/option.tcl:149
+msgid "Blame Copy Only On Changed Files"
+msgstr "Поиск копий только в изменённых файлах"
+
+#: lib/option.tcl:150
+msgid "Minimum Letters To Blame Copy On"
+msgstr "Минимальное количество символов для поиска копий"
+
+#: lib/option.tcl:151
+msgid "Blame History Context Radius (days)"
+msgstr "Радиус исторического контекста (в днях)"
+
+#: lib/option.tcl:152
 msgid "Number of Diff Context Lines"
 msgstr "Число строк в контексте diff"
 
-#: lib/option.tcl:127
+#: lib/option.tcl:153
 msgid "Commit Message Text Width"
-msgstr "Ширина комментария к состоянию:"
+msgstr "Ширина текста комментария"
 
-#: lib/option.tcl:128
+#: lib/option.tcl:154
 msgid "New Branch Name Template"
 msgstr "Шаблон для имени новой ветви"
 
-#: lib/option.tcl:192
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "Кодировка содержания файла по умолчанию"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "Изменить"
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr "Словарь для проверки правописания:"
 
-#: lib/option.tcl:216
+#: lib/option.tcl:254
 msgid "Change Font"
-msgstr "Изменить шрифт"
+msgstr "Изменить"
 
-#: lib/option.tcl:220
+#: lib/option.tcl:258
 #, tcl-format
 msgid "Choose %s"
 msgstr "Выберите %s"
 
 # carbon copy
-#: lib/option.tcl:226
+#: lib/option.tcl:264
 msgid "pt."
-msgstr ""
+msgstr "pt."
 
-#: lib/option.tcl:240
+#: lib/option.tcl:278
 msgid "Preferences"
 msgstr "Настройки"
 
-#: lib/option.tcl:275
+#: lib/option.tcl:314
 msgid "Failed to completely save options:"
 msgstr "Не удалось полностью сохранить настройки:"
 
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Удалить ссылку на внешний репозиторий"
+
+#: lib/remote.tcl:168
+msgid "Prune from"
+msgstr "Чистка"
+
+#: lib/remote.tcl:173
+msgid "Fetch from"
+msgstr "Получение из"
+
+#: lib/remote.tcl:215
+msgid "Push to"
+msgstr "Отправить"
+
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "Зарегистрировать внешний репозиторий"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "Добавить внешний репозиторий"
+
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+msgid "Add"
+msgstr ""
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "Информация о внешнем репозитории"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Положение:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "Следующая операция"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "Скачать сразу"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "Инициализировать внешний репозиторий и отправить"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "Больше ничего не делать"
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr "Укажите название внешнего репозитория."
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "Недопустимое название внешнего репозитория '%s'."
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Не удалось добавить '%s' из '%s'. "
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "получение %s"
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "Получение %s"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Невозможно инициалировать репозиторий в '%s'."
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/transport.tcl:81
+#, tcl-format
+msgid "push %s"
+msgstr "отправить %s"
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Настройка %s (в %s)"
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
-msgstr "УдалиÑ\82Ñ\8c Ð²Ð½ÐµÑ\88нÑ\8eÑ\8e Ð²ÐµÑ\82вÑ\8c"
+msgid "Delete Branch Remotely"
+msgstr "Удаление Ð²ÐµÑ\82ви Ð²Ð¾ Ð²Ð½ÐµÑ\88нем Ñ\80епозиÑ\82оÑ\80ии"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
 msgstr "Из репозитория"
 
-#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
 msgid "Remote:"
 msgstr "внешний:"
 
-#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
-msgstr "по Ñ\83казанномÑ\83 URL:"
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
+msgid "Arbitrary Location:"
+msgstr "Указаное Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ:"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -1786,15 +2186,15 @@ msgstr "Удалить только в случае, если"
 
 #: lib/remote_branch_delete.tcl:111
 msgid "Merged Into:"
-msgstr "Ð\9eбÑ\8aединено с:"
+msgstr "СлиÑ\8fние с:"
 
 #: lib/remote_branch_delete.tcl:119
 msgid "Always (Do not perform merge checks)"
-msgstr "Ð\92Ñ\81егда (не Ð²Ñ\8bполнÑ\8fÑ\82Ñ\8c Ð¿Ñ\80овеÑ\80кÑ\83 Ð¾Ð±Ñ\8aединений)"
+msgstr "Ð\92Ñ\81егда (не Ð²Ñ\8bполнÑ\8fÑ\82Ñ\8c Ð¿Ñ\80овеÑ\80кÑ\83 Ð½Ð° Ñ\81лиÑ\8fние)"
 
 #: lib/remote_branch_delete.tcl:152
 msgid "A branch is required for 'Merged Into'."
-msgstr "Ð\94лÑ\8f Ð¾Ð¿Ñ\86ии 'Ð\9eбÑ\8aединено с' требуется указать ветвь."
+msgstr "Ð\94лÑ\8f Ð¾Ð¿Ñ\86ии 'СлиÑ\8fние с' требуется указать ветвь."
 
 #: lib/remote_branch_delete.tcl:184
 #, tcl-format
@@ -1803,7 +2203,8 @@ msgid ""
 "\n"
 " - %s"
 msgstr ""
-"Следующие ветви объединены с %s не полностью:\n"
+"Следующие ветви могут быть объединены с %s при помощи операции слияния:\n"
+"\n"
 " - %s"
 
 #: lib/remote_branch_delete.tcl:189
@@ -1812,7 +2213,7 @@ msgid ""
 "One or more of the merge tests failed because you have not fetched the "
 "necessary commits.  Try fetching from %s first."
 msgstr ""
-"Ð\9eдин Ð¸Ð»Ð¸ Ð½ÐµÑ\81колÑ\8cко Ñ\82еÑ\81Ñ\82ов Ð½Ð° Ð¾Ð±Ñ\8aединение не прошли, потому что Вы не "
+"Ð\9dекоÑ\82оÑ\80Ñ\8bе Ñ\82еÑ\81Ñ\82Ñ\8b Ð½Ð° Ñ\81лиÑ\8fние не прошли, потому что Вы не "
 "получили необходимые состояния. Попытайтесь получить их из %s."
 
 #: lib/remote_branch_delete.tcl:207
@@ -1843,17 +2244,21 @@ msgstr "Не указан репозиторий."
 msgid "Scanning %s..."
 msgstr "Перечитывание %s... "
 
-#: lib/remote.tcl:165
-msgid "Prune from"
-msgstr "ЧиÑ\81Ñ\82ка"
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "Ð\9fоиÑ\81к:"
 
-#: lib/remote.tcl:170
-msgid "Fetch from"
-msgstr "Ð\9fолÑ\83Ñ\87ение Ð¸Ð·"
+#: lib/search.tcl:23
+msgid "Next"
+msgstr "Ð\94алÑ\8cÑ\88е"
 
-#: lib/remote.tcl:213
-msgid "Push to"
-msgstr "Отправить"
+#: lib/search.tcl:24
+msgid "Prev"
+msgstr "Обратно"
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr "Игн. большие/маленькие"
 
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
@@ -1888,27 +2293,192 @@ msgstr "Программа проверки правописания не смо
 msgid "Unrecognized spell checker"
 msgstr "Нераспознаная программа проверки правописания"
 
-#: lib/spellcheck.tcl:180
+#: lib/spellcheck.tcl:186
 msgid "No Suggestions"
 msgstr "Исправлений не найдено"
 
-#: lib/spellcheck.tcl:381
+#: lib/spellcheck.tcl:388
 msgid "Unexpected EOF from spell checker"
 msgstr "Программа проверки правописания прервала передачу данных"
 
-#: lib/spellcheck.tcl:385
+#: lib/spellcheck.tcl:392
 msgid "Spell Checker Failed"
 msgstr "Ошибка проверки правописания"
 
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Ключ не найден"
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Публичный ключ из %s"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Создать ключ"
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr "Скопировать в буфер обмена"
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr "Ваш публичный ключ OpenSSH"
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr "Создание..."
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"Ошибка запуска ssh-keygen:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr "Ключ не создан."
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr "Создание ключа завершилось, но результат не был найден"
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "Ваш ключ находится в: %s"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%s ... %*i из %*i %s (%3i%%)"
 
-#: lib/transport.tcl:6
+#: lib/tools.tcl:75
 #, tcl-format
-msgid "fetch %s"
-msgstr "получение %s"
+msgid "Running %s requires a selected file."
+msgstr "Запуск %s требует выбранного файла."
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Действительно запустить %s?"
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Вспомогательная операция: %s"
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr "Выполнение: %s"
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed succesfully: %s"
+msgstr "Программа %s успешно завершилась."
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "Ошибка выполнения программы: %s"
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Добавить вспомогательную операцию"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Новая вспомогательная операция"
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr "Добавить для всех репозиториев"
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr "Описание вспомогательной операции"
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Испольуйте '/' для создания подменю"
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr "Команда:"
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr "Показать диалог перед запуском"
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Запрос на выбор версии (устанавливает $REVISION)"
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Запрос дополнительных аргументов (устанавливает $ARGS)"
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr "Не показывать окно вывода команды"
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Запуск только если показан список изменений ($FILENAME не пусто)"
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr "Укажите название вспомогательной операции."
+
+#: lib/tools_dlg.tcl:129
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "Вспомогательная операция '%s' уже существует."
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Ошибка добавления программы:\n"
+"%s"
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr "Удалить программу"
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr "Удалить команды программы"
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr "Удалить"
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Синим выделены программы локальные репозиторию)"
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Запуск команды: %s"
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr "Аргументы"
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
+msgstr "OK"
 
 #: lib/transport.tcl:7
 #, tcl-format
@@ -1926,48 +2496,46 @@ msgstr "чистка внешнего %s"
 msgid "Pruning tracking branches deleted from %s"
 msgstr "Чистка ветвей слежения, удаленных из %s"
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr "отправить %s"
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
 msgstr "Отправка изменений в %s "
 
-#: lib/transport.tcl:72
+#: lib/transport.tcl:64
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "Точное копирование в %s"
+
+#: lib/transport.tcl:82
 #, tcl-format
 msgid "Pushing %s %s to %s"
 msgstr "Отправка %s %s в %s"
 
-#: lib/transport.tcl:89
+#: lib/transport.tcl:100
 msgid "Push Branches"
 msgstr "Отправить изменения в ветвях"
 
-#: lib/transport.tcl:103
+#: lib/transport.tcl:114
 msgid "Source Branches"
 msgstr "Исходные ветви"
 
-#: lib/transport.tcl:120
+#: lib/transport.tcl:131
 msgid "Destination Repository"
 msgstr "Репозиторий назначения"
 
-#: lib/transport.tcl:158
+#: lib/transport.tcl:169
 msgid "Transfer Options"
 msgstr "Настройки отправки"
 
-#: lib/transport.tcl:160
+#: lib/transport.tcl:171
 msgid "Force overwrite existing branch (may discard changes)"
 msgstr "Намеренно переписать существующую ветвь (возможна потеря изменений)"
 
-#: lib/transport.tcl:164
+#: lib/transport.tcl:175
 msgid "Use thin pack (for slow network connections)"
 msgstr "Использовать thin pack (для медленных сетевых подключений)"
 
-#: lib/transport.tcl:168
+#: lib/transport.tcl:179
 msgid "Include tags"
-msgstr "Передать таги"
+msgstr "Передать метки"
 
-#~ msgid "Next >"
-#~ msgstr "Дальше >"
index 167654c7094c73f85b212ce5f48c401a31fca91b..c1535f94e86f2d619b4609965324a7c8ca0b396c 100644 (file)
@@ -780,16 +780,6 @@ 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
-msgid ""
-"Recovering deleted branches is difficult. \n"
-"\n"
-" Delete the selected branches?"
-msgstr ""
-"Det är svårt att återställa borttagna grenar.\n"
-"\n"
-" Ta bort valda grenar?"
-
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
@@ -2398,7 +2388,7 @@ msgstr "Exekverar: %s"
 
 #: lib/tools.tcl:149
 #, tcl-format
-msgid "Tool completed succesfully: %s"
+msgid "Tool completed successfully: %s"
 msgstr "Verktyget avslutades framgångsrikt: %s"
 
 #: lib/tools.tcl:151
index d2c686667163ec4cd83045efd429e3413564290e..91c1be23c2bb8e87a6e08188f5a0f0b860af4242 100644 (file)
@@ -676,16 +676,6 @@ msgstr "总是合并 (不作合并测试.)"
 msgid "The following branches are not completely merged into %s:"
 msgstr "下列分支没有完全被合并到 %s:"
 
-#: lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult. \n"
-"\n"
-" Delete the selected branches?"
-msgstr ""
-"恢复被删除的分支非常困难.\n"
-"\n"
-"是否要删除所选分支?"
-
 #: lib/branch_delete.tcl:141
 #, tcl-format
 msgid ""
index 53c3a94686813936445efbb055dc4f02885c70e9..66bbb2f8faaf83bc87819a9e288a0592f400e147 100644 (file)
@@ -3,7 +3,12 @@
 exec wish "$0" -- "$@"
 
 if { $argc >=2 && [lindex $argv 0] == "--working-dir" } {
-       cd [lindex $argv 1]
+       set workdir [lindex $argv 1]
+       cd $workdir
+       if {[lindex [file split $workdir] end] eq {.git}} {
+               # Workaround for Explorer right click "Git GUI Here" on .git/
+               cd ..
+       }
        set argv [lrange $argv 2 end]
        incr argc -2
 }
index 0843372b57371b62cd68f2818f634209f55d5395..5f4419b69b58769e8fee7a60109520a095a831c5 100755 (executable)
@@ -49,7 +49,7 @@ resolve_full_httpd () {
        esac
 
        httpd_only="$(echo $httpd | cut -f1 -d' ')"
-       if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac
+       if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null 2>&1;; esac
        then
                full_httpd=$httpd
        else
@@ -179,11 +179,74 @@ lighttpd_conf () {
        cat > "$conf" <<EOF
 server.document-root = "$fqgitdir/gitweb"
 server.port = $port
-server.modules = ( "mod_cgi" )
+server.modules = ( "mod_setenv", "mod_cgi" )
 server.indexfiles = ( "gitweb.cgi" )
 server.pid-file = "$fqgitdir/pid"
+server.errorlog = "$fqgitdir/gitweb/error.log"
+
+# to enable, add "mod_access", "mod_accesslog" to server.modules
+# variable above and uncomment this
+#accesslog.filename = "$fqgitdir/gitweb/access.log"
+
+setenv.add-environment = ( "PATH" => "/usr/local/bin:/usr/bin:/bin" )
+
 cgi.assign = ( ".cgi" => "" )
-mimetype.assign = ( ".css" => "text/css" )
+
+# mimetype mapping
+mimetype.assign             = (
+  ".pdf"          =>      "application/pdf",
+  ".sig"          =>      "application/pgp-signature",
+  ".spl"          =>      "application/futuresplash",
+  ".class"        =>      "application/octet-stream",
+  ".ps"           =>      "application/postscript",
+  ".torrent"      =>      "application/x-bittorrent",
+  ".dvi"          =>      "application/x-dvi",
+  ".gz"           =>      "application/x-gzip",
+  ".pac"          =>      "application/x-ns-proxy-autoconfig",
+  ".swf"          =>      "application/x-shockwave-flash",
+  ".tar.gz"       =>      "application/x-tgz",
+  ".tgz"          =>      "application/x-tgz",
+  ".tar"          =>      "application/x-tar",
+  ".zip"          =>      "application/zip",
+  ".mp3"          =>      "audio/mpeg",
+  ".m3u"          =>      "audio/x-mpegurl",
+  ".wma"          =>      "audio/x-ms-wma",
+  ".wax"          =>      "audio/x-ms-wax",
+  ".ogg"          =>      "application/ogg",
+  ".wav"          =>      "audio/x-wav",
+  ".gif"          =>      "image/gif",
+  ".jpg"          =>      "image/jpeg",
+  ".jpeg"         =>      "image/jpeg",
+  ".png"          =>      "image/png",
+  ".xbm"          =>      "image/x-xbitmap",
+  ".xpm"          =>      "image/x-xpixmap",
+  ".xwd"          =>      "image/x-xwindowdump",
+  ".css"          =>      "text/css",
+  ".html"         =>      "text/html",
+  ".htm"          =>      "text/html",
+  ".js"           =>      "text/javascript",
+  ".asc"          =>      "text/plain",
+  ".c"            =>      "text/plain",
+  ".cpp"          =>      "text/plain",
+  ".log"          =>      "text/plain",
+  ".conf"         =>      "text/plain",
+  ".text"         =>      "text/plain",
+  ".txt"          =>      "text/plain",
+  ".dtd"          =>      "text/xml",
+  ".xml"          =>      "text/xml",
+  ".mpeg"         =>      "video/mpeg",
+  ".mpg"          =>      "video/mpeg",
+  ".mov"          =>      "video/quicktime",
+  ".qt"           =>      "video/quicktime",
+  ".avi"          =>      "video/x-msvideo",
+  ".asf"          =>      "video/x-ms-asf",
+  ".asx"          =>      "video/x-ms-asf",
+  ".wmv"          =>      "video/x-ms-wmv",
+  ".bz2"          =>      "application/x-bzip",
+  ".tbz"          =>      "application/x-bzip-compressed-tar",
+  ".tar.bz2"      =>      "application/x-bzip-compressed-tar",
+  ""              =>      "text/plain"
+ )
 EOF
        test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf"
 }
index e1eb9632660146396a0b5f3f2a410d8cb027ff9d..9c2c1b7202462370509a7bdb391982ae32564bd6 100755 (executable)
@@ -113,6 +113,10 @@ case "${1:-.}${2:-.}${3:-.}" in
        src1=`git-unpack-file $2`
        git merge-file "$src1" "$orig" "$src2"
        ret=$?
+       msg=
+       if [ $ret -ne 0 ]; then
+               msg='content conflict'
+       fi
 
        # Create the working tree file, using "our tree" version from the
        # index, and then store the result of the merge.
@@ -120,7 +124,10 @@ case "${1:-.}${2:-.}${3:-.}" in
        rm -f -- "$orig" "$src1" "$src2"
 
        if [ "$6" != "$7" ]; then
-               echo "ERROR: Permissions conflict: $5->$6,$7."
+               if [ -n "$msg" ]; then
+                       msg="$msg, "
+               fi
+               msg="${msg}permissions conflict: $5->$6,$7"
                ret=1
        fi
        if [ "$1" = '' ]; then
@@ -128,7 +135,7 @@ case "${1:-.}${2:-.}${3:-.}" in
        fi
 
        if [ $ret -ne 0 ]; then
-               echo "ERROR: Merge conflict in $4"
+               echo "ERROR: $msg in $4"
                exit 1
        fi
        exec git update-index -- "$4"
index 93bcfc2f5dce418d00f26257788932d5c738785c..c9da747fcfe504b1fd233c68d91e549def0f3571 100755 (executable)
@@ -37,10 +37,10 @@ then
        exit 2
 fi
 
-git update-index --refresh 2>/dev/null
+git update-index -q --refresh
 git read-tree -u -m --aggressive $bases $head $remotes || exit 2
 echo "Trying simple merge."
-if result_tree=$(git write-tree  2>/dev/null)
+if result_tree=$(git write-tree 2>/dev/null)
 then
        exit 0
 else
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
new file mode 100644 (file)
index 0000000..a16a279
--- /dev/null
@@ -0,0 +1,385 @@
+# git-mergetool--lib is a library for common merge tool functions
+diff_mode() {
+       test "$TOOL_MODE" = diff
+}
+
+merge_mode() {
+       test "$TOOL_MODE" = merge
+}
+
+translate_merge_tool_path () {
+       case "$1" in
+       vimdiff)
+               echo vim
+               ;;
+       gvimdiff)
+               echo gvim
+               ;;
+       emerge)
+               echo emacs
+               ;;
+       *)
+               echo "$1"
+               ;;
+       esac
+}
+
+check_unchanged () {
+       if test "$MERGED" -nt "$BACKUP"; then
+               status=0
+       else
+               while true; do
+                       echo "$MERGED seems unchanged."
+                       printf "Was the merge successful? [y/n] "
+                       read answer < /dev/tty
+                       case "$answer" in
+                       y*|Y*) status=0; break ;;
+                       n*|N*) status=1; break ;;
+                       esac
+               done
+       fi
+}
+
+valid_tool () {
+       case "$1" in
+       kdiff3 | tkdiff | xxdiff | meld | opendiff | \
+       emerge | vimdiff | gvimdiff | ecmerge | diffuse)
+               ;; # happy
+       tortoisemerge)
+               if ! merge_mode; then
+                       return 1
+               fi
+               ;;
+       kompare)
+               if ! diff_mode; then
+                       return 1
+               fi
+               ;;
+       *)
+               if test -z "$(get_merge_tool_cmd "$1")"; then
+                       return 1
+               fi
+               ;;
+       esac
+}
+
+get_merge_tool_cmd () {
+       # Prints the custom command for a merge tool
+       if test -n "$1"; then
+               merge_tool="$1"
+       else
+               merge_tool="$(get_merge_tool)"
+       fi
+       if diff_mode; then
+               echo "$(git config difftool.$merge_tool.cmd ||
+                       git config mergetool.$merge_tool.cmd)"
+       else
+               echo "$(git config mergetool.$merge_tool.cmd)"
+       fi
+}
+
+run_merge_tool () {
+       merge_tool_path="$(get_merge_tool_path "$1")" || exit
+       base_present="$2"
+       status=0
+
+       case "$1" in
+       kdiff3)
+               if merge_mode; then
+                       if $base_present; then
+                               ("$merge_tool_path" --auto \
+                                       --L1 "$MERGED (Base)" \
+                                       --L2 "$MERGED (Local)" \
+                                       --L3 "$MERGED (Remote)" \
+                                       -o "$MERGED" \
+                                       "$BASE" "$LOCAL" "$REMOTE" \
+                               > /dev/null 2>&1)
+                       else
+                               ("$merge_tool_path" --auto \
+                                       --L1 "$MERGED (Local)" \
+                                       --L2 "$MERGED (Remote)" \
+                                       -o "$MERGED" \
+                                       "$LOCAL" "$REMOTE" \
+                               > /dev/null 2>&1)
+                       fi
+                       status=$?
+               else
+                       ("$merge_tool_path" --auto \
+                               --L1 "$MERGED (A)" \
+                               --L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
+                       > /dev/null 2>&1)
+               fi
+               ;;
+       kompare)
+               "$merge_tool_path" "$LOCAL" "$REMOTE"
+               ;;
+       tkdiff)
+               if merge_mode; then
+                       if $base_present; then
+                               "$merge_tool_path" -a "$BASE" \
+                                       -o "$MERGED" "$LOCAL" "$REMOTE"
+                       else
+                               "$merge_tool_path" \
+                                       -o "$MERGED" "$LOCAL" "$REMOTE"
+                       fi
+                       status=$?
+               else
+                       "$merge_tool_path" "$LOCAL" "$REMOTE"
+               fi
+               ;;
+       meld)
+               if merge_mode; then
+                       touch "$BACKUP"
+                       "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
+                       check_unchanged
+               else
+                       "$merge_tool_path" "$LOCAL" "$REMOTE"
+               fi
+               ;;
+       diffuse)
+               if merge_mode; then
+                       touch "$BACKUP"
+                       if $base_present; then
+                               "$merge_tool_path" \
+                                       "$LOCAL" "$MERGED" "$REMOTE" \
+                                       "$BASE" | cat
+                       else
+                               "$merge_tool_path" \
+                                       "$LOCAL" "$MERGED" "$REMOTE" | cat
+                       fi
+                       check_unchanged
+               else
+                       "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
+               fi
+               ;;
+       vimdiff)
+               if merge_mode; then
+                       touch "$BACKUP"
+                       "$merge_tool_path" -d -c "wincmd l" \
+                               "$LOCAL" "$MERGED" "$REMOTE"
+                       check_unchanged
+               else
+                       "$merge_tool_path" -d -c "wincmd l" \
+                               "$LOCAL" "$REMOTE"
+               fi
+               ;;
+       gvimdiff)
+               if merge_mode; then
+                       touch "$BACKUP"
+                       "$merge_tool_path" -d -c "wincmd l" -f \
+                               "$LOCAL" "$MERGED" "$REMOTE"
+                       check_unchanged
+               else
+                       "$merge_tool_path" -d -c "wincmd l" -f \
+                               "$LOCAL" "$REMOTE"
+               fi
+               ;;
+       xxdiff)
+               if merge_mode; then
+                       touch "$BACKUP"
+                       if $base_present; then
+                               "$merge_tool_path" -X --show-merged-pane \
+                                       -R 'Accel.SaveAsMerged: "Ctrl-S"' \
+                                       -R 'Accel.Search: "Ctrl+F"' \
+                                       -R 'Accel.SearchForward: "Ctrl-G"' \
+                                       --merged-file "$MERGED" \
+                                       "$LOCAL" "$BASE" "$REMOTE"
+                       else
+                               "$merge_tool_path" -X $extra \
+                                       -R 'Accel.SaveAsMerged: "Ctrl-S"' \
+                                       -R 'Accel.Search: "Ctrl+F"' \
+                                       -R 'Accel.SearchForward: "Ctrl-G"' \
+                                       --merged-file "$MERGED" \
+                                       "$LOCAL" "$REMOTE"
+                       fi
+                       check_unchanged
+               else
+                       "$merge_tool_path" \
+                               -R 'Accel.Search: "Ctrl+F"' \
+                               -R 'Accel.SearchForward: "Ctrl-G"' \
+                               "$LOCAL" "$REMOTE"
+               fi
+               ;;
+       opendiff)
+               if merge_mode; then
+                       touch "$BACKUP"
+                       if $base_present; then
+                               "$merge_tool_path" "$LOCAL" "$REMOTE" \
+                                       -ancestor "$BASE" \
+                                       -merge "$MERGED" | cat
+                       else
+                               "$merge_tool_path" "$LOCAL" "$REMOTE" \
+                                       -merge "$MERGED" | cat
+                       fi
+                       check_unchanged
+               else
+                       "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
+               fi
+               ;;
+       ecmerge)
+               if merge_mode; then
+                       touch "$BACKUP"
+                       if $base_present; then
+                               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
+                                       --default --mode=merge3 --to="$MERGED"
+                       else
+                               "$merge_tool_path" "$LOCAL" "$REMOTE" \
+                                       --default --mode=merge2 --to="$MERGED"
+                       fi
+                       check_unchanged
+               else
+                       "$merge_tool_path" "$LOCAL" "$REMOTE" \
+                               --default --mode=merge2 --to="$MERGED"
+               fi
+               ;;
+       emerge)
+               if merge_mode; then
+                       if $base_present; then
+                               "$merge_tool_path" \
+                                       -f emerge-files-with-ancestor-command \
+                                       "$LOCAL" "$REMOTE" "$BASE" \
+                                       "$(basename "$MERGED")"
+                       else
+                               "$merge_tool_path" \
+                                       -f emerge-files-command \
+                                       "$LOCAL" "$REMOTE" \
+                                       "$(basename "$MERGED")"
+                       fi
+                       status=$?
+               else
+                       "$merge_tool_path" -f emerge-files-command \
+                               "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
+               fi
+               ;;
+       tortoisemerge)
+               if $base_present; then
+                       touch "$BACKUP"
+                       "$merge_tool_path" \
+                               -base:"$BASE" -mine:"$LOCAL" \
+                               -theirs:"$REMOTE" -merged:"$MERGED"
+                       check_unchanged
+               else
+                       echo "TortoiseMerge cannot be used without a base" 1>&2
+                       status=1
+               fi
+               ;;
+       *)
+               merge_tool_cmd="$(get_merge_tool_cmd "$1")"
+               if test -z "$merge_tool_cmd"; then
+                       if merge_mode; then
+                               status=1
+                       fi
+                       break
+               fi
+               if merge_mode; then
+                       trust_exit_code="$(git config --bool \
+                               mergetool."$1".trustExitCode || echo false)"
+                       if test "$trust_exit_code" = "false"; then
+                               touch "$BACKUP"
+                               ( eval $merge_tool_cmd )
+                               check_unchanged
+                       else
+                               ( eval $merge_tool_cmd )
+                               status=$?
+                       fi
+               else
+                       ( eval $merge_tool_cmd )
+               fi
+               ;;
+       esac
+       return $status
+}
+
+guess_merge_tool () {
+       if merge_mode; then
+               tools="tortoisemerge"
+       else
+               tools="kompare"
+       fi
+       if test -n "$DISPLAY"; then
+               if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
+                       tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
+               else
+                       tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
+               fi
+               tools="$tools gvimdiff diffuse ecmerge"
+       fi
+       if echo "${VISUAL:-$EDITOR}" | grep emacs > /dev/null 2>&1; then
+               # $EDITOR is emacs so add emerge as a candidate
+               tools="$tools emerge vimdiff"
+       elif echo "${VISUAL:-$EDITOR}" | grep vim > /dev/null 2>&1; then
+               # $EDITOR is vim so add vimdiff as a candidate
+               tools="$tools vimdiff emerge"
+       else
+               tools="$tools emerge vimdiff"
+       fi
+       echo >&2 "merge tool candidates: $tools"
+
+       # Loop over each candidate and stop when a valid merge tool is found.
+       for i in $tools
+       do
+               merge_tool_path="$(translate_merge_tool_path "$i")"
+               if type "$merge_tool_path" > /dev/null 2>&1; then
+                       echo "$i"
+                       return 0
+               fi
+       done
+
+       echo >&2 "No known merge resolution program available."
+       return 1
+}
+
+get_configured_merge_tool () {
+       # Diff mode first tries diff.tool and falls back to merge.tool.
+       # Merge mode only checks merge.tool
+       if diff_mode; then
+               merge_tool=$(git config diff.tool || git config merge.tool)
+       else
+               merge_tool=$(git config merge.tool)
+       fi
+       if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
+               echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
+               echo >&2 "Resetting to default..."
+               return 1
+       fi
+       echo "$merge_tool"
+}
+
+get_merge_tool_path () {
+       # A merge tool has been set, so verify that it's valid.
+       if test -n "$1"; then
+               merge_tool="$1"
+       else
+               merge_tool="$(get_merge_tool)"
+       fi
+       if ! valid_tool "$merge_tool"; then
+               echo >&2 "Unknown merge tool $merge_tool"
+               exit 1
+       fi
+       if diff_mode; then
+               merge_tool_path=$(git config difftool."$merge_tool".path ||
+                                 git config mergetool."$merge_tool".path)
+       else
+               merge_tool_path=$(git config mergetool."$merge_tool".path)
+       fi
+       if test -z "$merge_tool_path"; then
+               merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
+       fi
+       if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
+       ! type "$merge_tool_path" > /dev/null 2>&1; then
+               echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
+                        "'$merge_tool_path'"
+               exit 1
+       fi
+       echo "$merge_tool_path"
+}
+
+get_merge_tool () {
+       # Check if a merge tool has been configured
+       merge_tool=$(get_configured_merge_tool)
+       # Try to guess an appropriate merge tool if no tool has been set.
+       if test -z "$merge_tool"; then
+               merge_tool="$(guess_merge_tool)" || exit
+       fi
+       echo "$merge_tool"
+}
index 87fa88af5526c8e27b823a65ca15bee4085f8ef2..b52a7410bcb7b37dce0f4d6213dddedd2c1e42e7 100755 (executable)
@@ -11,7 +11,9 @@
 USAGE='[--tool=tool] [-y|--no-prompt|--prompt] [file to merge] ...'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
+TOOL_MODE=merge
 . git-sh-setup
+. git-mergetool--lib
 require_work_tree
 
 # Returns true if the mode reflects a symlink
@@ -110,22 +112,6 @@ resolve_deleted_merge () {
        done
 }
 
-check_unchanged () {
-    if test "$MERGED" -nt "$BACKUP" ; then
-       status=0;
-    else
-       while true; do
-           echo "$MERGED seems unchanged."
-           printf "Was the merge successful? [y/n] "
-           read answer < /dev/tty
-           case "$answer" in
-               y*|Y*) status=0; break ;;
-               n*|N*) status=1; break ;;
-           esac
-       done
-    fi
-}
-
 checkout_staged_file () {
     tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^    ]*\)    ')
 
@@ -137,7 +123,7 @@ checkout_staged_file () {
 merge_file () {
     MERGED="$1"
 
-    f=`git ls-files -u -- "$MERGED"`
+    f=$(git ls-files -u -- "$MERGED")
     if test -z "$f" ; then
        if test ! -f "$MERGED" ; then
            echo "$MERGED: file not found"
@@ -156,9 +142,9 @@ merge_file () {
     mv -- "$MERGED" "$BACKUP"
     cp -- "$BACKUP" "$MERGED"
 
-    base_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}'`
-    local_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}'`
-    remote_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}'`
+    base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
+    local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
+    remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
 
     base_present   && checkout_staged_file 1 "$MERGED" "$BASE"
     local_present  && checkout_staged_file 2 "$MERGED" "$LOCAL"
@@ -188,97 +174,13 @@ merge_file () {
        read ans
     fi
 
-    case "$merge_tool" in
-       kdiff3)
-           if base_present ; then
-               ("$merge_tool_path" --auto --L1 "$MERGED (Base)" --L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" \
-                   -o "$MERGED" "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
-           else
-               ("$merge_tool_path" --auto --L1 "$MERGED (Local)" --L2 "$MERGED (Remote)" \
-                   -o "$MERGED" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
-           fi
-           status=$?
-           ;;
-       tkdiff)
-           if base_present ; then
-               "$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE"
-           else
-               "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
-           fi
-           status=$?
-           ;;
-       meld)
-           touch "$BACKUP"
-           "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
-           check_unchanged
-           ;;
-       vimdiff)
-           touch "$BACKUP"
-           "$merge_tool_path" -c "wincmd l" "$LOCAL" "$MERGED" "$REMOTE"
-           check_unchanged
-           ;;
-       gvimdiff)
-           touch "$BACKUP"
-           "$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$MERGED" "$REMOTE"
-           check_unchanged
-           ;;
-       xxdiff)
-           touch "$BACKUP"
-           if base_present ; then
-               "$merge_tool_path" -X --show-merged-pane \
-                   -R 'Accel.SaveAsMerged: "Ctrl-S"' \
-                   -R 'Accel.Search: "Ctrl+F"' \
-                   -R 'Accel.SearchForward: "Ctrl-G"' \
-                   --merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE"
-           else
-               "$merge_tool_path" -X --show-merged-pane \
-                   -R 'Accel.SaveAsMerged: "Ctrl-S"' \
-                   -R 'Accel.Search: "Ctrl+F"' \
-                   -R 'Accel.SearchForward: "Ctrl-G"' \
-                   --merged-file "$MERGED" "$LOCAL" "$REMOTE"
-           fi
-           check_unchanged
-           ;;
-       opendiff)
-           touch "$BACKUP"
-           if base_present; then
-               "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED" | cat
-           else
-               "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED" | cat
-           fi
-           check_unchanged
-           ;;
-       ecmerge)
-           touch "$BACKUP"
-           if base_present; then
-               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --default --mode=merge3 --to="$MERGED"
-           else
-               "$merge_tool_path" "$LOCAL" "$REMOTE" --default --mode=merge2 --to="$MERGED"
-           fi
-           check_unchanged
-           ;;
-       emerge)
-           if base_present ; then
-               "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$MERGED")"
-           else
-               "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
-           fi
-           status=$?
-           ;;
-       *)
-           if test -n "$merge_tool_cmd"; then
-               if test "$merge_tool_trust_exit_code" = "false"; then
-                   touch "$BACKUP"
-                   ( eval $merge_tool_cmd )
-                   check_unchanged
-               else
-                   ( eval $merge_tool_cmd )
-                   status=$?
-               fi
-           fi
-           ;;
-    esac
-    if test "$status" -ne 0; then
+    if base_present; then
+           present=true
+    else
+           present=false
+    fi
+
+    if ! run_merge_tool "$merge_tool" "$present"; then
        echo "merge of $MERGED failed" 1>&2
        mv -- "$BACKUP" "$MERGED"
 
@@ -308,7 +210,7 @@ do
        -t|--tool*)
            case "$#,$1" in
                *,*=*)
-                   merge_tool=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+                   merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
                    ;;
                1,*)
                    usage ;;
@@ -337,38 +239,6 @@ do
     shift
 done
 
-valid_custom_tool()
-{
-    merge_tool_cmd="$(git config mergetool.$1.cmd)"
-    test -n "$merge_tool_cmd"
-}
-
-valid_tool() {
-       case "$1" in
-               kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
-                       ;; # happy
-               *)
-                       if ! valid_custom_tool "$1"; then
-                               return 1
-                       fi
-                       ;;
-       esac
-}
-
-init_merge_tool_path() {
-       merge_tool_path=`git config mergetool.$1.path`
-       if test -z "$merge_tool_path" ; then
-               case "$1" in
-                       emerge)
-                               merge_tool_path=emacs
-                               ;;
-                       *)
-                               merge_tool_path=$1
-                               ;;
-               esac
-       fi
-}
-
 prompt_after_failed_merge() {
     while true; do
        printf "Continue merging other unresolved paths (y/n) ? "
@@ -387,67 +257,16 @@ prompt_after_failed_merge() {
 }
 
 if test -z "$merge_tool"; then
-    merge_tool=`git config merge.tool`
-    if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
-           echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
-           echo >&2 "Resetting to default..."
-           unset merge_tool
-    fi
-fi
-
-if test -z "$merge_tool" ; then
-    if test -n "$DISPLAY"; then
-        if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
-            merge_tool_candidates="meld kdiff3 tkdiff xxdiff gvimdiff"
-        else
-            merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
-        fi
-    fi
-    if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
-        merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
-    elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
-        merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
-    else
-        merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
-    fi
-    echo "merge tool candidates: $merge_tool_candidates"
-    for i in $merge_tool_candidates; do
-        init_merge_tool_path $i
-        if type "$merge_tool_path" > /dev/null 2>&1; then
-            merge_tool=$i
-            break
-        fi
-    done
-    if test -z "$merge_tool" ; then
-       echo "No known merge resolution program available."
-       exit 1
-    fi
-else
-    if ! valid_tool "$merge_tool"; then
-        echo >&2 "Unknown merge_tool $merge_tool"
-        exit 1
-    fi
-
-    init_merge_tool_path "$merge_tool"
-
-    merge_keep_backup="$(git config --bool merge.keepBackup || echo true)"
-    merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
-
-    if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
-        echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
-        exit 1
-    fi
-
-    if ! test -z "$merge_tool_cmd"; then
-        merge_tool_trust_exit_code="$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)"
-    fi
+    merge_tool=$(get_merge_tool "$merge_tool") || exit
 fi
+merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
+merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
 
 last_status=0
 rollup_status=0
 
 if test $# -eq 0 ; then
-    files=`git ls-files -u | sed -e 's/^[^     ]*      //' | sort -u`
+    files=$(git ls-files -u | sed -e 's/^[^    ]*      //' | sort -u)
     if test -z "$files" ; then
        echo "No files need merging"
        exit 0
index 695a4094bb4230341618bd6f16d0bea9bff2e826..a296719861ec3b7aab5e530e67dc99b58267a56a 100755 (executable)
@@ -2,7 +2,7 @@
 
 # git-ls-remote could be called from outside a git managed repository;
 # this would fail in that case and would issue an error message.
-GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) || :;
+GIT_DIR=$(git rev-parse -q --git-dir) || :;
 
 get_data_source () {
        case "$1" in
index c8920596050abc531d721dc95dfc8b11096ecd92..cab367ada009659da23774ec303119af3d972038 100755 (executable)
@@ -16,7 +16,7 @@ cd_to_toplevel
 test -z "$(git ls-files -u)" ||
        die "You are in the middle of a conflicted merge."
 
-strategy_args= no_stat= no_commit= squash= no_ff= log_arg= verbosity=
+strategy_args= diffstat= no_commit= squash= no_ff= log_arg= verbosity=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
@@ -28,9 +28,9 @@ do
        -v|--verbose)
                verbosity="$verbosity -v" ;;
        -n|--no-stat|--no-summary)
-               no_stat=-n ;;
+               diffstat=--no-stat ;;
        --stat|--summary)
-               no_stat=$1 ;;
+               diffstat=--stat ;;
        --log|--no-log)
                log_arg=$1 ;;
        --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
@@ -147,7 +147,7 @@ then
        echo >&2 "Warning: fetch updated the current branch head."
        echo >&2 "Warning: fast forwarding your working tree from"
        echo >&2 "Warning: commit $orig_head."
-       git update-index --refresh 2>/dev/null
+       git update-index -q --refresh
        git read-tree -u -m "$orig_head" "$curr_head" ||
                die 'Cannot fast-forward your working tree.
 After making sure that you saved anything precious from
@@ -176,13 +176,11 @@ case "$merge_head" in
 ?*' '?*)
        if test -z "$orig_head"
        then
-               echo >&2 "Cannot merge multiple branches into empty head"
-               exit 1
+               die "Cannot merge multiple branches into empty head"
        fi
        if test true = "$rebase"
        then
-               echo >&2 "Cannot rebase onto multiple branches"
-               exit 1
+               die "Cannot rebase onto multiple branches"
        fi
        ;;
 esac
@@ -196,7 +194,7 @@ fi
 
 merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 test true = "$rebase" &&
-       exec git-rebase $strategy_args --onto $merge_head \
+       exec git-rebase $diffstat $strategy_args --onto $merge_head \
        ${oldremoteref:-$merge_head}
-exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \
+exec git-merge $diffstat $no_commit $squash $no_ff $log_arg $strategy_args \
        "$merge_name" HEAD $merge_head $verbosity
index 3dc659dd5896ce6c887eb786718919aef82f4ac6..f96d887d23653019e3387eced2779d50b3f09fa2 100755 (executable)
@@ -420,7 +420,7 @@ do_next () {
        NEWHEAD=$(git rev-parse HEAD) &&
        case $HEADNAME in
        refs/*)
-               message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
+               message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO" &&
                git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
                git symbolic-ref HEAD $HEADNAME
                ;;
@@ -442,6 +442,30 @@ do_rest () {
        done
 }
 
+# skip picking commits whose parents are unchanged
+skip_unnecessary_picks () {
+       fd=3
+       while read command sha1 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"*)
+                       # pick a commit whose parent is current $ONTO -> skip
+                       ONTO=$sha1
+                       ;;
+               3,#*|3,,*)
+                       # copy comments
+                       ;;
+               *)
+                       fd=1
+                       ;;
+               esac
+               echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
+       done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
+       mv -f "$TODO".new "$TODO" ||
+       die "Could not skip unnecessary pick commands"
+}
+
 # check if no other options are set
 is_standalone () {
        test $# -eq 2 -a "$2" = '--' &&
@@ -746,6 +770,8 @@ EOF
                has_action "$TODO" ||
                        die_abort "Nothing to do"
 
+               test -d "$REWRITTEN" || skip_unnecessary_picks
+
                git update-ref ORIG_HEAD $HEAD
                output git checkout $ONTO && do_rest
                ;;
index 368c0ef4342df990a5617fd6fbe2cd973b1b208b..334629fc97c0d06e9c9cfda058bdd999810f9d55 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano.
 #
 
-USAGE='[--interactive | -i] [-v] [--onto <newbase>] [<upstream>|--root] [<branch>]'
+USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>]'
 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
 same name.  When the --onto option is provided the new branch starts
 out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -46,8 +46,10 @@ do_merge=
 dotest="$GIT_DIR"/rebase-merge
 prec=4
 verbose=
+diffstat=$(git config --bool rebase.stat)
 git_am_opt=
 rebase_root=
+force_rebase=
 
 continue_merge () {
        test -n "$prev_head" || die "prev_head must be defined"
@@ -166,10 +168,8 @@ run_pre_rebase_hook () {
        if test -z "$OK_TO_SKIP_PRE_REBASE" &&
           test -x "$GIT_DIR/hooks/pre-rebase"
        then
-               "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
-                       echo >&2 "The pre-rebase hook refused to rebase."
-                       exit 1
-               }
+               "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
+               die "The pre-rebase hook refused to rebase."
        fi
 }
 
@@ -289,11 +289,27 @@ do
                esac
                do_merge=t
                ;;
+       -n|--no-stat)
+               diffstat=
+               ;;
+       --stat)
+               diffstat=t
+               ;;
        -v|--verbose)
                verbose=t
+               diffstat=t
                ;;
        --whitespace=*)
                git_am_opt="$git_am_opt $1"
+               case "$1" in
+               --whitespace=fix|--whitespace=strip)
+                       force_rebase=t
+                       ;;
+               esac
+               ;;
+       --committer-date-is-author-date|--ignore-date)
+               git_am_opt="$git_am_opt $1"
+               force_rebase=t
                ;;
        -C*)
                git_am_opt="$git_am_opt $1"
@@ -301,6 +317,9 @@ do
        --root)
                rebase_root=t
                ;;
+       -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase)
+               force_rebase=t
+               ;;
        -*)
                usage
                ;;
@@ -338,8 +357,7 @@ fi
 
 # The tree must be really really clean.
 if ! git update-index --ignore-submodules --refresh; then
-       echo >&2 "cannot rebase: you have unstaged changes"
-       exit 1
+       die "cannot rebase: you have unstaged changes"
 fi
 diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
 case "$diff" in
@@ -420,17 +438,15 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
        # linear history?
        ! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null
 then
-       # Lazily switch to the target branch if needed...
-       test -z "$switch_to" || git checkout "$switch_to"
-       echo >&2 "Current branch $branch_name is up to date."
-       exit 0
-fi
-
-if test -n "$verbose"
-then
-       echo "Changes from $mb to $onto:"
-       # We want color (if set), but no pager
-       GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
+       if test -z "$force_rebase"
+       then
+               # Lazily switch to the target branch if needed...
+               test -z "$switch_to" || git checkout "$switch_to"
+               echo >&2 "Current branch $branch_name is up to date."
+               exit 0
+       else
+               echo "Current branch $branch_name is up to date, rebase forced."
+       fi
 fi
 
 # Detach HEAD and reset the tree
@@ -438,6 +454,16 @@ echo "First, rewinding head to replay your work on top of it..."
 git checkout -q "$onto^0" || die "could not detach HEAD"
 git update-ref ORIG_HEAD $branch
 
+if test -n "$diffstat"
+then
+       if test -n "$verbose"
+       then
+               echo "Changes from $mb to $onto:"
+       fi
+       # We want color (if set), but no pager
+       GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
+fi
+
 # If the $onto is a proper descendant of the tip of the branch, then
 # we just fast forwarded.
 if test "$mb" = "$branch"
index a2cf5b82150a77fd9ddb775fea1bc8d5e8ee7392..fd95beadab6aa01fc3fb26c7d72ba182201f8502 100755 (executable)
@@ -12,6 +12,9 @@ OPTIONS_SPEC=
 . git-sh-setup
 . git-parse-remote
 
+GIT_PAGER=
+export GIT_PAGER
+
 base=$1
 url=$2
 head=${3-HEAD}
@@ -25,16 +28,16 @@ headrev=`git rev-parse --verify "$head"^0` || exit
 merge_base=`git merge-base $baserev $headrev` ||
 die "fatal: No commits in common between $base and $head"
 
-url=$(get_remote_url "$url")
 branch=$(git ls-remote "$url" \
        | sed -n -e "/^$headrev refs.heads./{
                s/^.*   refs.heads.//
                p
                q
        }")
+url=$(get_remote_url "$url")
 if [ -z "$branch" ]; then
        echo "warn: No branch of $url is at:" >&2
-       git log --max-count=1 --pretty='format:warn:   %h: %s' $headrev >&2
+       git log --max-count=1 --pretty='tformat:warn:   %h: %s' $headrev >&2
        echo "warn: Are you sure you pushed $head there?" >&2
        echo >&2
        echo >&2
@@ -42,8 +45,6 @@ if [ -z "$branch" ]; then
        status=1
 fi
 
-PAGER=
-export PAGER
 echo "The following changes since commit $baserev:"
 git shortlog --max-count=1 $baserev | sed -e 's/^\(.\)/  \1/'
 
index 09fe3d95c4e076af1bf6230d31ca7b67e1893dc2..17f930f0f3c975725d83a5b0865b85a52c8741d0 100755 (executable)
@@ -75,6 +75,8 @@ git send-email [options] <file | directory | rev-list options >
     --[no-]thread                  * Use In-Reply-To: field. Default on.
 
   Administering:
+    --confirm               <str>  * Confirm recipients before sending;
+                                     auto, cc, compose, always, or never.
     --quiet                        * Output one line of info per email.
     --dry-run                      * Don't actually send the emails.
     --[no-]validate                * Perform patch sanity checks. Default on.
@@ -181,7 +183,7 @@ sub do_edit {
 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);
-my ($validate);
+my ($validate, $confirm);
 my (@suppress_cc);
 
 my %config_bool_settings = (
@@ -207,6 +209,7 @@ my %config_settings = (
     "suppresscc" => \@suppress_cc,
     "envelopesender" => \$envelope_sender,
     "multiedit" => \$multiedit,
+    "confirm"   => \$confirm,
 );
 
 # Handle Uncouth Termination
@@ -258,6 +261,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
                    "suppress-from!" => \$suppress_from,
                    "suppress-cc=s" => \@suppress_cc,
                    "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
+                   "confirm=s" => \$confirm,
                    "dry-run" => \$dry_run,
                    "envelope-sender=s" => \$envelope_sender,
                    "thread!" => \$thread,
@@ -346,6 +350,14 @@ if ($suppress_cc{'body'}) {
        delete $suppress_cc{'body'};
 }
 
+# Set confirm's default value
+my $confirm_unconfigured = !defined $confirm;
+if ($confirm_unconfigured) {
+       $confirm = scalar %suppress_cc ? 'compose' : 'auto';
+};
+die "Unknown --confirm setting: '$confirm'\n"
+       unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
+
 # Debugging, print out the suppressions.
 if (0) {
        print "suppressions:\n";
@@ -406,6 +418,14 @@ my %parse_alias = (
                        $x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next;
                        $aliases{$1} = [ split_addrs($2) ];
                }},
+       elm => sub  { my $fh = shift;
+                     while (<$fh>) {
+                         if (/^(\S+)\s+=\s+[^=]+=\s(\S+)/) {
+                             my ($alias, $addr) = ($1, $2);
+                              $aliases{$alias} = [ split_addrs($addr) ];
+                         }
+                     } },
+
        gnus => sub { my $fh = shift; while (<$fh>) {
                if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {
                        $aliases{$1} = [ $2 ];
@@ -429,7 +449,6 @@ sub check_file_rev_conflict($) {
        try {
                $repo->command('rev-parse', '--verify', '--quiet', $f);
                if (defined($format_patch)) {
-                       print "foo\n";
                        return $format_patch;
                }
                die(<<EOF);
@@ -594,32 +613,43 @@ EOT
        do_edit(@files);
 }
 
+sub ask {
+       my ($prompt, %arg) = @_;
+       my $valid_re = $arg{valid_re};
+       my $default = $arg{default};
+       my $resp;
+       my $i = 0;
+       return defined $default ? $default : undef
+               unless defined $term->IN and defined fileno($term->IN) and
+                      defined $term->OUT and defined fileno($term->OUT);
+       while ($i++ < 10) {
+               $resp = $term->readline($prompt);
+               if (!defined $resp) { # EOF
+                       print "\n";
+                       return defined $default ? $default : undef;
+               }
+               if ($resp eq '' and defined $default) {
+                       return $default;
+               }
+               if (!defined $valid_re or $resp =~ /$valid_re/) {
+                       return $resp;
+               }
+       }
+       return undef;
+}
+
 my $prompting = 0;
 if (!defined $sender) {
        $sender = $repoauthor || $repocommitter || '';
-
-       while (1) {
-               $_ = $term->readline("Who should the emails appear to be from? [$sender] ");
-               last if defined $_;
-               print "\n";
-       }
-
-       $sender = $_ if ($_);
+       $sender = ask("Who should the emails appear to be from? [$sender] ",
+                     default => $sender);
        print "Emails will be sent from: ", $sender, "\n";
        $prompting++;
 }
 
 if (!@to) {
-
-
-       while (1) {
-               $_ = $term->readline("Who should the emails be sent to? ", "");
-               last if defined $_;
-               print "\n";
-       }
-
-       my $to = $_;
-       push @to, parse_address_line($to);
+       my $to = ask("Who should the emails be sent to? ");
+       push @to, parse_address_line($to) if defined $to; # sanitized/validated later
        $prompting++;
 }
 
@@ -639,13 +669,8 @@ sub expand_aliases {
 @bcclist = expand_aliases(@bcclist);
 
 if ($thread && !defined $initial_reply_to && $prompting) {
-       while (1) {
-               $_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
-               last if defined $_;
-               print "\n";
-       }
-
-       $initial_reply_to = $_;
+       $initial_reply_to = ask(
+               "Message-ID to be used as In-Reply-To for the first email? ");
 }
 if (defined $initial_reply_to) {
        $initial_reply_to =~ s/^\s*<?//;
@@ -663,25 +688,13 @@ if (!defined $smtp_server) {
        $smtp_server ||= 'localhost'; # could be 127.0.0.1, too... *shrug*
 }
 
-if ($compose) {
-       while (1) {
-               $_ = $term->readline("Send this email? (y|n) ");
-               last if defined $_;
-               print "\n";
-       }
-
-       if (uc substr($_,0,1) ne 'Y') {
-               cleanup_compose_files();
-               exit(0);
-       }
-
-       if ($compose > 0) {
-               @files = ($compose_filename . ".final", @files);
-       }
+if ($compose && $compose > 0) {
+       @files = ($compose_filename . ".final", @files);
 }
 
 # Variables we set as part of the loop over files
-our ($message_id, %mail, $subject, $reply_to, $references, $message);
+our ($message_id, %mail, $subject, $reply_to, $references, $message,
+       $needs_confirm, $message_num, $ask_default);
 
 sub extract_valid_address {
        my $address = shift;
@@ -770,12 +783,13 @@ sub sanitize_address
        }
 
        # if recipient_name is already quoted, do nothing
-       if ($recipient_name =~ /^(".*"|=\?utf-8\?q\?.*\?=)$/) {
+       if ($recipient_name =~ /^("[[:ascii:]]*"|=\?utf-8\?q\?.*\?=)$/) {
                return $recipient;
        }
 
        # rfc2047 is needed if a non-ascii char is included
        if ($recipient_name =~ /[^[:ascii:]]/) {
+               $recipient_name =~ s/^"(.*)"$/$1/;
                $recipient_name = quote_rfc2047($recipient_name);
        }
 
@@ -837,6 +851,35 @@ X-Mailer: git-send-email $gitversion
        unshift (@sendmail_parameters,
                        '-f', $raw_from) if(defined $envelope_sender);
 
+       if ($needs_confirm && !$dry_run) {
+               print "\n$header\n";
+               if ($needs_confirm eq "inform") {
+                       $confirm_unconfigured = 0; # squelch this message for the rest of this run
+                       $ask_default = "y"; # assume yes on EOF since user hasn't explicitly asked for confirmation
+                       print "    The Cc list above has been expanded by additional\n";
+                       print "    addresses found in the patch commit message. By default\n";
+                       print "    send-email prompts before sending whenever this occurs.\n";
+                       print "    This behavior is controlled by the sendemail.confirm\n";
+                       print "    configuration setting.\n";
+                       print "\n";
+                       print "    For additional information, run 'git send-email --help'.\n";
+                       print "    To retain the current behavior, but squelch this message,\n";
+                       print "    run 'git config --global sendemail.confirm auto'.\n\n";
+               }
+               $_ = ask("Send this email? ([y]es|[n]o|[q]uit|[a]ll): ",
+                        valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
+                        default => $ask_default);
+               die "Send this email reply required" unless defined $_;
+               if (/^n/i) {
+                       return;
+               } elsif (/^q/i) {
+                       cleanup_compose_files();
+                       exit(0);
+               } elsif (/^a/i) {
+                       $confirm = 'never';
+               }
+       }
+
        if ($dry_run) {
                # We don't want to send the email.
        } elsif ($smtp_server =~ m#^/#) {
@@ -935,6 +978,7 @@ X-Mailer: git-send-email $gitversion
 $reply_to = $initial_reply_to;
 $references = $initial_reply_to || '';
 $subject = $initial_subject;
+$message_num = 0;
 
 foreach my $t (@files) {
        open(F,"<",$t) or die "can't open file $t";
@@ -943,11 +987,12 @@ foreach my $t (@files) {
        my $author_encoding;
        my $has_content_type;
        my $body_encoding;
-       @cc = @initial_cc;
+       @cc = ();
        @xh = ();
        my $input_format = undef;
        my @header = ();
        $message = "";
+       $message_num++;
        # First unfold multiline header fields
        while(<F>) {
                last if /^\s*$/;
@@ -1080,6 +1125,14 @@ foreach my $t (@files) {
                }
        }
 
+       $needs_confirm = (
+               $confirm eq "always" or
+               ($confirm =~ /^(?:auto|cc)$/ && @cc) or
+               ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1));
+       $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc);
+
+       @cc = (@initial_cc, @cc);
+
        send_message();
 
        # set up for the next message
@@ -1094,13 +1147,10 @@ foreach my $t (@files) {
        $message_id = undef;
 }
 
-if ($compose) {
-       cleanup_compose_files();
-}
+cleanup_compose_files();
 
 sub cleanup_compose_files() {
-       unlink($compose_filename, $compose_filename . ".final");
-
+       unlink($compose_filename, $compose_filename . ".final") if $compose;
 }
 
 $smtp->quit if $smtp;
index 7c2e060ae770710ed7ed27d0aed4cfd2e8145810..8e234a4028d22e11baedba11f871d33f56945716 100755 (executable)
@@ -204,8 +204,15 @@ cmd_add()
        else
 
                module_clone "$path" "$realrepo" || exit
-               (unset GIT_DIR; cd "$path" && git checkout -f -q ${branch:+-b "$branch" "origin/$branch"}) ||
-               die "Unable to checkout submodule '$path'"
+               (
+                       unset GIT_DIR
+                       cd "$path" &&
+                       # ash fails to wordsplit ${branch:+-b "$branch"...}
+                       case "$branch" in
+                       '') git checkout -f -q ;;
+                       ?*) git checkout -f -q -b "$branch" "origin/$branch" ;;
+                       esac
+               ) || die "Unable to checkout submodule '$path'"
        fi
 
        git add "$path" ||
index 3b524207ffef124ca62c82af33396fe87723be6c..ef1d30db3889d0d33b43b1d15cd19281291f5aea 100755 (executable)
@@ -47,7 +47,8 @@ BEGIN {
        # import functions from Git into our packages, en masse
        no strict 'refs';
        foreach (qw/command command_oneline command_noisy command_output_pipe
-                   command_input_pipe command_close_pipe/) {
+                   command_input_pipe command_close_pipe
+                   command_bidi_pipe command_close_bidi_pipe/) {
                for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher
                        Git::SVN::Migration Git::SVN::Log Git::SVN),
                        __PACKAGE__) {
@@ -63,11 +64,12 @@ $sha1_short = qr/[a-f\d]{4,40}/;
 my ($_stdin, $_help, $_edit,
        $_message, $_file,
        $_template, $_shared,
-       $_version, $_fetch_all, $_no_rebase,
+       $_version, $_fetch_all, $_no_rebase, $_fetch_parent,
        $_merge, $_strategy, $_dry_run, $_local,
        $_prefix, $_no_checkout, $_url, $_verbose,
        $_git_format, $_commit_url, $_tag);
 $Git::SVN::_follow_parent = 1;
+$_q ||= 0;
 my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
                     'config-dir=s' => \$Git::SVN::Ra::config_dir,
                     'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache,
@@ -80,7 +82,7 @@ my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent,
                'useSvnsyncProps' => \$Git::SVN::_use_svnsync_props,
                'log-window-size=i' => \$Git::SVN::Ra::_log_window_size,
                'no-checkout' => \$_no_checkout,
-               'quiet|q' => \$_q,
+               'quiet|q+' => \$_q,
                'repack-flags|repack-args|repack-opts=s' =>
                   \$Git::SVN::_repack_flags,
                'use-log-author' => \$Git::SVN::_use_log_author,
@@ -111,6 +113,7 @@ my %cmd = (
        fetch => [ \&cmd_fetch, "Download new revisions from SVN",
                        { 'revision|r=s' => \$_revision,
                          'fetch-all|all' => \$_fetch_all,
+                         'parent|p' => \$_fetch_parent,
                           %fc_opts } ],
        clone => [ \&cmd_clone, "Initialize and fetch revisions",
                        { 'revision|r=s' => \$_revision,
@@ -325,6 +328,7 @@ sub do_git_init_db {
                command_noisy(@init_db);
                $_repository = Git->repository(Repository => ".git");
        }
+       command_noisy('config', 'core.autocrlf', 'false');
        my $set;
        my $pfx = "svn-remote.$Git::SVN::default_repo_id";
        foreach my $i (keys %icv) {
@@ -333,6 +337,9 @@ sub do_git_init_db {
                command_noisy('config', "$pfx.$i", $icv{$i});
                $set = $i;
        }
+       my $ignore_regex = \$SVN::Git::Fetcher::_ignore_regex;
+       command_noisy('config', "$pfx.ignore-paths", $$ignore_regex)
+               if defined $$ignore_regex;
 }
 
 sub init_subdir {
@@ -380,12 +387,21 @@ sub cmd_fetch {
        }
        my ($remote) = @_;
        if (@_ > 1) {
-               die "Usage: $0 fetch [--all] [svn-remote]\n";
+               die "Usage: $0 fetch [--all] [--parent] [svn-remote]\n";
        }
-       $remote ||= $Git::SVN::default_repo_id;
-       if ($_fetch_all) {
+       if ($_fetch_parent) {
+               my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+               unless ($gs) {
+                       die "Unable to determine upstream SVN information from ",
+                           "working tree history\n";
+               }
+               # just fetch, don't checkout.
+               $_no_checkout = 'true';
+               $_fetch_all ? $gs->fetch_all : $gs->fetch;
+       } elsif ($_fetch_all) {
                cmd_multi_fetch();
        } else {
+               $remote ||= $Git::SVN::default_repo_id;
                Git::SVN::fetch_all($remote, Git::SVN::read_all_remotes());
        }
 }
@@ -1253,6 +1269,40 @@ sub cmt_metadata {
                command(qw/cat-file commit/, shift)))[-1]);
 }
 
+sub cmt_sha2rev_batch {
+       my %s2r;
+       my ($pid, $in, $out, $ctx) = command_bidi_pipe(qw/cat-file --batch/);
+       my $list = shift;
+
+       foreach my $sha (@{$list}) {
+               my $first = 1;
+               my $size = 0;
+               print $out $sha, "\n";
+
+               while (my $line = <$in>) {
+                       if ($first && $line =~ /^[[:xdigit:]]{40}\smissing$/) {
+                               last;
+                       } elsif ($first &&
+                              $line =~ /^[[:xdigit:]]{40}\scommit\s(\d+)$/) {
+                               $first = 0;
+                               $size = $1;
+                               next;
+                       } elsif ($line =~ /^(git-svn-id: )/) {
+                               my (undef, $rev, undef) =
+                                                     extract_metadata($line);
+                               $s2r{$sha} = $rev;
+                       }
+
+                       $size -= length($line);
+                       last if ($size == 0);
+               }
+       }
+
+       command_close_bidi_pipe($pid, $in, $out, $ctx);
+
+       return \%s2r;
+}
+
 sub working_head_info {
        my ($head, $refs) = @_;
        my @args = ('log', '--no-color', '--first-parent', '--pretty=medium');
@@ -2331,13 +2381,13 @@ sub do_git_commit {
 
        $self->{last_rev} = $log_entry->{revision};
        $self->{last_commit} = $commit;
-       print "r$log_entry->{revision}";
+       print "r$log_entry->{revision}" unless $::_q > 1;
        if (defined $log_entry->{svm_revision}) {
-                print " (\@$log_entry->{svm_revision})";
+                print " (\@$log_entry->{svm_revision})" unless $::_q > 1;
                 $self->rev_map_set($log_entry->{svm_revision}, $commit,
                                   0, $self->svm_uuid);
        }
-       print " = $commit ($self->{ref_id})\n";
+       print " = $commit ($self->{ref_id})\n" unless $::_q > 1;
        if (--$_gc_nr == 0) {
                $_gc_nr = $_gc_period;
                gc();
@@ -2351,7 +2401,10 @@ sub match_paths {
        if (my $path = $paths->{"/$self->{path}"}) {
                return ($path->{action} eq 'D') ? 0 : 1;
        }
-       $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//;
+       my $repos_root = $self->ra->{repos_root};
+       my $extended_path = $self->{url} . '/' . $self->{path};
+       $extended_path =~ s#^\Q$repos_root\E(/|$)##;
+       $self->{path_regex} ||= qr/^\/\Q$extended_path\E\//;
        if (grep /$self->{path_regex}/, keys %$paths) {
                return 1;
        }
@@ -3282,6 +3335,8 @@ sub new {
                $self->{empty_symlinks} =
                                  _mark_empty_symlinks($git_svn, $switch_path);
        }
+       $self->{ignore_regex} = eval { command_oneline('config', '--get',
+                            "svn-remote.$git_svn->{repo_id}.ignore-paths") };
        $self->{empty} = {};
        $self->{dir_prop} = {};
        $self->{file_prop} = {};
@@ -3346,8 +3401,10 @@ sub in_dot_git {
 
 # return value: 0 -- don't ignore, 1 -- ignore
 sub is_path_ignored {
-       my ($path) = @_;
+       my ($self, $path) = @_;
        return 1 if in_dot_git($path);
+       return 1 if defined($self->{ignore_regex}) &&
+                   $path =~ m!$self->{ignore_regex}!;
        return 0 unless defined($_ignore_regex);
        return 1 if $path =~ m!$_ignore_regex!o;
        return 0;
@@ -3378,7 +3435,7 @@ sub git_path {
 
 sub delete_entry {
        my ($self, $path, $rev, $pb) = @_;
-       return undef if is_path_ignored($path);
+       return undef if $self->is_path_ignored($path);
 
        my $gpath = $self->git_path($path);
        return undef if ($gpath eq '');
@@ -3411,7 +3468,7 @@ sub open_file {
        my ($self, $path, $pb, $rev) = @_;
        my ($mode, $blob);
 
-       goto out if is_path_ignored($path);
+       goto out if $self->is_path_ignored($path);
 
        my $gpath = $self->git_path($path);
        ($mode, $blob) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
@@ -3431,7 +3488,7 @@ sub add_file {
        my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
        my $mode;
 
-       if (!is_path_ignored($path)) {
+       if (!$self->is_path_ignored($path)) {
                my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
                delete $self->{empty}->{$dir};
                $mode = '100644';
@@ -3442,7 +3499,7 @@ sub add_file {
 
 sub add_directory {
        my ($self, $path, $cp_path, $cp_rev) = @_;
-       goto out if is_path_ignored($path);
+       goto out if $self->is_path_ignored($path);
        my $gpath = $self->git_path($path);
        if ($gpath eq '') {
                my ($ls, $ctx) = command_output_pipe(qw/ls-tree
@@ -3466,7 +3523,7 @@ out:
 
 sub change_dir_prop {
        my ($self, $db, $prop, $value) = @_;
-       return undef if is_path_ignored($db->{path});
+       return undef if $self->is_path_ignored($db->{path});
        $self->{dir_prop}->{$db->{path}} ||= {};
        $self->{dir_prop}->{$db->{path}}->{$prop} = $value;
        undef;
@@ -3474,7 +3531,7 @@ sub change_dir_prop {
 
 sub absent_directory {
        my ($self, $path, $pb) = @_;
-       return undef if is_path_ignored($path);
+       return undef if $self->is_path_ignored($path);
        $self->{absent_dir}->{$pb->{path}} ||= [];
        push @{$self->{absent_dir}->{$pb->{path}}}, $path;
        undef;
@@ -3482,7 +3539,7 @@ sub absent_directory {
 
 sub absent_file {
        my ($self, $path, $pb) = @_;
-       return undef if is_path_ignored($path);
+       return undef if $self->is_path_ignored($path);
        $self->{absent_file}->{$pb->{path}} ||= [];
        push @{$self->{absent_file}->{$pb->{path}}}, $path;
        undef;
@@ -3490,7 +3547,7 @@ sub absent_file {
 
 sub change_file_prop {
        my ($self, $fb, $prop, $value) = @_;
-       return undef if is_path_ignored($fb->{path});
+       return undef if $self->is_path_ignored($fb->{path});
        if ($prop eq 'svn:executable') {
                if ($fb->{mode_b} != 120000) {
                        $fb->{mode_b} = defined $value ? 100755 : 100644;
@@ -3506,7 +3563,7 @@ sub change_file_prop {
 
 sub apply_textdelta {
        my ($self, $fb, $exp) = @_;
-       return undef if is_path_ignored($fb->{path});
+       return undef if $self->is_path_ignored($fb->{path});
        my $fh = $::_repository->temp_acquire('svn_delta');
        # $fh gets auto-closed() by SVN::TxDelta::apply(),
        # (but $base does not,) so dup() it for reading in close_file
@@ -3553,7 +3610,7 @@ sub apply_textdelta {
 
 sub close_file {
        my ($self, $fb, $exp) = @_;
-       return undef if is_path_ignored($fb->{path});
+       return undef if $self->is_path_ignored($fb->{path});
 
        my $hash;
        my $path = $self->git_path($fb->{path});
@@ -4987,11 +5044,22 @@ sub cmd_blame {
                                                  '--', $path);
                my ($sha1);
                my %authors;
+               my @buffer;
+               my %dsha; #distinct sha keys
+
                while (my $line = <$fh>) {
+                       push @buffer, $line;
+                       if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
+                               $dsha{$1} = 1;
+                       }
+               }
+
+               my $s2r = ::cmt_sha2rev_batch([keys %dsha]);
+
+               foreach my $line (@buffer) {
                        if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
-                               $sha1 = $1;
-                               (undef, $rev, undef) = ::cmt_metadata($1);
-                               $rev = '0' if (!$rev);
+                               $rev = $s2r->{$1};
+                               $rev = '0' if (!$rev)
                        }
                        elsif ($line =~ /^author (.*)/) {
                                $authors{$rev} = $1;
diff --git a/git.c b/git.c
index c2b181ed78daa4510f5cfb7bbff5b78f449f872a..5a00726d09a13d5f70ab0378e02bbbd299124d5c 100644 (file)
--- a/git.c
+++ b/git.c
@@ -5,7 +5,7 @@
 #include "run-command.h"
 
 const char git_usage_string[] =
-       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
+       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
 
 const char git_more_info_string[] =
        "See 'git help COMMAND' for more information on a specific command.";
@@ -47,7 +47,7 @@ static void commit_pager_choice(void) {
        }
 }
 
-static int handle_options(const char*** argv, int* argc, int* envchanged)
+static int handle_options(const char ***argv, int *argc, int *envchanged)
 {
        int handled = 0;
 
@@ -75,6 +75,9 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
                                puts(git_exec_path());
                                exit(0);
                        }
+               } else if (!strcmp(cmd, "--html-path")) {
+                       puts(system_path(GIT_HTML_PATH));
+                       exit(0);
                } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
                        use_pager = 1;
                } else if (!strcmp(cmd, "--no-pager")) {
@@ -133,7 +136,7 @@ static int handle_alias(int *argcp, const char ***argv)
        int envchanged = 0, ret = 0, saved_errno = errno;
        const char *subdir;
        int count, option_count;
-       const char** new_argv;
+       const char **new_argv;
        const char *alias_command;
        char *alias_string;
        int unused_nongit;
@@ -184,10 +187,10 @@ static int handle_alias(int *argcp, const char ***argv)
                                  "trace: alias expansion: %s =>",
                                  alias_command);
 
-               new_argv = xrealloc(new_argv, sizeof(char*) *
+               new_argv = xrealloc(new_argv, sizeof(char *) *
                                    (count + *argcp + 1));
                /* insert after command name */
-               memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp);
+               memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
                new_argv[count+*argcp] = NULL;
 
                *argv = new_argv;
@@ -271,6 +274,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "annotate", cmd_annotate, RUN_SETUP },
                { "apply", cmd_apply },
                { "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 },
@@ -493,7 +497,7 @@ int main(int argc, const char **argv)
 
        /*
         * We use PATH to find git commands, but we prepend some higher
-        * precidence paths: the "--exec-path" option, the GIT_EXEC_PATH
+        * precedence paths: the "--exec-path" option, the GIT_EXEC_PATH
         * environment, and the $(gitexecdir) from the Makefile at build
         * time.
         */
index 1773ae63eb54f3b602b782a3633034aab2f828ae..1a7887b2528140d63b84a72843c0d0ef64f56538 100644 (file)
@@ -521,7 +521,7 @@ proc updatecommits {} {
     incr viewactive($view)
     set viewcomplete($view) 0
     reset_pending_select {}
-    nowbusy $view "Reading"
+    nowbusy $view [mc "Reading"]
     if {$showneartags} {
        getallcommits
     }
@@ -1830,7 +1830,9 @@ proc setoptions {} {
     option add *Button.font uifont startupFile
     option add *Checkbutton.font uifont startupFile
     option add *Radiobutton.font uifont startupFile
-    option add *Menu.font uifont startupFile
+    if {[tk windowingsystem] ne "aqua"} {
+       option add *Menu.font uifont startupFile
+    }
     option add *Menubutton.font uifont startupFile
     option add *Label.font uifont startupFile
     option add *Message.font uifont startupFile
@@ -1910,8 +1912,8 @@ proc makewindow {} {
 
     # The "mc" arguments here are purely so that xgettext
     # sees the following string as needing to be translated
-    makemenu .bar {
-       {mc "File" cascade {
+    set file {
+       mc "File" cascade {
            {mc "Update" command updatecommits -accelerator F5}
            {mc "Reload" command reloadcommits -accelerator Meta1-F5}
            {mc "Reread references" command rereadrefs}
@@ -1921,21 +1923,41 @@ proc makewindow {} {
            {xx "" separator}
            {mc "Quit" command doquit -accelerator Meta1-Q}
        }}
-       {mc "Edit" cascade {
+    set edit {
+       mc "Edit" cascade {
            {mc "Preferences" command doprefs}
        }}
-       {mc "View" cascade {
+    set view {
+       mc "View" cascade {
            {mc "New view..." command {newview 0} -accelerator Shift-F4}
            {mc "Edit view..." command editview -state disabled -accelerator F4}
            {mc "Delete view" command delview -state disabled}
            {xx "" separator}
            {mc "All files" radiobutton {selectedview 0} -command {showview 0}}
        }}
-       {mc "Help" cascade {
+    if {[tk windowingsystem] ne "aqua"} {
+       set help {
+       mc "Help" cascade {
            {mc "About gitk" command about}
            {mc "Key bindings" command keys}
        }}
+       set bar [list $file $edit $view $help]
+    } else {
+       proc ::tk::mac::ShowPreferences {} {doprefs}
+       proc ::tk::mac::Quit {} {doquit}
+       lset file end [lreplace [lindex $file end] end-1 end]
+       set apple {
+       xx "Apple" cascade {
+           {mc "About gitk" command about}
+           {xx "" separator}
+       }}
+       set help {
+       mc "Help" cascade {
+           {mc "Key bindings" command keys}
+       }}
+       set bar [list $apple $file $view $help]
     }
+    makemenu .bar $bar
     . configure -menu .bar
 
     # the gui has upper and lower half, parts of a paned window.
@@ -2229,10 +2251,16 @@ proc makewindow {} {
        }
     }
 
+    if {[info exists geometry(state)] && $geometry(state) eq "zoomed"} {
+        wm state . $geometry(state)
+    }
+
     if {[tk windowingsystem] eq {aqua}} {
         set M1B M1
+        set ::BM "3"
     } else {
         set M1B Control
+        set ::BM "2"
     }
 
     bind .pwbottom <Configure> {resizecdetpanes %W %w}
@@ -2250,10 +2278,14 @@ proc makewindow {} {
                 set delta [expr {- (%D)}]
                 allcanvs yview scroll $delta units
             }
+            bindall <Shift-MouseWheel> {
+                set delta [expr {- (%D)}]
+                $canv xview scroll $delta units
+            }
         }
     }
-    bindall <2> "canvscan mark %W %x %y"
-    bindall <B2-Motion> "canvscan dragto %W %x %y"
+    bindall <$::BM> "canvscan mark %W %x %y"
+    bindall <B$::BM-Motion> "canvscan dragto %W %x %y"
     bindkey <Home> selfirstline
     bindkey <End> sellastline
     bind . <Key-Up> "selnextline -1"
@@ -2285,6 +2317,7 @@ proc makewindow {} {
     bindkey d "$ctext yview scroll 18 units"
     bindkey u "$ctext yview scroll -18 units"
     bindkey / {focus $fstring}
+    bindkey <Key-KP_Divide> {focus $fstring}
     bindkey <Key-Return> {dofind 1 1}
     bindkey ? {dofind -1 1}
     bindkey f nextfile
@@ -2331,6 +2364,10 @@ proc makewindow {} {
        {mc "Create new branch" command mkbranch}
        {mc "Cherry-pick this commit" command cherrypick}
        {mc "Reset HEAD branch to here" command resethead}
+       {mc "Mark this commit" command markhere}
+       {mc "Return to mark" command gotomark}
+       {mc "Find descendant of this and mark" command find_common_desc}
+       {mc "Compare with marked commit" command compare_commits}
     }
     $rowctxmenu configure -tearoff 0
 
@@ -2487,6 +2524,9 @@ proc savestuff {w} {
     if {![winfo viewable .]} return
     catch {
        set f [open "~/.gitk-new" w]
+       if {$::tcl_platform(platform) eq {windows}} {
+           file attributes "~/.gitk-new" -hidden true
+       }
        puts $f [list set mainfont $mainfont]
        puts $f [list set textfont $textfont]
        puts $f [list set uifont $uifont]
@@ -2512,6 +2552,7 @@ proc savestuff {w} {
        puts $f [list set perfile_attrs $perfile_attrs]
 
        puts $f "set geometry(main) [wm geometry .]"
+       puts $f "set geometry(state) [wm state .]"
        puts $f "set geometry(topwidth) [winfo width .tf]"
        puts $f "set geometry(topheight) [winfo height .tf]"
         puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
@@ -3204,9 +3245,8 @@ proc external_diff {} {
     set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
 
     if {$difffromfile ne {} && $difftofile ne {}} {
-        set cmd [concat | [shellsplit $extdifftool] \
-                    [list $difffromfile $difftofile]]
-        if {[catch {set fl [open $cmd r]} err]} {
+        set cmd [list [shellsplit $extdifftool] $difffromfile $difftofile]
+        if {[catch {set fl [open |$cmd r]} err]} {
             file delete -force $diffdir
             error_popup "$extdifftool: [mc "command failed:"] $err"
         } else {
@@ -3737,7 +3777,7 @@ proc editview {} {
     set newviewopts($curview,perm) $viewperm($curview)
     set newviewopts($curview,cmd)  $viewargscmd($curview)
     decode_view_opts $curview $viewargs($curview)
-    vieweditor $top $curview "Gitk: edit view $viewname($curview)"
+    vieweditor $top $curview "[mc "Gitk: edit view"] $viewname($curview)"
 }
 
 proc vieweditor {top n title} {
@@ -4046,7 +4086,7 @@ proc ishighlighted {id} {
 }
 
 proc bolden {id font} {
-    global canv linehtag currentid boldids need_redisplay
+    global canv linehtag currentid boldids need_redisplay markedid
 
     # need_redisplay = 1 means the display is stale and about to be redrawn
     if {$need_redisplay} return
@@ -4059,6 +4099,9 @@ proc bolden {id font} {
                   -fill [$canv cget -selectbackground]]
        $canv lower $t
     }
+    if {[info exists markedid] && $id eq $markedid} {
+       make_idmark $id
+    }
 }
 
 proc bolden_name {id font} {
@@ -5563,7 +5606,7 @@ proc drawcmittext {id row col} {
     global cmitlisted commitinfo rowidlist parentlist
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag selectedline
-    global canvxmax boldids boldnameids fgcolor
+    global canvxmax boldids boldnameids fgcolor markedid
     global mainheadid nullid nullid2 circleitem circlecolors ctxbut
 
     # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
@@ -5645,6 +5688,9 @@ proc drawcmittext {id row col} {
     if {$selectedline == $row} {
        make_secsel $id
     }
+    if {[info exists markedid] && $markedid eq $id} {
+       make_idmark $id
+    }
     set xr [expr {$xt + [font measure $font $headline]}]
     if {$xr > $canvxmax} {
        set canvxmax $xr
@@ -6443,6 +6489,17 @@ proc setlink {id lk} {
     }
 }
 
+proc appendshortlink {id {pre {}} {post {}}} {
+    global ctext linknum
+
+    $ctext insert end $pre
+    $ctext tag delete link$linknum
+    $ctext insert end [string range $id 0 7] link$linknum
+    $ctext insert end $post
+    setlink $id link$linknum
+    incr linknum
+}
+
 proc makelink {id} {
     global pendinglinks
 
@@ -6499,7 +6556,7 @@ proc appendrefs {pos ids var} {
        }
     }
     if {[llength $tags] > $maxrefs} {
-       $ctext insert $pos "many ([llength $tags])"
+       $ctext insert $pos "[mc "many"] ([llength $tags])"
     } else {
        set tags [lsort -index 0 -decreasing $tags]
        set sep {}
@@ -6586,6 +6643,16 @@ proc make_secsel {id} {
     $canv3 lower $t
 }
 
+proc make_idmark {id} {
+    global linehtag canv fgcolor
+
+    if {![info exists linehtag($id)]} return
+    $canv delete markid
+    set t [eval $canv create rect [$canv bbox $linehtag($id)] \
+              -tags markid -outline $fgcolor]
+    $canv raise $t
+}
+
 proc selectline {l isnew {desired_loc {}}} {
     global canv ctext commitinfo selectedline
     global canvy0 linespc parents children curview
@@ -7216,7 +7283,7 @@ proc getblobdiffs {ids} {
     set diffnparents 0
     set diffinhdr 0
     set diffencoding [get_path_encoding {}]
-    fconfigure $bdf -blocking 0 -encoding binary
+    fconfigure $bdf -blocking 0 -encoding binary -eofchar {}
     set blobdifffd($ids) $bdf
     filerun $bdf [list getblobdiffline $bdf $diffids]
 }
@@ -7367,7 +7434,8 @@ proc getblobdiffline {bdf ids} {
            $ctext insert end "$line\n" filesep
 
        } else {
-           set line [encoding convertfrom $diffencoding $line]
+           set line [string map {\x1A ^Z} \
+                          [encoding convertfrom $diffencoding $line]]
            # parse the prefix - one ' ', '-' or '+' for each parent
            set prefix [string range $line 0 [expr {$diffnparents - 1}]]
            set tag [expr {$diffnparents > 1? "m": "d"}]
@@ -7970,7 +8038,7 @@ proc mstime {} {
 
 proc rowmenu {x y id} {
     global rowctxmenu selectedline rowmenuid curview
-    global nullid nullid2 fakerowmenu mainhead
+    global nullid nullid2 fakerowmenu mainhead markedid
 
     stopfinding
     set rowmenuid $id
@@ -7986,6 +8054,15 @@ proc rowmenu {x y id} {
        } else {
            $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
        }
+       if {[info exists markedid] && $markedid ne $id} {
+           $menu entryconfigure 9 -state normal
+           $menu entryconfigure 10 -state normal
+           $menu entryconfigure 11 -state normal
+       } else {
+           $menu entryconfigure 9 -state disabled
+           $menu entryconfigure 10 -state disabled
+           $menu entryconfigure 11 -state disabled
+       }
     } else {
        set menu $fakerowmenu
     }
@@ -7995,6 +8072,162 @@ proc rowmenu {x y id} {
     tk_popup $menu $x $y
 }
 
+proc markhere {} {
+    global rowmenuid markedid canv
+
+    set markedid $rowmenuid
+    make_idmark $markedid
+}
+
+proc gotomark {} {
+    global markedid
+
+    if {[info exists markedid]} {
+       selbyid $markedid
+    }
+}
+
+proc replace_by_kids {l r} {
+    global curview children
+
+    set id [commitonrow $r]
+    set l [lreplace $l 0 0]
+    foreach kid $children($curview,$id) {
+       lappend l [rowofcommit $kid]
+    }
+    return [lsort -integer -decreasing -unique $l]
+}
+
+proc find_common_desc {} {
+    global markedid rowmenuid curview children
+
+    if {![info exists markedid]} return
+    if {![commitinview $markedid $curview] ||
+       ![commitinview $rowmenuid $curview]} return
+    #set t1 [clock clicks -milliseconds]
+    set l1 [list [rowofcommit $markedid]]
+    set l2 [list [rowofcommit $rowmenuid]]
+    while 1 {
+       set r1 [lindex $l1 0]
+       set r2 [lindex $l2 0]
+       if {$r1 eq {} || $r2 eq {}} break
+       if {$r1 == $r2} {
+           selectline $r1 1
+           break
+       }
+       if {$r1 > $r2} {
+           set l1 [replace_by_kids $l1 $r1]
+       } else {
+           set l2 [replace_by_kids $l2 $r2]
+       }
+    }
+    #set t2 [clock clicks -milliseconds]
+    #puts "took [expr {$t2-$t1}]ms"
+}
+
+proc compare_commits {} {
+    global markedid rowmenuid curview children
+
+    if {![info exists markedid]} return
+    if {![commitinview $markedid $curview]} return
+    addtohistory [list do_cmp_commits $markedid $rowmenuid]
+    do_cmp_commits $markedid $rowmenuid
+}
+
+proc getpatchid {id} {
+    global patchids
+
+    if {![info exists patchids($id)]} {
+       set cmd [diffcmd [list $id] {-p --root}]
+       # trim off the initial "|"
+       set cmd [lrange $cmd 1 end]
+       if {[catch {
+           set x [eval exec $cmd | git patch-id]
+           set patchids($id) [lindex $x 0]
+       }]} {
+           set patchids($id) "error"
+       }
+    }
+    return $patchids($id)
+}
+
+proc do_cmp_commits {a b} {
+    global ctext curview parents children patchids commitinfo
+
+    $ctext conf -state normal
+    clear_ctext
+    init_flist {}
+    for {set i 0} {$i < 100} {incr i} {
+       set skipa 0
+       set skipb 0
+       if {[llength $parents($curview,$a)] > 1} {
+           appendshortlink $a [mc "Skipping merge commit "] "\n"
+           set skipa 1
+       } else {
+           set patcha [getpatchid $a]
+       }
+       if {[llength $parents($curview,$b)] > 1} {
+           appendshortlink $b [mc "Skipping merge commit "] "\n"
+           set skipb 1
+       } else {
+           set patchb [getpatchid $b]
+       }
+       if {!$skipa && !$skipb} {
+           set heada [lindex $commitinfo($a) 0]
+           set headb [lindex $commitinfo($b) 0]
+           if {$patcha eq "error"} {
+               appendshortlink $a [mc "Error getting patch ID for "] \
+                   [mc " - stopping\n"]
+               break
+           }
+           if {$patchb eq "error"} {
+               appendshortlink $b [mc "Error getting patch ID for "] \
+                   [mc " - stopping\n"]
+               break
+           }
+           if {$patcha eq $patchb} {
+               if {$heada eq $headb} {
+                   appendshortlink $a [mc "Commit "]
+                   appendshortlink $b " == " "  $heada\n"
+               } else {
+                   appendshortlink $a [mc "Commit "] "  $heada\n"
+                   appendshortlink $b [mc " is the same patch as\n       "] \
+                       "  $headb\n"
+               }
+               set skipa 1
+               set skipb 1
+           } else {
+               $ctext insert end "\n"
+               appendshortlink $a [mc "Commit "] "  $heada\n"
+               appendshortlink $b [mc " differs from\n       "] \
+                   "  $headb\n"
+               $ctext insert end [mc "- stopping\n"]
+               break
+           }
+       }
+       if {$skipa} {
+           if {[llength $children($curview,$a)] != 1} {
+               $ctext insert end "\n"
+               appendshortlink $a [mc "Commit "] \
+                   [mc " has %s children - stopping\n" \
+                        [llength $children($curview,$a)]]
+               break
+           }
+           set a [lindex $children($curview,$a) 0]
+       }
+       if {$skipb} {
+           if {[llength $children($curview,$b)] != 1} {
+               appendshortlink $b [mc "Commit "] \
+                   [mc " has %s children - stopping\n" \
+                        [llength $children($curview,$b)]]
+               break
+           }
+           set b [lindex $children($curview,$b) 0]
+       }
+    }
+    $ctext conf -state disabled
+}
+
 proc diffvssel {dirn} {
     global rowmenuid selectedline
 
@@ -8189,7 +8422,7 @@ proc domktag {} {
 }
 
 proc redrawtags {id} {
-    global canv linehtag idpos currentid curview cmitlisted
+    global canv linehtag idpos currentid curview cmitlisted markedid
     global canvxmax iddrawn circleitem mainheadid circlecolors
 
     if {![commitinview $id $curview]} return
@@ -8214,6 +8447,9 @@ proc redrawtags {id} {
     if {[info exists currentid] && $currentid == $id} {
        make_secsel $id
     }
+    if {[info exists markedid] && $markedid eq $id} {
+       make_idmark $id
+    }
 }
 
 proc mktagcan {} {
@@ -10197,7 +10433,7 @@ proc doprefs {} {
 proc choose_extdiff {} {
     global extdifftool
 
-    set prog [tk_getOpenFile -title "External diff tool" -multiple false]
+    set prog [tk_getOpenFile -title [mc "External diff tool"] -multiple false]
     if {$prog ne {}} {
        set extdifftool $prog
     }
@@ -10240,6 +10476,7 @@ proc setfg {c} {
     }
     allcanvs itemconf text -fill $c
     $canv itemconf circle -outline $c
+    $canv itemconf markid -outline $c
 }
 
 proc prefscan {} {
@@ -10689,9 +10926,15 @@ catch {
     }
 }
 
-set mainfont {Helvetica 9}
-set textfont {Courier 9}
-set uifont {Helvetica 9 bold}
+if {[tk windowingsystem] eq "aqua"} {
+    set mainfont {{Lucida Grande} 9}
+    set textfont {Monaco 9}
+    set uifont {{Lucida Grande} 9 bold}
+} else {
+    set mainfont {Helvetica 9}
+    set textfont {Courier 9}
+    set uifont {Helvetica 9 bold}
+}
 set tabstop 8
 set findmergefiles 0
 set maxgraphpct 50
@@ -10712,7 +10955,11 @@ set datetimeformat "%Y-%m-%d %H:%M:%S"
 set autoselect 1
 set perfile_attrs 0
 
-set extdifftool "meld"
+if {[tk windowingsystem] eq "aqua"} {
+    set extdifftool "opendiff"
+} else {
+    set extdifftool "meld"
+}
 
 set colors {green red blue magenta darkgrey brown orange}
 set bgcolor white
@@ -10883,9 +11130,33 @@ set lserial 0
 set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
 setcoords
 makewindow
+catch {
+    image create photo gitlogo      -width 16 -height 16
+
+    image create photo gitlogominus -width  4 -height  2
+    gitlogominus put #C00000 -to 0 0 4 2
+    gitlogo copy gitlogominus -to  1 5
+    gitlogo copy gitlogominus -to  6 5
+    gitlogo copy gitlogominus -to 11 5
+    image delete gitlogominus
+
+    image create photo gitlogoplus  -width  4 -height  4
+    gitlogoplus  put #008000 -to 1 0 3 4
+    gitlogoplus  put #008000 -to 0 1 4 3
+    gitlogo copy gitlogoplus  -to  1 9
+    gitlogo copy gitlogoplus  -to  6 9
+    gitlogo copy gitlogoplus  -to 11 9
+    image delete gitlogoplus
+
+    image create photo gitlogo32    -width 32 -height 32
+    gitlogo32 copy gitlogo -zoom 2 2
+
+    wm iconphoto . -default gitlogo gitlogo32
+}
 # wait for the window to become visible
 tkwait visibility .
 wm title . "[file tail $argv0]: [file tail [pwd]]"
+update
 readrefs
 
 if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
diff --git a/gitk-git/po/ru.po b/gitk-git/po/ru.po
new file mode 100644 (file)
index 0000000..704eba8
--- /dev/null
@@ -0,0 +1,1085 @@
+#
+# Translation of gitk to Russian.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-04-24 16:00+0200\n"
+"PO-Revision-Date: 2009-04-24 16:00+0200\n"
+"Last-Translator: Alex Riesen <raa.lkml@gmail.com>\n"
+"Language-Team: Russian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: gitk:113
+msgid "Couldn't get list of unmerged files:"
+msgstr ""
+"Невозможно получить список файлов незавершённой операции слияния:"
+
+#: gitk:268
+msgid "Error parsing revisions:"
+msgstr "Ошибка в идентификаторе версии:"
+
+#: gitk:323
+msgid "Error executing --argscmd command:"
+msgstr "Ошибка выполнения команды заданой --argscmd:"
+
+#: gitk:336
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Файлы не выбраны: указан --merge, но не было найдено ни одного файла "
+"где эта операция должна быть завершена."
+
+#: gitk:339
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Файлы не выбраны: указан --merge, но в рамках указаного "
+"ограничения на имена файлов нет ни одного "
+"где эта операция должна быть завершена."
+
+#: gitk:361 gitk:508
+msgid "Error executing git log:"
+msgstr "Ошибка запуска git log:"
+
+#: gitk:379
+msgid "Reading"
+msgstr "Чтение"
+
+#: gitk:439 gitk:4021
+msgid "Reading commits..."
+msgstr "Чтение версий..."
+
+#: gitk:442 gitk:1560 gitk:4024
+msgid "No commits selected"
+msgstr "Ничего не выбрано"
+
+#: gitk:1436
+msgid "Can't parse git log output:"
+msgstr "Ошибка обработки вывода команды git log:"
+
+#: gitk:1656
+msgid "No commit information available"
+msgstr "Нет информации о состоянии"
+
+#: gitk:1791 gitk:1815 gitk:3814 gitk:8478 gitk:10014 gitk:10186
+msgid "OK"
+msgstr "Ok"
+
+#: gitk:1817 gitk:3816 gitk:8078 gitk:8152 gitk:8259 gitk:8308 gitk:8480
+#: gitk:10015 gitk:10187
+msgid "Cancel"
+msgstr "Отмена"
+
+#: gitk:1915
+msgid "Update"
+msgstr "Обновить"
+
+#: gitk:1916
+msgid "Reload"
+msgstr "Перечитать"
+
+#: gitk:1917
+msgid "Reread references"
+msgstr "Обновить список ссылок"
+
+#: gitk:1918
+msgid "List references"
+msgstr "Список ссылок"
+
+#: gitk:1920
+msgid "Start git gui"
+msgstr "Запустить git gui"
+
+#: gitk:1922
+msgid "Quit"
+msgstr "Завершить"
+
+#: gitk:1914
+msgid "File"
+msgstr "Файл"
+
+#: gitk:1925
+msgid "Preferences"
+msgstr "Настройки"
+
+#: gitk:1924
+msgid "Edit"
+msgstr "Редактировать"
+
+#: gitk:1928
+msgid "New view..."
+msgstr "Новое представление..."
+
+#: gitk:1929
+msgid "Edit view..."
+msgstr "Редактировать представление..."
+
+#: gitk:1930
+msgid "Delete view"
+msgstr "Удалить представление"
+
+#: gitk:1932
+msgid "All files"
+msgstr "Все файлы"
+
+#: gitk:1927 gitk:3626
+msgid "View"
+msgstr "Представление"
+
+#: gitk:1935 gitk:2609
+msgid "About gitk"
+msgstr "О gitk"
+
+#: gitk:1936
+msgid "Key bindings"
+msgstr "Назначения клавиатуры"
+
+#: gitk:1934
+msgid "Help"
+msgstr "Подсказка"
+
+#: gitk:1994
+msgid "SHA1 ID: "
+msgstr "SHA1:"
+
+#: gitk:2025
+msgid "Row"
+msgstr "Строка"
+
+#: gitk:2056
+msgid "Find"
+msgstr "Поиск"
+
+#: gitk:2057
+msgid "next"
+msgstr "След."
+
+#: gitk:2058
+msgid "prev"
+msgstr "Пред."
+
+#: gitk:2059
+msgid "commit"
+msgstr "состояние"
+
+#: gitk:2062 gitk:2064 gitk:4179 gitk:4202 gitk:4226 gitk:6164 gitk:6236
+#: gitk:6320
+msgid "containing:"
+msgstr "содержащее:"
+
+#: gitk:2065 gitk:3117 gitk:3122 gitk:4254
+msgid "touching paths:"
+msgstr "касательно файлов:"
+
+#: gitk:2066 gitk:4259
+msgid "adding/removing string:"
+msgstr "добавив/удалив строку:"
+
+#: gitk:2075 gitk:2077
+msgid "Exact"
+msgstr "Точно"
+
+#: gitk:2077 gitk:4334 gitk:6132
+msgid "IgnCase"
+msgstr "Игнорировать большие/маленькие"
+
+#: gitk:2077 gitk:4228 gitk:4332 gitk:6128
+msgid "Regexp"
+msgstr "Регулярные выражения"
+
+#: gitk:2079 gitk:2080 gitk:4353 gitk:4383 gitk:4390 gitk:6256 gitk:6324
+msgid "All fields"
+msgstr "Во всех полях"
+
+#: gitk:2080 gitk:4351 gitk:4383 gitk:6195
+msgid "Headline"
+msgstr "Заголовок"
+
+#: gitk:2081 gitk:4351 gitk:6195 gitk:6324 gitk:6737
+msgid "Comments"
+msgstr "Комментарии"
+
+#: gitk:2081 gitk:4351 gitk:4355 gitk:4390 gitk:6195 gitk:6672 gitk:7923
+#: gitk:7938
+msgid "Author"
+msgstr "Автор"
+
+#: gitk:2081 gitk:4351 gitk:6195 gitk:6674
+msgid "Committer"
+msgstr "Сохранивший состояние"
+
+#: gitk:2110
+msgid "Search"
+msgstr "Найти"
+
+#: gitk:2117
+msgid "Diff"
+msgstr "Сравнить"
+
+#: gitk:2119
+msgid "Old version"
+msgstr "Старая версия"
+
+#: gitk:2121
+msgid "New version"
+msgstr "Новая версия"
+
+#: gitk:2123
+msgid "Lines of context"
+msgstr "Строк контекста"
+
+#: gitk:2133
+msgid "Ignore space change"
+msgstr "Игнорировать пробелы"
+
+#: gitk:2191
+msgid "Patch"
+msgstr "Патч"
+
+#: gitk:2193
+msgid "Tree"
+msgstr "Файлы"
+
+#: gitk:2326 gitk:2339
+msgid "Diff this -> selected"
+msgstr "Сравнить это состояние с выделеным"
+
+#: gitk:2327 gitk:2340
+msgid "Diff selected -> this"
+msgstr "Сравнить выделеное с этим состоянием"
+
+#: gitk:2328 gitk:2341
+msgid "Make patch"
+msgstr "Создать патч"
+
+#: gitk:2329 gitk:8136
+msgid "Create tag"
+msgstr "Создать метку"
+
+#: gitk:2330 gitk:8239
+msgid "Write commit to file"
+msgstr "Сохранить изменения в файл"
+
+#: gitk:2331 gitk:8296
+msgid "Create new branch"
+msgstr "Создать ветвь"
+
+#: gitk:2332
+msgid "Cherry-pick this commit"
+msgstr "Скопировать это состояние"
+
+#: gitk:2333
+msgid "Reset HEAD branch to here"
+msgstr "Установить HEAD на это состояние"
+
+#: gitk:2347
+msgid "Check out this branch"
+msgstr "Перейти на эту ветвь"
+
+#: gitk:2348
+msgid "Remove this branch"
+msgstr "Удалить эту ветвь"
+
+#: gitk:2355
+msgid "Highlight this too"
+msgstr "Подсветить этот тоже"
+
+#: gitk:2356
+msgid "Highlight this only"
+msgstr "Подсветить только этот"
+
+#: gitk:2357
+msgid "External diff"
+msgstr "Программа сравнения"
+
+#: gitk:2358
+msgid "Blame parent commit"
+msgstr "Аннотировать родительское состояние"
+
+#: gitk:2365
+msgid "Show origin of this line"
+msgstr "Показать источник этой строки"
+
+#: gitk:2366
+msgid "Run git gui blame on this line"
+msgstr "Запустить git gui blame для этой строки"
+
+#: gitk:2611
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright © 2005-2008 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - программа просмотра истории репозиториев Git\n"
+"\n"
+"Copyright (c) 2005-2008 Paul Mackerras\n"
+"\n"
+"Использование и распространение согласно условиям GNU General Public License"
+
+#: gitk:2619 gitk:2681 gitk:8661
+msgid "Close"
+msgstr "Закрыть"
+
+#: gitk:2638
+msgid "Gitk key bindings"
+msgstr "Назначения клавиатуры в Gitk"
+
+#: gitk:2641
+msgid "Gitk key bindings:"
+msgstr "Назначения клавиатуры в Gitk:"
+
+#: gitk:2643
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tЗавершить"
+
+#: gitk:2644
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tПерейти к первому состоянию"
+
+#: gitk:2645
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tПерейти к последнему состоянию"
+
+#: gitk:2646
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Up>, p, i\tПерейти к следующему состоянию"
+
+#: gitk:2647
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Down>, n, k\tПерейти к предыдущему состоянию"
+
+#: gitk:2648
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Left>, z, j\tПоказать ранее посещённое состояние"
+
+#: gitk:2649
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\tПоказать следующее посещённое состояние"
+
+#: gitk:2650
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tПерейти на страницу выше в списке состояний"
+
+#: gitk:2651
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tПерейти на страницу ниже в списке состояний"
+
+#: gitk:2652
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tПоказать начало списка состояний"
+
+#: gitk:2653
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tПоказать конец списка состояний"
+
+#: gitk:2654
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tПровернуть список состояний вверх"
+
+#: gitk:2655
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tПровернуть список состояний вниз"
+
+#: gitk:2656
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tПровернуть список состояний на страницу вверх"
+
+#: gitk:2657
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tПровернуть список состояний на страницу вниз"
+
+#: gitk:2658
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr ""
+"<Shift-Up>\tПоиск в обратном порядке (вверх, среди новых состояний)"
+
+#: gitk:2659
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\tПоиск (вниз, среди старых состояний)"
+
+#: gitk:2660
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tПрокрутить список изменений на страницу выше"
+
+#: gitk:2661
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tПрокрутить список изменений на страницу выше"
+
+#: gitk:2662
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Leertaste>\t\tПрокрутить список изменений на страницу ниже"
+
+#: gitk:2663
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tПрокрутить список изменений на 18 строк вверх"
+
+#: gitk:2664
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tПрокрутить список изменений на 18 строк вниз"
+
+#: gitk:2665
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tПоиск"
+
+#: gitk:2666
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tПерейти к следующему найденому состоянию"
+
+#: gitk:2667
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\tПерейти к следующему найденому состоянию"
+
+#: gitk:2668
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tПерейти к полю поиска"
+
+#: gitk:2669
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tПерейти к предыдущему найденому состоянию"
+
+#: gitk:2670
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tПрокрутить список изменений к следующему файлу"
+
+#: gitk:2671
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tПродолжить поиск в списке изменений"
+
+#: gitk:2672
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tПерейти к предыдущему найденому тексту в списке изменений"
+
+#: gitk:2673
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tУвеличить размер шрифта"
+
+#: gitk:2674
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tУвеличить размер шрифта"
+
+#: gitk:2675
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tУменьшить размер шрифта"
+
+#: gitk:2676
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tУменьшить размер шрифта"
+
+#: gitk:2677
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tОбновить"
+
+#: gitk:3132
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Ошибка получения \"%s\" из %s:"
+
+#: gitk:3189 gitk:3198
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Ошибка создания временного каталога %s:"
+
+#: gitk:3211
+msgid "command failed:"
+msgstr "ошибка выполнения команды:"
+
+#: gitk:3357
+msgid "No such commit"
+msgstr "Состояние не найдено"
+
+#: gitk:3371
+msgid "git gui blame: command failed:"
+msgstr "git gui blame: ошибка выполнения команды:"
+
+#: gitk:3402
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Ошибка чтения MERGE_HEAD: %s"
+
+#: gitk:3410
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Ошибка чтения индекса: %s"
+
+#: gitk:3435
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Ошибка запуска git blame: %s"
+
+#: gitk:3438 gitk:6163
+msgid "Searching"
+msgstr "Поиск"
+
+#: gitk:3470
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Ошибка выполнения git blame: %s"
+
+#: gitk:3498
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr ""
+"Эта строка принадлежит состоянию %s, которое не показано в этом "
+"представлении"
+
+#: gitk:3512
+msgid "External diff viewer failed:"
+msgstr "Ошибка выполнения программы сравнения:"
+
+#: gitk:3630
+msgid "Gitk view definition"
+msgstr "Gitk определение представлений"
+
+#: gitk:3634
+msgid "Remember this view"
+msgstr "Запомнить представление"
+
+#: gitk:3635
+msgid "Commits to include (arguments to git log):"
+msgstr "Включить состояния (аргументы для git-log):"
+
+#: gitk:3636
+msgid "Use all refs"
+msgstr "Использовать все ветви"
+
+#: gitk:3637
+msgid "Strictly sort by date"
+msgstr "Строгая сортировка по дате"
+
+#: gitk:3638
+msgid "Mark branch sides"
+msgstr "Отметить стороны ветвей"
+
+#: gitk:3639
+msgid "Since date:"
+msgstr "С даты:"
+
+#: gitk:3640
+msgid "Until date:"
+msgstr "По дату:"
+
+#: gitk:3641
+msgid "Max count:"
+msgstr "Макс. количество:"
+
+#: gitk:3642
+msgid "Skip:"
+msgstr "Пропустить:"
+
+#: gitk:3643
+msgid "Limit to first parent"
+msgstr "Ограничить первым предком"
+
+#: gitk:3644
+msgid "Command to generate more commits to include:"
+msgstr "Дополнительная команда для списка состояний:"
+
+#: gitk:3753
+msgid "Name"
+msgstr "Имя"
+
+#: gitk:3801
+msgid "Enter files and directories to include, one per line:"
+msgstr "Файлы и каталоги для ограничения истории, по одному на строку:"
+
+#: gitk:3815
+msgid "Apply (F5)"
+msgstr "Применить (F5)"
+
+#: gitk:3853
+msgid "Error in commit selection arguments:"
+msgstr "Ошибка в параметрах выбора состояний:"
+
+#: gitk:3906 gitk:3958 gitk:4403 gitk:4417 gitk:5675 gitk:10867 gitk:10868
+msgid "None"
+msgstr "Ни одного"
+
+#: gitk:4351 gitk:6195 gitk:7925 gitk:7940
+msgid "Date"
+msgstr "Дата"
+
+#: gitk:4351 gitk:6195
+msgid "CDate"
+msgstr "Дата ввода"
+
+#: gitk:4500 gitk:4505
+msgid "Descendant"
+msgstr "Порождённое"
+
+#: gitk:4501
+msgid "Not descendant"
+msgstr "Не порождённое"
+
+#: gitk:4508 gitk:4513
+msgid "Ancestor"
+msgstr "Предок"
+
+#: gitk:4509
+msgid "Not ancestor"
+msgstr "Не предок"
+
+#: gitk:4799
+msgid "Local changes checked in to index but not committed"
+msgstr "Изменения зарегистрированные в индексе, но не сохранённые"
+
+#: gitk:4835
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Изменения в рабочем каталоге, не зарегистрированные в индексе"
+
+#: gitk:6676
+msgid "Tags:"
+msgstr "Таги:"
+
+#: gitk:6693 gitk:6699 gitk:7918
+msgid "Parent"
+msgstr "Предок"
+
+#: gitk:6704
+msgid "Child"
+msgstr "Потомок"
+
+#: gitk:6713
+msgid "Branch"
+msgstr "Ветвь"
+
+#: gitk:6716
+msgid "Follows"
+msgstr "Следует за"
+
+#: gitk:6719
+msgid "Precedes"
+msgstr "Предшествует"
+
+#: gitk:7212
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Ошибка получения изменений: %s"
+
+#: gitk:7751
+msgid "Goto:"
+msgstr "Перейти к:"
+
+#: gitk:7753
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:7772
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "Сокращённый SHA1 идентификатор %s неоднозначен"
+
+#: gitk:7784
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "SHA1 идентификатор %s не найден"
+
+#: gitk:7786
+#, tcl-format
+msgid "Tag/Head %s is not known"
+msgstr "Метка или ветвь %s не найдена"
+
+#: gitk:7928
+msgid "Children"
+msgstr "Потомки"
+
+#: gitk:7985
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Установить ветвь %s на это состояние"
+
+#: gitk:7987
+msgid "Detached head: can't reset"
+msgstr "Состояние не принадлежит ни одной ветви, переход невозможен"
+
+#: gitk:8019
+msgid "Top"
+msgstr "Верх"
+
+#: gitk:8020
+msgid "From"
+msgstr "От"
+
+#: gitk:8025
+msgid "To"
+msgstr "До"
+
+#: gitk:8049
+msgid "Generate patch"
+msgstr "Создать патч"
+
+#: gitk:8051
+msgid "From:"
+msgstr "От:"
+
+#: gitk:8060
+msgid "To:"
+msgstr "До:"
+
+#: gitk:8069
+msgid "Reverse"
+msgstr "В обратном порядке"
+
+#: gitk:8071 gitk:8253
+msgid "Output file:"
+msgstr "Файл для сохранения:"
+
+#: gitk:8077
+msgid "Generate"
+msgstr "Создать"
+
+#: gitk:8115
+msgid "Error creating patch:"
+msgstr "Ошибка создания патча:"
+
+#: gitk:8138 gitk:8241 gitk:8298
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:8147
+msgid "Tag name:"
+msgstr "Имя метки:"
+
+#: gitk:8151 gitk:8307
+msgid "Create"
+msgstr "Создать"
+
+#: gitk:8168
+msgid "No tag name specified"
+msgstr "Не задано имя метки"
+
+#: gitk:8172
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "Метка \"%s\" уже существует"
+
+#: gitk:8178
+msgid "Error creating tag:"
+msgstr "Ошибка создания метки:"
+
+#: gitk:8250
+msgid "Command:"
+msgstr "Команда:"
+
+#: gitk:8258
+msgid "Write"
+msgstr "Запись"
+
+#: gitk:8276
+msgid "Error writing commit:"
+msgstr "Ошибка сохранения состояния:"
+
+#: gitk:8303
+msgid "Name:"
+msgstr "Имя:"
+
+#: gitk:8326
+msgid "Please specify a name for the new branch"
+msgstr "Укажите имя для новой ветви"
+
+#: gitk:8331
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "Ветвь '%s' уже существует. Переписать?"
+
+#: gitk:8397
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr ""
+"Состояние %s уже принадлежит ветви %s. Продолжить операцию?"
+
+#: gitk:8402
+msgid "Cherry-picking"
+msgstr "Копирование изменений"
+
+#: gitk:8411
+#, 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 ""
+"Копирование невозможно из-за изменений в файле '%s'.\n"
+"Сохраните или отмените изменения и повторите операцию."
+
+#: gitk:8417
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"Копирование изменений невозможно из-за незавершённой операции "
+"слияния.\nЗапустить git citool для завершения этой операции?"
+
+#: gitk:8433
+msgid "No changes committed"
+msgstr "Изменения не сохранены"
+
+#: gitk:8459
+msgid "Confirm reset"
+msgstr "Подтвердите операцию перехода"
+
+#: gitk:8461
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Установить ветвь %s на состояние %s?"
+
+#: gitk:8465
+msgid "Reset type:"
+msgstr "Тип операции перехода:"
+
+#: gitk:8469
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Лёгкий: оставить рабочий каталог и индекс неизменными"
+
+#: gitk:8472
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr ""
+"Смешаный: оставить рабочий каталог неизменным, установить индекс"
+
+#: gitk:8475
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Жесткий: переписать индекс и рабочий каталог\n"
+"(все изменения в рабочем каталоги будут потеряны)"
+
+#: gitk:8492
+msgid "Resetting"
+msgstr "Установка"
+
+#: gitk:8549
+msgid "Checking out"
+msgstr "Переход"
+
+#: gitk:8602
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Активная ветвь не может быть удалена"
+
+#: gitk:8608
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Состояния ветви %s больше не принадлежат никакой другой ветви.\n"
+"Действительно удалить ветвь %s?"
+
+#: gitk:8639
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Метки и ветви: %s"
+
+#: gitk:8654
+msgid "Filter"
+msgstr "Фильтровать"
+
+#: gitk:8949
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Ошибка чтения истории проекта; информация о ветвях и состояниях "
+"вокруг меток (до/после) может быть неполной."
+
+#: gitk:9935
+msgid "Tag"
+msgstr "Метка"
+
+#: gitk:9935
+msgid "Id"
+msgstr "Id"
+
+#: gitk:9983
+msgid "Gitk font chooser"
+msgstr "Шрифт Gitk"
+
+#: gitk:10000
+msgid "B"
+msgstr "Ж"
+
+#: gitk:10003
+msgid "I"
+msgstr "К"
+
+#: gitk:10098
+msgid "Gitk preferences"
+msgstr "Настройки Gitk"
+
+#: gitk:10100
+msgid "Commit list display options"
+msgstr "Параметры показа списка состояний"
+
+#: gitk:10103
+msgid "Maximum graph width (lines)"
+msgstr "Макс. ширина графа (строк)"
+
+#: gitk:10107
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Макс. ширина графа (% ширины панели)"
+
+#: gitk:10111
+msgid "Show local changes"
+msgstr "Показывать изменения в рабочем каталоге"
+
+#: gitk:10114
+msgid "Auto-select SHA1"
+msgstr "Выделить SHA1"
+
+#: gitk:10118
+msgid "Diff display options"
+msgstr "Параметры показа изменений"
+
+#: gitk:10120
+msgid "Tab spacing"
+msgstr "Ширина табуляции"
+
+#: gitk:10123
+msgid "Display nearby tags"
+msgstr "Показывать близкие метки"
+
+#: gitk:10126
+msgid "Limit diffs to listed paths"
+msgstr "Ограничить показ изменений выбраными файлами"
+
+#: gitk:10129
+msgid "Support per-file encodings"
+msgstr "Поддержка кодировок в отдельных файлах"
+
+#: gitk:10135
+msgid "External diff tool"
+msgstr "Программа для показа изменений"
+
+#: gitk:10137
+msgid "Choose..."
+msgstr "Выберите..."
+
+#: gitk:10142
+msgid "Colors: press to choose"
+msgstr "Цвета: нажмите для выбора"
+
+#: gitk:10145
+msgid "Background"
+msgstr "Фон"
+
+#: gitk:10146 gitk:10176
+msgid "background"
+msgstr "фон"
+
+#: gitk:10149
+msgid "Foreground"
+msgstr "Передний план"
+
+#: gitk:10150
+msgid "foreground"
+msgstr "передний план"
+
+#: gitk:10153
+msgid "Diff: old lines"
+msgstr "Изменения: старый текст"
+
+#: gitk:10154
+msgid "diff old lines"
+msgstr "старый текст изменения"
+
+#: gitk:10158
+msgid "Diff: new lines"
+msgstr "Изменения: новый текст"
+
+#: gitk:10159
+msgid "diff new lines"
+msgstr "новый текст изменения"
+
+#: gitk:10163
+msgid "Diff: hunk header"
+msgstr "Изменения: заголовок блока"
+
+#: gitk:10165
+msgid "diff hunk header"
+msgstr "заголовок блока изменений"
+
+#: gitk:10169
+msgid "Marked line bg"
+msgstr "Фон выбраной строки"
+
+#: gitk:10171
+msgid "marked line background"
+msgstr "фон выбраной строки"
+
+#: gitk:10175
+msgid "Select bg"
+msgstr "Выберите фон"
+
+#: gitk:10179
+msgid "Fonts: press to choose"
+msgstr "Шрифт: нажмите для выбора"
+
+#: gitk:10181
+msgid "Main font"
+msgstr "Основной шрифт"
+
+#: gitk:10182
+msgid "Diff display font"
+msgstr "Шрифт показа изменений"
+
+#: gitk:10183
+msgid "User interface font"
+msgstr "Шрифт интерфейса"
+
+#: gitk:10210
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: выберите цвет для %s"
+
+#: gitk:10656
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr ""
+"К сожалению gitk не может работать с этой версий Tcl/Tk.\n"
+"Требуется как минимум Tcl/Tk 8.4."
+
+#: gitk:10773
+msgid "Cannot find a git repository here."
+msgstr "Git-репозитарий не найден в текущем каталоге."
+
+#: gitk:10777
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "Git-репозитарий \"%s\" не найден."
+
+#: gitk:10824
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Неоднозначный аргумент '%s': существует как версия и имя файла"
+
+#: gitk:10836
+msgid "Bad arguments to gitk:"
+msgstr "Неправильные аргументы для gitk:"
+
+#: gitk:10896
+msgid "Command line"
+msgstr "Командная строка"
+
index 8433dd1d45780b3947cc4a0b31ee2059ea6e65bf..9056d1e090b314cfa617ccd8aa03047b3aea23b4 100644 (file)
@@ -206,7 +206,7 @@ not include variables usually directly set during build):
  * $fallback_encoding
    Gitweb assumes this charset if line contains non-UTF-8 characters.
    Fallback decoding is used without error checking, so it can be even
-   'utf-8'. Value mist be valid encodig; see Encoding::Supported(3pm) man
+   'utf-8'. Value must be valid encoding; see Encoding::Supported(3pm) man
    page for a list.   By default 'latin1', aka. 'iso-8859-1'.
  * @diff_opts
    Rename detection options for git-diff and git-diff-tree. By default
@@ -377,7 +377,7 @@ named without a .git extension (e.g. /pub/git/project instead of
 
        DocumentRoot /var/www/gitweb
 
-       AliasMatch ^(/.*?)(\.git)(/.*)? /pub/git$1$3
+       AliasMatch ^(/.*?)(\.git)(/.*)?$ /pub/git$1$3
        <Directory /var/www/gitweb>
                Options ExecCGI
                AddHandler cgi-script cgi
@@ -402,6 +402,14 @@ http://git.example.com/project
 
 will provide human-friendly gitweb access.
 
+This solution is not 100% bulletproof, in the sense that if some project
+has a named ref (branch, tag) starting with 'git/', then paths such as
+
+http://git.example.com/project/command/abranch..git/abranch
+
+will fail with a 404 error.
+
+
 
 Originally written by:
   Kay Sievers <kay.sievers@vrfy.org>
index 43fa791de0691e7800e1471a6f0dfc81ce32a9dd..6f355d80699dabc315cec5a6eaea133ca55c3e6f 100755 (executable)
@@ -688,10 +688,10 @@ sub evaluate_path_info {
                # extensions. Allowed extensions are both the defined suffix
                # (which includes the initial dot already) and the snapshot
                # format key itself, with a prepended dot
-               while (my ($fmt, %opt) = each %known_snapshot_formats) {
+               while (my ($fmt, $opt) = each %known_snapshot_formats) {
                        my $hash = $refname;
                        my $sfx;
-                       $hash =~ s/(\Q$opt{'suffix'}\E|\Q.$fmt\E)$//;
+                       $hash =~ s/(\Q$opt->{'suffix'}\E|\Q.$fmt\E)$//;
                        next unless $sfx = $1;
                        # a valid suffix was found, so set the snapshot format
                        # and reset the hash parameter
diff --git a/graph.c b/graph.c
index 162a516ee15cca1f8ab118dd41b803a5d76e42ff..06fbeb6c2416d0c1c5fd7df1a12924656592cdc3 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "commit.h"
+#include "color.h"
 #include "graph.h"
 #include "diff.h"
 #include "revision.h"
@@ -34,7 +35,7 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);
  * newline.  A new graph line will not be printed after the final newline.
  * If the strbuf is empty, no output will be printed.
  *
- * Since the first line will not include the graph ouput, the caller is
+ * Since the first line will not include the graph output, the caller is
  * responsible for printing this line's graph (perhaps via
  * graph_show_commit() or graph_show_oneline()) before calling
  * graph_show_strbuf().
@@ -43,10 +44,6 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);
 
 /*
  * TODO:
- * - Add colors to the graph.
- *   Pick a color for each column, and print all characters
- *   in that column with the specified color.
- *
  * - Limit the number of columns, similar to the way gitk does.
  *   If we reach more than a specified number of columns, omit
  *   sections of some columns.
@@ -72,9 +69,10 @@ struct column {
         */
        struct commit *commit;
        /*
-        * XXX: Once we add support for colors, struct column could also
-        * contain the color of its branch line.
+        * The color to (optionally) print this column in.  This is an
+        * index into column_colors.
         */
+       unsigned short color;
 };
 
 enum graph_state {
@@ -86,6 +84,41 @@ enum graph_state {
        GRAPH_COLLAPSING
 };
 
+/*
+ * The list of available column colors.
+ */
+static char column_colors[][COLOR_MAXLEN] = {
+       GIT_COLOR_RED,
+       GIT_COLOR_GREEN,
+       GIT_COLOR_YELLOW,
+       GIT_COLOR_BLUE,
+       GIT_COLOR_MAGENTA,
+       GIT_COLOR_CYAN,
+       GIT_COLOR_BOLD GIT_COLOR_RED,
+       GIT_COLOR_BOLD GIT_COLOR_GREEN,
+       GIT_COLOR_BOLD GIT_COLOR_YELLOW,
+       GIT_COLOR_BOLD GIT_COLOR_BLUE,
+       GIT_COLOR_BOLD GIT_COLOR_MAGENTA,
+       GIT_COLOR_BOLD GIT_COLOR_CYAN,
+};
+
+#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
+
+static const char *column_get_color_code(const struct column *c)
+{
+       return column_colors[c->color];
+}
+
+static void strbuf_write_column(struct strbuf *sb, const struct column *c,
+                               char col_char)
+{
+       if (c->color < COLUMN_COLORS_MAX)
+               strbuf_addstr(sb, column_get_color_code(c));
+       strbuf_addch(sb, col_char);
+       if (c->color < COLUMN_COLORS_MAX)
+               strbuf_addstr(sb, GIT_COLOR_RESET);
+}
+
 struct git_graph {
        /*
         * The commit currently being processed
@@ -185,6 +218,11 @@ struct git_graph {
         * temporary array each time we have to output a collapsing line.
         */
        int *new_mapping;
+       /*
+        * The current default column color being used.  This is
+        * stored as an index into the array column_colors.
+        */
+       unsigned short default_column_color;
 };
 
 struct git_graph *graph_init(struct rev_info *opt)
@@ -201,6 +239,7 @@ struct git_graph *graph_init(struct rev_info *opt)
        graph->num_columns = 0;
        graph->num_new_columns = 0;
        graph->mapping_size = 0;
+       graph->default_column_color = 0;
 
        /*
         * Allocate a reasonably large default number of columns
@@ -312,6 +351,33 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph)
        return next_interesting_parent(graph, parents);
 }
 
+static unsigned short graph_get_current_column_color(const struct git_graph *graph)
+{
+       if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF))
+               return COLUMN_COLORS_MAX;
+       return graph->default_column_color;
+}
+
+/*
+ * Update the graph's default column color.
+ */
+static void graph_increment_column_color(struct git_graph *graph)
+{
+       graph->default_column_color = (graph->default_column_color + 1) %
+               COLUMN_COLORS_MAX;
+}
+
+static unsigned short graph_find_commit_color(const struct git_graph *graph,
+                                             const struct commit *commit)
+{
+       int i;
+       for (i = 0; i < graph->num_columns; i++) {
+               if (graph->columns[i].commit == commit)
+                       return graph->columns[i].color;
+       }
+       return graph_get_current_column_color(graph);
+}
+
 static void graph_insert_into_new_columns(struct git_graph *graph,
                                          struct commit *commit,
                                          int *mapping_index)
@@ -334,6 +400,7 @@ static void graph_insert_into_new_columns(struct git_graph *graph,
         * This commit isn't already in new_columns.  Add it.
         */
        graph->new_columns[graph->num_new_columns].commit = commit;
+       graph->new_columns[graph->num_new_columns].color = graph_find_commit_color(graph, commit);
        graph->mapping[*mapping_index] = graph->num_new_columns;
        *mapping_index += 2;
        graph->num_new_columns++;
@@ -445,6 +512,12 @@ static void graph_update_columns(struct git_graph *graph)
                        for (parent = first_interesting_parent(graph);
                             parent;
                             parent = next_interesting_parent(graph, parent)) {
+                               /*
+                                * If this is a merge increment the current
+                                * color.
+                                */
+                               if (graph->num_parents > 1)
+                                       graph_increment_column_color(graph);
                                graph_insert_into_new_columns(graph,
                                                              parent->item,
                                                              &mapping_idx);
@@ -560,7 +633,8 @@ static int graph_is_mapping_correct(struct git_graph *graph)
        return 1;
 }
 
-static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb)
+static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb,
+                                  int chars_written)
 {
        /*
         * Add additional spaces to the end of the strbuf, so that all
@@ -570,10 +644,10 @@ static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb)
         * aligned for the entire commit.
         */
        int extra;
-       if (sb->len >= graph->width)
+       if (chars_written >= graph->width)
                return;
 
-       extra = graph->width - sb->len;
+       extra = graph->width - chars_written;
        strbuf_addf(sb, "%*s", (int) extra, "");
 }
 
@@ -596,10 +670,11 @@ static void graph_output_padding_line(struct git_graph *graph,
         * Output a padding row, that leaves all branch lines unchanged
         */
        for (i = 0; i < graph->num_new_columns; i++) {
-               strbuf_addstr(sb, "| ");
+               strbuf_write_column(sb, &graph->new_columns[i], '|');
+               strbuf_addch(sb, ' ');
        }
 
-       graph_pad_horizontally(graph, sb);
+       graph_pad_horizontally(graph, sb, graph->num_new_columns * 2);
 }
 
 static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)
@@ -609,7 +684,7 @@ static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)
         * of the graph is missing.
         */
        strbuf_addstr(sb, "...");
-       graph_pad_horizontally(graph, sb);
+       graph_pad_horizontally(graph, sb, 3);
 
        if (graph->num_parents >= 3 &&
            graph->commit_index < (graph->num_columns - 1))
@@ -623,6 +698,7 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
 {
        int num_expansion_rows;
        int i, seen_this;
+       int chars_written;
 
        /*
         * This function formats a row that increases the space around a commit
@@ -645,11 +721,14 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
         * Output the row
         */
        seen_this = 0;
+       chars_written = 0;
        for (i = 0; i < graph->num_columns; i++) {
                struct column *col = &graph->columns[i];
                if (col->commit == graph->commit) {
                        seen_this = 1;
-                       strbuf_addf(sb, "| %*s", graph->expansion_row, "");
+                       strbuf_write_column(sb, col, '|');
+                       strbuf_addf(sb, "%*s", graph->expansion_row, "");
+                       chars_written += 1 + graph->expansion_row;
                } else if (seen_this && (graph->expansion_row == 0)) {
                        /*
                         * This is the first line of the pre-commit output.
@@ -662,17 +741,22 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
                         */
                        if (graph->prev_state == GRAPH_POST_MERGE &&
                            graph->prev_commit_index < i)
-                               strbuf_addstr(sb, "\\ ");
+                               strbuf_write_column(sb, col, '\\');
                        else
-                               strbuf_addstr(sb, "| ");
+                               strbuf_write_column(sb, col, '|');
+                       chars_written++;
                } else if (seen_this && (graph->expansion_row > 0)) {
-                       strbuf_addstr(sb, "\\ ");
+                       strbuf_write_column(sb, col, '\\');
+                       chars_written++;
                } else {
-                       strbuf_addstr(sb, "| ");
+                       strbuf_write_column(sb, col, '|');
+                       chars_written++;
                }
+               strbuf_addch(sb, ' ');
+               chars_written++;
        }
 
-       graph_pad_horizontally(graph, sb);
+       graph_pad_horizontally(graph, sb, chars_written);
 
        /*
         * Increment graph->expansion_row,
@@ -714,10 +798,34 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
        strbuf_addch(sb, '*');
 }
 
+/*
+ * Draw an octopus merge and return the number of characters written.
+ */
+static int graph_draw_octopus_merge(struct git_graph *graph,
+                                   struct strbuf *sb)
+{
+       /*
+        * Here dashless_commits represents the number of parents
+        * which don't need to have dashes (because their edges fit
+        * neatly under the commit).
+        */
+       const int dashless_commits = 2;
+       int col_num, i;
+       int num_dashes =
+               ((graph->num_parents - dashless_commits) * 2) - 1;
+       for (i = 0; i < num_dashes; i++) {
+               col_num = (i / 2) + dashless_commits;
+               strbuf_write_column(sb, &graph->new_columns[col_num], '-');
+       }
+       col_num = (i / 2) + dashless_commits;
+       strbuf_write_column(sb, &graph->new_columns[col_num], '.');
+       return num_dashes + 1;
+}
+
 static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
 {
        int seen_this = 0;
-       int i, j;
+       int i, chars_written;
 
        /*
         * Output the row containing this commit
@@ -727,7 +835,9 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
         * children that we have already processed.)
         */
        seen_this = 0;
+       chars_written = 0;
        for (i = 0; i <= graph->num_columns; i++) {
+               struct column *col = &graph->columns[i];
                struct commit *col_commit;
                if (i == graph->num_columns) {
                        if (seen_this)
@@ -740,18 +850,14 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
                if (col_commit == graph->commit) {
                        seen_this = 1;
                        graph_output_commit_char(graph, sb);
+                       chars_written++;
 
-                       if (graph->num_parents < 3)
-                               strbuf_addch(sb, ' ');
-                       else {
-                               int num_dashes =
-                                       ((graph->num_parents - 2) * 2) - 1;
-                               for (j = 0; j < num_dashes; j++)
-                                       strbuf_addch(sb, '-');
-                               strbuf_addstr(sb, ". ");
-                       }
+                       if (graph->num_parents > 2)
+                               chars_written += graph_draw_octopus_merge(graph,
+                                                                         sb);
                } else if (seen_this && (graph->num_parents > 2)) {
-                       strbuf_addstr(sb, "\\ ");
+                       strbuf_write_column(sb, col, '\\');
+                       chars_written++;
                } else if (seen_this && (graph->num_parents == 2)) {
                        /*
                         * This is a 2-way merge commit.
@@ -768,15 +874,19 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
                         */
                        if (graph->prev_state == GRAPH_POST_MERGE &&
                            graph->prev_commit_index < i)
-                               strbuf_addstr(sb, "\\ ");
+                               strbuf_write_column(sb, col, '\\');
                        else
-                               strbuf_addstr(sb, "| ");
+                               strbuf_write_column(sb, col, '|');
+                       chars_written++;
                } else {
-                       strbuf_addstr(sb, "| ");
+                       strbuf_write_column(sb, col, '|');
+                       chars_written++;
                }
+               strbuf_addch(sb, ' ');
+               chars_written++;
        }
 
-       graph_pad_horizontally(graph, sb);
+       graph_pad_horizontally(graph, sb, chars_written);
 
        /*
         * Update graph->state
@@ -789,37 +899,75 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
                graph_update_state(graph, GRAPH_COLLAPSING);
 }
 
+static struct column *find_new_column_by_commit(struct git_graph *graph,
+                                               struct commit *commit)
+{
+       int i;
+       for (i = 0; i < graph->num_new_columns; i++) {
+               if (graph->new_columns[i].commit == commit)
+                       return &graph->new_columns[i];
+       }
+       return 0;
+}
+
 static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
 {
        int seen_this = 0;
-       int i, j;
+       int i, j, chars_written;
 
        /*
         * Output the post-merge row
         */
+       chars_written = 0;
        for (i = 0; i <= graph->num_columns; i++) {
+               struct column *col = &graph->columns[i];
                struct commit *col_commit;
                if (i == graph->num_columns) {
                        if (seen_this)
                                break;
                        col_commit = graph->commit;
                } else {
-                       col_commit = graph->columns[i].commit;
+                       col_commit = col->commit;
                }
 
                if (col_commit == graph->commit) {
+                       /*
+                        * Since the current commit is a merge find
+                        * the columns for the parent commits in
+                        * new_columns and use those to format the
+                        * edges.
+                        */
+                       struct commit_list *parents = NULL;
+                       struct column *par_column;
                        seen_this = 1;
-                       strbuf_addch(sb, '|');
-                       for (j = 0; j < graph->num_parents - 1; j++)
-                               strbuf_addstr(sb, "\\ ");
+                       parents = first_interesting_parent(graph);
+                       assert(parents);
+                       par_column = find_new_column_by_commit(graph, parents->item);
+                       assert(par_column);
+
+                       strbuf_write_column(sb, par_column, '|');
+                       chars_written++;
+                       for (j = 0; j < graph->num_parents - 1; j++) {
+                               parents = next_interesting_parent(graph, parents);
+                               assert(parents);
+                               par_column = find_new_column_by_commit(graph, parents->item);
+                               assert(par_column);
+                               strbuf_write_column(sb, par_column, '\\');
+                               strbuf_addch(sb, ' ');
+                       }
+                       chars_written += j * 2;
                } else if (seen_this) {
-                       strbuf_addstr(sb, "\\ ");
+                       strbuf_write_column(sb, col, '\\');
+                       strbuf_addch(sb, ' ');
+                       chars_written += 2;
                } else {
-                       strbuf_addstr(sb, "| ");
+                       strbuf_write_column(sb, col, '|');
+                       strbuf_addch(sb, ' ');
+                       chars_written += 2;
                }
        }
 
-       graph_pad_horizontally(graph, sb);
+       graph_pad_horizontally(graph, sb, chars_written);
 
        /*
         * Update graph->state
@@ -912,12 +1060,12 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                if (target < 0)
                        strbuf_addch(sb, ' ');
                else if (target * 2 == i)
-                       strbuf_addch(sb, '|');
+                       strbuf_write_column(sb, &graph->new_columns[target], '|');
                else
-                       strbuf_addch(sb, '/');
+                       strbuf_write_column(sb, &graph->new_columns[target], '/');
        }
 
-       graph_pad_horizontally(graph, sb);
+       graph_pad_horizontally(graph, sb, graph->mapping_size);
 
        /*
         * Swap mapping and new_mapping
@@ -979,9 +1127,10 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
         * children that we have already processed.)
         */
        for (i = 0; i < graph->num_columns; i++) {
-               struct commit *col_commit = graph->columns[i].commit;
+               struct column *col = &graph->columns[i];
+               struct commit *col_commit = col->commit;
                if (col_commit == graph->commit) {
-                       strbuf_addch(sb, '|');
+                       strbuf_write_column(sb, col, '|');
 
                        if (graph->num_parents < 3)
                                strbuf_addch(sb, ' ');
@@ -991,11 +1140,12 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
                                        strbuf_addch(sb, ' ');
                        }
                } else {
-                       strbuf_addstr(sb, "| ");
+                       strbuf_write_column(sb, col, '|');
+                       strbuf_addch(sb, ' ');
                }
        }
 
-       graph_pad_horizontally(graph, sb);
+       graph_pad_horizontally(graph, sb, graph->num_columns);
 
        /*
         * Update graph->prev_state since we have output a padding line
diff --git a/grep.c b/grep.c
index b0d992a6e0a838d0fb4e71e3a8bebe8266ed1070..92a47c71e7d93eef7dc8d6967cd071aa061218ce 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -39,6 +39,8 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
 {
        int err;
 
+       p->word_regexp = opt->word_regexp;
+
        if (opt->fixed || is_fixed(p->pattern))
                p->fixed = 1;
        if (opt->regflags & REG_ICASE)
@@ -192,7 +194,8 @@ void compile_grep_patterns(struct grep_opt *opt)
         * A classic recursive descent parser would do.
         */
        p = opt->pattern_list;
-       opt->pattern_expression = compile_pattern_expr(&p);
+       if (p)
+               opt->pattern_expression = compile_pattern_expr(&p);
        if (p)
                die("incomplete pattern expression: %s", p->pattern);
 }
@@ -253,18 +256,6 @@ static int word_char(char ch)
        return isalnum(ch) || ch == '_';
 }
 
-static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
-                     const char *name, unsigned lno, char sign)
-{
-       if (opt->null_following_name)
-               sign = '\0';
-       if (opt->pathname)
-               printf("%s%c", name, sign);
-       if (opt->linenum)
-               printf("%d%c", lno, sign);
-       printf("%.*s\n", (int)(eol-bol), bol);
-}
-
 static void show_name(struct grep_opt *opt, const char *name)
 {
        printf("%s%c", name, opt->null_following_name ? '\0' : '\n');
@@ -308,11 +299,13 @@ static struct {
        { "committer ", 10 },
 };
 
-static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx)
+static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
+                            enum grep_context ctx,
+                            regmatch_t *pmatch, int eflags)
 {
        int hit = 0;
        int saved_ch = 0;
-       regmatch_t pmatch[10];
+       const char *start = bol;
 
        if ((p->token != GREP_PATTERN) &&
            ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
@@ -331,18 +324,14 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
        }
 
  again:
-       if (!p->fixed) {
-               regex_t *exp = &p->regexp;
-               hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
-                              pmatch, 0);
-       }
-       else {
+       if (p->fixed)
                hit = !fixmatch(p->pattern, bol, pmatch);
-       }
+       else
+               hit = !regexec(&p->regexp, bol, 1, pmatch, eflags);
 
-       if (hit && opt->word_regexp) {
+       if (hit && p->word_regexp) {
                if ((pmatch[0].rm_so < 0) ||
-                   (eol - bol) <= pmatch[0].rm_so ||
+                   (eol - bol) < pmatch[0].rm_so ||
                    (pmatch[0].rm_eo < 0) ||
                    (eol - bol) < pmatch[0].rm_eo)
                        die("regexp returned nonsense");
@@ -361,6 +350,10 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
                else
                        hit = 0;
 
+               /* Words consist of at least one character. */
+               if (pmatch->rm_so == pmatch->rm_eo)
+                       hit = 0;
+
                if (!hit && pmatch[0].rm_so + bol + 1 < eol) {
                        /* There could be more than one match on the
                         * line, and the first match might not be
@@ -371,50 +364,49 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
                        bol = pmatch[0].rm_so + bol + 1;
                        while (word_char(bol[-1]) && bol < eol)
                                bol++;
+                       eflags |= REG_NOTBOL;
                        if (bol < eol)
                                goto again;
                }
        }
        if (p->token == GREP_PATTERN_HEAD && saved_ch)
                *eol = saved_ch;
+       if (hit) {
+               pmatch[0].rm_so += bol - start;
+               pmatch[0].rm_eo += bol - start;
+       }
        return hit;
 }
 
-static int match_expr_eval(struct grep_opt *o,
-                          struct grep_expr *x,
-                          char *bol, char *eol,
-                          enum grep_context ctx,
-                          int collect_hits)
+static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
+                          enum grep_context ctx, int collect_hits)
 {
        int h = 0;
+       regmatch_t match;
 
        if (!x)
                die("Not a valid grep expression");
        switch (x->node) {
        case GREP_NODE_ATOM:
-               h = match_one_pattern(o, x->u.atom, bol, eol, ctx);
+               h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
                break;
        case GREP_NODE_NOT:
-               h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);
+               h = !match_expr_eval(x->u.unary, bol, eol, ctx, 0);
                break;
        case GREP_NODE_AND:
-               if (!collect_hits)
-                       return (match_expr_eval(o, x->u.binary.left,
-                                               bol, eol, ctx, 0) &&
-                               match_expr_eval(o, x->u.binary.right,
-                                               bol, eol, ctx, 0));
-               h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
-               h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);
+               if (!match_expr_eval(x->u.binary.left, bol, eol, ctx, 0))
+                       return 0;
+               h = match_expr_eval(x->u.binary.right, bol, eol, ctx, 0);
                break;
        case GREP_NODE_OR:
                if (!collect_hits)
-                       return (match_expr_eval(o, x->u.binary.left,
+                       return (match_expr_eval(x->u.binary.left,
                                                bol, eol, ctx, 0) ||
-                               match_expr_eval(o, x->u.binary.right,
+                               match_expr_eval(x->u.binary.right,
                                                bol, eol, ctx, 0));
-               h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
+               h = match_expr_eval(x->u.binary.left, bol, eol, ctx, 0);
                x->u.binary.left->hit |= h;
-               h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);
+               h |= match_expr_eval(x->u.binary.right, bol, eol, ctx, 1);
                break;
        default:
                die("Unexpected node type (internal error) %d", x->node);
@@ -428,24 +420,106 @@ static int match_expr(struct grep_opt *opt, char *bol, char *eol,
                      enum grep_context ctx, int collect_hits)
 {
        struct grep_expr *x = opt->pattern_expression;
-       return match_expr_eval(opt, x, bol, eol, ctx, collect_hits);
+       return match_expr_eval(x, bol, eol, ctx, collect_hits);
 }
 
 static int match_line(struct grep_opt *opt, char *bol, char *eol,
                      enum grep_context ctx, int collect_hits)
 {
        struct grep_pat *p;
+       regmatch_t match;
+
        if (opt->extended)
                return match_expr(opt, bol, eol, ctx, collect_hits);
 
        /* we do not call with collect_hits without being extended */
        for (p = opt->pattern_list; p; p = p->next) {
-               if (match_one_pattern(opt, p, bol, eol, ctx))
+               if (match_one_pattern(p, bol, eol, ctx, &match, 0))
                        return 1;
        }
        return 0;
 }
 
+static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,
+                             enum grep_context ctx,
+                             regmatch_t *pmatch, int eflags)
+{
+       regmatch_t match;
+
+       if (!match_one_pattern(p, bol, eol, ctx, &match, eflags))
+               return 0;
+       if (match.rm_so < 0 || match.rm_eo < 0)
+               return 0;
+       if (pmatch->rm_so >= 0 && pmatch->rm_eo >= 0) {
+               if (match.rm_so > pmatch->rm_so)
+                       return 1;
+               if (match.rm_so == pmatch->rm_so && match.rm_eo < pmatch->rm_eo)
+                       return 1;
+       }
+       pmatch->rm_so = match.rm_so;
+       pmatch->rm_eo = match.rm_eo;
+       return 1;
+}
+
+static int next_match(struct grep_opt *opt, char *bol, char *eol,
+                     enum grep_context ctx, regmatch_t *pmatch, int eflags)
+{
+       struct grep_pat *p;
+       int hit = 0;
+
+       pmatch->rm_so = pmatch->rm_eo = -1;
+       if (bol < eol) {
+               for (p = opt->pattern_list; p; p = p->next) {
+                       switch (p->token) {
+                       case GREP_PATTERN: /* atom */
+                       case GREP_PATTERN_HEAD:
+                       case GREP_PATTERN_BODY:
+                               hit |= match_next_pattern(p, bol, eol, ctx,
+                                                         pmatch, eflags);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+       return hit;
+}
+
+static void show_line(struct grep_opt *opt, char *bol, char *eol,
+                     const char *name, unsigned lno, char sign)
+{
+       int rest = eol - bol;
+
+       if (opt->null_following_name)
+               sign = '\0';
+       if (opt->pathname)
+               printf("%s%c", name, sign);
+       if (opt->linenum)
+               printf("%d%c", lno, sign);
+       if (opt->color) {
+               regmatch_t match;
+               enum grep_context ctx = GREP_CONTEXT_BODY;
+               int ch = *eol;
+               int eflags = 0;
+
+               *eol = '\0';
+               while (next_match(opt, bol, eol, ctx, &match, eflags)) {
+                       if (match.rm_so == match.rm_eo)
+                               break;
+                       printf("%.*s%s%.*s%s",
+                              (int)match.rm_so, bol,
+                              opt->color_match,
+                              (int)(match.rm_eo - match.rm_so), bol + match.rm_so,
+                              GIT_COLOR_RESET);
+                       bol += match.rm_eo;
+                       rest -= match.rm_eo;
+                       eflags = REG_NOTBOL;
+               }
+               *eol = ch;
+       }
+       printf("%.*s\n", rest, bol);
+}
+
 static int grep_buffer_1(struct grep_opt *opt, const char *name,
                         char *buf, unsigned long size, int collect_hits)
 {
diff --git a/grep.h b/grep.h
index 5102ce335d29811dd448e173f2e90e8d03b5f011..a67005de62d1442e7ba6a8dc27320225a0d55819 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -1,5 +1,6 @@
 #ifndef GREP_H
 #define GREP_H
+#include "color.h"
 
 enum grep_pat_token {
        GREP_PATTERN,
@@ -31,6 +32,7 @@ struct grep_pat {
        enum grep_header_field field;
        regex_t regexp;
        unsigned fixed:1;
+       unsigned word_regexp:1;
 };
 
 enum grep_expr_node {
@@ -76,6 +78,9 @@ struct grep_opt {
        unsigned relative:1;
        unsigned pathname:1;
        unsigned null_following_name:1;
+       int color;
+       char color_match[COLOR_MAXLEN];
+       const char *color_external;
        int regflags;
        unsigned pre_context;
        unsigned post_context;
diff --git a/help.c b/help.c
index fd87bb5aeec82beec600be46248b19b13bb33804..da0cca0a15300c2ebaab99565fd6e85b0cd374ec 100644 (file)
--- a/help.c
+++ b/help.c
@@ -302,7 +302,7 @@ const char *help_unknown_cmd(const char *cmd)
        struct cmdnames main_cmds, other_cmds;
 
        memset(&main_cmds, 0, sizeof(main_cmds));
-       memset(&other_cmds, 0, sizeof(main_cmds));
+       memset(&other_cmds, 0, sizeof(other_cmds));
        memset(&aliases, 0, sizeof(aliases));
 
        git_config(git_unknown_cmd_config, NULL);
@@ -334,7 +334,7 @@ const char *help_unknown_cmd(const char *cmd)
                const char *assumed = main_cmds.names[0]->name;
                main_cmds.names[0] = NULL;
                clean_cmdnames(&main_cmds);
-               fprintf(stderr, "WARNING: You called a Git program named '%s', "
+               fprintf(stderr, "WARNING: You called a Git command named '%s', "
                        "which does not exist.\n"
                        "Continuing under the assumption that you meant '%s'\n",
                        cmd, assumed);
index f101df40861a13db40cb2f564476369f3765a28f..0b12ffe18f1197abf1c9351d015fcb0f8ef99931 100644 (file)
@@ -97,7 +97,7 @@ struct repo
        struct remote_lock *locks;
 };
 
-static struct repo *remote;
+static struct repo *repo;
 
 enum transfer_state {
        NEED_FETCH,
@@ -186,6 +186,32 @@ enum dav_header_flag {
        DAV_HEADER_TIMEOUT = (1u << 2)
 };
 
+static char *xml_entities(char *s)
+{
+       struct strbuf buf = STRBUF_INIT;
+       while (*s) {
+               size_t len = strcspn(s, "\"<>&");
+               strbuf_add(&buf, s, len);
+               s += len;
+               switch (*s) {
+               case '"':
+                       strbuf_addstr(&buf, "&quot;");
+                       break;
+               case '<':
+                       strbuf_addstr(&buf, "&lt;");
+                       break;
+               case '>':
+                       strbuf_addstr(&buf, "&gt;");
+                       break;
+               case '&':
+                       strbuf_addstr(&buf, "&amp;");
+                       break;
+               }
+               s++;
+       }
+       return strbuf_detach(&buf, NULL);
+}
+
 static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -289,9 +315,9 @@ static void start_fetch_loose(struct transfer_request *request)
                 "%s.temp", filename);
 
        snprintf(prevfile, sizeof(prevfile), "%s.prev", request->filename);
-       unlink(prevfile);
+       unlink_or_warn(prevfile);
        rename(request->tmpfile, prevfile);
-       unlink(request->tmpfile);
+       unlink_or_warn(request->tmpfile);
 
        if (request->local_fileno != -1)
                error("fd leakage in start: %d", request->local_fileno);
@@ -324,7 +350,7 @@ static void start_fetch_loose(struct transfer_request *request)
 
        git_SHA1_Init(&request->c);
 
-       url = get_remote_object_url(remote->url, hex, 0);
+       url = get_remote_object_url(repo->url, hex, 0);
        request->url = xstrdup(url);
 
        /* If a previous temp file is present, process what was already
@@ -346,7 +372,7 @@ static void start_fetch_loose(struct transfer_request *request)
                } while (prev_read > 0);
                close(prevlocal);
        }
-       unlink(prevfile);
+       unlink_or_warn(prevfile);
 
        /* Reset inflate/SHA1 if there was an error reading the previous temp
           file; also rewind to the beginning of the local file. */
@@ -389,7 +415,7 @@ static void start_fetch_loose(struct transfer_request *request)
        request->state = RUN_FETCH_LOOSE;
        if (!start_active_slot(slot)) {
                fprintf(stderr, "Unable to start GET request\n");
-               remote->can_update_info_refs = 0;
+               repo->can_update_info_refs = 0;
                release_request(request);
        }
 }
@@ -399,7 +425,7 @@ static void start_mkcol(struct transfer_request *request)
        char *hex = sha1_to_hex(request->obj->sha1);
        struct active_request_slot *slot;
 
-       request->url = get_remote_object_url(remote->url, hex, 1);
+       request->url = get_remote_object_url(repo->url, hex, 1);
 
        slot = get_active_slot();
        slot->callback_func = process_response;
@@ -434,10 +460,10 @@ static void start_fetch_packed(struct transfer_request *request)
        struct transfer_request *check_request = request_queue_head;
        struct active_request_slot *slot;
 
-       target = find_sha1_pack(request->obj->sha1, remote->packs);
+       target = find_sha1_pack(request->obj->sha1, repo->packs);
        if (!target) {
                fprintf(stderr, "Unable to fetch %s, will not be able to update server info refs\n", sha1_to_hex(request->obj->sha1));
-               remote->can_update_info_refs = 0;
+               repo->can_update_info_refs = 0;
                release_request(request);
                return;
        }
@@ -450,9 +476,9 @@ static void start_fetch_packed(struct transfer_request *request)
        snprintf(request->tmpfile, sizeof(request->tmpfile),
                 "%s.temp", filename);
 
-       url = xmalloc(strlen(remote->url) + 64);
+       url = xmalloc(strlen(repo->url) + 64);
        sprintf(url, "%sobjects/pack/pack-%s.pack",
-               remote->url, sha1_to_hex(target->sha1));
+               repo->url, sha1_to_hex(target->sha1));
 
        /* Make sure there isn't another open request for this pack */
        while (check_request) {
@@ -469,7 +495,7 @@ static void start_fetch_packed(struct transfer_request *request)
        if (!packfile) {
                fprintf(stderr, "Unable to open local file %s for pack",
                        request->tmpfile);
-               remote->can_update_info_refs = 0;
+               repo->can_update_info_refs = 0;
                free(url);
                return;
        }
@@ -505,7 +531,7 @@ static void start_fetch_packed(struct transfer_request *request)
        request->state = RUN_FETCH_PACKED;
        if (!start_active_slot(slot)) {
                fprintf(stderr, "Unable to start GET request\n");
-               remote->can_update_info_refs = 0;
+               repo->can_update_info_refs = 0;
                release_request(request);
        }
 }
@@ -554,10 +580,10 @@ static void start_put(struct transfer_request *request)
        request->buffer.buf.len = stream.total_out;
 
        strbuf_addstr(&buf, "Destination: ");
-       append_remote_object_url(&buf, remote->url, hex, 0);
+       append_remote_object_url(&buf, repo->url, hex, 0);
        request->dest = strbuf_detach(&buf, NULL);
 
-       append_remote_object_url(&buf, remote->url, hex, 0);
+       append_remote_object_url(&buf, repo->url, hex, 0);
        strbuf_add(&buf, request->lock->tmpfile_suffix, 41);
        request->url = strbuf_detach(&buf, NULL);
 
@@ -567,6 +593,10 @@ static void start_put(struct transfer_request *request)
        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer);
        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.buf.len);
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+       curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &request->buffer);
+#endif
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
@@ -648,7 +678,7 @@ static int refresh_lock(struct remote_lock *lock)
 
 static void check_locks(void)
 {
-       struct remote_lock *lock = remote->locks;
+       struct remote_lock *lock = repo->locks;
        time_t current_time = time(NULL);
        int time_remaining;
 
@@ -694,9 +724,11 @@ static void finish_request(struct transfer_request *request)
        struct stat st;
        struct packed_git *target;
        struct packed_git **lst;
+       struct active_request_slot *slot;
 
        request->curl_result = request->slot->curl_result;
        request->http_code = request->slot->http_code;
+       slot = request->slot;
        request->slot = NULL;
 
        /* Keep locks active */
@@ -754,18 +786,18 @@ static void finish_request(struct transfer_request *request)
                    request->http_code != 416) {
                        if (stat(request->tmpfile, &st) == 0) {
                                if (st.st_size == 0)
-                                       unlink(request->tmpfile);
+                                       unlink_or_warn(request->tmpfile);
                        }
                } else {
                        if (request->http_code == 416)
-                               fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
+                               warning("requested range invalid; we may already have all the data.");
 
                        git_inflate_end(&request->stream);
                        git_SHA1_Final(request->real_sha1, &request->c);
                        if (request->zret != Z_STREAM_END) {
-                               unlink(request->tmpfile);
+                               unlink_or_warn(request->tmpfile);
                        } else if (hashcmp(request->obj->sha1, request->real_sha1)) {
-                               unlink(request->tmpfile);
+                               unlink_or_warn(request->tmpfile);
                        } else {
                                request->rename =
                                        move_temp_to_file(
@@ -787,17 +819,18 @@ static void finish_request(struct transfer_request *request)
                if (request->curl_result != CURLE_OK) {
                        fprintf(stderr, "Unable to get pack file %s\n%s",
                                request->url, curl_errorstr);
-                       remote->can_update_info_refs = 0;
+                       repo->can_update_info_refs = 0;
                } else {
                        off_t pack_size = ftell(request->local_stream);
 
                        fclose(request->local_stream);
                        request->local_stream = NULL;
+                       slot->local = NULL;
                        if (!move_temp_to_file(request->tmpfile,
                                               request->filename)) {
                                target = (struct packed_git *)request->userData;
                                target->pack_size = pack_size;
-                               lst = &remote->packs;
+                               lst = &repo->packs;
                                while (*lst != target)
                                        lst = &((*lst)->next);
                                *lst = (*lst)->next;
@@ -805,7 +838,7 @@ static void finish_request(struct transfer_request *request)
                                if (!verify_pack(target))
                                        install_packed_git(target);
                                else
-                                       remote->can_update_info_refs = 0;
+                                       repo->can_update_info_refs = 0;
                        }
                }
                release_request(request);
@@ -815,7 +848,7 @@ static void finish_request(struct transfer_request *request)
 #ifdef USE_CURL_MULTI
 static int fill_active_slot(void *unused)
 {
-       struct transfer_request *request = request_queue_head;
+       struct transfer_request *request;
 
        if (aborted)
                return 0;
@@ -888,7 +921,7 @@ static int add_send_request(struct object *obj, struct remote_lock *lock)
                get_remote_object_list(obj->sha1[0]);
        if (obj->flags & (REMOTE | PUSHING))
                return 0;
-       target = find_sha1_pack(obj->sha1, remote->packs);
+       target = find_sha1_pack(obj->sha1, repo->packs);
        if (target) {
                obj->flags |= REMOTE;
                return 0;
@@ -929,8 +962,8 @@ static int fetch_index(unsigned char *sha1)
        struct slot_results results;
 
        /* Don't use the index if the pack isn't there */
-       url = xmalloc(strlen(remote->url) + 64);
-       sprintf(url, "%sobjects/pack/pack-%s.pack", remote->url, hex);
+       url = xmalloc(strlen(repo->url) + 64);
+       sprintf(url, "%sobjects/pack/pack-%s.pack", repo->url, hex);
        slot = get_active_slot();
        slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
@@ -955,7 +988,7 @@ static int fetch_index(unsigned char *sha1)
        if (push_verbosely)
                fprintf(stderr, "Getting index for pack %s\n", hex);
 
-       sprintf(url, "%sobjects/pack/pack-%s.idx", remote->url, hex);
+       sprintf(url, "%sobjects/pack/pack-%s.idx", repo->url, hex);
 
        filename = sha1_pack_index_name(sha1);
        snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
@@ -994,17 +1027,20 @@ static int fetch_index(unsigned char *sha1)
                if (results.curl_result != CURLE_OK) {
                        free(url);
                        fclose(indexfile);
+                       slot->local = NULL;
                        return error("Unable to get pack index %s\n%s", url,
                                     curl_errorstr);
                }
        } else {
                free(url);
                fclose(indexfile);
+               slot->local = NULL;
                return error("Unable to start request");
        }
 
        free(url);
        fclose(indexfile);
+       slot->local = NULL;
 
        return move_temp_to_file(tmpfile, filename);
 }
@@ -1017,8 +1053,8 @@ static int setup_index(unsigned char *sha1)
                return -1;
 
        new_pack = parse_pack_index(sha1);
-       new_pack->next = remote->packs;
-       remote->packs = new_pack;
+       new_pack->next = repo->packs;
+       repo->packs = new_pack;
        return 0;
 }
 
@@ -1036,8 +1072,8 @@ static int fetch_indices(void)
        if (push_verbosely)
                fprintf(stderr, "Getting pack list\n");
 
-       url = xmalloc(strlen(remote->url) + 20);
-       sprintf(url, "%sobjects/info/packs", remote->url);
+       url = xmalloc(strlen(repo->url) + 20);
+       sprintf(url, "%sobjects/info/packs", repo->url);
 
        slot = get_active_slot();
        slot->results = &results;
@@ -1221,12 +1257,13 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
        struct remote_lock *lock = NULL;
        struct curl_slist *dav_headers = NULL;
        struct xml_ctx ctx;
+       char *escaped;
 
-       url = xmalloc(strlen(remote->url) + strlen(path) + 1);
-       sprintf(url, "%s%s", remote->url, path);
+       url = xmalloc(strlen(repo->url) + strlen(path) + 1);
+       sprintf(url, "%s%s", repo->url, path);
 
        /* Make sure leading directories exist for the remote ref */
-       ep = strchr(url + strlen(remote->url) + 1, '/');
+       ep = strchr(url + strlen(repo->url) + 1, '/');
        while (ep) {
                char saved_character = ep[1];
                ep[1] = '\0';
@@ -1255,7 +1292,9 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
                ep = strchr(ep + 1, '/');
        }
 
-       strbuf_addf(&out_buffer.buf, LOCK_REQUEST, git_default_email);
+       escaped = xml_entities(git_default_email);
+       strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped);
+       free(escaped);
 
        sprintf(timeout_header, "Timeout: Second-%ld", timeout);
        dav_headers = curl_slist_append(dav_headers, timeout_header);
@@ -1266,6 +1305,10 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+       curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
+#endif
        curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
@@ -1318,8 +1361,8 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
        } else {
                lock->url = url;
                lock->start_time = time(NULL);
-               lock->next = remote->locks;
-               remote->locks = lock;
+               lock->next = repo->locks;
+               repo->locks = lock;
        }
 
        return lock;
@@ -1329,7 +1372,7 @@ static int unlock_remote(struct remote_lock *lock)
 {
        struct active_request_slot *slot;
        struct slot_results results;
-       struct remote_lock *prev = remote->locks;
+       struct remote_lock *prev = repo->locks;
        struct curl_slist *dav_headers;
        int rc = 0;
 
@@ -1355,8 +1398,8 @@ static int unlock_remote(struct remote_lock *lock)
 
        curl_slist_free_all(dav_headers);
 
-       if (remote->locks == lock) {
-               remote->locks = lock->next;
+       if (repo->locks == lock) {
+               repo->locks = lock->next;
        } else {
                while (prev && prev->next != lock)
                        prev = prev->next;
@@ -1374,7 +1417,7 @@ static int unlock_remote(struct remote_lock *lock)
 
 static void remove_locks(void)
 {
-       struct remote_lock *lock = remote->locks;
+       struct remote_lock *lock = repo->locks;
 
        fprintf(stderr, "Removing remote locks...\n");
        while (lock) {
@@ -1457,7 +1500,7 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
                                }
                        }
                        if (path) {
-                               path += remote->path_len;
+                               path += repo->path_len;
                                ls->dentry_name = xstrdup(path);
                        }
                } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
@@ -1480,7 +1523,7 @@ static void remote_ls(const char *path, int flags,
                      void (*userFunc)(struct remote_ls_ctx *ls),
                      void *userData)
 {
-       char *url = xmalloc(strlen(remote->url) + strlen(path) + 1);
+       char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
        struct active_request_slot *slot;
        struct slot_results results;
        struct strbuf in_buffer = STRBUF_INIT;
@@ -1496,7 +1539,7 @@ static void remote_ls(const char *path, int flags,
        ls.userData = userData;
        ls.userFunc = userFunc;
 
-       sprintf(url, "%s%s", remote->url, path);
+       sprintf(url, "%s%s", repo->url, path);
 
        strbuf_addf(&out_buffer.buf, PROPFIND_ALL_REQUEST);
 
@@ -1508,6 +1551,10 @@ static void remote_ls(const char *path, int flags,
        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+       curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
+#endif
        curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
@@ -1573,8 +1620,11 @@ static int locking_available(void)
        struct curl_slist *dav_headers = NULL;
        struct xml_ctx ctx;
        int lock_flags = 0;
+       char *escaped;
 
-       strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, remote->url);
+       escaped = xml_entities(repo->url);
+       strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, escaped);
+       free(escaped);
 
        dav_headers = curl_slist_append(dav_headers, "Depth: 0");
        dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
@@ -1584,9 +1634,13 @@ static int locking_available(void)
        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+       curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
+#endif
        curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_URL, remote->url);
+       curl_easy_setopt(slot->curl, CURLOPT_URL, repo->url);
        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
@@ -1616,16 +1670,16 @@ static int locking_available(void)
                        }
                        XML_ParserFree(parser);
                        if (!lock_flags)
-                               error("Error: no DAV locking support on %s",
-                                     remote->url);
+                               error("no DAV locking support on %s",
+                                     repo->url);
 
                } else {
                        error("Cannot access URL %s, return code %d",
-                             remote->url, results.curl_result);
+                             repo->url, results.curl_result);
                        lock_flags = 0;
                }
        } else {
-               error("Unable to start PROPFIND request on %s", remote->url);
+               error("Unable to start PROPFIND request on %s", repo->url);
        }
 
        strbuf_release(&out_buffer.buf);
@@ -1766,6 +1820,10 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+       curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
+#endif
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
@@ -1792,21 +1850,8 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
        return 1;
 }
 
-static struct ref *local_refs, **local_tail;
 static struct ref *remote_refs, **remote_tail;
 
-static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-       struct ref *ref;
-       int len = strlen(refname) + 1;
-       ref = xcalloc(1, sizeof(*ref) + len);
-       hashcpy(ref->new_sha1, sha1);
-       memcpy(ref->name, refname, len);
-       *local_tail = ref;
-       local_tail = &ref->next;
-       return 0;
-}
-
 static void one_remote_ref(char *refname)
 {
        struct ref *ref;
@@ -1814,10 +1859,10 @@ static void one_remote_ref(char *refname)
 
        ref = alloc_ref(refname);
 
-       if (http_fetch_ref(remote->url, ref) != 0) {
+       if (http_fetch_ref(repo->url, ref) != 0) {
                fprintf(stderr,
                        "Unable to fetch ref %s from %s\n",
-                       refname, remote->url);
+                       refname, repo->url);
                free(ref);
                return;
        }
@@ -1826,7 +1871,7 @@ static void one_remote_ref(char *refname)
         * Fetch a copy of the object if it doesn't exist locally - it
         * may be required for updating server info later.
         */
-       if (remote->can_update_info_refs && !has_sha1_file(ref->old_sha1)) {
+       if (repo->can_update_info_refs && !has_sha1_file(ref->old_sha1)) {
                obj = lookup_unknown_object(ref->old_sha1);
                if (obj) {
                        fprintf(stderr, "  fetch %s for %s\n",
@@ -1839,12 +1884,6 @@ static void one_remote_ref(char *refname)
        remote_tail = &ref->next;
 }
 
-static void get_local_heads(void)
-{
-       local_tail = &local_refs;
-       for_each_ref(one_local_ref, NULL);
-}
-
 static void get_dav_remote_heads(void)
 {
        remote_tail = &remote_refs;
@@ -1862,55 +1901,6 @@ static int is_zero_sha1(const unsigned char *sha1)
        return 1;
 }
 
-static void unmark_and_free(struct commit_list *list, unsigned int mark)
-{
-       while (list) {
-               struct commit_list *temp = list;
-               temp->item->object.flags &= ~mark;
-               list = temp->next;
-               free(temp);
-       }
-}
-
-static int ref_newer(const unsigned char *new_sha1,
-                    const unsigned char *old_sha1)
-{
-       struct object *o;
-       struct commit *old, *new;
-       struct commit_list *list, *used;
-       int found = 0;
-
-       /* Both new and old must be commit-ish and new is descendant of
-        * old.  Otherwise we require --force.
-        */
-       o = deref_tag(parse_object(old_sha1), NULL, 0);
-       if (!o || o->type != OBJ_COMMIT)
-               return 0;
-       old = (struct commit *) o;
-
-       o = deref_tag(parse_object(new_sha1), NULL, 0);
-       if (!o || o->type != OBJ_COMMIT)
-               return 0;
-       new = (struct commit *) o;
-
-       if (parse_commit(new) < 0)
-               return 0;
-
-       used = list = NULL;
-       commit_list_insert(new, &list);
-       while (list) {
-               new = pop_most_recent_commit(&list, TMP_MARK);
-               commit_list_insert(new, &used);
-               if (new == old) {
-                       found = 1;
-                       break;
-               }
-       }
-       unmark_and_free(list, TMP_MARK);
-       unmark_and_free(used, TMP_MARK);
-       return found;
-}
-
 static void add_remote_info_ref(struct remote_ls_ctx *ls)
 {
        struct strbuf *buf = (struct strbuf *)ls->userData;
@@ -1921,10 +1911,10 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)
 
        ref = alloc_ref(ls->dentry_name);
 
-       if (http_fetch_ref(remote->url, ref) != 0) {
+       if (http_fetch_ref(repo->url, ref) != 0) {
                fprintf(stderr,
                        "Unable to fetch ref %s from %s\n",
-                       ls->dentry_name, remote->url);
+                       ls->dentry_name, repo->url);
                aborted = 1;
                free(ref);
                return;
@@ -1978,6 +1968,10 @@ static void update_remote_info_refs(struct remote_lock *lock)
                curl_easy_setopt(slot->curl, CURLOPT_INFILE, &buffer);
                curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, buffer.buf.len);
                curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+               curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+               curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &buffer);
+#endif
                curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
                curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
                curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
@@ -1999,12 +1993,12 @@ static void update_remote_info_refs(struct remote_lock *lock)
 
 static int remote_exists(const char *path)
 {
-       char *url = xmalloc(strlen(remote->url) + strlen(path) + 1);
+       char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
        struct active_request_slot *slot;
        struct slot_results results;
        int ret = -1;
 
-       sprintf(url, "%s%s", remote->url, path);
+       sprintf(url, "%s%s", repo->url, path);
 
        slot = get_active_slot();
        slot->results = &results;
@@ -2034,8 +2028,8 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
        struct active_request_slot *slot;
        struct slot_results results;
 
-       url = xmalloc(strlen(remote->url) + strlen(path) + 1);
-       sprintf(url, "%s%s", remote->url, path);
+       url = xmalloc(strlen(repo->url) + strlen(path) + 1);
+       sprintf(url, "%s%s", repo->url, path);
 
        slot = get_active_slot();
        slot->results = &results;
@@ -2150,7 +2144,7 @@ static int delete_remote_branch(char *pattern, int force)
                                     "of your current HEAD.\n"
                                     "If you are sure you want to delete it,"
                                     " run:\n\t'git http-push -D %s %s'",
-                                    remote_ref->name, remote->url, pattern);
+                                    remote_ref->name, repo->url, pattern);
                }
        }
 
@@ -2158,8 +2152,8 @@ static int delete_remote_branch(char *pattern, int force)
        fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name);
        if (dry_run)
                return 0;
-       url = xmalloc(strlen(remote->url) + strlen(remote_ref->name) + 1);
-       sprintf(url, "%s%s", remote->url, remote_ref->name);
+       url = xmalloc(strlen(repo->url) + strlen(remote_ref->name) + 1);
+       sprintf(url, "%s%s", repo->url, remote_ref->name);
        slot = get_active_slot();
        slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
@@ -2195,14 +2189,15 @@ int main(int argc, char **argv)
        int rc = 0;
        int i;
        int new_refs;
-       struct ref *ref;
+       struct ref *ref, *local_refs;
+       struct remote *remote;
        char *rewritten_url = NULL;
 
        git_extract_argv0_path(argv[0]);
 
        setup_git_directory();
 
-       remote = xcalloc(sizeof(*remote), 1);
+       repo = xcalloc(sizeof(*repo), 1);
 
        argv++;
        for (i = 1; i < argc; i++, argv++) {
@@ -2235,14 +2230,14 @@ int main(int argc, char **argv)
                                continue;
                        }
                }
-               if (!remote->url) {
+               if (!repo->url) {
                        char *path = strstr(arg, "//");
-                       remote->url = arg;
-                       remote->path_len = strlen(arg);
+                       repo->url = arg;
+                       repo->path_len = strlen(arg);
                        if (path) {
-                               remote->path = strchr(path+2, '/');
-                               if (remote->path)
-                                       remote->path_len = strlen(remote->path);
+                               repo->path = strchr(path+2, '/');
+                               if (repo->path)
+                                       repo->path_len = strlen(repo->path);
                        }
                        continue;
                }
@@ -2255,7 +2250,7 @@ int main(int argc, char **argv)
        die("git-push is not available for http/https repository when not compiled with USE_CURL_MULTI");
 #endif
 
-       if (!remote->url)
+       if (!repo->url)
                usage(http_push_usage);
 
        if (delete_branch && nr_refspec != 1)
@@ -2263,17 +2258,24 @@ int main(int argc, char **argv)
 
        memset(remote_dir_exists, -1, 256);
 
-       http_init(NULL);
+       /*
+        * Create a minimum remote by hand to give to http_init(),
+        * primarily to allow it to look at the URL.
+        */
+       remote = xcalloc(sizeof(*remote), 1);
+       ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
+       remote->url[remote->url_nr++] = repo->url;
+       http_init(remote);
 
        no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
 
-       if (remote->url && remote->url[strlen(remote->url)-1] != '/') {
-               rewritten_url = xmalloc(strlen(remote->url)+2);
-               strcpy(rewritten_url, remote->url);
+       if (repo->url && repo->url[strlen(repo->url)-1] != '/') {
+               rewritten_url = xmalloc(strlen(repo->url)+2);
+               strcpy(rewritten_url, repo->url);
                strcat(rewritten_url, "/");
-               remote->path = rewritten_url + (remote->path - remote->url);
-               remote->path_len++;
-               remote->url = rewritten_url;
+               repo->path = rewritten_url + (repo->path - repo->url);
+               repo->path_len++;
+               repo->url = rewritten_url;
        }
 
        /* Verify DAV compliance/lock support */
@@ -2285,24 +2287,24 @@ int main(int argc, char **argv)
        sigchain_push_common(remove_locks_on_signal);
 
        /* Check whether the remote has server info files */
-       remote->can_update_info_refs = 0;
-       remote->has_info_refs = remote_exists("info/refs");
-       remote->has_info_packs = remote_exists("objects/info/packs");
-       if (remote->has_info_refs) {
+       repo->can_update_info_refs = 0;
+       repo->has_info_refs = remote_exists("info/refs");
+       repo->has_info_packs = remote_exists("objects/info/packs");
+       if (repo->has_info_refs) {
                info_ref_lock = lock_remote("info/refs", LOCK_TIME);
                if (info_ref_lock)
-                       remote->can_update_info_refs = 1;
+                       repo->can_update_info_refs = 1;
                else {
-                       fprintf(stderr, "Error: cannot lock existing info/refs\n");
+                       error("cannot lock existing info/refs");
                        rc = 1;
                        goto cleanup;
                }
        }
-       if (remote->has_info_packs)
+       if (repo->has_info_packs)
                fetch_indices();
 
        /* Get a list of all local and remote heads to validate refspecs */
-       get_local_heads();
+       local_refs = get_local_heads();
        fprintf(stderr, "Fetching remote heads...\n");
        get_dav_remote_heads();
 
@@ -2456,8 +2458,8 @@ int main(int argc, char **argv)
        }
 
        /* Update remote server info if appropriate */
-       if (remote->has_info_refs && new_refs) {
-               if (info_ref_lock && remote->can_update_info_refs) {
+       if (repo->has_info_refs && new_refs) {
+               if (info_ref_lock && repo->can_update_info_refs) {
                        fprintf(stderr, "Updating remote server info\n");
                        if (!dry_run)
                                update_remote_info_refs(info_ref_lock);
@@ -2470,7 +2472,7 @@ int main(int argc, char **argv)
        free(rewritten_url);
        if (info_ref_lock)
                unlock_remote(info_ref_lock);
-       free(remote);
+       free(repo);
 
        curl_slist_free_all(no_pragma_header);
 
index c5a3ea3b31045be9407579a9d1aba222ee3e9914..9377851925ae61d60ff6235e8bd8f1c4eeb092b8 100644 (file)
@@ -111,9 +111,9 @@ static void start_object_request(struct walker *walker,
        struct walker_data *data = walker->data;
 
        snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
-       unlink(prevfile);
+       unlink_or_warn(prevfile);
        rename(obj_req->tmpfile, prevfile);
-       unlink(obj_req->tmpfile);
+       unlink_or_warn(obj_req->tmpfile);
 
        if (obj_req->local != -1)
                error("fd leakage in start: %d", obj_req->local);
@@ -177,7 +177,7 @@ static void start_object_request(struct walker *walker,
                } while (prev_read > 0);
                close(prevlocal);
        }
-       unlink(prevfile);
+       unlink_or_warn(prevfile);
 
        /* Reset inflate/SHA1 if there was an error reading the previous temp
           file; also rewind to the beginning of the local file. */
@@ -238,18 +238,18 @@ static void finish_object_request(struct object_request *obj_req)
        } else if (obj_req->curl_result != CURLE_OK) {
                if (stat(obj_req->tmpfile, &st) == 0)
                        if (st.st_size == 0)
-                               unlink(obj_req->tmpfile);
+                               unlink_or_warn(obj_req->tmpfile);
                return;
        }
 
        git_inflate_end(&obj_req->stream);
        git_SHA1_Final(obj_req->real_sha1, &obj_req->c);
        if (obj_req->zret != Z_STREAM_END) {
-               unlink(obj_req->tmpfile);
+               unlink_or_warn(obj_req->tmpfile);
                return;
        }
        if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
-               unlink(obj_req->tmpfile);
+               unlink_or_warn(obj_req->tmpfile);
                return;
        }
        obj_req->rename =
@@ -418,15 +418,18 @@ static int fetch_index(struct walker *walker, struct alt_base *repo, unsigned ch
                run_active_slot(slot);
                if (results.curl_result != CURLE_OK) {
                        fclose(indexfile);
+                       slot->local = NULL;
                        return error("Unable to get pack index %s\n%s", url,
                                     curl_errorstr);
                }
        } else {
                fclose(indexfile);
+               slot->local = NULL;
                return error("Unable to start request");
        }
 
        fclose(indexfile);
+       slot->local = NULL;
 
        return move_temp_to_file(tmpfile, filename);
 }
@@ -776,16 +779,19 @@ static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned cha
                run_active_slot(slot);
                if (results.curl_result != CURLE_OK) {
                        fclose(packfile);
+                       slot->local = NULL;
                        return error("Unable to get pack file %s\n%s", url,
                                     curl_errorstr);
                }
        } else {
                fclose(packfile);
+               slot->local = NULL;
                return error("Unable to start request");
        }
 
        target->pack_size = ftell(packfile);
        fclose(packfile);
+       slot->local = NULL;
 
        ret = move_temp_to_file(tmpfile, filename);
        if (ret)
@@ -809,7 +815,7 @@ static void abort_object_request(struct object_request *obj_req)
                close(obj_req->local);
                obj_req->local = -1;
        }
-       unlink(obj_req->tmpfile);
+       unlink_or_warn(obj_req->tmpfile);
        if (obj_req->slot) {
                release_active_slot(obj_req->slot);
                obj_req->slot = NULL;
diff --git a/http.c b/http.c
index ee58799ca8cd433218ca7d40946580732a9010e7..b5323a68b74fe16bfed55d1bf002b39296db0a60 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,7 +1,7 @@
 #include "http.h"
 
 int data_received;
-int active_requests = 0;
+int active_requests;
 
 #ifdef USE_CURL_MULTI
 static int max_requests = -1;
@@ -13,22 +13,23 @@ static CURL *curl_default;
 char curl_errorstr[CURL_ERROR_SIZE];
 
 static int curl_ssl_verify = -1;
-static const char *ssl_cert = NULL;
-#if LIBCURL_VERSION_NUM >= 0x070902
-static const char *ssl_key = NULL;
+static const char *ssl_cert;
+#if LIBCURL_VERSION_NUM >= 0x070903
+static const char *ssl_key;
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
-static const char *ssl_capath = NULL;
+static const char *ssl_capath;
 #endif
-static const char *ssl_cainfo = NULL;
+static const char *ssl_cainfo;
 static long curl_low_speed_limit = -1;
 static long curl_low_speed_time = -1;
-static int curl_ftp_no_epsv = 0;
-static const char *curl_http_proxy = NULL;
+static int curl_ftp_no_epsv;
+static const char *curl_http_proxy;
+static char *user_name, *user_pass;
 
 static struct curl_slist *pragma_header;
 
-static struct active_request_slot *active_queue_head = NULL;
+static struct active_request_slot *active_queue_head;
 
 size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
 {
@@ -43,6 +44,25 @@ size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
        return size;
 }
 
+#ifndef NO_CURL_IOCTL
+curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
+{
+       struct buffer *buffer = clientp;
+
+       switch (cmd) {
+       case CURLIOCMD_NOP:
+               return CURLIOE_OK;
+
+       case CURLIOCMD_RESTARTREAD:
+               buffer->posn = 0;
+               return CURLIOE_OK;
+
+       default:
+               return CURLIOE_UNKNOWNCMD;
+       }
+}
+#endif
+
 size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
 {
        size_t size = eltsize * nmemb;
@@ -94,53 +114,33 @@ static void process_curl_messages(void)
 static int http_options(const char *var, const char *value, void *cb)
 {
        if (!strcmp("http.sslverify", var)) {
-               if (curl_ssl_verify == -1) {
-                       curl_ssl_verify = git_config_bool(var, value);
-               }
-               return 0;
-       }
-
-       if (!strcmp("http.sslcert", var)) {
-               if (ssl_cert == NULL)
-                       return git_config_string(&ssl_cert, var, value);
-               return 0;
-       }
-#if LIBCURL_VERSION_NUM >= 0x070902
-       if (!strcmp("http.sslkey", var)) {
-               if (ssl_key == NULL)
-                       return git_config_string(&ssl_key, var, value);
+               curl_ssl_verify = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp("http.sslcert", var))
+               return git_config_string(&ssl_cert, var, value);
+#if LIBCURL_VERSION_NUM >= 0x070903
+       if (!strcmp("http.sslkey", var))
+               return git_config_string(&ssl_key, var, value);
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
-       if (!strcmp("http.sslcapath", var)) {
-               if (ssl_capath == NULL)
-                       return git_config_string(&ssl_capath, var, value);
-               return 0;
-       }
+       if (!strcmp("http.sslcapath", var))
+               return git_config_string(&ssl_capath, var, value);
 #endif
-       if (!strcmp("http.sslcainfo", var)) {
-               if (ssl_cainfo == NULL)
-                       return git_config_string(&ssl_cainfo, var, value);
-               return 0;
-       }
-
+       if (!strcmp("http.sslcainfo", var))
+               return git_config_string(&ssl_cainfo, var, value);
 #ifdef USE_CURL_MULTI
        if (!strcmp("http.maxrequests", var)) {
-               if (max_requests == -1)
-                       max_requests = git_config_int(var, value);
+               max_requests = git_config_int(var, value);
                return 0;
        }
 #endif
-
        if (!strcmp("http.lowspeedlimit", var)) {
-               if (curl_low_speed_limit == -1)
-                       curl_low_speed_limit = (long)git_config_int(var, value);
+               curl_low_speed_limit = (long)git_config_int(var, value);
                return 0;
        }
        if (!strcmp("http.lowspeedtime", var)) {
-               if (curl_low_speed_time == -1)
-                       curl_low_speed_time = (long)git_config_int(var, value);
+               curl_low_speed_time = (long)git_config_int(var, value);
                return 0;
        }
 
@@ -148,19 +148,28 @@ static int http_options(const char *var, const char *value, void *cb)
                curl_ftp_no_epsv = git_config_bool(var, value);
                return 0;
        }
-       if (!strcmp("http.proxy", var)) {
-               if (curl_http_proxy == NULL)
-                       return git_config_string(&curl_http_proxy, var, value);
-               return 0;
-       }
+       if (!strcmp("http.proxy", var))
+               return git_config_string(&curl_http_proxy, var, value);
 
        /* Fall back on the default ones */
        return git_default_config(var, value, cb);
 }
 
-static CURL* get_curl_handle(void)
+static void init_curl_http_auth(CURL *result)
+{
+       if (user_name) {
+               struct strbuf up = STRBUF_INIT;
+               if (!user_pass)
+                       user_pass = xstrdup(getpass("Password: "));
+               strbuf_addf(&up, "%s:%s", user_name, user_pass);
+               curl_easy_setopt(result, CURLOPT_USERPWD,
+                                strbuf_detach(&up, NULL));
+       }
+}
+
+static CURL *get_curl_handle(void)
 {
-       CURLresult = curl_easy_init();
+       CURL *result = curl_easy_init();
 
        if (!curl_ssl_verify) {
                curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
@@ -176,9 +185,11 @@ static CURL* get_curl_handle(void)
        curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
 #endif
 
+       init_curl_http_auth(result);
+
        if (ssl_cert != NULL)
                curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
-#if LIBCURL_VERSION_NUM >= 0x070902
+#if LIBCURL_VERSION_NUM >= 0x070903
        if (ssl_key != NULL)
                curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
 #endif
@@ -213,11 +224,60 @@ static CURL* get_curl_handle(void)
        return result;
 }
 
+static void http_auth_init(const char *url)
+{
+       char *at, *colon, *cp, *slash;
+       int len;
+
+       cp = strstr(url, "://");
+       if (!cp)
+               return;
+
+       /*
+        * Ok, the URL looks like "proto://something".  Which one?
+        * "proto://<user>:<pass>@<host>/...",
+        * "proto://<user>@<host>/...", or just
+        * "proto://<host>/..."?
+        */
+       cp += 3;
+       at = strchr(cp, '@');
+       colon = strchr(cp, ':');
+       slash = strchrnul(cp, '/');
+       if (!at || slash <= at)
+               return; /* No credentials */
+       if (!colon || at <= colon) {
+               /* Only username */
+               len = at - cp;
+               user_name = xmalloc(len + 1);
+               memcpy(user_name, cp, len);
+               user_name[len] = '\0';
+               user_pass = NULL;
+       } else {
+               len = colon - cp;
+               user_name = xmalloc(len + 1);
+               memcpy(user_name, cp, len);
+               user_name[len] = '\0';
+               len = at - (colon + 1);
+               user_pass = xmalloc(len + 1);
+               memcpy(user_pass, colon + 1, len);
+               user_pass[len] = '\0';
+       }
+}
+
+static void set_from_env(const char **var, const char *envname)
+{
+       const char *val = getenv(envname);
+       if (val)
+               *var = val;
+}
+
 void http_init(struct remote *remote)
 {
        char *low_speed_limit;
        char *low_speed_time;
 
+       git_config(http_options, NULL);
+
        curl_global_init(CURL_GLOBAL_ALL);
 
        if (remote && remote->http_proxy)
@@ -242,14 +302,14 @@ void http_init(struct remote *remote)
        if (getenv("GIT_SSL_NO_VERIFY"))
                curl_ssl_verify = 0;
 
-       ssl_cert = getenv("GIT_SSL_CERT");
-#if LIBCURL_VERSION_NUM >= 0x070902
-       ssl_key = getenv("GIT_SSL_KEY");
+       set_from_env(&ssl_cert, "GIT_SSL_CERT");
+#if LIBCURL_VERSION_NUM >= 0x070903
+       set_from_env(&ssl_key, "GIT_SSL_KEY");
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
-       ssl_capath = getenv("GIT_SSL_CAPATH");
+       set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
 #endif
-       ssl_cainfo = getenv("GIT_SSL_CAINFO");
+       set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
 
        low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
        if (low_speed_limit != NULL)
@@ -258,8 +318,6 @@ void http_init(struct remote *remote)
        if (low_speed_time != NULL)
                curl_low_speed_time = strtol(low_speed_time, NULL, 10);
 
-       git_config(http_options, NULL);
-
        if (curl_ssl_verify == -1)
                curl_ssl_verify = 1;
 
@@ -271,6 +329,9 @@ void http_init(struct remote *remote)
        if (getenv("GIT_CURL_FTP_NO_EPSV"))
                curl_ftp_no_epsv = 1;
 
+       if (remote && remote->url && remote->url[0])
+               http_auth_init(remote->url[0]);
+
 #ifndef NO_CURL_EASY_DUPHANDLE
        curl_default = get_curl_handle();
 #endif
@@ -322,15 +383,14 @@ struct active_request_slot *get_active_slot(void)
        /* Wait for a slot to open up if the queue is full */
        while (active_requests >= max_requests) {
                curl_multi_perform(curlm, &num_transfers);
-               if (num_transfers < active_requests) {
+               if (num_transfers < active_requests)
                        process_curl_messages();
-               }
        }
 #endif
 
-       while (slot != NULL && slot->in_use) {
+       while (slot != NULL && slot->in_use)
                slot = slot->next;
-       }
+
        if (slot == NULL) {
                newslot = xmalloc(sizeof(*newslot));
                newslot->curl = NULL;
@@ -341,9 +401,8 @@ struct active_request_slot *get_active_slot(void)
                if (slot == NULL) {
                        active_queue_head = newslot;
                } else {
-                       while (slot->next != NULL) {
+                       while (slot->next != NULL)
                                slot = slot->next;
-                       }
                        slot->next = newslot;
                }
                slot = newslot;
@@ -404,7 +463,7 @@ struct fill_chain {
        struct fill_chain *next;
 };
 
-static struct fill_chain *fill_cfg = NULL;
+static struct fill_chain *fill_cfg;
 
 void add_fill_function(void *data, int (*fill)(void *))
 {
@@ -535,9 +594,8 @@ static void finish_active_slot(struct active_request_slot *slot)
        }
 
        /* Run callback if appropriate */
-       if (slot->callback_func != NULL) {
+       if (slot->callback_func != NULL)
                slot->callback_func(slot->callback_data);
-       }
 }
 
 void finish_all_active_slots(void)
@@ -567,37 +625,29 @@ static inline int needs_quote(int ch)
 
 static inline int hex(int v)
 {
-       if (v < 10) return '0' + v;
-       else return 'A' + v - 10;
+       if (v < 10)
+               return '0' + v;
+       else
+               return 'A' + v - 10;
 }
 
 static char *quote_ref_url(const char *base, const char *ref)
 {
+       struct strbuf buf = STRBUF_INIT;
        const char *cp;
-       char *dp, *qref;
-       int len, baselen, ch;
+       int ch;
 
-       baselen = strlen(base);
-       len = baselen + 2; /* '/' after base and terminating NUL */
-       for (cp = ref; (ch = *cp) != 0; cp++, len++)
+       strbuf_addstr(&buf, base);
+       if (buf.len && buf.buf[buf.len - 1] != '/' && *ref != '/')
+               strbuf_addstr(&buf, "/");
+
+       for (cp = ref; (ch = *cp) != 0; cp++)
                if (needs_quote(ch))
-                       len += 2; /* extra two hex plus replacement % */
-       qref = xmalloc(len);
-       memcpy(qref, base, baselen);
-       dp = qref + baselen;
-       *(dp++) = '/';
-       for (cp = ref; (ch = *cp) != 0; cp++) {
-               if (needs_quote(ch)) {
-                       *dp++ = '%';
-                       *dp++ = hex((ch >> 4) & 0xF);
-                       *dp++ = hex(ch & 0xF);
-               }
+                       strbuf_addf(&buf, "%%%02x", ch);
                else
-                       *dp++ = ch;
-       }
-       *dp = 0;
+                       strbuf_addch(&buf, *cp);
 
-       return qref;
+       return strbuf_detach(&buf, NULL);
 }
 
 int http_fetch_ref(const char *base, struct ref *ref)
diff --git a/http.h b/http.h
index 905b4629a47789705c13745fd56ce0c91adea41b..26abebed1f35db971e423f79c433bed8c5338789 100644 (file)
--- a/http.h
+++ b/http.h
 #define CURLE_HTTP_RETURNED_ERROR CURLE_HTTP_NOT_FOUND
 #endif
 
+#if LIBCURL_VERSION_NUM < 0x070c03
+#define NO_CURL_IOCTL
+#endif
+
 struct slot_results
 {
        CURLcode curl_result;
@@ -67,6 +71,9 @@ struct buffer
 extern size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
 extern size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
 extern size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+#ifndef NO_CURL_IOCTL
+extern curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
+#endif
 
 /* Slot lifecycle functions */
 extern struct active_request_slot *get_active_slot(void);
index f91293c23f2bcb6d673e0316cd3736fdddd0fbe4..8154cb2116da9257ecf286c46f77fc1ed2a62afc 100644 (file)
@@ -135,6 +135,7 @@ struct imap_server_conf {
        char *pass;
        int use_ssl;
        int ssl_verify;
+       int use_html;
 };
 
 struct imap_store_conf {
@@ -578,7 +579,7 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
                        n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen);
                        free(cmd->cb.data);
                        if (n != cmd->cb.dlen ||
-                           (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2) {
+                           socket_write(&imap->buf.sock, "\r\n", 2) != 2) {
                                free(cmd->cmd);
                                free(cmd);
                                return NULL;
@@ -1263,6 +1264,53 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid)
        return DRV_OK;
 }
 
+static void encode_html_chars(struct strbuf *p)
+{
+       int i;
+       for (i = 0; i < p->len; i++) {
+               if (p->buf[i] == '&')
+                       strbuf_splice(p, i, 1, "&amp;", 5);
+               if (p->buf[i] == '<')
+                       strbuf_splice(p, i, 1, "&lt;", 4);
+               if (p->buf[i] == '>')
+                       strbuf_splice(p, i, 1, "&gt;", 4);
+               if (p->buf[i] == '"')
+                       strbuf_splice(p, i, 1, "&quot;", 6);
+       }
+}
+static void wrap_in_html(struct msg_data *msg)
+{
+       struct strbuf buf = STRBUF_INIT;
+       struct strbuf **lines;
+       struct strbuf **p;
+       static char *content_type = "Content-Type: text/html;\n";
+       static char *pre_open = "<pre>\n";
+       static char *pre_close = "</pre>\n";
+       int added_header = 0;
+
+       strbuf_attach(&buf, msg->data, msg->len, msg->len);
+       lines = strbuf_split(&buf, '\n');
+       strbuf_release(&buf);
+       for (p = lines; *p; p++) {
+               if (! added_header) {
+                       if ((*p)->len == 1 && *((*p)->buf) == '\n') {
+                               strbuf_addstr(&buf, content_type);
+                               strbuf_addbuf(&buf, *p);
+                               strbuf_addstr(&buf, pre_open);
+                               added_header = 1;
+                               continue;
+                       }
+               }
+               else
+                       encode_html_chars(*p);
+               strbuf_addbuf(&buf, *p);
+       }
+       strbuf_addstr(&buf, pre_close);
+       strbuf_list_free(lines);
+       msg->len  = buf.len;
+       msg->data = strbuf_detach(&buf, NULL);
+}
+
 #define CHUNKSIZE 0x1000
 
 static int read_message(FILE *f, struct msg_data *msg)
@@ -1339,6 +1387,7 @@ static struct imap_server_conf server = {
        NULL,   /* pass */
        0,      /* use_ssl */
        1,      /* ssl_verify */
+       0,      /* use_html */
 };
 
 static char *imap_folder;
@@ -1377,6 +1426,8 @@ static int git_imap_config(const char *key, const char *val, void *cb)
                server.tunnel = xstrdup(val);
        else if (!strcmp("sslverify", key))
                server.ssl_verify = git_config_bool(key, val);
+       else if (!strcmp("preformattedHTML", key))
+               server.use_html = git_config_bool(key, val);
        return 0;
 }
 
@@ -1439,6 +1490,8 @@ int main(int argc, char **argv)
                fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
                if (!split_msg(&all_msgs, &msg, &ofs))
                        break;
+               if (server.use_html)
+                       wrap_in_html(&msg);
                r = imap_store_msg(ctx, &msg, &uid);
                if (r != DRV_OK)
                        break;
index 5dfe03ee6cbbf531a5db053fc8f8fd41b0a59aab..0c92bafcbb80cc539de9a86305562ff291361db9 100644 (file)
@@ -232,7 +232,7 @@ static void free_base_data(struct base_data *c)
 
 static void prune_base_data(struct base_data *retain)
 {
-       struct base_data *b = base_cache;
+       struct base_data *b;
        for (b = base_cache;
             base_cache_used > delta_base_cache_limit && b;
             b = b->child) {
@@ -293,8 +293,8 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size)
 
 static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
 {
-       unsigned char *p, c;
-       unsigned long size;
+       unsigned char *p;
+       unsigned long size, c;
        off_t base_offset;
        unsigned shift;
        void *data;
@@ -312,7 +312,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
                p = fill(1);
                c = *p;
                use(1);
-               size += (c & 0x7fUL) << shift;
+               size += (c & 0x7f) << shift;
                shift += 7;
        }
        obj->size = size;
index a32f4cdc458f823f7e8754742d2f8e21b2aabff3..fc281597fd2fd5ad2299eab0848da99355e383fc 100644 (file)
@@ -27,7 +27,7 @@
  *
  * It does so by calculating the costs of the path ending in characters
  * i (in string1) and j (in string2), respectively, given that the last
- * operation is a substition, a swap, a deletion, or an insertion.
+ * operation is a substitution, a swap, a deletion, or an insertion.
  *
  * This implementation allows the costs to be weighted:
  *
index 30ded3d4dd9d41432eba317ccc8765746b7d5c95..8953548c07bb36f20798c7ca344d07960c22618c 100644 (file)
@@ -141,14 +141,15 @@ static void add_pending_tree(struct rev_info *revs, struct tree *tree)
 
 void traverse_commit_list(struct rev_info *revs,
                          show_commit_fn show_commit,
-                         show_object_fn show_object)
+                         show_object_fn show_object,
+                         void *data)
 {
        int i;
        struct commit *commit;
 
        while ((commit = get_revision(revs)) != NULL) {
                add_pending_tree(revs, commit->tree);
-               show_commit(commit);
+               show_commit(commit, data);
        }
        for (i = 0; i < revs->pending.nr; i++) {
                struct object_array_entry *pending = revs->pending.objects + i;
index 0b2de64301b59b5e27ca738242c92109332f9e07..d65dbf03e657facb29a2846144eda2fa3687bc2f 100644 (file)
@@ -1,11 +1,11 @@
 #ifndef LIST_OBJECTS_H
 #define LIST_OBJECTS_H
 
-typedef void (*show_commit_fn)(struct commit *);
+typedef void (*show_commit_fn)(struct commit *, void *);
 typedef void (*show_object_fn)(struct object *, const struct name_path *, const char *);
 typedef void (*show_edge_fn)(struct commit *);
 
-void traverse_commit_list(struct rev_info *revs, show_commit_fn, show_object_fn);
+void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
 
 void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
 
index 9723f3a5dc659893080ec1481d595e53f937e252..f7c2bc9278a906baa6dc22289acd920234072500 100644 (file)
@@ -219,7 +219,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
        close(fd);
  bad:
        for (i = 0; i < 3; i++)
-               unlink(temp[i]);
+               unlink_or_warn(temp[i]);
        strbuf_release(&cmd);
        return status;
 }
index 3dbb2d1ff9407a7417be294adedc5312e7421a96..eb931eded5a6ed20f1d80dadf08cbb8009d85767 100644 (file)
@@ -16,7 +16,7 @@ static void remove_lock_file(void)
                    lock_file_list->filename[0]) {
                        if (lock_file_list->fd >= 0)
                                close(lock_file_list->fd);
-                       unlink(lock_file_list->filename);
+                       unlink_or_warn(lock_file_list->filename);
                }
                lock_file_list = lock_file_list->next;
        }
@@ -109,7 +109,7 @@ static char *resolve_symlink(char *p, size_t s)
                         * link is a relative path, so I must replace the
                         * last element of p with it.
                         */
-                       char *r = (char*)last_path_elm(p);
+                       char *r = (char *)last_path_elm(p);
                        if (r - p + link_len < s)
                                strcpy(r, link);
                        else {
@@ -259,7 +259,7 @@ void rollback_lock_file(struct lock_file *lk)
        if (lk->filename[0]) {
                if (lk->fd >= 0)
                        close(lk->fd);
-               unlink(lk->filename);
+               unlink_or_warn(lk->filename);
        }
        lk->filename[0] = 0;
 }
index 84a74e544b7bcc20c887f321e389ecf3cfb560d6..5bd29e6994c92268ec576671bb8564b57d1a5c9d 100644 (file)
@@ -6,6 +6,7 @@
 #include "log-tree.h"
 #include "reflog-walk.h"
 #include "refs.h"
+#include "string-list.h"
 
 struct decoration name_decoration = { "object names" };
 
@@ -79,18 +80,18 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
  */
 static int detect_any_signoff(char *letter, int size)
 {
-       char ch, *cp;
+       char *cp;
        int seen_colon = 0;
        int seen_at = 0;
        int seen_name = 0;
        int seen_head = 0;
 
        cp = letter + size;
-       while (letter <= --cp && (ch = *cp) == '\n')
+       while (letter <= --cp && *cp == '\n')
                continue;
 
        while (letter <= cp) {
-               ch = *cp--;
+               char ch = *cp--;
                if (ch == '\n')
                        break;
 
@@ -178,13 +179,31 @@ static int has_non_ascii(const char *s)
        return 0;
 }
 
-void log_write_email_headers(struct rev_info *opt, const char *name,
+void get_patch_filename(struct commit *commit, int nr, const char *suffix,
+                       struct strbuf *buf)
+{
+       int suffix_len = strlen(suffix) + 1;
+       int start_len = buf->len;
+
+       strbuf_addf(buf, commit ? "%04d-" : "%d", nr);
+       if (commit) {
+               int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len;
+
+               format_commit_message(commit, "%f", buf, DATE_NORMAL);
+               if (max_len < buf->len)
+                       strbuf_setlen(buf, max_len);
+               strbuf_addstr(buf, suffix);
+       }
+}
+
+void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                             const char **subject_p,
                             const char **extra_headers_p,
                             int *need_8bit_cte_p)
 {
        const char *subject = NULL;
        const char *extra_headers = opt->extra_headers;
+       const char *name = sha1_to_hex(commit->object.sha1);
 
        *need_8bit_cte_p = 0; /* unknown */
        if (opt->total > 0) {
@@ -211,14 +230,19 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
                printf("Message-Id: <%s>\n", opt->message_id);
                graph_show_oneline(opt->graph);
        }
-       if (opt->ref_message_id) {
-               printf("In-Reply-To: <%s>\nReferences: <%s>\n",
-                      opt->ref_message_id, opt->ref_message_id);
+       if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {
+               int i, n;
+               n = opt->ref_message_ids->nr;
+               printf("In-Reply-To: <%s>\n", opt->ref_message_ids->items[n-1].string);
+               for (i = 0; i < n; i++)
+                       printf("%s<%s>\n", (i > 0 ? "\t" : "References: "),
+                              opt->ref_message_ids->items[i].string);
                graph_show_oneline(opt->graph);
        }
        if (opt->mime_boundary) {
                static char subject_buffer[1024];
                static char buffer[1024];
+               struct strbuf filename =  STRBUF_INIT;
                *need_8bit_cte_p = -1; /* NEVER */
                snprintf(subject_buffer, sizeof(subject_buffer) - 1,
                         "%s"
@@ -237,18 +261,21 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
                         mime_boundary_leader, opt->mime_boundary);
                extra_headers = subject_buffer;
 
+               get_patch_filename(opt->numbered_files ? NULL : commit, opt->nr,
+                                   opt->patch_suffix, &filename);
                snprintf(buffer, sizeof(buffer) - 1,
                         "\n--%s%s\n"
                         "Content-Type: text/x-patch;"
-                        " name=\"%s.diff\"\n"
+                        " name=\"%s\"\n"
                         "Content-Transfer-Encoding: 8bit\n"
                         "Content-Disposition: %s;"
-                        " filename=\"%s.diff\"\n\n",
+                        " filename=\"%s\"\n\n",
                         mime_boundary_leader, opt->mime_boundary,
-                        name,
+                        filename.buf,
                         opt->no_inline ? "attachment" : "inline",
-                        name);
+                        filename.buf);
                opt->diffopt.stat_sep = buffer;
+               strbuf_release(&filename);
        }
        *subject_p = subject;
        *extra_headers_p = extra_headers;
@@ -328,8 +355,7 @@ void show_log(struct rev_info *opt)
         */
 
        if (opt->commit_format == CMIT_FMT_EMAIL) {
-               log_write_email_headers(opt, sha1_to_hex(commit->object.sha1),
-                                       &subject, &extra_headers,
+               log_write_email_headers(opt, commit, &subject, &extra_headers,
                                        &need_8bit_cte);
        } else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
                fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
index f2a90084ae1874632318c7880e95426eca2682ea..20b5caf1aa45aa0ba076ec60320d252c42abfb64 100644 (file)
@@ -13,10 +13,14 @@ int log_tree_commit(struct rev_info *, struct commit *);
 int log_tree_opt_parse(struct rev_info *, const char **, int);
 void show_log(struct rev_info *opt);
 void show_decorations(struct rev_info *opt, struct commit *commit);
-void log_write_email_headers(struct rev_info *opt, const char *name,
+void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                             const char **subject_p,
                             const char **extra_headers_p,
                             int *need_8bit_cte_p);
 void load_ref_decorations(void);
 
+#define FORMAT_PATCH_NAME_MAX 64
+void get_patch_filename(struct commit *commit, int nr, const char *suffix,
+                       struct strbuf *buf);
+
 #endif
index 6be91b60dfc8c37bd21c45d1480f7b3cf1fe5a99..bb1f2fb711a588d2af0d61decbd4b3eb2f2aebbe 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -99,7 +99,8 @@ static void add_mapping(struct string_list *map,
                 old_name, old_email, new_name, new_email);
 }
 
-static char *parse_name_and_email(char *buffer, char **name, char **email)
+static char *parse_name_and_email(char *buffer, char **name,
+               char **email, int allow_empty_email)
 {
        char *left, *right, *nstart, *nend;
        *name = *email = 0;
@@ -108,7 +109,7 @@ static char *parse_name_and_email(char *buffer, char **name, char **email)
                return NULL;
        if ((right = strchr(left+1, '>')) == NULL)
                return NULL;
-       if (left+1 == right)
+       if (!allow_empty_email && (left+1 == right))
                return NULL;
 
        /* remove whitespace from beginning and end of name */
@@ -159,8 +160,8 @@ static int read_single_mailmap(struct string_list *map, const char *filename, ch
                        }
                        continue;
                }
-               if ((name2 = parse_name_and_email(buffer, &name1, &email1)) != NULL)
-                       parse_name_and_email(name2, &name2, &email2);
+               if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
+                       parse_name_and_email(name2, &name2, &email2, 1);
 
                if (email1)
                        add_mapping(map, name1, email1, name2, email2);
index 9bf5cc71754df1df29057822cf16bbfe51579d37..f5df9b961b2999e4824b900d61d28c254b2ef858 100644 (file)
@@ -520,8 +520,12 @@ static void update_file_flags(struct merge_options *o,
                unsigned long size;
 
                if (S_ISGITLINK(mode))
-                       die("cannot read object %s '%s': It is a submodule!",
-                           sha1_to_hex(sha), path);
+                       /*
+                        * 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.
+                        */
+                       goto update_index;
 
                buf = read_sha1_file(sha, &type, &size);
                if (!buf)
@@ -801,22 +805,19 @@ static int process_renames(struct merge_options *o,
        }
 
        for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
-               int compare;
                char *src;
-               struct string_list *renames1, *renames2, *renames2Dst;
+               struct string_list *renames1, *renames2Dst;
                struct rename *ren1 = NULL, *ren2 = NULL;
                const char *branch1, *branch2;
                const char *ren1_src, *ren1_dst;
 
                if (i >= a_renames->nr) {
-                       compare = 1;
                        ren2 = b_renames->items[j++].util;
                } else if (j >= b_renames->nr) {
-                       compare = -1;
                        ren1 = a_renames->items[i++].util;
                } else {
-                       compare = strcmp(a_renames->items[i].string,
-                                       b_renames->items[j].string);
+                       int compare = strcmp(a_renames->items[i].string,
+                                            b_renames->items[j].string);
                        if (compare <= 0)
                                ren1 = a_renames->items[i++].util;
                        if (compare >= 0)
@@ -826,14 +827,12 @@ static int process_renames(struct merge_options *o,
                /* TODO: refactor, so that 1/2 are not needed */
                if (ren1) {
                        renames1 = a_renames;
-                       renames2 = b_renames;
                        renames2Dst = &b_by_dst;
                        branch1 = o->branch1;
                        branch2 = o->branch2;
                } else {
                        struct rename *tmp;
                        renames1 = b_renames;
-                       renames2 = a_renames;
                        renames2Dst = &a_by_dst;
                        branch1 = o->branch2;
                        branch2 = o->branch1;
@@ -934,11 +933,12 @@ static int process_renames(struct merge_options *o,
                                       ren1_src, ren1_dst, branch1,
                                       branch2);
                                update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
-                               update_stages(ren1_dst, NULL,
-                                               branch1 == o->branch1 ?
-                                               ren1->pair->two : NULL,
-                                               branch1 == o->branch1 ?
-                                               NULL : ren1->pair->two, 1);
+                               if (!o->call_depth)
+                                       update_stages(ren1_dst, NULL,
+                                                       branch1 == o->branch1 ?
+                                                       ren1->pair->two : NULL,
+                                                       branch1 == o->branch1 ?
+                                                       NULL : ren1->pair->two, 1);
                        } else if (!sha_eq(dst_other.sha1, null_sha1)) {
                                const char *new_path;
                                clean_merge = 0;
index 7e6a92c88e7b139ec03e0ff26e97e1559a06a220..e1feef9c3329e0370e7caff612b4f6c8684cbaef 100644 (file)
--- a/object.c
+++ b/object.c
@@ -45,7 +45,8 @@ int type_from_string(const char *str)
 
 static unsigned int hash_obj(struct object *obj, unsigned int n)
 {
-       unsigned int hash = *(unsigned int *)obj->sha1;
+       unsigned int hash;
+       memcpy(&hash, obj->sha1, sizeof(unsigned int));
        return hash % n;
 }
 
index 2c76fb181f64e10c65517224b0c09cb648db81ac..301fc60eae1ad53721851a272a0fbd0192881801 100644 (file)
@@ -66,7 +66,7 @@ static void prune_ref(struct ref_to_prune *r)
        struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
 
        if (lock) {
-               unlink(git_path("%s", r->name));
+               unlink_or_warn(git_path("%s", r->name));
                unlock_ref(lock);
        }
 }
index 4c5d09dd25aede8ea7e14886f5c17ed847a8f0d9..cf71bcffd2e1e9aeacb44df488b0825f09d54255 100644 (file)
@@ -244,6 +244,9 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
        ctx->out  = argv;
        ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
        ctx->flags = flags;
+       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");
 }
 
 static int usage_with_options_internal(const char * const *,
@@ -253,6 +256,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                       const struct option *options,
                       const char * const usagestr[])
 {
+       int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
+
        /* we must reset ->opt, unknown short option leave it dangling */
        ctx->opt = NULL;
 
@@ -268,18 +273,18 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 
                if (arg[1] != '-') {
                        ctx->opt = arg + 1;
-                       if (*ctx->opt == 'h')
+                       if (internal_help && *ctx->opt == 'h')
                                return parse_options_usage(usagestr, options);
                        switch (parse_short_opt(ctx, options)) {
                        case -1:
                                return parse_options_usage(usagestr, options);
                        case -2:
-                               return PARSE_OPT_UNKNOWN;
+                               goto unknown;
                        }
                        if (ctx->opt)
                                check_typos(arg + 1, options);
                        while (ctx->opt) {
-                               if (*ctx->opt == 'h')
+                               if (internal_help && *ctx->opt == 'h')
                                        return parse_options_usage(usagestr, options);
                                switch (parse_short_opt(ctx, options)) {
                                case -1:
@@ -292,7 +297,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                                         */
                                        ctx->argv[0] = xstrdup(ctx->opt - 1);
                                        *(char *)ctx->argv[0] = '-';
-                                       return PARSE_OPT_UNKNOWN;
+                                       goto unknown;
                                }
                        }
                        continue;
@@ -306,16 +311,22 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                        break;
                }
 
-               if (!strcmp(arg + 2, "help-all"))
+               if (internal_help && !strcmp(arg + 2, "help-all"))
                        return usage_with_options_internal(usagestr, options, 1);
-               if (!strcmp(arg + 2, "help"))
+               if (internal_help && !strcmp(arg + 2, "help"))
                        return parse_options_usage(usagestr, options);
                switch (parse_long_opt(ctx, arg + 2, options)) {
                case -1:
                        return parse_options_usage(usagestr, options);
                case -2:
-                       return PARSE_OPT_UNKNOWN;
+                       goto unknown;
                }
+               continue;
+unknown:
+               if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
+                       return PARSE_OPT_UNKNOWN;
+               ctx->out[ctx->cpidx++] = ctx->argv[0];
+               ctx->opt = NULL;
        }
        return PARSE_OPT_DONE;
 }
@@ -356,6 +367,9 @@ int parse_options(int argc, const char **argv, const struct option *options,
 int usage_with_options_internal(const char * const *usagestr,
                                const struct option *opts, int full)
 {
+       if (!usagestr)
+               return PARSE_OPT_HELP;
+
        fprintf(stderr, "usage: %s\n", *usagestr++);
        while (*usagestr && **usagestr)
                fprintf(stderr, "   or: %s\n", *usagestr++);
index 912290549bcbb03b7e82750a40297fb1a70b8206..b54eec128bcda1a8b4c4a2c33f2ce799c506ac23 100644 (file)
@@ -21,6 +21,8 @@ enum parse_opt_flags {
        PARSE_OPT_KEEP_DASHDASH = 1,
        PARSE_OPT_STOP_AT_NON_OPTION = 2,
        PARSE_OPT_KEEP_ARGV0 = 4,
+       PARSE_OPT_KEEP_UNKNOWN = 8,
+       PARSE_OPT_NO_INTERNAL_HELP = 16,
 };
 
 enum parse_opt_option_flags {
@@ -50,7 +52,7 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
  *
  * `argh`::
  *   token to explain the kind of argument this option wants. Keep it
- *   homogenous across the repository.
+ *   homogeneous across the repository.
  *
  * `help`::
  *   the short help associated to what the option does.
@@ -59,7 +61,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 optionnal (not for BOOLEANs)
+ *   PARSE_OPT_OPTARG: says that the argument is optional (not for BOOLEANs)
  *   PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs
  *   PARSE_OPT_NONEG: says that this option cannot be negated
  *   PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in
@@ -103,7 +105,7 @@ struct option {
        { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
 
 /* parse_options() will filter out the processed options and leave the
- * non-option argments in argv[].
+ * non-option arguments in argv[].
  * Returns the number of arguments left in argv[].
  */
 extern int parse_options(int argc, const char **argv,
@@ -113,7 +115,7 @@ extern int parse_options(int argc, const char **argv,
 extern NORETURN void usage_with_options(const char * const *usagestr,
                                         const struct option *options);
 
-/*----- incremantal advanced APIs -----*/
+/*----- incremental advanced APIs -----*/
 
 enum {
        PARSE_OPT_HELP = -1,
index ed9db81fa82c812c9ffa07f5a40540dbb15da0d3..ef748ce96d246bb17c6e64b51b882d535d7f7774 100644 (file)
@@ -44,7 +44,7 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
                        if (cmd & 0x01) cp_off = *data++;
                        if (cmd & 0x02) cp_off |= (*data++ << 8);
                        if (cmd & 0x04) cp_off |= (*data++ << 16);
-                       if (cmd & 0x08) cp_off |= (*data++ << 24);
+                       if (cmd & 0x08) cp_off |= ((unsigned) *data++ << 24);
                        if (cmd & 0x10) cp_size = *data++;
                        if (cmd & 0x20) cp_size |= (*data++ << 8);
                        if (cmd & 0x40) cp_size |= (*data++ << 16);
index 3be5d3165e0009761a0ca69e15e4a9132c6cfaff..5717257051aceff129a4d0777c0a11bc156cae54 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "diff.h"
 #include "commit.h"
+#include "sha1-lookup.h"
 #include "patch-ids.h"
 
 static int commit_patch_id(struct commit *commit, struct diff_options *options,
@@ -15,99 +16,15 @@ static int commit_patch_id(struct commit *commit, struct diff_options *options,
        return diff_flush_patch_id(options, sha1);
 }
 
-static uint32_t take2(const unsigned char *id)
+static const unsigned char *patch_id_access(size_t index, void *table)
 {
-       return ((id[0] << 8) | id[1]);
+       struct patch_id **id_table = table;
+       return id_table[index]->patch_id;
 }
 
-/*
- * Conventional binary search loop looks like this:
- *
- *      do {
- *              int mi = (lo + hi) / 2;
- *              int cmp = "entry pointed at by mi" minus "target";
- *              if (!cmp)
- *                      return (mi is the wanted one)
- *              if (cmp > 0)
- *                      hi = mi; "mi is larger than target"
- *              else
- *                      lo = mi+1; "mi is smaller than target"
- *      } while (lo < hi);
- *
- * The invariants are:
- *
- * - When entering the loop, lo points at a slot that is never
- *   above the target (it could be at the target), hi points at a
- *   slot that is guaranteed to be above the target (it can never
- *   be at the target).
- *
- * - We find a point 'mi' between lo and hi (mi could be the same
- *   as lo, but never can be the same as hi), and check if it hits
- *   the target.  There are three cases:
- *
- *    - if it is a hit, we are happy.
- *
- *    - if it is strictly higher than the target, we update hi with
- *      it.
- *
- *    - if it is strictly lower than the target, we update lo to be
- *      one slot after it, because we allow lo to be at the target.
- *
- * When choosing 'mi', we do not have to take the "middle" but
- * anywhere in between lo and hi, as long as lo <= mi < hi is
- * satisfied.  When we somehow know that the distance between the
- * target and lo is much shorter than the target and hi, we could
- * pick mi that is much closer to lo than the midway.
- */
 static int patch_pos(struct patch_id **table, int nr, const unsigned char *id)
 {
-       int hi = nr;
-       int lo = 0;
-       int mi = 0;
-
-       if (!nr)
-               return -1;
-
-       if (nr != 1) {
-               unsigned lov, hiv, miv, ofs;
-
-               for (ofs = 0; ofs < 18; ofs += 2) {
-                       lov = take2(table[0]->patch_id + ofs);
-                       hiv = take2(table[nr-1]->patch_id + ofs);
-                       miv = take2(id + ofs);
-                       if (miv < lov)
-                               return -1;
-                       if (hiv < miv)
-                               return -1 - nr;
-                       if (lov != hiv) {
-                               /*
-                                * At this point miv could be equal
-                                * to hiv (but id could still be higher);
-                                * the invariant of (mi < hi) should be
-                                * kept.
-                                */
-                               mi = (nr-1) * (miv - lov) / (hiv - lov);
-                               if (lo <= mi && mi < hi)
-                                       break;
-                               die("oops");
-                       }
-               }
-               if (18 <= ofs)
-                       die("cannot happen -- lo and hi are identical");
-       }
-
-       do {
-               int cmp;
-               cmp = hashcmp(table[mi]->patch_id, id);
-               if (!cmp)
-                       return mi;
-               if (cmp > 0)
-                       hi = mi;
-               else
-                       lo = mi + 1;
-               mi = (hi + lo) / 2;
-       } while (lo < hi);
-       return -lo-1;
+       return sha1_pos(id, table, nr, patch_id_access);
 }
 
 #define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */
index 7d7f2b1d367b505676032878615b2843eb64ed7b..291ff5b53c1883ee8fce67fbb7e2b32393ef0800 100644 (file)
@@ -56,7 +56,7 @@ require Exporter;
 @EXPORT_OK = qw(command command_oneline command_noisy
                 command_output_pipe command_input_pipe command_close_pipe
                 command_bidi_pipe command_close_bidi_pipe
-                version exec_path hash_object git_cmd_try
+                version exec_path html_path hash_object git_cmd_try
                 remote_refs
                 temp_acquire temp_release temp_reset temp_path);
 
@@ -492,6 +492,16 @@ C<git --exec-path>). Useful mostly only internally.
 sub exec_path { command_oneline('--exec-path') }
 
 
+=item html_path ()
+
+Return path to the Git html documentation (the same as
+C<git --html-path>). Useful mostly only internally.
+
+=cut
+
+sub html_path { command_oneline('--html-path') }
+
+
 =item repo_path ()
 
 Return path to the git repository. Must be called on a repository instance.
index e9540e46da7af16e3aa765d79a5b03a4847975df..a0ef356558f4cdb148010f1b47dbd3fcc363d8ba 100644 (file)
--- a/pretty.c
+++ b/pretty.c
 
 static char *user_format;
 
+static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
+{
+       free(user_format);
+       user_format = xstrdup(cp);
+       if (is_tformat)
+               rev->use_terminator = 1;
+       rev->commit_format = CMIT_FMT_USERFORMAT;
+}
+
 void get_commit_format(const char *arg, struct rev_info *rev)
 {
        int i;
@@ -33,12 +42,7 @@ void get_commit_format(const char *arg, struct rev_info *rev)
                return;
        }
        if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) {
-               const char *cp = strchr(arg, ':') + 1;
-               free(user_format);
-               user_format = xstrdup(cp);
-               if (arg[0] == 't')
-                       rev->use_terminator = 1;
-               rev->commit_format = CMIT_FMT_USERFORMAT;
+               save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
                return;
        }
        for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
@@ -50,6 +54,10 @@ void get_commit_format(const char *arg, struct rev_info *rev)
                        return;
                }
        }
+       if (strchr(arg, '%')) {
+               save_user_format(rev, arg, 1);
+               return;
+       }
 
        die("invalid --pretty format: %s", arg);
 }
@@ -127,7 +135,6 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
        int namelen;
        unsigned long time;
        int tz;
-       const char *filler = "    ";
 
        if (fmt == CMIT_FMT_ONELINE)
                return;
@@ -146,7 +153,6 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
                while (line < name_tail && isspace(name_tail[-1]))
                        name_tail--;
                display_name_length = name_tail - line;
-               filler = "";
                strbuf_addstr(sb, "From: ");
                add_rfc2047(sb, line, display_name_length, encoding);
                strbuf_add(sb, name_tail, namelen - display_name_length);
@@ -154,7 +160,7 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
        } else {
                strbuf_addf(sb, "%s: %.*s%.*s\n", what,
                              (fmt == CMIT_FMT_FULLER) ? 4 : 0,
-                             filler, namelen, line);
+                             "    ", namelen, line);
        }
        switch (fmt) {
        case CMIT_FMT_MEDIUM:
@@ -487,6 +493,40 @@ static void parse_commit_header(struct format_commit_context *context)
        context->commit_header_parsed = 1;
 }
 
+static int istitlechar(char c)
+{
+       return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+               (c >= '0' && c <= '9') || c == '.' || c == '_';
+}
+
+static void format_sanitized_subject(struct strbuf *sb, const char *msg)
+{
+       size_t trimlen;
+       size_t start_len = sb->len;
+       int space = 2;
+
+       for (; *msg && *msg != '\n'; msg++) {
+               if (istitlechar(*msg)) {
+                       if (space == 1)
+                               strbuf_addch(sb, '-');
+                       space = 0;
+                       strbuf_addch(sb, *msg);
+                       if (*msg == '.')
+                               while (*(msg+1) == '.')
+                                       msg++;
+               } else
+                       space |= 1;
+       }
+
+       /* trim any trailing '.' or '-' characters */
+       trimlen = 0;
+       while (sb->len - trimlen > start_len &&
+               (sb->buf[sb->len - 1 - trimlen] == '.'
+               || sb->buf[sb->len - 1 - trimlen] == '-'))
+               trimlen++;
+       strbuf_remove(sb, sb->len - trimlen, trimlen);
+}
+
 const char *format_subject(struct strbuf *sb, const char *msg,
                           const char *line_separator)
 {
@@ -567,16 +607,16 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                        return end - placeholder + 1;
                }
                if (!prefixcmp(placeholder + 1, "red")) {
-                       strbuf_addstr(sb, "\033[31m");
+                       strbuf_addstr(sb, GIT_COLOR_RED);
                        return 4;
                } else if (!prefixcmp(placeholder + 1, "green")) {
-                       strbuf_addstr(sb, "\033[32m");
+                       strbuf_addstr(sb, GIT_COLOR_GREEN);
                        return 6;
                } else if (!prefixcmp(placeholder + 1, "blue")) {
-                       strbuf_addstr(sb, "\033[34m");
+                       strbuf_addstr(sb, GIT_COLOR_BLUE);
                        return 5;
                } else if (!prefixcmp(placeholder + 1, "reset")) {
-                       strbuf_addstr(sb, "\033[m");
+                       strbuf_addstr(sb, GIT_COLOR_RESET);
                        return 6;
                } else
                        return 0;
@@ -677,6 +717,9 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
        case 's':       /* subject */
                format_subject(sb, msg + c->subject_off, " ");
                return 1;
+       case 'f':       /* sanitized subject */
+               format_sanitized_subject(sb, msg + c->subject_off);
+               return 1;
        case 'b':       /* body */
                strbuf_addstr(sb, msg + c->body_off);
                return 1;
index 55a8687ad15788f8ea5a5beb463d216908f618b2..621c34edc2a3a6a9bceb0f708ed5b0df05145fb5 100644 (file)
@@ -121,13 +121,13 @@ static void throughput_string(struct throughput *tp, off_t total,
                              (int)(total >> 30),
                              (int)(total & ((1 << 30) - 1)) / 10737419);
        } else if (total > 1 << 20) {
+               int x = total + 5243;  /* for rounding */
                l -= snprintf(tp->display, l, ", %u.%2.2u MiB",
-                             (int)(total >> 20),
-                             ((int)(total & ((1 << 20) - 1)) * 100) >> 20);
+                             x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20);
        } else if (total > 1 << 10) {
+               int x = total + 5;  /* for rounding */
                l -= snprintf(tp->display, l, ", %u.%2.2u KiB",
-                             (int)(total >> 10),
-                             ((int)(total & ((1 << 10) - 1)) * 100) >> 10);
+                             x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
        } else {
                l -= snprintf(tp->display, l, ", %u bytes", (int)total);
        }
diff --git a/quote.c b/quote.c
index 6a520855d6c418ecb1384ef9571b122b134af1af..7a49fcf69671646a0d3ba6de6478cfc6767c31fe 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -72,7 +72,7 @@ void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
        }
 }
 
-char *sq_dequote(char *arg)
+char *sq_dequote_step(char *arg, char **next)
 {
        char *dst = arg;
        char *src = arg;
@@ -92,6 +92,8 @@ char *sq_dequote(char *arg)
                switch (*++src) {
                case '\0':
                        *dst = 0;
+                       if (next)
+                               *next = NULL;
                        return arg;
                case '\\':
                        c = *++src;
@@ -101,11 +103,40 @@ char *sq_dequote(char *arg)
                        }
                /* Fallthrough */
                default:
-                       return NULL;
+                       if (!next || !isspace(*src))
+                               return NULL;
+                       do {
+                               c = *++src;
+                       } while (isspace(c));
+                       *dst = 0;
+                       *next = src;
+                       return arg;
                }
        }
 }
 
+char *sq_dequote(char *arg)
+{
+       return sq_dequote_step(arg, NULL);
+}
+
+int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
+{
+       char *next = arg;
+
+       if (!*arg)
+               return 0;
+       do {
+               char *dequoted = sq_dequote_step(next, &next);
+               if (!dequoted)
+                       return -1;
+               ALLOC_GROW(*argv, *nr + 1, *alloc);
+               (*argv)[(*nr)++] = dequoted;
+       } while (next);
+
+       return 0;
+}
+
 /* 1 means: quote as octal
  * 0 means: quote as octal if (quote_path_fully)
  * -1 means: never quote
diff --git a/quote.h b/quote.h
index c5eea6f18e2dfabd071b73e6507c34c2b7b5e39f..66730f2bff3cee42bc7c670e2a6d7da240db1d08 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -39,6 +39,15 @@ extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);
  */
 extern char *sq_dequote(char *);
 
+/*
+ * Same as the above, but can be used to unwrap many arguments in the
+ * same string separated by space. "next" is changed to point to the
+ * next argument that should be passed as first parameter. When there
+ * is no more argument to be dequoted, "next" is updated to point to NULL.
+ */
+extern char *sq_dequote_step(char *arg, char **next);
+extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
+
 extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
 extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
 extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
index 940ec76fdf231ac1345079ca2dc5da88925bcfb6..3f587110cb9d7be1890b7db68a0bdac35d48cd35 100644 (file)
@@ -67,8 +67,10 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
  */
 void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 {
-       ce->ce_ctime = st->st_ctime;
-       ce->ce_mtime = st->st_mtime;
+       ce->ce_ctime.sec = (unsigned int)st->st_ctime;
+       ce->ce_mtime.sec = (unsigned int)st->st_mtime;
+       ce->ce_ctime.nsec = ST_CTIME_NSEC(*st);
+       ce->ce_mtime.nsec = ST_MTIME_NSEC(*st);
        ce->ce_dev = st->st_dev;
        ce->ce_ino = st->st_ino;
        ce->ce_uid = st->st_uid;
@@ -196,11 +198,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
        default:
                die("internal error: ce_mode is %o", ce->ce_mode);
        }
-       if (ce->ce_mtime != (unsigned int) st->st_mtime)
+       if (ce->ce_mtime.sec != (unsigned int)st->st_mtime)
                changed |= MTIME_CHANGED;
-       if (trust_ctime && ce->ce_ctime != (unsigned int) st->st_ctime)
+       if (trust_ctime && ce->ce_ctime.sec != (unsigned int)st->st_ctime)
                changed |= CTIME_CHANGED;
 
+#ifdef USE_NSEC
+       if (ce->ce_mtime.nsec != ST_MTIME_NSEC(*st))
+               changed |= MTIME_CHANGED;
+       if (trust_ctime && ce->ce_ctime.nsec != ST_CTIME_NSEC(*st))
+               changed |= CTIME_CHANGED;
+#endif
+
        if (ce->ce_uid != (unsigned int) st->st_uid ||
            ce->ce_gid != (unsigned int) st->st_gid)
                changed |= OWNER_CHANGED;
@@ -232,8 +241,16 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
 {
        return (!S_ISGITLINK(ce->ce_mode) &&
-               istate->timestamp &&
-               ((unsigned int)istate->timestamp) <= ce->ce_mtime);
+               istate->timestamp.sec &&
+#ifdef USE_NSEC
+                /* nanosecond timestamped files can also be racy! */
+               (istate->timestamp.sec < ce->ce_mtime.sec ||
+                (istate->timestamp.sec == ce->ce_mtime.sec &&
+                 istate->timestamp.nsec <= ce->ce_mtime.nsec))
+#else
+               istate->timestamp.sec <= ce->ce_mtime.sec
+#endif
+                );
 }
 
 int ie_match_stat(const struct index_state *istate,
@@ -443,6 +460,26 @@ int remove_index_entry_at(struct index_state *istate, int pos)
        return 1;
 }
 
+/*
+ * Remove all cache ententries marked for removal, that is where
+ * CE_REMOVE is set in ce_flags.  This is much more effective than
+ * calling remove_index_entry_at() for each entry to be removed.
+ */
+void remove_marked_cache_entries(struct index_state *istate)
+{
+       struct cache_entry **ce_array = istate->cache;
+       unsigned int i, j;
+
+       for (i = j = 0; i < istate->cache_nr; i++) {
+               if (ce_array[i]->ce_flags & CE_REMOVE)
+                       remove_name_hash(ce_array[i]);
+               else
+                       ce_array[j++] = ce_array[i];
+       }
+       istate->cache_changed = 1;
+       istate->cache_nr = j;
+}
+
 int remove_file_from_index(struct index_state *istate, const char *path)
 {
        int pos = index_name_pos(istate, path, strlen(path));
@@ -1139,8 +1176,10 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en
        size_t len;
        const char *name;
 
-       ce->ce_ctime = ntohl(ondisk->ctime.sec);
-       ce->ce_mtime = ntohl(ondisk->mtime.sec);
+       ce->ce_ctime.sec = ntohl(ondisk->ctime.sec);
+       ce->ce_mtime.sec = ntohl(ondisk->mtime.sec);
+       ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec);
+       ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec);
        ce->ce_dev   = ntohl(ondisk->dev);
        ce->ce_ino   = ntohl(ondisk->ino);
        ce->ce_mode  = ntohl(ondisk->mode);
@@ -1206,7 +1245,8 @@ int read_index_from(struct index_state *istate, const char *path)
                return istate->cache_nr;
 
        errno = ENOENT;
-       istate->timestamp = 0;
+       istate->timestamp.sec = 0;
+       istate->timestamp.nsec = 0;
        fd = open(path, O_RDONLY);
        if (fd < 0) {
                if (errno == ENOENT)
@@ -1258,7 +1298,9 @@ int read_index_from(struct index_state *istate, const char *path)
                src_offset += ondisk_ce_size(ce);
                dst_offset += ce_size(ce);
        }
-       istate->timestamp = st.st_mtime;
+       istate->timestamp.sec = st.st_mtime;
+       istate->timestamp.nsec = ST_MTIME_NSEC(st);
+
        while (src_offset <= mmap_size - 20 - 8) {
                /* After an array of active_nr index entries,
                 * there can be arbitrary number of extended
@@ -1288,14 +1330,15 @@ unmap:
 
 int is_index_unborn(struct index_state *istate)
 {
-       return (!istate->cache_nr && !istate->alloc && !istate->timestamp);
+       return (!istate->cache_nr && !istate->alloc && !istate->timestamp.sec);
 }
 
 int discard_index(struct index_state *istate)
 {
        istate->cache_nr = 0;
        istate->cache_changed = 0;
-       istate->timestamp = 0;
+       istate->timestamp.sec = 0;
+       istate->timestamp.nsec = 0;
        istate->name_hash_initialized = 0;
        free_hash(&istate->name_hash);
        cache_tree_free(&(istate->cache_tree));
@@ -1441,10 +1484,10 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
        struct ondisk_cache_entry *ondisk = xcalloc(1, size);
        char *name;
 
-       ondisk->ctime.sec = htonl(ce->ce_ctime);
-       ondisk->ctime.nsec = 0;
-       ondisk->mtime.sec = htonl(ce->ce_mtime);
-       ondisk->mtime.nsec = 0;
+       ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
+       ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
+       ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
+       ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec);
        ondisk->dev  = htonl(ce->ce_dev);
        ondisk->ino  = htonl(ce->ce_ino);
        ondisk->mode = htonl(ce->ce_mode);
@@ -1466,13 +1509,14 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
        return ce_write(c, fd, ondisk, size);
 }
 
-int write_index(const struct index_state *istate, int newfd)
+int write_index(struct index_state *istate, int newfd)
 {
        git_SHA_CTX c;
        struct cache_header hdr;
        int i, err, removed, extended;
        struct cache_entry **cache = istate->cache;
        int entries = istate->cache_nr;
+       struct stat st;
 
        for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
@@ -1516,7 +1560,12 @@ int write_index(const struct index_state *istate, int newfd)
                if (err)
                        return -1;
        }
-       return ce_flush(&c, newfd);
+
+       if (ce_flush(&c, newfd) || fstat(newfd, &st))
+               return -1;
+       istate->timestamp.sec = (unsigned int)st.st_mtime;
+       istate->timestamp.nsec = ST_MTIME_NSEC(st);
+       return 0;
 }
 
 /*
index f751fdc8d832cae54647c1a70d888e979d324fd8..5623ea6b48a2f355e8866eabb2805b51b4127a4a 100644 (file)
@@ -241,8 +241,8 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
        commit->object.flags &= ~(ADDED | SEEN | SHOWN);
 }
 
-void show_reflog_message(struct reflog_walk_infoinfo, int oneline,
-       int relative_date)
+void show_reflog_message(struct reflog_walk_info *info, int oneline,
+       enum date_mode dmode)
 {
        if (info && info->last_commit_reflog) {
                struct commit_reflog *commit_reflog = info->last_commit_reflog;
@@ -251,8 +251,10 @@ void show_reflog_message(struct reflog_walk_info* info, int oneline,
                info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
                if (oneline) {
                        printf("%s@{", commit_reflog->reflogs->ref);
-                       if (commit_reflog->flag || relative_date)
-                               printf("%s", show_date(info->timestamp, 0, 1));
+                       if (commit_reflog->flag || dmode)
+                               printf("%s", show_date(info->timestamp,
+                                                      info->tz,
+                                                      dmode));
                        else
                                printf("%d", commit_reflog->reflogs->nr
                                       - 2 - commit_reflog->recno);
@@ -260,10 +262,10 @@ void show_reflog_message(struct reflog_walk_info* info, int oneline,
                }
                else {
                        printf("Reflog: %s@{", commit_reflog->reflogs->ref);
-                       if (commit_reflog->flag || relative_date)
+                       if (commit_reflog->flag || dmode)
                                printf("%s", show_date(info->timestamp,
                                                        info->tz,
-                                                       relative_date));
+                                                       dmode));
                        else
                                printf("%d", commit_reflog->reflogs->nr
                                       - 2 - commit_reflog->recno);
index 7ca1438f4d74b652f962c6bdfddd08fe0d75802d..74c90964bd95dcd97a5acde83c83b2f3b61c2441 100644 (file)
@@ -1,11 +1,14 @@
 #ifndef REFLOG_WALK_H
 #define REFLOG_WALK_H
 
+#include "cache.h"
+
 extern void init_reflog_walk(struct reflog_walk_info** info);
 extern int add_reflog_for_walk(struct reflog_walk_info *info,
                struct commit *commit, const char *name);
 extern void fake_reflog_parent(struct reflog_walk_info *info,
                struct commit *commit);
-extern void show_reflog_message(struct reflog_walk_info *info, int, int);
+extern void show_reflog_message(struct reflog_walk_info *info, int,
+               enum date_mode);
 
 #endif
diff --git a/refs.c b/refs.c
index 6eb5f5384611bb5d159d892a1bfd120d72e54b9b..3da3c8cefcbe123485fb5a68f2406488275ce43b 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -531,9 +531,10 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
 {
        if (strncmp(base, entry->name, trim))
                return 0;
+       /* Is this a "negative ref" that represents a deleted ref? */
+       if (is_null_sha1(entry->sha1))
+               return 0;
        if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
-               if (is_null_sha1(entry->sha1))
-                       return 0;
                if (!has_sha1_file(entry->sha1)) {
                        error("%s does not point to a valid object!", entry->name);
                        return 0;
@@ -647,19 +648,24 @@ int for_each_ref(each_ref_fn fn, void *cb_data)
        return do_for_each_ref("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);
+}
+
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/tags/", fn, 10, 0, cb_data);
+       return for_each_ref_in("refs/tags/", fn, cb_data);
 }
 
 int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/heads/", fn, 11, 0, cb_data);
+       return for_each_ref_in("refs/heads/", fn, cb_data);
 }
 
 int for_each_remote_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/remotes/", fn, 13, 0, cb_data);
+       return for_each_ref_in("refs/remotes/", fn, cb_data);
 }
 
 int for_each_rawref(each_ref_fn fn, void *cb_data)
@@ -676,6 +682,7 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
  * - it has double dots "..", or
  * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
  * - it ends with a "/".
+ * - it ends with ".lock"
  */
 
 static inline int bad_ref_char(int ch)
@@ -693,7 +700,8 @@ static inline int bad_ref_char(int ch)
 
 int check_ref_format(const char *ref)
 {
-       int ch, level, bad_type;
+       int ch, level, bad_type, last;
+       int ret = CHECK_REF_FORMAT_OK;
        const char *cp = ref;
 
        level = 0;
@@ -709,33 +717,50 @@ int check_ref_format(const char *ref)
                        return CHECK_REF_FORMAT_ERROR;
                bad_type = bad_ref_char(ch);
                if (bad_type) {
-                       return (bad_type == 2 && !*cp)
-                               ? CHECK_REF_FORMAT_WILDCARD
-                               : CHECK_REF_FORMAT_ERROR;
+                       if (bad_type == 2 && (!*cp || *cp == '/') &&
+                           ret == CHECK_REF_FORMAT_OK)
+                               ret = CHECK_REF_FORMAT_WILDCARD;
+                       else
+                               return CHECK_REF_FORMAT_ERROR;
                }
 
+               last = ch;
                /* scan the rest of the path component */
                while ((ch = *cp++) != 0) {
                        bad_type = bad_ref_char(ch);
-                       if (bad_type) {
-                               return (bad_type == 2 && !*cp)
-                                       ? CHECK_REF_FORMAT_WILDCARD
-                                       : CHECK_REF_FORMAT_ERROR;
-                       }
+                       if (bad_type)
+                               return CHECK_REF_FORMAT_ERROR;
                        if (ch == '/')
                                break;
-                       if (ch == '.' && *cp == '.')
+                       if (last == '.' && ch == '.')
+                               return CHECK_REF_FORMAT_ERROR;
+                       if (last == '@' && ch == '{')
                                return CHECK_REF_FORMAT_ERROR;
+                       last = ch;
                }
                level++;
                if (!ch) {
+                       if (ref <= cp - 2 && cp[-2] == '.')
+                               return CHECK_REF_FORMAT_ERROR;
                        if (level < 2)
                                return CHECK_REF_FORMAT_ONELEVEL;
-                       return CHECK_REF_FORMAT_OK;
+                       if (has_extension(ref, ".lock"))
+                               return CHECK_REF_FORMAT_ERROR;
+                       return ret;
                }
        }
 }
 
+const char *prettify_ref(const struct ref *ref)
+{
+       const char *name = ref->name;
+       return name + (
+               !prefixcmp(name, "refs/heads/") ? 11 :
+               !prefixcmp(name, "refs/tags/") ? 10 :
+               !prefixcmp(name, "refs/remotes/") ? 13 :
+               0);
+}
+
 const char *ref_rev_parse_rules[] = {
        "%.*s",
        "refs/%.*s",
@@ -869,8 +894,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
         * name is a proper prefix of our refname.
         */
        if (missing &&
-            !is_refname_available(ref, NULL, get_packed_refs(), 0))
+            !is_refname_available(ref, NULL, get_packed_refs(), 0)) {
+               last_errno = ENOTDIR;
                goto error_return;
+       }
 
        lock->lk = xcalloc(1, sizeof(struct lock_file));
 
@@ -978,12 +1005,10 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
                } else {
                        path = git_path("%s", refname);
                }
-               err = unlink(path);
-               if (err && errno != ENOENT) {
+               err = unlink_or_warn(path);
+               if (err && errno != ENOENT)
                        ret = 1;
-                       error("unlink(%s) failed: %s",
-                             path, strerror(errno));
-               }
+
                if (!(delopt & REF_NODEREF))
                        lock->lk->filename[i] = '.';
        }
@@ -993,10 +1018,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
         */
        ret |= repack_without_ref(refname);
 
-       err = unlink(git_path("logs/%s", lock->ref_name));
-       if (err && errno != ENOENT)
-               fprintf(stderr, "warning: unlink(%s) failed: %s",
-                       git_path("logs/%s", lock->ref_name), strerror(errno));
+       unlink_or_warn(git_path("logs/%s", lock->ref_name));
        invalidate_cached_refs();
        unlock_ref(lock);
        return ret;
@@ -1357,7 +1379,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
        if (adjust_shared_perm(git_HEAD)) {
                error("Unable to fix permissions on %s", lockpath);
        error_unlink_return:
-               unlink(lockpath);
+               unlink_or_warn(lockpath);
        error_free_return:
                free(git_HEAD);
                return -1;
@@ -1437,8 +1459,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
                                if (get_sha1_hex(rec + 41, sha1))
                                        die("Log %s is corrupt.", logfile);
                                if (hashcmp(logged_sha1, sha1)) {
-                                       fprintf(stderr,
-                                               "warning: Log %s has gap after %s.\n",
+                                       warning("Log %s has gap after %s.",
                                                logfile, show_date(date, tz, DATE_RFC2822));
                                }
                        }
@@ -1450,8 +1471,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
                                if (get_sha1_hex(rec + 41, logged_sha1))
                                        die("Log %s is corrupt.", logfile);
                                if (hashcmp(logged_sha1, sha1)) {
-                                       fprintf(stderr,
-                                               "warning: Log %s unexpectedly ended on %s.\n",
+                                       warning("Log %s unexpectedly ended on %s.",
                                                logfile, show_date(date, tz, DATE_RFC2822));
                                }
                        }
@@ -1506,8 +1526,10 @@ int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs,
                if (fstat(fileno(logfp), &statbuf) ||
                    statbuf.st_size < ofs ||
                    fseek(logfp, -ofs, SEEK_END) ||
-                   fgets(buf, sizeof(buf), logfp))
+                   fgets(buf, sizeof(buf), logfp)) {
+                       fclose(logfp);
                        return -1;
+               }
        }
 
        while (fgets(buf, sizeof(buf), logfp)) {
@@ -1628,10 +1650,121 @@ int update_ref(const char *action, const char *refname,
        return 0;
 }
 
-struct ref *find_ref_by_name(struct ref *list, const char *name)
+struct ref *find_ref_by_name(const struct ref *list, const char *name)
 {
        for ( ; list; list = list->next)
                if (!strcmp(list->name, name))
-                       return list;
+                       return (struct ref *)list;
        return NULL;
 }
+
+/*
+ * generate a format suitable for scanf from a ref_rev_parse_rules
+ * rule, that is replace the "%.*s" spec with a "%s" spec
+ */
+static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
+{
+       char *spec;
+
+       spec = strstr(rule, "%.*s");
+       if (!spec || strstr(spec + 4, "%.*s"))
+               die("invalid rule in ref_rev_parse_rules: %s", rule);
+
+       /* copy all until spec */
+       strncpy(scanf_fmt, rule, spec - rule);
+       scanf_fmt[spec - rule] = '\0';
+       /* copy new spec */
+       strcat(scanf_fmt, "%s");
+       /* copy remaining rule */
+       strcat(scanf_fmt, spec + 4);
+
+       return;
+}
+
+char *shorten_unambiguous_ref(const char *ref, int strict)
+{
+       int i;
+       static char **scanf_fmts;
+       static int nr_rules;
+       char *short_name;
+
+       /* pre generate scanf formats from ref_rev_parse_rules[] */
+       if (!nr_rules) {
+               size_t total_len = 0;
+
+               /* the rule list is NULL terminated, count them first */
+               for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
+                       /* no +1 because strlen("%s") < strlen("%.*s") */
+                       total_len += strlen(ref_rev_parse_rules[nr_rules]);
+
+               scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
+
+               total_len = 0;
+               for (i = 0; i < nr_rules; i++) {
+                       scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
+                                       + total_len;
+                       gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
+                       total_len += strlen(ref_rev_parse_rules[i]);
+               }
+       }
+
+       /* bail out if there are no rules */
+       if (!nr_rules)
+               return xstrdup(ref);
+
+       /* buffer for scanf result, at most ref must fit */
+       short_name = xstrdup(ref);
+
+       /* skip first rule, it will always match */
+       for (i = nr_rules - 1; i > 0 ; --i) {
+               int j;
+               int rules_to_fail = i;
+               int short_name_len;
+
+               if (1 != sscanf(ref, scanf_fmts[i], short_name))
+                       continue;
+
+               short_name_len = strlen(short_name);
+
+               /*
+                * in strict mode, all (except the matched one) rules
+                * must fail to resolve to a valid non-ambiguous ref
+                */
+               if (strict)
+                       rules_to_fail = nr_rules;
+
+               /*
+                * check if the short name resolves to a valid ref,
+                * but use only rules prior to the matched one
+                */
+               for (j = 0; j < rules_to_fail; j++) {
+                       const char *rule = ref_rev_parse_rules[j];
+                       unsigned char short_objectname[20];
+                       char refname[PATH_MAX];
+
+                       /* skip matched rule */
+                       if (i == j)
+                               continue;
+
+                       /*
+                        * the short name is ambiguous, if it resolves
+                        * (with this previous rule) to a valid ref
+                        * read_ref() returns 0 on success
+                        */
+                       mksnpath(refname, sizeof(refname),
+                                rule, short_name_len, short_name);
+                       if (!read_ref(refname, short_objectname))
+                               break;
+               }
+
+               /*
+                * short name is non-ambiguous if all previous rules
+                * haven't resolved to a valid ref
+                */
+               if (j == rules_to_fail)
+                       return short_name;
+       }
+
+       free(short_name);
+       return xstrdup(ref);
+}
diff --git a/refs.h b/refs.h
index 29bdcecd4edb5e7281a4da36a06aa05e025f38a7..29d17a48e4a2923bc72337deb1ef64cf7b467381 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -20,6 +20,7 @@ struct ref_lock {
 typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
 extern int head_ref(each_ref_fn, void *);
 extern int for_each_ref(each_ref_fn, void *);
+extern int for_each_ref_in(const char *, each_ref_fn, void *);
 extern int for_each_tag_ref(each_ref_fn, void *);
 extern int for_each_branch_ref(each_ref_fn, void *);
 extern int for_each_remote_ref(each_ref_fn, void *);
@@ -79,6 +80,9 @@ extern int for_each_reflog(each_ref_fn, void *);
 #define CHECK_REF_FORMAT_WILDCARD (-3)
 extern int check_ref_format(const char *target);
 
+extern const char *prettify_ref(const struct ref *ref);
+extern char *shorten_unambiguous_ref(const char *ref, int strict);
+
 /** rename ref, return 0 on success **/
 extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
 
index c7a8c2b1fbdea9aa03d8a5ccd8cbfb39eed742df..39583d52b9b632a67594acfb4372b6e192200b41 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -5,13 +5,14 @@
 #include "diff.h"
 #include "revision.h"
 #include "dir.h"
+#include "tag.h"
 
 static struct refspec s_tag_refspec = {
        0,
        1,
        0,
-       "refs/tags/",
-       "refs/tags/"
+       "refs/tags/*",
+       "refs/tags/*"
 };
 
 const struct refspec *tag_refspec = &s_tag_refspec;
@@ -365,7 +366,7 @@ static int handle_config(const char *key, const char *value, void *cb)
        }
        subkey = strrchr(name, '.');
        if (!subkey)
-               return error("Config with no key for remote %s", name);
+               return 0;
        remote = make_remote(name, subkey - name);
        remote->origin = REMOTE_CONFIG;
        if (!strcmp(subkey, ".mirror"))
@@ -454,16 +455,11 @@ static void read_config(void)
  */
 static int verify_refname(char *name, int is_glob)
 {
-       int result, len = -1;
+       int result;
 
-       if (is_glob) {
-               len = strlen(name);
-               assert(name[len - 1] == '/');
-               name[len - 1] = '\0';
-       }
        result = check_ref_format(name);
-       if (is_glob)
-               name[len - 1] = '/';
+       if (is_glob && result == CHECK_REF_FORMAT_WILDCARD)
+               result = CHECK_REF_FORMAT_OK;
        return result;
 }
 
@@ -498,7 +494,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
                int is_glob;
                const char *lhs, *rhs;
 
-               llen = is_glob = 0;
+               is_glob = 0;
 
                lhs = refspec[i];
                if (*lhs == '+') {
@@ -519,16 +515,15 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
 
                if (rhs) {
                        size_t rlen = strlen(++rhs);
-                       is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*"));
-                       rs[i].dst = xstrndup(rhs, rlen - is_glob);
+                       is_glob = (1 <= rlen && strchr(rhs, '*'));
+                       rs[i].dst = xstrndup(rhs, rlen);
                }
 
                llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
-               if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) {
+               if (1 <= llen && memchr(lhs, '*', llen)) {
                        if ((rhs && !is_glob) || (!rhs && fetch))
                                goto invalid;
                        is_glob = 1;
-                       llen--;
                } else if (rhs && is_glob) {
                        goto invalid;
                }
@@ -672,6 +667,17 @@ struct remote *remote_get(const char *name)
        return ret;
 }
 
+int remote_is_configured(const char *name)
+{
+       int i;
+       read_config();
+
+       for (i = 0; i < remotes_nr; i++)
+               if (!strcmp(name, remotes[i]->name))
+                       return 1;
+       return 0;
+}
+
 int for_each_remote(each_remote_fn fn, void *priv)
 {
        int i, result = 0;
@@ -728,6 +734,41 @@ int remote_has_url(struct remote *remote, const char *url)
        return 0;
 }
 
+static int match_name_with_pattern(const char *key, const char *name,
+                                  const char *value, char **result)
+{
+       const char *kstar = strchr(key, '*');
+       size_t klen;
+       size_t ksuffixlen;
+       size_t namelen;
+       int ret;
+       if (!kstar)
+               die("Key '%s' of pattern had no '*'", key);
+       klen = kstar - key;
+       ksuffixlen = strlen(kstar + 1);
+       namelen = strlen(name);
+       ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
+               !memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
+       if (ret && value) {
+               const char *vstar = strchr(value, '*');
+               size_t vlen;
+               size_t vsuffixlen;
+               if (!vstar)
+                       die("Value '%s' of pattern has no '*'", value);
+               vlen = vstar - value;
+               vsuffixlen = strlen(vstar + 1);
+               *result = xmalloc(vlen + vsuffixlen +
+                                 strlen(name) -
+                                 klen - ksuffixlen + 1);
+               strncpy(*result, value, vlen);
+               strncpy(*result + vlen,
+                       name + klen, namelen - klen - ksuffixlen);
+               strcpy(*result + vlen + namelen - klen - ksuffixlen,
+                      vstar + 1);
+       }
+       return ret;
+}
+
 int remote_find_tracking(struct remote *remote, struct refspec *refspec)
 {
        int find_src = refspec->src == NULL;
@@ -751,13 +792,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec)
                if (!fetch->dst)
                        continue;
                if (fetch->pattern) {
-                       if (!prefixcmp(needle, key)) {
-                               *result = xmalloc(strlen(value) +
-                                                 strlen(needle) -
-                                                 strlen(key) + 1);
-                               strcpy(*result, value);
-                               strcpy(*result + strlen(value),
-                                      needle + strlen(key));
+                       if (match_name_with_pattern(key, needle, value, result)) {
                                refspec->force = fetch->force;
                                return 0;
                        }
@@ -787,10 +822,18 @@ struct ref *alloc_ref(const char *name)
 
 static struct ref *copy_ref(const struct ref *ref)
 {
-       struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
-       memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1);
-       ret->next = NULL;
-       return ret;
+       struct ref *cpy;
+       size_t len;
+       if (!ref)
+               return NULL;
+       len = strlen(ref->name);
+       cpy = xmalloc(sizeof(struct ref) + len + 1);
+       memcpy(cpy, ref, sizeof(struct ref) + len + 1);
+       cpy->next = NULL;
+       cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL;
+       cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL;
+       cpy->peer_ref = copy_ref(ref->peer_ref);
+       return cpy;
 }
 
 struct ref *copy_ref_list(const struct ref *ref)
@@ -809,6 +852,7 @@ static void free_ref(struct ref *ref)
 {
        if (!ref)
                return;
+       free_ref(ref->peer_ref);
        free(ref->remote_status);
        free(ref->symref);
        free(ref);
@@ -819,7 +863,6 @@ void free_refs(struct ref *ref)
        struct ref *next;
        while (ref) {
                next = ref->next;
-               free(ref->peer_ref);
                free_ref(ref);
                ref = next;
        }
@@ -936,6 +979,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
                          struct refspec *rs)
 {
        struct ref *matched_src, *matched_dst;
+       int copy_src;
 
        const char *dst_value = rs->dst;
        char *dst_guess;
@@ -946,6 +990,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
        matched_src = matched_dst = NULL;
        switch (count_refspec_match(rs->src, src, &matched_src)) {
        case 1:
+               copy_src = 1;
                break;
        case 0:
                /* The source could be in the get_sha1() format
@@ -955,6 +1000,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
                matched_src = try_explicit_object_name(rs->src);
                if (!matched_src)
                        return error("src refspec %s does not match any.", rs->src);
+               copy_src = 0;
                break;
        default:
                return error("src refspec %s matches more than one.", rs->src);
@@ -1000,7 +1046,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
                return error("dst ref %s receives from more than one src.",
                      matched_dst->name);
        else {
-               matched_dst->peer_ref = matched_src;
+               matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src;
                matched_dst->force = rs->force;
        }
        return 0;
@@ -1029,7 +1075,8 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
                        continue;
                }
 
-               if (rs[i].pattern && !prefixcmp(src->name, rs[i].src))
+               if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name,
+                                                            NULL, NULL))
                        return rs + i;
        }
        if (matching_refs != -1)
@@ -1049,6 +1096,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
        struct refspec *rs;
        int send_all = flags & MATCH_REFS_ALL;
        int send_mirror = flags & MATCH_REFS_MIRROR;
+       int errs;
        static const char *default_refspec[] = { ":", 0 };
 
        if (!nr_refspec) {
@@ -1056,8 +1104,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                refspec = default_refspec;
        }
        rs = parse_push_refspec(nr_refspec, (const char **) refspec);
-       if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
-               return -1;
+       errs = match_explicit_refs(src, dst, dst_tail, rs, nr_refspec);
 
        /* pick the remainder */
        for ( ; src; src = src->next) {
@@ -1083,11 +1130,9 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
 
                } else {
                        const char *dst_side = pat->dst ? pat->dst : pat->src;
-                       dst_name = xmalloc(strlen(dst_side) +
-                                          strlen(src->name) -
-                                          strlen(pat->src) + 2);
-                       strcpy(dst_name, dst_side);
-                       strcat(dst_name, src->name + strlen(pat->src));
+                       if (!match_name_with_pattern(pat->src, src->name,
+                                                    dst_side, &dst_name))
+                               die("Didn't think it matches any more");
                }
                dst_peer = find_ref_by_name(dst, dst_name);
                if (dst_peer) {
@@ -1108,11 +1153,13 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                        dst_peer = make_linked_ref(dst_name, dst_tail);
                        hashcpy(dst_peer->new_sha1, src->new_sha1);
                }
-               dst_peer->peer_ref = src;
+               dst_peer->peer_ref = copy_ref(src);
                dst_peer->force = pat->force;
        free_name:
                free(dst_name);
        }
+       if (errs)
+               return -1;
        return 0;
 }
 
@@ -1134,8 +1181,9 @@ struct branch *branch_get(const char *name)
                        for (i = 0; i < ret->merge_nr; i++) {
                                ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
                                ret->merge[i]->src = xstrdup(ret->merge_name[i]);
-                               remote_find_tracking(ret->remote,
-                                                    ret->merge[i]);
+                               if (remote_find_tracking(ret->remote, ret->merge[i])
+                                   && !strcmp(ret->remote_name, "."))
+                                       ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
                        }
                }
        }
@@ -1163,19 +1211,17 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
        struct ref *ret = NULL;
        struct ref **tail = &ret;
 
-       int remote_prefix_len = strlen(refspec->src);
-       int local_prefix_len = strlen(refspec->dst);
+       char *expn_name;
 
        for (ref = remote_refs; ref; ref = ref->next) {
                if (strchr(ref->name, '^'))
                        continue; /* a dereference item */
-               if (!prefixcmp(ref->name, refspec->src)) {
-                       const char *match;
+               if (match_name_with_pattern(refspec->src, ref->name,
+                                           refspec->dst, &expn_name)) {
                        struct ref *cpy = copy_ref(ref);
-                       match = ref->name + remote_prefix_len;
 
-                       cpy->peer_ref = alloc_ref_with_prefix(refspec->dst,
-                                       local_prefix_len, match);
+                       cpy->peer_ref = alloc_ref(expn_name);
+                       free(expn_name);
                        if (refspec->force)
                                cpy->peer_ref->force = 1;
                        *tail = cpy;
@@ -1208,7 +1254,7 @@ struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
 
 static struct ref *get_local_ref(const char *name)
 {
-       if (!name)
+       if (!name || name[0] == '\0')
                return NULL;
 
        if (!prefixcmp(name, "refs/"))
@@ -1278,6 +1324,54 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)
        return 1;
 }
 
+static void unmark_and_free(struct commit_list *list, unsigned int mark)
+{
+       while (list) {
+               struct commit_list *temp = list;
+               temp->item->object.flags &= ~mark;
+               list = temp->next;
+               free(temp);
+       }
+}
+
+int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
+{
+       struct object *o;
+       struct commit *old, *new;
+       struct commit_list *list, *used;
+       int found = 0;
+
+       /* Both new and old must be commit-ish and new is descendant of
+        * old.  Otherwise we require --force.
+        */
+       o = deref_tag(parse_object(old_sha1), NULL, 0);
+       if (!o || o->type != OBJ_COMMIT)
+               return 0;
+       old = (struct commit *) o;
+
+       o = deref_tag(parse_object(new_sha1), NULL, 0);
+       if (!o || o->type != OBJ_COMMIT)
+               return 0;
+       new = (struct commit *) o;
+
+       if (parse_commit(new) < 0)
+               return 0;
+
+       used = list = NULL;
+       commit_list_insert(new, &list);
+       while (list) {
+               new = pop_most_recent_commit(&list, TMP_MARK);
+               commit_list_insert(new, &used);
+               if (new == old) {
+                       found = 1;
+                       break;
+               }
+       }
+       unmark_and_free(list, TMP_MARK);
+       unmark_and_free(used, TMP_MARK);
+       return found;
+}
+
 /*
  * Return true if there is anything to report, otherwise false.
  */
@@ -1366,9 +1460,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                return 0;
 
        base = branch->merge[0]->dst;
-       if (!prefixcmp(base, "refs/remotes/")) {
-               base += strlen("refs/remotes/");
-       }
+       base = shorten_unambiguous_ref(base, 0);
        if (!num_theirs)
                strbuf_addf(sb, "Your branch is ahead of '%s' "
                            "by %d commit%s.\n",
@@ -1385,3 +1477,68 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                            base, num_ours, num_theirs);
        return 1;
 }
+
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+       struct ref ***local_tail = cb_data;
+       struct ref *ref;
+       int len;
+
+       /* we already know it starts with refs/ to get here */
+       if (check_ref_format(refname + 5))
+               return 0;
+
+       len = strlen(refname) + 1;
+       ref = xcalloc(1, sizeof(*ref) + len);
+       hashcpy(ref->new_sha1, sha1);
+       memcpy(ref->name, refname, len);
+       **local_tail = ref;
+       *local_tail = &ref->next;
+       return 0;
+}
+
+struct ref *get_local_heads(void)
+{
+       struct ref *local_refs = NULL, **local_tail = &local_refs;
+       for_each_ref(one_local_ref, &local_tail);
+       return local_refs;
+}
+
+struct ref *guess_remote_head(const struct ref *head,
+                             const struct ref *refs,
+                             int all)
+{
+       const struct ref *r;
+       struct ref *list = NULL;
+       struct ref **tail = &list;
+
+       if (!head)
+               return NULL;
+
+       /*
+        * Some transports support directly peeking at
+        * where HEAD points; if that is the case, then
+        * we don't have to guess.
+        */
+       if (head->symref)
+               return copy_ref(find_ref_by_name(refs, head->symref));
+
+       /* If refs/heads/master could be right, it is. */
+       if (!all) {
+               r = find_ref_by_name(refs, "refs/heads/master");
+               if (r && !hashcmp(r->old_sha1, head->old_sha1))
+                       return copy_ref(r);
+       }
+
+       /* Look for another ref that points there */
+       for (r = refs; r; r = r->next) {
+               if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) {
+                       *tail = copy_ref(r);
+                       tail = &((*tail)->next);
+                       if (!all)
+                               break;
+               }
+       }
+
+       return list;
+}
index a46a5be131caf1d1d71f97cab3c3ba2cebb6386c..99706a89bc6011c01fcd661d8bad4b26f59b0ca7 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -45,6 +45,7 @@ struct remote {
 };
 
 struct remote *remote_get(const char *name);
+int remote_is_configured(const char *name);
 
 typedef int each_remote_fn(struct remote *remote, void *priv);
 int for_each_remote(each_remote_fn fn, void *priv);
@@ -74,6 +75,7 @@ int check_ref_type(const struct ref *ref, int flags);
 void free_refs(struct ref *ref);
 
 int resolve_remote_symref(struct ref *ref, struct ref *list);
+int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1);
 
 /*
  * Removes and frees any duplicate refs in the map.
@@ -137,4 +139,15 @@ enum match_refs_flags {
 int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
 int format_tracking_info(struct branch *branch, struct strbuf *sb);
 
+struct ref *get_local_heads(void);
+/*
+ * Find refs from a list which are likely to be pointed to by the given HEAD
+ * ref. If 'all' is false, returns the most likely ref; otherwise, returns a
+ * list of all candidate refs. If no match is found (or 'head' is NULL),
+ * returns NULL. All returns are newly allocated and should be freed.
+ */
+struct ref *guess_remote_head(const struct ref *head,
+                             const struct ref *refs,
+                             int all);
+
 #endif
index 3518207c178904b91ce28f8d3bf2ba0ee560d0e7..87360dc23e54aabf47b3bda160e58058507d4a2f 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -12,15 +12,15 @@ static int rerere_autoupdate;
 
 static char *merge_rr_path;
 
-static const char *rr_path(const char *name, const char *file)
+const char *rerere_path(const char *hex, const char *file)
 {
-       return git_path("rr-cache/%s/%s", name, file);
+       return git_path("rr-cache/%s/%s", hex, file);
 }
 
-static int has_resolution(const char *name)
+int has_rerere_resolution(const char *hex)
 {
        struct stat st;
-       return !stat(rr_path(name, "postimage"), &st);
+       return !stat(rerere_path(hex, "postimage"), &st);
 }
 
 static void read_rr(struct string_list *rr)
@@ -173,7 +173,7 @@ static int handle_file(const char *path,
                git_SHA1_Final(sha1, &ctx);
        if (hunk != RR_CONTEXT) {
                if (output)
-                       unlink(output);
+                       unlink_or_warn(output);
                return error("Could not parse conflict hunks in %s", path);
        }
        if (wrerror)
@@ -208,12 +208,12 @@ static int merge(const char *name, const char *path)
        mmbuffer_t result = {NULL, 0};
        xpparam_t xpp = {XDF_NEED_MINIMAL};
 
-       if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0)
+       if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0)
                return 1;
 
-       if (read_mmfile(&cur, rr_path(name, "thisimage")) ||
-                       read_mmfile(&base, rr_path(name, "preimage")) ||
-                       read_mmfile(&other, rr_path(name, "postimage")))
+       if (read_mmfile(&cur, rerere_path(name, "thisimage")) ||
+                       read_mmfile(&base, rerere_path(name, "preimage")) ||
+                       read_mmfile(&other, rerere_path(name, "postimage")))
                return 1;
        ret = xdl_merge(&base, &cur, "", &other, "",
                        &xpp, XDL_MERGE_ZEALOUS, &result);
@@ -291,7 +291,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
                        string_list_insert(path, rr)->util = hex;
                        if (mkdir(git_path("rr-cache/%s", hex), 0755))
                                continue;
-                       handle_file(path, NULL, rr_path(hex, "preimage"));
+                       handle_file(path, NULL, rerere_path(hex, "preimage"));
                        fprintf(stderr, "Recorded preimage for '%s'\n", path);
                }
        }
@@ -307,7 +307,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
                const char *path = rr->items[i].string;
                const char *name = (const char *)rr->items[i].util;
 
-               if (has_resolution(name)) {
+               if (has_rerere_resolution(name)) {
                        if (!merge(name, path)) {
                                if (rerere_autoupdate)
                                        string_list_insert(path, &update);
@@ -326,7 +326,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
                        continue;
 
                fprintf(stderr, "Recorded resolution for '%s'.\n", path);
-               copy_file(rr_path(name, "postimage"), path, 0666);
+               copy_file(rerere_path(name, "postimage"), path, 0666);
        mark_resolved:
                rr->items[i].util = NULL;
        }
index f9b03862fe78b560ee606637be3b1ce972a2cc14..13313f3f2b2cc6a8d895305b7ac92c12c1753682 100644 (file)
--- a/rerere.h
+++ b/rerere.h
@@ -5,5 +5,7 @@
 
 extern int setup_rerere(struct string_list *);
 extern int rerere(void);
+extern const char *rerere_path(const char *hex, const char *file);
+extern int has_rerere_resolution(const char *hex);
 
 #endif
index d7d345bdd9c71eb0f8d77179b4af1b79049d054c..18b7ebbbd599417462b5c4275c2581b91ebeca6a 100644 (file)
@@ -209,7 +209,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object
        }
 
        /*
-        * Tree object? Either mark it uniniteresting, or add it
+        * Tree object? Either mark it uninteresting, or add it
         * to the list of objects to look at later..
         */
        if (object->type == OBJ_TREE) {
@@ -1130,9 +1130,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--pretty")) {
                revs->verbose_header = 1;
                get_commit_format(arg+8, revs);
-       } else if (!prefixcmp(arg, "--pretty=")) {
+       } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
                revs->verbose_header = 1;
                get_commit_format(arg+9, revs);
+       } else if (!strcmp(arg, "--oneline")) {
+               revs->verbose_header = 1;
+               get_commit_format("oneline", revs);
+               revs->abbrev_commit = 1;
        } else if (!strcmp(arg, "--graph")) {
                revs->topo_order = 1;
                revs->rewrite_parents = 1;
index bfe27071bfff3dda4a468844b0cc9195c67b5625..be39e7d38643817cced4e07a89b6db5201f2b747 100644 (file)
@@ -85,8 +85,10 @@ struct rev_info {
        struct log_info *loginfo;
        int             nr, total;
        const char      *mime_boundary;
+       const char      *patch_suffix;
+       int             numbered_files;
        char            *message_id;
-       const char      *ref_message_id;
+       struct string_list *ref_message_ids;
        const char      *add_signoff;
        const char      *extra_headers;
        const char      *log_reencode;
index b05c734d05e99cd009a0df26f0fc95fa13ae6f25..eb2efc33073512efd14b65e67db8cf814ca3fa33 100644 (file)
@@ -106,7 +106,7 @@ int start_command(struct child_process *cmd)
                if (cmd->env) {
                        for (; *cmd->env; cmd->env++) {
                                if (strchr(*cmd->env, '='))
-                                       putenv((char*)*cmd->env);
+                                       putenv((char *)*cmd->env);
                                else
                                        unsetenv(*cmd->env);
                        }
index 8ff1dc35390083c3648c4ee5790f35633d956069..1d7b1b3b4f41177dfd6637fcd98a0fc01f310c09 100644 (file)
@@ -2,17 +2,16 @@
 #define SEND_PACK_H
 
 struct send_pack_args {
-       const char *receivepack;
        unsigned verbose:1,
-               send_all:1,
                send_mirror:1,
                force_update:1,
                use_thin_pack:1,
+               use_ofs_delta:1,
                dry_run:1;
 };
 
 int send_pack(struct send_pack_args *args,
-             const char *dest, struct remote *remote,
-             int nr_heads, const char **heads);
+             int fd[], struct child_process *conn,
+             struct ref *remote_refs, struct extra_have_objects *extra_have);
 
 #endif
index 66b0d9d878a011393582b837301eb1fd5caf2e40..4098ca2b5c166c32cfe4aea9d1bff2e6593e9a60 100644 (file)
@@ -132,8 +132,8 @@ static int read_pack_info_file(const char *infofile)
 
 static int compare_info(const void *a_, const void *b_)
 {
-       struct pack_info * const* a = a_;
-       struct pack_info * const* b = b_;
+       struct pack_info *const *a = a_;
+       struct pack_info *const *b = b_;
 
        if (0 <= (*a)->old_num && 0 <= (*b)->old_num)
                /* Keep the order in the original */
@@ -246,7 +246,7 @@ int update_server_info(int force)
        errs = errs | update_info_packs(force);
 
        /* remove leftover rev-cache file if there is any */
-       unlink(git_path("info/rev-cache"));
+       unlink_or_warn(git_path("info/rev-cache"));
 
        return errs;
 }
index da357479cf19aad4bebc64f874c76fdf8566712b..c4dc55d1f5cd07adcf46865354f841cc587c51f6 100644 (file)
@@ -1,6 +1,107 @@
 #include "cache.h"
 #include "sha1-lookup.h"
 
+static uint32_t take2(const unsigned char *sha1)
+{
+       return ((sha1[0] << 8) | sha1[1]);
+}
+
+/*
+ * Conventional binary search loop looks like this:
+ *
+ *      do {
+ *              int mi = (lo + hi) / 2;
+ *              int cmp = "entry pointed at by mi" minus "target";
+ *              if (!cmp)
+ *                      return (mi is the wanted one)
+ *              if (cmp > 0)
+ *                      hi = mi; "mi is larger than target"
+ *              else
+ *                      lo = mi+1; "mi is smaller than target"
+ *      } while (lo < hi);
+ *
+ * The invariants are:
+ *
+ * - When entering the loop, lo points at a slot that is never
+ *   above the target (it could be at the target), hi points at a
+ *   slot that is guaranteed to be above the target (it can never
+ *   be at the target).
+ *
+ * - We find a point 'mi' between lo and hi (mi could be the same
+ *   as lo, but never can be the same as hi), and check if it hits
+ *   the target.  There are three cases:
+ *
+ *    - if it is a hit, we are happy.
+ *
+ *    - if it is strictly higher than the target, we update hi with
+ *      it.
+ *
+ *    - if it is strictly lower than the target, we update lo to be
+ *      one slot after it, because we allow lo to be at the target.
+ *
+ * When choosing 'mi', we do not have to take the "middle" but
+ * anywhere in between lo and hi, as long as lo <= mi < hi is
+ * satisfied.  When we somehow know that the distance between the
+ * target and lo is much shorter than the target and hi, we could
+ * pick mi that is much closer to lo than the midway.
+ */
+/*
+ * The table should contain "nr" elements.
+ * The sha1 of element i (between 0 and nr - 1) should be returned
+ * by "fn(i, table)".
+ */
+int sha1_pos(const unsigned char *sha1, void *table, size_t nr,
+            sha1_access_fn fn)
+{
+       size_t hi = nr;
+       size_t lo = 0;
+       size_t mi = 0;
+
+       if (!nr)
+               return -1;
+
+       if (nr != 1) {
+               size_t lov, hiv, miv, ofs;
+
+               for (ofs = 0; ofs < 18; ofs += 2) {
+                       lov = take2(fn(0, table) + ofs);
+                       hiv = take2(fn(nr - 1, table) + ofs);
+                       miv = take2(sha1 + ofs);
+                       if (miv < lov)
+                               return -1;
+                       if (hiv < miv)
+                               return -1 - nr;
+                       if (lov != hiv) {
+                               /*
+                                * At this point miv could be equal
+                                * to hiv (but sha1 could still be higher);
+                                * the invariant of (mi < hi) should be
+                                * kept.
+                                */
+                               mi = (nr - 1) * (miv - lov) / (hiv - lov);
+                               if (lo <= mi && mi < hi)
+                                       break;
+                               die("BUG: assertion failed in binary search");
+                       }
+               }
+               if (18 <= ofs)
+                       die("cannot happen -- lo and hi are identical");
+       }
+
+       do {
+               int cmp;
+               cmp = hashcmp(fn(mi, table), sha1);
+               if (!cmp)
+                       return mi;
+               if (cmp > 0)
+                       hi = mi;
+               else
+                       lo = mi + 1;
+               mi = (hi + lo) / 2;
+       } while (lo < hi);
+       return -lo-1;
+}
+
 /*
  * Conventional binary search loop looks like this:
  *
index 3249a81b3d664afc89c98e6d9dd6b512092a82f9..20af2856818ed51b2afb1718a7e317133ee0d7bd 100644 (file)
@@ -1,6 +1,13 @@
 #ifndef SHA1_LOOKUP_H
 #define SHA1_LOOKUP_H
 
+typedef const unsigned char *sha1_access_fn(size_t index, void *table);
+
+extern int sha1_pos(const unsigned char *sha1,
+                   void *table,
+                   size_t nr,
+                   sha1_access_fn fn);
+
 extern int sha1_entry_pos(const void *table,
                          size_t elem_size,
                          size_t key_offset,
index 8fe135dc61908103cf2d7de700794843f83db057..8f5fe62d545ace21c338cd554c76bac5d5acb431 100644 (file)
@@ -720,6 +720,8 @@ static int open_packed_git_1(struct packed_git *p)
                return error("packfile %s index unavailable", p->pack_name);
 
        p->pack_fd = open(p->pack_name, O_RDONLY);
+       while (p->pack_fd < 0 && errno == EMFILE && unuse_one_window(p, -1))
+               p->pack_fd = open(p->pack_name, O_RDONLY);
        if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
                return -1;
 
@@ -791,7 +793,7 @@ static int in_window(struct pack_window *win, off_t offset)
                && (offset + 20) <= (win_off + win->len);
 }
 
-unsigned charuse_pack(struct packed_git *p,
+unsigned char *use_pack(struct packed_git *p,
                struct pack_window **w_cursor,
                off_t offset,
                unsigned int *left)
@@ -937,6 +939,8 @@ static void prepare_packed_git_one(char *objdir, int local)
        sprintf(path, "%s/pack", objdir);
        len = strlen(path);
        dir = opendir(path);
+       while (!dir && errno == EMFILE && unuse_one_window(packed_git, -1))
+               dir = opendir(path);
        if (!dir) {
                if (errno != ENOENT)
                        error("unable to open object pack directory: %s: %s",
@@ -1158,8 +1162,7 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
                unsigned long len, enum object_type *type, unsigned long *sizep)
 {
        unsigned shift;
-       unsigned char c;
-       unsigned long size;
+       unsigned long size, c;
        unsigned long used = 0;
 
        c = buf[used++];
@@ -2225,7 +2228,9 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
 {
        int ret = 0;
 
-       if (link(tmpfile, filename))
+       if (object_creation_mode == OBJECT_CREATION_USES_RENAMES)
+               goto try_rename;
+       else if (link(tmpfile, filename))
                ret = errno;
 
        /*
@@ -2240,11 +2245,12 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
         * left to unlink.
         */
        if (ret && ret != EEXIST) {
+       try_rename:
                if (!rename(tmpfile, filename))
                        goto out;
                ret = errno;
        }
-       unlink(tmpfile);
+       unlink_or_warn(tmpfile);
        if (ret) {
                if (ret != EEXIST) {
                        return error("unable to write sha1 filename %s: %s\n", filename, strerror(ret));
@@ -2336,6 +2342,8 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
 
        filename = sha1_file_name(sha1);
        fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
+       while (fd < 0 && errno == EMFILE && unuse_one_window(packed_git, -1))
+               fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
        if (fd < 0) {
                if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
index 2f75179f4c6c1d05bdd7594b23dcf77007c26751..44bb62d270739a232e87c90c05ce89fcc86bc15b 100644 (file)
@@ -242,10 +242,10 @@ static int ambiguous_path(const char *path, int len)
  * *string and *len will only be substituted, and *string returned (for
  * later free()ing) if the string passed in is of the form @{-<n>}.
  */
-static char *substitute_nth_last_branch(const char **string, int *len)
+static char *substitute_branch_name(const char **string, int *len)
 {
        struct strbuf buf = STRBUF_INIT;
-       int ret = interpret_nth_last_branch(*string, &buf);
+       int ret = interpret_branch_name(*string, &buf);
 
        if (ret == *len) {
                size_t size;
@@ -259,7 +259,7 @@ static char *substitute_nth_last_branch(const char **string, int *len)
 
 int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
 {
-       char *last_branch = substitute_nth_last_branch(&str, &len);
+       char *last_branch = substitute_branch_name(&str, &len);
        const char **p, *r;
        int refs_found = 0;
 
@@ -288,7 +288,7 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
 
 int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
 {
-       char *last_branch = substitute_nth_last_branch(&str, &len);
+       char *last_branch = substitute_branch_name(&str, &len);
        const char **p;
        int logs_found = 0;
 
@@ -355,7 +355,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                struct strbuf buf = STRBUF_INIT;
                int ret;
                /* try the @{-N} syntax for n-th checkout */
-               ret = interpret_nth_last_branch(str+at, &buf);
+               ret = interpret_branch_name(str+at, &buf);
                if (ret > 0) {
                        /* substitute this branch name and restart */
                        return get_sha1_1(buf.buf, buf.len, sha1);
@@ -750,7 +750,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
  * If the input was ok but there are not N branch switches in the
  * reflog, it returns 0.
  */
-int interpret_nth_last_branch(const char *name, struct strbuf *buf)
+int interpret_branch_name(const char *name, struct strbuf *buf)
 {
        long nth;
        int i, retval;
@@ -777,8 +777,6 @@ int interpret_nth_last_branch(const char *name, struct strbuf *buf)
        for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb);
        if (cb.cnt < nth) {
                cb.cnt = 0;
-               for (i = 0; i < nth; i++)
-                       strbuf_release(&cb.buf[i]);
                for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
        }
        if (cb.cnt < nth)
diff --git a/shell.c b/shell.c
index e3393690dd3b79af80e6b95710583e744fd574d4..b968be79f487f8d93e205fd0a844a206e24f21f8 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -40,6 +40,7 @@ static struct commands {
 } cmd_list[] = {
        { "git-receive-pack", do_generic_cmd },
        { "git-upload-pack", do_generic_cmd },
+       { "git-upload-archive", do_generic_cmd },
        { "cvs", do_cvs_cmd },
        { NULL },
 };
index cca3360546dabf9f018b882f690bd1dea9de534d..899b1ff36619d0077dced9da77f538ab98396a83 100644 (file)
@@ -19,7 +19,7 @@
 
 #define FIX_SIZE 10  /* large enough for any of the above */
 
-int recv_sideband(const char *me, int in_stream, int out, int err)
+int recv_sideband(const char *me, int in_stream, int out)
 {
        unsigned pf = strlen(PREFIX);
        unsigned sf;
@@ -41,8 +41,7 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
                if (len == 0)
                        break;
                if (len < 1) {
-                       len = sprintf(buf, "%s: protocol error: no band designator\n", me);
-                       safe_write(err, buf, len);
+                       fprintf(stderr, "%s: protocol error: no band designator\n", me);
                        return SIDEBAND_PROTOCOL_ERROR;
                }
                band = buf[pf] & 0xff;
@@ -50,8 +49,8 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
                switch (band) {
                case 3:
                        buf[pf] = ' ';
-                       buf[pf+1+len] = '\n';
-                       safe_write(err, buf, pf+1+len+1);
+                       buf[pf+1+len] = '\0';
+                       fprintf(stderr, "%s\n", buf);
                        return SIDEBAND_REMOTE_ERROR;
                case 2:
                        buf[pf] = ' ';
@@ -95,12 +94,12 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
                                        memcpy(save, b + brk, sf);
                                        b[brk + sf - 1] = b[brk - 1];
                                        memcpy(b + brk - 1, suffix, sf);
-                                       safe_write(err, b, brk + sf);
+                                       fprintf(stderr, "%.*s", brk + sf, b);
                                        memcpy(b + brk, save, sf);
                                        len -= brk;
                                } else {
                                        int l = brk ? brk : len;
-                                       safe_write(err, b, l);
+                                       fprintf(stderr, "%.*s", l, b);
                                        len -= l;
                                }
 
@@ -112,10 +111,8 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
                        safe_write(out, buf + pf+1, len);
                        continue;
                default:
-                       len = sprintf(buf,
-                                     "%s: protocol error: bad band #%d\n",
-                                     me, band);
-                       safe_write(err, buf, len);
+                       fprintf(stderr, "%s: protocol error: bad band #%d\n",
+                               me, band);
                        return SIDEBAND_PROTOCOL_ERROR;
                }
        }
index a84b6917c7a17b5f8a922540801e98d46aa24431..d72db35d1e0dc109f75b292762013c11b86426aa 100644 (file)
@@ -7,7 +7,7 @@
 #define DEFAULT_PACKET_MAX 1000
 #define LARGE_PACKET_MAX 65520
 
-int recv_sideband(const char *me, int in_stream, int out, int err);
+int recv_sideband(const char *me, int in_stream, int out);
 ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
 
 #endif
index 6ed06840b856a91f6d215c9a862e064f521384f0..f03d11702b3f6212ca7305df60f2f9ea6ca49e35 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "refs.h"
 
 int prefixcmp(const char *str, const char *prefix)
 {
@@ -139,14 +140,11 @@ void strbuf_list_free(struct strbuf **sbs)
 
 int strbuf_cmp(const struct strbuf *a, const struct strbuf *b)
 {
-       int cmp;
-       if (a->len < b->len) {
-               cmp = memcmp(a->buf, b->buf, a->len);
-               return cmp ? cmp : -1;
-       } else {
-               cmp = memcmp(a->buf, b->buf, b->len);
-               return cmp ? cmp : a->len != b->len;
-       }
+       int len = a->len < b->len ? a->len: b->len;
+       int cmp = memcmp(a->buf, b->buf, len);
+       if (cmp)
+               return cmp;
+       return a->len < b->len ? -1: a->len != b->len;
 }
 
 void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
@@ -262,7 +260,7 @@ size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
        res = fread(sb->buf + sb->len, 1, size, f);
        if (res > 0)
                strbuf_setlen(sb, sb->len + res);
-       else if (res < 0 && oldalloc == 0)
+       else if (oldalloc == 0)
                strbuf_release(sb);
        return res;
 }
@@ -360,3 +358,19 @@ 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 89bd36e15a541e268117ff023afc6a43137cdd6e..eaa8704d5fa6e85d33953db77dd03de3cb462df4 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -11,7 +11,7 @@
  *    build complex strings/buffers whose final size isn't easily known.
  *
  *    It is NOT legal to copy the ->buf pointer away.
- *    `strbuf_detach' is the operation that detachs a buffer from its shell
+ *    `strbuf_detach' is the operation that detaches a buffer from its shell
  *    while keeping the shell valid wrt its invariants.
  *
  * 2. the ->buf member is a byte array that has at least ->len + 1 bytes
@@ -131,4 +131,7 @@ extern int strbuf_getline(struct strbuf *, FILE *, int);
 extern void stripspace(struct strbuf *buf, int skip_comments);
 extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
 
+extern int strbuf_branchname(struct strbuf *sb, const char *name);
+extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
+
 #endif /* STRBUF_H */
index 15e14cf47a586613a62970b393c269728ffbef93..1ac536e638dbbc02deb2d4e4a607cc52f7f7c108 100644 (file)
@@ -92,6 +92,16 @@ struct string_list_item *string_list_lookup(const char *string, struct string_li
        return list->items + i;
 }
 
+int for_each_string_list(string_list_each_func_t fn,
+                        struct string_list *list, void *cb_data)
+{
+       int i, ret = 0;
+       for (i = 0; i < list->nr; i++)
+               if ((ret = fn(&list->items[i], cb_data)))
+                       break;
+       return ret;
+}
+
 void string_list_clear(struct string_list *list, int free_util)
 {
        if (list->items) {
index d32ba05202880733dd76f5465a0ae16753d1fba6..14bbc477decc32618139e912fda22e585d815159 100644 (file)
@@ -20,6 +20,11 @@ void string_list_clear(struct string_list *list, int free_util);
 typedef void (*string_list_clear_func_t)(void *p, const char *str);
 void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
 
+/* Use this function to iterate over each item */
+typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
+int for_each_string_list(string_list_each_func_t,
+                        struct string_list *list, void *cb_data);
+
 /* Use these functions only on sorted lists: */
 int string_list_has_string(const struct string_list *list, const char *string);
 int string_list_find_insert_index(const struct string_list *list, const char *string,
index f262b7c44b387f4c60901124c30585b3c059fa73..1d6b35b858020300f502e2a9341b82d4fa8a61fd 100644 (file)
@@ -1,51 +1,54 @@
 #include "cache.h"
 
-static struct cache_def {
-       char path[PATH_MAX + 1];
-       int len;
-       int flags;
-       int track_flags;
-       int prefix_len_stat_func;
-} cache;
-
 /*
  * Returns the length (on a path component basis) of the longest
- * common prefix match of 'name' and the cached path string.
+ * common prefix match of 'name_a' and 'name_b'.
  */
-static inline int longest_match_lstat_cache(int len, const char *name,
-                                           int *previous_slash)
+static int longest_path_match(const char *name_a, int len_a,
+                             const char *name_b, int len_b,
+                             int *previous_slash)
 {
        int max_len, match_len = 0, match_len_prev = 0, i = 0;
 
-       max_len = len < cache.len ? len : cache.len;
-       while (i < max_len && name[i] == cache.path[i]) {
-               if (name[i] == '/') {
+       max_len = len_a < len_b ? len_a : len_b;
+       while (i < max_len && name_a[i] == name_b[i]) {
+               if (name_a[i] == '/') {
                        match_len_prev = match_len;
                        match_len = i;
                }
                i++;
        }
-       /* Is the cached path string a substring of 'name'? */
-       if (i == cache.len && cache.len < len && name[cache.len] == '/') {
-               match_len_prev = match_len;
-               match_len = cache.len;
-       /* Is 'name' a substring of the cached path string? */
-       } else if ((i == len && len < cache.len && cache.path[len] == '/') ||
-                  (i == len && len == cache.len)) {
+       /*
+        * Is 'name_b' a substring of 'name_a', the other way around,
+        * or is 'name_a' and 'name_b' the exact same string?
+        */
+       if (i >= max_len && ((len_a > len_b && name_a[len_b] == '/') ||
+                            (len_a < len_b && name_b[len_a] == '/') ||
+                            (len_a == len_b))) {
                match_len_prev = match_len;
-               match_len = len;
+               match_len = i;
        }
        *previous_slash = match_len_prev;
        return match_len;
 }
 
-static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func)
+static struct cache_def {
+       char path[PATH_MAX + 1];
+       int len;
+       int flags;
+       int track_flags;
+       int prefix_len_stat_func;
+} cache;
+
+static inline void reset_lstat_cache(void)
 {
        cache.path[0] = '\0';
        cache.len = 0;
        cache.flags = 0;
-       cache.track_flags = track_flags;
-       cache.prefix_len_stat_func = prefix_len_stat_func;
+       /*
+        * The track_flags and prefix_len_stat_func members is only
+        * set by the safeguard rule inside lstat_cache()
+        */
 }
 
 #define FL_DIR      (1 << 0)
@@ -67,7 +70,7 @@ static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func)
  * 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(int len, const char *name,
+static int lstat_cache(const char *name, int len,
                       int track_flags, int prefix_len_stat_func)
 {
        int match_len, last_slash, last_slash_dir, previous_slash;
@@ -77,11 +80,13 @@ static int lstat_cache(int len, const char *name,
        if (cache.track_flags != track_flags ||
            cache.prefix_len_stat_func != prefix_len_stat_func) {
                /*
-                * As a safeguard we clear the cache if the values of
-                * track_flags and/or prefix_len_stat_func does not
-                * match with the last supplied values.
+                * As a safeguard rule we clear the cache if the
+                * values of track_flags and/or prefix_len_stat_func
+                * does not match with the last supplied values.
                 */
-               reset_lstat_cache(track_flags, prefix_len_stat_func);
+               reset_lstat_cache();
+               cache.track_flags = track_flags;
+               cache.prefix_len_stat_func = prefix_len_stat_func;
                match_len = last_slash = 0;
        } else {
                /*
@@ -89,7 +94,8 @@ static int lstat_cache(int len, const char *name,
                 * the 2 "excluding" path types.
                 */
                match_len = last_slash =
-                       longest_match_lstat_cache(len, name, &previous_slash);
+                       longest_path_match(name, len, cache.path, cache.len,
+                                          &previous_slash);
                match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
                if (match_flags && match_len == cache.len)
                        return match_flags;
@@ -153,7 +159,7 @@ static int lstat_cache(int len, const char *name,
                cache.path[last_slash] = '\0';
                cache.len = last_slash;
                cache.flags = save_flags;
-       } else if (track_flags & FL_DIR &&
+       } else if ((track_flags & FL_DIR) &&
                   last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
                /*
                 * We have a separate test for the directory case,
@@ -170,7 +176,7 @@ static int lstat_cache(int len, const char *name,
                cache.len = last_slash_dir;
                cache.flags = FL_DIR;
        } else {
-               reset_lstat_cache(track_flags, prefix_len_stat_func);
+               reset_lstat_cache();
        }
        return ret_flags;
 }
@@ -179,19 +185,19 @@ static int lstat_cache(int len, const char *name,
  * Invalidate the given 'name' from the cache, if 'name' matches
  * completely with the cache.
  */
-void invalidate_lstat_cache(int len, const char *name)
+void invalidate_lstat_cache(const char *name, int len)
 {
        int match_len, previous_slash;
 
-       match_len = longest_match_lstat_cache(len, name, &previous_slash);
+       match_len = longest_path_match(name, len, cache.path, cache.len,
+                                      &previous_slash);
        if (len == match_len) {
                if ((cache.track_flags & FL_DIR) && previous_slash > 0) {
                        cache.path[previous_slash] = '\0';
                        cache.len = previous_slash;
                        cache.flags = FL_DIR;
                } else
-                       reset_lstat_cache(cache.track_flags,
-                                         cache.prefix_len_stat_func);
+                       reset_lstat_cache();
        }
 }
 
@@ -200,7 +206,7 @@ void invalidate_lstat_cache(int len, const char *name)
  */
 void clear_lstat_cache(void)
 {
-       reset_lstat_cache(0, 0);
+       reset_lstat_cache();
 }
 
 #define USE_ONLY_LSTAT  0
@@ -208,9 +214,9 @@ void clear_lstat_cache(void)
 /*
  * Return non-zero if path 'name' has a leading symlink component
  */
-int has_symlink_leading_path(int len, const char *name)
+int has_symlink_leading_path(const char *name, int len)
 {
-       return lstat_cache(len, name,
+       return lstat_cache(name, len,
                           FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
                FL_SYMLINK;
 }
@@ -219,9 +225,9 @@ int has_symlink_leading_path(int len, const char *name)
  * Return non-zero if path 'name' has a leading symlink component or
  * if some leading path component does not exists.
  */
-int has_symlink_or_noent_leading_path(int len, const char *name)
+int has_symlink_or_noent_leading_path(const char *name, int len)
 {
-       return lstat_cache(len, name,
+       return lstat_cache(name, len,
                           FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
                (FL_SYMLINK|FL_NOENT);
 }
@@ -233,9 +239,68 @@ int has_symlink_or_noent_leading_path(int len, const char *name)
  * 'prefix_len', thus we then allow for symlinks in the prefix part as
  * long as those points to real existing directories.
  */
-int has_dirs_only_path(int len, const char *name, int prefix_len)
+int has_dirs_only_path(const char *name, int len, int prefix_len)
 {
-       return lstat_cache(len, name,
+       return lstat_cache(name, len,
                           FL_DIR|FL_FULLPATH, prefix_len) &
                FL_DIR;
 }
+
+static struct removal_def {
+       char path[PATH_MAX];
+       int len;
+} removal;
+
+static void do_remove_scheduled_dirs(int new_len)
+{
+       while (removal.len > new_len) {
+               removal.path[removal.len] = '\0';
+               if (rmdir(removal.path))
+                       break;
+               do {
+                       removal.len--;
+               } while (removal.len > new_len &&
+                        removal.path[removal.len] != '/');
+       }
+       removal.len = new_len;
+       return;
+}
+
+void schedule_dir_for_removal(const char *name, int len)
+{
+       int match_len, last_slash, i, previous_slash;
+
+       match_len = last_slash = i =
+               longest_path_match(name, len, removal.path, removal.len,
+                                  &previous_slash);
+       /* Find last slash inside 'name' */
+       while (i < len) {
+               if (name[i] == '/')
+                       last_slash = i;
+               i++;
+       }
+
+       /*
+        * If we are about to go down the directory tree, we check if
+        * we must first go upwards the tree, such that we then can
+        * remove possible empty directories as we go upwards.
+        */
+       if (match_len < last_slash && match_len < removal.len)
+               do_remove_scheduled_dirs(match_len);
+       /*
+        * If we go deeper down the directory tree, we only need to
+        * save the new path components as we go down.
+        */
+       if (match_len < last_slash) {
+               memcpy(&removal.path[match_len], &name[match_len],
+                      last_slash - match_len);
+               removal.len = last_slash;
+       }
+       return;
+}
+
+void remove_scheduled_dirs(void)
+{
+       do_remove_scheduled_dirs(0);
+       return;
+}
index 9149373032ef8e1403c820cc289728393e5b8aa8..bf816fc8505508c91999175ad6544a67febabb33 100644 (file)
@@ -38,4 +38,7 @@ 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
 
-.PHONY: pre-clean $(T) aggregate-results clean
+valgrind:
+       GIT_TEST_OPTS=--valgrind $(MAKE)
+
+.PHONY: pre-clean $(T) aggregate-results clean valgrind
index f208cf1db972d580d977c356bb589cf6147247a7..d8f6c7de6d27e27a33982e82893baa3bb5ffd7fd 100644 (file)
--- a/t/README
+++ b/t/README
@@ -39,7 +39,8 @@ this:
     * passed all 3 test(s)
 
 You can pass --verbose (or -v), --debug (or -d), and --immediate
-(or -i) command line argument to the test.
+(or -i) command line argument to the test, or by setting GIT_TEST_OPTS
+appropriately before running "make".
 
 --verbose::
        This makes the test more verbose.  Specifically, the
@@ -58,6 +59,21 @@ You can pass --verbose (or -v), --debug (or -d), and --immediate
        This causes additional long-running tests to be run (where
        available), for more exhaustive testing.
 
+--valgrind::
+       Execute all Git binaries with valgrind and exit with status
+       126 on errors (just like regular tests, this will only stop
+       the test script when running under -i).  Valgrind errors
+       go to stderr, so you might want to pass the -v option, too.
+
+       Since it makes no sense to run the tests with --valgrind and
+       not see any output, this option implies --verbose.  For
+       convenience, it also implies --tee.
+
+--tee::
+       In addition to printing the test output to the terminal,
+       write it to files named 't/test-results/$TEST_NAME.out'.
+       As the names depend on the tests' file names, it is safe to
+       run the tests with this option in parallel.
 
 Skipping Tests
 --------------
index cacb273afff1fbddf152bb440451fa141589cf33..396b9653a3ad80490cf360c86a910edff58862a2 100644 (file)
@@ -114,7 +114,10 @@ test_expect_success \
 test_expect_success \
     'some edit' \
     'mv file file.orig &&
-    sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" < file.orig > file &&
+    {
+       cat file.orig &&
+       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"'
 
index 67c431d4ebbb32fe8d88a83104485b38d746fa62..773d47cf3cb704d7976185738a2b9840c159a33c 100644 (file)
@@ -5,9 +5,12 @@ git_svn_id=git""-svn-id
 
 if test -n "$NO_SVN_TESTS"
 then
-       test_expect_success 'skipping git svn tests, NO_SVN_TESTS defined' :
+       say 'skipping git svn tests, NO_SVN_TESTS defined'
+       test_done
+fi
+if ! test_have_prereq PERL; then
+       say 'skipping git svn tests, perl not available'
        test_done
-       exit
 fi
 
 GIT_DIR=$PWD/.git
@@ -17,9 +20,8 @@ SVN_TREE=$GIT_SVN_DIR/svn-tree
 svn >/dev/null 2>&1
 if test $? -ne 1
 then
-    test_expect_success 'skipping git svn tests, svn not found' :
+    say 'skipping git svn tests, svn not found'
     test_done
-    exit
 fi
 
 svnrepo=$PWD/svnrepo
@@ -41,9 +43,8 @@ then
        else
                err='Perl SVN libraries not found or unusable, skipping test'
        fi
-       test_expect_success "$err" :
+       say "$err"
        test_done
-       exit
 fi
 
 rawsvnrepo="$svnrepo"
@@ -144,7 +145,6 @@ require_svnserve () {
     then
         say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)'
         test_done
-        exit
     fi
 }
 
index 86cdebc727c4964899a71366151a7782558153c7..cde659d14ac599e78bb4cd12a2ad88eab179549e 100644 (file)
@@ -8,7 +8,6 @@ then
        say "skipping test, network testing disabled by default"
        say "(define GIT_TEST_HTTPD to enable)"
        test_done
-       exit
 fi
 
 HTTPD_PARA=""
@@ -36,7 +35,6 @@ if ! test -x "$LIB_HTTPD_PATH"
 then
        say "skipping test, no web server found at '$LIB_HTTPD_PATH'"
        test_done
-       exit
 fi
 
 HTTPD_VERSION=`$LIB_HTTPD_PATH -v | \
@@ -50,7 +48,6 @@ then
                then
                        say "skipping test, at least Apache version 2 is required"
                        test_done
-                       exit
                fi
 
                LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH"
@@ -94,13 +91,18 @@ prepare_httpd() {
 }
 
 start_httpd() {
-       prepare_httpd
+       prepare_httpd >&3 2>&4
 
        trap 'stop_httpd; die' EXIT
 
        "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
                -f "$TEST_PATH/apache.conf" $HTTPD_PARA \
-               -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start
+               -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
+               >&3 2>&4
+       if ! test $? = 0; then
+               say "skipping test, web server setup failed"
+               test_done
+       fi
 }
 
 stop_httpd() {
index af6e5e1d6ae503dfadc94feef4d9cb4b623163ec..21aa42f1c613538ba26c0b613cc0d1b001bc52b9 100644 (file)
@@ -1,15 +1,13 @@
 ServerName dummy
+LockFile accept.lock
 PidFile httpd.pid
 DocumentRoot www
 LogFormat "%h %l %u %t \"%r\" %>s %b" common
 CustomLog access.log common
 ErrorLog error.log
-
-<IfDefine Darwin>
+<IfModule !mod_log_config.c>
        LoadModule log_config_module modules/mod_log_config.so
-       LockFile accept.lock
-       PidFile httpd.pid
-</IfDefine>
+</IfModule>
 
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
index 70df15cbd8339b552a56a95ca0c0893138550201..f4ca4fc85c6b52a2ba919528284f2b668e6bd3d2 100755 (executable)
@@ -57,6 +57,21 @@ test_expect_failure 'pretend we have a known breakage' '
 test_expect_failure 'pretend we have fixed a known breakage' '
     :
 '
+test_set_prereq HAVEIT
+haveit=no
+test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
+    test_have_prereq HAVEIT &&
+    haveit=yes
+'
+donthaveit=yes
+test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' '
+    donthaveit=no
+'
+if test $haveit$donthaveit != yesyes
+then
+       say "bug in test framework: prerequisite tags do not work reliably"
+       exit 1
+fi
 
 ################################################################
 # Basics of the basics
@@ -100,12 +115,31 @@ test_expect_success \
     'test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904'
 
 # Various types of objects
+# Some filesystems do not support symblic links; on such systems
+# some expected values are different
 mkdir path2 path3 path3/subp3
-for p in path0 path2/file2 path3/file3 path3/subp3/file3
+paths='path0 path2/file2 path3/file3 path3/subp3/file3'
+for p in $paths
 do
     echo "hello $p" >$p
-    ln -s "hello $p" ${p}sym
 done
+if test_have_prereq SYMLINKS
+then
+       for p in $paths
+       do
+               ln -s "hello $p" ${p}sym
+       done
+       expectfilter=cat
+       expectedtree=087704a96baf1c2d1c869a8b084481e121c88b5b
+       expectedptree1=21ae8269cacbe57ae09138dcc3a2887f904d02b3
+       expectedptree2=3c5e5399f3a333eddecce7a9b9465b63f65f51e2
+else
+       expectfilter='grep -v sym'
+       expectedtree=8e18edf7d7edcf4371a3ac6ae5f07c2641db7c46
+       expectedptree1=cfb8591b2f65de8b8cc1020cd7d9e67e7793b325
+       expectedptree2=ce580448f0148b985a513b693fdf7d802cacb44f
+fi
+
 test_expect_success \
     'adding various types of objects with git update-index --add.' \
     'find path* ! -type d -print | xargs git update-index --add'
@@ -115,7 +149,7 @@ test_expect_success \
     'showing stage with git ls-files --stage' \
     'git ls-files --stage >current'
 
-cat >expected <<\EOF
+$expectfilter >expected <<\EOF
 100644 f87290f8eb2cbbea7857214459a0739927eab154 0      path0
 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0      path0sym
 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0      path2/file2
@@ -127,14 +161,14 @@ cat >expected <<\EOF
 EOF
 test_expect_success \
     'validate git ls-files output for a known tree.' \
-    'diff current expected'
+    'test_cmp expected current'
 
 test_expect_success \
     'writing tree out with git write-tree.' \
     'tree=$(git write-tree)'
 test_expect_success \
     'validate object ID for a known tree.' \
-    'test "$tree" = 087704a96baf1c2d1c869a8b084481e121c88b5b'
+    'test "$tree" = "$expectedtree"'
 
 test_expect_success \
     'showing tree with git ls-tree' \
@@ -145,16 +179,16 @@ cat >expected <<\EOF
 040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe   path2
 040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3   path3
 EOF
-test_expect_success \
+test_expect_success SYMLINKS \
     'git ls-tree output for a known tree.' \
-    'diff current expected'
+    'test_cmp expected current'
 
 # This changed in ls-tree pathspec change -- recursive does
 # not show tree nodes anymore.
 test_expect_success \
     'showing tree with git ls-tree -r' \
     'git ls-tree -r $tree >current'
-cat >expected <<\EOF
+$expectfilter >expected <<\EOF
 100644 blob f87290f8eb2cbbea7857214459a0739927eab154   path0
 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01   path0sym
 100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7   path2/file2
@@ -166,7 +200,7 @@ cat >expected <<\EOF
 EOF
 test_expect_success \
     'git ls-tree -r output for a known tree.' \
-    'diff current expected'
+    'test_cmp expected current'
 
 # But with -r -t we can have both.
 test_expect_success \
@@ -185,23 +219,23 @@ cat >expected <<\EOF
 100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f   path3/subp3/file3
 120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c   path3/subp3/file3sym
 EOF
-test_expect_success \
+test_expect_success SYMLINKS \
     'git ls-tree -r output for a known tree.' \
-    'diff current expected'
+    'test_cmp expected current'
 
 test_expect_success \
     'writing partial tree out with git write-tree --prefix.' \
     'ptree=$(git write-tree --prefix=path3)'
 test_expect_success \
     'validate object ID for a known tree.' \
-    'test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3'
+    'test "$ptree" = "$expectedptree1"'
 
 test_expect_success \
     'writing partial tree out with git write-tree --prefix.' \
     'ptree=$(git write-tree --prefix=path3/subp3)'
 test_expect_success \
     'validate object ID for a known tree.' \
-    'test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2'
+    'test "$ptree" = "$expectedptree2"'
 
 cat >badobjects <<EOF
 100644 blob 1000000000000000000000000000000000000000   dir/file1
@@ -234,7 +268,7 @@ test_expect_success \
      newtree=$(git write-tree) &&
      test "$newtree" = "$tree"'
 
-cat >expected <<\EOF
+$expectfilter >expected <<\EOF
 :100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M     path0
 :120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M     path0sym
 :100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M     path2/file2
@@ -257,7 +291,7 @@ test_expect_success \
     'git diff-files >current && cmp -s current /dev/null'
 
 ################################################################
-P=087704a96baf1c2d1c869a8b084481e121c88b5b
+P=$expectedtree
 test_expect_success \
     'git commit-tree records the correct tree in a commit.' \
     'commit0=$(echo NO | git commit-tree $P) &&
@@ -293,7 +327,7 @@ test_expect_success 'update-index D/F conflict' '
        test $numpath0 = 1
 '
 
-test_expect_success 'absolute path works as expected' '
+test_expect_success SYMLINKS 'absolute path works as expected' '
        mkdir first &&
        ln -s ../.git first/.git &&
        mkdir second &&
index 63e1217e7162435c3da8ec7984b5f6a53b3a10e2..2342ac5788a9976b591cb78593279f092d1dc2f6 100755 (executable)
@@ -15,7 +15,7 @@ test_expect_success setup '
 
 '
 
-test_expect_success 'write-tree should notice unwritable repository' '
+test_expect_success POSIXPERM 'write-tree should notice unwritable repository' '
 
        (
                chmod a-w .git/objects .git/objects/?? &&
@@ -27,7 +27,7 @@ test_expect_success 'write-tree should notice unwritable repository' '
 
 '
 
-test_expect_success 'commit should notice unwritable repository' '
+test_expect_success POSIXPERM 'commit should notice unwritable repository' '
 
        (
                chmod a-w .git/objects .git/objects/?? &&
@@ -39,7 +39,7 @@ test_expect_success 'commit should notice unwritable repository' '
 
 '
 
-test_expect_success 'update-index should notice unwritable repository' '
+test_expect_success POSIXPERM 'update-index should notice unwritable repository' '
 
        (
                echo 6O >file &&
@@ -52,7 +52,7 @@ test_expect_success 'update-index should notice unwritable repository' '
 
 '
 
-test_expect_success 'add should notice unwritable repository' '
+test_expect_success POSIXPERM 'add should notice unwritable repository' '
 
        (
                echo b >file &&
index 1be7446d8d9f8a46b463f2474a8c25bdd33044d2..4e72b53140bd35db87a6c873eda9e75e896e1cdd 100755 (executable)
@@ -429,6 +429,37 @@ test_expect_success 'in-tree .gitattributes (4)' '
        }
 '
 
+test_expect_success 'checkout with existing .gitattributes' '
+
+       git config core.autocrlf true &&
+       git config --unset core.safecrlf &&
+       echo ".file2 -crlfQ" | q_to_cr >> .gitattributes &&
+       git add .gitattributes &&
+       git commit -m initial &&
+       echo ".file -crlfQ" | q_to_cr >> .gitattributes &&
+       echo "contents" > .file &&
+       git add .gitattributes .file &&
+       git commit -m second &&
+
+       git checkout master~1 &&
+       git checkout master &&
+       test "$(git diff-files --raw)" = ""
+
+'
+
+test_expect_success 'checkout when deleting .gitattributes' '
+
+       git rm .gitattributes &&
+       echo "contentsQ" | q_to_cr > .file2 &&
+       git add .file2 &&
+       git commit -m third
+
+       git checkout master~1 &&
+       git checkout master &&
+       remove_cr .file2 >/dev/null
+
+'
+
 test_expect_success 'invalid .gitattributes (must not crash)' '
 
        echo "three +crlf" >>.gitattributes &&
index e5330395fc866b2a913b4b20ecb66f1f2a3bbaff..c7d0324374e9df5131a58a9ae0cadf7ee4dc03e7 100755 (executable)
@@ -28,12 +28,12 @@ test_expect_success 'tar archive' '
 
 "$UNZIP" -v >/dev/null 2>&1
 if [ $? -eq 127 ]; then
-       echo "Skipping ZIP test, because unzip was not found"
-       test_done
-       exit
+       say "Skipping ZIP test, because unzip was not found"
+else
+       test_set_prereq UNZIP
 fi
 
-test_expect_success 'zip archive' '
+test_expect_success UNZIP 'zip archive' '
 
        git archive --format=zip HEAD >test.zip &&
 
index 7edf49db3c37982a6d599a39b98ce60ceeb0039b..89282ccf7a1a73d4b5ee085c4236b1204dc502c8 100755 (executable)
@@ -8,7 +8,9 @@ auml=`printf '\xc3\xa4'`
 aumlcdiar=`printf '\x61\xcc\x88'`
 
 case_insensitive=
-test_expect_success 'see if we expect ' '
+unibad=
+no_symlinks=
+test_expect_success 'see what we expect' '
 
        test_case=test_expect_success
        test_unicode=test_expect_success
@@ -19,7 +21,6 @@ test_expect_success 'see if we expect ' '
        then
                test_case=test_expect_failure
                case_insensitive=t
-               say "will test on a case insensitive filesystem"
        fi &&
        rm -fr junk &&
        mkdir junk &&
@@ -27,13 +28,26 @@ test_expect_success 'see if we expect ' '
        case "$(cd junk && echo *)" in
        "$aumlcdiar")
                test_unicode=test_expect_failure
-               say "will test on a unicode corrupting filesystem"
+               unibad=t
                ;;
        *)      ;;
        esac &&
-       rm -fr junk
+       rm -fr junk &&
+       {
+               ln -s x y 2> /dev/null &&
+               test -h y 2> /dev/null ||
+               no_symlinks=1
+               rm -f y
+       }
 '
 
+test "$case_insensitive" &&
+       say "will test on a case insensitive filesystem"
+test "$unibad" &&
+       say "will test on a unicode corrupting filesystem"
+test "$no_symlinks" &&
+       say "will test on a filesystem lacking symbolic links"
+
 if test "$case_insensitive"
 then
 test_expect_success "detection of case insensitive filesystem during repo init" '
@@ -48,6 +62,21 @@ test_expect_success "detection of case insensitive filesystem during repo init"
 '
 fi
 
+if test "$no_symlinks"
+then
+test_expect_success "detection of filesystem w/o symlink support during repo init" '
+
+       v=$(git config --bool core.symlinks) &&
+       test "$v" = false
+'
+else
+test_expect_success "detection of filesystem w/o symlink support during repo init" '
+
+       test_must_fail git config --bool core.symlinks ||
+       test "$(git config --bool core.symlinks)" = true
+'
+fi
+
 test_expect_success "setup case tests" '
 
        git config core.ignorecase true &&
index b29c37a5a4de42239474c4fdf87c86c6f27b6ade..0c6ff567a1d47f52492dd89bd098b25bae737bad 100755 (executable)
@@ -4,7 +4,7 @@ test_description='update-index and add refuse to add beyond symlinks'
 
 . ./test-lib.sh
 
-test_expect_success setup '
+test_expect_success SYMLINKS setup '
        >a &&
        mkdir b &&
        ln -s b c &&
@@ -12,12 +12,12 @@ test_expect_success setup '
        git update-index --add a b/d
 '
 
-test_expect_success 'update-index --add beyond symlinks' '
+test_expect_success SYMLINKS 'update-index --add beyond symlinks' '
        test_must_fail git update-index --add c/d &&
        ! ( git ls-files | grep c/d )
 '
 
-test_expect_success 'add beyond symlinks' '
+test_expect_success SYMLINKS 'add beyond symlinks' '
        test_must_fail git add c/d &&
        ! ( git ls-files | grep c/d )
 '
index 8336114f9820aa5972d38efe316de7d9e39bcc5c..53cf1f8dc4acad959fdec359b738387aeb126b1e 100755 (executable)
@@ -7,41 +7,91 @@ test_description='Test various path utilities'
 
 . ./test-lib.sh
 
-norm_abs() {
-       test_expect_success "normalize absolute: $1 => $2" \
+norm_path() {
+       test_expect_success $3 "normalize path: $1 => $2" \
        "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
 }
 
+# On Windows, we are using MSYS's bash, which mangles the paths.
+# Absolute paths are anchored at the MSYS installation directory,
+# which means that the path / accounts for this many characters:
+rootoff=$(test-path-utils normalize_path_copy / | wc -c)
+# Account for the trailing LF:
+if test $rootoff = 2; then
+       rootoff=        # we are on Unix
+else
+       rootoff=$(($rootoff-1))
+fi
+
 ancestor() {
-       test_expect_success "longest ancestor: $1 $2 => $3" \
-       "test \"\$(test-path-utils longest_ancestor_length '$1' '$2')\" = '$3'"
+       # We do some math with the expected ancestor length.
+       expected=$3
+       if test -n "$rootoff" && test "x$expected" != x-1; then
+               expected=$(($expected+$rootoff))
+       fi
+       test_expect_success "longest ancestor: $1 $2 => $expected" \
+       "actual=\$(test-path-utils longest_ancestor_length '$1' '$2') &&
+        test \"\$actual\" = '$expected'"
 }
 
-norm_abs "" ""
-norm_abs / /
-norm_abs // /
-norm_abs /// /
-norm_abs /. /
-norm_abs /./ /
-norm_abs /./.. ++failed++
-norm_abs /../. ++failed++
-norm_abs /./../.// ++failed++
-norm_abs /dir/.. /
-norm_abs /dir/sub/../.. /
-norm_abs /dir/sub/../../.. ++failed++
-norm_abs /dir /dir
-norm_abs /dir// /dir/
-norm_abs /./dir /dir
-norm_abs /dir/. /dir/
-norm_abs /dir///./ /dir/
-norm_abs /dir//sub/.. /dir/
-norm_abs /dir/sub/../ /dir/
-norm_abs //dir/sub/../. /dir/
-norm_abs /dir/s1/../s2/ /dir/s2/
-norm_abs /d1/s1///s2/..//../s3/ /d1/s3/
-norm_abs /d1/s1//../s2/../../d2 /d2
-norm_abs /d1/.../d2 /d1/.../d2
-norm_abs /d1/..././../d2 /d1/d2
+# Absolute path tests must be skipped on Windows because due to path mangling
+# the test program never sees a POSIX-style absolute path
+case $(uname -s) in
+*MINGW*)
+       ;;
+*)
+       test_set_prereq POSIX
+       ;;
+esac
+
+norm_path "" ""
+norm_path . ""
+norm_path ./ ""
+norm_path ./. ""
+norm_path ./.. ++failed++
+norm_path ../. ++failed++
+norm_path ./../.// ++failed++
+norm_path dir/.. ""
+norm_path dir/sub/../.. ""
+norm_path dir/sub/../../.. ++failed++
+norm_path dir dir
+norm_path dir// dir/
+norm_path ./dir dir
+norm_path dir/. dir/
+norm_path dir///./ dir/
+norm_path dir//sub/.. dir/
+norm_path dir/sub/../ dir/
+norm_path dir/sub/../. dir/
+norm_path dir/s1/../s2/ dir/s2/
+norm_path d1/s1///s2/..//../s3/ d1/s3/
+norm_path d1/s1//../s2/../../d2 d2
+norm_path d1/.../d2 d1/.../d2
+norm_path d1/..././../d2 d1/d2
+
+norm_path / / POSIX
+norm_path // / POSIX
+norm_path /// / POSIX
+norm_path /. / POSIX
+norm_path /./ / POSIX
+norm_path /./.. ++failed++ POSIX
+norm_path /../. ++failed++ POSIX
+norm_path /./../.// ++failed++ POSIX
+norm_path /dir/.. / POSIX
+norm_path /dir/sub/../.. / POSIX
+norm_path /dir/sub/../../.. ++failed++ POSIX
+norm_path /dir /dir POSIX
+norm_path /dir// /dir/ POSIX
+norm_path /./dir /dir POSIX
+norm_path /dir/. /dir/ POSIX
+norm_path /dir///./ /dir/ POSIX
+norm_path /dir//sub/.. /dir/ POSIX
+norm_path /dir/sub/../ /dir/ POSIX
+norm_path //dir/sub/../. /dir/ POSIX
+norm_path /dir/s1/../s2/ /dir/s2/ POSIX
+norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX
+norm_path /d1/s1//../s2/../../d2 /d2 POSIX
+norm_path /d1/.../d2 /d1/.../d2 POSIX
+norm_path /d1/..././../d2 /d1/d2 POSIX
 
 ancestor / "" -1
 ancestor / / -1
@@ -80,10 +130,10 @@ ancestor /foo/bar /:/foo:/bar/ 4
 ancestor /foo/bar /foo:/:/bar/ 4
 ancestor /foo/bar /:/bar/:/fo 0
 ancestor /foo/bar /:/bar/ 0
-ancestor /foo/bar :://foo/. 4
-ancestor /foo/bar :://foo/.:: 4
-ancestor /foo/bar //foo/./::/bar 4
-ancestor /foo/bar ::/bar -1
+ancestor /foo/bar .:/foo/. 4
+ancestor /foo/bar .:/foo/.:.: 4
+ancestor /foo/bar /foo/./:.:/bar 4
+ancestor /foo/bar .:/bar -1
 
 test_expect_success 'strip_path_suffix' '
        test c:/msysgit = $(test-path-utils strip_path_suffix \
index 570d3729bd2312a8d9cf90f3d2e1121a58f43de6..f19b4a2a4afa89fd1242d1acccb1e999e6a88c6d 100755 (executable)
@@ -157,7 +157,7 @@ test_expect_success '3-way not overwriting local changes (their side)' '
 
 '
 
-test_expect_success 'funny symlink in work tree' '
+test_expect_success SYMLINKS 'funny symlink in work tree' '
 
        git reset --hard &&
        git checkout -b sym-b side-b &&
@@ -177,7 +177,7 @@ test_expect_success 'funny symlink in work tree' '
 
 '
 
-test_expect_success 'funny symlink in work tree, un-unlink-able' '
+test_expect_success SYMLINKS 'funny symlink in work tree, un-unlink-able' '
 
        rm -fr a b &&
        git reset --hard &&
@@ -189,7 +189,7 @@ test_expect_success 'funny symlink in work tree, un-unlink-able' '
 '
 
 # clean-up from the above test
-chmod a+w a
+chmod a+w a 2>/dev/null
 rm -fr a b
 
 test_expect_success 'D/F setup' '
index fc386ba033ac165a5f4a9fca0c6c6f5db49a314e..210e594f6f3c83cc1b0c423a0f692380ff57176b 100755 (executable)
@@ -126,7 +126,7 @@ test_expect_success 'no file/rev ambiguity check inside a bare repo' '
        cd foo.git && git show -s HEAD
 '
 
-test_expect_success 'detection should not be fooled by a symlink' '
+test_expect_success SYMLINKS 'detection should not be fooled by a symlink' '
        cd "$HERE" &&
        rm -fr foo.git &&
        git clone -s .git another &&
index 7f7fc36734d96de96801c59af41024db97dc614d..c4414ff576fc03b3234c532bfdcd0f2c8eca9c09 100755 (executable)
@@ -40,6 +40,6 @@ test_expect_success \
 
 test_expect_success \
     'compare commit' \
-    'diff expected commit'
+    'test_cmp expected commit'
 
 test_done
index 3c06842d99a68ea37ce82546b4bfa0cd487b87d7..91cbd551d8b8031b91437238e281591268a0268f 100755 (executable)
@@ -118,7 +118,14 @@ EOF
 
 test_expect_success 'multiple unset is correct' 'cmp .git/config expect'
 
-mv .git/config2 .git/config
+cp .git/config2 .git/config
+
+test_expect_success '--replace-all missing value' '
+       test_must_fail git config --replace-all beta.haha &&
+       test_cmp .git/config2 .git/config
+'
+
+rm .git/config2
 
 test_expect_success '--replace-all' \
        'git config --replace-all beta.haha gamma'
@@ -726,7 +733,12 @@ echo >>result
 
 test_expect_success '--null --get-regexp' 'cmp result expect'
 
-test_expect_success 'symlinked configuration' '
+test_expect_success 'inner whitespace kept verbatim' '
+       git config section.val "foo       bar" &&
+       test "z$(git config section.val)" = "zfoo         bar"
+'
+
+test_expect_success SYMLINKS 'symlinked configuration' '
 
        ln -s notyet myconfig &&
        GIT_CONFIG=myconfig git config test.frotz nitfol &&
index 3fddc9ee781ba9fb5d189bbc5ec0b900b159723a..de42d21c922045415abedf3c81163682d0754eb5 100755 (executable)
@@ -26,7 +26,7 @@ modebits () {
 
 for u in 002 022
 do
-       test_expect_success "shared=1 does not clear bits preset by umask $u" '
+       test_expect_success POSIXPERM "shared=1 does not clear bits preset by umask $u" '
                mkdir sub && (
                        cd sub &&
                        umask $u &&
@@ -54,7 +54,7 @@ test_expect_success 'shared=all' '
        test 2 = $(git config core.sharedrepository)
 '
 
-test_expect_success 'update-server-info honors core.sharedRepository' '
+test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' '
        : > a1 &&
        git add a1 &&
        test_tick &&
@@ -85,7 +85,7 @@ do
        git config core.sharedrepository "$u" &&
        umask 0277 &&
 
-       test_expect_success "shared = $u ($y) ro" '
+       test_expect_success POSIXPERM "shared = $u ($y) ro" '
 
                rm -f .git/info/refs &&
                git update-server-info &&
@@ -97,7 +97,7 @@ do
        '
 
        umask 077 &&
-       test_expect_success "shared = $u ($x) rw" '
+       test_expect_success POSIXPERM "shared = $u ($x) rw" '
 
                rm -f .git/info/refs &&
                git update-server-info &&
@@ -111,7 +111,7 @@ do
 
 done
 
-test_expect_success 'git reflog expire honors core.sharedRepository' '
+test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
        git config core.sharedRepository group &&
        git reflog expire --all &&
        actual="$(ls -l .git/logs/refs/heads/master)" &&
@@ -126,7 +126,7 @@ test_expect_success 'git reflog expire honors core.sharedRepository' '
        esac
 '
 
-test_expect_success 'forced modes' '
+test_expect_success POSIXPERM 'forced modes' '
        mkdir -p templates/hooks &&
        echo update-server-info >templates/hooks/post-update &&
        chmod +x templates/hooks/post-update &&
index bd589268fcf459f07ff6c53e43d41e66fe1e7903..54ba3df95f66ecc060adaec6846877c793016aa1 100755 (executable)
@@ -137,7 +137,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
 EOF
 test_expect_success \
        "verifying $m's log" \
-       "diff expect .git/logs/$m"
+       "test_cmp expect .git/logs/$m"
 rm -rf .git/$m .git/logs expect
 
 test_expect_success \
@@ -168,7 +168,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
 EOF
 test_expect_success \
        "verifying $m's log" \
-       'diff expect .git/logs/$m'
+       'test_cmp expect .git/logs/$m'
 rm -f .git/$m .git/logs/$m expect
 
 git update-ref $m $D
@@ -272,7 +272,7 @@ $h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000      c
 EOF
 test_expect_success \
        'git commit logged updates' \
-       "diff expect .git/logs/$m"
+       "test_cmp expect .git/logs/$m"
 unset h_TEST h_OTHER h_FIXED h_MERGED
 
 test_expect_success \
index 5b24f05573221afb3dc472e78a443ba718db3c60..80af6b9b7ea50652dff6804b589d134207e9258f 100755 (executable)
@@ -70,9 +70,7 @@ test_expect_success setup '
        E=`git rev-parse --verify HEAD:A/B/E` &&
        check_fsck &&
 
-       chmod +x C &&
-       ( test "`git config --bool core.filemode`" != false ||
-         echo executable >>C ) &&
+       test_chmod +x C &&
        git add C &&
        test_tick && git commit -m dragon &&
        L=`git rev-parse --verify HEAD` &&
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
new file mode 100755 (executable)
index 0000000..c18ed8e
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='Test reflog display routines'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo content >file &&
+       git add file &&
+       test_tick &&
+       git commit -m one
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{0} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'log -g shows reflog headers' '
+       git log -g -1 >tmp &&
+       grep ^Reflog <tmp >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{0}: commit (initial): one
+EOF
+test_expect_success 'oneline reflog format' '
+       git log -g -1 --oneline >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{Thu Apr 7 15:13:13 2005 -0700} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'using @{now} syntax shows reflog date (multiline)' '
+       git log -g -1 HEAD@{now} >tmp &&
+       grep ^Reflog <tmp >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{Thu Apr 7 15:13:13 2005 -0700}: commit (initial): one
+EOF
+test_expect_success 'using @{now} syntax shows reflog date (oneline)' '
+       git log -g -1 --oneline HEAD@{now} >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{1112911993 -0700} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'using --date= shows reflog date (multiline)' '
+       git log -g -1 --date=raw >tmp &&
+       grep ^Reflog <tmp >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{1112911993 -0700}: commit (initial): one
+EOF
+test_expect_success 'using --date= shows reflog date (oneline)' '
+       git log -g -1 --oneline --date=raw >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 4597af0eb6c5c0508dd40306ce2790fc4f6afb0c..a22632f483e2068642aa09a5bc28fde01d2294b9 100755 (executable)
@@ -28,4 +28,71 @@ test_expect_success 'loose objects borrowed from alternate are not missing' '
        )
 '
 
+# 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 '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)"
+       mv .git/objects/$old .git/objects/$new &&
+       git update-index --add --cacheinfo 100644 $sha foo &&
+       tree=$(git write-tree) &&
+       cmt=$(echo bogus | git commit-tree $tree) &&
+       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_expect_success 'branch pointing to non-commit' '
+       git rev-parse HEAD^{tree} > .git/refs/heads/invalid &&
+       git fsck 2>out &&
+       grep "not a commit" out &&
+       git update-ref -d refs/heads/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
+
+test_expect_failure 'tag pointing to nonexistent' '
+       tag=$(git hash-object -w --stdin < invalid-tag) &&
+       echo $tag > .git/refs/tags/invalid &&
+       git fsck --tags 2>out &&
+       cat out &&
+       grep "could not load tagged object" out &&
+       rm .git/refs/tags/invalid
+'
+
+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_failure 'tag pointing to something else than its type' '
+       tag=$(git hash-object -w --stdin < wrong-tag) &&
+       echo $tag > .git/refs/tags/wrong &&
+       git fsck --tags 2>out &&
+       cat out &&
+       grep "some sane error message" out &&
+       rm .git/refs/tags/wrong
+'
+
+
+
 test_done
index e377d48902cd5fd539a0ce4cb1fb32ceb8732632..df5ad8c686a959c9b03355c1ebc325f3c755ed46 100755 (executable)
@@ -13,7 +13,7 @@ test_fail() {
        "git rev-parse --show-prefix"
 }
 
-TRASH_ROOT="$(pwd)"
+TRASH_ROOT="$PWD"
 ROOT_PARENT=$(dirname "$TRASH_ROOT")
 
 
index ef007532b15108d72445f7c95a2906a3039fbbbb..98aa73e8239355eba098253edfd156d4ea254be2 100755 (executable)
@@ -59,10 +59,10 @@ test_expect_success \
     'git read-tree -m $tree1 && git checkout-index -f -a'
 test_debug 'show_files $tree1'
 
-ln -s path0 path1
-test_expect_success \
+test_expect_success SYMLINKS \
     'git update-index --add a symlink.' \
-    'git update-index --add path1'
+    'ln -s path0 path1 &&
+     git update-index --add path1'
 test_expect_success \
     'writing tree out with git write-tree' \
     'tree3=$(git write-tree)'
index 71894b37439bd1b9c72194cbbabe37680d2f9743..02a4fc5d36a08d1046b9384c799f697640da0c4e 100755 (executable)
@@ -19,7 +19,7 @@ test_expect_success \
     echo rezrov >path1/file1 &&
     git update-index --add path0 path1/file1'
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'have symlink in place where dir is expected.' \
     'rm -fr path0 path1 &&
      mkdir path2 &&
@@ -59,7 +59,7 @@ test_expect_success \
      test ! -f path1/file1'
 
 # Linus fix #1
-test_expect_success \
+test_expect_success SYMLINKS \
     'use --prefix=tmp/orary/ where tmp is a symlink' \
     'rm -fr path0 path1 path2 tmp* &&
      mkdir tmp1 tmp1/orary &&
@@ -71,7 +71,7 @@ test_expect_success \
      test -h tmp'
 
 # Linus fix #2
-test_expect_success \
+test_expect_success SYMLINKS \
     'use --prefix=tmp/orary- where tmp is a symlink' \
     'rm -fr path0 path1 path2 tmp* &&
      mkdir tmp1 &&
@@ -82,7 +82,7 @@ test_expect_success \
      test -h tmp'
 
 # Linus fix #3
-test_expect_success \
+test_expect_success SYMLINKS \
     'use --prefix=tmp- where tmp-path1 is a symlink' \
     'rm -fr path0 path1 path2 tmp* &&
      mkdir tmp1 &&
index 39133b8c7a4b56cb7273cec607ea89081a426eff..36cca14d957f85733174d6ce514e22acfff3b1c9 100755 (executable)
@@ -194,7 +194,7 @@ test_expect_success \
  test $(cat ../$s1) = tree1asubdir/path5)
 )'
 
-test_expect_success \
+test_expect_success SYMLINKS \
 'checkout --temp symlink' '
 rm -f path* .merge_* out .git/index &&
 ln -s b a &&
index 0526fce163fc13273daf035a0920a6b53a3acefb..20f33436d00077b64dcc855fc263cd5d0efcca38 100755 (executable)
@@ -6,6 +6,12 @@ test_description='git checkout to switch between branches with symlink<->dir'
 
 . ./test-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+       say "symbolic links not supported - skipping tests"
+       test_done
+fi
+
 test_expect_success setup '
 
        mkdir frotz &&
index 6ef2dcfd8afece86aaf6345630179af179eb2ed9..2df3fdde8bf665a2b531dd367b70a7a767ee3dbc 100755 (executable)
@@ -26,7 +26,12 @@ All of the attempts should fail.
 
 mkdir path2 path3
 date >path0
-ln -s xyzzy path1
+if test_have_prereq SYMLINKS
+then
+       ln -s xyzzy path1
+else
+       date > path1
+fi
 date >path2/file2
 date >path3/file3
 
@@ -38,7 +43,12 @@ rm -fr path?
 
 mkdir path0 path1
 date >path2
-ln -s frotz path3
+if test_have_prereq SYMLINKS
+then
+       ln -s frotz path3
+else
+       date > path3
+fi
 date >path0/file0
 date >path1/file1
 
index b2ddf5ace3581bc2a7056c61b9d43499b2657b65..912075063b9946d38a9ff72621cb80bcf2c05399 100755 (executable)
@@ -80,7 +80,7 @@ test_expect_success 'change gets noticed' '
 
 '
 
-test_expect_success 'replace a file with a symlink' '
+test_expect_success SYMLINKS 'replace a file with a symlink' '
 
        rm foo &&
        ln -s top foo &&
@@ -150,7 +150,7 @@ test_expect_success 'add -u resolves unmerged paths' '
        echo 2 >path3 &&
        echo 2 >path5 &&
        git add -u &&
-       git ls-files -s "path?" >actual &&
+       git ls-files -s path1 path2 path3 path4 path5 path6 >actual &&
        {
                echo "100644 $three 0   path1"
                echo "100644 $one 1     path3"
index d24c7d9e5fce0e9c0f8ef5576dab86ffdbc11331..2e8f70245204bd4dc78e67f227e86838e1cdad5b 100755 (executable)
@@ -11,7 +11,13 @@ test_expect_success setup '
        _empty=$(git hash-object --stdin <xyzzy) &&
        >yomin &&
        >caskly &&
-       ln -s frotz nitfol &&
+       if test_have_prereq SYMLINKS; then
+               ln -s frotz nitfol &&
+               T_letter=T
+       else
+               printf %s frotz > nitfol &&
+               T_letter=M
+       fi &&
        mkdir rezrov &&
        >rezrov/bozbar &&
        git add caskly xyzzy yomin nitfol rezrov/bozbar &&
@@ -29,7 +35,11 @@ test_expect_success modify '
        >nitfol &&
        # rezrov/bozbar disappears
        rm -fr rezrov &&
-       ln -s xyzzy rezrov &&
+       if test_have_prereq SYMLINKS; then
+               ln -s xyzzy rezrov
+       else
+               printf %s xyzzy > rezrov
+       fi &&
        # xyzzy disappears (not a submodule)
        mkdir xyzzy &&
        echo gnusto >xyzzy/bozbar &&
@@ -71,7 +81,7 @@ test_expect_success modify '
                                s/blob/000000/
                        }
                        /       nitfol/{
-                               s/      nitfol/ $_z40 T&/
+                               s/      nitfol/ $_z40 $T_letter&/
                                s/blob/100644/
                        }
                        /       rezrov.bozbar/{
index 293dc353b1601e137f86dfe441fff57b96c92753..3b01ad2e4de152d23bbe8267d3b1a5cafd615e88 100755 (executable)
@@ -5,7 +5,7 @@ test_description='cd_to_toplevel'
 . ./test-lib.sh
 
 test_cd_to_toplevel () {
-       test_expect_success "$2" '
+       test_expect_success $3 "$2" '
                (
                        cd '"'$1'"' &&
                        . git-sh-setup &&
@@ -24,14 +24,14 @@ test_cd_to_toplevel repo 'at physical root'
 
 test_cd_to_toplevel repo/sub/dir 'at physical subdir'
 
-ln -s repo symrepo
-test_cd_to_toplevel symrepo 'at symbolic root'
+ln -s repo symrepo 2>/dev/null
+test_cd_to_toplevel symrepo 'at symbolic root' SYMLINKS
 
-ln -s repo/sub/dir subdir-link
-test_cd_to_toplevel subdir-link 'at symbolic subdir'
+ln -s repo/sub/dir subdir-link 2>/dev/null
+test_cd_to_toplevel subdir-link 'at symbolic subdir' SYMLINKS
 
 cd repo
-ln -s sub/dir internal-link
-test_cd_to_toplevel internal-link 'at internal symbolic subdir'
+ln -s sub/dir internal-link 2>/dev/null
+test_cd_to_toplevel internal-link 'at internal symbolic subdir' SYMLINKS
 
 test_done
index bc0a3513920cab41e4335b8c1b5163e25e8354d3..86291e839942e6842bf1d2b40f2d7f7c1d8d4a9f 100755 (executable)
@@ -13,12 +13,18 @@ filesystem.
     path2/file2 - a file in a directory
     path3-junk  - a file to confuse things
     path3/file3 - a file in a directory
+    path4       - an empty directory
 '
 . ./test-lib.sh
 
 date >path0
-ln -s xyzzy path1
-mkdir path2 path3
+if test_have_prereq SYMLINKS
+then
+       ln -s xyzzy path1
+else
+       date > path1
+fi
+mkdir path2 path3 path4
 date >path2/file2
 date >path2-junk
 date >path3/file3
@@ -28,6 +34,7 @@ git update-index --add path3-junk path3/file3
 cat >expected1 <<EOF
 expected1
 expected2
+expected3
 output
 path0
 path1
@@ -35,6 +42,8 @@ path2-junk
 path2/file2
 EOF
 sed -e 's|path2/file2|path2/|' <expected1 >expected2
+cat <expected2 >expected3
+echo path4/ >>expected2
 
 test_expect_success \
     'git ls-files --others to show output.' \
@@ -42,7 +51,7 @@ test_expect_success \
 
 test_expect_success \
     'git ls-files --others should pick up symlinks.' \
-    'diff output expected1'
+    'test_cmp expected1 output'
 
 test_expect_success \
     'git ls-files --others --directory to show output.' \
@@ -51,6 +60,14 @@ test_expect_success \
 
 test_expect_success \
     'git ls-files --others --directory should not get confused.' \
-    'diff output expected2'
+    'test_cmp expected2 output'
+
+test_expect_success \
+    'git ls-files --others --directory --no-empty-directory to show output.' \
+    'git ls-files --others --directory --no-empty-directory >output'
+
+test_expect_success \
+    '--no-empty-directory hides empty directory' \
+    'test_cmp expected3 output'
 
 test_done
index ec1404063701eef04667d5ffbbb4bdc8051c773b..95671c205364a12bea02173b33d0d427d5c546fe 100755 (executable)
@@ -38,7 +38,12 @@ modified without reporting path9 and path10.
 . ./test-lib.sh
 
 date >path0
-ln -s xyzzy path1
+if test_have_prereq SYMLINKS
+then
+       ln -s xyzzy path1
+else
+       date > path1
+fi
 mkdir path2 path3
 date >path2/file2
 date >path3/file3
@@ -52,8 +57,14 @@ test_expect_success \
 
 rm -fr path? ;# leave path10 alone
 date >path2
-ln -s frotz path3
-ln -s nitfol path5
+if test_have_prereq SYMLINKS
+then
+       ln -s frotz path3
+       ln -s nitfol path5
+else
+       date > path3
+       date > path5
+fi
 mkdir path0 path1 path6
 date >path0/file0
 date >path1/file1
@@ -75,7 +86,7 @@ EOF
 
 test_expect_success \
     'validate git ls-files -k output.' \
-    'diff .output .expected'
+    'test_cmp .expected .output'
 
 test_expect_success \
     'git ls-files -m to show modified files.' \
@@ -91,6 +102,6 @@ EOF
 
 test_expect_success \
     'validate git ls-files -m output.' \
-    'diff .output .expected'
+    'test_cmp .expected .output'
 
 test_done
index 0de613dc53d85c01f6d122834e094503a2736507..9b3fa2bdcd9bebae46a000ffe9c2059cea5a93fd 100755 (executable)
@@ -276,6 +276,9 @@ test_expect_success 'fail if the index has unresolved entries' '
 
        test_must_fail git merge "$c5" &&
        test_must_fail git merge "$c5" 2> out &&
+       grep "You have not concluded your merge" out &&
+       rm -f .git/MERGE_HEAD &&
+       test_must_fail git merge "$c5" 2> out &&
        grep "You are in the middle of a conflicted merge" out
 
 '
diff --git a/t/t3031-merge-criscross.sh b/t/t3031-merge-criscross.sh
new file mode 100755 (executable)
index 0000000..7f41607
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description='merge-recursive backend test'
+
+. ./test-lib.sh
+
+#         A      <- create some files
+#        / \
+#       B   C    <- cause rename/delete conflicts between B and C
+#      /     \
+#     |\     /|
+#     | D   E |
+#     |  \ /  |
+#     |   X   |
+#     |  / \  |
+#     | /   \ |
+#     |/     \|
+#     F       G  <- merge E into B, D into C
+#      \     /
+#       \   /
+#        \ /
+#         H      <- recursive merge crashes
+#
+
+# initialize
+test_expect_success 'setup repo with criss-cross history' '
+       mkdir data &&
+
+       # create a bunch of files
+       n=1 &&
+       while test $n -le 10
+       do
+               echo $n > data/$n &&
+               n=$(($n+1)) ||
+               break
+       done &&
+
+       # check them in
+       git add data &&
+       git commit -m A &&
+       git branch A &&
+
+       # a file in one branch
+       git checkout -b B A &&
+       git rm data/9 &&
+       git add data &&
+       git commit -m B &&
+
+       # with a branch off of it
+       git branch D &&
+
+       # put some commits on D
+       git checkout D &&
+       echo testD > data/testD &&
+       git add data &&
+       git commit -m D &&
+
+       # back up to the top, create another branch and cause
+       # a rename conflict with the file we deleted earlier
+       git checkout -b C A &&
+       git mv data/9 data/new-9 &&
+       git add data &&
+       git commit -m C &&
+
+       # with a branch off of it
+       git branch E &&
+
+       # put a commit on E
+       git checkout E &&
+       echo testE > data/testE &&
+       git add data &&
+       git commit -m E &&
+
+       # now, merge E into B
+       git checkout B &&
+       test_must_fail git merge E &&
+       # force-resolve
+       git add data &&
+       git commit -m F &&
+       git branch F &&
+
+       # and merge D into C
+       git checkout C &&
+       test_must_fail git merge D &&
+       # force-resolve
+       git add data &&
+       git commit -m G &&
+       git branch G
+'
+
+test_expect_success 'recursive merge between F and G, causes segfault' '
+       git merge F
+'
+
+test_done
index 6e6a2542a22712006ae23874069c55943a3cba27..ee60d03fe8a582100e9df5511f6fea8900bcc543 100755 (executable)
@@ -22,9 +22,21 @@ test_expect_success \
     'setup' \
     'mkdir path2 path2/baz &&
      echo Hi >path0 &&
-     ln -s path0 path1 &&
+     if test_have_prereq SYMLINKS
+     then
+       ln -s path0 path1 &&
+       ln -s ../path1 path2/bazbo
+       make_expected () {
+               cat >expected
+       }
+     else
+       printf path0 > path1 &&
+       printf ../path1 > path2/bazbo
+       make_expected () {
+               sed -e "s/120000 /100644 /" >expected
+       }
+     fi &&
      echo Lo >path2/foo &&
-     ln -s ../path1 path2/bazbo &&
      echo Mi >path2/baz/b &&
      find path? \( -type f -o -type l \) -print |
      xargs git update-index --add &&
@@ -41,7 +53,7 @@ test_output () {
 test_expect_success \
     'ls-tree plain' \
     'git ls-tree $tree >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 100644 blob X  path0
 120000 blob X  path1
 040000 tree X  path2
@@ -51,7 +63,7 @@ EOF
 test_expect_success \
     'ls-tree recursive' \
     'git ls-tree -r $tree >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 100644 blob X  path0
 120000 blob X  path1
 100644 blob X  path2/baz/b
@@ -63,7 +75,7 @@ EOF
 test_expect_success \
     'ls-tree recursive with -t' \
     'git ls-tree -r -t $tree >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 100644 blob X  path0
 120000 blob X  path1
 040000 tree X  path2
@@ -77,7 +89,7 @@ EOF
 test_expect_success \
     'ls-tree recursive with -d' \
     'git ls-tree -r -d $tree >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 040000 tree X  path2
 040000 tree X  path2/baz
 EOF
@@ -86,7 +98,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path' \
     'git ls-tree $tree path >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 EOF
      test_output'
 
@@ -96,7 +108,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path1 path0' \
     'git ls-tree $tree path1 path0 >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 100644 blob X  path0
 120000 blob X  path1
 EOF
@@ -105,7 +117,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path0/' \
     'git ls-tree $tree path0/ >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 EOF
      test_output'
 
@@ -114,7 +126,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path2' \
     'git ls-tree $tree path2 >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 040000 tree X  path2
 EOF
      test_output'
@@ -123,7 +135,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path2/' \
     'git ls-tree $tree path2/ >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 040000 tree X  path2/baz
 120000 blob X  path2/bazbo
 100644 blob X  path2/foo
@@ -135,7 +147,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path2/baz' \
     'git ls-tree $tree path2/baz >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 040000 tree X  path2/baz
 EOF
      test_output'
@@ -143,14 +155,14 @@ EOF
 test_expect_success \
     'ls-tree filtered with path2/bak' \
     'git ls-tree $tree path2/bak >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 EOF
      test_output'
 
 test_expect_success \
     'ls-tree -t filtered with path2/bak' \
     'git ls-tree -t $tree path2/bak >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 040000 tree X  path2
 EOF
      test_output'
index 1b1e9ece572b68d5a415458c799690c06ddd3695..d59a9b4aef5fc53e42ec95a3b96a18fb67aa82cb 100755 (executable)
@@ -121,7 +121,7 @@ test_expect_success 'renaming a symref is not allowed' \
        ! test -f .git/refs/heads/master3
 '
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'git branch -m u v should fail when the reflog for u is a symlink' '
      git branch -l u &&
      mv .git/logs/refs/heads/u real-u &&
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
new file mode 100755 (executable)
index 0000000..809d1c4
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='git branch display tests'
+. ./test-lib.sh
+
+test_expect_success 'make commits' '
+       echo content >file &&
+       git add file &&
+       git commit -m one &&
+       echo content >>file &&
+       git commit -a -m two
+'
+
+test_expect_success 'make branches' '
+       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 symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/branch-one
+'
+
+cat >expect <<'EOF'
+  branch-one
+  branch-two
+* master
+EOF
+test_expect_success 'git branch shows local branches' '
+       git branch >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+  origin/HEAD -> origin/branch-one
+  origin/branch-one
+  origin/branch-two
+EOF
+test_expect_success 'git branch -r shows remote branches' '
+       git branch -r >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+  branch-one
+  branch-two
+* master
+  remotes/origin/HEAD -> origin/branch-one
+  remotes/origin/branch-one
+  remotes/origin/branch-two
+EOF
+test_expect_success 'git branch -a shows local and remote branches' '
+       git branch -a >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+two
+one
+two
+EOF
+test_expect_success 'git branch -v shows branch summaries' '
+       git branch -v >tmp &&
+       awk "{print \$NF}" <tmp >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+* (no branch)
+  branch-one
+  branch-two
+  master
+EOF
+test_expect_success 'git branch shows detached HEAD properly' '
+       git checkout HEAD^0 &&
+       git branch >actual &&
+       test_cmp expect actual
+'
+
+test_done
index be7ae5a0041a0d06b40a7152d4c5d495a842a87f..6e391a370208c9a0a6e73facc87f6a0e5081b929 100755 (executable)
@@ -83,9 +83,9 @@ test_expect_success 'rebase a single mode change' '
      git checkout -b modechange HEAD^ &&
      echo 1 > X &&
      git add X &&
-     chmod a+x A &&
+     test_chmod +x A &&
      test_tick &&
-     git commit -m modechange A X &&
+     git commit -m modechange &&
      GIT_TRACE=1 git rebase master
 '
 
index 603b003edff6d32fe8725f119778658c76c806fb..c32ff6682b932b3d9b270de7260a2671d8d07403 100755 (executable)
@@ -459,4 +459,15 @@ test_expect_success 'submodule rebase -i' '
        FAKE_LINES="1 squash 2 3" git rebase -i A
 '
 
+test_expect_success 'avoid unnecessary reset' '
+       git checkout master &&
+       test-chmtime =123456789 file3 &&
+       git update-index --refresh &&
+       HEAD=$(git rev-parse HEAD) &&
+       git rebase -i HEAD~4 &&
+       test $HEAD = $(git rev-parse HEAD) &&
+       MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') &&
+       test 123456789 = $MTIME
+'
+
 test_done
index 539108094345e3e0ba4cf03fc20a5ca6486fa230..85fc7c4af8cebdb50a7fa294b274bb2e7988997b 100755 (executable)
@@ -22,7 +22,8 @@ test_expect_success setup '
        git checkout topic &&
        quick_one A &&
        quick_one B &&
-       quick_one Z
+       quick_one Z &&
+       git tag start
 
 '
 
@@ -41,4 +42,24 @@ test_expect_success 'rebase -m' '
 
 '
 
+test_expect_success 'rebase --stat' '
+        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 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 config rebase.stat true &&
+        git rebase -n master >diffstat.txt &&
+        ! grep "^ fileX |  *1 +$" diffstat.txt
+'
+
 test_done
diff --git a/t/t3409-rebase-hook.sh b/t/t3409-rebase-hook.sh
deleted file mode 100755 (executable)
index 098b755..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/bin/sh
-
-test_description='git rebase with its hook(s)'
-
-. ./test-lib.sh
-
-test_expect_success setup '
-       echo hello >file &&
-       git add file &&
-       test_tick &&
-       git commit -m initial &&
-       echo goodbye >file &&
-       git add file &&
-       test_tick &&
-       git commit -m second &&
-       git checkout -b side HEAD^ &&
-       echo world >git &&
-       git add git &&
-       test_tick &&
-       git commit -m side &&
-       git checkout master &&
-       git log --pretty=oneline --abbrev-commit --graph --all &&
-       git branch test side
-'
-
-test_expect_success 'rebase' '
-       git checkout test &&
-       git reset --hard side &&
-       git rebase master &&
-       test "z$(cat git)" = zworld
-'
-
-test_expect_success 'rebase -i' '
-       git checkout test &&
-       git reset --hard side &&
-       EDITOR=true git rebase -i master &&
-       test "z$(cat git)" = zworld
-'
-
-test_expect_success 'setup pre-rebase hook' '
-       mkdir -p .git/hooks &&
-       cat >.git/hooks/pre-rebase <<EOF &&
-#!$SHELL_PATH
-echo "\$1,\$2" >.git/PRE-REBASE-INPUT
-EOF
-       chmod +x .git/hooks/pre-rebase
-'
-
-test_expect_success 'pre-rebase hook gets correct input (1)' '
-       git checkout test &&
-       git reset --hard side &&
-       git rebase master &&
-       test "z$(cat git)" = zworld &&
-       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,
-
-'
-
-test_expect_success 'pre-rebase hook gets correct input (2)' '
-       git checkout test &&
-       git reset --hard side &&
-       git rebase master test &&
-       test "z$(cat git)" = zworld &&
-       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
-'
-
-test_expect_success 'pre-rebase hook gets correct input (3)' '
-       git checkout test &&
-       git reset --hard side &&
-       git checkout master &&
-       git rebase master test &&
-       test "z$(cat git)" = zworld &&
-       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
-'
-
-test_expect_success 'pre-rebase hook gets correct input (4)' '
-       git checkout test &&
-       git reset --hard side &&
-       EDITOR=true git rebase -i master &&
-       test "z$(cat git)" = zworld &&
-       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,
-
-'
-
-test_expect_success 'pre-rebase hook gets correct input (5)' '
-       git checkout test &&
-       git reset --hard side &&
-       EDITOR=true git rebase -i master test &&
-       test "z$(cat git)" = zworld &&
-       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
-'
-
-test_expect_success 'pre-rebase hook gets correct input (6)' '
-       git checkout test &&
-       git reset --hard side &&
-       git checkout master &&
-       EDITOR=true git rebase -i master test &&
-       test "z$(cat git)" = zworld &&
-       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
-'
-
-test_expect_success 'setup pre-rebase hook that fails' '
-       mkdir -p .git/hooks &&
-       cat >.git/hooks/pre-rebase <<EOF &&
-#!$SHELL_PATH
-false
-EOF
-       chmod +x .git/hooks/pre-rebase
-'
-
-test_expect_success 'pre-rebase hook stops rebase (1)' '
-       git checkout test &&
-       git reset --hard side &&
-       test_must_fail git rebase master &&
-       test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
-       test 0 = $(git rev-list HEAD...side | wc -l)
-'
-
-test_expect_success 'pre-rebase hook stops rebase (2)' '
-       git checkout test &&
-       git reset --hard side &&
-       (
-               EDITOR=:
-               export EDITOR
-               test_must_fail git rebase -i master
-       ) &&
-       test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
-       test 0 = $(git rev-list HEAD...side | wc -l)
-'
-
-test_expect_success 'rebase --no-verify overrides pre-rebase (1)' '
-       git checkout test &&
-       git reset --hard side &&
-       git rebase --no-verify master &&
-       test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
-       test "z$(cat git)" = zworld
-'
-
-test_expect_success 'rebase --no-verify overrides pre-rebase (2)' '
-       git checkout test &&
-       git reset --hard side &&
-       EDITOR=true git rebase --no-verify -i master &&
-       test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
-       test "z$(cat git)" = zworld
-'
-
-test_done
diff --git a/t/t3413-rebase-hook.sh b/t/t3413-rebase-hook.sh
new file mode 100755 (executable)
index 0000000..098b755
--- /dev/null
@@ -0,0 +1,146 @@
+#!/bin/sh
+
+test_description='git rebase with its hook(s)'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo hello >file &&
+       git add file &&
+       test_tick &&
+       git commit -m initial &&
+       echo goodbye >file &&
+       git add file &&
+       test_tick &&
+       git commit -m second &&
+       git checkout -b side HEAD^ &&
+       echo world >git &&
+       git add git &&
+       test_tick &&
+       git commit -m side &&
+       git checkout master &&
+       git log --pretty=oneline --abbrev-commit --graph --all &&
+       git branch test side
+'
+
+test_expect_success 'rebase' '
+       git checkout test &&
+       git reset --hard side &&
+       git rebase master &&
+       test "z$(cat git)" = zworld
+'
+
+test_expect_success 'rebase -i' '
+       git checkout test &&
+       git reset --hard side &&
+       EDITOR=true git rebase -i master &&
+       test "z$(cat git)" = zworld
+'
+
+test_expect_success 'setup pre-rebase hook' '
+       mkdir -p .git/hooks &&
+       cat >.git/hooks/pre-rebase <<EOF &&
+#!$SHELL_PATH
+echo "\$1,\$2" >.git/PRE-REBASE-INPUT
+EOF
+       chmod +x .git/hooks/pre-rebase
+'
+
+test_expect_success 'pre-rebase hook gets correct input (1)' '
+       git checkout test &&
+       git reset --hard side &&
+       git rebase master &&
+       test "z$(cat git)" = zworld &&
+       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,
+
+'
+
+test_expect_success 'pre-rebase hook gets correct input (2)' '
+       git checkout test &&
+       git reset --hard side &&
+       git rebase master test &&
+       test "z$(cat git)" = zworld &&
+       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+'
+
+test_expect_success 'pre-rebase hook gets correct input (3)' '
+       git checkout test &&
+       git reset --hard side &&
+       git checkout master &&
+       git rebase master test &&
+       test "z$(cat git)" = zworld &&
+       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+'
+
+test_expect_success 'pre-rebase hook gets correct input (4)' '
+       git checkout test &&
+       git reset --hard side &&
+       EDITOR=true git rebase -i master &&
+       test "z$(cat git)" = zworld &&
+       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,
+
+'
+
+test_expect_success 'pre-rebase hook gets correct input (5)' '
+       git checkout test &&
+       git reset --hard side &&
+       EDITOR=true git rebase -i master test &&
+       test "z$(cat git)" = zworld &&
+       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+'
+
+test_expect_success 'pre-rebase hook gets correct input (6)' '
+       git checkout test &&
+       git reset --hard side &&
+       git checkout master &&
+       EDITOR=true git rebase -i master test &&
+       test "z$(cat git)" = zworld &&
+       test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+'
+
+test_expect_success 'setup pre-rebase hook that fails' '
+       mkdir -p .git/hooks &&
+       cat >.git/hooks/pre-rebase <<EOF &&
+#!$SHELL_PATH
+false
+EOF
+       chmod +x .git/hooks/pre-rebase
+'
+
+test_expect_success 'pre-rebase hook stops rebase (1)' '
+       git checkout test &&
+       git reset --hard side &&
+       test_must_fail git rebase master &&
+       test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+       test 0 = $(git rev-list HEAD...side | wc -l)
+'
+
+test_expect_success 'pre-rebase hook stops rebase (2)' '
+       git checkout test &&
+       git reset --hard side &&
+       (
+               EDITOR=:
+               export EDITOR
+               test_must_fail git rebase -i master
+       ) &&
+       test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+       test 0 = $(git rev-list HEAD...side | wc -l)
+'
+
+test_expect_success 'rebase --no-verify overrides pre-rebase (1)' '
+       git checkout test &&
+       git reset --hard side &&
+       git rebase --no-verify master &&
+       test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+       test "z$(cat git)" = zworld
+'
+
+test_expect_success 'rebase --no-verify overrides pre-rebase (2)' '
+       git checkout test &&
+       git reset --hard side &&
+       EDITOR=true git rebase --no-verify -i master &&
+       test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+       test "z$(cat git)" = zworld
+'
+
+test_done
index 9aaeabd972ffd3009fb62b9884516cc876bbb35f..e51e505a9fb902ec7d4cedfa32052f03a04e612e 100755 (executable)
@@ -17,11 +17,11 @@ test_expect_success setup '
 
 '
 
-test_expect_code 1 'cherry-pick an empty commit' '
-
-       git checkout master &&
-       git cherry-pick empty-branch
-
+test_expect_success 'cherry-pick an empty commit' '
+       git checkout master && {
+               git cherry-pick empty-branch
+               test "$?" = 1
+       }
 '
 
 test_expect_success 'index lockfile was removed' '
index 95542e9cfec1d7cc0631521c1c2bef3b7a139af7..76b1bb45456a18a8c1c33256695396cc2b65a3a9 100755 (executable)
@@ -12,30 +12,37 @@ test_expect_success \
     'Initialize test directory' \
     "touch -- foo bar baz 'space embedded' -q &&
      git add -- foo bar baz 'space embedded' -q &&
-     git commit -m 'add normal files' &&
-     test_tabs=y &&
-     if touch -- 'tab  embedded' 'newline
-embedded'
-     then
+     git commit -m 'add normal files'"
+
+if touch -- 'tab       embedded' 'newline
+embedded' 2>/dev/null
+then
+       test_set_prereq FUNNYNAMES
+else
+       say 'Your filesystem does not allow tabs in filenames.'
+fi
+
+test_expect_success FUNNYNAMES 'add files with funny names' "
      git add -- 'tab   embedded' 'newline
 embedded' &&
      git commit -m 'add files with tabs and newlines'
-     else
-         say 'Your filesystem does not allow tabs in filenames.'
-         test_tabs=n
-     fi"
+"
 
+# 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_expect_success \
-    'Determine rm behavior' \
-    ': >test-file
-     chmod a-w .
-     rm -f test-file
-     test -f test-file && test_failed_remove=y
-     chmod 775 .
-     rm -f test-file'
+: >test-file
+chmod a-w .
+rm -f test-file 2>/dev/null
+if test -f test-file
+then
+       test_set_prereq RO_DIR
+else
+       say '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' \
@@ -100,20 +107,16 @@ test_expect_success \
     'Test that "git rm -- -q" succeeds (remove a file that looks like an option)' \
     'git rm -- -q'
 
-test "$test_tabs" = y && test_expect_success \
+test_expect_success FUNNYNAMES \
     "Test that \"git rm -f\" succeeds with embedded space, tab, or newline characters." \
     "git rm -f 'space embedded' 'tab   embedded' 'newline
 embedded'"
 
-if test "$test_failed_remove" = y; then
-chmod a-w .
-test_expect_success \
-    'Test that "git rm -f" fails if its rm fails' \
-    'test_must_fail git rm -f baz'
-chmod 775 .
-else
-    test_expect_success 'skipping removal failure (perhaps running as root?)' :
-fi
+test_expect_success RO_DIR 'Test that "git rm -f" fails if its rm fails' '
+       chmod a-w . &&
+       test_must_fail git rm -f baz &&
+       chmod 775 .
+'
 
 test_expect_success \
     'When the rm in "git rm -f" fails, it should not remove the file from the index' \
index 9f6454d2ffa22ef8a207b1a4e5e2ba156e84cdb9..fe93ce52e7a8a42fd7f7cfe82bbadf39e1a7d96e 100755 (executable)
@@ -30,7 +30,7 @@ test_expect_success \
         *) echo fail; git ls-files --stage xfoo1; (exit 1);;
         esac'
 
-test_expect_success 'git add: filemode=0 should not get confused by symlink' '
+test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
        rm -f xfoo1 &&
        ln -s foo xfoo1 &&
        git add xfoo1 &&
@@ -51,7 +51,7 @@ test_expect_success \
         *) echo fail; git ls-files --stage xfoo2; (exit 1);;
         esac'
 
-test_expect_success 'git add: filemode=0 should not get confused by symlink' '
+test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
        rm -f xfoo2 &&
        ln -s foo xfoo2 &&
        git update-index --add xfoo2 &&
@@ -61,7 +61,7 @@ test_expect_success 'git add: filemode=0 should not get confused by symlink' '
        esac
 '
 
-test_expect_success \
+test_expect_success SYMLINKS \
        'git update-index --add: Test that executable bit is not used...' \
        'git config core.filemode 0 &&
         ln -s xfoo2 xfoo3 &&
@@ -179,7 +179,7 @@ test_expect_success 'git add --refresh' '
        test -z "`git diff-index HEAD -- foo`"
 '
 
-test_expect_success 'git add should fail atomically upon an unreadable file' '
+test_expect_success POSIXPERM 'git add should fail atomically upon an unreadable file' '
        git reset --hard &&
        date >foo1 &&
        date >foo2 &&
@@ -190,7 +190,7 @@ test_expect_success 'git add should fail atomically upon an unreadable file' '
 
 rm -f foo2
 
-test_expect_success 'git add --ignore-errors' '
+test_expect_success POSIXPERM 'git add --ignore-errors' '
        git reset --hard &&
        date >foo1 &&
        date >foo2 &&
@@ -201,7 +201,7 @@ test_expect_success 'git add --ignore-errors' '
 
 rm -f foo2
 
-test_expect_success 'git add (add.ignore-errors)' '
+test_expect_success POSIXPERM 'git add (add.ignore-errors)' '
        git config add.ignore-errors 1 &&
        git reset --hard &&
        date >foo1 &&
@@ -212,7 +212,7 @@ test_expect_success 'git add (add.ignore-errors)' '
 '
 rm -f foo2
 
-test_expect_success 'git add (add.ignore-errors = false)' '
+test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' '
        git config add.ignore-errors 0 &&
        git reset --hard &&
        date >foo1 &&
@@ -221,8 +221,21 @@ test_expect_success 'git add (add.ignore-errors = false)' '
        test_must_fail git add --verbose . &&
        ! ( git ls-files foo1 | grep foo1 )
 '
+rm -f foo2
+
+test_expect_success POSIXPERM '--no-ignore-errors overrides config' '
+       git config add.ignore-errors 1 &&
+       git reset --hard &&
+       date >foo1 &&
+       date >foo2 &&
+       chmod 0 foo2 &&
+       test_must_fail git add --verbose --no-ignore-errors . &&
+       ! ( git ls-files foo1 | grep foo1 ) &&
+       git config add.ignore-errors 0
+'
+rm -f foo2
 
-test_expect_success 'git add '\''fo\[ou\]bar'\'' ignores foobar' '
+test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" '
        git reset --hard &&
        touch fo\[ou\]bar foobar &&
        git add '\''fo\[ou\]bar'\'' &&
index e95663d8e66d5b94e574a6b956625fccfd341a05..62fd65e18d434711176ddb1c22ef057b879e992a 100755 (executable)
@@ -3,6 +3,11 @@
 test_description='add -i basic tests'
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+       say 'skipping git add -i tests, perl not available'
+       test_done
+fi
+
 test_expect_success 'setup (initial)' '
        echo content >file &&
        git add file &&
@@ -135,10 +140,12 @@ test_expect_success 'real edit works' '
 
 if test "$(git config --bool core.filemode)" = false
 then
-    say 'skipping filemode tests (filesystem does not properly support modes)'
+       say 'skipping filemode tests (filesystem does not properly support modes)'
 else
+       test_set_prereq FILEMODE
+fi
 
-test_expect_success 'patch does not affect mode' '
+test_expect_success FILEMODE 'patch does not affect mode' '
        git reset --hard &&
        echo content >>file &&
        chmod +x file &&
@@ -147,7 +154,7 @@ test_expect_success 'patch does not affect mode' '
        git diff file | grep "new mode"
 '
 
-test_expect_success 'stage mode but not hunk' '
+test_expect_success FILEMODE 'stage mode but not hunk' '
        git reset --hard &&
        echo content >>file &&
        chmod +x file &&
@@ -156,7 +163,55 @@ test_expect_success 'stage mode but not hunk' '
        git diff          file | grep "+content"
 '
 
-fi
+
+test_expect_success FILEMODE 'stage mode and hunk' '
+       git reset --hard &&
+       echo content >>file &&
+       chmod +x file &&
+       printf "y\\ny\\n" | git add -p &&
+       git diff --cached file | grep "new mode" &&
+       git diff --cached file | grep "+content" &&
+       test -z "$(git diff file)"
+'
+
 # end of tests disabled when filemode is not usable
 
+test_expect_success 'setup again' '
+       git reset --hard &&
+       test_chmod +x file &&
+       echo content >>file
+'
+
+# Write the patch file with a new line at the top and bottom
+cat >patch <<EOF
+index 180b47c..b6f2c08 100644
+--- a/file
++++ b/file
+@@ -1,2 +1,4 @@
++firstline
+ baseline
+ content
++lastline
+EOF
+# Expected output, similar to the patch but w/ diff at the top
+cat >expected <<EOF
+diff --git a/file b/file
+index b6f2c08..61b9053 100755
+--- a/file
++++ b/file
+@@ -1,2 +1,4 @@
++firstline
+ baseline
+ content
++lastline
+EOF
+# Test splitting the first patch, then adding both
+test_expect_success 'add first line works' '
+       git commit -am "clear local changes" &&
+       git apply patch &&
+       (echo s; echo y; echo y) | git add -p file &&
+       git diff --cached > diff &&
+       test_cmp expected diff
+'
+
 test_done
index cc3681f16118ca70ae9f65a27ccd6f354a6deee1..18695ce8218a7b383258eeb0bad84b4d4bde45be 100755 (executable)
@@ -258,4 +258,12 @@ test_expect_success \
     git diff-tree -r -R $tree_A $tree_B >.test-b &&
     cmp -s .test-a .test-b'
 
+test_expect_success \
+    'diff can read from stdin' \
+    'test_must_fail git diff --no-index -- MN - < NN |
+        grep -v "^index" | sed "s#/-#/NN#" >.test-a &&
+    test_must_fail git diff --no-index -- MN NN |
+        grep -v "^index" >.test-b &&
+    test_cmp .test-a .test-b'
+
 test_done
index b35af9b42d318904bd12649562be309fd49977a3..a4da1196a93a00502c8945a14e3aafd628efda53 100755 (executable)
@@ -12,6 +12,12 @@ by an edit for them.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+       say 'Symbolic links not supported, skipping tests.'
+       test_done
+fi
+
 test_expect_success \
     'prepare reference tree' \
     'echo xyzzy | tr -d '\\\\'012 >yomin &&
index 4e92fce1d00a55cfbc39e55b53f95cc309e96ff2..8c1b81e248bced2ccb5e4ff0067996462e89deb8 100755 (executable)
@@ -15,21 +15,10 @@ test_expect_success \
      tree=`git write-tree` &&
      echo $tree'
 
-if [ "$(git config --get core.filemode)" = false ]
-then
-       say 'filemode disabled on the filesystem, using update-index --chmod=+x'
-       test_expect_success \
-           'git update-index --chmod=+x' \
-           'git update-index rezrov &&
-            git update-index --chmod=+x rezrov &&
-            git diff-index $tree >current'
-else
-       test_expect_success \
-           'chmod' \
-           'chmod +x rezrov &&
-            git update-index rezrov &&
-            git diff-index $tree >current'
-fi
+test_expect_success \
+    'chmod' \
+    'test_chmod +x rezrov &&
+     git diff-index $tree >current'
 
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
index 7e343a9cd130a73547ed1df9a4d9cd364e60bf9f..e19ca65885a1e49916f68eee4abbe65e98227c50 100755 (executable)
@@ -99,7 +99,7 @@ test_expect_success \
     'validate result of -B -M (#4)' \
     'compare_diff_raw expected current'
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'make file0 into something completely different' \
     'rm -f file0 &&
      ln -s frotz file0 &&
@@ -114,7 +114,7 @@ cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100  file1
 EOF
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'validate result of -B (#5)' \
     'compare_diff_raw expected current'
 
@@ -129,7 +129,7 @@ cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R     file0   file1
 EOF
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'validate result of -B -M (#6)' \
     'compare_diff_raw expected current'
 
@@ -144,7 +144,7 @@ cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M     file1
 EOF
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'validate result of -M (#7)' \
     'compare_diff_raw expected current'
 
index 9055c8b318aa8cfa8b89fd19b20e94a7435ee155..d7e327cc5bc5984546032fb085fb581de5755e11 100755 (executable)
@@ -9,6 +9,12 @@ test_description='Test diff of symlinks.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+       say 'Symbolic links not supported, skipping tests.'
+       test_done
+fi
+
 cat > expected << EOF
 diff --git a/frotz b/frotz
 new file mode 120000
index 3cf5b5c4ea05d861d75aa146a52d7d273dcaa4cb..f64aa48d24f2f5704d07b9285c94ba983f7d92ac 100755 (executable)
@@ -87,7 +87,7 @@ nul_to_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
+       (: hide error code from diff, which just indicates differences
         git diff --binary --no-index /dev/null binary >current ||
         true
        ) &&
index 9c709022efb1b553ef53b3100d39e852117cbeda..8b33321f8c44dc7f8a08fa144bca4f10444ceaa1 100755 (executable)
@@ -101,8 +101,7 @@ do
        '' | '#'*) continue ;;
        esac
        test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
-       cnt=`expr $test_count + 1`
-       pfx=`printf "%04d" $cnt`
+       pfx=`printf "%04d" $test_count`
        expect="$TEST_DIRECTORY/t4013/diff.$test"
        actual="$pfx-diff.$test"
 
@@ -207,6 +206,10 @@ log --root -c --patch-with-stat --summary master
 log --root --cc --patch-with-stat --summary master
 log -SF master
 log -SF -p master
+log --decorate --all
+
+rev-list --parents HEAD
+rev-list --children HEAD
 
 whatchanged master
 whatchanged -p master
@@ -243,11 +246,12 @@ format-patch --stdout initial..master
 format-patch --stdout --no-numbered initial..master
 format-patch --stdout --numbered initial..master
 format-patch --attach --stdout initial..side
+format-patch --attach --stdout --suffix=.diff initial..side
 format-patch --attach --stdout initial..master^
 format-patch --attach --stdout initial..master
 format-patch --inline --stdout initial..side
 format-patch --inline --stdout initial..master^
-format-patch --inline --stdout initial..master
+format-patch --inline --stdout --numbered-files initial..master
 format-patch --inline --stdout initial..master
 format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
 config format.subjectprefix DIFFERENT_PREFIX
@@ -268,6 +272,7 @@ diff --no-index --name-status dir2 dir
 diff --no-index --name-status -- dir2 dir
 diff --no-index dir dir3
 diff master master^ side
+diff --dirstat master~1 master~2
 EOF
 
 test_done
diff --git a/t/t4013/diff.diff_--dirstat_master~1_master~2 b/t/t4013/diff.diff_--dirstat_master~1_master~2
new file mode 100644 (file)
index 0000000..b672e1c
--- /dev/null
@@ -0,0 +1,3 @@
+$ git diff --dirstat master~1 master~2
+  40.0% dir/
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
new file mode 100644 (file)
index 0000000..52116d3
--- /dev/null
@@ -0,0 +1,61 @@
+$ git format-patch --attach --stdout --suffix=.diff initial..side
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Side.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: attachment; filename="0001-Side.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
index e5ab74437ef5e42f7863e56546f0bdb1923c2949..ce49bd676e1a59eb015efc77e9963caa6a57a419 100644 (file)
@@ -22,9 +22,9 @@ This is the second commit.
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: attachment; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Type: text/x-patch; name="0002-Third.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Disposition: attachment; filename="0002-Third.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -129,9 +129,9 @@ Content-Transfer-Encoding: 8bit
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Type: text/x-patch; name="0003-Side.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Disposition: attachment; filename="0003-Side.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
index 2c71d20d376094fe1be51baa316fb080ad2155b4..5f1b23863bd286a946dda09a8a7f3beb022b1f66 100644 (file)
@@ -22,9 +22,9 @@ This is the second commit.
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: attachment; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Type: text/x-patch; name="0002-Third.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Disposition: attachment; filename="0002-Third.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
index 38f790290a41311e490c493bdaf71774853cc861..4a2364abc2263e0e8925053acdd7c3c98282df2e 100644 (file)
@@ -20,9 +20,9 @@ Content-Transfer-Encoding: 8bit
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Type: text/x-patch; name="0001-Side.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Disposition: attachment; filename="0001-Side.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
new file mode 100644 (file)
index 0000000..43b81eb
--- /dev/null
@@ -0,0 +1,170 @@
+$ git format-patch --inline --stdout --numbered-files initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/3] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="1"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="1"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/3] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="2"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="2"
+
+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
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH 3/3] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="3"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="3"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
index 58f8a7b7d6a86021e5b01888324dea97e8a50347..ca3f60bf0ed3858903fc6c86b99309211c340d13 100644 (file)
@@ -22,9 +22,9 @@ This is the second commit.
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: inline; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Type: text/x-patch; name="0002-Third.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Disposition: inline; filename="0002-Third.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -129,9 +129,9 @@ Content-Transfer-Encoding: 8bit
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Type: text/x-patch; name="0003-Side.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Disposition: inline; filename="0003-Side.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
index 9e7bbdffa226e1d687b1ac93aa5334540708a97e..08f23014bc15792946160c4ef45fdcfeba7e933d 100644 (file)
@@ -22,9 +22,9 @@ This is the second commit.
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: inline; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Type: text/x-patch; name="0002-Third.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Disposition: inline; filename="0002-Third.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -129,9 +129,9 @@ Content-Transfer-Encoding: 8bit
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Type: text/x-patch; name="0003-Side.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Disposition: inline; filename="0003-Side.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
index f881f644ccdd9954ae38709a75060a5621666849..07f1230d31f4cdd9f712bd2a69fea035a6998eb2 100644 (file)
@@ -22,9 +22,9 @@ This is the second commit.
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: inline; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Type: text/x-patch; name="0002-Third.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Disposition: inline; filename="0002-Third.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
index 4f258b8858df79ecf475514b69df904e83e29ffa..29e00ab8af8503e6da3b15c55147da550d4919f7 100644 (file)
@@ -22,9 +22,9 @@ This is the second commit.
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: inline; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
index e86dce69a3a78d5cfe5cd82067df0381b0f635bd..67633d424a466507015c5a02e721be9b8e6fdfe4 100644 (file)
@@ -20,9 +20,9 @@ Content-Transfer-Encoding: 8bit
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Type: text/x-patch; name="0001-Side.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Disposition: inline; filename="0001-Side.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
new file mode 100644 (file)
index 0000000..12da8ac
--- /dev/null
@@ -0,0 +1,34 @@
+$ git log --decorate --all
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (refs/heads/master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.rev-list_--children_HEAD b/t/t4013/diff.rev-list_--children_HEAD
new file mode 100644 (file)
index 0000000..e7f17d5
--- /dev/null
@@ -0,0 +1,7 @@
+$ git rev-list --children HEAD
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 59d314ad6f356dd08601a4cd5e530381da3e3c64
+9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 59d314ad6f356dd08601a4cd5e530381da3e3c64
+1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+444ac553ac7612cc88969031b02b3767fb8a353a 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+$
diff --git a/t/t4013/diff.rev-list_--parents_HEAD b/t/t4013/diff.rev-list_--parents_HEAD
new file mode 100644 (file)
index 0000000..65d2a80
--- /dev/null
@@ -0,0 +1,7 @@
+$ git rev-list --parents HEAD
+59d314ad6f356dd08601a4cd5e530381da3e3c64 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 444ac553ac7612cc88969031b02b3767fb8a353a
+9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 444ac553ac7612cc88969031b02b3767fb8a353a
+444ac553ac7612cc88969031b02b3767fb8a353a
+$
index f045898fe3196b068d03a66fd9edeea6f32add30..922a8941ed4720e2ad6dcd500a3759187aace969 100755 (executable)
@@ -16,9 +16,7 @@ test_expect_success setup '
        git checkout -b side &&
 
        for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
-       chmod +x elif &&
-       git update-index file elif &&
-       git update-index --chmod=+x elif &&
+       test_chmod +x elif &&
        git commit -m "Side changes #1" &&
 
        for i in D E F; do echo "$i"; done >>file &&
@@ -130,6 +128,21 @@ test_expect_success 'additional command line cc' '
        grep "^ *S. E. Cipient <scipient@example.com>$" patch5
 '
 
+test_expect_success 'command line headers' '
+
+       git config --unset-all format.headers &&
+       git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch6 &&
+       grep "^Cc: R. E. Cipient <rcipient@example.com>$" patch6
+'
+
+test_expect_success 'configuration headers and command line headers' '
+
+       git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
+       git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch7 &&
+       grep "^Cc: R. E. Cipient <rcipient@example.com>,$" patch7 &&
+       grep "^ *S. E. Cipient <scipient@example.com>$" patch7
+'
+
 test_expect_success 'multiple files' '
 
        rm -rf patches/ &&
@@ -138,56 +151,243 @@ test_expect_success 'multiple files' '
        ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
 '
 
-test_expect_success 'thread' '
+check_threading () {
+       expect="$1" &&
+       shift &&
+       (git format-patch --stdout "$@"; echo $? > status.out) |
+       # Prints everything between the Message-ID and In-Reply-To,
+       # and replaces all Message-ID-lookalikes by a sequence number
+       perl -ne '
+               if (/^(message-id|references|in-reply-to)/i) {
+                       $printing = 1;
+               } elsif (/^\S/) {
+                       $printing = 0;
+               }
+               if ($printing) {
+                       $h{$1}=$i++ if (/<([^>]+)>/ and !exists $h{$1});
+                       for $k (keys %h) {s/$k/$h{$k}/};
+                       print;
+               }
+               print "---\n" if /^From /i;
+       ' > actual &&
+       test 0 = "$(cat status.out)" &&
+       test_cmp "$expect" actual
+}
+
+cat >> expect.no-threading <<EOF
+---
+---
+---
+EOF
 
-       rm -rf patches/ &&
+test_expect_success 'no threading' '
        git checkout side &&
-       git format-patch --thread -o patches/ master &&
-       FIRST_MID=$(grep "Message-Id:" patches/0001-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
-       for i in patches/0002-* patches/0003-*
-       do
-         grep "References: $FIRST_MID" $i &&
-         grep "In-Reply-To: $FIRST_MID" $i || break
-       done
+       check_threading expect.no-threading master
 '
 
-test_expect_success 'thread in-reply-to' '
+cat > expect.thread <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <0>
+EOF
 
-       rm -rf patches/ &&
-       git checkout side &&
-       git format-patch --in-reply-to="<test.message>" --thread -o patches/ master &&
-       FIRST_MID="<test.message>" &&
-       for i in patches/*
-       do
-         grep "References: $FIRST_MID" $i &&
-         grep "In-Reply-To: $FIRST_MID" $i || break
-       done
+test_expect_success 'thread' '
+       check_threading expect.thread --thread master
 '
 
-test_expect_success 'thread cover-letter' '
+cat > expect.in-reply-to <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <3>
+In-Reply-To: <1>
+References: <1>
+EOF
 
-       rm -rf patches/ &&
-       git checkout side &&
-       git format-patch --cover-letter --thread -o patches/ master &&
-       FIRST_MID=$(grep "Message-Id:" patches/0000-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
-       for i in patches/0001-* patches/0002-* patches/0003-*
-       do
-         grep "References: $FIRST_MID" $i &&
-         grep "In-Reply-To: $FIRST_MID" $i || break
-       done
+test_expect_success 'thread in-reply-to' '
+       check_threading expect.in-reply-to --in-reply-to="<test.message>" \
+               --thread master
+'
+
+cat > expect.cover-letter <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <3>
+In-Reply-To: <0>
+References: <0>
+EOF
+
+test_expect_success 'thread cover-letter' '
+       check_threading expect.cover-letter --cover-letter --thread master
 '
 
+cat > expect.cl-irt <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <1>
+       <0>
+---
+Message-Id: <3>
+In-Reply-To: <0>
+References: <1>
+       <0>
+---
+Message-Id: <4>
+In-Reply-To: <0>
+References: <1>
+       <0>
+EOF
+
 test_expect_success 'thread cover-letter in-reply-to' '
+       check_threading expect.cl-irt --cover-letter \
+               --in-reply-to="<test.message>" --thread master
+'
 
-       rm -rf patches/ &&
-       git checkout side &&
-       git format-patch --cover-letter --in-reply-to="<test.message>" --thread -o patches/ master &&
-       FIRST_MID="<test.message>" &&
-       for i in patches/*
-       do
-         grep "References: $FIRST_MID" $i &&
-         grep "In-Reply-To: $FIRST_MID" $i || break
-       done
+test_expect_success 'thread explicit shallow' '
+       check_threading expect.cl-irt --cover-letter \
+               --in-reply-to="<test.message>" --thread=shallow master
+'
+
+cat > expect.deep <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <1>
+References: <0>
+       <1>
+EOF
+
+test_expect_success 'thread deep' '
+       check_threading expect.deep --thread=deep master
+'
+
+cat > expect.deep-irt <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <1>
+       <0>
+---
+Message-Id: <3>
+In-Reply-To: <2>
+References: <1>
+       <0>
+       <2>
+EOF
+
+test_expect_success 'thread deep in-reply-to' '
+       check_threading expect.deep-irt  --thread=deep \
+               --in-reply-to="<test.message>" master
+'
+
+cat > expect.deep-cl <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <1>
+References: <0>
+       <1>
+---
+Message-Id: <3>
+In-Reply-To: <2>
+References: <0>
+       <1>
+       <2>
+EOF
+
+test_expect_success 'thread deep cover-letter' '
+       check_threading expect.deep-cl --cover-letter --thread=deep master
+'
+
+cat > expect.deep-cl-irt <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <1>
+       <0>
+---
+Message-Id: <3>
+In-Reply-To: <2>
+References: <1>
+       <0>
+       <2>
+---
+Message-Id: <4>
+In-Reply-To: <3>
+References: <1>
+       <0>
+       <2>
+       <3>
+EOF
+
+test_expect_success 'thread deep cover-letter in-reply-to' '
+       check_threading expect.deep-cl-irt --cover-letter \
+               --in-reply-to="<test.message>" --thread=deep master
+'
+
+test_expect_success 'thread via config' '
+       git config format.thread true &&
+       check_threading expect.thread master
+'
+
+test_expect_success 'thread deep via config' '
+       git config format.thread deep &&
+       check_threading expect.deep master
+'
+
+test_expect_success 'thread config + override' '
+       git config format.thread deep &&
+       check_threading expect.thread --thread master
+'
+
+test_expect_success 'thread config + --no-thread' '
+       git config format.thread deep &&
+       check_threading expect.no-threading --no-thread master
 '
 
 test_expect_success 'excessive subject' '
@@ -305,4 +505,15 @@ test_expect_success 'format-patch from a subdirectory (3)' '
        test -f "$basename"
 '
 
+test_expect_success 'format-patch --in-reply-to' '
+       git format-patch -1 --stdout --in-reply-to "baz@foo.bar" > patch8 &&
+       grep "^In-Reply-To: <baz@foo.bar>" patch8 &&
+       grep "^References: <baz@foo.bar>" patch8
+'
+
+test_expect_success 'format-patch --signoff' '
+       git format-patch -1 --signoff --stdout |
+       grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+'
+
 test_done
diff --git a/t/t4017-quiet.sh b/t/t4017-quiet.sh
deleted file mode 100755 (executable)
index e747e84..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/bin/sh
-
-test_description='Return value of diffs'
-
-. ./test-lib.sh
-
-test_expect_success 'setup' '
-       echo 1 >a &&
-       git add . &&
-       git commit -m first &&
-       echo 2 >b &&
-       git add . &&
-       git commit -a -m second
-'
-
-test_expect_success 'git diff-tree HEAD^ HEAD' '
-       git diff-tree --quiet HEAD^ HEAD >cnt
-       test $? = 1 && test $(wc -l <cnt) = 0
-'
-test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
-       git diff-tree --quiet HEAD^ HEAD -- a >cnt
-       test $? = 0 && test $(wc -l <cnt) = 0
-'
-test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
-       git diff-tree --quiet HEAD^ HEAD -- b >cnt
-       test $? = 1 && test $(wc -l <cnt) = 0
-'
-# this diff outputs one line: sha1 of the given head
-test_expect_success 'echo HEAD | git diff-tree --stdin' '
-       echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
-       test $? = 1 && test $(wc -l <cnt) = 1
-'
-test_expect_success 'git diff-tree HEAD HEAD' '
-       git diff-tree --quiet HEAD HEAD >cnt
-       test $? = 0 && test $(wc -l <cnt) = 0
-'
-test_expect_success 'git diff-files' '
-       git diff-files --quiet >cnt
-       test $? = 0 && test $(wc -l <cnt) = 0
-'
-test_expect_success 'git diff-index --cached HEAD' '
-       git diff-index --quiet --cached HEAD >cnt
-       test $? = 0 && test $(wc -l <cnt) = 0
-'
-test_expect_success 'git diff-index --cached HEAD^' '
-       git diff-index --quiet --cached HEAD^ >cnt
-       test $? = 1 && test $(wc -l <cnt) = 0
-'
-test_expect_success 'git diff-index --cached HEAD^' '
-       echo text >>b &&
-       echo 3 >c &&
-       git add . && {
-               git diff-index --quiet --cached HEAD^ >cnt
-               test $? = 1 && test $(wc -l <cnt) = 0
-       }
-'
-test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
-       git commit -m "text in b" && {
-               git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
-               test $? = 1 && test $(wc -l <cnt) = 0
-       }
-'
-test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
-       git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
-       test $? = 0 && test $(wc -l <cnt) = 0
-'
-test_expect_success 'git diff-files' '
-       echo 3 >>c && {
-               git diff-files --quiet >cnt
-               test $? = 1 && test $(wc -l <cnt) = 0
-       }
-'
-test_expect_success 'git diff-index --cached HEAD' '
-       git update-index c && {
-               git diff-index --quiet --cached HEAD >cnt
-               test $? = 1 && test $(wc -l <cnt) = 0
-       }
-'
-
-test_done
index 390af2389f3b9559fcfebe1e21a24317562ab7af..709b3231ca8d9da631727b4aadfb2f46049d37e9 100755 (executable)
@@ -86,6 +86,13 @@ test_expect_success 'format.numbered && --no-numbered' '
 
 '
 
+test_expect_success 'format.numbered && --keep-subject' '
+
+       git format-patch --keep-subject --stdout HEAD^ >patch4a &&
+       grep "^Subject: Third" patch4a
+
+'
+
 test_expect_success 'format.numbered = auto' '
 
        git config format.numbered auto
@@ -108,4 +115,10 @@ test_expect_success 'format.numbered = auto && --no-numbered' '
 
 '
 
+test_expect_success '--start-number && --numbered' '
+
+       git format-patch --start-number 3 --numbered --stdout HEAD~1 > patch8 &&
+       grep "^Subject: \[PATCH 3/3\]" patch8
+'
+
 test_done
diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4021-format-patch-signer-mime.sh
deleted file mode 100755 (executable)
index ba43f18..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/sh
-
-test_description='format-patch -s should force MIME encoding as needed'
-
-. ./test-lib.sh
-
-test_expect_success setup '
-
-       >F &&
-       git add F &&
-       git commit -m initial &&
-       echo new line >F &&
-
-       test_tick &&
-       git commit -m "This adds some lines to F" F
-
-'
-
-test_expect_success 'format normally' '
-
-       git format-patch --stdout -1 >output &&
-       ! grep Content-Type output
-
-'
-
-test_expect_success 'format with signoff without funny signer name' '
-
-       git format-patch -s --stdout -1 >output &&
-       ! grep Content-Type output
-
-'
-
-test_expect_success 'format with non ASCII signer name' '
-
-       GIT_COMMITTER_NAME="はまの ふにおう" \
-       git format-patch -s --stdout -1 >output &&
-       grep Content-Type output
-
-'
-
-test_expect_success 'attach and signoff do not duplicate mime headers' '
-
-       GIT_COMMITTER_NAME="はまの ふにおう" \
-       git format-patch -s --stdout -1 --attach >output &&
-       test `grep -ci ^MIME-Version: output` = 1
-
-'
-
-test_done
-
index 297ddb5a25b682412851aed70370771aa5a15976..9bdf6596d82878f709a375084095697124a97609 100755 (executable)
@@ -4,6 +4,12 @@ test_description='typechange rename detection'
 
 . ./test-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+       say 'Symbolic links not supported, skipping tests.'
+       test_done
+fi
+
 test_expect_success setup '
 
        rm -f foo bar &&
index 9ddbbcde57489e266e2f229314ebac5dbfeb3be6..3ccc237a8d4443bfc8763fbb9cb51033f846b0e8 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 #
 # Copyright (c) Jim Meyering
 #
diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh
new file mode 100755 (executable)
index 0000000..e747e84
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='Return value of diffs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo 1 >a &&
+       git add . &&
+       git commit -m first &&
+       echo 2 >b &&
+       git add . &&
+       git commit -a -m second
+'
+
+test_expect_success 'git diff-tree HEAD^ HEAD' '
+       git diff-tree --quiet HEAD^ HEAD >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
+       git diff-tree --quiet HEAD^ HEAD -- a >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
+       git diff-tree --quiet HEAD^ HEAD -- b >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+# this diff outputs one line: sha1 of the given head
+test_expect_success 'echo HEAD | git diff-tree --stdin' '
+       echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
+       test $? = 1 && test $(wc -l <cnt) = 1
+'
+test_expect_success 'git diff-tree HEAD HEAD' '
+       git diff-tree --quiet HEAD HEAD >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+       git diff-files --quiet >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git diff-index --quiet --cached HEAD >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       git diff-index --quiet --cached HEAD^ >cnt
+       test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+       echo text >>b &&
+       echo 3 >c &&
+       git add . && {
+               git diff-index --quiet --cached HEAD^ >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
+       git commit -m "text in b" && {
+               git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
+       git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
+       test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+       echo 3 >>c && {
+               git diff-files --quiet >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+test_expect_success 'git diff-index --cached HEAD' '
+       git update-index c && {
+               git diff-index --quiet --cached HEAD >cnt
+               test $? = 1 && test $(wc -l <cnt) = 0
+       }
+'
+
+test_done
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
new file mode 100755 (executable)
index 0000000..ba43f18
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='format-patch -s should force MIME encoding as needed'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       >F &&
+       git add F &&
+       git commit -m initial &&
+       echo new line >F &&
+
+       test_tick &&
+       git commit -m "This adds some lines to F" F
+
+'
+
+test_expect_success 'format normally' '
+
+       git format-patch --stdout -1 >output &&
+       ! grep Content-Type output
+
+'
+
+test_expect_success 'format with signoff without funny signer name' '
+
+       git format-patch -s --stdout -1 >output &&
+       ! grep Content-Type output
+
+'
+
+test_expect_success 'format with non ASCII signer name' '
+
+       GIT_COMMITTER_NAME="はまの ふにおう" \
+       git format-patch -s --stdout -1 >output &&
+       grep Content-Type output
+
+'
+
+test_expect_success 'attach and signoff do not duplicate mime headers' '
+
+       GIT_COMMITTER_NAME="はまの ふにおう" \
+       git format-patch -s --stdout -1 --attach >output &&
+       test `grep -ci ^MIME-Version: output` = 1
+
+'
+
+test_done
+
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
new file mode 100755 (executable)
index 0000000..2cf7e01
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='combined diff'
+
+. ./test-lib.sh
+
+setup_helper () {
+       one=$1 branch=$2 side=$3 &&
+
+       git branch $side $branch &&
+       for l in $one two three fyra
+       do
+               echo $l
+       done >file &&
+       git add file &&
+       test_tick &&
+       git commit -m $branch &&
+       git checkout $side &&
+       for l in $one two three quatro
+       do
+               echo $l
+       done >file &&
+       git add file &&
+       test_tick &&
+       git commit -m $side &&
+       test_must_fail git merge $branch &&
+       for l in $one three four
+       do
+               echo $l
+       done >file &&
+       git add file &&
+       test_tick &&
+       git commit -m "merge $branch into $side"
+}
+
+verify_helper () {
+       it=$1 &&
+
+       # Ignore lines that were removed only from the other parent
+       sed -e '
+               1,/^@@@/d
+               /^ -/d
+               s/^\(.\)./\1/
+       ' "$it" >"$it.actual.1" &&
+       sed -e '
+               1,/^@@@/d
+               /^- /d
+               s/^.\(.\)/\1/
+       ' "$it" >"$it.actual.2" &&
+
+       git diff "$it^" "$it" -- | sed -e '1,/^@@/d' >"$it.expect.1" &&
+       test_cmp "$it.expect.1" "$it.actual.1" &&
+
+       git diff "$it^2" "$it" -- | sed -e '1,/^@@/d' >"$it.expect.2" &&
+       test_cmp "$it.expect.2" "$it.actual.2"
+}
+
+test_expect_success setup '
+       >file &&
+       git add file &&
+       test_tick &&
+       git commit -m initial &&
+
+       git branch withone &&
+       git branch sansone &&
+
+       git checkout withone &&
+       setup_helper one withone sidewithone &&
+
+       git checkout sansone &&
+       setup_helper "" sansone sidesansone
+'
+
+test_expect_success 'check combined output (1)' '
+       git show sidewithone -- >sidewithone &&
+       verify_helper sidewithone
+'
+
+test_expect_failure 'check combined output (2)' '
+       git show sidesansone -- >sidesansone &&
+       verify_helper sidesansone
+'
+
+test_done
index d42abff1ad59343fa1c84bded9a82c3212370da0..1597965241c3566aa3a14e986fd5a630fb348479 100755 (executable)
@@ -31,14 +31,16 @@ test_expect_success setup \
 test_expect_success apply \
     'git apply --index --stat --summary --apply test-patch'
 
-if [ "$(git config --get core.filemode)" = false ]
+if test "$(git config --bool core.filemode)" = false
 then
        say 'filemode disabled on the filesystem'
 else
-       test_expect_success validate \
-           'test -f bar && ls -l bar | grep "^-..x......"'
+       test_set_prereq FILEMODE
 fi
 
+test_expect_success FILEMODE validate \
+           'test -f bar && ls -l bar | grep "^-..x......"'
+
 test_expect_success 'apply reverse' \
     'git apply -R --index --stat --summary --apply test-patch &&
      test "$(cat foo)" = "This is foo"'
index 0f185caa44f3a9d048a2c058d963a1e86e9984fd..99ec13dd531c71299681acc3eb678b490ff68707 100755 (executable)
@@ -9,6 +9,12 @@ test_description='git apply should not get confused with type changes.
 
 . ./test-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+       say 'Symbolic links not supported, skipping tests.'
+       test_done
+fi
+
 test_expect_success 'setup repository and commits' '
        echo "hello world" > foo &&
        echo "hi planet" > bar &&
index 9ace578f17a07aafc050ccaf935aef8a4a3cab4e..b852e5898009bca0205c231033f8f72f48962b81 100755 (executable)
@@ -9,6 +9,12 @@ test_description='git apply symlinks and partial files
 
 . ./test-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+       say 'Symbolic links not supported, skipping tests.'
+       test_done
+fi
+
 test_expect_success setup '
 
        ln -s path1/path2/path3/path4/path5 link1 &&
index f92e259cc6f251ec6f89edee3fc16720f264d82f..65f2e4c3efb9ae5b5459e15df337e07201d78c38 100755 (executable)
@@ -20,10 +20,10 @@ test_expect_success setup '
                cat file1 &&
                echo Q | tr -d "\\012"
        } >file2 &&
-       cat file2 >file2.orig
+       cat file2 >file2.orig &&
        git add file1 file2 &&
        sed -e "/^B/d" <file1.orig >file1 &&
-       sed -e "/^[BQ]/d" <file2.orig >file2 &&
+       cat file1 > file2 &&
        echo Q | tr -d "\\012" >>file2 &&
        cat file1 >file1.mods &&
        cat file2 >file2.mods &&
index 841773f75fc085d07836b39b3775f49bde5d8d19..0d3c1d5dd5c0f35f9cc44eab4fcba5ba2e36ddd7 100755 (executable)
@@ -3,6 +3,12 @@
 test_description='apply to deeper directory without getting fooled with symlink'
 . ./test-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+       say 'Symbolic links not supported, skipping tests.'
+       test_done
+fi
+
 lecho () {
        for l_
        do
index f83322e513b96bb90e71ce39340515c6be0db186..fac2093d7f20d502f41f895d2ccdfccd5e639e2b 100755 (executable)
@@ -148,4 +148,26 @@ do
        done
 done
 
+create_patch () {
+       sed -e "s/_/ /" <<-\EOF
+               diff --git a/target b/target
+               index e69de29..8bd6648 100644
+               --- a/target
+               +++ b/target
+               @@ -0,0 +1,3 @@
+               +An empty line follows
+               +
+               +A line with trailing whitespace and no newline_
+               \ No newline at end of file
+       EOF
+}
+
+test_expect_success 'trailing whitespace & no newline at the end of file' '
+       >target &&
+       create_patch >patch-file &&
+       git apply --whitespace=fix patch-file &&
+       grep "newline$" target &&
+       grep "^$" target
+'
+
 test_done
index adfcbb5a3b9a6ec7e77f45141c2753cb79ed3763..fc7af0493102f12438d59ac5ed6e3e96eda8c841 100755 (executable)
@@ -4,6 +4,13 @@ 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_expect_success setup '
        echo original >file &&
        git add file &&
@@ -16,14 +23,14 @@ test_expect_success setup '
        git diff --stat -p >patch-1.txt
 '
 
-test_expect_success 'same mode (no index)' '
+test_expect_success FILEMODE 'same mode (no index)' '
        git reset --hard &&
        chmod +x file &&
        git apply patch-0.txt &&
        test -x file
 '
 
-test_expect_success 'same mode (with index)' '
+test_expect_success FILEMODE 'same mode (with index)' '
        git reset --hard &&
        chmod +x file &&
        git add file &&
@@ -32,7 +39,7 @@ test_expect_success 'same mode (with index)' '
        git diff --exit-code
 '
 
-test_expect_success 'same mode (index only)' '
+test_expect_success FILEMODE 'same mode (index only)' '
        git reset --hard &&
        chmod +x file &&
        git add file &&
@@ -40,20 +47,20 @@ test_expect_success 'same mode (index only)' '
        git ls-files -s file | grep "^100755"
 '
 
-test_expect_success 'mode update (no index)' '
+test_expect_success FILEMODE 'mode update (no index)' '
        git reset --hard &&
        git apply patch-1.txt &&
        test -x file
 '
 
-test_expect_success 'mode update (with index)' '
+test_expect_success FILEMODE 'mode update (with index)' '
        git reset --hard &&
        git apply --index patch-1.txt &&
        test -x file &&
        git diff --exit-code
 '
 
-test_expect_success 'mode update (index only)' '
+test_expect_success FILEMODE 'mode update (index only)' '
        git reset --hard &&
        git apply --cached patch-1.txt &&
        git ls-files -s file | grep "^100755"
diff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh
new file mode 100755 (executable)
index 0000000..94373ca
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Stephen Boyd
+#
+
+test_description='git apply --build-fake-ancestor handling.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit 1 &&
+       test_commit 2 &&
+       mkdir sub &&
+       test_commit 3 sub/3 &&
+       test_commit 4
+'
+
+test_expect_success 'apply --build-fake-ancestor' '
+       git checkout 2 &&
+       echo "A" > 1.t &&
+       git diff > 1.patch &&
+       git reset --hard &&
+       git checkout 1 &&
+       git apply --build-fake-ancestor 1.ancestor 1.patch
+'
+
+test_expect_success 'apply --build-fake-ancestor in a subdirectory' '
+       git checkout 3 &&
+       echo "C" > sub/3.t &&
+       git diff > 3.patch &&
+       git reset --hard &&
+       git checkout 4 &&
+       (
+               cd sub &&
+               git apply --build-fake-ancestor 3.ancestor ../3.patch &&
+               test -f 3.ancestor
+       ) &&
+       git apply --build-fake-ancestor 3.ancestor 3.patch &&
+       test_cmp sub/3.ancestor 3.ancestor
+'
+
+test_done
index 5e65afa0c10d02e50c79b550a3c142d8ff1f0674..d6ebbaebe2c258a4694cfb709809e13d24084231 100755 (executable)
@@ -290,4 +290,19 @@ test_expect_success 'am --ignore-date' '
        echo "$at" | grep "+0000"
 '
 
+test_expect_success 'am into an unborn branch' '
+       rm -fr subdir &&
+       mkdir -p subdir &&
+       git format-patch --numbered-files -o subdir -1 first &&
+       (
+               cd subdir &&
+               git init &&
+               git am 1
+       ) &&
+       result=$(
+               cd subdir && git rev-parse HEAD^{tree}
+       ) &&
+       test "z$result" = "z$(git rev-parse first^{tree})"
+'
+
 test_done
index b68ab11f2915789cd04ac6bd43363aeab2079198..a6bc028a57115729d38e4b228cd259880d0bf6f8 100755 (executable)
@@ -57,7 +57,7 @@ test_expect_success 'conflicting merge' '
        test_must_fail git merge first
 '
 
-sha1=$(sed -e 's/      .*//' .git/MERGE_RR)
+sha1=$(perl -pe 's/    .*//' .git/MERGE_RR)
 rr=.git/rr-cache/$sha1
 test_expect_success 'recorded preimage' "grep ^=======$ $rr/preimage"
 
@@ -190,8 +190,6 @@ test_expect_success 'file2 added differently in two branches' '
        git add file2 &&
        git commit -m version2 &&
        test_must_fail git merge fourth &&
-       sha1=$(sed -e "s/       .*//" .git/MERGE_RR) &&
-       rr=.git/rr-cache/$sha1 &&
        echo Cello > file2 &&
        git add file2 &&
        git commit -m resolution
index 7b976ee36db140550dab33ea990ada8e52dfb13e..64502e2be762a82b65a72af6847d5a3b79303d45 100755 (executable)
@@ -37,6 +37,46 @@ test_expect_success setup '
 
 '
 
+printf "sixth\nfifth\nfourth\nthird\nsecond\ninitial" > expect
+test_expect_success 'pretty' '
+
+       git log --pretty="format:%s" > actual &&
+       test_cmp expect actual
+'
+
+printf "sixth\nfifth\nfourth\nthird\nsecond\ninitial\n" > expect
+test_expect_success 'pretty (tformat)' '
+
+       git log --pretty="tformat:%s" > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'pretty (shortcut)' '
+
+       git log --pretty="%s" > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'format' '
+
+       git log --format="%s" > actual &&
+       test_cmp expect actual
+'
+
+cat > expect << EOF
+804a787 sixth
+394ef78 fifth
+5d31159 fourth
+2fbe8c0 third
+f7dab8e second
+3a2fdcb initial
+EOF
+test_expect_success 'oneline' '
+
+       git log --oneline > actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'diff-filter=A' '
 
        actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) &&
@@ -134,5 +174,179 @@ test_expect_success 'log --grep -i' '
        test_cmp expect actual
 '
 
+cat > expect <<EOF
+* Second
+* sixth
+* fifth
+* fourth
+* third
+* second
+* initial
+EOF
+
+test_expect_success 'simple log --graph' '
+       git log --graph --pretty=tformat:%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'set up merge history' '
+       git checkout -b side HEAD~4 &&
+       test_commit side-1 1 1 &&
+       test_commit side-2 2 2 &&
+       git checkout master &&
+       git merge side
+'
+
+cat > expect <<\EOF
+*   Merge branch 'side'
+|\
+| * side-2
+| * side-1
+* | Second
+* | sixth
+* | fifth
+* | fourth
+|/
+* third
+* second
+* initial
+EOF
+
+test_expect_success 'log --graph with merge' '
+       git log --graph --date-order --pretty=tformat:%s |
+               sed "s/ *$//" >actual &&
+       test_cmp expect actual
+'
+
+cat > expect <<\EOF
+*   commit master
+|\  Merge: A B
+| | Author: A U Thor <author@example.com>
+| |
+| |     Merge branch 'side'
+| |
+| * commit side
+| | Author: A U Thor <author@example.com>
+| |
+| |     side-2
+| |
+| * commit tags/side-1
+| | Author: A U Thor <author@example.com>
+| |
+| |     side-1
+| |
+* | commit master~1
+| | Author: A U Thor <author@example.com>
+| |
+| |     Second
+| |
+* | commit master~2
+| | Author: A U Thor <author@example.com>
+| |
+| |     sixth
+| |
+* | commit master~3
+| | Author: A U Thor <author@example.com>
+| |
+| |     fifth
+| |
+* | commit master~4
+|/  Author: A U Thor <author@example.com>
+|
+|       fourth
+|
+* commit tags/side-1~1
+| Author: A U Thor <author@example.com>
+|
+|     third
+|
+* commit tags/side-1~2
+| Author: A U Thor <author@example.com>
+|
+|     second
+|
+* commit tags/side-1~3
+  Author: A U Thor <author@example.com>
+
+      initial
+EOF
+
+test_expect_success 'log --graph with full output' '
+       git log --graph --date-order --pretty=short |
+               git name-rev --name-only --stdin |
+               sed "s/Merge:.*/Merge: A B/;s/ *$//" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'set up more tangled history' '
+       git checkout -b tangle HEAD~6 &&
+       test_commit tangle-a tangle-a a &&
+       git merge master~3 &&
+       git merge side~1 &&
+       git checkout master &&
+       git merge tangle &&
+       git checkout -b reach &&
+       test_commit reach &&
+       git checkout master &&
+       git checkout -b octopus-a &&
+       test_commit octopus-a &&
+       git checkout master &&
+       git checkout -b octopus-b &&
+       test_commit octopus-b &&
+       git checkout master &&
+       test_commit seventh &&
+       git merge octopus-a octopus-b
+       git merge reach
+'
+
+cat > expect <<\EOF
+*   Merge branch 'reach'
+|\
+| \
+|  \
+*-. \   Merge branches 'octopus-a' and 'octopus-b'
+|\ \ \
+* | | | seventh
+| | * | octopus-b
+| |/ /
+|/| |
+| * | octopus-a
+|/ /
+| * reach
+|/
+*   Merge branch 'tangle'
+|\
+| *   Merge branch 'side' (early part) into tangle
+| |\
+| * \   Merge branch 'master' (early part) into tangle
+| |\ \
+| * | | tangle-a
+* | | |   Merge branch 'side'
+|\ \ \ \
+| * | | | side-2
+| | | |/
+| | |/|
+| |/| |
+| * | | side-1
+* | | | Second
+* | | | sixth
+| | |/
+| |/|
+|/| |
+* | | fifth
+* | | fourth
+|/ /
+* | third
+|/
+* second
+* initial
+EOF
+
+test_expect_success 'log --graph with merge' '
+       git log --graph --date-order --pretty=tformat:%s |
+               sed "s/ *$//" >actual &&
+       test_cmp expect actual
+'
+
 test_done
 
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
new file mode 100755 (executable)
index 0000000..04f7bae
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='git patch-id'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit initial foo a &&
+       test_commit first foo b &&
+       git checkout -b same HEAD^ &&
+       test_commit same-msg foo b &&
+       git checkout -b notsame HEAD^ &&
+       test_commit notsame-msg foo c
+'
+
+test_expect_success 'patch-id output is well-formed' '
+       git log -p -1 | git patch-id > output &&
+       grep "^[a-f0-9]\{40\} $(git rev-parse HEAD)$" output
+'
+
+get_patch_id () {
+       git log -p -1 "$1" | git patch-id |
+               sed "s# .*##" > patch-id_"$1"
+}
+
+test_expect_success 'patch-id detects equality' '
+       get_patch_id master &&
+       get_patch_id same &&
+       test_cmp patch-id_master patch-id_same
+'
+
+test_expect_success 'patch-id detects inequality' '
+       get_patch_id master &&
+       get_patch_id notsame &&
+       ! test_cmp patch-id_master patch-id_notsame
+'
+
+test_done
index c942c8be85339157e22f755d8fc94e64efaee4dd..5f84b18fa5f0f199905bc7fc045672b70035a708 100755 (executable)
@@ -37,7 +37,11 @@ test_expect_success \
      cp /bin/sh a/bin &&
      printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
      printf "A not substituted O" >a/substfile2 &&
-     ln -s a a/l1 &&
+     if test_have_prereq SYMLINKS; then
+       ln -s a a/l1
+     else
+       printf %s a > a/l1
+     fi &&
      (p=long_path_to_a_file && cd a &&
       for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
       echo text >file_with_long_path) &&
@@ -46,7 +50,7 @@ test_expect_success \
 test_expect_success \
     'add ignored file' \
     'echo ignore me >a/ignored &&
-     echo ignored export-ignore >.gitattributes'
+     echo ignored export-ignore >.git/info/attributes'
 
 test_expect_success \
     'add files to repository' \
@@ -60,7 +64,7 @@ test_expect_success \
 test_expect_success \
     'create bare clone' \
     'git clone --bare . bare.git &&
-     cp .gitattributes bare.git/info/attributes'
+     cp .git/info/attributes bare.git/info/attributes'
 
 test_expect_success \
     'remove ignored file' \
@@ -76,7 +80,7 @@ test_expect_success \
 
 test_expect_success \
     'git archive vs. git tar-tree' \
-    'diff b.tar b2.tar'
+    'test_cmp b.tar b2.tar'
 
 test_expect_success \
     'git archive in a bare repo' \
@@ -86,18 +90,26 @@ test_expect_success \
     'git archive vs. the same in a bare repo' \
     'test_cmp b.tar b3.tar'
 
+test_expect_success 'git archive with --output' \
+    'git archive --output=b4.tar HEAD &&
+    test_cmp b.tar b4.tar'
+
+test_expect_success 'git archive --remote' \
+    'git archive --remote=. HEAD >b5.tar &&
+    test_cmp b.tar b5.tar'
+
 test_expect_success \
     'validate file modification time' \
     'mkdir extract &&
      "$TAR" xf b.tar -C extract a/a &&
      test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime &&
      echo "1117231200" >expected.mtime &&
-     diff expected.mtime b.mtime'
+     test_cmp expected.mtime b.mtime'
 
 test_expect_success \
     'git get-tar-commit-id' \
     'git get-tar-commit-id <b.tar >b.commitid &&
-     diff .git/$(git symbolic-ref HEAD) b.commitid'
+     test_cmp .git/$(git symbolic-ref HEAD) b.commitid'
 
 test_expect_success \
     'extract tar archive' \
@@ -106,7 +118,7 @@ test_expect_success \
 test_expect_success \
     'validate filenames' \
     '(cd b/a && find .) | sort >b.lst &&
-     diff a.lst b.lst'
+     test_cmp a.lst b.lst'
 
 test_expect_success \
     'validate file contents' \
@@ -123,7 +135,7 @@ test_expect_success \
 test_expect_success \
     'validate filenames with prefix' \
     '(cd c/prefix/a && find .) | sort >c.lst &&
-     diff a.lst c.lst'
+     test_cmp a.lst c.lst'
 
 test_expect_success \
     'validate file contents with prefix' \
@@ -131,10 +143,11 @@ test_expect_success \
 
 test_expect_success \
     'create archives with substfiles' \
-    'echo "substfile?" export-subst >a/.gitattributes &&
+    'cp .git/info/attributes .git/info/attributes.before &&
+     echo "substfile?" export-subst >>.git/info/attributes &&
      git archive HEAD >f.tar &&
      git archive --prefix=prefix/ HEAD >g.tar &&
-     rm a/.gitattributes'
+     mv .git/info/attributes.before .git/info/attributes'
 
 test_expect_success \
     'extract substfiles' \
@@ -144,8 +157,8 @@ test_expect_success \
      'validate substfile contents' \
      'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
       >f/a/substfile1.expected &&
-      diff f/a/substfile1.expected f/a/substfile1 &&
-      diff a/substfile2 f/a/substfile2
+      test_cmp f/a/substfile1.expected f/a/substfile1 &&
+      test_cmp a/substfile2 f/a/substfile2
 '
 
 test_expect_success \
@@ -156,8 +169,8 @@ test_expect_success \
      'validate substfile contents from archive with prefix' \
      'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
       >g/prefix/a/substfile1.expected &&
-      diff g/prefix/a/substfile1.expected g/prefix/a/substfile1 &&
-      diff a/substfile2 g/prefix/a/substfile2
+      test_cmp g/prefix/a/substfile1.expected g/prefix/a/substfile1 &&
+      test_cmp a/substfile2 g/prefix/a/substfile2
 '
 
 test_expect_success \
@@ -172,23 +185,27 @@ test_expect_success \
     'git archive --format=zip vs. the same in a bare repo' \
     'test_cmp d.zip d1.zip'
 
+test_expect_success 'git archive --format=zip with --output' \
+    'git archive --format=zip --output=d2.zip HEAD &&
+    test_cmp d.zip d2.zip'
+
 $UNZIP -v >/dev/null 2>&1
 if [ $? -eq 127 ]; then
-       echo "Skipping ZIP tests, because unzip was not found"
-       test_done
-       exit
+       say "Skipping ZIP tests, because unzip was not found"
+else
+       test_set_prereq UNZIP
 fi
 
-test_expect_success \
+test_expect_success UNZIP \
     'extract ZIP archive' \
     '(mkdir d && cd d && $UNZIP ../d.zip)'
 
-test_expect_success \
+test_expect_success UNZIP \
     'validate filenames' \
     '(cd d/a && find .) | sort >d.lst &&
-     diff a.lst d.lst'
+     test_cmp a.lst d.lst'
 
-test_expect_success \
+test_expect_success UNZIP \
     'validate file contents' \
     'diff -r a d/a'
 
@@ -196,16 +213,16 @@ test_expect_success \
     'git archive --format=zip with prefix' \
     'git archive --format=zip --prefix=prefix/ HEAD >e.zip'
 
-test_expect_success \
+test_expect_success UNZIP \
     'extract ZIP archive with prefix' \
     '(mkdir e && cd e && $UNZIP ../e.zip)'
 
-test_expect_success \
+test_expect_success UNZIP \
     'validate filenames with prefix' \
     '(cd e/prefix/a && find .) | sort >e.lst &&
-     diff a.lst e.lst'
+     test_cmp a.lst e.lst'
 
-test_expect_success \
+test_expect_success UNZIP \
     'validate file contents with prefix' \
     'diff -r a e/prefix/a'
 
diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh
new file mode 100755 (executable)
index 0000000..426b319
--- /dev/null
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+test_description='git archive attribute tests'
+
+. ./test-lib.sh
+
+SUBSTFORMAT=%H%n
+
+test_expect_exists() {
+       test_expect_success " $1 exists" "test -e $1"
+}
+
+test_expect_missing() {
+       test_expect_success " $1 does not exist" "test ! -e $1"
+}
+
+test_expect_success 'setup' '
+       echo ignored >ignored &&
+       echo ignored export-ignore >>.git/info/attributes &&
+       git add ignored &&
+
+       echo ignored by tree >ignored-by-tree &&
+       echo ignored-by-tree export-ignore >.gitattributes &&
+       git add ignored-by-tree .gitattributes &&
+
+       echo ignored by worktree >ignored-by-worktree &&
+       echo ignored-by-worktree export-ignore >.gitattributes &&
+       git add ignored-by-worktree &&
+
+       printf "A\$Format:%s\$O" "$SUBSTFORMAT" >nosubstfile &&
+       printf "A\$Format:%s\$O" "$SUBSTFORMAT" >substfile1 &&
+       printf "A not substituted O" >substfile2 &&
+       echo "substfile?" export-subst >>.git/info/attributes &&
+       git add nosubstfile substfile1 substfile2 &&
+
+       git commit -m. &&
+
+       git clone --bare . bare &&
+       cp .git/info/attributes bare/info/attributes
+'
+
+test_expect_success 'git archive' '
+       git archive HEAD >archive.tar &&
+       (mkdir archive && cd archive && "$TAR" xf -) <archive.tar
+'
+
+test_expect_missing    archive/ignored
+test_expect_missing    archive/ignored-by-tree
+test_expect_exists     archive/ignored-by-worktree
+
+test_expect_success 'git archive with worktree attributes' '
+       git archive --worktree-attributes HEAD >worktree.tar &&
+       (mkdir worktree && cd worktree && "$TAR" xf -) <worktree.tar
+'
+
+test_expect_missing    worktree/ignored
+test_expect_exists     worktree/ignored-by-tree
+test_expect_missing    worktree/ignored-by-worktree
+
+test_expect_success 'git archive vs. bare' '
+       (cd bare && git archive HEAD) >bare-archive.tar &&
+       test_cmp archive.tar bare-archive.tar
+'
+
+test_expect_success 'git archive with worktree attributes, bare' '
+       (cd bare && git archive --worktree-attributes HEAD) >bare-worktree.tar &&
+       (mkdir bare-worktree && cd bare-worktree && "$TAR" xf -) <bare-worktree.tar
+'
+
+test_expect_missing    bare-worktree/ignored
+test_expect_exists     bare-worktree/ignored-by-tree
+test_expect_exists     bare-worktree/ignored-by-worktree
+
+test_expect_success 'export-subst' '
+       git log "--pretty=format:A${SUBSTFORMAT}O" HEAD >substfile1.expected &&
+       test_cmp nosubstfile archive/nosubstfile &&
+       test_cmp substfile1.expected archive/substfile1 &&
+       test_cmp substfile2 archive/substfile2
+'
+
+test_expect_success 'git tar-tree vs. git archive with worktree attributes' '
+       git tar-tree HEAD >tar-tree.tar &&
+       test_cmp worktree.tar tar-tree.tar
+'
+
+test_expect_success 'git tar-tree vs. git archive with worktree attrs, bare' '
+       (cd bare && git tar-tree HEAD) >bare-tar-tree.tar &&
+       test_cmp bare-worktree.tar bare-tar-tree.tar
+'
+
+test_done
index ccfc64c6eef7e0aba7bd8a8496427470e9020309..e2aa254eae2ea930f29c4735f3f3d11bc790c455 100755 (executable)
@@ -13,11 +13,10 @@ TRASH=`pwd`
 test_expect_success \
     'setup' \
     'rm -f .git/index*
-     for i in a b c
-     do
-            dd if=/dev/zero bs=4k count=1 | perl -pe "y/\\000/$i/" >$i &&
-            git update-index --add $i || return 1
-     done &&
+     perl -e "print \"a\" x 4096;" > a &&
+     perl -e "print \"b\" x 4096;" > b &&
+     perl -e "print \"c\" x 4096;" > c &&
+     git update-index --add a b c &&
      cat c >d && echo foo >>d && git update-index --add d &&
      tree=`git write-tree` &&
      commit=`git commit-tree $tree </dev/null` && {
@@ -221,7 +220,7 @@ test_expect_success \
 test_expect_success \
     'verify-pack catches a corrupted pack signature' \
     'cat test-1-${packname_1}.pack >test-3.pack &&
-     dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
+     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
      if git verify-pack test-3.idx
      then false
      else :;
@@ -230,7 +229,7 @@ test_expect_success \
 test_expect_success \
     'verify-pack catches a corrupted pack version' \
     'cat test-1-${packname_1}.pack >test-3.pack &&
-     dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
+     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
      if git verify-pack test-3.idx
      then false
      else :;
@@ -239,7 +238,7 @@ test_expect_success \
 test_expect_success \
     'verify-pack catches a corrupted type/size of the 1st packed object data' \
     'cat test-1-${packname_1}.pack >test-3.pack &&
-     dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
+     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
      if git verify-pack test-3.idx
      then false
      else :;
@@ -250,7 +249,7 @@ test_expect_success \
     'l=`wc -c <test-3.idx` &&
      l=`expr $l - 20` &&
      cat test-1-${packname_1}.pack >test-3.pack &&
-     dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
+     printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
      if git verify-pack test-3.pack
      then false
      else :;
index e6f70d474f2f855d3b2b40eed526e4873b3288d5..4360e77d317bfdaf7b40795859b4a023a5b89c13 100755 (executable)
@@ -69,32 +69,27 @@ test_expect_success \
     'index v2: force some 64-bit offsets with pack-objects' \
     'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)'
 
-have_64bits=
 if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) ||
        ! (echo "$msg" | grep "pack too large .* off_t")
 then
-       have_64bits=t
+       test_set_prereq OFF64_T
 else
        say "skipping tests concerning 64-bit offsets"
 fi
 
-test "$have_64bits" &&
-test_expect_success \
+test_expect_success OFF64_T \
     'index v2: verify a pack with some 64-bit offsets' \
     'git verify-pack -v "test-3-${pack3}.pack"'
 
-test "$have_64bits" &&
-test_expect_success \
+test_expect_success OFF64_T \
     '64-bit offsets: should be different from previous index v2 results' \
     '! cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
 
-test "$have_64bits" &&
-test_expect_success \
+test_expect_success OFF64_T \
     'index v2: force some 64-bit offsets with index-pack' \
     'git index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"'
 
-test "$have_64bits" &&
-test_expect_success \
+test_expect_success OFF64_T \
     '64-bit offsets: index-pack result should match pack-objects one' \
     'cmp "test-3-${pack3}.idx" "3.idx"'
 
@@ -208,7 +203,7 @@ test_expect_success \
      obj=`git hash-object file_001` &&
      nr=`index_obj_nr ".git/objects/pack/pack-${pack1}.idx" $obj` &&
      chmod +w ".git/objects/pack/pack-${pack1}.idx" &&
-     dd if=/dev/zero of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \
+     printf xxxx | dd of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \
         bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l <obj-list` * 20 + $nr * 4)) &&
      ( while read obj
        do git cat-file -p $obj >/dev/null || exit 1
index d4e30fc43c68ed9b0fc175f2a88a07de6458f58e..5132d4130968691bb115f5fc08cd0d2fd64ce669 100755 (executable)
@@ -55,6 +55,8 @@ do_corrupt_object() {
     test_must_fail git verify-pack ${pack}.pack
 }
 
+printf '\0' > zero
+
 test_expect_success \
     'initial setup validation' \
     'create_test_files &&
@@ -66,7 +68,7 @@ test_expect_success \
 
 test_expect_success \
     'create corruption in header of first object' \
-    'do_corrupt_object $blob_1 0 < /dev/zero &&
+    'do_corrupt_object $blob_1 0 < zero &&
      test_must_fail git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -125,7 +127,7 @@ test_expect_success \
     'create corruption in header of first delta' \
     'create_new_pack &&
      git prune-packed &&
-     do_corrupt_object $blob_2 0 < /dev/zero &&
+     do_corrupt_object $blob_2 0 < zero &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -180,7 +182,7 @@ test_expect_success \
     'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \
     'create_new_pack &&
      git prune-packed &&
-     do_corrupt_object $blob_2 2 < /dev/zero &&
+     do_corrupt_object $blob_2 2 < zero &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -207,7 +209,7 @@ test_expect_success \
     'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' \
     'create_new_pack --delta-base-offset &&
      git prune-packed &&
-     do_corrupt_object $blob_2 2 < /dev/zero &&
+     do_corrupt_object $blob_2 2 < zero &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -259,7 +261,7 @@ test_expect_success \
 
 test_expect_success \
     '... and a redundant pack allows for full recovery too' \
-    'do_corrupt_object $blob_2 2 < /dev/zero &&
+    'do_corrupt_object $blob_2 2 < zero &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null &&
index 9b2e1a94c5fb5aa094853a169cbdf2f7dc56e152..5858b868ed6ff05d458996cf8c359a7148591582 100755 (executable)
@@ -71,4 +71,18 @@ test_expect_success 'post-checkout receives the right args when not switching br
         test $old = $new -a $flag = 0
 '
 
+if test "$(git config --bool core.filemode)" = true; then
+mkdir -p templates/hooks
+cat >templates/hooks/post-checkout <<'EOF'
+#!/bin/sh
+echo $@ > $GIT_DIR/post-checkout.args
+EOF
+chmod +x templates/hooks/post-checkout
+
+test_expect_success 'post-checkout hook is triggered by clone' '
+       git clone --template=templates . clone3 &&
+       test -f clone3/.git/post-checkout.args
+'
+fi
+
 test_done
index 4074e23ffa2c7a93fdbd4a367e273118224b9038..d5db75d826c8584e1d7f0f5ef298021fecd6f055 100755 (executable)
@@ -4,6 +4,12 @@ test_description='test automatic tag following'
 
 . ./test-lib.sh
 
+case $(uname -s) in
+*MINGW*)
+       say "GIT_DEBUG_SEND_PACK not supported - skipping tests"
+       test_done
+esac
+
 # End state of the repository:
 #
 #         T - tag1          S - tag2
index eb637184a00007c61e6d92f7b5546eed6ec5a0ae..e70246b3fb5f0040a65db953b0adee7ada2d51ed 100755 (executable)
@@ -28,7 +28,7 @@ tokens_match () {
 }
 
 check_remote_track () {
-       actual=$(git remote show "$1" | sed -e '1,/Tracked/d') &&
+       actual=$(git remote show "$1" | sed -ne 's|^    \(.*\) tracked$|\1|p')
        shift &&
        tokens_match "$*" "$actual"
 }
@@ -136,47 +136,73 @@ EOF
 cat > test/expect << EOF
 * remote origin
   URL: $(pwd)/one
-  Remote branch merged with 'git pull' while on branch master
+  HEAD branch: master
+  Remote branches:
+    master new (next fetch will store in remotes/origin)
+    side   tracked
+  Local branches configured for 'git pull':
+    ahead    merges with remote master
+    master   merges with remote master
+    octopus  merges with remote topic-a
+                and with remote topic-b
+                and with remote topic-c
+    rebase  rebases onto remote master
+  Local refs configured for 'git push':
+    master pushes to master   (local out of date)
+    master pushes to upstream (create)
+* remote two
+  URL: ../two
+  HEAD branch (remote HEAD is ambiguous, may be one of the following):
+    another
     master
-  New remote branch (next fetch will store in remotes/origin)
-    master
-  Tracked remote branches
-    side
-    master
-  Local branches pushed with 'git push'
-    master:upstream
-    +refs/tags/lastbackup
+  Local refs configured for 'git push':
+    ahead  forces to master  (fast forwardable)
+    master pushes to another (up to date)
 EOF
 
 test_expect_success 'show' '
        (cd test &&
-        git config --add remote.origin.fetch \
-               refs/heads/master:refs/heads/upstream &&
+        git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
         git fetch &&
+        git checkout -b ahead origin/master &&
+        echo 1 >> file &&
+        test_tick &&
+        git commit -m update file &&
+        git checkout master &&
+        git branch --track octopus origin/master &&
+        git branch --track rebase origin/master &&
         git branch -d -r origin/master &&
+        git config --add remote.two.url ../two &&
+        git config branch.rebase.rebase true &&
+        git config branch.octopus.merge "topic-a topic-b topic-c" &&
         (cd ../one &&
          echo 1 > file &&
          test_tick &&
          git commit -m update file) &&
-        git config remote.origin.push \
-               refs/heads/master:refs/heads/upstream &&
-        git config --add remote.origin.push \
-               +refs/tags/lastbackup &&
-        git remote show origin > output &&
+        git config --add remote.origin.push : &&
+        git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
+        git config --add remote.origin.push +refs/tags/lastbackup &&
+        git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
+        git config --add remote.two.push refs/heads/master:refs/heads/another &&
+        git remote show origin two > output &&
+        git branch -d rebase octopus &&
         test_cmp expect output)
 '
 
 cat > test/expect << EOF
 * remote origin
   URL: $(pwd)/one
-  Remote branch merged with 'git pull' while on branch master
-    master
-  Tracked remote branches
+  HEAD branch: (not queried)
+  Remote branches: (status not queried)
     master
     side
-  Local branches pushed with 'git push'
-    master:upstream
-    +refs/tags/lastbackup
+  Local branches configured for 'git pull':
+    ahead  merges with remote master
+    master merges with remote master
+  Local refs configured for 'git push' (status not queried):
+    (matching)           pushes to (matching)
+    refs/heads/master    pushes to refs/heads/upstream
+    refs/tags/lastbackup forces to refs/tags/lastbackup
 EOF
 
 test_expect_success 'show -n' '
@@ -197,6 +223,46 @@ test_expect_success 'prune' '
         test_must_fail git rev-parse refs/remotes/origin/side)
 '
 
+test_expect_success 'set-head --delete' '
+       (cd test &&
+        git symbolic-ref refs/remotes/origin/HEAD &&
+        git remote set-head --delete origin &&
+        test_must_fail git symbolic-ref refs/remotes/origin/HEAD)
+'
+
+test_expect_success 'set-head --auto' '
+       (cd test &&
+        git remote set-head --auto origin &&
+        echo refs/remotes/origin/master >expect &&
+        git symbolic-ref refs/remotes/origin/HEAD >output &&
+        test_cmp expect output
+       )
+'
+
+cat >test/expect <<EOF
+error: Multiple remote HEAD branches. Please choose one explicitly with:
+  git remote set-head two another
+  git remote set-head two master
+EOF
+
+test_expect_success 'set-head --auto fails w/multiple HEADs' '
+       (cd test &&
+        test_must_fail git remote set-head --auto two >output 2>&1 &&
+       test_cmp expect output)
+'
+
+cat >test/expect <<EOF
+refs/remotes/origin/side2
+EOF
+
+test_expect_success 'set-head explicit' '
+       (cd test &&
+        git remote set-head origin side2 &&
+        git symbolic-ref refs/remotes/origin/HEAD >output &&
+        git remote set-head origin master &&
+        test_cmp expect output)
+'
+
 cat > test/expect << EOF
 Pruning origin
 URL: $(pwd)/one
@@ -343,7 +409,7 @@ test_expect_success '"remote show" does not show symbolic refs' '
        git clone one three &&
        (cd three &&
         git remote show origin > output &&
-        ! grep HEAD < output &&
+        ! grep "^ *HEAD$" < output &&
         ! grep -i stale < output)
 
 '
@@ -428,5 +494,15 @@ test_expect_success 'remote prune to cause a dangling symref' '
        grep "dangling symref" err
 '
 
+test_expect_success 'show empty remote' '
+
+       test_create_repo empty &&
+       git clone empty empty-clone &&
+       (
+               cd empty-clone &&
+               git remote show origin
+       )
+'
+
 test_done
 
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
new file mode 100755 (executable)
index 0000000..2a1806b
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='git remote group handling'
+. ./test-lib.sh
+
+mark() {
+       echo "$1" >mark
+}
+
+update_repo() {
+       (cd $1 &&
+       echo content >>file &&
+       git add file &&
+       git commit -F ../mark)
+}
+
+update_repos() {
+       update_repo one $1 &&
+       update_repo two $1
+}
+
+repo_fetched() {
+       if test "`git log -1 --pretty=format:%s $1 --`" = "`cat mark`"; then
+               echo >&2 "repo was fetched: $1"
+               return 0
+       fi
+       echo >&2 "repo was not fetched: $1"
+       return 1
+}
+
+test_expect_success 'setup' '
+       mkdir one && (cd one && git init) &&
+       mkdir two && (cd two && git init) &&
+       git remote add -m master one one &&
+       git remote add -m master two two
+'
+
+test_expect_success 'no group updates all' '
+       mark update-all &&
+       update_repos &&
+       git remote update &&
+       repo_fetched one &&
+       repo_fetched two
+'
+
+test_expect_success 'nonexistant group produces error' '
+       mark nonexistant &&
+       update_repos &&
+       test_must_fail git remote update nonexistant &&
+       ! repo_fetched one &&
+       ! repo_fetched two
+'
+
+test_expect_success 'updating group updates all members' '
+       mark group-all &&
+       update_repos &&
+       git config --add remotes.all one &&
+       git config --add remotes.all two &&
+       git remote update all &&
+       repo_fetched one &&
+       repo_fetched two
+'
+
+test_expect_success 'updating group does not update non-members' '
+       mark group-some &&
+       update_repos &&
+       git config --add remotes.some one &&
+       git remote update some &&
+       repo_fetched one &&
+       ! repo_fetched two
+'
+
+test_expect_success 'updating remote name updates that remote' '
+       mark remote-name &&
+       update_repos &&
+       git remote update one &&
+       repo_fetched one &&
+       ! repo_fetched two
+'
+
+test_done
index 22ba380034775e7584a33ca606294af34f568443..c28932216b1dd0893bc3c3a0a643963663ff8e1f 100755 (executable)
@@ -72,4 +72,16 @@ test_refspec fetch ':refs/remotes/frotz/HEAD-to-me'
 test_refspec push ':refs/remotes/frotz/delete me'              invalid
 test_refspec fetch ':refs/remotes/frotz/HEAD to me'            invalid
 
+test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
+
+test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
+
+test_refspec fetch 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
+
+test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*'
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*'
+
 test_done
index 1f4608d8ba4748a2bd5c7a3d5a75a04364e8f646..dbb927dec8ea9f40e8e106f416c276f1b6a07868 100755 (executable)
@@ -129,8 +129,7 @@ do
        '' | '#'*) continue ;;
        esac
        test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
-       cnt=`expr $test_count + 1`
-       pfx=`printf "%04d" $cnt`
+       pfx=`printf "%04d" $test_count`
        expect_f="$TEST_DIRECTORY/t5515/fetch.$test"
        actual_f="$pfx-fetch.$test"
        expect_r="$TEST_DIRECTORY/t5515/refs.$test"
diff --git a/t/t5521-pull-symlink.sh b/t/t5521-pull-symlink.sh
deleted file mode 100755 (executable)
index 5672b51..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/bin/sh
-
-test_description='pulling from symlinked subdir'
-
-. ./test-lib.sh
-
-# The scenario we are building:
-#
-#   trash\ directory/
-#     clone-repo/
-#       subdir/
-#         bar
-#     subdir-link -> clone-repo/subdir/
-#
-# The working directory is subdir-link.
-
-mkdir subdir
-echo file >subdir/file
-git add subdir/file
-git commit -q -m file
-git clone -q . clone-repo
-ln -s clone-repo/subdir/ subdir-link
-
-
-# Demonstrate that things work if we just avoid the symlink
-#
-test_expect_success 'pulling from real subdir' '
-       (
-               echo real >subdir/file &&
-               git commit -m real subdir/file &&
-               cd clone-repo/subdir/ &&
-               git pull &&
-               test real = $(cat file)
-       )
-'
-
-# From subdir-link, pulling should work as it does from
-# clone-repo/subdir/.
-#
-# Instead, the error pull gave was:
-#
-#   fatal: 'origin': unable to chdir or not a git archive
-#   fatal: The remote end hung up unexpectedly
-#
-# because git would find the .git/config for the "trash directory"
-# repo, not for the clone-repo repo.  The "trash directory" repo
-# had no entry for origin.  Git found the wrong .git because
-# git rev-parse --show-cdup printed a path relative to
-# clone-repo/subdir/, not subdir-link/.  Git rev-parse --show-cdup
-# used the correct .git, but when the git pull shell script did
-# "cd `git rev-parse --show-cdup`", it ended up in the wrong
-# 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' '
-       (
-               echo link >subdir/file &&
-               git commit -m link subdir/file &&
-               cd subdir-link/ &&
-               git pull &&
-               test link = $(cat file)
-       )
-'
-
-# 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' '
-       (
-               cd subdir-link/ &&
-               echo push >file &&
-               git commit -m push ./file &&
-               git push
-       ) &&
-       test push = $(git show HEAD:subdir/file)
-'
-
-test_done
diff --git a/t/t5522-pull-symlink.sh b/t/t5522-pull-symlink.sh
new file mode 100755 (executable)
index 0000000..86bbd7d
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='pulling from symlinked subdir'
+
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+       say 'Symbolic links not supported, skipping tests.'
+       test_done
+fi
+
+# The scenario we are building:
+#
+#   trash\ directory/
+#     clone-repo/
+#       subdir/
+#         bar
+#     subdir-link -> clone-repo/subdir/
+#
+# The working directory is subdir-link.
+
+mkdir subdir
+echo file >subdir/file
+git add subdir/file
+git commit -q -m file
+git clone -q . clone-repo
+ln -s clone-repo/subdir/ subdir-link
+
+
+# Demonstrate that things work if we just avoid the symlink
+#
+test_expect_success 'pulling from real subdir' '
+       (
+               echo real >subdir/file &&
+               git commit -m real subdir/file &&
+               cd clone-repo/subdir/ &&
+               git pull &&
+               test real = $(cat file)
+       )
+'
+
+# From subdir-link, pulling should work as it does from
+# clone-repo/subdir/.
+#
+# Instead, the error pull gave was:
+#
+#   fatal: 'origin': unable to chdir or not a git archive
+#   fatal: The remote end hung up unexpectedly
+#
+# because git would find the .git/config for the "trash directory"
+# repo, not for the clone-repo repo.  The "trash directory" repo
+# had no entry for origin.  Git found the wrong .git because
+# git rev-parse --show-cdup printed a path relative to
+# clone-repo/subdir/, not subdir-link/.  Git rev-parse --show-cdup
+# used the correct .git, but when the git pull shell script did
+# "cd `git rev-parse --show-cdup`", it ended up in the wrong
+# 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' '
+       (
+               echo link >subdir/file &&
+               git commit -m link subdir/file &&
+               cd subdir-link/ &&
+               git pull &&
+               test link = $(cat file)
+       )
+'
+
+# 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' '
+       (
+               cd subdir-link/ &&
+               echo push >file &&
+               git commit -m push ./file &&
+               git push
+       ) &&
+       test push = $(git show HEAD:subdir/file)
+'
+
+test_done
index 10e5fd0d5a5c38eaa102d88e607f2585e1157526..5fe479e1c2362ba05d059805d412b605aa7a83aa 100755 (executable)
@@ -11,22 +11,16 @@ This test runs various sanity checks on http-push.'
 
 ROOT_PATH="$PWD"
 LIB_HTTPD_DAV=t
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
 
 if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
 then
        say "skipping test, USE_CURL_MULTI is not defined"
        test_done
-       exit
 fi
 
 . "$TEST_DIRECTORY"/lib-httpd.sh
-
-if ! start_httpd >&3 2>&4
-then
-       say "skipping test, web server setup failed"
-       test_done
-       exit
-fi
+start_httpd
 
 test_expect_success 'setup remote repository' '
        cd "$ROOT_PATH" &&
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
new file mode 100755 (executable)
index 0000000..05b1b62
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+test_description='test fetching over http'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+       say 'skipping test, git built without http support'
+       test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
+start_httpd
+
+test_expect_success 'setup repository' '
+       echo content >file &&
+       git add file &&
+       git commit -m one
+'
+
+test_expect_success 'create http-accessible bare repository' '
+       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+        git --bare init &&
+        echo "exec git update-server-info" >hooks/post-update &&
+        chmod +x hooks/post-update
+       ) &&
+       git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git push public master:master
+'
+
+test_expect_success 'clone http repository' '
+       git clone $HTTPD_URL/repo.git clone &&
+       test_cmp file clone/file
+'
+
+test_expect_success 'fetch changes via http' '
+       echo content >>file &&
+       git commit -a -m two &&
+       git push public
+       (cd clone && git pull) &&
+       test_cmp file clone/file
+'
+
+test_expect_success 'http remote detects correct HEAD' '
+       git push public master:other &&
+       (cd clone &&
+        git remote set-head origin -d &&
+        git remote set-head origin -a &&
+        git symbolic-ref refs/remotes/origin/HEAD > output &&
+        echo refs/remotes/origin/master > expect &&
+        test_cmp expect output
+       )
+'
+
+stop_httpd
+test_done
index c3ffc8f15c6165713e29fecacdd0f335aba1d56c..214756731baf199e6a50f9ab2380a8b4bfc0fb18 100755 (executable)
@@ -161,4 +161,19 @@ test_expect_success 'clone a void' '
        test_cmp target-6/.git/config target-7/.git/config
 '
 
+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 &&
+               rm -fr dst &&
+               git clone src dst &&
+               cd dst &&
+               actual="z$(git config branch.master.rebase)" &&
+               test ztrue = $actual
+       )
+'
+
 test_done
index 82b1d1e2b3f6704cf08540f0e6f4cecf0981cb7d..deffdaee490d620c44baaee143f11be604171a42 100755 (executable)
@@ -18,8 +18,8 @@ test_expect_success 'clone calls git upload-pack unqualified with no -u option'
 '
 
 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 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 3559d179647035a6ad8783aabfbecb8edcd4c254..19b5c0d552fa8b4665b5e396833264e258365b28 100755 (executable)
@@ -132,4 +132,14 @@ test_expect_success 'clone empty repository' '
         test $actual = $expected)
 '
 
+test_expect_success 'clone empty repository, and then push should not segfault.' '
+       cd "$D" &&
+       rm -fr empty/ empty-clone/ &&
+       mkdir empty &&
+       (cd empty && git init) &&
+       git clone empty empty-clone &&
+       (cd empty-clone &&
+       test_must_fail git push)
+'
+
 test_done
diff --git a/t/t5705-clone-2gb.sh b/t/t5705-clone-2gb.sh
new file mode 100755 (executable)
index 0000000..9f52154
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='Test cloning a repository larger than 2 gigabyte'
+. ./test-lib.sh
+
+test -z "$GIT_TEST_CLONE_2GB" &&
+say "Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t" &&
+test_done &&
+exit
+
+test_expect_success 'setup' '
+
+       git config pack.compression 0 &&
+       git config pack.depth 0 &&
+       blobsize=$((20*1024*1024)) &&
+       blobcount=$((2*1024*1024*1024/$blobsize+1)) &&
+       i=1 &&
+       (while test $i -le $blobcount
+        do
+               printf "Generating blob $i/$blobcount\r" >&2 &&
+               printf "blob\nmark :$i\ndata $blobsize\n" &&
+               #test-genrandom $i $blobsize &&
+               printf "%-${blobsize}s" $i &&
+               echo "M 100644 :$i $i" >> commit
+               i=$(($i+1)) ||
+               echo $? > exit-status
+        done &&
+        echo "commit refs/heads/master" &&
+        echo "author A U Thor <author@email.com> 123456789 +0000" &&
+        echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+        echo "data 5" &&
+        echo ">2gb" &&
+        cat commit) |
+       git fast-import &&
+       test ! -f exit-status
+
+'
+
+test_expect_success 'clone' '
+
+       git clone --bare --no-hardlinks . clone
+
+'
+
+test_done
index f8942bc8908fb92b1b11580e3554a81377dc3ba1..7dcf39191476f272431e19e10ebb299d6aa55bb1 100755 (executable)
@@ -54,6 +54,12 @@ deduxit me super semitas jusitiae,
 EOF
 printf "propter nomen suum." >> new4.txt
 
+test_expect_success 'merge with no changes' '
+       cp orig.txt test.txt &&
+       git merge-file test.txt orig.txt orig.txt &&
+       test_cmp test.txt orig.txt
+'
+
 cp new1.txt test.txt
 test_expect_success "merge without conflict" \
        "git merge-file test.txt orig.txt new2.txt"
diff --git a/t/t6023-merge-rename-nocruft.sh b/t/t6023-merge-rename-nocruft.sh
deleted file mode 100755 (executable)
index 65be95f..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/bin/sh
-
-test_description='Merge-recursive merging renames'
-. ./test-lib.sh
-
-test_expect_success setup \
-'
-cat >A <<\EOF &&
-a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-c cccccccccccccccccccccccccccccccccccccccccccccccc
-d dddddddddddddddddddddddddddddddddddddddddddddddd
-e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
-f ffffffffffffffffffffffffffffffffffffffffffffffff
-g gggggggggggggggggggggggggggggggggggggggggggggggg
-h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
-i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
-j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
-k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
-l llllllllllllllllllllllllllllllllllllllllllllllll
-m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
-n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
-o oooooooooooooooooooooooooooooooooooooooooooooooo
-EOF
-
-cat >M <<\EOF &&
-A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
-C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
-D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
-E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
-F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
-H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
-I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
-J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
-K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
-L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
-M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
-N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
-O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
-EOF
-
-git add A M &&
-git commit -m "initial has A and M" &&
-git branch white &&
-git branch red &&
-git branch blue &&
-
-git checkout white &&
-sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
-sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
-rm -f A M &&
-git update-index --add --remove A B M N &&
-git commit -m "white renames A->B, M->N" &&
-
-git checkout red &&
-echo created by red >R &&
-git update-index --add R &&
-git commit -m "red creates R" &&
-
-git checkout blue &&
-sed -e "/^o /s/.*/g : blue changes a line/" <A >B &&
-rm -f A &&
-mv B A &&
-git update-index A &&
-git commit -m "blue modify A" &&
-
-git checkout master'
-
-# This test broke in 65ac6e9c3f47807cb603af07a6a9e1a43bc119ae
-test_expect_success 'merge white into red (A->B,M->N)' \
-'
-       git checkout -b red-white red &&
-       git merge white &&
-       git write-tree >/dev/null || {
-               echo "BAD: merge did not complete"
-               return 1
-       }
-
-       test -f B || {
-               echo "BAD: B does not exist in working directory"
-               return 1
-       }
-       test -f N || {
-               echo "BAD: N does not exist in working directory"
-               return 1
-       }
-       test -f R || {
-               echo "BAD: R does not exist in working directory"
-               return 1
-       }
-
-       test -f A && {
-               echo "BAD: A still exists in working directory"
-               return 1
-       }
-       test -f M && {
-               echo "BAD: M still exists in working directory"
-               return 1
-       }
-       return 0
-'
-
-# This test broke in 8371234ecaaf6e14fe3f2082a855eff1bbd79ae9
-test_expect_success 'merge blue into white (A->B, mod A, A untracked)' \
-'
-       git checkout -b white-blue white &&
-       echo dirty >A &&
-       git merge blue &&
-       git write-tree >/dev/null || {
-               echo "BAD: merge did not complete"
-               return 1
-       }
-
-       test -f A || {
-               echo "BAD: A does not exist in working directory"
-               return 1
-       }
-       test `cat A` = dirty || {
-               echo "BAD: A content is wrong"
-               return 1
-       }
-       test -f B || {
-               echo "BAD: B does not exist in working directory"
-               return 1
-       }
-       test -f N || {
-               echo "BAD: N does not exist in working directory"
-               return 1
-       }
-       test -f M && {
-               echo "BAD: M still exists in working directory"
-               return 1
-       }
-       return 0
-'
-
-test_done
index 052a6c90f5a184ddc82f2db1a2907a1b1104166c..54b7ea6505d8c189c6c557cffd3d6518df06fb73 100755 (executable)
@@ -506,6 +506,66 @@ test_expect_success 'optimized merge base checks' '
        unset GIT_TRACE
 '
 
+# This creates another side branch called "parallel" with some files
+# in some directories, to test bisecting with paths.
+#
+# We should have the following:
+#
+#    P1-P2-P3-P4-P5-P6-P7
+#   /        /        /
+# H1-H2-H3-H4-H5-H6-H7
+#            \  \     \
+#             S5-A     \
+#              \        \
+#               S6-S7----B
+#
+test_expect_success '"parallel" side branch creation' '
+       git bisect reset &&
+       git checkout -b parallel $HASH1 &&
+       mkdir dir1 dir2 &&
+       add_line_into_file "1(para): line 1 on parallel branch" dir1/file1 &&
+       PARA_HASH1=$(git rev-parse --verify HEAD) &&
+       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)
+       git merge -m "merge HASH4 and PARA_HASH3" "$HASH4" &&
+       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)
+       add_line_into_file "6(para): add line on parallel branch" dir2/file2 &&
+       PARA_HASH6=$(git rev-parse --verify HEAD)
+       git merge -m "merge HASH7 and PARA_HASH6" "$HASH7" &&
+       PARA_HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'restricting bisection on one dir' '
+       git bisect reset &&
+       git bisect start HEAD $HASH1 -- dir1 &&
+       para1=$(git rev-parse --verify HEAD) &&
+       test "$para1" = "$PARA_HASH1" &&
+       git bisect bad > my_bisect_log.txt &&
+       grep "$PARA_HASH1 is first bad commit" my_bisect_log.txt
+'
+
+test_expect_success 'restricting bisection on one dir and a file' '
+       git bisect reset &&
+       git bisect start HEAD $HASH1 -- dir1 hello &&
+       para4=$(git rev-parse --verify HEAD) &&
+       test "$para4" = "$PARA_HASH4" &&
+       git bisect bad &&
+       hash3=$(git rev-parse --verify HEAD) &&
+       test "$hash3" = "$HASH3" &&
+       git bisect good &&
+       hash4=$(git rev-parse --verify HEAD) &&
+       test "$hash4" = "$HASH4" &&
+       git bisect good &&
+       para1=$(git rev-parse --verify HEAD) &&
+       test "$para1" = "$PARA_HASH1" &&
+       git bisect good > my_bisect_log.txt &&
+       grep "$PARA_HASH4 is first bad commit" my_bisect_log.txt
+'
+
 #
 #
 test_done
index 8073e0c3efb2ac01e4a81e722fc357bcab13f5d4..8a3304fb0b5901fb02435d3b77c3d049404f4e25 100755 (executable)
@@ -3,8 +3,10 @@
 test_description='merge-recursive: handle file mode'
 . ./test-lib.sh
 
-# Note that we follow "chmod +x F" with "update-index --chmod=+x F" to
-# help filesystems that do not have the executable bit.
+if ! test "$(git config --bool core.filemode)" = false
+then
+       test_set_prereq FILEMODE
+fi
 
 test_expect_success 'mode change in one branch: keep changed version' '
        : >file1 &&
@@ -15,11 +17,14 @@ test_expect_success 'mode change in one branch: keep changed version' '
        git add dummy &&
        git commit -m a &&
        git checkout -b b1 master &&
-       chmod +x file1 &&
-       git update-index --chmod=+x file1 &&
+       test_chmod +x file1 &&
        git commit -m b1 &&
        git checkout a1 &&
        git merge-recursive master -- a1 b1 &&
+       git ls-files -s file1 | grep ^100755
+'
+
+test_expect_success FILEMODE 'verify executable bit on file' '
        test -x file1
 '
 
@@ -28,8 +33,7 @@ test_expect_success 'mode change in both branches: expect conflict' '
        git checkout -b a2 master &&
        : >file2 &&
        H=$(git hash-object file2) &&
-       chmod +x file2 &&
-       git update-index --add --chmod=+x file2 &&
+       test_chmod +x file2 &&
        git commit -m a2 &&
        git checkout -b b2 master &&
        : >file2 &&
@@ -46,6 +50,10 @@ test_expect_success 'mode change in both branches: expect conflict' '
                echo "100644 $H 3       file2"
        ) >expect &&
        test_cmp actual expect &&
+       git ls-files -s file2 | grep ^100755
+'
+
+test_expect_success FILEMODE 'verify executable bit on file' '
        test -x file2
 '
 
diff --git a/t/t6034-merge-rename-nocruft.sh b/t/t6034-merge-rename-nocruft.sh
new file mode 100755 (executable)
index 0000000..65be95f
--- /dev/null
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+test_description='Merge-recursive merging renames'
+. ./test-lib.sh
+
+test_expect_success setup \
+'
+cat >A <<\EOF &&
+a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+c cccccccccccccccccccccccccccccccccccccccccccccccc
+d dddddddddddddddddddddddddddddddddddddddddddddddd
+e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+f ffffffffffffffffffffffffffffffffffffffffffffffff
+g gggggggggggggggggggggggggggggggggggggggggggggggg
+h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
+i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
+k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
+l llllllllllllllllllllllllllllllllllllllllllllllll
+m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
+o oooooooooooooooooooooooooooooooooooooooooooooooo
+EOF
+
+cat >M <<\EOF &&
+A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
+H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
+J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
+K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
+L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+EOF
+
+git add A M &&
+git commit -m "initial has A and M" &&
+git branch white &&
+git branch red &&
+git branch blue &&
+
+git checkout white &&
+sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
+sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+rm -f A M &&
+git update-index --add --remove A B M N &&
+git commit -m "white renames A->B, M->N" &&
+
+git checkout red &&
+echo created by red >R &&
+git update-index --add R &&
+git commit -m "red creates R" &&
+
+git checkout blue &&
+sed -e "/^o /s/.*/g : blue changes a line/" <A >B &&
+rm -f A &&
+mv B A &&
+git update-index A &&
+git commit -m "blue modify A" &&
+
+git checkout master'
+
+# This test broke in 65ac6e9c3f47807cb603af07a6a9e1a43bc119ae
+test_expect_success 'merge white into red (A->B,M->N)' \
+'
+       git checkout -b red-white red &&
+       git merge white &&
+       git write-tree >/dev/null || {
+               echo "BAD: merge did not complete"
+               return 1
+       }
+
+       test -f B || {
+               echo "BAD: B does not exist in working directory"
+               return 1
+       }
+       test -f N || {
+               echo "BAD: N does not exist in working directory"
+               return 1
+       }
+       test -f R || {
+               echo "BAD: R does not exist in working directory"
+               return 1
+       }
+
+       test -f A && {
+               echo "BAD: A still exists in working directory"
+               return 1
+       }
+       test -f M && {
+               echo "BAD: M still exists in working directory"
+               return 1
+       }
+       return 0
+'
+
+# This test broke in 8371234ecaaf6e14fe3f2082a855eff1bbd79ae9
+test_expect_success 'merge blue into white (A->B, mod A, A untracked)' \
+'
+       git checkout -b white-blue white &&
+       echo dirty >A &&
+       git merge blue &&
+       git write-tree >/dev/null || {
+               echo "BAD: merge did not complete"
+               return 1
+       }
+
+       test -f A || {
+               echo "BAD: A does not exist in working directory"
+               return 1
+       }
+       test `cat A` = dirty || {
+               echo "BAD: A content is wrong"
+               return 1
+       }
+       test -f B || {
+               echo "BAD: B does not exist in working directory"
+               return 1
+       }
+       test -f N || {
+               echo "BAD: N does not exist in working directory"
+               return 1
+       }
+       test -f M && {
+               echo "BAD: M still exists in working directory"
+               return 1
+       }
+       return 0
+'
+
+test_done
index ba9060190d31f0b57a461c114e1c856523845f78..3d6db4d386a0a009892fac490818294664d188fa 100755 (executable)
@@ -29,7 +29,9 @@ test_expect_success setup '
                git checkout -b b4 origin &&
                advance e &&
                advance f
-       )
+       ) &&
+       git checkout -b follower --track master &&
+       advance g
 '
 
 script='s/^..\(b.\)[    0-9a-f]*\[\([^]]*\)\].*/\1 \2/p'
@@ -56,6 +58,12 @@ test_expect_success 'checkout' '
        grep "have 1 and 1 different" actual
 '
 
+test_expect_success 'checkout with local tracked branch' '
+       git checkout master &&
+       git checkout follower >actual
+       grep "is ahead of" actual
+'
+
 test_expect_success 'status' '
        (
                cd test &&
index 8f5a06f7dd8383722022eb7a8abd0ff9bafa6f45..42f6fff373ba9707216279011b112c6c59af8780 100755 (executable)
@@ -83,13 +83,13 @@ test_expect_success 'merge-msg test #1' '
 '
 
 cat >expected <<EOF
-Merge branch 'left' of $TEST_DIRECTORY/$test
+Merge branch 'left' of $(pwd)
 EOF
 
 test_expect_success 'merge-msg test #2' '
 
        git checkout master &&
-       git fetch "$TEST_DIRECTORY/$test" left &&
+       git fetch "$(pwd)" left &&
 
        git fmt-merge-msg <.git/FETCH_HEAD >actual &&
        test_cmp expected actual
@@ -208,4 +208,36 @@ test_expect_success 'merge-msg test #5-2' '
        test_cmp expected actual
 '
 
+test_expect_success 'merge-msg -F' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
+       git config merge.summary yes &&
+
+       git checkout master &&
+       setdate &&
+       git fetch . left right &&
+
+       git fmt-merge-msg -F .git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'merge-msg -F in subdirectory' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
+       git config merge.summary yes &&
+
+       git checkout master &&
+       setdate &&
+       git fetch . left right &&
+       mkdir sub &&
+       cp .git/FETCH_HEAD sub/FETCH_HEAD &&
+       (
+               cd sub &&
+               git fmt-merge-msg -F FETCH_HEAD >../actual
+       ) &&
+       test_cmp expected actual
+'
+
 test_done
index 8bfae44a8392898453785f43c7e35b65e9c1f017..8052c86ad3516505765ab214f4801940c8cc1684 100755 (executable)
@@ -26,6 +26,13 @@ test_expect_success 'Create sample commit with known timestamp' '
        git tag -a -m "Tagging at $datestamp" testtag
 '
 
+test_expect_success 'Create upstream config' '
+       git update-ref refs/remotes/origin/master master &&
+       git remote add origin nowhere &&
+       git config branch.master.remote origin &&
+       git config branch.master.merge refs/heads/master
+'
+
 test_atom() {
        case "$1" in
                head) ref=refs/heads/master ;;
@@ -39,6 +46,7 @@ test_atom() {
 }
 
 test_atom head refname refs/heads/master
+test_atom head upstream refs/remotes/origin/master
 test_atom head objecttype commit
 test_atom head objectsize 171
 test_atom head objectname 67a36f10722846e891fbada1ba48ed035de75581
@@ -68,6 +76,7 @@ test_atom head contents 'Initial
 '
 
 test_atom tag refname refs/tags/testtag
+test_atom tag upstream ''
 test_atom tag objecttype tag
 test_atom tag objectsize 154
 test_atom tag objectname 98b46b1d36e5b07909de1b3886224e3e81e87322
@@ -203,6 +212,7 @@ test_expect_success 'Check format "rfc2822" date fields output' '
 
 cat >expected <<\EOF
 refs/heads/master
+refs/remotes/origin/master
 refs/tags/testtag
 EOF
 
@@ -214,6 +224,7 @@ test_expect_success 'Verify ascending sort' '
 
 cat >expected <<\EOF
 refs/tags/testtag
+refs/remotes/origin/master
 refs/heads/master
 EOF
 
@@ -224,6 +235,7 @@ test_expect_success 'Verify descending sort' '
 
 cat >expected <<\EOF
 'refs/heads/master'
+'refs/remotes/origin/master'
 'refs/tags/testtag'
 EOF
 
@@ -244,6 +256,7 @@ test_expect_success 'Quoting style: python' '
 
 cat >expected <<\EOF
 "refs/heads/master"
+"refs/remotes/origin/master"
 "refs/tags/testtag"
 EOF
 
@@ -273,16 +286,26 @@ test_expect_success 'Check short refname format' '
        test_cmp expected actual
 '
 
+cat >expected <<EOF
+origin/master
+EOF
+
+test_expect_success 'Check short upstream format' '
+       git for-each-ref --format="%(upstream:short)" refs/heads >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'Check for invalid refname format' '
        test_must_fail git for-each-ref --format="%(refname:INVALID)"
 '
 
 cat >expected <<\EOF
 heads/master
-master
+tags/master
 EOF
 
-test_expect_success 'Check ambiguous head and tag refs' '
+test_expect_success 'Check ambiguous head and tag refs (strict)' '
+       git config --bool core.warnambiguousrefs true &&
        git checkout -b newtag &&
        echo "Using $datestamp" > one &&
        git add one &&
@@ -293,12 +316,23 @@ test_expect_success 'Check ambiguous head and tag refs' '
        test_cmp expected actual
 '
 
+cat >expected <<\EOF
+heads/master
+master
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs (loose)' '
+       git config --bool core.warnambiguousrefs false &&
+       git for-each-ref --format "%(refname:short)" refs/heads/master refs/tags/master >actual &&
+       test_cmp expected actual
+'
+
 cat >expected <<\EOF
 heads/ambiguous
 ambiguous
 EOF
 
-test_expect_success 'Check ambiguous head and tag refs II' '
+test_expect_success 'Check ambiguous head and tag refs II (loose)' '
        git checkout master &&
        git tag ambiguous testtag^0 &&
        git branch ambiguous testtag^0 &&
index 8fb3a56838dd476b9b0923f835ce70bd95499f2b..10b8f8c44befdb4eb00b3959f8b29cbebb7a22e1 100755 (executable)
@@ -206,7 +206,7 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' '
 
 rm -f dirty dirty2
 
-test_expect_success 'git mv should overwrite symlink to a file' '
+test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' '
 
        rm -fr .git &&
        git init &&
@@ -225,7 +225,7 @@ test_expect_success 'git mv should overwrite symlink to a file' '
 
 rm -f moved symlink
 
-test_expect_success 'git mv should overwrite file with a symlink' '
+test_expect_success SYMLINKS 'git mv should overwrite file with a symlink' '
 
        rm -fr .git &&
        git init &&
index b81593780a2a6adaf34bb10293b607e3a303cfcd..7868af8f1896e621b98a2ae9b71492a68a96e584 100755 (executable)
@@ -16,12 +16,13 @@ test_expect_success setup '
                echo foo mmap bar_mmap
                echo foo_mmap bar mmap baz
        } >file &&
+       echo ww w >w &&
        echo x x xx x >x &&
        echo y yy >y &&
        echo zzz > z &&
        mkdir t &&
        echo test >t/t &&
-       git add file x y z t/t &&
+       git add file x y z t/t &&
        test_tick &&
        git commit -m initial
 '
@@ -48,6 +49,12 @@ do
                diff expected actual
        '
 
+       test_expect_success "grep -w $L (w)" '
+               : >expected &&
+               ! git grep -n -w -e "^w" >actual &&
+               test_cmp expected actual
+       '
+
        test_expect_success "grep -w $L (x)" '
                {
                        echo ${HC}x:1:x x xx x
@@ -118,6 +125,36 @@ do
 
 done
 
+cat >expected <<EOF
+file:foo mmap bar_mmap
+EOF
+
+test_expect_success 'grep -e A --and -e B' '
+       git grep -e "foo mmap" --and -e bar_mmap >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo_mmap bar mmap
+file:foo_mmap bar mmap baz
+EOF
+
+
+test_expect_success 'grep ( -e A --or -e B ) --and -e B' '
+       git grep \( -e foo_ --or -e baz \) \
+               --and -e " mmap" >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+EOF
+
+test_expect_success 'grep -e A --and --not -e B' '
+       git grep -e "foo mmap" --and --not -e bar_mmap >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'log grep setup' '
        echo a >>file &&
        test_tick &&
index 69501e2711f26a7c498d545520dcf47c00d9b1e1..73dbc4360b58c95ae135577031fb8e60b3f395f7 100755 (executable)
@@ -185,8 +185,9 @@ cba
 EOF
 test_expect_success \
        'listing tags with substring as pattern must print those matching' '
-       git tag -l "*a*" > actual &&
-       test_cmp expect actual
+       rm *a* &&
+       git tag -l "*a*" > current &&
+       test_cmp expect current
 '
 
 cat >expect <<EOF
@@ -580,28 +581,38 @@ test_expect_success \
 '
 
 # subsequent tests require gpg; check if it is available
-gpg --version >/dev/null
+gpg --version >/dev/null 2>/dev/null
 if [ $? -eq 127 ]; then
-       echo "gpg not found - skipping tag signing and verification tests"
-       test_done
-       exit
+       say "gpg not found - skipping tag signing and verification tests"
+else
+       # As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
+       # the gpg version 1.0.6 didn't parse trust packets correctly, so for
+       # that version, creation of signed tags using the generated key fails.
+       case "$(gpg --version)" in
+       'gpg (GnuPG) 1.0.6'*)
+               say "Skipping signed tag tests, because a bug in 1.0.6 version"
+               ;;
+       *)
+               test_set_prereq GPG
+               ;;
+       esac
 fi
 
 # trying to verify annotated non-signed tags:
 
-test_expect_success \
+test_expect_success GPG \
        'trying to verify an annotated non-signed tag should fail' '
        tag_exists annotated-tag &&
        test_must_fail git tag -v annotated-tag
 '
 
-test_expect_success \
+test_expect_success GPG \
        'trying to verify a file-annotated non-signed tag should fail' '
        tag_exists file-annotated-tag &&
        test_must_fail git tag -v file-annotated-tag
 '
 
-test_expect_success \
+test_expect_success GPG \
        'trying to verify two annotated non-signed tags should fail' '
        tag_exists annotated-tag file-annotated-tag &&
        test_must_fail git tag -v annotated-tag file-annotated-tag
@@ -609,17 +620,6 @@ test_expect_success \
 
 # creating and verifying signed tags:
 
-# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
-# the gpg version 1.0.6 didn't parse trust packets correctly, so for
-# that version, creation of signed tags using the generated key fails.
-case "$(gpg --version)" in
-'gpg (GnuPG) 1.0.6'*)
-       echo "Skipping signed tag tests, because a bug in 1.0.6 version"
-       test_done
-       exit
-       ;;
-esac
-
 # key generation info: gpg --homedir t/t7004 --gen-key
 # Type DSA and Elgamal, size 2048 bits, no expiration date.
 # Name and email: C O Mitter <committer@example.com>
@@ -633,7 +633,7 @@ export GNUPGHOME
 get_tag_header signed-tag $commit commit $time >expect
 echo 'A signed tag message' >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success 'creating a signed tag with -m message should succeed' '
+test_expect_success GPG 'creating a signed tag with -m message should succeed' '
        git tag -s -m "A signed tag message" signed-tag &&
        get_tag_msg signed-tag >actual &&
        test_cmp expect actual
@@ -642,7 +642,7 @@ test_expect_success 'creating a signed tag with -m message should succeed' '
 get_tag_header u-signed-tag $commit commit $time >expect
 echo 'Another message' >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success 'sign with a given key id' '
+test_expect_success GPG 'sign with a given key id' '
 
        git tag -u committer@example.com -m "Another message" u-signed-tag &&
        get_tag_msg u-signed-tag >actual &&
@@ -650,14 +650,14 @@ test_expect_success 'sign with a given key id' '
 
 '
 
-test_expect_success 'sign with an unknown id (1)' '
+test_expect_success GPG 'sign with an unknown id (1)' '
 
        test_must_fail git tag -u author@example.com \
                -m "Another message" o-signed-tag
 
 '
 
-test_expect_success 'sign with an unknown id (2)' '
+test_expect_success GPG 'sign with an unknown id (2)' '
 
        test_must_fail git tag -u DEADBEEF -m "Another message" o-signed-tag
 
@@ -674,7 +674,7 @@ chmod +x fakeeditor
 get_tag_header implied-sign $commit commit $time >expect
 ./fakeeditor >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success '-u implies signed tag' '
+test_expect_success GPG '-u implies signed tag' '
        GIT_EDITOR=./fakeeditor git tag -u CDDE430D implied-sign &&
        get_tag_msg implied-sign >actual &&
        test_cmp expect actual
@@ -687,7 +687,7 @@ EOF
 get_tag_header file-signed-tag $commit commit $time >expect
 cat sigmsgfile >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag with -F messagefile should succeed' '
        git tag -s -F sigmsgfile file-signed-tag &&
        get_tag_msg file-signed-tag >actual &&
@@ -701,7 +701,7 @@ EOF
 get_tag_header stdin-signed-tag $commit commit $time >expect
 cat siginputmsg >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success 'creating a signed tag with -F - should succeed' '
+test_expect_success GPG 'creating a signed tag with -F - should succeed' '
        git tag -s -F - stdin-signed-tag <siginputmsg &&
        get_tag_msg stdin-signed-tag >actual &&
        test_cmp expect actual
@@ -710,13 +710,13 @@ test_expect_success 'creating a signed tag with -F - should succeed' '
 get_tag_header implied-annotate $commit commit $time >expect
 ./fakeeditor >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success '-s implies annotated tag' '
+test_expect_success GPG '-s implies annotated tag' '
        GIT_EDITOR=./fakeeditor git tag -s implied-annotate &&
        get_tag_msg implied-annotate >actual &&
        test_cmp expect actual
 '
 
-test_expect_success \
+test_expect_success GPG \
        'trying to create a signed tag with non-existing -F file should fail' '
        ! test -f nonexistingfile &&
        ! tag_exists nosigtag &&
@@ -724,13 +724,13 @@ test_expect_success \
        ! tag_exists nosigtag
 '
 
-test_expect_success 'verifying a signed tag should succeed' \
+test_expect_success GPG 'verifying a signed tag should succeed' \
        'git tag -v signed-tag'
 
-test_expect_success 'verifying two signed tags in one command should succeed' \
+test_expect_success GPG 'verifying two signed tags in one command should succeed' \
        'git tag -v signed-tag file-signed-tag'
 
-test_expect_success \
+test_expect_success GPG \
        'verifying many signed and non-signed tags should fail' '
        test_must_fail git tag -v signed-tag annotated-tag &&
        test_must_fail git tag -v file-annotated-tag file-signed-tag &&
@@ -739,7 +739,7 @@ test_expect_success \
        test_must_fail git tag -v signed-tag annotated-tag file-signed-tag
 '
 
-test_expect_success 'verifying a forged tag should fail' '
+test_expect_success GPG 'verifying a forged tag should fail' '
        forged=$(git cat-file tag signed-tag |
                sed -e "s/signed-tag/forged-tag/" |
                git mktag) &&
@@ -751,7 +751,7 @@ test_expect_success 'verifying a forged tag should fail' '
 
 get_tag_header empty-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag with an empty -m message should succeed' '
        git tag -s -m "" empty-signed-tag &&
        get_tag_msg empty-signed-tag >actual &&
@@ -762,7 +762,7 @@ test_expect_success \
 >sigemptyfile
 get_tag_header emptyfile-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag with an empty -F messagefile should succeed' '
        git tag -s -F sigemptyfile emptyfile-signed-tag &&
        get_tag_msg emptyfile-signed-tag >actual &&
@@ -785,7 +785,7 @@ Trailing spaces
 Trailing blank lines
 EOF
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'extra blanks in the message for a signed tag should be removed' '
        git tag -s -F sigblanksfile blanks-signed-tag &&
        get_tag_msg blanks-signed-tag >actual &&
@@ -795,7 +795,7 @@ test_expect_success \
 
 get_tag_header blank-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag with a blank -m message should succeed' '
        git tag -s -m "     " blank-signed-tag &&
        get_tag_msg blank-signed-tag >actual &&
@@ -808,7 +808,7 @@ echo ''      >>sigblankfile
 echo '  '    >>sigblankfile
 get_tag_header blankfile-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag with blank -F file with spaces should succeed' '
        git tag -s -F sigblankfile blankfile-signed-tag &&
        get_tag_msg blankfile-signed-tag >actual &&
@@ -819,7 +819,7 @@ test_expect_success \
 printf '      ' >sigblanknonlfile
 get_tag_header blanknonlfile-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag with spaces and no newline should succeed' '
        git tag -s -F sigblanknonlfile blanknonlfile-signed-tag &&
        get_tag_msg blanknonlfile-signed-tag >actual &&
@@ -856,7 +856,7 @@ Another line.
 Last line.
 EOF
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag with a -F file with #comments should succeed' '
        git tag -s -F sigcommentsfile comments-signed-tag &&
        get_tag_msg comments-signed-tag >actual &&
@@ -866,7 +866,7 @@ test_expect_success \
 
 get_tag_header comment-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag with #commented -m message should succeed' '
        git tag -s -m "#comment" comment-signed-tag &&
        get_tag_msg comment-signed-tag >actual &&
@@ -879,7 +879,7 @@ echo ''         >>sigcommentfile
 echo '####'     >>sigcommentfile
 get_tag_header commentfile-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag with #commented -F messagefile should succeed' '
        git tag -s -F sigcommentfile commentfile-signed-tag &&
        get_tag_msg commentfile-signed-tag >actual &&
@@ -890,7 +890,7 @@ test_expect_success \
 printf '#comment' >sigcommentnonlfile
 get_tag_header commentnonlfile-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag with a #comment and no newline should succeed' '
        git tag -s -F sigcommentnonlfile commentnonlfile-signed-tag &&
        get_tag_msg commentnonlfile-signed-tag >actual &&
@@ -900,7 +900,7 @@ test_expect_success \
 
 # listing messages for signed tags:
 
-test_expect_success \
+test_expect_success GPG \
        'listing the one-line message of a signed tag should succeed' '
        git tag -s -m "A message line signed" stag-one-line &&
 
@@ -925,7 +925,7 @@ test_expect_success \
        test_cmp expect actual
 '
 
-test_expect_success \
+test_expect_success GPG \
        'listing the zero-lines message of a signed tag should succeed' '
        git tag -s -m "" stag-zero-lines &&
 
@@ -953,7 +953,7 @@ test_expect_success \
 echo 'stag line one' >sigtagmsg
 echo 'stag line two' >>sigtagmsg
 echo 'stag line three' >>sigtagmsg
-test_expect_success \
+test_expect_success GPG \
        'listing many message lines of a signed tag should succeed' '
        git tag -s -F sigtagmsg stag-lines &&
 
@@ -998,12 +998,12 @@ test_expect_success \
 
 tree=$(git rev-parse HEAD^{tree})
 blob=$(git rev-parse HEAD:foo)
-tag=$(git rev-parse signed-tag)
+tag=$(git rev-parse signed-tag 2>/dev/null)
 
 get_tag_header tree-signed-tag $tree tree $time >expect
 echo "A message for a tree" >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag pointing to a tree should succeed' '
        git tag -s -m "A message for a tree" tree-signed-tag HEAD^{tree} &&
        get_tag_msg tree-signed-tag >actual &&
@@ -1013,7 +1013,7 @@ test_expect_success \
 get_tag_header blob-signed-tag $blob blob $time >expect
 echo "A message for a blob" >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag pointing to a blob should succeed' '
        git tag -s -m "A message for a blob" blob-signed-tag HEAD:foo &&
        get_tag_msg blob-signed-tag >actual &&
@@ -1023,7 +1023,7 @@ test_expect_success \
 get_tag_header tag-signed-tag $tag tag $time >expect
 echo "A message for another tag" >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
        'creating a signed tag pointing to another tag should succeed' '
        git tag -s -m "A message for another tag" tag-signed-tag signed-tag &&
        get_tag_msg tag-signed-tag >actual &&
@@ -1032,7 +1032,7 @@ test_expect_success \
 
 # try to sign with bad user.signingkey
 git config user.signingkey BobTheMouse
-test_expect_success \
+test_expect_success GPG \
        'git tag -s fails if gpg is misconfigured' \
        'test_must_fail git tag -s -m tail tag-gpg-failure'
 git config --unset user.signingkey
@@ -1040,7 +1040,7 @@ git config --unset user.signingkey
 # try to verify without gpg:
 
 rm -rf gpghome
-test_expect_success \
+test_expect_success GPG \
        'verify signed tag fails when public key is not present' \
        'test_must_fail git tag -v signed-tag'
 
index 2d919d69ef110408b820c76185d6b8da63ea183e..b647957d75fa0d0ce4d88c7c3c7243f31af38b4a 100755 (executable)
@@ -7,6 +7,7 @@ test_description='GIT_EDITOR, core.editor, and stuff'
 for i in GIT_EDITOR core_editor EDITOR VISUAL vi
 do
        cat >e-$i.sh <<-EOF
+       #!$SHELL_PATH
        echo "Edited by $i" >"\$1"
        EOF
        chmod +x e-$i.sh
@@ -87,30 +88,26 @@ do
        '
 done
 
+if ! echo 'echo space > "$1"' > "e space.sh"
+then
+       say "Skipping; FS does not support spaces in filenames"
+       test_done
+fi
+
 test_expect_success 'editor with a space' '
 
-       if echo "echo space > \"\$1\"" > "e space.sh"
-       then
-               chmod a+x "e space.sh" &&
-               GIT_EDITOR="./e\ space.sh" git commit --amend &&
-               test space = "$(git show -s --pretty=format:%s)"
-       else
-               say "Skipping; FS does not support spaces in filenames"
-       fi
+       chmod a+x "e space.sh" &&
+       GIT_EDITOR="./e\ space.sh" git commit --amend &&
+       test space = "$(git show -s --pretty=format:%s)"
 
 '
 
 unset GIT_EDITOR
 test_expect_success 'core.editor with a space' '
 
-       if test -f "e space.sh"
-       then
-               git config core.editor \"./e\ space.sh\" &&
-               git commit --amend &&
-               test space = "$(git show -s --pretty=format:%s)"
-       else
-               say "Skipping; FS does not support spaces in filenames"
-       fi
+       git config core.editor \"./e\ space.sh\" &&
+       git commit --amend &&
+       test space = "$(git show -s --pretty=format:%s)"
 
 '
 
index bdb808af1aefae97c297f7845a8cd3b595404e89..ebfd34df36068f8808406a98d371731fb85012c4 100755 (executable)
@@ -534,4 +534,12 @@ test_expect_success 'failing checkout -b should not break working tree' '
 
 '
 
+test_expect_success 'switch out of non-branch' '
+       git reset --hard master &&
+       git checkout master^0 &&
+       echo modified >one &&
+       test_must_fail git checkout renamer 2>error.log &&
+       ! grep "^Previous HEAD" error.log
+'
+
 test_done
index 1636fac2a43e30a99674df98ff4544ee04612cc5..929d5d4d3b9d55f570cef1617a0716b17265c988 100755 (executable)
@@ -373,9 +373,9 @@ test_expect_success 'removal failure' '
 
        mkdir foo &&
        touch foo/bar &&
-       exec <foo/bar &&
-       chmod 0 foo &&
-       test_must_fail git clean -f -d
+       (exec <foo/bar &&
+        chmod 0 foo &&
+        test_must_fail git clean -f -d)
 
 '
 chmod 755 foo
index af690ec6c1f36871dbd0044d22ab78ab95103541..0f2ccc6cf0123951d9bdbb880931868f29de5b4e 100755 (executable)
@@ -64,6 +64,16 @@ test_expect_success 'submodule add' '
        )
 '
 
+test_expect_success 'submodule add --branch' '
+       (
+               cd addtest &&
+               git submodule add -b initial "$submodurl" submod-branch &&
+               git submodule init &&
+               cd submod-branch &&
+               git branch | grep initial
+       )
+'
+
 test_expect_success 'submodule add with ./ in path' '
        (
                cd addtest &&
index aa6c44ce4f5855bd4f18f737c7b1239407549eab..9a21f783d3a5fedaa56df26919ab8e0456872e90 100755 (executable)
@@ -54,13 +54,13 @@ test_expect_success setup '
        git merge -s ours a
 '
 
-test_expect_failure 'merging with modify/modify conflict' '
+test_expect_success 'merging with modify/modify conflict' '
 
        git checkout -b test1 a &&
        test_must_fail git merge b &&
        test -f .git/MERGE_MSG &&
-       git diff
-
+       git diff &&
+       test -n "$(git ls-files -u)"
 '
 
 test_expect_success 'merging with a modify/modify conflict between merge bases' '
index 5998baf27b5ad0fb32bf0fd42023d029a65d0e6b..8eec0fa9bc7278981b9a16571c588ede0a94d341 100755 (executable)
@@ -183,4 +183,14 @@ test_expect_success 'commit message from stdin' '
        commit_msg_is "Log with foo word"
 '
 
+test_expect_success 'commit -F overrides -t' '
+       (
+               cd subdir &&
+               echo "-F log" > f.log &&
+               echo "-t template" > t.template &&
+               git commit --allow-empty -F f.log -t t.template
+       ) &&
+       commit_msg_is "-F log"
+'
+
 test_done
index b4e2b4db8421c24cb714fcad7cd6ec00ea88c016..e2ef53228ecc6162afdcce78109d426e154b7caa 100755 (executable)
@@ -38,7 +38,7 @@ test_expect_success \
        "echo King of the bongo >file &&
        test_must_fail git commit -m foo -a file"
 
-test_expect_success \
+test_expect_success PERL \
        "using paths with --interactive" \
        "echo bong-o-bong >file &&
        ! (echo 7 | git commit -m foo --interactive file)"
@@ -119,7 +119,7 @@ test_expect_success \
        "echo 'gak' >file && \
         git commit -m 'author' --author 'Rubber Duck <rduck@convoy.org>' -a"
 
-test_expect_success \
+test_expect_success PERL \
        "interactive add" \
        "echo 7 | git commit --interactive | grep 'What now'"
 
index ad42c78d7c21497a6ebb4e9cef4efd6334f947da..56cd866019dcc871e5dea684f1d879fcf2c1b884 100755 (executable)
@@ -234,7 +234,7 @@ cat >.git/FAKE_EDITOR <<EOF
 # kill -TERM command added below.
 EOF
 
-test_expect_success 'a SIGTERM should break locks' '
+test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' '
        echo >>negative &&
        ! "$SHELL_PATH" -c '\''
          echo kill -TERM $$ >> .git/FAKE_EDITOR
diff --git a/t/t7502-status.sh b/t/t7502-status.sh
deleted file mode 100755 (executable)
index 93f875f..0000000
+++ /dev/null
@@ -1,400 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2007 Johannes E. Schindelin
-#
-
-test_description='git status'
-
-. ./test-lib.sh
-
-test_expect_success 'setup' '
-       : > tracked &&
-       : > modified &&
-       mkdir dir1 &&
-       : > dir1/tracked &&
-       : > dir1/modified &&
-       mkdir dir2 &&
-       : > dir1/tracked &&
-       : > dir1/modified &&
-       git add . &&
-
-       git status >output &&
-
-       test_tick &&
-       git commit -m initial &&
-       : > untracked &&
-       : > dir1/untracked &&
-       : > dir2/untracked &&
-       echo 1 > dir1/modified &&
-       echo 2 > dir2/modified &&
-       echo 3 > dir2/added &&
-       git add dir2/added
-'
-
-test_expect_success 'status (1)' '
-
-       grep "use \"git rm --cached <file>\.\.\.\" to unstage" output
-
-'
-
-cat > expect << \EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#      new file:   dir2/added
-#
-# Changed but not updated:
-#   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
-#
-#      modified:   dir1/modified
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#      dir1/untracked
-#      dir2/modified
-#      dir2/untracked
-#      expect
-#      output
-#      untracked
-EOF
-
-test_expect_success 'status (2)' '
-
-       git status > output &&
-       test_cmp expect output
-
-'
-
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#      new file:   dir2/added
-#
-# Changed but not updated:
-#   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
-#
-#      modified:   dir1/modified
-#
-# Untracked files not listed (use -u option to show untracked files)
-EOF
-test_expect_success 'status -uno' '
-       mkdir dir3 &&
-       : > dir3/untracked1 &&
-       : > dir3/untracked2 &&
-       git status -uno >output &&
-       test_cmp expect output
-'
-
-test_expect_success 'status (status.showUntrackedFiles no)' '
-       git config status.showuntrackedfiles no
-       git status >output &&
-       test_cmp expect output
-'
-
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#      new file:   dir2/added
-#
-# Changed but not updated:
-#   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
-#
-#      modified:   dir1/modified
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#      dir1/untracked
-#      dir2/modified
-#      dir2/untracked
-#      dir3/
-#      expect
-#      output
-#      untracked
-EOF
-test_expect_success 'status -unormal' '
-       git status -unormal >output &&
-       test_cmp expect output
-'
-
-test_expect_success 'status (status.showUntrackedFiles normal)' '
-       git config status.showuntrackedfiles normal
-       git status >output &&
-       test_cmp expect output
-'
-
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#      new file:   dir2/added
-#
-# Changed but not updated:
-#   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
-#
-#      modified:   dir1/modified
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#      dir1/untracked
-#      dir2/modified
-#      dir2/untracked
-#      dir3/untracked1
-#      dir3/untracked2
-#      expect
-#      output
-#      untracked
-EOF
-test_expect_success 'status -uall' '
-       git status -uall >output &&
-       test_cmp expect output
-'
-test_expect_success 'status (status.showUntrackedFiles all)' '
-       git config status.showuntrackedfiles all
-       git status >output &&
-       rm -rf dir3 &&
-       git config --unset status.showuntrackedfiles &&
-       test_cmp expect output
-'
-
-cat > expect << \EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#      new file:   ../dir2/added
-#
-# Changed but not updated:
-#   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
-#
-#      modified:   modified
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#      untracked
-#      ../dir2/modified
-#      ../dir2/untracked
-#      ../expect
-#      ../output
-#      ../untracked
-EOF
-
-test_expect_success 'status with relative paths' '
-
-       (cd dir1 && git status) > output &&
-       test_cmp expect output
-
-'
-
-cat > expect << \EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#      new file:   dir2/added
-#
-# Changed but not updated:
-#   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
-#
-#      modified:   dir1/modified
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#      dir1/untracked
-#      dir2/modified
-#      dir2/untracked
-#      expect
-#      output
-#      untracked
-EOF
-
-test_expect_success 'status without relative paths' '
-
-       git config status.relativePaths false
-       (cd dir1 && git status) > output &&
-       test_cmp expect output
-
-'
-
-cat <<EOF >expect
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#      modified:   dir1/modified
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#      dir1/untracked
-#      dir2/
-#      expect
-#      output
-#      untracked
-EOF
-test_expect_success 'status of partial commit excluding new file in index' '
-       git status dir1/modified >output &&
-       test_cmp expect output
-'
-
-test_expect_success 'setup status submodule summary' '
-       test_create_repo sm && (
-               cd sm &&
-               >foo &&
-               git add foo &&
-               git commit -m "Add foo"
-       ) &&
-       git add sm
-'
-
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#      new file:   dir2/added
-#      new file:   sm
-#
-# Changed but not updated:
-#   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
-#
-#      modified:   dir1/modified
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#      dir1/untracked
-#      dir2/modified
-#      dir2/untracked
-#      expect
-#      output
-#      untracked
-EOF
-test_expect_success 'status submodule summary is disabled by default' '
-       git status >output &&
-       test_cmp expect output
-'
-
-# we expect the same as the previous test
-test_expect_success 'status --untracked-files=all does not show submodule' '
-       git status --untracked-files=all >output &&
-       test_cmp expect output
-'
-
-head=$(cd sm && git rev-parse --short=7 --verify HEAD)
-
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#      new file:   dir2/added
-#      new file:   sm
-#
-# Changed but not updated:
-#   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
-#
-#      modified:   dir1/modified
-#
-# Modified submodules:
-#
-# * sm 0000000...$head (1):
-#   > Add foo
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#      dir1/untracked
-#      dir2/modified
-#      dir2/untracked
-#      expect
-#      output
-#      untracked
-EOF
-test_expect_success 'status submodule summary' '
-       git config status.submodulesummary 10 &&
-       git status >output &&
-       test_cmp expect output
-'
-
-
-cat >expect <<EOF
-# On branch master
-# Changed but not updated:
-#   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
-#
-#      modified:   dir1/modified
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#      dir1/untracked
-#      dir2/modified
-#      dir2/untracked
-#      expect
-#      output
-#      untracked
-no changes added to commit (use "git add" and/or "git commit -a")
-EOF
-test_expect_success 'status submodule summary (clean submodule)' '
-       git commit -m "commit submodule" &&
-       git config status.submodulesummary 10 &&
-       test_must_fail git status >output &&
-       test_cmp expect output
-'
-
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD^1 <file>..." to unstage)
-#
-#      new file:   dir2/added
-#      new file:   sm
-#
-# Changed but not updated:
-#   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
-#
-#      modified:   dir1/modified
-#
-# Modified submodules:
-#
-# * sm 0000000...$head (1):
-#   > Add foo
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#      dir1/untracked
-#      dir2/modified
-#      dir2/untracked
-#      expect
-#      output
-#      untracked
-EOF
-test_expect_success 'status submodule summary (--amend)' '
-       git config status.submodulesummary 10 &&
-       git status --amend >output &&
-       test_cmp expect output
-'
-
-test_done
index b06909599564a1c8afa027b0f9c71ef6bb61d6e4..8528f64c8d1491fd3c279f030b6f8aee2050cdf7 100755 (executable)
@@ -69,7 +69,7 @@ test_expect_success '--no-verify with failing hook' '
 '
 
 chmod -x "$HOOK"
-test_expect_success 'with non-executable hook' '
+test_expect_success POSIXPERM 'with non-executable hook' '
 
        echo "content" >> file &&
        git add file &&
@@ -77,7 +77,7 @@ test_expect_success 'with non-executable hook' '
 
 '
 
-test_expect_success '--no-verify with non-executable hook' '
+test_expect_success POSIXPERM '--no-verify with non-executable hook' '
 
        echo "more content" >> file &&
        git add file &&
index 47680e6df41c2bc14f23514b010a8aefb3fedcd7..1f53ea8090355c9a351da1983388e1a49fd88ae3 100755 (executable)
@@ -136,7 +136,7 @@ test_expect_success '--no-verify with failing hook (editor)' '
 '
 
 chmod -x "$HOOK"
-test_expect_success 'with non-executable hook' '
+test_expect_success POSIXPERM 'with non-executable hook' '
 
        echo "content" >> file &&
        git add file &&
@@ -144,7 +144,7 @@ test_expect_success 'with non-executable hook' '
 
 '
 
-test_expect_success 'with non-executable hook (editor)' '
+test_expect_success POSIXPERM 'with non-executable hook (editor)' '
 
        echo "content again" >> file &&
        git add file &&
@@ -153,7 +153,7 @@ test_expect_success 'with non-executable hook (editor)' '
 
 '
 
-test_expect_success '--no-verify with non-executable hook' '
+test_expect_success POSIXPERM '--no-verify with non-executable hook' '
 
        echo "more content" >> file &&
        git add file &&
@@ -161,7 +161,7 @@ test_expect_success '--no-verify with non-executable hook' '
 
 '
 
-test_expect_success '--no-verify with non-executable hook (editor)' '
+test_expect_success POSIXPERM '--no-verify with non-executable hook (editor)' '
 
        echo "even more content" >> file &&
        git add file &&
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
new file mode 100755 (executable)
index 0000000..93f875f
--- /dev/null
@@ -0,0 +1,400 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='git status'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       : > tracked &&
+       : > modified &&
+       mkdir dir1 &&
+       : > dir1/tracked &&
+       : > dir1/modified &&
+       mkdir dir2 &&
+       : > dir1/tracked &&
+       : > dir1/modified &&
+       git add . &&
+
+       git status >output &&
+
+       test_tick &&
+       git commit -m initial &&
+       : > untracked &&
+       : > dir1/untracked &&
+       : > dir2/untracked &&
+       echo 1 > dir1/modified &&
+       echo 2 > dir2/modified &&
+       echo 3 > dir2/added &&
+       git add dir2/added
+'
+
+test_expect_success 'status (1)' '
+
+       grep "use \"git rm --cached <file>\.\.\.\" to unstage" output
+
+'
+
+cat > expect << \EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+EOF
+
+test_expect_success 'status (2)' '
+
+       git status > output &&
+       test_cmp expect output
+
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   dir1/modified
+#
+# Untracked files not listed (use -u option to show untracked files)
+EOF
+test_expect_success 'status -uno' '
+       mkdir dir3 &&
+       : > dir3/untracked1 &&
+       : > dir3/untracked2 &&
+       git status -uno >output &&
+       test_cmp expect output
+'
+
+test_expect_success 'status (status.showUntrackedFiles no)' '
+       git config status.showuntrackedfiles no
+       git status >output &&
+       test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      dir3/
+#      expect
+#      output
+#      untracked
+EOF
+test_expect_success 'status -unormal' '
+       git status -unormal >output &&
+       test_cmp expect output
+'
+
+test_expect_success 'status (status.showUntrackedFiles normal)' '
+       git config status.showuntrackedfiles normal
+       git status >output &&
+       test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      dir3/untracked1
+#      dir3/untracked2
+#      expect
+#      output
+#      untracked
+EOF
+test_expect_success 'status -uall' '
+       git status -uall >output &&
+       test_cmp expect output
+'
+test_expect_success 'status (status.showUntrackedFiles all)' '
+       git config status.showuntrackedfiles all
+       git status >output &&
+       rm -rf dir3 &&
+       git config --unset status.showuntrackedfiles &&
+       test_cmp expect output
+'
+
+cat > expect << \EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   ../dir2/added
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      untracked
+#      ../dir2/modified
+#      ../dir2/untracked
+#      ../expect
+#      ../output
+#      ../untracked
+EOF
+
+test_expect_success 'status with relative paths' '
+
+       (cd dir1 && git status) > output &&
+       test_cmp expect output
+
+'
+
+cat > expect << \EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+EOF
+
+test_expect_success 'status without relative paths' '
+
+       git config status.relativePaths false
+       (cd dir1 && git status) > output &&
+       test_cmp expect output
+
+'
+
+cat <<EOF >expect
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/
+#      expect
+#      output
+#      untracked
+EOF
+test_expect_success 'status of partial commit excluding new file in index' '
+       git status dir1/modified >output &&
+       test_cmp expect output
+'
+
+test_expect_success 'setup status submodule summary' '
+       test_create_repo sm && (
+               cd sm &&
+               >foo &&
+               git add foo &&
+               git commit -m "Add foo"
+       ) &&
+       git add sm
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#      new file:   sm
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+EOF
+test_expect_success 'status submodule summary is disabled by default' '
+       git status >output &&
+       test_cmp expect output
+'
+
+# we expect the same as the previous test
+test_expect_success 'status --untracked-files=all does not show submodule' '
+       git status --untracked-files=all >output &&
+       test_cmp expect output
+'
+
+head=$(cd sm && git rev-parse --short=7 --verify HEAD)
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#      new file:   sm
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   dir1/modified
+#
+# Modified submodules:
+#
+# * sm 0000000...$head (1):
+#   > Add foo
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+EOF
+test_expect_success 'status submodule summary' '
+       git config status.submodulesummary 10 &&
+       git status >output &&
+       test_cmp expect output
+'
+
+
+cat >expect <<EOF
+# On branch master
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+test_expect_success 'status submodule summary (clean submodule)' '
+       git commit -m "commit submodule" &&
+       git config status.submodulesummary 10 &&
+       test_must_fail git status >output &&
+       test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD^1 <file>..." to unstage)
+#
+#      new file:   dir2/added
+#      new file:   sm
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   dir1/modified
+#
+# Modified submodules:
+#
+# * sm 0000000...$head (1):
+#   > Add foo
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+EOF
+test_expect_success 'status submodule summary (--amend)' '
+       git config status.submodulesummary 10 &&
+       git status --amend >output &&
+       test_cmp expect output
+'
+
+test_done
index 6b29bff782f5a46bb6970d70598fd3be82c679fa..87c9b0e121db6c8ab7074e6c4968cd06d37851fe 100755 (executable)
@@ -69,7 +69,7 @@ test_expect_success 'packed obs in alt ODB are repacked even when local repo is
        done
 '
 
-test_expect_failure 'packed obs in alt ODB are repacked when local repo has packs' '
+test_expect_success 'packed obs in alt ODB are repacked when local repo has packs' '
        rm -f .git/objects/pack/* &&
        echo new_content >> file1 &&
        git add file1 &&
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
new file mode 100755 (executable)
index 0000000..ebdccf9
--- /dev/null
@@ -0,0 +1,216 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 David Aguilar
+#
+
+test_description='git-difftool
+
+Testing basic diff tool invocation
+'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+       say 'skipping difftool tests, perl not available'
+       test_done
+fi
+
+remove_config_vars()
+{
+       # Unset all config variables used by git-difftool
+       git config --unset diff.tool
+       git config --unset difftool.test-tool.cmd
+       git config --unset difftool.prompt
+       git config --unset merge.tool
+       git config --unset mergetool.test-tool.cmd
+       return 0
+}
+
+restore_test_defaults()
+{
+       # Restores the test defaults used by several tests
+       remove_config_vars
+       unset GIT_DIFF_TOOL
+       unset GIT_MERGE_TOOL
+       unset GIT_DIFFTOOL_PROMPT
+       unset GIT_DIFFTOOL_NO_PROMPT
+       git config diff.tool test-tool &&
+       git config difftool.test-tool.cmd 'cat $LOCAL'
+}
+
+prompt_given()
+{
+       prompt="$1"
+       test "$prompt" = "Hit return to launch 'test-tool': branch"
+}
+
+# Create a file on master and change it on branch
+test_expect_success 'setup' '
+       echo master >file &&
+       git add file &&
+       git commit -m "added file" &&
+
+       git checkout -b branch master &&
+       echo branch >file &&
+       git commit -a -m "branch changed file" &&
+       git checkout master
+'
+
+# Configure a custom difftool.<tool>.cmd and use it
+test_expect_success 'custom commands' '
+       restore_test_defaults &&
+       git config difftool.test-tool.cmd "cat \$REMOTE" &&
+
+       diff=$(git difftool --no-prompt branch) &&
+       test "$diff" = "master" &&
+
+       restore_test_defaults &&
+       diff=$(git difftool --no-prompt branch) &&
+       test "$diff" = "branch"
+'
+
+# Ensures that git-difftool ignores bogus --tool values
+test_expect_success 'difftool ignores bad --tool values' '
+       diff=$(git difftool --no-prompt --tool=bogus-tool branch)
+       test "$?" = 1 &&
+       test "$diff" = ""
+'
+
+# Specify the diff tool using $GIT_DIFF_TOOL
+test_expect_success 'GIT_DIFF_TOOL variable' '
+       git config --unset diff.tool
+       GIT_DIFF_TOOL=test-tool &&
+       export GIT_DIFF_TOOL &&
+
+       diff=$(git difftool --no-prompt branch) &&
+       test "$diff" = "branch" &&
+
+       restore_test_defaults
+'
+
+# Test the $GIT_*_TOOL variables and ensure
+# that $GIT_DIFF_TOOL always wins unless --tool is specified
+test_expect_success 'GIT_DIFF_TOOL overrides' '
+       git config diff.tool bogus-tool &&
+       git config merge.tool bogus-tool &&
+
+       GIT_MERGE_TOOL=test-tool &&
+       export GIT_MERGE_TOOL &&
+       diff=$(git difftool --no-prompt branch) &&
+       test "$diff" = "branch" &&
+       unset GIT_MERGE_TOOL &&
+
+       GIT_MERGE_TOOL=bogus-tool &&
+       GIT_DIFF_TOOL=test-tool &&
+       export GIT_MERGE_TOOL &&
+       export GIT_DIFF_TOOL &&
+
+       diff=$(git difftool --no-prompt branch) &&
+       test "$diff" = "branch" &&
+
+       GIT_DIFF_TOOL=bogus-tool &&
+       export GIT_DIFF_TOOL &&
+
+       diff=$(git difftool --no-prompt --tool=test-tool branch) &&
+       test "$diff" = "branch" &&
+
+       restore_test_defaults
+'
+
+# 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' '
+       GIT_DIFFTOOL_NO_PROMPT=true &&
+       export GIT_DIFFTOOL_NO_PROMPT &&
+
+       diff=$(git difftool branch) &&
+       test "$diff" = "branch" &&
+
+       restore_test_defaults
+'
+
+# git-difftool supports the difftool.prompt variable.
+# Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false
+test_expect_success 'GIT_DIFFTOOL_PROMPT variable' '
+       git config difftool.prompt false &&
+       GIT_DIFFTOOL_PROMPT=true &&
+       export GIT_DIFFTOOL_PROMPT &&
+
+       prompt=$(echo | git difftool --prompt branch | tail -1) &&
+       prompt_given "$prompt" &&
+
+       restore_test_defaults
+'
+
+# Test that we don't have to pass --no-prompt when difftool.prompt is false
+test_expect_success 'difftool.prompt config variable is false' '
+       git config difftool.prompt false &&
+
+       diff=$(git difftool branch) &&
+       test "$diff" = "branch" &&
+
+       restore_test_defaults
+'
+
+# Test that the -y flag can override difftool.prompt = true
+test_expect_success 'difftool.prompt can overridden with -y' '
+       git config difftool.prompt true &&
+
+       diff=$(git difftool -y branch) &&
+       test "$diff" = "branch" &&
+
+       restore_test_defaults
+'
+
+# Test that the --prompt flag can override difftool.prompt = false
+test_expect_success 'difftool.prompt can overridden with --prompt' '
+       git config difftool.prompt false &&
+
+       prompt=$(echo | git difftool --prompt branch | tail -1) &&
+       prompt_given "$prompt" &&
+
+       restore_test_defaults
+'
+
+# Test that the last flag passed on the command-line wins
+test_expect_success 'difftool last flag wins' '
+       diff=$(git difftool --prompt --no-prompt branch) &&
+       test "$diff" = "branch" &&
+
+       restore_test_defaults &&
+
+       prompt=$(echo | git difftool --no-prompt --prompt branch | tail -1) &&
+       prompt_given "$prompt" &&
+
+       restore_test_defaults
+'
+
+# git-difftool falls back to git-mergetool config variables
+# so test that behavior here
+test_expect_success 'difftool + mergetool config variables' '
+       remove_config_vars
+       git config merge.tool test-tool &&
+       git config mergetool.test-tool.cmd "cat \$LOCAL" &&
+
+       diff=$(git difftool --no-prompt branch) &&
+       test "$diff" = "branch" &&
+
+       # set merge.tool to something bogus, diff.tool to test-tool
+       git config merge.tool bogus-tool &&
+       git config diff.tool test-tool &&
+
+       diff=$(git difftool --no-prompt branch) &&
+       test "$diff" = "branch" &&
+
+       restore_test_defaults
+'
+
+test_expect_success 'difftool.<tool>.path' '
+       git config difftool.tkdiff.path echo &&
+       diff=$(git difftool --tool=tkdiff --no-prompt branch) &&
+       git config --unset difftool.tkdiff.path &&
+       lines=$(echo "$diff" | grep file | wc -l) &&
+       test "$lines" -eq 1
+'
+
+test_done
index 966bb0a61a89ed63dec085338d3c45f766a7f777..13c25f1d528ca1ec90575e42e0393accff5d8f35 100755 (executable)
@@ -129,4 +129,19 @@ test_expect_success 'blame wholesale copy and more' '
 
 '
 
+test_expect_success 'blame path that used to be a directory' '
+       mkdir path &&
+       echo A A A A A >path/file &&
+       echo B B B B B >path/elif &&
+       git add path &&
+       test_tick &&
+       git commit -m "path was a directory" &&
+       rm -fr path &&
+       echo A A A A A >path &&
+       git add path &&
+       test_tick &&
+       git commit -m "path is a regular file" &&
+       git blame HEAD^.. -- path
+'
+
 test_done
index 4470a92bb235420a2a0123eca5b57e06039ff011..9c1e21a60be83322c67c7586a86d2410b472be7b 100755 (executable)
@@ -4,7 +4,7 @@ test_description='git blame encoding conversion'
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/t8005/utf8.txt
-. "$TEST_DIRECTORY"/t8005/cp1251.txt
+. "$TEST_DIRECTORY"/t8005/euc-japan.txt
 . "$TEST_DIRECTORY"/t8005/sjis.txt
 
 test_expect_success 'setup the repository' '
@@ -13,10 +13,10 @@ test_expect_success 'setup the repository' '
        git add file &&
        git commit --author "$UTF8_NAME <utf8@localhost>" -m "$UTF8_MSG" &&
 
-       echo "CP1251 LINE" >> file &&
+       echo "EUC-JAPAN LINE" >> file &&
        git add file &&
-       git config i18n.commitencoding cp1251 &&
-       git commit --author "$CP1251_NAME <cp1251@localhost>" -m "$CP1251_MSG" &&
+       git config i18n.commitencoding eucJP &&
+       git commit --author "$EUC_JAPAN_NAME <euc-japan@localhost>" -m "$EUC_JAPAN_MSG" &&
 
        echo "SJIS LINE" >> file &&
        git add file &&
@@ -36,24 +36,24 @@ EOF
 test_expect_success \
        'blame respects i18n.commitencoding' '
        git blame --incremental file | \
-               grep "^\(author\|summary\) " > actual &&
+               egrep "^(author|summary) " > actual &&
        test_cmp actual expected
 '
 
 cat >expected <<EOF
-author $CP1251_NAME
-summary $CP1251_MSG
-author $CP1251_NAME
-summary $CP1251_MSG
-author $CP1251_NAME
-summary $CP1251_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
 EOF
 
 test_expect_success \
        'blame respects i18n.logoutputencoding' '
-       git config i18n.logoutputencoding cp1251 &&
+       git config i18n.logoutputencoding eucJP &&
        git blame --incremental file | \
-               grep "^\(author\|summary\) " > actual &&
+               egrep "^(author|summary) " > actual &&
        test_cmp actual expected
 '
 
@@ -69,15 +69,15 @@ EOF
 test_expect_success \
        'blame respects --encoding=utf-8' '
        git blame --incremental --encoding=utf-8 file | \
-               grep "^\(author\|summary\) " > actual &&
+               egrep "^(author|summary) " > actual &&
        test_cmp actual expected
 '
 
 cat >expected <<EOF
 author $SJIS_NAME
 summary $SJIS_MSG
-author $CP1251_NAME
-summary $CP1251_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
 author $UTF8_NAME
 summary $UTF8_MSG
 EOF
@@ -85,7 +85,7 @@ EOF
 test_expect_success \
        'blame respects --encoding=none' '
        git blame --incremental --encoding=none file | \
-               grep "^\(author\|summary\) " > actual &&
+               egrep "^(author|summary) " > actual &&
        test_cmp actual expected
 '
 
diff --git a/t/t8005/euc-japan.txt b/t/t8005/euc-japan.txt
new file mode 100644 (file)
index 0000000..288f040
--- /dev/null
@@ -0,0 +1,2 @@
+EUC_JAPAN_NAME="»³ÅÄ ÂÀϺ"
+EUC_JAPAN_MSG="¥Ö¥ì¡¼¥à¤Î¥Æ¥¹¥È¤Ç¤¹¡£"
index 2ccfbad207c6e96b1f4f528031d9e4938d364b92..bbdefeaced4b54f98e5d9a85ddd8e0d7346fe7e3 100644 (file)
@@ -1,2 +1,2 @@
-SJIS_NAME="\84I\84r\84p\84\84P\84u\84\84\84\82\84\80\84r\84y\84\89 \84R\84y\84t\84\80\84\82\84\80\84r"
-SJIS_MSG="\84S\84u\84\83\84\84\84\80\84r\84\80\84\84\83\84\80\84\80\84q\84\8b\84u\84~\84y\84u"
+SJIS_NAME="\8eR\93\91¾\98Y"
+SJIS_MSG="\83u\83\8c\81[\83\80\82Ì\83e\83X\83g\82Å\82·\81B"
index f46cfc56d80797740c3ec15e166add052f905fcb..4d00dbea7659ee27fda283e7e45cfb2d5f6ea4d1 100644 (file)
@@ -1,2 +1,2 @@
-UTF8_NAME="Иван Петрович Сидоров"
-UTF8_MSG="Тестовое сообщение"
+UTF8_NAME="山田 太郎"
+UTF8_MSG="ブレームのテストです。"
index d7634187aaa82578dbabbccd6866319b40356854..ce26ea4ac53a4608f6f8234b67868a9cc85be7f8 100755 (executable)
@@ -3,6 +3,11 @@
 test_description='git send-email'
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+       say 'skipping git send-email tests, perl not available'
+       test_done
+fi
+
 PROG='git send-email'
 test_expect_success \
     'prepare reference tree' \
@@ -35,6 +40,47 @@ test_expect_success 'Extract patches' '
     patches=`git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1`
 '
 
+# Test no confirm early to ensure remaining tests will not hang
+test_no_confirm () {
+       rm -f no_confirm_okay
+       echo n | \
+               GIT_SEND_EMAIL_NOTTY=1 \
+               git send-email \
+               --from="Example <from@example.com>" \
+               --to=nobody@example.com \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               $@ \
+               $patches > stdout &&
+               test_must_fail grep "Send this email" stdout &&
+               > no_confirm_okay
+}
+
+# Exit immediately to prevent hang if a no-confirm test fails
+check_no_confirm () {
+       test -f no_confirm_okay || {
+               say 'No confirm test failed; skipping remaining tests to prevent hanging'
+               test_done
+       }
+}
+
+test_expect_success 'No confirm with --suppress-cc' '
+       test_no_confirm --suppress-cc=sob
+'
+check_no_confirm
+
+test_expect_success 'No confirm with --confirm=never' '
+       test_no_confirm --confirm=never
+'
+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' '
+       git config sendemail.confirm never &&
+       test_no_confirm --compose --subject=foo
+'
+check_no_confirm
+
 test_expect_success '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
 '
@@ -47,7 +93,7 @@ cat >expected <<\EOF
 EOF
 test_expect_success \
     'Verify commandline' \
-    'diff commandline1 expected'
+    'test_cmp expected commandline1'
 
 cat >expected-show-all-headers <<\EOF
 0001-Second.patch
@@ -89,6 +135,19 @@ test_expect_success 'Show all headers' '
        test_cmp expected-show-all-headers actual-show-all-headers
 '
 
+test_expect_success 'Prompting works' '
+       clean_fake_sendmail &&
+       (echo "Example <from@example.com>"
+        echo "to@example.com"
+        echo ""
+       ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               $patches \
+               2>errors &&
+               grep "^From: Example <from@example.com>$" msgtxt1 &&
+               grep "^To: to@example.com$" msgtxt1
+'
+
 z8=zzzzzzzz
 z64=$z8$z8$z8$z8$z8$z8$z8$z8
 z512=$z64$z64$z64$z64$z64$z64$z64$z64
@@ -175,15 +234,13 @@ test_set_editor "$(pwd)/fake-editor"
 
 test_expect_success '--compose works' '
        clean_fake_sendmail &&
-       echo y | \
-               GIT_SEND_EMAIL_NOTTY=1 \
-               git send-email \
-               --compose --subject foo \
-               --from="Example <nobody@example.com>" \
-               --to=nobody@example.com \
-               --smtp-server="$(pwd)/fake.sendmail" \
-               $patches \
-               2>errors
+       git send-email \
+       --compose --subject foo \
+       --from="Example <nobody@example.com>" \
+       --to=nobody@example.com \
+       --smtp-server="$(pwd)/fake.sendmail" \
+       $patches \
+       2>errors
 '
 
 test_expect_success 'first message is compose text' '
@@ -375,15 +432,117 @@ test_expect_success '--suppress-cc=cc' '
        test_suppression cc
 '
 
+test_confirm () {
+       echo y | \
+               GIT_SEND_EMAIL_NOTTY=1 \
+               git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               $@ $patches > stdout &&
+       grep "Send this email" stdout
+}
+
+test_expect_success '--confirm=always' '
+       test_confirm --confirm=always --suppress-cc=all
+'
+
+test_expect_success '--confirm=auto' '
+       test_confirm --confirm=auto
+'
+
+test_expect_success '--confirm=cc' '
+       test_confirm --confirm=cc
+'
+
+test_expect_success '--confirm=compose' '
+       test_confirm --confirm=compose --compose
+'
+
+test_expect_success 'confirm by default (due to cc)' '
+       CONFIRM=$(git config --get sendemail.confirm) &&
+       git config --unset sendemail.confirm &&
+       test_confirm
+       ret="$?"
+       git config sendemail.confirm ${CONFIRM:-never}
+       test $ret = "0"
+'
+
+test_expect_success 'confirm by default (due to --compose)' '
+       CONFIRM=$(git config --get sendemail.confirm) &&
+       git config --unset sendemail.confirm &&
+       test_confirm --suppress-cc=all --compose
+       ret="$?"
+       git config sendemail.confirm ${CONFIRM:-never}
+       test $ret = "0"
+'
+
+test_expect_success 'confirm detects EOF (inform assumes y)' '
+       CONFIRM=$(git config --get sendemail.confirm) &&
+       git config --unset sendemail.confirm &&
+       rm -fr outdir &&
+       git format-patch -2 -o outdir &&
+       GIT_SEND_EMAIL_NOTTY=1 \
+               git send-email \
+                       --from="Example <nobody@example.com>" \
+                       --to=nobody@example.com \
+                       --smtp-server="$(pwd)/fake.sendmail" \
+                       outdir/*.patch < /dev/null
+       ret="$?"
+       git config sendemail.confirm ${CONFIRM:-never}
+       test $ret = "0"
+'
+
+test_expect_success 'confirm detects EOF (auto causes failure)' '
+       CONFIRM=$(git config --get sendemail.confirm) &&
+       git config sendemail.confirm auto &&
+       GIT_SEND_EMAIL_NOTTY=1 &&
+       export GIT_SEND_EMAIL_NOTTY &&
+               test_must_fail git send-email \
+                       --from="Example <nobody@example.com>" \
+                       --to=nobody@example.com \
+                       --smtp-server="$(pwd)/fake.sendmail" \
+                       $patches < /dev/null
+       ret="$?"
+       git config sendemail.confirm ${CONFIRM:-never}
+       test $ret = "0"
+'
+
+test_expect_success 'confirm doesnt loop forever' '
+       CONFIRM=$(git config --get sendemail.confirm) &&
+       git config sendemail.confirm auto &&
+       GIT_SEND_EMAIL_NOTTY=1 &&
+       export GIT_SEND_EMAIL_NOTTY &&
+               yes "bogus" | test_must_fail git send-email \
+                       --from="Example <nobody@example.com>" \
+                       --to=nobody@example.com \
+                       --smtp-server="$(pwd)/fake.sendmail" \
+                       $patches
+       ret="$?"
+       git config sendemail.confirm ${CONFIRM:-never}
+       test $ret = "0"
+'
+
+test_expect_success 'utf8 Cc is rfc2047 encoded' '
+       clean_fake_sendmail &&
+       rm -fr outdir &&
+       git format-patch -1 -o outdir --cc="àéìöú <utf8@example.com>" &&
+       git send-email \
+       --from="Example <nobody@example.com>" \
+       --to=nobody@example.com \
+       --smtp-server="$(pwd)/fake.sendmail" \
+       outdir/*.patch &&
+       grep "^Cc:" msgtxt1 |
+       grep "=?utf-8?q?=C3=A0=C3=A9=C3=AC=C3=B6=C3=BA?= <utf8@example.com>"
+'
+
 test_expect_success '--compose adds MIME for utf8 body' '
        clean_fake_sendmail &&
        (echo "#!$SHELL_PATH" &&
         echo "echo utf8 body: àéìöú >>\"\$1\""
        ) >fake-editor-utf8 &&
        chmod +x fake-editor-utf8 &&
-       echo y | \
          GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \
-         GIT_SEND_EMAIL_NOTTY=1 \
          git send-email \
          --compose --subject foo \
          --from="Example <nobody@example.com>" \
@@ -405,9 +564,7 @@ test_expect_success '--compose respects user mime type' '
         echo " echo utf8 body: àéìöú) >\"\$1\""
        ) >fake-editor-utf8-mime &&
        chmod +x fake-editor-utf8-mime &&
-       echo y | \
          GIT_EDITOR="\"$(pwd)/fake-editor-utf8-mime\"" \
-         GIT_SEND_EMAIL_NOTTY=1 \
          git send-email \
          --compose --subject foo \
          --from="Example <nobody@example.com>" \
@@ -421,9 +578,7 @@ test_expect_success '--compose respects user mime type' '
 
 test_expect_success '--compose adds MIME for utf8 subject' '
        clean_fake_sendmail &&
-       echo y | \
          GIT_EDITOR="\"$(pwd)/fake-editor\"" \
-         GIT_SEND_EMAIL_NOTTY=1 \
          git send-email \
          --compose --subject utf8-sübjëct \
          --from="Example <nobody@example.com>" \
@@ -445,7 +600,7 @@ test_expect_success 'detects ambiguous reference/file conflict' '
 test_expect_success 'feed two files' '
        rm -fr outdir &&
        git format-patch -2 -o outdir &&
-       GIT_SEND_EMAIL_NOTTY=1 git send-email \
+       git send-email \
        --dry-run \
        --from="Example <nobody@example.com>" \
        --to=nobody@example.com \
@@ -461,7 +616,7 @@ test_expect_success 'in-reply-to but no threading' '
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
                --in-reply-to="<in-reply-id@example.com>" \
-               --no-thread \
+               --nothread \
                $patches |
        grep "In-Reply-To: <in-reply-id@example.com>"
 '
index bb921af56af2c9a559c843dd4c3b69993c34206c..4eee2e9fa6bacdc0d130a692f727885c5fa42e49 100755 (executable)
@@ -6,19 +6,19 @@
 test_description='git svn basic tests'
 GIT_SVN_LC_ALL=${LC_ALL:-$LANG}
 
+. ./lib-git-svn.sh
+
+say 'define NO_SVN_TESTS to skip git svn tests'
+
 case "$GIT_SVN_LC_ALL" in
 *.UTF-8)
-       have_utf8=t
+       test_set_prereq UTF8
        ;;
 *)
-       have_utf8=
+       say "UTF-8 locale not set, some tests skipped ($GIT_SVN_LC_ALL)"
        ;;
 esac
 
-. ./lib-git-svn.sh
-
-say 'define NO_SVN_TESTS to skip git svn tests'
-
 test_expect_success \
     'initialize git svn' '
        mkdir import &&
@@ -171,20 +171,15 @@ test_expect_success "$name" '
        test ! -L "$SVN_TREE"/exec-2.sh &&
        test_cmp help "$SVN_TREE"/exec-2.sh'
 
-if test "$have_utf8" = t
-then
-       name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL"
-       LC_ALL="$GIT_SVN_LC_ALL"
-       export LC_ALL
-       test_expect_success "$name" "
-               echo '# hello' >> exec-2.sh &&
-               git update-index exec-2.sh &&
-               git commit -m 'éï∏' &&
-               git svn set-tree HEAD"
-       unset LC_ALL
-else
-       say "UTF-8 locale not set, test skipped ($GIT_SVN_LC_ALL)"
-fi
+name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL"
+LC_ALL="$GIT_SVN_LC_ALL"
+export LC_ALL
+test_expect_success UTF8 "$name" "
+       echo '# hello' >> exec-2.sh &&
+       git update-index exec-2.sh &&
+       git commit -m 'éï∏' &&
+       git svn set-tree HEAD"
+unset LC_ALL
 
 name='test fetch functionality (svn => git) with alternate GIT_SVN_ID'
 GIT_SVN_ID=alt
@@ -197,7 +192,7 @@ test_expect_success "$name" \
 
 name='check imported tree checksums expected tree checksums'
 rm -f expected
-if test "$have_utf8" = t
+if test_have_prereq UTF8
 then
        echo tree bf522353586b1b883488f2bc73dab0d9f774b9a9 > expected
 fi
diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9106-git-svn-dcommit-clobber-series.sh
deleted file mode 100755 (executable)
index fd18501..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2007 Eric Wong
-test_description='git svn dcommit clobber series'
-. ./lib-git-svn.sh
-
-test_expect_success 'initialize repo' '
-       mkdir import &&
-       cd import &&
-       awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
-       svn import -m "initial" . "$svnrepo" &&
-       cd .. &&
-       git svn init "$svnrepo" &&
-       git svn fetch &&
-       test -e file
-       '
-
-test_expect_success '(supposedly) non-conflicting change from SVN' '
-       test x"`sed -n -e 58p < file`" = x58 &&
-       test x"`sed -n -e 61p < file`" = x61 &&
-       svn co "$svnrepo" tmp &&
-       cd tmp &&
-               perl -i.bak -p -e "s/^58$/5588/" file &&
-               perl -i.bak -p -e "s/^61$/6611/" file &&
-               poke file &&
-               test x"`sed -n -e 58p < file`" = x5588 &&
-               test x"`sed -n -e 61p < file`" = x6611 &&
-               svn commit -m "58 => 5588, 61 => 6611" &&
-               cd ..
-       '
-
-test_expect_success 'some unrelated changes to git' "
-       echo hi > life &&
-       git update-index --add life &&
-       git commit -m hi-life &&
-       echo bye >> life &&
-       git commit -m bye-life life
-       "
-
-test_expect_success 'change file but in unrelated area' "
-       test x\"\`sed -n -e 4p < file\`\" = x4 &&
-       test x\"\`sed -n -e 7p < file\`\" = x7 &&
-       perl -i.bak -p -e 's/^4\$/4444/' file &&
-       perl -i.bak -p -e 's/^7\$/7777/' file &&
-       test x\"\`sed -n -e 4p < file\`\" = x4444 &&
-       test x\"\`sed -n -e 7p < file\`\" = x7777 &&
-       git commit -m '4 => 4444, 7 => 7777' file &&
-       git svn dcommit &&
-       svn up tmp &&
-       cd tmp &&
-               test x\"\`sed -n -e 4p < file\`\" = x4444 &&
-               test x\"\`sed -n -e 7p < file\`\" = x7777 &&
-               test x\"\`sed -n -e 58p < file\`\" = x5588 &&
-               test x\"\`sed -n -e 61p < file\`\" = x6611
-       "
-
-test_expect_success 'attempt to dcommit with a dirty index' '
-       echo foo >>file &&
-       git add file &&
-       test_must_fail git svn dcommit
-'
-
-test_done
diff --git a/t/t9108-git-svn-multi-glob.sh b/t/t9108-git-svn-multi-glob.sh
deleted file mode 100755 (executable)
index 8f79c3f..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/bin/sh
-# Copyright (c) 2007 Eric Wong
-test_description='git svn globbing refspecs'
-. ./lib-git-svn.sh
-
-cat > expect.end <<EOF
-the end
-hi
-start a new branch
-initial
-EOF
-
-test_expect_success 'test refspec globbing' '
-       mkdir -p trunk/src/a trunk/src/b trunk/doc &&
-       echo "hello world" > trunk/src/a/readme &&
-       echo "goodbye world" > trunk/src/b/readme &&
-       svn import -m "initial" trunk "$svnrepo"/trunk &&
-       svn co "$svnrepo" tmp &&
-       (
-               cd tmp &&
-               mkdir branches branches/v1 tags &&
-               svn add branches tags &&
-               svn cp trunk branches/v1/start &&
-               svn commit -m "start a new branch" &&
-               svn up &&
-               echo "hi" >> branches/v1/start/src/b/readme &&
-               poke branches/v1/start/src/b/readme &&
-               echo "hey" >> branches/v1/start/src/a/readme &&
-               poke branches/v1/start/src/a/readme &&
-               svn commit -m "hi" &&
-               svn up &&
-               svn cp branches/v1/start tags/end &&
-               echo "bye" >> tags/end/src/b/readme &&
-               poke tags/end/src/b/readme &&
-               echo "aye" >> tags/end/src/a/readme &&
-               poke tags/end/src/a/readme &&
-               svn commit -m "the end" &&
-               echo "byebye" >> tags/end/src/b/readme &&
-               poke tags/end/src/b/readme &&
-               svn commit -m "nothing to see here"
-       ) &&
-       git config --add svn-remote.svn.url "$svnrepo" &&
-       git config --add svn-remote.svn.fetch \
-                        "trunk/src/a:refs/remotes/trunk" &&
-       git config --add svn-remote.svn.branches \
-                        "branches/*/*/src/a:refs/remotes/branches/*/*" &&
-       git config --add svn-remote.svn.tags\
-                        "tags/*/src/a:refs/remotes/tags/*" &&
-       git svn multi-fetch &&
-       git log --pretty=oneline refs/remotes/tags/end | \
-           sed -e "s/^.\{41\}//" > output.end &&
-       test_cmp expect.end output.end &&
-       test "`git rev-parse refs/remotes/tags/end~1`" = \
-               "`git rev-parse refs/remotes/branches/v1/start`" &&
-       test "`git rev-parse refs/remotes/branches/v1/start~2`" = \
-               "`git rev-parse refs/remotes/trunk`" &&
-       test_must_fail git rev-parse refs/remotes/tags/end@3
-       '
-
-echo try to try > expect.two
-echo nothing to see here >> expect.two
-cat expect.end >> expect.two
-
-test_expect_success 'test left-hand-side only globbing' '
-       git config --add svn-remote.two.url "$svnrepo" &&
-       git config --add svn-remote.two.fetch trunk:refs/remotes/two/trunk &&
-       git config --add svn-remote.two.branches \
-                        "branches/*/*:refs/remotes/two/branches/*/*" &&
-       git config --add svn-remote.two.tags \
-                        "tags/*:refs/remotes/two/tags/*" &&
-       (
-               cd tmp &&
-               echo "try try" >> tags/end/src/b/readme &&
-               poke tags/end/src/b/readme &&
-               svn commit -m "try to try"
-       ) &&
-       git svn fetch two &&
-       test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 &&
-       test `git rev-list refs/remotes/two/branches/v1/start | wc -l` -eq 3 &&
-       test `git rev-parse refs/remotes/two/branches/v1/start~2` = \
-            `git rev-parse refs/remotes/two/trunk` &&
-       test `git rev-parse refs/remotes/two/tags/end~3` = \
-            `git rev-parse refs/remotes/two/branches/v1/start` &&
-       git log --pretty=oneline refs/remotes/two/tags/end | \
-           sed -e "s/^.\{41\}//" > output.two &&
-       test_cmp expect.two output.two
-       '
-cat > expect.four <<EOF
-adios
-adding more
-Changed 2 in v2/start
-Another versioned branch
-initial
-EOF
-
-test_expect_success 'test another branch' '
-       (
-               cd tmp &&
-               mkdir branches/v2 &&
-               svn add branches/v2 &&
-               svn cp trunk branches/v2/start &&
-               svn commit -m "Another versioned branch" &&
-               svn up &&
-               echo "hello" >> branches/v2/start/src/b/readme &&
-               poke branches/v2/start/src/b/readme &&
-               echo "howdy" >> branches/v2/start/src/a/readme &&
-               poke branches/v2/start/src/a/readme &&
-               svn commit -m "Changed 2 in v2/start" &&
-               svn up &&
-               svn cp branches/v2/start tags/next &&
-               echo "bye" >> tags/next/src/b/readme &&
-               poke tags/next/src/b/readme &&
-               echo "aye" >> tags/next/src/a/readme &&
-               poke tags/next/src/a/readme &&
-               svn commit -m "adding more" &&
-               echo "byebye" >> tags/next/src/b/readme &&
-               poke tags/next/src/b/readme &&
-               svn commit -m "adios"
-       ) &&
-       git config --add svn-remote.four.url "$svnrepo" &&
-       git config --add svn-remote.four.fetch trunk:refs/remotes/four/trunk &&
-       git config --add svn-remote.four.branches \
-                        "branches/*/*:refs/remotes/four/branches/*/*" &&
-       git config --add svn-remote.four.tags \
-                        "tags/*:refs/remotes/four/tags/*" &&
-       git svn fetch four &&
-       test `git rev-list refs/remotes/four/tags/next | wc -l` -eq 5 &&
-       test `git rev-list refs/remotes/four/branches/v2/start | wc -l` -eq 3 &&
-       test `git rev-parse refs/remotes/four/branches/v2/start~2` = \
-            `git rev-parse refs/remotes/four/trunk` &&
-       test `git rev-parse refs/remotes/four/tags/next~2` = \
-            `git rev-parse refs/remotes/four/branches/v2/start` &&
-       git log --pretty=oneline refs/remotes/four/tags/next | \
-           sed -e "s/^.\{41\}//" > output.four &&
-       test_cmp expect.four output.four
-       '
-
-echo "Only one set of wildcard directories" \
-     "(e.g. '*' or '*/*/*') is supported: 'branches/*/t/*'" > expect.three
-echo "" >> expect.three
-
-test_expect_success 'test disallow multiple globs' '
-       git config --add svn-remote.three.url "$svnrepo" &&
-       git config --add svn-remote.three.fetch \
-                        trunk:refs/remotes/three/trunk &&
-       git config --add svn-remote.three.branches \
-                        "branches/*/t/*:refs/remotes/three/branches/*/*" &&
-       git config --add svn-remote.three.tags \
-                        "tags/*:refs/remotes/three/tags/*" &&
-       (
-               cd tmp &&
-               echo "try try" >> tags/end/src/b/readme &&
-               poke tags/end/src/b/readme &&
-               svn commit -m "try to try"
-       ) &&
-       test_must_fail git svn fetch three 2> stderr.three &&
-       test_cmp expect.three stderr.three
-       '
-
-test_done
diff --git a/t/t9109-git-svn-multi-glob.sh b/t/t9109-git-svn-multi-glob.sh
new file mode 100755 (executable)
index 0000000..8f79c3f
--- /dev/null
@@ -0,0 +1,160 @@
+#!/bin/sh
+# Copyright (c) 2007 Eric Wong
+test_description='git svn globbing refspecs'
+. ./lib-git-svn.sh
+
+cat > expect.end <<EOF
+the end
+hi
+start a new branch
+initial
+EOF
+
+test_expect_success 'test refspec globbing' '
+       mkdir -p trunk/src/a trunk/src/b trunk/doc &&
+       echo "hello world" > trunk/src/a/readme &&
+       echo "goodbye world" > trunk/src/b/readme &&
+       svn import -m "initial" trunk "$svnrepo"/trunk &&
+       svn co "$svnrepo" tmp &&
+       (
+               cd tmp &&
+               mkdir branches branches/v1 tags &&
+               svn add branches tags &&
+               svn cp trunk branches/v1/start &&
+               svn commit -m "start a new branch" &&
+               svn up &&
+               echo "hi" >> branches/v1/start/src/b/readme &&
+               poke branches/v1/start/src/b/readme &&
+               echo "hey" >> branches/v1/start/src/a/readme &&
+               poke branches/v1/start/src/a/readme &&
+               svn commit -m "hi" &&
+               svn up &&
+               svn cp branches/v1/start tags/end &&
+               echo "bye" >> tags/end/src/b/readme &&
+               poke tags/end/src/b/readme &&
+               echo "aye" >> tags/end/src/a/readme &&
+               poke tags/end/src/a/readme &&
+               svn commit -m "the end" &&
+               echo "byebye" >> tags/end/src/b/readme &&
+               poke tags/end/src/b/readme &&
+               svn commit -m "nothing to see here"
+       ) &&
+       git config --add svn-remote.svn.url "$svnrepo" &&
+       git config --add svn-remote.svn.fetch \
+                        "trunk/src/a:refs/remotes/trunk" &&
+       git config --add svn-remote.svn.branches \
+                        "branches/*/*/src/a:refs/remotes/branches/*/*" &&
+       git config --add svn-remote.svn.tags\
+                        "tags/*/src/a:refs/remotes/tags/*" &&
+       git svn multi-fetch &&
+       git log --pretty=oneline refs/remotes/tags/end | \
+           sed -e "s/^.\{41\}//" > output.end &&
+       test_cmp expect.end output.end &&
+       test "`git rev-parse refs/remotes/tags/end~1`" = \
+               "`git rev-parse refs/remotes/branches/v1/start`" &&
+       test "`git rev-parse refs/remotes/branches/v1/start~2`" = \
+               "`git rev-parse refs/remotes/trunk`" &&
+       test_must_fail git rev-parse refs/remotes/tags/end@3
+       '
+
+echo try to try > expect.two
+echo nothing to see here >> expect.two
+cat expect.end >> expect.two
+
+test_expect_success 'test left-hand-side only globbing' '
+       git config --add svn-remote.two.url "$svnrepo" &&
+       git config --add svn-remote.two.fetch trunk:refs/remotes/two/trunk &&
+       git config --add svn-remote.two.branches \
+                        "branches/*/*:refs/remotes/two/branches/*/*" &&
+       git config --add svn-remote.two.tags \
+                        "tags/*:refs/remotes/two/tags/*" &&
+       (
+               cd tmp &&
+               echo "try try" >> tags/end/src/b/readme &&
+               poke tags/end/src/b/readme &&
+               svn commit -m "try to try"
+       ) &&
+       git svn fetch two &&
+       test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 &&
+       test `git rev-list refs/remotes/two/branches/v1/start | wc -l` -eq 3 &&
+       test `git rev-parse refs/remotes/two/branches/v1/start~2` = \
+            `git rev-parse refs/remotes/two/trunk` &&
+       test `git rev-parse refs/remotes/two/tags/end~3` = \
+            `git rev-parse refs/remotes/two/branches/v1/start` &&
+       git log --pretty=oneline refs/remotes/two/tags/end | \
+           sed -e "s/^.\{41\}//" > output.two &&
+       test_cmp expect.two output.two
+       '
+cat > expect.four <<EOF
+adios
+adding more
+Changed 2 in v2/start
+Another versioned branch
+initial
+EOF
+
+test_expect_success 'test another branch' '
+       (
+               cd tmp &&
+               mkdir branches/v2 &&
+               svn add branches/v2 &&
+               svn cp trunk branches/v2/start &&
+               svn commit -m "Another versioned branch" &&
+               svn up &&
+               echo "hello" >> branches/v2/start/src/b/readme &&
+               poke branches/v2/start/src/b/readme &&
+               echo "howdy" >> branches/v2/start/src/a/readme &&
+               poke branches/v2/start/src/a/readme &&
+               svn commit -m "Changed 2 in v2/start" &&
+               svn up &&
+               svn cp branches/v2/start tags/next &&
+               echo "bye" >> tags/next/src/b/readme &&
+               poke tags/next/src/b/readme &&
+               echo "aye" >> tags/next/src/a/readme &&
+               poke tags/next/src/a/readme &&
+               svn commit -m "adding more" &&
+               echo "byebye" >> tags/next/src/b/readme &&
+               poke tags/next/src/b/readme &&
+               svn commit -m "adios"
+       ) &&
+       git config --add svn-remote.four.url "$svnrepo" &&
+       git config --add svn-remote.four.fetch trunk:refs/remotes/four/trunk &&
+       git config --add svn-remote.four.branches \
+                        "branches/*/*:refs/remotes/four/branches/*/*" &&
+       git config --add svn-remote.four.tags \
+                        "tags/*:refs/remotes/four/tags/*" &&
+       git svn fetch four &&
+       test `git rev-list refs/remotes/four/tags/next | wc -l` -eq 5 &&
+       test `git rev-list refs/remotes/four/branches/v2/start | wc -l` -eq 3 &&
+       test `git rev-parse refs/remotes/four/branches/v2/start~2` = \
+            `git rev-parse refs/remotes/four/trunk` &&
+       test `git rev-parse refs/remotes/four/tags/next~2` = \
+            `git rev-parse refs/remotes/four/branches/v2/start` &&
+       git log --pretty=oneline refs/remotes/four/tags/next | \
+           sed -e "s/^.\{41\}//" > output.four &&
+       test_cmp expect.four output.four
+       '
+
+echo "Only one set of wildcard directories" \
+     "(e.g. '*' or '*/*/*') is supported: 'branches/*/t/*'" > expect.three
+echo "" >> expect.three
+
+test_expect_success 'test disallow multiple globs' '
+       git config --add svn-remote.three.url "$svnrepo" &&
+       git config --add svn-remote.three.fetch \
+                        trunk:refs/remotes/three/trunk &&
+       git config --add svn-remote.three.branches \
+                        "branches/*/t/*:refs/remotes/three/branches/*/*" &&
+       git config --add svn-remote.three.tags \
+                        "tags/*:refs/remotes/three/tags/*" &&
+       (
+               cd tmp &&
+               echo "try try" >> tags/end/src/b/readme &&
+               poke tags/end/src/b/readme &&
+               svn commit -m "try to try"
+       ) &&
+       test_must_fail git svn fetch three 2> stderr.three &&
+       test_cmp expect.three stderr.three
+       '
+
+test_done
index 9c7b1ad18bca943e6c5b6e50f55d914ccadbfce8..3200ab38efa03076b81b94713e74205d740e38e8 100755 (executable)
@@ -70,24 +70,26 @@ do
 done
 
 if locale -a |grep -q en_US.utf8; then
-       test_expect_success 'ISO-8859-1 should match UTF-8 in svn' '
+       test_set_prereq UTF8
+else
+       say "UTF-8 locale not available, test skipped"
+fi
+
+test_expect_success UTF8 'ISO-8859-1 should match UTF-8 in svn' '
        (
                cd ISO-8859-1 &&
                compare_svn_head_with "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
        )
-       '
+'
 
-       for H in EUCJP ISO-2022-JP
-       do
-               test_expect_success '$H should match UTF-8 in svn' '
+for H in EUCJP ISO-2022-JP
+do
+       test_expect_success UTF8 "$H should match UTF-8 in svn" '
                (
                        cd $H &&
                        compare_svn_head_with "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
                )
-               '
-       done
-else
-       say "UTF-8 locale not available, test skipped"
-fi
+       '
+done
 
 test_done
index 8f35e294aa885e7afbb704186c9afe9467172570..9a24a65b64111b4540db28315fedd4efd3b19ae7 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 '"bar" becomes a symlink' 'test -L x/bar'
+test_expect_success SYMLINKS '"bar" becomes a symlink' 'test -L x/bar'
 
 
 test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" y'
index b8de59e493cbf0f0e85e60daf49fd4dc716244e1..6c4c90b03694d2ebe986897b744444fbc00b7125 100755 (executable)
@@ -85,7 +85,7 @@ EOF
 
 test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" x'
 
-test_expect_success '"bar" is a symlink that points to "asdf"' '
+test_expect_success SYMLINKS '"bar" is a symlink that points to "asdf"' '
        test -L x/bar &&
        (cd x && test xasdf = x"`git cat-file blob HEAD:bar`")
 '
@@ -94,7 +94,7 @@ test_expect_success 'get "bar" => symlink fix from svn' '
        (cd x && git svn rebase)
 '
 
-test_expect_success '"bar" remains a proper symlink' '
+test_expect_success SYMLINKS '"bar" remains a proper symlink' '
        test -L x/bar &&
        (cd x && test xdoink = x"`git cat-file blob HEAD:bar`")
 '
index c4b5b8bcf7949f8f60d5d80d6cd53e892b8743f2..71fdc4a69dde2ff0bfdb092d8e28aeba495a1dc0 100755 (executable)
@@ -31,6 +31,22 @@ test_expect_success 'clone an SVN repository with ignored www directory' '
        test_cmp expect expect2
 '
 
+test_expect_success 'init+fetch an SVN repository with ignored www directory' '
+       git svn init "$svnrepo" c &&
+       ( cd c && git svn fetch --ignore-paths="^www" ) &&
+       rm expect2 &&
+       echo test_qqq > expect &&
+       for i in c/*/*.txt; do cat $i >> expect2; done &&
+       test_cmp expect expect2
+'
+
+test_expect_success 'verify ignore-paths config saved by clone' '
+       (
+           cd g &&
+           git config --get svn-remote.svn.ignore-paths | fgrep "www"
+       )
+'
+
 test_expect_success 'SVN-side change outside of www' '
        (
                cd s &&
@@ -41,9 +57,20 @@ test_expect_success 'SVN-side change outside of www' '
        )
 '
 
-test_expect_success 'update git svn-cloned repo' '
+test_expect_success 'update git svn-cloned repo (config ignore)' '
        (
                cd g &&
+               git svn rebase &&
+               printf "test_qqq\nb\n" > expect &&
+               for i in */*.txt; do cat $i >> expect2; done &&
+               test_cmp expect2 expect &&
+               rm expect expect2
+       )
+'
+
+test_expect_success 'update git svn-cloned repo (option ignore)' '
+       (
+               cd c &&
                git svn rebase --ignore-paths="^www" &&
                printf "test_qqq\nb\n" > expect &&
                for i in */*.txt; do cat $i >> expect2; done &&
@@ -62,9 +89,20 @@ test_expect_success 'SVN-side change inside of ignored www' '
        )
 '
 
-test_expect_success 'update git svn-cloned repo' '
+test_expect_success 'update git svn-cloned repo (config ignore)' '
        (
                cd g &&
+               git svn rebase &&
+               printf "test_qqq\nb\n" > expect &&
+               for i in */*.txt; do cat $i >> expect2; done &&
+               test_cmp expect2 expect &&
+               rm expect expect2
+       )
+'
+
+test_expect_success 'update git svn-cloned repo (option ignore)' '
+       (
+               cd c &&
                git svn rebase --ignore-paths="^www" &&
                printf "test_qqq\nb\n" > expect &&
                for i in */*.txt; do cat $i >> expect2; done &&
@@ -84,9 +122,20 @@ test_expect_success 'SVN-side change in and out of ignored www' '
        )
 '
 
-test_expect_success 'update git svn-cloned repo again' '
+test_expect_success 'update git svn-cloned repo again (config ignore)' '
        (
                cd g &&
+               git svn rebase &&
+               printf "test_qqq\nb\nygg\n" > expect &&
+               for i in */*.txt; do cat $i >> expect2; done &&
+               test_cmp expect2 expect &&
+               rm expect expect2
+       )
+'
+
+test_expect_success 'update git svn-cloned repo again (option ignore)' '
+       (
+               cd c &&
                git svn rebase --ignore-paths="^www" &&
                printf "test_qqq\nb\nygg\n" > expect &&
                for i in */*.txt; do cat $i >> expect2; done &&
diff --git a/t/t9137-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh
new file mode 100755 (executable)
index 0000000..fd18501
--- /dev/null
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+test_description='git svn dcommit clobber series'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+       mkdir import &&
+       cd import &&
+       awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
+       svn import -m "initial" . "$svnrepo" &&
+       cd .. &&
+       git svn init "$svnrepo" &&
+       git svn fetch &&
+       test -e file
+       '
+
+test_expect_success '(supposedly) non-conflicting change from SVN' '
+       test x"`sed -n -e 58p < file`" = x58 &&
+       test x"`sed -n -e 61p < file`" = x61 &&
+       svn co "$svnrepo" tmp &&
+       cd tmp &&
+               perl -i.bak -p -e "s/^58$/5588/" file &&
+               perl -i.bak -p -e "s/^61$/6611/" file &&
+               poke file &&
+               test x"`sed -n -e 58p < file`" = x5588 &&
+               test x"`sed -n -e 61p < file`" = x6611 &&
+               svn commit -m "58 => 5588, 61 => 6611" &&
+               cd ..
+       '
+
+test_expect_success 'some unrelated changes to git' "
+       echo hi > life &&
+       git update-index --add life &&
+       git commit -m hi-life &&
+       echo bye >> life &&
+       git commit -m bye-life life
+       "
+
+test_expect_success 'change file but in unrelated area' "
+       test x\"\`sed -n -e 4p < file\`\" = x4 &&
+       test x\"\`sed -n -e 7p < file\`\" = x7 &&
+       perl -i.bak -p -e 's/^4\$/4444/' file &&
+       perl -i.bak -p -e 's/^7\$/7777/' file &&
+       test x\"\`sed -n -e 4p < file\`\" = x4444 &&
+       test x\"\`sed -n -e 7p < file\`\" = x7777 &&
+       git commit -m '4 => 4444, 7 => 7777' file &&
+       git svn dcommit &&
+       svn up tmp &&
+       cd tmp &&
+               test x\"\`sed -n -e 4p < file\`\" = x4444 &&
+               test x\"\`sed -n -e 7p < file\`\" = x7777 &&
+               test x\"\`sed -n -e 58p < file\`\" = x5588 &&
+               test x\"\`sed -n -e 61p < file\`\" = x6611
+       "
+
+test_expect_success 'attempt to dcommit with a dirty index' '
+       echo foo >>file &&
+       git add file &&
+       test_must_fail git svn dcommit
+'
+
+test_done
index 245a7c36628e955e04852a8c2beb75b8deaf4d7f..56b7c06921d9ef3b72ff3ee6f62f7a1c426b3028 100755 (executable)
@@ -6,12 +6,16 @@ test_description='Test export of commits to CVS'
 
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+       say 'skipping git cvsexportcommit tests, perl not available'
+       test_done
+fi
+
 cvs >/dev/null 2>&1
 if test $? -ne 1
 then
-    test_expect_success 'skipping git cvsexportcommit tests, cvs not found' :
+    say 'skipping git cvsexportcommit tests, cvs not found'
     test_done
-    exit
 fi
 
 CVSROOT=$(pwd)/cvsroot
@@ -225,11 +229,12 @@ test_expect_success \
       test_must_fail git cvsexportcommit -c $id
       )'
 
-case "$(git config --bool core.filemode)" in
-false)
-       ;;
-*)
-test_expect_success \
+if ! test "$(git config --bool core.filemode)" = false
+then
+       test_set_prereq FILEMODE
+fi
+
+test_expect_success FILEMODE \
      'Retain execute bit' \
      'mkdir G &&
       echo executeon >G/on &&
@@ -243,8 +248,6 @@ test_expect_success \
       test -x G/on &&
       ! test -x G/off
       )'
-       ;;
-esac
 
 test_expect_success '-w option should work with relative GIT_DIR' '
       mkdir W &&
index 4a87f3625800eba388aae336909b765fea431ea1..8da9ce54596416af6fc9779a5ac3f7131958aec8 100755 (executable)
@@ -8,6 +8,9 @@ test_description='git fast-export'
 
 test_expect_success 'setup' '
 
+       echo break it > file0 &&
+       git add file0 &&
+       test_tick &&
        echo Wohlauf > file &&
        git add file &&
        test_tick &&
@@ -57,8 +60,8 @@ test_expect_success 'fast-export master~2..master' '
                (cd new &&
                 git fast-import &&
                 test $MASTER != $(git rev-parse --verify refs/heads/partial) &&
-                git diff master..partial &&
-                git diff master^..partial^ &&
+                git diff --exit-code master partial &&
+                git diff --exit-code master^ partial^ &&
                 test_must_fail git rev-parse partial~2)
 
 '
index 6a37f71d11800f92a117bfdcf38172bfc9000d77..64f947d75bc0700fc75e8c7c87d97ec4f3e62e44 100755 (executable)
@@ -10,17 +10,19 @@ cvs CLI client via git-cvsserver server'
 
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+       say 'skipping git cvsserver tests, perl not available'
+       test_done
+fi
 cvs >/dev/null 2>&1
 if test $? -ne 1
 then
-    test_expect_success 'skipping git-cvsserver tests, cvs not found' :
+    say 'skipping git-cvsserver tests, cvs not found'
     test_done
-    exit
 fi
 perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
-    test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' :
+    say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
     test_done
-    exit
 }
 
 unset GIT_DIR GIT_CONFIG
@@ -44,7 +46,7 @@ test_expect_success 'setup' '
   git add secondrootfile &&
   git commit -m "second root") &&
   git pull secondroot master &&
-  git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+  git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
   GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
 '
@@ -267,7 +269,7 @@ test_expect_success 'gitcvs.ext.dbname' \
 
 rm -fr "$SERVERDIR"
 cd "$WORKDIR" &&
-git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
 GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ||
 exit 1
index e27a1c5f85bbecac652ce8d224f8fc5e99b02a4e..aca40c1b1ff0f957652f75383625367d85242c14 100755 (executable)
@@ -49,14 +49,17 @@ not_present() {
 cvs >/dev/null 2>&1
 if test $? -ne 1
 then
-    test_expect_success 'skipping git-cvsserver tests, cvs not found' :
+    say 'skipping git-cvsserver tests, cvs not found'
+    test_done
+fi
+if ! test_have_prereq PERL
+then
+    say 'skipping git-cvsserver tests, perl not available'
     test_done
-    exit
 fi
 perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
-    test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' :
+    say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
     test_done
-    exit
 }
 
 unset GIT_DIR GIT_CONFIG
@@ -84,7 +87,7 @@ test_expect_success 'setup' '
     echo "subdir/file.h crlf" >> .gitattributes &&
     git add .gitattributes textfile.c binfile.bin mixedUp.c subdir/* &&
     git commit -q -m "First Commit" &&
-    git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+    git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
     GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
     GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
 '
index 6ed10d0933daa493e7a32296b38e9a32a23a725a..f4210fbb04065cfe32f26053eb5f5f054d52e3cf 100755 (executable)
@@ -63,20 +63,16 @@ gitweb_run () {
        # gitweb.log is left for debugging
 }
 
-safe_chmod () {
-       chmod "$1" "$2" &&
-       if [ "$(git config --get core.filemode)" = false ]
-       then
-               git update-index --chmod="$1" "$2"
-       fi
-}
-
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+       say 'skipping gitweb tests, perl not available'
+       test_done
+fi
+
 perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
-    test_expect_success 'skipping gitweb tests, perl version is too old' :
+    say 'skipping gitweb tests, perl version is too old'
     test_done
-    exit
 }
 
 gitweb_init
@@ -242,7 +238,7 @@ test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(0): mode change' \
-       'safe_chmod +x new_file &&
+       'test_chmod +x new_file &&
         git commit -a -m "Mode changed." &&
         gitweb_run "p=.git;a=commitdiff"'
 test_debug 'cat gitweb.log'
@@ -254,7 +250,7 @@ test_expect_success \
         gitweb_run "p=.git;a=commitdiff"'
 test_debug 'cat gitweb.log'
 
-test_expect_success \
+test_expect_success SYMLINKS \
        'commitdiff(0): file to symlink' \
        'rm renamed_file &&
         ln -s file renamed_file &&
@@ -281,7 +277,7 @@ test_debug 'cat gitweb.log'
 test_expect_success \
        'commitdiff(0): mode change and modified' \
        'echo "New line" >> file2 &&
-        safe_chmod +x file2 &&
+        test_chmod +x file2 &&
         git commit -a -m "Mode change and modification." &&
         gitweb_run "p=.git;a=commitdiff"'
 test_debug 'cat gitweb.log'
@@ -308,7 +304,7 @@ test_expect_success \
        'commitdiff(0): renamed, mode change and modified' \
        'git mv file3 file2 &&
         echo "Propter nomen suum." >> file2 &&
-        safe_chmod +x file2 &&
+        test_chmod +x file2 &&
         git commit -a -m "File rename, mode change and modification." &&
         gitweb_run "p=.git;a=commitdiff"'
 test_debug 'cat gitweb.log'
@@ -316,7 +312,7 @@ test_debug 'cat gitweb.log'
 # ----------------------------------------------------------------------
 # commitdiff testing (taken from t4114-apply-typechange.sh)
 
-test_expect_success 'setup typechange commits' '
+test_expect_success SYMLINKS 'setup typechange commits' '
        echo "hello world" > foo &&
        echo "hi planet" > bar &&
        git update-index --add foo bar &&
@@ -425,10 +421,15 @@ test_expect_success \
         git add 03-new &&
         git mv 04-rename-from 04-rename-to &&
         echo "Changed" >> 04-rename-to &&
-        safe_chmod +x 05-mode-change &&
-        rm -f 06-file-or-symlink && ln -s 01-change 06-file-or-symlink &&
+        test_chmod +x 05-mode-change &&
+        rm -f 06-file-or-symlink &&
+        if test_have_prereq SYMLINKS; then
+               ln -s 01-change 06-file-or-symlink
+        else
+               printf %s 01-change > 06-file-or-symlink
+        fi &&
         echo "Changed and have mode changed" > 07-change-mode-change   &&
-        safe_chmod +x 07-change-mode-change &&
+        test_chmod +x 07-change-mode-change &&
         git commit -a -m "Large commit" &&
         git checkout master'
 
index d2379e7f62a4da76791e65dbc2c70f4dfe14ff3b..4322a0c1ed6d792cbba4b52a7ba8bad74986bbad 100755 (executable)
@@ -3,6 +3,11 @@
 test_description='git cvsimport basic tests'
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+       say 'skipping git cvsimport tests, perl not available'
+       test_done
+fi
+
 CVSROOT=$(pwd)/cvsroot
 export CVSROOT
 unset CVS_SERVER
@@ -14,7 +19,6 @@ if ! type cvs >/dev/null 2>&1
 then
        say 'skipping cvsimport tests, cvs not found'
        test_done
-       exit
 fi
 
 cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
@@ -24,12 +28,10 @@ case "$cvsps_version" in
 '')
        say 'skipping cvsimport tests, cvsps not found'
        test_done
-       exit
        ;;
 *)
        say 'skipping cvsimport tests, unsupported cvsps version'
        test_done
-       exit
        ;;
 esac
 
index b81d5dfc340e050815ad9b2fd0d0636c529ce8d3..b4ca244626a6635faa2587722c26f4c17692af65 100755 (executable)
@@ -6,8 +6,13 @@
 test_description='perl interface (Git.pm)'
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+       say 'skipping perl interface tests, perl not available'
+       test_done
+fi
+
 perl -MTest::More -e 0 2>/dev/null || {
-       say_color skip "Perl Test::More unavailable, skipping test"
+       say "Perl Test::More unavailable, skipping test"
        test_done
 }
 
index 59d82d25e9ad586ff43a4a9485d348492fb36d19..dad1437fa49596cf6f36b40b1ab18b008620a246 100644 (file)
@@ -3,6 +3,22 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
+# if --tee was passed, write the output not only to the terminal, but
+# additionally to the file test-results/$BASENAME.out, too.
+case "$GIT_TEST_TEE_STARTED, $* " in
+done,*)
+       # do not redirect again
+       ;;
+*' --tee '*|*' --va'*)
+       mkdir -p test-results
+       BASE=test-results/$(basename "$0" .sh)
+       (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
+        echo $? > $BASE.exit) | tee $BASE.out
+       test "$(cat $BASE.exit)" = 0
+       exit
+       ;;
+esac
+
 # Keep the original TERM for say_color
 ORIGINAL_TERM=$TERM
 
@@ -94,6 +110,10 @@ do
        --no-python)
                # noop now...
                shift ;;
+       --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
+               valgrind=t; verbose=t; shift ;;
+       --tee)
+               shift ;; # was handled already
        *)
                break ;;
        esac
@@ -218,18 +238,50 @@ test_merge () {
        git tag "$1"
 }
 
+# This function helps systems where core.filemode=false is set.
+# Use it instead of plain 'chmod +x' to set or unset the executable bit
+# of a file in the working directory and add it to the index.
+
+test_chmod () {
+       chmod "$@" &&
+       git update-index --add "--chmod=$@"
+}
+
+# Use test_set_prereq to tell that a particular prerequisite is available.
+# The prerequisite can later be checked for in two ways:
+#
+# - Explicitly using test_have_prereq.
+#
+# - Implicitly by specifying the prerequisite tag in the calls to
+#   test_expect_{success,failure,code}.
+#
+# The single parameter is the prerequisite tag (a simple word, in all
+# capital letters by convention).
+
+test_set_prereq () {
+       satisfied="$satisfied$1 "
+}
+satisfied=" "
+
+test_have_prereq () {
+       case $satisfied in
+       *" $1 "*)
+               : yes, have it ;;
+       *)
+               ! : nope ;;
+       esac
+}
+
 # You are not expected to call test_ok_ and test_failure_ directly, use
 # the text_expect_* functions instead.
 
 test_ok_ () {
-       test_count=$(expr "$test_count" + 1)
-       test_success=$(expr "$test_success" + 1)
+       test_success=$(($test_success + 1))
        say_color "" "  ok $test_count: $@"
 }
 
 test_failure_ () {
-       test_count=$(expr "$test_count" + 1)
-       test_failure=$(expr "$test_failure" + 1);
+       test_failure=$(($test_failure + 1))
        say_color error "FAIL $test_count: $1"
        shift
        echo "$@" | sed -e 's/^/        /'
@@ -237,13 +289,11 @@ test_failure_ () {
 }
 
 test_known_broken_ok_ () {
-       test_count=$(expr "$test_count" + 1)
        test_fixed=$(($test_fixed+1))
        say_color "" "  FIXED $test_count: $@"
 }
 
 test_known_broken_failure_ () {
-       test_count=$(expr "$test_count" + 1)
        test_broken=$(($test_broken+1))
        say_color skip "  still broken $test_count: $@"
 }
@@ -259,20 +309,23 @@ test_run_ () {
 }
 
 test_skip () {
-       this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$')
-       this_test="$this_test.$(expr "$test_count" + 1)"
+       test_count=$(($test_count+1))
        to_skip=
        for skp in $GIT_SKIP_TESTS
        do
-               case "$this_test" in
+               case $this_test.$test_count in
                $skp)
                        to_skip=t
                esac
        done
+       if test -z "$to_skip" && test -n "$prereq" &&
+          ! test_have_prereq "$prereq"
+       then
+               to_skip=t
+       fi
        case "$to_skip" in
        t)
                say_color skip >&3 "skipping test: $@"
-               test_count=$(expr "$test_count" + 1)
                say_color skip "skip $test_count: $1"
                : true
                ;;
@@ -283,8 +336,9 @@ test_skip () {
 }
 
 test_expect_failure () {
+       test "$#" = 3 && { prereq=$1; shift; } || prereq=
        test "$#" = 2 ||
-       error "bug in the test script: not 2 parameters to test-expect-failure"
+       error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
        if ! test_skip "$@"
        then
                say >&3 "checking known breakage: $2"
@@ -300,8 +354,9 @@ test_expect_failure () {
 }
 
 test_expect_success () {
+       test "$#" = 3 && { prereq=$1; shift; } || prereq=
        test "$#" = 2 ||
-       error "bug in the test script: not 2 parameters to test-expect-success"
+       error "bug in the test script: not 2 or 3 parameters to test-expect-success"
        if ! test_skip "$@"
        then
                say >&3 "expecting success: $2"
@@ -317,8 +372,9 @@ test_expect_success () {
 }
 
 test_expect_code () {
+       test "$#" = 4 && { prereq=$1; shift; } || prereq=
        test "$#" = 3 ||
-       error "bug in the test script: not 3 parameters to test-expect-code"
+       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"
@@ -342,15 +398,16 @@ test_expect_code () {
 # Usage: test_external description command arguments...
 # Example: test_external 'Perl API' perl ../path/to/test.pl
 test_external () {
-       test "$#" -eq 3 ||
-       error >&5 "bug in the test script: not 3 parameters to test_external"
+       test "$#" = 4 && { prereq=$1; shift; } || prereq=
+       test "$#" = 3 ||
+       error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
        descr="$1"
        shift
        if ! test_skip "$descr" "$@"
        then
                # Announce the script to reduce confusion about the
                # test output that follows.
-               say_color "" " run $(expr "$test_count" + 1): $descr ($*)"
+               say_color "" " run $test_count: $descr ($*)"
                # Run command; redirect its stderr to &4 as in
                # test_run_, but keep its stdout on our stdout even in
                # non-verbose mode.
@@ -434,7 +491,7 @@ test_create_repo () {
        repo="$1"
        mkdir -p "$repo"
        cd "$repo" || error "Cannot setup test environment"
-       "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >&3 2>&4 ||
+       "$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"
@@ -444,7 +501,7 @@ test_done () {
        trap - EXIT
        test_results_dir="$TEST_DIRECTORY/test-results"
        mkdir -p "$test_results_dir"
-       test_results_path="$test_results_dir/${0%-*}-$$"
+       test_results_path="$test_results_dir/${0%.sh}-$$"
 
        echo "total $test_count" >> $test_results_path
        echo "success $test_success" >> $test_results_path
@@ -484,8 +541,81 @@ 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)
-PATH=$TEST_DIRECTORY/..:$PATH
-GIT_EXEC_PATH=$(pwd)/..
+if test -z "$valgrind"
+then
+       if test -z "$GIT_TEST_INSTALLED"
+       then
+               PATH=$TEST_DIRECTORY/..:$PATH
+               GIT_EXEC_PATH=$TEST_DIRECTORY/..
+       else
+               GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
+               error "Cannot run git from $GIT_TEST_INSTALLED."
+               PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH
+               GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
+       fi
+else
+       make_symlink () {
+               test -h "$2" &&
+               test "$1" = "$(readlink "$2")" || {
+                       # be super paranoid
+                       if mkdir "$2".lock
+                       then
+                               rm -f "$2" &&
+                               ln -s "$1" "$2" &&
+                               rm -r "$2".lock
+                       else
+                               while test -d "$2".lock
+                               do
+                                       say "Waiting for lock on $2."
+                                       sleep 1
+                               done
+                       fi
+               }
+       }
+
+       make_valgrind_symlink () {
+               # handle only executables
+               test -x "$1" || return
+
+               base=$(basename "$1")
+               symlink_target=$TEST_DIRECTORY/../$base
+               # do not override scripts
+               if test -x "$symlink_target" &&
+                   test ! -d "$symlink_target" &&
+                   test "#!" != "$(head -c 2 < "$symlink_target")"
+               then
+                       symlink_target=../valgrind.sh
+               fi
+               case "$base" in
+               *.sh|*.perl)
+                       symlink_target=../unprocessed-script
+               esac
+               # create the link, or replace it if it is out of date
+               make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit
+       }
+
+       # 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-*
+       do
+               make_valgrind_symlink $file
+       done
+       OLDIFS=$IFS
+       IFS=:
+       for path in $PATH
+       do
+               ls "$path"/git-* 2> /dev/null |
+               while read file
+               do
+                       make_valgrind_symlink "$file"
+               done
+       done
+       IFS=$OLDIFS
+       PATH=$GIT_VALGRIND/bin:$PATH
+       GIT_EXEC_PATH=$GIT_VALGRIND/bin
+       export GIT_VALGRIND
+fi
 GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
 unset GIT_CONFIG
 GIT_CONFIG_NOSYSTEM=1
@@ -520,7 +650,8 @@ test_create_repo "$test"
 # in subprocesses like git equals our $PWD (for pathname comparisons).
 cd -P "$test" || exit 1
 
-this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$')
+this_test=${0##*/}
+this_test=${this_test%%-*}
 for skp in $GIT_SKIP_TESTS
 do
        to_skip=
@@ -538,3 +669,37 @@ do
                test_done
        esac
 done
+
+# Fix some commands on Windows
+case $(uname -s) in
+*MINGW*)
+       # Windows has its own (incompatible) sort and find
+       sort () {
+               /usr/bin/sort "$@"
+       }
+       find () {
+               /usr/bin/find "$@"
+       }
+       sum () {
+               md5sum "$@"
+       }
+       # git sees Windows-style pwd
+       pwd () {
+               builtin pwd -W
+       }
+       # no POSIX permissions
+       # backslashes in pathspec are converted to '/'
+       # exec does not inherit the PID
+       ;;
+*)
+       test_set_prereq POSIXPERM
+       test_set_prereq BSLASHPSPEC
+       test_set_prereq EXECKEEPSPID
+       ;;
+esac
+
+test -z "$NO_PERL" && test_set_prereq PERL
+
+# 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
diff --git a/t/valgrind/.gitignore b/t/valgrind/.gitignore
new file mode 100644 (file)
index 0000000..d4ae667
--- /dev/null
@@ -0,0 +1,2 @@
+/bin/
+/templates
diff --git a/t/valgrind/analyze.sh b/t/valgrind/analyze.sh
new file mode 100755 (executable)
index 0000000..d8105d9
--- /dev/null
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+out_prefix=$(dirname "$0")/../test-results/valgrind.out
+output=
+count=0
+total_count=0
+missing_message=
+new_line='
+'
+
+# start outputting the current valgrind error in $out_prefix.++$count,
+# and the test case which failed in the corresponding .message file
+start_output () {
+       test -z "$output" || return
+
+       # progress
+       total_count=$(($total_count+1))
+       test -t 2 && printf "\rFound %d errors" $total_count >&2
+
+       count=$(($count+1))
+       output=$out_prefix.$count
+       : > $output
+
+       echo "*** $1 ***" > $output.message
+}
+
+finish_output () {
+       test ! -z "$output" || return
+       output=
+
+       # if a test case has more than one valgrind error, we need to
+       # copy the last .message file to the previous errors
+       test -z "$missing_message" || {
+               while test $missing_message -lt $count
+               do
+                       cp $out_prefix.$count.message \
+                               $out_prefix.$missing_message.message
+                       missing_message=$(($missing_message+1))
+               done
+               missing_message=
+       }
+}
+
+# group the valgrind errors by backtrace
+output_all () {
+       last_line=
+       j=0
+       i=1
+       while test $i -le $count
+       do
+               # output <number> <backtrace-in-one-line>
+               echo "$i $(tr '\n' ' ' < $out_prefix.$i)"
+               i=$(($i+1))
+       done |
+       sort -t ' ' -k 2 | # order by <backtrace-in-one-line>
+       while read number line
+       do
+               # find duplicates, do not output backtrace twice
+               if test "$line" != "$last_line"
+               then
+                       last_line=$line
+                       j=$(($j+1))
+                       printf "\nValgrind error $j:\n\n"
+                       cat $out_prefix.$number
+                       printf "\nfound in:\n"
+               fi
+               # print the test case where this came from
+               printf "\n"
+               cat $out_prefix.$number.message
+       done
+}
+
+handle_one () {
+       OLDIFS=$IFS
+       IFS="$new_line"
+       while read line
+       do
+               case "$line" in
+               # backtrace, possibly a new one
+               ==[0-9]*)
+
+                       # Does the current valgrind error have a message yet?
+                       case "$output" in
+                       *.message)
+                               test -z "$missing_message" &&
+                               missing_message=$count
+                               output=
+                       esac
+
+                       start_output $(basename $1)
+                       echo "$line" |
+                       sed 's/==[0-9]*==/==valgrind==/' >> $output
+                       ;;
+               # end of backtrace
+               '}')
+                       test -z "$output" || {
+                               echo "$line" >> $output
+                               test $output = ${output%.message} &&
+                               output=$output.message
+                       }
+                       ;;
+               # end of test case
+               '')
+                       finish_output
+                       ;;
+               # normal line; if $output is set, print the line
+               *)
+                       test -z "$output" || echo "$line" >> $output
+                       ;;
+               esac
+       done < $1
+       IFS=$OLDIFS
+
+       # just to be safe
+       finish_output
+}
+
+for test_script in "$(dirname "$0")"/../test-results/*.out
+do
+       handle_one $test_script
+done
+
+output_all
diff --git a/t/valgrind/default.supp b/t/valgrind/default.supp
new file mode 100644 (file)
index 0000000..9e013fa
--- /dev/null
@@ -0,0 +1,45 @@
+{
+       ignore-zlib-errors-cond
+       Memcheck:Cond
+       obj:*libz.so*
+}
+
+{
+       ignore-zlib-errors-value8
+       Memcheck:Value8
+       obj:*libz.so*
+}
+
+{
+       ignore-zlib-errors-value4
+       Memcheck:Value4
+       obj:*libz.so*
+}
+
+{
+       ignore-ldso-cond
+       Memcheck:Cond
+       obj:*ld-*.so
+}
+
+{
+       ignore-ldso-addr8
+       Memcheck:Addr8
+       obj:*ld-*.so
+}
+
+{
+       ignore-ldso-addr4
+       Memcheck:Addr4
+       obj:*ld-*.so
+}
+
+{
+       writing-data-from-zlib-triggers-even-more-errors
+       Memcheck:Param
+       write(buf)
+       obj:/lib/ld-*.so
+       fun:write_in_full
+       fun:write_buffer
+       fun:write_loose_object
+}
diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh
new file mode 100755 (executable)
index 0000000..582b4dc
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+base=$(basename "$0")
+
+TRACK_ORIGINS=
+
+VALGRIND_VERSION=$(valgrind --version)
+VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
+VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
+test 3 -gt "$VALGRIND_MAJOR" ||
+test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
+TRACK_ORIGINS=--track-origins=yes
+
+exec valgrind -q --error-exitcode=126 \
+       --leak-check=no \
+       --suppressions="$GIT_VALGRIND/default.supp" \
+       --gen-suppressions=all \
+       $TRACK_ORIGINS \
+       --log-fd=4 \
+       --input-fd=4 \
+       $GIT_VALGRIND_OPTIONS \
+       "$GIT_VALGRIND"/../../"$base" "$@"
index 0e49279c7f7b805c78f1bc4760a0d1c70a84a0d9..0ba62076fb4a381e588eba90ed639dd560860fd3 100755 (executable)
@@ -7,7 +7,7 @@
 #
 # To enable this hook, rename this file to "pre-commit".
 
-if git-rev-parse --verify HEAD 2>/dev/null
+if git-rev-parse --verify HEAD >/dev/null 2>&1
 then
        against=HEAD
 else
index 93c605594fc06683088b934273873165215ccbb5..f8bf490cffa2d41d19ffc9a31839ca3c2686df16 100755 (executable)
@@ -16,6 +16,9 @@
 # hooks.allowdeletebranch
 #   This boolean sets whether deleting branches will be allowed in the
 #   repository.  By default they won't be.
+# hooks.denycreatebranch
+#   This boolean sets whether remotely creating branches will be denied
+#   in the repository.  By default this is allowed.
 #
 
 # --- Command line
@@ -39,18 +42,22 @@ fi
 # --- Config
 allowunannotated=$(git config --bool hooks.allowunannotated)
 allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --bool hooks.denycreatebranch)
 allowdeletetag=$(git config --bool hooks.allowdeletetag)
 
 # check for no description
 projectdesc=$(sed -e '1q' "$GIT_DIR/description")
-if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then
+case "$projectdesc" in
+"Unnamed repository"* | "")
        echo "*** Project description file hasn't been set" >&2
        exit 1
-fi
+       ;;
+esac
 
 # --- Check types
 # if $newrev is 0000...0000, it's a commit to delete a ref.
-if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then
+zero="0000000000000000000000000000000000000000"
+if [ "$newrev" = "$zero" ]; then
        newrev_type=delete
 else
        newrev_type=$(git-cat-file -t $newrev)
@@ -78,6 +85,10 @@ case "$refname","$newrev_type" in
                ;;
        refs/heads/*,commit)
                # branch
+               if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
+                       echo "*** Creating a branch is not allowed in this repository" >&2
+                       exit 1
+               fi
                ;;
        refs/heads/*,delete)
                # delete branch
index c6f25e80b8bcf0a21db2bea368b9e444c19bc0bf..498b267a8c7812490d6479839c5577eaaec79d62 100644 (file)
@@ -1 +1 @@
-Unnamed repository; edit this file to name it for gitweb.
+Unnamed repository; edit this file 'description' to name the repository.
index 9ae92cd39c3f45cb2a58a24801a790ce6622d22c..efecb65258ba95ee743f0e3ca178e74e2756e7d1 100644 (file)
@@ -143,7 +143,7 @@ static const char *rsync_url(const char *url)
        return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
 }
 
-static struct ref *get_refs_via_rsync(struct transport *transport)
+static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 {
        struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
        struct ref dummy, *tail = &dummy;
@@ -151,6 +151,9 @@ static struct ref *get_refs_via_rsync(struct transport *transport)
        const char *args[5];
        int temp_dir_len;
 
+       if (for_push)
+               return NULL;
+
        /* copy the refs to the temporary directory */
 
        strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
@@ -429,7 +432,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
        return !!err;
 }
 
-static struct ref *get_refs_via_curl(struct transport *transport)
+static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
 {
        struct strbuf buffer = STRBUF_INIT;
        char *data, *start, *mid;
@@ -446,6 +449,9 @@ static struct ref *get_refs_via_curl(struct transport *transport)
 
        struct walker *walker;
 
+       if (for_push)
+               return NULL;
+
        if (!transport->data)
                transport->data = get_http_walker(transport->url,
                                                transport->remote);
@@ -532,12 +538,15 @@ struct bundle_transport_data {
        struct bundle_header header;
 };
 
-static struct ref *get_refs_from_bundle(struct transport *transport)
+static struct ref *get_refs_from_bundle(struct transport *transport, int for_push)
 {
        struct bundle_transport_data *data = transport->data;
        struct ref *result = NULL;
        int i;
 
+       if (for_push)
+               return NULL;
+
        if (data->fd > 0)
                close(data->fd);
        data->fd = read_bundle_header(transport->url, &data->header);
@@ -578,6 +587,7 @@ struct git_transport_data {
        int fd[2];
        const char *uploadpack;
        const char *receivepack;
+       struct extra_have_objects extra_have;
 };
 
 static int set_git_option(struct transport *connection,
@@ -609,20 +619,23 @@ static int set_git_option(struct transport *connection,
        return 1;
 }
 
-static int connect_setup(struct transport *transport)
+static int connect_setup(struct transport *transport, int for_push, int verbose)
 {
        struct git_transport_data *data = transport->data;
-       data->conn = git_connect(data->fd, transport->url, data->uploadpack, 0);
+       data->conn = git_connect(data->fd, transport->url,
+                                for_push ? data->receivepack : data->uploadpack,
+                                verbose ? CONNECT_VERBOSE : 0);
        return 0;
 }
 
-static struct ref *get_refs_via_connect(struct transport *transport)
+static struct ref *get_refs_via_connect(struct transport *transport, int for_push)
 {
        struct git_transport_data *data = transport->data;
        struct ref *refs;
 
-       connect_setup(transport);
-       get_remote_heads(data->fd[0], &refs, 0, NULL, 0, NULL);
+       connect_setup(transport, for_push, 0);
+       get_remote_heads(data->fd[0], &refs, 0, NULL,
+                        for_push ? REF_NORMAL : 0, &data->extra_have);
 
        return refs;
 }
@@ -654,7 +667,7 @@ static int fetch_refs_via_pack(struct transport *transport,
                origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
 
        if (!data->conn) {
-               connect_setup(transport);
+               connect_setup(transport, 0, 0);
                get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
        }
 
@@ -677,20 +690,216 @@ static int fetch_refs_via_pack(struct transport *transport,
        return (refs ? 0 : -1);
 }
 
-static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
+static int refs_pushed(struct ref *ref)
+{
+       for (; ref; ref = ref->next) {
+               switch(ref->status) {
+               case REF_STATUS_NONE:
+               case REF_STATUS_UPTODATE:
+                       break;
+               default:
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static void update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)
+{
+       struct refspec rs;
+
+       if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
+               return;
+
+       rs.src = ref->name;
+       rs.dst = NULL;
+
+       if (!remote_find_tracking(remote, &rs)) {
+               if (verbose)
+                       fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
+               if (ref->deletion) {
+                       delete_ref(rs.dst, NULL, 0);
+               } else
+                       update_ref("update by push", rs.dst,
+                                       ref->new_sha1, NULL, 0, 0);
+               free(rs.dst);
+       }
+}
+
+#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
+
+static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
+{
+       fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+       if (from)
+               fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
+       else
+               fputs(prettify_ref(to), stderr);
+       if (msg) {
+               fputs(" (", stderr);
+               fputs(msg, stderr);
+               fputc(')', stderr);
+       }
+       fputc('\n', stderr);
+}
+
+static const char *status_abbrev(unsigned char sha1[20])
+{
+       return find_unique_abbrev(sha1, DEFAULT_ABBREV);
+}
+
+static void print_ok_ref_status(struct ref *ref)
+{
+       if (ref->deletion)
+               print_ref_status('-', "[deleted]", ref, NULL, NULL);
+       else if (is_null_sha1(ref->old_sha1))
+               print_ref_status('*',
+                       (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
+                         "[new branch]"),
+                       ref, ref->peer_ref, NULL);
+       else {
+               char quickref[84];
+               char type;
+               const char *msg;
+
+               strcpy(quickref, status_abbrev(ref->old_sha1));
+               if (ref->nonfastforward) {
+                       strcat(quickref, "...");
+                       type = '+';
+                       msg = "forced update";
+               } else {
+                       strcat(quickref, "..");
+                       type = ' ';
+                       msg = NULL;
+               }
+               strcat(quickref, status_abbrev(ref->new_sha1));
+
+               print_ref_status(type, quickref, ref, ref->peer_ref, msg);
+       }
+}
+
+static int print_one_push_status(struct ref *ref, const char *dest, int count)
+{
+       if (!count)
+               fprintf(stderr, "To %s\n", dest);
+
+       switch(ref->status) {
+       case REF_STATUS_NONE:
+               print_ref_status('X', "[no match]", ref, NULL, NULL);
+               break;
+       case REF_STATUS_REJECT_NODELETE:
+               print_ref_status('!', "[rejected]", ref, NULL,
+                               "remote does not support deleting refs");
+               break;
+       case REF_STATUS_UPTODATE:
+               print_ref_status('=', "[up to date]", ref,
+                               ref->peer_ref, NULL);
+               break;
+       case REF_STATUS_REJECT_NONFASTFORWARD:
+               print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+                               "non-fast forward");
+               break;
+       case REF_STATUS_REMOTE_REJECT:
+               print_ref_status('!', "[remote rejected]", ref,
+                               ref->deletion ? NULL : ref->peer_ref,
+                               ref->remote_status);
+               break;
+       case REF_STATUS_EXPECTING_REPORT:
+               print_ref_status('!', "[remote failure]", ref,
+                               ref->deletion ? NULL : ref->peer_ref,
+                               "remote failed to report status");
+               break;
+       case REF_STATUS_OK:
+               print_ok_ref_status(ref);
+               break;
+       }
+
+       return 1;
+}
+
+static void print_push_status(const char *dest, struct ref *refs, int verbose)
+{
+       struct ref *ref;
+       int n = 0;
+
+       if (verbose) {
+               for (ref = refs; ref; ref = ref->next)
+                       if (ref->status == REF_STATUS_UPTODATE)
+                               n += print_one_push_status(ref, dest, n);
+       }
+
+       for (ref = refs; ref; ref = ref->next)
+               if (ref->status == REF_STATUS_OK)
+                       n += print_one_push_status(ref, dest, n);
+
+       for (ref = refs; ref; ref = ref->next) {
+               if (ref->status != REF_STATUS_NONE &&
+                   ref->status != REF_STATUS_UPTODATE &&
+                   ref->status != REF_STATUS_OK)
+                       n += print_one_push_status(ref, dest, n);
+       }
+}
+
+static void verify_remote_names(int nr_heads, const char **heads)
+{
+       int i;
+
+       for (i = 0; i < nr_heads; i++) {
+               const char *local = heads[i];
+               const char *remote = strrchr(heads[i], ':');
+
+               if (*local == '+')
+                       local++;
+
+               /* A matching refspec is okay.  */
+               if (remote == local && remote[1] == '\0')
+                       continue;
+
+               remote = remote ? (remote + 1) : local;
+               switch (check_ref_format(remote)) {
+               case 0: /* ok */
+               case CHECK_REF_FORMAT_ONELEVEL:
+                       /* ok but a single level -- that is fine for
+                        * a match pattern.
+                        */
+               case CHECK_REF_FORMAT_WILDCARD:
+                       /* ok but ends with a pattern-match character */
+                       continue;
+               }
+               die("remote part of refspec is not a valid name in %s",
+                   heads[i]);
+       }
+}
+
+static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags)
 {
        struct git_transport_data *data = transport->data;
        struct send_pack_args args;
+       int ret;
+
+       if (!data->conn) {
+               struct ref *tmp_refs;
+               connect_setup(transport, 1, 0);
+
+               get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
+                                NULL);
+       }
 
-       args.receivepack = data->receivepack;
-       args.send_all = !!(flags & TRANSPORT_PUSH_ALL);
        args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
        args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
        args.use_thin_pack = data->thin;
        args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
 
-       return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec);
+       ret = send_pack(&args, data->fd, data->conn, remote_refs,
+                       &data->extra_have);
+
+       close(data->fd[1]);
+       close(data->fd[0]);
+       ret |= finish_connect(data->conn);
+       data->conn = NULL;
+
+       return ret;
 }
 
 static int disconnect_git(struct transport *transport)
@@ -760,7 +969,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
                ret->set_option = set_git_option;
                ret->get_refs_list = get_refs_via_connect;
                ret->fetch = fetch_refs_via_pack;
-               ret->push = git_transport_push;
+               ret->push_refs = git_transport_push;
                ret->disconnect = disconnect_git;
 
                data->thin = 1;
@@ -787,15 +996,53 @@ int transport_set_option(struct transport *transport,
 int transport_push(struct transport *transport,
                   int refspec_nr, const char **refspec, int flags)
 {
-       if (!transport->push)
-               return 1;
-       return transport->push(transport, refspec_nr, refspec, flags);
+       verify_remote_names(refspec_nr, refspec);
+
+       if (transport->push)
+               return transport->push(transport, refspec_nr, refspec, flags);
+       if (transport->push_refs) {
+               struct ref *remote_refs =
+                       transport->get_refs_list(transport, 1);
+               struct ref **remote_tail;
+               struct ref *local_refs = get_local_heads();
+               int match_flags = MATCH_REFS_NONE;
+               int verbose = flags & TRANSPORT_PUSH_VERBOSE;
+               int ret;
+
+               if (flags & TRANSPORT_PUSH_ALL)
+                       match_flags |= MATCH_REFS_ALL;
+               if (flags & TRANSPORT_PUSH_MIRROR)
+                       match_flags |= MATCH_REFS_MIRROR;
+
+               remote_tail = &remote_refs;
+               while (*remote_tail)
+                       remote_tail = &((*remote_tail)->next);
+               if (match_refs(local_refs, remote_refs, &remote_tail,
+                              refspec_nr, refspec, match_flags)) {
+                       return -1;
+               }
+
+               ret = transport->push_refs(transport, remote_refs, flags);
+
+               print_push_status(transport->url, remote_refs, verbose);
+
+               if (!(flags & TRANSPORT_PUSH_DRY_RUN)) {
+                       struct ref *ref;
+                       for (ref = remote_refs; ref; ref = ref->next)
+                               update_tracking_ref(transport->remote, ref, verbose);
+               }
+
+               if (!ret && !refs_pushed(remote_refs))
+                       fprintf(stderr, "Everything up-to-date\n");
+               return ret;
+       }
+       return 1;
 }
 
 const struct ref *transport_get_remote_refs(struct transport *transport)
 {
        if (!transport->remote_refs)
-               transport->remote_refs = transport->get_refs_list(transport);
+               transport->remote_refs = transport->get_refs_list(transport, 0);
        return transport->remote_refs;
 }
 
@@ -822,7 +1069,7 @@ int transport_fetch_refs(struct transport *transport, const struct ref *refs)
 void transport_unlock_pack(struct transport *transport)
 {
        if (transport->pack_lockfile) {
-               unlink(transport->pack_lockfile);
+               unlink_or_warn(transport->pack_lockfile);
                free(transport->pack_lockfile);
                transport->pack_lockfile = NULL;
        }
index 6bbc1a82642ab9e5722cfe6ab34ec4246b3a9dd4..b1c225276619c3bc4dda5dcd0469e242ab421ea0 100644 (file)
@@ -18,8 +18,9 @@ struct transport {
        int (*set_option)(struct transport *connection, const char *name,
                          const char *value);
 
-       struct ref *(*get_refs_list)(struct transport *transport);
+       struct ref *(*get_refs_list)(struct transport *transport, int for_push);
        int (*fetch)(struct transport *transport, int refs_nr, const struct ref **refs);
+       int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
        int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
 
        int (*disconnect)(struct transport *connection);
index b05d0f43555d28f872fd5225e6773eeb636ef302..edd83949bf0896bedff4cab8e2b037eba574a45e 100644 (file)
@@ -374,7 +374,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
        }
 
        /*
-        * Then, discard all the non-relevane file pairs...
+        * Then, discard all the non-relevant file pairs...
         */
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
diff --git a/unimplemented.sh b/unimplemented.sh
new file mode 100644 (file)
index 0000000..5252de4
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo >&2 "fatal: git was built without support for `basename $0` (@@REASON@@)."
+exit 128
index 5820ce48f37c08cc4d6cab359af09f121645b7c1..a0697d24126b8cc894649da9a25e652bbea9dab7 100644 (file)
@@ -7,6 +7,7 @@
 #include "unpack-trees.h"
 #include "progress.h"
 #include "refs.h"
+#include "attr.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -52,36 +53,17 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
        add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
-/* Unlink the last component and attempt to remove leading
- * directories, in case this unlink is the removal of the
- * last entry in the directory -- empty directories are removed.
+/*
+ * 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)
 {
-       char *cp, *prev;
-       char *name = ce->name;
-
-       if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name))
+       if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
                return;
-       if (unlink(name))
+       if (unlink_or_warn(ce->name))
                return;
-       prev = NULL;
-       while (1) {
-               int status;
-               cp = strrchr(name, '/');
-               if (prev)
-                       *prev = '/';
-               if (!cp)
-                       break;
-
-               *cp = 0;
-               status = rmdir(name);
-               if (status) {
-                       *cp = '/';
-                       break;
-               }
-               prev = cp;
-       }
+       schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
 
 static struct checkout state;
@@ -105,6 +87,8 @@ static int check_updates(struct unpack_trees_options *o)
                cnt = 0;
        }
 
+       if (o->update)
+               git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result);
        for (i = 0; i < index->cache_nr; i++) {
                struct cache_entry *ce = index->cache[i];
 
@@ -112,11 +96,10 @@ static int check_updates(struct unpack_trees_options *o)
                        display_progress(progress, ++cnt);
                        if (o->update)
                                unlink_entry(ce);
-                       remove_index_entry_at(&o->result, i);
-                       i--;
-                       continue;
                }
        }
+       remove_marked_cache_entries(&o->result);
+       remove_scheduled_dirs();
 
        for (i = 0; i < index->cache_nr; i++) {
                struct cache_entry *ce = index->cache[i];
@@ -130,6 +113,8 @@ static int check_updates(struct unpack_trees_options *o)
                }
        }
        stop_progress(&progress);
+       if (o->update)
+               git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
        return errs != 0;
 }
 
@@ -380,8 +365,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 
        memset(&o->result, 0, sizeof(o->result));
        o->result.initialized = 1;
-       if (o->src_index)
-               o->result.timestamp = o->src_index->timestamp;
+       if (o->src_index) {
+               o->result.timestamp.sec = o->src_index->timestamp.sec;
+               o->result.timestamp.nsec = o->src_index->timestamp.nsec;
+       }
        o->merge_size = len;
 
        if (!dfc)
@@ -446,7 +433,7 @@ static int verify_uptodate(struct cache_entry *ce,
 {
        struct stat st;
 
-       if (o->index_only || o->reset)
+       if (o->index_only || o->reset || ce_uptodate(ce))
                return 0;
 
        if (!lstat(ce->name, &st)) {
@@ -583,7 +570,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,
        if (o->index_only || o->reset || !o->update)
                return 0;
 
-       if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name))
+       if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
                return 0;
 
        if (!lstat(ce->name, &st)) {
@@ -995,7 +982,7 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
                return error("Cannot do a oneway merge of %d trees",
                             o->merge_size);
 
-       if (!a)
+       if (!a || a == o->df_conflict_entry)
                return deleted_entry(old, old, o);
 
        if (old && same(old, a)) {
index b98b1d61c127de6368d24cc3c3b16a054724ed74..edc78612289c5bd774e18fe5f8e1b9ea80378a5f 100644 (file)
@@ -66,7 +66,7 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
 }
 
 static FILE *pack_pipe = NULL;
-static void show_commit(struct commit *commit)
+static void show_commit(struct commit *commit, void *data)
 {
        if (commit->object.flags & BOUNDARY)
                fputc('-', pack_pipe);
@@ -136,7 +136,7 @@ static int do_rev_list(int fd, void *create_full_pack)
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
-       traverse_commit_list(&revs, show_commit, show_object);
+       traverse_commit_list(&revs, show_commit, show_object, NULL);
        fflush(pack_pipe);
        fclose(pack_pipe);
        return 0;
@@ -399,12 +399,11 @@ static int get_common_commits(void)
        static char line[1000];
        unsigned char sha1[20];
        char hex[41], last_hex[41];
-       int len;
 
        save_commit_buffer = 0;
 
        for(;;) {
-               len = packet_read_line(0, line, sizeof(line));
+               int len = packet_read_line(0, line, sizeof(line));
                reset_timeout();
 
                if (!len) {
@@ -412,7 +411,7 @@ static int get_common_commits(void)
                                packet_write(1, "NAK\n");
                        continue;
                }
-               len = strip(line, len);
+               strip(line, len);
                if (!prefixcmp(line, "have ")) {
                        switch (got_sha1(line+5, sha1)) {
                        case -1: /* they have what we do not */
@@ -647,7 +646,7 @@ int main(int argc, char **argv)
        dir = argv[i];
 
        if (!enter_repo(dir, strict))
-               die("'%s': unable to chdir or not a git archive", dir);
+               die("'%s' does not appear to be a git repository", dir);
        if (is_repository_shallow())
                die("attempt to fetch/clone from a shallow repository");
        if (getenv("GIT_DEBUG_SEND_PACK"))
index d556da975197a718979575864e37c2b9b9f3327a..57529ae63d49826952b29860b3d4106b60250c7b 100644 (file)
@@ -13,7 +13,8 @@ PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
         "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"),
 PATTERNS("java",
         "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
-        "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
+        "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$",
+        /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
         "|[-+*/<>%&^|=!]="
@@ -25,7 +26,7 @@ PATTERNS("objc",
         /* Objective-C methods */
         "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
         /* C functions */
-        "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
+        "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$\n"
         /* Objective-C class/protocol definitions */
         "^(@(implementation|interface|protocol)[ \t].*)$",
         /* -- */
index d8efb1365a01104db568633fa8f6aef0c67b4cd1..7eb3218ee995991881252e4bb599452d7214ae4f 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -289,3 +289,19 @@ int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
        safe_create_leading_directories(name);
        return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
 }
+
+int unlink_or_warn(const char *file)
+{
+       int rc = unlink(file);
+
+       if (rc < 0) {
+               int err = errno;
+               if (ENOENT != err) {
+                       warning("unable to unlink %s: %s",
+                               file, strerror(errno));
+                       errno = err;
+               }
+       }
+       return rc;
+}
+
diff --git a/ws.c b/ws.c
index b1efcd9d753a29d295702b36fb1beba58fb16995..59d0883c1f3a46edcbb2e45b172c9eac3b9d5e35 100644 (file)
--- a/ws.c
+++ b/ws.c
 static struct whitespace_rule {
        const char *rule_name;
        unsigned rule_bits;
+       unsigned loosens_error;
 } whitespace_rule_names[] = {
-       { "trailing-space", WS_TRAILING_SPACE },
-       { "space-before-tab", WS_SPACE_BEFORE_TAB },
-       { "indent-with-non-tab", WS_INDENT_WITH_NON_TAB },
-       { "cr-at-eol", WS_CR_AT_EOL },
+       { "trailing-space", WS_TRAILING_SPACE, 0 },
+       { "space-before-tab", WS_SPACE_BEFORE_TAB, 0 },
+       { "indent-with-non-tab", WS_INDENT_WITH_NON_TAB, 0 },
+       { "cr-at-eol", WS_CR_AT_EOL, 1 },
 };
 
 unsigned parse_whitespace_rule(const char *string)
@@ -79,7 +80,8 @@ unsigned whitespace_rule(const char *pathname)
                        unsigned all_rule = 0;
                        int i;
                        for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
-                               all_rule |= whitespace_rule_names[i].rule_bits;
+                               if (!whitespace_rule_names[i].loosens_error)
+                                       all_rule |= whitespace_rule_names[i].rule_bits;
                        return all_rule;
                } else if (ATTR_FALSE(value)) {
                        /* false (-whitespace) */
@@ -259,12 +261,11 @@ int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *erro
        /*
         * Strip trailing whitespace
         */
-       if ((ws_rule & WS_TRAILING_SPACE) &&
-           (2 <= len && isspace(src[len-2]))) {
-               if (src[len - 1] == '\n') {
+       if (ws_rule & WS_TRAILING_SPACE) {
+               if (0 < len && src[len - 1] == '\n') {
                        add_nl_to_tail = 1;
                        len--;
-                       if (1 < len && src[len - 1] == '\r') {
+                       if (0 < len && src[len - 1] == '\r') {
                                add_cr_to_tail = !!(ws_rule & WS_CR_AT_EOL);
                                len--;
                        }
index 96ff2f8f564b907b9ef77bd2ea41b5e854a13085..1b6df45450c39c0f456df68485b5735950816ac8 100644 (file)
@@ -15,11 +15,11 @@ int wt_status_relative_paths = 1;
 int wt_status_use_color = -1;
 int wt_status_submodule_summary;
 static char wt_status_colors[][COLOR_MAXLEN] = {
-       "",         /* WT_STATUS_HEADER: normal */
-       "\033[32m", /* WT_STATUS_UPDATED: green */
-       "\033[31m", /* WT_STATUS_CHANGED: red */
-       "\033[31m", /* WT_STATUS_UNTRACKED: red */
-       "\033[31m", /* WT_STATUS_NOBRANCH: red */
+       GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
+       GIT_COLOR_GREEN,  /* WT_STATUS_UPDATED */
+       GIT_COLOR_RED,    /* WT_STATUS_CHANGED */
+       GIT_COLOR_RED,    /* WT_STATUS_UNTRACKED */
+       GIT_COLOR_RED,    /* WT_STATUS_NOBRANCH */
 };
 
 enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
@@ -40,7 +40,7 @@ static int parse_status_slot(const char *var, int offset)
        die("bad config variable '%s'", var);
 }
 
-static const charcolor(int slot)
+static const char *color(int slot)
 {
        return wt_status_use_color > 0 ? wt_status_colors[slot] : "";
 }
@@ -250,10 +250,9 @@ static void wt_status_print_untracked(struct wt_status *s)
 
        memset(&dir, 0, sizeof(dir));
 
-       if (!s->untracked) {
-               dir.show_other_directories = 1;
-               dir.hide_empty_directories = 1;
-       }
+       if (!s->untracked)
+               dir.flags |=
+                       DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
        setup_standard_excludes(&dir);
 
        read_directory(&dir, ".", "", 0, NULL);
index d782f06d9916bdde33dc3f7312f9eac4e14ef3a1..b9b0db8d86615d6ca1046b932772f3d9750a8062 100644 (file)
@@ -15,11 +15,10 @@ static int parse_num(char **cp_p, int *num_p)
 {
        char *cp = *cp_p;
        int num = 0;
-       int read_some;
 
        while ('0' <= *cp && *cp <= '9')
                num = num * 10 + *cp++ - '0';
-       if (!(read_some = cp - *cp_p))
+       if (!(cp - *cp_p))
                return -1;
        *cp_p = cp;
        *num_p = num;
index 3e97462bdd2ed72b4ec60a1eb3869e516609867b..1ebab687f77218d1520a4527214170eeab176d31 100644 (file)
@@ -293,15 +293,14 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
                for (; off1 < lim1; off1++)
                        rchg1[rindex1[off1]] = 1;
        } else {
-               long ec;
                xdpsplit_t spl;
                spl.i1 = spl.i2 = 0;
 
                /*
                 * Divide ...
                 */
-               if ((ec = xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,
-                                   need_min, &spl, xenv)) < 0) {
+               if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,
+                             need_min, &spl, xenv) < 0) {
 
                        return -1;
                }
@@ -457,7 +456,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
                        /*
                         * Record the end-of-group position in case we are matched
                         * with a group of changes in the other file (that is, the
-                        * change record before the enf-of-group index in the other
+                        * change record before the end-of-group index in the other
                         * file is set).
                         */
                        ixref = rchgo[ixo - 1] ? ix: nrec;
index 05bfa41f102801a5182e7fc642976f91e9ba7db6..c4bedf0d1ce1252563d7f36da7d846f5943343b0 100644 (file)
@@ -132,7 +132,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
        if (xecfg->flags & XDL_EMIT_COMMON)
                return xdl_emit_common(xe, xscr, ecb, xecfg);
 
-       for (xch = xche = xscr; xch; xch = xche->next) {
+       for (xch = xscr; xch; xch = xche->next) {
                xche = xdl_get_hunk(xch, xecfg);
 
                s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
index d9737f04c220645aa762d79ff14a84855721ffda..1cb65a95166a8cb60af590118f59d78aa4d24b74 100644 (file)
@@ -563,23 +563,22 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
                return -1;
        }
        status = 0;
-       if (xscr1 || xscr2) {
-               if (!xscr1) {
-                       result->ptr = xdl_malloc(mf2->size);
-                       memcpy(result->ptr, mf2->ptr, mf2->size);
-                       result->size = mf2->size;
-               } else if (!xscr2) {
-                       result->ptr = xdl_malloc(mf1->size);
-                       memcpy(result->ptr, mf1->ptr, mf1->size);
-                       result->size = mf1->size;
-               } else {
-                       status = xdl_do_merge(&xe1, xscr1, name1,
-                                             &xe2, xscr2, name2,
-                                             flags, xpp, result);
-               }
-               xdl_free_script(xscr1);
-               xdl_free_script(xscr2);
+       if (!xscr1) {
+               result->ptr = xdl_malloc(mf2->size);
+               memcpy(result->ptr, mf2->ptr, mf2->size);
+               result->size = mf2->size;
+       } else if (!xscr2) {
+               result->ptr = xdl_malloc(mf1->size);
+               memcpy(result->ptr, mf1->ptr, mf1->size);
+               result->size = mf1->size;
+       } else {
+               status = xdl_do_merge(&xe1, xscr1, name1,
+                                     &xe2, xscr2, name2,
+                                     flags, xpp, result);
        }
+       xdl_free_script(xscr1);
+       xdl_free_script(xscr2);
+
        xdl_free_env(&xe1);
        xdl_free_env(&xe2);