summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Documentation/CodingGuidelines3
-rw-r--r--Documentation/Makefile145
-rw-r--r--Documentation/RelNotes-1.6.3.txt181
-rw-r--r--Documentation/SubmittingPatches10
-rw-r--r--Documentation/asciidoc.conf15
-rw-r--r--Documentation/blame-options.txt8
-rw-r--r--Documentation/callouts.xsl30
-rw-r--r--Documentation/config.txt111
-rw-r--r--Documentation/docbook-xsl.css14
-rw-r--r--Documentation/git-add.txt3
-rw-r--r--Documentation/git-archive.txt4
-rw-r--r--Documentation/git-bisect.txt18
-rw-r--r--Documentation/git-branch.txt33
-rw-r--r--Documentation/git-check-ref-format.txt20
-rw-r--r--Documentation/git-checkout.txt90
-rw-r--r--Documentation/git-config.txt8
-rw-r--r--Documentation/git-cvsimport.txt36
-rw-r--r--Documentation/git-difftool.txt (renamed from contrib/difftool/git-difftool.txt)68
-rw-r--r--Documentation/git-filter-branch.txt17
-rw-r--r--Documentation/git-for-each-ref.txt7
-rw-r--r--Documentation/git-format-patch.txt27
-rw-r--r--Documentation/git-grep.txt8
-rw-r--r--Documentation/git-imap-send.txt7
-rw-r--r--Documentation/git-merge.txt10
-rw-r--r--Documentation/git-mergetool--lib.txt54
-rw-r--r--Documentation/git-mergetool.txt3
-rw-r--r--Documentation/git-patch-id.txt2
-rw-r--r--Documentation/git-push.txt26
-rw-r--r--Documentation/git-rebase.txt29
-rw-r--r--Documentation/git-remote.txt32
-rw-r--r--Documentation/git-rev-parse.txt13
-rw-r--r--Documentation/git-send-email.txt66
-rw-r--r--Documentation/git-shell.txt6
-rw-r--r--Documentation/git-svn.txt34
-rw-r--r--Documentation/git.txt11
-rw-r--r--Documentation/gitcli.txt24
-rw-r--r--Documentation/githooks.txt4
-rw-r--r--Documentation/glossary-content.txt8
-rw-r--r--Documentation/manpage-1.72.xsl27
-rw-r--r--Documentation/manpage-base.xsl35
-rw-r--r--Documentation/manpage-bold-literal.xsl17
-rw-r--r--Documentation/manpage-normal.xsl13
-rw-r--r--Documentation/manpage-suppress-sp.xsl21
-rw-r--r--Documentation/merge-config.txt3
-rw-r--r--Documentation/merge-strategies.txt14
-rw-r--r--Documentation/pretty-formats.txt10
-rw-r--r--Documentation/pretty-options.txt5
-rw-r--r--Documentation/rev-list-options.txt22
-rw-r--r--Documentation/technical/api-parse-options.txt28
-rw-r--r--Documentation/user-manual.txt200
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile97
l---------RelNotes2
-rw-r--r--archive.c5
-rw-r--r--attr.c73
-rw-r--r--attr.h6
-rw-r--r--bisect.c556
-rw-r--r--bisect.h29
-rw-r--r--branch.c70
-rw-r--r--branch.h7
-rw-r--r--builtin-add.c4
-rw-r--r--builtin-apply.c78
-rw-r--r--builtin-archive.c105
-rw-r--r--builtin-bisect--helper.c27
-rw-r--r--builtin-blame.c174
-rw-r--r--builtin-branch.c204
-rw-r--r--builtin-check-ref-format.c9
-rw-r--r--builtin-checkout.c26
-rw-r--r--builtin-clean.c4
-rw-r--r--builtin-clone.c101
-rw-r--r--builtin-config.c425
-rw-r--r--builtin-diff-tree.c1
-rw-r--r--builtin-fast-export.c16
-rw-r--r--builtin-fetch-pack.c13
-rw-r--r--builtin-fetch.c9
-rw-r--r--builtin-fmt-merge-msg.c6
-rw-r--r--builtin-for-each-ref.c169
-rw-r--r--builtin-gc.c3
-rw-r--r--builtin-grep.c70
-rw-r--r--builtin-init-db.c7
-rw-r--r--builtin-log.c239
-rw-r--r--builtin-ls-files.c264
-rw-r--r--builtin-ls-tree.c22
-rw-r--r--builtin-mailinfo.c1
-rw-r--r--builtin-merge.c7
-rw-r--r--builtin-pack-objects.c8
-rw-r--r--builtin-push.c76
-rw-r--r--builtin-read-tree.c2
-rw-r--r--builtin-receive-pack.c2
-rw-r--r--builtin-reflog.c76
-rw-r--r--builtin-remote.c659
-rw-r--r--builtin-rerere.c25
-rw-r--r--builtin-rev-list.c571
-rw-r--r--builtin-rev-parse.c23
-rw-r--r--builtin-rm.c3
-rw-r--r--builtin-send-pack.c229
-rw-r--r--builtin-shortlog.c1
-rw-r--r--builtin-show-branch.c3
-rw-r--r--builtin-show-ref.c2
-rw-r--r--builtin-update-index.c2
-rw-r--r--builtin-upload-archive.c2
-rw-r--r--builtin.h1
-rw-r--r--cache.h32
-rw-r--r--color.c8
-rw-r--r--color.h11
-rw-r--r--combine-diff.c7
-rw-r--r--command-list.txt1
-rw-r--r--compat/mingw.c144
-rw-r--r--compat/mingw.h13
-rw-r--r--compat/win32mmap.c53
-rw-r--r--compat/winansi.c18
-rw-r--r--config.c45
-rw-r--r--configure.ac386
-rw-r--r--connect.c11
-rwxr-xr-xcontrib/completion/git-completion.bash356
-rwxr-xr-xcontrib/difftool/git-difftool-helper240
-rwxr-xr-xcontrib/fast-import/git-p42
-rwxr-xr-xcontrib/fast-import/import-tars.perl16
-rw-r--r--daemon.c2
-rw-r--r--diff-lib.c2
-rw-r--r--diff-no-index.c4
-rw-r--r--diff.c54
-rw-r--r--diff.h3
-rw-r--r--diffcore-break.c3
-rw-r--r--dir.c19
-rw-r--r--dir.h12
-rw-r--r--entry.c108
-rw-r--r--environment.c1
-rw-r--r--fast-import.c15
-rw-r--r--fsck.c6
-rwxr-xr-xgit-add--interactive.perl78
-rwxr-xr-xgit-am.sh29
-rwxr-xr-xgit-bisect.sh102
-rw-r--r--git-compat-util.h26
-rwxr-xr-xgit-difftool--helper.sh59
-rwxr-xr-xgit-difftool.perl (renamed from contrib/difftool/git-difftool)34
-rwxr-xr-xgit-filter-branch.sh10
-rwxr-xr-xgit-instaweb.sh69
-rw-r--r--git-mergetool--lib.sh385
-rwxr-xr-xgit-mergetool.sh217
-rwxr-xr-xgit-pull.sh10
-rwxr-xr-xgit-rebase--interactive.sh26
-rwxr-xr-xgit-rebase.sh53
-rwxr-xr-xgit-send-email.perl141
-rwxr-xr-xgit-svn.perl116
-rw-r--r--git.c6
-rw-r--r--graph.c246
-rw-r--r--grep.c148
-rw-r--r--grep.h5
-rw-r--r--http-push.c300
-rw-r--r--http.c218
-rw-r--r--http.h7
-rw-r--r--imap-send.c55
-rw-r--r--index-pack.c2
-rw-r--r--list-objects.c9
-rw-r--r--list-objects.h6
-rw-r--r--log-tree.c52
-rw-r--r--log-tree.h6
-rw-r--r--mailmap.c9
-rw-r--r--merge-recursive.c11
-rw-r--r--parse-options.c28
-rw-r--r--parse-options.h2
-rw-r--r--patch-ids.c93
-rw-r--r--perl/Git.pm12
-rw-r--r--pretty.c69
-rw-r--r--quote.c35
-rw-r--r--quote.h9
-rw-r--r--read-cache.c85
-rw-r--r--reflog-walk.c12
-rw-r--r--reflog-walk.h5
-rw-r--r--refs.c175
-rw-r--r--refs.h4
-rw-r--r--remote.c258
-rw-r--r--remote.h13
-rw-r--r--rerere.c22
-rw-r--r--rerere.h2
-rw-r--r--revision.c6
-rw-r--r--revision.h4
-rw-r--r--send-pack.h6
-rw-r--r--sha1-lookup.c101
-rw-r--r--sha1-lookup.h7
-rw-r--r--sha1_name.c12
-rw-r--r--shell.c1
-rw-r--r--sideband.c19
-rw-r--r--sideband.h2
-rw-r--r--strbuf.c30
-rw-r--r--strbuf.h3
-rw-r--r--string-list.c10
-rw-r--r--string-list.h5
-rw-r--r--symlinks.c153
-rw-r--r--t/Makefile5
-rw-r--r--t/README18
-rw-r--r--t/lib-git-svn.sh14
-rw-r--r--t/lib-httpd.sh12
-rw-r--r--t/lib-httpd/apache.conf8
-rwxr-xr-xt/t0000-basic.sh66
-rwxr-xr-xt/t0004-unwritable.sh8
-rwxr-xr-xt/t0020-crlf.sh31
-rwxr-xr-xt/t0024-crlf-archive.sh8
-rwxr-xr-xt/t0050-filesystem.sh37
-rwxr-xr-xt/t0055-beyond-symlinks.sh6
-rwxr-xr-xt/t0060-path-utils.sh116
-rwxr-xr-xt/t1004-read-tree-m-u-wf.sh6
-rwxr-xr-xt/t1020-subdirectory.sh2
-rwxr-xr-xt/t1100-commit-tree-options.sh2
-rwxr-xr-xt/t1300-repo-config.sh11
-rwxr-xr-xt/t1301-shared-repo.sh24
-rwxr-xr-xt/t1400-update-ref.sh6
-rwxr-xr-xt/t1410-reflog.sh4
-rwxr-xr-xt/t1411-reflog-show.sh67
-rwxr-xr-xt/t1450-fsck.sh67
-rwxr-xr-xt/t1504-ceiling-dirs.sh2
-rwxr-xr-xt/t2001-checkout-cache-clash.sh6
-rwxr-xr-xt/t2003-checkout-cache-mkdir.sh8
-rwxr-xr-xt/t2004-checkout-cache-temp.sh2
-rwxr-xr-xt/t2007-checkout-symlink.sh6
-rwxr-xr-xt/t2100-update-cache-badpath.sh14
-rwxr-xr-xt/t2200-add-update.sh4
-rwxr-xr-xt/t2201-add-update-typechange.sh16
-rwxr-xr-xt/t2300-cd-to-toplevel.sh14
-rwxr-xr-xt/t3000-ls-files-others.sh25
-rwxr-xr-xt/t3010-ls-files-killed-modified.sh21
-rwxr-xr-xt/t3100-ls-tree-restrict.sh40
-rwxr-xr-xt/t3200-branch.sh2
-rwxr-xr-xt/t3203-branch-output.sh81
-rwxr-xr-xt/t3400-rebase.sh4
-rwxr-xr-xt/t3404-rebase-interactive.sh11
-rwxr-xr-xt/t3406-rebase-message.sh23
-rwxr-xr-xt/t3413-rebase-hook.sh (renamed from t/t3409-rebase-hook.sh)0
-rwxr-xr-xt/t3600-rm.sh57
-rwxr-xr-xt/t3700-add.sh16
-rwxr-xr-xt/t3701-add-interactive.sh14
-rwxr-xr-xt/t4002-diff-basic.sh8
-rwxr-xr-xt/t4004-diff-rename-symlink.sh6
-rwxr-xr-xt/t4006-diff-mode.sh19
-rwxr-xr-xt/t4008-diff-break-rewrite.sh8
-rwxr-xr-xt/t4011-diff-symlink.sh6
-rwxr-xr-xt/t4012-diff-binary.sh2
-rwxr-xr-xt/t4013-diff-various.sh11
-rw-r--r--t/t4013/diff.diff_--dirstat_master~1_master~23
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side61
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..master12
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..master^8
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..side4
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master170
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master12
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..master12
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..master^8
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..master^^4
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..side4
-rw-r--r--t/t4013/diff.log_--decorate_--all34
-rw-r--r--t/t4013/diff.rev-list_--children_HEAD7
-rw-r--r--t/t4013/diff.rev-list_--parents_HEAD7
-rwxr-xr-xt/t4014-format-patch.sh282
-rwxr-xr-xt/t4023-diff-rename-typechange.sh6
-rwxr-xr-xt/t4035-diff-quiet.sh (renamed from t/t4017-quiet.sh)0
-rwxr-xr-xt/t4036-format-patch-signer-mime.sh (renamed from t/t4021-format-patch-signer-mime.sh)0
-rwxr-xr-xt/t4102-apply-rename.sh8
-rwxr-xr-xt/t4114-apply-typechange.sh6
-rwxr-xr-xt/t4115-apply-symlink.sh6
-rwxr-xr-xt/t4122-apply-symlink-inside.sh6
-rwxr-xr-xt/t4129-apply-samemode.sh19
-rwxr-xr-xt/t4130-apply-criss-cross-rename.sh38
-rwxr-xr-xt/t4150-am.sh15
-rwxr-xr-xt/t4202-log.sh188
-rwxr-xr-xt/t4204-patch-id.sh38
-rwxr-xr-xt/t5000-tar-tree.sh54
-rwxr-xr-xt/t5300-pack-object.sh17
-rwxr-xr-xt/t5302-pack-index.sh17
-rwxr-xr-xt/t5303-pack-corruption-resilience.sh12
-rwxr-xr-xt/t5403-post-checkout-hook.sh14
-rwxr-xr-xt/t5503-tagfollow.sh6
-rwxr-xr-xt/t5505-remote.sh114
-rwxr-xr-xt/t5506-remote-groups.sh81
-rwxr-xr-xt/t5511-refspec.sh12
-rwxr-xr-xt/t5515-fetch-merge-logic.sh3
-rwxr-xr-xt/t5522-pull-symlink.sh (renamed from t/t5521-pull-symlink.sh)6
-rwxr-xr-xt/t5540-http-push.sh10
-rwxr-xr-xt/t5550-http-fetch.sh57
-rwxr-xr-xt/t5601-clone.sh15
-rwxr-xr-xt/t5602-clone-remote-exec.sh4
-rwxr-xr-xt/t5705-clone-2gb.sh45
-rwxr-xr-xt/t6030-bisect-porcelain.sh60
-rwxr-xr-xt/t6031-merge-recursive.sh20
-rwxr-xr-xt/t6034-merge-rename-nocruft.sh (renamed from t/t6023-merge-rename-nocruft.sh)0
-rwxr-xr-xt/t6040-tracking-info.sh10
-rwxr-xr-xt/t6200-fmt-merge-msg.sh4
-rwxr-xr-xt/t6300-for-each-ref.sh40
-rwxr-xr-xt/t7001-mv.sh4
-rwxr-xr-xt/t7004-tag.sh104
-rwxr-xr-xt/t7005-editor.sh29
-rwxr-xr-xt/t7300-clean.sh6
-rwxr-xr-xt/t7501-commit.sh4
-rwxr-xr-xt/t7502-commit.sh2
-rwxr-xr-xt/t7503-pre-commit-hook.sh4
-rwxr-xr-xt/t7504-commit-msg-hook.sh8
-rwxr-xr-xt/t7508-status.sh (renamed from t/t7502-status.sh)0
-rwxr-xr-xt/t7800-difftool.sh211
-rwxr-xr-xt/t9001-send-email.sh189
-rwxr-xr-xt/t9100-git-svn-basic.sh37
-rwxr-xr-xt/t9109-git-svn-multi-glob.sh (renamed from t/t9108-git-svn-multi-glob.sh)0
-rwxr-xr-xt/t9129-git-svn-i18n-commitencoding.sh22
-rwxr-xr-xt/t9131-git-svn-empty-symlink.sh2
-rwxr-xr-xt/t9132-git-svn-broken-symlink.sh4
-rwxr-xr-xt/t9134-git-svn-ignore-paths.sh55
-rwxr-xr-xt/t9137-git-svn-dcommit-clobber-series.sh (renamed from t/t9106-git-svn-dcommit-clobber-series.sh)0
-rwxr-xr-xt/t9200-git-cvsexportcommit.sh21
-rwxr-xr-xt/t9301-fast-export.sh22
-rwxr-xr-xt/t9400-git-cvsserver-server.sh14
-rwxr-xr-xt/t9401-git-cvsserver-crlf.sh13
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh37
-rwxr-xr-xt/t9600-cvsimport.sh8
-rwxr-xr-xt/t9700-perl-git.sh7
-rw-r--r--t/test-lib.sh207
-rw-r--r--t/valgrind/.gitignore2
-rwxr-xr-xt/valgrind/analyze.sh123
-rw-r--r--t/valgrind/default.supp45
-rwxr-xr-xt/valgrind/valgrind.sh22
-rwxr-xr-xtemplates/hooks--update.sample17
-rw-r--r--templates/this--description2
-rw-r--r--transport.c283
-rw-r--r--transport.h3
-rw-r--r--unimplemented.sh4
-rw-r--r--unpack-trees.c49
-rw-r--r--upload-pack.c13
-rw-r--r--wt-status.c17
-rw-r--r--xdiff-interface.c3
-rw-r--r--xdiff/xdiffi.c5
-rw-r--r--xdiff/xemit.c2
330 files changed, 10834 insertions, 4559 deletions
diff --git a/.gitignore b/.gitignore
index 1c57d4c958..41c0b20a76 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 0d7fa9cca9..b8bf618a30 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -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.
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 144ec32f12..e18242a6d4 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -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) -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) -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) -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.txt b/Documentation/RelNotes-1.6.3.txt
new file mode 100644
index 0000000000..839498c38a
--- /dev/null
+++ b/Documentation/RelNotes-1.6.3.txt
@@ -0,0 +1,181 @@
+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.
+
+(performance)
+
+* many uses of lstat(2) in the codepath for "git checkout" have been
+ optimized out.
+
+* pruning reflog entries that are unreachable from the tip of the ref
+ during "git reflog prune" (hence "git gc") was very inefficient.
+
+(usability, bells and whistles)
+
+* 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".
+
+* 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.
+
+* The number of commits shown in "you are ahead/behind your upstream"
+ messages given by "git checkout" and "git status" used to count merge
+ commits; now it doesn't.
+
+* @{-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-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-fast-export choked when seeing a tag that does not point at commit.
+
+* 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.
+
+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.
+
+* The initial checkout did not read the attributes from the .gitattribute
+ file that is being checked out.
+
+* "git-checkout <tree-ish> <submodule>" did not update the index entry at
+ the named path; it now does.
+
+* git-gc spent excessive amount of time to decide if an object appears
+ in a locally existing pack (if needed, backport by merging 69e020a).
+
+* "git-ls-tree" and "git-diff-tree" used a pathspec correctly when
+ deciding to descend into a subdirectory but they did not match the
+ individual paths correctly. This caused pathspecs "abc/d ab" to match
+ "abc/0" ("abc/d" made them decide to descend into the directory "abc/",
+ and then "ab" incorrectly matched "abc/0" when it shouldn't).
+
+* "git-merge-recursive" was broken when a submodule entry was involved in
+ a criss-cross merge situation.
+
+---
+exec >/var/tmp/1
+O=v1.6.2.3-497-g54a4749
+echo O=$(git describe master)
+git shortlog --no-merges $O..master ^maint
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 9b559adefc..8d818a2160 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -491,6 +491,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 +509,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 +522,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!
+
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 1e735df3bb..dc76e7f073 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -27,7 +27,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 +42,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 +59,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[]
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 26cfb62195..1625ffce6a 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -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
index 6a361a2136..0000000000
--- a/Documentation/callouts.xsl
+++ /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>
diff --git a/Documentation/config.txt b/Documentation/config.txt
index f5152c5038..5319df5058 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -25,7 +25,7 @@ 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
+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.
@@ -39,7 +39,7 @@ in the section header, like in example below:
--------
Subsection names can contain any characters except newline (doublequote
-'`"`' and backslash have to be escaped as '`\"`' and '`\\`',
+`"` and backslash have to be escaped as `\"` and `\\`,
respectively) and are case sensitive. Section header 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
@@ -53,7 +53,7 @@ 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.
@@ -69,15 +69,15 @@ 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 '`\`'.
+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
+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 value ending in a `\` is continued on the next line in the
customary UNIX fashion.
Some variables may require special value format.
@@ -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
@@ -382,9 +387,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
@@ -468,10 +473,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
@@ -548,6 +557,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").
@@ -639,6 +667,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
@@ -677,6 +726,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
@@ -1151,7 +1217,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
@@ -1160,6 +1226,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
diff --git a/Documentation/docbook-xsl.css b/Documentation/docbook-xsl.css
index b878b385c6..e11c8f053a 100644
--- a/Documentation/docbook-xsl.css
+++ b/Documentation/docbook-xsl.css
@@ -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 {
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index ce71838b9e..d938b42289 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -245,8 +245,11 @@ patch::
y - stage this hunk
n - do not stage this hunk
+ q - quit, do not stage this hunk nor any of the remaining ones
a - stage this and all the remaining hunks in the file
d - do not stage this hunk nor any of the remaining hunks in the file
+ g - select a hunk to go to
+ / - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
index ad38f7f39d..c1adf59497 100644
--- a/Documentation/git-archive.txt
+++ b/Documentation/git-archive.txt
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
+ [--output=<file>]
[--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
[path...]
@@ -47,6 +48,9 @@ OPTIONS
--prefix=<prefix>/::
Prepend <prefix>/ to each filename in the archive.
+--output=<file>::
+ Write the archive to <file> instead of stdout.
+
<extra>::
This can be any options that the archiver backend understands.
See next section.
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index e5862b9dbb..ffc02c737c 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -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>
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 7f7b781f24..cbd4275871 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -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.
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index 171b68377d..c1ce26884e 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -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
---
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 132fc4faa5..ad4b31e892 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -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:
+
------------
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 6ab2af4b61..7131ee3c66 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -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
-----------
@@ -161,6 +162,11 @@ See also <<FILES>>.
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
-----
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt
index 8f9ba74c8b..d7bab13f6c 100644
--- a/Documentation/git-cvsimport.txt
+++ b/Documentation/git-cvsimport.txt
@@ -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 practise:
+
+* cvs2git (part of cvs2svn), `http://cvs2svn.tigris.org`
+* parsecvs, `http://cgit.freedesktop.org/~keithp/parsecvs`
Author
------
diff --git a/contrib/difftool/git-difftool.txt b/Documentation/git-difftool.txt
index 6e2610cda6..15b247bab4 100644
--- a/contrib/difftool/git-difftool.txt
+++ b/Documentation/git-difftool.txt
@@ -3,52 +3,54 @@ git-difftool(1)
NAME
----
-git-difftool - compare changes using common merge tools
+git-difftool - Show changes using common diff tools
SYNOPSIS
--------
-'git difftool' [--tool=<tool>] [--no-prompt] ['git diff' options]
+'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 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.
+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 merge resolution program specified by <tool>.
+ Use the diff tool specified by <tool>.
Valid merge tools are:
- kdiff3, kompare, tkdiff, meld, xxdiff, emerge,
- vimdiff, gvimdiff, ecmerge, and opendiff
+ kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff,
+ ecmerge, diffuse 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'
+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 `mergetool.<tool>.path`. For example, you
+configuration variable `difftool.<tool>.path`. For example, you
can configure the absolute path to kdiff3 by setting
-`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the
+`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the
tool is available in PATH.
+
-Instead of running one of the known merge tool programs,
+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 `mergetool.<tool>.cmd`.
+variable `difftool.<tool>.cmd`.
+
When 'git-difftool' is invoked with this tool (either through the
-`-t` or `--tool` option or the `merge.tool` configuration variable)
+`-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`
@@ -56,29 +58,27 @@ 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.
+See linkgit:git-diff[1] for the full list of supported options.
CONFIG VARIABLES
----------------
-merge.tool::
- The default merge tool to use.
-+
-See the `--tool=<tool>` option above for more details.
+'git-difftool' falls back to 'git-mergetool' config variables when the
+difftool equivalents have not been defined.
-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).
+diff.tool::
+ The default diff tool to use.
-mergetool.<tool>.path::
+difftool.<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.
+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
--------
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index c1193953a1..ab527b5b31 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -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:
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 5061d3e4e7..8dc873fd44 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -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.
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3c29655d75..eb2fbcff1a 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -10,7 +10,8 @@ SYNOPSIS
--------
[verse]
'git format-patch' [-k] [-o <dir> | --stdout] [--thread]
- [--attach[=<boundary>] | --inline[=<boundary>]]
+ [--attach[=<boundary>] | --inline[=<boundary>] |
+ [--no-attach]]
[-s | --signoff] [<common diff options>]
[-n | --numbered | -N | --no-numbered]
[--start-number <n>] [--numbered-files]
@@ -112,15 +113,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 +157,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
@@ -176,7 +194,8 @@ 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.
+and file suffix, control attachements, and number patches when outputting
+more than one.
------------
[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
------------
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index 553da6cbb1..fccb82deb4 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -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
diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index eed50572e0..d016dafd49 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -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
~~~~~~~~
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index f7be5846a6..c04ae739ed 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -40,8 +40,8 @@ include::merge-options.txt[]
include::merge-strategies.txt[]
-If you tried a merge which resulted in a 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
index 0000000000..78eb03f0ae
--- /dev/null
+++ b/Documentation/git-mergetool--lib.txt
@@ -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
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index 5d3c632872..ff9700d17a 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -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
diff --git a/Documentation/git-patch-id.txt b/Documentation/git-patch-id.txt
index 477785e134..253fc0fc25 100644
--- a/Documentation/git-patch-id.txt
+++ b/Documentation/git-patch-id.txt
@@ -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.
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 4e7e5a719a..fd53c49fb8 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -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
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index da3c38cd60..3d5a066c31 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -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
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index fad983e297..9e2b4eaa38 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -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>.
@@ -101,6 +125,8 @@ the configuration parameter remotes.default will get 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
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 3ccef2f2b3..fba30b12ed 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -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.
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index fc0a4ab441..0b1f183ce8 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -39,13 +39,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 +60,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 +97,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 +105,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 +122,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 +132,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 +140,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 +149,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 +163,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 +174,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 +211,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.
@@ -255,6 +270,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
------
diff --git a/Documentation/git-shell.txt b/Documentation/git-shell.txt
index 3f8d973af1..0f3ad811cf 100644
--- a/Documentation/git-shell.txt
+++ b/Documentation/git-shell.txt
@@ -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
------
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index cda3389331..9229d45ad9 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -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 a 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,9 +688,9 @@ 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
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 9a26bde73e..470fdc5ecd 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -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,12 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.6.2/git.html[documentation for release 1.6.2]
+* link:v1.6.2.3/git.html[documentation for release 1.6.2.3]
* release notes for
+ 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 +180,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).
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 29e5929db2..be39ed7c15 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -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
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 1fd512bca2..1c736738cc 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -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.
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 9afca755ed..572374f7a6 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -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
diff --git a/Documentation/manpage-1.72.xsl b/Documentation/manpage-1.72.xsl
index 4065a3a27a..b4d315cb8c 100644
--- a/Documentation/manpage-1.72.xsl
+++ b/Documentation/manpage-1.72.xsl
@@ -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
index 0000000000..a264fa6160
--- /dev/null
+++ b/Documentation/manpage-base.xsl
@@ -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
index 0000000000..608eb5df62
--- /dev/null
+++ b/Documentation/manpage-bold-literal.xsl
@@ -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
index 0000000000..a48f5b11f3
--- /dev/null
+++ b/Documentation/manpage-normal.xsl
@@ -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
index 0000000000..a63c7632a8
--- /dev/null
+++ b/Documentation/manpage-suppress-sp.xsl
@@ -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>
diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt
index 1ff08ff2cc..4832bc75e2 100644
--- a/Documentation/merge-config.txt
+++ b/Documentation/merge-config.txt
@@ -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.
diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt
index 1276f858ad..4365b7e842 100644
--- a/Documentation/merge-strategies.txt
+++ b/Documentation/merge-strategies.txt
@@ -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 a 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 a 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
+ a 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
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 159390c35a..2a845b1e57 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -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
+---------------------
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 5f21efe407..bff94991b6 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -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
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 7dd237c2f6..11eec941df 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -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).
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 539863b1f9..e66ca9f70c 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -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
--------------
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 96af8977f6..dbbeb7e7c7 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -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
@@ -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`.
+`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
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 04713fc7c9..97fc1e0519 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.2.3
+DEF_VER=v1.6.2.GIT
LF='
'
diff --git a/Makefile b/Makefile
index 0675c43e73..57f9f26254 100644
--- 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
@@ -257,14 +265,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 +300,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
@@ -335,7 +358,10 @@ 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
+ifndef NO_PERL
+OTHER_PROGRAMS += gitweb/gitweb.cgi
+endif
# Set paths to tools early so that they can be used for version tests.
ifndef SHELL_PATH
@@ -414,6 +440,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
@@ -514,6 +541,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
@@ -657,6 +685,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
@@ -706,6 +735,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
@@ -734,6 +764,7 @@ ifeq ($(uname_S),AIX)
NO_MEMMEM = YesPlease
NO_MKDTEMP = YesPlease
NO_STRLCPY = YesPlease
+ NO_NSEC = YesPlease
FREAD_READS_DIRECTORIES = UnfortunatelyYes
INTERNAL_QSORT = UnfortunatelyYes
NEEDS_LIBICONV=YesPlease
@@ -776,7 +807,6 @@ ifneq (,$(findstring CYGWIN,$(uname_S)))
COMPAT_OBJS += compat/cygwin.o
endif
ifneq (,$(findstring MINGW,$(uname_S)))
- NO_MMAP = YesPlease
NO_PREAD = YesPlease
NO_OPENSSL = YesPlease
NO_CURL = YesPlease
@@ -799,6 +829,8 @@ 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
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
@@ -920,6 +952,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
@@ -967,6 +1008,11 @@ 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 NO_PREAD
COMPAT_CFLAGS += -DNO_PREAD
@@ -1068,6 +1114,10 @@ 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 =
@@ -1142,7 +1192,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:
@@ -1155,6 +1207,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)
@@ -1189,6 +1242,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
@@ -1248,6 +1302,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) $@ $<+ && \
@@ -1363,6 +1426,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
@@ -1565,9 +1630,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
@@ -1640,3 +1707,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
diff --git a/RelNotes b/RelNotes
index 22ea275eed..dd8bc4bb4a 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.2.4.txt \ No newline at end of file
+Documentation/RelNotes-1.6.3.txt \ No newline at end of file
diff --git a/archive.c b/archive.c
index e6de0397cc..96b62d4309 100644
--- a/archive.c
+++ b/archive.c
@@ -253,6 +253,7 @@ 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;
@@ -262,6 +263,8 @@ static int parse_archive_args(int argc, const char **argv,
OPT_STRING(0, "format", &format, "fmt", "archive format"),
OPT_STRING(0, "prefix", &base, "prefix",
"prepend prefix to each pathname in the archive"),
+ OPT_STRING(0, "output", &output, "file",
+ "write the archive to this file"),
OPT__VERBOSE(&verbose),
OPT__COMPR('0', &compression_level, "store only", 0),
OPT__COMPR('1', &compression_level, "compress faster", 1),
@@ -290,6 +293,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 = "";
diff --git a/attr.c b/attr.c
index 17f6a4dca5..43259e5b01 100644
--- a/attr.c
+++ b/attr.c
@@ -1,3 +1,4 @@
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
#include "attr.h"
@@ -318,6 +319,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 +344,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 +355,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 +371,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 +396,30 @@ 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 {
+ 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);
+ }
+ 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 +447,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) {
@@ -642,3 +670,12 @@ 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;
+ direction = new;
+ if (new != old)
+ drop_attr_stack();
+ use_index = istate;
+}
diff --git a/attr.h b/attr.h
index f1c2038b09..3a2f4ec1a0 100644
--- a/attr.h
+++ b/attr.h
@@ -31,4 +31,10 @@ 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
+};
+void git_attr_set_direction(enum git_attr_direction, struct index_state *);
+
#endif /* ATTR_H */
diff --git a/bisect.c b/bisect.c
new file mode 100644
index 0000000000..58f7e6f773
--- /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
index 0000000000..fdba913877
--- /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
diff --git a/branch.c b/branch.c
index 1f00e44deb..62030af4b5 100644
--- 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)) {
diff --git a/branch.h b/branch.h
index 9f0c2a2c1f..eed817a64c 100644
--- 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
diff --git a/builtin-add.c b/builtin-add.c
index 08443f2f1e..cb67d2c17e 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -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);
}
diff --git a/builtin-apply.c b/builtin-apply.c
index a664338643..7b404ef660 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -2271,6 +2271,25 @@ static struct patch *in_fn_table(const char *name)
return NULL;
}
+/*
+ * item->util in the filename table records the status of the path.
+ * Usually it points at a patch (whose result records the contents
+ * of it after applying it), but it could be PATH_WAS_DELETED for a
+ * path that a previously applied patch has already removed.
+ */
+ #define PATH_TO_BE_DELETED ((struct patch *) -2)
+#define PATH_WAS_DELETED ((struct patch *) -1)
+
+static int to_be_deleted(struct patch *patch)
+{
+ return patch == PATH_TO_BE_DELETED;
+}
+
+static int was_deleted(struct patch *patch)
+{
+ return patch == PATH_WAS_DELETED;
+}
+
static void add_to_fn_table(struct patch *patch)
{
struct string_list_item *item;
@@ -2291,7 +2310,22 @@ static void add_to_fn_table(struct patch *patch)
*/
if ((patch->new_name == NULL) || (patch->is_rename)) {
item = string_list_insert(patch->old_name, &fn_table);
- item->util = (struct patch *) -1;
+ item->util = PATH_WAS_DELETED;
+ }
+}
+
+static void prepare_fn_table(struct patch *patch)
+{
+ /*
+ * store information about incoming file deletion
+ */
+ while (patch) {
+ if ((patch->new_name == NULL) || (patch->is_rename)) {
+ struct string_list_item *item;
+ item = string_list_insert(patch->old_name, &fn_table);
+ item->util = PATH_TO_BE_DELETED;
+ }
+ patch = patch->next;
}
}
@@ -2304,8 +2338,8 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
struct patch *tpatch;
if (!(patch->is_copy || patch->is_rename) &&
- ((tpatch = in_fn_table(patch->old_name)) != NULL)) {
- if (tpatch == (struct patch *) -1) {
+ (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) {
+ if (was_deleted(tpatch)) {
return error("patch %s has been renamed/deleted",
patch->old_name);
}
@@ -2360,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);
@@ -2399,10 +2433,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
assert(patch->is_new <= 0);
if (!(patch->is_copy || patch->is_rename) &&
- (tpatch = in_fn_table(old_name)) != NULL) {
- if (tpatch == (struct patch *) -1) {
+ (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) {
+ if (was_deleted(tpatch))
return error("%s: has been deleted/renamed", old_name);
- }
st_mode = tpatch->new_mode;
} else if (!cached) {
stat_ret = lstat(old_name, st);
@@ -2410,6 +2443,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
return error("%s: %s", old_name, strerror(errno));
}
+ if (to_be_deleted(tpatch))
+ tpatch = NULL;
+
if (check_index && !tpatch) {
int pos = cache_name_pos(old_name, strlen(old_name));
if (pos < 0) {
@@ -2451,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;
@@ -2471,6 +2507,7 @@ static int check_patch(struct patch *patch)
const char *new_name = patch->new_name;
const char *name = old_name ? old_name : new_name;
struct cache_entry *ce = NULL;
+ struct patch *tpatch;
int ok_if_exists;
int status;
@@ -2481,7 +2518,8 @@ static int check_patch(struct patch *patch)
return status;
old_name = patch->old_name;
- if (in_fn_table(new_name) == (struct patch *) -1)
+ if ((tpatch = in_fn_table(new_name)) &&
+ (was_deleted(tpatch) || to_be_deleted(tpatch)))
/*
* A type-change diff is always split into a patch to
* delete old, immediately followed by a patch to
@@ -2533,6 +2571,7 @@ static int check_patch_list(struct patch *patch)
{
int err = 0;
+ prepare_fn_table(patch);
while (patch) {
if (apply_verbosely)
say_patch_name(stderr,
@@ -2932,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);
@@ -3224,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,
@@ -3315,8 +3353,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");
}
@@ -3326,12 +3364,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" : "");
diff --git a/builtin-archive.c b/builtin-archive.c
index 5ceec433fd..ab50cebba0 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -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
index 0000000000..8fe778766a
--- /dev/null
+++ b/builtin-bisect--helper.c
@@ -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);
+}
diff --git a/builtin-blame.c b/builtin-blame.c
index 1ead9b4830..83141fc84e 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -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);
}
@@ -1198,6 +1205,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;
}
@@ -1415,6 +1426,39 @@ static void write_filename_info(const char *path)
}
/*
+ * 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 +1473,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 +1536,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 +1576,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);
@@ -1806,36 +1815,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);
@@ -1975,6 +1954,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 +2224,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 +2254,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 |
diff --git a/builtin-branch.c b/builtin-branch.c
index 07166b8856..91098ca9b1 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -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,7 +184,8 @@ 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;
};
@@ -200,22 +197,47 @@ struct ref_list {
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
+ 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;
commit = lookup_commit_reference_gently(sha1, 1);
@@ -246,9 +268,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 +284,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 +301,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 +339,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,30 +390,27 @@ 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;
}
@@ -397,11 +446,13 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
is_descendant_of(head_commit, with_commit)) {
struct ref_item item;
item.name = xstrdup("(no branch)");
+ item.len = strlen(item.name);
item.kind = REF_LOCAL_BRANCH;
+ item.dest = NULL;
item.commit = head_commit;
- if (strlen(item.name) > ref_list.maxwidth)
- ref_list.maxwidth = strlen(item.name);
- print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
+ if (item.len > ref_list.maxwidth)
+ ref_list.maxwidth = item.len;
+ print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1, "");
free(item.name);
}
@@ -409,8 +460,11 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
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 +475,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 +504,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);
diff --git a/builtin-check-ref-format.c b/builtin-check-ref-format.c
index 701de439ae..f9381e07ea 100644
--- a/builtin-check-ref-format.c
+++ b/builtin-check-ref-format.c
@@ -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]);
diff --git a/builtin-checkout.c b/builtin-checkout.c
index b121fe56de..ee1edd406f 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -292,6 +292,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);
}
@@ -348,16 +350,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);
}
@@ -402,7 +399,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);
@@ -553,8 +550,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
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;
}
@@ -733,12 +730,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);
}
diff --git a/builtin-clean.c b/builtin-clean.c
index f78c2fb108..c5ad33d3e6 100644
--- a/builtin-clean.c
+++ b/builtin-clean.c
@@ -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);
diff --git a/builtin-clone.c b/builtin-clone.c
index c338910b1c..880373f279 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -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:
@@ -267,7 +270,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 +296,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 +316,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 +328,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 +341,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 +349,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 +406,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 +436,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 +452,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 +492,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 +503,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 +533,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) {
@@ -631,6 +581,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);
@@ -638,5 +591,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;
}
diff --git a/builtin-config.c b/builtin-config.c
index f71016204b..d8da72cf20 100644
--- a/builtin-config.c
+++ b/builtin-config.c
@@ -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,188 @@ 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;
- char* value;
- 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);
+ 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;
}
diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c
index 8ecefd4f0f..79cedb72c4 100644
--- a/builtin-diff-tree.c
+++ b/builtin-diff-tree.c
@@ -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);
diff --git a/builtin-fast-export.c b/builtin-fast-export.c
index fdf4ae9ebd..6731713223 100644
--- a/builtin-fast-export.c
+++ b/builtin-fast-export.c
@@ -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);
@@ -362,7 +363,10 @@ static void get_tags_and_duplicates(struct object_array *pending,
break;
case OBJ_TAG:
tag = (struct tag *)e->item;
+
+ /* handle nested tags */
while (tag && tag->object.type == OBJ_TAG) {
+ parse_object(tag->object.sha1);
string_list_append(full_name, extra_refs)->util = tag;
tag = (struct tag *)tag->tagged;
}
@@ -375,11 +379,17 @@ static void get_tags_and_duplicates(struct object_array *pending,
case OBJ_BLOB:
handle_object(tag->object.sha1);
continue;
+ default: /* OBJ_TAG (nested tags) is already handled */
+ warning("Tag points to object of unexpected type %s, skipping.",
+ typename(tag->object.type));
+ continue;
}
break;
default:
- die ("Unexpected object of type %s",
- typename(e->item->type));
+ warning("%s: Unexpected object of type %s, skipping.",
+ e->name,
+ typename(e->item->type));
+ continue;
}
if (commit->util)
/* more than one name for the same object */
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index 67fb80ec48..5d134be47c 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -216,9 +216,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);
@@ -483,7 +482,7 @@ static int sideband_demux(int fd, void *data)
{
int *xd = data;
- return recv_sideband("fetch-pack", xd[0], fd, 2);
+ return recv_sideband("fetch-pack", xd[0], fd);
}
static int get_pack(int xd[2], char **pack_lockfile)
@@ -606,7 +605,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.");
@@ -801,15 +800,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");
diff --git a/builtin-fetch.c b/builtin-fetch.c
index 7fb35fca9d..3c998ea740 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -197,11 +197,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);
@@ -544,7 +540,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,
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
index df18f4070f..a7883690d7 100644
--- a/builtin-fmt-merge-msg.c
+++ b/builtin-fmt-merge-msg.c
@@ -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;
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
index e46b7adc97..91e8f95fd2 100644
--- a/builtin-for-each-ref.c
+++ b/builtin-for-each-ref.c
@@ -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" },
};
/*
@@ -544,109 +546,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.
*/
static void populate_value(struct refinfo *ref)
@@ -672,32 +571,50 @@ static void populate_value(struct refinfo *ref)
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;
}
}
@@ -943,7 +860,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,6 +918,9 @@ 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);
diff --git a/builtin-gc.c b/builtin-gc.c
index 8d990ed493..fc556ed7f3 100644
--- a/builtin-gc.c
+++ b/builtin-gc.c
@@ -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);
diff --git a/builtin-grep.c b/builtin-grep.c
index 3f12ba3826..89489ddcf8 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -22,6 +22,28 @@
static int builtin_grep;
+static int grep_config(const char *var, const char *value, void *cb)
+{
+ struct grep_opt *opt = cb;
+
+ if (!strcmp(var, "grep.color") || !strcmp(var, "color.grep")) {
+ opt->color = git_config_colorbool(var, value, -1);
+ return 0;
+ }
+ if (!strcmp(var, "grep.color.external") ||
+ !strcmp(var, "color.grep.external")) {
+ return git_config_string(&(opt->color_external), var, value);
+ }
+ if (!strcmp(var, "grep.color.match") ||
+ !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 +291,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 +376,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 +590,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 +792,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 +825,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)
diff --git a/builtin-init-db.c b/builtin-init-db.c
index bfdfc7b411..d1fa12a59e 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -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);
diff --git a/builtin-log.c b/builtin-log.c
index 0f0adf2bab..5eaec5d24e 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -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,7 +752,6 @@ 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;
@@ -787,6 +772,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
@@ -833,13 +823,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 +833,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 +847,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 +870,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');
@@ -1009,8 +1014,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 +1040,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);
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index ca6f33d046..88e2697aeb 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -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,157 +378,139 @@ 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 */
read_cache();
@@ -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);
diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c
index fca46312f6..22008dfa8f 100644
--- a/builtin-ls-tree.c
+++ b/builtin-ls-tree.c
@@ -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)
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 2789ccdf7d..1eeeb4de6d 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -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"
diff --git a/builtin-merge.c b/builtin-merge.c
index 6d2160d0a3..6a51823a55 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -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;
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 6cf5b86e15..d3360ac1f8 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -1612,7 +1612,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,13 +1901,13 @@ 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;
}
-static void show_object(struct object_array_entry *p)
+static void show_object(struct object_array_entry *p, void *data)
{
add_preferred_base_object(p->name);
add_object_entry(p->item->sha1, p->item->type, p->name, 0);
@@ -2073,7 +2073,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);
diff --git a/builtin-push.c b/builtin-push.c
index ca36fb1e58..2eabcd3bdf 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -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++) {
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 38fef34d3f..8e0273864d 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -170,7 +170,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
diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
index 849f1fe6f9..a970b39505 100644
--- a/builtin-receive-pack.c
+++ b/builtin-receive-pack.c
@@ -675,7 +675,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");
diff --git a/builtin-reflog.c b/builtin-reflog.c
index d95f515f2e..249ad2a311 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -52,6 +52,7 @@ struct collect_reflog_cb {
#define INCOMPLETE (1u<<10)
#define STUDYING (1u<<11)
+#define REACHABLE (1u<<12)
static int tree_is_complete(const unsigned char *sha1)
{
@@ -209,6 +210,70 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
return 1;
}
+static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
+{
+ /*
+ * We may or may not have the commit yet - if not, look it
+ * up using the supplied sha1.
+ */
+ if (!commit) {
+ if (is_null_sha1(sha1))
+ return 0;
+
+ commit = lookup_commit_reference_gently(sha1, 1);
+
+ /* Not a commit -- keep it */
+ if (!commit)
+ return 0;
+ }
+
+ /* Reachable from the current ref? Don't prune. */
+ if (commit->object.flags & REACHABLE)
+ return 0;
+ if (in_merge_bases(commit, &cb->ref_commit, 1))
+ return 0;
+
+ /* We can't reach it - prune it. */
+ return 1;
+}
+
+static void mark_reachable(struct commit *commit, unsigned long expire_limit)
+{
+ /*
+ * We need to compute if commit on either side of an 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
+ * reachable from the tip without running in_merge_bases()
+ * at all.
+ */
+ struct commit_list *pending = NULL;
+
+ commit_list_insert(commit, &pending);
+ while (pending) {
+ struct commit_list *entry = pending;
+ struct commit_list *parent;
+ pending = entry->next;
+ commit = entry->item;
+ free(entry);
+ if (commit->object.flags & REACHABLE)
+ continue;
+ if (parse_commit(commit))
+ continue;
+ commit->object.flags |= REACHABLE;
+ if (commit->date < expire_limit)
+ continue;
+ parent = commit->parents;
+ while (parent) {
+ commit = parent->item;
+ parent = parent->next;
+ if (commit->object.flags & REACHABLE)
+ continue;
+ commit_list_insert(commit, &pending);
+ }
+ }
+}
+
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
const char *email, unsigned long timestamp, int tz,
const char *message, void *cb_data)
@@ -230,12 +295,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
if (timestamp < cb->cmd->expire_unreachable) {
if (!cb->ref_commit)
goto prune;
- if (!old && !is_null_sha1(osha1))
- old = lookup_commit_reference_gently(osha1, 1);
- if (!new && !is_null_sha1(nsha1))
- new = lookup_commit_reference_gently(nsha1, 1);
- if ((old && !in_merge_bases(old, &cb->ref_commit, 1)) ||
- (new && !in_merge_bases(new, &cb->ref_commit, 1)))
+ if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
goto prune;
}
@@ -288,7 +348,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
cb.ref = ref;
cb.cmd = cmd;
+ if (cb.ref_commit)
+ mark_reachable(cb.ref_commit, cmd->expire_total);
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
+ if (cb.ref_commit)
+ clear_commit_marks(cb.ref_commit, REACHABLE);
finish:
if (cb.newlog) {
if (fclose(cb.newlog)) {
diff --git a/builtin-remote.c b/builtin-remote.c
index ac69d37c8a..ca7c639ad3 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -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();
+ ref = push_map = copy_ref_list(remote_refs);
+ while (ref->next)
+ ref = ref->next;
+ push_tail = &ref->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;
@@ -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)
@@ -648,102 +791,333 @@ static int get_remote_ref_states(const char *name,
if (query) {
transport = transport_get(NULL, 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);
+ get_remote_ref_states(*argv, &states, query_flag);
printf("* remote %s\n URL: %s\n", *argv,
states.remote->url_nr > 0 ?
states.remote->url[0] : "(no URL)");
-
- 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)
+ 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);
}
- 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, "");
- }
+ /* 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);
+ }
- 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) : "");
- }
- }
+ return result;
+}
- /* NEEDSWORK: free remote */
- string_list_clear(&states.new, 0);
- string_list_clear(&states.stale, 0);
- string_list_clear(&states.tracked, 0);
+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;
}
@@ -755,49 +1129,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 +1188,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 +1210,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 fecthing"),
+ 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 +1229,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 +1317,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"))
diff --git a/builtin-rerere.c b/builtin-rerere.c
index bd8fc77a7a..020af7377b 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -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,7 +113,7 @@ 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"));
@@ -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);
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 436afa45f5..193993cf44 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -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);
@@ -168,20 +156,20 @@ static void finish_commit(struct commit *commit)
commit->buffer = NULL;
}
-static void finish_object(struct object_array_entry *p)
+static void finish_object(struct object_array_entry *p, void *data)
{
if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
}
-static void show_object(struct object_array_entry *p)
+static void show_object(struct object_array_entry *p, void *data)
{
/* An object with name "foo\n0000000..." can be used to
* confuse downstream "git pack-objects" very badly.
*/
const char *ep = strchr(p->name, '\n');
- finish_object(p);
+ finish_object(p, data);
if (ep) {
printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
(int) (ep - p->name),
@@ -196,389 +184,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;
@@ -589,6 +310,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];
@@ -598,7 +322,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")) {
@@ -608,6 +332,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;
}
@@ -627,19 +352,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)
@@ -660,47 +383,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;
}
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 81d5a6ffc9..22c6d6ad16 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -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;
diff --git a/builtin-rm.c b/builtin-rm.c
index c11f455858..269d60890a 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -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;
}
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index d65d019692..d5a1c48d0e 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -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
@@ -50,7 +47,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
struct child_process po;
int i;
- if (args.use_thin_pack)
+ if (args->use_thin_pack)
argv[4] = "--thin";
memset(&po, 0, sizeof(po));
po.argv = argv;
@@ -84,82 +81,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 +168,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,27 +296,19 @@ 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"))
@@ -413,19 +316,9 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
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 (!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 +330,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 +369,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 +377,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 +398,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 +459,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 +479,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 +491,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 +520,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 +530,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 +542,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;
}
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index badd912038..b28091b445 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -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);
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 306b850c72..828e6f86de 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -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;
}
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 572b114119..dc76c5090f 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -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)) {
diff --git a/builtin-update-index.c b/builtin-update-index.c
index dd43d5bef4..1fde893cfa 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -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);
/*
diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c
index a9b02fa32f..0206b416cb 100644
--- a/builtin-upload-archive.c
+++ b/builtin-upload-archive.c
@@ -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;
diff --git a/builtin.h b/builtin.h
index 1495cf6a20..425ff8e89b 100644
--- 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 dfeb1e9c86..ab1294d6fb 100644
--- 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,17 @@ 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;
#define GIT_REPO_VERSION 0
extern int repository_format_version;
@@ -670,7 +680,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 +735,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 +814,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);
diff --git a/color.c b/color.c
index db4dccfb77..62977f4808 100644
--- 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 5019df82f7..18abeb7c7d 100644
--- 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
*/
diff --git a/combine-diff.c b/combine-diff.c
index bccc018ab2..066ce841ed 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -526,7 +526,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;
@@ -592,7 +591,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);
@@ -713,9 +712,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
result_size = buf.len;
result = strbuf_detach(&buf, NULL);
elem->mode = canon_mode(st.st_mode);
- }
- else if (0 <= (fd = open(elem->path, O_RDONLY)) &&
- !fstat(fd, &st)) {
+ } else if (0 <= (fd = open(elem->path, O_RDONLY))) {
size_t len = xsize_t(st.st_size);
ssize_t done;
int is_file, i;
diff --git a/command-list.txt b/command-list.txt
index 3583a33ee9..fb03a2ebb5 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -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
diff --git a/compat/mingw.c b/compat/mingw.c
index 3dbe6a77ff..2839d9df6e 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -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));
@@ -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++;
@@ -1003,3 +1118,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;
+}
diff --git a/compat/mingw.h b/compat/mingw.h
index a255898801..762eb143a7 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -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
diff --git a/compat/win32mmap.c b/compat/win32mmap.c
new file mode 100644
index 0000000000..779d796cd5
--- /dev/null
+++ b/compat/win32mmap.c
@@ -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);
+}
diff --git a/compat/winansi.c b/compat/winansi.c
index e2d96dfe6f..44dc293ad3 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -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 */
diff --git a/config.c b/config.c
index 7a83c76f4b..2d70398b16 100644
--- a/config.c
+++ b/config.c
@@ -565,6 +565,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 +613,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 +672,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;
}
diff --git a/configure.ac b/configure.ac
index 082a03d3cf..4e728bca35 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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,57 @@ 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.
+
+GIT_STASH_FLAGS($ICONVDIR)
+
AC_DEFUN([ICONVTEST_SRC], [
#include <iconv.h>
@@ -227,25 +396,46 @@ 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"
+
#
# Define NO_DEFLATE_BOUND if deflateBound is missing from zlib.
+
+GIT_STASH_FLAGS($ZLIB_PATH)
+
AC_DEFUN([ZLIBTEST_SRC], [
#include <zlib.h>
@@ -263,7 +453,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 +491,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 +693,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
diff --git a/connect.c b/connect.c
index 2f23ab3b87..7636bf976e 100644
--- a/connect.c
+++ b/connect.c
@@ -373,8 +373,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 +381,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 +426,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 +504,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;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 8431837f97..1a90cb87f5 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -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,72 @@ __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
+ elif [ -f "$g/MERGE_HEAD" ]; then
r="|MERGING"
b="$(git symbolic-ref HEAD 2>/dev/null)"
else
- if [ -f "$g/BISECT_LOG" ]
- then
+ if [ -f "$g/BISECT_LOG" ]; then
r="|BISECTING"
fi
- if ! b="$(git symbolic-ref HEAD 2>/dev/null)"
- then
- if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
- then
- b="$(cut -c1-7 "$g/HEAD")..."
+ if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then
+ if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then
+ if [ -r "$g/HEAD" ]; then
+ b="$(cut -c1-7 "$g/HEAD")..."
+ fi
fi
fi
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 config --bool core.bare 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 +303,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 +387,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 +486,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 +646,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 +900,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 +910,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 +979,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]}"
@@ -1014,6 +1133,11 @@ _git_log ()
" "" "${cur##--pretty=}"
return
;;
+ --format=*)
+ __gitcomp "$__git_log_pretty_formats
+ " "" "${cur##--format=}"
+ return
+ ;;
--date=*)
__gitcomp "
relative iso8601 rfc2822 short local default
@@ -1029,7 +1153,7 @@ _git_log ()
--follow
--abbrev-commit --abbrev=
--relative-date --date=
- --pretty=
+ --pretty= --format= --oneline
--cherry-pick
--graph
--decorate
@@ -1045,24 +1169,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 +1192,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 +1227,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 +1274,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
@@ -1176,8 +1288,8 @@ _git_send_email ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
- __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 --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
@@ -1443,7 +1555,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 +1653,13 @@ _git_show ()
" "" "${cur##--pretty=}"
return
;;
+ --format=*)
+ __gitcomp "$__git_log_pretty_formats
+ " "" "${cur##--format=}"
+ return
+ ;;
--*)
- __gitcomp "--pretty=
+ __gitcomp "--pretty= --format=
$__git_diff_common_options
"
return
@@ -1771,6 +1888,7 @@ _git ()
--bare
--version
--exec-path
+ --html-path
--work-tree=
--help
"
@@ -1800,8 +1918,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 +1961,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"
diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper
deleted file mode 100755
index db3af6a833..0000000000
--- a/contrib/difftool/git-difftool-helper
+++ /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/fast-import/git-p4 b/contrib/fast-import/git-p4
index 3832f60225..342529db30 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -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
diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl
index 23aeb257b9..6309d146e7 100755
--- a/contrib/fast-import/import-tars.perl
+++ b/contrib/fast-import/import-tars.perl
@@ -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);
@@ -92,7 +97,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 +105,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.
@@ -119,7 +125,7 @@ EOF
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
diff --git a/daemon.c b/daemon.c
index d93cf960f9..13401f1baf 100644
--- 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;
}
diff --git a/diff-lib.c b/diff-lib.c
index 79d0606834..a310fb2ad0 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -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];
diff --git a/diff-no-index.c b/diff-no-index.c
index 7273a7a0b6..42c1dd8ad3 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -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))
diff --git a/diff.c b/diff.c
index 4f980aded6..3ac71686eb 100644
--- 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")) {
@@ -875,7 +878,7 @@ static void fill_print_name(struct diffstat_file *file)
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,
@@ -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"))
diff --git a/diff.h b/diff.h
index 6703a4fb4f..6616877ee5 100644
--- 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;
diff --git a/diffcore-break.c b/diffcore-break.c
index 31cdcfe8bc..d7097bb576 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -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 */
diff --git a/dir.c b/dir.c
index 2245749b3f..c91ebfb46f 100644
--- a/dir.c
+++ b/dir.c
@@ -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;
@@ -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 bdc2d47447..541286ad1d 100644
--- 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 05aa58d348..5daacc2fe5 100644
--- 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. */
/*
@@ -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,16 @@ 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 (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 +167,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 +185,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 +214,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);
}
diff --git a/environment.c b/environment.c
index e278bce0ea..4696885b22 100644
--- a/environment.c
+++ b/environment.c
@@ -42,6 +42,7 @@ 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;
/* Parallel index stat data preload? */
int core_preload_index = 0;
diff --git a/fast-import.c b/fast-import.c
index d5fc042bbf..23c496d683 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -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;
@@ -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 97f76c5815..511b82cba9 100644
--- 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/git-add--interactive.perl b/git-add--interactive.perl
index 064d4c68d0..60dd1b5be2 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -620,11 +620,12 @@ sub parse_diff {
if ($diff_use_color) {
@colored = run_cmd_pipe(qw(git diff-files -p --color --), $path);
}
- my (@hunk) = { TEXT => [], DISPLAY => [] };
+ my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
for (my $i = 0; $i < @diff; $i++) {
if ($diff[$i] =~ /^@@ /) {
- push @hunk, { TEXT => [], DISPLAY => [] };
+ push @hunk, { TEXT => [], DISPLAY => [],
+ TYPE => 'hunk' };
}
push @{$hunk[-1]{TEXT}}, $diff[$i];
push @{$hunk[-1]{DISPLAY}},
@@ -636,8 +637,8 @@ sub parse_diff {
sub parse_diff_header {
my $src = shift;
- my $head = { TEXT => [], DISPLAY => [] };
- my $mode = { TEXT => [], DISPLAY => [] };
+ my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };
+ my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };
for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
my $dest = $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ?
@@ -684,6 +685,7 @@ sub split_hunk {
my $this = +{
TEXT => [],
DISPLAY => [],
+ TYPE => 'hunk',
OLD => $o_ofs,
NEW => $n_ofs,
OCNT => 0,
@@ -802,6 +804,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>;
@@ -869,7 +875,11 @@ sub edit_hunk_loop {
if (!defined $text) {
return undef;
}
- my $newhunk = { TEXT => $text, USE => 1 };
+ my $newhunk = {
+ TEXT => $text,
+ TYPE => $hunk->[$ix]->{TYPE},
+ USE => 1
+ };
if (diff_applies($head,
@{$hunk}[0..$ix-1],
$newhunk,
@@ -890,6 +900,7 @@ sub help_patch_cmd {
print colored $help_color, <<\EOF ;
y - stage this hunk
n - do not stage this hunk
+q - quit, do not stage this hunk nor any of the remaining ones
a - stage this and all the remaining hunks in the file
d - do not stage this hunk nor any of the remaining hunks in the file
g - select a hunk to go to
@@ -926,7 +937,7 @@ sub patch_update_cmd {
@mods);
}
for (@them) {
- patch_update_file($_->{VALUE});
+ return 0 if patch_update_file($_->{VALUE});
}
}
@@ -972,6 +983,7 @@ sub display_hunks {
}
sub patch_update_file {
+ my $quit = 0;
my ($ix, $num);
my $path = shift;
my ($head, @hunk) = parse_diff($path);
@@ -981,32 +993,7 @@ sub patch_update_file {
}
if (@{$mode->{TEXT}}) {
- while (1) {
- print @{$mode->{DISPLAY}};
- print colored $prompt_color,
- "Stage mode change [y/n/a/d/?]? ";
- my $line = prompt_single_character;
- if ($line =~ /^y/i) {
- $mode->{USE} = 1;
- last;
- }
- elsif ($line =~ /^n/i) {
- $mode->{USE} = 0;
- last;
- }
- elsif ($line =~ /^a/i) {
- $_->{USE} = 1 foreach ($mode, @hunk);
- last;
- }
- elsif ($line =~ /^d/i) {
- $_->{USE} = 0 foreach ($mode, @hunk);
- last;
- }
- else {
- help_patch_cmd('');
- next;
- }
- }
+ unshift @hunk, $mode;
}
$num = scalar @hunk;
@@ -1050,14 +1037,19 @@ sub patch_update_file {
}
last if (!$undecided);
- if (hunk_splittable($hunk[$ix]{TEXT})) {
+ if ($hunk[$ix]{TYPE} eq 'hunk' &&
+ hunk_splittable($hunk[$ix]{TEXT})) {
$other .= ',s';
}
- $other .= ',e';
+ if ($hunk[$ix]{TYPE} eq 'hunk') {
+ $other .= ',e';
+ }
for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
- print colored $prompt_color, "Stage this hunk [y,n,a,d,/$other,?]? ";
+ print colored $prompt_color, 'Stage ',
+ ($hunk[$ix]{TYPE} eq 'mode' ? 'mode change' : 'this hunk'),
+ " [y,n,a,d,/$other,?]? ";
my $line = prompt_single_character;
if ($line) {
if ($line =~ /^y/i) {
@@ -1109,6 +1101,16 @@ sub patch_update_file {
}
next;
}
+ elsif ($line =~ /^q/i) {
+ while ($ix < $num) {
+ if (!defined $hunk[$ix]{USE}) {
+ $hunk[$ix]{USE} = 0;
+ }
+ $ix++;
+ }
+ $quit = 1;
+ next;
+ }
elsif ($line =~ m|^/(.*)|) {
my $regex = $1;
if ($1 eq "") {
@@ -1189,7 +1191,7 @@ sub patch_update_file {
$num = scalar @hunk;
next;
}
- elsif ($line =~ /^e/) {
+ elsif ($other =~ /e/ && $line =~ /^e/) {
my $newhunk = edit_hunk_loop($head, \@hunk, $ix);
if (defined $newhunk) {
splice @hunk, $ix, 1, $newhunk;
@@ -1210,9 +1212,6 @@ sub patch_update_file {
my $n_lofs = 0;
my @result = ();
- if ($mode->{USE}) {
- push @result, @{$mode->{TEXT}};
- }
for (@hunk) {
if ($_->{USE}) {
push @result, @{$_->{TEXT}};
@@ -1235,6 +1234,7 @@ sub patch_update_file {
}
print "\n";
+ return $quit;
}
sub diff_cmd {
diff --git a/git-am.sh b/git-am.sh
index d3390755fc..774383fb68 100755
--- 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
diff --git a/git-bisect.sh b/git-bisect.sh
index 10ad340920..24712ff304 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -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() {
diff --git a/git-compat-util.h b/git-compat-util.h
index 878d83dd08..f09f244061 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -166,7 +166,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 +180,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 +394,18 @@ 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
+
#endif
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
new file mode 100755
index 0000000000..57e8e3256d
--- /dev/null
+++ b/git-difftool--helper.sh
@@ -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/contrib/difftool/git-difftool b/git-difftool.perl
index 0cda3d2eea..948ff7f6fd 100755
--- a/contrib/difftool/git-difftool
+++ b/git-difftool.perl
@@ -2,9 +2,12 @@
# 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.
+# 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;
@@ -18,7 +21,7 @@ my $DIR = abs_path(dirname($0));
sub usage
{
print << 'USAGE';
-usage: git difftool [--tool=<tool>] [--no-prompt] ["git diff" options]
+usage: git difftool [--tool=<tool>] [-y|--no-prompt] ["git diff" options]
USAGE
exit 1;
}
@@ -27,13 +30,16 @@ sub setup_environment
{
$ENV{PATH} = "$DIR:$ENV{PATH}";
$ENV{GIT_PAGER} = '';
- $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper';
+ $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
}
sub exe
{
my $exe = shift;
- return defined $ENV{COMSPEC} ? "$exe.exe" : $exe;
+ if ($^O eq 'MSWin32' || $^O eq 'msys') {
+ return "$exe.exe";
+ }
+ return $exe;
}
sub generate_command
@@ -47,21 +53,27 @@ sub generate_command
$skip_next = 0;
next;
}
- if ($arg eq '-t' or $arg eq '--tool') {
+ if ($arg eq '-t' || $arg eq '--tool') {
usage() if $#ARGV <= $idx;
- $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1];
+ $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];
$skip_next = 1;
next;
}
if ($arg =~ /^--tool=/) {
- $ENV{GIT_MERGE_TOOL} = substr($arg, 7);
+ $ENV{GIT_DIFF_TOOL} = substr($arg, 7);
next;
}
- if ($arg eq '--no-prompt') {
+ 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' or $arg eq '--help') {
+ if ($arg eq '-h' || $arg eq '--help') {
usage();
}
push @command, $arg;
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 9a09ba1382..b90d3df3a7 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -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
"")
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 0843372b57..5f4419b69b 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -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"
}
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
new file mode 100644
index 0000000000..a16a2795d7
--- /dev/null
+++ b/git-mergetool--lib.sh
@@ -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"
+}
diff --git a/git-mergetool.sh b/git-mergetool.sh
index 87fa88af55..b52a7410bc 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -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
diff --git a/git-pull.sh b/git-pull.sh
index c892059605..8c750270e9 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -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)
@@ -196,7 +196,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
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 3dc659dd58..314cd364b8 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -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
;;
diff --git a/git-rebase.sh b/git-rebase.sh
index 368c0ef434..b83fd3f970 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -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"
@@ -289,11 +291,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 +319,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
;;
@@ -420,17 +441,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 +457,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"
diff --git a/git-send-email.perl b/git-send-email.perl
index 09fe3d95c4..172b53c2d5 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -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";
@@ -594,32 +606,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 +662,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 +681,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 +776,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 +844,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 +971,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 +980,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 +1118,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 +1140,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;
diff --git a/git-svn.perl b/git-svn.perl
index 931d1a37bc..c5965c9aaf 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -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 c2b181ed78..bfb6508ad0 100644
--- 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.";
@@ -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")) {
@@ -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 },
diff --git a/graph.c b/graph.c
index 162a516ee1..d4571cf31d 100644
--- 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"
@@ -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 += 2 + 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 > 3)
+ 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 062b2b6f28..f3a27d7d6e 100644
--- 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)
@@ -190,7 +192,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);
}
@@ -251,18 +254,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');
@@ -306,11 +297,12 @@ 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];
if ((p->token != GREP_PATTERN) &&
((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
@@ -329,16 +321,12 @@ 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 ||
(pmatch[0].rm_eo < 0) ||
@@ -378,39 +366,33 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
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;
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);
@@ -424,24 +406,104 @@ 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)) {
+ 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 5102ce335d..a67005de62 100644
--- 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/http-push.c b/http-push.c
index 968b6b0662..5138224cc3 100644
--- a/http-push.c
+++ b/http-push.c
@@ -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;
@@ -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
@@ -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;
@@ -758,7 +788,7 @@ static void finish_request(struct transfer_request *request)
}
} 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);
@@ -787,7 +817,7 @@ 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);
@@ -797,7 +827,7 @@ static void finish_request(struct transfer_request *request)
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 +835,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 +845,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 +918,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 +959,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 +985,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);
@@ -1017,8 +1047,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 +1066,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 +1251,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 +1286,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 +1299,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 +1355,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 +1366,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 +1392,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 +1411,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) {
@@ -1456,7 +1493,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)) {
@@ -1479,7 +1516,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;
@@ -1495,7 +1532,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);
@@ -1507,6 +1544,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);
@@ -1572,8 +1613,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");
@@ -1583,9 +1627,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);
@@ -1615,16 +1663,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);
@@ -1765,6 +1813,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);
@@ -1791,21 +1843,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;
@@ -1813,10 +1852,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;
}
@@ -1825,7 +1864,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",
@@ -1838,12 +1877,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;
@@ -1861,55 +1894,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;
@@ -1920,10 +1904,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;
@@ -1977,6 +1961,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);
@@ -1998,12 +1986,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;
@@ -2033,8 +2021,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;
@@ -2149,7 +2137,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);
}
}
@@ -2157,8 +2145,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);
@@ -2194,14 +2182,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++) {
@@ -2234,14 +2223,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;
}
@@ -2254,7 +2243,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)
@@ -2262,17 +2251,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 */
@@ -2284,24 +2280,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();
@@ -2455,8 +2451,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);
@@ -2469,7 +2465,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);
diff --git a/http.c b/http.c
index ee58799ca8..2e3d6493ef 100644
--- 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;
+static const char *ssl_cert;
#if LIBCURL_VERSION_NUM >= 0x070902
-static const char *ssl_key = NULL;
+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);
+ 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 >= 0x070902
- if (!strcmp("http.sslkey", var)) {
- if (ssl_key == NULL)
- return git_config_string(&ssl_key, var, value);
- return 0;
- }
+ 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)
{
- CURL* result = curl_easy_init();
+ CURL *result = curl_easy_init();
if (!curl_ssl_verify) {
curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
@@ -176,6 +185,8 @@ 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
@@ -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");
+ set_from_env(&ssl_cert, "GIT_SSL_CERT");
#if LIBCURL_VERSION_NUM >= 0x070902
- ssl_key = getenv("GIT_SSL_KEY");
+ 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 905b4629a4..26abebed1f 100644
--- a/http.h
+++ b/http.h
@@ -37,6 +37,10 @@
#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);
diff --git a/imap-send.c b/imap-send.c
index f91293c23f..8154cb2116 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -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;
diff --git a/index-pack.c b/index-pack.c
index 5dfe03ee6c..6e93ee6af6 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -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) {
diff --git a/list-objects.c b/list-objects.c
index dd243c7c66..433394a107 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -135,8 +135,9 @@ void mark_edges_uninteresting(struct commit_list *list,
}
void traverse_commit_list(struct rev_info *revs,
- void (*show_commit)(struct commit *),
- void (*show_object)(struct object_array_entry *))
+ show_commit_fn show_commit,
+ show_object_fn show_object,
+ void *data)
{
int i;
struct commit *commit;
@@ -144,7 +145,7 @@ void traverse_commit_list(struct rev_info *revs,
while ((commit = get_revision(revs)) != NULL) {
process_tree(revs, commit->tree, &objects, NULL, "");
- show_commit(commit);
+ show_commit(commit, data);
}
for (i = 0; i < revs->pending.nr; i++) {
struct object_array_entry *pending = revs->pending.objects + i;
@@ -171,7 +172,7 @@ void traverse_commit_list(struct rev_info *revs,
sha1_to_hex(obj->sha1), name);
}
for (i = 0; i < objects.nr; i++)
- show_object(&objects.objects[i]);
+ show_object(&objects.objects[i], data);
free(objects.objects);
if (revs->pending.nr) {
free(revs->pending.objects);
diff --git a/list-objects.h b/list-objects.h
index 0f41391ecc..47fae2e468 100644
--- a/list-objects.h
+++ b/list-objects.h
@@ -1,11 +1,11 @@
#ifndef LIST_OBJECTS_H
#define LIST_OBJECTS_H
-typedef void (*show_commit_fn)(struct commit *);
-typedef void (*show_object_fn)(struct object_array_entry *);
+typedef void (*show_commit_fn)(struct commit *, void *);
+typedef void (*show_object_fn)(struct object_array_entry *, void *);
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);
diff --git a/log-tree.c b/log-tree.c
index 84a74e544b..5bd29e6994 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -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);
diff --git a/log-tree.h b/log-tree.h
index f2a90084ae..20b5caf1aa 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -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
diff --git a/mailmap.c b/mailmap.c
index 6be91b60df..bb1f2fb711 100644
--- 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);
diff --git a/merge-recursive.c b/merge-recursive.c
index 9bf5cc7175..d6f0582238 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -801,22 +801,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 +823,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;
diff --git a/parse-options.c b/parse-options.c
index 4c5d09dd25..cf71bcffd2 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -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++);
diff --git a/parse-options.h b/parse-options.h
index 912290549b..f8ef1db128 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -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 {
diff --git a/patch-ids.c b/patch-ids.c
index 3be5d3165e..5717257051 100644
--- a/patch-ids.c
+++ b/patch-ids.c
@@ -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 */
diff --git a/perl/Git.pm b/perl/Git.pm
index 7d7f2b1d36..291ff5b53c 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -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.
diff --git a/pretty.c b/pretty.c
index e9540e46da..a0ef356558 100644
--- a/pretty.c
+++ b/pretty.c
@@ -10,6 +10,15 @@
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;
diff --git a/quote.c b/quote.c
index 6a520855d6..7a49fcf696 100644
--- 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 c5eea6f18e..66730f2bff 100644
--- 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);
diff --git a/read-cache.c b/read-cache.c
index 940ec76fdf..3f587110cb 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -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;
}
/*
diff --git a/reflog-walk.c b/reflog-walk.c
index f751fdc8d8..fd065f4e1a 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -242,7 +242,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
}
void show_reflog_message(struct reflog_walk_info* info, int oneline,
- int relative_date)
+ 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);
diff --git a/reflog-walk.h b/reflog-walk.h
index 7ca1438f4d..74c90964bd 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -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 6eb5f53846..e65a3b4c4e 100644
--- a/refs.c
+++ b/refs.c
@@ -647,19 +647,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 +681,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 +699,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 +716,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",
@@ -995,7 +1019,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
err = unlink(git_path("logs/%s", lock->ref_name));
if (err && errno != ENOENT)
- fprintf(stderr, "warning: unlink(%s) failed: %s",
+ warning("unlink(%s) failed: %s",
git_path("logs/%s", lock->ref_name), strerror(errno));
invalidate_cached_refs();
unlock_ref(lock);
@@ -1437,8 +1461,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 +1473,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));
}
}
@@ -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 29bdcecd4e..29d17a48e4 100644
--- 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);
diff --git a/remote.c b/remote.c
index c7a8c2b1fb..41c5b59736 100644
--- 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;
@@ -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;
@@ -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.
*/
@@ -1319,9 +1413,10 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
if (theirs == ours)
return 0;
- /* Run "rev-list --left-right ours...theirs" internally... */
+ /* Run "rev-list --no-merges --left-right ours...theirs" internally... */
rev_argc = 0;
rev_argv[rev_argc++] = NULL;
+ rev_argv[rev_argc++] = "--no-merges";
rev_argv[rev_argc++] = "--left-right";
rev_argv[rev_argc++] = symmetric;
rev_argv[rev_argc++] = "--";
@@ -1366,9 +1461,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 +1478,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;
+}
diff --git a/remote.h b/remote.h
index a46a5be131..99706a89bc 100644
--- 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
diff --git a/rerere.c b/rerere.c
index 3518207c17..713c6e16ac 100644
--- 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)
@@ -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;
}
diff --git a/rerere.h b/rerere.h
index f9b03862fe..13313f3f2b 100644
--- 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
diff --git a/revision.c b/revision.c
index 34ee490ea0..b6215cc72c 100644
--- a/revision.c
+++ b/revision.c
@@ -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;
diff --git a/revision.h b/revision.h
index 66d211ac2e..5adfc91405 100644
--- a/revision.h
+++ b/revision.h
@@ -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;
diff --git a/send-pack.h b/send-pack.h
index 8ff1dc3539..83d76c7e35 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -2,9 +2,7 @@
#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,
@@ -12,7 +10,7 @@ struct send_pack_args {
};
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
diff --git a/sha1-lookup.c b/sha1-lookup.c
index da357479cf..c4dc55d1f5 100644
--- a/sha1-lookup.c
+++ b/sha1-lookup.c
@@ -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:
*
diff --git a/sha1-lookup.h b/sha1-lookup.h
index 3249a81b3d..20af285681 100644
--- a/sha1-lookup.h
+++ b/sha1-lookup.h
@@ -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,
diff --git a/sha1_name.c b/sha1_name.c
index 2f75179f4c..904bcd96a5 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -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;
diff --git a/shell.c b/shell.c
index e3393690dd..b968be79f4 100644
--- 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 },
};
diff --git a/sideband.c b/sideband.c
index cca3360546..899b1ff366 100644
--- a/sideband.c
+++ b/sideband.c
@@ -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;
}
}
diff --git a/sideband.h b/sideband.h
index a84b6917c7..d72db35d1e 100644
--- a/sideband.h
+++ b/sideband.h
@@ -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
diff --git a/strbuf.c b/strbuf.c
index 6ed06840b8..a88496030b 100644
--- 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,
@@ -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);
+}
diff --git a/strbuf.h b/strbuf.h
index 89bd36e15a..9ee908a3ec 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -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 */
diff --git a/string-list.c b/string-list.c
index 15e14cf47a..1ac536e638 100644
--- a/string-list.c
+++ b/string-list.c
@@ -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) {
diff --git a/string-list.h b/string-list.h
index d32ba05202..14bbc477de 100644
--- a/string-list.h
+++ b/string-list.h
@@ -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,
diff --git a/symlinks.c b/symlinks.c
index f262b7c44b..1d6b35b858 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -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;
+}
diff --git a/t/Makefile b/t/Makefile
index 9149373032..bf816fc850 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -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
diff --git a/t/README b/t/README
index f208cf1db9..d8f6c7de6d 100644
--- 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
--------------
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 67c431d4eb..773d47cf3c 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -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
}
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 86cdebc727..cde659d14a 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -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() {
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index af6e5e1d6a..21aa42f1c6 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -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
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 70df15cbd8..f4ca4fc85c 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -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 &&
diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh
index 63e1217e71..2342ac5788 100755
--- a/t/t0004-unwritable.sh
+++ b/t/t0004-unwritable.sh
@@ -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 &&
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 1be7446d8d..4e72b53140 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -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 &&
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
index e5330395fc..c7d0324374 100755
--- a/t/t0024-crlf-archive.sh
+++ b/t/t0024-crlf-archive.sh
@@ -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 &&
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 7edf49db3c..89282ccf7a 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -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 &&
diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh
index b29c37a5a4..0c6ff567a1 100755
--- a/t/t0055-beyond-symlinks.sh
+++ b/t/t0055-beyond-symlinks.sh
@@ -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 )
'
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 8336114f98..53cf1f8dc4 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -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 \
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index 570d3729bd..f19b4a2a4a 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -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' '
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index fc386ba033..210e594f6f 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -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 &&
diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh
index 7f7fc36734..c4414ff576 100755
--- a/t/t1100-commit-tree-options.sh
+++ b/t/t1100-commit-tree-options.sh
@@ -40,6 +40,6 @@ test_expect_success \
test_expect_success \
'compare commit' \
- 'diff expected commit'
+ 'test_cmp expected commit'
test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 3c06842d99..43ea283242 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -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,7 @@ echo >>result
test_expect_success '--null --get-regexp' 'cmp result expect'
-test_expect_success 'symlinked configuration' '
+test_expect_success SYMLINKS 'symlinked configuration' '
ln -s notyet myconfig &&
GIT_CONFIG=myconfig git config test.frotz nitfol &&
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 3c8a2373ac..de42d21c92 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -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 &&
@@ -141,11 +141,14 @@ test_expect_success 'forced modes' '
git commit -a -m initial &&
git repack
) &&
- find new/.git -print |
+ # List repository files meant to be protected; note that
+ # COMMIT_EDITMSG does not matter---0mode is not about a
+ # repository with a work tree.
+ find new/.git -type f -name COMMIT_EDITMSG -prune -o -print |
xargs ls -ld >actual &&
# Everything must be unaccessible to others
- test -z "$(sed -n -e "/^.......---/d" actual)" &&
+ test -z "$(sed -e "/^.......---/d" actual)" &&
# All directories must have either 2770 or 770
test -z "$(sed -n -e "/^drwxrw[sx]---/d" -e "/^d/p" actual)" &&
@@ -156,10 +159,11 @@ test_expect_success 'forced modes' '
p
}" actual)" &&
- # All files inside objects must be 0440
+ # All files inside objects must be accessible by us
test -z "$(sed -n -e "/objects\//{
/^d/d
- /^-r--r-----/d
+ /^-r.-r.----/d
+ p
}" actual)"
'
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index bd589268fc..54ba3df95f 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -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 \
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 5b24f05573..80af6b9b7e 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -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
index 0000000000..c18ed8edf9
--- /dev/null
+++ b/t/t1411-reflog-show.sh
@@ -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
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 4597af0eb6..a22632f483 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -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
diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh
index e377d48902..df5ad8c686 100755
--- a/t/t1504-ceiling-dirs.sh
+++ b/t/t1504-ceiling-dirs.sh
@@ -13,7 +13,7 @@ test_fail() {
"git rev-parse --show-prefix"
}
-TRASH_ROOT="$(pwd)"
+TRASH_ROOT="$PWD"
ROOT_PARENT=$(dirname "$TRASH_ROOT")
diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh
index ef007532b1..98aa73e823 100755
--- a/t/t2001-checkout-cache-clash.sh
+++ b/t/t2001-checkout-cache-clash.sh
@@ -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)'
diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh
index 71894b3743..02a4fc5d36 100755
--- a/t/t2003-checkout-cache-mkdir.sh
+++ b/t/t2003-checkout-cache-mkdir.sh
@@ -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 &&
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
index 39133b8c7a..36cca14d95 100755
--- a/t/t2004-checkout-cache-temp.sh
+++ b/t/t2004-checkout-cache-temp.sh
@@ -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 &&
diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh
index 0526fce163..20f33436d0 100755
--- a/t/t2007-checkout-symlink.sh
+++ b/t/t2007-checkout-symlink.sh
@@ -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 &&
diff --git a/t/t2100-update-cache-badpath.sh b/t/t2100-update-cache-badpath.sh
index 6ef2dcfd8a..2df3fdde8b 100755
--- a/t/t2100-update-cache-badpath.sh
+++ b/t/t2100-update-cache-badpath.sh
@@ -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
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index b2ddf5ace3..912075063b 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -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"
diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh
index d24c7d9e5f..2e8f702452 100755
--- a/t/t2201-add-update-typechange.sh
+++ b/t/t2201-add-update-typechange.sh
@@ -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/{
diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh
index 293dc353b1..3b01ad2e4d 100755
--- a/t/t2300-cd-to-toplevel.sh
+++ b/t/t2300-cd-to-toplevel.sh
@@ -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
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index bc0a351392..86291e8399 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -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
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index ec14040637..95671c2053 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -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
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
index 6e6a2542a2..ee60d03fe8 100755
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -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'
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 1b1e9ece57..d59a9b4aef 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -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
index 0000000000..809d1c4ed4
--- /dev/null
+++ b/t/t3203-branch-output.sh
@@ -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
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index be7ae5a004..6e391a3702 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -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
'
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 603b003edf..c32ff6682b 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -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
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 5391080943..85fc7c4af8 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -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/t3413-rebase-hook.sh
index 098b75507b..098b75507b 100755
--- a/t/t3409-rebase-hook.sh
+++ b/t/t3413-rebase-hook.sh
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 95542e9cfe..76b1bb4545 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -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' \
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 9f6454d2ff..050de42ef4 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -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 &&
@@ -222,7 +222,7 @@ test_expect_success 'git add (add.ignore-errors = false)' '
! ( git ls-files foo1 | grep foo1 )
'
-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'\'' &&
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index e95663d8e6..dfc65601aa 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -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,6 @@ test_expect_success 'stage mode but not hunk' '
git diff file | grep "+content"
'
-fi
# end of tests disabled when filemode is not usable
test_done
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index cc3681f161..18695ce821 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -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
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index b35af9b42d..a4da1196a9 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -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 &&
diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh
index 4e92fce1d0..8c1b81e248 100755
--- a/t/t4006-diff-mode.sh
+++ b/t/t4006-diff-mode.sh
@@ -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"
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index 7e343a9cd1..e19ca65885 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -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'
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index 9055c8b318..d7e327cc5b 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -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
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index 3cf5b5c4ea..f64aa48d24 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -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
) &&
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 9c709022ef..8b33321f8c 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -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
index 0000000000..b672e1ca63
--- /dev/null
+++ b/t/t4013/diff.diff_--dirstat_master~1_master~2
@@ -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
index 0000000000..52116d3ead
--- /dev/null
+++ b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
@@ -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--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
index e5ab74437e..ce49bd676e 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
@@ -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
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
index 2c71d20d37..5f1b23863b 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
@@ -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
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
index 38f790290a..4a2364abc2 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
@@ -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
index 0000000000..43b81eba54
--- /dev/null
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
@@ -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--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
index 58f8a7b7d6..ca3f60bf0e 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
@@ -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
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_initial..master
index 9e7bbdffa2..08f23014bc 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master
@@ -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
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
index f881f644cc..07f1230d31 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
@@ -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
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
index 4f258b8858..29e00ab8af 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
@@ -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
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..side b/t/t4013/diff.format-patch_--inline_--stdout_initial..side
index e86dce69a3..67633d424a 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..side
@@ -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
index 0000000000..12da8ac07d
--- /dev/null
+++ b/t/t4013/diff.log_--decorate_--all
@@ -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
index 0000000000..e7f17d5aa0
--- /dev/null
+++ b/t/t4013/diff.rev-list_--children_HEAD
@@ -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
index 0000000000..65d2a80208
--- /dev/null
+++ b/t/t4013/diff.rev-list_--parents_HEAD
@@ -0,0 +1,7 @@
+$ git rev-list --parents HEAD
+59d314ad6f356dd08601a4cd5e530381da3e3c64 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 444ac553ac7612cc88969031b02b3767fb8a353a
+9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 444ac553ac7612cc88969031b02b3767fb8a353a
+444ac553ac7612cc88969031b02b3767fb8a353a
+$
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index f045898fe3..11061ddd5b 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -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' '
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
index 297ddb5a25..9bdf6596d8 100755
--- a/t/t4023-diff-rename-typechange.sh
+++ b/t/t4023-diff-rename-typechange.sh
@@ -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 &&
diff --git a/t/t4017-quiet.sh b/t/t4035-diff-quiet.sh
index e747e84227..e747e84227 100755
--- a/t/t4017-quiet.sh
+++ b/t/t4035-diff-quiet.sh
diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
index ba43f18549..ba43f18549 100755
--- a/t/t4021-format-patch-signer-mime.sh
+++ b/t/t4036-format-patch-signer-mime.sh
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
index d42abff1ad..1597965241 100755
--- a/t/t4102-apply-rename.sh
+++ b/t/t4102-apply-rename.sh
@@ -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"'
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
index 0f185caa44..99ec13dd53 100755
--- a/t/t4114-apply-typechange.sh
+++ b/t/t4114-apply-typechange.sh
@@ -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 &&
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index 9ace578f17..b852e58980 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -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 &&
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index 841773f75f..0d3c1d5dd5 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -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
diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
index adfcbb5a3b..fc7af04931 100755
--- a/t/t4129-apply-samemode.sh
+++ b/t/t4129-apply-samemode.sh
@@ -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/t4130-apply-criss-cross-rename.sh b/t/t4130-apply-criss-cross-rename.sh
new file mode 100755
index 0000000000..8623dbebab
--- /dev/null
+++ b/t/t4130-apply-criss-cross-rename.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='git apply handling criss-cross rename patch.'
+. ./test-lib.sh
+
+create_file() {
+ cnt=0
+ while test $cnt -le 100
+ do
+ cnt=$(($cnt + 1))
+ echo "$2" >> "$1"
+ done
+}
+
+test_expect_success 'setup' '
+ create_file file1 "File1 contents" &&
+ create_file file2 "File2 contents" &&
+ git add file1 file2 &&
+ git commit -m 1
+'
+
+test_expect_success 'criss-cross rename' '
+ mv file1 tmp &&
+ mv file2 file1 &&
+ mv tmp file2
+'
+
+test_expect_success 'diff -M -B' '
+ git diff -M -B > diff &&
+ git reset --hard
+
+'
+
+test_expect_success 'apply' '
+ git apply diff
+'
+
+test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 5e65afa0c1..d6ebbaebe2 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -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
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 7b976ee36d..b98619035c 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -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,153 @@ 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
+'
+
+cat > expect <<\EOF
+* 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
index 0000000000..04f7bae850
--- /dev/null
+++ b/t/t4204-patch-id.sh
@@ -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
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index c942c8be85..7641e0dd46 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -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) &&
@@ -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,22 @@ 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 \
'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 +114,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 +131,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' \
@@ -144,8 +152,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 +164,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 +180,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 +208,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/t5300-pack-object.sh b/t/t5300-pack-object.sh
index ccfc64c6ee..e2aa254eae 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -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 :;
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index e6f70d474f..4360e77d31 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -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
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index d4e30fc43c..5132d41309 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -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 &&
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
index 9b2e1a94c5..5858b868ed 100755
--- a/t/t5403-post-checkout-hook.sh
+++ b/t/t5403-post-checkout-hook.sh
@@ -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
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index 4074e23ffa..d5db75d826 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -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
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index eb637184a0..5ec668d6d8 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -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)
'
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
new file mode 100755
index 0000000000..2a1806b0b4
--- /dev/null
+++ b/t/t5506-remote-groups.sh
@@ -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
diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh
index 22ba380034..c28932216b 100755
--- a/t/t5511-refspec.sh
+++ b/t/t5511-refspec.sh
@@ -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
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 1f4608d8ba..dbb927dec8 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -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/t5522-pull-symlink.sh
index 5672b51e2e..86bbd7d024 100755
--- a/t/t5521-pull-symlink.sh
+++ b/t/t5522-pull-symlink.sh
@@ -4,6 +4,12 @@ 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/
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
index 10e5fd0d5a..5fe479e1c2 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -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
index 0000000000..05b1b62cb6
--- /dev/null
+++ b/t/t5550-http-fetch.sh
@@ -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
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 44793f2eee..2335d8bc85 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -159,4 +159,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
diff --git a/t/t5602-clone-remote-exec.sh b/t/t5602-clone-remote-exec.sh
index 82b1d1e2b3..deffdaee49 100755
--- a/t/t5602-clone-remote-exec.sh
+++ b/t/t5602-clone-remote-exec.sh
@@ -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
'
diff --git a/t/t5705-clone-2gb.sh b/t/t5705-clone-2gb.sh
new file mode 100755
index 0000000000..9f52154cac
--- /dev/null
+++ b/t/t5705-clone-2gb.sh
@@ -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
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 052a6c90f5..54b7ea6505 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -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
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
index 8073e0c3ef..8a3304fb0b 100755
--- a/t/t6031-merge-recursive.sh
+++ b/t/t6031-merge-recursive.sh
@@ -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/t6023-merge-rename-nocruft.sh b/t/t6034-merge-rename-nocruft.sh
index 65be95fbaa..65be95fbaa 100755
--- a/t/t6023-merge-rename-nocruft.sh
+++ b/t/t6034-merge-rename-nocruft.sh
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index ba9060190d..3d6db4d386 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -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 &&
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index 8f5a06f7dd..2049ab6cf8 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -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
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 8bfae44a83..8052c86ad3 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -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 &&
@@ -294,11 +317,22 @@ test_expect_success 'Check ambiguous head and tag refs' '
'
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 &&
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 8fb3a56838..10b8f8c44b 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -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 &&
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 69501e2711..73dbc4360b 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -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'
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index 2d919d69ef..b647957d75 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -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)"
'
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 1636fac2a4..929d5d4d3b 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -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
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index b4e2b4db84..e2ef53228e 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -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'"
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index ad42c78d7c..56cd866019 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -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/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh
index b069095995..8528f64c8d 100755
--- a/t/t7503-pre-commit-hook.sh
+++ b/t/t7503-pre-commit-hook.sh
@@ -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 &&
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index 47680e6df4..1f53ea8090 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -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/t7502-status.sh b/t/t7508-status.sh
index 93f875f500..93f875f500 100755
--- a/t/t7502-status.sh
+++ b/t/t7508-status.sh
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
new file mode 100755
index 0000000000..2586f864a0
--- /dev/null
+++ b/t/t7800-difftool.sh
@@ -0,0 +1,211 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 David Aguilar
+#
+
+test_description='git-difftool
+
+Testing basic diff tool invocation
+'
+
+. ./test-lib.sh
+
+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
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index d7634187aa..d9420e0a3b 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -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 \
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index bb921af56a..4eee2e9fa6 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -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/t9108-git-svn-multi-glob.sh b/t/t9109-git-svn-multi-glob.sh
index 8f79c3f251..8f79c3f251 100755
--- a/t/t9108-git-svn-multi-glob.sh
+++ b/t/t9109-git-svn-multi-glob.sh
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 9c7b1ad18b..3200ab38ef 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -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
diff --git a/t/t9131-git-svn-empty-symlink.sh b/t/t9131-git-svn-empty-symlink.sh
index 8f35e294aa..9a24a65b64 100755
--- a/t/t9131-git-svn-empty-symlink.sh
+++ b/t/t9131-git-svn-empty-symlink.sh
@@ -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'
diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
index b8de59e493..6c4c90b036 100755
--- a/t/t9132-git-svn-broken-symlink.sh
+++ b/t/t9132-git-svn-broken-symlink.sh
@@ -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`")
'
diff --git a/t/t9134-git-svn-ignore-paths.sh b/t/t9134-git-svn-ignore-paths.sh
index c4b5b8bcf7..71fdc4a69d 100755
--- a/t/t9134-git-svn-ignore-paths.sh
+++ b/t/t9134-git-svn-ignore-paths.sh
@@ -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/t9106-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh
index fd185011b7..fd185011b7 100755
--- a/t/t9106-git-svn-dcommit-clobber-series.sh
+++ b/t/t9137-git-svn-dcommit-clobber-series.sh
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 245a7c3662..56b7c06921 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -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 &&
diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh
index 86c376088c..8da9ce5459 100755
--- a/t/t9301-fast-export.sh
+++ b/t/t9301-fast-export.sh
@@ -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)
'
@@ -259,4 +262,19 @@ test_expect_success 'cope with tagger-less tags' '
'
+test_expect_success 'set-up a few more tags for tag export tests' '
+ git checkout -f master &&
+ HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` &&
+ git tag tree_tag -m "tagging a tree" $HEAD_TREE &&
+ git tag -a tree_tag-obj -m "tagging a tree" $HEAD_TREE &&
+ git tag tag-obj_tag -m "tagging a tag" tree_tag-obj &&
+ git tag -a tag-obj_tag-obj -m "tagging a tag" tree_tag-obj
+'
+
+# NEEDSWORK: not just check return status, but validate the output
+test_expect_success 'tree_tag' 'git fast-export tree_tag'
+test_expect_success 'tree_tag-obj' 'git fast-export tree_tag-obj'
+test_expect_success 'tag-obj_tag' 'git fast-export tag-obj_tag'
+test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
+
test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 6a37f71d11..64f947d75b 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -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
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index e27a1c5f85..aca40c1b1f 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -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"
'
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 6ed10d0933..f4210fbb04 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -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'
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index d2379e7f62..4322a0c1ed 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -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
diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh
index b81d5dfc34..b4ca244626 100755
--- a/t/t9700-perl-git.sh
+++ b/t/t9700-perl-git.sh
@@ -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
}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 59d82d25e9..4bd986f430 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -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=$owd/../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
index 0000000000..d4ae6676d1
--- /dev/null
+++ b/t/valgrind/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+/templates
diff --git a/t/valgrind/analyze.sh b/t/valgrind/analyze.sh
new file mode 100755
index 0000000000..d8105d9fab
--- /dev/null
+++ b/t/valgrind/analyze.sh
@@ -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
index 0000000000..9e013fa3b2
--- /dev/null
+++ b/t/valgrind/default.supp
@@ -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
index 0000000000..582b4dca94
--- /dev/null
+++ b/t/valgrind/valgrind.sh
@@ -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" "$@"
diff --git a/templates/hooks--update.sample b/templates/hooks--update.sample
index 93c605594f..f8bf490cff 100755
--- a/templates/hooks--update.sample
+++ b/templates/hooks--update.sample
@@ -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
diff --git a/templates/this--description b/templates/this--description
index c6f25e80b8..498b267a8c 100644
--- a/templates/this--description
+++ b/templates/this--description
@@ -1 +1 @@
-Unnamed repository; edit this file to name it for gitweb.
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/transport.c b/transport.c
index 9ae92cd39c..3dfb03c06e 100644
--- a/transport.c
+++ b/transport.c
@@ -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;
}
diff --git a/transport.h b/transport.h
index 6bbc1a8264..b1c2252766 100644
--- a/transport.h
+++ b/transport.h
@@ -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);
diff --git a/unimplemented.sh b/unimplemented.sh
new file mode 100644
index 0000000000..5252de4b25
--- /dev/null
+++ b/unimplemented.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo >&2 "fatal: git was built without support for `basename $0` (@@REASON@@)."
+exit 128
diff --git a/unpack-trees.c b/unpack-trees.c
index 5820ce48f3..6847c2d966 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -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(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,7 @@ static int check_updates(struct unpack_trees_options *o)
cnt = 0;
}
+ 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 +95,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 +112,7 @@ static int check_updates(struct unpack_trees_options *o)
}
}
stop_progress(&progress);
+ git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
return errs != 0;
}
@@ -380,8 +363,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 +431,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 +568,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)) {
diff --git a/upload-pack.c b/upload-pack.c
index 19c24db643..495c99f80a 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -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);
@@ -78,7 +78,7 @@ static void show_commit(struct commit *commit)
commit->buffer = NULL;
}
-static void show_object(struct object_array_entry *p)
+static void show_object(struct object_array_entry *p, void *data)
{
/* An object with name "foo\n0000000..." can be used to
* confuse downstream git-pack-objects very badly.
@@ -134,7 +134,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;
@@ -397,12 +397,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) {
@@ -410,7 +409,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 */
@@ -645,7 +644,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"))
diff --git a/wt-status.c b/wt-status.c
index 96ff2f8f56..929b00f592 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -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;
@@ -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);
diff --git a/xdiff-interface.c b/xdiff-interface.c
index d782f06d99..b9b0db8d86 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -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;
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 3e97462bdd..02184d9cde 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -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;
}
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index 05bfa41f10..c4bedf0d1c 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -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);