summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/RelNotes-1.7.1.2.txt19
-rw-r--r--Documentation/RelNotes-1.7.2.txt155
-rw-r--r--Documentation/config.txt114
-rw-r--r--Documentation/diff-options.txt51
-rw-r--r--Documentation/fetch-options.txt1
-rw-r--r--Documentation/git-cat-file.txt10
-rw-r--r--Documentation/git-checkout.txt29
-rw-r--r--Documentation/git-cherry-pick.txt71
-rw-r--r--Documentation/git-commit.txt8
-rw-r--r--Documentation/git-cvsserver.txt50
-rw-r--r--Documentation/git-for-each-ref.txt1
-rw-r--r--Documentation/git-format-patch.txt7
-rw-r--r--Documentation/git-gc.txt10
-rw-r--r--Documentation/git-grep.txt8
-rw-r--r--Documentation/git-instaweb.txt2
-rw-r--r--Documentation/git-log.txt5
-rw-r--r--Documentation/git-notes.txt9
-rw-r--r--Documentation/git-remote.txt21
-rw-r--r--Documentation/git-rev-parse.txt15
-rw-r--r--Documentation/git-revert.txt51
-rw-r--r--Documentation/git-status.txt21
-rw-r--r--Documentation/git-submodule.txt10
-rw-r--r--Documentation/git-svn.txt2
-rw-r--r--Documentation/git.txt20
-rw-r--r--Documentation/gitattributes.txt187
-rw-r--r--Documentation/gitmodules.txt3
-rwxr-xr-xDocumentation/install-webdoc.sh2
-rw-r--r--Documentation/pretty-formats.txt12
-rw-r--r--Documentation/rev-list-options.txt61
-rw-r--r--Documentation/technical/api-run-command.txt5
-rw-r--r--Documentation/technical/api-string-list.txt4
-rw-r--r--Documentation/technical/protocol-capabilities.txt2
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile150
l---------RelNotes2
-rw-r--r--aclocal.m440
-rw-r--r--attr.c2
-rw-r--r--attr.h2
-rw-r--r--block-sha1/sha1.c1
-rw-r--r--builtin.h5
-rw-r--r--builtin/add.c4
-rw-r--r--builtin/apply.c95
-rw-r--r--builtin/blame.c95
-rw-r--r--builtin/branch.c32
-rw-r--r--builtin/cat-file.c42
-rw-r--r--builtin/checkout.c43
-rw-r--r--builtin/clone.c16
-rw-r--r--builtin/commit-tree.c70
-rw-r--r--builtin/commit.c44
-rw-r--r--builtin/config.c6
-rw-r--r--builtin/fast-export.c4
-rw-r--r--builtin/fetch.c24
-rw-r--r--builtin/fmt-merge-msg.c18
-rw-r--r--builtin/for-each-ref.c3
-rw-r--r--builtin/grep.c129
-rw-r--r--builtin/help.c2
-rw-r--r--builtin/log.c87
-rw-r--r--builtin/ls-files.c2
-rw-r--r--builtin/ls-remote.c19
-rw-r--r--builtin/mailinfo.c4
-rw-r--r--builtin/mailsplit.c2
-rw-r--r--builtin/merge.c81
-rw-r--r--builtin/mv.c2
-rw-r--r--builtin/notes.c13
-rw-r--r--builtin/pack-objects.c6
-rw-r--r--builtin/patch-id.c107
-rw-r--r--builtin/read-tree.c5
-rw-r--r--builtin/receive-pack.c130
-rw-r--r--builtin/reflog.c159
-rw-r--r--builtin/remote.c202
-rw-r--r--builtin/rerere.c2
-rw-r--r--builtin/rev-list.c16
-rw-r--r--builtin/rev-parse.c5
-rw-r--r--builtin/revert.c296
-rw-r--r--builtin/shortlog.c4
-rw-r--r--builtin/show-ref.c2
-rw-r--r--cache.h58
-rw-r--r--color.c28
-rw-r--r--color.h1
-rw-r--r--commit.c55
-rw-r--r--commit.h7
-rw-r--r--compat/mingw.h4
-rw-r--r--compat/win32/pthread.c8
-rw-r--r--compat/win32/pthread.h25
-rw-r--r--config.c99
-rw-r--r--config.mak.in8
-rw-r--r--configure.ac76
-rw-r--r--connect.c10
-rwxr-xr-xcontrib/completion/git-completion.bash163
-rwxr-xr-xcontrib/examples/git-fetch.sh4
-rwxr-xr-xcontrib/git-resurrect.sh1
-rw-r--r--convert.c211
-rw-r--r--ctype.c2
-rw-r--r--daemon.c21
-rw-r--r--date.c56
-rw-r--r--diff-lib.c1
-rw-r--r--diff-no-index.c2
-rw-r--r--diff.c681
-rw-r--r--diff.h26
-rw-r--r--diffcore-break.c6
-rw-r--r--diffcore-pickaxe.c3
-rw-r--r--diffcore-rename.c3
-rw-r--r--diffcore.h7
-rw-r--r--dir.c6
-rw-r--r--environment.c3
-rw-r--r--fast-import.c4
-rw-r--r--fsck.c45
-rwxr-xr-xgit-am.sh31
-rw-r--r--git-compat-util.h9
-rwxr-xr-xgit-cvsserver.perl110
-rwxr-xr-xgit-instaweb.sh251
-rwxr-xr-xgit-merge-one-file.sh2
-rwxr-xr-xgit-rebase--interactive.sh19
-rwxr-xr-xgit-rebase.sh17
-rw-r--r--git-remote-testgit.py244
-rwxr-xr-xgit-request-pull.sh13
-rwxr-xr-xgit-stash.sh2
-rwxr-xr-xgit-submodule.sh14
-rwxr-xr-xgit-svn.perl18
-rwxr-xr-xgit-web--browse.sh9
-rw-r--r--git.c11
-rw-r--r--git.spec.in15
-rw-r--r--git_remote_helpers/git/exporter.py53
-rw-r--r--git_remote_helpers/git/importer.py40
-rw-r--r--git_remote_helpers/git/non_local.py69
-rw-r--r--git_remote_helpers/git/repo.py75
-rw-r--r--gitweb/INSTALL33
-rw-r--r--gitweb/Makefile60
-rw-r--r--gitweb/README14
-rwxr-xr-xgitweb/gitweb.perl582
-rw-r--r--gitweb/static/git-favicon.png (renamed from gitweb/git-favicon.png)bin115 -> 115 bytes
-rw-r--r--gitweb/static/git-logo.png (renamed from gitweb/git-logo.png)bin207 -> 207 bytes
-rw-r--r--gitweb/static/gitweb.css (renamed from gitweb/gitweb.css)18
-rw-r--r--gitweb/static/gitweb.js (renamed from gitweb/gitweb.js)0
-rw-r--r--graph.c19
-rw-r--r--grep.c98
-rw-r--r--grep.h10
-rw-r--r--http-backend.c71
-rw-r--r--http-push.c2
-rw-r--r--http-walker.c2
-rw-r--r--imap-send.c2
-rw-r--r--ll-merge.c14
-rw-r--r--log-tree.c114
-rw-r--r--log-tree.h1
-rw-r--r--mailmap.c8
-rw-r--r--merge-recursive.c24
-rw-r--r--merge-recursive.h5
-rw-r--r--notes-cache.c94
-rw-r--r--notes-cache.h20
-rw-r--r--notes.c19
-rw-r--r--notes.h5
-rw-r--r--parse-options.c82
-rw-r--r--parse-options.h5
-rw-r--r--pretty.c171
-rw-r--r--reflog-walk.c4
-rw-r--r--refs.c82
-rw-r--r--refs.h3
-rw-r--r--remote.c11
-rw-r--r--remote.h2
-rw-r--r--rerere.c12
-rw-r--r--resolve-undo.c8
-rw-r--r--revision.c115
-rw-r--r--revision.h6
-rw-r--r--run-command.c75
-rw-r--r--run-command.h8
-rw-r--r--setup.c30
-rw-r--r--sha1_file.c10
-rw-r--r--sha1_name.c43
-rw-r--r--string-list.c18
-rw-r--r--string-list.h16
-rw-r--r--submodule.c13
-rw-r--r--submodule.h3
-rw-r--r--t/Makefile5
-rw-r--r--t/README253
-rwxr-xr-xt/aggregate-results.sh2
-rw-r--r--t/gitweb-lib.sh10
-rw-r--r--t/lib-cvs.sh6
-rw-r--r--t/lib-git-svn.sh15
-rw-r--r--t/lib-httpd.sh11
-rw-r--r--t/lib-pager.sh15
-rw-r--r--t/lib-patch-mode.sh2
-rw-r--r--t/lib-t6000.sh2
-rwxr-xr-xt/t0000-basic.sh2
-rwxr-xr-xt/t0005-signals.sh1
-rwxr-xr-xt/t0006-date.sh1
-rwxr-xr-xt/t0020-crlf.sh52
-rwxr-xr-xt/t0021-conversion.sh16
-rwxr-xr-xt/t0025-crlf-auto.sh155
-rwxr-xr-xt/t0026-eol-config.sh83
-rwxr-xr-xt/t0040-parse-options.sh8
-rwxr-xr-xt/t1001-read-tree-m-2way.sh16
-rwxr-xr-xt/t1020-subdirectory.sh12
-rwxr-xr-xt/t1300-repo-config.sh8
-rwxr-xr-xt/t1304-default-acl.sh9
-rwxr-xr-xt/t1450-fsck.sh36
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh26
-rwxr-xr-xt/t1509-root-worktree.sh6
-rwxr-xr-xt/t2007-checkout-symlink.sh2
-rwxr-xr-xt/t2017-checkout-orphan.sh126
-rwxr-xr-xt/t2102-update-index-symlinks.sh2
-rwxr-xr-xt/t3030-merge-recursive.sh29
-rwxr-xr-xt/t3200-branch.sh28
-rwxr-xr-xt/t3210-pack-refs.sh8
-rwxr-xr-xt/t3300-funny-names.sh2
-rwxr-xr-xt/t3302-notes-index-expensive.sh2
-rwxr-xr-xt/t3306-notes-prune.sh48
-rwxr-xr-xt/t3400-rebase.sh13
-rwxr-xr-xt/t3404-rebase-interactive.sh18
-rwxr-xr-xt/t3500-cherry.sh5
-rwxr-xr-xt/t3501-revert-cherry-pick.sh18
-rwxr-xr-xt/t3508-cherry-pick-many-commits.sh105
-rwxr-xr-xt/t3600-rm.sh2
-rwxr-xr-xt/t3700-add.sh12
-rwxr-xr-xt/t3701-add-interactive.sh4
-rwxr-xr-xt/t3902-quoted.sh2
-rwxr-xr-xt/t3903-stash.sh2
-rwxr-xr-xt/t4002-diff-basic.sh2
-rwxr-xr-xt/t4004-diff-rename-symlink.sh2
-rwxr-xr-xt/t4011-diff-symlink.sh7
-rw-r--r--t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^3
-rwxr-xr-xt/t4014-format-patch.sh52
-rwxr-xr-xt/t4015-diff-whitespace.sh42
-rwxr-xr-xt/t4016-diff-quote.sh2
-rwxr-xr-xt/t4023-diff-rename-typechange.sh2
-rwxr-xr-xt/t4027-diff-submodule.sh18
-rwxr-xr-xt/t4034-diff-words.sh122
-rwxr-xr-xt/t4041-diff-submodule-option.sh (renamed from t/t4041-diff-submodule.sh)81
-rwxr-xr-xt/t4042-diff-textconv-caching.sh109
-rwxr-xr-xt/t4044-diff-index-unique-abbrev.sh35
-rwxr-xr-xt/t4114-apply-typechange.sh2
-rwxr-xr-xt/t4115-apply-symlink.sh2
-rwxr-xr-xt/t4122-apply-symlink-inside.sh2
-rwxr-xr-xt/t4124-apply-ws-rule.sh63
-rwxr-xr-xt/t4127-apply-same-fn.sh6
-rwxr-xr-xt/t4202-log.sh49
-rwxr-xr-xt/t4204-patch-id.sh28
-rwxr-xr-xt/t4205-log-pretty-formats.sh74
-rwxr-xr-xt/t4206-log-follow-harder-copies.sh56
-rwxr-xr-xt/t4207-log-decoration-colors.sh66
-rwxr-xr-xt/t5150-request-pull.sh228
-rwxr-xr-xt/t5300-pack-object.sh6
-rwxr-xr-xt/t5302-pack-index.sh2
-rwxr-xr-xt/t5503-tagfollow.sh2
-rwxr-xr-xt/t5505-remote.sh151
-rwxr-xr-xt/t5510-fetch.sh2
-rwxr-xr-xt/t5512-ls-remote.sh74
-rwxr-xr-xt/t5516-fetch-push.sh118
-rwxr-xr-xt/t5520-pull.sh2
-rwxr-xr-xt/t5522-pull-symlink.sh2
-rwxr-xr-xt/t5530-upload-pack-error.sh18
-rwxr-xr-xt/t5540-http-push.sh2
-rwxr-xr-xt/t5541-http-push.sh2
-rwxr-xr-xt/t5550-http-fetch.sh2
-rwxr-xr-xt/t5551-http-fetch.sh2
-rwxr-xr-xt/t5561-http-backend.sh2
-rwxr-xr-xt/t5601-clone.sh12
-rwxr-xr-xt/t5700-clone-reference.sh8
-rwxr-xr-xt/t5705-clone-2gb.sh2
-rwxr-xr-xt/t5800-remote-helpers.sh82
-rwxr-xr-xt/t6001-rev-list-graft.sh2
-rwxr-xr-xt/t6006-rev-list-format.sh19
-rwxr-xr-xt/t6007-rev-list-cherry-pick-file.sh29
-rwxr-xr-xt/t6018-rev-list-glob.sh17
-rwxr-xr-xt/t6019-rev-list-ancestry-path.sh73
-rwxr-xr-xt/t6022-merge-rename.sh4
-rwxr-xr-xt/t6035-merge-dir-to-symlink.sh2
-rwxr-xr-xt/t6300-for-each-ref.sh9
-rwxr-xr-xt/t7004-tag.sh2
-rwxr-xr-xt/t7005-editor.sh8
-rwxr-xr-xt/t7006-pager.sh26
-rwxr-xr-xt/t7008-grep-binary.sh102
-rwxr-xr-xt/t7400-submodule-basic.sh353
-rwxr-xr-xt/t7405-submodule-merge.sh2
-rwxr-xr-xt/t7407-submodule-foreach.sh10
-rwxr-xr-xt/t7500-commit.sh22
-rwxr-xr-xt/t7508-status.sh174
-rwxr-xr-xt/t7600-merge.sh3
-rwxr-xr-xt/t7700-repack.sh9
-rwxr-xr-xt/t7701-repack-unpack-unreachable.sh13
-rwxr-xr-xt/t7800-difftool.sh2
-rwxr-xr-xt/t7810-grep.sh (renamed from t/t7002-grep.sh)16
-rwxr-xr-xt/t7811-grep-open.sh153
-rwxr-xr-xt/t8006-blame-textconv.sh80
-rwxr-xr-xt/t8007-cat-file-textconv.sh70
-rwxr-xr-xt/t9001-send-email.sh4
-rwxr-xr-xt/t9100-git-svn-basic.sh2
-rwxr-xr-xt/t9118-git-svn-funky-branch-names.sh53
-rwxr-xr-xt/t9119-git-svn-info.sh2
-rwxr-xr-xt/t9129-git-svn-i18n-commitencoding.sh2
-rwxr-xr-xt/t9143-git-svn-gc.sh2
-rwxr-xr-xt/t9200-git-cvsexportcommit.sh30
-rwxr-xr-xt/t9400-git-cvsserver-server.sh24
-rwxr-xr-xt/t9401-git-cvsserver-crlf.sh6
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh29
-rwxr-xr-xt/t9600-cvsimport.sh2
-rwxr-xr-xt/t9700-perl-git.sh7
-rwxr-xr-xt/t9700/test.pl12
-rw-r--r--t/test-lib.sh105
-rw-r--r--test-date.c4
-rw-r--r--trace.c6
-rw-r--r--transport-helper.c137
-rw-r--r--transport.c51
-rw-r--r--tree-diff.c2
-rw-r--r--unpack-trees.c4
-rw-r--r--url.c126
-rw-r--r--url.h10
-rw-r--r--usage.c10
-rw-r--r--userdiff.c9
-rw-r--r--userdiff.h4
-rw-r--r--wrapper.c6
-rw-r--r--ws.c66
-rw-r--r--wt-status.c172
-rw-r--r--wt-status.h10
314 files changed, 10045 insertions, 2276 deletions
diff --git a/.gitignore b/.gitignore
index dbf1b90..14e2b6b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -112,6 +112,7 @@
/git-remote-https
/git-remote-ftp
/git-remote-ftps
+/git-remote-testgit
/git-repack
/git-replace
/git-repo-config
diff --git a/Documentation/RelNotes-1.7.1.2.txt b/Documentation/RelNotes-1.7.1.2.txt
new file mode 100644
index 0000000..46b6a96
--- /dev/null
+++ b/Documentation/RelNotes-1.7.1.2.txt
@@ -0,0 +1,19 @@
+Git v1.7.1.2 Release Notes
+==========================
+
+Fixes since v1.7.1.1
+--------------------
+
+ * "git commit" did not honor GIT_REFLOG_ACTION environment variable, resulting
+ reflog messages for cherry-pick and revert actions to be recorded as "commit".
+
+ * "git clone/fetch/pull" issued an incorrect error message when a ref and
+ a symref that points to the ref were updated at the same time. This
+ obviously would update them to the same value, and should not result in
+ an error condition.
+
+ * "git diff" inside a tree with many pathnames that have certain
+ characters has become very slow in 1.7.0 by mistake.
+
+ * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
+ when --keep-dashdash was in effect.
diff --git a/Documentation/RelNotes-1.7.2.txt b/Documentation/RelNotes-1.7.2.txt
new file mode 100644
index 0000000..f24b387
--- /dev/null
+++ b/Documentation/RelNotes-1.7.2.txt
@@ -0,0 +1,155 @@
+Git v1.7.2 Release Notes (draft)
+================================
+
+Updates since v1.7.1
+--------------------
+
+ * core.eol configuration and text/eol attributes are the new way to control
+ the end of line conventions for files in the working tree.
+
+ * core.autocrlf has been made safer - it will now only handle line
+ endings for new files and files that are LF-only in the
+ repository. To normalize content that has been checked in with
+ CRLF, use the new eol/text attributes.
+
+ * The whitespace rules used in "git apply --whitespace" and "git diff"
+ gained a new member in the family (tab-in-indent) to help projects with
+ policy to indent only with spaces.
+
+ * When working from a subdirectory, by default, git does not look for its
+ metadirectory ".git" across filesystems, primarily to help people who
+ have invocations of git in their custom PS1 prompts, as being outside
+ of a git repository would look for ".git" all the way up to the root
+ directory, and NFS mounts are often slow. DISCOVERY_ACROSS_FILESYSTEM
+ environment variable can be used to tell git not to stop at a
+ filesystem boundary.
+
+ * Usage help messages generated by parse-options library (i.e. most
+ of the Porcelain commands) are sent to the standard output now.
+
+ * ':/<string>' notation to look for a commit now takes regular expression
+ and it is not anchored at the beginning of the commit log message
+ anymore (this is a backward incompatible change).
+
+ * "git" wrapper learned "-c name=value" option to override configuration
+ variable from the command line.
+
+ * Improved portability for various platforms including older SunOS,
+ HP-UX 10/11, AIX, Tru64, etc. and platforms with Python 2.4.
+
+ * The message from "git am -3" has been improved when conflict
+ resolution ended up making the patch a no-op.
+
+ * "git blame" applies the textconv filter to the contents it works
+ on, when available.
+
+ * "git checkout --orphan newbranch" is similar to "-b newbranch" but
+ prepares to create a root commit that is not connected to any existing
+ commit.
+
+ * "git cherry-pick" learned to pick a range of commits
+ (e.g. "cherry-pick A..B" and "cherry-pick --stdin"), so did "git
+ revert"; these do not support the nicer sequencing control "rebase
+ [-i]" has, though.
+
+ * "git cherry-pick" and "git revert" learned --strategy option to specify
+ the merge strategy to be used when performing three-way merges.
+
+ * "git cvsserver" can be told to use pserver; its password file can be
+ stored outside the repository.
+
+ * The output from the textconv filter used by "git diff" can be cached to
+ speed up their reuse.
+
+ * "git diff --word-diff=<mode>" extends the existing "--color-words"
+ option, making it more useful in color-challenged environments.
+
+ * The regexp to detect function headers used by "git diff" for PHP has
+ been enhanced for visibility modifiers (public, protected, etc.) to
+ better support PHP5.
+
+ * "diff.noprefix" configuration variable can be used to implicitly
+ ask for "diff --no-prefix" behaviour.
+
+ * "git for-each-ref" learned "%(objectname:short)" that gives the object
+ name abbreviated.
+
+ * "git format-patch" learned --signature option and format.signature
+ configuration variable to customize the e-mail signature used in the
+ output.
+
+ * Various options to "git grep" (e.g. --count, --name-only) work better
+ with binary files.
+
+ * "git grep" learned "-Ovi" to open the files with hits in your editor.
+
+ * "git help -w" learned "chrome" and "chromium" browsers.
+
+ * "git log --decorate" shows commit decorations in various colours.
+
+ * "git log --follow <path>" follows across copies (it used to only follow
+ renames). This may make the processing more expensive.
+
+ * "git log --pretty=format:<template>" specifier learned "% <something>"
+ magic that inserts a space only when %<something> expands to a
+ non-empty string; this is similar to "%+<something>" magic, but is
+ useful in a context to generate a single line output.
+
+ * "git notes prune" learned "-n" (dry-run) and "-v" options, similar to
+ what "git prune" has.
+
+ * "git patch-id" can be fed a mbox without getting confused by the
+ signature line in the format-patch output.
+
+ * "git remote" learned "set-branches" subcommand.
+
+ * "git rev-list A..B" learned --ancestry-path option to further limit
+ the result to the commits that are on the ancestry chain between A and
+ B (i.e. commits that are not descendants of A are excluded).
+
+ * "git show -5" is equivalent to "git show --do-walk 5"; this is similar
+ to the update to make "git show master..next" walk the history,
+ introduced in 1.6.4.
+
+ * "git status [-s] --ignored" can be used to list ignored paths.
+
+ * "git status -s -b" shows the current branch in the output.
+
+ * "git status" learned "--ignore-submodules" option.
+
+ * Various "gitweb" enhancements and clean-ups, including syntax
+ highlighting, "plackup" support for instaweb, .fcgi suffix to run
+ it as FastCGI script, etc.
+
+ * The test harness has been updated to produce TAP-friendly output.
+
+
+Fixes since v1.7.1
+------------------
+
+All of the fixes in v1.7.1.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * We didn't URL decode "file:///path/to/repo" correctly when path/to/repo
+ had percent-encoded characters (638794c, 9d2e942, ce83eda, 3c73a1d).
+
+ * "git clone" did not configure remote.origin.url correctly for bare
+ clones (df61c889).
+
+ * "git diff --graph" works better with "--color-words" and other options
+ (81fa024..4297c0a).
+
+ * "git diff" could show ambiguous abbreviation of blob object names on
+ its "index" line (3e5a188).
+
+ * "git reset --hard" started from a wrong directory and a working tree in
+ a nonstandard location is in use got confused (560fb6a1).
+
+ * "git read-tree -m A B" used to switch to branch B while retaining
+ local changes added an incorrect cache-tree information (b1f47514).
+
+--
+exec >/var/tmp/1
+O=v1.7.2-rc2-17-gc9a9766
+echo O=$(git describe HEAD)
+git shortlog --no-merges HEAD ^maint ^$O
diff --git a/Documentation/config.txt b/Documentation/config.txt
index ae174c9..e75434b 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -196,20 +196,17 @@ core.quotepath::
quoted without `-z` regardless of the setting of this
variable.
-core.autocrlf::
- If true, makes git convert `CRLF` at the end of lines in text files to
- `LF` when reading from the work tree, and convert in reverse when
- writing to the work tree. The variable can be set to
- 'input', in which case the conversion happens only while
- reading from the work tree but files are written out to the work
- tree with `LF` at the end of lines. A file is considered
- "text" (i.e. be subjected to the autocrlf mechanism) based on
- the file's `crlf` attribute, or if `crlf` is unspecified,
- based on the file's contents. See linkgit:gitattributes[5].
+core.eol::
+ Sets the line ending type to use in the working directory for
+ files that have the `text` property set. Alternatives are
+ 'lf', 'crlf' and 'native', which uses the platform's native
+ line ending. The default value is `native`. See
+ linkgit:gitattributes[5] for more information on end-of-line
+ conversion.
core.safecrlf::
- If true, makes git check if converting `CRLF` as controlled by
- `core.autocrlf` is reversible. Git will verify if a command
+ If true, makes git check if converting `CRLF` is reversible when
+ end-of-line conversion is active. Git will verify if a command
modifies a file in the work tree either directly or indirectly.
For example, committing a file followed by checking out the
same file should yield the original file in the work tree. If
@@ -219,7 +216,7 @@ core.safecrlf::
irreversible conversion but continue the operation.
+
CRLF conversion bears a slight chance of corrupting data.
-autocrlf=true will convert CRLF to LF during commit and LF to
+When it is enabled, git will convert CRLF to LF during commit and LF to
CRLF during checkout. A file that contains a mixture of LF and
CRLF before the commit cannot be recreated by git. For text
files this is the right thing to do: it corrects line endings
@@ -243,15 +240,25 @@ converting CRLFs corrupts data.
+
Note, this safety check does not mean that a checkout will generate a
file identical to the original file for a different setting of
-`core.autocrlf`, but only for the current one. For example, a text
-file with `LF` would be accepted with `core.autocrlf=input` and could
-later be checked out with `core.autocrlf=true`, in which case the
+`core.eol` and `core.autocrlf`, but only for the current one. For
+example, a text file with `LF` would be accepted with `core.eol=lf`
+and could later be checked out with `core.eol=crlf`, in which case the
resulting file would contain `CRLF`, although the original file
contained `LF`. However, in both work trees the line endings would be
consistent, that is either all `LF` or all `CRLF`, but never mixed. A
file with mixed line endings would be reported by the `core.safecrlf`
mechanism.
+core.autocrlf::
+ Setting this variable to "true" is almost the same as setting
+ the `text` attribute to "auto" on all files except that text
+ files are not guaranteed to be normalized: files that contain
+ `CRLF` in the repository will not be touched. Use this
+ setting if you want to have `CRLF` line endings in your
+ working directory even though the repository does not have
+ normalized line endings. This variable can be set to 'input',
+ in which case no output conversion is performed.
+
core.symlinks::
If false, symbolic links are checked out as small plain files that
contain the link text. linkgit:git-update-index[1] and
@@ -481,6 +488,8 @@ core.whitespace::
error (enabled by default).
* `indent-with-non-tab` treats a line that is indented with 8 or more
space characters as an error (not enabled by default).
+* `tab-in-indent` treats a tab character in the initial indent part of
+ the line as an error (not enabled by default).
* `blank-at-eof` treats blank lines added at the end of file as an error
(enabled by default).
* `trailing-space` is a short-hand to cover both `blank-at-eol` and
@@ -681,6 +690,11 @@ color.diff.<slot>::
(highlighting whitespace errors). The values of these variables may be
specified as in color.branch.<slot>.
+color.decorate.<slot>::
+ Use customized color for 'git log --decorate' output. `<slot>` is one
+ of `branch`, `remoteBranch`, `tag`, `stash` or `HEAD` for local
+ branches, remote tracking branches, tags, stash and HEAD, respectively.
+
color.grep::
When set to `always`, always highlight matches. When `false` (or
`never`), never. When set to `true` or `auto`, use color only
@@ -790,6 +804,8 @@ diff.mnemonicprefix::
standard "a/" and "b/" depending on what is being compared. When
this configuration is in effect, reverse diff output also swaps
the order of the prefixes:
+diff.noprefix::
+ If set, 'git diff' does not show any source or destination prefix.
`git diff`;;
compares the (i)ndex and the (w)ork tree;
`git diff HEAD`;;
@@ -870,14 +886,22 @@ format.headers::
Additional email headers to include in a patch to be submitted
by mail. See linkgit:git-format-patch[1].
+format.to::
format.cc::
- Additional "Cc:" headers to include in a patch to be submitted
- by mail. See the --cc option in linkgit:git-format-patch[1].
+ Additional recipients to include in a patch to be submitted
+ by mail. See the --to and --cc options in
+ linkgit:git-format-patch[1].
format.subjectprefix::
The default for format-patch is to output files with the '[PATCH]'
subject prefix. Use this variable to change that prefix.
+format.signature::
+ The default for format-patch is to output a signature containing
+ the git version number. Use this variable to change that default.
+ Set this variable to the empty string ("") to suppress
+ signature generation.
+
format.suffix::
The default for format-patch is to output files with the suffix
`.patch`. Use this variable to change that suffix (make sure to
@@ -938,13 +962,19 @@ gc.pruneexpire::
unreachable objects immediately.
gc.reflogexpire::
+gc.<pattern>.reflogexpire::
'git reflog expire' removes reflog entries older than
- this time; defaults to 90 days.
+ this time; defaults to 90 days. With "<pattern>" (e.g.
+ "refs/stash") in the middle the setting applies only to
+ the refs that match the <pattern>.
gc.reflogexpireunreachable::
+gc.<ref>.reflogexpireunreachable::
'git reflog expire' removes reflog entries older than
this time and are not reachable from the current tip;
- defaults to 30 days.
+ defaults to 30 days. With "<pattern>" (e.g. "refs/stash")
+ in the middle, the setting applies only to the refs that
+ match the <pattern>.
gc.rerereresolved::
Records of conflicted merge you resolved earlier are
@@ -969,13 +999,15 @@ gitcvs.logfile::
various stuff. See linkgit:git-cvsserver[1].
gitcvs.usecrlfattr::
- If true, the server will look up the `crlf` attribute for
- files to determine the '-k' modes to use. If `crlf` is set,
- the '-k' mode will be left blank, so cvs clients will
- treat it as text. If `crlf` is explicitly unset, the file
+ If true, the server will look up the end-of-line conversion
+ attributes for files to determine the '-k' modes to use. If
+ the attributes force git to treat a file as text,
+ the '-k' mode will be left blank so cvs clients will
+ treat it as text. If they suppress text conversion, the file
will be set with '-kb' mode, which suppresses any newline munging
- the client might otherwise do. If `crlf` is not specified,
- then 'gitcvs.allbinary' is used. See linkgit:gitattributes[5].
+ the client might otherwise do. If the attributes do not allow
+ the file type to be determined, then 'gitcvs.allbinary' is
+ used. See linkgit:gitattributes[5].
gitcvs.allbinary::
This is used if 'gitcvs.usecrlfattr' does not resolve
@@ -1262,6 +1294,13 @@ log.date::
following alternatives: {relative,local,default,iso,rfc,short}.
See linkgit:git-log[1].
+log.decorate::
+ Print out the ref names of any commits that are shown by the log
+ command. If 'short' is specified, the ref name prefixes 'refs/heads/',
+ 'refs/tags/' and 'refs/remotes/' will not be printed. If 'full' is
+ specified, the full ref name (including prefix) will be printed.
+ This is the same as the log commands '--decorate' option.
+
log.showroot::
If true, the initial commit will be shown as a big creation event.
This is equivalent to a diff against an empty tree.
@@ -1460,6 +1499,16 @@ pager.<cmd>::
it takes precedence over this option. To disable pagination for
all commands, set `core.pager` or `GIT_PAGER` to `cat`.
+pretty.<name>::
+ Alias for a --pretty= format string, as specified in
+ linkgit:git-log[1]. Any aliases defined here can be used just
+ as the built-in pretty formats could. For example,
+ running `git config pretty.changelog "format:{asterisk} %H %s"`
+ would cause the invocation `git log --pretty=changelog`
+ to be equivalent to running `git log "--pretty=format:{asterisk} %H %s"`.
+ Note that an alias with the same name as a built-in format
+ will be silently ignored.
+
pull.octopus::
The default merge strategy to use when pulling multiple branches
at once.
@@ -1572,7 +1621,9 @@ remote.<name>.uploadpack::
remote.<name>.tagopt::
Setting this value to \--no-tags disables automatic tag following when
- fetching from remote <name>
+ fetching from remote <name>. Setting it to \--tags will fetch every
+ tag from remote <name>, even if they are not reachable from remote
+ branch heads.
remote.<name>.vcs::
Setting this to a value <vcs> will cause git to interact with
@@ -1683,6 +1734,15 @@ status.submodulesummary::
summary of commits for modified submodules will be shown (see
--summary-limit option of linkgit:git-submodule[1]).
+submodule.<name>.path::
+submodule.<name>.url::
+submodule.<name>.update::
+ The path within this project, URL, and the updating strategy
+ for a submodule. These variables are initially populated
+ by 'git submodule init'; edit them to override the
+ URL and other values found in the `.gitmodules` file. See
+ linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
+
tar.umask::
This variable can be used to restrict the permission bits of
tar archive entries. The default is 0002, which turns off the
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 3070ddd..2371262 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -21,6 +21,7 @@ endif::git-format-patch[]
ifndef::git-format-patch[]
-p::
-u::
+--patch::
Generate patch (see section on generating patches).
{git-diff? This is the default.}
endif::git-format-patch[]
@@ -126,11 +127,39 @@ any of those replacements occurred.
gives the default to color output.
Same as `--color=never`.
---color-words[=<regex>]::
- Show colored word diff, i.e., color words which have changed.
- By default, words are separated by whitespace.
+--word-diff[=<mode>]::
+ Show a word diff, using the <mode> to delimit changed words.
+ By default, words are delimited by whitespace; see
+ `--word-diff-regex` below. The <mode> defaults to 'plain', and
+ must be one of:
++
+--
+color::
+ Highlight changed words using only colors. Implies `--color`.
+plain::
+ Show words as `[-removed-]` and `{+added+}`. Makes no
+ attempts to escape the delimiters if they appear in the input,
+ so the output may be ambiguous.
+porcelain::
+ Use a special line-based format intended for script
+ consumption. Added/removed/unchanged runs are printed in the
+ usual unified diff format, starting with a `+`/`-`/` `
+ character at the beginning of the line and extending to the
+ end of the line. Newlines in the input are represented by a
+ tilde `~` on a line of its own.
+none::
+ Disable word diff again.
+--
++
+Note that despite the name of the first mode, color is used to
+highlight the changed parts in all modes if enabled.
+
+--word-diff-regex=<regex>::
+ Use <regex> to decide what a word is, instead of considering
+ runs of non-whitespace to be a word. Also implies
+ `--word-diff` unless it was already enabled.
+
-When a <regex> is specified, every non-overlapping match of the
+Every non-overlapping match of the
<regex> is considered a word. Anything between these matches is
considered whitespace and ignored(!) for the purposes of finding
differences. You may want to append `|[^[:space:]]` to your regular
@@ -142,6 +171,10 @@ The regex can also be set via a diff driver or configuration option, see
linkgit:gitattributes[1] or linkgit:git-config[1]. Giving it explicitly
overrides any diff driver or configuration setting. Diff drivers
override configuration settings.
+
+--color-words[=<regex>]::
+ Equivalent to `--word-diff=color` plus (if a regex was
+ specified) `--word-diff-regex=<regex>`.
endif::git-format-patch[]
--no-renames::
@@ -295,8 +328,14 @@ endif::git-format-patch[]
--no-ext-diff::
Disallow external diff drivers.
---ignore-submodules::
- Ignore changes to submodules in the diff generation.
+--ignore-submodules[=<when>]::
+ Ignore changes to submodules in the diff generation. <when> can be
+ either "untracked", "dirty" or "all", which is the default. When
+ "untracked" is used submodules are not considered dirty when they only
+ contain untracked content (but they are still scanned for modified
+ content). Using "dirty" ignores all changes to the work tree of submodules,
+ only changes to the commits stored in the superproject are shown (this was
+ the behavior until 1.7.0). Using "all" hides all changes to submodules.
--src-prefix=<prefix>::
Show the given source prefix instead of "a/".
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 044ec88..9333c42 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -34,6 +34,7 @@ ifndef::git-pull[]
Allow several <repository> and <group> arguments to be
specified. No <refspec>s may be specified.
+-p::
--prune::
After fetching, remove any remote tracking branches which
no longer exist on the remote.
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 58c8d65..9ebbe94 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -9,14 +9,15 @@ git-cat-file - Provide content or type and size information for repository objec
SYNOPSIS
--------
[verse]
-'git cat-file' (-t | -s | -e | -p | <type>) <object>
+'git cat-file' (-t | -s | -e | -p | <type> | --textconv ) <object>
'git cat-file' (--batch | --batch-check) < <list-of-objects>
DESCRIPTION
-----------
In its first form, the command provides the content or the type of an object in
the repository. The type is required unless '-t' or '-p' is used to find the
-object type, or '-s' is used to find the object size.
+object type, or '-s' is used to find the object size, or '--textconv' is used
+(which implies type "blob").
In the second form, a list of objects (separated by linefeeds) is provided on
stdin, and the SHA1, type, and size of each object is printed on stdout.
@@ -51,6 +52,11 @@ OPTIONS
or to ask for a "blob" with <object> being a tag object that
points at it.
+--textconv::
+ Show the content as transformed by a textconv filter. In this case,
+ <object> has be of the form <treeish>:<path>, or :<path> in order
+ to apply the filter to the content recorded in the index at <path>.
+
--batch::
Print the SHA1, type, size, and contents of each object provided on
stdin. May not be combined with any other options or arguments.
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 7146288..1bacd2e 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -9,7 +9,7 @@ SYNOPSIS
--------
[verse]
'git checkout' [-q] [-f] [-m] [<branch>]
-'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
+'git checkout' [-q] [-f] [-m] [[-b|--orphan] <new_branch>] [<start_point>]
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
'git checkout' --patch [<tree-ish>] [--] [<paths>...]
@@ -98,6 +98,31 @@ explicitly give a name with '-b' in such a case.
Create the new branch's reflog; see linkgit:git-branch[1] for
details.
+--orphan::
+ Create a new 'orphan' branch, named <new_branch>, started from
+ <start_point> and switch to it. The first commit made on this
+ new branch will have no parents and it will be the root of a new
+ history totally disconnected from all the other branches and
+ commits.
++
+The index and the working tree are adjusted as if you had previously run
+"git checkout <start_point>". This allows you to start a new history
+that records a set of paths similar to <start_point> by easily running
+"git commit -a" to make the root commit.
++
+This can be useful when you want to publish the tree from a commit
+without exposing its full history. You might want to do this to publish
+an open source branch of a project whose current tree is "clean", but
+whose full history contains proprietary or otherwise encumbered bits of
+code.
++
+If you want to start a disconnected history that records a set of paths
+that is totally different from the one of <start_point>, then you should
+clear the index and the working tree right after creating the orphan
+branch by running "git rm -rf ." from the top level of the working tree.
+Afterwards you will be ready to prepare your new files, repopulating the
+working tree, by copying them from elsewhere, extracting a tarball, etc.
+
-m::
--merge::
When switching branches,
@@ -238,7 +263,7 @@ the above checkout would fail like this:
+
------------
$ git checkout mytopic
-fatal: Entry 'frotz' not uptodate. Cannot merge.
+error: You have local changes to 'frotz'; not switching branches.
------------
+
You can give the `-m` flag to the command, which would try a
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index d71607a..ca485db 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -3,24 +3,28 @@ git-cherry-pick(1)
NAME
----
-git-cherry-pick - Apply the change introduced by an existing commit
+git-cherry-pick - Apply the changes introduced by some existing commits
SYNOPSIS
--------
-'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>
+'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...
DESCRIPTION
-----------
-Given one existing commit, apply the change the patch introduces, and record a
-new commit that records it. This requires your working tree to be clean (no
-modifications from the HEAD commit).
+
+Given one or more existing commits, apply the change each one
+introduces, recording a new commit for each. This requires your
+working tree to be clean (no modifications from the HEAD commit).
OPTIONS
-------
-<commit>::
- Commit to cherry-pick.
+<commit>...::
+ Commits to cherry-pick.
For a more complete list of ways to spell commits, see the
"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+ Sets of commits can be passed but no traversal is done by
+ default, as if the '--no-walk' option was specified, see
+ linkgit:git-rev-list[1].
-e::
--edit::
@@ -55,10 +59,10 @@ OPTIONS
-n::
--no-commit::
- Usually the command automatically creates a commit.
- This flag applies the change necessary to cherry-pick
- the named commit to your working tree and the index,
- but does not make the commit. In addition, when this
+ Usually the command automatically creates a sequence of commits.
+ This flag applies the changes necessary to cherry-pick
+ each named commit to your working tree and the index,
+ without making any commit. In addition, when this
option is used, your index does not have to match the
HEAD commit. The cherry-pick is done against the
beginning state of your index.
@@ -75,6 +79,47 @@ effect to your index in a row.
cherry-pick'ed commit, then a fast forward to this commit will
be performed.
+EXAMPLES
+--------
+git cherry-pick master::
+
+ Apply the change introduced by the commit at the tip of the
+ master branch and create a new commit with this change.
+
+git cherry-pick ..master::
+git cherry-pick ^HEAD master::
+
+ Apply the changes introduced by all commits that are ancestors
+ of master but not of HEAD to produce new commits.
+
+git cherry-pick master\~4 master~2::
+
+ Apply the changes introduced by the fifth and third last
+ commits pointed to by master and create 2 new commits with
+ these changes.
+
+git cherry-pick -n master~1 next::
+
+ Apply to the working tree and the index the changes introduced
+ by the second last commit pointed to by master and by the last
+ commit pointed to by next, but do not create any commit with
+ these changes.
+
+git cherry-pick --ff ..next::
+
+ If history is linear and HEAD is an ancestor of next, update
+ the working tree and advance the HEAD pointer to match next.
+ Otherwise, apply the changes introduced by those commits that
+ are in next but not HEAD to the current branch, creating a new
+ commit for each new change.
+
+git rev-list --reverse master \-- README | git cherry-pick -n --stdin::
+
+ Apply the changes introduced by all commits on the master
+ branch that touched README to the working tree and index,
+ so the result can be inspected and made into a single new
+ commit if suitable.
+
Author
------
Written by Junio C Hamano <gitster@pobox.com>
@@ -83,6 +128,10 @@ Documentation
--------------
Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+SEE ALSO
+--------
+linkgit:git-revert[1]
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 69eb86e..c28603e 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -10,7 +10,7 @@ SYNOPSIS
[verse]
'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run]
[(-c | -C) <commit>] [-F <file> | -m <msg>] [--reset-author]
- [--allow-empty] [--no-verify] [-e] [--author=<author>]
+ [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
[--date=<date>] [--cleanup=<mode>] [--status | --no-status] [--]
[[-i | -o ]<file>...]
@@ -132,6 +132,12 @@ OPTIONS
from making such a commit. This option bypasses the safety, and
is primarily for use by foreign scm interface scripts.
+--allow-empty-message::
+ Like --allow-empty this command is primarily for use by foreign
+ scm interface scripts. It allows you to create a commit with an
+ empty commit message without using plumbing commands like
+ linkgit:git-commit-tree[1].
+
--cleanup=<mode>::
This option sets how the commit message is cleaned up.
The '<mode>' can be one of 'verbatim', 'whitespace', 'strip',
diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt
index dbb053e..7004dd2 100644
--- a/Documentation/git-cvsserver.txt
+++ b/Documentation/git-cvsserver.txt
@@ -72,9 +72,6 @@ plugin. Most functionality works fine with both of these clients.
LIMITATIONS
-----------
-Currently cvsserver works over SSH connections for read/write clients, and
-over pserver for anonymous CVS access.
-
CVS clients cannot tag, branch or perform GIT merges.
'git-cvsserver' maps GIT branches to CVS modules. This is very different
@@ -84,7 +81,7 @@ one or more directories.
INSTALLATION
------------
-1. If you are going to offer anonymous CVS access via pserver, add a line in
+1. If you are going to offer CVS access via pserver, add a line in
/etc/inetd.conf like
+
--
@@ -101,6 +98,38 @@ looks like
cvspserver stream tcp nowait nobody /usr/bin/git-cvsserver git-cvsserver pserver
------
+
+Only anonymous access is provided by pserve by default. To commit you
+will have to create pserver accounts, simply add a gitcvs.authdb
+setting in the config file of the repositories you want the cvsserver
+to allow writes to, for example:
+
+------
+
+ [gitcvs]
+ authdb = /etc/cvsserver/passwd
+
+------
+The format of these files is username followed by the crypted password,
+for example:
+
+------
+ myuser:$1Oyx5r9mdGZ2
+ myuser:$1$BA)@$vbnMJMDym7tA32AamXrm./
+------
+You can use the 'htpasswd' facility that comes with Apache to make these
+files, but Apache's MD5 crypt method differs from the one used by most C
+library's crypt() function, so don't use the -m option.
+
+Alternatively you can produce the password with perl's crypt() operator:
+-----
+ perl -e 'my ($user, $pass) = @ARGV; printf "%s:%s\n", $user, crypt($user, $pass)' $USER password
+-----
+
+Then provide your password via the pserver method, for example:
+------
+ cvs -d:pserver:someuser:somepassword <at> server/path/repo.git co <HEAD_name>
+------
No special setup is needed for SSH access, other than having GIT tools
in the PATH. If you have clients that do not accept the CVS_SERVER
environment variable, you can rename 'git-cvsserver' to `cvs`.
@@ -340,16 +369,13 @@ By default the server leaves the '-k' mode blank for all files,
which causes the cvs client to treat them as a text files, subject
to crlf conversion on some platforms.
-You can make the server use `crlf` attributes to set the '-k' modes
-for files by setting the `gitcvs.usecrlfattr` config variable.
-In this case, if `crlf` is explicitly unset ('-crlf'), then the
-server will set '-kb' mode for binary files. If `crlf` is set,
-then the '-k' mode will explicitly be left blank. See
-also linkgit:gitattributes[5] for more information about the `crlf`
-attribute.
+You can make the server use the end-of-line conversion attributes to
+set the '-k' modes for files by setting the `gitcvs.usecrlfattr`
+config variable. See linkgit:gitattributes[5] for more information
+about end-of-line conversion.
Alternatively, if `gitcvs.usecrlfattr` config is not enabled
-or if the `crlf` attribute is unspecified for a filename, then
+or the attributes do not allow automatic detection for a filename, then
the server uses the `gitcvs.allbinary` config for the default setting.
If `gitcvs.allbinary` is set, then file not otherwise
specified will default to '-kb' mode. Otherwise the '-k' mode
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 7e83288..390d85c 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -86,6 +86,7 @@ objectsize::
objectname::
The object name (aka SHA-1).
+ For a non-ambiguous abbreviation of the object name append `:short`.
upstream::
The name of a local ref which can be considered ``upstream''
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 835fb71..c8c81e8 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -13,6 +13,7 @@ SYNOPSIS
[--no-thread | --thread[=<style>]]
[(--attach|--inline)[=<boundary>] | --no-attach]
[-s | --signoff]
+ [--signature=<signature> | --no-signature]
[-n | --numbered | -N | --no-numbered]
[--start-number <n>] [--numbered-files]
[--in-reply-to=Message-Id] [--suffix=.<sfx>]
@@ -180,6 +181,12 @@ will want to ensure that threading is disabled for `git send-email`.
containing the shortlog and the overall diffstat. You can
fill in a description in the file before sending it out.
+--[no]-signature=<signature>::
+ Add a signature to each message produced. Per RFC 3676 the signature
+ is separated from the body by a line with '-- ' on it. If the
+ signature option is omitted the signature defaults to the git version
+ number.
+
--suffix=.<sfx>::
Instead of using `.patch` as the suffix for generated
filenames, use specified suffix. A common alternative is
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index cbe74d5..315f07e 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -88,6 +88,16 @@ commits prior to the amend or rebase occurring. Since these changes
are not part of the current project most users will want to expire
them sooner. This option defaults to '30 days'.
+The above two configuration variables can be given to a pattern. For
+example, this sets non-default expiry values only to remote tracking
+branches:
+
+------------
+[gc "refs/remotes/*"]
+ reflogExpire = never
+ reflogexpireUnreachable = 3 days
+------------
+
The optional configuration variable 'gc.rerereresolved' indicates
how long records of conflicted merge you resolved earlier are
kept. This defaults to 60 days.
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index 912bddd..5474dd7 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -14,6 +14,7 @@ SYNOPSIS
[-E | --extended-regexp] [-G | --basic-regexp]
[-F | --fixed-strings] [-n]
[-l | --files-with-matches] [-L | --files-without-match]
+ [(-O | --open-files-in-pager) [<pager>]]
[-z | --null]
[-c | --count] [--all-match] [-q | --quiet]
[--max-depth <depth>]
@@ -104,6 +105,13 @@ OPTIONS
For better compatibility with 'git diff', `--name-only` is a
synonym for `--files-with-matches`.
+-O [<pager>]::
+--open-files-in-pager [<pager>]::
+ Open the matching files in the pager (not the output of 'grep').
+ If the pager happens to be "less" or "vi", and the user
+ specified only one pattern, the first file is positioned at
+ the first match automatically.
+
-z::
--null::
Output \0 instead of the character that normally follows a
diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt
index a1f17df..2c3c4d2 100644
--- a/Documentation/git-instaweb.txt
+++ b/Documentation/git-instaweb.txt
@@ -29,7 +29,7 @@ OPTIONS
The HTTP daemon command-line that will be executed.
Command-line options may be specified here, and the
configuration file will be added at the end of the command-line.
- Currently apache2, lighttpd, mongoose and webrick are supported.
+ Currently apache2, lighttpd, mongoose, plackup and webrick are supported.
(Default: lighttpd)
-m::
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index d7f6a9c..0e6ff31 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -37,7 +37,8 @@ include::diff-options.txt[]
and <until>, see "SPECIFYING REVISIONS" section in
linkgit:git-rev-parse[1].
---decorate[=short|full]::
+--no-decorate::
+--decorate[=short|full|no]::
Print out the ref names of any commits that are shown. If 'short' is
specified, the ref name prefixes 'refs/heads/', 'refs/tags/' and
'refs/remotes/' will not be printed. If 'full' is specified, the
@@ -56,7 +57,7 @@ include::diff-options.txt[]
commits, and doesn't limit diff for those commits.
--follow::
- Continue listing the history of a file beyond renames.
+ Continue listing the history of a file beyond renames/copies.
--log-size::
Before the log message print out its size in bytes. Intended
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index de63ef0..5540af5 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -15,7 +15,7 @@ SYNOPSIS
'git notes' edit [<object>]
'git notes' show [<object>]
'git notes' remove [<object>]
-'git notes' prune
+'git notes' prune [-n | -v]
DESCRIPTION
@@ -128,6 +128,13 @@ OPTIONS
'GIT_NOTES_REF' and the "core.notesRef" configuration. The ref
is taken to be in `refs/notes/` if it is not qualified.
+-n::
+ Do not remove anything; just report the object names whose notes
+ would be removed.
+
+-v::
+ Report all object names whose notes are removed.
+
DISCUSSION
----------
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 3fc599c..aa021b0 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -10,10 +10,11 @@ SYNOPSIS
--------
[verse]
'git remote' [-v | --verbose]
-'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
+'git remote add' [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror] <name> <url>
'git remote rename' <old> <new>
'git remote rm' <name>
'git remote set-head' <name> (-a | -d | <branch>)
+'git remote set-branches' <name> [--add] <branch>...
'git remote set-url' [--push] <name> <newurl> [<oldurl>]
'git remote set-url --add' [--push] <name> <newurl>
'git remote set-url --delete' [--push] <name> <url>
@@ -51,6 +52,12 @@ update remote-tracking branches <name>/<branch>.
With `-f` option, `git fetch <name>` is run immediately after
the remote information is set up.
+
+With `--tags` option, `git fetch <name>` imports every tag from the
+remote repository.
++
+With `--no-tags` option, `git fetch <name>` does not import tags from
+the remote repository.
++
With `-t <branch>` option, instead of the default glob
refspec for the remote to track all branches under
`$GIT_DIR/remotes/<name>/`, a refspec to track only `<branch>`
@@ -104,6 +111,18 @@ remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to
`refs/remotes/origin/master` already exists; if not it must be fetched first.
+
+'set-branches'::
+
+Changes the list of branches tracked by the named remote.
+This can be used to track a subset of the available remote branches
+after the initial setup for a remote.
++
+The named branches will be interpreted as if specified with the
+`-t` option on the 'git remote add' command line.
++
+With `--add`, instead of replacing the list of currently tracked
+branches, adds to that list.
+
'set-url'::
Changes URL remote points to. Sets first URL remote points to matching
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 8db600f..833a2a2 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -256,7 +256,7 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
the branch the ref is set to build on top of. Missing ref defaults
to the current branch.
-* A suffix '{caret}' to a revision parameter means the first parent of
+* A suffix '{caret}' to a revision parameter (e.g. 'HEAD{caret}') means the first parent of
that commit object. '{caret}<n>' means the <n>th parent (i.e.
'rev{caret}'
is equivalent to 'rev{caret}1'). As a special rule,
@@ -282,21 +282,24 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
and dereference the tag recursively until a non-tag object is
found.
-* A colon, followed by a slash, followed by a text: this names
+* A colon, followed by a slash, followed by a text (e.g. `:/fix nasty bug`): this names
a commit whose commit message starts with the specified text.
This name returns the youngest matching commit which is
reachable from any ref. If the commit message starts with a
'!', you have to repeat that; the special sequence ':/!',
followed by something else than '!' is reserved for now.
-* A suffix ':' followed by a path; this names the blob or tree
+* A suffix ':' followed by a path (e.g. `HEAD:README`); this names the blob or tree
at the given path in the tree-ish object named by the part
before the colon.
+ ':path' (with an empty part before the colon, e.g. `:README`)
+ is a special case of the syntax described next: content
+ recorded in the index at the given path.
* A colon, optionally followed by a stage number (0 to 3) and a
- colon, followed by a path; this names a blob object in the
- index at the given path. Missing stage number (and the colon
- that follows it) names a stage 0 entry. During a merge, stage
+ colon, followed by a path (e.g. `:0:README`); this names a blob object in the
+ index at the given path. Missing stage number (and the colon
+ that follows it, e.g. `:README`) names a stage 0 entry. During a merge, stage
1 is the common ancestor, stage 2 is the target branch's version
(typically the current branch), and stage 3 is the version from
the branch being merged.
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index c66bf80..dea4f53 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -3,20 +3,22 @@ git-revert(1)
NAME
----
-git-revert - Revert an existing commit
+git-revert - Revert some existing commits
SYNOPSIS
--------
-'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>
+'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...
DESCRIPTION
-----------
-Given one existing commit, revert the change the patch introduces, and record a
-new commit that records it. This requires your working tree to be clean (no
-modifications from the HEAD commit).
-Note: 'git revert' is used to record a new commit to reverse the
-effect of an earlier commit (often a faulty one). If you want to
+Given one or more existing commits, revert the changes that the
+related patches introduce, and record some new commits that record
+them. This requires your working tree to be clean (no modifications
+from the HEAD commit).
+
+Note: 'git revert' is used to record some new commits to reverse the
+effect of some earlier commits (often only a faulty one). If you want to
throw away all uncommitted changes in your working directory, you
should see linkgit:git-reset[1], particularly the '--hard' option. If
you want to extract specific files as they were in another commit, you
@@ -26,10 +28,13 @@ both will discard uncommitted changes in your working directory.
OPTIONS
-------
-<commit>::
- Commit to revert.
+<commit>...::
+ Commits to revert.
For a more complete list of ways to spell commit names, see
"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+ Sets of commits can also be given but no traversal is done by
+ default, see linkgit:git-rev-list[1] and its '--no-walk'
+ option.
-e::
--edit::
@@ -59,11 +64,11 @@ more details.
-n::
--no-commit::
- Usually the command automatically creates a commit with
- a commit log message stating which commit was
- reverted. This flag applies the change necessary
- to revert the named commit to your working tree
- and the index, but does not make the commit. In addition,
+ Usually the command automatically creates some commits with
+ commit log messages stating which commits were
+ reverted. This flag applies the changes necessary
+ to revert the named commits to your working tree
+ and the index, but does not make the commits. In addition,
when this option is used, your index does not have to match
the HEAD commit. The revert is done against the
beginning state of your index.
@@ -75,6 +80,20 @@ effect to your index in a row.
--signoff::
Add Signed-off-by line at the end of the commit message.
+EXAMPLES
+--------
+git revert HEAD~3::
+
+ Revert the changes specified by the fourth last commit in HEAD
+ and create a new commit with the reverted changes.
+
+git revert -n master\~5..master~2::
+
+ Revert the changes done by commits from the fifth last commit
+ in master (included) to the third last commit in master
+ (included), but do not create any commit with the reverted
+ changes. The revert only modifies the working tree and the
+ index.
Author
------
@@ -84,6 +103,10 @@ Documentation
--------------
Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+SEE ALSO
+--------
+linkgit:git-cherry-pick[1]
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 2d4bbfc..2fd054c 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -27,6 +27,10 @@ OPTIONS
--short::
Give the output in the short-format.
+-b::
+--branch::
+ Show the branch and tracking info even in short-format.
+
--porcelain::
Give the output in a stable, easy-to-parse format for scripts.
Currently this is identical to --short output, but is guaranteed
@@ -49,6 +53,17 @@ See linkgit:git-config[1] for configuration variable
used to change the default for when the option is not
specified.
+--ignore-submodules[=<when>]::
+ Ignore changes to submodules when looking for changes. <when> can be
+ either "untracked", "dirty" or "all", which is the default. When
+ "untracked" is used submodules are not considered dirty when they only
+ contain untracked content (but they are still scanned for modified
+ content). Using "dirty" ignores all changes to the work tree of submodules,
+ only changes to the commits stored in the superproject are shown (this was
+ the behavior before 1.7.0). Using "all" hides all changes to submodules
+ (and suppresses the output of submodule summaries when the config option
+ `status.submodulesummary` is set).
+
-z::
Terminate entries with NUL, instead of LF. This implies
the `--porcelain` output format if no other format is given.
@@ -120,6 +135,10 @@ Ignored files are not listed.
? ? untracked
-------------------------------------------------
+If -b is used the short-format status is preceded by a line
+
+## branchname tracking info
+
There is an alternate -z format recommended for machine parsing. In
that format, the status field is the same, but some other things
change. First, the '->' is omitted from rename entries and the field
@@ -128,7 +147,7 @@ order is reversed (e.g 'from -> to' becomes 'to from'). Second, a NUL
and the terminating newline (but a space still separates the status
field from the first filename). Third, filenames containing special
characters are not specially formatted; no quoting or
-backslash-escaping is performed.
+backslash-escaping is performed. Fourth, there is no branch line.
CONFIGURATION
-------------
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 2502531..76a832a 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -95,6 +95,10 @@ is the superproject and submodule repositories will be kept
together in the same relative location, and only the
superproject's URL needs to be provided: git-submodule will correctly
locate the submodule using the relative URL in .gitmodules.
++
+The submodule will be added with "git add --force <path>". I.e. git
+doesn't care if the new path is in a `gitignore`. Your invocation of
+"git submodule add" is considered enough to override it.
status::
Show the status of the submodules. This will print the SHA-1 of the
@@ -145,10 +149,12 @@ summary::
foreach::
Evaluates an arbitrary shell command in each checked out submodule.
- The command has access to the variables $name, $path and $sha1:
+ The command has access to the variables $name, $path, $sha1 and
+ $toplevel:
$name is the name of the relevant submodule section in .gitmodules,
$path is the name of the submodule directory relative to the
- superproject, and $sha1 is the commit as recorded in the superproject.
+ superproject, $sha1 is the commit as recorded in the superproject,
+ and $toplevel is the absolute path to the top-level of the superproject.
Any submodules defined in the superproject but not checked out are
ignored by this command. Unless given --quiet, foreach prints the name
of each submodule before evaluating the command.
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 99f3c1e..b09bd97 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -243,7 +243,7 @@ where <name> is the name of the SVN repository as specified by the -R option to
--username;;
Specify the SVN username to perform the commit as. This option overrides
- configuration property 'username'.
+ the 'username' configuration property.
--commit-url;;
Use the specified URL to connect to the destination Subversion
diff --git a/Documentation/git.txt b/Documentation/git.txt
index c4024d0..8f0dd7f 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -12,6 +12,7 @@ SYNOPSIS
'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
[-p|--paginate|--no-pager] [--no-replace-objects]
[--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
+ [-c name=value]
[--help] COMMAND [ARGS]
DESCRIPTION
@@ -43,9 +44,10 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.7.1/git.html[documentation for release 1.7.1]
+* link:v1.7.1.1/git.html[documentation for release 1.7.1.1]
* release notes for
+ link:RelNotes-1.7.1.1.txt[1.7.1.1],
link:RelNotes-1.7.1.txt[1.7.1].
* link:v1.7.0.6/git.html[documentation for release 1.7.0.6]
@@ -228,6 +230,12 @@ displayed. See linkgit:git-help[1] for more information,
because `git --help ...` is converted internally into `git
help ...`.
+-c <name>=<value>::
+ Pass a configuration parameter to the command. The value
+ given will override values from configuration files.
+ The <name> is expected in the same format as listed by
+ 'git config' (subkeys separated by dots).
+
--exec-path::
Path to wherever your core git programs are installed.
This can also be controlled by setting the GIT_EXEC_PATH
@@ -538,6 +546,16 @@ git so take care if using Cogito etc.
a GIT_DIR set on the command line or in the environment.
(Useful for excluding slow-loading network directories.)
+'GIT_DISCOVERY_ACROSS_FILESYSTEM'::
+ When run in a directory that does not have ".git" repository
+ directory, git tries to find such a directory in the parent
+ directories to find the top of the working tree, but by default it
+ does not cross filesystem boundaries. This environment variable
+ can be set to true to tell git not to stop at filesystem
+ boundaries. Like 'GIT_CEILING_DIRECTORIES', this will not affect
+ an explicit repository directory set via 'GIT_DIR' or on the
+ command line.
+
git Commits
~~~~~~~~~~~
'GIT_AUTHOR_NAME'::
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index d892e64..564586b 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -92,53 +92,154 @@ such as 'git checkout' and 'git merge' run. They also affect how
git stores the contents you prepare in the working tree in the
repository upon 'git add' and 'git commit'.
-`crlf`
+`text`
^^^^^^
-This attribute controls the line-ending convention.
+This attribute enables and controls end-of-line normalization. When a
+text file is normalized, its line endings are converted to LF in the
+repository. To control what line ending style is used in the working
+directory, use the `eol` attribute for a single file and the
+`core.eol` configuration variable for all text files.
Set::
- Setting the `crlf` attribute on a path is meant to mark
- the path as a "text" file. 'core.autocrlf' conversion
- takes place without guessing the content type by
- inspection.
+ Setting the `text` attribute on a path enables end-of-line
+ normalization and marks the path as a text file. End-of-line
+ conversion takes place without guessing the content type.
Unset::
- Unsetting the `crlf` attribute on a path tells git not to
+ Unsetting the `text` attribute on a path tells git not to
attempt any end-of-line conversion upon checkin or checkout.
+Set to string value "auto"::
+
+ When `text` is set to "auto", the path is marked for automatic
+ end-of-line normalization. If git decides that the content is
+ text, its line endings are normalized to LF on checkin.
+
Unspecified::
- Unspecified `crlf` attribute tells git to apply the
- `core.autocrlf` conversion when the file content looks
- like text.
+ If the `text` attribute is unspecified, git uses the
+ `core.autocrlf` configuration variable to determine if the
+ file should be converted.
-Set to string value "input"::
+Any other value causes git to act as if `text` has been left
+unspecified.
- This is similar to setting the attribute to `true`, but
- also forces git to act as if `core.autocrlf` is set to
- `input` for the path.
+`eol`
+^^^^^
-Any other value set to `crlf` attribute is ignored and git acts
-as if the attribute is left unspecified.
+This attribute sets a specific line-ending style to be used in the
+working directory. It enables end-of-line normalization without any
+content checks, effectively setting the `text` attribute.
+Set to string value "crlf"::
-The `core.autocrlf` conversion
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ This setting forces git to normalize line endings for this
+ file on checkin and convert them to CRLF when the file is
+ checked out.
+
+Set to string value "lf"::
+
+ This setting forces git to normalize line endings to LF on
+ checkin and prevents conversion to CRLF when the file is
+ checked out.
+
+Backwards compatibility with `crlf` attribute
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For backwards compatibility, the `crlf` attribute is interpreted as
+follows:
+
+------------------------
+crlf text
+-crlf -text
+crlf=input eol=lf
+------------------------
+
+End-of-line conversion
+^^^^^^^^^^^^^^^^^^^^^^
+
+While git normally leaves file contents alone, it can be configured to
+normalize line endings to LF in the repository and, optionally, to
+convert them to CRLF when files are checked out.
+
+Here is an example that will make git normalize .txt, .vcproj and .sh
+files, ensure that .vcproj files have CRLF and .sh files have LF in
+the working directory, and prevent .jpg files from being normalized
+regardless of their content.
+
+------------------------
+*.txt text
+*.vcproj eol=crlf
+*.sh eol=lf
+*.jpg -text
+------------------------
+
+Other source code management systems normalize all text files in their
+repositories, and there are two ways to enable similar automatic
+normalization in git.
+
+If you simply want to have CRLF line endings in your working directory
+regardless of the repository you are working with, you can set the
+config variable "core.autocrlf" without changing any attributes.
-If the configuration variable `core.autocrlf` is false, no
-conversion is done.
+------------------------
+[core]
+ autocrlf = true
+------------------------
+
+This does not force normalization of all text files, but does ensure
+that text files that you introduce to the repository have their line
+endings normalized to LF when they are added, and that files that are
+already normalized in the repository stay normalized.
+
+If you want to interoperate with a source code management system that
+enforces end-of-line normalization, or you simply want all text files
+in your repository to be normalized, you should instead set the `text`
+attribute to "auto" for _all_ files.
+
+------------------------
+* text=auto
+------------------------
+
+This ensures that all files that git considers to be text will have
+normalized (LF) line endings in the repository. The `core.eol`
+configuration variable controls which line endings git will use for
+normalized files in your working directory; the default is to use the
+native line ending for your platform, or CRLF if `core.autocrlf` is
+set.
+
+NOTE: When `text=auto` normalization is enabled in an existing
+repository, any text files containing CRLFs should be normalized. If
+they are not they will be normalized the next time someone tries to
+change them, causing unfortunate misattribution. From a clean working
+directory:
+
+-------------------------------------------------
+$ echo "* text=auto" >>.gitattributes
+$ rm .git/index # Remove the index to force git to
+$ git reset # re-scan the working directory
+$ git status # Show files that will be normalized
+$ git add -u
+$ git add .gitattributes
+$ git commit -m "Introduce end-of-line normalization"
+-------------------------------------------------
+
+If any files that should not be normalized show up in 'git status',
+unset their `text` attribute before running 'git add -u'.
-When `core.autocrlf` is true, it means that the platform wants
-CRLF line endings for files in the working tree, and you want to
-convert them back to the normal LF line endings when checking
-in to the repository.
+------------------------
+manual.pdf -text
+------------------------
-When `core.autocrlf` is set to "input", line endings are
-converted to LF upon checkin, but there is no conversion done
-upon checkout.
+Conversely, text files that git does not detect can have normalization
+enabled manually.
+
+------------------------
+weirdchars.txt text
+------------------------
If `core.safecrlf` is set to "true" or "warn", git verifies if
the conversion is reversible for the current setting of
@@ -223,11 +324,11 @@ Interaction between checkin/checkout attributes
In the check-in codepath, the worktree file is first converted
with `filter` driver (if specified and corresponding driver
defined), then the result is processed with `ident` (if
-specified), and then finally with `crlf` (again, if specified
+specified), and then finally with `text` (again, if specified
and applicable).
In the check-out codepath, the blob content is first converted
-with `crlf`, and then `ident` and fed to `filter`.
+with `text`, and then `ident` and fed to `filter`.
Generating diff text
@@ -360,7 +461,7 @@ patterns are available:
Customizing word diff
^^^^^^^^^^^^^^^^^^^^^
-You can customize the rules that `git diff --color-words` uses to
+You can customize the rules that `git diff --word-diff` uses to
split words in a line, by specifying an appropriate regular expression
in the "diff.*.wordRegex" configuration variable. For example, in TeX
a backslash followed by a sequence of letters forms a command, but
@@ -414,6 +515,26 @@ because it quickly conveys the changes you have made), you
should generate it separately and send it as a comment _in
addition to_ the usual binary diff that you might send.
+Because text conversion can be slow, especially when doing a
+large number of them with `git log -p`, git provides a mechanism
+to cache the output and use it in future diffs. To enable
+caching, set the "cachetextconv" variable in your diff driver's
+config. For example:
+
+------------------------
+[diff "jpg"]
+ textconv = exif
+ cachetextconv = true
+------------------------
+
+This will cache the result of running "exif" on each blob
+indefinitely. If you change the textconv config variable for a
+diff driver, git will automatically invalidate the cache entries
+and re-run the textconv filter. If you want to invalidate the
+cache manually (e.g., because your version of "exif" was updated
+and now produces better output), you can remove the cache
+manually with `git update-ref -d refs/notes/textconv/jpg` (where
+"jpg" is the name of the diff driver, as in the example above).
Performing a three-way merge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -631,7 +752,7 @@ You do not want any end-of-line conversions applied to, nor textual diffs
produced for, any binary file you track. You would need to specify e.g.
------------
-*.jpg -crlf -diff
+*.jpg -text -diff
------------
but that may become cumbersome, when you have many attributes. Using
@@ -644,7 +765,7 @@ the same time. The system knows a built-in attribute macro, `binary`:
which is equivalent to the above. Note that the attribute macros can only
be "Set" (see the above example that sets "binary" macro as if it were an
-ordinary attribute --- setting it in turn unsets "crlf" and "diff").
+ordinary attribute --- setting it in turn unsets "text" and "diff").
DEFINING ATTRIBUTE MACROS
@@ -655,7 +776,7 @@ at the toplevel (i.e. not in any subdirectory). The built-in attribute
macro "binary" is equivalent to:
------------
-[attr]binary -diff -crlf
+[attr]binary -diff -text
------------
diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
index 5daf750..72a13d1 100644
--- a/Documentation/gitmodules.txt
+++ b/Documentation/gitmodules.txt
@@ -29,6 +29,9 @@ submodule.<name>.path::
submodule.<name>.url::
Defines an url from where the submodule repository can be cloned.
+ This may be either an absolute URL ready to be passed to
+ linkgit:git-clone[1] or (if it begins with ./ or ../) a location
+ relative to the superproject's origin repository.
submodule.<name>.update::
Defines what to do when the submodule is updated by the superproject.
diff --git a/Documentation/install-webdoc.sh b/Documentation/install-webdoc.sh
index 2135a8e..34d02a2 100755
--- a/Documentation/install-webdoc.sh
+++ b/Documentation/install-webdoc.sh
@@ -12,7 +12,7 @@ do
then
: did not match
elif test -f "$T/$h" &&
- diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
+ $DIFF -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
then
:; # up to date
else
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index c85a52c..561cc9f 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -11,7 +11,12 @@ have limited your view of history: for example, if you are
only interested in changes related to a certain directory or
file.
-Here are some additional details for each format:
+There are several built-in formats, and you can define
+additional formats by setting a pretty.<name>
+config option to either another format name, or a
+'format:' string, as described below (see
+linkgit:git-config[1]). Here are the details of the
+built-in formats:
* 'oneline'
@@ -123,6 +128,7 @@ The placeholders are:
- '%s': subject
- '%f': sanitized subject line, suitable for a filename
- '%b': body
+- '%B': raw body (unwrapped subject and body)
- '%N': commit notes
- '%gD': reflog selector, e.g., `refs/stash@\{1\}`
- '%gd': shortened reflog selector, e.g., `stash@\{1\}`
@@ -153,6 +159,10 @@ If you add a `-` (minus sign) after '%' of a placeholder, line-feeds that
immediately precede the expansion are deleted if and only if the
placeholder expands to an empty string.
+If you add a ` ` (space) after '%' of a placeholder, a space
+is inserted immediately before the expansion if and only if the
+placeholder expands to a non-empty string.
+
* 'tformat:'
+
The 'tformat:' format works exactly like 'format:', except that it
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index b9fb7a8..cc562a0 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -98,6 +98,15 @@ you would get an output like this:
This implies the '--topo-order' option by default, but the
'--date-order' option may also be specified.
+ifdef::git-rev-list[]
+--count::
+ Print a number stating how many commits would have been
+ listed, and suppress all other output. When used together
+ with '--left-right', instead print the counts for left and
+ right commits, separated by a tab.
+endif::git-rev-list[]
+
+
ifndef::git-rev-list[]
Diff Formatting
~~~~~~~~~~~~~~~
@@ -384,6 +393,14 @@ Default mode::
merges from the resulting history, as there are no selected
commits contributing to this merge.
+--ancestry-path::
+
+ When given a range of commits to display (e.g. 'commit1..commit2'
+ or 'commit2 {caret}commit1'), only display commits that exist
+ directly on the ancestry chain between the 'commit1' and
+ 'commit2', i.e. commits that are both descendants of 'commit1',
+ and ancestors of 'commit2'.
+
A more detailed explanation follows.
Suppose you specified `foo` as the <paths>. We shall call commits
@@ -440,7 +457,7 @@ This results in:
+
-----------------------------------------------------------------------
.-A---N---O
- / /
+ / / /
I---------D
-----------------------------------------------------------------------
+
@@ -511,8 +528,6 @@ Note that without '\--full-history', this still simplifies merges: if
one of the parents is TREESAME, we follow only that one, so the other
sides of the merge are never walked.
-Finally, there is a fourth simplification mode available:
-
--simplify-merges::
First, build a history graph in the same way that
@@ -554,6 +569,46 @@ Note the major differences in `N` and `P` over '\--full-history':
removed completely, because it had one parent and is TREESAME.
--
+Finally, there is a fifth simplification mode available:
+
+--ancestry-path::
+
+ Limit the displayed commits to those directly on the ancestry
+ chain between the "from" and "to" commits in the given commit
+ range. I.e. only display commits that are ancestor of the "to"
+ commit, and descendants of the "from" commit.
++
+As an example use case, consider the following commit history:
++
+-----------------------------------------------------------------------
+ D---E-------F
+ / \ \
+ B---C---G---H---I---J
+ / \
+ A-------K---------------L--M
+-----------------------------------------------------------------------
++
+A regular 'D..M' computes the set of commits that are ancestors of `M`,
+but excludes the ones that are ancestors of `D`. This is useful to see
+what happened to the history leading to `M` since `D`, in the sense
+that "what does `M` have that did not exist in `D`". The result in this
+example would be all the commits, except `A` and `B` (and `D` itself,
+of course).
++
+When we want to find out what commits in `M` are contaminated with the
+bug introduced by `D` and need fixing, however, we might want to view
+only the subset of 'D..M' that are actually descendants of `D`, i.e.
+excluding `C` and `K`. This is exactly what the '\--ancestry-path'
+option does. Applied to the 'D..M' range, it results in:
++
+-----------------------------------------------------------------------
+ E-------F
+ \ \
+ G---H---I---J
+ \
+ L--M
+-----------------------------------------------------------------------
+
The '\--simplify-by-decoration' option allows you to view only the
big picture of the topology of the history, by omitting commits
that are not referenced by tags. Commits are marked as !TREESAME
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
index 44876fa..f18b4f4 100644
--- a/Documentation/technical/api-run-command.txt
+++ b/Documentation/technical/api-run-command.txt
@@ -231,8 +231,9 @@ The function pointer in .proc has the following signature:
There are serious restrictions on what the asynchronous function can do
-because this facility is implemented by a pipe to a forked process on
-UNIX, but by a thread in the same address space on Windows:
+because this facility is implemented by a thread in the same address
+space on most platforms (when pthreads is available), but by a pipe to
+a forked process otherwise:
. It cannot change the program's state (global variables, environment,
etc.) in a way that the caller notices; in other words, .in and .out
diff --git a/Documentation/technical/api-string-list.txt b/Documentation/technical/api-string-list.txt
index 6d8c24b..3f575bd 100644
--- a/Documentation/technical/api-string-list.txt
+++ b/Documentation/technical/api-string-list.txt
@@ -38,8 +38,8 @@ struct string_list list;
int i;
memset(&list, 0, sizeof(struct string_list));
-string_list_append("foo", &list);
-string_list_append("bar", &list);
+string_list_append(&list, "foo");
+string_list_append(&list, "bar");
for (i = 0; i < list.nr; i++)
printf("%s\n", list.items[i].string)
----
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
index fd1a593..b15517f 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -119,7 +119,7 @@ both.
ofs-delta
---------
-Server can send, and client understand PACKv2 with delta refering to
+Server can send, and client understand PACKv2 with delta referring to
its base by position in pack rather than by an obj-id. That is, they can
send/read OBJ_OFS_DELTA (aka type 6) in a packfile.
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index d4cb58e..e45513d 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.1.1
+DEF_VER=v1.7.1.GIT
LF='
'
diff --git a/Makefile b/Makefile
index b18768e..bc3c570 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,12 @@ all::
# Define SANE_TOOL_PATH to a colon-separated list of paths to prepend
# to PATH if your tools in /usr/bin are broken.
#
+# Define SOCKLEN_T to a suitable type (such as 'size_t') if your
+# system headers do not define a socklen_t type.
+#
+# Define INLINE to a suitable substitute (such as '__inline' or '') if git
+# fails to compile with errors about undefined inline functions or similar.
+#
# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
# or vsnprintf() return -1 instead of number of characters which would
# have been written to the final string if enough space had been available.
@@ -227,6 +233,8 @@ all::
#
# Define CHECK_HEADER_DEPENDENCIES to check for problems in the hard-coded
# dependency rules.
+#
+# Define NATIVE_CRLF if your platform uses CRLF for line endings.
GIT-VERSION-FILE: FORCE
@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -249,7 +257,7 @@ endif
CFLAGS = -g -O2 -Wall
LDFLAGS =
-ALL_CFLAGS = $(CFLAGS)
+ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
ALL_LDFLAGS = $(LDFLAGS)
STRIP ?= strip
@@ -272,6 +280,7 @@ mandir = share/man
infodir = share/info
gitexecdir = libexec/git-core
sharedir = $(prefix)/share
+gitwebdir = $(sharedir)/gitweb
template_dir = share/git-core/templates
htmldir = share/doc/git-doc
ifeq ($(prefix),/usr)
@@ -285,11 +294,12 @@ lib = lib
# DESTDIR=
pathsep = :
-export prefix bindir sharedir sysconfdir
+export prefix bindir sharedir sysconfdir gitwebdir
CC = gcc
AR = ar
RM = rm -f
+DIFF = diff
TAR = tar
FIND = find
INSTALL = install
@@ -297,6 +307,7 @@ RPMBUILD = rpmbuild
TCL_PATH = tclsh
TCLTK_PATH = wish
PTHREAD_LIBS = -lpthread
+PTHREAD_CFLAGS =
export TCL_PATH TCLTK_PATH
@@ -369,6 +380,8 @@ SCRIPT_PERL += git-relink.perl
SCRIPT_PERL += git-send-email.perl
SCRIPT_PERL += git-svn.perl
+SCRIPT_PYTHON += git-remote-testgit.py
+
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
$(patsubst %.py,%,$(SCRIPT_PYTHON)) \
@@ -489,6 +502,7 @@ LIB_H += log-tree.h
LIB_H += mailmap.h
LIB_H += merge-recursive.h
LIB_H += notes.h
+LIB_H += notes-cache.h
LIB_H += object.h
LIB_H += pack.h
LIB_H += pack-refs.h
@@ -578,6 +592,7 @@ LIB_OBJS += merge-file.o
LIB_OBJS += merge-recursive.o
LIB_OBJS += name-hash.o
LIB_OBJS += notes.o
+LIB_OBJS += notes-cache.o
LIB_OBJS += object.o
LIB_OBJS += pack-check.o
LIB_OBJS += pack-refs.o
@@ -623,6 +638,7 @@ LIB_OBJS += tree-diff.o
LIB_OBJS += tree.o
LIB_OBJS += tree-walk.o
LIB_OBJS += unpack-trees.o
+LIB_OBJS += url.o
LIB_OBJS += usage.o
LIB_OBJS += userdiff.o
LIB_OBJS += utf8.o
@@ -735,6 +751,13 @@ EXTLIBS =
# because maintaining the nesting to match is a pain. If
# we had "elif" things would have been much nicer...
+ifeq ($(uname_S),OSF1)
+ # Need this for u_short definitions et al
+ BASIC_CFLAGS += -D_OSF_SOURCE
+ SOCKLEN_T = int
+ NO_STRTOULL = YesPlease
+ NO_NSEC = YesPlease
+endif
ifeq ($(uname_S),Linux)
NO_STRLCPY = YesPlease
NO_MKSTEMPS = YesPlease
@@ -809,6 +832,18 @@ ifeq ($(uname_S),SunOS)
NO_MKDTEMP = YesPlease
NO_MKSTEMPS = YesPlease
NO_REGEX = YesPlease
+ ifeq ($(uname_R),5.6)
+ SOCKLEN_T = int
+ NO_HSTRERROR = YesPlease
+ NO_IPV6 = YesPlease
+ NO_SOCKADDR_STORAGE = YesPlease
+ NO_UNSETENV = YesPlease
+ NO_SETENV = YesPlease
+ NO_STRLCPY = YesPlease
+ NO_C99_FORMAT = YesPlease
+ NO_STRTOUMAX = YesPlease
+ GIT_TEST_CMP = cmp
+ endif
ifeq ($(uname_R),5.7)
NEEDS_RESOLV = YesPlease
NO_IPV6 = YesPlease
@@ -818,18 +853,21 @@ ifeq ($(uname_S),SunOS)
NO_STRLCPY = YesPlease
NO_C99_FORMAT = YesPlease
NO_STRTOUMAX = YesPlease
+ GIT_TEST_CMP = cmp
endif
ifeq ($(uname_R),5.8)
NO_UNSETENV = YesPlease
NO_SETENV = YesPlease
NO_C99_FORMAT = YesPlease
NO_STRTOUMAX = YesPlease
+ GIT_TEST_CMP = cmp
endif
ifeq ($(uname_R),5.9)
NO_UNSETENV = YesPlease
NO_SETENV = YesPlease
NO_C99_FORMAT = YesPlease
NO_STRTOUMAX = YesPlease
+ GIT_TEST_CMP = cmp
endif
INSTALL = /usr/ucb/install
TAR = gtar
@@ -907,7 +945,13 @@ ifeq ($(uname_S),AIX)
BASIC_CFLAGS += -D_LARGE_FILES
ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
NO_PTHREADS = YesPlease
+ else
+ PTHREAD_LIBS = -lpthread
+ endif
+ ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3)
+ INLINE=''
endif
+ GIT_TEST_CMP = cmp
endif
ifeq ($(uname_S),GNU)
# GNU/Hurd
@@ -952,6 +996,7 @@ ifeq ($(uname_S),IRIX64)
NEEDS_LIBGEN = YesPlease
endif
ifeq ($(uname_S),HP-UX)
+ INLINE = __inline
NO_IPV6=YesPlease
NO_SETENV=YesPlease
NO_STRCASESTR=YesPlease
@@ -963,6 +1008,20 @@ ifeq ($(uname_S),HP-UX)
NO_HSTRERROR = YesPlease
NO_SYS_SELECT_H = YesPlease
SNPRINTF_RETURNS_BOGUS = YesPlease
+ NO_NSEC = YesPlease
+ ifeq ($(uname_R),B.11.00)
+ NO_INET_NTOP = YesPlease
+ NO_INET_PTON = YesPlease
+ endif
+ ifeq ($(uname_R),B.10.20)
+ # Override HP-UX 11.x setting:
+ INLINE =
+ SOCKLEN_T = size_t
+ NO_PREAD = YesPlease
+ NO_INET_NTOP = YesPlease
+ NO_INET_PTON = YesPlease
+ endif
+ GIT_TEST_CMP = cmp
endif
ifeq ($(uname_S),Windows)
GIT_VERSION := $(GIT_VERSION).MSVC
@@ -999,6 +1058,7 @@ ifeq ($(uname_S),Windows)
NO_CURL = YesPlease
NO_PYTHON = YesPlease
BLK_SHA1 = YesPlease
+ NATIVE_CRLF = YesPlease
CC = compat/vcbuild/scripts/clink.pl
AR = compat/vcbuild/scripts/lib.pl
@@ -1036,7 +1096,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
NO_STRTOUMAX = YesPlease
NO_MKDTEMP = YesPlease
NO_MKSTEMPS = YesPlease
- SNPRINTF_RETURNS_BOGUS = YesPlease
NO_SVN_TESTS = YesPlease
NO_PERL_MAKEMAKER = YesPlease
RUNTIME_PREFIX = YesPlease
@@ -1073,6 +1132,7 @@ endif
-include config.mak
ifdef CHECK_HEADER_DEPENDENCIES
+COMPUTE_HEADER_DEPENDENCIES =
USE_COMPUTED_HEADER_DEPENDENCIES =
endif
@@ -1088,6 +1148,14 @@ else
BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
endif
+ifneq (,$(INLINE))
+ BASIC_CFLAGS += -Dinline=$(INLINE)
+endif
+
+ifneq (,$(SOCKLEN_T))
+ BASIC_CFLAGS += -Dsocklen_t=$(SOCKLEN_T)
+endif
+
ifeq ($(uname_S),Darwin)
ifndef NO_FINK
ifeq ($(shell test -d /sw/lib && echo y),y)
@@ -1359,6 +1427,7 @@ endif
ifdef NO_PTHREADS
BASIC_CFLAGS += -DNO_PTHREADS
else
+ BASIC_CFLAGS += $(PTHREAD_CFLAGS)
EXTLIBS += $(PTHREAD_LIBS)
LIB_OBJS += thread-utils.o
endif
@@ -1383,6 +1452,10 @@ ifdef USE_NED_ALLOCATOR
COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
endif
+ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
+ export GIT_TEST_CMP_USE_COPIED_CONTEXT
+endif
+
ifeq ($(TCLTK_PATH),)
NO_TCLTK=NoThanks
endif
@@ -1439,11 +1512,13 @@ gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
template_dir_SQ = $(subst ','\'',$(template_dir))
htmldir_SQ = $(subst ','\'',$(htmldir))
prefix_SQ = $(subst ','\'',$(prefix))
+gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
+DIFF_SQ = $(subst ','\'',$(DIFF))
LIBS = $(GITLIBS) $(EXTLIBS)
@@ -1470,7 +1545,7 @@ endif
ALL_CFLAGS += $(BASIC_CFLAGS)
ALL_LDFLAGS += $(BASIC_LDFLAGS)
-export TAR INSTALL DESTDIR SHELL_PATH
+export DIFF TAR INSTALL DESTDIR SHELL_PATH
### Build rules
@@ -1532,6 +1607,7 @@ define cmd_munge_script
$(RM) $@ $@+ && \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+ -e 's|@@DIFF@@|$(DIFF_SQ)|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-e $(BROKEN_PATH_FIX) \
@@ -1559,11 +1635,10 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
sed -e '1{' \
-e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-e ' h' \
- -e ' s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
+ -e ' s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));=' \
-e ' H' \
-e ' x' \
-e '}' \
- -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
$@.perl >$@+ && \
chmod +x $@+ && \
@@ -1575,45 +1650,38 @@ gitweb:
$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
ifdef JSMIN
-GITWEB_PROGRAMS += gitweb/gitweb.min.js
-GITWEB_JS = gitweb/gitweb.min.js
+GITWEB_PROGRAMS += gitweb/static/gitweb.min.js
+GITWEB_JS = gitweb/static/gitweb.min.js
else
-GITWEB_JS = gitweb/gitweb.js
+GITWEB_JS = gitweb/static/gitweb.js
endif
ifdef CSSMIN
-GITWEB_PROGRAMS += gitweb/gitweb.min.css
-GITWEB_CSS = gitweb/gitweb.min.css
+GITWEB_PROGRAMS += gitweb/static/gitweb.min.css
+GITWEB_CSS = gitweb/static/gitweb.min.css
else
-GITWEB_CSS = gitweb/gitweb.css
+GITWEB_CSS = gitweb/static/gitweb.css
endif
OTHER_PROGRAMS += gitweb/gitweb.cgi $(GITWEB_PROGRAMS)
gitweb/gitweb.cgi: gitweb/gitweb.perl $(GITWEB_PROGRAMS)
$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
ifdef JSMIN
-gitweb/gitweb.min.js: gitweb/gitweb.js
+gitweb/static/gitweb.min.js: gitweb/static/gitweb.js
$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
endif # JSMIN
ifdef CSSMIN
-gitweb/gitweb.min.css: gitweb/gitweb.css
+gitweb/static/gitweb.min.css: gitweb/static/gitweb.css
$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
endif # CSSMIN
-git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css gitweb/gitweb.js
+git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/static/gitweb.css gitweb/static/gitweb.js
$(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
- -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
- -e '/@@GITWEB_CGI@@/d' \
- -e '/@@GITWEB_CSS@@/r $(GITWEB_CSS)' \
- -e '/@@GITWEB_CSS@@/d' \
- -e '/@@GITWEB_JS@@/r $(GITWEB_JS)' \
- -e '/@@GITWEB_JS@@/d' \
+ -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
-e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
- -e 's|@@GITWEB_CSS_NAME@@|$(GITWEB_CSS)|' \
- -e 's|@@GITWEB_JS_NAME@@|$(GITWEB_JS)|' \
$@.sh > $@+ && \
chmod +x $@+ && \
mv $@+ $@
@@ -1634,13 +1702,8 @@ $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
--no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
instlibdir` && \
- sed -e '1{' \
- -e ' s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
- -e '}' \
- -e 's|^import sys.*|&; \\\
- import os; \\\
- sys.path.insert(0, os.getenv("GITPYTHONLIB",\
- "@@INSTLIBDIR@@"));|' \
+ sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
+ -e 's|\(os\.getenv("GITPYTHONLIB"\)[^)]*)|\1,"@@INSTLIBDIR@@")|' \
-e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
$@.py >$@+ && \
chmod +x $@+ && \
@@ -1670,7 +1733,10 @@ git.o git.spec \
TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
GIT_OBJS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
- git.o http.o http-walker.o remote-curl.o
+ git.o
+ifndef NO_CURL
+ GIT_OBJS += http.o http-walker.o remote-curl.o
+endif
XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
xdiff/xmerge.o xdiff/xpatience.o
OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS)
@@ -1887,10 +1953,18 @@ GIT-CFLAGS: FORCE
GIT-BUILD-OPTIONS: FORCE
@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
@echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
+ @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@
+ @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
@echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
+ifdef GIT_TEST_CMP
+ @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@
+endif
+ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
+ @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@
+endif
### Detect Tck/Tk interpreter path changes
ifndef NO_TCLTK
@@ -1985,6 +2059,7 @@ install: all
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
ifndef NO_PERL
$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
+ $(MAKE) -C gitweb install
endif
ifndef NO_PYTHON
$(MAKE) -C git_remote_helpers prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
@@ -2004,21 +2079,24 @@ endif
test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
ln "$$bindir/git$X" "$$execdir/git$X" 2>/dev/null || \
cp "$$bindir/git$X" "$$execdir/git$X"; } ; } && \
- { for p in $(BUILT_INS); do \
+ for p in $(BUILT_INS); do \
$(RM) "$$execdir/$$p" && \
ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
- done; } && \
- { test x"$(REMOTE_CURL_ALIASES)" = x || \
- { for p in $(REMOTE_CURL_ALIASES); do \
+ done && \
+ remote_curl_aliases="$(REMOTE_CURL_ALIASES)" && \
+ for p in $$remote_curl_aliases; do \
$(RM) "$$execdir/$$p" && \
ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
- done; } ; } && \
+ done && \
./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
+install-gitweb:
+ $(MAKE) -C gitweb install
+
install-doc:
$(MAKE) -C Documentation install
diff --git a/RelNotes b/RelNotes
index 136c1b6..3da01a5 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.7.1.1.txt \ No newline at end of file
+Documentation/RelNotes-1.7.2.txt \ No newline at end of file
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..d399de2
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,40 @@
+dnl Check for socklen_t: historically on BSD it is an int, and in
+dnl POSIX 1g it is a type of its own, but some platforms use different
+dnl types for the argument to getsockopt, getpeername, etc. So we
+dnl have to test to find something that will work.
+AC_DEFUN([TYPE_SOCKLEN_T],
+[
+ AC_CHECK_TYPE([socklen_t], ,[
+ AC_MSG_CHECKING([for socklen_t equivalent])
+ AC_CACHE_VAL([git_cv_socklen_t_equiv],
+ [
+ # Systems have either "struct sockaddr *" or
+ # "void *" as the second argument to getpeername
+ git_cv_socklen_t_equiv=
+ for arg2 in "struct sockaddr" void; do
+ for t in int size_t unsigned long "unsigned long"; do
+ AC_TRY_COMPILE([
+ #include <sys/types.h>
+ #include <sys/socket.h>
+
+ int getpeername (int, $arg2 *, $t *);
+ ],[
+ $t len;
+ getpeername(0,0,&len);
+ ],[
+ git_cv_socklen_t_equiv="$t"
+ break 2
+ ])
+ done
+ done
+
+ if test "x$git_cv_socklen_t_equiv" = x; then
+ AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
+ fi
+ ])
+ AC_MSG_RESULT($git_cv_socklen_t_equiv)
+ AC_DEFINE_UNQUOTED(socklen_t, $git_cv_socklen_t_equiv,
+ [type to use in place of socklen_t if not defined])],
+ [#include <sys/types.h>
+#include <sys/socket.h>])
+])
diff --git a/attr.c b/attr.c
index 7467baf..8ba606c 100644
--- a/attr.c
+++ b/attr.c
@@ -287,7 +287,7 @@ static void free_attr_elem(struct attr_stack *e)
}
static const char *builtin_attr[] = {
- "[attr]binary -diff -crlf",
+ "[attr]binary -diff -text",
NULL,
};
diff --git a/attr.h b/attr.h
index 450f49d..8b3f19b 100644
--- a/attr.h
+++ b/attr.h
@@ -34,7 +34,7 @@ int git_checkattr(const char *path, int, struct git_attr_check *);
enum git_attr_direction {
GIT_ATTR_CHECKIN,
GIT_ATTR_CHECKOUT,
- GIT_ATTR_INDEX,
+ GIT_ATTR_INDEX
};
void git_attr_set_direction(enum git_attr_direction, struct index_state *);
diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c
index d880a53..c0054a0 100644
--- a/block-sha1/sha1.c
+++ b/block-sha1/sha1.c
@@ -70,6 +70,7 @@
*/
#if defined(__i386__) || defined(__x86_64__) || \
+ defined(_M_IX86) || defined(_M_X64) || \
defined(__ppc__) || defined(__ppc64__) || \
defined(__powerpc__) || defined(__powerpc64__) || \
defined(__s390__) || defined(__s390x__)
diff --git a/builtin.h b/builtin.h
index fdae027..ed6ee26 100644
--- a/builtin.h
+++ b/builtin.h
@@ -17,9 +17,6 @@ extern void prune_packed_objects(int);
extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
struct strbuf *out);
extern int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out);
-extern int commit_tree(const char *msg, unsigned char *tree,
- struct commit_list *parents, unsigned char *ret,
- const char *author);
extern int commit_notes(struct notes_tree *t, const char *msg);
struct notes_rewrite_cfg {
@@ -40,6 +37,8 @@ void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
extern int check_pager_config(const char *cmd);
+extern int textconv_object(const char *path, const unsigned char *sha1, char **buf, unsigned long *buf_size);
+
extern int 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);
diff --git a/builtin/add.c b/builtin/add.c
index 87d2980..17149cf 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -261,12 +261,14 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
{
char *file = xstrdup(git_path("ADD_EDIT.patch"));
const char *apply_argv[] = { "apply", "--recount", "--cached",
- file, NULL };
+ NULL, NULL };
struct child_process child;
struct rev_info rev;
int out;
struct stat st;
+ apply_argv[3] = file;
+
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
if (read_cache() < 0)
diff --git a/builtin/apply.c b/builtin/apply.c
index 59bbcdb..12ef9ea 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -56,7 +56,7 @@ static enum ws_error_action {
nowarn_ws_error,
warn_on_ws_error,
die_on_ws_error,
- correct_ws_error,
+ correct_ws_error
} ws_error_action = warn_on_ws_error;
static int whitespace_error;
static int squelch_whitespace_errors = 5;
@@ -64,7 +64,7 @@ static int applied_after_fixing_ws;
static enum ws_ignore {
ignore_ws_none,
- ignore_ws_change,
+ ignore_ws_change
} ws_ignore_action = ignore_ws_none;
@@ -1854,6 +1854,8 @@ static int match_fragment(struct image *img,
{
int i;
char *fixed_buf, *buf, *orig, *target;
+ struct strbuf fixed;
+ size_t fixed_len;
int preimage_limit;
if (preimage->nr + try_lno <= img->nr) {
@@ -1977,12 +1979,12 @@ static int match_fragment(struct image *img,
* use the whitespace from the preimage.
*/
extra_chars = preimage_end - preimage_eof;
- fixed_buf = xmalloc(imgoff + extra_chars);
- memcpy(fixed_buf, img->buf + try, imgoff);
- memcpy(fixed_buf + imgoff, preimage_eof, extra_chars);
- imgoff += extra_chars;
+ strbuf_init(&fixed, imgoff + extra_chars);
+ strbuf_add(&fixed, img->buf + try, imgoff);
+ strbuf_add(&fixed, preimage_eof, extra_chars);
+ fixed_buf = strbuf_detach(&fixed, &fixed_len);
update_pre_post_images(preimage, postimage,
- fixed_buf, imgoff, postlen);
+ fixed_buf, fixed_len, postlen);
return 1;
}
@@ -1999,27 +2001,22 @@ static int match_fragment(struct image *img,
* but in this loop we will only handle the part of the
* preimage that falls within the file.
*/
- fixed_buf = xmalloc(preimage->len + 1);
- buf = fixed_buf;
+ strbuf_init(&fixed, preimage->len + 1);
orig = preimage->buf;
target = img->buf + try;
for (i = 0; i < preimage_limit; i++) {
- size_t fixlen; /* length after fixing the preimage */
size_t oldlen = preimage->line[i].len;
size_t tgtlen = img->line[try_lno + i].len;
- size_t tgtfixlen; /* length after fixing the target line */
- char tgtfixbuf[1024], *tgtfix;
+ size_t fixstart = fixed.len;
+ struct strbuf tgtfix;
int match;
/* Try fixing the line in the preimage */
- fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+ ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
/* Try fixing the line in the target */
- if (sizeof(tgtfixbuf) > tgtlen)
- tgtfix = tgtfixbuf;
- else
- tgtfix = xmalloc(tgtlen);
- tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
+ strbuf_init(&tgtfix, tgtlen);
+ ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL);
/*
* If they match, either the preimage was based on
@@ -2031,15 +2028,15 @@ static int match_fragment(struct image *img,
* so we might as well take the fix together with their
* real change.
*/
- match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
+ match = (tgtfix.len == fixed.len - fixstart &&
+ !memcmp(tgtfix.buf, fixed.buf + fixstart,
+ fixed.len - fixstart));
- if (tgtfix != tgtfixbuf)
- free(tgtfix);
+ strbuf_release(&tgtfix);
if (!match)
goto unmatch_exit;
orig += oldlen;
- buf += fixlen;
target += tgtlen;
}
@@ -2051,19 +2048,18 @@ static int match_fragment(struct image *img,
* false).
*/
for ( ; i < preimage->nr; i++) {
- size_t fixlen; /* length after fixing the preimage */
+ size_t fixstart = fixed.len; /* start of the fixed preimage */
size_t oldlen = preimage->line[i].len;
int j;
/* Try fixing the line in the preimage */
- fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+ ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
- for (j = 0; j < fixlen; j++)
- if (!isspace(buf[j]))
+ for (j = fixstart; j < fixed.len; j++)
+ if (!isspace(fixed.buf[j]))
goto unmatch_exit;
orig += oldlen;
- buf += fixlen;
}
/*
@@ -2071,12 +2067,13 @@ static int match_fragment(struct image *img,
* has whitespace breakages unfixed, and fixing them makes the
* hunk match. Update the context lines in the postimage.
*/
+ fixed_buf = strbuf_detach(&fixed, &fixed_len);
update_pre_post_images(preimage, postimage,
- fixed_buf, buf - fixed_buf, 0);
+ fixed_buf, fixed_len, 0);
return 1;
unmatch_exit:
- free(fixed_buf);
+ strbuf_release(&fixed);
return 0;
}
@@ -2244,7 +2241,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
int match_beginning, match_end;
const char *patch = frag->patch;
int size = frag->size;
- char *old, *new, *oldlines, *newlines;
+ char *old, *oldlines;
+ struct strbuf newlines;
int new_blank_lines_at_end = 0;
unsigned long leading, trailing;
int pos, applied_pos;
@@ -2254,16 +2252,16 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
memset(&preimage, 0, sizeof(preimage));
memset(&postimage, 0, sizeof(postimage));
oldlines = xmalloc(size);
- newlines = xmalloc(size);
+ strbuf_init(&newlines, size);
old = oldlines;
- new = newlines;
while (size > 0) {
char first;
int len = linelen(patch, size);
- int plen, added;
+ int plen;
int added_blank_line = 0;
int is_blank_context = 0;
+ size_t start;
if (!len)
break;
@@ -2293,7 +2291,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
/* ... followed by '\No newline'; nothing */
break;
*old++ = '\n';
- *new++ = '\n';
+ strbuf_addch(&newlines, '\n');
add_line_info(&preimage, "\n", 1, LINE_COMMON);
add_line_info(&postimage, "\n", 1, LINE_COMMON);
is_blank_context = 1;
@@ -2315,18 +2313,17 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
if (first == '+' && no_add)
break;
+ start = newlines.len;
if (first != '+' ||
!whitespace_error ||
ws_error_action != correct_ws_error) {
- memcpy(new, patch + 1, plen);
- added = plen;
+ strbuf_add(&newlines, patch + 1, plen);
}
else {
- added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+ ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
}
- add_line_info(&postimage, new, added,
+ add_line_info(&postimage, newlines.buf + start, newlines.len - start,
(first == '+' ? 0 : LINE_COMMON));
- new += added;
if (first == '+' &&
(ws_rule & WS_BLANK_AT_EOF) &&
ws_blank_line(patch + 1, plen, ws_rule))
@@ -2351,9 +2348,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
}
if (inaccurate_eof &&
old > oldlines && old[-1] == '\n' &&
- new > newlines && new[-1] == '\n') {
+ newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
old--;
- new--;
+ strbuf_setlen(&newlines, newlines.len - 1);
}
leading = frag->leading;
@@ -2385,8 +2382,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
pos = frag->newpos ? (frag->newpos - 1) : 0;
preimage.buf = oldlines;
preimage.len = old - oldlines;
- postimage.buf = newlines;
- postimage.len = new - newlines;
+ postimage.buf = newlines.buf;
+ postimage.len = newlines.len;
preimage.line = preimage.line_allocated;
postimage.line = postimage.line_allocated;
@@ -2462,7 +2459,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
}
free(oldlines);
- free(newlines);
+ strbuf_release(&newlines);
free(preimage.line_allocated);
free(postimage.line_allocated);
@@ -2631,7 +2628,7 @@ static struct patch *in_fn_table(const char *name)
if (name == NULL)
return NULL;
- item = string_list_lookup(name, &fn_table);
+ item = string_list_lookup(&fn_table, name);
if (item != NULL)
return (struct patch *)item->util;
@@ -2667,7 +2664,7 @@ static void add_to_fn_table(struct patch *patch)
* file creations and copies
*/
if (patch->new_name != NULL) {
- item = string_list_insert(patch->new_name, &fn_table);
+ item = string_list_insert(&fn_table, patch->new_name);
item->util = patch;
}
@@ -2676,7 +2673,7 @@ static void add_to_fn_table(struct patch *patch)
* later chunks shouldn't patch old names
*/
if ((patch->new_name == NULL) || (patch->is_rename)) {
- item = string_list_insert(patch->old_name, &fn_table);
+ item = string_list_insert(&fn_table, patch->old_name);
item->util = PATH_WAS_DELETED;
}
}
@@ -2689,7 +2686,7 @@ static void prepare_fn_table(struct patch *patch)
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 = string_list_insert(&fn_table, patch->old_name);
item->util = PATH_TO_BE_DELETED;
}
patch = patch->next;
@@ -3397,7 +3394,7 @@ static void add_name_limit(const char *name, int exclude)
{
struct string_list_item *it;
- it = string_list_append(name, &limit_by_name);
+ it = string_list_append(&limit_by_name, name);
it->util = exclude ? NULL : (void *) 1;
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 8506286..01e62fd 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -20,6 +20,7 @@
#include "mailmap.h"
#include "parse-options.h"
#include "utf8.h"
+#include "userdiff.h"
static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
@@ -86,16 +87,50 @@ struct origin {
};
/*
+ * Prepare diff_filespec and convert it using diff textconv API
+ * if the textconv driver exists.
+ * Return 1 if the conversion succeeds, 0 otherwise.
+ */
+int textconv_object(const char *path,
+ const unsigned char *sha1,
+ char **buf,
+ unsigned long *buf_size)
+{
+ struct diff_filespec *df;
+ struct userdiff_driver *textconv;
+
+ df = alloc_filespec(path);
+ fill_filespec(df, sha1, S_IFREG | 0664);
+ textconv = get_textconv(df);
+ if (!textconv) {
+ free_filespec(df);
+ return 0;
+ }
+
+ *buf_size = fill_textconv(textconv, df, buf);
+ free_filespec(df);
+ return 1;
+}
+
+/*
* Given an origin, prepare mmfile_t structure to be used by the
* diff machinery
*/
-static void fill_origin_blob(struct origin *o, mmfile_t *file)
+static void fill_origin_blob(struct diff_options *opt,
+ struct origin *o, mmfile_t *file)
{
if (!o->file.ptr) {
enum object_type type;
+ unsigned long file_size;
+
num_read_blob++;
- file->ptr = read_sha1_file(o->blob_sha1, &type,
- (unsigned long *)(&(file->size)));
+ if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+ textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size))
+ ;
+ else
+ file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
+ file->size = file_size;
+
if (!file->ptr)
die("Cannot read blob %s for path %s",
sha1_to_hex(o->blob_sha1),
@@ -282,7 +317,6 @@ static struct origin *get_origin(struct scoreboard *sb,
static int fill_blob_sha1(struct origin *origin)
{
unsigned mode;
-
if (!is_null_sha1(origin->blob_sha1))
return 0;
if (get_tree_entry(origin->commit->object.sha1,
@@ -733,16 +767,17 @@ static int pass_blame_to_parent(struct scoreboard *sb,
{
int last_in_target;
mmfile_t file_p, file_o;
- struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
+ struct blame_chunk_cb_data d;
xpparam_t xpp;
xdemitconf_t xecfg;
-
+ memset(&d, 0, sizeof(d));
+ d.sb = sb; d.target = target; d.parent = parent;
last_in_target = find_last_in_target(sb, target);
if (last_in_target < 0)
return 1; /* nothing remains for this target */
- fill_origin_blob(parent, &file_p);
- fill_origin_blob(target, &file_o);
+ fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
+ fill_origin_blob(&sb->revs->diffopt, target, &file_o);
num_get_patch++;
memset(&xpp, 0, sizeof(xpp));
@@ -875,10 +910,11 @@ static void find_copy_in_blob(struct scoreboard *sb,
const char *cp;
int cnt;
mmfile_t file_o;
- struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
+ struct handle_split_cb_data d;
xpparam_t xpp;
xdemitconf_t xecfg;
-
+ memset(&d, 0, sizeof(d));
+ d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
/*
* Prepare mmfile that contains only the lines in ent.
*/
@@ -922,7 +958,7 @@ static int find_move_in_parent(struct scoreboard *sb,
if (last_in_target < 0)
return 1; /* nothing remains for this target */
- fill_origin_blob(parent, &file_p);
+ fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
if (!file_p.ptr)
return 0;
@@ -1063,7 +1099,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
norigin = get_origin(sb, parent, p->one->path);
hashcpy(norigin->blob_sha1, p->one->sha1);
- fill_origin_blob(norigin, &file_p);
+ fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
if (!file_p.ptr)
continue;
@@ -1983,6 +2019,16 @@ static int git_blame_config(const char *var, const char *value, void *cb)
blame_date_mode = parse_date_format(value);
return 0;
}
+
+ switch (userdiff_config(var, value)) {
+ case 0:
+ break;
+ case -1:
+ return -1;
+ default:
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
@@ -1990,7 +2036,9 @@ static int git_blame_config(const char *var, const char *value, void *cb)
* Prepare a dummy commit that represents the work tree (or staged) item.
* Note that annotating work tree item never works in the reverse.
*/
-static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
+static struct commit *fake_working_tree_commit(struct diff_options *opt,
+ const char *path,
+ const char *contents_from)
{
struct commit *commit;
struct origin *origin;
@@ -2018,6 +2066,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
if (!contents_from || strcmp("-", contents_from)) {
struct stat st;
const char *read_from;
+ unsigned long buf_len;
if (contents_from) {
if (stat(contents_from, &st) < 0)
@@ -2030,9 +2079,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
read_from = path;
}
mode = canon_mode(st.st_mode);
+
switch (st.st_mode & S_IFMT) {
case S_IFREG:
- if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
+ if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+ textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
+ buf.len = buf_len;
+ else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
die_errno("cannot open or read '%s'", read_from);
break;
case S_IFLNK:
@@ -2248,6 +2301,7 @@ 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;
+ DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
save_commit_buffer = 0;
dashdash_pos = 0;
@@ -2384,7 +2438,8 @@ parse_done:
* or "--contents".
*/
setup_work_tree();
- sb.final = fake_working_tree_commit(path, contents_from);
+ sb.final = fake_working_tree_commit(&sb.revs->diffopt,
+ path, contents_from);
add_pending_object(&revs, &(sb.final->object), ":");
}
else if (contents_from)
@@ -2411,8 +2466,14 @@ parse_done:
if (fill_blob_sha1(o))
die("no such path %s in %s", path, final_commit_name);
- sb.final_buf = read_sha1_file(o->blob_sha1, &type,
- &sb.final_buf_size);
+ if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
+ textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
+ &sb.final_buf_size))
+ ;
+ else
+ sb.final_buf = read_sha1_file(o->blob_sha1, &type,
+ &sb.final_buf_size);
+
if (!sb.final_buf)
die("Cannot read blob %s for path %s",
sha1_to_hex(o->blob_sha1),
diff --git a/builtin/branch.c b/builtin/branch.c
index 6cf7e72..87976f0 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -43,13 +43,13 @@ enum color_branch {
BRANCH_COLOR_PLAIN = 1,
BRANCH_COLOR_REMOTE = 2,
BRANCH_COLOR_LOCAL = 3,
- BRANCH_COLOR_CURRENT = 4,
+ BRANCH_COLOR_CURRENT = 4
};
static enum merge_filter {
NO_FILTER = 0,
SHOW_NOT_MERGED,
- SHOW_MERGED,
+ SHOW_MERGED
} merge_filter;
static unsigned char merge_filter_ref[20];
@@ -257,9 +257,15 @@ static char *resolve_symref(const char *src, const char *prefix)
return xstrdup(dst);
}
+struct append_ref_cb {
+ struct ref_list *ref_list;
+ int ret;
+};
+
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 append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
+ struct ref_list *ref_list = cb->ref_list;
struct ref_item *newitem;
struct commit *commit;
int kind, i;
@@ -293,8 +299,10 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
commit = NULL;
if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
commit = lookup_commit_reference_gently(sha1, 1);
- if (!commit)
- return error("branch '%s' does not point at a commit", refname);
+ if (!commit) {
+ cb->ret = error("branch '%s' does not point at a commit", refname);
+ return 0;
+ }
/* Filter with with_commit if specified */
if (!is_descendant_of(commit, ref_list->with_commit))
@@ -484,9 +492,10 @@ static void show_detached(struct ref_list *ref_list)
}
}
-static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
{
int i;
+ struct append_ref_cb cb;
struct ref_list ref_list;
memset(&ref_list, 0, sizeof(ref_list));
@@ -496,7 +505,9 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
ref_list.with_commit = with_commit;
if (merge_filter != NO_FILTER)
init_revisions(&ref_list.revs, NULL);
- for_each_rawref(append_ref, &ref_list);
+ cb.ref_list = &ref_list;
+ cb.ret = 0;
+ for_each_rawref(append_ref, &cb);
if (merge_filter != NO_FILTER) {
struct commit *filter;
filter = lookup_commit_reference_gently(merge_filter_ref, 0);
@@ -527,6 +538,11 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
}
free_ref_list(&ref_list);
+
+ if (cb.ret)
+ error("some refs could not be read");
+
+ return cb.ret;
}
static void rename_branch(const char *oldname, const char *newname, int force)
@@ -679,7 +695,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (delete)
return delete_branches(argc, argv, delete > 1, kinds);
else if (argc == 0)
- print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+ return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
else if (rename && (argc == 1))
rename_branch(head, argv[0], rename > 1);
else if (rename && (argc == 2))
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index a933eaa..76ec3fe 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -9,6 +9,8 @@
#include "tree.h"
#include "builtin.h"
#include "parse-options.h"
+#include "diff.h"
+#include "userdiff.h"
#define BATCH 1
#define BATCH_CHECK 2
@@ -84,10 +86,11 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
{
unsigned char sha1[20];
enum object_type type;
- void *buf;
+ char *buf;
unsigned long size;
+ struct object_context obj_context;
- if (get_sha1(obj_name, sha1))
+ if (get_sha1_with_context(obj_name, sha1, &obj_context))
die("Not a valid object name %s", obj_name);
buf = NULL;
@@ -118,7 +121,9 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
/* custom pretty-print here */
if (type == OBJ_TREE) {
- const char *ls_args[3] = {"ls-tree", obj_name, NULL};
+ const char *ls_args[3] = { NULL };
+ ls_args[0] = "ls-tree";
+ ls_args[1] = obj_name;
return cmd_ls_tree(2, ls_args, NULL);
}
@@ -132,6 +137,17 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
/* otherwise just spit out the data */
break;
+
+ case 'c':
+ if (!obj_context.path[0])
+ die("git cat-file --textconv %s: <object> must be <sha1:path>",
+ obj_name);
+
+ if (!textconv_object(obj_context.path, sha1, &buf, &size))
+ die("git cat-file --textconv: unable to run textconv on %s",
+ obj_name);
+ break;
+
case 0:
buf = read_object_with_reference(sha1, exp_type, &size, NULL);
break;
@@ -201,11 +217,25 @@ static int batch_objects(int print_contents)
}
static const char * const cat_file_usage[] = {
- "git cat-file (-t|-s|-e|-p|<type>) <object>",
+ "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>",
"git cat-file (--batch|--batch-check) < <list_of_objects>",
NULL
};
+static int git_cat_file_config(const char *var, const char *value, void *cb)
+{
+ switch (userdiff_config(var, value)) {
+ case 0:
+ break;
+ case -1:
+ return -1;
+ default:
+ return 0;
+ }
+
+ return git_default_config(var, value, cb);
+}
+
int cmd_cat_file(int argc, const char **argv, const char *prefix)
{
int opt = 0, batch = 0;
@@ -218,6 +248,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_SET_INT('e', NULL, &opt,
"exit with zero when there's no error", 'e'),
OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'),
+ OPT_SET_INT(0, "textconv", &opt,
+ "for blob objects, run textconv on object's content", 'c'),
OPT_SET_INT(0, "batch", &batch,
"show info and content of objects fed from the standard input",
BATCH),
@@ -227,7 +259,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
OPT_END()
};
- git_config(git_default_config, NULL);
+ git_config(git_cat_file_config, NULL);
if (argc != 3 && argc != 2)
usage_with_options(cat_file_usage, options);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 88b1f43..1994be9 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -33,6 +33,7 @@ struct checkout_opts {
int writeout_error;
const char *new_branch;
+ const char *new_orphan_branch;
int new_branch_log;
enum branch_track track;
};
@@ -492,8 +493,26 @@ static void update_refs_for_switch(struct checkout_opts *opts,
struct strbuf msg = STRBUF_INIT;
const char *old_desc;
if (opts->new_branch) {
- create_branch(old->name, opts->new_branch, new->name, 0,
- opts->new_branch_log, opts->track);
+ if (opts->new_orphan_branch) {
+ if (opts->new_branch_log && !log_all_ref_updates) {
+ int temp;
+ char log_file[PATH_MAX];
+ char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+
+ temp = log_all_ref_updates;
+ log_all_ref_updates = 1;
+ if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
+ fprintf(stderr, "Can not do reflog for '%s'\n",
+ opts->new_orphan_branch);
+ log_all_ref_updates = temp;
+ return;
+ }
+ log_all_ref_updates = temp;
+ }
+ }
+ else
+ create_branch(old->name, opts->new_branch, new->name, 0,
+ opts->new_branch_log, opts->track);
new->name = opts->new_branch;
setup_branch_path(new);
}
@@ -515,6 +534,14 @@ static void update_refs_for_switch(struct checkout_opts *opts,
opts->new_branch ? " a new" : "",
new->name);
}
+ if (old->path && old->name) {
+ char log_file[PATH_MAX], ref_file[PATH_MAX];
+
+ git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
+ git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
+ if (!file_exists(ref_file) && file_exists(log_file))
+ remove_path(log_file);
+ }
} else if (strcmp(new->name, "HEAD")) {
update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
REF_NODEREF, DIE_ON_ERR);
@@ -609,7 +636,8 @@ static int check_tracking_name(const char *refname, const unsigned char *sha1,
static const char *unique_tracking_name(const char *name)
{
- struct tracking_name_data cb_data = { name, NULL, 1 };
+ struct tracking_name_data cb_data = { NULL, NULL, 1 };
+ cb_data.name = name;
for_each_ref(check_tracking_name, &cb_data);
if (cb_data.unique)
return cb_data.remote;
@@ -633,6 +661,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
OPT_SET_INT('t', "track", &opts.track, "track",
BRANCH_TRACK_EXPLICIT),
+ OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
2),
OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
@@ -678,6 +707,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
opts.new_branch = argv0 + 1;
}
+ if (opts.new_orphan_branch) {
+ if (opts.new_branch)
+ die("--orphan and -b are mutually exclusive");
+ if (opts.track > 0)
+ die("--orphan cannot be used with -t");
+ opts.new_branch = opts.new_orphan_branch;
+ }
+
if (conflict_style) {
opts.merge = 1; /* implied */
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
diff --git a/builtin/clone.c b/builtin/clone.c
index 3a3625b..efb1e6f 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -475,9 +475,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
*/
unsetenv(CONFIG_ENVIRONMENT);
- if (option_reference)
- setup_reference(git_dir);
-
git_config(git_default_config, NULL);
if (option_bare) {
@@ -503,12 +500,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
git_config_set(key.buf, "true");
strbuf_reset(&key);
}
-
- strbuf_addf(&key, "remote.%s.url", option_origin);
- git_config_set(key.buf, repo);
- strbuf_reset(&key);
}
+ strbuf_addf(&key, "remote.%s.url", option_origin);
+ git_config_set(key.buf, repo);
+ strbuf_reset(&key);
+
+ if (option_reference)
+ setup_reference(git_dir);
+
fetch_pattern = value.buf;
refspec = parse_fetch_refspec(1, &fetch_pattern);
@@ -518,7 +518,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
refs = clone_local(path, git_dir);
mapped_refs = wanted_peer_refs(refs, refspec);
} else {
- struct remote *remote = remote_get(argv[0]);
+ struct remote *remote = remote_get(option_origin);
transport = transport_get(remote, remote->url[0]);
if (!transport->get_refs_list || !transport->fetch)
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 90dac34..87f0591 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -9,19 +9,6 @@
#include "builtin.h"
#include "utf8.h"
-/*
- * FIXME! Share the code with "write-tree.c"
- */
-static void check_valid(unsigned char *sha1, enum object_type expect)
-{
- enum object_type type = sha1_object_info(sha1, NULL);
- if (type < 0)
- die("%s is not a valid object", sha1_to_hex(sha1));
- if (type != expect)
- die("%s is not a valid '%s' object", sha1_to_hex(sha1),
- typename(expect));
-}
-
static const char commit_tree_usage[] = "git commit-tree <sha1> [-p <sha1>]* < changelog";
static void new_parent(struct commit *parent, struct commit_list **parents_p)
@@ -38,61 +25,6 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p)
commit_list_insert(parent, parents_p);
}
-static const char commit_utf8_warn[] =
-"Warning: commit message does not conform to UTF-8.\n"
-"You may want to amend it after fixing the message, or set the config\n"
-"variable i18n.commitencoding to the encoding your project uses.\n";
-
-int commit_tree(const char *msg, unsigned char *tree,
- struct commit_list *parents, unsigned char *ret,
- const char *author)
-{
- int result;
- int encoding_is_utf8;
- struct strbuf buffer;
-
- check_valid(tree, OBJ_TREE);
-
- /* Not having i18n.commitencoding is the same as having utf-8 */
- encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
-
- strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
- strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
-
- /*
- * NOTE! This ordering means that the same exact tree merged with a
- * different order of parents will be a _different_ changeset even
- * if everything else stays the same.
- */
- while (parents) {
- struct commit_list *next = parents->next;
- strbuf_addf(&buffer, "parent %s\n",
- sha1_to_hex(parents->item->object.sha1));
- free(parents);
- parents = next;
- }
-
- /* Person/date information */
- if (!author)
- author = git_author_info(IDENT_ERROR_ON_NO_NAME);
- strbuf_addf(&buffer, "author %s\n", author);
- strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
- if (!encoding_is_utf8)
- strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
- strbuf_addch(&buffer, '\n');
-
- /* And add the comment */
- strbuf_addstr(&buffer, msg);
-
- /* And check the encoding */
- if (encoding_is_utf8 && !is_utf8(buffer.buf))
- fprintf(stderr, commit_utf8_warn);
-
- result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
- strbuf_release(&buffer);
- return result;
-}
-
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
{
int i;
@@ -117,7 +49,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
if (get_sha1(b, sha1))
die("Not a valid object name %s", b);
- check_valid(sha1, OBJ_COMMIT);
+ assert_sha1_type(sha1, OBJ_COMMIT);
new_parent(lookup_commit(sha1), &parents);
}
diff --git a/builtin/commit.c b/builtin/commit.c
index da18287..a78dbd8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -62,7 +62,7 @@ static struct lock_file false_lock; /* used only for partial commits */
static enum {
COMMIT_AS_IS = 1,
COMMIT_NORMAL,
- COMMIT_PARTIAL,
+ COMMIT_PARTIAL
} commit_style;
static const char *logfile, *force_author;
@@ -71,8 +71,8 @@ static char *edit_message, *use_message;
static char *author_name, *author_email, *author_date;
static int all, edit_flag, also, interactive, only, amend, signoff;
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
-static int no_post_rewrite;
-static char *untracked_files_arg, *force_date;
+static int no_post_rewrite, allow_empty_message;
+static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
/*
* The default commit message cleanup mode will remove the lines
* beginning with # (shell comments) and leading and trailing
@@ -83,11 +83,12 @@ static char *untracked_files_arg, *force_date;
static enum {
CLEANUP_SPACE,
CLEANUP_NONE,
- CLEANUP_ALL,
+ CLEANUP_ALL
} cleanup_mode;
static char *cleanup_arg;
static int use_editor = 1, initial_commit, in_merge, include_status = 1;
+static int show_ignored_in_status;
static const char *only_include_assumed;
static struct strbuf message;
@@ -95,8 +96,9 @@ static int null_termination;
static enum {
STATUS_FORMAT_LONG,
STATUS_FORMAT_SHORT,
- STATUS_FORMAT_PORCELAIN,
+ STATUS_FORMAT_PORCELAIN
} status_format = STATUS_FORMAT_LONG;
+static int status_show_branch;
static int opt_parse_m(const struct option *opt, const char *arg, int unset)
{
@@ -138,6 +140,7 @@ static struct option builtin_commit_options[] = {
OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
OPT_SET_INT(0, "short", &status_format, "show status concisely",
STATUS_FORMAT_SHORT),
+ OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
OPT_SET_INT(0, "porcelain", &status_format,
"show porcelain output format", STATUS_FORMAT_PORCELAIN),
OPT_BOOLEAN('z', "null", &null_termination,
@@ -145,9 +148,15 @@ static struct option builtin_commit_options[] = {
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
- OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
/* end commit contents options */
+ { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
+ "ok to record an empty change",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+ { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
+ "ok to record a change with an empty message",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+
OPT_END()
};
@@ -210,7 +219,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
continue;
if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
continue;
- item = string_list_insert(ce->name, list);
+ item = string_list_insert(list, ce->name);
if (ce_skip_worktree(ce))
item->util = item; /* better a valid pointer than a fake one */
}
@@ -426,7 +435,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
switch (status_format) {
case STATUS_FORMAT_SHORT:
- wt_shortstatus_print(s, null_termination);
+ wt_shortstatus_print(s, null_termination, status_show_branch);
break;
case STATUS_FORMAT_PORCELAIN:
wt_porcelain_print(s, null_termination);
@@ -734,7 +743,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (use_editor) {
char index[PATH_MAX];
- const char *env[2] = { index, NULL };
+ const char *env[2] = { NULL };
+ env[0] = index;
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
if (launch_editor(git_path(commit_editmsg), NULL, env)) {
fprintf(stderr,
@@ -1040,6 +1050,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
OPT__VERBOSE(&verbose),
OPT_SET_INT('s', "short", &status_format,
"show status concisely", STATUS_FORMAT_SHORT),
+ OPT_BOOLEAN('b', "branch", &status_show_branch,
+ "show branch information"),
OPT_SET_INT(0, "porcelain", &status_format,
"show porcelain output format",
STATUS_FORMAT_PORCELAIN),
@@ -1049,6 +1061,11 @@ int cmd_status(int argc, const char **argv, const char *prefix)
"mode",
"show untracked files, optional modes: all, normal, no. (Default: all)",
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+ OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
+ "show ignored files"),
+ { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
+ "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
+ PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
OPT_END(),
};
@@ -1062,7 +1079,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
builtin_status_options,
builtin_status_usage, 0);
handle_untracked_files_arg(&s);
-
+ if (show_ignored_in_status)
+ s.show_ignored_files = 1;
if (*argv)
s.pathspec = get_pathspec(prefix, argv);
@@ -1080,6 +1098,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
s.in_merge = in_merge;
+ s.ignore_submodule_arg = ignore_submodule_arg;
wt_status_collect(&s);
if (s.relative_paths)
@@ -1091,13 +1110,14 @@ int cmd_status(int argc, const char **argv, const char *prefix)
switch (status_format) {
case STATUS_FORMAT_SHORT:
- wt_shortstatus_print(&s, null_termination);
+ wt_shortstatus_print(&s, null_termination, status_show_branch);
break;
case STATUS_FORMAT_PORCELAIN:
wt_porcelain_print(&s, null_termination);
break;
case STATUS_FORMAT_LONG:
s.verbose = verbose;
+ s.ignore_submodule_arg = ignore_submodule_arg;
wt_status_print(&s);
break;
}
@@ -1324,7 +1344,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (cleanup_mode != CLEANUP_NONE)
stripspace(&sb, cleanup_mode == CLEANUP_ALL);
- if (message_is_empty(&sb)) {
+ if (message_is_empty(&sb) && !allow_empty_message) {
rollback_index_files();
fprintf(stderr, "Aborting commit due to empty commit message.\n");
exit(1);
diff --git a/builtin/config.c b/builtin/config.c
index 4bc46b1..f3d1660 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -197,7 +197,11 @@ static int get_value(const char *key_, const char *regex_)
git_config_from_file(show_config, system_wide, NULL);
if (do_all && global)
git_config_from_file(show_config, global, NULL);
- git_config_from_file(show_config, local, NULL);
+ if (do_all)
+ git_config_from_file(show_config, local, NULL);
+ git_config_from_parameters(show_config, NULL);
+ if (!do_all && !seen)
+ git_config_from_file(show_config, local, NULL);
if (!do_all && !seen && global)
git_config_from_file(show_config, global, NULL);
if (!do_all && !seen && system_wide)
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index c6dd71a..9fe25ff 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -438,7 +438,7 @@ static void get_tags_and_duplicates(struct object_array *pending,
/* handle nested tags */
while (tag && tag->object.type == OBJ_TAG) {
parse_object(tag->object.sha1);
- string_list_append(full_name, extra_refs)->util = tag;
+ string_list_append(extra_refs, full_name)->util = tag;
tag = (struct tag *)tag->tagged;
}
if (!tag)
@@ -464,7 +464,7 @@ static void get_tags_and_duplicates(struct object_array *pending,
}
if (commit->util)
/* more than one name for the same object */
- string_list_append(full_name, extra_refs)->util = commit;
+ string_list_append(extra_refs, full_name)->util = commit;
else
commit->util = full_name;
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 8470850..6eb1dfe 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -528,7 +528,7 @@ static int add_existing(const char *refname, const unsigned char *sha1,
int flag, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
- struct string_list_item *item = string_list_insert(refname, list);
+ struct string_list_item *item = string_list_insert(list, refname);
item->util = (void *)sha1;
return 0;
}
@@ -574,9 +574,10 @@ static void find_non_local_tags(struct transport *transport,
{
struct string_list existing_refs = { NULL, 0, 0, 0 };
struct string_list remote_refs = { NULL, 0, 0, 0 };
- struct tag_data data = {head, tail};
+ struct tag_data data;
const struct ref *ref;
struct string_list_item *item = NULL;
+ data.head = head; data.tail = tail;
for_each_ref(add_existing, &existing_refs);
for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
@@ -616,7 +617,7 @@ static void find_non_local_tags(struct transport *transport,
string_list_has_string(&existing_refs, ref->name))
continue;
- item = string_list_insert(ref->name, &remote_refs);
+ item = string_list_insert(&remote_refs, ref->name);
item->util = (void *)ref->old_sha1;
}
string_list_clear(&existing_refs, 0);
@@ -633,7 +634,7 @@ static void find_non_local_tags(struct transport *transport,
* For all the tags in the remote_refs string list, call
* add_to_tail to add them to the list of refs to be fetched
*/
- for_each_string_list(add_to_tail, &remote_refs, &data);
+ for_each_string_list(&remote_refs, add_to_tail, &data);
string_list_clear(&remote_refs, 0);
}
@@ -695,8 +696,8 @@ static int do_fetch(struct transport *transport,
for (rm = ref_map; rm; rm = rm->next) {
if (rm->peer_ref) {
- peer_item = string_list_lookup(rm->peer_ref->name,
- &existing_refs);
+ peer_item = string_list_lookup(&existing_refs,
+ rm->peer_ref->name);
if (peer_item)
hashcpy(rm->peer_ref->old_sha1,
peer_item->util);
@@ -745,7 +746,7 @@ static int get_one_remote_for_fetch(struct remote *remote, void *priv)
{
struct string_list *list = priv;
if (!remote->skip_default_update)
- string_list_append(remote->name, list);
+ string_list_append(list, remote->name);
return 0;
}
@@ -764,8 +765,8 @@ static int get_remote_group(const char *key, const char *value, void *priv)
int space = strcspn(value, " \t\n");
while (*value) {
if (space > 1) {
- string_list_append(xstrndup(value, space),
- g->list);
+ string_list_append(g->list,
+ xstrndup(value, space));
}
value += space + (value[space] != '\0');
space = strcspn(value, " \t\n");
@@ -778,7 +779,8 @@ static int get_remote_group(const char *key, const char *value, void *priv)
static int add_remote_or_group(const char *name, struct string_list *list)
{
int prev_nr = list->nr;
- struct remote_group_data g = { name, list };
+ struct remote_group_data g;
+ g.name = name; g.list = list;
git_config(get_remote_group, &g);
if (list->nr == prev_nr) {
@@ -786,7 +788,7 @@ static int add_remote_or_group(const char *name, struct string_list *list)
if (!remote_is_configured(name))
return 0;
remote = remote_get(name);
- string_list_append(remote->name, list);
+ string_list_append(list, remote->name);
}
return 1;
}
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 4420425..bc3c5e6 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -82,7 +82,7 @@ static int handle_line(char *line)
item = unsorted_string_list_lookup(&srcs, src);
if (!item) {
- item = string_list_append(src, &srcs);
+ item = string_list_append(&srcs, src);
item->util = xcalloc(1, sizeof(struct src_data));
init_src_data(item->util);
}
@@ -93,19 +93,19 @@ static int handle_line(char *line)
src_data->head_status |= 1;
} else if (!prefixcmp(line, "branch ")) {
origin = line + 7;
- string_list_append(origin, &src_data->branch);
+ string_list_append(&src_data->branch, origin);
src_data->head_status |= 2;
} else if (!prefixcmp(line, "tag ")) {
origin = line;
- string_list_append(origin + 4, &src_data->tag);
+ string_list_append(&src_data->tag, origin + 4);
src_data->head_status |= 2;
} else if (!prefixcmp(line, "remote branch ")) {
origin = line + 14;
- string_list_append(origin, &src_data->r_branch);
+ string_list_append(&src_data->r_branch, origin);
src_data->head_status |= 2;
} else {
origin = src;
- string_list_append(line, &src_data->generic);
+ string_list_append(&src_data->generic, line);
src_data->head_status |= 2;
}
@@ -118,7 +118,7 @@ static int handle_line(char *line)
sprintf(new_origin, "%s of %s", origin, src);
origin = new_origin;
}
- string_list_append(origin, &origins)->util = sha1;
+ string_list_append(&origins, origin)->util = sha1;
return 0;
}
@@ -176,10 +176,10 @@ static void shortlog(const char *name, unsigned char *sha1,
strbuf_ltrim(&sb);
if (!sb.len)
- string_list_append(sha1_to_hex(commit->object.sha1),
- &subjects);
+ string_list_append(&subjects,
+ sha1_to_hex(commit->object.sha1));
else
- string_list_append(strbuf_detach(&sb, NULL), &subjects);
+ string_list_append(&subjects, strbuf_detach(&sb, NULL));
}
if (count > limit)
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 7f5011f..a2b28c6 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -227,6 +227,9 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
strcpy(s, sha1_to_hex(obj->sha1));
v->s = s;
}
+ else if (!strcmp(name, "objectname:short")) {
+ v->s = find_unique_abbrev(obj->sha1, DEFAULT_ABBREV);
+ }
}
}
diff --git a/builtin/grep.c b/builtin/grep.c
index b194ea3..232cd1c 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -11,6 +11,8 @@
#include "tree-walk.h"
#include "builtin.h"
#include "parse-options.h"
+#include "string-list.h"
+#include "run-command.h"
#include "userdiff.h"
#include "grep.h"
#include "quote.h"
@@ -556,6 +558,33 @@ static int grep_file(struct grep_opt *opt, const char *filename)
}
}
+static void append_path(struct grep_opt *opt, const void *data, size_t len)
+{
+ struct string_list *path_list = opt->output_priv;
+
+ if (len == 1 && *(const char *)data == '\0')
+ return;
+ string_list_append(path_list, xstrndup(data, len));
+}
+
+static void run_pager(struct grep_opt *opt, const char *prefix)
+{
+ struct string_list *path_list = opt->output_priv;
+ const char **argv = xmalloc(sizeof(const char *) * (path_list->nr + 1));
+ int i, status;
+
+ for (i = 0; i < path_list->nr; i++)
+ argv[i] = path_list->items[i].string;
+ argv[path_list->nr] = NULL;
+
+ if (prefix && chdir(prefix))
+ die("Failed to chdir: %s", prefix);
+ status = run_command_v_opt(argv, RUN_USING_SHELL);
+ if (status)
+ exit(status);
+ free(argv);
+}
+
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
{
int hit = 0;
@@ -590,7 +619,6 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
if (hit && opt->status_only)
break;
}
- free_grep_patterns(opt);
return hit;
}
@@ -675,6 +703,25 @@ static int grep_object(struct grep_opt *opt, const char **paths,
die("unable to grep from object of type %s", typename(obj->type));
}
+static int grep_objects(struct grep_opt *opt, const char **paths,
+ const struct object_array *list)
+{
+ unsigned int i;
+ int hit = 0;
+ const unsigned int nr = list->nr;
+
+ for (i = 0; i < nr; i++) {
+ struct object *real_obj;
+ real_obj = deref_tag(list->objects[i].item, NULL, 0);
+ if (grep_object(opt, paths, real_obj, list->objects[i].name)) {
+ hit = 1;
+ if (opt->status_only)
+ break;
+ }
+ }
+ return hit;
+}
+
static int grep_directory(struct grep_opt *opt, const char **paths)
{
struct dir_struct dir;
@@ -689,7 +736,6 @@ static int grep_directory(struct grep_opt *opt, const char **paths)
if (hit && opt->status_only)
break;
}
- free_grep_patterns(opt);
return hit;
}
@@ -724,11 +770,15 @@ static int file_callback(const struct option *opt, const char *arg, int unset)
if (!patterns)
die_errno("cannot open '%s'", arg);
while (strbuf_getline(&sb, patterns, '\n') == 0) {
+ char *s;
+ size_t len;
+
/* ignore empty line like grep does */
if (sb.len == 0)
continue;
- append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg,
- ++lno, GREP_PATTERN);
+
+ s = strbuf_detach(&sb, &len);
+ append_grep_pat(grep_opt, s, len, arg, ++lno, GREP_PATTERN);
}
fclose(patterns);
strbuf_release(&sb);
@@ -782,9 +832,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
int cached = 0;
int seen_dashdash = 0;
int external_grep_allowed__ignored;
+ const char *show_in_pager = NULL, *default_pager = "dummy";
struct grep_opt opt;
struct object_array list = { 0, 0, NULL };
const char **paths = NULL;
+ struct string_list path_list = { NULL, 0, 0, 0 };
int i;
int dummy;
int nongit = 0, use_index = 1;
@@ -868,6 +920,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN(0, "all-match", &opt.all_match,
"show only matches from files that match all patterns"),
OPT_GROUP(""),
+ { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
+ "pager", "show matching files in the pager",
+ PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored,
"allow calling of grep(1) (ignored by this build)"),
{ OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
@@ -943,6 +998,17 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
argc--;
}
+ if (show_in_pager == default_pager)
+ show_in_pager = git_pager(1);
+ if (show_in_pager) {
+ opt.name_only = 1;
+ opt.null_following_name = 1;
+ opt.output_priv = &path_list;
+ opt.output = append_path;
+ string_list_append(&path_list, show_in_pager);
+ use_threads = 0;
+ }
+
if (!opt.pattern_list)
die("no pattern given.");
if (!opt.fixed && opt.ignore_case)
@@ -999,44 +1065,51 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
paths[1] = NULL;
}
+ if (show_in_pager && (cached || list.nr))
+ die("--open-files-in-pager only works on the worktree");
+
+ if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) {
+ const char *pager = path_list.items[0].string;
+ int len = strlen(pager);
+
+ if (len > 4 && is_dir_sep(pager[len - 5]))
+ pager += len - 4;
+
+ if (!strcmp("less", pager) || !strcmp("vi", pager)) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addf(&buf, "+/%s%s",
+ strcmp("less", pager) ? "" : "*",
+ opt.pattern_list->pattern);
+ string_list_append(&path_list, buf.buf);
+ strbuf_detach(&buf, NULL);
+ }
+ }
+
+ if (!show_in_pager)
+ setup_pager();
+
+
if (!use_index) {
- int hit;
if (cached)
die("--cached cannot be used with --no-index.");
if (list.nr)
die("--no-index cannot be used with revs.");
hit = grep_directory(&opt, paths);
- if (use_threads)
- hit |= wait_all();
- return !hit;
- }
-
- if (!list.nr) {
- int hit;
+ } else if (!list.nr) {
if (!cached)
setup_work_tree();
hit = grep_cache(&opt, paths, cached);
- if (use_threads)
- hit |= wait_all();
- return !hit;
- }
-
- if (cached)
- die("both --cached and trees are given.");
-
- for (i = 0; i < list.nr; i++) {
- struct object *real_obj;
- real_obj = deref_tag(list.objects[i].item, NULL, 0);
- if (grep_object(&opt, paths, real_obj, list.objects[i].name)) {
- hit = 1;
- if (opt.status_only)
- break;
- }
+ } else {
+ if (cached)
+ die("both --cached and trees are given.");
+ hit = grep_objects(&opt, paths, &list);
}
if (use_threads)
hit |= wait_all();
+ if (hit && show_in_pager)
+ run_pager(&opt, prefix);
free_grep_patterns(&opt);
return !hit;
}
diff --git a/builtin/help.c b/builtin/help.c
index 3182a2b..a9836b00 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -26,7 +26,7 @@ enum help_format {
HELP_FORMAT_NONE,
HELP_FORMAT_MAN,
HELP_FORMAT_INFO,
- HELP_FORMAT_WEB,
+ HELP_FORMAT_WEB
};
static int show_all = 0;
diff --git a/builtin/log.c b/builtin/log.c
index 6208703..08b8722 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -24,6 +24,7 @@
static const char *default_date_mode = NULL;
static int default_show_root = 1;
+static int decoration_style;
static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
@@ -31,11 +32,28 @@ static const char * const builtin_log_usage =
"git log [<options>] [<since>..<until>] [[--] <path>...]\n"
" or: git show [options] <object>...";
+static int parse_decoration_style(const char *var, const char *value)
+{
+ switch (git_config_maybe_bool(var, value)) {
+ case 1:
+ return DECORATE_SHORT_REFS;
+ case 0:
+ return 0;
+ default:
+ break;
+ }
+ if (!strcmp(value, "full"))
+ return DECORATE_FULL_REFS;
+ else if (!strcmp(value, "short"))
+ return DECORATE_SHORT_REFS;
+ return -1;
+}
+
static void cmd_log_init(int argc, const char **argv, const char *prefix,
struct rev_info *rev, struct setup_revision_opt *opt)
{
int i;
- int decoration_style = 0;
+ int decoration_given = 0;
struct userformat_want w;
rev->abbrev = DEFAULT_ABBREV;
@@ -78,14 +96,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
const char *arg = argv[i];
if (!strcmp(arg, "--decorate")) {
decoration_style = DECORATE_SHORT_REFS;
+ decoration_given = 1;
} else if (!prefixcmp(arg, "--decorate=")) {
const char *v = skip_prefix(arg, "--decorate=");
- if (!strcmp(v, "full"))
- decoration_style = DECORATE_FULL_REFS;
- else if (!strcmp(v, "short"))
- decoration_style = DECORATE_SHORT_REFS;
- else
+ decoration_style = parse_decoration_style(arg, v);
+ if (decoration_style < 0)
die("invalid --decorate option: %s", arg);
+ decoration_given = 1;
+ } else if (!strcmp(arg, "--no-decorate")) {
+ decoration_style = 0;
} else if (!strcmp(arg, "--source")) {
rev->show_source = 1;
} else if (!strcmp(arg, "-h")) {
@@ -93,6 +112,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
} else
die("unrecognized argument: %s", arg);
}
+
+ /*
+ * defeat log.decorate configuration interacting with --pretty=raw
+ * from the command line.
+ */
+ if (!decoration_given && rev->pretty_given
+ && rev->commit_format == CMIT_FMT_RAW)
+ decoration_style = 0;
+
if (decoration_style) {
rev->show_decorations = 1;
load_ref_decorations(decoration_style);
@@ -258,10 +286,19 @@ static int git_log_config(const char *var, const char *value, void *cb)
return git_config_string(&fmt_patch_subject_prefix, var, value);
if (!strcmp(var, "log.date"))
return git_config_string(&default_date_mode, var, value);
+ if (!strcmp(var, "log.decorate")) {
+ decoration_style = parse_decoration_style(var, value);
+ if (decoration_style < 0)
+ decoration_style = 0; /* maybe warn? */
+ return 0;
+ }
if (!strcmp(var, "log.showroot")) {
default_show_root = git_config_bool(var, value);
return 0;
}
+ if (!prefixcmp(var, "color.decorate."))
+ return parse_decorate_color_config(var, 15, value);
+
return git_diff_ui_config(var, value, cb);
}
@@ -501,13 +538,13 @@ static void add_header(const char *value)
len--;
if (!strncasecmp(value, "to: ", 4)) {
- item = string_list_append(value + 4, &extra_to);
+ item = string_list_append(&extra_to, value + 4);
len -= 4;
} else if (!strncasecmp(value, "cc: ", 4)) {
- item = string_list_append(value + 4, &extra_cc);
+ item = string_list_append(&extra_cc, value + 4);
len -= 4;
} else {
- item = string_list_append(value, &extra_hdr);
+ item = string_list_append(&extra_hdr, value);
}
item->string[len] = '\0';
@@ -515,8 +552,9 @@ static void add_header(const char *value)
#define THREAD_SHALLOW 1
#define THREAD_DEEP 2
-static int thread = 0;
-static int do_signoff = 0;
+static int thread;
+static int do_signoff;
+static const char *signature = git_version_string;
static int git_format_config(const char *var, const char *value, void *cb)
{
@@ -531,13 +569,13 @@ static int git_format_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "format.to")) {
if (!value)
return config_error_nonbool(var);
- string_list_append(value, &extra_to);
+ string_list_append(&extra_to, value);
return 0;
}
if (!strcmp(var, "format.cc")) {
if (!value)
return config_error_nonbool(var);
- string_list_append(value, &extra_cc);
+ string_list_append(&extra_cc, value);
return 0;
}
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
@@ -575,6 +613,8 @@ static int git_format_config(const char *var, const char *value, void *cb)
do_signoff = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "format.signature"))
+ return git_config_string(&signature, var, value);
return git_log_config(var, value, cb);
}
@@ -669,6 +709,12 @@ static void gen_message_id(struct rev_info *info, char *base)
info->message_id = strbuf_detach(&buf, NULL);
}
+static void print_signature(void)
+{
+ if (signature && *signature)
+ printf("-- \n%s\n\n", signature);
+}
+
static void make_cover_letter(struct rev_info *rev, int use_stdout,
int numbered, int numbered_files,
struct commit *origin,
@@ -762,6 +808,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
diff_flush(&opts);
printf("\n");
+ print_signature();
}
static const char *clean_message_id(const char *msg_id)
@@ -915,7 +962,7 @@ static int to_callback(const struct option *opt, const char *arg, int unset)
if (unset)
string_list_clear(&extra_to, 0);
else
- string_list_append(arg, &extra_to);
+ string_list_append(&extra_to, arg);
return 0;
}
@@ -924,7 +971,7 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
if (unset)
string_list_clear(&extra_cc, 0);
else
- string_list_append(arg, &extra_cc);
+ string_list_append(&extra_cc, arg);
return 0;
}
@@ -1001,6 +1048,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 0, "thread", &thread, "style",
"enable message threading, styles: shallow, deep",
PARSE_OPT_OPTARG, thread_callback },
+ OPT_STRING(0, "signature", &signature, "signature",
+ "add a signature"),
OPT_END()
};
@@ -1205,7 +1254,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
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);
+ string_list_append(rev.ref_message_ids, msgid);
}
rev.numbered_files = numbered_files;
rev.patch_suffix = fmt_patch_suffix;
@@ -1252,8 +1301,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
&& (!cover_letter || rev.nr > 1))
free(rev.message_id);
else
- string_list_append(rev.message_id,
- rev.ref_message_ids);
+ string_list_append(rev.ref_message_ids,
+ rev.message_id);
}
gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
}
@@ -1279,7 +1328,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
mime_boundary_leader,
rev.mime_boundary);
else
- printf("-- \n%s\n\n", git_version_string);
+ print_signature();
}
if (!use_stdout)
fclose(stdout);
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 0804047..1b9b8a8 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -190,7 +190,7 @@ static void show_ru_info(void)
{
if (!the_index.resolve_undo)
return;
- for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
+ for_each_string_list(the_index.resolve_undo, show_one_ru, NULL);
}
static void show_files(struct dir_struct *dir)
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 70f5622..34480cf 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -4,7 +4,8 @@
#include "remote.h"
static const char ls_remote_usage[] =
-"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>] <repository> <refs>...";
+"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>]\n"
+" [-q|--quiet] [<repository> [<refs>...]]";
/*
* Is there one among the list of patterns that match the tail part
@@ -33,6 +34,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
const char *dest = NULL;
int nongit;
unsigned flags = 0;
+ int quiet = 0;
const char *uploadpack = NULL;
const char **pattern = NULL;
@@ -66,6 +68,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
flags |= REF_NORMAL;
continue;
}
+ if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
+ quiet = 1;
+ continue;
+ }
usage(ls_remote_usage);
}
dest = arg;
@@ -73,9 +79,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
break;
}
- if (!dest)
- usage(ls_remote_usage);
-
if (argv[i]) {
int j;
pattern = xcalloc(sizeof(const char *), argc - i + 1);
@@ -87,6 +90,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
}
}
remote = remote_get(dest);
+ if (!remote) {
+ if (dest)
+ die("bad repository '%s'", dest);
+ die("No remote configured to list refs from.");
+ }
if (!remote->url_nr)
die("remote %s has no configured URL", dest);
transport = transport_get(remote, NULL);
@@ -96,6 +104,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
ref = transport_get_remote_refs(transport);
if (transport_disconnect(transport))
return 1;
+
+ if (!dest && !quiet)
+ fprintf(stderr, "From %s\n", *remote->url);
for ( ; ref; ref = ref->next) {
if (!check_ref_type(ref, flags))
continue;
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 4a9729b..2320d98 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -17,10 +17,10 @@ static struct strbuf name = STRBUF_INIT;
static struct strbuf email = STRBUF_INIT;
static enum {
- TE_DONTCARE, TE_QP, TE_BASE64,
+ TE_DONTCARE, TE_QP, TE_BASE64
} transfer_encoding;
static enum {
- TYPE_TEXT, TYPE_OTHER,
+ TYPE_TEXT, TYPE_OTHER
} message_type;
static struct strbuf charset = STRBUF_INIT;
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index cdfc1b7..e4560da 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -121,7 +121,7 @@ static int populate_maildir_list(struct string_list *list, const char *path)
if (dent->d_name[0] == '.')
continue;
snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name);
- string_list_insert(name, list);
+ string_list_insert(list, name);
}
closedir(dir);
diff --git a/builtin/merge.c b/builtin/merge.c
index cae1cbe..37ce4f5 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -548,13 +548,53 @@ static void write_tree_trivial(unsigned char *sha1)
die("git write-tree failed to write a tree");
}
-static int try_merge_strategy(const char *strategy, struct commit_list *common,
- const char *head_arg)
+int try_merge_command(const char *strategy, struct commit_list *common,
+ const char *head_arg, struct commit_list *remotes)
{
const char **args;
int i = 0, x = 0, ret;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
+
+ args = xmalloc((4 + xopts_nr + commit_list_count(common) +
+ commit_list_count(remotes)) * sizeof(char *));
+ strbuf_addf(&buf, "merge-%s", strategy);
+ args[i++] = buf.buf;
+ for (x = 0; x < xopts_nr; x++) {
+ char *s = xmalloc(strlen(xopts[x])+2+1);
+ strcpy(s, "--");
+ strcpy(s+2, xopts[x]);
+ args[i++] = s;
+ }
+ for (j = common; j; j = j->next)
+ args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ args[i++] = "--";
+ args[i++] = head_arg;
+ for (j = remotes; j; j = j->next)
+ args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ args[i] = NULL;
+ ret = run_command_v_opt(args, RUN_GIT_CMD);
+ strbuf_release(&buf);
+ i = 1;
+ for (x = 0; x < xopts_nr; x++)
+ free((void *)args[i++]);
+ for (j = common; j; j = j->next)
+ free((void *)args[i++]);
+ i += 2;
+ for (j = remotes; j; j = j->next)
+ free((void *)args[i++]);
+ free(args);
+ discard_cache();
+ if (read_cache() < 0)
+ die("failed to read the cache");
+ resolve_undo_clear();
+
+ return ret;
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+ const char *head_arg)
+{
int index_fd;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
@@ -567,12 +607,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
rollback_lock_file(lock);
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
- int clean;
+ int clean, x;
struct commit *result;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int index_fd;
struct commit_list *reversed = NULL;
struct merge_options o;
+ struct commit_list *j;
if (remoteheads->next) {
error("Not handling anything other than two heads merge.");
@@ -612,39 +653,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
rollback_lock_file(lock);
return clean ? 0 : 1;
} else {
- args = xmalloc((4 + xopts_nr + commit_list_count(common) +
- commit_list_count(remoteheads)) * sizeof(char *));
- strbuf_addf(&buf, "merge-%s", strategy);
- args[i++] = buf.buf;
- for (x = 0; x < xopts_nr; x++) {
- char *s = xmalloc(strlen(xopts[x])+2+1);
- strcpy(s, "--");
- strcpy(s+2, xopts[x]);
- args[i++] = s;
- }
- for (j = common; j; j = j->next)
- args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
- args[i++] = "--";
- args[i++] = head_arg;
- for (j = remoteheads; j; j = j->next)
- args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
- args[i] = NULL;
- ret = run_command_v_opt(args, RUN_GIT_CMD);
- strbuf_release(&buf);
- i = 1;
- for (x = 0; x < xopts_nr; x++)
- free((void *)args[i++]);
- for (j = common; j; j = j->next)
- free((void *)args[i++]);
- i += 2;
- for (j = remoteheads; j; j = j->next)
- free((void *)args[i++]);
- free(args);
- discard_cache();
- if (read_cache() < 0)
- die("failed to read the cache");
- resolve_undo_clear();
- return ret;
+ return try_merge_command(strategy, common, head_arg, remoteheads);
}
}
diff --git a/builtin/mv.c b/builtin/mv.c
index c07f53b..38574b8 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -180,7 +180,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
} else if (string_list_has_string(&src_for_dst, dst))
bad = "multiple sources for the same target";
else
- string_list_insert(dst, &src_for_dst);
+ string_list_insert(&src_for_dst, dst);
if (bad) {
if (ignore_errors) {
diff --git a/builtin/notes.c b/builtin/notes.c
index f678f9c..190005f 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -26,7 +26,7 @@ static const char * const git_notes_usage[] = {
"git notes [--ref <notes_ref>] edit [<object>]",
"git notes [--ref <notes_ref>] show [<object>]",
"git notes [--ref <notes_ref>] remove [<object>]",
- "git notes [--ref <notes_ref>] prune",
+ "git notes [--ref <notes_ref>] prune [-n | -v]",
NULL
};
@@ -67,7 +67,7 @@ static const char * const git_notes_remove_usage[] = {
};
static const char * const git_notes_prune_usage[] = {
- "git notes prune",
+ "git notes prune [<options>]",
NULL
};
@@ -796,7 +796,10 @@ static int remove_cmd(int argc, const char **argv, const char *prefix)
static int prune(int argc, const char **argv, const char *prefix)
{
struct notes_tree *t;
+ int show_only = 0, verbose = 0;
struct option options[] = {
+ OPT_BOOLEAN('n', NULL, &show_only, "do not remove, show only"),
+ OPT_BOOLEAN('v', NULL, &verbose, "report pruned notes"),
OPT_END()
};
@@ -810,8 +813,10 @@ static int prune(int argc, const char **argv, const char *prefix)
t = init_notes_check("prune");
- prune_notes(t);
- commit_notes(t, "Notes removed by 'git notes prune'");
+ prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) |
+ (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) );
+ if (!show_only)
+ commit_notes(t, "Notes removed by 'git notes prune'");
free_notes(t);
return 0;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 214d7ef..0e81673 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1529,6 +1529,8 @@ static void try_to_free_from_threads(size_t size)
read_unlock();
}
+try_to_free_t old_try_to_free_routine;
+
/*
* The main thread waits on the condition that (at least) one of the workers
* has stopped working (which is indicated in the .working member of
@@ -1563,12 +1565,12 @@ static void init_threaded_search(void)
pthread_mutex_init(&cache_mutex, NULL);
pthread_mutex_init(&progress_mutex, NULL);
pthread_cond_init(&progress_cond, NULL);
- set_try_to_free_routine(try_to_free_from_threads);
+ old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
}
static void cleanup_threaded_search(void)
{
- set_try_to_free_routine(NULL);
+ set_try_to_free_routine(old_try_to_free_routine);
pthread_cond_destroy(&progress_cond);
pthread_mutex_destroy(&read_mutex);
pthread_mutex_destroy(&cache_mutex);
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index af0911e..5125300 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -28,16 +28,42 @@ static int remove_space(char *line)
return dst - line;
}
-static void generate_id_list(void)
+static int scan_hunk_header(const char *p, int *p_before, int *p_after)
+{
+ static const char digits[] = "0123456789";
+ const char *q, *r;
+ int n;
+
+ q = p + 4;
+ n = strspn(q, digits);
+ if (q[n] == ',') {
+ q += n + 1;
+ n = strspn(q, digits);
+ }
+ if (n == 0 || q[n] != ' ' || q[n+1] != '+')
+ return 0;
+
+ r = q + n + 2;
+ n = strspn(r, digits);
+ if (r[n] == ',') {
+ r += n + 1;
+ n = strspn(r, digits);
+ }
+ if (n == 0)
+ return 0;
+
+ *p_before = atoi(q);
+ *p_after = atoi(r);
+ return 1;
+}
+
+int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx)
{
- static unsigned char sha1[20];
static char line[1000];
- git_SHA_CTX ctx;
- int patchlen = 0;
+ int patchlen = 0, found_next = 0;
+ int before = -1, after = -1;
- git_SHA1_Init(&ctx);
while (fgets(line, sizeof(line), stdin) != NULL) {
- unsigned char n[20];
char *p = line;
int len;
@@ -45,32 +71,75 @@ static void generate_id_list(void)
p += 10;
else if (!memcmp(line, "commit ", 7))
p += 7;
+ else if (!memcmp(line, "From ", 5))
+ p += 5;
- if (!get_sha1_hex(p, n)) {
- flush_current_id(patchlen, sha1, &ctx);
- hashcpy(sha1, n);
- patchlen = 0;
- continue;
+ if (!get_sha1_hex(p, next_sha1)) {
+ found_next = 1;
+ break;
}
/* Ignore commit comments */
if (!patchlen && memcmp(line, "diff ", 5))
continue;
- /* Ignore git-diff index header */
- if (!memcmp(line, "index ", 6))
- continue;
+ /* Parsing diff header? */
+ if (before == -1) {
+ if (!memcmp(line, "index ", 6))
+ continue;
+ else if (!memcmp(line, "--- ", 4))
+ before = after = 1;
+ else if (!isalpha(line[0]))
+ break;
+ }
- /* Ignore line numbers when computing the SHA1 of the patch */
- if (!memcmp(line, "@@ -", 4))
- continue;
+ /* Looking for a valid hunk header? */
+ if (before == 0 && after == 0) {
+ if (!memcmp(line, "@@ -", 4)) {
+ /* Parse next hunk, but ignore line numbers. */
+ scan_hunk_header(line, &before, &after);
+ continue;
+ }
+
+ /* Split at the end of the patch. */
+ if (memcmp(line, "diff ", 5))
+ break;
+
+ /* Else we're parsing another header. */
+ before = after = -1;
+ }
+
+ /* If we get here, we're inside a hunk. */
+ if (line[0] == '-' || line[0] == ' ')
+ before--;
+ if (line[0] == '+' || line[0] == ' ')
+ after--;
/* Compute the sha without whitespace */
len = remove_space(line);
patchlen += len;
- git_SHA1_Update(&ctx, line, len);
+ git_SHA1_Update(ctx, line, len);
+ }
+
+ if (!found_next)
+ hashclr(next_sha1);
+
+ return patchlen;
+}
+
+static void generate_id_list(void)
+{
+ unsigned char sha1[20], n[20];
+ git_SHA_CTX ctx;
+ int patchlen;
+
+ git_SHA1_Init(&ctx);
+ hashclr(sha1);
+ while (!feof(stdin)) {
+ patchlen = get_one_patchid(n, &ctx);
+ flush_current_id(patchlen, sha1, &ctx);
+ hashcpy(sha1, n);
}
- flush_current_id(patchlen, sha1, &ctx);
}
static const char patch_id_usage[] = "git patch-id < patch";
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 8bdcab1..9ad1e66 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -219,14 +219,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
* "-m ent" or "--reset ent" form), we can obtain a fully
* valid cache-tree because the index must match exactly
* what came from the tree.
- *
- * The same holds true if we are switching between two trees
- * using read-tree -m A B. The index must match B after that.
*/
if (nr_trees == 1 && !opts.prefix)
prime_cache_tree(&active_cache_tree, trees[0]);
- else if (nr_trees == 2 && opts.merge)
- prime_cache_tree(&active_cache_tree, trees[1]);
if (write_cache(newfd, active_cache, active_nr) ||
commit_locked_index(&lock_file))
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 0559fcc..d634b5a 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -9,6 +9,7 @@
#include "object.h"
#include "remote.h"
#include "transport.h"
+#include "string-list.h"
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
@@ -16,7 +17,7 @@ enum deny_action {
DENY_UNCONFIGURED,
DENY_IGNORE,
DENY_WARN,
- DENY_REFUSE,
+ DENY_REFUSE
};
static int deny_deletes;
@@ -129,13 +130,12 @@ static void write_head_info(void)
struct command {
struct command *next;
const char *error_string;
+ unsigned int skip_update;
unsigned char old_sha1[20];
unsigned char new_sha1[20];
char ref_name[FLEX_ARRAY]; /* more */
};
-static struct command *commands;
-
static const char pre_receive_hook[] = "hooks/pre-receive";
static const char post_receive_hook[] = "hooks/post-receive";
@@ -188,7 +188,7 @@ static int copy_to_sideband(int in, int out, void *arg)
return 0;
}
-static int run_receive_hook(const char *hook_name)
+static int run_receive_hook(struct command *commands, const char *hook_name)
{
static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
struct command *cmd;
@@ -447,15 +447,15 @@ static const char *update(struct command *cmd)
static char update_post_hook[] = "hooks/post-update";
-static void run_update_post_hook(struct command *cmd)
+static void run_update_post_hook(struct command *commands)
{
- struct command *cmd_p;
+ struct command *cmd;
int argc;
const char **argv;
struct child_process proc;
- for (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
- if (cmd_p->error_string)
+ for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
+ if (cmd->error_string)
continue;
argc++;
}
@@ -464,12 +464,12 @@ static void run_update_post_hook(struct command *cmd)
argv = xmalloc(sizeof(*argv) * (2 + argc));
argv[0] = update_post_hook;
- for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
+ for (argc = 1, cmd = commands; cmd; cmd = cmd->next) {
char *p;
- if (cmd_p->error_string)
+ if (cmd->error_string)
continue;
- p = xmalloc(strlen(cmd_p->ref_name) + 1);
- strcpy(p, cmd_p->ref_name);
+ p = xmalloc(strlen(cmd->ref_name) + 1);
+ strcpy(p, cmd->ref_name);
argv[argc] = p;
argc++;
}
@@ -488,37 +488,92 @@ static void run_update_post_hook(struct command *cmd)
}
}
-static void execute_commands(const char *unpacker_error)
+static void check_aliased_update(struct command *cmd, struct string_list *list)
+{
+ struct string_list_item *item;
+ struct command *dst_cmd;
+ unsigned char sha1[20];
+ char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
+ int flag;
+
+ const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);
+
+ if (!(flag & REF_ISSYMREF))
+ return;
+
+ if ((item = string_list_lookup(list, dst_name)) == NULL)
+ return;
+
+ cmd->skip_update = 1;
+
+ dst_cmd = (struct command *) item->util;
+
+ if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&
+ !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))
+ return;
+
+ dst_cmd->skip_update = 1;
+
+ strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));
+ strcpy(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
+ strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));
+ strcpy(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
+ rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
+ " its target '%s' (%s..%s)",
+ cmd->ref_name, cmd_oldh, cmd_newh,
+ dst_cmd->ref_name, dst_oldh, dst_newh);
+
+ cmd->error_string = dst_cmd->error_string =
+ "inconsistent aliased update";
+}
+
+static void check_aliased_updates(struct command *commands)
+{
+ struct command *cmd;
+ struct string_list ref_list = { NULL, 0, 0, 0 };
+
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ struct string_list_item *item =
+ string_list_append(&ref_list, cmd->ref_name);
+ item->util = (void *)cmd;
+ }
+ sort_string_list(&ref_list);
+
+ for (cmd = commands; cmd; cmd = cmd->next)
+ check_aliased_update(cmd, &ref_list);
+
+ string_list_clear(&ref_list, 0);
+}
+
+static void execute_commands(struct command *commands, const char *unpacker_error)
{
- struct command *cmd = commands;
+ struct command *cmd;
unsigned char sha1[20];
if (unpacker_error) {
- while (cmd) {
+ for (cmd = commands; cmd; cmd = cmd->next)
cmd->error_string = "n/a (unpacker error)";
- cmd = cmd->next;
- }
return;
}
- if (run_receive_hook(pre_receive_hook)) {
- while (cmd) {
+ if (run_receive_hook(commands, pre_receive_hook)) {
+ for (cmd = commands; cmd; cmd = cmd->next)
cmd->error_string = "pre-receive hook declined";
- cmd = cmd->next;
- }
return;
}
+ check_aliased_updates(commands);
+
head_name = resolve_ref("HEAD", sha1, 0, NULL);
- while (cmd) {
- cmd->error_string = update(cmd);
- cmd = cmd->next;
- }
+ for (cmd = commands; cmd; cmd = cmd->next)
+ if (!cmd->skip_update)
+ cmd->error_string = update(cmd);
}
-static void read_head_info(void)
+static struct command *read_head_info(void)
{
+ struct command *commands = NULL;
struct command **p = &commands;
for (;;) {
static char line[1000];
@@ -548,15 +603,14 @@ static void read_head_info(void)
if (strstr(refname + reflen + 1, "side-band-64k"))
use_sideband = LARGE_PACKET_MAX;
}
- cmd = xmalloc(sizeof(struct command) + len - 80);
+ cmd = xcalloc(1, sizeof(struct command) + len - 80);
hashcpy(cmd->old_sha1, old_sha1);
hashcpy(cmd->new_sha1, new_sha1);
memcpy(cmd->ref_name, line + 82, len - 81);
- cmd->error_string = NULL;
- cmd->next = NULL;
*p = cmd;
p = &cmd->next;
}
+ return commands;
}
static const char *parse_pack_header(struct pack_header *hdr)
@@ -643,7 +697,7 @@ static const char *unpack(void)
}
}
-static void report(const char *unpack_status)
+static void report(struct command *commands, const char *unpack_status)
{
struct command *cmd;
struct strbuf buf = STRBUF_INIT;
@@ -667,12 +721,12 @@ static void report(const char *unpack_status)
strbuf_release(&buf);
}
-static int delete_only(struct command *cmd)
+static int delete_only(struct command *commands)
{
- while (cmd) {
+ struct command *cmd;
+ for (cmd = commands; cmd; cmd = cmd->next) {
if (!is_null_sha1(cmd->new_sha1))
return 0;
- cmd = cmd->next;
}
return 1;
}
@@ -722,6 +776,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
int stateless_rpc = 0;
int i;
char *dir = NULL;
+ struct command *commands;
argv++;
for (i = 1; i < argc; i++) {
@@ -772,18 +827,17 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if (advertise_refs)
return 0;
- read_head_info();
- if (commands) {
+ if ((commands = read_head_info()) != NULL) {
const char *unpack_status = NULL;
if (!delete_only(commands))
unpack_status = unpack();
- execute_commands(unpack_status);
+ execute_commands(commands, unpack_status);
if (pack_lockfile)
unlink_or_warn(pack_lockfile);
if (report_status)
- report(unpack_status);
- run_receive_hook(post_receive_hook);
+ report(commands, unpack_status);
+ run_receive_hook(commands, post_receive_hook);
run_update_post_hook(commands);
if (auto_gc) {
const char *argv_gc_auto[] = {
diff --git a/builtin/reflog.c b/builtin/reflog.c
index bd7880d..ebf610e 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -34,8 +34,13 @@ struct cmd_reflog_expire_cb {
struct expire_reflog_cb {
FILE *newlog;
- const char *ref;
- struct commit *ref_commit;
+ enum {
+ UE_NORMAL,
+ UE_ALWAYS,
+ UE_HEAD
+ } unreachable_expire_kind;
+ struct commit_list *mark_list;
+ unsigned long mark_limit;
struct cmd_reflog_expire_cb *cmd;
unsigned char last_kept_sha1[20];
};
@@ -210,46 +215,23 @@ 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)
+/*
+ * Starting from commits in the cb->mark_list, mark commits that are
+ * reachable from them. Stop the traversal at commits older than
+ * the expire_limit and queue them back, so that the caller can call
+ * us again to restart the traversal with longer expire_limit.
+ */
+static void mark_reachable(struct expire_reflog_cb *cb)
{
- /*
- * 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;
-}
+ struct commit *commit;
+ struct commit_list *pending;
+ unsigned long expire_limit = cb->mark_limit;
+ struct commit_list *leftover = NULL;
-static void mark_reachable(struct commit *commit, unsigned long expire_limit)
-{
- /*
- * We need to compute whether the commit on either side of a reflog
- * entry is reachable from the tip of the ref for all entries.
- * Mark commits that are reachable from the tip down to the
- * time threshold first; we know a commit marked thusly is
- * reachable from the tip without running in_merge_bases()
- * at all.
- */
- struct commit_list *pending = NULL;
+ for (pending = cb->mark_list; pending; pending = pending->next)
+ pending->item->object.flags &= ~REACHABLE;
- commit_list_insert(commit, &pending);
+ pending = cb->mark_list;
while (pending) {
struct commit_list *entry = pending;
struct commit_list *parent;
@@ -261,8 +243,11 @@ static void mark_reachable(struct commit *commit, unsigned long expire_limit)
if (parse_commit(commit))
continue;
commit->object.flags |= REACHABLE;
- if (commit->date < expire_limit)
+ if (commit->date < expire_limit) {
+ commit_list_insert(commit, &leftover);
continue;
+ }
+ commit->object.flags |= REACHABLE;
parent = commit->parents;
while (parent) {
commit = parent->item;
@@ -272,6 +257,36 @@ static void mark_reachable(struct commit *commit, unsigned long expire_limit)
commit_list_insert(commit, &pending);
}
}
+ cb->mark_list = leftover;
+}
+
+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 (cb->mark_list && cb->mark_limit) {
+ cb->mark_limit = 0; /* dig down to the root */
+ mark_reachable(cb);
+ }
+
+ return !(commit->object.flags & REACHABLE);
}
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
@@ -293,7 +308,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
goto prune;
if (timestamp < cb->cmd->expire_unreachable) {
- if (!cb->ref_commit)
+ if (cb->unreachable_expire_kind == UE_ALWAYS)
goto prune;
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
goto prune;
@@ -320,12 +335,27 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
return 0;
}
+static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+ struct commit_list **list = cb_data;
+ struct commit *tip_commit;
+ if (flags & REF_ISSYMREF)
+ return 0;
+ tip_commit = lookup_commit_reference_gently(sha1, 1);
+ if (!tip_commit)
+ return 0;
+ commit_list_insert(tip_commit, list);
+ return 0;
+}
+
static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
{
struct cmd_reflog_expire_cb *cmd = cb_data;
struct expire_reflog_cb cb;
struct ref_lock *lock;
char *log_file, *newlog_path = NULL;
+ struct commit *tip_commit;
+ struct commit_list *tips;
int status = 0;
memset(&cb, 0, sizeof(cb));
@@ -345,14 +375,49 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
cb.newlog = fopen(newlog_path, "w");
}
- 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);
+
+ if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
+ tip_commit = NULL;
+ cb.unreachable_expire_kind = UE_HEAD;
+ } else {
+ tip_commit = lookup_commit_reference_gently(sha1, 1);
+ if (!tip_commit)
+ cb.unreachable_expire_kind = UE_ALWAYS;
+ else
+ cb.unreachable_expire_kind = UE_NORMAL;
+ }
+
+ if (cmd->expire_unreachable <= cmd->expire_total)
+ cb.unreachable_expire_kind = UE_ALWAYS;
+
+ cb.mark_list = NULL;
+ tips = NULL;
+ if (cb.unreachable_expire_kind != UE_ALWAYS) {
+ if (cb.unreachable_expire_kind == UE_HEAD) {
+ struct commit_list *elem;
+ for_each_ref(push_tip_to_list, &tips);
+ for (elem = tips; elem; elem = elem->next)
+ commit_list_insert(elem->item, &cb.mark_list);
+ } else {
+ commit_list_insert(tip_commit, &cb.mark_list);
+ }
+ cb.mark_limit = cmd->expire_total;
+ mark_reachable(&cb);
+ }
+
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
- if (cb.ref_commit)
- clear_commit_marks(cb.ref_commit, REACHABLE);
+
+ if (cb.unreachable_expire_kind != UE_ALWAYS) {
+ if (cb.unreachable_expire_kind == UE_HEAD) {
+ struct commit_list *elem;
+ for (elem = tips; elem; elem = elem->next)
+ clear_commit_marks(tip_commit, REACHABLE);
+ free_commit_list(tips);
+ } else {
+ clear_commit_marks(tip_commit, REACHABLE);
+ }
+ }
finish:
if (cb.newlog) {
if (fclose(cb.newlog)) {
diff --git a/builtin/remote.c b/builtin/remote.c
index 277765b..6699bc5 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -16,6 +16,7 @@ static const char * const builtin_remote_usage[] = {
"git remote [-v | --verbose] show [-n] <name>",
"git remote prune [-n | --dry-run] <name>",
"git remote [-v | --verbose] update [-p | --prune] [group | remote]",
+ "git remote set-branches <name> [--add] <branch>...",
"git remote set-url <name> <newurl> [<oldurl>]",
"git remote set-url --add <name> <newurl>",
"git remote set-url --delete <name> <url>",
@@ -42,6 +43,12 @@ static const char * const builtin_remote_sethead_usage[] = {
NULL
};
+static const char * const builtin_remote_setbranches_usage[] = {
+ "git remote set-branches <name> <branch>...",
+ "git remote set-branches --add <name> <branch>...",
+ NULL
+};
+
static const char * const builtin_remote_show_usage[] = {
"git remote show [<options>] <name>",
NULL
@@ -87,7 +94,7 @@ static int opt_parse_track(const struct option *opt, const char *arg, int not)
if (not)
string_list_clear(list, 0);
else
- string_list_append(arg, list);
+ string_list_append(list, arg);
return 0;
}
@@ -104,9 +111,29 @@ static int fetch_remote(const char *name)
return 0;
}
+enum {
+ TAGS_UNSET = 0,
+ TAGS_DEFAULT = 1,
+ TAGS_SET = 2
+};
+
+static int add_branch(const char *key, const char *branchname,
+ const char *remotename, int mirror, struct strbuf *tmp)
+{
+ strbuf_reset(tmp);
+ strbuf_addch(tmp, '+');
+ if (mirror)
+ strbuf_addf(tmp, "refs/%s:refs/%s",
+ branchname, branchname);
+ else
+ strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
+ branchname, remotename, branchname);
+ return git_config_set_multivar(key, tmp->buf, "^$", 0);
+}
+
static int add(int argc, const char **argv)
{
- int fetch = 0, mirror = 0;
+ int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
struct string_list track = { NULL, 0, 0 };
const char *master = NULL;
struct remote *remote;
@@ -116,6 +143,11 @@ static int add(int argc, const char **argv)
struct option options[] = {
OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
+ OPT_SET_INT(0, "tags", &fetch_tags,
+ "import all tags and associated objects when fetching",
+ TAGS_SET),
+ OPT_SET_INT(0, NULL, &fetch_tags,
+ "or do not fetch any tag at all (--no-tags)", TAGS_UNSET),
OPT_CALLBACK('t', "track", &track, "branch",
"branch(es) to track", opt_parse_track),
OPT_STRING('m', "master", &master, "branch", "master branch"),
@@ -149,19 +181,10 @@ static int add(int argc, const char **argv)
strbuf_addf(&buf, "remote.%s.fetch", name);
if (track.nr == 0)
- string_list_append("*", &track);
+ string_list_append(&track, "*");
for (i = 0; i < track.nr; i++) {
- struct string_list_item *item = track.items + i;
-
- strbuf_reset(&buf2);
- strbuf_addch(&buf2, '+');
- if (mirror)
- strbuf_addf(&buf2, "refs/%s:refs/%s",
- item->string, item->string);
- else
- strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
- item->string, name, item->string);
- if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+ if (add_branch(buf.buf, track.items[i].string,
+ name, mirror, &buf2))
return 1;
}
@@ -172,6 +195,14 @@ static int add(int argc, const char **argv)
return 1;
}
+ if (fetch_tags != TAGS_DEFAULT) {
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "remote.%s.tagopt", name);
+ if (git_config_set(buf.buf,
+ fetch_tags == TAGS_SET ? "--tags" : "--no-tags"))
+ return 1;
+ }
+
if (fetch && fetch_remote(name))
return 1;
@@ -232,7 +263,7 @@ static int config_read_branches(const char *key, const char *value, void *cb)
} else
return 0;
- item = string_list_insert(name, &branch_list);
+ item = string_list_insert(&branch_list, name);
if (!item->util)
item->util = xcalloc(sizeof(struct branch_info), 1);
@@ -247,11 +278,11 @@ static int config_read_branches(const char *key, const char *value, void *cb)
while (space) {
char *merge;
merge = xstrndup(value, space - value);
- string_list_append(merge, &info->merge);
+ string_list_append(&info->merge, merge);
value = abbrev_branch(space + 1);
space = strchr(value, ' ');
}
- string_list_append(xstrdup(value), &info->merge);
+ string_list_append(&info->merge, xstrdup(value));
} else
info->rebase = git_config_bool(orig_key, value);
}
@@ -288,14 +319,14 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
for (ref = fetch_map; ref; ref = ref->next) {
unsigned char sha1[20];
if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
- string_list_append(abbrev_branch(ref->name), &states->new);
+ string_list_append(&states->new, abbrev_branch(ref->name));
else
- string_list_append(abbrev_branch(ref->name), &states->tracked);
+ string_list_append(&states->tracked, abbrev_branch(ref->name));
}
stale_refs = get_stale_heads(states->remote, fetch_map);
for (ref = stale_refs; ref; ref = ref->next) {
struct string_list_item *item =
- string_list_append(abbrev_branch(ref->name), &states->stale);
+ string_list_append(&states->stale, abbrev_branch(ref->name));
item->util = xstrdup(ref->name);
}
free_refs(stale_refs);
@@ -317,7 +348,7 @@ struct push_info {
PUSH_STATUS_UPTODATE,
PUSH_STATUS_FASTFORWARD,
PUSH_STATUS_OUTOFDATE,
- PUSH_STATUS_NOTQUERIED,
+ PUSH_STATUS_NOTQUERIED
} status;
};
@@ -344,8 +375,8 @@ static int get_push_ref_states(const struct ref *remote_refs,
continue;
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
- item = string_list_append(abbrev_branch(ref->peer_ref->name),
- &states->push);
+ item = string_list_append(&states->push,
+ abbrev_branch(ref->peer_ref->name));
item->util = xcalloc(sizeof(struct push_info), 1);
info = item->util;
info->forced = ref->force;
@@ -380,7 +411,7 @@ static int get_push_ref_states_noquery(struct ref_states *states)
states->push.strdup_strings = 1;
if (!remote->push_refspec_nr) {
- item = string_list_append("(matching)", &states->push);
+ item = string_list_append(&states->push, "(matching)");
info = item->util = xcalloc(sizeof(struct push_info), 1);
info->status = PUSH_STATUS_NOTQUERIED;
info->dest = xstrdup(item->string);
@@ -388,11 +419,11 @@ static int get_push_ref_states_noquery(struct ref_states *states)
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);
+ item = string_list_append(&states->push, "(matching)");
else if (strlen(spec->src))
- item = string_list_append(spec->src, &states->push);
+ item = string_list_append(&states->push, spec->src);
else
- item = string_list_append("(delete)", &states->push);
+ item = string_list_append(&states->push, "(delete)");
info = item->util = xcalloc(sizeof(struct push_info), 1);
info->forced = spec->force;
@@ -416,7 +447,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
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);
+ string_list_append(&states->heads, abbrev_branch(ref->name));
free_refs(fetch_map);
free_refs(matches);
@@ -480,8 +511,8 @@ static int add_branch_for_removal(const char *refname,
if (prefixcmp(refname, "refs/remotes")) {
/* advise user how to delete local branches */
if (!prefixcmp(refname, "refs/heads/"))
- string_list_append(abbrev_branch(refname),
- branches->skipped);
+ string_list_append(branches->skipped,
+ abbrev_branch(refname));
/* silently skip over other non-remote refs */
return 0;
}
@@ -490,7 +521,7 @@ static int add_branch_for_removal(const char *refname,
if (flags & REF_ISSYMREF)
return unlink(git_path("%s", refname));
- item = string_list_append(refname, branches->branches);
+ item = string_list_append(branches->branches, refname);
item->util = xmalloc(20);
hashcpy(item->util, sha1);
@@ -515,7 +546,7 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s", rename->old);
if (!prefixcmp(refname, buf.buf)) {
- item = string_list_append(xstrdup(refname), rename->remote_branches);
+ item = string_list_append(rename->remote_branches, xstrdup(refname));
symref = resolve_ref(refname, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
item->util = xstrdup(symref);
@@ -705,11 +736,14 @@ static int rm(int argc, const char **argv)
struct known_remotes known_remotes = { NULL, NULL };
struct string_list branches = { NULL, 0, 0, 1 };
struct string_list skipped = { NULL, 0, 0, 1 };
- struct branches_for_remote cb_data = {
- NULL, &branches, &skipped, &known_remotes
- };
+ struct branches_for_remote cb_data;
int i, result;
+ memset(&cb_data, 0, sizeof(cb_data));
+ cb_data.branches = &branches;
+ cb_data.skipped = &skipped;
+ cb_data.keep = &known_remotes;
+
if (argc != 2)
usage_with_options(builtin_remote_rm_usage, options);
@@ -798,7 +832,7 @@ static int append_ref_to_tracked_list(const char *refname,
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);
+ string_list_append(&states->tracked, abbrev_branch(refspec.src));
return 0;
}
@@ -851,7 +885,7 @@ static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
int n = strlen(item->string);
if (n > info->width)
info->width = n;
- string_list_insert(item->string, info->list);
+ string_list_insert(info->list, item->string);
return 0;
}
@@ -898,7 +932,7 @@ static int add_local_to_show_info(struct string_list_item *branch_item, void *cb
if (branch_info->rebase)
show_info->any_rebase = 1;
- item = string_list_insert(branch_item->string, show_info->list);
+ item = string_list_insert(show_info->list, branch_item->string);
item->util = branch_info;
return 0;
@@ -946,7 +980,7 @@ static int add_push_to_show_info(struct string_list_item *push_item, void *cb_da
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 = string_list_append(show_info->list, push_item->string);
item->util = push_item->util;
return 0;
}
@@ -1062,24 +1096,24 @@ static int show(int argc, const char **argv)
/* 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);
+ for_each_string_list(&states.new, add_remote_to_show_info, &info);
+ for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
+ for_each_string_list(&states.stale, add_remote_to_show_info, &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);
+ for_each_string_list(info.list, show_remote_info_item, &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);
+ for_each_string_list(&branch_list, add_local_to_show_info, &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);
+ for_each_string_list(info.list, show_local_info_item, &info);
string_list_clear(info.list, 0);
/* git push info */
@@ -1087,14 +1121,14 @@ static int show(int argc, const char **argv)
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);
+ for_each_string_list(&states.push, add_push_to_show_info, &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);
+ for_each_string_list(info.list, show_push_info_item, &info);
string_list_clear(info.list, 0);
free_remote_ref_states(&states);
@@ -1265,6 +1299,72 @@ static int update(int argc, const char **argv)
return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
}
+static int remove_all_fetch_refspecs(const char *remote, const char *key)
+{
+ return git_config_set_multivar(key, NULL, NULL, 1);
+}
+
+static int add_branches(struct remote *remote, const char **branches,
+ const char *key)
+{
+ const char *remotename = remote->name;
+ int mirror = remote->mirror;
+ struct strbuf refspec = STRBUF_INIT;
+
+ for (; *branches; branches++)
+ if (add_branch(key, *branches, remotename, mirror, &refspec)) {
+ strbuf_release(&refspec);
+ return 1;
+ }
+
+ strbuf_release(&refspec);
+ return 0;
+}
+
+static int set_remote_branches(const char *remotename, const char **branches,
+ int add_mode)
+{
+ struct strbuf key = STRBUF_INIT;
+ struct remote *remote;
+
+ strbuf_addf(&key, "remote.%s.fetch", remotename);
+
+ if (!remote_is_configured(remotename))
+ die("No such remote '%s'", remotename);
+ remote = remote_get(remotename);
+
+ if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
+ strbuf_release(&key);
+ return 1;
+ }
+ if (add_branches(remote, branches, key.buf)) {
+ strbuf_release(&key);
+ return 1;
+ }
+
+ strbuf_release(&key);
+ return 0;
+}
+
+static int set_branches(int argc, const char **argv)
+{
+ int add_mode = 0;
+ struct option options[] = {
+ OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, NULL, options,
+ builtin_remote_setbranches_usage, 0);
+ if (argc == 0) {
+ error("no remote specified");
+ usage_with_options(builtin_remote_seturl_usage, options);
+ }
+ argv[argc] = NULL;
+
+ return set_remote_branches(argv[0], argv + 1, add_mode);
+}
+
static int set_url(int argc, const char **argv)
{
int i, push_mode = 0, add_mode = 0, delete_mode = 0;
@@ -1360,10 +1460,10 @@ static int get_one_entry(struct remote *remote, void *priv)
if (remote->url_nr > 0) {
strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
- string_list_append(remote->name, list)->util =
+ string_list_append(list, remote->name)->util =
strbuf_detach(&url_buf, NULL);
} else
- string_list_append(remote->name, list)->util = NULL;
+ string_list_append(list, remote->name)->util = NULL;
if (remote->pushurl_nr) {
url = remote->pushurl;
url_nr = remote->pushurl_nr;
@@ -1374,7 +1474,7 @@ static int get_one_entry(struct remote *remote, void *priv)
for (i = 0; i < url_nr; i++)
{
strbuf_addf(&url_buf, "%s (push)", url[i]);
- string_list_append(remote->name, list)->util =
+ string_list_append(list, remote->name)->util =
strbuf_detach(&url_buf, NULL);
}
@@ -1430,6 +1530,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
result = rm(argc, argv);
else if (!strcmp(argv[0], "set-head"))
result = set_head(argc, argv);
+ else if (!strcmp(argv[0], "set-branches"))
+ result = set_branches(argc, argv);
else if (!strcmp(argv[0], "set-url"))
result = set_url(argc, argv);
else if (!strcmp(argv[0], "show"))
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 0048f9e..980d542 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -59,7 +59,7 @@ static void garbage_collect(struct string_list *rr)
cutoff = (has_rerere_resolution(e->d_name)
? cutoff_resolve : cutoff_noresolve);
if (then < now - cutoff * 86400)
- string_list_append(e->d_name, &to_remove);
+ string_list_append(&to_remove, e->d_name);
}
for (i = 0; i < to_remove.nr; i++)
unlink_rr_item(to_remove.items[i].string);
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 51ceb19..efe9360 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -50,6 +50,15 @@ static void show_commit(struct commit *commit, void *data)
graph_show_commit(revs->graph);
+ if (revs->count) {
+ if (commit->object.flags & SYMMETRIC_LEFT)
+ revs->count_left++;
+ else
+ revs->count_right++;
+ finish_commit(commit, data);
+ return;
+ }
+
if (info->show_timestamp)
printf("%lu ", commit->date);
if (info->header_prefix)
@@ -400,5 +409,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
quiet ? finish_object : show_object,
&info);
+ if (revs.count) {
+ if (revs.left_right)
+ printf("%d\t%d\n", revs.count_left, revs.count_right);
+ else
+ printf("%d\n", revs.count_left + revs.count_right);
+ }
+
return 0;
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 8fbf9d0..a5a1c86 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -407,8 +407,9 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
ALLOC_GROW(opts, onb + 1, osz);
memset(opts + onb, 0, sizeof(opts[onb]));
argc = parse_options(argc, argv, prefix, opts, usage,
- keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0 |
- stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0);
+ (keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) |
+ (stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
+ PARSE_OPT_SHELL_EVAL);
strbuf_addf(&parsed, " --");
sq_quote_argv(&parsed, argv, 0);
diff --git a/builtin/revert.c b/builtin/revert.c
index 7d68ef7..8b9d829 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -39,29 +39,34 @@ static const char * const cherry_pick_usage[] = {
static int edit, no_replay, no_commit, mainline, signoff, allow_ff;
static enum { REVERT, CHERRY_PICK } action;
static struct commit *commit;
-static const char *commit_name;
+static int commit_argc;
+static const char **commit_argv;
static int allow_rerere_auto;
static const char *me;
+static const char *strategy;
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
static char *get_encoding(const char *message);
+static const char * const *revert_or_cherry_pick_usage(void)
+{
+ return action == REVERT ? revert_usage : cherry_pick_usage;
+}
+
static void parse_args(int argc, const char **argv)
{
- const char * const * usage_str =
- action == REVERT ? revert_usage : cherry_pick_usage;
- unsigned char sha1[20];
+ const char * const * usage_str = revert_or_cherry_pick_usage();
int noop;
struct option options[] = {
OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
- OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"),
OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_INTEGER('m', "mainline", &mainline, "parent number"),
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
+ OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
OPT_END(),
OPT_END(),
OPT_END(),
@@ -69,6 +74,7 @@ static void parse_args(int argc, const char **argv)
if (action == CHERRY_PICK) {
struct option cp_extra[] = {
+ OPT_BOOLEAN('x', NULL, &no_replay, "append commit name"),
OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"),
OPT_END(),
};
@@ -76,15 +82,13 @@ static void parse_args(int argc, const char **argv)
die("program error");
}
- if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1)
+ commit_argc = parse_options(argc, argv, NULL, options, usage_str,
+ PARSE_OPT_KEEP_ARGV0 |
+ PARSE_OPT_KEEP_UNKNOWN);
+ if (commit_argc < 2)
usage_with_options(usage_str, options);
- commit_name = argv[0];
- if (get_sha1(commit_name, sha1))
- die ("Cannot find '%s'", commit_name);
- commit = lookup_commit_reference(sha1);
- if (!commit)
- exit(1);
+ commit_argv = argv;
}
struct commit_message {
@@ -174,28 +178,17 @@ static char *get_encoding(const char *message)
return NULL;
}
-static struct lock_file msg_file;
-static int msg_fd;
-
-static void add_to_msg(const char *string)
-{
- int len = strlen(string);
- if (write_in_full(msg_fd, string, len) < 0)
- die_errno ("Could not write to MERGE_MSG");
-}
-
-static void add_message_to_msg(const char *message)
+static void add_message_to_msg(struct strbuf *msgbuf, const char *message)
{
const char *p = message;
while (*p && (*p != '\n' || p[1] != '\n'))
p++;
if (!*p)
- add_to_msg(sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(msgbuf, sha1_to_hex(commit->object.sha1));
p += 2;
- add_to_msg(p);
- return;
+ strbuf_addstr(msgbuf, p);
}
static void set_author_ident_env(const char *message)
@@ -248,7 +241,7 @@ static void set_author_ident_env(const char *message)
sha1_to_hex(commit->object.sha1));
}
-static char *help_msg(const char *name)
+static char *help_msg(void)
{
struct strbuf helpbuf = STRBUF_INIT;
char *msg = getenv("GIT_CHERRY_PICK_HELP");
@@ -264,13 +257,26 @@ static char *help_msg(const char *name)
strbuf_addf(&helpbuf, " with: \n"
"\n"
" git commit -c %s\n",
- name);
+ sha1_to_hex(commit->object.sha1));
}
else
strbuf_addch(&helpbuf, '.');
return strbuf_detach(&helpbuf, NULL);
}
+static void write_message(struct strbuf *msgbuf, const char *filename)
+{
+ static struct lock_file msg_file;
+
+ int msg_fd = hold_lock_file_for_update(&msg_file, filename,
+ LOCK_DIE_ON_ERROR);
+ if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
+ die_errno("Could not write to %s.", filename);
+ strbuf_release(msgbuf);
+ if (commit_lock_file(&msg_file) < 0)
+ die("Error wrapping up %s", filename);
+}
+
static struct tree *empty_tree(void)
{
struct tree *tree = xcalloc(1, sizeof(struct tree));
@@ -305,40 +311,71 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)
return write_ref_sha1(ref_lock, to, "cherry-pick");
}
-static int revert_or_cherry_pick(int argc, const char **argv)
+static void do_recursive_merge(struct commit *base, struct commit *next,
+ const char *base_label, const char *next_label,
+ unsigned char *head, struct strbuf *msgbuf,
+ char *defmsg)
{
- unsigned char head[20];
- struct commit *base, *next, *parent;
- const char *base_label, *next_label;
- int i, index_fd, clean;
- struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
- char *defmsg = NULL;
struct merge_options o;
struct tree *result, *next_tree, *base_tree, *head_tree;
+ int clean, index_fd;
static struct lock_file index_lock;
- git_config(git_default_config, NULL);
- me = action == REVERT ? "revert" : "cherry-pick";
- setenv(GIT_REFLOG_ACTION, me, 0);
- parse_args(argc, argv);
+ index_fd = hold_locked_index(&index_lock, 1);
- /* this is copied from the shell script, but it's never triggered... */
- if (action == REVERT && !no_replay)
- die("revert is incompatible with replay");
+ read_cache();
+ init_merge_options(&o);
+ o.ancestor = base ? base_label : "(empty tree)";
+ o.branch1 = "HEAD";
+ o.branch2 = next ? next_label : "(empty tree)";
- if (allow_ff) {
- if (signoff)
- die("cherry-pick --ff cannot be used with --signoff");
- if (no_commit)
- die("cherry-pick --ff cannot be used with --no-commit");
- if (no_replay)
- die("cherry-pick --ff cannot be used with -x");
- if (edit)
- die("cherry-pick --ff cannot be used with --edit");
+ head_tree = parse_tree_indirect(head);
+ next_tree = next ? next->tree : empty_tree();
+ base_tree = base ? base->tree : empty_tree();
+
+ clean = merge_trees(&o,
+ head_tree,
+ next_tree, base_tree, &result);
+
+ if (active_cache_changed &&
+ (write_cache(index_fd, active_cache, active_nr) ||
+ commit_locked_index(&index_lock)))
+ die("%s: Unable to write new index file", me);
+ rollback_lock_file(&index_lock);
+
+ if (!clean) {
+ int i;
+ strbuf_addstr(msgbuf, "\nConflicts:\n\n");
+ for (i = 0; i < active_nr;) {
+ struct cache_entry *ce = active_cache[i++];
+ if (ce_stage(ce)) {
+ strbuf_addch(msgbuf, '\t');
+ strbuf_addstr(msgbuf, ce->name);
+ strbuf_addch(msgbuf, '\n');
+ while (i < active_nr && !strcmp(ce->name,
+ active_cache[i]->name))
+ i++;
+ }
+ }
+ write_message(msgbuf, defmsg);
+ fprintf(stderr, "Automatic %s failed.%s\n",
+ me, help_msg());
+ rerere(allow_rerere_auto);
+ exit(1);
}
+ write_message(msgbuf, defmsg);
+ fprintf(stderr, "Finished one %s.\n", me);
+}
+
+static int do_pick_commit(void)
+{
+ unsigned char head[20];
+ struct commit *base, *next, *parent;
+ const char *base_label, *next_label;
+ struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+ char *defmsg = NULL;
+ struct strbuf msgbuf = STRBUF_INIT;
- if (read_cache() < 0)
- die("git %s: failed to read the index", me);
if (no_commit) {
/*
* We do not intend to commit immediately. We just want to
@@ -403,83 +440,59 @@ static int revert_or_cherry_pick(int argc, const char **argv)
*/
defmsg = git_pathdup("MERGE_MSG");
- msg_fd = hold_lock_file_for_update(&msg_file, defmsg,
- LOCK_DIE_ON_ERROR);
-
- index_fd = hold_locked_index(&index_lock, 1);
if (action == REVERT) {
base = commit;
base_label = msg.label;
next = parent;
next_label = msg.parent_label;
- add_to_msg("Revert \"");
- add_to_msg(msg.subject);
- add_to_msg("\"\n\nThis reverts commit ");
- add_to_msg(sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(&msgbuf, "Revert \"");
+ strbuf_addstr(&msgbuf, msg.subject);
+ strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
if (commit->parents->next) {
- add_to_msg(", reversing\nchanges made to ");
- add_to_msg(sha1_to_hex(parent->object.sha1));
+ strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
}
- add_to_msg(".\n");
+ strbuf_addstr(&msgbuf, ".\n");
} else {
base = parent;
base_label = msg.parent_label;
next = commit;
next_label = msg.label;
set_author_ident_env(msg.message);
- add_message_to_msg(msg.message);
+ add_message_to_msg(&msgbuf, msg.message);
if (no_replay) {
- add_to_msg("(cherry picked from commit ");
- add_to_msg(sha1_to_hex(commit->object.sha1));
- add_to_msg(")\n");
+ strbuf_addstr(&msgbuf, "(cherry picked from commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(&msgbuf, ")\n");
}
}
- read_cache();
- init_merge_options(&o);
- o.ancestor = base ? base_label : "(empty tree)";
- o.branch1 = "HEAD";
- o.branch2 = next ? next_label : "(empty tree)";
-
- head_tree = parse_tree_indirect(head);
- next_tree = next ? next->tree : empty_tree();
- base_tree = base ? base->tree : empty_tree();
-
- clean = merge_trees(&o,
- head_tree,
- next_tree, base_tree, &result);
-
- if (active_cache_changed &&
- (write_cache(index_fd, active_cache, active_nr) ||
- commit_locked_index(&index_lock)))
- die("%s: Unable to write new index file", me);
- rollback_lock_file(&index_lock);
-
- if (!clean) {
- add_to_msg("\nConflicts:\n\n");
- for (i = 0; i < active_nr;) {
- struct cache_entry *ce = active_cache[i++];
- if (ce_stage(ce)) {
- add_to_msg("\t");
- add_to_msg(ce->name);
- add_to_msg("\n");
- while (i < active_nr && !strcmp(ce->name,
- active_cache[i]->name))
- i++;
- }
+ if (!strategy || !strcmp(strategy, "recursive") || action == REVERT)
+ do_recursive_merge(base, next, base_label, next_label,
+ head, &msgbuf, defmsg);
+ else {
+ int res;
+ struct commit_list *common = NULL;
+ struct commit_list *remotes = NULL;
+ write_message(&msgbuf, defmsg);
+ commit_list_insert(base, &common);
+ commit_list_insert(next, &remotes);
+ res = try_merge_command(strategy, common,
+ sha1_to_hex(head), remotes);
+ free_commit_list(common);
+ free_commit_list(remotes);
+ if (res) {
+ fprintf(stderr, "Automatic %s with strategy %s failed.%s\n",
+ me, strategy, help_msg());
+ rerere(allow_rerere_auto);
+ exit(1);
}
- if (commit_lock_file(&msg_file) < 0)
- die ("Error wrapping up %s", defmsg);
- fprintf(stderr, "Automatic %s failed.%s\n",
- me, help_msg(commit_name));
- rerere(allow_rerere_auto);
- exit(1);
}
- if (commit_lock_file(&msg_file) < 0)
- die ("Error wrapping up %s", defmsg);
- fprintf(stderr, "Finished one %s.\n", me);
+
+ free_message(&msg);
/*
*
@@ -493,7 +506,9 @@ static int revert_or_cherry_pick(int argc, const char **argv)
if (!no_commit) {
/* 6 is max possible length of our args array including NULL */
const char *args[6];
+ int res;
int i = 0;
+
args[i++] = "commit";
args[i++] = "-n";
if (signoff)
@@ -503,26 +518,81 @@ static int revert_or_cherry_pick(int argc, const char **argv)
args[i++] = defmsg;
}
args[i] = NULL;
- return execv_git_cmd(args);
+ res = run_command_v_opt(args, RUN_GIT_CMD);
+ free(defmsg);
+
+ return res;
}
- free_message(&msg);
+
free(defmsg);
return 0;
}
+static void prepare_revs(struct rev_info *revs)
+{
+ int argc;
+
+ init_revisions(revs, NULL);
+ revs->no_walk = 1;
+ if (action != REVERT)
+ revs->reverse = 1;
+
+ argc = setup_revisions(commit_argc, commit_argv, revs, NULL);
+ if (argc > 1)
+ usage(*revert_or_cherry_pick_usage());
+
+ if (prepare_revision_walk(revs))
+ die("revision walk setup failed");
+
+ if (!revs->commits)
+ die("empty commit set passed");
+}
+
+static int revert_or_cherry_pick(int argc, const char **argv)
+{
+ struct rev_info revs;
+
+ git_config(git_default_config, NULL);
+ me = action == REVERT ? "revert" : "cherry-pick";
+ setenv(GIT_REFLOG_ACTION, me, 0);
+ parse_args(argc, argv);
+
+ if (allow_ff) {
+ if (signoff)
+ die("cherry-pick --ff cannot be used with --signoff");
+ if (no_commit)
+ die("cherry-pick --ff cannot be used with --no-commit");
+ if (no_replay)
+ die("cherry-pick --ff cannot be used with -x");
+ if (edit)
+ die("cherry-pick --ff cannot be used with --edit");
+ }
+
+ if (read_cache() < 0)
+ die("git %s: failed to read the index", me);
+
+ prepare_revs(&revs);
+
+ while ((commit = get_revision(&revs))) {
+ int res = do_pick_commit();
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
int cmd_revert(int argc, const char **argv, const char *prefix)
{
if (isatty(0))
edit = 1;
- no_replay = 1;
action = REVERT;
return revert_or_cherry_pick(argc, argv);
}
int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
{
- no_replay = 0;
action = CHERRY_PICK;
return revert_or_cherry_pick(argc, argv);
}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 5089502..0a9681b 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -84,7 +84,7 @@ static void insert_one_record(struct shortlog *log,
snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
}
- item = string_list_insert(namebuf, &log->list);
+ item = string_list_insert(&log->list, namebuf);
if (item->util == NULL)
item->util = xcalloc(1, sizeof(struct string_list));
@@ -115,7 +115,7 @@ static void insert_one_record(struct shortlog *log,
}
}
- string_list_append(buffer, item->util);
+ string_list_append(item->util, buffer);
}
static void read_from_stdin(struct shortlog *log)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 17ada88..0b2a9ad 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -105,7 +105,7 @@ match:
static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
- string_list_insert(refname, list);
+ string_list_insert(list, refname);
return 0;
}
diff --git a/cache.h b/cache.h
index 0d101e4..c9fa3df 100644
--- a/cache.h
+++ b/cache.h
@@ -361,7 +361,7 @@ enum object_type {
OBJ_OFS_DELTA = 6,
OBJ_REF_DELTA = 7,
OBJ_ANY,
- OBJ_MAX,
+ OBJ_MAX
};
static inline enum object_type object_type(unsigned int mode)
@@ -547,7 +547,6 @@ extern int core_compression_seen;
extern size_t packed_git_window_size;
extern size_t packed_git_limit;
extern size_t delta_base_cache_limit;
-extern int auto_crlf;
extern int read_replace_refs;
extern int fsync_object_files;
extern int core_preload_index;
@@ -556,32 +555,53 @@ extern int core_apply_sparse_checkout;
enum safe_crlf {
SAFE_CRLF_FALSE = 0,
SAFE_CRLF_FAIL = 1,
- SAFE_CRLF_WARN = 2,
+ SAFE_CRLF_WARN = 2
};
extern enum safe_crlf safe_crlf;
+enum auto_crlf {
+ AUTO_CRLF_FALSE = 0,
+ AUTO_CRLF_TRUE = 1,
+ AUTO_CRLF_INPUT = -1,
+};
+
+extern enum auto_crlf auto_crlf;
+
+enum eol {
+ EOL_UNSET,
+ EOL_CRLF,
+ EOL_LF,
+#ifdef NATIVE_CRLF
+ EOL_NATIVE = EOL_CRLF
+#else
+ EOL_NATIVE = EOL_LF
+#endif
+};
+
+extern enum eol eol;
+
enum branch_track {
BRANCH_TRACK_UNSPECIFIED = -1,
BRANCH_TRACK_NEVER = 0,
BRANCH_TRACK_REMOTE,
BRANCH_TRACK_ALWAYS,
BRANCH_TRACK_EXPLICIT,
- BRANCH_TRACK_OVERRIDE,
+ BRANCH_TRACK_OVERRIDE
};
enum rebase_setup_type {
AUTOREBASE_NEVER = 0,
AUTOREBASE_LOCAL,
AUTOREBASE_REMOTE,
- AUTOREBASE_ALWAYS,
+ AUTOREBASE_ALWAYS
};
enum push_default_type {
PUSH_DEFAULT_NOTHING = 0,
PUSH_DEFAULT_MATCHING,
PUSH_DEFAULT_TRACKING,
- PUSH_DEFAULT_CURRENT,
+ PUSH_DEFAULT_CURRENT
};
extern enum branch_track git_branch_track;
@@ -590,7 +610,7 @@ extern enum push_default_type push_default;
enum object_creation_mode {
OBJECT_CREATION_USES_HARDLINKS = 0,
- OBJECT_CREATION_USES_RENAMES = 1,
+ OBJECT_CREATION_USES_RENAMES = 1
};
extern enum object_creation_mode object_creation_mode;
@@ -670,7 +690,7 @@ enum sharedrepo {
OLD_PERM_GROUP = 1,
OLD_PERM_EVERYBODY = 2,
PERM_GROUP = 0660,
- PERM_EVERYBODY = 0664,
+ PERM_EVERYBODY = 0664
};
int git_config_perm(const char *var, const char *value);
int set_shared_perm(const char *path, int mode);
@@ -718,6 +738,8 @@ extern int has_loose_object_nonlocal(const unsigned char *sha1);
extern int has_pack_index(const unsigned char *sha1);
+extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
+
extern const signed char hexval_table[256];
static inline unsigned int hexval(unsigned char c)
{
@@ -728,12 +750,23 @@ static inline unsigned int hexval(unsigned char c)
#define MINIMUM_ABBREV 4
#define DEFAULT_ABBREV 7
+struct object_context {
+ unsigned char tree[20];
+ char path[PATH_MAX];
+ unsigned mode;
+};
+
extern int get_sha1(const char *str, unsigned char *sha1);
extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
{
return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
}
+extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int gently, const char *prefix);
+static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
+{
+ return get_sha1_with_context_1(str, sha1, orc, 1, NULL);
+}
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1);
@@ -880,7 +913,7 @@ struct ref {
REF_STATUS_REJECT_NODELETE,
REF_STATUS_UPTODATE,
REF_STATUS_REMOTE_REJECT,
- REF_STATUS_EXPECTING_REPORT,
+ REF_STATUS_EXPECTING_REPORT
} status;
char *remote_status;
struct ref *peer_ref; /* when renaming */
@@ -937,12 +970,15 @@ extern int update_server_info(int);
typedef int (*config_fn_t)(const char *, const char *, void *);
extern int git_default_config(const char *, const char *, void *);
extern int git_config_from_file(config_fn_t fn, const char *, void *);
+extern int git_config_parse_parameter(const char *text);
+extern int git_config_from_parameters(config_fn_t fn, void *data);
extern int git_config(config_fn_t fn, void *);
extern int git_parse_ulong(const char *, unsigned long *);
extern int git_config_int(const char *, const char *);
extern unsigned long git_config_ulong(const char *, const char *);
extern int git_config_bool_or_int(const char *, const char *, int *);
extern int git_config_bool(const char *, const char *);
+extern int git_config_maybe_bool(const char *, const char *);
extern int git_config_string(const char **, const char *, const char *);
extern int git_config_pathname(const char **, const char *, const char *);
extern int git_config_set(const char *, const char *);
@@ -950,6 +986,7 @@ extern int git_config_set_multivar(const char *, const char *, const char *, int
extern int git_config_rename_section(const char *, const char *);
extern const char *git_etc_gitconfig(void);
extern int check_repository_format_version(const char *var, const char *value, void *cb);
+extern int git_env_bool(const char *, int);
extern int git_config_system(void);
extern int git_config_global(void);
extern int config_error_nonbool(const char *);
@@ -1041,6 +1078,7 @@ void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *
#define WS_INDENT_WITH_NON_TAB 04
#define WS_CR_AT_EOL 010
#define WS_BLANK_AT_EOF 020
+#define WS_TAB_IN_INDENT 040
#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
extern unsigned whitespace_rule_cfg;
@@ -1049,7 +1087,7 @@ extern unsigned parse_whitespace_rule(const char *);
extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
extern char *whitespace_error_string(unsigned ws);
-extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
+extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
/* ls-files */
diff --git a/color.c b/color.c
index bcf4e2c..1b00554 100644
--- a/color.c
+++ b/color.c
@@ -211,31 +211,3 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
va_end(args);
return r;
}
-
-/*
- * This function splits the buffer by newlines and colors the lines individually.
- *
- * Returns 0 on success.
- */
-int color_fwrite_lines(FILE *fp, const char *color,
- size_t count, const char *buf)
-{
- if (!*color)
- return fwrite(buf, count, 1, fp) != 1;
- while (count) {
- char *p = memchr(buf, '\n', count);
- if (p != buf && (fputs(color, fp) < 0 ||
- fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
- fputs(GIT_COLOR_RESET, fp) < 0))
- return -1;
- if (!p)
- return 0;
- if (fputc('\n', fp) < 0)
- return -1;
- count -= p + 1 - buf;
- buf = p + 1;
- }
- return 0;
-}
-
-
diff --git a/color.h b/color.h
index 5c264b0..03ca064 100644
--- a/color.h
+++ b/color.h
@@ -61,6 +61,5 @@ __attribute__((format (printf, 3, 4)))
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
__attribute__((format (printf, 3, 4)))
int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
-int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
#endif /* COLOR_H */
diff --git a/commit.c b/commit.c
index 731191e..e9b0750 100644
--- a/commit.c
+++ b/commit.c
@@ -790,3 +790,58 @@ struct commit_list *reduce_heads(struct commit_list *heads)
free(other);
return result;
}
+
+static const char commit_utf8_warn[] =
+"Warning: commit message does not conform to UTF-8.\n"
+"You may want to amend it after fixing the message, or set the config\n"
+"variable i18n.commitencoding to the encoding your project uses.\n";
+
+int commit_tree(const char *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author)
+{
+ int result;
+ int encoding_is_utf8;
+ struct strbuf buffer;
+
+ assert_sha1_type(tree, OBJ_TREE);
+
+ /* Not having i18n.commitencoding is the same as having utf-8 */
+ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
+
+ strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
+ strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
+
+ /*
+ * NOTE! This ordering means that the same exact tree merged with a
+ * different order of parents will be a _different_ changeset even
+ * if everything else stays the same.
+ */
+ while (parents) {
+ struct commit_list *next = parents->next;
+ strbuf_addf(&buffer, "parent %s\n",
+ sha1_to_hex(parents->item->object.sha1));
+ free(parents);
+ parents = next;
+ }
+
+ /* Person/date information */
+ if (!author)
+ author = git_author_info(IDENT_ERROR_ON_NO_NAME);
+ strbuf_addf(&buffer, "author %s\n", author);
+ strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
+ if (!encoding_is_utf8)
+ strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+ strbuf_addch(&buffer, '\n');
+
+ /* And add the comment */
+ strbuf_addstr(&buffer, msg);
+
+ /* And check the encoding */
+ if (encoding_is_utf8 && !is_utf8(buffer.buf))
+ fprintf(stderr, commit_utf8_warn);
+
+ result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+ strbuf_release(&buffer);
+ return result;
+}
diff --git a/commit.h b/commit.h
index 26ec8c0..eb2b8ac 100644
--- a/commit.h
+++ b/commit.h
@@ -28,6 +28,7 @@ extern const char *commit_type;
extern struct decoration name_decoration;
struct name_decoration {
struct name_decoration *next;
+ int type;
char name[1];
};
@@ -60,7 +61,7 @@ enum cmit_fmt {
CMIT_FMT_EMAIL,
CMIT_FMT_USERFORMAT,
- CMIT_FMT_UNSPECIFIED,
+ CMIT_FMT_UNSPECIFIED
};
struct pretty_print_context
@@ -163,4 +164,8 @@ static inline int single_parent(struct commit *commit)
struct commit_list *reduce_heads(struct commit_list *heads);
+extern int commit_tree(const char *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author);
+
#endif /* COMMIT_H */
diff --git a/compat/mingw.h b/compat/mingw.h
index 0e3e743..3b2477b 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -80,7 +80,7 @@ static inline int fork(void)
static inline unsigned int alarm(unsigned int seconds)
{ return 0; }
static inline int fsync(int fd)
-{ return 0; }
+{ return _commit(fd); }
static inline int getppid(void)
{ return 1; }
static inline void sync(void)
@@ -89,7 +89,7 @@ static inline int getuid()
{ return 1; }
static inline struct passwd *getpwnam(const char *name)
{ return NULL; }
-static inline int fcntl(int fd, int cmd, long arg)
+static inline int fcntl(int fd, int cmd, ...)
{
if (cmd == F_GETFD || cmd == F_SETFD)
return 0;
diff --git a/compat/win32/pthread.c b/compat/win32/pthread.c
index 0f949fc..010e875 100644
--- a/compat/win32/pthread.c
+++ b/compat/win32/pthread.c
@@ -16,6 +16,7 @@
static unsigned __stdcall win32_start_routine(void *arg)
{
pthread_t *thread = arg;
+ thread->tid = GetCurrentThreadId();
thread->arg = thread->start_routine(thread->arg);
return 0;
}
@@ -49,6 +50,13 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr)
}
}
+pthread_t pthread_self(void)
+{
+ pthread_t t = { 0 };
+ t.tid = GetCurrentThreadId();
+ return t;
+}
+
int pthread_cond_init(pthread_cond_t *cond, const void *unused)
{
cond->waiters = 0;
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
index a45f8d6..2e20548 100644
--- a/compat/win32/pthread.h
+++ b/compat/win32/pthread.h
@@ -58,6 +58,7 @@ typedef struct {
HANDLE handle;
void *(*start_routine)(void*);
void *arg;
+ DWORD tid;
} pthread_t;
extern int pthread_create(pthread_t *thread, const void *unused,
@@ -71,4 +72,28 @@ extern int pthread_create(pthread_t *thread, const void *unused,
extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
+#define pthread_equal(t1, t2) ((t1).tid == (t2).tid)
+extern pthread_t pthread_self(void);
+
+static inline int pthread_exit(void *ret)
+{
+ ExitThread((DWORD)ret);
+}
+
+typedef DWORD pthread_key_t;
+static inline int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *value))
+{
+ return (*keyp = TlsAlloc()) == TLS_OUT_OF_INDEXES ? EAGAIN : 0;
+}
+
+static inline int pthread_setspecific(pthread_key_t key, const void *value)
+{
+ return TlsSetValue(key, (void *)value) ? 0 : EINVAL;
+}
+
+static inline void *pthread_getspecific(pthread_key_t key)
+{
+ return TlsGetValue(key);
+}
+
#endif /* PTHREAD_H */
diff --git a/config.c b/config.c
index 6963fbe..cdcf583 100644
--- a/config.c
+++ b/config.c
@@ -7,6 +7,7 @@
*/
#include "cache.h"
#include "exec_cmd.h"
+#include "strbuf.h"
#define MAXNAME (256)
@@ -18,6 +19,48 @@ static int zlib_compression_seen;
const char *config_exclusive_filename = NULL;
+struct config_item
+{
+ struct config_item *next;
+ char *name;
+ char *value;
+};
+static struct config_item *config_parameters;
+static struct config_item **config_parameters_tail = &config_parameters;
+
+static void lowercase(char *p)
+{
+ for (; *p; p++)
+ *p = tolower(*p);
+}
+
+int git_config_parse_parameter(const char *text)
+{
+ struct config_item *ct;
+ struct strbuf tmp = STRBUF_INIT;
+ struct strbuf **pair;
+ strbuf_addstr(&tmp, text);
+ pair = strbuf_split(&tmp, '=');
+ if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')
+ strbuf_setlen(pair[0], pair[0]->len - 1);
+ strbuf_trim(pair[0]);
+ if (!pair[0]->len) {
+ strbuf_list_free(pair);
+ return -1;
+ }
+ ct = xcalloc(1, sizeof(struct config_item));
+ ct->name = strbuf_detach(pair[0], NULL);
+ if (pair[1]) {
+ strbuf_trim(pair[1]);
+ ct->value = strbuf_detach(pair[1], NULL);
+ }
+ strbuf_list_free(pair);
+ lowercase(ct->name);
+ *config_parameters_tail = ct;
+ config_parameters_tail = &ct->next;
+ return 0;
+}
+
static int get_next_char(void)
{
int c;
@@ -322,17 +365,30 @@ unsigned long git_config_ulong(const char *name, const char *value)
return ret;
}
-int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
+int git_config_maybe_bool(const char *name, const char *value)
{
- *is_bool = 1;
if (!value)
return 1;
if (!*value)
return 0;
- if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
+ if (!strcasecmp(value, "true")
+ || !strcasecmp(value, "yes")
+ || !strcasecmp(value, "on"))
return 1;
- if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
+ if (!strcasecmp(value, "false")
+ || !strcasecmp(value, "no")
+ || !strcasecmp(value, "off"))
return 0;
+ return -1;
+}
+
+int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
+{
+ int v = git_config_maybe_bool(name, value);
+ if (0 <= v) {
+ *is_bool = 1;
+ return v;
+ }
*is_bool = 0;
return git_config_int(name, value);
}
@@ -461,7 +517,9 @@ static int git_default_core_config(const char *var, const char *value)
if (!strcmp(var, "core.autocrlf")) {
if (value && !strcasecmp(value, "input")) {
- auto_crlf = -1;
+ if (eol == EOL_CRLF)
+ return error("core.autocrlf=input conflicts with core.eol=crlf");
+ auto_crlf = AUTO_CRLF_INPUT;
return 0;
}
auto_crlf = git_config_bool(var, value);
@@ -477,6 +535,20 @@ static int git_default_core_config(const char *var, const char *value)
return 0;
}
+ if (!strcmp(var, "core.eol")) {
+ if (value && !strcasecmp(value, "lf"))
+ eol = EOL_LF;
+ else if (value && !strcasecmp(value, "crlf"))
+ eol = EOL_CRLF;
+ else if (value && !strcasecmp(value, "native"))
+ eol = EOL_NATIVE;
+ else
+ eol = EOL_UNSET;
+ if (eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
+ return error("core.autocrlf=input conflicts with core.eol=crlf");
+ return 0;
+ }
+
if (!strcmp(var, "core.notesref")) {
notes_ref_name = xstrdup(value);
return 0;
@@ -683,7 +755,7 @@ const char *git_etc_gitconfig(void)
return system_wide;
}
-static int git_env_bool(const char *k, int def)
+int git_env_bool(const char *k, int def)
{
const char *v = getenv(k);
return v ? git_config_bool(k, v) : def;
@@ -699,6 +771,15 @@ int git_config_global(void)
return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
}
+int git_config_from_parameters(config_fn_t fn, void *data)
+{
+ const struct config_item *ct;
+ for (ct = config_parameters; ct; ct = ct->next)
+ if (fn(ct->name, ct->value, data) < 0)
+ return -1;
+ return 0;
+}
+
int git_config(config_fn_t fn, void *data)
{
int ret = 0, found = 0;
@@ -730,6 +811,12 @@ int git_config(config_fn_t fn, void *data)
found += 1;
}
free(repo_config);
+
+ if (config_parameters) {
+ ret += git_config_from_parameters(fn, data);
+ found += 1;
+ }
+
if (found == 0)
return -1;
return ret;
diff --git a/config.mak.in b/config.mak.in
index 0d4b64d..b4e65c3 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -3,10 +3,12 @@
CC = @CC@
CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
CC_LD_DYNPATH = @CC_LD_DYNPATH@
AR = @AR@
TAR = @TAR@
+DIFF = @DIFF@
#INSTALL = @INSTALL@ # needs install-sh or install.sh in sources
TCLTK_PATH = @TCLTK_PATH@
@@ -42,6 +44,7 @@ NO_D_TYPE_IN_DIRENT=@NO_D_TYPE_IN_DIRENT@
NO_SOCKADDR_STORAGE=@NO_SOCKADDR_STORAGE@
NO_IPV6=@NO_IPV6@
NO_C99_FORMAT=@NO_C99_FORMAT@
+NO_HSTRERROR=@NO_HSTRERROR@
NO_STRCASESTR=@NO_STRCASESTR@
NO_MEMMEM=@NO_MEMMEM@
NO_STRLCPY=@NO_STRLCPY@
@@ -51,10 +54,15 @@ NO_SETENV=@NO_SETENV@
NO_UNSETENV=@NO_UNSETENV@
NO_MKDTEMP=@NO_MKDTEMP@
NO_MKSTEMPS=@NO_MKSTEMPS@
+NO_INET_NTOP=@NO_INET_NTOP@
+NO_INET_PTON=@NO_INET_PTON@
NO_ICONV=@NO_ICONV@
OLD_ICONV=@OLD_ICONV@
NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
+INLINE=@INLINE@
+SOCKLEN_T=@SOCKLEN_T@
FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
NO_PTHREADS=@NO_PTHREADS@
+PTHREAD_CFLAGS=@PTHREAD_CFLAGS@
PTHREAD_LIBS=@PTHREAD_LIBS@
diff --git a/configure.ac b/configure.ac
index 71038fc..5601e8b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -327,6 +327,12 @@ GIT_PARSE_WITH(tcltk))
AC_MSG_NOTICE([CHECKS for programs])
#
AC_PROG_CC([cc gcc])
+AC_C_INLINE
+case $ac_cv_c_inline in
+ inline | yes | no) ;;
+ *) AC_SUBST([INLINE], [$ac_cv_c_inline]) ;;
+esac
+
# which switch to pass runtime path to dynamic libraries to the linker
AC_CACHE_CHECK([if linker supports -R], git_cv_ld_dashr, [
SAVE_LDFLAGS="${LDFLAGS}"
@@ -362,6 +368,7 @@ fi
#AC_PROG_INSTALL # needs install-sh or install.sh in sources
AC_CHECK_TOOLS(AR, [gar ar], :)
AC_CHECK_PROGS(TAR, [gtar tar])
+AC_CHECK_PROGS(DIFF, [gnudiff gdiff diff])
# TCLTK_PATH will be set to some value if we want Tcl/Tk
# or will be empty otherwise.
if test -z "$NO_TCLTK"; then
@@ -544,13 +551,47 @@ AC_SUBST(NEEDS_SOCKET)
test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket"
#
-# Define NEEDS_RESOLV if linking with -lnsl and/or -lsocket is not enough.
-# Notably on Solaris hstrerror resides in libresolv and on Solaris 7
-# inet_ntop and inet_pton additionally reside there.
-AC_CHECK_LIB([c], [hstrerror],
-[NEEDS_RESOLV=],
-[NEEDS_RESOLV=YesPlease])
+# The next few tests will define NEEDS_RESOLV if linking with
+# libresolv provides some of the functions we would normally get
+# from libc.
+NEEDS_RESOLV=
AC_SUBST(NEEDS_RESOLV)
+#
+# Define NO_INET_NTOP if linking with -lresolv is not enough.
+# Solaris 2.7 in particular hos inet_ntop in -lresolv.
+NO_INET_NTOP=
+AC_SUBST(NO_INET_NTOP)
+AC_CHECK_FUNC([inet_ntop],
+ [],
+ [AC_CHECK_LIB([resolv], [inet_ntop],
+ [NEEDS_RESOLV=YesPlease],
+ [NO_INET_NTOP=YesPlease])
+])
+#
+# Define NO_INET_PTON if linking with -lresolv is not enough.
+# Solaris 2.7 in particular hos inet_pton in -lresolv.
+NO_INET_PTON=
+AC_SUBST(NO_INET_PTON)
+AC_CHECK_FUNC([inet_pton],
+ [],
+ [AC_CHECK_LIB([resolv], [inet_pton],
+ [NEEDS_RESOLV=YesPlease],
+ [NO_INET_PTON=YesPlease])
+])
+#
+# Define NO_HSTRERROR if linking with -lresolv is not enough.
+# Solaris 2.6 in particular has no hstrerror, even in -lresolv.
+NO_HSTRERROR=
+AC_CHECK_FUNC([hstrerror],
+ [],
+ [AC_CHECK_LIB([resolv], [hstrerror],
+ [NEEDS_RESOLV=YesPlease],
+ [NO_HSTRERROR=YesPlease])
+])
+AC_SUBST(NO_HSTRERROR)
+#
+# If any of the above tests determined that -lresolv is needed at
+# build-time, also set it here for remaining configure-time checks.
test -n "$NEEDS_RESOLV" && LIBS="$LIBS -lresolv"
AC_CHECK_LIB([c], [basename],
@@ -598,6 +639,12 @@ AC_SUBST(OLD_ICONV)
## Checks for typedefs, structures, and compiler characteristics.
AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
#
+TYPE_SOCKLEN_T
+case $ac_cv_type_socklen_t in
+ yes) ;;
+ *) AC_SUBST([SOCKLEN_T], [$git_cv_socklen_t_equiv]) ;;
+esac
+
# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
AC_CHECK_MEMBER(struct dirent.d_ino,
[NO_D_INO_IN_DIRENT=],
@@ -808,7 +855,11 @@ AC_DEFUN([PTHREADTEST_SRC], [
int main(void)
{
pthread_mutex_t test_mutex;
- return (0);
+ int retcode = 0;
+ retcode |= pthread_mutex_init(&test_mutex,(void *)0);
+ retcode |= pthread_mutex_lock(&test_mutex);
+ retcode |= pthread_mutex_unlock(&test_mutex);
+ return retcode;
}
])
@@ -825,7 +876,8 @@ if test -n "$USER_NOPTHREAD"; then
# 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
+ threads_found=no
+ for opt in -mt -pthread -lpthread; do
old_CFLAGS="$CFLAGS"
CFLAGS="$opt $CFLAGS"
AC_MSG_CHECKING([Checking for POSIX Threads with '$opt'])
@@ -833,11 +885,18 @@ elif test -z "$PTHREAD_CFLAGS"; then
[AC_MSG_RESULT([yes])
NO_PTHREADS=
PTHREAD_LIBS="$opt"
+ PTHREAD_CFLAGS="$opt"
+ threads_found=yes
break
],
[AC_MSG_RESULT([no])])
CFLAGS="$old_CFLAGS"
done
+ if test $threads_found != yes; then
+ AC_CHECK_LIB([pthread], [pthread_create],
+ [PTHREAD_LIBS="-lpthread"],
+ [NO_PTHREADS=UnfortunatelyYes])
+ fi
else
old_CFLAGS="$CFLAGS"
CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
@@ -854,6 +913,7 @@ fi
CFLAGS="$old_CFLAGS"
+AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(NO_PTHREADS)
diff --git a/connect.c b/connect.c
index 9ae991a..02e738a 100644
--- a/connect.c
+++ b/connect.c
@@ -5,6 +5,7 @@
#include "refs.h"
#include "run-command.h"
#include "remote.h"
+#include "url.h"
static char *server_capabilities;
@@ -131,7 +132,7 @@ int path_match(const char *path, int nr, char **match)
enum protocol {
PROTO_LOCAL = 1,
PROTO_SSH,
- PROTO_GIT,
+ PROTO_GIT
};
static enum protocol get_protocol(const char *name)
@@ -450,7 +451,7 @@ static struct child_process no_fork;
struct child_process *git_connect(int fd[2], const char *url_orig,
const char *prog, int flags)
{
- char *url = xstrdup(url_orig);
+ char *url;
char *host, *path;
char *end;
int c;
@@ -466,6 +467,11 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
*/
signal(SIGCHLD, SIG_DFL);
+ if (is_url(url_orig))
+ url = url_decode(url_orig);
+ else
+ url = xstrdup(url_orig);
+
host = strstr(url, "://");
if (host) {
*host = '\0';
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 57245a8..6756990 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -42,6 +42,24 @@
# set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're
# untracked files, then a '%' will be shown next to the branch name.
#
+# If you would like to see the difference between HEAD and its
+# upstream, set GIT_PS1_SHOWUPSTREAM="auto". A "<" indicates
+# you are behind, ">" indicates you are ahead, and "<>"
+# indicates you have diverged. You can further control
+# behaviour by setting GIT_PS1_SHOWUPSTREAM to a space-separated
+# list of values:
+# verbose show number of commits ahead/behind (+/-) upstream
+# legacy don't use the '--count' option available in recent
+# versions of git-rev-list
+# git always compare HEAD to @{upstream}
+# svn always compare HEAD to your SVN upstream
+# By default, __git_ps1 will compare HEAD to your SVN upstream
+# if it can find one, or @{upstream} otherwise. Once you have
+# set GIT_PS1_SHOWUPSTREAM, you can override it on a
+# per-repository basis by setting the bash.showUpstream config
+# variable.
+#
+#
# To submit patches:
#
# *) Read Documentation/SubmittingPatches
@@ -78,14 +96,133 @@ __gitdir ()
fi
}
+# stores the divergence from upstream in $p
+# used by GIT_PS1_SHOWUPSTREAM
+__git_ps1_show_upstream ()
+{
+ local key value
+ local svn_remote=() svn_url_pattern count n
+ local upstream=git legacy="" verbose=""
+
+ # get some config options from git-config
+ while read key value; do
+ case "$key" in
+ bash.showupstream)
+ GIT_PS1_SHOWUPSTREAM="$value"
+ if [[ -z "${GIT_PS1_SHOWUPSTREAM}" ]]; then
+ p=""
+ return
+ fi
+ ;;
+ svn-remote.*.url)
+ svn_remote[ $((${#svn_remote[@]} + 1)) ]="$value"
+ svn_url_pattern+="\\|$value"
+ upstream=svn+git # default upstream is SVN if available, else git
+ ;;
+ esac
+ done < <(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')
+
+ # parse configuration values
+ for option in ${GIT_PS1_SHOWUPSTREAM}; do
+ case "$option" in
+ git|svn) upstream="$option" ;;
+ verbose) verbose=1 ;;
+ legacy) legacy=1 ;;
+ esac
+ done
+
+ # Find our upstream
+ case "$upstream" in
+ git) upstream="@{upstream}" ;;
+ svn*)
+ # get the upstream from the "git-svn-id: ..." in a commit message
+ # (git-svn uses essentially the same procedure internally)
+ local svn_upstream=($(git log --first-parent -1 \
+ --grep="^git-svn-id: \(${svn_url_pattern:2}\)" 2>/dev/null))
+ if [[ 0 -ne ${#svn_upstream[@]} ]]; then
+ svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
+ svn_upstream=${svn_upstream%@*}
+ for ((n=1; "$n" <= "${#svn_remote[@]}"; ++n)); do
+ svn_upstream=${svn_upstream#${svn_remote[$n]}}
+ done
+
+ if [[ -z "$svn_upstream" ]]; then
+ # default branch name for checkouts with no layout:
+ upstream=${GIT_SVN_ID:-git-svn}
+ else
+ upstream=${svn_upstream#/}
+ fi
+ elif [[ "svn+git" = "$upstream" ]]; then
+ upstream="@{upstream}"
+ fi
+ ;;
+ esac
+
+ # Find how many commits we are ahead/behind our upstream
+ if [[ -z "$legacy" ]]; then
+ count="$(git rev-list --count --left-right \
+ "$upstream"...HEAD 2>/dev/null)"
+ else
+ # produce equivalent output to --count for older versions of git
+ local commits
+ if commits="$(git rev-list --left-right "$upstream"...HEAD 2>/dev/null)"
+ then
+ local commit behind=0 ahead=0
+ for commit in $commits
+ do
+ case "$commit" in
+ "<"*) let ++behind
+ ;;
+ *) let ++ahead
+ ;;
+ esac
+ done
+ count="$behind $ahead"
+ else
+ count=""
+ fi
+ fi
+
+ # calculate the result
+ if [[ -z "$verbose" ]]; then
+ case "$count" in
+ "") # no upstream
+ p="" ;;
+ "0 0") # equal to upstream
+ p="=" ;;
+ "0 "*) # ahead of upstream
+ p=">" ;;
+ *" 0") # behind upstream
+ p="<" ;;
+ *) # diverged from upstream
+ p="<>" ;;
+ esac
+ else
+ case "$count" in
+ "") # no upstream
+ p="" ;;
+ "0 0") # equal to upstream
+ p=" u=" ;;
+ "0 "*) # ahead of upstream
+ p=" u+${count#0 }" ;;
+ *" 0") # behind upstream
+ p=" u-${count% 0}" ;;
+ *) # diverged from upstream
+ p=" u+${count#* }-${count% *}" ;;
+ esac
+ fi
+
+}
+
+
# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
# returns text to add to bash PS1 prompt (includes branch name)
__git_ps1 ()
{
local g="$(__gitdir)"
if [ -n "$g" ]; then
- local r
- local b
+ local r=""
+ local b=""
if [ -f "$g/rebase-merge/interactive" ]; then
r="|REBASE-i"
b="$(cat "$g/rebase-merge/head-name")"
@@ -127,11 +264,12 @@ __git_ps1 ()
}
fi
- local w
- local i
- local s
- local u
- local c
+ local w=""
+ local i=""
+ local s=""
+ local u=""
+ local c=""
+ local p=""
if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
@@ -159,10 +297,14 @@ __git_ps1 ()
u="%"
fi
fi
+
+ if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
+ __git_ps1_show_upstream
+ fi
fi
local f="$w$i$s$u"
- printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r"
+ printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p"
fi
}
@@ -842,7 +984,7 @@ _git_checkout ()
--*)
__gitcomp "
--quiet --ours --theirs --track --no-track --merge
- --conflict= --patch
+ --conflict= --orphan --patch
"
;;
*)
@@ -1052,7 +1194,7 @@ _git_format_patch ()
--numbered --start-number
--numbered-files
--keep-subject
- --signoff
+ --signoff --signature --no-signature
--in-reply-to= --cc=
--full-index --binary
--not --all
@@ -1726,6 +1868,7 @@ _git_config ()
format.headers
format.numbered
format.pretty
+ format.signature
format.signoff
format.subjectprefix
format.suffix
diff --git a/contrib/examples/git-fetch.sh b/contrib/examples/git-fetch.sh
index e44af2c..a314273 100755
--- a/contrib/examples/git-fetch.sh
+++ b/contrib/examples/git-fetch.sh
@@ -127,10 +127,12 @@ then
orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
fi
-# Allow --notags from remote.$1.tagopt
+# Allow --tags/--notags from remote.$1.tagopt
case "$tags$no_tags" in
'')
case "$(git config --get "remote.$1.tagopt")" in
+ --tags)
+ tags=t ;;
--no-tags)
no_tags=t ;;
esac
diff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh
index c364dda..a4ed4c3 100755
--- a/contrib/git-resurrect.sh
+++ b/contrib/git-resurrect.sh
@@ -9,6 +9,7 @@ other/Merge <other> into <name> (respectively) commit subjects, which
is rather slow but allows you to resurrect other people's topic
branches."
+OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC="\
git resurrect $USAGE
--
diff --git a/convert.c b/convert.c
index 4f8fcb7..e41a31e 100644
--- a/convert.c
+++ b/convert.c
@@ -8,13 +8,17 @@
* This should use the pathname to decide on whether it wants to do some
* more interesting conversions (automatic gzip/unzip, general format
* conversions etc etc), but by default it just does automatic CRLF<->LF
- * translation when the "auto_crlf" option is set.
+ * translation when the "text" attribute or "auto_crlf" option is set.
*/
-#define CRLF_GUESS (-1)
-#define CRLF_BINARY 0
-#define CRLF_TEXT 1
-#define CRLF_INPUT 2
+enum action {
+ CRLF_GUESS = -1,
+ CRLF_BINARY = 0,
+ CRLF_TEXT,
+ CRLF_INPUT,
+ CRLF_CRLF,
+ CRLF_AUTO,
+};
struct text_stat {
/* NUL, CR, LF and CRLF counts */
@@ -89,49 +93,111 @@ static int is_binary(unsigned long size, struct text_stat *stats)
return 0;
}
-static void check_safe_crlf(const char *path, int action,
+static enum eol determine_output_conversion(enum action action) {
+ switch (action) {
+ case CRLF_BINARY:
+ return EOL_UNSET;
+ case CRLF_CRLF:
+ return EOL_CRLF;
+ case CRLF_INPUT:
+ return EOL_LF;
+ case CRLF_GUESS:
+ if (!auto_crlf)
+ return EOL_UNSET;
+ /* fall through */
+ case CRLF_TEXT:
+ case CRLF_AUTO:
+ if (auto_crlf == AUTO_CRLF_TRUE)
+ return EOL_CRLF;
+ else if (auto_crlf == AUTO_CRLF_INPUT)
+ return EOL_LF;
+ else if (eol == EOL_UNSET)
+ return EOL_NATIVE;
+ }
+ return eol;
+}
+
+static void check_safe_crlf(const char *path, enum action action,
struct text_stat *stats, enum safe_crlf checksafe)
{
if (!checksafe)
return;
- if (action == CRLF_INPUT || auto_crlf <= 0) {
+ if (determine_output_conversion(action) == EOL_LF) {
/*
* CRLFs would not be restored by checkout:
* check if we'd remove CRLFs
*/
if (stats->crlf) {
if (checksafe == SAFE_CRLF_WARN)
- warning("CRLF will be replaced by LF in %s.", path);
+ warning("CRLF will be replaced by LF in %s.\nThe file will have its original line endings in your working directory.", path);
else /* i.e. SAFE_CRLF_FAIL */
die("CRLF would be replaced by LF in %s.", path);
}
- } else if (auto_crlf > 0) {
+ } else if (determine_output_conversion(action) == EOL_CRLF) {
/*
* CRLFs would be added by checkout:
* check if we have "naked" LFs
*/
if (stats->lf != stats->crlf) {
if (checksafe == SAFE_CRLF_WARN)
- warning("LF will be replaced by CRLF in %s", path);
+ warning("LF will be replaced by CRLF in %s.\nThe file will have its original line endings in your working directory.", path);
else /* i.e. SAFE_CRLF_FAIL */
die("LF would be replaced by CRLF in %s", path);
}
}
}
+static int has_cr_in_index(const char *path)
+{
+ int pos, len;
+ unsigned long sz;
+ enum object_type type;
+ void *data;
+ int has_cr;
+ struct index_state *istate = &the_index;
+
+ len = strlen(path);
+ pos = index_name_pos(istate, path, len);
+ if (pos < 0) {
+ /*
+ * We might be in the middle of a merge, in which
+ * case we would read stage #2 (ours).
+ */
+ int i;
+ for (i = -pos - 1;
+ (pos < 0 && i < istate->cache_nr &&
+ !strcmp(istate->cache[i]->name, path));
+ i++)
+ if (ce_stage(istate->cache[i]) == 2)
+ pos = i;
+ }
+ if (pos < 0)
+ return 0;
+ data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+ if (!data || type != OBJ_BLOB) {
+ free(data);
+ return 0;
+ }
+
+ has_cr = memchr(data, '\r', sz) != NULL;
+ free(data);
+ return has_cr;
+}
+
static int crlf_to_git(const char *path, const char *src, size_t len,
- struct strbuf *buf, int action, enum safe_crlf checksafe)
+ struct strbuf *buf, enum action action, enum safe_crlf checksafe)
{
struct text_stat stats;
char *dst;
- if ((action == CRLF_BINARY) || !auto_crlf || !len)
+ if (action == CRLF_BINARY ||
+ (action == CRLF_GUESS && auto_crlf == AUTO_CRLF_FALSE) || !len)
return 0;
gather_stats(src, len, &stats);
- if (action == CRLF_GUESS) {
+ if (action == CRLF_AUTO || action == CRLF_GUESS) {
/*
* We're currently not going to even try to convert stuff
* that has bare CR characters. Does anybody do that crazy
@@ -145,6 +211,15 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
*/
if (is_binary(len, &stats))
return 0;
+
+ if (action == CRLF_GUESS) {
+ /*
+ * If the file in the index has any CR in it, do not convert.
+ * This is the new safer autocrlf handling.
+ */
+ if (has_cr_in_index(path))
+ return 0;
+ }
}
check_safe_crlf(path, action, &stats, checksafe);
@@ -157,7 +232,7 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
if (strbuf_avail(buf) + buf->len < len)
strbuf_grow(buf, len - buf->len);
dst = buf->buf;
- if (action == CRLF_GUESS) {
+ if (action == CRLF_AUTO || action == CRLF_GUESS) {
/*
* If we guessed, we already know we rejected a file with
* lone CR, and we can strip a CR without looking at what
@@ -180,16 +255,12 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
}
static int crlf_to_worktree(const char *path, const char *src, size_t len,
- struct strbuf *buf, int action)
+ struct strbuf *buf, enum action action)
{
char *to_free = NULL;
struct text_stat stats;
- if ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||
- auto_crlf <= 0)
- return 0;
-
- if (!len)
+ if (!len || determine_output_conversion(action) != EOL_CRLF)
return 0;
gather_stats(src, len, &stats);
@@ -202,7 +273,14 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
if (stats.lf == stats.crlf)
return 0;
- if (action == CRLF_GUESS) {
+ if (action == CRLF_AUTO || action == CRLF_GUESS) {
+ if (action == CRLF_GUESS) {
+ /* If we have any CR or CRLF line endings, we do not touch it */
+ /* This is the new safer autocrlf-handling */
+ if (stats.cr > 0 || stats.crlf > 0)
+ return 0;
+ }
+
/* If we have any bare CR characters, we're not going to touch it */
if (stats.cr != stats.crlf)
return 0;
@@ -249,7 +327,9 @@ static int filter_buffer(int in, int out, void *data)
struct child_process child_process;
struct filter_params *params = (struct filter_params *)data;
int write_err, status;
- const char *argv[] = { params->cmd, NULL };
+ const char *argv[] = { NULL, NULL };
+
+ argv[0] = params->cmd;
memset(&child_process, 0, sizeof(child_process));
child_process.argv = argv;
@@ -374,12 +454,16 @@ static int read_convert_config(const char *var, const char *value, void *cb)
static void setup_convert_check(struct git_attr_check *check)
{
+ static struct git_attr *attr_text;
static struct git_attr *attr_crlf;
+ static struct git_attr *attr_eol;
static struct git_attr *attr_ident;
static struct git_attr *attr_filter;
- if (!attr_crlf) {
+ if (!attr_text) {
+ attr_text = git_attr("text");
attr_crlf = git_attr("crlf");
+ attr_eol = git_attr("eol");
attr_ident = git_attr("ident");
attr_filter = git_attr("filter");
user_convert_tail = &user_convert;
@@ -388,6 +472,8 @@ static void setup_convert_check(struct git_attr_check *check)
check[0].attr = attr_crlf;
check[1].attr = attr_ident;
check[2].attr = attr_filter;
+ check[3].attr = attr_eol;
+ check[4].attr = attr_text;
}
static int count_ident(const char *cp, unsigned long size)
@@ -425,6 +511,8 @@ static int count_ident(const char *cp, unsigned long size)
cnt++;
break;
}
+ if (ch == '\n')
+ break;
}
}
return cnt;
@@ -455,6 +543,11 @@ static int ident_to_git(const char *path, const char *src, size_t len,
dollar = memchr(src + 3, '$', len - 3);
if (!dollar)
break;
+ if (memchr(src + 3, '\n', dollar - src - 3)) {
+ /* Line break before the next dollar. */
+ continue;
+ }
+
memcpy(dst, "Id$", 3);
dst += 3;
len -= dollar + 1 - src;
@@ -470,7 +563,7 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
struct strbuf *buf, int ident)
{
unsigned char sha1[20];
- char *to_free = NULL, *dollar;
+ char *to_free = NULL, *dollar, *spc;
int cnt;
if (!ident)
@@ -506,7 +599,10 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
} else if (src[2] == ':') {
/*
* It's possible that an expanded Id has crept its way into the
- * repository, we cope with that by stripping the expansion out
+ * repository, we cope with that by stripping the expansion out.
+ * This is probably not a good idea, since it will cause changes
+ * on checkout, which won't go away by stash, but let's keep it
+ * for git-style ids.
*/
dollar = memchr(src + 3, '$', len - 3);
if (!dollar) {
@@ -514,6 +610,20 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
break;
}
+ if (memchr(src + 3, '\n', dollar - src - 3)) {
+ /* Line break before the next dollar. */
+ continue;
+ }
+
+ spc = memchr(src + 4, ' ', dollar - src - 4);
+ if (spc && spc < dollar-1) {
+ /* There are spaces in unexpected places.
+ * This is probably an id from some other
+ * versioning system. Keep it for now.
+ */
+ continue;
+ }
+
len -= dollar + 1 - src;
src = dollar + 1;
} else {
@@ -544,9 +654,24 @@ static int git_path_check_crlf(const char *path, struct git_attr_check *check)
;
else if (!strcmp(value, "input"))
return CRLF_INPUT;
+ else if (!strcmp(value, "auto"))
+ return CRLF_AUTO;
return CRLF_GUESS;
}
+static int git_path_check_eol(const char *path, struct git_attr_check *check)
+{
+ const char *value = check->value;
+
+ if (ATTR_UNSET(value))
+ ;
+ else if (!strcmp(value, "lf"))
+ return EOL_LF;
+ else if (!strcmp(value, "crlf"))
+ return EOL_CRLF;
+ return EOL_UNSET;
+}
+
static struct convert_driver *git_path_check_convert(const char *path,
struct git_attr_check *check)
{
@@ -568,20 +693,34 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check)
return !!ATTR_TRUE(value);
}
+enum action determine_action(enum action text_attr, enum eol eol_attr) {
+ if (text_attr == CRLF_BINARY)
+ return CRLF_BINARY;
+ if (eol_attr == EOL_LF)
+ return CRLF_INPUT;
+ if (eol_attr == EOL_CRLF)
+ return CRLF_CRLF;
+ return text_attr;
+}
+
int convert_to_git(const char *path, const char *src, size_t len,
struct strbuf *dst, enum safe_crlf checksafe)
{
- struct git_attr_check check[3];
- int crlf = CRLF_GUESS;
+ struct git_attr_check check[5];
+ enum action action = CRLF_GUESS;
+ enum eol eol_attr = EOL_UNSET;
int ident = 0, ret = 0;
const char *filter = NULL;
setup_convert_check(check);
if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
struct convert_driver *drv;
- crlf = git_path_check_crlf(path, check + 0);
+ action = git_path_check_crlf(path, check + 4);
+ if (action == CRLF_GUESS)
+ action = git_path_check_crlf(path, check + 0);
ident = git_path_check_ident(path, check + 1);
drv = git_path_check_convert(path, check + 2);
+ eol_attr = git_path_check_eol(path, check + 3);
if (drv && drv->clean)
filter = drv->clean;
}
@@ -591,7 +730,8 @@ int convert_to_git(const char *path, const char *src, size_t len,
src = dst->buf;
len = dst->len;
}
- ret |= crlf_to_git(path, src, len, dst, crlf, checksafe);
+ action = determine_action(action, eol_attr);
+ ret |= crlf_to_git(path, src, len, dst, action, checksafe);
if (ret) {
src = dst->buf;
len = dst->len;
@@ -601,17 +741,21 @@ int convert_to_git(const char *path, const char *src, size_t len,
int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
{
- struct git_attr_check check[3];
- int crlf = CRLF_GUESS;
+ struct git_attr_check check[5];
+ enum action action = CRLF_GUESS;
+ enum eol eol_attr = EOL_UNSET;
int ident = 0, ret = 0;
const char *filter = NULL;
setup_convert_check(check);
if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
struct convert_driver *drv;
- crlf = git_path_check_crlf(path, check + 0);
+ action = git_path_check_crlf(path, check + 4);
+ if (action == CRLF_GUESS)
+ action = git_path_check_crlf(path, check + 0);
ident = git_path_check_ident(path, check + 1);
drv = git_path_check_convert(path, check + 2);
+ eol_attr = git_path_check_eol(path, check + 3);
if (drv && drv->smudge)
filter = drv->smudge;
}
@@ -621,7 +765,8 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc
src = dst->buf;
len = dst->len;
}
- ret |= crlf_to_worktree(path, src, len, dst, crlf);
+ action = determine_action(action, eol_attr);
+ ret |= crlf_to_worktree(path, src, len, dst, action);
if (ret) {
src = dst->buf;
len = dst->len;
diff --git a/ctype.c b/ctype.c
index 7ee64c7..de60027 100644
--- a/ctype.c
+++ b/ctype.c
@@ -10,7 +10,7 @@ enum {
A = GIT_ALPHA,
D = GIT_DIGIT,
G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
- R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */
+ R = GIT_REGEX_SPECIAL /* $, (, ), +, ., ^, {, | */
};
unsigned char sane_ctype[256] = {
diff --git a/daemon.c b/daemon.c
index a90ab10..e22a2b7 100644
--- a/daemon.c
+++ b/daemon.c
@@ -141,15 +141,14 @@ static char *path_ok(char *directory)
}
else if (interpolated_path && saw_extended_args) {
struct strbuf expanded_path = STRBUF_INIT;
- struct strbuf_expand_dict_entry dict[] = {
- { "H", hostname },
- { "CH", canon_hostname },
- { "IP", ip_address },
- { "P", tcp_port },
- { "D", directory },
- { NULL }
- };
-
+ struct strbuf_expand_dict_entry dict[6];
+
+ dict[0].placeholder = "H"; dict[0].value = hostname;
+ dict[1].placeholder = "CH"; dict[1].value = canon_hostname;
+ dict[2].placeholder = "IP"; dict[2].value = ip_address;
+ dict[3].placeholder = "P"; dict[3].value = tcp_port;
+ dict[4].placeholder = "D"; dict[4].value = directory;
+ dict[5].placeholder = NULL; dict[5].value = NULL;
if (*dir != '/') {
/* Allow only absolute */
logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
@@ -343,7 +342,9 @@ static int upload_pack(void)
{
/* Timeout as string */
char timeout_buf[64];
- const char *argv[] = { "upload-pack", "--strict", timeout_buf, ".", NULL };
+ const char *argv[] = { "upload-pack", "--strict", NULL, ".", NULL };
+
+ argv[2] = timeout_buf;
snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
return run_service_command(argv);
diff --git a/date.c b/date.c
index 6bae49c..3c981f7 100644
--- a/date.c
+++ b/date.c
@@ -586,11 +586,17 @@ static int date_string(unsigned long date, int offset, char *buf, int len)
/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
(i.e. English) day/month names, and it doesn't work correctly with %z. */
-int parse_date(const char *date, char *result, int maxlen)
+int parse_date_toffset(const char *date, unsigned long *timestamp, int *offset)
{
struct tm tm;
- int offset, tm_gmt;
- time_t then;
+ int tm_gmt;
+ unsigned long dummy_timestamp;
+ int dummy_offset;
+
+ if (!timestamp)
+ timestamp = &dummy_timestamp;
+ if (!offset)
+ offset = &dummy_offset;
memset(&tm, 0, sizeof(tm));
tm.tm_year = -1;
@@ -600,7 +606,7 @@ int parse_date(const char *date, char *result, int maxlen)
tm.tm_hour = -1;
tm.tm_min = -1;
tm.tm_sec = -1;
- offset = -1;
+ *offset = -1;
tm_gmt = 0;
for (;;) {
@@ -612,11 +618,11 @@ int parse_date(const char *date, char *result, int maxlen)
break;
if (isalpha(c))
- match = match_alpha(date, &tm, &offset);
+ match = match_alpha(date, &tm, offset);
else if (isdigit(c))
- match = match_digit(date, &tm, &offset, &tm_gmt);
+ match = match_digit(date, &tm, offset, &tm_gmt);
else if ((c == '-' || c == '+') && isdigit(date[1]))
- match = match_tz(date, &offset);
+ match = match_tz(date, offset);
if (!match) {
/* BAD CRAP */
@@ -627,16 +633,26 @@ int parse_date(const char *date, char *result, int maxlen)
}
/* mktime uses local timezone */
- then = tm_to_time_t(&tm);
- if (offset == -1)
- offset = (then - mktime(&tm)) / 60;
+ *timestamp = tm_to_time_t(&tm);
+ if (*offset == -1)
+ *offset = ((time_t)*timestamp - mktime(&tm)) / 60;
- if (then == -1)
+ if (*timestamp == -1)
return -1;
if (!tm_gmt)
- then -= offset * 60;
- return date_string(then, offset, result, maxlen);
+ *timestamp -= *offset * 60;
+ return 1; /* success */
+}
+
+int parse_date(const char *date, char *result, int maxlen)
+{
+ unsigned long timestamp;
+ int offset;
+ if (parse_date_toffset(date, &timestamp, &offset) > 0)
+ return date_string(timestamp, offset, result, maxlen);
+ else
+ return -1;
}
enum date_mode parse_date_format(const char *format)
@@ -984,11 +1000,12 @@ static unsigned long approxidate_str(const char *date,
unsigned long approxidate_relative(const char *date, const struct timeval *tv)
{
- char buffer[50];
+ unsigned long timestamp;
+ int offset;
int errors = 0;
- if (parse_date(date, buffer, sizeof(buffer)) > 0)
- return strtoul(buffer, NULL, 0);
+ if (parse_date_toffset(date, &timestamp, &offset) > 0)
+ return timestamp;
return approxidate_str(date, tv, &errors);
}
@@ -996,14 +1013,15 @@ unsigned long approxidate_relative(const char *date, const struct timeval *tv)
unsigned long approxidate_careful(const char *date, int *error_ret)
{
struct timeval tv;
- char buffer[50];
+ unsigned long timestamp;
+ int offset;
int dummy = 0;
if (!error_ret)
error_ret = &dummy;
- if (parse_date(date, buffer, sizeof(buffer)) > 0) {
+ if (parse_date_toffset(date, &timestamp, &offset) > 0) {
*error_ret = 0;
- return strtoul(buffer, NULL, 0);
+ return timestamp;
}
gettimeofday(&tv, NULL);
diff --git a/diff-lib.c b/diff-lib.c
index c9f6e05..8b8978ae 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -70,6 +70,7 @@ static int match_stat_with_submodule(struct diff_options *diffopt,
int changed = ce_match_stat(ce, st, ce_option);
if (S_ISGITLINK(ce->ce_mode)
&& !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)
+ && !DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
&& (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) {
*dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
}
diff --git a/diff-no-index.c b/diff-no-index.c
index 4cd9dac..43aeeba 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -26,7 +26,7 @@ static int read_directory(const char *path, struct string_list *list)
while ((e = readdir(dir)))
if (strcmp(".", e->d_name) && strcmp("..", e->d_name))
- string_list_insert(e->d_name, list);
+ string_list_insert(list, e->d_name);
closedir(dir);
return 0;
diff --git a/diff.c b/diff.c
index 57e1212..17873f3 100644
--- a/diff.c
+++ b/diff.c
@@ -30,6 +30,7 @@ static const char *diff_word_regex_cfg;
static const char *external_diff_cmd_cfg;
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
+static int diff_no_prefix;
static char diff_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
@@ -43,9 +44,6 @@ static char diff_colors[][COLOR_MAXLEN] = {
GIT_COLOR_NORMAL, /* FUNCINFO */
};
-static void diff_filespec_load_driver(struct diff_filespec *one);
-static char *run_textconv(const char *, struct diff_filespec *, size_t *);
-
static int parse_diff_color_slot(const char *var, int ofs)
{
if (!strcasecmp(var+ofs, "plain"))
@@ -100,6 +98,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
diff_mnemonic_prefix = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "diff.noprefix")) {
+ diff_no_prefix = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "diff.external"))
return git_config_string(&external_diff_cmd_cfg, var, value);
if (!strcmp(var, "diff.wordregex"))
@@ -193,8 +195,8 @@ struct emit_callback {
sane_truncate_fn truncate;
const char **label_path;
struct diff_words_data *diff_words;
+ struct diff_options *opt;
int *found_changesp;
- FILE *file;
struct strbuf *header;
};
@@ -281,11 +283,19 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
}
-static void emit_line_0(FILE *file, const char *set, const char *reset,
+static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
int first, const char *line, int len)
{
int has_trailing_newline, has_trailing_carriage_return;
int nofirst;
+ FILE *file = o->file;
+
+ if (o->output_prefix) {
+ struct strbuf *msg = NULL;
+ msg = o->output_prefix(o, o->output_prefix_data);
+ assert(msg);
+ fwrite(msg->buf, msg->len, 1, file);
+ }
if (len == 0) {
has_trailing_newline = (first == '\n');
@@ -315,10 +325,10 @@ static void emit_line_0(FILE *file, const char *set, const char *reset,
fputc('\n', file);
}
-static void emit_line(FILE *file, const char *set, const char *reset,
+static void emit_line(struct diff_options *o, const char *set, const char *reset,
const char *line, int len)
{
- emit_line_0(file, set, reset, line[0], line+1, len-1);
+ emit_line_0(o, set, reset, line[0], line+1, len-1);
}
static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
@@ -340,15 +350,15 @@ static void emit_add_line(const char *reset,
const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
if (!*ws)
- emit_line_0(ecbdata->file, set, reset, '+', line, len);
+ emit_line_0(ecbdata->opt, set, reset, '+', line, len);
else if (new_blank_line_at_eof(ecbdata, line, len))
/* Blank line at EOF - paint '+' as well */
- emit_line_0(ecbdata->file, ws, reset, '+', line, len);
+ emit_line_0(ecbdata->opt, ws, reset, '+', line, len);
else {
/* Emit just the prefix, then the rest. */
- emit_line_0(ecbdata->file, set, reset, '+', "", 0);
+ emit_line_0(ecbdata->opt, set, reset, '+', "", 0);
ws_check_emit(line, len, ecbdata->ws_rule,
- ecbdata->file, set, reset, ws);
+ ecbdata->opt->file, set, reset, ws);
}
}
@@ -361,6 +371,9 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
static const char atat[2] = { '@', '@' };
const char *cp, *ep;
+ struct strbuf msgbuf = STRBUF_INIT;
+ int org_len = len;
+ int i = 1;
/*
* As a hunk header must begin with "@@ -<old>, +<new> @@",
@@ -369,23 +382,42 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
if (len < 10 ||
memcmp(line, atat, 2) ||
!(ep = memmem(line + 2, len - 2, atat, 2))) {
- emit_line(ecbdata->file, plain, reset, line, len);
+ emit_line(ecbdata->opt, plain, reset, line, len);
return;
}
ep += 2; /* skip over @@ */
/* The hunk header in fraginfo color */
- emit_line(ecbdata->file, frag, reset, line, ep - line);
+ strbuf_add(&msgbuf, frag, strlen(frag));
+ strbuf_add(&msgbuf, line, ep - line);
+ strbuf_add(&msgbuf, reset, strlen(reset));
+
+ /*
+ * trailing "\r\n"
+ */
+ for ( ; i < 3; i++)
+ if (line[len - i] == '\r' || line[len - i] == '\n')
+ len--;
/* blank before the func header */
for (cp = ep; ep - line < len; ep++)
if (*ep != ' ' && *ep != '\t')
break;
- if (ep != cp)
- emit_line(ecbdata->file, plain, reset, cp, ep - cp);
+ if (ep != cp) {
+ strbuf_add(&msgbuf, plain, strlen(plain));
+ strbuf_add(&msgbuf, cp, ep - cp);
+ strbuf_add(&msgbuf, reset, strlen(reset));
+ }
+
+ if (ep < line + len) {
+ strbuf_add(&msgbuf, func, strlen(func));
+ strbuf_add(&msgbuf, ep, line + len - ep);
+ strbuf_add(&msgbuf, reset, strlen(reset));
+ }
- if (ep < line + len)
- emit_line(ecbdata->file, func, reset, ep, line + len - ep);
+ strbuf_add(&msgbuf, line + len, org_len - len);
+ emit_line(ecbdata->opt, "", "", msgbuf.buf, msgbuf.len);
+ strbuf_release(&msgbuf);
}
static struct diff_tempfile *claim_diff_tempfile(void) {
@@ -445,7 +477,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
len = endp ? (endp - data + 1) : size;
if (prefix != '+') {
ecb->lno_in_preimage++;
- emit_line_0(ecb->file, old, reset, '-',
+ emit_line_0(ecb->opt, old, reset, '-',
data, len);
} else {
ecb->lno_in_postimage++;
@@ -457,7 +489,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
if (!endp) {
const char *plain = diff_get_color(ecb->color_diff,
DIFF_PLAIN);
- emit_line_0(ecb->file, plain, reset, '\\',
+ emit_line_0(ecb->opt, plain, reset, '\\',
nneof, strlen(nneof));
}
}
@@ -466,8 +498,8 @@ static void emit_rewrite_diff(const char *name_a,
const char *name_b,
struct diff_filespec *one,
struct diff_filespec *two,
- const char *textconv_one,
- const char *textconv_two,
+ struct userdiff_driver *textconv_one,
+ struct userdiff_driver *textconv_two,
struct diff_options *o)
{
int lc_a, lc_b;
@@ -478,9 +510,16 @@ static void emit_rewrite_diff(const char *name_a,
const char *reset = diff_get_color(color_diff, DIFF_RESET);
static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
const char *a_prefix, *b_prefix;
- const char *data_one, *data_two;
+ char *data_one, *data_two;
size_t size_one, size_two;
struct emit_callback ecbdata;
+ char *line_prefix = "";
+ struct strbuf *msgbuf;
+
+ if (o && o->output_prefix) {
+ msgbuf = o->output_prefix(o, o->output_prefix_data);
+ line_prefix = msgbuf->buf;
+ }
if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
a_prefix = o->b_prefix;
@@ -500,32 +539,14 @@ static void emit_rewrite_diff(const char *name_a,
quote_two_c_style(&a_name, a_prefix, name_a, 0);
quote_two_c_style(&b_name, b_prefix, name_b, 0);
- diff_populate_filespec(one, 0);
- diff_populate_filespec(two, 0);
- if (textconv_one) {
- data_one = run_textconv(textconv_one, one, &size_one);
- if (!data_one)
- die("unable to read files to diff");
- }
- else {
- data_one = one->data;
- size_one = one->size;
- }
- if (textconv_two) {
- data_two = run_textconv(textconv_two, two, &size_two);
- if (!data_two)
- die("unable to read files to diff");
- }
- else {
- data_two = two->data;
- size_two = two->size;
- }
+ size_one = fill_textconv(textconv_one, one, &data_one);
+ size_two = fill_textconv(textconv_two, two, &data_two);
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.color_diff = color_diff;
ecbdata.found_changesp = &o->found_changes;
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
- ecbdata.file = o->file;
+ ecbdata.opt = o;
if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
mmfile_t mf1, mf2;
mf1.ptr = (char *)data_one;
@@ -540,9 +561,10 @@ static void emit_rewrite_diff(const char *name_a,
lc_a = count_lines(data_one, size_one);
lc_b = count_lines(data_two, size_two);
fprintf(o->file,
- "%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
- metainfo, a_name.buf, name_a_tab, reset,
- metainfo, b_name.buf, name_b_tab, reset, fraginfo);
+ "%s%s--- %s%s%s\n%s%s+++ %s%s%s\n%s%s@@ -",
+ line_prefix, metainfo, a_name.buf, name_a_tab, reset,
+ line_prefix, metainfo, b_name.buf, name_b_tab, reset,
+ line_prefix, fraginfo);
print_line_count(o->file, lc_a);
fprintf(o->file, " +");
print_line_count(o->file, lc_b);
@@ -577,23 +599,134 @@ static void diff_words_append(char *line, unsigned long len,
buffer->text.ptr[buffer->text.size] = '\0';
}
+struct diff_words_style_elem
+{
+ const char *prefix;
+ const char *suffix;
+ const char *color; /* NULL; filled in by the setup code if
+ * color is enabled */
+};
+
+struct diff_words_style
+{
+ enum diff_words_type type;
+ struct diff_words_style_elem new, old, ctx;
+ const char *newline;
+};
+
+struct diff_words_style diff_words_styles[] = {
+ { DIFF_WORDS_PORCELAIN, {"+", "\n"}, {"-", "\n"}, {" ", "\n"}, "~\n" },
+ { DIFF_WORDS_PLAIN, {"{+", "+}"}, {"[-", "-]"}, {"", ""}, "\n" },
+ { DIFF_WORDS_COLOR, {"", ""}, {"", ""}, {"", ""}, "\n" }
+};
+
struct diff_words_data {
struct diff_words_buffer minus, plus;
const char *current_plus;
- FILE *file;
+ int last_minus;
+ struct diff_options *opt;
regex_t *word_regex;
+ enum diff_words_type type;
+ struct diff_words_style *style;
};
+static int fn_out_diff_words_write_helper(FILE *fp,
+ struct diff_words_style_elem *st_el,
+ const char *newline,
+ size_t count, const char *buf,
+ const char *line_prefix)
+{
+ int print = 0;
+
+ while (count) {
+ char *p = memchr(buf, '\n', count);
+ if (print)
+ fputs(line_prefix, fp);
+ if (p != buf) {
+ if (st_el->color && fputs(st_el->color, fp) < 0)
+ return -1;
+ if (fputs(st_el->prefix, fp) < 0 ||
+ fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
+ fputs(st_el->suffix, fp) < 0)
+ return -1;
+ if (st_el->color && *st_el->color
+ && fputs(GIT_COLOR_RESET, fp) < 0)
+ return -1;
+ }
+ if (!p)
+ return 0;
+ if (fputs(newline, fp) < 0)
+ return -1;
+ count -= p + 1 - buf;
+ buf = p + 1;
+ print = 1;
+ }
+ return 0;
+}
+
+/*
+ * '--color-words' algorithm can be described as:
+ *
+ * 1. collect a the minus/plus lines of a diff hunk, divided into
+ * minus-lines and plus-lines;
+ *
+ * 2. break both minus-lines and plus-lines into words and
+ * place them into two mmfile_t with one word for each line;
+ *
+ * 3. use xdiff to run diff on the two mmfile_t to get the words level diff;
+ *
+ * And for the common parts of the both file, we output the plus side text.
+ * diff_words->current_plus is used to trace the current position of the plus file
+ * which printed. diff_words->last_minus is used to trace the last minus word
+ * printed.
+ *
+ * For '--graph' to work with '--color-words', we need to output the graph prefix
+ * on each line of color words output. Generally, there are two conditions on
+ * which we should output the prefix.
+ *
+ * 1. diff_words->last_minus == 0 &&
+ * diff_words->current_plus == diff_words->plus.text.ptr
+ *
+ * that is: the plus text must start as a new line, and if there is no minus
+ * word printed, a graph prefix must be printed.
+ *
+ * 2. diff_words->current_plus > diff_words->plus.text.ptr &&
+ * *(diff_words->current_plus - 1) == '\n'
+ *
+ * that is: a graph prefix must be printed following a '\n'
+ */
+static int color_words_output_graph_prefix(struct diff_words_data *diff_words)
+{
+ if ((diff_words->last_minus == 0 &&
+ diff_words->current_plus == diff_words->plus.text.ptr) ||
+ (diff_words->current_plus > diff_words->plus.text.ptr &&
+ *(diff_words->current_plus - 1) == '\n')) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
{
struct diff_words_data *diff_words = priv;
+ struct diff_words_style *style = diff_words->style;
int minus_first, minus_len, plus_first, plus_len;
const char *minus_begin, *minus_end, *plus_begin, *plus_end;
+ struct diff_options *opt = diff_words->opt;
+ struct strbuf *msgbuf;
+ char *line_prefix = "";
if (line[0] != '@' || parse_hunk_header(line, len,
&minus_first, &minus_len, &plus_first, &plus_len))
return;
+ assert(opt);
+ if (opt->output_prefix) {
+ msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
+ line_prefix = msgbuf->buf;
+ }
+
/* POSIX requires that first be decremented by one if len == 0... */
if (minus_len) {
minus_begin = diff_words->minus.orig[minus_first].begin;
@@ -609,20 +742,32 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
} else
plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
- if (diff_words->current_plus != plus_begin)
- fwrite(diff_words->current_plus,
- plus_begin - diff_words->current_plus, 1,
- diff_words->file);
- if (minus_begin != minus_end)
- color_fwrite_lines(diff_words->file,
- diff_get_color(1, DIFF_FILE_OLD),
- minus_end - minus_begin, minus_begin);
- if (plus_begin != plus_end)
- color_fwrite_lines(diff_words->file,
- diff_get_color(1, DIFF_FILE_NEW),
- plus_end - plus_begin, plus_begin);
+ if (color_words_output_graph_prefix(diff_words)) {
+ fputs(line_prefix, diff_words->opt->file);
+ }
+ if (diff_words->current_plus != plus_begin) {
+ fn_out_diff_words_write_helper(diff_words->opt->file,
+ &style->ctx, style->newline,
+ plus_begin - diff_words->current_plus,
+ diff_words->current_plus, line_prefix);
+ if (*(plus_begin - 1) == '\n')
+ fputs(line_prefix, diff_words->opt->file);
+ }
+ if (minus_begin != minus_end) {
+ fn_out_diff_words_write_helper(diff_words->opt->file,
+ &style->old, style->newline,
+ minus_end - minus_begin, minus_begin,
+ line_prefix);
+ }
+ if (plus_begin != plus_end) {
+ fn_out_diff_words_write_helper(diff_words->opt->file,
+ &style->new, style->newline,
+ plus_end - plus_begin, plus_begin,
+ line_prefix);
+ }
diff_words->current_plus = plus_end;
+ diff_words->last_minus = minus_first;
}
/* This function starts looking at *begin, and returns 0 iff a word was found. */
@@ -701,17 +846,31 @@ static void diff_words_show(struct diff_words_data *diff_words)
xpparam_t xpp;
xdemitconf_t xecfg;
mmfile_t minus, plus;
+ struct diff_words_style *style = diff_words->style;
+
+ struct diff_options *opt = diff_words->opt;
+ struct strbuf *msgbuf;
+ char *line_prefix = "";
+
+ assert(opt);
+ if (opt->output_prefix) {
+ msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
+ line_prefix = msgbuf->buf;
+ }
/* special case: only removal */
if (!diff_words->plus.text.size) {
- color_fwrite_lines(diff_words->file,
- diff_get_color(1, DIFF_FILE_OLD),
- diff_words->minus.text.size, diff_words->minus.text.ptr);
+ fputs(line_prefix, diff_words->opt->file);
+ fn_out_diff_words_write_helper(diff_words->opt->file,
+ &style->old, style->newline,
+ diff_words->minus.text.size,
+ diff_words->minus.text.ptr, line_prefix);
diff_words->minus.text.size = 0;
return;
}
diff_words->current_plus = diff_words->plus.text.ptr;
+ diff_words->last_minus = 0;
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
@@ -725,11 +884,15 @@ static void diff_words_show(struct diff_words_data *diff_words)
free(minus.ptr);
free(plus.ptr);
if (diff_words->current_plus != diff_words->plus.text.ptr +
- diff_words->plus.text.size)
- fwrite(diff_words->current_plus,
+ diff_words->plus.text.size) {
+ if (color_words_output_graph_prefix(diff_words))
+ fputs(line_prefix, diff_words->opt->file);
+ fn_out_diff_words_write_helper(diff_words->opt->file,
+ &style->ctx, style->newline,
diff_words->plus.text.ptr + diff_words->plus.text.size
- - diff_words->current_plus, 1,
- diff_words->file);
+ - diff_words->current_plus, diff_words->current_plus,
+ line_prefix);
+ }
diff_words->minus.text.size = diff_words->plus.text.size = 0;
}
@@ -801,9 +964,17 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
+ struct diff_options *o = ecbdata->opt;
+ char *line_prefix = "";
+ struct strbuf *msgbuf;
+
+ if (o && o->output_prefix) {
+ msgbuf = o->output_prefix(o, o->output_prefix_data);
+ line_prefix = msgbuf->buf;
+ }
if (ecbdata->header) {
- fprintf(ecbdata->file, "%s", ecbdata->header->buf);
+ fprintf(ecbdata->opt->file, "%s", ecbdata->header->buf);
strbuf_reset(ecbdata->header);
ecbdata->header = NULL;
}
@@ -815,10 +986,10 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
- fprintf(ecbdata->file, "%s--- %s%s%s\n",
- meta, ecbdata->label_path[0], reset, name_a_tab);
- fprintf(ecbdata->file, "%s+++ %s%s%s\n",
- meta, ecbdata->label_path[1], reset, name_b_tab);
+ fprintf(ecbdata->opt->file, "%s%s--- %s%s%s\n",
+ line_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);
+ fprintf(ecbdata->opt->file, "%s%s+++ %s%s%s\n",
+ line_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);
ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
}
@@ -835,12 +1006,15 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
find_lno(line, ecbdata);
emit_hunk_header(ecbdata, line, len);
if (line[len-1] != '\n')
- putc('\n', ecbdata->file);
+ putc('\n', ecbdata->opt->file);
return;
}
if (len < 1) {
- emit_line(ecbdata->file, reset, reset, line, len);
+ emit_line(ecbdata->opt, reset, reset, line, len);
+ if (ecbdata->diff_words
+ && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)
+ fputs("~\n", ecbdata->opt->file);
return;
}
@@ -855,9 +1029,13 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
return;
}
diff_words_flush(ecbdata);
- line++;
- len--;
- emit_line(ecbdata->file, plain, reset, line, len);
+ if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
+ emit_line(ecbdata->opt, plain, reset, line, len);
+ fputs("~\n", ecbdata->opt->file);
+ } else {
+ /* don't print the prefix character */
+ emit_line(ecbdata->opt, plain, reset, line+1, len-1);
+ }
return;
}
@@ -868,7 +1046,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
ecbdata->lno_in_preimage++;
if (line[0] == ' ')
ecbdata->lno_in_postimage++;
- emit_line(ecbdata->file, color, reset, line, len);
+ emit_line(ecbdata->opt, color, reset, line, len);
} else {
ecbdata->lno_in_postimage++;
emit_add_line(reset, ecbdata, line + 1, len - 1);
@@ -1048,10 +1226,17 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
int total_files = data->nr;
int width, name_width;
const char *reset, *set, *add_c, *del_c;
+ const char *line_prefix = "";
+ struct strbuf *msg = NULL;
if (data->nr == 0)
return;
+ if (options->output_prefix) {
+ msg = options->output_prefix(options, options->output_prefix_data);
+ line_prefix = msg->buf;
+ }
+
width = options->stat_width ? options->stat_width : 80;
name_width = options->stat_name_width ? options->stat_name_width : 50;
@@ -1121,6 +1306,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
}
if (data->files[i]->is_binary) {
+ fprintf(options->file, "%s", line_prefix);
show_name(options->file, prefix, name, len);
fprintf(options->file, " Bin ");
fprintf(options->file, "%s%"PRIuMAX"%s",
@@ -1133,6 +1319,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
continue;
}
else if (data->files[i]->is_unmerged) {
+ fprintf(options->file, "%s", line_prefix);
show_name(options->file, prefix, name, len);
fprintf(options->file, " Unmerged\n");
continue;
@@ -1155,6 +1342,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
add = scale_linear(add, width, max_change);
del = scale_linear(del, width, max_change);
}
+ fprintf(options->file, "%s", line_prefix);
show_name(options->file, prefix, name, len);
fprintf(options->file, "%5"PRIuMAX"%s", added + deleted,
added + deleted ? " " : "");
@@ -1162,6 +1350,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
show_graph(options->file, '-', del, del_c, reset);
fprintf(options->file, "\n");
}
+ fprintf(options->file, "%s", line_prefix);
fprintf(options->file,
" %d files changed, %d insertions(+), %d deletions(-)\n",
total_files, adds, dels);
@@ -1188,6 +1377,12 @@ static void show_shortstats(struct diffstat_t *data, struct diff_options *option
}
}
}
+ if (options->output_prefix) {
+ struct strbuf *msg = NULL;
+ msg = options->output_prefix(options,
+ options->output_prefix_data);
+ fprintf(options->file, "%s", msg->buf);
+ }
fprintf(options->file, " %d files changed, %d insertions(+), %d deletions(-)\n",
total_files, adds, dels);
}
@@ -1202,6 +1397,13 @@ static void show_numstat(struct diffstat_t *data, struct diff_options *options)
for (i = 0; i < data->nr; i++) {
struct diffstat_file *file = data->files[i];
+ if (options->output_prefix) {
+ struct strbuf *msg = NULL;
+ msg = options->output_prefix(options,
+ options->output_prefix_data);
+ fprintf(options->file, "%s", msg->buf);
+ }
+
if (file->is_binary)
fprintf(options->file, "-\t-\t");
else
@@ -1237,10 +1439,18 @@ struct dirstat_dir {
int alloc, nr, percent, cumulative;
};
-static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen)
+static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,
+ unsigned long changed, const char *base, int baselen)
{
unsigned long this_dir = 0;
unsigned int sources = 0;
+ const char *line_prefix = "";
+ struct strbuf *msg = NULL;
+
+ if (opt->output_prefix) {
+ msg = opt->output_prefix(opt, opt->output_prefix_data);
+ line_prefix = msg->buf;
+ }
while (dir->nr) {
struct dirstat_file *f = dir->files;
@@ -1255,7 +1465,7 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch
slash = strchr(f->name + baselen, '/');
if (slash) {
int newbaselen = slash + 1 - f->name;
- this = gather_dirstat(file, dir, changed, f->name, newbaselen);
+ this = gather_dirstat(opt, dir, changed, f->name, newbaselen);
sources++;
} else {
this = f->changed;
@@ -1277,7 +1487,8 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch
if (permille) {
int percent = permille / 10;
if (percent >= dir->percent) {
- fprintf(file, "%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
+ fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix,
+ percent, permille % 10, baselen, base);
if (!dir->cumulative)
return 0;
}
@@ -1357,7 +1568,7 @@ static void show_dirstat(struct diff_options *options)
/* Show all directories with more than x% of the changes */
qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
- gather_dirstat(options->file, &dir, changed, "", 0);
+ gather_dirstat(options, &dir, changed, "", 0);
}
static void free_diffstat_info(struct diffstat_t *diffstat)
@@ -1415,6 +1626,15 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
const char *reset = diff_get_color(color_diff, DIFF_RESET);
const char *set = diff_get_color(color_diff, DIFF_FILE_NEW);
char *err;
+ char *line_prefix = "";
+ struct strbuf *msgbuf;
+
+ assert(data->o);
+ if (data->o->output_prefix) {
+ msgbuf = data->o->output_prefix(data->o,
+ data->o->output_prefix_data);
+ line_prefix = msgbuf->buf;
+ }
if (line[0] == '+') {
unsigned bad;
@@ -1422,18 +1642,18 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
if (is_conflict_marker(line + 1, marker_size, len - 1)) {
data->status |= 1;
fprintf(data->o->file,
- "%s:%d: leftover conflict marker\n",
- data->filename, data->lineno);
+ "%s%s:%d: leftover conflict marker\n",
+ line_prefix, data->filename, data->lineno);
}
bad = ws_check(line + 1, len - 1, data->ws_rule);
if (!bad)
return;
data->status |= bad;
err = whitespace_error_string(bad);
- fprintf(data->o->file, "%s:%d: %s.\n",
- data->filename, data->lineno, err);
+ fprintf(data->o->file, "%s%s:%d: %s.\n",
+ line_prefix, data->filename, data->lineno, err);
free(err);
- emit_line(data->o->file, set, reset, line, 1);
+ emit_line(data->o, set, reset, line, 1);
ws_check_emit(line + 1, len - 1, data->ws_rule,
data->o->file, set, reset, ws);
} else if (line[0] == ' ') {
@@ -1471,7 +1691,7 @@ static unsigned char *deflate_it(char *data,
return deflated;
}
-static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two, char *prefix)
{
void *cp;
void *delta;
@@ -1500,13 +1720,13 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
}
if (delta && delta_size < deflate_size) {
- fprintf(file, "delta %lu\n", orig_size);
+ fprintf(file, "%sdelta %lu\n", prefix, orig_size);
free(deflated);
data = delta;
data_size = delta_size;
}
else {
- fprintf(file, "literal %lu\n", two->size);
+ fprintf(file, "%sliteral %lu\n", prefix, two->size);
free(delta);
data = deflated;
data_size = deflate_size;
@@ -1524,18 +1744,19 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
line[0] = bytes - 26 + 'a' - 1;
encode_85(line + 1, cp, bytes);
cp = (char *) cp + bytes;
+ fprintf(file, "%s", prefix);
fputs(line, file);
fputc('\n', file);
}
- fprintf(file, "\n");
+ fprintf(file, "%s\n", prefix);
free(data);
}
-static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two, char *prefix)
{
- fprintf(file, "GIT binary patch\n");
- emit_binary_diff_body(file, one, two);
- emit_binary_diff_body(file, two, one);
+ fprintf(file, "%sGIT binary patch\n", prefix);
+ emit_binary_diff_body(file, one, two, prefix);
+ emit_binary_diff_body(file, two, one, prefix);
}
static void diff_filespec_load_driver(struct diff_filespec *one)
@@ -1585,14 +1806,26 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
options->b_prefix = b;
}
-static const char *get_textconv(struct diff_filespec *one)
+struct userdiff_driver *get_textconv(struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one))
return NULL;
if (!S_ISREG(one->mode))
return NULL;
diff_filespec_load_driver(one);
- return one->driver->textconv;
+ if (!one->driver->textconv)
+ return NULL;
+
+ if (one->driver->textconv_want_cache && !one->driver->textconv_cache) {
+ struct notes_cache *c = xmalloc(sizeof(*c));
+ struct strbuf name = STRBUF_INIT;
+
+ strbuf_addf(&name, "textconv/%s", one->driver->name);
+ notes_cache_init(c, name.buf, one->driver->textconv);
+ one->driver->textconv_cache = c;
+ }
+
+ return one->driver;
}
static void builtin_diff(const char *name_a,
@@ -1610,8 +1843,16 @@ static void builtin_diff(const char *name_a,
const char *set = diff_get_color_opt(o, DIFF_METAINFO);
const char *reset = diff_get_color_opt(o, DIFF_RESET);
const char *a_prefix, *b_prefix;
- const char *textconv_one = NULL, *textconv_two = NULL;
+ struct userdiff_driver *textconv_one = NULL;
+ struct userdiff_driver *textconv_two = NULL;
struct strbuf header = STRBUF_INIT;
+ struct strbuf *msgbuf;
+ char *line_prefix = "";
+
+ if (o->output_prefix) {
+ msgbuf = o->output_prefix(o, o->output_prefix_data);
+ line_prefix = msgbuf->buf;
+ }
if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
(!one->mode || S_ISGITLINK(one->mode)) &&
@@ -1646,24 +1887,24 @@ static void builtin_diff(const char *name_a,
b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
- strbuf_addf(&header, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
+ strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, set, a_one, b_two, reset);
if (lbl[0][0] == '/') {
/* /dev/null */
- strbuf_addf(&header, "%snew file mode %06o%s\n", set, two->mode, reset);
+ strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, set, two->mode, reset);
if (xfrm_msg)
strbuf_addstr(&header, xfrm_msg);
must_show_header = 1;
}
else if (lbl[1][0] == '/') {
- strbuf_addf(&header, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
+ strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, set, one->mode, reset);
if (xfrm_msg)
strbuf_addstr(&header, xfrm_msg);
must_show_header = 1;
}
else {
if (one->mode != two->mode) {
- strbuf_addf(&header, "%sold mode %06o%s\n", set, one->mode, reset);
- strbuf_addf(&header, "%snew mode %06o%s\n", set, two->mode, reset);
+ strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, set, one->mode, reset);
+ strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, set, two->mode, reset);
must_show_header = 1;
}
if (xfrm_msg)
@@ -1687,12 +1928,11 @@ static void builtin_diff(const char *name_a,
}
}
- if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
- die("unable to read files to diff");
-
if (!DIFF_OPT_TST(o, TEXT) &&
- ( (diff_filespec_is_binary(one) && !textconv_one) ||
- (diff_filespec_is_binary(two) && !textconv_two) )) {
+ ( (!textconv_one && diff_filespec_is_binary(one)) ||
+ (!textconv_two && diff_filespec_is_binary(two)) )) {
+ if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ die("unable to read files to diff");
/* Quite common confusing case */
if (mf1.size == mf2.size &&
!memcmp(mf1.ptr, mf2.ptr, mf1.size)) {
@@ -1703,10 +1943,10 @@ static void builtin_diff(const char *name_a,
fprintf(o->file, "%s", header.buf);
strbuf_reset(&header);
if (DIFF_OPT_TST(o, BINARY))
- emit_binary_diff(o->file, &mf1, &mf2);
+ emit_binary_diff(o->file, &mf1, &mf2, line_prefix);
else
- fprintf(o->file, "Binary files %s and %s differ\n",
- lbl[0], lbl[1]);
+ fprintf(o->file, "%sBinary files %s and %s differ\n",
+ line_prefix, lbl[0], lbl[1]);
o->found_changes = 1;
}
else {
@@ -1722,20 +1962,8 @@ static void builtin_diff(const char *name_a,
strbuf_reset(&header);
}
- if (textconv_one) {
- size_t size;
- mf1.ptr = run_textconv(textconv_one, one, &size);
- if (!mf1.ptr)
- die("unable to read files to diff");
- mf1.size = size;
- }
- if (textconv_two) {
- size_t size;
- mf2.ptr = run_textconv(textconv_two, two, &size);
- if (!mf2.ptr)
- die("unable to read files to diff");
- mf2.size = size;
- }
+ mf1.size = fill_textconv(textconv_one, one, &mf1.ptr);
+ mf2.size = fill_textconv(textconv_two, two, &mf2.ptr);
pe = diff_funcname_pattern(one);
if (!pe)
@@ -1750,7 +1978,7 @@ static void builtin_diff(const char *name_a,
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
check_blank_at_eof(&mf1, &mf2, &ecbdata);
- ecbdata.file = o->file;
+ ecbdata.opt = o;
ecbdata.header = header.len ? &header : NULL;
xpp.flags = o->xdl_opts;
xecfg.ctxlen = o->context;
@@ -1764,10 +1992,13 @@ static void builtin_diff(const char *name_a,
xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
else if (!prefixcmp(diffopts, "-u"))
xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
- if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
+ if (o->word_diff) {
+ int i;
+
ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data));
- ecbdata.diff_words->file = o->file;
+ ecbdata.diff_words->type = o->word_diff;
+ ecbdata.diff_words->opt = o;
if (!o->word_regex)
o->word_regex = userdiff_word_regex(one);
if (!o->word_regex)
@@ -1783,10 +2014,23 @@ static void builtin_diff(const char *name_a,
die ("Invalid regular expression: %s",
o->word_regex);
}
+ for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
+ if (o->word_diff == diff_words_styles[i].type) {
+ ecbdata.diff_words->style =
+ &diff_words_styles[i];
+ break;
+ }
+ }
+ if (DIFF_OPT_TST(o, COLOR_DIFF)) {
+ struct diff_words_style *st = ecbdata.diff_words->style;
+ st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
+ st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
+ st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
+ }
}
xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
&xpp, &xecfg);
- if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
+ if (o->word_diff)
free_diff_words_data(&ecbdata);
if (textconv_one)
free(mf1.ptr);
@@ -2332,31 +2576,41 @@ static void fill_metainfo(struct strbuf *msg,
{
const char *set = diff_get_color(use_color, DIFF_METAINFO);
const char *reset = diff_get_color(use_color, DIFF_RESET);
+ struct strbuf *msgbuf;
+ char *line_prefix = "";
*must_show_header = 1;
+ if (o->output_prefix) {
+ msgbuf = o->output_prefix(o, o->output_prefix_data);
+ line_prefix = msgbuf->buf;
+ }
strbuf_init(msg, PATH_MAX * 2 + 300);
switch (p->status) {
case DIFF_STATUS_COPIED:
- strbuf_addf(msg, "%ssimilarity index %d%%",
- set, similarity_index(p));
- strbuf_addf(msg, "%s\n%scopy from ", reset, set);
+ strbuf_addf(msg, "%s%ssimilarity index %d%%",
+ line_prefix, set, similarity_index(p));
+ strbuf_addf(msg, "%s\n%s%scopy from ",
+ reset, line_prefix, set);
quote_c_style(name, msg, NULL, 0);
- strbuf_addf(msg, "%s\n%scopy to ", reset, set);
+ strbuf_addf(msg, "%s\n%s%scopy to ", reset, line_prefix, set);
quote_c_style(other, msg, NULL, 0);
strbuf_addf(msg, "%s\n", reset);
break;
case DIFF_STATUS_RENAMED:
- strbuf_addf(msg, "%ssimilarity index %d%%",
- set, similarity_index(p));
- strbuf_addf(msg, "%s\n%srename from ", reset, set);
+ strbuf_addf(msg, "%s%ssimilarity index %d%%",
+ line_prefix, set, similarity_index(p));
+ strbuf_addf(msg, "%s\n%s%srename from ",
+ reset, line_prefix, set);
quote_c_style(name, msg, NULL, 0);
- strbuf_addf(msg, "%s\n%srename to ", reset, set);
+ strbuf_addf(msg, "%s\n%s%srename to ",
+ reset, line_prefix, set);
quote_c_style(other, msg, NULL, 0);
strbuf_addf(msg, "%s\n", reset);
break;
case DIFF_STATUS_MODIFIED:
if (p->score) {
- strbuf_addf(msg, "%sdissimilarity index %d%%%s\n",
+ strbuf_addf(msg, "%s%sdissimilarity index %d%%%s\n",
+ line_prefix,
set, similarity_index(p), reset);
break;
}
@@ -2373,9 +2627,9 @@ static void fill_metainfo(struct strbuf *msg,
(!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
abbrev = 40;
}
- strbuf_addf(msg, "%sindex %.*s..%.*s", set,
- abbrev, sha1_to_hex(one->sha1),
- abbrev, sha1_to_hex(two->sha1));
+ strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
+ find_unique_abbrev(one->sha1, abbrev));
+ strbuf_addstr(msg, find_unique_abbrev(two->sha1, abbrev));
if (one->mode == two->mode)
strbuf_addf(msg, " %06o", one->mode);
strbuf_addf(msg, "%s\n", reset);
@@ -2560,6 +2814,7 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
void diff_setup(struct diff_options *options)
{
memset(options, 0, sizeof(*options));
+ memset(&diff_queued_diff, 0, sizeof(diff_queued_diff));
options->file = stdout;
@@ -2575,7 +2830,9 @@ void diff_setup(struct diff_options *options)
DIFF_OPT_SET(options, COLOR_DIFF);
options->detect_rename = diff_detect_rename_default;
- if (!diff_mnemonic_prefix) {
+ if (diff_no_prefix) {
+ options->a_prefix = options->b_prefix = "";
+ } else if (!diff_mnemonic_prefix) {
options->a_prefix = "a/";
options->b_prefix = "b/";
}
@@ -2738,7 +2995,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
const char *arg = av[0];
/* Output format options */
- if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
+ if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch"))
options->output_format |= DIFF_FORMAT_PATCH;
else if (opt_arg(arg, 'U', "unified", &options->context))
options->output_format |= DIFF_FORMAT_PATCH;
@@ -2866,13 +3123,37 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
DIFF_OPT_CLR(options, COLOR_DIFF);
else if (!strcmp(arg, "--color-words")) {
DIFF_OPT_SET(options, COLOR_DIFF);
- DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
+ options->word_diff = DIFF_WORDS_COLOR;
}
else if (!prefixcmp(arg, "--color-words=")) {
DIFF_OPT_SET(options, COLOR_DIFF);
- DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
+ options->word_diff = DIFF_WORDS_COLOR;
options->word_regex = arg + 14;
}
+ else if (!strcmp(arg, "--word-diff")) {
+ if (options->word_diff == DIFF_WORDS_NONE)
+ options->word_diff = DIFF_WORDS_PLAIN;
+ }
+ else if (!prefixcmp(arg, "--word-diff=")) {
+ const char *type = arg + 12;
+ if (!strcmp(type, "plain"))
+ options->word_diff = DIFF_WORDS_PLAIN;
+ else if (!strcmp(type, "color")) {
+ DIFF_OPT_SET(options, COLOR_DIFF);
+ options->word_diff = DIFF_WORDS_COLOR;
+ }
+ else if (!strcmp(type, "porcelain"))
+ options->word_diff = DIFF_WORDS_PORCELAIN;
+ else if (!strcmp(type, "none"))
+ options->word_diff = DIFF_WORDS_NONE;
+ else
+ die("bad --word-diff argument: %s", type);
+ }
+ else if (!prefixcmp(arg, "--word-diff-regex=")) {
+ if (options->word_diff == DIFF_WORDS_NONE)
+ options->word_diff = DIFF_WORDS_PLAIN;
+ options->word_regex = arg + 18;
+ }
else if (!strcmp(arg, "--exit-code"))
DIFF_OPT_SET(options, EXIT_WITH_STATUS);
else if (!strcmp(arg, "--quiet"))
@@ -2886,7 +3167,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
else if (!strcmp(arg, "--no-textconv"))
DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
else if (!strcmp(arg, "--ignore-submodules"))
- DIFF_OPT_SET(options, IGNORE_SUBMODULES);
+ handle_ignore_submodules_arg(options, "all");
+ else if (!prefixcmp(arg, "--ignore-submodules="))
+ handle_ignore_submodules_arg(options, arg + 20);
else if (!strcmp(arg, "--submodule"))
DIFF_OPT_SET(options, SUBMODULE_LOG);
else if (!prefixcmp(arg, "--submodule=")) {
@@ -3059,6 +3342,11 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
{
int line_termination = opt->line_termination;
int inter_name_termination = line_termination ? '\t' : '\0';
+ if (opt->output_prefix) {
+ struct strbuf *msg = NULL;
+ msg = opt->output_prefix(opt, opt->output_prefix_data);
+ fprintf(opt->file, "%s", msg->buf);
+ }
if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
@@ -3304,48 +3592,62 @@ static void show_file_mode_name(FILE *file, const char *newdelete, struct diff_f
}
-static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name)
+static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name,
+ const char *line_prefix)
{
if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
- fprintf(file, " mode change %06o => %06o%c", p->one->mode, p->two->mode,
- show_name ? ' ' : '\n');
+ fprintf(file, "%s mode change %06o => %06o%c", line_prefix, p->one->mode,
+ p->two->mode, show_name ? ' ' : '\n');
if (show_name) {
write_name_quoted(p->two->path, file, '\n');
}
}
}
-static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p)
+static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p,
+ const char *line_prefix)
{
char *names = pprint_rename(p->one->path, p->two->path);
fprintf(file, " %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
free(names);
- show_mode_change(file, p, 0);
+ show_mode_change(file, p, 0, line_prefix);
}
-static void diff_summary(FILE *file, struct diff_filepair *p)
+static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
{
+ FILE *file = opt->file;
+ char *line_prefix = "";
+
+ if (opt->output_prefix) {
+ struct strbuf *buf = opt->output_prefix(opt, opt->output_prefix_data);
+ line_prefix = buf->buf;
+ }
+
switch(p->status) {
case DIFF_STATUS_DELETED:
+ fputs(line_prefix, file);
show_file_mode_name(file, "delete", p->one);
break;
case DIFF_STATUS_ADDED:
+ fputs(line_prefix, file);
show_file_mode_name(file, "create", p->two);
break;
case DIFF_STATUS_COPIED:
- show_rename_copy(file, "copy", p);
+ fputs(line_prefix, file);
+ show_rename_copy(file, "copy", p, line_prefix);
break;
case DIFF_STATUS_RENAMED:
- show_rename_copy(file, "rename", p);
+ fputs(line_prefix, file);
+ show_rename_copy(file, "rename", p, line_prefix);
break;
default:
if (p->score) {
- fputs(" rewrite ", file);
+ fprintf(file, "%s rewrite ", line_prefix);
write_name_quoted(p->two->path, file, ' ');
fprintf(file, "(%d%%)\n", similarity_index(p));
}
- show_mode_change(file, p, !p->score);
+ show_mode_change(file, p, !p->score, line_prefix);
break;
}
}
@@ -3477,8 +3779,7 @@ int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1)
diff_free_filepair(q->queue[i]);
free(q->queue);
- q->queue = NULL;
- q->nr = q->alloc = 0;
+ DIFF_QUEUE_CLEAR(q);
return result;
}
@@ -3555,8 +3856,9 @@ void diff_flush(struct diff_options *options)
show_dirstat(options);
if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
- for (i = 0; i < q->nr; i++)
- diff_summary(options->file, q->queue[i]);
+ for (i = 0; i < q->nr; i++) {
+ diff_summary(options, q->queue[i]);
+ }
separator++;
}
@@ -3606,8 +3908,7 @@ void diff_flush(struct diff_options *options)
diff_free_filepair(q->queue[i]);
free_queue:
free(q->queue);
- q->queue = NULL;
- q->nr = q->alloc = 0;
+ DIFF_QUEUE_CLEAR(q);
if (options->close_file)
fclose(options->file);
@@ -3629,8 +3930,7 @@ static void diffcore_apply_filter(const char *filter)
int i;
struct diff_queue_struct *q = &diff_queued_diff;
struct diff_queue_struct outq;
- outq.queue = NULL;
- outq.nr = outq.alloc = 0;
+ DIFF_QUEUE_CLEAR(&outq);
if (!filter)
return;
@@ -3698,8 +3998,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
int i;
struct diff_queue_struct *q = &diff_queued_diff;
struct diff_queue_struct outq;
- outq.queue = NULL;
- outq.nr = outq.alloc = 0;
+ DIFF_QUEUE_CLEAR(&outq);
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
@@ -3760,6 +4059,12 @@ void diffcore_fix_diff_index(struct diff_options *options)
void diffcore_std(struct diff_options *options)
{
+ /* We never run this function more than one time, because the
+ * rename/copy detection logic can only run once.
+ */
+ if (diff_queued_diff.run)
+ return;
+
if (options->skip_stat_unmatch)
diffcore_skip_stat_unmatch(options);
if (options->break_opt != -1)
@@ -3779,6 +4084,8 @@ void diffcore_std(struct diff_options *options)
DIFF_OPT_SET(options, HAS_CHANGES);
else
DIFF_OPT_CLR(options, HAS_CHANGES);
+
+ diff_queued_diff.run = 1;
}
int diff_result_code(struct diff_options *opt, int status)
@@ -3932,3 +4239,47 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
return strbuf_detach(&buf, outsize);
}
+
+size_t fill_textconv(struct userdiff_driver *driver,
+ struct diff_filespec *df,
+ char **outbuf)
+{
+ size_t size;
+
+ if (!driver || !driver->textconv) {
+ if (!DIFF_FILE_VALID(df)) {
+ *outbuf = "";
+ return 0;
+ }
+ if (diff_populate_filespec(df, 0))
+ die("unable to read files to diff");
+ *outbuf = df->data;
+ return df->size;
+ }
+
+ if (driver->textconv_cache) {
+ *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
+ &size);
+ if (*outbuf)
+ return size;
+ }
+
+ *outbuf = run_textconv(driver->textconv, df, &size);
+ if (!*outbuf)
+ die("unable to read files to diff");
+
+ if (driver->textconv_cache) {
+ /* ignore errors, as we might be in a readonly repository */
+ notes_cache_put(driver->textconv_cache, df->sha1, *outbuf,
+ size);
+ /*
+ * we could save up changes and flush them all at the end,
+ * but we would need an extra call after all diffing is done.
+ * Since generating a cache entry is the slow path anyway,
+ * this extra overhead probably isn't a big deal.
+ */
+ notes_cache_write(driver->textconv_cache);
+ }
+
+ return size;
+}
diff --git a/diff.h b/diff.h
index 6a71013..063d10a 100644
--- a/diff.h
+++ b/diff.h
@@ -9,6 +9,9 @@
struct rev_info;
struct diff_options;
struct diff_queue_struct;
+struct strbuf;
+struct diff_filespec;
+struct userdiff_driver;
typedef void (*change_fn_t)(struct diff_options *options,
unsigned old_mode, unsigned new_mode,
@@ -25,6 +28,8 @@ typedef void (*add_remove_fn_t)(struct diff_options *options,
typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
struct diff_options *options, void *data);
+typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data);
+
#define DIFF_FORMAT_RAW 0x0001
#define DIFF_FORMAT_DIFFSTAT 0x0002
#define DIFF_FORMAT_NUMSTAT 0x0004
@@ -54,7 +59,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
#define DIFF_OPT_FIND_COPIES_HARDER (1 << 6)
#define DIFF_OPT_FOLLOW_RENAMES (1 << 7)
#define DIFF_OPT_COLOR_DIFF (1 << 8)
-#define DIFF_OPT_COLOR_DIFF_WORDS (1 << 9)
+/* (1 << 9) unused */
#define DIFF_OPT_HAS_CHANGES (1 << 10)
#define DIFF_OPT_QUICK (1 << 11)
#define DIFF_OPT_NO_INDEX (1 << 12)
@@ -71,6 +76,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
#define DIFF_OPT_SUBMODULE_LOG (1 << 23)
#define DIFF_OPT_DIRTY_SUBMODULES (1 << 24)
#define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
+#define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
@@ -79,6 +85,13 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
#define DIFF_XDL_SET(opts, flag) ((opts)->xdl_opts |= XDF_##flag)
#define DIFF_XDL_CLR(opts, flag) ((opts)->xdl_opts &= ~XDF_##flag)
+enum diff_words_type {
+ DIFF_WORDS_NONE = 0,
+ DIFF_WORDS_PORCELAIN,
+ DIFF_WORDS_PLAIN,
+ DIFF_WORDS_COLOR
+};
+
struct diff_options {
const char *filter;
const char *orderfile;
@@ -108,6 +121,7 @@ struct diff_options {
int stat_width;
int stat_name_width;
const char *word_regex;
+ enum diff_words_type word_diff;
/* this is set by diffcore for DIFF_FORMAT_PATCH */
int found_changes;
@@ -122,6 +136,8 @@ struct diff_options {
add_remove_fn_t add_remove;
diff_format_fn_t format_callback;
void *format_callback_data;
+ diff_prefix_fn_t output_prefix;
+ void *output_prefix_data;
};
enum color_diff {
@@ -133,7 +149,7 @@ enum color_diff {
DIFF_FILE_NEW = 5,
DIFF_COMMIT = 6,
DIFF_WHITESPACE = 7,
- DIFF_FUNCINFO = 8,
+ DIFF_FUNCINFO = 8
};
const char *diff_get_color(int diff_use_color, enum color_diff ix);
#define diff_get_color_opt(o, ix) \
@@ -279,4 +295,10 @@ extern void diff_no_index(struct rev_info *, int, const char **, int, const char
extern int index_differs_from(const char *def, int diff_flags);
+extern size_t fill_textconv(struct userdiff_driver *driver,
+ struct diff_filespec *df,
+ char **outbuf);
+
+extern struct userdiff_driver *get_textconv(struct diff_filespec *one);
+
#endif /* DIFF_H */
diff --git a/diffcore-break.c b/diffcore-break.c
index 3a7b60a..44f8678 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -162,8 +162,7 @@ void diffcore_break(int break_score)
if (!merge_score)
merge_score = DEFAULT_MERGE_SCORE;
- outq.nr = outq.alloc = 0;
- outq.queue = NULL;
+ DIFF_QUEUE_CLEAR(&outq);
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
@@ -256,8 +255,7 @@ void diffcore_merge_broken(void)
struct diff_queue_struct outq;
int i, j;
- outq.nr = outq.alloc = 0;
- outq.queue = NULL;
+ DIFF_QUEUE_CLEAR(&outq);
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index d0ef839..929de15 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -55,8 +55,7 @@ void diffcore_pickaxe(const char *needle, int opts)
int i, has_changes;
regex_t regex, *regexp = NULL;
struct diff_queue_struct outq;
- outq.queue = NULL;
- outq.nr = outq.alloc = 0;
+ DIFF_QUEUE_CLEAR(&outq);
if (opts & DIFF_PICKAXE_REGEX) {
int err;
diff --git a/diffcore-rename.c b/diffcore-rename.c
index d6fd3ca..df41be5 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -569,8 +569,7 @@ void diffcore_rename(struct diff_options *options)
/* At this point, we have found some renames and copies and they
* are recorded in rename_dst. The original list is still in *q.
*/
- outq.queue = NULL;
- outq.nr = outq.alloc = 0;
+ DIFF_QUEUE_CLEAR(&outq);
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
struct diff_filepair *pair_to_free = NULL;
diff --git a/diffcore.h b/diffcore.h
index fcd00bf..491bea0 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -91,7 +91,14 @@ struct diff_queue_struct {
struct diff_filepair **queue;
int alloc;
int nr;
+ int run;
};
+#define DIFF_QUEUE_CLEAR(q) \
+ do { \
+ (q)->queue = NULL; \
+ (q)->nr = (q)->alloc = 0; \
+ (q)->run = 0; \
+ } while(0);
extern struct diff_queue_struct diff_queued_diff;
extern struct diff_filepair *diff_queue(struct diff_queue_struct *,
diff --git a/dir.c b/dir.c
index 7f912c7..151ea67 100644
--- a/dir.c
+++ b/dir.c
@@ -465,7 +465,7 @@ static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pat
enum exist_status {
index_nonexistent = 0,
index_directory,
- index_gitdir,
+ index_gitdir
};
/*
@@ -533,7 +533,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
enum directory_treatment {
show_directory,
ignore_directory,
- recurse_into_directory,
+ recurse_into_directory
};
static enum directory_treatment treat_directory(struct dir_struct *dir,
@@ -684,7 +684,7 @@ static int get_dtype(struct dirent *de, const char *path, int len)
enum path_treatment {
path_ignored,
path_handled,
- path_recurse,
+ path_recurse
};
static enum path_treatment treat_one_path(struct dir_struct *dir,
diff --git a/environment.c b/environment.c
index 876c5e5..83d38d3 100644
--- a/environment.c
+++ b/environment.c
@@ -38,8 +38,9 @@ const char *pager_program;
int pager_use_color = 1;
const char *editor_program;
const char *excludes_file;
-int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
+enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
int read_replace_refs = 1;
+enum eol eol = EOL_UNSET;
enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
diff --git a/fast-import.c b/fast-import.c
index 309f2c5..1e5d66e 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -267,7 +267,7 @@ struct hash_list
typedef enum {
WHENSPEC_RAW = 1,
WHENSPEC_RFC2822,
- WHENSPEC_NOW,
+ WHENSPEC_NOW
} whenspec_type;
struct recent_command
@@ -2707,6 +2707,7 @@ static void option_import_marks(const char *marks, int from_stream)
}
import_marks_file = make_fast_import_path(marks);
+ safe_create_leading_directories_const(import_marks_file);
import_marks_file_from_stream = from_stream;
}
@@ -2737,6 +2738,7 @@ static void option_active_branches(const char *branches)
static void option_export_marks(const char *marks)
{
export_marks_file = make_fast_import_path(marks);
+ safe_create_leading_directories_const(export_marks_file);
}
static void option_export_pack_edges(const char *edges)
diff --git a/fsck.c b/fsck.c
index 89278c1..3d05d4a 100644
--- a/fsck.c
+++ b/fsck.c
@@ -222,12 +222,47 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
return retval;
}
+static int fsck_ident(char **ident, struct object *obj, fsck_error error_func)
+{
+ if (**ident == '<' || **ident == '\n')
+ return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email");
+ *ident += strcspn(*ident, "<\n");
+ if ((*ident)[-1] != ' ')
+ return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email");
+ if (**ident != '<')
+ return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing email");
+ (*ident)++;
+ *ident += strcspn(*ident, "<>\n");
+ if (**ident != '>')
+ return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad email");
+ (*ident)++;
+ if (**ident != ' ')
+ return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before date");
+ (*ident)++;
+ if (**ident == '0' && (*ident)[1] != ' ')
+ return error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date");
+ *ident += strspn(*ident, "0123456789");
+ if (**ident != ' ')
+ return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date");
+ (*ident)++;
+ if ((**ident != '+' && **ident != '-') ||
+ !isdigit((*ident)[1]) ||
+ !isdigit((*ident)[2]) ||
+ !isdigit((*ident)[3]) ||
+ !isdigit((*ident)[4]) ||
+ ((*ident)[5] != '\n'))
+ return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad time zone");
+ (*ident) += 6;
+ return 0;
+}
+
static int fsck_commit(struct commit *commit, fsck_error error_func)
{
char *buffer = commit->buffer;
unsigned char tree_sha1[20], sha1[20];
struct commit_graft *graft;
int parents = 0;
+ int err;
if (commit->date == ULONG_MAX)
return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line");
@@ -266,6 +301,16 @@ static int fsck_commit(struct commit *commit, fsck_error error_func)
}
if (memcmp(buffer, "author ", 7))
return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line");
+ buffer += 7;
+ err = fsck_ident(&buffer, &commit->object, error_func);
+ if (err)
+ return err;
+ if (memcmp(buffer, "committer ", strlen("committer ")))
+ return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'committer' line");
+ buffer += strlen("committer ");
+ err = fsck_ident(&buffer, &commit->object, error_func);
+ if (err)
+ return err;
if (!commit->tree)
return error_func(&commit->object, FSCK_ERROR, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
diff --git a/git-am.sh b/git-am.sh
index 1df5b04..e7f008c 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -52,6 +52,16 @@ else
HAS_HEAD=
fi
+cmdline="git am"
+if test '' != "$interactive"
+then
+ cmdline="$cmdline -i"
+fi
+if test '' != "$threeway"
+then
+ cmdline="$cmdline -3"
+fi
+
sq () {
git rev-parse --sq-quote "$@"
}
@@ -66,15 +76,6 @@ stop_here_user_resolve () {
printf '%s\n' "$resolvemsg"
stop_here $1
fi
- cmdline="git am"
- if test '' != "$interactive"
- then
- cmdline="$cmdline -i"
- fi
- if test '' != "$threeway"
- then
- cmdline="$cmdline -3"
- fi
echo "When you have resolved this problem run \"$cmdline --resolved\"."
echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
@@ -591,6 +592,8 @@ do
test -s "$dotest/patch" || {
echo "Patch is empty. Was it split wrong?"
+ echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
+ echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
stop_here $this
}
rm -f "$dotest/original-commit" "$dotest/author-script"
@@ -696,7 +699,13 @@ do
else
action=yes
fi
- FIRSTLINE=$(sed 1q "$dotest/final-commit")
+
+ if test -f "$dotest/final-commit"
+ then
+ FIRSTLINE=$(sed 1q "$dotest/final-commit")
+ else
+ FIRSTLINE=""
+ fi
if test $action = skip
then
@@ -732,6 +741,8 @@ do
resolved=
git diff-index --quiet --cached HEAD -- && {
echo "No changes - did you forget to use 'git add'?"
+ echo "If there is nothing left to stage, chances are that something else"
+ echo "already introduced the same changes; you might want to skip this patch."
stop_here_user_resolve $this
}
unmerged=$(git ls-files -u)
diff --git a/git-compat-util.h b/git-compat-util.h
index c0198dd..02a73ee 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -200,6 +200,7 @@ extern char *gitbasename(char *);
#include "compat/bswap.h"
/* General helper functions */
+extern void vreportf(const char *prefix, const char *err, va_list params);
extern NORETURN void usage(const char *err);
extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
@@ -224,7 +225,6 @@ static inline const char *skip_prefix(const char *str, const char *prefix)
#define PROT_READ 1
#define PROT_WRITE 2
#define MAP_PRIVATE 1
-#define MAP_FAILED ((void*)-1)
#endif
#define mmap git_mmap
@@ -253,6 +253,10 @@ extern int git_munmap(void *start, size_t length);
#endif /* NO_MMAP */
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
#ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
#define on_disk_bytes(st) ((st).st_size)
#else
@@ -363,7 +367,8 @@ static inline void *gitmempcpy(void *dest, const void *src, size_t n)
extern void release_pack_memory(size_t, int);
-extern void set_try_to_free_routine(void (*routine)(size_t));
+typedef void (*try_to_free_t)(size_t);
+extern try_to_free_t set_try_to_free_routine(try_to_free_t);
extern char *xstrdup(const char *str);
extern void *xmalloc(size_t size);
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 13751db..e9f3037 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -183,12 +183,58 @@ if ($state->{method} eq 'pserver') {
exit 1;
}
$line = <STDIN>; chomp $line;
- unless ($line eq 'anonymous') {
- print "E Only anonymous user allowed via pserver\n";
- print "I HATE YOU\n";
- exit 1;
+ my $user = $line;
+ $line = <STDIN>; chomp $line;
+ my $password = $line;
+
+ if ($user eq 'anonymous') {
+ # "A" will be 1 byte, use length instead in case the
+ # encryption method ever changes (yeah, right!)
+ if (length($password) > 1 ) {
+ print "E Don't supply a password for the `anonymous' user\n";
+ print "I HATE YOU\n";
+ exit 1;
+ }
+
+ # Fall through to LOVE
+ } else {
+ # Trying to authenticate a user
+ if (not exists $cfg->{gitcvs}->{authdb}) {
+ print "E the repo config file needs a [gitcvs] section with an 'authdb' parameter set to the filename of the authentication database\n";
+ print "I HATE YOU\n";
+ exit 1;
+ }
+
+ my $authdb = $cfg->{gitcvs}->{authdb};
+
+ unless (-e $authdb) {
+ print "E The authentication database specified in [gitcvs.authdb] does not exist\n";
+ print "I HATE YOU\n";
+ exit 1;
+ }
+
+ my $auth_ok;
+ open my $passwd, "<", $authdb or die $!;
+ while (<$passwd>) {
+ if (m{^\Q$user\E:(.*)}) {
+ if (crypt($user, descramble($password)) eq $1) {
+ $auth_ok = 1;
+ }
+ };
+ }
+ close $passwd;
+
+ unless ($auth_ok) {
+ print "I HATE YOU\n";
+ exit 1;
+ }
+
+ # Fall through to LOVE
}
- $line = <STDIN>; chomp $line; # validate the password?
+
+ # For checking whether the user is anonymous on commit
+ $state->{user} = $user;
+
$line = <STDIN>; chomp $line;
unless ($line eq "END $request REQUEST") {
die "E Do not understand $line -- expecting END $request REQUEST\n";
@@ -1271,9 +1317,9 @@ sub req_ci
$log->info("req_ci : " . ( defined($data) ? $data : "[NULL]" ));
- if ( $state->{method} eq 'pserver')
+ if ( $state->{method} eq 'pserver' and $state->{user} eq 'anonymous' )
{
- print "error 1 pserver access cannot commit\n";
+ print "error 1 anonymous user cannot commit via pserver\n";
cleanupWorkTree();
exit;
}
@@ -2369,15 +2415,20 @@ sub kopts_from_path
if ( defined ( $cfg->{gitcvs}{usecrlfattr} ) and
$cfg->{gitcvs}{usecrlfattr} =~ /\s*(1|true|yes)\s*$/i )
{
- my ($val) = check_attr( "crlf", $path );
- if ( $val eq "set" )
+ my ($val) = check_attr( "text", $path );
+ if ( $val eq "unspecified" )
{
- return "";
+ $val = check_attr( "crlf", $path );
}
- elsif ( $val eq "unset" )
+ if ( $val eq "unset" )
{
return "-kb"
}
+ elsif ( check_attr( "eol", $path ) ne "unspecified" ||
+ $val eq "set" || $val eq "input" )
+ {
+ return "";
+ }
else
{
$log->info("Unrecognized check_attr crlf $path : $val");
@@ -2586,6 +2637,43 @@ sub cvs_author
$author;
}
+
+sub descramble
+{
+ # This table is from src/scramble.c in the CVS source
+ my @SHIFTS = (
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
+ 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
+ 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
+ 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
+ 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
+ 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
+ 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
+ 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
+ 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
+ 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
+ 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
+ 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
+ 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
+ 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
+ );
+ my ($str) = @_;
+
+ # This should never happen, the same password format (A) has been
+ # used by CVS since the beginning of time
+ {
+ my $fmt = substr($str, 0, 1);
+ die "invalid password format `$fmt'" unless $fmt eq 'A';
+ }
+
+ my @str = unpack "C*", substr($str, 1);
+ my $ret = join '', map { chr $SHIFTS[$_] } @str;
+ return $ret;
+}
+
+
package GITCVS::log;
####
diff --git a/git-instaweb.sh b/git-instaweb.sh
index f608014..6635fbe 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -24,6 +24,7 @@ restart restart the web server
fqgitdir="$GIT_DIR"
local="$(git config --bool --get instaweb.local)"
httpd="$(git config --get instaweb.httpd)"
+root="$(git config --get instaweb.gitwebdir)"
port=$(git config --get instaweb.port)
module_path="$(git config --get instaweb.modulepath)"
@@ -34,6 +35,9 @@ conf="$GIT_DIR/gitweb/httpd.conf"
# if installed, it doesn't need further configuration (module_path)
test -z "$httpd" && httpd='lighttpd -f'
+# Default is @@GITWEBDIR@@
+test -z "$root" && root='@@GITWEBDIR@@'
+
# any untaken local port will do...
test -z "$port" && port=1234
@@ -46,6 +50,12 @@ resolve_full_httpd () {
httpd="$httpd -f"
fi
;;
+ *plackup*)
+ # server is started by running via generated gitweb.psgi in $fqgitdir/gitweb
+ full_httpd="$fqgitdir/gitweb/gitweb.psgi"
+ httpd_only="${httpd%% *}" # cut on first space
+ return
+ ;;
esac
httpd_only="$(echo $httpd | cut -f1 -d' ')"
@@ -57,7 +67,7 @@ resolve_full_httpd () {
# these days and those are not in most users $PATHs
# in addition, we may have generated a server script
# in $fqgitdir/gitweb.
- for i in /usr/local/sbin /usr/sbin "$fqgitdir/gitweb"
+ for i in /usr/local/sbin /usr/sbin "$root" "$fqgitdir/gitweb"
do
if test -x "$i/$httpd_only"
then
@@ -83,8 +93,8 @@ start_httpd () {
# don't quote $full_httpd, there can be arguments to it (-f)
case "$httpd" in
- *mongoose*)
- #The mongoose server doesn't have a daemon mode so we'll have to fork it
+ *mongoose*|*plackup*)
+ #These servers don't have a daemon mode so we'll have to fork it
$full_httpd "$fqgitdir/gitweb/httpd.conf" &
#Save the pid before doing anything else (we'll print it later)
pid=$!
@@ -110,6 +120,20 @@ EOF
stop_httpd () {
test -f "$fqgitdir/pid" && kill $(cat "$fqgitdir/pid")
+ rm -f "$fqgitdir/pid"
+}
+
+httpd_is_ready () {
+ "$PERL" -MIO::Socket::INET -e "
+local \$| = 1; # turn on autoflush
+exit if (IO::Socket::INET->new('127.0.0.1:$port'));
+print 'Waiting for \'$httpd\' to start ..';
+do {
+ print '.';
+ sleep(1);
+} until (IO::Socket::INET->new('127.0.0.1:$port'));
+print qq! (done)\n!;
+"
}
while test $# != 0
@@ -159,8 +183,8 @@ done
mkdir -p "$GIT_DIR/gitweb/tmp"
GIT_EXEC_PATH="$(git --exec-path)"
GIT_DIR="$fqgitdir"
-export GIT_EXEC_PATH GIT_DIR
-
+GITWEB_CONFIG="$fqgitdir/gitweb/gitweb_config.perl"
+export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
webrick_conf () {
# generate a standalone server script in $fqgitdir/gitweb.
@@ -192,7 +216,7 @@ EOF
cat >"$conf" <<EOF
:Port: $port
-:DocumentRoot: "$fqgitdir/gitweb"
+:DocumentRoot: "$root"
:DirectoryIndex: ["gitweb.cgi"]
:PidFile: "$fqgitdir/pid"
EOF
@@ -201,18 +225,18 @@ EOF
lighttpd_conf () {
cat > "$conf" <<EOF
-server.document-root = "$fqgitdir/gitweb"
+server.document-root = "$root"
server.port = $port
server.modules = ( "mod_setenv", "mod_cgi" )
server.indexfiles = ( "gitweb.cgi" )
server.pid-file = "$fqgitdir/pid"
-server.errorlog = "$fqgitdir/gitweb/error.log"
+server.errorlog = "$fqgitdir/gitweb/$httpd_only/error.log"
# to enable, add "mod_access", "mod_accesslog" to server.modules
# variable above and uncomment this
-#accesslog.filename = "$fqgitdir/gitweb/access.log"
+#accesslog.filename = "$fqgitdir/gitweb/$httpd_only/access.log"
-setenv.add-environment = ( "PATH" => env.PATH )
+setenv.add-environment = ( "PATH" => env.PATH, "GITWEB_CONFIG" => env.GITWEB_CONFIG )
cgi.assign = ( ".cgi" => "" )
@@ -277,14 +301,15 @@ EOF
apache2_conf () {
test -z "$module_path" && module_path=/usr/lib/apache2/modules
- mkdir -p "$GIT_DIR/gitweb/logs"
bind=
test x"$local" = xtrue && bind='127.0.0.1:'
echo 'text/css css' > "$fqgitdir/mime.types"
cat > "$conf" <<EOF
ServerName "git-instaweb"
-ServerRoot "$fqgitdir/gitweb"
-DocumentRoot "$fqgitdir/gitweb"
+ServerRoot "$root"
+DocumentRoot "$root"
+ErrorLog "$fqgitdir/gitweb/$httpd_only/error.log"
+CustomLog "$fqgitdir/gitweb/$httpd_only/access.log" combined
PidFile "$fqgitdir/pid"
Listen $bind$port
EOF
@@ -303,13 +328,14 @@ EOF
# check to see if Dennis Stosberg's mod_perl compatibility patch
# (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
if test -f "$module_path/mod_perl.so" &&
- sane_grep 'MOD_PERL' "$GIT_DIR/gitweb/gitweb.cgi" >/dev/null
+ sane_grep 'MOD_PERL' "$root/gitweb.cgi" >/dev/null
then
# favor mod_perl if available
cat >> "$conf" <<EOF
LoadModule perl_module $module_path/mod_perl.so
PerlPassEnv GIT_DIR
PerlPassEnv GIT_EXEC_DIR
+PerlPassEnv GITWEB_CONFIG
<Location /gitweb.cgi>
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
@@ -353,15 +379,15 @@ mongoose_conf() {
# For detailed description of every option, visit
# http://code.google.com/p/mongoose/wiki/MongooseManual
-root $fqgitdir/gitweb
+root $root
ports $port
index_files gitweb.cgi
#ssl_cert $fqgitdir/gitweb/ssl_cert.pem
-error_log $fqgitdir/gitweb/error.log
-access_log $fqgitdir/gitweb/access.log
+error_log $fqgitdir/gitweb/$httpd_only/error.log
+access_log $fqgitdir/gitweb/$httpd_only/access.log
#cgi setup
-cgi_env PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH
+cgi_env PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH,GITWEB_CONFIG=$GITWEB_CONFIG
cgi_interp $PERL
cgi_ext cgi,pl
@@ -370,41 +396,165 @@ mime_types .gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-t
EOF
}
-
-script='
-s#^(my|our) \$projectroot =.*#$1 \$projectroot = "'$(dirname "$fqgitdir")'";#;
-s#(my|our) \$gitbin =.*#$1 \$gitbin = "'$GIT_EXEC_PATH'";#;
-s#(my|our) \$projects_list =.*#$1 \$projects_list = \$projectroot;#;
-s#(my|our) \$git_temp =.*#$1 \$git_temp = "'$fqgitdir/gitweb/tmp'";#;'
-
-gitweb_cgi () {
- cat > "$1.tmp" <<\EOFGITWEB
-@@GITWEB_CGI@@
-EOFGITWEB
- # Use the configured full path to perl to match the generated
- # scripts' 'hashpling' line
- "$PERL" -p -e "$script" "$1.tmp" > "$1"
- chmod +x "$1"
- rm -f "$1.tmp"
+plackup_conf () {
+ # generate a standalone 'plackup' server script in $fqgitdir/gitweb
+ # with embedded configuration; it does not use "$conf" file
+ cat > "$fqgitdir/gitweb/gitweb.psgi" <<EOF
+#!$PERL
+
+# gitweb - simple web interface to track changes in git repositories
+# PSGI wrapper and server starter (see http://plackperl.org)
+
+use strict;
+
+use IO::Handle;
+use Plack::MIME;
+use Plack::Builder;
+use Plack::App::WrapCGI;
+use CGI::Emulate::PSGI 0.07; # minimum version required to work with gitweb
+
+# mimetype mapping (from lighttpd_conf)
+Plack::MIME->add_type(
+ ".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"
+);
+
+my \$app = builder {
+ # to be able to override \$SIG{__WARN__} to log build time warnings
+ use CGI::Carp; # it sets \$SIG{__WARN__} itself
+
+ my \$logdir = "$fqgitdir/gitweb/$httpd_only";
+ open my \$access_log_fh, '>>', "\$logdir/access.log"
+ or die "Couldn't open access log '\$logdir/access.log': \$!";
+ open my \$error_log_fh, '>>', "\$logdir/error.log"
+ or die "Couldn't open error log '\$logdir/error.log': \$!";
+
+ \$access_log_fh->autoflush(1);
+ \$error_log_fh->autoflush(1);
+
+ # redirect build time warnings to error.log
+ \$SIG{'__WARN__'} = sub {
+ my \$msg = shift;
+ # timestamp warning like in CGI::Carp::warn
+ my \$stamp = CGI::Carp::stamp();
+ \$msg =~ s/^/\$stamp/gm;
+ print \$error_log_fh \$msg;
+ };
+
+ # write errors to error.log, access to access.log
+ enable 'AccessLog',
+ format => "combined",
+ logger => sub { print \$access_log_fh @_; };
+ enable sub {
+ my \$app = shift;
+ sub {
+ my \$env = shift;
+ \$env->{'psgi.errors'} = \$error_log_fh;
+ \$app->(\$env);
+ }
+ };
+ # gitweb currently doesn't work with $SIG{CHLD} set to 'IGNORE',
+ # because it uses 'close $fd or die...' on piped filehandle $fh
+ # (which causes the parent process to wait for child to finish).
+ enable_if { \$SIG{'CHLD'} eq 'IGNORE' } sub {
+ my \$app = shift;
+ sub {
+ my \$env = shift;
+ local \$SIG{'CHLD'} = 'DEFAULT';
+ local \$SIG{'CLD'} = 'DEFAULT';
+ \$app->(\$env);
+ }
+ };
+ # serve static files, i.e. stylesheet, images, script
+ enable 'Static',
+ path => sub { m!\.(js|css|png)\$! && s!^/gitweb/!! },
+ root => "$root/",
+ encoding => 'utf-8'; # encoding for 'text/plain' files
+ # convert CGI application to PSGI app
+ Plack::App::WrapCGI->new(script => "$root/gitweb.cgi")->to_app;
+};
+
+# make it runnable as standalone app,
+# like it would be run via 'plackup' utility
+if (__FILE__ eq \$0) {
+ require Plack::Runner;
+
+ my \$runner = Plack::Runner->new();
+ \$runner->parse_options(qw(--env deployment --port $port),
+ "$local" ? qw(--host 127.0.0.1) : ());
+ \$runner->run(\$app);
}
+__END__
+EOF
-gitweb_css () {
- cat > "$1" <<\EOFGITWEB
-@@GITWEB_CSS@@
-
-EOFGITWEB
+ chmod a+x "$fqgitdir/gitweb/gitweb.psgi"
+ # configuration is embedded in server script file, gitweb.psgi
+ rm -f "$conf"
}
-gitweb_js () {
- cat > "$1" <<\EOFGITWEB
-@@GITWEB_JS@@
-
-EOFGITWEB
+gitweb_conf() {
+ cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
+#!/usr/bin/perl
+our \$projectroot = "$(dirname "$fqgitdir")";
+our \$git_temp = "$fqgitdir/gitweb/tmp";
+our \$projects_list = \$projectroot;
+EOF
}
-gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi"
-gitweb_css "$GIT_DIR/@@GITWEB_CSS_NAME@@"
-gitweb_js "$GIT_DIR/@@GITWEB_JS_NAME@@"
+gitweb_conf
+
+resolve_full_httpd
+mkdir -p "$fqgitdir/gitweb/$httpd_only"
case "$httpd" in
*lighttpd*)
@@ -419,6 +569,9 @@ webrick)
*mongoose*)
mongoose_conf
;;
+*plackup*)
+ plackup_conf
+ ;;
*)
echo "Unknown httpd specified: $httpd"
exit 1
@@ -429,7 +582,7 @@ start_httpd
url=http://127.0.0.1:$port
if test -n "$browser"; then
- git web--browse -b "$browser" $url || echo $url
+ httpd_is_ready && git web--browse -b "$browser" $url || echo $url
else
- git web--browse -c "instaweb.browser" $url || echo $url
+ httpd_is_ready && git web--browse -c "instaweb.browser" $url || echo $url
fi
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index d067894..b86402a 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -107,7 +107,7 @@ case "${1:-.}${2:-.}${3:-.}" in
# remove lines that are unique to ours.
orig=`git-unpack-file $2`
sz0=`wc -c <"$orig"`
- diff -u -La/$orig -Lb/$orig $orig $src2 | git apply --no-add
+ @@DIFF@@ -u -La/$orig -Lb/$orig $orig $src2 | git apply --no-add
sz1=`wc -c <"$orig"`
# If we do not have enough common material, it is not
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 6b86abc..31e6860 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -263,10 +263,10 @@ pick_one_preserving_merges () {
then
if test "$fast_forward" = t
then
- cat "$DOTEST"/current-commit | while read current_commit
+ while read current_commit
do
git rev-parse HEAD > "$REWRITTEN"/$current_commit
- done
+ done <"$DOTEST"/current-commit
rm "$DOTEST"/current-commit ||
die "Cannot write current commit's replacement sha1"
fi
@@ -440,9 +440,9 @@ record_in_rewritten() {
echo "$oldsha1" >> "$REWRITTEN_PENDING"
case "$(peek_next_command)" in
- squash|s|fixup|f)
+ squash|s|fixup|f)
;;
- *)
+ *)
flush_rewritten_pending
;;
esac
@@ -450,7 +450,7 @@ record_in_rewritten() {
do_next () {
rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit
- read command sha1 rest < "$TODO"
+ read -r command sha1 rest < "$TODO"
case "$command" in
'#'*|''|noop)
mark_action_done
@@ -591,7 +591,7 @@ do_rest () {
# skip picking commits whose parents are unchanged
skip_unnecessary_picks () {
fd=3
- while read command sha1 rest
+ while read -r command sha1 rest
do
# fd=3 means we skip the command
case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in
@@ -644,13 +644,13 @@ rearrange_squash () {
test -s "$1.sq" || return
used=
- while read pick sha1 message
+ while read -r pick sha1 message
do
case " $used" in
*" $sha1 "*) continue ;;
esac
echo "$pick $sha1 $message"
- while read squash action msg
+ while read -r squash action msg
do
case "$message" in
"$msg"*)
@@ -890,7 +890,8 @@ first and then run 'git rebase --continue' again."
git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
--abbrev=7 --reverse --left-right --topo-order \
$REVISIONS | \
- sed -n "s/^>//p" | while read shortsha1 rest
+ sed -n "s/^>//p" |
+ while read -r shortsha1 rest
do
if test t != "$PRESERVE_MERGES"
then
diff --git a/git-rebase.sh b/git-rebase.sh
index 44f5c65..ab4afa7 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano.
#
-USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
+USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] (<upstream>|--root) [<branch>] [--quiet | -q]'
LONG_USAGE='git-rebase replaces <branch> with a new branch of the
same name. When the --onto option is provided the new branch starts
out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -198,14 +198,6 @@ test -f "$GIT_DIR"/rebase-apply/applying &&
is_interactive "$@" && exec git-rebase--interactive "$@"
-if test $# -eq 0
-then
- test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || usage
- test -d "$dotest" -o -f "$GIT_DIR"/rebase-apply/rebasing &&
- die 'A rebase is in progress, try --continue, --skip or --abort.'
- die "No arguments given and $GIT_DIR/rebase-apply already exists."
-fi
-
while test $# != 0
do
case "$1" in
@@ -370,6 +362,13 @@ do
done
test $# -gt 2 && usage
+if test $# -eq 0 && test -z "$rebase_root"
+then
+ test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || usage
+ test -d "$dotest" -o -f "$GIT_DIR"/rebase-apply/rebasing &&
+ die 'A rebase is in progress, try --continue, --skip or --abort.'
+fi
+
# Make sure we do not have $GIT_DIR/rebase-apply
if test -z "$do_merge"
then
diff --git a/git-remote-testgit.py b/git-remote-testgit.py
new file mode 100644
index 0000000..df9d512
--- /dev/null
+++ b/git-remote-testgit.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python
+
+# hashlib is only available in python >= 2.5
+try:
+ import hashlib
+ _digest = hashlib.sha1
+except ImportError:
+ import sha
+ _digest = sha.new
+import sys
+import os
+sys.path.insert(0, os.getenv("GITPYTHONLIB","."))
+
+from git_remote_helpers.util import die, debug, warn
+from git_remote_helpers.git.repo import GitRepo
+from git_remote_helpers.git.exporter import GitExporter
+from git_remote_helpers.git.importer import GitImporter
+from git_remote_helpers.git.non_local import NonLocalGit
+
+def get_repo(alias, url):
+ """Returns a git repository object initialized for usage.
+ """
+
+ repo = GitRepo(url)
+ repo.get_revs()
+ repo.get_head()
+
+ hasher = _digest()
+ hasher.update(repo.path)
+ repo.hash = hasher.hexdigest()
+
+ repo.get_base_path = lambda base: os.path.join(
+ base, 'info', 'fast-import', repo.hash)
+
+ prefix = 'refs/testgit/%s/' % alias
+ debug("prefix: '%s'", prefix)
+
+ repo.gitdir = ""
+ repo.alias = alias
+ repo.prefix = prefix
+
+ repo.exporter = GitExporter(repo)
+ repo.importer = GitImporter(repo)
+ repo.non_local = NonLocalGit(repo)
+
+ return repo
+
+
+def local_repo(repo, path):
+ """Returns a git repository object initalized for usage.
+ """
+
+ local = GitRepo(path)
+
+ local.non_local = None
+ local.gitdir = repo.gitdir
+ local.alias = repo.alias
+ local.prefix = repo.prefix
+ local.hash = repo.hash
+ local.get_base_path = repo.get_base_path
+ local.exporter = GitExporter(local)
+ local.importer = GitImporter(local)
+
+ return local
+
+
+def do_capabilities(repo, args):
+ """Prints the supported capabilities.
+ """
+
+ print "import"
+ print "export"
+ print "gitdir"
+ print "refspec refs/heads/*:%s*" % repo.prefix
+
+ print # end capabilities
+
+
+def do_list(repo, args):
+ """Lists all known references.
+
+ Bug: This will always set the remote head to master for non-local
+ repositories, since we have no way of determining what the remote
+ head is at clone time.
+ """
+
+ for ref in repo.revs:
+ debug("? refs/heads/%s", ref)
+ print "? refs/heads/%s" % ref
+
+ if repo.head:
+ debug("@refs/heads/%s HEAD" % repo.head)
+ print "@refs/heads/%s HEAD" % repo.head
+ else:
+ debug("@refs/heads/master HEAD")
+ print "@refs/heads/master HEAD"
+
+ print # end list
+
+
+def update_local_repo(repo):
+ """Updates (or clones) a local repo.
+ """
+
+ if repo.local:
+ return repo
+
+ path = repo.non_local.clone(repo.gitdir)
+ repo.non_local.update(repo.gitdir)
+ repo = local_repo(repo, path)
+ return repo
+
+
+def do_import(repo, args):
+ """Exports a fast-import stream from testgit for git to import.
+ """
+
+ if len(args) != 1:
+ die("Import needs exactly one ref")
+
+ if not repo.gitdir:
+ die("Need gitdir to import")
+
+ repo = update_local_repo(repo)
+ repo.exporter.export_repo(repo.gitdir)
+
+
+def do_export(repo, args):
+ """Imports a fast-import stream from git to testgit.
+ """
+
+ if not repo.gitdir:
+ die("Need gitdir to export")
+
+ dirname = repo.get_base_path(repo.gitdir)
+
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ path = os.path.join(dirname, 'testgit.marks')
+ print path
+ if os.path.exists(path):
+ print path
+ else:
+ print ""
+ sys.stdout.flush()
+
+ update_local_repo(repo)
+ repo.importer.do_import(repo.gitdir)
+ repo.non_local.push(repo.gitdir)
+
+
+def do_gitdir(repo, args):
+ """Stores the location of the gitdir.
+ """
+
+ if not args:
+ die("gitdir needs an argument")
+
+ repo.gitdir = ' '.join(args)
+
+
+COMMANDS = {
+ 'capabilities': do_capabilities,
+ 'list': do_list,
+ 'import': do_import,
+ 'export': do_export,
+ 'gitdir': do_gitdir,
+}
+
+
+def sanitize(value):
+ """Cleans up the url.
+ """
+
+ if value.startswith('testgit::'):
+ value = value[9:]
+
+ return value
+
+
+def read_one_line(repo):
+ """Reads and processes one command.
+ """
+
+ line = sys.stdin.readline()
+
+ cmdline = line
+
+ if not cmdline:
+ warn("Unexpected EOF")
+ return False
+
+ cmdline = cmdline.strip().split()
+ if not cmdline:
+ # Blank line means we're about to quit
+ return False
+
+ cmd = cmdline.pop(0)
+ debug("Got command '%s' with args '%s'", cmd, ' '.join(cmdline))
+
+ if cmd not in COMMANDS:
+ die("Unknown command, %s", cmd)
+
+ func = COMMANDS[cmd]
+ func(repo, cmdline)
+ sys.stdout.flush()
+
+ return True
+
+
+def main(args):
+ """Starts a new remote helper for the specified repository.
+ """
+
+ if len(args) != 3:
+ die("Expecting exactly three arguments.")
+ sys.exit(1)
+
+ if os.getenv("GIT_DEBUG_TESTGIT"):
+ import git_remote_helpers.util
+ git_remote_helpers.util.DEBUG = True
+
+ alias = sanitize(args[1])
+ url = sanitize(args[2])
+
+ if not alias.isalnum():
+ warn("non-alnum alias '%s'", alias)
+ alias = "tmp"
+
+ args[1] = alias
+ args[2] = url
+
+ repo = get_repo(alias, url)
+
+ debug("Got arguments %s", args[1:])
+
+ more = True
+
+ while (more):
+ more = read_one_line(repo)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/git-request-pull.sh b/git-request-pull.sh
index 8fd15f6..6fdea39 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -1,4 +1,4 @@
-#!/bin/sh -e
+#!/bin/sh
# Copyright 2005, Ryan Anderson <ryan@michonline.com>
#
# This file is licensed under the GPL v2, or a later version
@@ -8,6 +8,7 @@ USAGE='<start> <url> [<end>]'
LONG_USAGE='Summarizes the changes between two commits to the standard output,
and includes the given URL in the generated summary.'
SUBDIRECTORY_OK='Yes'
+OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC='git request-pull [options] start url [end]
--
p show patch text as well
@@ -69,10 +70,10 @@ git show -s --format='The following changes since commit %H:
%s (%ci)
-are available in the git repository at:' $baserev
-echo " $url $branch"
-echo
+are available in the git repository at:' $baserev &&
+echo " $url $branch" &&
+echo &&
-git shortlog ^$baserev $headrev
-git diff -M --stat --summary $patch $merge_base..$headrev
+git shortlog ^$baserev $headrev &&
+git diff -M --stat --summary $patch $merge_base..$headrev || exit
exit $status
diff --git a/git-stash.sh b/git-stash.sh
index 0f858d3..1d95447 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -57,7 +57,7 @@ create_stash () {
# state of the base commit
if b_commit=$(git rev-parse --verify HEAD)
then
- head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --)
+ head=$(git rev-list --oneline -n 1 HEAD --)
else
die "You do not have the initial commit yet"
fi
diff --git a/git-submodule.sh b/git-submodule.sh
index 3319b83..ad2417d 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -234,12 +234,12 @@ cmd_add()
) || die "Unable to checkout submodule '$path'"
fi
- git add "$path" ||
+ git add --force "$path" ||
die "Failed to add submodule '$path'"
git config -f .gitmodules submodule."$path".path "$path" &&
git config -f .gitmodules submodule."$path".url "$repo" &&
- git add .gitmodules ||
+ git add --force .gitmodules ||
die "Failed to register submodule '$path'"
}
@@ -271,6 +271,8 @@ cmd_foreach()
shift
done
+ toplevel=$(pwd)
+
module_list |
while read mode sha1 stage path
do
@@ -578,7 +580,7 @@ cmd_summary() {
cd_to_toplevel
# Get modified modules cared by user
- modules=$(git $diff_cmd $cached --raw $head -- "$@" |
+ modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
sane_egrep '^:([0-7]* )?160000' |
while read mod_src mod_dst sha1_src sha1_dst status name
do
@@ -592,7 +594,7 @@ cmd_summary() {
test -z "$modules" && return
- git $diff_cmd $cached --raw $head -- $modules |
+ git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- $modules |
sane_egrep '^:([0-7]* )?160000' |
cut -c2- |
while read mod_src mod_dst sha1_src sha1_dst status name
@@ -650,7 +652,7 @@ cmd_summary() {
range=$sha1_dst
fi
GIT_DIR="$name/.git" \
- git log --pretty=oneline --first-parent $range | wc -l
+ git rev-list --first-parent $range -- | wc -l
)
total_commits=" ($(($total_commits + 0)))"
;;
@@ -758,7 +760,7 @@ cmd_status()
continue;
fi
set_name_rev "$path" "$sha1"
- if git diff-files --quiet -- "$path"
+ if git diff-files --ignore-submodules=dirty --quiet -- "$path"
then
say " $sha1 $displaypath$revname"
else
diff --git a/git-svn.perl b/git-svn.perl
index 2c86ea2..19d6848 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -963,6 +963,7 @@ sub cmd_multi_init {
}
do_git_init_db();
if (defined $_trunk) {
+ $_trunk =~ s#^/+##;
my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk';
# try both old-style and new-style lookups:
my $gs_trunk = eval { Git::SVN->new($trunk_ref) };
@@ -1185,6 +1186,7 @@ sub cmd_reset {
"history\n";
}
my ($r, $c) = $gs->find_rev_before($target, not $_fetch_parent);
+ die "Cannot find SVN revision $target\n" unless defined($c);
$gs->rev_map_set($r, $c, 'reset', $uuid);
print "r$r = $c ($gs->{ref_id})\n";
}
@@ -2053,6 +2055,9 @@ sub new {
"\":$ref_id\$\" in config\n";
($self->{path}, undef) = split(/\s*:\s*/, $fetch);
}
+ $self->{path} =~ s{/+}{/}g;
+ $self->{path} =~ s{\A/}{};
+ $self->{path} =~ s{/\z}{};
$self->{url} = command_oneline('config', '--get',
"svn-remote.$repo_id.url") or
die "Failed to read \"svn-remote.$repo_id.url\" in config\n";
@@ -2086,6 +2091,14 @@ sub refname {
# .. becomes %2E%2E
$refname =~ s{\.\.}{%2E%2E}g;
+ # trailing dots and .lock are not allowed
+ # .$ becomes %2E and .lock becomes %2Elock
+ $refname =~ s{\.(?=$|lock$)}{%2E};
+
+ # the sequence @{ is used to access the reflog
+ # @{ becomes %40{
+ $refname =~ s{\@\{}{%40\{}g;
+
return $refname;
}
@@ -2827,8 +2840,9 @@ sub mkemptydirs {
foreach my $d (sort keys %empty_dirs) {
$d = uri_decode($d);
$d =~ s/$strip//;
+ next unless length($d);
next if -d $d;
- if (-e _) {
+ if (-e $d) {
warn "$d exists but is not a directory\n";
} else {
print "creating empty directory: $d\n";
@@ -3605,6 +3619,7 @@ sub mkfile {
sub rev_map_set {
my ($self, $rev, $commit, $update_ref, $uuid) = @_;
+ defined $commit or die "missing arg3\n";
length $commit == 40 or die "arg3 must be a full SHA1 hexsum\n";
my $db = $self->map_path($uuid);
my $db_lock = "$db.lock";
@@ -3998,7 +4013,6 @@ use vars qw/@ISA/;
use strict;
use warnings;
use Carp qw/croak/;
-use File::Temp qw/tempfile/;
use IO::File qw//;
use vars qw/$_ignore_regex/;
diff --git a/git-web--browse.sh b/git-web--browse.sh
index a578c3a..dbded76 100755
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -31,7 +31,7 @@ valid_custom_tool()
valid_tool() {
case "$1" in
- firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open | start)
+ firefox | iceweasel | chrome | chromium | konqueror | w3m | links | lynx | dillo | open | start)
;; # happy
*)
valid_custom_tool "$1" || return 1
@@ -103,7 +103,7 @@ fi
if test -z "$browser" ; then
if test -n "$DISPLAY"; then
- browser_candidates="firefox iceweasel konqueror w3m links lynx dillo"
+ browser_candidates="firefox iceweasel chrome chromium konqueror w3m links lynx dillo"
if test "$KDE_FULL_SESSION" = "true"; then
browser_candidates="konqueror $browser_candidates"
fi
@@ -146,6 +146,11 @@ case "$browser" in
test "$vers" -lt 2 && NEWTAB=''
"$browser_path" $NEWTAB "$@" &
;;
+ chrome|chromium)
+ # Actual command for chromium is chromium-browser.
+ # No need to specify newTab. It's default in chromium
+ eval "$browser_path" "$@" &
+ ;;
konqueror)
case "$(basename "$browser_path")" in
konqueror)
diff --git a/git.c b/git.c
index 6bae305..265fa09 100644
--- a/git.c
+++ b/git.c
@@ -8,6 +8,7 @@ const char git_usage_string[] =
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
" [-p|--paginate|--no-pager] [--no-replace-objects]\n"
" [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
+ " [-c name=value\n"
" [--help] COMMAND [ARGS]";
const char git_more_info_string[] =
@@ -130,6 +131,14 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "-c")) {
+ if (*argc < 2) {
+ fprintf(stderr, "-c expects a configuration string\n" );
+ usage(git_usage_string);
+ }
+ git_config_parse_parameter((*argv)[1]);
+ (*argv)++;
+ (*argc)--;
} else {
fprintf(stderr, "Unknown option: %s\n", cmd);
usage(git_usage_string);
@@ -320,7 +329,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "fsck-objects", cmd_fsck, RUN_SETUP },
{ "gc", cmd_gc, RUN_SETUP },
{ "get-tar-commit-id", cmd_get_tar_commit_id },
- { "grep", cmd_grep, USE_PAGER },
+ { "grep", cmd_grep },
{ "hash-object", cmd_hash_object },
{ "help", cmd_help },
{ "index-pack", cmd_index_pack },
diff --git a/git.spec.in b/git.spec.in
index 9533147..91c8462 100644
--- a/git.spec.in
+++ b/git.spec.in
@@ -35,6 +35,7 @@ Requires: git-cvs = %{version}-%{release}
Requires: git-arch = %{version}-%{release}
Requires: git-email = %{version}-%{release}
Requires: gitk = %{version}-%{release}
+Requires: gitweb = %{version}-%{release}
Requires: git-gui = %{version}-%{release}
Obsoletes: git <= 1.5.4.2
@@ -87,6 +88,13 @@ Requires: git = %{version}-%{release}, tk >= 8.4
%description -n gitk
Git revision tree visualiser ('gitk')
+%package -n gitweb
+Summary: Git web interface
+Group: Development/Tools
+Requires: git = %{version}-%{release}
+%description -n gitweb
+Browsing git repository on the web
+
%package -n perl-Git
Summary: Perl interface to Git
Group: Development/Libraries
@@ -189,6 +197,10 @@ rm -rf $RPM_BUILD_ROOT
%{!?_without_docs: %{_mandir}/man1/*gitk*.1*}
%{!?_without_docs: %doc Documentation/*gitk*.html }
+%files -n gitweb
+%defattr(-,root,root)
+%{_datadir}/gitweb
+
%files -n perl-Git -f perl-files
%defattr(-,root,root)
@@ -196,6 +208,9 @@ rm -rf $RPM_BUILD_ROOT
# No files for you!
%changelog
+* Wed Jun 30 2010 Junio C Hamano <gitster@pobox.com>
+- Add 'gitweb' subpackage.
+
* Fri Mar 26 2010 Ian Ward Comfort <icomfort@stanford.edu>
- Ship bash completion support from contrib/ in the core package.
diff --git a/git_remote_helpers/git/exporter.py b/git_remote_helpers/git/exporter.py
new file mode 100644
index 0000000..f40f9d6
--- /dev/null
+++ b/git_remote_helpers/git/exporter.py
@@ -0,0 +1,53 @@
+import os
+import subprocess
+import sys
+
+
+class GitExporter(object):
+ """An exporter for testgit repositories.
+
+ The exporter simply delegates to git fast-export.
+ """
+
+ def __init__(self, repo):
+ """Creates a new exporter for the specified repo.
+ """
+
+ self.repo = repo
+
+ def export_repo(self, base):
+ """Exports a fast-export stream for the given directory.
+
+ Simply delegates to git fast-epxort and pipes it through sed
+ to make the refs show up under the prefix rather than the
+ default refs/heads. This is to demonstrate how the export
+ data can be stored under it's own ref (using the refspec
+ capability).
+ """
+
+ dirname = self.repo.get_base_path(base)
+ path = os.path.abspath(os.path.join(dirname, 'testgit.marks'))
+
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ print "feature relative-marks"
+ if os.path.exists(os.path.join(dirname, 'git.marks')):
+ print "feature import-marks=%s/git.marks" % self.repo.hash
+ print "feature export-marks=%s/git.marks" % self.repo.hash
+ sys.stdout.flush()
+
+ args = ["git", "--git-dir=" + self.repo.gitpath, "fast-export", "--export-marks=" + path]
+
+ if os.path.exists(path):
+ args.append("--import-marks=" + path)
+
+ args.append("HEAD")
+
+ p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
+
+ args = ["sed", "s_refs/heads/_" + self.repo.prefix + "_g"]
+
+ child = subprocess.Popen(args, stdin=p1.stdout)
+ if child.wait() != 0:
+ raise CalledProcessError
diff --git a/git_remote_helpers/git/importer.py b/git_remote_helpers/git/importer.py
new file mode 100644
index 0000000..70a7127
--- /dev/null
+++ b/git_remote_helpers/git/importer.py
@@ -0,0 +1,40 @@
+import os
+import subprocess
+
+
+class GitImporter(object):
+ """An importer for testgit repositories.
+
+ This importer simply delegates to git fast-import.
+ """
+
+ def __init__(self, repo):
+ """Creates a new importer for the specified repo.
+ """
+
+ self.repo = repo
+
+ def do_import(self, base):
+ """Imports a fast-import stream to the given directory.
+
+ Simply delegates to git fast-import.
+ """
+
+ dirname = self.repo.get_base_path(base)
+ if self.repo.local:
+ gitdir = self.repo.gitpath
+ else:
+ gitdir = os.path.abspath(os.path.join(dirname, '.git'))
+ path = os.path.abspath(os.path.join(dirname, 'git.marks'))
+
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ args = ["git", "--git-dir=" + gitdir, "fast-import", "--quiet", "--export-marks=" + path]
+
+ if os.path.exists(path):
+ args.append("--import-marks=" + path)
+
+ child = subprocess.Popen(args)
+ if child.wait() != 0:
+ raise CalledProcessError
diff --git a/git_remote_helpers/git/non_local.py b/git_remote_helpers/git/non_local.py
new file mode 100644
index 0000000..f27389b
--- /dev/null
+++ b/git_remote_helpers/git/non_local.py
@@ -0,0 +1,69 @@
+import os
+import subprocess
+
+from git_remote_helpers.util import die, warn
+
+
+class NonLocalGit(object):
+ """Handler to interact with non-local repos.
+ """
+
+ def __init__(self, repo):
+ """Creates a new non-local handler for the specified repo.
+ """
+
+ self.repo = repo
+
+ def clone(self, base):
+ """Clones the non-local repo to base.
+
+ Does nothing if a clone already exists.
+ """
+
+ path = os.path.join(self.repo.get_base_path(base), '.git')
+
+ # already cloned
+ if os.path.exists(path):
+ return path
+
+ os.makedirs(path)
+ args = ["git", "clone", "--bare", "--quiet", self.repo.gitpath, path]
+
+ child = subprocess.Popen(args)
+ if child.wait() != 0:
+ raise CalledProcessError
+
+ return path
+
+ def update(self, base):
+ """Updates checkout of the non-local repo in base.
+ """
+
+ path = os.path.join(self.repo.get_base_path(base), '.git')
+
+ if not os.path.exists(path):
+ die("could not find repo at %s", path)
+
+ args = ["git", "--git-dir=" + path, "fetch", "--quiet", self.repo.gitpath]
+ child = subprocess.Popen(args)
+ if child.wait() != 0:
+ raise CalledProcessError
+
+ args = ["git", "--git-dir=" + path, "update-ref", "refs/heads/master", "FETCH_HEAD"]
+ child = subprocess.Popen(args)
+ if child.wait() != 0:
+ raise CalledProcessError
+
+ def push(self, base):
+ """Pushes from the non-local repo to base.
+ """
+
+ path = os.path.join(self.repo.get_base_path(base), '.git')
+
+ if not os.path.exists(path):
+ die("could not find repo at %s", path)
+
+ args = ["git", "--git-dir=" + path, "push", "--quiet", self.repo.gitpath]
+ child = subprocess.Popen(args)
+ if child.wait() != 0:
+ raise CalledProcessError
diff --git a/git_remote_helpers/git/repo.py b/git_remote_helpers/git/repo.py
new file mode 100644
index 0000000..58e1cdb
--- /dev/null
+++ b/git_remote_helpers/git/repo.py
@@ -0,0 +1,75 @@
+import os
+import subprocess
+
+def sanitize(rev, sep='\t'):
+ """Converts a for-each-ref line to a name/value pair.
+ """
+
+ splitrev = rev.split(sep)
+ branchval = splitrev[0]
+ branchname = splitrev[1].strip()
+ if branchname.startswith("refs/heads/"):
+ branchname = branchname[11:]
+
+ return branchname, branchval
+
+def is_remote(url):
+ """Checks whether the specified value is a remote url.
+ """
+
+ prefixes = ["http", "file", "git"]
+
+ for prefix in prefixes:
+ if url.startswith(prefix):
+ return True
+ return False
+
+class GitRepo(object):
+ """Repo object representing a repo.
+ """
+
+ def __init__(self, path):
+ """Initializes a new repo at the given path.
+ """
+
+ self.path = path
+ self.head = None
+ self.revmap = {}
+ self.local = not is_remote(self.path)
+
+ if(self.path.endswith('.git')):
+ self.gitpath = self.path
+ else:
+ self.gitpath = os.path.join(self.path, '.git')
+
+ if self.local and not os.path.exists(self.gitpath):
+ os.makedirs(self.gitpath)
+
+ def get_revs(self):
+ """Fetches all revs from the remote.
+ """
+
+ args = ["git", "ls-remote", self.gitpath]
+ path = ".cached_revs"
+ ofile = open(path, "w")
+
+ child = subprocess.Popen(args, stdout=ofile)
+ if child.wait() != 0:
+ raise CalledProcessError
+ output = open(path).readlines()
+ self.revmap = dict(sanitize(i) for i in output)
+ if "HEAD" in self.revmap:
+ del self.revmap["HEAD"]
+ self.revs = self.revmap.keys()
+ ofile.close()
+
+ def get_head(self):
+ """Determines the head of a local repo.
+ """
+
+ if not self.local:
+ return
+
+ path = os.path.join(self.gitpath, "HEAD")
+ head = open(path).readline()
+ self.head, _ = sanitize(head, ' ')
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index cbdc136..8230531 100644
--- a/gitweb/INSTALL
+++ b/gitweb/INSTALL
@@ -2,12 +2,13 @@ GIT web Interface (gitweb) Installation
=======================================
First you have to generate gitweb.cgi from gitweb.perl using
-"make gitweb", then copy appropriate files (gitweb.cgi, gitweb.js,
-gitweb.css, git-logo.png and git-favicon.png) to their destination.
-For example if git was (or is) installed with /usr prefix, you can do
+"make gitweb", then "make install-gitweb" appropriate files
+(gitweb.cgi, gitweb.js, gitweb.css, git-logo.png and git-favicon.png)
+to their destination. For example if git was (or is) installed with
+/usr prefix and gitwebdir is /var/www/cgi-bin, you can do
- $ make prefix=/usr gitweb ;# as yourself
- # cp gitweb/git* /var/www/cgi-bin/ ;# as root
+ $ make prefix=/usr gitweb ;# as yourself
+ # make gitwebdir=/var/www/cgi-bin install-gitweb ;# as root
Alternatively you can use autoconf generated ./configure script to
set up path to git binaries (via config.mak.autogen), so you can write
@@ -16,7 +17,8 @@ instead
$ make configure ;# as yourself
$ ./configure --prefix=/usr ;# as yourself
$ make gitweb ;# as yourself
- # cp gitweb/git* /var/www/cgi-bin/ ;# as root
+ # make gitwebdir=/var/www/cgi-bin \
+ install-gitweb ;# as root
The above example assumes that your web server is configured to run
[executable] files in /var/www/cgi-bin/ as server scripts (as CGI
@@ -74,21 +76,20 @@ file for gitweb (in gitweb/README).
Build example
~~~~~~~~~~~~~
-- To install gitweb to /var/www/cgi-bin/gitweb/ when git wrapper
- is installed at /usr/local/bin/git and the repositories (projects)
- we want to display are under /home/local/scm, you can do
+- To install gitweb to /var/www/cgi-bin/gitweb/, when git wrapper
+ is installed at /usr/local/bin/git, the repositories (projects)
+ we want to display are under /home/local/scm, and you do not use
+ minifiers, you can do
make GITWEB_PROJECTROOT="/home/local/scm" \
- GITWEB_JS="/gitweb/gitweb.js" \
- GITWEB_CSS="/gitweb/gitweb.css" \
- GITWEB_LOGO="/gitweb/git-logo.png" \
- GITWEB_FAVICON="/gitweb/git-favicon.png" \
+ GITWEB_JS="gitweb/static/gitweb.js" \
+ GITWEB_CSS="gitweb/static/gitweb.css" \
+ GITWEB_LOGO="gitweb/static/git-logo.png" \
+ GITWEB_FAVICON="gitweb/static/git-favicon.png" \
bindir=/usr/local/bin \
gitweb
- cp -fv ~/git/gitweb/gitweb.{cgi,js,css} \
- ~/git/gitweb/git-{favicon,logo}.png \
- /var/www/cgi-bin/gitweb/
+ make gitwebdir=/var/www/cgi-bin/gitweb install-gitweb
Gitweb config file
diff --git a/gitweb/Makefile b/gitweb/Makefile
index e7dd252..2fb7c2d 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -4,15 +4,18 @@ all::
# Define V=1 to have a more verbose compile.
#
# Define JSMIN to point to JavaScript minifier that functions as
-# a filter to have gitweb.js minified.
+# a filter to have static/gitweb.js minified.
#
# Define CSSMIN to point to a CSS minifier in order to generate a minified
-# version of gitweb.css
+# version of static/gitweb.css
#
prefix ?= $(HOME)
bindir ?= $(prefix)/bin
+gitwebdir ?= /var/www/cgi-bin
+
RM ?= rm -f
+INSTALL ?= install
# default configuration for gitweb
GITWEB_CONFIG = gitweb_config.perl
@@ -26,10 +29,10 @@ GITWEB_STRICT_EXPORT =
GITWEB_BASE_URL =
GITWEB_LIST =
GITWEB_HOMETEXT = indextext.html
-GITWEB_CSS = gitweb.css
-GITWEB_LOGO = git-logo.png
-GITWEB_FAVICON = git-favicon.png
-GITWEB_JS = gitweb.js
+GITWEB_CSS = static/gitweb.css
+GITWEB_LOGO = static/git-logo.png
+GITWEB_FAVICON = static/git-favicon.png
+GITWEB_JS = static/gitweb.js
GITWEB_SITE_HEADER =
GITWEB_SITE_FOOTER =
@@ -49,9 +52,12 @@ SHELL_PATH ?= $(SHELL)
PERL_PATH ?= /usr/bin/perl
# Shell quote;
-bindir_SQ = $(subst ','\'',$(bindir)) #'
-SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) #'
-PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) #'
+bindir_SQ = $(subst ','\'',$(bindir))#'
+gitwebdir_SQ = $(subst ','\'',$(gitwebdir))#'
+gitwebstaticdir_SQ = $(subst ','\'',$(gitwebdir)/static)#'
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))#'
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))#'
+DESTDIR_SQ = $(subst ','\'',$(DESTDIR))#'
# Quiet generation (unless V=1)
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
@@ -80,20 +86,30 @@ endif
all:: gitweb.cgi
+GITWEB_PROGRAMS = gitweb.cgi
+
ifdef JSMIN
-GITWEB_JS = gitweb.min.js
-all:: gitweb.min.js
-gitweb.min.js: gitweb.js GITWEB-BUILD-OPTIONS
+GITWEB_FILES += static/gitweb.min.js
+GITWEB_JS = static/gitweb.min.js
+all:: static/gitweb.min.js
+static/gitweb.min.js: static/gitweb.js GITWEB-BUILD-OPTIONS
$(QUIET_GEN)$(JSMIN) <$< >$@
+else
+GITWEB_FILES += static/gitweb.js
endif
ifdef CSSMIN
-GITWEB_CSS = gitweb.min.css
-all:: gitweb.min.css
-gitweb.min.css: gitweb.css GITWEB-BUILD-OPTIONS
+GITWEB_FILES += static/gitweb.min.css
+GITWEB_CSS = static/gitweb.min.css
+all:: static/gitweb.min.css
+static/gitweb.min.css: static/gitweb.css GITWEB-BUILD-OPTIONS
$(QUIET_GEN)$(CSSMIN) <$< >$@
+else
+GITWEB_FILES += static/gitweb.css
endif
+GITWEB_FILES += static/git-logo.png static/git-favicon.png
+
GITWEB_REPLACE = \
-e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
-e 's|++GIT_BINDIR++|$(bindir)|g' \
@@ -127,8 +143,18 @@ gitweb.cgi: gitweb.perl GITWEB-BUILD-OPTIONS
chmod +x $@+ && \
mv $@+ $@
+### Installation rules
+
+install: all
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitwebdir_SQ)'
+ $(INSTALL) -m 755 $(GITWEB_PROGRAMS) '$(DESTDIR_SQ)$(gitwebdir_SQ)'
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitwebstaticdir_SQ)'
+ $(INSTALL) -m 644 $(GITWEB_FILES) '$(DESTDIR_SQ)$(gitwebstaticdir_SQ)'
+
+### Cleaning rules
+
clean:
- $(RM) gitweb.cgi gitweb.min.js gitweb.min.css GITWEB-BUILD-OPTIONS
+ $(RM) gitweb.cgi static/gitweb.min.js static/gitweb.min.css GITWEB-BUILD-OPTIONS
-.PHONY: all clean .FORCE-GIT-VERSION-FILE FORCE
+.PHONY: all clean install .FORCE-GIT-VERSION-FILE FORCE
diff --git a/gitweb/README b/gitweb/README
index 71742b3..0e19be8 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -80,24 +80,26 @@ You can specify the following configuration variables when building GIT:
Points to the location where you put gitweb.css on your web server
(or to be more generic, the URI of gitweb stylesheet). Relative to the
base URI of gitweb. Note that you can setup multiple stylesheets from
- the gitweb config file. [Default: gitweb.css (or gitweb.min.css if the
- CSSMIN variable is defined / CSS minifier is used)]
+ the gitweb config file. [Default: static/gitweb.css (or
+ static/gitweb.min.css if the CSSMIN variable is defined / CSS minifier
+ is used)]
* GITWEB_LOGO
Points to the location where you put git-logo.png on your web server
(or to be more generic URI of logo, 72x27 size, displayed in top right
corner of each gitweb page, and used as logo for Atom feed). Relative
- to base URI of gitweb. [Default: git-logo.png]
+ to base URI of gitweb. [Default: static/git-logo.png]
* GITWEB_FAVICON
Points to the location where you put git-favicon.png on your web server
(or to be more generic URI of favicon, assumed to be image/png type;
web browsers that support favicons (website icons) may display them
in the browser's URL bar and next to site name in bookmarks). Relative
- to base URI of gitweb. [Default: git-favicon.png]
+ to base URI of gitweb. [Default: static/git-favicon.png]
* GITWEB_JS
Points to the localtion where you put gitweb.js on your web server
(or to be more generic URI of JavaScript code used by gitweb).
- Relative to base URI of gitweb. [Default: gitweb.js (or gitweb.min.js
- if JSMIN build variable is defined / JavaScript minifier is used)]
+ Relative to base URI of gitweb. [Default: static/gitweb.js (or
+ static/gitweb.min.js if JSMIN build variable is defined / JavaScript
+ minifier is used)]
* GITWEB_CONFIG
This Perl file will be loaded using 'do' and can be used to override any
of the options above as well as some other options -- see the "Runtime
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index c356e95..cedc357 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -11,7 +11,7 @@ use strict;
use warnings;
use CGI qw(:standard :escapeHTML -nosticky);
use CGI::Util qw(unescape);
-use CGI::Carp qw(fatalsToBrowser);
+use CGI::Carp qw(fatalsToBrowser set_message);
use Encode;
use Fcntl ':mode';
use File::Find qw();
@@ -28,34 +28,42 @@ BEGIN {
CGI->compile() if $ENV{'MOD_PERL'};
}
-our $cgi = new CGI;
our $version = "++GIT_VERSION++";
-our $my_url = $cgi->url();
-our $my_uri = $cgi->url(-absolute => 1);
-# Base URL for relative URLs in gitweb ($logo, $favicon, ...),
-# needed and used only for URLs with nonempty PATH_INFO
-our $base_url = $my_url;
+our ($my_url, $my_uri, $base_url, $path_info, $home_link);
+sub evaluate_uri {
+ our $cgi;
-# When the script is used as DirectoryIndex, the URL does not contain the name
-# of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we
-# have to do it ourselves. We make $path_info global because it's also used
-# later on.
-#
-# Another issue with the script being the DirectoryIndex is that the resulting
-# $my_url data is not the full script URL: this is good, because we want
-# generated links to keep implying the script name if it wasn't explicitly
-# indicated in the URL we're handling, but it means that $my_url cannot be used
-# as base URL.
-# Therefore, if we needed to strip PATH_INFO, then we know that we have
-# to build the base URL ourselves:
-our $path_info = $ENV{"PATH_INFO"};
-if ($path_info) {
- if ($my_url =~ s,\Q$path_info\E$,, &&
- $my_uri =~ s,\Q$path_info\E$,, &&
- defined $ENV{'SCRIPT_NAME'}) {
- $base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'};
+ our $my_url = $cgi->url();
+ our $my_uri = $cgi->url(-absolute => 1);
+
+ # Base URL for relative URLs in gitweb ($logo, $favicon, ...),
+ # needed and used only for URLs with nonempty PATH_INFO
+ our $base_url = $my_url;
+
+ # When the script is used as DirectoryIndex, the URL does not contain the name
+ # of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we
+ # have to do it ourselves. We make $path_info global because it's also used
+ # later on.
+ #
+ # Another issue with the script being the DirectoryIndex is that the resulting
+ # $my_url data is not the full script URL: this is good, because we want
+ # generated links to keep implying the script name if it wasn't explicitly
+ # indicated in the URL we're handling, but it means that $my_url cannot be used
+ # as base URL.
+ # Therefore, if we needed to strip PATH_INFO, then we know that we have
+ # to build the base URL ourselves:
+ our $path_info = $ENV{"PATH_INFO"};
+ if ($path_info) {
+ if ($my_url =~ s,\Q$path_info\E$,, &&
+ $my_uri =~ s,\Q$path_info\E$,, &&
+ defined $ENV{'SCRIPT_NAME'}) {
+ $base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'};
+ }
}
+
+ # target of the home link on top of all pages
+ our $home_link = $my_uri || "/";
}
# core git executable to use
@@ -70,9 +78,6 @@ our $projectroot = "++GITWEB_PROJECTROOT++";
# the number is relative to the projectroot
our $project_maxdepth = "++GITWEB_PROJECT_MAXDEPTH++";
-# target of the home link on top of all pages
-our $home_link = $my_uri || "/";
-
# string of the home link on top of all pages
our $home_link_str = "++GITWEB_HOME_LINK_STR++";
@@ -445,6 +450,19 @@ our %feature = (
'javascript-actions' => {
'override' => 0,
'default' => [0]},
+
+ # Syntax highlighting support. This is based on Daniel Svensson's
+ # and Sham Chukoury's work in gitweb-xmms2.git.
+ # It requires the 'highlight' program present in $PATH,
+ # and therefore is disabled by default.
+
+ # To enable system wide have in $GITWEB_CONFIG
+ # $feature{'highlight'}{'default'} = [1];
+
+ 'highlight' => {
+ 'sub' => sub { feature_bool('highlight', @_) },
+ 'override' => 0,
+ 'default' => [0]},
);
sub gitweb_get_feature {
@@ -553,15 +571,18 @@ sub filter_snapshot_fmts {
!$known_snapshot_formats{$_}{'disabled'}} @fmts;
}
-our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
-our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
-# die if there are errors parsing config file
-if (-e $GITWEB_CONFIG) {
- do $GITWEB_CONFIG;
- die $@ if $@;
-} elsif (-e $GITWEB_CONFIG_SYSTEM) {
- do $GITWEB_CONFIG_SYSTEM;
- die $@ if $@;
+our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM);
+sub evaluate_gitweb_config {
+ our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
+ our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
+ # die if there are errors parsing config file
+ if (-e $GITWEB_CONFIG) {
+ do $GITWEB_CONFIG;
+ die $@ if $@;
+ } elsif (-e $GITWEB_CONFIG_SYSTEM) {
+ do $GITWEB_CONFIG_SYSTEM;
+ die $@ if $@;
+ }
}
# Get loadavg of system, to compare against $maxload.
@@ -587,13 +608,16 @@ sub get_loadavg {
}
# version of the core git binary
-our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
-$number_of_git_cmds++;
-
-$projects_list ||= $projectroot;
+our $git_version;
+sub evaluate_git_version {
+ our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
+ $number_of_git_cmds++;
+}
-if (defined $maxload && get_loadavg() > $maxload) {
- die_error(503, "The load average on the server is too high");
+sub check_loadavg {
+ if (defined $maxload && get_loadavg() > $maxload) {
+ die_error(503, "The load average on the server is too high");
+ }
}
# ======================================================================
@@ -680,11 +704,15 @@ our %allowed_options = (
# should be single values, but opt can be an array. We should probably
# build an array of parameters that can be multi-valued, but since for the time
# being it's only this one, we just single it out
-while (my ($name, $symbol) = each %cgi_param_mapping) {
- if ($symbol eq 'opt') {
- $input_params{$name} = [ $cgi->param($symbol) ];
- } else {
- $input_params{$name} = $cgi->param($symbol);
+sub evaluate_query_params {
+ our $cgi;
+
+ while (my ($name, $symbol) = each %cgi_param_mapping) {
+ if ($symbol eq 'opt') {
+ $input_params{$name} = [ $cgi->param($symbol) ];
+ } else {
+ $input_params{$name} = $cgi->param($symbol);
+ }
}
}
@@ -831,152 +859,277 @@ sub evaluate_path_info {
}
}
}
-evaluate_path_info();
-our $action = $input_params{'action'};
-if (defined $action) {
- if (!validate_action($action)) {
- die_error(400, "Invalid action parameter");
+our ($action, $project, $file_name, $file_parent, $hash, $hash_parent, $hash_base,
+ $hash_parent_base, @extra_options, $page, $searchtype, $search_use_regexp,
+ $searchtext, $search_regexp);
+sub evaluate_and_validate_params {
+ our $action = $input_params{'action'};
+ if (defined $action) {
+ if (!validate_action($action)) {
+ die_error(400, "Invalid action parameter");
+ }
}
-}
-# parameters which are pathnames
-our $project = $input_params{'project'};
-if (defined $project) {
- if (!validate_project($project)) {
- undef $project;
- die_error(404, "No such project");
+ # parameters which are pathnames
+ our $project = $input_params{'project'};
+ if (defined $project) {
+ if (!validate_project($project)) {
+ undef $project;
+ die_error(404, "No such project");
+ }
}
-}
-our $file_name = $input_params{'file_name'};
-if (defined $file_name) {
- if (!validate_pathname($file_name)) {
- die_error(400, "Invalid file parameter");
+ our $file_name = $input_params{'file_name'};
+ if (defined $file_name) {
+ if (!validate_pathname($file_name)) {
+ die_error(400, "Invalid file parameter");
+ }
}
-}
-our $file_parent = $input_params{'file_parent'};
-if (defined $file_parent) {
- if (!validate_pathname($file_parent)) {
- die_error(400, "Invalid file parent parameter");
+ our $file_parent = $input_params{'file_parent'};
+ if (defined $file_parent) {
+ if (!validate_pathname($file_parent)) {
+ die_error(400, "Invalid file parent parameter");
+ }
}
-}
-# parameters which are refnames
-our $hash = $input_params{'hash'};
-if (defined $hash) {
- if (!validate_refname($hash)) {
- die_error(400, "Invalid hash parameter");
+ # parameters which are refnames
+ our $hash = $input_params{'hash'};
+ if (defined $hash) {
+ if (!validate_refname($hash)) {
+ die_error(400, "Invalid hash parameter");
+ }
}
-}
-our $hash_parent = $input_params{'hash_parent'};
-if (defined $hash_parent) {
- if (!validate_refname($hash_parent)) {
- die_error(400, "Invalid hash parent parameter");
+ our $hash_parent = $input_params{'hash_parent'};
+ if (defined $hash_parent) {
+ if (!validate_refname($hash_parent)) {
+ die_error(400, "Invalid hash parent parameter");
+ }
}
-}
-our $hash_base = $input_params{'hash_base'};
-if (defined $hash_base) {
- if (!validate_refname($hash_base)) {
- die_error(400, "Invalid hash base parameter");
+ our $hash_base = $input_params{'hash_base'};
+ if (defined $hash_base) {
+ if (!validate_refname($hash_base)) {
+ die_error(400, "Invalid hash base parameter");
+ }
}
-}
-our @extra_options = @{$input_params{'extra_options'}};
-# @extra_options is always defined, since it can only be (currently) set from
-# CGI, and $cgi->param() returns the empty array in array context if the param
-# is not set
-foreach my $opt (@extra_options) {
- if (not exists $allowed_options{$opt}) {
- die_error(400, "Invalid option parameter");
- }
- if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
- die_error(400, "Invalid option parameter for this action");
+ our @extra_options = @{$input_params{'extra_options'}};
+ # @extra_options is always defined, since it can only be (currently) set from
+ # CGI, and $cgi->param() returns the empty array in array context if the param
+ # is not set
+ foreach my $opt (@extra_options) {
+ if (not exists $allowed_options{$opt}) {
+ die_error(400, "Invalid option parameter");
+ }
+ if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
+ die_error(400, "Invalid option parameter for this action");
+ }
}
-}
-our $hash_parent_base = $input_params{'hash_parent_base'};
-if (defined $hash_parent_base) {
- if (!validate_refname($hash_parent_base)) {
- die_error(400, "Invalid hash parent base parameter");
+ our $hash_parent_base = $input_params{'hash_parent_base'};
+ if (defined $hash_parent_base) {
+ if (!validate_refname($hash_parent_base)) {
+ die_error(400, "Invalid hash parent base parameter");
+ }
}
-}
-# other parameters
-our $page = $input_params{'page'};
-if (defined $page) {
- if ($page =~ m/[^0-9]/) {
- die_error(400, "Invalid page parameter");
+ # other parameters
+ our $page = $input_params{'page'};
+ if (defined $page) {
+ if ($page =~ m/[^0-9]/) {
+ die_error(400, "Invalid page parameter");
+ }
}
-}
-our $searchtype = $input_params{'searchtype'};
-if (defined $searchtype) {
- if ($searchtype =~ m/[^a-z]/) {
- die_error(400, "Invalid searchtype parameter");
+ our $searchtype = $input_params{'searchtype'};
+ if (defined $searchtype) {
+ if ($searchtype =~ m/[^a-z]/) {
+ die_error(400, "Invalid searchtype parameter");
+ }
}
-}
-our $search_use_regexp = $input_params{'search_use_regexp'};
+ our $search_use_regexp = $input_params{'search_use_regexp'};
-our $searchtext = $input_params{'searchtext'};
-our $search_regexp;
-if (defined $searchtext) {
- if (length($searchtext) < 2) {
- die_error(403, "At least two characters are required for search parameter");
+ our $searchtext = $input_params{'searchtext'};
+ our $search_regexp;
+ if (defined $searchtext) {
+ if (length($searchtext) < 2) {
+ die_error(403, "At least two characters are required for search parameter");
+ }
+ $search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;
}
- $search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;
}
# path to the current git repository
our $git_dir;
-$git_dir = "$projectroot/$project" if $project;
-
-# list of supported snapshot formats
-our @snapshot_fmts = gitweb_get_feature('snapshot');
-@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
-
-# check that the avatar feature is set to a known provider name,
-# and for each provider check if the dependencies are satisfied.
-# if the provider name is invalid or the dependencies are not met,
-# reset $git_avatar to the empty string.
-our ($git_avatar) = gitweb_get_feature('avatar');
-if ($git_avatar eq 'gravatar') {
- $git_avatar = '' unless (eval { require Digest::MD5; 1; });
-} elsif ($git_avatar eq 'picon') {
- # no dependencies
-} else {
- $git_avatar = '';
+sub evaluate_git_dir {
+ our $git_dir = "$projectroot/$project" if $project;
}
-# dispatch
-if (!defined $action) {
- if (defined $hash) {
- $action = git_get_type($hash);
- } elsif (defined $hash_base && defined $file_name) {
- $action = git_get_type("$hash_base:$file_name");
- } elsif (defined $project) {
- $action = 'summary';
+our (@snapshot_fmts, $git_avatar);
+sub configure_gitweb_features {
+ # list of supported snapshot formats
+ our @snapshot_fmts = gitweb_get_feature('snapshot');
+ @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
+
+ # check that the avatar feature is set to a known provider name,
+ # and for each provider check if the dependencies are satisfied.
+ # if the provider name is invalid or the dependencies are not met,
+ # reset $git_avatar to the empty string.
+ our ($git_avatar) = gitweb_get_feature('avatar');
+ if ($git_avatar eq 'gravatar') {
+ $git_avatar = '' unless (eval { require Digest::MD5; 1; });
+ } elsif ($git_avatar eq 'picon') {
+ # no dependencies
} else {
- $action = 'project_list';
+ $git_avatar = '';
+ }
+}
+
+# custom error handler: 'die <message>' is Internal Server Error
+sub handle_errors_html {
+ my $msg = shift; # it is already HTML escaped
+
+ # to avoid infinite loop where error occurs in die_error,
+ # change handler to default handler, disabling handle_errors_html
+ set_message("Error occured when inside die_error:\n$msg");
+
+ # you cannot jump out of die_error when called as error handler;
+ # the subroutine set via CGI::Carp::set_message is called _after_
+ # HTTP headers are already written, so it cannot write them itself
+ die_error(undef, undef, $msg, -error_handler => 1, -no_http_header => 1);
+}
+set_message(\&handle_errors_html);
+
+# dispatch
+sub dispatch {
+ if (!defined $action) {
+ if (defined $hash) {
+ $action = git_get_type($hash);
+ } elsif (defined $hash_base && defined $file_name) {
+ $action = git_get_type("$hash_base:$file_name");
+ } elsif (defined $project) {
+ $action = 'summary';
+ } else {
+ $action = 'project_list';
+ }
+ }
+ if (!defined($actions{$action})) {
+ die_error(400, "Unknown action");
}
+ if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
+ !$project) {
+ die_error(400, "Project needed");
+ }
+ $actions{$action}->();
+}
+
+sub reset_timer {
+ our $t0 = [Time::HiRes::gettimeofday()]
+ if defined $t0;
+ our $number_of_git_cmds = 0;
+}
+
+sub run_request {
+ reset_timer();
+
+ evaluate_uri();
+ check_loadavg();
+
+ evaluate_query_params();
+ evaluate_path_info();
+ evaluate_and_validate_params();
+ evaluate_git_dir();
+
+ configure_gitweb_features();
+
+ dispatch();
+}
+
+our $is_last_request = sub { 1 };
+our ($pre_dispatch_hook, $post_dispatch_hook, $pre_listen_hook);
+our $CGI = 'CGI';
+our $cgi;
+sub configure_as_fcgi {
+ require CGI::Fast;
+ our $CGI = 'CGI::Fast';
+
+ my $request_number = 0;
+ # let each child service 100 requests
+ our $is_last_request = sub { ++$request_number > 100 };
}
-if (!defined($actions{$action})) {
- die_error(400, "Unknown action");
+sub evaluate_argv {
+ my $script_name = $ENV{'SCRIPT_NAME'} || $ENV{'SCRIPT_FILENAME'} || __FILE__;
+ configure_as_fcgi()
+ if $script_name =~ /\.fcgi$/;
+
+ return unless (@ARGV);
+
+ require Getopt::Long;
+ Getopt::Long::GetOptions(
+ 'fastcgi|fcgi|f' => \&configure_as_fcgi,
+ 'nproc|n=i' => sub {
+ my ($arg, $val) = @_;
+ return unless eval { require FCGI::ProcManager; 1; };
+ my $proc_manager = FCGI::ProcManager->new({
+ n_processes => $val,
+ });
+ our $pre_listen_hook = sub { $proc_manager->pm_manage() };
+ our $pre_dispatch_hook = sub { $proc_manager->pm_pre_dispatch() };
+ our $post_dispatch_hook = sub { $proc_manager->pm_post_dispatch() };
+ },
+ );
}
-if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
- !$project) {
- die_error(400, "Project needed");
+
+sub run {
+ evaluate_argv();
+ evaluate_gitweb_config();
+ evaluate_git_version();
+
+ # $projectroot and $projects_list might be set in gitweb config file
+ $projects_list ||= $projectroot;
+
+ $pre_listen_hook->()
+ if $pre_listen_hook;
+
+ REQUEST:
+ while ($cgi = $CGI->new()) {
+ $pre_dispatch_hook->()
+ if $pre_dispatch_hook;
+
+ run_request();
+
+ $pre_dispatch_hook->()
+ if $post_dispatch_hook;
+
+ last REQUEST if ($is_last_request->());
+ }
+
+ DONE_GITWEB:
+ 1;
+}
+
+run();
+
+if (defined caller) {
+ # wrapped in a subroutine processing requests,
+ # e.g. mod_perl with ModPerl::Registry, or PSGI with Plack::App::WrapCGI
+ return;
+} else {
+ # pure CGI script, serving single request
+ exit;
}
-$actions{$action}->();
-exit;
## ======================================================================
## action links
+# possible values of extra options
+# -full => 0|1 - use absolute/full URL ($my_uri/$my_url as base)
+# -replay => 1 - start from a current view (replay with modifications)
+# -path_info => 0|1 - don't use/use path_info URL (if possible)
sub href {
my %params = @_;
# default is to use -absolute url() i.e. $my_uri
@@ -993,7 +1146,8 @@ sub href {
}
my $use_pathinfo = gitweb_check_feature('pathinfo');
- if ($use_pathinfo and defined $params{'project'}) {
+ if (defined $params{'project'} &&
+ (exists $params{-path_info} ? $params{-path_info} : $use_pathinfo)) {
# try to put as many parameters as possible in PATH_INFO:
# - project name
# - action
@@ -1173,8 +1327,7 @@ sub esc_param {
sub esc_url {
my $str = shift;
return undef unless defined $str;
- $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg;
- $str =~ s/\+/%2B/g;
+ $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&= ]+)/CGI::escape($1)/eg;
$str =~ s/ /\+/g;
return $str;
}
@@ -2420,6 +2573,9 @@ sub git_get_projects_list {
follow_skip => 2, # ignore duplicates
dangling_symlinks => 0, # ignore dangling symlinks, silently
wanted => sub {
+ # global variables
+ our $project_maxdepth;
+ our $projectroot;
# skip project-list toplevel, if we get it.
return if (m!^[/.]$!);
# only directories can be git repositories
@@ -3155,26 +3311,88 @@ sub blob_contenttype {
return $type;
}
+# guess file syntax for syntax highlighting; return undef if no highlighting
+# the name of syntax can (in the future) depend on syntax highlighter used
+sub guess_file_syntax {
+ my ($highlight, $mimetype, $file_name) = @_;
+ return undef unless ($highlight && defined $file_name);
+
+ # configuration for 'highlight' (http://www.andre-simon.de/)
+ # match by basename
+ my %highlight_basename = (
+ #'Program' => 'py',
+ #'Library' => 'py',
+ 'SConstruct' => 'py', # SCons equivalent of Makefile
+ 'Makefile' => 'make',
+ );
+ # match by extension
+ my %highlight_ext = (
+ # main extensions, defining name of syntax;
+ # see files in /usr/share/highlight/langDefs/ directory
+ map { $_ => $_ }
+ qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
+ # alternate extensions, see /etc/highlight/filetypes.conf
+ 'h' => 'c',
+ map { $_ => 'cpp' } qw(cxx c++ cc),
+ map { $_ => 'php' } qw(php3 php4),
+ map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi'
+ 'mak' => 'make',
+ map { $_ => 'xml' } qw(xhtml html htm),
+ );
+
+ my $basename = basename($file_name, '.in');
+ return $highlight_basename{$basename}
+ if exists $highlight_basename{$basename};
+
+ $basename =~ /\.([^.]*)$/;
+ my $ext = $1 or return undef;
+ return $highlight_ext{$ext}
+ if exists $highlight_ext{$ext};
+
+ return undef;
+}
+
+# run highlighter and return FD of its output,
+# or return original FD if no highlighting
+sub run_highlighter {
+ my ($fd, $highlight, $syntax) = @_;
+ return $fd unless ($highlight && defined $syntax);
+
+ close $fd
+ or die_error(404, "Reading blob failed");
+ open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
+ "highlight --xhtml --fragment --syntax $syntax |"
+ or die_error(500, "Couldn't open file or run syntax highlighter");
+ return $fd;
+}
+
## ======================================================================
## functions printing HTML: header, footer, error page
+sub get_page_title {
+ my $title = to_utf8($site_name);
+
+ return $title unless (defined $project);
+ $title .= " - " . to_utf8($project);
+
+ return $title unless (defined $action);
+ $title .= "/$action"; # $action is US-ASCII (7bit ASCII)
+
+ return $title unless (defined $file_name);
+ $title .= " - " . esc_path($file_name);
+ if ($action eq "tree" && $file_name !~ m|/$|) {
+ $title .= "/";
+ }
+
+ return $title;
+}
+
sub git_header_html {
my $status = shift || "200 OK";
my $expires = shift;
+ my %opts = @_;
- my $title = "$site_name";
- if (defined $project) {
- $title .= " - " . to_utf8($project);
- if (defined $action) {
- $title .= "/$action";
- if (defined $file_name) {
- $title .= " - " . esc_path($file_name);
- if ($action eq "tree" && $file_name !~ m|/$|) {
- $title .= "/";
- }
- }
- }
- }
+ my $title = get_page_title();
my $content_type;
# require explicit support from the UA if we are to send the page as
# 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
@@ -3188,7 +3406,8 @@ sub git_header_html {
$content_type = 'text/html';
}
print $cgi->header(-type=>$content_type, -charset => 'utf-8',
- -status=> $status, -expires => $expires);
+ -status=> $status, -expires => $expires)
+ unless ($opts{'-no_http_header'});
my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
print <<EOF;
<?xml version="1.0" encoding="utf-8"?>
@@ -3405,6 +3624,7 @@ sub die_error {
my $status = shift || 500;
my $error = esc_html(shift) || "Internal Server Error";
my $extra = shift;
+ my %opts = @_;
my %http_responses = (
400 => '400 Bad Request',
@@ -3413,7 +3633,7 @@ sub die_error {
500 => '500 Internal Server Error',
503 => '503 Service Unavailable',
);
- git_header_html($http_responses{$status});
+ git_header_html($http_responses{$status}, undef, %opts);
print <<EOF;
<div class="page_body">
<br /><br />
@@ -3427,7 +3647,8 @@ EOF
print "</div>\n";
git_footer_html();
- exit;
+ goto DONE_GITWEB
+ unless ($opts{'-error_handler'});
}
## ----------------------------------------------------------------------
@@ -5346,6 +5567,7 @@ sub git_blob {
open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
or die_error(500, "Couldn't cat $file_name, $hash");
my $mimetype = blob_mimetype($fd, $file_name);
+ # use 'blob_plain' (aka 'raw') view for files that cannot be displayed
if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) {
close $fd;
return git_blob_plain($mimetype);
@@ -5353,6 +5575,11 @@ sub git_blob {
# we can have blame only for text/* mimetype
$have_blame &&= ($mimetype =~ m!^text/!);
+ my $highlight = gitweb_check_feature('highlight');
+ my $syntax = guess_file_syntax($highlight, $mimetype, $file_name);
+ $fd = run_highlighter($fd, $highlight, $syntax)
+ if $syntax;
+
git_header_html(undef, $expires);
my $formats_nav = '';
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
@@ -5402,9 +5629,8 @@ sub git_blob {
chomp $line;
$nr++;
$line = untabify($line);
- printf "<div class=\"pre\"><a id=\"l%i\" href=\"" . href(-replay => 1)
- . "#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
- $nr, $nr, $nr, esc_html($line, -nbsp=>1);
+ printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!,
+ $nr, href(-replay => 1), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
}
}
close $fd
@@ -6117,8 +6343,8 @@ sub git_commitdiff {
}
push @commit_spec, '--root', $hash;
}
- open $fd, "-|", git_cmd(), "format-patch", '--encoding=utf8',
- '--stdout', @commit_spec
+ open $fd, "-|", git_cmd(), "format-patch", @diff_opts,
+ '--encoding=utf8', '--stdout', @commit_spec
or die_error(500, "Open git-format-patch failed");
} else {
die_error(400, "Unknown commitdiff format");
diff --git a/gitweb/git-favicon.png b/gitweb/static/git-favicon.png
index aae35a7..aae35a7 100644
--- a/gitweb/git-favicon.png
+++ b/gitweb/static/git-favicon.png
Binary files differ
diff --git a/gitweb/git-logo.png b/gitweb/static/git-logo.png
index f4ede2e..f4ede2e 100644
--- a/gitweb/git-logo.png
+++ b/gitweb/static/git-logo.png
Binary files differ
diff --git a/gitweb/gitweb.css b/gitweb/static/gitweb.css
index 50067f2..4132aab 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/static/gitweb.css
@@ -572,3 +572,21 @@ span.match {
div.binary {
font-style: italic;
}
+
+/* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
+
+/* Highlighting theme definition: */
+
+.num { color:#2928ff; }
+.esc { color:#ff00ff; }
+.str { color:#ff0000; }
+.dstr { color:#818100; }
+.slc { color:#838183; font-style:italic; }
+.com { color:#838183; font-style:italic; }
+.dir { color:#008200; }
+.sym { color:#000000; }
+.line { color:#555555; }
+.kwa { color:#000000; font-weight:bold; }
+.kwb { color:#830000; }
+.kwc { color:#000000; font-weight:bold; }
+.kwd { color:#010181; }
diff --git a/gitweb/gitweb.js b/gitweb/static/gitweb.js
index 9c66928..9c66928 100644
--- a/gitweb/gitweb.js
+++ b/gitweb/static/gitweb.js
diff --git a/graph.c b/graph.c
index e6bbcaa..ac7c605 100644
--- a/graph.c
+++ b/graph.c
@@ -211,6 +211,18 @@ struct git_graph {
unsigned short default_column_color;
};
+static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void *data)
+{
+ struct git_graph *graph = data;
+ static struct strbuf msgbuf = STRBUF_INIT;
+
+ assert(graph);
+
+ strbuf_reset(&msgbuf);
+ graph_padding_line(graph, &msgbuf);
+ return &msgbuf;
+}
+
struct git_graph *graph_init(struct rev_info *opt)
{
struct git_graph *graph = xmalloc(sizeof(struct git_graph));
@@ -244,6 +256,13 @@ struct git_graph *graph_init(struct rev_info *opt)
graph->mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
graph->new_mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
+ /*
+ * The diff output prefix callback, with this we can make
+ * all the diff output to align with the graph lines.
+ */
+ opt->diffopt.output_prefix = diff_output_prefix_callback;
+ opt->diffopt.output_prefix_data = graph;
+
return graph;
}
diff --git a/grep.c b/grep.c
index 543b1d5..82fb349 100644
--- a/grep.c
+++ b/grep.c
@@ -7,6 +7,7 @@ void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field fie
{
struct grep_pat *p = xcalloc(1, sizeof(*p));
p->pattern = pat;
+ p->patternlen = strlen(pat);
p->origin = "header";
p->no = 0;
p->token = GREP_PATTERN_HEAD;
@@ -19,8 +20,15 @@ void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field fie
void append_grep_pattern(struct grep_opt *opt, const char *pat,
const char *origin, int no, enum grep_pat_token t)
{
+ append_grep_pat(opt, pat, strlen(pat), origin, no, t);
+}
+
+void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen,
+ const char *origin, int no, enum grep_pat_token t)
+{
struct grep_pat *p = xcalloc(1, sizeof(*p));
p->pattern = pat;
+ p->patternlen = patlen;
p->origin = origin;
p->no = no;
p->token = t;
@@ -44,8 +52,8 @@ struct grep_opt *grep_opt_dup(const struct grep_opt *opt)
append_header_grep_pattern(ret, pat->field,
pat->pattern);
else
- append_grep_pattern(ret, pat->pattern, pat->origin,
- pat->no, pat->token);
+ append_grep_pat(ret, pat->pattern, pat->patternlen,
+ pat->origin, pat->no, pat->token);
}
return ret;
@@ -329,14 +337,21 @@ static void show_name(struct grep_opt *opt, const char *name)
opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
}
-
-static int fixmatch(const char *pattern, char *line, int ignore_case, regmatch_t *match)
+static int fixmatch(struct grep_pat *p, char *line, char *eol,
+ regmatch_t *match)
{
char *hit;
- if (ignore_case)
- hit = strcasestr(line, pattern);
- else
- hit = strstr(line, pattern);
+
+ if (p->ignore_case) {
+ char *s = line;
+ do {
+ hit = strcasestr(s, p->pattern);
+ if (hit)
+ break;
+ s += strlen(s) + 1;
+ } while (s < eol);
+ } else
+ hit = memmem(line, eol - line, p->pattern, p->patternlen);
if (!hit) {
match->rm_so = match->rm_eo = -1;
@@ -344,11 +359,22 @@ static int fixmatch(const char *pattern, char *line, int ignore_case, regmatch_t
}
else {
match->rm_so = hit - line;
- match->rm_eo = match->rm_so + strlen(pattern);
+ match->rm_eo = match->rm_so + p->patternlen;
return 0;
}
}
+static int regmatch(const regex_t *preg, char *line, char *eol,
+ regmatch_t *match, int eflags)
+{
+#ifdef REG_STARTEND
+ match->rm_so = 0;
+ match->rm_eo = eol - line;
+ eflags |= REG_STARTEND;
+#endif
+ return regexec(preg, line, 1, match, eflags);
+}
+
static int strip_timestamp(char *bol, char **eol_p)
{
char *eol = *eol_p;
@@ -399,9 +425,9 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
again:
if (p->fixed)
- hit = !fixmatch(p->pattern, bol, p->ignore_case, pmatch);
+ hit = !fixmatch(p, bol, eol, pmatch);
else
- hit = !regexec(&p->regexp, bol, 1, pmatch, eflags);
+ hit = !regmatch(&p->regexp, bol, eol, pmatch, eflags);
if (hit && p->word_regexp) {
if ((pmatch[0].rm_so < 0) ||
@@ -726,16 +752,9 @@ static int look_ahead(struct grep_opt *opt,
regmatch_t m;
if (p->fixed)
- hit = !fixmatch(p->pattern, bol, p->ignore_case, &m);
- else {
-#ifdef REG_STARTEND
- m.rm_so = 0;
- m.rm_eo = *left_p;
- hit = !regexec(&p->regexp, bol, 1, &m, REG_STARTEND);
-#else
- hit = !regexec(&p->regexp, bol, 1, &m, 0);
-#endif
- }
+ hit = !fixmatch(p, bol, bol + *left_p, &m);
+ else
+ hit = !regmatch(&p->regexp, bol, bol + *left_p, &m, 0);
if (!hit || m.rm_so < 0 || m.rm_eo < 0)
continue;
if (earliest < 0 || m.rm_so < earliest)
@@ -800,17 +819,19 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
opt->show_hunk_mark = 1;
opt->last_shown = 0;
- if (buffer_is_binary(buf, size)) {
- switch (opt->binary) {
- case GREP_BINARY_DEFAULT:
+ switch (opt->binary) {
+ case GREP_BINARY_DEFAULT:
+ if (buffer_is_binary(buf, size))
binary_match_only = 1;
- break;
- case GREP_BINARY_NOMATCH:
+ break;
+ case GREP_BINARY_NOMATCH:
+ if (buffer_is_binary(buf, size))
return 0; /* Assume unmatch */
- break;
- default:
- break;
- }
+ break;
+ case GREP_BINARY_TEXT:
+ break;
+ default:
+ die("bug: unknown binary handling mode");
}
memset(&xecfg, 0, sizeof(xecfg));
@@ -871,6 +892,12 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
count++;
if (opt->status_only)
return 1;
+ if (opt->name_only) {
+ show_name(opt, name);
+ return 1;
+ }
+ if (opt->count)
+ goto next_line;
if (binary_match_only) {
opt->output(opt, "Binary file ", 12);
output_color(opt, name, strlen(name),
@@ -878,22 +905,14 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
opt->output(opt, " matches\n", 9);
return 1;
}
- if (opt->name_only) {
- show_name(opt, name);
- return 1;
- }
/* Hit at this line. If we haven't shown the
* pre-context lines, we would need to show them.
- * When asked to do "count", this still show
- * the context which is nonsense, but the user
- * deserves to get that ;-).
*/
if (opt->pre_context)
show_pre_context(opt, name, buf, bol, lno);
else if (opt->funcname)
show_funcname_line(opt, name, buf, bol, lno);
- if (!opt->count)
- show_line(opt, bol, eol, name, lno, ':');
+ show_line(opt, bol, eol, name, lno, ':');
last_hit = lno;
}
else if (last_hit &&
@@ -937,6 +956,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
output_sep(opt, ':');
snprintf(buf, sizeof(buf), "%u\n", count);
opt->output(opt, buf, strlen(buf));
+ return 1;
}
return !!last_hit;
}
diff --git a/grep.h b/grep.h
index 89342e5..efa8cff 100644
--- a/grep.h
+++ b/grep.h
@@ -10,17 +10,17 @@ enum grep_pat_token {
GREP_OPEN_PAREN,
GREP_CLOSE_PAREN,
GREP_NOT,
- GREP_OR,
+ GREP_OR
};
enum grep_context {
GREP_CONTEXT_HEAD,
- GREP_CONTEXT_BODY,
+ GREP_CONTEXT_BODY
};
enum grep_header_field {
GREP_HEADER_AUTHOR = 0,
- GREP_HEADER_COMMITTER,
+ GREP_HEADER_COMMITTER
};
struct grep_pat {
@@ -29,6 +29,7 @@ struct grep_pat {
int no;
enum grep_pat_token token;
const char *pattern;
+ size_t patternlen;
enum grep_header_field field;
regex_t regexp;
unsigned fixed:1;
@@ -40,7 +41,7 @@ enum grep_expr_node {
GREP_NODE_ATOM,
GREP_NODE_NOT,
GREP_NODE_AND,
- GREP_NODE_OR,
+ GREP_NODE_OR
};
struct grep_expr {
@@ -104,6 +105,7 @@ struct grep_opt {
void *output_priv;
};
+extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
extern void compile_grep_patterns(struct grep_opt *opt);
diff --git a/http-backend.c b/http-backend.c
index d1e83d0..14c90c2 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -6,6 +6,7 @@
#include "exec_cmd.h"
#include "run-command.h"
#include "string-list.h"
+#include "url.h"
static const char content_type[] = "Content-Type";
static const char content_length[] = "Content-Length";
@@ -25,60 +26,6 @@ static struct rpc_service rpc_service[] = {
{ "receive-pack", "receivepack", -1 },
};
-static int decode_char(const char *q)
-{
- int i;
- unsigned char val = 0;
- for (i = 0; i < 2; i++) {
- unsigned char c = *q++;
- val <<= 4;
- if (c >= '0' && c <= '9')
- val += c - '0';
- else if (c >= 'a' && c <= 'f')
- val += c - 'a' + 10;
- else if (c >= 'A' && c <= 'F')
- val += c - 'A' + 10;
- else
- return -1;
- }
- return val;
-}
-
-static char *decode_parameter(const char **query, int is_name)
-{
- const char *q = *query;
- struct strbuf out;
-
- strbuf_init(&out, 16);
- do {
- unsigned char c = *q;
-
- if (!c)
- break;
- if (c == '&' || (is_name && c == '=')) {
- q++;
- break;
- }
-
- if (c == '%') {
- int val = decode_char(q + 1);
- if (0 <= val) {
- strbuf_addch(&out, val);
- q += 3;
- continue;
- }
- }
-
- if (c == '+')
- strbuf_addch(&out, ' ');
- else
- strbuf_addch(&out, c);
- q++;
- } while (1);
- *query = q;
- return strbuf_detach(&out, NULL);
-}
-
static struct string_list *get_parameters(void)
{
if (!query_params) {
@@ -86,13 +33,13 @@ static struct string_list *get_parameters(void)
query_params = xcalloc(1, sizeof(*query_params));
while (query && *query) {
- char *name = decode_parameter(&query, 1);
- char *value = decode_parameter(&query, 0);
+ char *name = url_decode_parameter_name(&query);
+ char *value = url_decode_parameter_value(&query);
struct string_list_item *i;
- i = string_list_lookup(name, query_params);
+ i = string_list_lookup(query_params, name);
if (!i)
- i = string_list_insert(name, query_params);
+ i = string_list_insert(query_params, name);
else
free(i->util);
i->util = value;
@@ -104,7 +51,7 @@ static struct string_list *get_parameters(void)
static const char *get_parameter(const char *name)
{
struct string_list_item *i;
- i = string_list_lookup(name, get_parameters());
+ i = string_list_lookup(get_parameters(), name);
return i ? i->util : NULL;
}
@@ -541,14 +488,12 @@ static NORETURN void die_webcgi(const char *err, va_list params)
static int dead;
if (!dead) {
- char buffer[1000];
dead = 1;
-
- vsnprintf(buffer, sizeof(buffer), err, params);
- fprintf(stderr, "fatal: %s\n", buffer);
http_status(500, "Internal Server Error");
hdr_nocache();
end_headers();
+
+ vreportf("fatal: ", err, params);
}
exit(0); /* we successfully reported a failure ;-) */
}
diff --git a/http-push.c b/http-push.c
index 415b1ab..c9bcd11 100644
--- a/http-push.c
+++ b/http-push.c
@@ -105,7 +105,7 @@ enum transfer_state {
RUN_PUT,
RUN_MOVE,
ABORTED,
- COMPLETE,
+ COMPLETE
};
struct transfer_request
diff --git a/http-walker.c b/http-walker.c
index 8ca76d0..18bd650 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -15,7 +15,7 @@ enum object_request_state {
WAITING,
ABORTED,
ACTIVE,
- COMPLETE,
+ COMPLETE
};
struct object_request
diff --git a/imap-send.c b/imap-send.c
index 9d0097c..1a577a0 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -230,7 +230,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5,
+ AUTH_CRAM_MD5
};
static const char *cap_list[] = {
diff --git a/ll-merge.c b/ll-merge.c
index f9b3d85..3764a1a 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -139,17 +139,17 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
{
char temp[4][50];
struct strbuf cmd = STRBUF_INIT;
- struct strbuf_expand_dict_entry dict[] = {
- { "O", temp[0] },
- { "A", temp[1] },
- { "B", temp[2] },
- { "L", temp[3] },
- { NULL }
- };
+ struct strbuf_expand_dict_entry dict[5];
const char *args[] = { NULL, NULL };
int status, fd, i;
struct stat st;
+ dict[0].placeholder = "O"; dict[0].value = temp[0];
+ dict[1].placeholder = "A"; dict[1].value = temp[1];
+ dict[2].placeholder = "B"; dict[2].value = temp[2];
+ dict[3].placeholder = "L"; dict[3].value = temp[3];
+ dict[4].placeholder = NULL; dict[4].value = NULL;
+
if (fn->cmdline == NULL)
die("custom merge driver %s lacks command line.", fn->name);
diff --git a/log-tree.c b/log-tree.c
index d3ae969..b46ed3b 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -7,32 +7,113 @@
#include "reflog-walk.h"
#include "refs.h"
#include "string-list.h"
+#include "color.h"
struct decoration name_decoration = { "object names" };
-static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
+enum decoration_type {
+ DECORATION_NONE = 0,
+ DECORATION_REF_LOCAL,
+ DECORATION_REF_REMOTE,
+ DECORATION_REF_TAG,
+ DECORATION_REF_STASH,
+ DECORATION_REF_HEAD,
+};
+
+static char decoration_colors[][COLOR_MAXLEN] = {
+ GIT_COLOR_RESET,
+ GIT_COLOR_BOLD_GREEN, /* REF_LOCAL */
+ GIT_COLOR_BOLD_RED, /* REF_REMOTE */
+ GIT_COLOR_BOLD_YELLOW, /* REF_TAG */
+ GIT_COLOR_BOLD_MAGENTA, /* REF_STASH */
+ GIT_COLOR_BOLD_CYAN, /* REF_HEAD */
+};
+
+static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix)
+{
+ if (decorate_use_color)
+ return decoration_colors[ix];
+ return "";
+}
+
+static int parse_decorate_color_slot(const char *slot)
+{
+ /*
+ * We're comparing with 'ignore-case' on
+ * (because config.c sets them all tolower),
+ * but let's match the letters in the literal
+ * string values here with how they are
+ * documented in Documentation/config.txt, for
+ * consistency.
+ *
+ * We love being consistent, don't we?
+ */
+ if (!strcasecmp(slot, "branch"))
+ return DECORATION_REF_LOCAL;
+ if (!strcasecmp(slot, "remoteBranch"))
+ return DECORATION_REF_REMOTE;
+ if (!strcasecmp(slot, "tag"))
+ return DECORATION_REF_TAG;
+ if (!strcasecmp(slot, "stash"))
+ return DECORATION_REF_STASH;
+ if (!strcasecmp(slot, "HEAD"))
+ return DECORATION_REF_HEAD;
+ return -1;
+}
+
+int parse_decorate_color_config(const char *var, const int ofs, const char *value)
+{
+ int slot = parse_decorate_color_slot(var + ofs);
+ if (slot < 0)
+ return 0;
+ if (!value)
+ return config_error_nonbool(var);
+ color_parse(value, var, decoration_colors[slot]);
+ return 0;
+}
+
+/*
+ * log-tree.c uses DIFF_OPT_TST for determining whether to use color
+ * for showing the commit sha1, use the same check for --decorate
+ */
+#define decorate_get_color_opt(o, ix) \
+ decorate_get_color(DIFF_OPT_TST((o), COLOR_DIFF), ix)
+
+static void add_name_decoration(enum decoration_type type, const char *name, struct object *obj)
{
- int plen = strlen(prefix);
int nlen = strlen(name);
- struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
- memcpy(res->name, prefix, plen);
- memcpy(res->name + plen, name, nlen + 1);
+ struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + nlen);
+ memcpy(res->name, name, nlen + 1);
+ res->type = type;
res->next = add_decoration(&name_decoration, obj, res);
}
static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
{
struct object *obj = parse_object(sha1);
+ enum decoration_type type = DECORATION_NONE;
if (!obj)
return 0;
+
+ if (!prefixcmp(refname, "refs/heads"))
+ type = DECORATION_REF_LOCAL;
+ else if (!prefixcmp(refname, "refs/remotes"))
+ type = DECORATION_REF_REMOTE;
+ else if (!prefixcmp(refname, "refs/tags"))
+ type = DECORATION_REF_TAG;
+ else if (!prefixcmp(refname, "refs/stash"))
+ type = DECORATION_REF_STASH;
+ else if (!prefixcmp(refname, "HEAD"))
+ type = DECORATION_REF_HEAD;
+
if (!cb_data || *(int *)cb_data == DECORATE_SHORT_REFS)
refname = prettify_refname(refname);
- add_name_decoration("", refname, obj);
+ add_name_decoration(type, refname, obj);
while (obj->type == OBJ_TAG) {
obj = ((struct tag *)obj)->tagged;
if (!obj)
break;
- add_name_decoration("tag: ", refname, obj);
+ add_name_decoration(DECORATION_REF_TAG, refname, obj);
}
return 0;
}
@@ -60,6 +141,10 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
{
const char *prefix;
struct name_decoration *decoration;
+ const char *color_commit =
+ diff_get_color_opt(&opt->diffopt, DIFF_COMMIT);
+ const char *color_reset =
+ decorate_get_color_opt(&opt->diffopt, DECORATION_NONE);
if (opt->show_source && commit->util)
printf("\t%s", (char *) commit->util);
@@ -70,7 +155,14 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
return;
prefix = " (";
while (decoration) {
- printf("%s%s", prefix, decoration->name);
+ printf("%s", prefix);
+ fputs(decorate_get_color_opt(&opt->diffopt, decoration->type),
+ stdout);
+ if (decoration->type == DECORATION_REF_TAG)
+ fputs("tag: ", stdout);
+ printf("%s", decoration->name);
+ fputs(color_reset, stdout);
+ fputs(color_commit, stdout);
prefix = ", ";
decoration = decoration->next;
}
@@ -469,6 +561,12 @@ int log_tree_diff_flush(struct rev_info *opt)
int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
if ((pch & opt->diffopt.output_format) == pch)
printf("---");
+ if (opt->diffopt.output_prefix) {
+ struct strbuf *msg = NULL;
+ msg = opt->diffopt.output_prefix(&opt->diffopt,
+ opt->diffopt.output_prefix_data);
+ fwrite(msg->buf, msg->len, 1, stdout);
+ }
putchar('\n');
}
}
diff --git a/log-tree.h b/log-tree.h
index 3f7b400..5c4cf7c 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -7,6 +7,7 @@ struct log_info {
struct commit *commit, *parent;
};
+int parse_decorate_color_config(const char *var, const int ofs, const char *value);
void init_log_tree_opt(struct rev_info *);
int log_tree_diff_flush(struct rev_info *);
int log_tree_commit(struct rev_info *, struct commit *);
diff --git a/mailmap.c b/mailmap.c
index b68c1fe..f80b701 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -69,7 +69,7 @@ static void add_mapping(struct string_list *map,
index = -1 - index;
} else {
/* create mailmap entry */
- struct string_list_item *item = string_list_insert_at_index(index, old_email, map);
+ struct string_list_item *item = string_list_insert_at_index(map, index, old_email);
item->util = xmalloc(sizeof(struct mailmap_entry));
memset(item->util, 0, sizeof(struct mailmap_entry));
((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1;
@@ -92,7 +92,7 @@ static void add_mapping(struct string_list *map,
mi->name = xstrdup(new_name);
if (new_email)
mi->email = xstrdup(new_email);
- string_list_insert(old_name, &me->namemap)->util = mi;
+ string_list_insert(&me->namemap, old_name)->util = mi;
}
debug_mm("mailmap: '%s' <%s> -> '%s' <%s>\n",
@@ -214,13 +214,13 @@ int map_user(struct string_list *map,
mailbuf[i] = 0;
debug_mm("map_user: map '%s' <%s>\n", name, mailbuf);
- item = string_list_lookup(mailbuf, map);
+ item = string_list_lookup(map, mailbuf);
if (item != NULL) {
me = (struct mailmap_entry *)item->util;
if (me->namemap.nr) {
/* The item has multiple items, so we'll look up on name too */
/* If the name is not found, we choose the simple entry */
- struct string_list_item *subitem = string_list_lookup(name, &me->namemap);
+ struct string_list_item *subitem = string_list_lookup(&me->namemap, name);
if (subitem)
item = subitem;
}
diff --git a/merge-recursive.c b/merge-recursive.c
index 206c103..856e98c 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -238,9 +238,9 @@ static int save_files_dirs(const unsigned char *sha1,
newpath[baselen + len] = '\0';
if (S_ISDIR(mode))
- string_list_insert(newpath, &o->current_directory_set);
+ string_list_insert(&o->current_directory_set, newpath);
else
- string_list_insert(newpath, &o->current_file_set);
+ string_list_insert(&o->current_file_set, newpath);
free(newpath);
return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
@@ -271,7 +271,7 @@ static struct stage_data *insert_stage_data(const char *path,
e->stages[2].sha, &e->stages[2].mode);
get_tree_entry(b->object.sha1, path,
e->stages[3].sha, &e->stages[3].mode);
- item = string_list_insert(path, entries);
+ item = string_list_insert(entries, path);
item->util = e;
return e;
}
@@ -294,9 +294,9 @@ static struct string_list *get_unmerged(void)
if (!ce_stage(ce))
continue;
- item = string_list_lookup(ce->name, unmerged);
+ item = string_list_lookup(unmerged, ce->name);
if (!item) {
- item = string_list_insert(ce->name, unmerged);
+ item = string_list_insert(unmerged, ce->name);
item->util = xcalloc(1, sizeof(struct stage_data));
}
e = item->util;
@@ -356,20 +356,20 @@ static struct string_list *get_renames(struct merge_options *o,
re = xmalloc(sizeof(*re));
re->processed = 0;
re->pair = pair;
- item = string_list_lookup(re->pair->one->path, entries);
+ item = string_list_lookup(entries, re->pair->one->path);
if (!item)
re->src_entry = insert_stage_data(re->pair->one->path,
o_tree, a_tree, b_tree, entries);
else
re->src_entry = item->util;
- item = string_list_lookup(re->pair->two->path, entries);
+ item = string_list_lookup(entries, re->pair->two->path);
if (!item)
re->dst_entry = insert_stage_data(re->pair->two->path,
o_tree, a_tree, b_tree, entries);
else
re->dst_entry = item->util;
- item = string_list_insert(pair->one->path, renames);
+ item = string_list_insert(renames, pair->one->path);
item->util = re;
}
opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -432,7 +432,7 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
lstat(newpath, &st) == 0)
sprintf(p, "_%d", suffix++);
- string_list_insert(newpath, &o->current_file_set);
+ string_list_insert(&o->current_file_set, newpath);
return newpath;
}
@@ -811,12 +811,12 @@ static int process_renames(struct merge_options *o,
for (i = 0; i < a_renames->nr; i++) {
sre = a_renames->items[i].util;
- string_list_insert(sre->pair->two->path, &a_by_dst)->util
+ string_list_insert(&a_by_dst, sre->pair->two->path)->util
= sre->dst_entry;
}
for (i = 0; i < b_renames->nr; i++) {
sre = b_renames->items[i].util;
- string_list_insert(sre->pair->two->path, &b_by_dst)->util
+ string_list_insert(&b_by_dst, sre->pair->two->path)->util
= sre->dst_entry;
}
@@ -988,7 +988,7 @@ static int process_renames(struct merge_options *o,
output(o, 1, "Adding as %s instead", new_path);
update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
}
- } else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
+ } else if ((item = string_list_lookup(renames2Dst, ren1_dst))) {
ren2 = item->util;
clean_merge = 0;
ren2->processed = 1;
diff --git a/merge-recursive.h b/merge-recursive.h
index d1192f5..b831293 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -10,7 +10,7 @@ struct merge_options {
enum {
MERGE_RECURSIVE_NORMAL = 0,
MERGE_RECURSIVE_OURS,
- MERGE_RECURSIVE_THEIRS,
+ MERGE_RECURSIVE_THEIRS
} recursive_variant;
const char *subtree_shift;
unsigned buffer_output : 1;
@@ -54,4 +54,7 @@ int merge_recursive_generic(struct merge_options *o,
void init_merge_options(struct merge_options *o);
struct tree *write_tree_from_memory(struct merge_options *o);
+/* builtin/merge.c */
+int try_merge_command(const char *strategy, struct commit_list *common, const char *head_arg, struct commit_list *remotes);
+
#endif
diff --git a/notes-cache.c b/notes-cache.c
new file mode 100644
index 0000000..dee6d62
--- /dev/null
+++ b/notes-cache.c
@@ -0,0 +1,94 @@
+#include "cache.h"
+#include "notes-cache.h"
+#include "commit.h"
+#include "refs.h"
+
+static int notes_cache_match_validity(const char *ref, const char *validity)
+{
+ unsigned char sha1[20];
+ struct commit *commit;
+ struct pretty_print_context pretty_ctx;
+ struct strbuf msg = STRBUF_INIT;
+ int ret;
+
+ if (read_ref(ref, sha1) < 0)
+ return 0;
+
+ commit = lookup_commit_reference_gently(sha1, 1);
+ if (!commit)
+ return 0;
+
+ memset(&pretty_ctx, 0, sizeof(pretty_ctx));
+ format_commit_message(commit, "%s", &msg, &pretty_ctx);
+ strbuf_trim(&msg);
+
+ ret = !strcmp(msg.buf, validity);
+ strbuf_release(&msg);
+
+ return ret;
+}
+
+void notes_cache_init(struct notes_cache *c, const char *name,
+ const char *validity)
+{
+ struct strbuf ref = STRBUF_INIT;
+ int flags = 0;
+
+ memset(c, 0, sizeof(*c));
+ c->validity = xstrdup(validity);
+
+ strbuf_addf(&ref, "refs/notes/%s", name);
+ if (!notes_cache_match_validity(ref.buf, validity))
+ flags = NOTES_INIT_EMPTY;
+ init_notes(&c->tree, ref.buf, combine_notes_overwrite, flags);
+ strbuf_release(&ref);
+}
+
+int notes_cache_write(struct notes_cache *c)
+{
+ unsigned char tree_sha1[20];
+ unsigned char commit_sha1[20];
+
+ if (!c || !c->tree.initialized || !c->tree.ref || !*c->tree.ref)
+ return -1;
+ if (!c->tree.dirty)
+ return 0;
+
+ if (write_notes_tree(&c->tree, tree_sha1))
+ return -1;
+ if (commit_tree(c->validity, tree_sha1, NULL, commit_sha1, NULL) < 0)
+ return -1;
+ if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
+ 0, QUIET_ON_ERR) < 0)
+ return -1;
+
+ return 0;
+}
+
+char *notes_cache_get(struct notes_cache *c, unsigned char key_sha1[20],
+ size_t *outsize)
+{
+ const unsigned char *value_sha1;
+ enum object_type type;
+ char *value;
+ unsigned long size;
+
+ value_sha1 = get_note(&c->tree, key_sha1);
+ if (!value_sha1)
+ return NULL;
+ value = read_sha1_file(value_sha1, &type, &size);
+
+ *outsize = size;
+ return value;
+}
+
+int notes_cache_put(struct notes_cache *c, unsigned char key_sha1[20],
+ const char *data, size_t size)
+{
+ unsigned char value_sha1[20];
+
+ if (write_sha1_file(data, size, "blob", value_sha1) < 0)
+ return -1;
+ add_note(&c->tree, key_sha1, value_sha1, NULL);
+ return 0;
+}
diff --git a/notes-cache.h b/notes-cache.h
new file mode 100644
index 0000000..356f88f
--- /dev/null
+++ b/notes-cache.h
@@ -0,0 +1,20 @@
+#ifndef NOTES_CACHE_H
+#define NOTES_CACHE_H
+
+#include "notes.h"
+
+struct notes_cache {
+ struct notes_tree tree;
+ char *validity;
+};
+
+void notes_cache_init(struct notes_cache *c, const char *name,
+ const char *validity);
+int notes_cache_write(struct notes_cache *c);
+
+char *notes_cache_get(struct notes_cache *c, unsigned char sha1[20], size_t
+ *outsize);
+int notes_cache_put(struct notes_cache *c, unsigned char sha1[20],
+ const char *data, size_t size);
+
+#endif /* NOTES_CACHE_H */
diff --git a/notes.c b/notes.c
index cc92cf3..1978244 100644
--- a/notes.c
+++ b/notes.c
@@ -838,7 +838,7 @@ static int string_list_add_one_ref(const char *path, const unsigned char *sha1,
{
struct string_list *refs = cb;
if (!unsorted_string_list_has_string(refs, path))
- string_list_append(path, refs);
+ string_list_append(refs, path);
return 0;
}
@@ -851,7 +851,7 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
if (get_sha1(glob, sha1))
warning("notes ref %s is invalid", glob);
if (!unsorted_string_list_has_string(list, glob))
- string_list_append(glob, list);
+ string_list_append(list, glob);
}
}
@@ -969,7 +969,7 @@ struct notes_tree **load_notes_trees(struct string_list *refs)
trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
cb_data.counter = 0;
cb_data.trees = trees;
- for_each_string_list(load_one_display_note_ref, refs, &cb_data);
+ for_each_string_list(refs, load_one_display_note_ref, &cb_data);
trees[cb_data.counter] = NULL;
return trees;
}
@@ -983,7 +983,7 @@ void init_display_notes(struct display_notes_opt *opt)
assert(!display_notes_trees);
if (!opt || !opt->suppress_default_notes) {
- string_list_append(default_notes_ref(), &display_notes_refs);
+ string_list_append(&display_notes_refs, default_notes_ref());
display_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT);
if (display_ref_env) {
string_list_add_refs_from_colon_sep(&display_notes_refs,
@@ -996,8 +996,8 @@ void init_display_notes(struct display_notes_opt *opt)
git_config(notes_display_config, &load_config_refs);
if (opt && opt->extra_notes_refs)
- for_each_string_list(string_list_add_refs_from_list,
- opt->extra_notes_refs,
+ for_each_string_list(opt->extra_notes_refs,
+ string_list_add_refs_from_list,
&display_notes_refs);
display_notes_trees = load_notes_trees(&display_notes_refs);
@@ -1083,7 +1083,7 @@ int write_notes_tree(struct notes_tree *t, unsigned char *result)
return ret;
}
-void prune_notes(struct notes_tree *t)
+void prune_notes(struct notes_tree *t, int flags)
{
struct note_delete_list *l = NULL;
@@ -1094,7 +1094,10 @@ void prune_notes(struct notes_tree *t)
for_each_note(t, 0, prune_notes_helper, &l);
while (l) {
- remove_note(t, l->sha1);
+ if (flags & NOTES_PRUNE_VERBOSE)
+ printf("%s\n", sha1_to_hex(l->sha1));
+ if (!(flags & NOTES_PRUNE_DRYRUN))
+ remove_note(t, l->sha1);
l = l->next;
}
}
diff --git a/notes.h b/notes.h
index 109bc8f..65fc3a6 100644
--- a/notes.h
+++ b/notes.h
@@ -171,6 +171,9 @@ int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,
*/
int write_notes_tree(struct notes_tree *t, unsigned char *result);
+/* Flags controlling the operation of prune */
+#define NOTES_PRUNE_VERBOSE 1
+#define NOTES_PRUNE_DRYRUN 2
/*
* Remove all notes annotating non-existing objects from the given notes tree
*
@@ -181,7 +184,7 @@ int write_notes_tree(struct notes_tree *t, unsigned char *result);
* structure are not persistent until a subsequent call to write_notes_tree()
* returns zero.
*/
-void prune_notes(struct notes_tree *t);
+void prune_notes(struct notes_tree *t, int flags);
/*
* Free (and de-initialize) the given notes_tree structure
diff --git a/parse-options.c b/parse-options.c
index 8546d85..0fa79bc 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -4,8 +4,9 @@
#include "commit.h"
#include "color.h"
-static int parse_options_usage(const char * const *usagestr,
- const struct option *opts);
+static int parse_options_usage(struct parse_opt_ctx_t *ctx,
+ const char * const *usagestr,
+ const struct option *opts, int err);
#define OPT_SHORT 1
#define OPT_UNSET 2
@@ -351,8 +352,9 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
}
-static int usage_with_options_internal(const char * const *,
- const struct option *, int);
+static int usage_with_options_internal(struct parse_opt_ctx_t *,
+ const char * const *,
+ const struct option *, int, int);
int parse_options_step(struct parse_opt_ctx_t *ctx,
const struct option *options,
@@ -380,10 +382,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
if (arg[1] != '-') {
ctx->opt = arg + 1;
if (internal_help && *ctx->opt == 'h')
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 0);
switch (parse_short_opt(ctx, options)) {
case -1:
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 1);
case -2:
goto unknown;
}
@@ -391,10 +393,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
check_typos(arg + 1, options);
while (ctx->opt) {
if (internal_help && *ctx->opt == 'h')
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 0);
switch (parse_short_opt(ctx, options)) {
case -1:
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 1);
case -2:
/* fake a short option thing to hide the fact that we may have
* started to parse aggregated stuff
@@ -418,12 +420,12 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
}
if (internal_help && !strcmp(arg + 2, "help-all"))
- return usage_with_options_internal(usagestr, options, 1);
+ return usage_with_options_internal(ctx, usagestr, options, 1, 0);
if (internal_help && !strcmp(arg + 2, "help"))
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 0);
switch (parse_long_opt(ctx, arg + 2, options)) {
case -1:
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 1);
case -2:
goto unknown;
}
@@ -468,7 +470,7 @@ int parse_options(int argc, const char **argv, const char *prefix,
return parse_options_end(&ctx);
}
-static int usage_argh(const struct option *opts)
+static int usage_argh(const struct option *opts, FILE *outfile)
{
const char *s;
int literal = (opts->flags & PARSE_OPT_LITERAL_ARGHELP) || !opts->argh;
@@ -479,72 +481,81 @@ static int usage_argh(const struct option *opts)
s = literal ? "[%s]" : "[<%s>]";
else
s = literal ? " %s" : " <%s>";
- return fprintf(stderr, s, opts->argh ? opts->argh : "...");
+ return fprintf(outfile, s, opts->argh ? opts->argh : "...");
}
#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP 2
-static int usage_with_options_internal(const char * const *usagestr,
- const struct option *opts, int full)
+static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
+ const char * const *usagestr,
+ const struct option *opts, int full, int err)
{
+ FILE *outfile = err ? stderr : stdout;
+
if (!usagestr)
return PARSE_OPT_HELP;
- fprintf(stderr, "usage: %s\n", *usagestr++);
+ if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
+ fprintf(outfile, "cat <<\\EOF\n");
+
+ fprintf(outfile, "usage: %s\n", *usagestr++);
while (*usagestr && **usagestr)
- fprintf(stderr, " or: %s\n", *usagestr++);
+ fprintf(outfile, " or: %s\n", *usagestr++);
while (*usagestr) {
- fprintf(stderr, "%s%s\n",
+ fprintf(outfile, "%s%s\n",
**usagestr ? " " : "",
*usagestr);
usagestr++;
}
if (opts->type != OPTION_GROUP)
- fputc('\n', stderr);
+ fputc('\n', outfile);
for (; opts->type != OPTION_END; opts++) {
size_t pos;
int pad;
if (opts->type == OPTION_GROUP) {
- fputc('\n', stderr);
+ fputc('\n', outfile);
if (*opts->help)
- fprintf(stderr, "%s\n", opts->help);
+ fprintf(outfile, "%s\n", opts->help);
continue;
}
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
continue;
- pos = fprintf(stderr, " ");
+ pos = fprintf(outfile, " ");
if (opts->short_name && !(opts->flags & PARSE_OPT_NEGHELP)) {
if (opts->flags & PARSE_OPT_NODASH)
- pos += fprintf(stderr, "%c", opts->short_name);
+ pos += fprintf(outfile, "%c", opts->short_name);
else
- pos += fprintf(stderr, "-%c", opts->short_name);
+ pos += fprintf(outfile, "-%c", opts->short_name);
}
if (opts->long_name && opts->short_name)
- pos += fprintf(stderr, ", ");
+ pos += fprintf(outfile, ", ");
if (opts->long_name)
- pos += fprintf(stderr, "--%s%s",
+ pos += fprintf(outfile, "--%s%s",
(opts->flags & PARSE_OPT_NEGHELP) ? "no-" : "",
opts->long_name);
if (opts->type == OPTION_NUMBER)
- pos += fprintf(stderr, "-NUM");
+ pos += fprintf(outfile, "-NUM");
if (!(opts->flags & PARSE_OPT_NOARG))
- pos += usage_argh(opts);
+ pos += usage_argh(opts, outfile);
if (pos <= USAGE_OPTS_WIDTH)
pad = USAGE_OPTS_WIDTH - pos;
else {
- fputc('\n', stderr);
+ fputc('\n', outfile);
pad = USAGE_OPTS_WIDTH;
}
- fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
+ fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
}
- fputc('\n', stderr);
+ fputc('\n', outfile);
+
+ if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
+ fputs("EOF\n", outfile);
return PARSE_OPT_HELP;
}
@@ -552,7 +563,7 @@ static int usage_with_options_internal(const char * const *usagestr,
void usage_with_options(const char * const *usagestr,
const struct option *opts)
{
- usage_with_options_internal(usagestr, opts, 0);
+ usage_with_options_internal(NULL, usagestr, opts, 0, 1);
exit(129);
}
@@ -564,10 +575,11 @@ void usage_msg_opt(const char *msg,
usage_with_options(usagestr, options);
}
-static int parse_options_usage(const char * const *usagestr,
- const struct option *opts)
+static int parse_options_usage(struct parse_opt_ctx_t *ctx,
+ const char * const *usagestr,
+ const struct option *opts, int err)
{
- return usage_with_options_internal(usagestr, opts, 0);
+ return usage_with_options_internal(ctx, usagestr, opts, 0, err);
}
diff --git a/parse-options.h b/parse-options.h
index 7581e93..7435cdb 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -25,7 +25,7 @@ enum parse_opt_flags {
PARSE_OPT_STOP_AT_NON_OPTION = 2,
PARSE_OPT_KEEP_ARGV0 = 4,
PARSE_OPT_KEEP_UNKNOWN = 8,
- PARSE_OPT_NO_INTERNAL_HELP = 16,
+ PARSE_OPT_NO_INTERNAL_HELP = 16
};
enum parse_opt_option_flags {
@@ -37,6 +37,7 @@ enum parse_opt_option_flags {
PARSE_OPT_NODASH = 32,
PARSE_OPT_LITERAL_ARGHELP = 64,
PARSE_OPT_NEGHELP = 128,
+ PARSE_OPT_SHELL_EVAL = 256
};
struct option;
@@ -160,7 +161,7 @@ extern NORETURN void usage_msg_opt(const char *msg,
enum {
PARSE_OPT_HELP = -1,
PARSE_OPT_DONE,
- PARSE_OPT_UNKNOWN,
+ PARSE_OPT_UNKNOWN
};
/*
diff --git a/pretty.c b/pretty.c
index 2777d30..f85444b 100644
--- a/pretty.c
+++ b/pretty.c
@@ -11,6 +11,17 @@
#include "reflog-walk.h"
static char *user_format;
+static struct cmt_fmt_map {
+ const char *name;
+ enum cmit_fmt format;
+ int is_tformat;
+ int is_alias;
+ const char *user_format;
+} *commit_formats;
+static size_t builtin_formats_len;
+static size_t commit_formats_len;
+static size_t commit_formats_alloc;
+static struct cmt_fmt_map *find_commit_format(const char *sought);
static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
{
@@ -21,22 +32,118 @@ static void save_user_format(struct rev_info *rev, const char *cp, int is_tforma
rev->commit_format = CMIT_FMT_USERFORMAT;
}
-void get_commit_format(const char *arg, struct rev_info *rev)
+static int git_pretty_formats_config(const char *var, const char *value, void *cb)
{
+ struct cmt_fmt_map *commit_format = NULL;
+ const char *name;
+ const char *fmt;
int i;
- static struct cmt_fmt_map {
- const char *n;
- size_t cmp_len;
- enum cmit_fmt v;
- } cmt_fmts[] = {
- { "raw", 1, CMIT_FMT_RAW },
- { "medium", 1, CMIT_FMT_MEDIUM },
- { "short", 1, CMIT_FMT_SHORT },
- { "email", 1, CMIT_FMT_EMAIL },
- { "full", 5, CMIT_FMT_FULL },
- { "fuller", 5, CMIT_FMT_FULLER },
- { "oneline", 1, CMIT_FMT_ONELINE },
+
+ if (prefixcmp(var, "pretty."))
+ return 0;
+
+ name = var + strlen("pretty.");
+ for (i = 0; i < builtin_formats_len; i++) {
+ if (!strcmp(commit_formats[i].name, name))
+ return 0;
+ }
+
+ for (i = builtin_formats_len; i < commit_formats_len; i++) {
+ if (!strcmp(commit_formats[i].name, name)) {
+ commit_format = &commit_formats[i];
+ break;
+ }
+ }
+
+ if (!commit_format) {
+ ALLOC_GROW(commit_formats, commit_formats_len+1,
+ commit_formats_alloc);
+ commit_format = &commit_formats[commit_formats_len];
+ memset(commit_format, 0, sizeof(*commit_format));
+ commit_formats_len++;
+ }
+
+ commit_format->name = xstrdup(name);
+ commit_format->format = CMIT_FMT_USERFORMAT;
+ git_config_string(&fmt, var, value);
+ if (!prefixcmp(fmt, "format:") || !prefixcmp(fmt, "tformat:")) {
+ commit_format->is_tformat = fmt[0] == 't';
+ fmt = strchr(fmt, ':') + 1;
+ } else if (strchr(fmt, '%'))
+ commit_format->is_tformat = 1;
+ else
+ commit_format->is_alias = 1;
+ commit_format->user_format = fmt;
+
+ return 0;
+}
+
+static void setup_commit_formats(void)
+{
+ struct cmt_fmt_map builtin_formats[] = {
+ { "raw", CMIT_FMT_RAW, 0 },
+ { "medium", CMIT_FMT_MEDIUM, 0 },
+ { "short", CMIT_FMT_SHORT, 0 },
+ { "email", CMIT_FMT_EMAIL, 0 },
+ { "fuller", CMIT_FMT_FULLER, 0 },
+ { "full", CMIT_FMT_FULL, 0 },
+ { "oneline", CMIT_FMT_ONELINE, 1 }
};
+ commit_formats_len = ARRAY_SIZE(builtin_formats);
+ builtin_formats_len = commit_formats_len;
+ ALLOC_GROW(commit_formats, commit_formats_len, commit_formats_alloc);
+ memcpy(commit_formats, builtin_formats,
+ sizeof(*builtin_formats)*ARRAY_SIZE(builtin_formats));
+
+ git_config(git_pretty_formats_config, NULL);
+}
+
+static struct cmt_fmt_map *find_commit_format_recursive(const char *sought,
+ const char *original,
+ int num_redirections)
+{
+ struct cmt_fmt_map *found = NULL;
+ size_t found_match_len = 0;
+ int i;
+
+ if (num_redirections >= commit_formats_len)
+ die("invalid --pretty format: "
+ "'%s' references an alias which points to itself",
+ original);
+
+ for (i = 0; i < commit_formats_len; i++) {
+ size_t match_len;
+
+ if (prefixcmp(commit_formats[i].name, sought))
+ continue;
+
+ match_len = strlen(commit_formats[i].name);
+ if (found == NULL || found_match_len > match_len) {
+ found = &commit_formats[i];
+ found_match_len = match_len;
+ }
+ }
+
+ if (found && found->is_alias) {
+ found = find_commit_format_recursive(found->user_format,
+ original,
+ num_redirections+1);
+ }
+
+ return found;
+}
+
+static struct cmt_fmt_map *find_commit_format(const char *sought)
+{
+ if (!commit_formats)
+ setup_commit_formats();
+
+ return find_commit_format_recursive(sought, sought, 0);
+}
+
+void get_commit_format(const char *arg, struct rev_info *rev)
+{
+ struct cmt_fmt_map *commit_format;
rev->use_terminator = 0;
if (!arg || !*arg) {
@@ -47,21 +154,22 @@ void get_commit_format(const char *arg, struct rev_info *rev)
save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
return;
}
- for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
- if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
- !strncmp(arg, cmt_fmts[i].n, strlen(arg))) {
- if (cmt_fmts[i].v == CMIT_FMT_ONELINE)
- rev->use_terminator = 1;
- rev->commit_format = cmt_fmts[i].v;
- return;
- }
- }
+
if (strchr(arg, '%')) {
save_user_format(rev, arg, 1);
return;
}
- die("invalid --pretty format: %s", arg);
+ commit_format = find_commit_format(arg);
+ if (!commit_format)
+ die("invalid --pretty format: %s", arg);
+
+ rev->commit_format = commit_format->format;
+ rev->use_terminator = commit_format->is_tformat;
+ if (commit_format->format == CMIT_FMT_USERFORMAT) {
+ save_user_format(rev, commit_format->user_format,
+ commit_format->is_tformat);
+ }
}
/*
@@ -801,6 +909,10 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
case 'e': /* encoding */
strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
return 1;
+ case 'B': /* raw body */
+ /* message_off is always left at the initial newline */
+ strbuf_addstr(sb, msg + c->message_off + 1);
+ return 1;
}
/* Now we need to parse the commit message. */
@@ -830,6 +942,7 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
NO_MAGIC,
ADD_LF_BEFORE_NON_EMPTY,
DEL_LF_BEFORE_EMPTY,
+ ADD_SP_BEFORE_NON_EMPTY
} magic = NO_MAGIC;
switch (placeholder[0]) {
@@ -839,6 +952,9 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
case '+':
magic = ADD_LF_BEFORE_NON_EMPTY;
break;
+ case ' ':
+ magic = ADD_SP_BEFORE_NON_EMPTY;
+ break;
default:
break;
}
@@ -853,8 +969,11 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
if ((orig_len == sb->len) && magic == DEL_LF_BEFORE_EMPTY) {
while (sb->len && sb->buf[sb->len - 1] == '\n')
strbuf_setlen(sb, sb->len - 1);
- } else if ((orig_len != sb->len) && magic == ADD_LF_BEFORE_NON_EMPTY) {
- strbuf_insert(sb, orig_len, "\n", 1);
+ } else if (orig_len != sb->len) {
+ if (magic == ADD_LF_BEFORE_NON_EMPTY)
+ strbuf_insert(sb, orig_len, "\n", 1);
+ else if (magic == ADD_SP_BEFORE_NON_EMPTY)
+ strbuf_insert(sb, orig_len, " ", 1);
}
return consumed + 1;
}
@@ -864,7 +983,7 @@ static size_t userformat_want_item(struct strbuf *sb, const char *placeholder,
{
struct userformat_want *w = context;
- if (*placeholder == '+' || *placeholder == '-')
+ if (*placeholder == '+' || *placeholder == '-' || *placeholder == ' ')
placeholder++;
switch (*placeholder) {
diff --git a/reflog-walk.c b/reflog-walk.c
index caba4f7..4879615 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -162,7 +162,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
} else
recno = 0;
- item = string_list_lookup(branch, &info->complete_reflogs);
+ item = string_list_lookup(&info->complete_reflogs, branch);
if (item)
reflogs = item->util;
else {
@@ -190,7 +190,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
}
if (!reflogs || reflogs->nr == 0)
return -1;
- string_list_insert(branch, &info->complete_reflogs)->util
+ string_list_insert(&info->complete_reflogs, branch)->util
= reflogs;
}
diff --git a/refs.c b/refs.c
index d3db15a..b540067 100644
--- a/refs.c
+++ b/refs.c
@@ -314,7 +314,11 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
{
- struct warn_if_dangling_data data = { fp, refname, msg_fmt };
+ struct warn_if_dangling_data data;
+
+ data.fp = fp;
+ data.refname = refname;
+ data.msg_fmt = msg_fmt;
for_each_rawref(warn_if_dangling_symref, &data);
}
@@ -1086,6 +1090,15 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
return ret;
}
+/*
+ * People using contrib's git-new-workdir have .git/logs/refs ->
+ * /some/other/path/.git/logs/refs, and that may live on another device.
+ *
+ * IOW, to avoid cross device rename errors, the temporary renamed log must
+ * live into logs/refs.
+ */
+#define TMP_RENAMED_LOG "logs/refs/.tmp-renamed-log"
+
int rename_ref(const char *oldref, const char *newref, const char *logmsg)
{
static const char renamed_ref[] = "RENAMED-REF";
@@ -1119,8 +1132,8 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
if (write_ref_sha1(lock, orig_sha1, logmsg))
return error("unable to save current sha1 in %s", renamed_ref);
- if (log && rename(git_path("logs/%s", oldref), git_path("tmp-renamed-log")))
- return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
+ if (log && rename(git_path("logs/%s", oldref), git_path(TMP_RENAMED_LOG)))
+ return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
oldref, strerror(errno));
if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
@@ -1146,7 +1159,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
}
retry:
- if (log && rename(git_path("tmp-renamed-log"), git_path("logs/%s", newref))) {
+ if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newref))) {
if (errno==EISDIR || errno==ENOTDIR) {
/*
* rename(a, b) when b is an existing
@@ -1159,7 +1172,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
}
goto retry;
} else {
- error("unable to move logfile tmp-renamed-log to logs/%s: %s",
+ error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
newref, strerror(errno));
goto rollback;
}
@@ -1199,8 +1212,8 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
error("unable to restore logfile %s from %s: %s",
oldref, newref, strerror(errno));
if (!logmoved && log &&
- rename(git_path("tmp-renamed-log"), git_path("logs/%s", oldref)))
- error("unable to restore logfile %s from tmp-renamed-log: %s",
+ rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldref)))
+ error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s",
oldref, strerror(errno));
return 1;
@@ -1258,52 +1271,65 @@ static int copy_msg(char *buf, const char *msg)
return cp - buf;
}
-static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
- const unsigned char *new_sha1, const char *msg)
+int log_ref_setup(const char *ref_name, char *logfile, int bufsize)
{
- int logfd, written, oflags = O_APPEND | O_WRONLY;
- unsigned maxlen, len;
- int msglen;
- char log_file[PATH_MAX];
- char *logrec;
- const char *committer;
-
- if (log_all_ref_updates < 0)
- log_all_ref_updates = !is_bare_repository();
-
- git_snpath(log_file, sizeof(log_file), "logs/%s", ref_name);
+ int logfd, oflags = O_APPEND | O_WRONLY;
+ git_snpath(logfile, bufsize, "logs/%s", ref_name);
if (log_all_ref_updates &&
(!prefixcmp(ref_name, "refs/heads/") ||
!prefixcmp(ref_name, "refs/remotes/") ||
!prefixcmp(ref_name, "refs/notes/") ||
!strcmp(ref_name, "HEAD"))) {
- if (safe_create_leading_directories(log_file) < 0)
+ if (safe_create_leading_directories(logfile) < 0)
return error("unable to create directory for %s",
- log_file);
+ logfile);
oflags |= O_CREAT;
}
- logfd = open(log_file, oflags, 0666);
+ logfd = open(logfile, oflags, 0666);
if (logfd < 0) {
if (!(oflags & O_CREAT) && errno == ENOENT)
return 0;
if ((oflags & O_CREAT) && errno == EISDIR) {
- if (remove_empty_directories(log_file)) {
+ if (remove_empty_directories(logfile)) {
return error("There are still logs under '%s'",
- log_file);
+ logfile);
}
- logfd = open(log_file, oflags, 0666);
+ logfd = open(logfile, oflags, 0666);
}
if (logfd < 0)
return error("Unable to append to %s: %s",
- log_file, strerror(errno));
+ logfile, strerror(errno));
}
- adjust_shared_perm(log_file);
+ adjust_shared_perm(logfile);
+ close(logfd);
+ return 0;
+}
+
+static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
+ const unsigned char *new_sha1, const char *msg)
+{
+ int logfd, result, written, oflags = O_APPEND | O_WRONLY;
+ unsigned maxlen, len;
+ int msglen;
+ char log_file[PATH_MAX];
+ char *logrec;
+ const char *committer;
+
+ if (log_all_ref_updates < 0)
+ log_all_ref_updates = !is_bare_repository();
+
+ result = log_ref_setup(ref_name, log_file, sizeof(log_file));
+ if (result)
+ return result;
+ logfd = open(log_file, oflags);
+ if (logfd < 0)
+ return 0;
msglen = msg ? strlen(msg) : 0;
committer = git_committer_info(0);
maxlen = strlen(committer) + msglen + 100;
diff --git a/refs.h b/refs.h
index 4a18b08..762ce50 100644
--- a/refs.h
+++ b/refs.h
@@ -68,6 +68,9 @@ extern void unlock_ref(struct ref_lock *lock);
/** Writes sha1 into the ref specified by the lock. **/
extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
+/** Setup reflog before using. **/
+int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
+
/** Reads log for the value of ref during at_time. **/
extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
diff --git a/remote.c b/remote.c
index 26ce560..afbba47 100644
--- a/remote.c
+++ b/remote.c
@@ -443,6 +443,8 @@ static int handle_config(const char *key, const char *value, void *cb)
} else if (!strcmp(subkey, ".tagopt")) {
if (!strcmp(value, "--no-tags"))
remote->fetch_tags = -1;
+ else if (!strcmp(value, "--tags"))
+ remote->fetch_tags = 2;
} else if (!strcmp(subkey, ".proxy")) {
return git_config_string((const char **)&remote->http_proxy,
key, value);
@@ -657,10 +659,9 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
int valid_fetch_refspec(const char *fetch_refspec_str)
{
- const char *fetch_refspec[] = { fetch_refspec_str };
struct refspec *refspec;
- refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
+ refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
free_refspecs(refspec, 1);
return !!refspec;
}
@@ -761,7 +762,7 @@ void ref_remove_duplicates(struct ref *ref_map)
if (!ref_map->peer_ref)
continue;
- item = string_list_lookup(ref_map->peer_ref->name, &refs);
+ item = string_list_lookup(&refs, ref_map->peer_ref->name);
if (item) {
if (strcmp(((struct ref *)item->util)->name,
ref_map->name))
@@ -776,7 +777,7 @@ void ref_remove_duplicates(struct ref *ref_map)
continue;
}
- item = string_list_insert(ref_map->peer_ref->name, &refs);
+ item = string_list_insert(&refs, ref_map->peer_ref->name);
item->util = ref_map;
}
string_list_clear(&refs, 0);
@@ -1709,7 +1710,7 @@ struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
info.ref_names = &ref_names;
info.stale_refs_tail = &stale_refs;
for (ref = fetch_map; ref; ref = ref->next)
- string_list_append(ref->name, &ref_names);
+ string_list_append(&ref_names, ref->name);
sort_string_list(&ref_names);
for_each_ref(get_stale_heads_cb, &info);
string_list_clear(&ref_names, 0);
diff --git a/remote.h b/remote.h
index 6e13643..888d7c1 100644
--- a/remote.h
+++ b/remote.h
@@ -145,7 +145,7 @@ int branch_merge_matches(struct branch *, int n, const char *);
enum match_refs_flags {
MATCH_REFS_NONE = 0,
MATCH_REFS_ALL = (1 << 0),
- MATCH_REFS_MIRROR = (1 << 1),
+ MATCH_REFS_MIRROR = (1 << 1)
};
/* Reporting of tracking info */
diff --git a/rerere.c b/rerere.c
index f221bed..d03a696 100644
--- a/rerere.c
+++ b/rerere.c
@@ -46,7 +46,7 @@ static void read_rr(struct string_list *rr)
; /* do nothing */
if (i == sizeof(buf))
die("filename too long");
- string_list_insert(buf, rr)->util = name;
+ string_list_insert(rr, buf)->util = name;
}
fclose(in);
}
@@ -153,7 +153,7 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
git_SHA_CTX ctx;
int hunk_no = 0;
enum {
- RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,
+ RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL
} hunk = RR_CONTEXT;
struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
struct strbuf buf = STRBUF_INIT;
@@ -354,7 +354,7 @@ static int find_conflict(struct string_list *conflict)
ce_same_name(e2, e3) &&
S_ISREG(e2->ce_mode) &&
S_ISREG(e3->ce_mode)) {
- string_list_insert((const char *)e2->name, conflict);
+ string_list_insert(conflict, (const char *)e2->name);
i++; /* skip over both #2 and #3 */
}
}
@@ -449,7 +449,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
if (ret < 1)
continue;
hex = xstrdup(sha1_to_hex(sha1));
- string_list_insert(path, rr)->util = hex;
+ string_list_insert(rr, path)->util = hex;
if (mkdir(git_path("rr-cache/%s", hex), 0755))
continue;
handle_file(path, NULL, rerere_path(hex, "preimage"));
@@ -471,7 +471,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
if (has_rerere_resolution(name)) {
if (!merge(name, path)) {
if (rerere_autoupdate)
- string_list_insert(path, &update);
+ string_list_insert(&update, path);
fprintf(stderr,
"%s '%s' using previous resolution.\n",
rerere_autoupdate
@@ -577,7 +577,7 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
fprintf(stderr, "Updated preimage for '%s'\n", path);
- string_list_insert(path, rr)->util = hex;
+ string_list_insert(rr, path)->util = hex;
fprintf(stderr, "Forgot resolution for %s\n", path);
return 0;
}
diff --git a/resolve-undo.c b/resolve-undo.c
index 0f50ee0..174ebec 100644
--- a/resolve-undo.c
+++ b/resolve-undo.c
@@ -20,7 +20,7 @@ void record_resolve_undo(struct index_state *istate, struct cache_entry *ce)
istate->resolve_undo = resolve_undo;
}
resolve_undo = istate->resolve_undo;
- lost = string_list_insert(ce->name, resolve_undo);
+ lost = string_list_insert(resolve_undo, ce->name);
if (!lost->util)
lost->util = xcalloc(1, sizeof(*ui));
ui = lost->util;
@@ -50,7 +50,7 @@ static int write_one(struct string_list_item *item, void *cbdata)
void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)
{
- for_each_string_list(write_one, resolve_undo, sb);
+ for_each_string_list(resolve_undo, write_one, sb);
}
struct string_list *resolve_undo_read(const char *data, unsigned long size)
@@ -70,7 +70,7 @@ struct string_list *resolve_undo_read(const char *data, unsigned long size)
len = strlen(data) + 1;
if (size <= len)
goto error;
- lost = string_list_insert(data, resolve_undo);
+ lost = string_list_insert(resolve_undo, data);
if (!lost->util)
lost->util = xcalloc(1, sizeof(*ui));
ui = lost->util;
@@ -135,7 +135,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
pos++;
return pos - 1; /* return the last entry processed */
}
- item = string_list_lookup(ce->name, istate->resolve_undo);
+ item = string_list_lookup(istate->resolve_undo, ce->name);
if (!item)
return pos;
ru = item->util;
diff --git a/revision.c b/revision.c
index f4b8b38..7e82efd 100644
--- a/revision.c
+++ b/revision.c
@@ -646,6 +646,93 @@ static int still_interesting(struct commit_list *src, unsigned long date, int sl
return slop-1;
}
+/*
+ * "rev-list --ancestry-path A..B" computes commits that are ancestors
+ * of B but not ancestors of A but further limits the result to those
+ * that are descendants of A. This takes the list of bottom commits and
+ * the result of "A..B" without --ancestry-path, and limits the latter
+ * further to the ones that can reach one of the commits in "bottom".
+ */
+static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list)
+{
+ struct commit_list *p;
+ struct commit_list *rlist = NULL;
+ int made_progress;
+
+ /*
+ * Reverse the list so that it will be likely that we would
+ * process parents before children.
+ */
+ for (p = list; p; p = p->next)
+ commit_list_insert(p->item, &rlist);
+
+ for (p = bottom; p; p = p->next)
+ p->item->object.flags |= TMP_MARK;
+
+ /*
+ * Mark the ones that can reach bottom commits in "list",
+ * in a bottom-up fashion.
+ */
+ do {
+ made_progress = 0;
+ for (p = rlist; p; p = p->next) {
+ struct commit *c = p->item;
+ struct commit_list *parents;
+ if (c->object.flags & (TMP_MARK | UNINTERESTING))
+ continue;
+ for (parents = c->parents;
+ parents;
+ parents = parents->next) {
+ if (!(parents->item->object.flags & TMP_MARK))
+ continue;
+ c->object.flags |= TMP_MARK;
+ made_progress = 1;
+ break;
+ }
+ }
+ } while (made_progress);
+
+ /*
+ * NEEDSWORK: decide if we want to remove parents that are
+ * not marked with TMP_MARK from commit->parents for commits
+ * in the resulting list. We may not want to do that, though.
+ */
+
+ /*
+ * The ones that are not marked with TMP_MARK are uninteresting
+ */
+ for (p = list; p; p = p->next) {
+ struct commit *c = p->item;
+ if (c->object.flags & TMP_MARK)
+ continue;
+ c->object.flags |= UNINTERESTING;
+ }
+
+ /* We are done with the TMP_MARK */
+ for (p = list; p; p = p->next)
+ p->item->object.flags &= ~TMP_MARK;
+ for (p = bottom; p; p = p->next)
+ p->item->object.flags &= ~TMP_MARK;
+ free_commit_list(rlist);
+}
+
+/*
+ * Before walking the history, keep the set of "negative" refs the
+ * caller has asked to exclude.
+ *
+ * This is used to compute "rev-list --ancestry-path A..B", as we need
+ * to filter the result of "A..B" further to the ones that can actually
+ * reach A.
+ */
+static struct commit_list *collect_bottom_commits(struct commit_list *list)
+{
+ struct commit_list *elem, *bottom = NULL;
+ for (elem = list; elem; elem = elem->next)
+ if (elem->item->object.flags & UNINTERESTING)
+ commit_list_insert(elem->item, &bottom);
+ return bottom;
+}
+
static int limit_list(struct rev_info *revs)
{
int slop = SLOP;
@@ -653,6 +740,13 @@ static int limit_list(struct rev_info *revs)
struct commit_list *list = revs->commits;
struct commit_list *newlist = NULL;
struct commit_list **p = &newlist;
+ struct commit_list *bottom = NULL;
+
+ if (revs->ancestry_path) {
+ bottom = collect_bottom_commits(list);
+ if (!bottom)
+ die("--ancestry-path given but there are no bottom commits");
+ }
while (list) {
struct commit_list *entry = list;
@@ -694,6 +788,11 @@ static int limit_list(struct rev_info *revs)
if (revs->cherry_pick)
cherry_pick_list(newlist, revs);
+ if (bottom) {
+ limit_to_ancestry(bottom, newlist);
+ free_commit_list(bottom);
+ }
+
revs->commits = newlist;
return 0;
}
@@ -1063,18 +1162,22 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
if (!prefixcmp(arg, "--max-count=")) {
revs->max_count = atoi(arg + 12);
+ revs->no_walk = 0;
} else if (!prefixcmp(arg, "--skip=")) {
revs->skip_count = atoi(arg + 7);
} else if ((*arg == '-') && isdigit(arg[1])) {
/* accept -<digit>, like traditional "head" */
revs->max_count = atoi(arg + 1);
+ revs->no_walk = 0;
} else if (!strcmp(arg, "-n")) {
if (argc <= 1)
return error("-n requires an argument");
revs->max_count = atoi(argv[1]);
+ revs->no_walk = 0;
return 2;
} else if (!prefixcmp(arg, "-n")) {
revs->max_count = atoi(arg + 2);
+ revs->no_walk = 0;
} else if (!prefixcmp(arg, "--max-age=")) {
revs->max_age = atoi(arg + 10);
} else if (!prefixcmp(arg, "--since=")) {
@@ -1089,6 +1192,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->min_age = approxidate(arg + 8);
} else if (!strcmp(arg, "--first-parent")) {
revs->first_parent_only = 1;
+ } else if (!strcmp(arg, "--ancestry-path")) {
+ revs->ancestry_path = 1;
+ revs->simplify_history = 0;
+ revs->limited = 1;
} else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
init_reflog_walk(&revs->reflog_info);
} else if (!strcmp(arg, "--default")) {
@@ -1146,6 +1253,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->boundary = 1;
} else if (!strcmp(arg, "--left-right")) {
revs->left_right = 1;
+ } else if (!strcmp(arg, "--count")) {
+ revs->count = 1;
} else if (!strcmp(arg, "--cherry-pick")) {
revs->cherry_pick = 1;
revs->limited = 1;
@@ -1205,8 +1314,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
else
strbuf_addstr(&buf, "refs/notes/");
strbuf_addstr(&buf, arg+13);
- string_list_append(strbuf_detach(&buf, NULL),
- revs->notes_opt.extra_notes_refs);
+ string_list_append(revs->notes_opt.extra_notes_refs,
+ strbuf_detach(&buf, NULL));
} else if (!strcmp(arg, "--no-notes")) {
revs->show_notes = 0;
revs->show_notes_given = 1;
@@ -1781,7 +1890,7 @@ int prepare_revision_walk(struct rev_info *revs)
enum rewrite_result {
rewrite_one_ok,
rewrite_one_noparents,
- rewrite_one_error,
+ rewrite_one_error
};
static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
diff --git a/revision.h b/revision.h
index 568f1c9..36fdf22 100644
--- a/revision.h
+++ b/revision.h
@@ -57,6 +57,7 @@ struct rev_info {
limited:1,
unpacked:1,
boundary:2,
+ count:1,
left_right:1,
rewrite_parents:1,
print_parents:1,
@@ -66,6 +67,7 @@ struct rev_info {
reverse_output_stage:1,
cherry_pick:1,
bisect:1,
+ ancestry_path:1,
first_parent_only:1;
/* Diff flags */
@@ -131,6 +133,10 @@ struct rev_info {
/* notes-specific options: which refs to show */
struct display_notes_opt notes_opt;
+
+ /* commit counts */
+ int count_left;
+ int count_right;
};
#define REV_TREE_SAME 0
diff --git a/run-command.c b/run-command.c
index c7793f5..2a1041e 100644
--- a/run-command.c
+++ b/run-command.c
@@ -84,6 +84,7 @@ static NORETURN void die_child(const char *err, va_list params)
unused = write(child_err, "\n", 1);
exit(128);
}
+#endif
static inline void set_cloexec(int fd)
{
@@ -91,7 +92,6 @@ static inline void set_cloexec(int fd)
if (flags >= 0)
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
-#endif
static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
{
@@ -449,11 +449,35 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
return run_command(&cmd);
}
-#ifdef WIN32
-static unsigned __stdcall run_thread(void *data)
+#ifndef NO_PTHREADS
+static pthread_t main_thread;
+static int main_thread_set;
+static pthread_key_t async_key;
+
+static void *run_thread(void *data)
{
struct async *async = data;
- return async->proc(async->proc_in, async->proc_out, async->data);
+ intptr_t ret;
+
+ pthread_setspecific(async_key, async);
+ ret = async->proc(async->proc_in, async->proc_out, async->data);
+ return (void *)ret;
+}
+
+static NORETURN void die_async(const char *err, va_list params)
+{
+ vreportf("fatal: ", err, params);
+
+ if (!pthread_equal(main_thread, pthread_self())) {
+ struct async *async = pthread_getspecific(async_key);
+ if (async->proc_in >= 0)
+ close(async->proc_in);
+ if (async->proc_out >= 0)
+ close(async->proc_out);
+ pthread_exit((void *)128);
+ }
+
+ exit(128);
}
#endif
@@ -499,7 +523,7 @@ int start_async(struct async *async)
else
proc_out = -1;
-#ifndef WIN32
+#ifdef NO_PTHREADS
/* Flush stdio before fork() to avoid cloning buffers */
fflush(NULL);
@@ -526,12 +550,29 @@ int start_async(struct async *async)
else if (async->out)
close(async->out);
#else
+ if (!main_thread_set) {
+ /*
+ * We assume that the first time that start_async is called
+ * it is from the main thread.
+ */
+ main_thread_set = 1;
+ main_thread = pthread_self();
+ pthread_key_create(&async_key, NULL);
+ set_die_routine(die_async);
+ }
+
+ if (proc_in >= 0)
+ set_cloexec(proc_in);
+ if (proc_out >= 0)
+ set_cloexec(proc_out);
async->proc_in = proc_in;
async->proc_out = proc_out;
- async->tid = (HANDLE) _beginthreadex(NULL, 0, run_thread, async, 0, NULL);
- if (!async->tid) {
- error("cannot create thread: %s", strerror(errno));
- goto error;
+ {
+ int err = pthread_create(&async->tid, NULL, run_thread, async);
+ if (err) {
+ error("cannot create thread: %s", strerror(err));
+ goto error;
+ }
}
#endif
return 0;
@@ -551,17 +592,15 @@ error:
int finish_async(struct async *async)
{
-#ifndef WIN32
- int ret = wait_or_whine(async->pid, "child process", 0);
+#ifdef NO_PTHREADS
+ return wait_or_whine(async->pid, "child process", 0);
#else
- DWORD ret = 0;
- if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
- ret = error("waiting for thread failed: %lu", GetLastError());
- else if (!GetExitCodeThread(async->tid, &ret))
- ret = error("cannot get thread exit code: %lu", GetLastError());
- CloseHandle(async->tid);
+ void *ret = (void *)(intptr_t)(-1);
+
+ if (pthread_join(async->tid, &ret))
+ error("pthread_join failed");
+ return (int)(intptr_t)ret;
#endif
- return ret;
}
int run_hook(const char *index_file, const char *name, ...)
diff --git a/run-command.h b/run-command.h
index 94619f5..56491b9 100644
--- a/run-command.h
+++ b/run-command.h
@@ -1,6 +1,10 @@
#ifndef RUN_COMMAND_H
#define RUN_COMMAND_H
+#ifndef NO_PTHREADS
+#include <pthread.h>
+#endif
+
struct child_process {
const char **argv;
pid_t pid;
@@ -74,10 +78,10 @@ struct async {
void *data;
int in; /* caller writes here and closes it */
int out; /* caller reads from here and closes it */
-#ifndef WIN32
+#ifdef NO_PTHREADS
pid_t pid;
#else
- HANDLE tid;
+ pthread_t tid;
int proc_in;
int proc_out;
#endif
diff --git a/setup.c b/setup.c
index 0e4cfe6..87c21f0 100644
--- a/setup.c
+++ b/setup.c
@@ -323,6 +323,9 @@ const char *setup_git_directory_gently(int *nongit_ok)
const char *gitdirenv;
const char *gitfile_dir;
int len, offset, ceil_offset, root_len;
+ dev_t current_device = 0;
+ int one_filesystem = 1;
+ struct stat buf;
/*
* Let's assume that we are in a git repository.
@@ -390,6 +393,12 @@ const char *setup_git_directory_gently(int *nongit_ok)
* etc.
*/
offset = len = strlen(cwd);
+ one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
+ if (one_filesystem) {
+ if (stat(".", &buf))
+ die_errno("failed to stat '.'");
+ current_device = buf.st_dev;
+ }
for (;;) {
gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
if (gitfile_dir) {
@@ -422,8 +431,27 @@ const char *setup_git_directory_gently(int *nongit_ok)
}
die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
}
- if (chdir(".."))
+ if (one_filesystem) {
+ if (stat("..", &buf)) {
+ cwd[offset] = '\0';
+ die_errno("failed to stat '%s/..'", cwd);
+ }
+ if (buf.st_dev != current_device) {
+ if (nongit_ok) {
+ if (chdir(cwd))
+ die_errno("Cannot come back to cwd");
+ *nongit_ok = 1;
+ return NULL;
+ }
+ cwd[offset] = '\0';
+ die("Not a git repository (or any parent up to mount parent %s)\n"
+ "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
+ }
+ }
+ if (chdir("..")) {
+ cwd[offset] = '\0';
die_errno("Cannot change to '%s/..'", cwd);
+ }
}
inside_git_dir = 0;
diff --git a/sha1_file.c b/sha1_file.c
index 72de389..e42ef96 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -2525,3 +2525,13 @@ int read_pack_header(int fd, struct pack_header *header)
return PH_ERROR_PROTOCOL;
return 0;
}
+
+void assert_sha1_type(const unsigned char *sha1, enum object_type expect)
+{
+ enum object_type type = sha1_object_info(sha1, NULL);
+ if (type < 0)
+ die("%s is not a valid object", sha1_to_hex(sha1));
+ if (type != expect)
+ die("%s is not a valid '%s' object", sha1_to_hex(sha1),
+ typename(expect));
+}
diff --git a/sha1_name.c b/sha1_name.c
index bf92417..4f2af8d 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -679,8 +679,8 @@ static int handle_one_ref(const char *path,
/*
* This interprets names like ':/Initial revision of "git"' by searching
- * through history and returning the first commit whose message starts
- * with the given string.
+ * through history and returning the first commit whose message matches
+ * the given regular expression.
*
* For future extension, ':/!' is reserved. If you want to match a message
* beginning with a '!', you have to repeat the exclamation mark.
@@ -692,12 +692,17 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
struct commit_list *list = NULL, *backup = NULL, *l;
int retval = -1;
char *temp_commit_buffer = NULL;
+ regex_t regex;
if (prefix[0] == '!') {
if (prefix[1] != '!')
die ("Invalid search pattern: %s", prefix);
prefix++;
}
+
+ if (regcomp(&regex, prefix, REG_EXTENDED))
+ die("Invalid search pattern: %s", prefix);
+
for_each_ref(handle_one_ref, &list);
for (l = list; l; l = l->next)
commit_list_insert(l->item, &backup);
@@ -721,12 +726,13 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
}
if (!(p = strstr(p, "\n\n")))
continue;
- if (!prefixcmp(p + 2, prefix)) {
+ if (!regexec(&regex, p + 2, 0, NULL, 0)) {
hashcpy(sha1, commit->object.sha1);
retval = 0;
break;
}
}
+ regfree(&regex);
free(temp_commit_buffer);
free_commit_list(list);
for (l = backup; l; l = l->next)
@@ -933,8 +939,8 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
*/
int get_sha1(const char *name, unsigned char *sha1)
{
- unsigned unused;
- return get_sha1_with_mode(name, sha1, &unused);
+ struct object_context unused;
+ return get_sha1_with_context(name, sha1, &unused);
}
/* Must be called only when object_name:filename doesn't exist. */
@@ -1032,11 +1038,23 @@ static void diagnose_invalid_index_path(int stage,
int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode, int gently, const char *prefix)
{
+ struct object_context oc;
+ int ret;
+ ret = get_sha1_with_context_1(name, sha1, &oc, gently, prefix);
+ *mode = oc.mode;
+ return ret;
+}
+
+int get_sha1_with_context_1(const char *name, unsigned char *sha1,
+ struct object_context *oc,
+ int gently, const char *prefix)
+{
int ret, bracket_depth;
int namelen = strlen(name);
const char *cp;
- *mode = S_IFINVALID;
+ memset(oc, 0, sizeof(*oc));
+ oc->mode = S_IFINVALID;
ret = get_sha1_1(name, namelen, sha1);
if (!ret)
return ret;
@@ -1059,6 +1077,11 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
cp = name + 3;
}
namelen = namelen - (cp - name);
+
+ strncpy(oc->path, cp,
+ sizeof(oc->path));
+ oc->path[sizeof(oc->path)-1] = '\0';
+
if (!active_cache)
read_cache();
pos = cache_name_pos(cp, namelen);
@@ -1071,7 +1094,6 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
break;
if (ce_stage(ce) == stage) {
hashcpy(sha1, ce->sha1);
- *mode = ce->ce_mode;
return 0;
}
pos++;
@@ -1098,12 +1120,17 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
}
if (!get_sha1_1(name, cp-name, tree_sha1)) {
const char *filename = cp+1;
- ret = get_tree_entry(tree_sha1, filename, sha1, mode);
+ ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
if (!gently) {
diagnose_invalid_sha1_path(prefix, filename,
tree_sha1, object_name);
free(object_name);
}
+ hashcpy(oc->tree, tree_sha1);
+ strncpy(oc->path, filename,
+ sizeof(oc->path));
+ oc->path[sizeof(oc->path)-1] = '\0';
+
return ret;
} else {
if (!gently)
diff --git a/string-list.c b/string-list.c
index c9ad7fc..9b023a2 100644
--- a/string-list.c
+++ b/string-list.c
@@ -51,13 +51,13 @@ static int add_entry(int insert_at, struct string_list *list, const char *string
return index;
}
-struct string_list_item *string_list_insert(const char *string, struct string_list *list)
+struct string_list_item *string_list_insert(struct string_list *list, const char *string)
{
- return string_list_insert_at_index(-1, string, list);
+ return string_list_insert_at_index(list, -1, string);
}
-struct string_list_item *string_list_insert_at_index(int insert_at,
- const char *string, struct string_list *list)
+struct string_list_item *string_list_insert_at_index(struct string_list *list,
+ int insert_at, const char *string)
{
int index = add_entry(insert_at, list, string);
@@ -84,7 +84,7 @@ int string_list_find_insert_index(const struct string_list *list, const char *st
return index;
}
-struct string_list_item *string_list_lookup(const char *string, struct string_list *list)
+struct string_list_item *string_list_lookup(struct string_list *list, const char *string)
{
int exact_match, i = get_entry_index(list, string, &exact_match);
if (!exact_match)
@@ -92,8 +92,8 @@ 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 for_each_string_list(struct string_list *list,
+ string_list_each_func_t fn, void *cb_data)
{
int i, ret = 0;
for (i = 0; i < list->nr; i++)
@@ -139,7 +139,7 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c
}
-void print_string_list(const char *text, const struct string_list *p)
+void print_string_list(const struct string_list *p, const char *text)
{
int i;
if ( text )
@@ -148,7 +148,7 @@ void print_string_list(const char *text, const struct string_list *p)
printf("%s:%p\n", p->items[i].string, p->items[i].util);
}
-struct string_list_item *string_list_append(const char *string, struct string_list *list)
+struct string_list_item *string_list_append(struct string_list *list, const char *string)
{
ALLOC_GROW(list->items, list->nr + 1, list->alloc);
list->items[list->nr].string =
diff --git a/string-list.h b/string-list.h
index 63b69c8..680d600 100644
--- a/string-list.h
+++ b/string-list.h
@@ -12,7 +12,7 @@ struct string_list
unsigned int strdup_strings:1;
};
-void print_string_list(const char *text, const struct string_list *p);
+void print_string_list(const struct string_list *p, const char *text);
void string_list_clear(struct string_list *list, int free_util);
/* Use this function to call a custom clear function on each util pointer */
@@ -22,20 +22,20 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c
/* 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);
+int for_each_string_list(struct string_list *list,
+ string_list_each_func_t, 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,
int negative_existing_index);
-struct string_list_item *string_list_insert(const char *string, struct string_list *list);
-struct string_list_item *string_list_insert_at_index(int insert_at,
- const char *string, struct string_list *list);
-struct string_list_item *string_list_lookup(const char *string, struct string_list *list);
+struct string_list_item *string_list_insert(struct string_list *list, const char *string);
+struct string_list_item *string_list_insert_at_index(struct string_list *list,
+ int insert_at, const char *string);
+struct string_list_item *string_list_lookup(struct string_list *list, const char *string);
/* Use these functions only on unsorted lists: */
-struct string_list_item *string_list_append(const char *string, struct string_list *list);
+struct string_list_item *string_list_append(struct string_list *list, const char *string);
void sort_string_list(struct string_list *list);
int unsorted_string_list_has_string(struct string_list *list, const char *string);
struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
diff --git a/submodule.c b/submodule.c
index 676d48f..61cb6e2 100644
--- a/submodule.c
+++ b/submodule.c
@@ -46,6 +46,19 @@ done:
return ret;
}
+void handle_ignore_submodules_arg(struct diff_options *diffopt,
+ const char *arg)
+{
+ if (!strcmp(arg, "all"))
+ DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
+ else if (!strcmp(arg, "untracked"))
+ DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+ else if (!strcmp(arg, "dirty"))
+ DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
+ else
+ die("bad --ignore-submodules argument: %s", arg);
+}
+
void show_submodule_summary(FILE *f, const char *path,
unsigned char one[20], unsigned char two[20],
unsigned dirty_submodule,
diff --git a/submodule.h b/submodule.h
index dbda270..6fd3bb4 100644
--- a/submodule.h
+++ b/submodule.h
@@ -1,6 +1,9 @@
#ifndef SUBMODULE_H
#define SUBMODULE_H
+struct diff_options;
+
+void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
void show_submodule_summary(FILE *f, const char *path,
unsigned char one[20], unsigned char two[20],
unsigned dirty_submodule,
diff --git a/t/Makefile b/t/Makefile
index 25c559b..f9de24b 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -3,6 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
+-include ../config.mak.autogen
-include ../config.mak
#GIT_TEST_OPTS=--verbose --debug
@@ -35,7 +36,9 @@ aggregate-results-and-cleanup: $(T)
$(MAKE) clean
aggregate-results:
- '$(SHELL_PATH_SQ)' ./aggregate-results.sh test-results/t*-*
+ for f in test-results/t*-*; do \
+ echo "$$f"; \
+ done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
full-svn-test:
diff --git a/t/README b/t/README
index 0e4e8d8..b906ceb 100644
--- a/t/README
+++ b/t/README
@@ -18,25 +18,48 @@ The easiest way to run tests is to say "make". This runs all
the tests.
*** t0000-basic.sh ***
- * ok 1: .git/objects should be empty after git-init in an empty repo.
- * ok 2: .git/objects should have 256 subdirectories.
- * ok 3: git-update-index without --add should fail adding.
+ ok 1 - .git/objects should be empty after git init in an empty repo.
+ ok 2 - .git/objects should have 3 subdirectories.
+ ok 3 - success is reported like this
...
- * ok 23: no diff after checkout and git-update-index --refresh.
- * passed all 23 test(s)
- *** t0100-environment-names.sh ***
- * ok 1: using old names should issue warnings.
- * ok 2: using old names but having new names should not issue warnings.
- ...
-
-Or you can run each test individually from command line, like
-this:
-
- $ sh ./t3001-ls-files-killed.sh
- * ok 1: git-update-index --add to add various paths.
- * ok 2: git-ls-files -k to show killed files.
- * ok 3: validate git-ls-files -k output.
- * passed all 3 test(s)
+ ok 43 - very long name in the index handled sanely
+ # fixed 1 known breakage(s)
+ # still have 1 known breakage(s)
+ # passed all remaining 42 test(s)
+ 1..43
+ *** t0001-init.sh ***
+ ok 1 - plain
+ ok 2 - plain with GIT_WORK_TREE
+ ok 3 - plain bare
+
+Since the tests all output TAP (see http://testanything.org) they can
+be run with any TAP harness. Here's an example of parallel testing
+powered by a recent version of prove(1):
+
+ $ prove --timer --jobs 15 ./t[0-9]*.sh
+ [19:17:33] ./t0005-signals.sh ................................... ok 36 ms
+ [19:17:33] ./t0022-crlf-rename.sh ............................... ok 69 ms
+ [19:17:33] ./t0024-crlf-archive.sh .............................. ok 154 ms
+ [19:17:33] ./t0004-unwritable.sh ................................ ok 289 ms
+ [19:17:33] ./t0002-gitfile.sh ................................... ok 480 ms
+ ===( 102;0 25/? 6/? 5/? 16/? 1/? 4/? 2/? 1/? 3/? 1... )===
+
+prove and other harnesses come with a lot of useful options. The
+--state option in particular is very useful:
+
+ # Repeat until no more failures
+ $ prove -j 15 --state=failed,save ./t[0-9]*.sh
+
+You can also run each test individually from command line, like this:
+
+ $ sh ./t3010-ls-files-killed-modified.sh
+ ok 1 - git update-index --add to add various paths.
+ ok 2 - git ls-files -k to show killed files.
+ ok 3 - validate git ls-files -k output.
+ ok 4 - git ls-files -m to show modified files.
+ ok 5 - validate git ls-files -m output.
+ # passed all 5 test(s)
+ 1..5
You can pass --verbose (or -v), --debug (or -d), and --immediate
(or -i) command line argument to the test, or by setting GIT_TEST_OPTS
@@ -198,15 +221,101 @@ This test harness library does the following things:
- If the script is invoked with command line argument --help
(or -h), it shows the test_description and exits.
- - Creates an empty test directory with an empty .git/objects
- database and chdir(2) into it. This directory is 't/trash directory'
- if you must know, but I do not think you care.
+ - Creates an empty test directory with an empty .git/objects database
+ and chdir(2) into it. This directory is 't/trash
+ directory.$test_name_without_dotsh', with t/ subject to change by
+ the --root option documented above.
- Defines standard test helper functions for your scripts to
use. These functions are designed to make all scripts behave
consistently when command line arguments --verbose (or -v),
--debug (or -d), and --immediate (or -i) is given.
+Do's, don'ts & things to keep in mind
+-------------------------------------
+
+Here are a few examples of things you probably should and shouldn't do
+when writing tests.
+
+Do:
+
+ - Put all code inside test_expect_success and other assertions.
+
+ Even code that isn't a test per se, but merely some setup code
+ should be inside a test assertion.
+
+ - Chain your test assertions
+
+ Write test code like this:
+
+ git merge foo &&
+ git push bar &&
+ test ...
+
+ Instead of:
+
+ git merge hla
+ git push gh
+ test ...
+
+ That way all of the commands in your tests will succeed or fail. If
+ you must ignore the return value of something (e.g. the return
+ value of export is unportable) it's best to indicate so explicitly
+ with a semicolon:
+
+ export HLAGH;
+ git merge hla &&
+ git push gh &&
+ test ...
+
+Don't:
+
+ - exit() within a <script> part.
+
+ The harness will catch this as a programming error of the test.
+ Use test_done instead if you need to stop the tests early (see
+ "Skipping tests" below).
+
+ - Break the TAP output
+
+ The raw output from your test may be interpreted by a TAP harness. TAP
+ harnesses will ignore everything they don't know about, but don't step
+ on their toes in these areas:
+
+ - Don't print lines like "$x..$y" where $x and $y are integers.
+
+ - Don't print lines that begin with "ok" or "not ok".
+
+ TAP harnesses expect a line that begins with either "ok" and "not
+ ok" to signal a test passed or failed (and our harness already
+ produces such lines), so your script shouldn't emit such lines to
+ their output.
+
+ You can glean some further possible issues from the TAP grammar
+ (see http://search.cpan.org/perldoc?TAP::Parser::Grammar#TAP_Grammar)
+ but the best indication is to just run the tests with prove(1),
+ it'll complain if anything is amiss.
+
+Keep in mind:
+
+ - Inside <script> part, the standard output and standard error
+ streams are discarded, and the test harness only reports "ok" or
+ "not ok" to the end user running the tests. Under --verbose, they
+ are shown to help debugging the tests.
+
+
+Skipping tests
+--------------
+
+If you need to skip all the remaining tests you should set skip_all
+and immediately call test_done. The string you give to skip_all will
+be used as an explanation for why the test was skipped. for instance:
+
+ if ! test_have_prereq PERL
+ then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+ fi
End with test_done
------------------
@@ -222,9 +331,9 @@ Test harness library
There are a handful helper functions defined in the test harness
library for your script to use.
- - test_expect_success <message> <script>
+ - test_expect_success [<prereq>] <message> <script>
- This takes two strings as parameter, and evaluates the
+ Usually takes two strings as parameter, and evaluates the
<script>. If it yields success, test is considered
successful. <message> should state what it is testing.
@@ -234,7 +343,14 @@ library for your script to use.
'git-write-tree should be able to write an empty tree.' \
'tree=$(git-write-tree)'
- - test_expect_failure <message> <script>
+ If you supply three parameters the first will be taken to be a
+ prerequisite, see the test_set_prereq and test_have_prereq
+ documentation below:
+
+ test_expect_success TTY 'git --paginate rev-list uses a pager' \
+ ' ... '
+
+ - test_expect_failure [<prereq>] <message> <script>
This is NOT the opposite of test_expect_success, but is used
to mark a test that demonstrates a known breakage. Unlike
@@ -243,6 +359,16 @@ library for your script to use.
success and "still broken" on failure. Failures from these
tests won't cause -i (immediate) to stop.
+ Like test_expect_success this function can optionally use a three
+ argument invocation with a prerequisite as the first argument.
+
+ - test_expect_code [<prereq>] <code> <message> <script>
+
+ Analogous to test_expect_success, but pass the test if it exits
+ with a given exit <code>
+
+ test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
+
- test_debug <script>
This takes a single argument, <script>, and evaluates it only
@@ -275,6 +401,85 @@ library for your script to use.
Merges the given rev using the given message. Like test_commit,
creates a tag and calls test_tick before committing.
+ - test_set_prereq SOME_PREREQ
+
+ Set a test prerequisite to be used later with test_have_prereq. The
+ test-lib will set some prerequisites for you, e.g. PERL and PYTHON
+ which are derived from ./GIT-BUILD-OPTIONS (grep test_set_prereq
+ test-lib.sh for more). Others you can set yourself and use later
+ with either test_have_prereq directly, or the three argument
+ invocation of test_expect_success and test_expect_failure.
+
+ - test_have_prereq SOME PREREQ
+
+ Check if we have a prerequisite previously set with
+ test_set_prereq. The most common use of this directly is to skip
+ all the tests if we don't have some essential prerequisite:
+
+ if ! test_have_prereq PERL
+ then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+ fi
+
+ - test_external [<prereq>] <message> <external> <script>
+
+ Execute a <script> with an <external> interpreter (like perl). This
+ was added for tests like t9700-perl-git.sh which do most of their
+ work in an external test script.
+
+ test_external \
+ 'GitwebCache::*FileCache*' \
+ "$PERL_PATH" "$TEST_DIRECTORY"/t9503/test_cache_interface.pl
+
+ If the test is outputting its own TAP you should set the
+ test_external_has_tap variable somewhere before calling the first
+ test_external* function. See t9700-perl-git.sh for an example.
+
+ # The external test will outputs its own plan
+ test_external_has_tap=1
+
+ - test_external_without_stderr [<prereq>] <message> <external> <script>
+
+ Like test_external but fail if there's any output on stderr,
+ instead of checking the exit code.
+
+ test_external_without_stderr \
+ 'Perl API' \
+ "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
+
+ - test_must_fail <git-command>
+
+ Run a git command and ensure it fails in a controlled way. Use
+ this instead of "! <git-command>" to fail when git commands
+ segfault.
+
+ - test_might_fail <git-command>
+
+ Similar to test_must_fail, but tolerate success, too. Use this
+ instead of "<git-command> || :" to catch failures due to segv.
+
+ - test_cmp <expected> <actual>
+
+ Check whether the content of the <actual> file matches the
+ <expected> file. This behaves like "cmp" but produces more
+ helpful output when the test is run with "-v" option.
+
+ - test_when_finished <script>
+
+ Prepend <script> to a list of commands to run to clean up
+ at the end of the current test. If some clean-up command
+ fails, the test will not pass.
+
+ Example:
+
+ test_expect_success 'branch pointing to non-commit' '
+ git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+ test_when_finished "git update-ref -d refs/heads/invalid" &&
+ ...
+ '
+
+
Tips for Writing Tests
----------------------
diff --git a/t/aggregate-results.sh b/t/aggregate-results.sh
index d5bab75..d206b7c 100755
--- a/t/aggregate-results.sh
+++ b/t/aggregate-results.sh
@@ -6,7 +6,7 @@ failed=0
broken=0
total=0
-for file
+while read file
do
while read type value
do
diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh
index 5a734b1..81ef2a0 100644
--- a/t/gitweb-lib.sh
+++ b/t/gitweb-lib.sh
@@ -19,9 +19,9 @@ our \$site_name = '[localhost]';
our \$site_header = '';
our \$site_footer = '';
our \$home_text = 'indextext.html';
-our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/gitweb.css');
-our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/git-logo.png';
-our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/git-favicon.png';
+our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/static/gitweb.css');
+our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/static/git-logo.png';
+our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/static/git-favicon.png';
our \$projects_list = '';
our \$export_ok = '';
our \$strict_export = '';
@@ -76,12 +76,12 @@ gitweb_run () {
. ./test-lib.sh
if ! test_have_prereq PERL; then
- say 'skipping gitweb tests, perl not available'
+ skip_all='skipping gitweb tests, perl not available'
test_done
fi
perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
- say 'skipping gitweb tests, perl version is too old'
+ skip_all='skipping gitweb tests, perl version is too old'
test_done
}
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
index 4b3b793..648d161 100644
--- a/t/lib-cvs.sh
+++ b/t/lib-cvs.sh
@@ -9,7 +9,7 @@ export HOME
if ! type cvs >/dev/null 2>&1
then
- say 'skipping cvsimport tests, cvs not found'
+ skip_all='skipping cvsimport tests, cvs not found'
test_done
fi
@@ -21,11 +21,11 @@ case "$cvsps_version" in
2.1 | 2.2*)
;;
'')
- say 'skipping cvsimport tests, cvsps not found'
+ skip_all='skipping cvsimport tests, cvsps not found'
test_done
;;
*)
- say 'skipping cvsimport tests, unsupported cvsps version'
+ skip_all='skipping cvsimport tests, unsupported cvsps version'
test_done
;;
esac
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 0f7f35c..c3f6676 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -5,11 +5,11 @@ git_svn_id=git""-svn-id
if test -n "$NO_SVN_TESTS"
then
- say 'skipping git svn tests, NO_SVN_TESTS defined'
+ skip_all='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'
+ skip_all='skipping git svn tests, perl not available'
test_done
fi
@@ -21,7 +21,7 @@ PERL=${PERL:-perl}
svn >/dev/null 2>&1
if test $? -ne 1
then
- say 'skipping git svn tests, svn not found'
+ skip_all='skipping git svn tests, svn not found'
test_done
fi
@@ -40,13 +40,12 @@ x=$?
if test $x -ne 0
then
if test $x -eq 42; then
- err='Perl SVN libraries must be >= 1.1.0'
+ skip_all='Perl SVN libraries must be >= 1.1.0'
elif test $x -eq 41; then
- err='svnadmin failed to create fsfs repository'
+ skip_all='svnadmin failed to create fsfs repository'
else
- err='Perl SVN libraries not found or unusable, skipping test'
+ skip_all='Perl SVN libraries not found or unusable'
fi
- say "$err"
test_done
fi
@@ -159,7 +158,7 @@ EOF
require_svnserve () {
if test -z "$SVNSERVE_PORT"
then
- say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)'
+ skip_all='skipping svnserve test. (set $SVNSERVE_PORT to enable)'
test_done
fi
}
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index da4b8d5..71effc5 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -5,8 +5,7 @@
if test -z "$GIT_TEST_HTTPD"
then
- say "skipping test, network testing disabled by default"
- say "(define GIT_TEST_HTTPD to enable)"
+ skip_all="Network testing disabled (define GIT_TEST_HTTPD to enable)"
test_done
fi
@@ -46,7 +45,7 @@ HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www
if ! test -x "$LIB_HTTPD_PATH"
then
- say "skipping test, no web server found at '$LIB_HTTPD_PATH'"
+ skip_all="skipping test, no web server found at '$LIB_HTTPD_PATH'"
test_done
fi
@@ -59,12 +58,12 @@ then
then
if ! test $HTTPD_VERSION -ge 2
then
- say "skipping test, at least Apache version 2 is required"
+ skip_all="skipping test, at least Apache version 2 is required"
test_done
fi
if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
then
- say "Apache module directory not found. Skipping tests."
+ skip_all="Apache module directory not found. Skipping tests."
test_done
fi
@@ -119,7 +118,7 @@ start_httpd() {
>&3 2>&4
if test $? -ne 0
then
- say "skipping test, web server setup failed"
+ skip_all="skipping test, web server setup failed"
trap 'die' EXIT
test_done
fi
diff --git a/t/lib-pager.sh b/t/lib-pager.sh
new file mode 100644
index 0000000..ba03eab
--- /dev/null
+++ b/t/lib-pager.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+test_expect_success 'determine default pager' '
+ test_might_fail git config --unset core.pager &&
+ less=$(
+ unset PAGER GIT_PAGER;
+ git var GIT_PAGER
+ ) &&
+ test -n "$less"
+'
+
+if expr "$less" : '[a-z][a-z]*$' >/dev/null
+then
+ test_set_prereq SIMPLEPAGER
+fi
diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
index ce36f34..375e248 100644
--- a/t/lib-patch-mode.sh
+++ b/t/lib-patch-mode.sh
@@ -3,7 +3,7 @@
. ./test-lib.sh
if ! test_have_prereq PERL; then
- say 'skipping --patch tests, perl not available'
+ skip_all='skipping --patch tests, perl not available'
test_done
fi
diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh
index 985d517..ea25dd8 100644
--- a/t/lib-t6000.sh
+++ b/t/lib-t6000.sh
@@ -91,7 +91,7 @@ check_output()
shift 1
if eval "$*" | entag > $_name.actual
then
- diff $_name.expected $_name.actual
+ test_cmp $_name.expected $_name.actual
else
return 1;
fi
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 3ec9cbe..f2c7336 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -301,7 +301,7 @@ $expectfilter >expected <<\EOF
EOF
test_expect_success \
'validate git diff-files output for a know cache/work tree state.' \
- 'git diff-files >current && diff >/dev/null -b current expected'
+ 'git diff-files >current && test_cmp current expected >/dev/null'
test_expect_success \
'git update-index --refresh should succeed.' \
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index 09f855a..93e58c0 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -13,6 +13,7 @@ test_expect_success 'sigchain works' '
test-sigchain >actual
case "$?" in
143) true ;; # POSIX w/ SIGTERM=15
+ 271) true ;; # ksh w/ SIGTERM=15
3) true ;; # Windows
*) false ;;
esac &&
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 3ea4f9e..1d4d0a5 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -39,6 +39,7 @@ check_parse 2008-02 bad
check_parse 2008-02-14 bad
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
+check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
check_approxidate() {
echo "$1 -> $2 +0000" >expect
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index c3e7e32..234a94f 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -453,5 +453,57 @@ test_expect_success 'invalid .gitattributes (must not crash)' '
git diff
'
+# Some more tests here to add new autocrlf functionality.
+# We want to have a known state here, so start a bit from scratch
+
+test_expect_success 'setting up for new autocrlf tests' '
+ git config core.autocrlf false &&
+ git config core.safecrlf false &&
+ rm -rf .????* * &&
+ for w in I am all LF; do echo $w; done >alllf &&
+ for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+ for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+ git add -A . &&
+ git commit -m "alllf, allcrlf and mixed only" &&
+ git tag -a -m "message" autocrlf-checkpoint
+'
+
+test_expect_success 'report no change after setting autocrlf' '
+ git config core.autocrlf true &&
+ touch * &&
+ git diff --exit-code
+'
+
+test_expect_success 'files are clean after checkout' '
+ rm * &&
+ git checkout -f &&
+ git diff --exit-code
+'
+
+cr_to_Q_no_NL () {
+ tr '\015' Q | tr -d '\012'
+}
+
+test_expect_success 'LF only file gets CRLF with autocrlf' '
+ test "$(cr_to_Q_no_NL < alllf)" = "IQamQallQLFQ"
+'
+
+test_expect_success 'Mixed file is still mixed with autocrlf' '
+ test "$(cr_to_Q_no_NL < mixed)" = "OhhereisCRLFQintext"
+'
+
+test_expect_success 'CRLF only file has CRLF with autocrlf' '
+ test "$(cr_to_Q_no_NL < allcrlf)" = "IQamQallQCRLFQ"
+'
+
+test_expect_success 'New CRLF file gets LF in repo' '
+ tr -d "\015" < alllf | append_cr > alllf2 &&
+ git add alllf2 &&
+ git commit -m "alllf2 added" &&
+ git config core.autocrlf false &&
+ rm * &&
+ git checkout -f &&
+ test_cmp alllf alllf2
+'
test_done
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 6cb8d60..828e35b 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -65,17 +65,21 @@ test_expect_success expanded_in_repo '
echo "\$Id:NoSpaceAtFront \$"
echo "\$Id:NoSpaceAtEitherEnd\$"
echo "\$Id: NoTerminatingSymbol"
+ echo "\$Id: Foreign Commit With Spaces \$"
+ echo "\$Id: NoTerminatingSymbolAtEOF"
} > expanded-keywords &&
{
echo "File with expanded keywords"
- echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
- echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
- echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
- echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
- echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
- echo "\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
echo "\$Id: NoTerminatingSymbol"
+ echo "\$Id: Foreign Commit With Spaces \$"
+ echo "\$Id: NoTerminatingSymbolAtEOF"
} > expected-output &&
git add expanded-keywords &&
diff --git a/t/t0025-crlf-auto.sh b/t/t0025-crlf-auto.sh
new file mode 100755
index 0000000..f5f67a6
--- /dev/null
+++ b/t/t0025-crlf-auto.sh
@@ -0,0 +1,155 @@
+#!/bin/sh
+
+test_description='CRLF conversion'
+
+. ./test-lib.sh
+
+has_cr() {
+ tr '\015' Q <"$1" | grep Q >/dev/null
+}
+
+test_expect_success setup '
+
+ git config core.autocrlf false &&
+
+ for w in Hello world how are you; do echo $w; done >one &&
+ for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >two &&
+ for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >three &&
+ git add . &&
+
+ git commit -m initial &&
+
+ one=`git rev-parse HEAD:one` &&
+ two=`git rev-parse HEAD:two` &&
+ three=`git rev-parse HEAD:three` &&
+
+ echo happy.
+'
+
+test_expect_success 'default settings cause no changes' '
+
+ rm -f .gitattributes tmp one two three &&
+ git read-tree --reset -u HEAD &&
+
+ ! has_cr one &&
+ has_cr two &&
+ onediff=`git diff one` &&
+ twodiff=`git diff two` &&
+ threediff=`git diff three` &&
+ test -z "$onediff" -a -z "$twodiff" -a -z "$threediff"
+'
+
+test_expect_success 'crlf=true causes a CRLF file to be normalized' '
+
+ # Backwards compatibility check
+ rm -f .gitattributes tmp one two three &&
+ echo "two crlf" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ # Note, "normalized" means that git will normalize it if added
+ has_cr two &&
+ twodiff=`git diff two` &&
+ test -n "$twodiff"
+'
+
+test_expect_success 'text=true causes a CRLF file to be normalized' '
+
+ rm -f .gitattributes tmp one two three &&
+ echo "two text" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ # Note, "normalized" means that git will normalize it if added
+ has_cr two &&
+ twodiff=`git diff two` &&
+ test -n "$twodiff"
+'
+
+test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=false' '
+
+ rm -f .gitattributes tmp one two three &&
+ git config core.autocrlf false &&
+ echo "one eol=crlf" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr one &&
+ onediff=`git diff one` &&
+ test -z "$onediff"
+'
+
+test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=input' '
+
+ rm -f .gitattributes tmp one two three &&
+ git config core.autocrlf input &&
+ echo "one eol=crlf" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr one &&
+ onediff=`git diff one` &&
+ test -z "$onediff"
+'
+
+test_expect_success 'eol=lf gives a normalized file LFs with autocrlf=true' '
+
+ rm -f .gitattributes tmp one two three &&
+ git config core.autocrlf true &&
+ echo "one eol=lf" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ ! has_cr one &&
+ onediff=`git diff one` &&
+ test -z "$onediff"
+'
+
+test_expect_success 'autocrlf=true does not normalize CRLF files' '
+
+ rm -f .gitattributes tmp one two three &&
+ git config core.autocrlf true &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr one &&
+ has_cr two &&
+ onediff=`git diff one` &&
+ twodiff=`git diff two` &&
+ threediff=`git diff three` &&
+ test -z "$onediff" -a -z "$twodiff" -a -z "$threediff"
+'
+
+test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
+
+ rm -f .gitattributes tmp one two three &&
+ git config core.autocrlf true &&
+ echo "* text=auto" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr one &&
+ has_cr two &&
+ onediff=`git diff one` &&
+ twodiff=`git diff two` &&
+ threediff=`git diff three` &&
+ test -z "$onediff" -a -n "$twodiff" -a -z "$threediff"
+'
+
+test_expect_success 'text=auto, autocrlf=true does not normalize binary files' '
+
+ rm -f .gitattributes tmp one two three &&
+ git config core.autocrlf true &&
+ echo "* text=auto" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ ! has_cr three &&
+ threediff=`git diff three` &&
+ test -z "$threediff"
+'
+
+test_expect_success 'eol=crlf _does_ normalize binary files' '
+
+ rm -f .gitattributes tmp one two three &&
+ echo "three eol=crlf" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr three &&
+ threediff=`git diff three` &&
+ test -z "$threediff"
+'
+
+test_done
diff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh
new file mode 100755
index 0000000..f37ac8f
--- /dev/null
+++ b/t/t0026-eol-config.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+test_description='CRLF conversion'
+
+. ./test-lib.sh
+
+has_cr() {
+ tr '\015' Q <"$1" | grep Q >/dev/null
+}
+
+test_expect_success setup '
+
+ git config core.autocrlf false &&
+
+ echo "one text" > .gitattributes
+
+ for w in Hello world how are you; do echo $w; done >one &&
+ for w in I am very very fine thank you; do echo $w; done >two &&
+ git add . &&
+
+ git commit -m initial &&
+
+ one=`git rev-parse HEAD:one` &&
+ two=`git rev-parse HEAD:two` &&
+
+ echo happy.
+'
+
+test_expect_success 'eol=lf puts LFs in normalized file' '
+
+ rm -f .gitattributes tmp one two &&
+ git config core.eol lf &&
+ git read-tree --reset -u HEAD &&
+
+ ! has_cr one &&
+ ! has_cr two &&
+ onediff=`git diff one` &&
+ twodiff=`git diff two` &&
+ test -z "$onediff" -a -z "$twodiff"
+'
+
+test_expect_success 'eol=crlf puts CRLFs in normalized file' '
+
+ rm -f .gitattributes tmp one two &&
+ git config core.eol crlf &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr one &&
+ ! has_cr two &&
+ onediff=`git diff one` &&
+ twodiff=`git diff two` &&
+ test -z "$onediff" -a -z "$twodiff"
+'
+
+test_expect_success 'autocrlf=true overrides eol=lf' '
+
+ rm -f .gitattributes tmp one two &&
+ git config core.eol lf &&
+ git config core.autocrlf true &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr one &&
+ has_cr two &&
+ onediff=`git diff one` &&
+ twodiff=`git diff two` &&
+ test -z "$onediff" -a -z "$twodiff"
+'
+
+test_expect_success 'autocrlf=true overrides unset eol' '
+
+ rm -f .gitattributes tmp one two &&
+ git config --unset-all core.eol &&
+ git config core.autocrlf true &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr one &&
+ has_cr two &&
+ onediff=`git diff one` &&
+ twodiff=`git diff two` &&
+ test -z "$onediff" -a -z "$twodiff"
+'
+
+test_done
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 3d450ed..2092450 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -7,7 +7,7 @@ test_description='our own option parser'
. ./test-lib.sh
-cat > expect.err << EOF
+cat > expect << EOF
usage: test-parse-options <options>
-b, --boolean get a boolean
@@ -46,10 +46,12 @@ EOF
test_expect_success 'test help' '
test_must_fail test-parse-options -h > output 2> output.err &&
- test ! -s output &&
- test_cmp expect.err output.err
+ test ! -s output.err &&
+ test_cmp expect output
'
+mv expect expect.err
+
cat > expect << EOF
boolean: 2
integer: 1729
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 6327d20..0c562bb 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -390,4 +390,20 @@ test_expect_success \
git ls-files --stage | tee >treeMcheck.out &&
test_cmp treeM.out treeMcheck.out'
+test_expect_success '-m references the correct modified tree' '
+ echo >file-a &&
+ echo >file-b &&
+ git add file-a file-b &&
+ git commit -a -m "test for correct modified tree"
+ git branch initial-mod &&
+ echo b >file-b &&
+ git commit -a -m "B" &&
+ echo a >file-a &&
+ git add file-a &&
+ git ls-tree $(git write-tree) file-a >expect &&
+ git read-tree -m HEAD initial-mod &&
+ git ls-tree $(git write-tree) file-a >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index 210e594..5687499 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -24,18 +24,18 @@ test_expect_success 'update-index and ls-files' '
cd "$HERE" &&
git update-index --add one &&
case "`git ls-files`" in
- one) echo ok one ;;
+ one) echo pass one ;;
*) echo bad one; exit 1 ;;
esac &&
cd dir &&
git update-index --add two &&
case "`git ls-files`" in
- two) echo ok two ;;
+ two) echo pass two ;;
*) echo bad two; exit 1 ;;
esac &&
cd .. &&
case "`git ls-files`" in
- dir/two"$LF"one) echo ok both ;;
+ dir/two"$LF"one) echo pass both ;;
*) echo bad; exit 1 ;;
esac
'
@@ -58,17 +58,17 @@ test_expect_success 'diff-files' '
echo a >>one &&
echo d >>dir/two &&
case "`git diff-files --name-only`" in
- dir/two"$LF"one) echo ok top ;;
+ dir/two"$LF"one) echo pass top ;;
*) echo bad top; exit 1 ;;
esac &&
# diff should not omit leading paths
cd dir &&
case "`git diff-files --name-only`" in
- dir/two"$LF"one) echo ok subdir ;;
+ dir/two"$LF"one) echo pass subdir ;;
*) echo bad subdir; exit 1 ;;
esac &&
case "`git diff-files --name-only .`" in
- dir/two) echo ok subdir limited ;;
+ dir/two) echo pass subdir limited ;;
*) echo bad subdir limited; exit 1 ;;
esac
'
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index f11f98c..64f0508 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -824,4 +824,12 @@ test_expect_success 'check split_cmdline return' "
test_must_fail git merge master
"
+test_expect_success 'git -c "key=value" support' '
+ test "z$(git -c name=value config name)" = zvalue &&
+ test "z$(git -c core.name=value config core.name)" = zvalue &&
+ test "z$(git -c CamelCase=value config camelcase)" = zvalue &&
+ test "z$(git -c flag config --bool flag)" = ztrue &&
+ test_must_fail git -c core.name=value config name
+'
+
test_done
diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh
index 055ad00..97ab02a 100755
--- a/t/t1304-default-acl.sh
+++ b/t/t1304-default-acl.sh
@@ -15,9 +15,12 @@ umask 077
# is a good candidate: exists on all unices, and it has permission
# anyway, so we don't create a security hole running the testsuite.
-if ! setfacl -m u:root:rwx .; then
- say "Skipping ACL tests: unable to use setfacl"
- test_done
+setfacl_out="$(setfacl -m u:root:rwx . 2>&1)"
+setfacl_ret=$?
+
+if [ $setfacl_ret != 0 ]; then
+ skip_all="Skipping ACL tests: unable to use setfacl (output: '$setfacl_out'; return code: '$setfacl_ret')"
+ test_done
fi
check_perms_and_acl () {
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 49cae3e..759cf12 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -5,7 +5,9 @@ test_description='git fsck random collection of tests'
. ./test-lib.sh
test_expect_success setup '
+ git config i18n.commitencoding ISO-8859-1 &&
test_commit A fileA one &&
+ git config --unset i18n.commitencoding &&
git checkout HEAD^0 &&
test_commit B fileB two &&
git tag -d A B &&
@@ -28,6 +30,12 @@ test_expect_success 'loose objects borrowed from alternate are not missing' '
)
'
+test_expect_success 'valid objects appear valid' '
+ { git fsck 2>out; true; } &&
+ ! grep error out &&
+ ! grep fatal out
+'
+
# Corruption tests follow. Make sure to remove all traces of the
# specific corruption you test afterwards, lest a later test trip over
# it.
@@ -57,6 +65,34 @@ test_expect_success 'branch pointing to non-commit' '
git update-ref -d refs/heads/invalid
'
+new=nothing
+test_expect_success 'email without @ is okay' '
+ git cat-file commit HEAD >basis &&
+ sed "s/@/AT/" basis >okay &&
+ new=$(git hash-object -t commit -w --stdin <okay) &&
+ echo "$new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ git fsck 2>out &&
+ cat out &&
+ ! grep "error in commit $new" out
+'
+git update-ref -d refs/heads/bogus
+rm -f ".git/objects/$new"
+
+new=nothing
+test_expect_success 'email with embedded > is not okay' '
+ git cat-file commit HEAD >basis &&
+ sed "s/@[a-z]/&>/" basis >bad-email &&
+ new=$(git hash-object -t commit -w --stdin <bad-email) &&
+ echo "$new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ git fsck 2>out &&
+ cat out &&
+ grep "error in commit $new" out
+'
+git update-ref -d refs/heads/bogus
+rm -f ".git/objects/$new"
+
cat > invalid-tag <<EOF
object ffffffffffffffffffffffffffffffffffffffff
type commit
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index e504058..b3195c4 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -3,7 +3,8 @@
test_description='test git rev-parse --parseopt'
. ./test-lib.sh
-cat > expect.err <<EOF
+cat > expect <<\END_EXPECT
+cat <<\EOF
usage: some-command [options] <args>...
some-command does foo and bar!
@@ -19,6 +20,7 @@ Extras
--extra1 line above used to cause a segfault but no longer does
EOF
+END_EXPECT
cat > optionspec << EOF
some-command [options] <args>...
@@ -38,8 +40,8 @@ extra1 line above used to cause a segfault but no longer does
EOF
test_expect_success 'test --parseopt help output' '
- git rev-parse --parseopt -- -h 2> output.err < optionspec
- test_cmp expect.err output.err
+ git rev-parse --parseopt -- -h > output < optionspec
+ test_cmp expect output
'
cat > expect <<EOF
@@ -79,4 +81,22 @@ test_expect_success 'test --parseopt --keep-dashdash' '
test_cmp expect output
'
+cat >expect <<EOF
+set -- --foo -- '--' 'arg' '--spam=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option with --' '
+ git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo -- arg --spam=ham <optionspec >output &&
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo -- 'arg' '--spam=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option without --' '
+ git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo arg --spam=ham <optionspec >output &&
+ test_cmp expect output
+'
+
test_done
diff --git a/t/t1509-root-worktree.sh b/t/t1509-root-worktree.sh
index 5322a3b..7f60fd0 100755
--- a/t/t1509-root-worktree.sh
+++ b/t/t1509-root-worktree.sh
@@ -99,17 +99,17 @@ test_foobar_foobar() {
}
if ! test_have_prereq POSIXPERM || ! [ -w / ]; then
- say "Dangerous test skipped. Read this test if you want to execute it"
+ skip_all="Dangerous test skipped. Read this test if you want to execute it"
test_done
fi
if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
- say "You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
+ skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
test_done
fi
if [ "$UID" = 0 ]; then
- say "No you can't run this with root"
+ skip_all="No you can't run this with root"
test_done
fi
diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh
index 27e2127..05cc8fd 100755
--- a/t/t2007-checkout-symlink.sh
+++ b/t/t2007-checkout-symlink.sh
@@ -8,7 +8,7 @@ test_description='git checkout to switch between branches with symlink<->dir'
if ! test_have_prereq SYMLINKS
then
- say "symbolic links not supported - skipping tests"
+ skip_all="symbolic links not supported - skipping tests"
test_done
fi
diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh
new file mode 100755
index 0000000..be88d4b
--- /dev/null
+++ b/t/t2017-checkout-orphan.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Erick Mattos
+#
+
+test_description='git checkout --orphan
+
+Main Tests for --orphan functionality.'
+
+. ./test-lib.sh
+
+TEST_FILE=foo
+
+test_expect_success 'Setup' '
+ echo "Initial" >"$TEST_FILE" &&
+ git add "$TEST_FILE" &&
+ git commit -m "First Commit"
+ test_tick &&
+ echo "State 1" >>"$TEST_FILE" &&
+ git add "$TEST_FILE" &&
+ test_tick &&
+ git commit -m "Second Commit"
+'
+
+test_expect_success '--orphan creates a new orphan branch from HEAD' '
+ git checkout --orphan alpha &&
+ test_must_fail git rev-parse --verify HEAD &&
+ test "refs/heads/alpha" = "$(git symbolic-ref HEAD)" &&
+ test_tick &&
+ git commit -m "Third Commit" &&
+ test_must_fail git rev-parse --verify HEAD^ &&
+ git diff-tree --quiet master alpha
+'
+
+test_expect_success '--orphan creates a new orphan branch from <start_point>' '
+ git checkout master &&
+ git checkout --orphan beta master^ &&
+ test_must_fail git rev-parse --verify HEAD &&
+ test "refs/heads/beta" = "$(git symbolic-ref HEAD)" &&
+ test_tick &&
+ git commit -m "Fourth Commit" &&
+ test_must_fail git rev-parse --verify HEAD^ &&
+ git diff-tree --quiet master^ beta
+'
+
+test_expect_success '--orphan must be rejected with -b' '
+ git checkout master &&
+ test_must_fail git checkout --orphan new -b newer &&
+ test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan must be rejected with -t' '
+ git checkout master &&
+ test_must_fail git checkout --orphan new -t master &&
+ test refs/heads/mast