]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jh/maint-submodule-status-in-void'
authorJunio C Hamano <gitster@pobox.com>
Wed, 24 Mar 2010 23:55:37 +0000 (16:55 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 24 Mar 2010 23:55:37 +0000 (16:55 -0700)
* jh/maint-submodule-status-in-void:
  git submodule summary: Handle HEAD as argument when on an unborn branch
  submodule summary: do not fail before the first commit

322 files changed:
.gitignore
.mailmap
Documentation/RelNotes-1.7.0.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.7.0.2.txt [new file with mode: 0644]
Documentation/RelNotes-1.7.0.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.7.1.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-generate-patch.txt
Documentation/diff-options.txt
Documentation/fetch-options.txt
Documentation/git-am.txt
Documentation/git-branch.txt
Documentation/git-check-ref-format.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-cvsimport.txt
Documentation/git-describe.txt
Documentation/git-fast-import.txt
Documentation/git-fetch-pack.txt
Documentation/git-format-patch.txt
Documentation/git-grep.txt
Documentation/git-hash-object.txt
Documentation/git-imap-send.txt
Documentation/git-index-pack.txt
Documentation/git-init.txt
Documentation/git-log.txt
Documentation/git-mailsplit.txt
Documentation/git-merge-file.txt
Documentation/git-notes.txt
Documentation/git-pack-objects.txt
Documentation/git-prune.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-read-tree.txt
Documentation/git-reflog.txt
Documentation/git-reset.txt
Documentation/git-rev-parse.txt
Documentation/git-send-pack.txt
Documentation/git-show-branch.txt
Documentation/git-stash.txt
Documentation/git-var.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitdiffcore.txt
Documentation/githooks.txt
Documentation/merge-options.txt
Documentation/pretty-options.txt
Documentation/rev-list-options.txt
Documentation/technical/api-parse-options.txt
Documentation/technical/api-run-command.txt
Documentation/urls.txt
GIT-VERSION-GEN
Makefile
RelNotes
abspath.c
advice.c
advice.h
bisect.c
builtin.h
builtin/add.c [moved from builtin-add.c with 94% similarity]
builtin/annotate.c [moved from builtin-annotate.c with 100% similarity]
builtin/apply.c [moved from builtin-apply.c with 95% similarity]
builtin/archive.c [moved from builtin-archive.c with 100% similarity]
builtin/bisect--helper.c [moved from builtin-bisect--helper.c with 100% similarity]
builtin/blame.c [moved from builtin-blame.c with 99% similarity]
builtin/branch.c [moved from builtin-branch.c with 99% similarity]
builtin/bundle.c [moved from builtin-bundle.c with 100% similarity]
builtin/cat-file.c [moved from builtin-cat-file.c with 97% similarity]
builtin/check-attr.c [moved from builtin-check-attr.c with 100% similarity]
builtin/check-ref-format.c [moved from builtin-check-ref-format.c with 100% similarity]
builtin/checkout-index.c [moved from builtin-checkout-index.c with 100% similarity]
builtin/checkout.c [moved from builtin-checkout.c with 96% similarity]
builtin/clean.c [moved from builtin-clean.c with 100% similarity]
builtin/clone.c [moved from builtin-clone.c with 97% similarity]
builtin/commit-tree.c [moved from builtin-commit-tree.c with 100% similarity]
builtin/commit.c [moved from builtin-commit.c with 96% similarity]
builtin/config.c [moved from builtin-config.c with 100% similarity]
builtin/count-objects.c [moved from builtin-count-objects.c with 100% similarity]
builtin/describe.c [moved from builtin-describe.c with 100% similarity]
builtin/diff-files.c [moved from builtin-diff-files.c with 100% similarity]
builtin/diff-index.c [moved from builtin-diff-index.c with 100% similarity]
builtin/diff-tree.c [moved from builtin-diff-tree.c with 90% similarity]
builtin/diff.c [moved from builtin-diff.c with 100% similarity]
builtin/fast-export.c [moved from builtin-fast-export.c with 100% similarity]
builtin/fetch-pack.c [moved from builtin-fetch-pack.c with 99% similarity]
builtin/fetch.c [moved from builtin-fetch.c with 94% similarity]
builtin/fmt-merge-msg.c [moved from builtin-fmt-merge-msg.c with 100% similarity]
builtin/for-each-ref.c [moved from builtin-for-each-ref.c with 93% similarity]
builtin/fsck.c [moved from builtin-fsck.c with 100% similarity]
builtin/gc.c [moved from builtin-gc.c with 100% similarity]
builtin/grep.c [moved from builtin-grep.c with 89% similarity]
builtin/hash-object.c [moved from builtin-hash-object.c with 96% similarity]
builtin/help.c [moved from builtin-help.c with 100% similarity]
builtin/index-pack.c [moved from builtin-index-pack.c with 100% similarity]
builtin/init-db.c [moved from builtin-init-db.c with 95% similarity]
builtin/log.c [moved from builtin-log.c with 91% similarity]
builtin/ls-files.c [moved from builtin-ls-files.c with 100% similarity]
builtin/ls-remote.c [moved from builtin-ls-remote.c with 100% similarity]
builtin/ls-tree.c [moved from builtin-ls-tree.c with 100% similarity]
builtin/mailinfo.c [moved from builtin-mailinfo.c with 99% similarity]
builtin/mailsplit.c [moved from builtin-mailsplit.c with 98% similarity]
builtin/merge-base.c [moved from builtin-merge-base.c with 100% similarity]
builtin/merge-file.c [moved from builtin-merge-file.c with 80% similarity]
builtin/merge-index.c [moved from builtin-merge-index.c with 100% similarity]
builtin/merge-ours.c [moved from builtin-merge-ours.c with 100% similarity]
builtin/merge-recursive.c [moved from builtin-merge-recursive.c with 100% similarity]
builtin/merge-tree.c [moved from builtin-merge-tree.c with 100% similarity]
builtin/merge.c [moved from builtin-merge.c with 100% similarity]
builtin/mktag.c [moved from builtin-mktag.c with 100% similarity]
builtin/mktree.c [moved from builtin-mktree.c with 100% similarity]
builtin/mv.c [moved from builtin-mv.c with 100% similarity]
builtin/name-rev.c [moved from builtin-name-rev.c with 100% similarity]
builtin/notes.c [new file with mode: 0644]
builtin/pack-objects.c [moved from builtin-pack-objects.c with 98% similarity]
builtin/pack-redundant.c [moved from builtin-pack-redundant.c with 100% similarity]
builtin/pack-refs.c [moved from builtin-pack-refs.c with 100% similarity]
builtin/patch-id.c [moved from builtin-patch-id.c with 100% similarity]
builtin/prune-packed.c [moved from builtin-prune-packed.c with 100% similarity]
builtin/prune.c [moved from builtin-prune.c with 90% similarity]
builtin/push.c [moved from builtin-push.c with 92% similarity]
builtin/read-tree.c [moved from builtin-read-tree.c with 100% similarity]
builtin/receive-pack.c [moved from builtin-receive-pack.c with 81% similarity]
builtin/reflog.c [moved from builtin-reflog.c with 99% similarity]
builtin/remote.c [moved from builtin-remote.c with 100% similarity]
builtin/replace.c [moved from builtin-replace.c with 100% similarity]
builtin/rerere.c [moved from builtin-rerere.c with 100% similarity]
builtin/reset.c [moved from builtin-reset.c with 88% similarity]
builtin/rev-list.c [moved from builtin-rev-list.c with 98% similarity]
builtin/rev-parse.c [moved from builtin-rev-parse.c with 98% similarity]
builtin/revert.c [moved from builtin-revert.c with 92% similarity]
builtin/rm.c [moved from builtin-rm.c with 100% similarity]
builtin/send-pack.c [moved from builtin-send-pack.c with 67% similarity]
builtin/shortlog.c [moved from builtin-shortlog.c with 94% similarity]
builtin/show-branch.c [moved from builtin-show-branch.c with 98% similarity]
builtin/show-ref.c [moved from builtin-show-ref.c with 100% similarity]
builtin/stripspace.c [moved from builtin-stripspace.c with 100% similarity]
builtin/symbolic-ref.c [moved from builtin-symbolic-ref.c with 100% similarity]
builtin/tag.c [moved from builtin-tag.c with 100% similarity]
builtin/tar-tree.c [moved from builtin-tar-tree.c with 100% similarity]
builtin/unpack-file.c [moved from builtin-unpack-file.c with 100% similarity]
builtin/unpack-objects.c [moved from builtin-unpack-objects.c with 100% similarity]
builtin/update-index.c [moved from builtin-update-index.c with 100% similarity]
builtin/update-ref.c [moved from builtin-update-ref.c with 100% similarity]
builtin/update-server-info.c [moved from builtin-update-server-info.c with 100% similarity]
builtin/upload-archive.c [moved from builtin-upload-archive.c with 100% similarity]
builtin/var.c [moved from builtin-var.c with 94% similarity]
builtin/verify-pack.c [moved from builtin-verify-pack.c with 100% similarity]
builtin/verify-tag.c [moved from builtin-verify-tag.c with 100% similarity]
builtin/write-tree.c [moved from builtin-write-tree.c with 100% similarity]
cache.h
color.c
color.h
compat/mingw.c
compat/mingw.h
compat/mkdtemp.c
compat/mkstemps.c [deleted file]
compat/win32/pthread.c
connect.c
contrib/completion/git-completion.bash
contrib/examples/git-notes.sh [moved from git-notes.sh with 100% similarity]
contrib/fast-import/git-p4
contrib/fast-import/import-directories.perl
convert.c
daemon.c
diff-lib.c
diff.c
diff.h
diffcore.h
dir.c
environment.c
exec_cmd.c
fast-import.c
git-add--interactive.perl
git-am.sh
git-cvsimport.perl
git-parse-remote.sh [changed mode: 0755->0644]
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-request-pull.sh
git-send-email.perl
git-sh-setup.sh [changed mode: 0755->0644]
git-stash.sh
git-submodule.sh
git-svn.perl
git.c
gitweb/README
gitweb/gitweb.perl
graph.c
grep.c
grep.h
help.c
http-fetch.c
http-push.c
http-walker.c
http.c
imap-send.c
levenshtein.h
ll-merge.c
log-tree.c
merge-recursive.c
notes.c
notes.h
pack-write.c
pack.h
pager.c
parse-options.c
parse-options.h
path.c
perl/Git.pm
pretty.c
refs.c
refs.h
remote-curl.c
rerere.c
revision.c
revision.h
run-command.c
run-command.h
send-pack.h
setup.c
sha1_file.c
sha1_name.c
submodule.c
submodule.h
t/Makefile
t/lib-httpd.sh
t/lib-patch-mode.sh [changed mode: 0755->0644]
t/t0001-init.sh
t/t0050-filesystem.sh
t/t1007-hash-object.sh
t/t1304-default-acl.sh [new file with mode: 0755]
t/t1410-reflog.sh
t/t1411-reflog-show.sh
t/t1450-fsck.sh
t/t1509-root-worktree.sh [new file with mode: 0755]
t/t1509/excludes [new file with mode: 0644]
t/t1509/prepare-chroot.sh [new file with mode: 0755]
t/t2016-checkout-patch.sh
t/t2200-add-update.sh
t/t2204-add-ignored.sh [new file with mode: 0755]
t/t3020-ls-files-error-unmatch.sh
t/t3301-notes.sh
t/t3303-notes-subtrees.sh
t/t3304-notes-mixed.sh
t/t3305-notes-fanout.sh [new file with mode: 0755]
t/t3306-notes-prune.sh [new file with mode: 0755]
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3417-rebase-whitespace-fix.sh [new file with mode: 0755]
t/t3600-rm.sh
t/t3700-add.sh
t/t3800-mktag.sh
t/t3903-stash.sh
t/t4013-diff-various.sh
t/t4013/diff.log_-m_-p_--first-parent_master [new file with mode: 0644]
t/t4013/diff.log_-m_-p_master [new file with mode: 0644]
t/t4013/diff.log_-p_--first-parent_master [new file with mode: 0644]
t/t4013/diff.show_--first-parent_master [new file with mode: 0644]
t/t4013/diff.show_-c_master [new file with mode: 0644]
t/t4013/diff.show_-m_master [new file with mode: 0644]
t/t4014-format-patch.sh
t/t4017-diff-retval.sh
t/t4026-color.sh
t/t4041-diff-submodule.sh
t/t4103-apply-binary.sh
t/t4104-apply-boundary.sh
t/t4124-apply-ws-rule.sh
t/t4200-rerere.sh
t/t4253-am-keep-cr-dos.sh [new file with mode: 0755]
t/t5100/msg0015
t/t5304-prune.sh
t/t5401-update-hooks.sh
t/t5407-post-rewrite-hook.sh [new file with mode: 0755]
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5516-fetch-push.sh
t/t5521-pull-options.sh
t/t5540-http-push.sh
t/t5541-http-push.sh
t/t6000lib.sh [changed mode: 0755->0644]
t/t6023-merge-file.sh
t/t6030-bisect-porcelain.sh
t/t7002-grep.sh
t/t7006-pager.sh [new file with mode: 0755]
t/t7006/test-terminal.perl [new file with mode: 0755]
t/t7103-reset-bare.sh
t/t7110-reset-merge.sh
t/t7111-reset-table.sh
t/t7201-co.sh
t/t7406-submodule-update.sh
t/t7501-commit.sh
t/t7506-status-submodule.sh
t/t8003-blame.sh
t/t9001-send-email.sh
t/t9119-git-svn-info.sh
t/t9150-svk-mergetickets.sh
t/t9151-svn-mergeinfo.sh
t/t9151/make-svnmerge-dump
t/t9151/svn-mergeinfo.dump
t/t9400-git-cvsserver-server.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9501-gitweb-standalone-http-status.sh
t/t9600-cvsimport.sh
t/test-lib.sh
test-chmtime.c
transport-helper.c
transport.c
transport.h
tree-walk.c
upload-pack.c
utf8.c
utf8.h
walker.h
wrap-for-bin.sh
wrapper.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
xdiff/xdiff.h
xdiff/xmerge.c

index 8df8f88bea10e02371a41ca20420ee1607814dc8..7b3acb766491e2e002f293cf9dd50298d0d7d83a 100644 (file)
 *.exe
 *.[aos]
 *.py[co]
+*.o.d
 *+
 /config.mak
 /autom4te.cache
index 975e6758efa85c674207ffd6b400e3bbab2576a2..88bd01e16d91f002cf3ac405e2a923884058d724 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -36,6 +36,7 @@ Li Hong <leehong@pku.edu.cn>
 Lukas Sandström <lukass@etek.chalmers.se>
 Martin Langhoff <martin@catalyst.net.nz>
 Michael Coleman <tutufan@gmail.com>
+Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm>
 Michael W. Olson <mwolson@gnu.org>
 Michele Ballabio <barra_cuda@katamail.com>
 Nanako Shiraishi <nanako3@bluebottle.com>
diff --git a/Documentation/RelNotes-1.7.0.1.txt b/Documentation/RelNotes-1.7.0.1.txt
new file mode 100644 (file)
index 0000000..8ff5bca
--- /dev/null
@@ -0,0 +1,35 @@
+Git v1.7.0.1 Release Notes
+==========================
+
+Fixes since v1.7.0
+------------------
+
+ * In a freshly created repository "rev-parse HEAD^0" complained that
+   it is dangling symref, even though "rev-parse HEAD" didn't.
+
+ * "git show :no-such-name" tried to access the index without bounds
+   check, leading to a potential segfault.
+
+ * Message from "git cherry-pick" was harder to read and use than necessary
+   when it stopped due to conflicting changes.
+
+ * We referred to ".git/refs/" throughout the documentation when we
+   meant to talk about abstract notion of "ref namespace".  Because
+   people's repositories often have packed refs these days, this was
+   confusing.
+
+ * "git diff --output=/path/that/cannot/be/written" did not correctly
+   error out.
+
+ * "git grep -e -pattern-that-begin-with-dash paths..." could not be
+   spelled as "git grep -- -pattern-that-begin-with-dash paths..." which
+   would be a GNU way to use "--" as "end of options".
+
+ * "git grep" compiled with threading support tried to access an
+   uninitialized mutex on boxes with a single CPU.
+
+ * "git stash pop -q --index" failed because the unnecessary --index
+   option was propagated to "git stash drop" that is internally run at the
+   end.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.0.2.txt b/Documentation/RelNotes-1.7.0.2.txt
new file mode 100644 (file)
index 0000000..fcb46ca
--- /dev/null
@@ -0,0 +1,40 @@
+Git v1.7.0.2 Release Notes
+==========================
+
+Fixes since v1.7.0.1
+--------------------
+
+ * GIT_PAGER was not honored consistently by some scripted Porcelains, most
+   notably "git am".
+
+ * updating working tree files after telling git to add them to the
+   index and while it is still working created garbage object files in
+   the repository without diagnosing it as an error.
+
+ * "git bisect -- pathspec..." did not diagnose an error condition properly when
+   the simplification with given pathspec made the history empty.
+
+ * "git rev-list --cherry-pick A...B" now has an obvious optimization when the
+   histories haven't diverged (i.e. when one end is an ancestor of the other).
+
+ * "git diff --quiet -w" did not work as expected.
+
+ * "git fast-import" didn't work with a large input, as it lacked support
+   for producing the pack index in v2 format.
+
+ * "git imap-send" didn't use CRLF line endings over the imap protocol
+   when storing its payload to the draft box, violating RFC 3501.
+
+ * "git log --format='%w(x,y,z)%b'" and friends that rewrap message
+   has been optimized for utf-8 payload.
+
+ * Error messages generated on the receiving end did not come back to "git
+   push".
+
+ * "git status" in 1.7.0 lacked the optimization we used to have in 1.6.X series
+   to speed up scanning of large working tree.
+
+ * "gitweb" did not diagnose parsing errors properly while reading tis configuration
+   file.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.0.3.txt b/Documentation/RelNotes-1.7.0.3.txt
new file mode 100644 (file)
index 0000000..3b35573
--- /dev/null
@@ -0,0 +1,34 @@
+Git v1.7.0.3 Release Notes
+==========================
+
+Fixes since v1.7.0.2
+--------------------
+
+ * Object files are created in a more ACL friendly way in repositories
+   where group permission is ACL controlled.
+
+ * "git add -i" didn't handle a deleted path very well.
+
+ * "git blame" padded line numbers with one extra SP when the total number
+   of lines was one less than multiple of ten due to an off-by-one error.
+
+ * "git fetch --all/--multi" used to discard information for remotes that
+   are fetched earlier.
+
+ * "git log --author=me --grep=it" tried to find commits that have "it"
+   or are written by "me", instead of the ones that have "it" _and_ are
+   written by "me".
+
+ * "git log -g branch" misbehaved when there was no entries in the reflog
+   for the named branch.
+
+ * "git mailinfo" (hence "git am") incorrectly removed initial indent from
+   paragraphs.
+
+ * "git prune" and "git reflog" (hence "git gc" as well) didn't honor
+   an instruction never to expire by setting gc.reflogexpire to never.
+
+ * "git push" misbehaved when branch.<name>.merge was configured without
+   matching branch.<name>.remote.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.1.txt b/Documentation/RelNotes-1.7.1.txt
new file mode 100644 (file)
index 0000000..750d40f
--- /dev/null
@@ -0,0 +1,70 @@
+Git v1.7.1 Release Notes (draft)
+================================
+
+Updates since v1.7.0
+--------------------
+
+ * Some commands (e.g. svn and http interfaces) that interactively ask
+   password can be told to use an external program given via GIT_ASKPASS.
+
+ * Color values given to "color.<cmd>.<slot>" configuration can now have
+   more than one attributes (e.g. "bold ul").
+
+ * "git apply --whitespace=fix" didn't work well when an early patch in
+   a patch series adds trailing blank lines and a later one depended on
+   such a block of blank lines at the end.
+
+ * "git am" learned "--keep-cr" option to handle inputs that are
+   mixture of changes to files with and without CRLF line endings.
+
+ * "git cvsimport" learned -R option to leave revision mapping between
+   CVS revisions and resulting git commits.
+
+ * "git for-each-ref" learned %(symref), %(symref:short) and %(flag)
+   tokens.
+
+ * "git hash-object --stdin-paths" can take "--no-filters" option now.
+
+ * "git init" can be told to look at init.templatedir configuration
+   variable (obviously that has to come from either /etc/gitconfig or
+   $HOME/.gitconfig).
+
+ * "git grep" learned "--no-index" option, to search inside contents that
+   are not managed by git.
+
+ * "git grep" learned --color=auto/always/never.
+
+ * "git grep" learned to paint filename and line-number in colors.
+
+ * "git merge-file" learned to use custom conflict marker size and also use
+   the "union merge" behaviour.
+
+ * "git notes" command has been rewritten in C and learned quite a
+   many commands.
+
+ * "git request-pull" identifies the commit the request is relative to in
+   a more readable way.
+
+ * "git reset" learned "--keep" option that lets you discard commits
+   near the tip while preserving your local changes in a way similar
+   to how "git checkout branch" does.
+
+ * "git svn" should work better when interacting with repositories
+   with CRLF line endings.
+
+ * "git imap-send" learned to support CRAM-MD5 authentication.
+
+Fixes since v1.7.0
+------------------
+
+All of the fixes in v1.7.0.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git add frotz/nitfol" did not complain when the entire frotz/ directory
+   was ignored.
+
+---
+exec >/var/tmp/1
+echo O=$(git describe)
+O=v1.7.0.2-323-g0d0925c
+git shortlog --no-merges ^maint $O..
index 4c36aa95b7d3fc0d456d42bbdfdf5a118931094b..06b2f827b414651bde25165b4f42dc4160892d7a 100644 (file)
@@ -138,6 +138,11 @@ advice.*::
                Advice on how to set your identity configuration when
                your information is guessed from the system username and
                domain name. Default: true.
+
+       detachedHead::
+               Advice shown when you used linkgit::git-checkout[1] to
+               move to the detach HEAD state, to instruct how to create
+               a local branch after the fact.  Default: true.
 --
 
 core.fileMode::
@@ -514,10 +519,12 @@ check that makes sure that existing object files will not get overwritten.
 core.notesRef::
        When showing commit messages, also show notes which are stored in
        the given ref.  This ref is expected to contain files named
-       after the full SHA-1 of the commit they annotate.
+       after the full SHA-1 of the commit they annotate.  The ref
+       must be fully qualified.
 +
 If such a file exists in the given ref, the referenced blob is read, and
-appended to the commit message, separated by a "Notes:" line.  If the
+appended to the commit message, separated by a "Notes (<refname>):"
+line (shortened to "Notes:" in the case of "refs/notes/commits").  If the
 given ref itself does not exist, it is not an error, but means that no
 notes should be printed.
 +
@@ -550,6 +557,13 @@ it will be treated as a shell command.  For example, defining
 executed from the top-level directory of a repository, which may
 not necessarily be the current directory.
 
+am.keepcr::
+       If true, git-am will call git-mailsplit for patches in mbox format
+       with parameter '--keep-cr'. In this case git-mailsplit will
+       not remove `\r` from lines ending with `\r\n`. Can be overrriden
+       by giving '--no-keep-cr' from the command line.
+       See linkgit:git-am[1], linkgit:git-mailsplit[1].
+
 apply.ignorewhitespace::
        When set to 'change', tells 'git apply' to ignore changes in
        whitespace, in the same way as the '--ignore-space-change'
@@ -678,11 +692,29 @@ color.grep::
        `never`), never.  When set to `true` or `auto`, use color only
        when the output is written to the terminal.  Defaults to `false`.
 
-color.grep.match::
-       Use customized color for matches.  The value of this variable
-       may be specified as in color.branch.<slot>.  It is passed using
-       the environment variables 'GREP_COLOR' and 'GREP_COLORS' when
-       calling an external 'grep'.
+color.grep.<slot>::
+       Use customized color for grep colorization.  `<slot>` specifies which
+       part of the line to use the specified color, and is one of
++
+--
+`context`;;
+       non-matching text in context lines (when using `-A`, `-B`, or `-C`)
+`filename`;;
+       filename prefix (when not using `-h`)
+`function`;;
+       function name lines (when using `-p`)
+`linenumber`;;
+       line number prefix (when using `-n`)
+`match`;;
+       matching text
+`selected`;;
+       non-matching text in selected lines
+`separator`;;
+       separators between fields on a line (`:`, `-`, and `=`)
+       and between hunks (`--`)
+--
++
+The values of these variables may be specified as in color.branch.<slot>.
 
 color.interactive::
        When set to `always`, always use colors for interactive prompts
@@ -1200,6 +1232,10 @@ imap::
        The configuration variables in the 'imap' section are described
        in linkgit:git-imap-send[1].
 
+init.templatedir::
+       Specify the directory from which templates will be copied.
+       (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
+
 instaweb.browser::
        Specify the program that will be used to browse your working
        repository in gitweb. See linkgit:git-instaweb[1].
@@ -1300,6 +1336,53 @@ mergetool.keepTemporaries::
 mergetool.prompt::
        Prompt before each invocation of the merge resolution program.
 
+notes.displayRef::
+       The (fully qualified) refname from which to show notes when
+       showing commit messages.  The value of this variable can be set
+       to a glob, in which case notes from all matching refs will be
+       shown.  You may also specify this configuration variable
+       several times.  A warning will be issued for refs that do not
+       exist, but a glob that does not match any refs is silently
+       ignored.
++
+This setting can be overridden with the `GIT_NOTES_DISPLAY_REF`
+environment variable, which must be a colon separated list of refs or
+globs.
++
+The effective value of "core.notesRef" (possibly overridden by
+GIT_NOTES_REF) is also implicitly added to the list of refs to be
+displayed.
+
+notes.rewrite.<command>::
+       When rewriting commits with <command> (currently `amend` or
+       `rebase`) and this variable is set to `true`, git
+       automatically copies your notes from the original to the
+       rewritten commit.  Defaults to `true`, but see
+       "notes.rewriteRef" below.
++
+This setting can be overridden with the `GIT_NOTES_REWRITE_REF`
+environment variable, which must be a colon separated list of refs or
+globs.
+
+notes.rewriteMode::
+       When copying notes during a rewrite (see the
+       "notes.rewrite.<command>" option), determines what to do if
+       the target commit already has a note.  Must be one of
+       `overwrite`, `concatenate`, or `ignore`.  Defaults to
+       `concatenate`.
++
+This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
+environment variable.
+
+notes.rewriteRef::
+       When copying notes during a rewrite, specifies the (fully
+       qualified) ref whose notes should be copied.  The ref may be a
+       glob, in which case notes in all matching refs will be copied.
+       You may also specify this configuration several times.
++
+Does not have a default value; you must configure this variable to
+enable note rewriting.
+
 pack.window::
        The size of the window used by linkgit:git-pack-objects[1] when no
        window size is given on the command line. Defaults to 10.
@@ -1439,7 +1522,7 @@ receive.denyCurrentBranch::
        out of sync with the index and working tree. If set to "warn",
        print a warning of such a push to stderr, but allow the push to
        proceed. If set to false or "ignore", allow such pushes with no
-       message. Defaults to "warn".
+       message. Defaults to "refuse".
 
 receive.denyNonFastForwards::
        If set to true, git-receive-pack will deny a ref update which is
index 0f25ba7e3857e6c4f18c3589b31f082b602df6dc..8f9a2412fd44c80f2bf8d63271e2897ebeb3ddeb 100644 (file)
@@ -56,7 +56,8 @@ combined diff format
 
 "git-diff-tree", "git-diff-files" and "git-diff" can take '-c' or
 '--cc' option to produce 'combined diff'.  For showing a merge commit
-with "git log -p", this is the default format.
+with "git log -p", this is the default format; you can force showing
+full diff with the '-m' option.
 A 'combined diff' format looks like this:
 
 ------------
index 8707d0e7404543d0565d72566a8db5946d9467ee..60e922e6eff888f83a92d3747579ef45d993528a 100644 (file)
@@ -117,12 +117,14 @@ any of those replacements occurred.
        option and lists the commits in that commit range like the 'summary'
        option of linkgit:git-submodule[1] does.
 
---color::
+--color[=<when>]::
        Show colored diff.
+       The value must be always (the default), never, or auto.
 
 --no-color::
        Turn off colored diff, even when the configuration file
        gives the default to color output.
+       Same as `--color=never`.
 
 --color-words[=<regex>]::
        Show colored word diff, i.e., color words which have changed.
index fe716b2e42642de5c6eefe600b98382069b41247..044ec882ccf9f8437862a5e59a3caee7030c36ae 100644 (file)
@@ -78,9 +78,16 @@ ifndef::git-pull[]
 -q::
 --quiet::
        Pass --quiet to git-fetch-pack and silence any other internally
-       used git commands.
+       used git commands. Progress is not reported to the standard error
+       stream.
 
 -v::
 --verbose::
        Be verbose.
 endif::git-pull[]
+
+--progress::
+       Progress status is reported on the standard error stream
+       by default when it is attached to a terminal, unless -q
+       is specified. This flag forces progress status even if the
+       standard error stream is not directed to a terminal.
index c66c565bbe3d0e25ded50786e5503b749ad72163..9e62f8778f6590328de4d9c0c7c08044e8019eac 100644 (file)
@@ -9,7 +9,7 @@ git-am - Apply a series of patches from a mailbox
 SYNOPSIS
 --------
 [verse]
-'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
+'git am' [--signoff] [--keep] [--keep-cr | --no-keep-cr] [--utf8 | --no-utf8]
         [--3way] [--interactive] [--committer-date-is-author-date]
         [--ignore-date] [--ignore-space-change | --ignore-whitespace]
         [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
@@ -39,12 +39,19 @@ OPTIONS
 --keep::
        Pass `-k` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--keep-cr::
+--no-keep-cr::
+       With `--keep-cr`, call 'git mailsplit' (see linkgit:git-mailsplit[1])
+       with the same option, to prevent it from stripping CR at the end of
+       lines. `am.keepcr` configuration variable can be used to specify the
+       default behaviour.  `--no-keep-cr` is useful to override `am.keepcr`.
+
 -c::
 --scissors::
        Remove everything in body before a scissors line (see
        linkgit:git-mailinfo[1]).
 
----no-scissors::
+--no-scissors::
        Ignore scissors lines (see linkgit:git-mailinfo[1]).
 
 -q::
index 6b6c3da2d95ad2d5d94949034d5dd723f48d977a..903a690f10e3cdb7aea15a19332bf0e8bc87814e 100644 (file)
@@ -8,7 +8,7 @@ git-branch - List, create, or delete branches
 SYNOPSIS
 --------
 [verse]
-'git branch' [--color | --no-color] [-r | -a]
+'git branch' [--color[=<when>] | --no-color] [-r | -a]
        [-v [--abbrev=<length> | --no-abbrev]]
        [(--merged | --no-merged | --contains) [<commit>]]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
@@ -84,12 +84,14 @@ OPTIONS
 -M::
        Move/rename a branch even if the new branch name already exists.
 
---color::
+--color[=<when>]::
        Color branches to highlight current, local, and remote branches.
+       The value must be always (the default), never, or auto.
 
 --no-color::
        Turn off branch colors, even when the configuration file gives the
        default to color output.
+       Same as `--color=never`.
 
 -r::
        List or delete (if used with -d) the remote-tracking branches.
index e1c4320f02747cd8681c03f52ea22d64290d96ca..379eee6734323ee566480d5f9159dd28ed27ecd2 100644 (file)
@@ -19,8 +19,9 @@ status if it is not.
 
 A reference is used in git to specify branches and tags.  A
 branch head is stored under the `$GIT_DIR/refs/heads` directory, and
-a tag is stored under the `$GIT_DIR/refs/tags` directory.  git
-imposes the following rules on how references are named:
+a tag is stored under the `$GIT_DIR/refs/tags` directory (or, if refs
+are packed by `git gc`, as entries in the `$GIT_DIR/packed-refs` file).
+git imposes the following rules on how references are named:
 
 . They can include slash `/` for hierarchical (directory)
   grouping, but no slash-separated component can begin with a
index f43c8b2c08ab4ca45f759369dc3d8e85c9f76415..dc7d3d17b151d05827925fdcd2806945e2e278cb 100644 (file)
@@ -29,7 +29,7 @@ arguments will in addition merge the remote master branch into the
 current master branch, if any.
 
 This default configuration is achieved by creating references to
-the remote branch heads under `$GIT_DIR/refs/remotes/origin` and
+the remote branch heads under `refs/remotes/origin` and
 by initializing `remote.origin.url` and `remote.origin.fetch`
 configuration variables.
 
@@ -102,7 +102,8 @@ objects from the source repository into a pack in the cloned repository.
 
 --verbose::
 -v::
-       Run verbosely.
+       Run verbosely. Does not affect the reporting of progress status
+       to the standard error stream.
 
 --progress::
        Progress status is reported on the standard error stream
@@ -149,8 +150,7 @@ objects from the source repository into a pack in the cloned repository.
 
 --template=<template_directory>::
        Specify the directory from which templates will be used;
-       if unset the templates are taken from the installation
-       defined default, typically `/usr/share/git-core/templates`.
+       (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
 
 --depth <depth>::
        Create a 'shallow' clone with a history truncated to the
@@ -187,7 +187,7 @@ include::urls.txt[]
 Examples
 --------
 
-Clone from upstream::
+* Clone from upstream:
 +
 ------------
 $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
@@ -196,7 +196,7 @@ $ make
 ------------
 
 
-Make a local clone that borrows from the current directory, without checking things out::
+* Make a local clone that borrows from the current directory, without checking things out:
 +
 ------------
 $ git clone -l -s -n . ../copy
@@ -205,7 +205,7 @@ $ git show-branch
 ------------
 
 
-Clone from upstream while borrowing from an existing local directory::
+* Clone from upstream while borrowing from an existing local directory:
 +
 ------------
 $ git clone --reference my2.6 \
@@ -215,14 +215,14 @@ $ cd my2.7
 ------------
 
 
-Create a bare repository to publish your changes to the public::
+* Create a bare repository to publish your changes to the public:
 +
 ------------
 $ git clone --bare -l /home/proj/.git /pub/scm/proj.git
 ------------
 
 
-Create a repository on the kernel.org machine that borrows from Linus::
+* Create a repository on the kernel.org machine that borrows from Linus:
 +
 ------------
 $ git clone --bare -l -s /pub/scm/.../torvalds/linux-2.6.git \
index e99bb14754c29a7aee8d16ba27d69a403707b85d..64fb458b4533eeda9c583ac926025495b260cf9b 100644 (file)
@@ -197,13 +197,13 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
        Show untracked files (Default: 'all').
 +
 The mode parameter is optional, and is used to specify
-the handling of untracked files. The possible options are:
+the handling of untracked files.
++
+The possible options are:
 +
---
        - 'no'     - Show no untracked files
        - 'normal' - Shows untracked files and directories
        - 'all'    - Also shows individual files in untracked directories.
---
 +
 See linkgit:git-config[1] for configuration variable
 used to change the default for when the option is not
index ddfcb3d143239174d600c8fde9dbb58b772fff4b..8bcd875a677ab6ff91fc4600d541eac4750efce7 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
              [-A <author-conv-file>] [-p <options-for-cvsps>] [-P <file>]
              [-C <git_repository>] [-z <fuzz>] [-i] [-k] [-u] [-s <subst>]
              [-a] [-m] [-M <regex>] [-S <regex>] [-L <commitlimit>]
-             [-r <remote>] [<CVS_module>]
+             [-r <remote>] [-R] [<CVS_module>]
 
 
 DESCRIPTION
@@ -157,6 +157,22 @@ It is not recommended to use this feature if you intend to
 export changes back to CVS again later with
 'git cvsexportcommit'.
 
+-R::
+       Generate a `$GIT_DIR/cvs-revisions` file containing a mapping from CVS
+       revision numbers to newly-created Git commit IDs.  The generated file
+       will contain one line for each (filename, revision) pair imported;
+       each line will look like
++
+---------
+src/widget.c 1.1 1d862f173cdc7325b6fa6d2ae1cfd61fd1b512b7
+---------
++
+The revision data is appended to the file if it already exists, for use when
+doing incremental imports.
++
+This option may be useful if you have CVS revision numbers stored in commit
+messages, bug-tracking systems, email archives, and the like.
+
 -h::
        Print a short usage message and exit.
 
index 6fc5323ee6a9d3cd22f8c68ebaf514f26c82f684..7ef9d51577594ae6a27f71bae251d6ee2f52befa 100644 (file)
@@ -105,6 +105,9 @@ The number of additional commits is the number
 of commits which would be displayed by "git log v1.0.4..parent".
 The hash suffix is "-g" + 7-char abbreviation for the tip commit
 of parent (which was `2414721b194453f058079d897d13c4e377f92dc6`).
+The "g" prefix stands for "git" and is used to allow describing the version of
+a software depending on the SCM the software is managed with. This is useful
+in an environment where people may use different SCMs.
 
 Doing a 'git describe' on a tag-name will just show the tag name:
 
index 6764ff188688070bfea379de7dccc13a1557cb20..19082b04eb7ec77bcf974e0c76b8d20bb736c021 100644 (file)
@@ -45,10 +45,7 @@ OPTIONS
 
 --max-pack-size=<n>::
        Maximum size of each output packfile.
-       The default is 4 GiB as that is the maximum allowed
-       packfile size (due to file format limitations). Some
-       importers may wish to lower this, such as to ensure the
-       resulting packfiles fit on CDs.
+       The default is unlimited.
 
 --big-file-threshold=<n>::
        Maximum size of a blob that fast-import will attempt to
index e9952e82108d3a0fa209bc9d720fa1f1229310b4..4a8487c1547abc156c8fad9e7cafe4d3c5a535f6 100644 (file)
@@ -18,7 +18,7 @@ higher level wrapper of this command, instead.
 Invokes 'git-upload-pack' on a possibly remote repository
 and asks it to send objects missing from this repository, to
 update the named heads.  The list of commits available locally
-is found out by scanning local $GIT_DIR/refs/ and sent to
+is found out by scanning the local refs/ hierarchy and sent to
 'git-upload-pack' running on the other end.
 
 This command degenerates to download everything to complete the
@@ -44,8 +44,8 @@ OPTIONS
        locked against repacking.
 
 --thin::
-       Spend extra cycles to minimize the number of objects to be sent.
-       Use it on slower connection.
+       Fetch a "thin" pack, which records objects in deltified form based
+       on objects not included in the pack to reduce network traffic.
 
 --include-tag::
        If the remote side supports it, annotated tags objects will
index 9674f9de67b18880b51382caf4c06d85778284b8..835fb7135b9d159281ef8eb81daf699c6aeec57b 100644 (file)
@@ -18,7 +18,7 @@ SYNOPSIS
                   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
                   [--ignore-if-in-upstream]
                   [--subject-prefix=Subject-Prefix]
-                  [--cc=<email>]
+                  [--to=<email>] [--cc=<email>]
                   [--cover-letter]
                   [<common diff options>]
                   [ <since> | <revision range> ]
@@ -162,6 +162,10 @@ will want to ensure that threading is disabled for `git send-email`.
        allows for useful naming of a patch series, and can be
        combined with the `--numbered` option.
 
+--to=<email>::
+       Add a `To:` header to the email headers. This is in addition
+       to any configured headers, and may be used multiple times.
+
 --cc=<email>::
        Add a `Cc:` header to the email headers. This is in addition
        to any configured headers, and may be used multiple times.
@@ -202,8 +206,8 @@ CONFIGURATION
 -------------
 You can specify extra mail header lines to be added to each message,
 defaults for the subject prefix and file suffix, number patches when
-outputting more than one patch, add "Cc:" headers, configure attachments,
-and sign off patches with configuration variables.
+outputting more than one patch, add "To" or "Cc:" headers, configure
+attachments, and sign off patches with configuration variables.
 
 ------------
 [format]
@@ -211,6 +215,7 @@ and sign off patches with configuration variables.
        subjectprefix = CHANGE
        suffix = .txt
        numbered = auto
+       to = <email>
        cc = <email>
        attach [ = mime-boundary-string ]
        signoff = true
index e019e760b4b4d58dfbe8819941947008319aedac..4b32322a67d9f3de8b555d3d9d03e7ddab14e0a7 100644 (file)
@@ -9,8 +9,7 @@ git-grep - Print lines matching a pattern
 SYNOPSIS
 --------
 [verse]
-'git grep' [--cached]
-          [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
+'git grep' [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
           [-v | --invert-match] [-h|-H] [--full-name]
           [-E | --extended-regexp] [-G | --basic-regexp]
           [-F | --fixed-strings] [-n]
@@ -18,23 +17,27 @@ SYNOPSIS
           [-z | --null]
           [-c | --count] [--all-match] [-q | --quiet]
           [--max-depth <depth>]
-          [--color | --no-color]
+          [--color[=<when>] | --no-color]
           [-A <post-context>] [-B <pre-context>] [-C <context>]
           [-f <file>] [-e] <pattern>
-          [--and|--or|--not|(|)|-e <pattern>...] [<tree>...]
-          [--] [<path>...]
+          [--and|--or|--not|(|)|-e <pattern>...]
+          [--cached | --no-index | <tree>...]
+          [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
-Look for specified patterns in the working tree files, blobs
-registered in the index file, or given tree objects.
+Look for specified patterns in the tracked files in the work tree, blobs
+registered in the index file, or blobs in given tree objects.
 
 
 OPTIONS
 -------
 --cached::
-       Instead of searching in the working tree files, check
-       the blobs registered in the index file.
+       Instead of searching tracked files in the working tree, search
+       blobs registered in the index file.
+
+--no-index::
+       Search files in the current directory, not just those tracked by git.
 
 -a::
 --text::
@@ -49,7 +52,7 @@ OPTIONS
        Don't match the pattern in binary files.
 
 --max-depth <depth>::
-       For each pathspec given on command line, descend at most <depth>
+       For each <pathspec> given on command line, descend at most <depth>
        levels of directories. A negative value means no limit.
 
 -w::
@@ -98,8 +101,8 @@ OPTIONS
 --files-without-match::
        Instead of showing every matched line, show only the
        names of files that contain (or do not contain) matches.
-       For better compatibility with 'git diff', --name-only is a
-       synonym for --files-with-matches.
+       For better compatibility with 'git diff', `--name-only` is a
+       synonym for `--files-with-matches`.
 
 -z::
 --null::
@@ -111,12 +114,14 @@ OPTIONS
        Instead of showing every matched line, show the number of
        lines that match.
 
---color::
+--color[=<when>]::
        Show colored matches.
+       The value must be always (the default), never, or auto.
 
 --no-color::
        Turn off match highlighting, even when the configuration file
        gives the default to color output.
+       Same as `--color=never`.
 
 -[ABC] <context>::
        Show `context` trailing (`A` -- after), or leading (`B`
@@ -125,7 +130,7 @@ OPTIONS
        matches.
 
 -<num>::
-       A shortcut for specifying -C<num>.
+       A shortcut for specifying `-C<num>`.
 
 -p::
 --show-function::
@@ -140,7 +145,7 @@ OPTIONS
 
 -e::
        The next parameter is the pattern. This option has to be
-       used for patterns starting with - and should be used in
+       used for patterns starting with `-` and should be used in
        scripts passing user input to grep.  Multiple patterns are
        combined by 'or'.
 
@@ -163,16 +168,24 @@ OPTIONS
        Do not output matched lines; instead, exit with status 0 when
        there is a match and with non-zero status when there isn't.
 
-`<tree>...`::
-       Search blobs in the trees for specified patterns.
+<tree>...::
+       Instead of searching tracked files in the working tree, search
+       blobs in the given trees.
 
 \--::
        Signals the end of options; the rest of the parameters
-       are <path> limiters.
+       are <pathspec> limiters.
 
+<pathspec>...::
+       If given, limit the search to paths matching at least one pattern.
+       Both leading paths match and glob(7) patterns are supported.
 
-Example
--------
+Examples
+--------
+
+git grep 'time_t' -- '*.[ch]'::
+       Looks for `time_t` in all tracked .c and .h files in the working
+       directory and its subdirectories.
 
 git grep -e \'#define\' --and \( -e MAX_PATH -e PATH_MAX \)::
        Looks for a line that has `#define` and either `MAX_PATH` or
index 479fce4693982526f48c32a36e5215bf13dcc3e6..6904739a48443ba255b2506ad92c10f91fa61d21 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...
-'git hash-object' [-t <type>] [-w] --stdin-paths < <list-of-paths>
+'git hash-object' [-t <type>] [-w] --stdin-paths [--no-filters] < <list-of-paths>
 
 DESCRIPTION
 -----------
index 57db955bd4cc82f643ed5b932b33d440d8cf409a..6cafbe2ec191b2e2b35e7855c71837fa0f231785 100644 (file)
@@ -71,6 +71,10 @@ imap.preformattedHTML::
        option causes Thunderbird to send the patch as a plain/text,
        format=fixed email.  Default is `false`.
 
+imap.authMethod::
+       Specify authenticate method for authentication with IMAP server.
+       Current supported method is 'CRAM-MD5' only.
+
 Examples
 ~~~~~~~~
 
index 65a301becefb837b74bffa2d2dd11d51e229a06a..f3ccc72f0d68f03cd7b3fb6a36aabc8d7c7772a1 100644 (file)
@@ -46,14 +46,10 @@ OPTIONS
        'git repack'.
 
 --fix-thin::
-       It is possible for 'git pack-objects' to build
-       "thin" pack, which records objects in deltified form based on
-       objects not included in the pack to reduce network traffic.
-       Those objects are expected to be present on the receiving end
-       and they must be included in the pack for that pack to be self
-       contained and indexable. Without this option any attempt to
-       index a thin pack will fail. This option only makes sense in
-       conjunction with --stdin.
+       Fix a "thin" pack produced by `git pack-objects --thin` (see
+       linkgit:git-pack-objects[1] for details) by adding the
+       excluded objects the deltified objects are based on to the
+       pack. This option only makes sense in conjunction with --stdin.
 
 --keep::
        Before moving the index into its final destination
index 7ee102da485d0b6490c0b767a20966956bf0bd88..246b07ebf94394d2079c2acc3e8c1a2e6c40bba9 100644 (file)
@@ -28,14 +28,8 @@ current working directory.
 
 --template=<template_directory>::
 
-Provide the directory from which templates will be used.  The default template
-directory is `/usr/share/git-core/templates`.
-
-When specified, `<template_directory>` is used as the source of the template
-files rather than the default.  The template files include some directory
-structure, some suggested "exclude patterns", and copies of non-executing
-"hook" files.  The suggested patterns and hook files are all modifiable and
-extensible.
+Specify the directory from which templates will be used.  (See the "TEMPLATE
+DIRECTORY" section below.)
 
 --shared[={false|true|umask|group|all|world|everybody|0xxx}]::
 
@@ -106,6 +100,25 @@ of the repository, such as installing the default hooks and
 setting the configuration variables.  The old name is retained
 for backward compatibility reasons.
 
+TEMPLATE DIRECTORY
+------------------
+
+The template directory contains files and directories that will be copied to
+the `$GIT_DIR` after it is created.
+
+The template directory used will (in order):
+
+ - The argument given with the `--template` option.
+
+ - The contents of the `$GIT_TEMPLATE_DIR` environment variable.
+
+ - The `init.templatedir` configuration variable.
+
+ - The default template directory: `/usr/share/git-core/templates`.
+
+The default template directory includes some directory structure, some
+suggested "exclude patterns", and copies of sample "hook" files.
+The suggested patterns and hook files are all modifiable and extensible.
 
 EXAMPLES
 --------
index 0e39bb61eebfce5d1bff032c65bf04bb77f8ce62..fb184ba1863845797a5f296581ec74a1b31bef1a 100644 (file)
@@ -118,6 +118,15 @@ git log master --not --remotes=*/master::
        Shows all commits that are in local master but not in any remote
        repository master branches.
 
+git log -p -m --first-parent::
+
+       Shows the history including change diffs, but only from the
+       "main branch" perspective, skipping commits that come from merged
+       branches, and showing full diffs of changes introduced by the merges.
+       This makes sense only when following a strict policy of merging all
+       topic branches when staying on a single integration branch.
+
+
 Discussion
 ----------
 
index 5cc94ec53daf3057f57c993983d659543962abec..a634485281154cb665c8168cc3469dc0f5051697 100644 (file)
@@ -7,7 +7,7 @@ git-mailsplit - Simple UNIX mbox splitter program
 
 SYNOPSIS
 --------
-'git mailsplit' [-b] [-f<nn>] [-d<prec>] -o<directory> [--] [<mbox>|<Maildir>...]
+'git mailsplit' [-b] [-f<nn>] [-d<prec>] [--keep-cr] -o<directory> [--] [<mbox>|<Maildir>...]
 
 DESCRIPTION
 -----------
@@ -43,6 +43,9 @@ OPTIONS
        Skip the first <nn> numbers, for example if -f3 is specified,
        start the numbering with 0004.
 
+--keep-cr::
+       Do not remove `\r` from lines ending with `\r\n`.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
index 234269ae59234de67ea2cce42edec83cebc400be..f334d694e0160df91d197293e5b76cc0bfaac187 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]]
-       [--ours|--theirs] [-p|--stdout] [-q|--quiet]
+       [--ours|--theirs|--union] [-p|--stdout] [-q|--quiet] [--marker-size=<n>]
        <current-file> <base-file> <other-file>
 
 
@@ -35,9 +35,10 @@ normally outputs a warning and brackets the conflict with lines containing
        >>>>>>> B
 
 If there are conflicts, the user should edit the result and delete one of
-the alternatives.  When `--ours` or `--theirs` option is in effect, however,
-these conflicts are resolved favouring lines from `<current-file>` or
-lines from `<other-file>` respectively.
+the alternatives.  When `--ours`, `--theirs`, or `--union` option is in effect,
+however, these conflicts are resolved favouring lines from `<current-file>`,
+lines from `<other-file>`, or lines from both respectively.  The length of the
+conflict markers can be given with the `--marker-size` option.
 
 The exit value of this program is negative on error, and the number of
 conflicts otherwise. If the merge was clean, the exit value is 0.
@@ -67,8 +68,9 @@ OPTIONS
 
 --ours::
 --theirs::
+--union::
        Instead of leaving conflicts in the file, resolve conflicts
-       favouring our (or their) side of the lines.
+       favouring our (or their or both) side of the lines.
 
 
 EXAMPLES
index d4487cab5284670644f88d95680581ffd29ae952..4e5113b837230c8f050cf4bbe10e8de4b3271bd0 100644 (file)
@@ -3,57 +3,146 @@ git-notes(1)
 
 NAME
 ----
-git-notes - Add/inspect commit notes
+git-notes - Add/inspect object notes
 
 SYNOPSIS
 --------
 [verse]
-'git notes' (edit [-F <file> | -m <msg>] | show) [commit]
+'git notes' [list [<object>]]
+'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' copy [-f] ( --stdin | <from-object> <to-object> )
+'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' edit [<object>]
+'git notes' show [<object>]
+'git notes' remove [<object>]
+'git notes' prune
+
 
 DESCRIPTION
 -----------
-This command allows you to add notes to commit messages, without
-changing the commit.  To discern these notes from the message stored
-in the commit object, the notes are indented like the message, after
-an unindented line saying "Notes:".
+This command allows you to add/remove notes to/from objects, without
+changing the objects themselves.
+
+A typical use of notes is to extend a commit message without having
+to change the commit itself. Such commit notes can be shown by `git log`
+along with the original commit message. To discern these notes from the
+message stored in the commit object, the notes are indented like the
+message, after an unindented line saying "Notes (<refname>):" (or
+"Notes:" for the default setting).
 
-To disable commit notes, you have to set the config variable
-core.notesRef to the empty string.  Alternatively, you can set it
-to a different ref, something like "refs/notes/bugzilla".  This setting
-can be overridden by the environment variable "GIT_NOTES_REF".
+This command always manipulates the notes specified in "core.notesRef"
+(see linkgit:git-config[1]), which can be overridden by GIT_NOTES_REF.
+To change which notes are shown by 'git-log', see the
+"notes.displayRef" configuration.
+
+See the description of "notes.rewrite.<command>" in
+linkgit:git-config[1] for a way of carrying your notes across commands
+that rewrite commits.
 
 
 SUBCOMMANDS
 -----------
 
+list::
+       List the notes object for a given object. If no object is
+       given, show a list of all note objects and the objects they
+       annotate (in the format "<note object> <annotated object>").
+       This is the default subcommand if no subcommand is given.
+
+add::
+       Add notes for a given object (defaults to HEAD). Abort if the
+       object already has notes (use `-f` to overwrite an
+       existing note).
+
+copy::
+       Copy the notes for the first object onto the second object.
+       Abort if the second object already has notes, or if the first
+       object has none (use -f to overwrite existing notes to the
+       second object). This subcommand is equivalent to:
+       `git notes add [-f] -C $(git notes list <from-object>) <to-object>`
++
+In `\--stdin` mode, take lines in the format
++
+----------
+<from-object> SP <to-object> [ SP <rest> ] LF
+----------
++
+on standard input, and copy the notes from each <from-object> to its
+corresponding <to-object>.  (The optional `<rest>` is ignored so that
+the command can read the input given to the `post-rewrite` hook.)
+
+append::
+       Append to the notes of an existing object (defaults to HEAD).
+       Creates a new notes object if needed.
+
 edit::
-       Edit the notes for a given commit (defaults to HEAD).
+       Edit the notes for a given object (defaults to HEAD).
 
 show::
-       Show the notes for a given commit (defaults to HEAD).
+       Show the notes for a given object (defaults to HEAD).
+
+remove::
+       Remove the notes for a given object (defaults to HEAD).
+       This is equivalent to specifying an empty note message to
+       the `edit` subcommand.
 
+prune::
+       Remove all notes for non-existing/unreachable objects.
 
 OPTIONS
 -------
+-f::
+--force::
+       When adding notes to an object that already has notes,
+       overwrite the existing notes (instead of aborting).
+
 -m <msg>::
+--message=<msg>::
        Use the given note message (instead of prompting).
-       If multiple `-m` (or `-F`) options are given, their
-       values are concatenated as separate paragraphs.
+       If multiple `-m` options are given, their values
+       are concatenated as separate paragraphs.
 
 -F <file>::
+--file=<file>::
        Take the note message from the given file.  Use '-' to
        read the note message from the standard input.
-       If multiple `-F` (or `-m`) options are given, their
-       values are concatenated as separate paragraphs.
+
+-C <object>::
+--reuse-message=<object>::
+       Reuse the note message from the given note object.
+
+-c <object>::
+--reedit-message=<object>::
+       Like '-C', but with '-c' the editor is invoked, so that
+       the user can further edit the note message.
+
+--ref <ref>::
+       Manipulate the notes tree in <ref>.  This overrides both
+       GIT_NOTES_REF and the "core.notesRef" configuration.  The ref
+       is taken to be in `refs/notes/` if it is not qualified.
+
+
+NOTES
+-----
+
+Every notes change creates a new commit at the specified notes ref.
+You can therefore inspect the history of the notes by invoking, e.g.,
+`git log -p notes/commits`.
+
+Currently the commit message only records which operation triggered
+the update, and the commit authorship is determined according to the
+usual rules (see linkgit:git-commit[1]).  These details may change in
+the future.
 
 
 Author
 ------
-Written by Johannes Schindelin <johannes.schindelin@gmx.de>
+Written by Johannes Schindelin <johannes.schindelin@gmx.de> and
+Johan Herland <johan@herland.net>
 
 Documentation
 -------------
-Documentation by Johannes Schindelin
+Documentation by Johannes Schindelin and Johan Herland
 
 GIT
 ---
index ffd5025f7bdf68b8285f2c98c2c1b37c551e2cb4..8ed09c0b3cfc0fff2dbb8907fdc00e027c2c6d33 100644 (file)
@@ -21,16 +21,21 @@ DESCRIPTION
 Reads list of objects from the standard input, and writes a packed
 archive with specified base-name, or to the standard output.
 
-A packed archive is an efficient way to transfer set of objects
-between two repositories, and also is an archival format which
-is efficient to access.  The packed archive format (.pack) is
-designed to be self contained so that it can be unpacked without
-any further information, but for fast, random access to the objects
-in the pack, a pack index file (.idx) will be generated.
-
-Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
+A packed archive is an efficient way to transfer a set of objects
+between two repositories as well as an access efficient archival
+format.  In a packed archive, an object is either stored as a
+compressed whole or as a difference from some other object.
+The latter is often called a delta.
+
+The packed archive format (.pack) is designed to be self-contained
+so that it can be unpacked without any further information. Therefore,
+each object that a delta depends upon must be present within the pack.
+
+A pack index file (.idx) is generated for fast, random access to the
+objects in the pack. Placing both the index file (.idx) and the packed
+archive (.pack) in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
 any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES)
-enables git to read from such an archive.
+enables git to read from the pack archive.
 
 The 'git unpack-objects' command can read the packed archive and
 expand the objects contained in the pack into "one-file
@@ -38,10 +43,6 @@ one-object" format; this is typically done by the smart-pull
 commands when a pack is created on-the-fly for efficient network
 transport by their peers.
 
-In a packed archive, an object is either stored as a compressed
-whole, or as a difference from some other object.  The latter is
-often called a delta.
-
 
 OPTIONS
 -------
@@ -73,7 +74,7 @@ base-name::
 --all::
        This implies `--revs`.  In addition to the list of
        revision arguments read from the standard input, pretend
-       as if all refs under `$GIT_DIR/refs` are specified to be
+       as if all refs under `refs/` are specified to be
        included.
 
 --include-tag::
@@ -114,18 +115,17 @@ base-name::
 
 --honor-pack-keep::
        This flag causes an object already in a local pack that
-       has a .keep file to be ignored, even if it appears in the
-       standard input.
+       has a .keep file to be ignored, even if it it would have
+       otherwise been packed.
 
 --incremental::
-       This flag causes an object already in a pack ignored
-       even if it appears in the standard input.
+       This flag causes an object already in a pack to be ignored
+       even if it would have otherwise been packed.
 
 --local::
-       This flag is similar to `--incremental`; instead of
-       ignoring all packed objects, it only ignores objects
-       that are packed and/or not in the local object store
-       (i.e. borrowed from an alternate).
+       This flag causes an object that is borrowed from an alternate
+       object store to be ignored even if it would have otherwise been
+       packed.
 
 --non-empty::
         Only create a packed archive if it would contain at
@@ -179,6 +179,16 @@ base-name::
        Add --no-reuse-object if you want to force a uniform compression
        level on all data no matter the source.
 
+--thin::
+       Create a "thin" pack by omitting the common objects between a
+       sender and a receiver in order to reduce network transfer. This
+       option only makes sense in conjunction with --stdout.
++
+Note: A thin pack violates the packed archive format by omitting
+required objects and is thus unusable by git without making it
+self-contained. Use `git index-pack --fix-thin`
+(see linkgit:git-index-pack[1]) to restore the self-contained property.
+
 --delta-base-offset::
        A packed archive can express base object of a delta as
        either 20-byte object name or as an offset in the
index 3bb7304517ea3adfd2b5b89b5e75f9d830e57aff..15cfb7a8dc8e5e6eb25ee58b8fe15d89ad62dc8a 100644 (file)
@@ -17,7 +17,7 @@ NOTE: In most cases, users should run 'git gc', which calls
 'git prune'. See the section "NOTES", below.
 
 This runs 'git fsck --unreachable' using all the refs
-available in `$GIT_DIR/refs`, optionally with additional set of
+available in `refs/`, optionally with additional set of
 objects specified on the command line, and prunes all unpacked
 objects unreachable from any of these head objects from the object database.
 In addition, it
index 31f42ea21a249abfa1ab2e220a077fee30d3d5e4..ab4de103586e8382801dad7de2f43c57f4758e7e 100644 (file)
@@ -31,6 +31,16 @@ in a state that is hard to back out of in the case of a conflict.
 OPTIONS
 -------
 
+-q::
+--quiet::
+       This is passed to both underlying git-fetch to squelch reporting of
+       during transfer, and underlying git-merge to squelch output during
+       merging.
+
+-v::
+--verbose::
+       Pass --verbose to git-fetch and git-merge.
+
 Options related to merging
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index bd79119dd36092f7b31c156a2ca72c7969cd7586..59dc8b197ecb4b6912c9ec29f83f21c405ef8634 100644 (file)
@@ -69,11 +69,11 @@ nor in any Push line of the corresponding remotes file---see below).
 
 --all::
        Instead of naming each ref to push, specifies that all
-       refs under `$GIT_DIR/refs/heads/` be pushed.
+       refs under `refs/heads/` be pushed.
 
 --mirror::
        Instead of naming each ref to push, specifies that all
-       refs under `$GIT_DIR/refs/` (which includes but is not
+       refs under `refs/` (which includes but is not
        limited to `refs/heads/`, `refs/remotes/`, and `refs/tags/`)
        be mirrored to the remote repository.  Newly created local
        refs will be pushed to the remote end, locally updated refs
@@ -96,7 +96,7 @@ nor in any Push line of the corresponding remotes file---see below).
        the same as prefixing all refs with a colon.
 
 --tags::
-       All refs under `$GIT_DIR/refs/tags` are pushed, in
+       All refs under `refs/tags` are pushed, in
        addition to refspecs explicitly listed on the command
        line.
 
@@ -141,18 +141,26 @@ useful if you write an alias or script around 'git push'.
 
 --thin::
 --no-thin::
-       These options are passed to 'git send-pack'.  Thin
-       transfer spends extra cycles to minimize the number of
-       objects to be sent and meant to be used on slower connection.
+       These options are passed to linkgit:git-send-pack[1]. A thin transfer
+       significantly reduces the amount of sent data when the sender and
+       receiver share many of the same objects in common. The default is
+       \--thin.
+
+-q::
+--quiet::
+       Suppress all output, including the listing of updated refs,
+       unless an error occurs. Progress is not reported to the standard
+       error stream.
 
 -v::
 --verbose::
        Run verbosely.
 
--q::
---quiet::
-       Suppress all output, including the listing of updated refs,
-       unless an error occurs.
+--progress::
+       Progress status is reported on the standard error stream
+       by default when it is attached to a terminal, unless -q
+       is specified. This flag forces progress status even if the
+       standard error stream is not directed to a terminal.
 
 include::urls-remotes.txt[]
 
index 567671c013589d10b04b2239062dec3b1f6cd787..f6037c4f6a7c6988a9a497455e555979d44e2600 100644 (file)
@@ -130,7 +130,7 @@ Single Tree Merge
 ~~~~~~~~~~~~~~~~~
 If only 1 tree is specified, 'git read-tree' operates as if the user did not
 specify `-m`, except that if the original index has an entry for a
-given pathname, and the contents of the path matches with the tree
+given pathname, and the contents of the path match with the tree
 being read, the stat info from the index is used. (In other words, the
 index's stat()s take precedence over the merged tree's).
 
@@ -154,40 +154,42 @@ When two trees are specified, the user is telling 'git read-tree'
 the following:
 
      1. The current index and work tree is derived from $H, but
-        the user may have local changes in them since $H;
+       the user may have local changes in them since $H.
 
      2. The user wants to fast-forward to $M.
 
 In this case, the `git read-tree -m $H $M` command makes sure
 that no local change is lost as the result of this "merge".
-Here are the "carry forward" rules:
+Here are the "carry forward" rules, where "I" denotes the index,
+"clean" means that index and work tree coincide, and "exists"/"nothing"
+refer to the presence of a path in the specified commit:
 
-        I (index)           H        M        Result
+       I                   H        M        Result
        -------------------------------------------------------
-      0 nothing             nothing  nothing  (does not happen)
-      1 nothing             nothing  exists   use M
-      2 nothing             exists   nothing  remove path from index
-      3 nothing             exists   exists,  use M if "initial checkout"
+      nothing             nothing  nothing  (does not happen)
+      nothing             nothing  exists   use M
+      nothing             exists   nothing  remove path from index
+     3  nothing             exists   exists,  use M if "initial checkout",
                                     H == M   keep index otherwise
-                                    exists   fail
+                                    exists,  fail
                                     H != M
 
         clean I==H  I==M
        ------------------
-      4 yes   N/A   N/A     nothing  nothing  keep index
-      5 no    N/A   N/A     nothing  nothing  keep index
+      yes   N/A   N/A     nothing  nothing  keep index
+      no    N/A   N/A     nothing  nothing  keep index
 
-      6 yes   N/A   yes     nothing  exists   keep index
-      7 no    N/A   yes     nothing  exists   keep index
-      8 yes   N/A   no      nothing  exists   fail
-      9 no    N/A   no      nothing  exists   fail
+      yes   N/A   yes     nothing  exists   keep index
+      no    N/A   yes     nothing  exists   keep index
+      yes   N/A   no      nothing  exists   fail
+      no    N/A   no      nothing  exists   fail
 
      10 yes   yes   N/A     exists   nothing  remove path from index
      11 no    yes   N/A     exists   nothing  fail
      12 yes   no    N/A     exists   nothing  fail
      13 no    no    N/A     exists   nothing  fail
 
-        clean (H=M)
+       clean (H==M)
        ------
      14 yes                 exists   exists   keep index
      15 no                  exists   exists   keep index
@@ -202,26 +204,26 @@ Here are the "carry forward" rules:
      21 no    yes   no      exists   exists   fail
 
 In all "keep index" cases, the index entry stays as in the
-original index file.  If the entry were not up to date,
+original index file.  If the entry is not up to date,
 'git read-tree' keeps the copy in the work tree intact when
 operating under the -u flag.
 
 When this form of 'git read-tree' returns successfully, you can
-see what "local changes" you made are carried forward by running
+see which of the "local changes" that you made were carried forward by running
 `git diff-index --cached $M`.  Note that this does not
-necessarily match `git diff-index --cached $H` would have
+necessarily match what `git diff-index --cached $H` would have
 produced before such a two tree merge.  This is because of cases
 18 and 19 --- if you already had the changes in $M (e.g. maybe
 you picked it up via e-mail in a patch form), `git diff-index
 --cached $H` would have told you about the change before this
 merge, but it would not show in `git diff-index --cached $M`
-output after two-tree merge.
+output after the two-tree merge.
 
-Case #3 is slightly tricky and needs explanation.  The result from this
+Case 3 is slightly tricky and needs explanation.  The result from this
 rule logically should be to remove the path if the user staged the removal
 of the path and then switching to a new branch.  That however will prevent
 the initial checkout from happening, so the rule is modified to use M (new
-tree) only when the contents of the index is empty.  Otherwise the removal
+tree) only when the content of the index is empty.  Otherwise the removal
 of the path is kept as long as $H and $M are the same.
 
 3-Way Merge
index 802bd5791cdc74dfa487d3a5591aea67c22f6060..4eaa62b6913f15c354e8008c2884f476428ebb0b 100644 (file)
@@ -18,9 +18,7 @@ depending on the subcommand:
 [verse]
 'git reflog expire' [--dry-run] [--stale-fix] [--verbose]
        [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
-+
 'git reflog delete' ref@\{specifier\}...
-+
 'git reflog' ['show'] [log-options] [<ref>]
 
 Reflog is a mechanism to record when the tip of branches are
index 168db08627e009c8d760bae36a4344d433923632..645f0c17485d35e6696be950fc61f5fdcf3dd14e 100644 (file)
@@ -8,7 +8,7 @@ git-reset - Reset current HEAD to the specified state
 SYNOPSIS
 --------
 [verse]
-'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]
+'git reset' [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]
 'git reset' [-q] [<commit>] [--] <paths>...
 'git reset' --patch [<commit>] [--] [<paths>...]
 
@@ -52,6 +52,14 @@ OPTIONS
        and updates the files that are different between the named commit
        and the current commit in the working tree.
 
+--keep::
+       Reset the index to the given commit, keeping local changes in
+       the working tree since the current commit, while updating
+       working tree files without local changes to what appears in
+       the given commit.  If a file that is different between the
+       current commit and the given commit has local changes, reset
+       is aborted.
+
 -p::
 --patch::
        Interactively select hunks in the difference between the index
@@ -93,6 +101,7 @@ in the index and in state D in HEAD.
                                --mixed  A       D     D
                                --hard   D       D     D
                                --merge (disallowed)
+                               --keep  (disallowed)
 
       working index HEAD target         working index HEAD
       ----------------------------------------------------
@@ -100,6 +109,7 @@ in the index and in state D in HEAD.
                                --mixed  A       C     C
                                --hard   C       C     C
                                --merge (disallowed)
+                               --keep   A       C     C
 
       working index HEAD target         working index HEAD
       ----------------------------------------------------
@@ -107,6 +117,7 @@ in the index and in state D in HEAD.
                                --mixed  B       D     D
                                --hard   D       D     D
                                --merge  D       D     D
+                               --keep  (disallowed)
 
       working index HEAD target         working index HEAD
       ----------------------------------------------------
@@ -114,6 +125,7 @@ in the index and in state D in HEAD.
                                --mixed  B       C     C
                                --hard   C       C     C
                                --merge  C       C     C
+                               --keep   B       C     C
 
       working index HEAD target         working index HEAD
       ----------------------------------------------------
@@ -121,6 +133,7 @@ in the index and in state D in HEAD.
                                --mixed  B       D     D
                                --hard   D       D     D
                                --merge (disallowed)
+                               --keep  (disallowed)
 
       working index HEAD target         working index HEAD
       ----------------------------------------------------
@@ -128,6 +141,7 @@ in the index and in state D in HEAD.
                                --mixed  B       C     C
                                --hard   C       C     C
                                --merge  B       C     C
+                               --keep   B       C     C
 
 "reset --merge" is meant to be used when resetting out of a conflicted
 merge. Any mergy operation guarantees that the work tree file that is
@@ -138,6 +152,15 @@ between the index and the work tree, then it means that we are not
 resetting out from a state that a mergy operation left after failing
 with a conflict. That is why we disallow --merge option in this case.
 
+"reset --keep" is meant to be used when removing some of the last
+commits in the current branch while keeping changes in the working
+tree. If there could be conflicts between the changes in the commit we
+want to remove and the changes in the working tree we want to keep,
+the reset is disallowed. That's why it is disallowed if there are both
+changes between the working tree and HEAD, and between HEAD and the
+target. To be safe, it is also disallowed when there are unmerged
+entries.
+
 The following tables show what happens when there are unmerged
 entries:
 
@@ -147,6 +170,7 @@ entries:
                                --mixed  X       B     B
                                --hard   B       B     B
                                --merge  B       B     B
+                               --keep  (disallowed)
 
       working index HEAD target         working index HEAD
       ----------------------------------------------------
@@ -154,6 +178,7 @@ entries:
                                --mixed  X       A     A
                                --hard   A       A     A
                                --merge  A       A     A
+                               --keep  (disallowed)
 
 X means any state and U means an unmerged index.
 
@@ -325,6 +350,32 @@ $ git add frotz.c                           <3>
 <2> This commits all other changes in the index.
 <3> Adds the file to the index again.
 
+Keep changes in working tree while discarding some previous commits::
++
+Suppose you are working on something and you commit it, and then you
+continue working a bit more, but now you think that what you have in
+your working tree should be in another branch that has nothing to do
+with what you commited previously. You can start a new branch and
+reset it while keeping the changes in your work tree.
++
+------------
+$ git tag start
+$ git checkout -b branch1
+$ edit
+$ git commit ...                            <1>
+$ edit
+$ git checkout -b branch2                   <2>
+$ git reset --keep start                    <3>
+------------
++
+<1> This commits your first edits in branch1.
+<2> In the ideal world, you could have realized that the earlier
+    commit did not belong to the new topic when you created and switched
+    to branch2 (i.e. "git checkout -b branch2 start"), but nobody is
+    perfect.
+<3> But you can use "reset --keep" to remove the unwanted commit after
+    you switched to "branch2".
+
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com> and Linus Torvalds <torvalds@osdl.org>
index d677c72d5ea6a8d38cf77f663e6b5da591028efa..8db600f6ba01bcb7f85be6c6606795a0034822b2 100644 (file)
@@ -101,15 +101,14 @@ OPTIONS
        abbreviation mode.
 
 --all::
-       Show all refs found in `$GIT_DIR/refs`.
+       Show all refs found in `refs/`.
 
 --branches[=pattern]::
 --tags[=pattern]::
 --remotes[=pattern]::
        Show all branches, tags, or remote-tracking branches,
-       respectively (i.e., refs found in `$GIT_DIR/refs/heads`,
-       `$GIT_DIR/refs/tags`, or `$GIT_DIR/refs/remotes`,
-       respectively).
+       respectively (i.e., refs found in `refs/heads`,
+       `refs/tags`, or `refs/remotes`, respectively).
 +
 If a `pattern` is given, only refs matching the given shell glob are
 shown.  If the pattern does not contain a globbing character (`?`,
@@ -149,6 +148,12 @@ shown.  If the pattern does not contain a globbing character (`?`,
 --is-bare-repository::
        When the repository is bare print "true", otherwise "false".
 
+--local-env-vars::
+       List the GIT_* environment variables that are local to the
+       repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).
+       Only the names of the variables are listed, not their value,
+       even if they are set.
+
 --short::
 --short=number::
        Instead of outputting the full SHA1 values of object names try to
@@ -189,7 +194,7 @@ blobs contained in a commit.
   `g`, and an abbreviated object name.
 
 * A symbolic ref name.  E.g. 'master' typically means the commit
-  object referenced by $GIT_DIR/refs/heads/master.  If you
+  object referenced by refs/heads/master.  If you
   happen to have both heads/master and tags/master, you can
   explicitly say 'heads/master' to tell git which one you mean.
   When ambiguous, a `<name>` is disambiguated by taking the
@@ -198,15 +203,15 @@ blobs contained in a commit.
   . if `$GIT_DIR/<name>` exists, that is what you mean (this is usually
     useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD` and `MERGE_HEAD`);
 
-  . otherwise, `$GIT_DIR/refs/<name>` if exists;
+  . otherwise, `refs/<name>` if exists;
 
-  . otherwise, `$GIT_DIR/refs/tags/<name>` if exists;
+  . otherwise, `refs/tags/<name>` if exists;
 
-  . otherwise, `$GIT_DIR/refs/heads/<name>` if exists;
+  . otherwise, `refs/heads/<name>` if exists;
 
-  . otherwise, `$GIT_DIR/refs/remotes/<name>` if exists;
+  . otherwise, `refs/remotes/<name>` if exists;
 
-  . otherwise, `$GIT_DIR/refs/remotes/<name>/HEAD` if exists.
+  . otherwise, `refs/remotes/<name>/HEAD` if exists.
 +
 HEAD names the commit your changes in the working tree is based on.
 FETCH_HEAD records the branch you fetched from a remote repository
@@ -217,6 +222,9 @@ you can change the tip of the branch back to the state before you ran
 them easily.
 MERGE_HEAD records the commit(s) you are merging into your branch
 when you run 'git merge'.
++
+Note that any of the `refs/*` cases above may come either from
+the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
 
 * A ref followed by the suffix '@' with a date specification
   enclosed in a brace
index 8178d9264251e45d5af5cabfca2d193252ac1768..deaa7d9654f5938fabad3e1eae29ae7c7803d2ec 100644 (file)
@@ -48,8 +48,8 @@ OPTIONS
        Run verbosely.
 
 --thin::
-       Spend extra cycles to minimize the number of objects to be sent.
-       Use it on slower connection.
+       Send a "thin" pack, which records objects in deltified form based
+       on objects not included in the pack to reduce network traffic.
 
 <host>::
        A remote host to house the repository.  When this
index 734336119c6b1f7ea8241f0404eaa3ba2ae10f69..f1499bba88028775032819708db22a3250b5b840 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git show-branch' [-a|--all] [-r|--remotes] [--topo-order | --date-order]
-               [--current] [--color | --no-color] [--sparse]
+               [--current] [--color[=<when>] | --no-color] [--sparse]
                [--more=<n> | --list | --independent | --merge-base]
                [--no-name | --sha1-name] [--topics]
                [<rev> | <glob>]...
@@ -20,8 +20,8 @@ DESCRIPTION
 -----------
 
 Shows the commit ancestry graph starting from the commits named
-with <rev>s or <globs>s (or all refs under $GIT_DIR/refs/heads
-and/or $GIT_DIR/refs/tags) semi-visually.
+with <rev>s or <globs>s (or all refs under refs/heads
+and/or refs/tags) semi-visually.
 
 It cannot show more than 29 branches and commits at a time.
 
@@ -37,8 +37,8 @@ OPTIONS
 
 <glob>::
        A glob pattern that matches branch or tag names under
-       $GIT_DIR/refs.  For example, if you have many topic
-       branches under $GIT_DIR/refs/heads/topic, giving
+       refs/.  For example, if you have many topic
+       branches under refs/heads/topic, giving
        `topic/*` would show all of them.
 
 -r::
@@ -117,13 +117,15 @@ OPTIONS
        When no explicit <ref> parameter is given, it defaults to the
        current branch (or `HEAD` if it is detached).
 
---color::
+--color[=<when>]::
        Color the status sign (one of these: `*` `!` `+` `-`) of each commit
        corresponding to the branch it's in.
+       The value must be always (the default), never, or auto.
 
 --no-color::
        Turn off colored output, even when the configuration file gives the
        default to color output.
+       Same as `--color=never`.
 
 Note that --more, --list, --independent and --merge-base options
 are mutually exclusive.
@@ -176,7 +178,7 @@ EXAMPLE
 -------
 
 If you keep your primary branches immediately under
-`$GIT_DIR/refs/heads`, and topic branches in subdirectories of
+`refs/heads`, and topic branches in subdirectories of
 it, having the following in the configuration file may help:
 
 ------------
index 84e555d81d9bdd25afeacf868f55a6a868773cd0..473889a660402f5f88b45c6aa3d00d0e85004389 100644 (file)
@@ -33,7 +33,7 @@ A stash is by default listed as "WIP on 'branchname' ...", but
 you can give a more descriptive message on the command line when
 you create one.
 
-The latest stash you created is stored in `$GIT_DIR/refs/stash`; older
+The latest stash you created is stored in `refs/stash`; older
 stashes are found in the reflog of this reference and can be named using
 the usual reflog syntax (e.g. `stash@\{0}` is the most recently
 created stash, `stash@\{1}` is the one before it, `stash@\{2.hours.ago}`
index bb981822a470b2df6c79156db8aa94569869e43e..458f3e2755fcc6431757af6178942ef756f6a5bd 100644 (file)
@@ -8,7 +8,7 @@ git-var - Show a git logical variable
 
 SYNOPSIS
 --------
-'git var' [ -l | <variable> ]
+'git var' ( -l | <variable> )
 
 DESCRIPTION
 -----------
index 01c463101b9b212f002e10a0d9e89a2df12652a2..4e00b315ff7450efb4a48ba184c6d65991d53c58 100644 (file)
@@ -43,9 +43,12 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.7.0/git.html[documentation for release 1.7.0]
+* link:v1.7.0.3/git.html[documentation for release 1.7.0.3]
 
 * release notes for
+  link:RelNotes-1.7.0.3.txt[1.7.0.3],
+  link:RelNotes-1.7.0.2.txt[1.7.0.2],
+  link:RelNotes-1.7.0.1.txt[1.7.0.1],
   link:RelNotes-1.7.0.txt[1.7.0].
 
 * link:v1.6.6.2/git.html[documentation for release 1.6.6.2]
@@ -229,7 +232,10 @@ help ...`.
 
 -p::
 --paginate::
-       Pipe all output into 'less' (or if set, $PAGER).
+       Pipe all output into 'less' (or if set, $PAGER) if standard
+       output is a terminal.  This overrides the `pager.<cmd>`
+       configuration options (see the "Configuration Mechanism" section
+       below).
 
 --no-pager::
        Do not pipe git output into a pager.
@@ -401,7 +407,8 @@ people.  Here is an example:
 ------------
 
 Various commands read from the configuration file and adjust
-their operation accordingly.
+their operation accordingly.  See linkgit:git-config[1] for a
+list.
 
 
 Identifier Terminology
index b396a871b32de52962eaa36f9d6be19d108d88ee..d892e642eded92fd0409e39fc34c005f88bc4a9b 100644 (file)
@@ -511,7 +511,8 @@ command to run to merge ancestor's version (`%O`), current
 version (`%A`) and the other branches' version (`%B`).  These
 three tokens are replaced with the names of temporary files that
 hold the contents of these versions when the command line is
-built.
+built. Additionally, %L will be replaced with the conflict marker
+size (see below).
 
 The merge driver is expected to leave the result of the merge in
 the file named with `%A` by overwriting it, and exit with zero
index dcdea54df3450f82cc898db93474e4be4981187c..9de8caf5d11445f6db7193f7b7a4151c97befb29 100644 (file)
@@ -3,7 +3,7 @@ gitdiffcore(7)
 
 NAME
 ----
-gitdiffcore - Tweaking diff output (June 2005)
+gitdiffcore - Tweaking diff output
 
 SYNOPSIS
 --------
index 87e2c035a7bf1bfff8024db6f4d971ddb5b44e57..7183aa9abbc35018dc50a7c0e5254cc72115af23 100644 (file)
@@ -317,6 +317,44 @@ This hook is invoked by 'git gc --auto'. It takes no parameter, and
 exiting with non-zero status from this script causes the 'git gc --auto'
 to abort.
 
+post-rewrite
+~~~~~~~~~~~~
+
+This hook is invoked by commands that rewrite commits (`git commit
+--amend`, 'git-rebase'; currently 'git-filter-branch' does 'not' call
+it!).  Its first argument denotes the command it was invoked by:
+currently one of `amend` or `rebase`.  Further command-dependent
+arguments may be passed in the future.
+
+The hook receives a list of the rewritten commits on stdin, in the
+format
+
+  <old-sha1> SP <new-sha1> [ SP <extra-info> ] LF
+
+The 'extra-info' is again command-dependent.  If it is empty, the
+preceding SP is also omitted.  Currently, no commands pass any
+'extra-info'.
+
+The hook always runs after the automatic note copying (see
+"notes.rewrite.<command>" in linkgit:git-config.txt) has happened, and
+thus has access to these notes.
+
+The following command-specific comments apply:
+
+rebase::
+       For the 'squash' and 'fixup' operation, all commits that were
+       squashed are listed as being rewritten to the squashed commit.
+       This means that there will be several lines sharing the same
+       'new-sha1'.
++
+The commits are guaranteed to be listed in the order that they were
+processed by rebase.
+
+There is no default 'post-rewrite' hook, but see the
+`post-receive-copy-notes` script in `contrib/hooks` for an example
+that copies your git-notes to the rewritten commits.
+
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 3b83dba1a0d8ad1436d15d164783f08593f54357..37ce9a17fc3d66c402954246c83d0ec93fe007af 100644 (file)
@@ -67,6 +67,7 @@ option can be used to override --squash.
        Synonyms to --stat and --no-stat; these are deprecated and will be
        removed in the future.
 
+ifndef::git-pull[]
 -q::
 --quiet::
        Operate quietly.
@@ -74,6 +75,7 @@ option can be used to override --squash.
 -v::
 --verbose::
        Be verbose.
+endif::git-pull[]
 
 -X <option>::
 --strategy-option=<option>::
index aa96caeab26ee6132adbef03d2ca17e91f634208..af6d2b995a050007ae088dec8e2c3474e9f06ad5 100644 (file)
@@ -30,9 +30,18 @@ people using 80-column terminals.
        defaults to UTF-8.
 
 --no-notes::
---show-notes::
+--show-notes[=<ref>]::
        Show the notes (see linkgit:git-notes[1]) that annotate the
        commit, when showing the commit log message.  This is the default
        for `git log`, `git show` and `git whatchanged` commands when
        there is no `--pretty`, `--format` nor `--oneline` option is
        given on the command line.
++
+With an optional argument, add this ref to the list of notes.  The ref
+is taken to be in `refs/notes/` if it is not qualified.
+
+--[no-]standard-notes::
+       Enable or disable populating the notes ref list from the
+       'core.notesRef' and 'notes.displayRef' variables (or
+       corresponding environment overrides).  Enabled by default.
+       See linkgit:git-config[1].
index 6e9baf8b38be5fba5e3c82e617ab95d579731884..b9fb7a86bd4bf91205b77275fb51ff13a3bc5622 100644 (file)
@@ -108,8 +108,8 @@ options may be given. See linkgit:git-diff-files[1] for more options.
 
 -c::
 
-       This flag changes the way a merge commit is displayed.  It shows
-       the differences from each of the parents to the merge result
+       With this option, diff output for a merge commit
+       shows the differences from each of the parents to the merge result
        simultaneously instead of showing pairwise diff between a parent
        and the result one at a time. Furthermore, it lists only files
        which were modified from all parents.
@@ -121,6 +121,15 @@ options may be given. See linkgit:git-diff-files[1] for more options.
        the parents have only two variants and the merge result picks
        one of them without modification.
 
+-m::
+
+       This flag makes the merge commits show the full diff like
+       regular commits; for each merge parent, a separate log entry
+       and diff is generated. An exception is that only diff against
+       the first parent is shown when '--first-parent' option is given;
+       in that case, the output represents the changes the merge
+       brought _into_ the then-current branch.
+
 -r::
 
        Show recursive diffs.
@@ -225,26 +234,26 @@ endif::git-rev-list[]
 
 --all::
 
-       Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the
+       Pretend as if all the refs in `refs/` are listed on the
        command line as '<commit>'.
 
 --branches[=pattern]::
 
-       Pretend as if all the refs in `$GIT_DIR/refs/heads` are listed
+       Pretend as if all the refs in `refs/heads` are listed
        on the command line as '<commit>'. If `pattern` is given, limit
        branches to ones matching given shell glob. If pattern lacks '?',
        '*', or '[', '/*' at the end is implied.
 
 --tags[=pattern]::
 
-       Pretend as if all the refs in `$GIT_DIR/refs/tags` are listed
+       Pretend as if all the refs in `refs/tags` are listed
        on the command line as '<commit>'. If `pattern` is given, limit
        tags to ones matching given shell glob. If pattern lacks '?', '*',
        or '[', '/*' at the end is implied.
 
 --remotes[=pattern]::
 
-       Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed
+       Pretend as if all the refs in `refs/remotes` are listed
        on the command line as '<commit>'. If `pattern`is given, limit
        remote tracking branches to ones matching given shell glob.
        If pattern lacks '?', '*', or '[', '/*' at the end is implied.
@@ -259,9 +268,9 @@ endif::git-rev-list[]
 ifndef::git-rev-list[]
 --bisect::
 
-       Pretend as if the bad bisection ref `$GIT_DIR/refs/bisect/bad`
+       Pretend as if the bad bisection ref `refs/bisect/bad`
        was listed and as if it was followed by `--not` and the good
-       bisection refs `$GIT_DIR/refs/bisect/good-*` on the command
+       bisection refs `refs/bisect/good-*` on the command
        line.
 endif::git-rev-list[]
 
@@ -561,10 +570,10 @@ Bisection Helpers
 
 Limit output to the one commit object which is roughly halfway between
 included and excluded commits. Note that the bad bisection ref
-`$GIT_DIR/refs/bisect/bad` is added to the included commits (if it
-exists) and the good bisection refs `$GIT_DIR/refs/bisect/good-*` are
+`refs/bisect/bad` is added to the included commits (if it
+exists) and the good bisection refs `refs/bisect/good-*` are
 added to the excluded commits (if they exist). Thus, supposing there
-are no refs in `$GIT_DIR/refs/bisect/`, if
+are no refs in `refs/bisect/`, if
 
 -----------------------------------------------------------------------
        $ git rev-list --bisect foo ^bar ^baz
@@ -585,7 +594,7 @@ one.
 --bisect-vars::
 
 This calculates the same as `--bisect`, except that refs in
-`$GIT_DIR/refs/bisect/` are not used, and except that this outputs
+`refs/bisect/` are not used, and except that this outputs
 text ready to be eval'ed by the shell. These lines will assign the
 name of the midpoint revision to the variable `bisect_rev`, and the
 expected number of commits to be tested after `bisect_rev` is tested
@@ -599,7 +608,7 @@ number of commits to be tested if `bisect_rev` turns out to be bad to
 
 This outputs all the commit objects between the included and excluded
 commits, ordered by their distance to the included and excluded
-commits. Refs in `$GIT_DIR/refs/bisect/` are not used. The farthest
+commits. Refs in `refs/bisect/` are not used. The farthest
 from them is displayed first. (This is the only one displayed by
 `--bisect`.)
 +
index 50f9e9ac1708f3f754023c1bb60416adc9c73c74..312e3b2e2ba184b6329298753e996e14fe04386e 100644 (file)
@@ -115,6 +115,9 @@ There are some macros to easily define options:
 `OPT__ABBREV(&int_var)`::
        Add `\--abbrev[=<n>]`.
 
+`OPT__COLOR(&int_var, description)`::
+       Add `\--color[=<when>]` and `--no-color`.
+
 `OPT__DRY_RUN(&int_var)`::
        Add `-n, \--dry-run`.
 
@@ -183,6 +186,15 @@ There are some macros to easily define options:
        arguments.  Short options that happen to be digits take
        precedence over it.
 
+`OPT_COLOR_FLAG(short, long, &int_var, description)`::
+       Introduce an option that takes an optional argument that can
+       have one of three values: "always", "never", or "auto".  If the
+       argument is not given, it defaults to "always".  The `--no-` form
+       works like `--long=never`; it cannot take an argument.  If
+       "always", set `int_var` to 1; if "never", set `int_var` to 0; if
+       "auto", set `int_var` to 1 if stdout is a tty or a pager,
+       0 otherwise.
+
 
 The last element of the array must be `OPT_END()`.
 
index 68bf4cad8b0298349b6cc67e822a4d60e1ff1d24..44876fa703578f4952d6e928993e15ddec70439c 100644 (file)
@@ -64,8 +64,8 @@ The functions above do the following:
 `start_async`::
 
        Run a function asynchronously. Takes a pointer to a `struct
-       async` that specifies the details and returns a pipe FD
-       from which the caller reads. See below for details.
+       async` that specifies the details and returns a set of pipe FDs
+       for communication with the function. See below for details.
 
 `finish_async`::
 
@@ -135,7 +135,7 @@ stderr as follows:
 
        .in: The FD must be readable; it becomes child's stdin.
        .out: The FD must be writable; it becomes child's stdout.
-       .err > 0 is not supported.
+       .err: The FD must be writable; it becomes child's stderr.
 
   The specified FD is closed by start_command(), even if it fails to
   run the sub-process!
@@ -180,17 +180,47 @@ The caller:
    struct async variable;
 2. initializes .proc and .data;
 3. calls start_async();
-4. processes the data by reading from the fd in .out;
-5. closes .out;
+4. processes communicates with proc through .in and .out;
+5. closes .in and .out;
 6. calls finish_async().
 
+The members .in, .out are used to provide a set of fd's for
+communication between the caller and the callee as follows:
+
+. Specify 0 to have no file descriptor passed.  The callee will
+  receive -1 in the corresponding argument.
+
+. Specify < 0 to have a pipe allocated; start_async() replaces
+  with the pipe FD in the following way:
+
+       .in: Returns the writable pipe end into which the caller
+       writes; the readable end of the pipe becomes the function's
+       in argument.
+
+       .out: Returns the readable pipe end from which the caller
+       reads; the writable end of the pipe becomes the function's
+       out argument.
+
+  The caller of start_async() must close the returned FDs after it
+  has completed reading from/writing from them.
+
+. Specify a file descriptor > 0 to be used by the function:
+
+       .in: The FD must be readable; it becomes the function's in.
+       .out: The FD must be writable; it becomes the function's out.
+
+  The specified FD is closed by start_async(), even if it fails to
+  run the function.
+
 The function pointer in .proc has the following signature:
 
-       int proc(int fd, void *data);
+       int proc(int in, int out, void *data);
 
-. fd specifies a writable file descriptor to which the function must
-  write the data that it produces. The function *must* close this
-  descriptor before it returns.
+. in, out specifies a set of file descriptors to which the function
+  must read/write the data that it needs/produces.  The function
+  *must* close these descriptors before it returns.  A descriptor
+  may be -1 if the caller did not configure a descriptor for that
+  direction.
 
 . data is the value that the caller has specified in the .data member
   of struct async.
@@ -205,8 +235,8 @@ 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:
 
 . It cannot change the program's state (global variables, environment,
-  etc.) in a way that the caller notices; in other words, .out is the
-  only communication channel to the caller.
+  etc.) in a way that the caller notices; in other words, .in and .out
+  are the only communication channels to the caller.
 
 . It must not change the program's state that the caller of the
   facility also uses.
index d813ceb7239bc2d22eb0af4ad33a6e1be8408787..459a394dc0eb2c7ebecc7c9cf53808d791187006 100644 (file)
@@ -4,7 +4,6 @@ GIT URLS[[URLS]]
 One of the following notations can be used
 to name the remote repository:
 
-===============================================================
 - rsync://host.xz/path/to/repo.git/
 - http://host.xz{startsb}:port{endsb}/path/to/repo.git/
 - https://host.xz{startsb}:port{endsb}/path/to/repo.git/
@@ -14,7 +13,6 @@ to name the remote repository:
 - ssh://{startsb}user@{endsb}host.xz/path/to/repo.git/
 - ssh://{startsb}user@{endsb}host.xz/~user/path/to/repo.git/
 - ssh://{startsb}user@{endsb}host.xz/~/path/to/repo.git
-===============================================================
 
 SSH is the default transport protocol over the network.  You can
 optionally specify which user to log-in as, and an alternate,
@@ -23,18 +21,14 @@ username expansion, as does the native git protocol, but
 only the former supports port specification. The following
 three are identical to the last three above, respectively:
 
-===============================================================
 - {startsb}user@{endsb}host.xz:/path/to/repo.git/
 - {startsb}user@{endsb}host.xz:~user/path/to/repo.git/
 - {startsb}user@{endsb}host.xz:path/to/repo.git
-===============================================================
 
 To sync with a local directory, you can use:
 
-===============================================================
 - /path/to/repo.git/
 - file:///path/to/repo.git/
-===============================================================
 
 ifndef::git-clone[]
 They are mostly equivalent, except when cloning.  See
index 577e1fd20eb0bd94e001b9188576ca74f99741d7..076c18ecc3fc9dc77d1be5dc6e960da72c519576 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.0
+DEF_VER=v1.7.0.3
 
 LF='
 '
index 7bf2fca4070d2d00ac31d8b4dca6dff19b79cc79..3a6c6ea5259693db4846b91855675218b1e1a920 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -214,6 +214,13 @@ all::
 #   DEFAULT_EDITOR='~/bin/vi',
 #   DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR',
 #   DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork'
+#
+# Define COMPUTE_HEADER_DEPENDENCIES if your compiler supports the -MMD option
+# and you want to avoid rebuilding objects when an unrelated header file
+# changes.
+#
+# Define CHECK_HEADER_DEPENDENCIES to check for problems in the hard-coded
+# dependency rules.
 
 GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -301,7 +308,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
 # Those must not be GNU-specific; they are shared with perl/ which may
 # be built by a different compiler. (Note that this is an artifact now
 # but it still might be nice to keep that distinction.)
-BASIC_CFLAGS =
+BASIC_CFLAGS = -I.
 BASIC_LDFLAGS =
 
 # Guard against environment variables
@@ -309,13 +316,22 @@ BUILTIN_OBJS =
 BUILT_INS =
 COMPAT_CFLAGS =
 COMPAT_OBJS =
+EXTRA_CPPFLAGS =
 LIB_H =
 LIB_OBJS =
+PROGRAM_OBJS =
 PROGRAMS =
 SCRIPT_PERL =
 SCRIPT_PYTHON =
 SCRIPT_SH =
-TEST_PROGRAMS =
+SCRIPT_LIB =
+TEST_PROGRAMS_NEED_X =
+
+# Having this variable in your environment would break pipelines because
+# you cause "cd" to echo its destination to stdout.  It can also take
+# scripts to unexpected places.  If you like CDPATH, define it for your
+# interactive shell sessions without exporting it.
+unexport CDPATH
 
 SCRIPT_SH += git-am.sh
 SCRIPT_SH += git-bisect.sh
@@ -326,20 +342,20 @@ SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
-SCRIPT_SH += git-mergetool--lib.sh
-SCRIPT_SH += git-notes.sh
-SCRIPT_SH += git-parse-remote.sh
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-rebase--interactive.sh
 SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
-SCRIPT_SH += git-sh-setup.sh
 SCRIPT_SH += git-stash.sh
 SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
 
+SCRIPT_LIB += git-mergetool--lib
+SCRIPT_LIB += git-parse-remote
+SCRIPT_LIB += git-sh-setup
+
 SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-difftool.perl
 SCRIPT_PERL += git-archimport.perl
@@ -360,16 +376,35 @@ EXTRA_PROGRAMS =
 
 # ... and all the rest that could be moved out of bindir to gitexecdir
 PROGRAMS += $(EXTRA_PROGRAMS)
-PROGRAMS += git-fast-import$X
-PROGRAMS += git-imap-send$X
-PROGRAMS += git-shell$X
-PROGRAMS += git-show-index$X
-PROGRAMS += git-upload-pack$X
-PROGRAMS += git-http-backend$X
+
+PROGRAM_OBJS += fast-import.o
+PROGRAM_OBJS += imap-send.o
+PROGRAM_OBJS += shell.o
+PROGRAM_OBJS += show-index.o
+PROGRAM_OBJS += upload-pack.o
+PROGRAM_OBJS += http-backend.o
+
+PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
+
+TEST_PROGRAMS_NEED_X += test-chmtime
+TEST_PROGRAMS_NEED_X += test-ctype
+TEST_PROGRAMS_NEED_X += test-date
+TEST_PROGRAMS_NEED_X += test-delta
+TEST_PROGRAMS_NEED_X += test-dump-cache-tree
+TEST_PROGRAMS_NEED_X += test-genrandom
+TEST_PROGRAMS_NEED_X += test-match-trees
+TEST_PROGRAMS_NEED_X += test-parse-options
+TEST_PROGRAMS_NEED_X += test-path-utils
+TEST_PROGRAMS_NEED_X += test-run-command
+TEST_PROGRAMS_NEED_X += test-sha1
+TEST_PROGRAMS_NEED_X += test-sigchain
+TEST_PROGRAMS_NEED_X += test-index-version
+
+TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
 
 # List built-in command $C whose implementation cmd_$C() is not in
-# builtin-$C.o but is linked in as part of some other command.
-BUILT_INS += $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
+# builtin/$C.o but is linked in as part of some other command.
+BUILT_INS += $(patsubst builtin/%.o,git-%$X,$(BUILTIN_OBJS))
 
 BUILT_INS += git-cherry$X
 BUILT_INS += git-cherry-pick$X
@@ -425,6 +460,7 @@ LIB_H += blob.h
 LIB_H += builtin.h
 LIB_H += cache.h
 LIB_H += cache-tree.h
+LIB_H += color.h
 LIB_H += commit.h
 LIB_H += compat/bswap.h
 LIB_H += compat/cygwin.h
@@ -436,6 +472,7 @@ LIB_H += delta.h
 LIB_H += diffcore.h
 LIB_H += diff.h
 LIB_H += dir.h
+LIB_H += exec_cmd.h
 LIB_H += fsck.h
 LIB_H += git-compat-util.h
 LIB_H += graph.h
@@ -478,7 +515,8 @@ LIB_H += tree-walk.h
 LIB_H += unpack-trees.h
 LIB_H += userdiff.h
 LIB_H += utf8.h
-LIB_H += wt-status.h
+LIB_H += xdiff-interface.h
+LIB_H += xdiff/xdiff.h
 
 LIB_OBJS += abspath.o
 LIB_OBJS += advice.o
@@ -592,95 +630,96 @@ LIB_OBJS += ws.o
 LIB_OBJS += wt-status.o
 LIB_OBJS += xdiff-interface.o
 
-BUILTIN_OBJS += builtin-add.o
-BUILTIN_OBJS += builtin-annotate.o
-BUILTIN_OBJS += builtin-apply.o
-BUILTIN_OBJS += builtin-archive.o
-BUILTIN_OBJS += builtin-bisect--helper.o
-BUILTIN_OBJS += builtin-blame.o
-BUILTIN_OBJS += builtin-branch.o
-BUILTIN_OBJS += builtin-bundle.o
-BUILTIN_OBJS += builtin-cat-file.o
-BUILTIN_OBJS += builtin-check-attr.o
-BUILTIN_OBJS += builtin-check-ref-format.o
-BUILTIN_OBJS += builtin-checkout-index.o
-BUILTIN_OBJS += builtin-checkout.o
-BUILTIN_OBJS += builtin-clean.o
-BUILTIN_OBJS += builtin-clone.o
-BUILTIN_OBJS += builtin-commit-tree.o
-BUILTIN_OBJS += builtin-commit.o
-BUILTIN_OBJS += builtin-config.o
-BUILTIN_OBJS += builtin-count-objects.o
-BUILTIN_OBJS += builtin-describe.o
-BUILTIN_OBJS += builtin-diff-files.o
-BUILTIN_OBJS += builtin-diff-index.o
-BUILTIN_OBJS += builtin-diff-tree.o
-BUILTIN_OBJS += builtin-diff.o
-BUILTIN_OBJS += builtin-fast-export.o
-BUILTIN_OBJS += builtin-fetch-pack.o
-BUILTIN_OBJS += builtin-fetch.o
-BUILTIN_OBJS += builtin-fmt-merge-msg.o
-BUILTIN_OBJS += builtin-for-each-ref.o
-BUILTIN_OBJS += builtin-fsck.o
-BUILTIN_OBJS += builtin-gc.o
-BUILTIN_OBJS += builtin-grep.o
-BUILTIN_OBJS += builtin-hash-object.o
-BUILTIN_OBJS += builtin-help.o
-BUILTIN_OBJS += builtin-index-pack.o
-BUILTIN_OBJS += builtin-init-db.o
-BUILTIN_OBJS += builtin-log.o
-BUILTIN_OBJS += builtin-ls-files.o
-BUILTIN_OBJS += builtin-ls-remote.o
-BUILTIN_OBJS += builtin-ls-tree.o
-BUILTIN_OBJS += builtin-mailinfo.o
-BUILTIN_OBJS += builtin-mailsplit.o
-BUILTIN_OBJS += builtin-merge.o
-BUILTIN_OBJS += builtin-merge-base.o
-BUILTIN_OBJS += builtin-merge-file.o
-BUILTIN_OBJS += builtin-merge-index.o
-BUILTIN_OBJS += builtin-merge-ours.o
-BUILTIN_OBJS += builtin-merge-recursive.o
-BUILTIN_OBJS += builtin-merge-tree.o
-BUILTIN_OBJS += builtin-mktag.o
-BUILTIN_OBJS += builtin-mktree.o
-BUILTIN_OBJS += builtin-mv.o
-BUILTIN_OBJS += builtin-name-rev.o
-BUILTIN_OBJS += builtin-pack-objects.o
-BUILTIN_OBJS += builtin-pack-redundant.o
-BUILTIN_OBJS += builtin-pack-refs.o
-BUILTIN_OBJS += builtin-patch-id.o
-BUILTIN_OBJS += builtin-prune-packed.o
-BUILTIN_OBJS += builtin-prune.o
-BUILTIN_OBJS += builtin-push.o
-BUILTIN_OBJS += builtin-read-tree.o
-BUILTIN_OBJS += builtin-receive-pack.o
-BUILTIN_OBJS += builtin-reflog.o
-BUILTIN_OBJS += builtin-remote.o
-BUILTIN_OBJS += builtin-replace.o
-BUILTIN_OBJS += builtin-rerere.o
-BUILTIN_OBJS += builtin-reset.o
-BUILTIN_OBJS += builtin-rev-list.o
-BUILTIN_OBJS += builtin-rev-parse.o
-BUILTIN_OBJS += builtin-revert.o
-BUILTIN_OBJS += builtin-rm.o
-BUILTIN_OBJS += builtin-send-pack.o
-BUILTIN_OBJS += builtin-shortlog.o
-BUILTIN_OBJS += builtin-show-branch.o
-BUILTIN_OBJS += builtin-show-ref.o
-BUILTIN_OBJS += builtin-stripspace.o
-BUILTIN_OBJS += builtin-symbolic-ref.o
-BUILTIN_OBJS += builtin-tag.o
-BUILTIN_OBJS += builtin-tar-tree.o
-BUILTIN_OBJS += builtin-unpack-file.o
-BUILTIN_OBJS += builtin-unpack-objects.o
-BUILTIN_OBJS += builtin-update-index.o
-BUILTIN_OBJS += builtin-update-ref.o
-BUILTIN_OBJS += builtin-update-server-info.o
-BUILTIN_OBJS += builtin-upload-archive.o
-BUILTIN_OBJS += builtin-var.o
-BUILTIN_OBJS += builtin-verify-pack.o
-BUILTIN_OBJS += builtin-verify-tag.o
-BUILTIN_OBJS += builtin-write-tree.o
+BUILTIN_OBJS += builtin/add.o
+BUILTIN_OBJS += builtin/annotate.o
+BUILTIN_OBJS += builtin/apply.o
+BUILTIN_OBJS += builtin/archive.o
+BUILTIN_OBJS += builtin/bisect--helper.o
+BUILTIN_OBJS += builtin/blame.o
+BUILTIN_OBJS += builtin/branch.o
+BUILTIN_OBJS += builtin/bundle.o
+BUILTIN_OBJS += builtin/cat-file.o
+BUILTIN_OBJS += builtin/check-attr.o
+BUILTIN_OBJS += builtin/check-ref-format.o
+BUILTIN_OBJS += builtin/checkout-index.o
+BUILTIN_OBJS += builtin/checkout.o
+BUILTIN_OBJS += builtin/clean.o
+BUILTIN_OBJS += builtin/clone.o
+BUILTIN_OBJS += builtin/commit-tree.o
+BUILTIN_OBJS += builtin/commit.o
+BUILTIN_OBJS += builtin/config.o
+BUILTIN_OBJS += builtin/count-objects.o
+BUILTIN_OBJS += builtin/describe.o
+BUILTIN_OBJS += builtin/diff-files.o
+BUILTIN_OBJS += builtin/diff-index.o
+BUILTIN_OBJS += builtin/diff-tree.o
+BUILTIN_OBJS += builtin/diff.o
+BUILTIN_OBJS += builtin/fast-export.o
+BUILTIN_OBJS += builtin/fetch-pack.o
+BUILTIN_OBJS += builtin/fetch.o
+BUILTIN_OBJS += builtin/fmt-merge-msg.o
+BUILTIN_OBJS += builtin/for-each-ref.o
+BUILTIN_OBJS += builtin/fsck.o
+BUILTIN_OBJS += builtin/gc.o
+BUILTIN_OBJS += builtin/grep.o
+BUILTIN_OBJS += builtin/hash-object.o
+BUILTIN_OBJS += builtin/help.o
+BUILTIN_OBJS += builtin/index-pack.o
+BUILTIN_OBJS += builtin/init-db.o
+BUILTIN_OBJS += builtin/log.o
+BUILTIN_OBJS += builtin/ls-files.o
+BUILTIN_OBJS += builtin/ls-remote.o
+BUILTIN_OBJS += builtin/ls-tree.o
+BUILTIN_OBJS += builtin/mailinfo.o
+BUILTIN_OBJS += builtin/mailsplit.o
+BUILTIN_OBJS += builtin/merge.o
+BUILTIN_OBJS += builtin/merge-base.o
+BUILTIN_OBJS += builtin/merge-file.o
+BUILTIN_OBJS += builtin/merge-index.o
+BUILTIN_OBJS += builtin/merge-ours.o
+BUILTIN_OBJS += builtin/merge-recursive.o
+BUILTIN_OBJS += builtin/merge-tree.o
+BUILTIN_OBJS += builtin/mktag.o
+BUILTIN_OBJS += builtin/mktree.o
+BUILTIN_OBJS += builtin/mv.o
+BUILTIN_OBJS += builtin/name-rev.o
+BUILTIN_OBJS += builtin/notes.o
+BUILTIN_OBJS += builtin/pack-objects.o
+BUILTIN_OBJS += builtin/pack-redundant.o
+BUILTIN_OBJS += builtin/pack-refs.o
+BUILTIN_OBJS += builtin/patch-id.o
+BUILTIN_OBJS += builtin/prune-packed.o
+BUILTIN_OBJS += builtin/prune.o
+BUILTIN_OBJS += builtin/push.o
+BUILTIN_OBJS += builtin/read-tree.o
+BUILTIN_OBJS += builtin/receive-pack.o
+BUILTIN_OBJS += builtin/reflog.o
+BUILTIN_OBJS += builtin/remote.o
+BUILTIN_OBJS += builtin/replace.o
+BUILTIN_OBJS += builtin/rerere.o
+BUILTIN_OBJS += builtin/reset.o
+BUILTIN_OBJS += builtin/rev-list.o
+BUILTIN_OBJS += builtin/rev-parse.o
+BUILTIN_OBJS += builtin/revert.o
+BUILTIN_OBJS += builtin/rm.o
+BUILTIN_OBJS += builtin/send-pack.o
+BUILTIN_OBJS += builtin/shortlog.o
+BUILTIN_OBJS += builtin/show-branch.o
+BUILTIN_OBJS += builtin/show-ref.o
+BUILTIN_OBJS += builtin/stripspace.o
+BUILTIN_OBJS += builtin/symbolic-ref.o
+BUILTIN_OBJS += builtin/tag.o
+BUILTIN_OBJS += builtin/tar-tree.o
+BUILTIN_OBJS += builtin/unpack-file.o
+BUILTIN_OBJS += builtin/unpack-objects.o
+BUILTIN_OBJS += builtin/update-index.o
+BUILTIN_OBJS += builtin/update-ref.o
+BUILTIN_OBJS += builtin/update-server-info.o
+BUILTIN_OBJS += builtin/upload-archive.o
+BUILTIN_OBJS += builtin/var.o
+BUILTIN_OBJS += builtin/verify-pack.o
+BUILTIN_OBJS += builtin/verify-tag.o
+BUILTIN_OBJS += builtin/write-tree.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 EXTLIBS =
@@ -1020,6 +1059,14 @@ endif
 -include config.mak.autogen
 -include config.mak
 
+ifdef CHECK_HEADER_DEPENDENCIES
+USE_COMPUTED_HEADER_DEPENDENCIES =
+endif
+
+ifdef COMPUTE_HEADER_DEPENDENCIES
+USE_COMPUTED_HEADER_DEPENDENCIES = YesPlease
+endif
+
 ifdef SANE_TOOL_PATH
 SANE_TOOL_PATH_SQ = $(subst ','\'',$(SANE_TOOL_PATH))
 BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix $(SANE_TOOL_PATH_SQ)|'
@@ -1075,11 +1122,12 @@ else
        REMOTE_CURL_PRIMARY = git-remote-http$X
        REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X
        REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
-       PROGRAMS += $(REMOTE_CURL_NAMES) git-http-fetch$X
+       PROGRAM_OBJS += http-fetch.o
+       PROGRAMS += $(REMOTE_CURL_NAMES)
        curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "070908"
                ifndef NO_EXPAT
-                       PROGRAMS += git-http-push$X
+                       PROGRAM_OBJS += http-push.o
                endif
        endif
        ifndef NO_EXPAT
@@ -1099,7 +1147,7 @@ endif
 EXTLIBS += -lz
 
 ifndef NO_POSIX_ONLY_PROGRAMS
-       PROGRAMS += git-daemon$X
+       PROGRAM_OBJS += daemon.o
 endif
 ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
@@ -1200,7 +1248,6 @@ ifdef NO_MKDTEMP
 endif
 ifdef NO_MKSTEMPS
        COMPAT_CFLAGS += -DNO_MKSTEMPS
-       COMPAT_OBJS += compat/mkstemps.o
 endif
 ifdef NO_UNSETENV
        COMPAT_CFLAGS += -DNO_UNSETENV
@@ -1266,10 +1313,12 @@ endif
 ifdef BLK_SHA1
        SHA1_HEADER = "block-sha1/sha1.h"
        LIB_OBJS += block-sha1/sha1.o
+       LIB_H += block-sha1/sha1.h
 else
 ifdef PPC_SHA1
        SHA1_HEADER = "ppc/sha1.h"
        LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
+       LIB_H += ppc/sha1.h
 else
        SHA1_HEADER = <openssl/sha.h>
        EXTLIBS += $(LIB_4_CRYPTO)
@@ -1411,7 +1460,7 @@ export TAR INSTALL DESTDIR SHELL_PATH
 
 SHELL = $(SHELL_PATH)
 
-all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
+all:: shell_compatibility_test $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
 ifneq (,$X)
        $(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test -d '$p' -o '$p' -ef '$p$X' || $(RM) '$p';)
 endif
@@ -1438,15 +1487,15 @@ strip: $(PROGRAMS) git$X
        $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
 
 git.o: common-cmds.h
-git.s git.o: ALL_CFLAGS += -DGIT_VERSION='"$(GIT_VERSION)"' \
+git.s git.o: EXTRA_CPPFLAGS = -DGIT_VERSION='"$(GIT_VERSION)"' \
        '-DGIT_HTML_PATH="$(htmldir_SQ)"'
 
 git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
 
-builtin-help.o: common-cmds.h
-builtin-help.s builtin-help.o: ALL_CFLAGS += \
+builtin/help.o: common-cmds.h
+builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
        '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
        '-DGIT_MAN_PATH="$(mandir_SQ)"' \
        '-DGIT_INFO_PATH="$(infodir_SQ)"'
@@ -1462,17 +1511,25 @@ common-cmds.h: ./generate-cmdlist.sh command-list.txt
 common-cmds.h: $(wildcard Documentation/git-*.txt)
        $(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
 
+define cmd_munge_script
+$(RM) $@ $@+ && \
+sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+    -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+    -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
+    -e $(BROKEN_PATH_FIX) \
+    $@.sh >$@+
+endef
+
 $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
-       $(QUIET_GEN)$(RM) $@ $@+ && \
-       sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-           -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
-           -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-           -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-           -e $(BROKEN_PATH_FIX) \
-           $@.sh >$@+ && \
+       $(QUIET_GEN)$(cmd_munge_script) && \
        chmod +x $@+ && \
        mv $@+ $@
 
+$(SCRIPT_LIB) : % : %.sh
+       $(QUIET_GEN)$(cmd_munge_script) && \
+       mv $@+ $@
+
 ifndef NO_PERL
 $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
 
@@ -1582,28 +1639,148 @@ git.o git.spec \
        $(patsubst %.perl,%,$(SCRIPT_PERL)) \
        : GIT-VERSION-FILE
 
-%.o: %.c GIT-CFLAGS
-       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
+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
+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)
+
+dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)
+dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))
+
+ifdef COMPUTE_HEADER_DEPENDENCIES
+$(dep_dirs):
+       mkdir -p $@
+
+missing_dep_dirs := $(filter-out $(wildcard $(dep_dirs)),$(dep_dirs))
+dep_file = $(dir $@).depend/$(notdir $@).d
+dep_args = -MF $(dep_file) -MMD -MP
+ifdef CHECK_HEADER_DEPENDENCIES
+$(error cannot compute header dependencies outside a normal build. \
+Please unset CHECK_HEADER_DEPENDENCIES and try again)
+endif
+endif
+
+ifndef COMPUTE_HEADER_DEPENDENCIES
+ifndef CHECK_HEADER_DEPENDENCIES
+dep_dirs =
+missing_dep_dirs =
+dep_args =
+endif
+endif
+
+ifdef CHECK_HEADER_DEPENDENCIES
+ifndef PRINT_HEADER_DEPENDENCIES
+missing_deps = $(filter-out $(notdir $^), \
+       $(notdir $(shell $(MAKE) -s $@ \
+               CHECK_HEADER_DEPENDENCIES=YesPlease \
+               USE_COMPUTED_HEADER_DEPENDENCIES=YesPlease \
+               PRINT_HEADER_DEPENDENCIES=YesPlease)))
+endif
+endif
+
+ASM_SRC := $(wildcard $(OBJECTS:o=S))
+ASM_OBJ := $(ASM_SRC:S=o)
+C_OBJ := $(filter-out $(ASM_OBJ),$(OBJECTS))
+
+.SUFFIXES:
+
+ifdef PRINT_HEADER_DEPENDENCIES
+$(C_OBJ): %.o: %.c FORCE
+       echo $^
+$(ASM_OBJ): %.o: %.S FORCE
+       echo $^
+
+ifndef CHECK_HEADER_DEPENDENCIES
+$(error cannot print header dependencies during a normal build. \
+Please set CHECK_HEADER_DEPENDENCIES and try again)
+endif
+endif
+
+ifndef PRINT_HEADER_DEPENDENCIES
+ifdef CHECK_HEADER_DEPENDENCIES
+$(C_OBJ): %.o: %.c $(dep_files) FORCE
+       @set -e; echo CHECK $@; \
+       missing_deps="$(missing_deps)"; \
+       if test "$$missing_deps"; \
+       then \
+               echo missing dependencies: $$missing_deps; \
+               false; \
+       fi
+$(ASM_OBJ): %.o: %.S $(dep_files) FORCE
+       @set -e; echo CHECK $@; \
+       missing_deps="$(missing_deps)"; \
+       if test "$$missing_deps"; \
+       then \
+               echo missing dependencies: $$missing_deps; \
+               false; \
+       fi
+endif
+endif
+
+ifndef CHECK_HEADER_DEPENDENCIES
+$(C_OBJ): %.o: %.c GIT-CFLAGS $(missing_dep_dirs)
+       $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
+$(ASM_OBJ): %.o: %.S GIT-CFLAGS $(missing_dep_dirs)
+       $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
+endif
+
 %.s: %.c GIT-CFLAGS FORCE
-       $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
-%.o: %.S GIT-CFLAGS
-       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
+       $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
 
-exec_cmd.s exec_cmd.o: ALL_CFLAGS += \
+ifdef USE_COMPUTED_HEADER_DEPENDENCIES
+# Take advantage of gcc's on-the-fly dependency generation
+# See <http://gcc.gnu.org/gcc-3.0/features.html>.
+dep_files_present := $(wildcard $(dep_files))
+ifneq ($(dep_files_present),)
+include $(dep_files_present)
+endif
+else
+# Dependencies on header files, for platforms that do not support
+# the gcc -MMD option.
+#
+# Dependencies on automatically generated headers such as common-cmds.h
+# should _not_ be included here, since they are necessary even when
+# building an object for the first time.
+#
+# XXX. Please check occasionally that these include all dependencies
+# gcc detects!
+
+$(GIT_OBJS): $(LIB_H)
+builtin/branch.o builtin/checkout.o builtin/clone.o builtin/reset.o branch.o transport.o: branch.h
+builtin/bundle.o bundle.o transport.o: bundle.h
+builtin/bisect--helper.o builtin/rev-list.o bisect.o: bisect.h
+builtin/clone.o builtin/fetch-pack.o transport.o: fetch-pack.h
+builtin/grep.o: thread-utils.h
+builtin/send-pack.o transport.o: send-pack.h
+builtin/log.o builtin/shortlog.o: shortlog.h
+builtin/prune.o builtin/reflog.o reachable.o: reachable.h
+builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
+builtin/tar-tree.o archive-tar.o: tar.h
+builtin/pack-objects.o: thread-utils.h
+http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
+http.o http-walker.o http-push.o remote-curl.o: http.h
+
+xdiff-interface.o $(XDIFF_OBJS): \
+       xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
+       xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
+endif
+
+exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
        '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
        '-DBINDIR="$(bindir_relative_SQ)"' \
        '-DPREFIX="$(prefix_SQ)"'
 
-builtin-init-db.s builtin-init-db.o: ALL_CFLAGS += \
+builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
        -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
 
-config.s config.o: ALL_CFLAGS += -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
+config.s config.o: EXTRA_CPPFLAGS = -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
 
-http.s http.o: ALL_CFLAGS += -DGIT_USER_AGENT='"git/$(GIT_VERSION)"'
+http.s http.o: EXTRA_CPPFLAGS = -DGIT_USER_AGENT='"git/$(GIT_VERSION)"'
 
 ifdef NO_EXPAT
-http-walker.o: http.h
-http-walker.s http-walker.o: ALL_CFLAGS += -DNO_EXPAT
+http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
 endif
 
 git-%$X: %.o $(GITLIBS)
@@ -1613,10 +1790,6 @@ git-imap-send$X: imap-send.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
 
-http.o http-walker.o http-push.o: http.h
-
-http.o http-walker.o: $(LIB_H)
-
 git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL)
@@ -1634,18 +1807,9 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
-$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
-$(patsubst git-%$X,%.o,$(PROGRAMS)) git.o: $(LIB_H) $(wildcard */*.h)
-builtin-revert.o wt-status.o: wt-status.h
-
 $(LIB_FILE): $(LIB_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
 
-XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
-       xdiff/xmerge.o xdiff/xpatience.o
-$(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
-       xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
-
 $(XDIFF_LIB): $(XDIFF_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
 
@@ -1711,24 +1875,6 @@ GIT-GUI-VARS: FORCE
             fi
 endif
 
-### Testing rules
-
-TEST_PROGRAMS_NEED_X += test-chmtime
-TEST_PROGRAMS_NEED_X += test-ctype
-TEST_PROGRAMS_NEED_X += test-date
-TEST_PROGRAMS_NEED_X += test-delta
-TEST_PROGRAMS_NEED_X += test-dump-cache-tree
-TEST_PROGRAMS_NEED_X += test-genrandom
-TEST_PROGRAMS_NEED_X += test-match-trees
-TEST_PROGRAMS_NEED_X += test-parse-options
-TEST_PROGRAMS_NEED_X += test-path-utils
-TEST_PROGRAMS_NEED_X += test-run-command
-TEST_PROGRAMS_NEED_X += test-sha1
-TEST_PROGRAMS_NEED_X += test-sigchain
-TEST_PROGRAMS_NEED_X += test-index-version
-
-TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
-
 test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 
 all:: $(TEST_PROGRAMS) $(test_bindir_programs)
@@ -1746,6 +1892,8 @@ bin-wrappers/%: wrap-for-bin.sh
 
 export NO_SVN_TESTS
 
+### Testing rules
+
 test: all
        $(MAKE) -C t/ all
 
@@ -1757,9 +1905,7 @@ test-delta$X: diff-delta.o patch-delta.o
 
 test-parse-options$X: parse-options.o
 
-test-parse-options.o: parse-options.h
-
-.PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
+.PRECIOUS: $(TEST_OBJS)
 
 test-%$X: test-%.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
@@ -1805,6 +1951,7 @@ install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+       $(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
@@ -1923,10 +2070,11 @@ distclean: clean
 
 clean:
        $(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
-               $(LIB_FILE) $(XDIFF_LIB)
-       $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
+               builtin/*.o $(LIB_FILE) $(XDIFF_LIB)
+       $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
        $(RM) -r bin-wrappers
+       $(RM) -r $(dep_dirs)
        $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
        $(RM) -r autom4te.cache
        $(RM) config.log config.mak.autogen config.mak.append config.status config.cache
@@ -1956,12 +2104,13 @@ endif
 ### Check documentation
 #
 check-docs::
-       @(for v in $(ALL_PROGRAMS) $(BUILT_INS) git gitk; \
+       @(for v in $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git gitk; \
        do \
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
                git-merge-resolve | git-merge-subtree | \
                git-fsck-objects | git-init-db | \
+               git-remote-* | git-stage | \
                git-?*--?* ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
@@ -1999,9 +2148,12 @@ check-docs::
                documented,gitrepository-layout | \
                documented,gittutorial | \
                documented,gittutorial-2 | \
+               documented,git-bisect-lk2009 | \
+               documented,git-remote-helpers | \
+               documented,gitworkflows | \
                sentinel,not,matching,is,ok ) continue ;; \
                esac; \
-               case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \
+               case " $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git gitk " in \
                *" $$cmd "*)    ;; \
                *) echo "removed but $$how: $$cmd" ;; \
                esac; \
index 7b9bde663bd5b2b8eec6b7e91c1de275f6afa460..00e77229ddadcb8a13b10b693a96bd76a4cb9f33 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.7.0.txt
\ No newline at end of file
+Documentation/RelNotes-1.7.1.txt
\ No newline at end of file
index b88122cbe73ec0c438e2d375fdebd51e5febf9ae..c91a29cb298a3ad792ff8745f3e8e0eb28d71678 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -54,8 +54,9 @@ const char *make_absolute_path(const char *path)
                        if (len + strlen(last_elem) + 2 > PATH_MAX)
                                die ("Too long path name: '%s/%s'",
                                                buf, last_elem);
-                       buf[len] = '/';
-                       strcpy(buf + len + 1, last_elem);
+                       if (len && buf[len-1] != '/')
+                               buf[len++] = '/';
+                       strcpy(buf + len, last_elem);
                        free(last_elem);
                        last_elem = NULL;
                }
index 936d98ba2ba2c597f69188c8e7f9f1abcc7169a8..0be4b5f008e1646946ba12fa5e51aa0830911ece 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -5,6 +5,7 @@ int advice_status_hints = 1;
 int advice_commit_before_merge = 1;
 int advice_resolve_conflict = 1;
 int advice_implicit_identity = 1;
+int advice_detached_head = 1;
 
 static struct {
        const char *name;
@@ -15,6 +16,7 @@ static struct {
        { "commitbeforemerge", &advice_commit_before_merge },
        { "resolveconflict", &advice_resolve_conflict },
        { "implicitidentity", &advice_implicit_identity },
+       { "detachedhead", &advice_detached_head },
 };
 
 int git_default_advice_config(const char *var, const char *value)
index 9b7a3ad1ca126cb882e87d400820043043061f78..3244ebb5c1caffebaaf8f28ce221533c38ec29b5 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -8,6 +8,7 @@ extern int advice_status_hints;
 extern int advice_commit_before_merge;
 extern int advice_resolve_conflict;
 extern int advice_implicit_identity;
+extern int advice_detached_head;
 
 int git_default_advice_config(const char *var, const char *value);
 
index 6dc27ee7a6090e56d5b0f2072a72553d3b3e3b87..b556b11610480afd80cddd86a81af9737254ee36 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -986,6 +986,12 @@ int bisect_next_all(const char *prefix)
                exit(1);
        }
 
+       if (!all) {
+               fprintf(stderr, "No testable commit found.\n"
+                       "Maybe you started with bad path parameters?\n");
+               exit(4);
+       }
+
        bisect_rev = revs.commits->item->object.sha1;
        memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), 41);
 
index e8202f3f5e57a302634be9268866edaba0fd6eb0..464588b299a473e9e1ee58cbc61e2f981030e37d 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -5,6 +5,7 @@
 #include "strbuf.h"
 #include "cache.h"
 #include "commit.h"
+#include "notes.h"
 
 extern const char git_version_string[];
 extern const char git_usage_string[];
@@ -18,6 +19,24 @@ extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
 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 {
+       struct notes_tree **trees;
+       const char *cmd;
+       int enabled;
+       combine_notes_fn *combine;
+       struct string_list *refs;
+       int refs_from_env;
+       int mode_from_env;
+};
+
+combine_notes_fn *parse_combine_notes_fn(const char *v);
+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
+                         const unsigned char *from_obj, const unsigned char *to_obj);
+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
+
 extern int check_pager_config(const char *cmd);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
@@ -78,6 +97,7 @@ extern int cmd_mktag(int argc, const char **argv, const char *prefix);
 extern int cmd_mktree(int argc, const char **argv, const char *prefix);
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
+extern int cmd_notes(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
 extern int cmd_patch_id(int argc, const char **argv, const char *prefix);
similarity index 94%
rename from builtin-add.c
rename to builtin/add.c
index 2705f8d057a93f7b4a9351713b89fd9f4e041815..87d2980313e71afc08df3eacde4a99a8468ff874 100644 (file)
@@ -117,7 +117,19 @@ static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
        }
 }
 
-static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
+static char *find_used_pathspec(const char **pathspec)
+{
+       char *seen;
+       int i;
+
+       for (i = 0; pathspec[i];  i++)
+               ; /* just counting */
+       seen = xcalloc(i, 1);
+       fill_pathspec_matches(pathspec, seen, i);
+       return seen;
+}
+
+static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
 {
        char *seen;
        int i, specs;
@@ -137,13 +149,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
        }
        dir->nr = dst - dir->entries;
        fill_pathspec_matches(pathspec, seen, specs);
-
-       for (i = 0; i < specs; i++) {
-               if (!seen[i] && pathspec[i][0] && !file_exists(pathspec[i]))
-                       die("pathspec '%s' did not match any files",
-                                       pathspec[i]);
-       }
-        free(seen);
+       return seen;
 }
 
 static void treat_gitlinks(const char **pathspec)
@@ -359,6 +365,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        int flags;
        int add_new_files;
        int require_pathspec;
+       char *seen = NULL;
 
        git_config(add_config, NULL);
 
@@ -418,7 +425,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                /* This picks up the paths that are not tracked */
                baselen = fill_directory(&dir, pathspec);
                if (pathspec)
-                       prune_directory(&dir, pathspec, baselen);
+                       seen = prune_directory(&dir, pathspec, baselen);
        }
 
        if (refresh_only) {
@@ -426,6 +433,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                goto finish;
        }
 
+       if (pathspec) {
+               int i;
+               if (!seen)
+                       seen = find_used_pathspec(pathspec);
+               for (i = 0; pathspec[i]; i++) {
+                       if (!seen[i] && pathspec[i][0]
+                           && !file_exists(pathspec[i]))
+                               die("pathspec '%s' did not match any files",
+                                   pathspec[i]);
+               }
+               free(seen);
+       }
+
        exit_status |= add_files_to_cache(prefix, pathspec, flags);
 
        if (add_new_files)
similarity index 100%
rename from builtin-annotate.c
rename to builtin/annotate.c
similarity index 95%
rename from builtin-apply.c
rename to builtin/apply.c
index 2a1004d025fcfdea4d317ef3236ff6bc76e3e65a..7ca90472c10eed8535e76b1e0ec25c92af7cff05 100644 (file)
@@ -1854,33 +1854,76 @@ static int match_fragment(struct image *img,
 {
        int i;
        char *fixed_buf, *buf, *orig, *target;
+       int preimage_limit;
 
-       if (preimage->nr + try_lno > img->nr)
+       if (preimage->nr + try_lno <= img->nr) {
+               /*
+                * The hunk falls within the boundaries of img.
+                */
+               preimage_limit = preimage->nr;
+               if (match_end && (preimage->nr + try_lno != img->nr))
+                       return 0;
+       } else if (ws_error_action == correct_ws_error &&
+                  (ws_rule & WS_BLANK_AT_EOF) && match_end) {
+               /*
+                * This hunk that matches at the end extends beyond
+                * the end of img, and we are removing blank lines
+                * at the end of the file.  This many lines from the
+                * beginning of the preimage must match with img, and
+                * the remainder of the preimage must be blank.
+                */
+               preimage_limit = img->nr - try_lno;
+       } else {
+               /*
+                * The hunk extends beyond the end of the img and
+                * we are not removing blanks at the end, so we
+                * should reject the hunk at this position.
+                */
                return 0;
+       }
 
        if (match_beginning && try_lno)
                return 0;
 
-       if (match_end && preimage->nr + try_lno != img->nr)
-               return 0;
-
        /* Quick hash check */
-       for (i = 0; i < preimage->nr; i++)
+       for (i = 0; i < preimage_limit; i++)
                if (preimage->line[i].hash != img->line[try_lno + i].hash)
                        return 0;
 
-       /*
-        * Do we have an exact match?  If we were told to match
-        * at the end, size must be exactly at try+fragsize,
-        * otherwise try+fragsize must be still within the preimage,
-        * and either case, the old piece should match the preimage
-        * exactly.
-        */
-       if ((match_end
-            ? (try + preimage->len == img->len)
-            : (try + preimage->len <= img->len)) &&
-           !memcmp(img->buf + try, preimage->buf, preimage->len))
-               return 1;
+       if (preimage_limit == preimage->nr) {
+               /*
+                * Do we have an exact match?  If we were told to match
+                * at the end, size must be exactly at try+fragsize,
+                * otherwise try+fragsize must be still within the preimage,
+                * and either case, the old piece should match the preimage
+                * exactly.
+                */
+               if ((match_end
+                    ? (try + preimage->len == img->len)
+                    : (try + preimage->len <= img->len)) &&
+                   !memcmp(img->buf + try, preimage->buf, preimage->len))
+                       return 1;
+       } else {
+               /*
+                * The preimage extends beyond the end of img, so
+                * there cannot be an exact match.
+                *
+                * There must be one non-blank context line that match
+                * a line before the end of img.
+                */
+               char *buf_end;
+
+               buf = preimage->buf;
+               buf_end = buf;
+               for (i = 0; i < preimage_limit; i++)
+                       buf_end += preimage->line[i].len;
+
+               for ( ; buf < buf_end; buf++)
+                       if (!isspace(*buf))
+                               break;
+               if (buf == buf_end)
+                       return 0;
+       }
 
        /*
         * No exact match. If we are ignoring whitespace, run a line-by-line
@@ -1891,7 +1934,10 @@ static int match_fragment(struct image *img,
                size_t imgoff = 0;
                size_t preoff = 0;
                size_t postlen = postimage->len;
-               for (i = 0; i < preimage->nr; i++) {
+               size_t extra_chars;
+               char *preimage_eof;
+               char *preimage_end;
+               for (i = 0; i < preimage_limit; i++) {
                        size_t prelen = preimage->line[i].len;
                        size_t imglen = img->line[try_lno+i].len;
 
@@ -1905,20 +1951,36 @@ static int match_fragment(struct image *img,
                }
 
                /*
-                * Ok, the preimage matches with whitespace fuzz. Update it and
-                * the common postimage lines to use the same whitespace as the
-                * target. imgoff now holds the true length of the target that
-                * matches the preimage, and we need to update the line lengths
-                * of the preimage to match the target ones.
+                * Ok, the preimage matches with whitespace fuzz.
+                *
+                * imgoff now holds the true length of the target that
+                * matches the preimage before the end of the file.
+                *
+                * Count the number of characters in the preimage that fall
+                * beyond the end of the file and make sure that all of them
+                * are whitespace characters. (This can only happen if
+                * we are removing blank lines at the end of the file.)
                 */
-               fixed_buf = xmalloc(imgoff);
-               memcpy(fixed_buf, img->buf + try, imgoff);
-               for (i = 0; i < preimage->nr; i++)
-                       preimage->line[i].len = img->line[try_lno+i].len;
+               buf = preimage_eof = preimage->buf + preoff;
+               for ( ; i < preimage->nr; i++)
+                       preoff += preimage->line[i].len;
+               preimage_end = preimage->buf + preoff;
+               for ( ; buf < preimage_end; buf++)
+                       if (!isspace(*buf))
+                               return 0;
 
                /*
-                * Update the preimage buffer and the postimage context lines.
+                * Update the preimage and the common postimage context
+                * lines to use the same whitespace as the target.
+                * If whitespace is missing in the target (i.e.
+                * if the preimage extends beyond the end of the file),
+                * 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;
                update_pre_post_images(preimage, postimage,
                                fixed_buf, imgoff, postlen);
                return 1;
@@ -1932,12 +1994,16 @@ static int match_fragment(struct image *img,
         * it might with whitespace fuzz. We haven't been asked to
         * ignore whitespace, we were asked to correct whitespace
         * errors, so let's try matching after whitespace correction.
+        *
+        * The preimage may extend beyond the end of the file,
+        * 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;
        orig = preimage->buf;
        target = img->buf + try;
-       for (i = 0; i < preimage->nr; i++) {
+       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;
@@ -1977,6 +2043,29 @@ static int match_fragment(struct image *img,
                target += tgtlen;
        }
 
+
+       /*
+        * Now handle the lines in the preimage that falls beyond the
+        * end of the file (if any). They will only match if they are
+        * empty or only contain whitespace (if WS_BLANK_AT_EOL is
+        * false).
+        */
+       for ( ; i < preimage->nr; i++) {
+               size_t fixlen; /* length after fixing the 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);
+
+               for (j = 0; j < fixlen; j++)
+                       if (!isspace(buf[j]))
+                               goto unmatch_exit;
+
+               orig += oldlen;
+               buf += fixlen;
+       }
+
        /*
         * Yes, the preimage is based on an older version that still
         * has whitespace breakages unfixed, and fixing them makes the
@@ -2002,11 +2091,8 @@ static int find_pos(struct image *img,
        unsigned long backwards, forwards, try;
        int backwards_lno, forwards_lno, try_lno;
 
-       if (preimage->nr > img->nr)
-               return -1;
-
        /*
-        * If match_begining or match_end is specified, there is no
+        * If match_beginning or match_end is specified, there is no
         * point starting from a wrong line that will never match and
         * wander around and wait for a match at the specified end.
         */
@@ -2015,7 +2101,12 @@ static int find_pos(struct image *img,
        else if (match_end)
                line = img->nr - preimage->nr;
 
-       if (line > img->nr)
+       /*
+        * Because the comparison is unsigned, the following test
+        * will also take care of a negative line number that can
+        * result when match_end and preimage is larger than the target.
+        */
+       if ((size_t) line > img->nr)
                line = img->nr;
 
        try = 0;
@@ -2091,12 +2182,26 @@ static void update_image(struct image *img,
        int i, nr;
        size_t remove_count, insert_count, applied_at = 0;
        char *result;
+       int preimage_limit;
+
+       /*
+        * If we are removing blank lines at the end of img,
+        * the preimage may extend beyond the end.
+        * If that is the case, we must be careful only to
+        * remove the part of the preimage that falls within
+        * the boundaries of img. Initialize preimage_limit
+        * to the number of lines in the preimage that falls
+        * within the boundaries.
+        */
+       preimage_limit = preimage->nr;
+       if (preimage_limit > img->nr - applied_pos)
+               preimage_limit = img->nr - applied_pos;
 
        for (i = 0; i < applied_pos; i++)
                applied_at += img->line[i].len;
 
        remove_count = 0;
-       for (i = 0; i < preimage->nr; i++)
+       for (i = 0; i < preimage_limit; i++)
                remove_count += img->line[applied_pos + i].len;
        insert_count = postimage->len;
 
@@ -2113,8 +2218,8 @@ static void update_image(struct image *img,
        result[img->len] = '\0';
 
        /* Adjust the line table */
-       nr = img->nr + postimage->nr - preimage->nr;
-       if (preimage->nr < postimage->nr) {
+       nr = img->nr + postimage->nr - preimage_limit;
+       if (preimage_limit < postimage->nr) {
                /*
                 * NOTE: this knows that we never call remove_first_line()
                 * on anything other than pre/post image.
@@ -2122,10 +2227,10 @@ static void update_image(struct image *img,
                img->line = xrealloc(img->line, nr * sizeof(*img->line));
                img->line_allocated = img->line;
        }
-       if (preimage->nr != postimage->nr)
+       if (preimage_limit != postimage->nr)
                memmove(img->line + applied_pos + postimage->nr,
-                       img->line + applied_pos + preimage->nr,
-                       (img->nr - (applied_pos + preimage->nr)) *
+                       img->line + applied_pos + preimage_limit,
+                       (img->nr - (applied_pos + preimage_limit)) *
                        sizeof(*img->line));
        memcpy(img->line + applied_pos,
               postimage->line,
@@ -2321,7 +2426,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
 
        if (applied_pos >= 0) {
                if (new_blank_lines_at_end &&
-                   preimage.nr + applied_pos == img->nr &&
+                   preimage.nr + applied_pos >= img->nr &&
                    (ws_rule & WS_BLANK_AT_EOF) &&
                    ws_error_action != nowarn_ws_error) {
                        record_ws_error(WS_BLANK_AT_EOF, "+", 1, frag->linenr);
similarity index 100%
rename from builtin-archive.c
rename to builtin/archive.c
similarity index 99%
rename from builtin-blame.c
rename to builtin/blame.c
index 10f7eacf6e881cdb54a6b4a4c0aafc5f9751e5a9..fc1586350f94ae48e7e48a51818517b465e7a40d 100644 (file)
@@ -1772,7 +1772,7 @@ static int lineno_width(int lines)
 {
        int i, width;
 
-       for (width = 1, i = 10; i <= lines + 1; width++)
+       for (width = 1, i = 10; i <= lines; width++)
                i *= 10;
        return width;
 }
similarity index 99%
rename from builtin-branch.c
rename to builtin/branch.c
index a28a13986d11ebfecd11a206d1b7a1fb626865db..6cf7e721e6b59f50c7a8018296aa49848deecda3 100644 (file)
@@ -610,7 +610,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        BRANCH_TRACK_EXPLICIT),
                OPT_SET_INT( 0, "set-upstream",  &track, "change upstream info",
                        BRANCH_TRACK_OVERRIDE),
-               OPT_BOOLEAN( 0 , "color",  &branch_use_color, "use colored output"),
+               OPT__COLOR(&branch_use_color, "use colored output"),
                OPT_SET_INT('r', NULL,     &kinds, "act on remote-tracking branches",
                        REF_REMOTE_BRANCH),
                {
similarity index 100%
rename from builtin-bundle.c
rename to builtin/bundle.c
similarity index 97%
rename from builtin-cat-file.c
rename to builtin/cat-file.c
index 590684200854ad6a71653f30d494eb191fd4a324..a933eaa043257c84f35e1d86728ca91be035caff 100644 (file)
@@ -219,9 +219,10 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                            "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, "batch", &batch,
-                           "show info and content of objects feeded on stdin", BATCH),
+                           "show info and content of objects fed from the standard input",
+                           BATCH),
                OPT_SET_INT(0, "batch-check", &batch,
-                           "show info about objects feeded on stdin",
+                           "show info about objects fed from the standard input",
                            BATCH_CHECK),
                OPT_END()
        };
similarity index 100%
rename from builtin-check-attr.c
rename to builtin/check-attr.c
similarity index 96%
rename from builtin-checkout.c
rename to builtin/checkout.c
index 527781728e0706b906a94ddfb8ee2e8bb06fa05e..acefaaf41a4e26e45225f6d228734c93c8f2b23c 100644 (file)
@@ -128,24 +128,6 @@ static int checkout_stage(int stage, struct cache_entry *ce, int pos,
                     (stage == 2) ? "our" : "their");
 }
 
-/* NEEDSWORK: share with merge-recursive */
-static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
-{
-       unsigned long size;
-       enum object_type type;
-
-       if (!hashcmp(sha1, null_sha1)) {
-               mm->ptr = xstrdup("");
-               mm->size = 0;
-               return;
-       }
-
-       mm->ptr = read_sha1_file(sha1, &type, &size);
-       if (!mm->ptr || type != OBJ_BLOB)
-               die("unable to read blob object %s", sha1_to_hex(sha1));
-       mm->size = size;
-}
-
 static int checkout_merged(int pos, struct checkout *state)
 {
        struct cache_entry *ce = active_cache[pos];
@@ -163,9 +145,9 @@ static int checkout_merged(int pos, struct checkout *state)
            ce_stage(active_cache[pos+2]) != 3)
                return error("path '%s' does not have all 3 versions", path);
 
-       fill_mm(active_cache[pos]->sha1, &ancestor);
-       fill_mm(active_cache[pos+1]->sha1, &ours);
-       fill_mm(active_cache[pos+2]->sha1, &theirs);
+       read_mmblob(&ancestor, active_cache[pos]->sha1);
+       read_mmblob(&ours, active_cache[pos+1]->sha1);
+       read_mmblob(&theirs, active_cache[pos+2]->sha1);
 
        status = ll_merge(&result_buf, path, &ancestor,
                          &ours, "ours", &theirs, "theirs", 0);
@@ -488,6 +470,20 @@ static void report_tracking(struct branch_info *new)
        strbuf_release(&sb);
 }
 
+static void detach_advice(const char *old_path, const char *new_name)
+{
+       const char fmt[] =
+       "Note: checking out '%s'.\n\n"
+       "You are in 'detached HEAD' state. You can look around, make experimental\n"
+       "changes and commit them, and you can discard any commits you make in this\n"
+       "state without impacting any branches by performing another checkout.\n\n"
+       "If you want to create a new branch to retain commits you create, you may\n"
+       "do so (now or later) by using -b with the checkout command again. Example:\n\n"
+       "  git checkout -b new_branch_name\n\n";
+
+       fprintf(stderr, fmt, new_name);
+}
+
 static void update_refs_for_switch(struct checkout_opts *opts,
                                   struct branch_info *old,
                                   struct branch_info *new)
@@ -522,8 +518,8 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
                           REF_NODEREF, DIE_ON_ERR);
                if (!opts->quiet) {
-                       if (old->path)
-                               fprintf(stderr, "Note: moving to '%s' which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n  git checkout -b <new_branch_name>\n", new->name);
+                       if (old->path && advice_detached_head)
+                               detach_advice(old->path, new->name);
                        describe_detached_head("HEAD is now at", new->commit);
                }
        }
similarity index 100%
rename from builtin-clean.c
rename to builtin/clean.c
similarity index 97%
rename from builtin-clone.c
rename to builtin/clone.c
index 58bacbd552c1e2496034346265a3e5ab219e2672..05f8fb4771b1ef07030338a6fd38dc7cb3bc1d1d 100644 (file)
@@ -37,18 +37,17 @@ static const char * const builtin_clone_usage[] = {
        NULL
 };
 
-static int option_quiet, option_no_checkout, option_bare, option_mirror;
+static int option_no_checkout, option_bare, option_mirror;
 static int option_local, option_no_hardlinks, option_shared, option_recursive;
 static char *option_template, *option_reference, *option_depth;
 static char *option_origin = NULL;
 static char *option_branch = NULL;
 static char *option_upload_pack = "git-upload-pack";
-static int option_verbose;
+static int option_verbosity;
 static int option_progress;
 
 static struct option builtin_clone_options[] = {
-       OPT__QUIET(&option_quiet),
-       OPT__VERBOSE(&option_verbose),
+       OPT__VERBOSITY(&option_verbosity),
        OPT_BOOLEAN(0, "progress", &option_progress,
                        "force progress reporting"),
        OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
@@ -462,7 +461,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                die("could not create leading directories of '%s'", git_dir);
        set_git_dir(make_absolute_path(git_dir));
 
-       init_db(option_template, option_quiet ? INIT_DB_QUIET : 0);
+       init_db(option_template, (option_verbosity < 0) ? INIT_DB_QUIET : 0);
 
        /*
         * At this point, the config exists, so we do not need the
@@ -526,13 +525,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        transport_set_option(transport, TRANS_OPT_DEPTH,
                                             option_depth);
 
-               if (option_quiet)
-                       transport->verbose = -1;
-               else if (option_verbose)
-                       transport->verbose = 1;
-
-               if (option_progress)
-                       transport->progress = 1;
+               transport_set_verbosity(transport, option_verbosity, option_progress);
 
                if (option_upload_pack)
                        transport_set_option(transport, TRANS_OPT_UPLOADPACK,
@@ -641,7 +634,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                opts.update = 1;
                opts.merge = 1;
                opts.fn = oneway_merge;
-               opts.verbose_update = !option_quiet;
+               opts.verbose_update = (option_verbosity > 0);
                opts.src_index = &the_index;
                opts.dst_index = &the_index;
 
similarity index 100%
rename from builtin-commit-tree.c
rename to builtin/commit-tree.c
similarity index 96%
rename from builtin-commit.c
rename to builtin/commit.c
index 55676fd874466c70445e81c69cf397cd01380aaf..8dd104ee0b247f5536d02d54a932acfcbaec603b 100644 (file)
@@ -41,7 +41,7 @@ static const char implicit_ident_advice[] =
 "on your username and hostname. Please check that they are accurate.\n"
 "You can suppress this message by setting them explicitly:\n"
 "\n"
-"    git config --global user.name Your Name\n"
+"    git config --global user.name \"Your Name\"\n"
 "    git config --global user.email you@example.com\n"
 "\n"
 "If the identity used for this commit is wrong, you can fix it with:\n"
@@ -66,6 +66,7 @@ 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;
 /*
  * The default commit message cleanup mode will remove the lines
@@ -137,6 +138,7 @@ static struct option builtin_commit_options[] = {
        OPT_BOOLEAN('z', "null", &null_termination,
                    "terminate entries with NUL"),
        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 */
@@ -1046,7 +1048,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        if (*argv)
                s.pathspec = get_pathspec(prefix, argv);
 
-       read_cache();
+       read_cache_preload(s.pathspec);
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
        s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
        s.in_merge = in_merge;
@@ -1160,6 +1162,40 @@ static int git_commit_config(const char *k, const char *v, void *cb)
        return git_status_config(k, v, s);
 }
 
+static const char post_rewrite_hook[] = "hooks/post-rewrite";
+
+static int run_rewrite_hook(const unsigned char *oldsha1,
+                           const unsigned char *newsha1)
+{
+       /* oldsha1 SP newsha1 LF NUL */
+       static char buf[2*40 + 3];
+       struct child_process proc;
+       const char *argv[3];
+       int code;
+       size_t n;
+
+       if (access(git_path(post_rewrite_hook), X_OK) < 0)
+               return 0;
+
+       argv[0] = git_path(post_rewrite_hook);
+       argv[1] = "amend";
+       argv[2] = NULL;
+
+       memset(&proc, 0, sizeof(proc));
+       proc.argv = argv;
+       proc.in = -1;
+       proc.stdout_to_stderr = 1;
+
+       code = start_command(&proc);
+       if (code)
+               return code;
+       n = snprintf(buf, sizeof(buf), "%s %s\n",
+                    sha1_to_hex(oldsha1), sha1_to_hex(newsha1));
+       write_in_full(proc.in, buf, n);
+       close(proc.in);
+       return finish_command(&proc);
+}
+
 int cmd_commit(int argc, const char **argv, const char *prefix)
 {
        struct strbuf sb = STRBUF_INIT;
@@ -1303,6 +1339,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        rerere(0);
        run_hook(get_index_file(), "post-commit", NULL);
+       if (amend && !no_post_rewrite) {
+               struct notes_rewrite_cfg *cfg;
+               cfg = init_copy_notes_for_rewrite("amend");
+               if (cfg) {
+                       copy_note_for_rewrite(cfg, head_sha1, commit_sha1);
+                       finish_copy_notes_for_rewrite(cfg);
+               }
+               run_rewrite_hook(head_sha1, commit_sha1);
+       }
        if (!quiet)
                print_summary(prefix, commit_sha1);
 
similarity index 100%
rename from builtin-config.c
rename to builtin/config.c
similarity index 100%
rename from builtin-describe.c
rename to builtin/describe.c
similarity index 100%
rename from builtin-diff-files.c
rename to builtin/diff-files.c
similarity index 100%
rename from builtin-diff-index.c
rename to builtin/diff-index.c
similarity index 90%
rename from builtin-diff-tree.c
rename to builtin/diff-tree.c
index 2380c21951fb5fb8050ab1acf0e7f01f36ea5520..3c78bda5664e20086bcd500105b039d6241e3782 100644 (file)
@@ -92,12 +92,23 @@ static const char diff_tree_usage[] =
 "  --root        include the initial commit as diff against /dev/null\n"
 COMMON_DIFF_OPTIONS_HELP;
 
+static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
+{
+       if (!rev->diffopt.output_format) {
+               if (rev->dense_combined_merges)
+                       rev->diffopt.output_format = DIFF_FORMAT_PATCH;
+               else
+                       rev->diffopt.output_format = DIFF_FORMAT_RAW;
+       }
+}
+
 int cmd_diff_tree(int argc, const char **argv, const char *prefix)
 {
        int nr_sha1;
        char line[1000];
        struct object *tree1, *tree2;
        static struct rev_info *opt = &log_tree_opt;
+       struct setup_revision_opt s_r_opt;
        int read_stdin = 0;
 
        init_revisions(opt, prefix);
@@ -105,7 +116,9 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        opt->abbrev = 0;
        opt->diff = 1;
        opt->disable_stdin = 1;
-       argc = setup_revisions(argc, argv, opt, NULL);
+       memset(&s_r_opt, 0, sizeof(s_r_opt));
+       s_r_opt.tweak = diff_tree_tweak_rev;
+       argc = setup_revisions(argc, argv, opt, &s_r_opt);
 
        while (--argc > 0) {
                const char *arg = *++argv;
@@ -117,9 +130,6 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                usage(diff_tree_usage);
        }
 
-       if (!opt->diffopt.output_format)
-               opt->diffopt.output_format = DIFF_FORMAT_RAW;
-
        /*
         * NOTE! We expect "a ^b" to be equal to "a..b", so we
         * reverse the order of the objects if the second one
similarity index 100%
rename from builtin-diff.c
rename to builtin/diff.c
similarity index 100%
rename from builtin-fast-export.c
rename to builtin/fast-export.c
similarity index 99%
rename from builtin-fetch-pack.c
rename to builtin/fetch-pack.c
index 8ed4a6feaac2868523e6516e02865132dedbc9f5..dbd8b7bcc8b5ddea9bec3a0c774346007cb0d31c 100644 (file)
@@ -586,12 +586,12 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
        return retval;
 }
 
-static int sideband_demux(int fd, void *data)
+static int sideband_demux(int in, int out, void *data)
 {
        int *xd = data;
 
-       int ret = recv_sideband("fetch-pack", xd[0], fd);
-       close(fd);
+       int ret = recv_sideband("fetch-pack", xd[0], out);
+       close(out);
        return ret;
 }
 
@@ -613,6 +613,7 @@ static int get_pack(int xd[2], char **pack_lockfile)
                 */
                demux.proc = sideband_demux;
                demux.data = xd;
+               demux.out = -1;
                if (start_async(&demux))
                        die("fetch-pack: unable to fork off sideband"
                            " demultiplexer");
similarity index 94%
rename from builtin-fetch.c
rename to builtin/fetch.c
index 8654fa7a2dbe2c1ca76a493a4322447de5219b99..957be9f9269c657fb094b1b3daa4f309c1324ae0 100644 (file)
@@ -11,6 +11,7 @@
 #include "run-command.h"
 #include "parse-options.h"
 #include "sigchain.h"
+#include "transport.h"
 
 static const char * const builtin_fetch_usage[] = {
        "git fetch [options] [<repository> <refspec>...]",
@@ -27,6 +28,7 @@ enum {
 };
 
 static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
+static int progress;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -56,6 +58,7 @@ static struct option builtin_fetch_options[] = {
        OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
        OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
                    "allow updating of HEAD ref"),
+       OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
        OPT_STRING(0, "depth", &depth, "DEPTH",
                   "deepen history of shallow clone"),
        OPT_END()
@@ -104,10 +107,8 @@ static void add_merge_config(struct ref **head,
                 * there is no entry in the resulting FETCH_HEAD marked
                 * for merging.
                 */
+               memset(&refspec, 0, sizeof(refspec));
                refspec.src = branch->merge[i]->src;
-               refspec.dst = NULL;
-               refspec.pattern = 0;
-               refspec.force = 0;
                get_fetch_map(remote_refs, &refspec, tail, 1);
                for (rm = *old_tail; rm; rm = rm->next)
                        rm->merge = 1;
@@ -205,7 +206,6 @@ static int s_update_ref(const char *action,
        return 0;
 }
 
-#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 #define REFCOL_WIDTH  10
 
 static int update_local_ref(struct ref *ref,
@@ -224,7 +224,7 @@ static int update_local_ref(struct ref *ref,
 
        if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
                if (verbosity > 0)
-                       sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
+                       sprintf(display, "= %-*s %-*s -> %s", TRANSPORT_SUMMARY_WIDTH,
                                "[up to date]", REFCOL_WIDTH, remote,
                                pretty_ref);
                return 0;
@@ -239,7 +239,7 @@ static int update_local_ref(struct ref *ref,
                 * the head, and the old value of the head isn't empty...
                 */
                sprintf(display, "! %-*s %-*s -> %s  (can't fetch in current branch)",
-                       SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
+                       TRANSPORT_SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
                        pretty_ref);
                return 1;
        }
@@ -249,7 +249,7 @@ static int update_local_ref(struct ref *ref,
                int r;
                r = s_update_ref("updating tag", ref, 0);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-',
-                       SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote,
+                       TRANSPORT_SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote,
                        pretty_ref, r ? "  (unable to update local ref)" : "");
                return r;
        }
@@ -271,7 +271,7 @@ static int update_local_ref(struct ref *ref,
 
                r = s_update_ref(msg, ref, 0);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*',
-                       SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
+                       TRANSPORT_SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
                        r ? "  (unable to update local ref)" : "");
                return r;
        }
@@ -284,7 +284,7 @@ static int update_local_ref(struct ref *ref,
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
                r = s_update_ref("fast-forward", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
-                       SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
+                       TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
                        pretty_ref, r ? "  (unable to update local ref)" : "");
                return r;
        } else if (force || ref->force) {
@@ -295,13 +295,13 @@ static int update_local_ref(struct ref *ref,
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
                r = s_update_ref("forced-update", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s  (%s)", r ? '!' : '+',
-                       SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
+                       TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
                        pretty_ref,
                        r ? "unable to update local ref" : "forced update");
                return r;
        } else {
                sprintf(display, "! %-*s %-*s -> %s  (non-fast-forward)",
-                       SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
+                       TRANSPORT_SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
                        pretty_ref);
                return 1;
        }
@@ -389,11 +389,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                fputc(url[i], fp);
                fputc('\n', fp);
 
-               if (ref)
+               if (ref) {
                        rc |= update_local_ref(ref, what, note);
-               else
+                       free(ref);
+               } else
                        sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
-                               SUMMARY_WIDTH, *kind ? kind : "branch",
+                               TRANSPORT_SUMMARY_WIDTH, *kind ? kind : "branch",
                                 REFCOL_WIDTH, *what ? what : "HEAD");
                if (*note) {
                        if (verbosity >= 0 && !shown_url) {
@@ -514,7 +515,7 @@ static int prune_refs(struct transport *transport, struct ref *ref_map)
                        result |= delete_ref(ref->name, NULL, 0);
                if (verbosity >= 0) {
                        fprintf(stderr, " x %-*s %-*s -> %s\n",
-                               SUMMARY_WIDTH, "[deleted]",
+                               TRANSPORT_SUMMARY_WIDTH, "[deleted]",
                                REFCOL_WIDTH, "(none)", prettify_refname(ref->name));
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
@@ -588,7 +589,7 @@ static void find_non_local_tags(struct transport *transport,
                 * to fetch then we can mark the ref entry in the list
                 * as one to ignore by setting util to NULL.
                 */
-               if (!strcmp(ref->name + strlen(ref->name) - 3, "^{}")) {
+               if (!suffixcmp(ref->name, "^{}")) {
                        if (item && !has_sha1_file(ref->old_sha1) &&
                            !will_fetch(head, ref->old_sha1) &&
                            !has_sha1_file(item->util) &&
@@ -651,6 +652,17 @@ static void check_not_current_branch(struct ref *ref_map)
                            "of non-bare repository", current_branch->refname);
 }
 
+static int truncate_fetch_head(void)
+{
+       char *filename = git_path("FETCH_HEAD");
+       FILE *fp = fopen(filename, "w");
+
+       if (!fp)
+               return error("cannot open %s: %s\n", filename, strerror(errno));
+       fclose(fp);
+       return 0;
+}
+
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
@@ -672,11 +684,9 @@ static int do_fetch(struct transport *transport,
 
        /* if not appending, truncate FETCH_HEAD */
        if (!append && !dry_run) {
-               char *filename = git_path("FETCH_HEAD");
-               FILE *fp = fopen(filename, "w");
-               if (!fp)
-                       return error("cannot open %s: %s\n", filename, strerror(errno));
-               fclose(fp);
+               int errcode = truncate_fetch_head();
+               if (errcode)
+                       return errcode;
        }
 
        ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
@@ -784,13 +794,19 @@ static int add_remote_or_group(const char *name, struct string_list *list)
 static int fetch_multiple(struct string_list *list)
 {
        int i, result = 0;
-       const char *argv[] = { "fetch", NULL, NULL, NULL, NULL, NULL, NULL };
-       int argc = 1;
+       const char *argv[11] = { "fetch", "--append" };
+       int argc = 2;
 
        if (dry_run)
                argv[argc++] = "--dry-run";
        if (prune)
                argv[argc++] = "--prune";
+       if (update_head_ok)
+               argv[argc++] = "--update-head-ok";
+       if (force)
+               argv[argc++] = "--force";
+       if (keep)
+               argv[argc++] = "--keep";
        if (verbosity >= 2)
                argv[argc++] = "-v";
        if (verbosity >= 1)
@@ -798,9 +814,16 @@ static int fetch_multiple(struct string_list *list)
        else if (verbosity < 0)
                argv[argc++] = "-q";
 
+       if (!append && !dry_run) {
+               int errcode = truncate_fetch_head();
+               if (errcode)
+                       return errcode;
+       }
+
        for (i = 0; i < list->nr; i++) {
                const char *name = list->items[i].string;
                argv[argc] = name;
+               argv[argc + 1] = NULL;
                if (verbosity >= 0)
                        printf("Fetching %s\n", name);
                if (run_command_v_opt(argv, RUN_GIT_CMD)) {
@@ -823,10 +846,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
                die("Where do you want to fetch from today?");
 
        transport = transport_get(remote, NULL);
-       if (verbosity >= 2)
-               transport->verbose = verbosity <= 3 ? verbosity : 3;
-       if (verbosity < 0)
-               transport->verbose = -1;
+       transport_set_verbosity(transport, verbosity, progress);
        if (upload_pack)
                set_option(TRANS_OPT_UPLOADPACK, upload_pack);
        if (keep)
similarity index 93%
rename from builtin-for-each-ref.c
rename to builtin/for-each-ref.c
index a5a83f14693b94adf3ae0dbc1b500b2e6b2be54d..62be1bbfd6659f9dfac73a17acd1e2d5322dac66 100644 (file)
@@ -33,6 +33,8 @@ struct ref_sort {
 struct refinfo {
        char *refname;
        unsigned char objectname[20];
+       int flag;
+       const char *symref;
        struct atom_value *value;
 };
 
@@ -68,6 +70,8 @@ static struct {
        { "body" },
        { "contents" },
        { "upstream" },
+       { "symref" },
+       { "flag" },
 };
 
 /*
@@ -82,7 +86,7 @@ static struct {
  */
 static const char **used_atom;
 static cmp_type *used_atom_type;
-static int used_atom_cnt, sort_atom_limit, need_tagged;
+static int used_atom_cnt, sort_atom_limit, need_tagged, need_symref;
 
 /*
  * Used to parse format string and sort specifiers
@@ -133,6 +137,10 @@ static int parse_atom(const char *atom, const char *ep)
                                  (sizeof(*used_atom_type) * used_atom_cnt));
        used_atom[at] = xmemdupz(atom, ep - atom);
        used_atom_type[at] = valid_atom[i].cmp_type;
+       if (*atom == '*')
+               need_tagged = 1;
+       if (!strcmp(used_atom[at], "symref"))
+               need_symref = 1;
        return at;
 }
 
@@ -143,7 +151,8 @@ static const char *find_next(const char *cp)
 {
        while (*cp) {
                if (*cp == '%') {
-                       /* %( is the start of an atom;
+                       /*
+                        * %( is the start of an atom;
                         * %% is a quoted per-cent.
                         */
                        if (cp[1] == '(')
@@ -420,7 +429,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                        grab_date(wholine, v, name);
        }
 
-       /* For a tag or a commit object, if "creator" or "creatordate" is
+       /*
+        * For a tag or a commit object, if "creator" or "creatordate" is
         * requested, do something special.
         */
        if (strcmp(who, "tagger") && strcmp(who, "committer"))
@@ -502,7 +512,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
        }
 }
 
-/* We want to have empty print-string for field requests
+/*
+ * We want to have empty print-string for field requests
  * that do not apply (e.g. "authordate" for a tag object)
  */
 static void fill_missing_values(struct atom_value *val)
@@ -548,6 +559,13 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
        }
 }
 
+static inline char *copy_advance(char *dst, const char *src)
+{
+       while (*src)
+               *dst++ = *src++;
+       return dst;
+}
+
 /*
  * Parse the object referred by ref, and grab needed value.
  */
@@ -561,6 +579,16 @@ static void populate_value(struct refinfo *ref)
 
        ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
 
+       if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
+               unsigned char unused1[20];
+               const char *symref;
+               symref = resolve_ref(ref->refname, unused1, 1, NULL);
+               if (symref)
+                       ref->symref = xstrdup(symref);
+               else
+                       ref->symref = "";
+       }
+
        /* Fill in specials first */
        for (i = 0; i < used_atom_cnt; i++) {
                const char *name = used_atom[i];
@@ -576,6 +604,8 @@ static void populate_value(struct refinfo *ref)
 
                if (!prefixcmp(name, "refname"))
                        refname = ref->refname;
+               else if (!prefixcmp(name, "symref"))
+                       refname = ref->symref ? ref->symref : "";
                else if (!prefixcmp(name, "upstream")) {
                        struct branch *branch;
                        /* only local branches may have an upstream */
@@ -588,6 +618,20 @@ static void populate_value(struct refinfo *ref)
                                continue;
                        refname = branch->merge[0]->dst;
                }
+               else if (!strcmp(name, "flag")) {
+                       char buf[256], *cp = buf;
+                       if (ref->flag & REF_ISSYMREF)
+                               cp = copy_advance(cp, ",symref");
+                       if (ref->flag & REF_ISPACKED)
+                               cp = copy_advance(cp, ",packed");
+                       if (cp == buf)
+                               v->s = "";
+                       else {
+                               *cp = '\0';
+                               v->s = xstrdup(buf + 1);
+                       }
+                       continue;
+               }
                else
                        continue;
 
@@ -633,18 +677,21 @@ static void populate_value(struct refinfo *ref)
        if (!eaten)
                free(buf);
 
-       /* If there is no atom that wants to know about tagged
+       /*
+        * If there is no atom that wants to know about tagged
         * object, we are done.
         */
        if (!need_tagged || (obj->type != OBJ_TAG))
                return;
 
-       /* If it is a tag object, see if we use a value that derefs
+       /*
+        * If it is a tag object, see if we use a value that derefs
         * the object, and if we do grab the object it refers to.
         */
        tagged = ((struct tag *)obj)->tagged->sha1;
 
-       /* NEEDSWORK: This derefs tag only once, which
+       /*
+        * NEEDSWORK: This derefs tag only once, which
         * is good to deal with chains of trust, but
         * is not consistent with what deref_tag() does
         * which peels the onion to the core.
@@ -681,9 +728,8 @@ struct grab_ref_cbdata {
 };
 
 /*
- * A call-back given to for_each_ref().  It is unfortunate that we
- * need to use global variables to pass extra information to this
- * function.
+ * A call-back given to for_each_ref().  Filter refs and keep them for
+ * later object processing.
  */
 static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
@@ -711,13 +757,15 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
                        return 0;
        }
 
-       /* We do not open the object yet; sort may only need refname
+       /*
+        * We do not open the object yet; sort may only need refname
         * to do its job and the resulting list may yet to be pruned
         * by maxcount logic.
         */
        ref = xcalloc(1, sizeof(*ref));
        ref->refname = xstrdup(refname);
        hashcpy(ref->objectname, sha1);
+       ref->flag = flag;
 
        cnt = cb->grab_cnt;
        cb->grab_array = xrealloc(cb->grab_array,
@@ -938,13 +986,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        refs = cbdata.grab_array;
        num_refs = cbdata.grab_cnt;
 
-       for (i = 0; i < used_atom_cnt; i++) {
-               if (used_atom[i][0] == '*') {
-                       need_tagged = 1;
-                       break;
-               }
-       }
-
        sort_refs(sort, refs, num_refs);
 
        if (!maxcount || num_refs < maxcount)
similarity index 100%
rename from builtin-fsck.c
rename to builtin/fsck.c
similarity index 100%
rename from builtin-gc.c
rename to builtin/gc.c
similarity index 89%
rename from builtin-grep.c
rename to builtin/grep.c
index 26d4deb1cce3e5540411a47fdf35085649669d06..9d30ddb28df16f834c9fbb3bf6ba8cc172a502b3 100644 (file)
@@ -14,6 +14,7 @@
 #include "userdiff.h"
 #include "grep.h"
 #include "quote.h"
+#include "dir.h"
 
 #ifndef NO_PTHREADS
 #include "thread-utils.h"
@@ -288,6 +289,7 @@ static int wait_all(void)
 static int grep_config(const char *var, const char *value, void *cb)
 {
        struct grep_opt *opt = cb;
+       char *color = NULL;
 
        switch (userdiff_config(var, value)) {
        case 0: break;
@@ -295,17 +297,30 @@ static int grep_config(const char *var, const char *value, void *cb)
        default: return 0;
        }
 
-       if (!strcmp(var, "color.grep")) {
+       if (!strcmp(var, "color.grep"))
                opt->color = git_config_colorbool(var, value, -1);
-               return 0;
-       }
-       if (!strcmp(var, "color.grep.match")) {
+       else if (!strcmp(var, "color.grep.context"))
+               color = opt->color_context;
+       else if (!strcmp(var, "color.grep.filename"))
+               color = opt->color_filename;
+       else if (!strcmp(var, "color.grep.function"))
+               color = opt->color_function;
+       else if (!strcmp(var, "color.grep.linenumber"))
+               color = opt->color_lineno;
+       else if (!strcmp(var, "color.grep.match"))
+               color = opt->color_match;
+       else if (!strcmp(var, "color.grep.selected"))
+               color = opt->color_selected;
+       else if (!strcmp(var, "color.grep.separator"))
+               color = opt->color_sep;
+       else
+               return git_color_default_config(var, value, cb);
+       if (color) {
                if (!value)
                        return config_error_nonbool(var);
-               color_parse(value, var, opt->color_match);
-               return 0;
+               color_parse(value, var, color);
        }
-       return git_color_default_config(var, value, cb);
+       return 0;
 }
 
 /*
@@ -408,15 +423,25 @@ static int pathspec_matches(const char **paths, const char *name, int max_depth)
        return 0;
 }
 
+static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
+{
+       void *data;
+
+       if (use_threads) {
+               read_sha1_lock();
+               data = read_sha1_file(sha1, type, size);
+               read_sha1_unlock();
+       } else {
+               data = read_sha1_file(sha1, type, size);
+       }
+       return data;
+}
+
 static void *load_sha1(const unsigned char *sha1, unsigned long *size,
                       const char *name)
 {
        enum object_type type;
-       char *data;
-
-       read_sha1_lock();
-       data = read_sha1_file(sha1, &type, size);
-       read_sha1_unlock();
+       void *data = lock_and_read_sha1_file(sha1, &type, size);
 
        if (!data)
                error("'%s': unable to read %s", name, sha1_to_hex(sha1));
@@ -605,10 +630,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
                        void *data;
                        unsigned long size;
 
-                       read_sha1_lock();
-                       data = read_sha1_file(entry.sha1, &type, &size);
-                       read_sha1_unlock();
-
+                       data = lock_and_read_sha1_file(entry.sha1, &type, &size);
                        if (!data)
                                die("unable to read tree (%s)",
                                    sha1_to_hex(entry.sha1));
@@ -645,6 +667,24 @@ 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_directory(struct grep_opt *opt, const char **paths)
+{
+       struct dir_struct dir;
+       int i, hit = 0;
+
+       memset(&dir, 0, sizeof(dir));
+       setup_standard_excludes(&dir);
+
+       fill_directory(&dir, paths);
+       for (i = 0; i < dir.nr; i++) {
+               hit |= grep_file(opt, dir.entries[i]->name);
+               if (hit && opt->status_only)
+                       break;
+       }
+       free_grep_patterns(opt);
+       return hit;
+}
+
 static int context_callback(const struct option *opt, const char *arg,
                            int unset)
 {
@@ -739,9 +779,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        const char **paths = NULL;
        int i;
        int dummy;
+       int nongit = 0, use_index = 1;
        struct option options[] = {
                OPT_BOOLEAN(0, "cached", &cached,
                        "search in index instead of in the work tree"),
+               OPT_BOOLEAN(0, "index", &use_index,
+                       "--no-index finds in contents not managed by git"),
                OPT_GROUP(""),
                OPT_BOOLEAN('v', "invert-match", &opt.invert,
                        "show non-matching lines"),
@@ -782,7 +825,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        "print NUL after filenames"),
                OPT_BOOLEAN('c', "count", &opt.count,
                        "show the number of matches instead of matching lines"),
-               OPT_SET_INT(0, "color", &opt.color, "highlight matches", 1),
+               OPT__COLOR(&opt.color, "highlight matches"),
                OPT_GROUP(""),
                OPT_CALLBACK('C', NULL, &opt, "n",
                        "show <n> context lines before and after matches",
@@ -824,6 +867,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       prefix = setup_git_directory_gently(&nongit);
+
        /*
         * 'git grep -h', unlike 'git grep -h <pattern>', is a request
         * to show usage information and exit.
@@ -837,10 +882,17 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        opt.relative = 1;
        opt.pathname = 1;
        opt.pattern_tail = &opt.pattern_list;
+       opt.header_tail = &opt.header_list;
        opt.regflags = REG_NEWLINE;
        opt.max_depth = -1;
 
-       strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
+       strcpy(opt.color_context, "");
+       strcpy(opt.color_filename, "");
+       strcpy(opt.color_function, "");
+       strcpy(opt.color_lineno, "");
+       strcpy(opt.color_match, GIT_COLOR_BOLD_RED);
+       strcpy(opt.color_selected, "");
+       strcpy(opt.color_sep, GIT_COLOR_CYAN);
        opt.color = -1;
        git_config(grep_config, &opt);
        if (opt.color == -1)
@@ -861,6 +913,20 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_STOP_AT_NON_OPTION |
                             PARSE_OPT_NO_INTERNAL_HELP);
 
+       if (use_index && nongit)
+               /* die the same way as if we did it at the beginning */
+               setup_git_directory();
+
+       /*
+        * skip a -- separator; we know it cannot be
+        * separating revisions from pathnames if
+        * we haven't even had any patterns yet
+        */
+       if (argc > 0 && !opt.pattern_list && !strcmp(argv[0], "--")) {
+               argv++;
+               argc--;
+       }
+
        /* First unrecognized non-option token */
        if (argc > 0 && !opt.pattern_list) {
                append_grep_pattern(&opt, argv[0], "command line", 0,
@@ -922,6 +988,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                paths[1] = NULL;
        }
 
+       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;
                if (!cached)
similarity index 96%
rename from builtin-hash-object.c
rename to builtin/hash-object.c
index 6a5f5b5f0eaf06b29a4e6afc3335618dc5ac5de8..080af1a01b8155680faf6c04101217b60ae7b919 100644 (file)
@@ -33,6 +33,8 @@ static void hash_object(const char *path, const char *type, int write_object,
        hash_fd(fd, type, write_object, vpath);
 }
 
+static int no_filters;
+
 static void hash_stdin_paths(const char *type, int write_objects)
 {
        struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
@@ -44,7 +46,8 @@ static void hash_stdin_paths(const char *type, int write_objects)
                                die("line is badly quoted");
                        strbuf_swap(&buf, &nbuf);
                }
-               hash_object(buf.buf, type, write_objects, buf.buf);
+               hash_object(buf.buf, type, write_objects,
+                   no_filters ? NULL : buf.buf);
        }
        strbuf_release(&buf);
        strbuf_release(&nbuf);
@@ -60,7 +63,6 @@ static const char *type;
 static int write_object;
 static int hashstdin;
 static int stdin_paths;
-static int no_filters;
 static const char *vpath;
 
 static const struct option hash_object_options[] = {
@@ -100,8 +102,6 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
                        errstr = "Can't specify files with --stdin-paths";
                else if (vpath)
                        errstr = "Can't use --stdin-paths with --path";
-               else if (no_filters)
-                       errstr = "Can't use --stdin-paths with --no-filters";
        }
        else {
                if (hashstdin > 1)
similarity index 100%
rename from builtin-help.c
rename to builtin/help.c
similarity index 100%
rename from builtin-index-pack.c
rename to builtin/index-pack.c
similarity index 95%
rename from builtin-init-db.c
rename to builtin/init-db.c
index dd84caecbc2a07bca90c8524157d50a8fd5ae316..edc40ff5748fbd68b64f382c251c6b030cf88803 100644 (file)
@@ -20,6 +20,7 @@
 
 static int init_is_bare_repository = 0;
 static int init_shared_repository = -1;
+static const char *init_db_template_dir;
 
 static void safe_create_dir(const char *dir, int share)
 {
@@ -120,6 +121,8 @@ static void copy_templates(const char *template_dir)
 
        if (!template_dir)
                template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
+       if (!template_dir)
+               template_dir = init_db_template_dir;
        if (!template_dir)
                template_dir = system_path(DEFAULT_GIT_TEMPLATE_DIR);
        if (!template_dir[0])
@@ -165,6 +168,14 @@ static void copy_templates(const char *template_dir)
        closedir(dir);
 }
 
+static int git_init_db_config(const char *k, const char *v, void *cb)
+{
+       if (!strcmp(k, "init.templatedir"))
+               return git_config_pathname(&init_db_template_dir, k, v);
+
+       return 0;
+}
+
 static int create_default_files(const char *template_path)
 {
        const char *git_dir = get_git_dir();
@@ -190,6 +201,9 @@ static int create_default_files(const char *template_path)
        safe_create_dir(git_path("refs/heads"), 1);
        safe_create_dir(git_path("refs/tags"), 1);
 
+       /* Just look for `init.templatedir` */
+       git_config(git_init_db_config, NULL);
+
        /* First copy the templates -- we might have the default
         * config file there, in which case we would want to read
         * from it after installing.
@@ -331,11 +345,14 @@ int init_db(const char *template_dir, unsigned int flags)
                git_config_set("receive.denyNonFastforwards", "true");
        }
 
-       if (!(flags & INIT_DB_QUIET))
-               printf("%s%s Git repository in %s/\n",
+       if (!(flags & INIT_DB_QUIET)) {
+               const char *git_dir = get_git_dir();
+               int len = strlen(git_dir);
+               printf("%s%s Git repository in %s%s\n",
                       reinit ? "Reinitialized existing" : "Initialized empty",
                       shared_repository ? " shared" : "",
-                      get_git_dir());
+                      git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+       }
 
        return 0;
 }
similarity index 91%
rename from builtin-log.c
rename to builtin/log.c
index 8d16832f7e9483f7903009459a72efc39e267c98..a8dd8c989c5a135e14c6fc06ea3b23f4f0cd0bec 100644 (file)
@@ -32,7 +32,7 @@ static const char * const builtin_log_usage =
        "   or: git show [options] <object>...";
 
 static void cmd_log_init(int argc, const char **argv, const char *prefix,
-                     struct rev_info *rev)
+                        struct rev_info *rev, struct setup_revision_opt *opt)
 {
        int i;
        int decoration_style = 0;
@@ -56,10 +56,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
         */
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(builtin_log_usage);
-       argc = setup_revisions(argc, argv, rev, "HEAD");
+       argc = setup_revisions(argc, argv, rev, opt);
 
        if (!rev->show_notes_given && !rev->pretty_given)
                rev->show_notes = 1;
+       if (rev->show_notes)
+               init_display_notes(&rev->notes_opt);
 
        if (rev->diffopt.pickaxe || rev->diffopt.filter)
                rev->always_show_header = 0;
@@ -262,6 +264,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
 int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
+       struct setup_revision_opt opt;
 
        git_config(git_log_config, NULL);
 
@@ -271,7 +274,9 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
        init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.simplify_history = 0;
-       cmd_log_init(argc, argv, prefix, &rev);
+       memset(&opt, 0, sizeof(opt));
+       opt.def = "HEAD";
+       cmd_log_init(argc, argv, prefix, &rev, &opt);
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_RAW;
        return cmd_log_walk(&rev);
@@ -324,10 +329,26 @@ static int show_tree_object(const unsigned char *sha1,
        return 0;
 }
 
+static void show_rev_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
+{
+       if (rev->ignore_merges) {
+               /* There was no "-m" on the command line */
+               rev->ignore_merges = 0;
+               if (!rev->first_parent_only && !rev->combine_merges) {
+                       /* No "--first-parent", "-c", nor "--cc" */
+                       rev->combine_merges = 1;
+                       rev->dense_combined_merges = 1;
+               }
+       }
+       if (!rev->diffopt.output_format)
+               rev->diffopt.output_format = DIFF_FORMAT_PATCH;
+}
+
 int cmd_show(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
        struct object_array_entry *objects;
+       struct setup_revision_opt opt;
        int i, count, ret = 0;
 
        git_config(git_log_config, NULL);
@@ -337,12 +358,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 
        init_revisions(&rev, prefix);
        rev.diff = 1;
-       rev.combine_merges = 1;
-       rev.dense_combined_merges = 1;
        rev.always_show_header = 1;
-       rev.ignore_merges = 0;
        rev.no_walk = 1;
-       cmd_log_init(argc, argv, prefix, &rev);
+       memset(&opt, 0, sizeof(opt));
+       opt.def = "HEAD";
+       opt.tweak = show_rev_tweak_rev;
+       cmd_log_init(argc, argv, prefix, &rev, &opt);
 
        count = rev.pending.nr;
        objects = rev.pending.objects;
@@ -405,6 +426,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
+       struct setup_revision_opt opt;
 
        git_config(git_log_config, NULL);
 
@@ -415,7 +437,9 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
        init_reflog_walk(&rev.reflog_info);
        rev.abbrev_commit = 1;
        rev.verbose_header = 1;
-       cmd_log_init(argc, argv, prefix, &rev);
+       memset(&opt, 0, sizeof(opt));
+       opt.def = "HEAD";
+       cmd_log_init(argc, argv, prefix, &rev, &opt);
 
        /*
         * This means that we override whatever commit format the user gave
@@ -438,6 +462,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 int cmd_log(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
+       struct setup_revision_opt opt;
 
        git_config(git_log_config, NULL);
 
@@ -446,7 +471,9 @@ int cmd_log(int argc, const char **argv, const char *prefix)
 
        init_revisions(&rev, prefix);
        rev.always_show_header = 1;
-       cmd_log_init(argc, argv, prefix, &rev);
+       memset(&opt, 0, sizeof(opt));
+       opt.def = "HEAD";
+       cmd_log_init(argc, argv, prefix, &rev, &opt);
        return cmd_log_walk(&rev);
 }
 
@@ -458,35 +485,28 @@ static int auto_number = 1;
 
 static char *default_attach = NULL;
 
-static char **extra_hdr;
-static int extra_hdr_nr;
-static int extra_hdr_alloc;
-
-static char **extra_to;
-static int extra_to_nr;
-static int extra_to_alloc;
-
-static char **extra_cc;
-static int extra_cc_nr;
-static int extra_cc_alloc;
+static struct string_list extra_hdr;
+static struct string_list extra_to;
+static struct string_list extra_cc;
 
 static void add_header(const char *value)
 {
+       struct string_list_item *item;
        int len = strlen(value);
        while (len && value[len - 1] == '\n')
                len--;
+
        if (!strncasecmp(value, "to: ", 4)) {
-               ALLOC_GROW(extra_to, extra_to_nr + 1, extra_to_alloc);
-               extra_to[extra_to_nr++] = xstrndup(value + 4, len - 4);
-               return;
-       }
-       if (!strncasecmp(value, "cc: ", 4)) {
-               ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
-               extra_cc[extra_cc_nr++] = xstrndup(value + 4, len - 4);
-               return;
+               item = string_list_append(value + 4, &extra_to);
+               len -= 4;
+       } else if (!strncasecmp(value, "cc: ", 4)) {
+               item = string_list_append(value + 4, &extra_cc);
+               len -= 4;
+       } else {
+               item = string_list_append(value, &extra_hdr);
        }
-       ALLOC_GROW(extra_hdr, extra_hdr_nr + 1, extra_hdr_alloc);
-       extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
+
+       item->string[len] = '\0';
 }
 
 #define THREAD_SHALLOW 1
@@ -504,11 +524,16 @@ static int git_format_config(const char *var, const char *value, void *cb)
        }
        if (!strcmp(var, "format.suffix"))
                return git_config_string(&fmt_patch_suffix, var, value);
+       if (!strcmp(var, "format.to")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               string_list_append(value, &extra_to);
+               return 0;
+       }
        if (!strcmp(var, "format.cc")) {
                if (!value)
                        return config_error_nonbool(var);
-               ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
-               extra_cc[extra_cc_nr++] = xstrdup(value);
+               string_list_append(value, &extra_cc);
                return 0;
        }
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
@@ -871,14 +896,31 @@ static int inline_callback(const struct option *opt, const char *arg, int unset)
 
 static int header_callback(const struct option *opt, const char *arg, int unset)
 {
-       add_header(arg);
+       if (unset) {
+               string_list_clear(&extra_hdr, 0);
+               string_list_clear(&extra_to, 0);
+               string_list_clear(&extra_cc, 0);
+       } else {
+           add_header(arg);
+       }
+       return 0;
+}
+
+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);
        return 0;
 }
 
 static int cc_callback(const struct option *opt, const char *arg, int unset)
 {
-       ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
-       extra_cc[extra_cc_nr++] = xstrdup(arg);
+       if (unset)
+               string_list_clear(&extra_cc, 0);
+       else
+               string_list_append(arg, &extra_cc);
        return 0;
 }
 
@@ -887,6 +929,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        struct commit *commit;
        struct commit **list = NULL;
        struct rev_info rev;
+       struct setup_revision_opt s_r_opt;
        int nr = 0, total, i;
        int use_stdout = 0;
        int start_number = -1;
@@ -937,10 +980,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                  PARSE_OPT_NONEG | PARSE_OPT_NOARG },
                OPT_GROUP("Messaging"),
                { OPTION_CALLBACK, 0, "add-header", NULL, "header",
-                           "add email header", PARSE_OPT_NONEG,
-                           header_callback },
+                           "add email header", 0, header_callback },
+               { OPTION_CALLBACK, 0, "to", NULL, "email", "add To: header",
+                           0, to_callback },
                { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header",
-                           PARSE_OPT_NONEG, cc_callback },
+                           0, cc_callback },
                OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id",
                            "make first mail a reply to <message-id>"),
                { OPTION_CALLBACK, 0, "attach", &rev, "boundary",
@@ -956,6 +1000,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       extra_hdr.strdup_strings = 1;
+       extra_to.strdup_strings = 1;
+       extra_cc.strdup_strings = 1;
        git_config(git_format_config, NULL);
        init_revisions(&rev, prefix);
        rev.commit_format = CMIT_FMT_EMAIL;
@@ -964,8 +1011,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        rev.combine_merges = 0;
        rev.ignore_merges = 1;
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
-
        rev.subject_prefix = fmt_patch_subject_prefix;
+       memset(&s_r_opt, 0, sizeof(s_r_opt));
+       s_r_opt.def = "HEAD";
 
        if (default_attach) {
                rev.mime_boundary = default_attach;
@@ -992,29 +1040,29 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                add_signoff = xmemdupz(committer, endpos - committer + 1);
        }
 
-       for (i = 0; i < extra_hdr_nr; i++) {
-               strbuf_addstr(&buf, extra_hdr[i]);
+       for (i = 0; i < extra_hdr.nr; i++) {
+               strbuf_addstr(&buf, extra_hdr.items[i].string);
                strbuf_addch(&buf, '\n');
        }
 
-       if (extra_to_nr)
+       if (extra_to.nr)
                strbuf_addstr(&buf, "To: ");
-       for (i = 0; i < extra_to_nr; i++) {
+       for (i = 0; i < extra_to.nr; i++) {
                if (i)
                        strbuf_addstr(&buf, "    ");
-               strbuf_addstr(&buf, extra_to[i]);
-               if (i + 1 < extra_to_nr)
+               strbuf_addstr(&buf, extra_to.items[i].string);
+               if (i + 1 < extra_to.nr)
                        strbuf_addch(&buf, ',');
                strbuf_addch(&buf, '\n');
        }
 
-       if (extra_cc_nr)
+       if (extra_cc.nr)
                strbuf_addstr(&buf, "Cc: ");
-       for (i = 0; i < extra_cc_nr; i++) {
+       for (i = 0; i < extra_cc.nr; i++) {
                if (i)
                        strbuf_addstr(&buf, "    ");
-               strbuf_addstr(&buf, extra_cc[i]);
-               if (i + 1 < extra_cc_nr)
+               strbuf_addstr(&buf, extra_cc.items[i].string);
+               if (i + 1 < extra_cc.nr)
                        strbuf_addch(&buf, ',');
                strbuf_addch(&buf, '\n');
        }
@@ -1037,7 +1085,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (keep_subject && subject_prefix)
                die ("--subject-prefix and -k are mutually exclusive.");
 
-       argc = setup_revisions(argc, argv, &rev, "HEAD");
+       argc = setup_revisions(argc, argv, &rev, &s_r_opt);
        if (argc > 1)
                die ("unrecognized argument: %s", argv[1]);
 
@@ -1059,6 +1107,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
                DIFF_OPT_SET(&rev.diffopt, BINARY);
 
+       if (rev.show_notes)
+               init_display_notes(&rev.notes_opt);
+
        if (!use_stdout)
                output_directory = set_outdir(prefix, output_directory);
 
@@ -1089,7 +1140,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
        /*
         * We cannot move this anywhere earlier because we do want to
-        * know if --root was given explicitly from the comand line.
+        * know if --root was given explicitly from the command line.
         */
        rev.show_root_diff = 1;
 
@@ -1223,6 +1274,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        fclose(stdout);
        }
        free(list);
+       string_list_clear(&extra_to, 0);
+       string_list_clear(&extra_cc, 0);
+       string_list_clear(&extra_hdr, 0);
        if (ignore_if_in_upstream)
                free_patch_ids(&ids);
        return 0;
similarity index 100%
rename from builtin-ls-files.c
rename to builtin/ls-files.c
similarity index 100%
rename from builtin-ls-remote.c
rename to builtin/ls-remote.c
similarity index 100%
rename from builtin-ls-tree.c
rename to builtin/ls-tree.c
similarity index 99%
rename from builtin-mailinfo.c
rename to builtin/mailinfo.c
index a50ac2256cdbacd76ed44a50804212be07f949db..ce2ef6bede40fde8823336bc85762bd3e7cd4760 100644 (file)
@@ -779,8 +779,7 @@ static int handle_commit_msg(struct strbuf *line)
                return 0;
 
        if (still_looking) {
-               strbuf_ltrim(line);
-               if (!line->len)
+               if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
                        return 0;
        }
 
similarity index 98%
rename from builtin-mailsplit.c
rename to builtin/mailsplit.c
index 207e358ed19cecb8cf7b57d59a9149619909459d..cdfc1b70429dc5d47e42e7aabc9449a585623b0f 100644 (file)
@@ -10,7 +10,7 @@
 #include "strbuf.h"
 
 static const char git_mailsplit_usage[] =
-"git mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]";
+"git mailsplit [-d<prec>] [-f<n>] [-b] [--keep-cr] -o<directory> [<mbox>|<Maildir>...]";
 
 static int is_from_line(const char *line, int len)
 {
similarity index 100%
rename from builtin-merge-base.c
rename to builtin/merge-base.c
similarity index 80%
rename from builtin-merge-file.c
rename to builtin/merge-file.c
index 1e70073a7ed022675031706a8e9f8c57ff3aa2a9..69cc683332a04ddc6959fcef3d2853ccfb2860b8 100644 (file)
@@ -27,30 +27,35 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        mmbuffer_t result = {NULL, 0};
        xmparam_t xmp = {{XDF_NEED_MINIMAL}};
        int ret = 0, i = 0, to_stdout = 0;
-       int level = XDL_MERGE_ZEALOUS_ALNUM;
-       int style = 0, quiet = 0;
-       int favor = 0;
+       int quiet = 0;
        int nongit;
-
        struct option options[] = {
                OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
-               OPT_SET_INT(0, "diff3", &style, "use a diff3 based merge", XDL_MERGE_DIFF3),
-               OPT_SET_INT(0, "ours", &favor, "for conflicts, use our version",
+               OPT_SET_INT(0, "diff3", &xmp.style, "use a diff3 based merge", XDL_MERGE_DIFF3),
+               OPT_SET_INT(0, "ours", &xmp.favor, "for conflicts, use our version",
                            XDL_MERGE_FAVOR_OURS),
-               OPT_SET_INT(0, "theirs", &favor, "for conflicts, use their version",
+               OPT_SET_INT(0, "theirs", &xmp.favor, "for conflicts, use their version",
                            XDL_MERGE_FAVOR_THEIRS),
+               OPT_SET_INT(0, "union", &xmp.favor, "for conflicts, use a union version",
+                           XDL_MERGE_FAVOR_UNION),
+               OPT_INTEGER(0, "marker-size", &xmp.marker_size,
+                           "for conflicts, use this marker size"),
                OPT__QUIET(&quiet),
                OPT_CALLBACK('L', NULL, names, "name",
                             "set labels for file1/orig_file/file2", &label_cb),
                OPT_END(),
        };
 
+       xmp.level = XDL_MERGE_ZEALOUS_ALNUM;
+       xmp.style = 0;
+       xmp.favor = 0;
+
        prefix = setup_git_directory_gently(&nongit);
        if (!nongit) {
                /* Read the configuration file */
                git_config(git_xmerge_config, NULL);
                if (0 <= git_xmerge_style)
-                       style = git_xmerge_style;
+                       xmp.style = git_xmerge_style;
        }
 
        argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
@@ -73,7 +78,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        }
 
        ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
-                       &xmp, XDL_MERGE_FLAGS(level, style, favor), &result);
+                       &xmp, &result);
 
        for (i = 0; i < 3; i++)
                free(mmfs[i].ptr);
similarity index 100%
rename from builtin-merge-index.c
rename to builtin/merge-index.c
similarity index 100%
rename from builtin-merge-ours.c
rename to builtin/merge-ours.c
similarity index 100%
rename from builtin-merge-tree.c
rename to builtin/merge-tree.c
similarity index 100%
rename from builtin-merge.c
rename to builtin/merge.c
similarity index 100%
rename from builtin-mktag.c
rename to builtin/mktag.c
similarity index 100%
rename from builtin-mktree.c
rename to builtin/mktree.c
similarity index 100%
rename from builtin-mv.c
rename to builtin/mv.c
similarity index 100%
rename from builtin-name-rev.c
rename to builtin/name-rev.c
diff --git a/builtin/notes.c b/builtin/notes.c
new file mode 100644 (file)
index 0000000..4543d11
--- /dev/null
@@ -0,0 +1,650 @@
+/*
+ * Builtin "git notes"
+ *
+ * Copyright (c) 2010 Johan Herland <johan@herland.net>
+ *
+ * Based on git-notes.sh by Johannes Schindelin,
+ * and builtin-tag.c by Kristian Høgsberg and Carlos Rica.
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "notes.h"
+#include "blob.h"
+#include "commit.h"
+#include "refs.h"
+#include "exec_cmd.h"
+#include "run-command.h"
+#include "parse-options.h"
+#include "string-list.h"
+
+static const char * const git_notes_usage[] = {
+       "git notes [list [<object>]]",
+       "git notes add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
+       "git notes copy [-f] <from-object> <to-object>",
+       "git notes append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
+       "git notes edit [<object>]",
+       "git notes show [<object>]",
+       "git notes remove [<object>]",
+       "git notes prune",
+       NULL
+};
+
+static const char note_template[] =
+       "\n"
+       "#\n"
+       "# Write/edit the notes for the following object:\n"
+       "#\n";
+
+struct msg_arg {
+       int given;
+       int use_editor;
+       struct strbuf buf;
+};
+
+static int list_each_note(const unsigned char *object_sha1,
+               const unsigned char *note_sha1, char *note_path,
+               void *cb_data)
+{
+       printf("%s %s\n", sha1_to_hex(note_sha1), sha1_to_hex(object_sha1));
+       return 0;
+}
+
+static void write_note_data(int fd, const unsigned char *sha1)
+{
+       unsigned long size;
+       enum object_type type;
+       char *buf = read_sha1_file(sha1, &type, &size);
+       if (buf) {
+               if (size)
+                       write_or_die(fd, buf, size);
+               free(buf);
+       }
+}
+
+static void write_commented_object(int fd, const unsigned char *object)
+{
+       const char *show_args[5] =
+               {"show", "--stat", "--no-notes", sha1_to_hex(object), NULL};
+       struct child_process show;
+       struct strbuf buf = STRBUF_INIT;
+       FILE *show_out;
+
+       /* Invoke "git show --stat --no-notes $object" */
+       memset(&show, 0, sizeof(show));
+       show.argv = show_args;
+       show.no_stdin = 1;
+       show.out = -1;
+       show.err = 0;
+       show.git_cmd = 1;
+       if (start_command(&show))
+               die("unable to start 'show' for object '%s'",
+                   sha1_to_hex(object));
+
+       /* Open the output as FILE* so strbuf_getline() can be used. */
+       show_out = xfdopen(show.out, "r");
+       if (show_out == NULL)
+               die_errno("can't fdopen 'show' output fd");
+
+       /* Prepend "# " to each output line and write result to 'fd' */
+       while (strbuf_getline(&buf, show_out, '\n') != EOF) {
+               write_or_die(fd, "# ", 2);
+               write_or_die(fd, buf.buf, buf.len);
+               write_or_die(fd, "\n", 1);
+       }
+       strbuf_release(&buf);
+       if (fclose(show_out))
+               die_errno("failed to close pipe to 'show' for object '%s'",
+                         sha1_to_hex(object));
+       if (finish_command(&show))
+               die("failed to finish 'show' for object '%s'",
+                   sha1_to_hex(object));
+}
+
+static void create_note(const unsigned char *object, struct msg_arg *msg,
+                       int append_only, const unsigned char *prev,
+                       unsigned char *result)
+{
+       char *path = NULL;
+
+       if (msg->use_editor || !msg->given) {
+               int fd;
+
+               /* write the template message before editing: */
+               path = git_pathdup("NOTES_EDITMSG");
+               fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+               if (fd < 0)
+                       die_errno("could not create file '%s'", path);
+
+               if (msg->given)
+                       write_or_die(fd, msg->buf.buf, msg->buf.len);
+               else if (prev && !append_only)
+                       write_note_data(fd, prev);
+               write_or_die(fd, note_template, strlen(note_template));
+
+               write_commented_object(fd, object);
+
+               close(fd);
+               strbuf_reset(&(msg->buf));
+
+               if (launch_editor(path, &(msg->buf), NULL)) {
+                       die("Please supply the note contents using either -m" \
+                           " or -F option");
+               }
+               stripspace(&(msg->buf), 1);
+       }
+
+       if (prev && append_only) {
+               /* Append buf to previous note contents */
+               unsigned long size;
+               enum object_type type;
+               char *prev_buf = read_sha1_file(prev, &type, &size);
+
+               strbuf_grow(&(msg->buf), size + 1);
+               if (msg->buf.len && prev_buf && size)
+                       strbuf_insert(&(msg->buf), 0, "\n", 1);
+               if (prev_buf && size)
+                       strbuf_insert(&(msg->buf), 0, prev_buf, size);
+               free(prev_buf);
+       }
+
+       if (!msg->buf.len) {
+               fprintf(stderr, "Removing note for object %s\n",
+                       sha1_to_hex(object));
+               hashclr(result);
+       } else {
+               if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) {
+                       error("unable to write note object");
+                       if (path)
+                               error("The note contents has been left in %s",
+                                     path);
+                       exit(128);
+               }
+       }
+
+       if (path) {
+               unlink_or_warn(path);
+               free(path);
+       }
+}
+
+static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
+{
+       struct msg_arg *msg = opt->value;
+
+       strbuf_grow(&(msg->buf), strlen(arg) + 2);
+       if (msg->buf.len)
+               strbuf_addch(&(msg->buf), '\n');
+       strbuf_addstr(&(msg->buf), arg);
+       stripspace(&(msg->buf), 0);
+
+       msg->given = 1;
+       return 0;
+}
+
+static int parse_file_arg(const struct option *opt, const char *arg, int unset)
+{
+       struct msg_arg *msg = opt->value;
+
+       if (msg->buf.len)
+               strbuf_addch(&(msg->buf), '\n');
+       if (!strcmp(arg, "-")) {
+               if (strbuf_read(&(msg->buf), 0, 1024) < 0)
+                       die_errno("cannot read '%s'", arg);
+       } else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0)
+               die_errno("could not open or read '%s'", arg);
+       stripspace(&(msg->buf), 0);
+
+       msg->given = 1;
+       return 0;
+}
+
+static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
+{
+       struct msg_arg *msg = opt->value;
+       char *buf;
+       unsigned char object[20];
+       enum object_type type;
+       unsigned long len;
+
+       if (msg->buf.len)
+               strbuf_addch(&(msg->buf), '\n');
+
+       if (get_sha1(arg, object))
+               die("Failed to resolve '%s' as a valid ref.", arg);
+       if (!(buf = read_sha1_file(object, &type, &len)) || !len) {
+               free(buf);
+               die("Failed to read object '%s'.", arg);;
+       }
+       strbuf_add(&(msg->buf), buf, len);
+       free(buf);
+
+       msg->given = 1;
+       return 0;
+}
+
+static int parse_reedit_arg(const struct option *opt, const char *arg, int unset)
+{
+       struct msg_arg *msg = opt->value;
+       msg->use_editor = 1;
+       return parse_reuse_arg(opt, arg, unset);
+}
+
+int commit_notes(struct notes_tree *t, const char *msg)
+{
+       struct commit_list *parent;
+       unsigned char tree_sha1[20], prev_commit[20], new_commit[20];
+       struct strbuf buf = STRBUF_INIT;
+
+       if (!t)
+               t = &default_notes_tree;
+       if (!t->initialized || !t->ref || !*t->ref)
+               die("Cannot commit uninitialized/unreferenced notes tree");
+       if (!t->dirty)
+               return 0; /* don't have to commit an unchanged tree */
+
+       /* Prepare commit message and reflog message */
+       strbuf_addstr(&buf, "notes: "); /* commit message starts at index 7 */
+       strbuf_addstr(&buf, msg);
+       if (buf.buf[buf.len - 1] != '\n')
+               strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
+
+       /* Convert notes tree to tree object */
+       if (write_notes_tree(t, tree_sha1))
+               die("Failed to write current notes tree to database");
+
+       /* Create new commit for the tree object */
+       if (!read_ref(t->ref, prev_commit)) { /* retrieve parent commit */
+               parent = xmalloc(sizeof(*parent));
+               parent->item = lookup_commit(prev_commit);
+               parent->next = NULL;
+       } else {
+               hashclr(prev_commit);
+               parent = NULL;
+       }
+       if (commit_tree(buf.buf + 7, tree_sha1, parent, new_commit, NULL))
+               die("Failed to commit notes tree to database");
+
+       /* Update notes ref with new commit */
+       update_ref(buf.buf, t->ref, new_commit, prev_commit, 0, DIE_ON_ERR);
+
+       strbuf_release(&buf);
+       return 0;
+}
+
+
+combine_notes_fn *parse_combine_notes_fn(const char *v)
+{
+       if (!strcasecmp(v, "overwrite"))
+               return combine_notes_overwrite;
+       else if (!strcasecmp(v, "ignore"))
+               return combine_notes_ignore;
+       else if (!strcasecmp(v, "concatenate"))
+               return combine_notes_concatenate;
+       else
+               return NULL;
+}
+
+static int notes_rewrite_config(const char *k, const char *v, void *cb)
+{
+       struct notes_rewrite_cfg *c = cb;
+       if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
+               c->enabled = git_config_bool(k, v);
+               return 0;
+       } else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
+               if (!v)
+                       config_error_nonbool(k);
+               c->combine = parse_combine_notes_fn(v);
+               if (!c->combine) {
+                       error("Bad notes.rewriteMode value: '%s'", v);
+                       return 1;
+               }
+               return 0;
+       } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
+               /* note that a refs/ prefix is implied in the
+                * underlying for_each_glob_ref */
+               if (!prefixcmp(v, "refs/notes/"))
+                       string_list_add_refs_by_glob(c->refs, v);
+               else
+                       warning("Refusing to rewrite notes in %s"
+                               " (outside of refs/notes/)", v);
+               return 0;
+       }
+
+       return 0;
+}
+
+
+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
+{
+       struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));
+       const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);
+       const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);
+       c->cmd = cmd;
+       c->enabled = 1;
+       c->combine = combine_notes_concatenate;
+       c->refs = xcalloc(1, sizeof(struct string_list));
+       c->refs->strdup_strings = 1;
+       c->refs_from_env = 0;
+       c->mode_from_env = 0;
+       if (rewrite_mode_env) {
+               c->mode_from_env = 1;
+               c->combine = parse_combine_notes_fn(rewrite_mode_env);
+               if (!c->combine)
+                       error("Bad " GIT_NOTES_REWRITE_MODE_ENVIRONMENT
+                             " value: '%s'", rewrite_mode_env);
+       }
+       if (rewrite_refs_env) {
+               c->refs_from_env = 1;
+               string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);
+       }
+       git_config(notes_rewrite_config, c);
+       if (!c->enabled || !c->refs->nr) {
+               string_list_clear(c->refs, 0);
+               free(c->refs);
+               free(c);
+               return NULL;
+       }
+       c->trees = load_notes_trees(c->refs);
+       string_list_clear(c->refs, 0);
+       free(c->refs);
+       return c;
+}
+
+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
+                         const unsigned char *from_obj, const unsigned char *to_obj)
+{
+       int ret = 0;
+       int i;
+       for (i = 0; c->trees[i]; i++)
+               ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;
+       return ret;
+}
+
+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)
+{
+       int i;
+       for (i = 0; c->trees[i]; i++) {
+               commit_notes(c->trees[i], "Notes added by 'git notes copy'");
+               free_notes(c->trees[i]);
+       }
+       free(c->trees);
+       free(c);
+}
+
+int notes_copy_from_stdin(int force, const char *rewrite_cmd)
+{
+       struct strbuf buf = STRBUF_INIT;
+       struct notes_rewrite_cfg *c = NULL;
+       struct notes_tree *t;
+       int ret = 0;
+
+       if (rewrite_cmd) {
+               c = init_copy_notes_for_rewrite(rewrite_cmd);
+               if (!c)
+                       return 0;
+       } else {
+               init_notes(NULL, NULL, NULL, 0);
+               t = &default_notes_tree;
+       }
+
+       while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+               unsigned char from_obj[20], to_obj[20];
+               struct strbuf **split;
+               int err;
+
+               split = strbuf_split(&buf, ' ');
+               if (!split[0] || !split[1])
+                       die("Malformed input line: '%s'.", buf.buf);
+               strbuf_rtrim(split[0]);
+               strbuf_rtrim(split[1]);
+               if (get_sha1(split[0]->buf, from_obj))
+                       die("Failed to resolve '%s' as a valid ref.", split[0]->buf);
+               if (get_sha1(split[1]->buf, to_obj))
+                       die("Failed to resolve '%s' as a valid ref.", split[1]->buf);
+
+               if (rewrite_cmd)
+                       err = copy_note_for_rewrite(c, from_obj, to_obj);
+               else
+                       err = copy_note(t, from_obj, to_obj, force,
+                                       combine_notes_overwrite);
+
+               if (err) {
+                       error("Failed to copy notes from '%s' to '%s'",
+                             split[0]->buf, split[1]->buf);
+                       ret = 1;
+               }
+
+               strbuf_list_free(split);
+       }
+
+       if (!rewrite_cmd) {
+               commit_notes(t, "Notes added by 'git notes copy'");
+               free_notes(t);
+       } else {
+               finish_copy_notes_for_rewrite(c);
+       }
+       return ret;
+}
+
+int cmd_notes(int argc, const char **argv, const char *prefix)
+{
+       struct notes_tree *t;
+       unsigned char object[20], from_obj[20], new_note[20];
+       const unsigned char *note;
+       const char *object_ref;
+       char logmsg[100];
+
+       int list = 0, add = 0, copy = 0, append = 0, edit = 0, show = 0,
+           remove = 0, prune = 0, force = 0, from_stdin = 0;
+       int given_object = 0, i = 1, retval = 0;
+       struct msg_arg msg = { 0, 0, STRBUF_INIT };
+       const char *rewrite_cmd = NULL;
+       const char *override_notes_ref = NULL;
+       struct option options[] = {
+               OPT_GROUP("Notes contents options"),
+               { OPTION_CALLBACK, 'm', "message", &msg, "MSG",
+                       "note contents as a string", PARSE_OPT_NONEG,
+                       parse_msg_arg},
+               { OPTION_CALLBACK, 'F', "file", &msg, "FILE",
+                       "note contents in a file", PARSE_OPT_NONEG,
+                       parse_file_arg},
+               { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT",
+                       "reuse and edit specified note object", PARSE_OPT_NONEG,
+                       parse_reedit_arg},
+               { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT",
+                       "reuse specified note object", PARSE_OPT_NONEG,
+                       parse_reuse_arg},
+               OPT_GROUP("Other options"),
+               OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
+               OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
+               OPT_STRING(0, "ref", &override_notes_ref, "notes_ref",
+                          "use notes from <notes_ref>"),
+               OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command",
+                          "load rewriting config for <command> (implies --stdin)"),
+               OPT_END()
+       };
+
+       git_config(git_default_config, NULL);
+
+       argc = parse_options(argc, argv, prefix, options, git_notes_usage, 0);
+
+       if (override_notes_ref) {
+               struct strbuf sb = STRBUF_INIT;
+               if (!prefixcmp(override_notes_ref, "refs/notes/"))
+                       /* we're happy */;
+               else if (!prefixcmp(override_notes_ref, "notes/"))
+                       strbuf_addstr(&sb, "refs/");
+               else
+                       strbuf_addstr(&sb, "refs/notes/");
+               strbuf_addstr(&sb, override_notes_ref);
+               setenv("GIT_NOTES_REF", sb.buf, 1);
+               strbuf_release(&sb);
+       }
+
+       if (argc && !strcmp(argv[0], "list"))
+               list = 1;
+       else if (argc && !strcmp(argv[0], "add"))
+               add = 1;
+       else if (argc && !strcmp(argv[0], "copy"))
+               copy = 1;
+       else if (argc && !strcmp(argv[0], "append"))
+               append = 1;
+       else if (argc && !strcmp(argv[0], "edit"))
+               edit = 1;
+       else if (argc && !strcmp(argv[0], "show"))
+               show = 1;
+       else if (argc && !strcmp(argv[0], "remove"))
+               remove = 1;
+       else if (argc && !strcmp(argv[0], "prune"))
+               prune = 1;
+       else if (!argc) {
+               list = 1; /* Default to 'list' if no other subcommand given */
+               i = 0;
+       }
+
+       if (list + add + copy + append + edit + show + remove + prune != 1)
+               usage_with_options(git_notes_usage, options);
+
+       if (msg.given && !(add || append || edit)) {
+               error("cannot use -m/-F/-c/-C options with %s subcommand.",
+                     argv[0]);
+               usage_with_options(git_notes_usage, options);
+       }
+
+       if (msg.given && edit) {
+               fprintf(stderr, "The -m/-F/-c/-C options have been deprecated "
+                       "for the 'edit' subcommand.\n"
+                       "Please use 'git notes add -f -m/-F/-c/-C' instead.\n");
+       }
+
+       if (force && !(add || copy)) {
+               error("cannot use -f option with %s subcommand.", argv[0]);
+               usage_with_options(git_notes_usage, options);
+       }
+
+       if (!copy && rewrite_cmd) {
+               error("cannot use --for-rewrite with %s subcommand.", argv[0]);
+               usage_with_options(git_notes_usage, options);
+       }
+       if (!copy && from_stdin) {
+               error("cannot use --stdin with %s subcommand.", argv[0]);
+               usage_with_options(git_notes_usage, options);
+       }
+
+       if (copy) {
+               const char *from_ref;
+               if (from_stdin || rewrite_cmd) {
+                       if (argc > 1) {
+                               error("too many parameters");
+                               usage_with_options(git_notes_usage, options);
+                       } else {
+                               return notes_copy_from_stdin(force, rewrite_cmd);
+                       }
+               }
+               if (argc < 3) {
+                       error("too few parameters");
+                       usage_with_options(git_notes_usage, options);
+               }
+               from_ref = argv[i++];
+               if (get_sha1(from_ref, from_obj))
+                       die("Failed to resolve '%s' as a valid ref.", from_ref);
+       }
+
+       given_object = argc > i;
+       object_ref = given_object ? argv[i++] : "HEAD";
+
+       if (argc > i || (prune && given_object)) {
+               error("too many parameters");
+               usage_with_options(git_notes_usage, options);
+       }
+
+       if (get_sha1(object_ref, object))
+               die("Failed to resolve '%s' as a valid ref.", object_ref);
+
+       init_notes(NULL, NULL, NULL, 0);
+       t = &default_notes_tree;
+
+       if (prefixcmp(t->ref, "refs/notes/"))
+               die("Refusing to %s notes in %s (outside of refs/notes/)",
+                   argv[0], t->ref);
+
+       note = get_note(t, object);
+
+       /* list command */
+
+       if (list) {
+               if (given_object) {
+                       if (note) {
+                               puts(sha1_to_hex(note));
+                               goto end;
+                       }
+               } else {
+                       retval = for_each_note(t, 0, list_each_note, NULL);
+                       goto end;
+               }
+       }
+
+       /* show command */
+
+       if ((list || show) && !note) {
+               error("No note found for object %s.", sha1_to_hex(object));
+               retval = 1;
+               goto end;
+       } else if (show) {
+               const char *show_args[3] = {"show", sha1_to_hex(note), NULL};
+               retval = execv_git_cmd(show_args);
+               goto end;
+       }
+
+       /* add/append/edit/remove/prune command */
+
+       if ((add || copy) && note) {
+               if (!force) {
+                       error("Cannot %s notes. Found existing notes for object"
+                             " %s. Use '-f' to overwrite existing notes",
+                             argv[0], sha1_to_hex(object));
+                       retval = 1;
+                       goto end;
+               }
+               fprintf(stderr, "Overwriting existing notes for object %s\n",
+                       sha1_to_hex(object));
+       }
+
+       if (remove) {
+               msg.given = 1;
+               msg.use_editor = 0;
+               strbuf_reset(&(msg.buf));
+       }
+
+       if (prune) {
+               hashclr(new_note);
+               prune_notes(t);
+               goto commit;
+       } else if (copy) {
+               const unsigned char *from_note = get_note(t, from_obj);
+               if (!from_note) {
+                       error("Missing notes on source object %s. Cannot copy.",
+                             sha1_to_hex(from_obj));
+                       retval = 1;
+                       goto end;
+               }
+               hashcpy(new_note, from_note);
+       } else
+               create_note(object, &msg, append, note, new_note);
+
+       if (is_null_sha1(new_note))
+               remove_note(t, object);
+       else
+               add_note(t, object, new_note, combine_notes_overwrite);
+
+commit:
+       snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
+                is_null_sha1(new_note) ? "removed" : "added", argv[0]);
+       commit_notes(t, logmsg);
+
+end:
+       free_notes(t);
+       strbuf_release(&(msg.buf));
+       return retval;
+}
similarity index 98%
rename from builtin-pack-objects.c
rename to builtin/pack-objects.c
index e1d3adf405bb6ac842a3415e0461b4772396060d..97802585ea3ac69ac6ed2e7995605bdcae84558e 100644 (file)
@@ -154,33 +154,6 @@ static unsigned long do_compress(void **pptr, unsigned long size)
        return stream.total_out;
 }
 
-/*
- * The per-object header is a pretty dense thing, which is
- *  - first byte: low four bits are "size", then three bits of "type",
- *    and the high bit is "size continues".
- *  - each byte afterwards: low seven bits are size continuation,
- *    with the high bit being "size continues"
- */
-static int encode_header(enum object_type type, unsigned long size, unsigned char *hdr)
-{
-       int n = 1;
-       unsigned char c;
-
-       if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
-               die("bad type %d", type);
-
-       c = (type << 4) | (size & 15);
-       size >>= 4;
-       while (size) {
-               *hdr++ = c | 0x80;
-               c = size & 0x7f;
-               size >>= 7;
-               n++;
-       }
-       *hdr = c;
-       return n;
-}
-
 /*
  * we are going to reuse the existing object data as is.  make
  * sure it is not corrupt.
@@ -321,7 +294,7 @@ static unsigned long write_object(struct sha1file *f,
                 * The object header is a byte of 'type' followed by zero or
                 * more bytes of length.
                 */
-               hdrlen = encode_header(type, size, header);
+               hdrlen = encode_in_pack_object_header(type, size, header);
 
                if (type == OBJ_OFS_DELTA) {
                        /*
@@ -372,7 +345,7 @@ static unsigned long write_object(struct sha1file *f,
                if (entry->delta)
                        type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
-               hdrlen = encode_header(type, entry->size, header);
+               hdrlen = encode_in_pack_object_header(type, entry->size, header);
 
                offset = entry->in_pack_offset;
                revidx = find_pack_revindex(p, offset);
@@ -464,9 +437,6 @@ static int write_one(struct sha1file *f,
        return 1;
 }
 
-/* forward declaration for write_pack_file */
-static int adjust_perm(const char *path, mode_t mode);
-
 static void write_pack_file(void)
 {
        uint32_t i = 0, j;
@@ -523,21 +493,17 @@ static void write_pack_file(void)
                }
 
                if (!pack_to_stdout) {
-                       mode_t mode = umask(0);
                        struct stat st;
                        const char *idx_tmp_name;
                        char tmpname[PATH_MAX];
 
-                       umask(mode);
-                       mode = 0444 & ~mode;
-
                        idx_tmp_name = write_idx_file(NULL, written_list,
                                                      nr_written, sha1);
 
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
                                 base_name, sha1_to_hex(sha1));
                        free_pack_by_name(tmpname);
-                       if (adjust_perm(pack_tmp_name, mode))
+                       if (adjust_shared_perm(pack_tmp_name))
                                die_errno("unable to make temporary pack file readable");
                        if (rename(pack_tmp_name, tmpname))
                                die_errno("unable to rename temporary pack file");
@@ -565,7 +531,7 @@ static void write_pack_file(void)
 
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
                                 base_name, sha1_to_hex(sha1));
-                       if (adjust_perm(idx_tmp_name, mode))
+                       if (adjust_shared_perm(idx_tmp_name))
                                die_errno("unable to make temporary index file readable");
                        if (rename(idx_tmp_name, tmpname))
                                die_errno("unable to rename temporary index file");
@@ -2125,13 +2091,6 @@ static void get_object_list(int ac, const char **av)
                loosen_unused_packed_objects(&revs);
 }
 
-static int adjust_perm(const char *path, mode_t mode)
-{
-       if (chmod(path, mode))
-               return -1;
-       return adjust_shared_perm(path);
-}
-
 int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 {
        int use_internal_rev_list = 0;
similarity index 100%
rename from builtin-pack-refs.c
rename to builtin/pack-refs.c
similarity index 100%
rename from builtin-patch-id.c
rename to builtin/patch-id.c
similarity index 90%
rename from builtin-prune.c
rename to builtin/prune.c
index 8459aec8e8ea9d24a13448cf950d2e160361fd9d..81f915ec315eb75e529fed8f2f6bab436755f598 100644 (file)
@@ -18,13 +18,11 @@ static unsigned long expire;
 static int prune_tmp_object(const char *path, const char *filename)
 {
        const char *fullpath = mkpath("%s/%s", path, filename);
-       if (expire) {
-               struct stat st;
-               if (lstat(fullpath, &st))
-                       return error("Could not stat '%s'", fullpath);
-               if (st.st_mtime > expire)
-                       return 0;
-       }
+       struct stat st;
+       if (lstat(fullpath, &st))
+               return error("Could not stat '%s'", fullpath);
+       if (st.st_mtime > expire)
+               return 0;
        printf("Removing stale temporary file %s\n", fullpath);
        if (!show_only)
                unlink_or_warn(fullpath);
@@ -34,13 +32,11 @@ static int prune_tmp_object(const char *path, const char *filename)
 static int prune_object(char *path, const char *filename, const unsigned char *sha1)
 {
        const char *fullpath = mkpath("%s/%s", path, filename);
-       if (expire) {
-               struct stat st;
-               if (lstat(fullpath, &st))
-                       return error("Could not stat '%s'", fullpath);
-               if (st.st_mtime > expire)
-                       return 0;
-       }
+       struct stat st;
+       if (lstat(fullpath, &st))
+               return error("Could not stat '%s'", fullpath);
+       if (st.st_mtime > expire)
+               return 0;
        if (show_only || verbose) {
                enum object_type type = sha1_object_info(sha1, NULL);
                printf("%s %s\n", sha1_to_hex(sha1),
@@ -106,7 +102,7 @@ static void prune_object_dir(const char *path)
 /*
  * Write errors (particularly out of space) can result in
  * failed temporary packs (and more rarely indexes and other
- * files begining with "tmp_") accumulating in the object
+ * files beginning with "tmp_") accumulating in the object
  * and the pack directories.
  */
 static void remove_temporary_files(const char *path)
@@ -139,6 +135,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        };
        char *s;
 
+       expire = ULONG_MAX;
        save_commit_buffer = 0;
        read_replace_refs = 0;
        init_revisions(&revs, prefix);
similarity index 92%
rename from builtin-push.c
rename to builtin/push.c
index 5633f0ade49f7c845665b7aab202be18a8cc9d8d..62957ededdf72b93f088586f5d577ef8d38a7f6c 100644 (file)
@@ -17,6 +17,8 @@ static const char * const push_usage[] = {
 static int thin;
 static int deleterefs;
 static const char *receivepack;
+static int verbosity;
+static int progress;
 
 static const char **refspec;
 static int refspec_nr;
@@ -68,7 +70,7 @@ static void setup_push_tracking(void)
        struct branch *branch = branch_get(NULL);
        if (!branch)
                die("You are not currently on a branch.");
-       if (!branch->merge_nr)
+       if (!branch->merge_nr || !branch->merge)
                die("The current branch %s is not tracking anything.",
                    branch->name);
        if (branch->merge_nr != 1)
@@ -105,13 +107,16 @@ static int push_with_options(struct transport *transport, int flags)
 {
        int err;
        int nonfastforward;
+
+       transport_set_verbosity(transport, verbosity, progress);
+
        if (receivepack)
                transport_set_option(transport,
                                     TRANS_OPT_RECEIVEPACK, receivepack);
        if (thin)
                transport_set_option(transport, TRANS_OPT_THIN, "yes");
 
-       if (flags & TRANSPORT_PUSH_VERBOSE)
+       if (verbosity > 0)
                fprintf(stderr, "Pushing to %s\n", transport->url);
        err = transport_push(transport, refspec_nr, refspec, flags,
                             &nonfastforward);
@@ -124,9 +129,9 @@ static int push_with_options(struct transport *transport, int flags)
                return 0;
 
        if (nonfastforward && advice_push_nonfastforward) {
-               printf("To prevent you from losing history, non-fast-forward updates were rejected\n"
-                      "Merge the remote changes before pushing again.  See the 'Note about\n"
-                      "fast-forwards' section of 'git push --help' for details.\n");
+               fprintf(stderr, "To prevent you from losing history, non-fast-forward updates were rejected\n"
+                               "Merge the remote changes before pushing again.  See the 'Note about\n"
+                               "fast-forwards' section of 'git push --help' for details.\n");
        }
 
        return 1;
@@ -204,8 +209,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        int rc;
        const char *repo = NULL;        /* default repository */
        struct option options[] = {
-               OPT_BIT('q', "quiet", &flags, "be quiet", TRANSPORT_PUSH_QUIET),
-               OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE),
+               OPT__VERBOSITY(&verbosity),
                OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
                OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
                OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
@@ -220,6 +224,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
                OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
                        TRANSPORT_PUSH_SET_UPSTREAM),
+               OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
                OPT_END()
        };
 
similarity index 100%
rename from builtin-read-tree.c
rename to builtin/read-tree.c
similarity index 81%
rename from builtin-receive-pack.c
rename to builtin/receive-pack.c
index 4320c93e700a08911e42e3e949656af67b675244..0559fcc871894548050e99c8c258561a0dcad5c9 100644 (file)
@@ -2,6 +2,7 @@
 #include "pack.h"
 #include "refs.h"
 #include "pkt-line.h"
+#include "sideband.h"
 #include "run-command.h"
 #include "exec_cmd.h"
 #include "commit.h"
@@ -27,11 +28,12 @@ static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
 static int unpack_limit = 100;
 static int report_status;
+static int use_sideband;
 static int prefer_ofs_delta = 1;
 static int auto_update_server_info;
 static int auto_gc = 1;
 static const char *head_name;
-static char *capabilities_to_send;
+static int sent_capabilities;
 
 static enum deny_action parse_deny_action(const char *var, const char *value)
 {
@@ -105,19 +107,21 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
 
 static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-       if (!capabilities_to_send)
+       if (sent_capabilities)
                packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
        else
-               packet_write(1, "%s %s%c%s\n",
-                            sha1_to_hex(sha1), path, 0, capabilities_to_send);
-       capabilities_to_send = NULL;
+               packet_write(1, "%s %s%c%s%s\n",
+                            sha1_to_hex(sha1), path, 0,
+                            " report-status delete-refs side-band-64k",
+                            prefer_ofs_delta ? " ofs-delta" : "");
+       sent_capabilities = 1;
        return 0;
 }
 
 static void write_head_info(void)
 {
        for_each_ref(show_ref, NULL);
-       if (capabilities_to_send)
+       if (!sent_capabilities)
                show_ref("capabilities^{}", null_sha1, 0, NULL);
 
 }
@@ -135,11 +139,61 @@ static struct command *commands;
 static const char pre_receive_hook[] = "hooks/pre-receive";
 static const char post_receive_hook[] = "hooks/post-receive";
 
+static void rp_error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+static void rp_warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+
+static void report_message(const char *prefix, const char *err, va_list params)
+{
+       int sz = strlen(prefix);
+       char msg[4096];
+
+       strncpy(msg, prefix, sz);
+       sz += vsnprintf(msg + sz, sizeof(msg) - sz, err, params);
+       if (sz > (sizeof(msg) - 1))
+               sz = sizeof(msg) - 1;
+       msg[sz++] = '\n';
+
+       if (use_sideband)
+               send_sideband(1, 2, msg, sz, use_sideband);
+       else
+               xwrite(2, msg, sz);
+}
+
+static void rp_warning(const char *err, ...)
+{
+       va_list params;
+       va_start(params, err);
+       report_message("warning: ", err, params);
+       va_end(params);
+}
+
+static void rp_error(const char *err, ...)
+{
+       va_list params;
+       va_start(params, err);
+       report_message("error: ", err, params);
+       va_end(params);
+}
+
+static int copy_to_sideband(int in, int out, void *arg)
+{
+       char data[128];
+       while (1) {
+               ssize_t sz = xread(in, data, sizeof(data));
+               if (sz <= 0)
+                       break;
+               send_sideband(1, 2, data, sz, use_sideband);
+       }
+       close(in);
+       return 0;
+}
+
 static int run_receive_hook(const char *hook_name)
 {
        static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
        struct command *cmd;
        struct child_process proc;
+       struct async muxer;
        const char *argv[2];
        int have_input = 0, code;
 
@@ -159,9 +213,23 @@ static int run_receive_hook(const char *hook_name)
        proc.in = -1;
        proc.stdout_to_stderr = 1;
 
+       if (use_sideband) {
+               memset(&muxer, 0, sizeof(muxer));
+               muxer.proc = copy_to_sideband;
+               muxer.in = -1;
+               code = start_async(&muxer);
+               if (code)
+                       return code;
+               proc.err = muxer.in;
+       }
+
        code = start_command(&proc);
-       if (code)
+       if (code) {
+               if (use_sideband)
+                       finish_async(&muxer);
                return code;
+       }
+
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (!cmd->error_string) {
                        size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
@@ -173,6 +241,8 @@ static int run_receive_hook(const char *hook_name)
                }
        }
        close(proc.in);
+       if (use_sideband)
+               finish_async(&muxer);
        return finish_command(&proc);
 }
 
@@ -180,6 +250,8 @@ static int run_update_hook(struct command *cmd)
 {
        static const char update_hook[] = "hooks/update";
        const char *argv[5];
+       struct child_process proc;
+       int code;
 
        if (access(update_hook, X_OK) < 0)
                return 0;
@@ -190,8 +262,18 @@ static int run_update_hook(struct command *cmd)
        argv[3] = sha1_to_hex(cmd->new_sha1);
        argv[4] = NULL;
 
-       return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
-                                       RUN_COMMAND_STDOUT_TO_STDERR);
+       memset(&proc, 0, sizeof(proc));
+       proc.no_stdin = 1;
+       proc.stdout_to_stderr = 1;
+       proc.err = use_sideband ? -1 : 0;
+       proc.argv = argv;
+
+       code = start_command(&proc);
+       if (code)
+               return code;
+       if (use_sideband)
+               copy_to_sideband(proc.err, -1, NULL);
+       return finish_command(&proc);
 }
 
 static int is_ref_checked_out(const char *ref)
@@ -224,7 +306,7 @@ static void refuse_unconfigured_deny(void)
 {
        int i;
        for (i = 0; i < ARRAY_SIZE(refuse_unconfigured_deny_msg); i++)
-               error("%s", refuse_unconfigured_deny_msg[i]);
+               rp_error("%s", refuse_unconfigured_deny_msg[i]);
 }
 
 static char *refuse_unconfigured_deny_delete_current_msg[] = {
@@ -244,7 +326,7 @@ static void refuse_unconfigured_deny_delete_current(void)
        for (i = 0;
             i < ARRAY_SIZE(refuse_unconfigured_deny_delete_current_msg);
             i++)
-               error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
+               rp_error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
 }
 
 static const char *update(struct command *cmd)
@@ -256,7 +338,7 @@ static const char *update(struct command *cmd)
 
        /* only refs/... are allowed */
        if (prefixcmp(name, "refs/") || check_ref_format(name + 5)) {
-               error("refusing to create funny ref '%s' remotely", name);
+               rp_error("refusing to create funny ref '%s' remotely", name);
                return "funny refname";
        }
 
@@ -265,11 +347,11 @@ static const char *update(struct command *cmd)
                case DENY_IGNORE:
                        break;
                case DENY_WARN:
-                       warning("updating the current branch");
+                       rp_warning("updating the current branch");
                        break;
                case DENY_REFUSE:
                case DENY_UNCONFIGURED:
-                       error("refusing to update checked out branch: %s", name);
+                       rp_error("refusing to update checked out branch: %s", name);
                        if (deny_current_branch == DENY_UNCONFIGURED)
                                refuse_unconfigured_deny();
                        return "branch is currently checked out";
@@ -284,7 +366,7 @@ static const char *update(struct command *cmd)
 
        if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
                if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
-                       error("denying ref deletion for %s", name);
+                       rp_error("denying ref deletion for %s", name);
                        return "deletion prohibited";
                }
 
@@ -293,13 +375,13 @@ static const char *update(struct command *cmd)
                        case DENY_IGNORE:
                                break;
                        case DENY_WARN:
-                               warning("deleting the current branch");
+                               rp_warning("deleting the current branch");
                                break;
                        case DENY_REFUSE:
                        case DENY_UNCONFIGURED:
                                if (deny_delete_current == DENY_UNCONFIGURED)
                                        refuse_unconfigured_deny_delete_current();
-                               error("refusing to delete the current branch: %s", name);
+                               rp_error("refusing to delete the current branch: %s", name);
                                return "deletion of the current branch prohibited";
                        }
                }
@@ -329,23 +411,23 @@ static const char *update(struct command *cmd)
                                break;
                free_commit_list(bases);
                if (!ent) {
-                       error("denying non-fast-forward %s"
-                             " (you should pull first)", name);
+                       rp_error("denying non-fast-forward %s"
+                                " (you should pull first)", name);
                        return "non-fast-forward";
                }
        }
        if (run_update_hook(cmd)) {
-               error("hook declined to update %s", name);
+               rp_error("hook declined to update %s", name);
                return "hook declined";
        }
 
        if (is_null_sha1(new_sha1)) {
                if (!parse_object(old_sha1)) {
-                       warning ("Allowing deletion of corrupt ref.");
+                       rp_warning("Allowing deletion of corrupt ref.");
                        old_sha1 = NULL;
                }
                if (delete_ref(name, old_sha1, 0)) {
-                       error("failed to delete %s", name);
+                       rp_error("failed to delete %s", name);
                        return "failed to delete";
                }
                return NULL; /* good */
@@ -353,7 +435,7 @@ static const char *update(struct command *cmd)
        else {
                lock = lock_any_ref_for_update(name, old_sha1, 0);
                if (!lock) {
-                       error("failed to lock %s", name);
+                       rp_error("failed to lock %s", name);
                        return "failed to lock";
                }
                if (write_ref_sha1(lock, new_sha1, "push")) {
@@ -368,8 +450,9 @@ static char update_post_hook[] = "hooks/post-update";
 static void run_update_post_hook(struct command *cmd)
 {
        struct command *cmd_p;
-       int argc, status;
+       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)
@@ -391,8 +474,18 @@ static void run_update_post_hook(struct command *cmd)
                argc++;
        }
        argv[argc] = NULL;
-       status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
-                       | RUN_COMMAND_STDOUT_TO_STDERR);
+
+       memset(&proc, 0, sizeof(proc));
+       proc.no_stdin = 1;
+       proc.stdout_to_stderr = 1;
+       proc.err = use_sideband ? -1 : 0;
+       proc.argv = argv;
+
+       if (!start_command(&proc)) {
+               if (use_sideband)
+                       copy_to_sideband(proc.err, -1, NULL);
+               finish_command(&proc);
+       }
 }
 
 static void execute_commands(const char *unpacker_error)
@@ -452,6 +545,8 @@ static void read_head_info(void)
                if (reflen + 82 < len) {
                        if (strstr(refname + reflen + 1, "report-status"))
                                report_status = 1;
+                       if (strstr(refname + reflen + 1, "side-band-64k"))
+                               use_sideband = LARGE_PACKET_MAX;
                }
                cmd = xmalloc(sizeof(struct command) + len - 80);
                hashcpy(cmd->old_sha1, old_sha1);
@@ -551,17 +646,25 @@ static const char *unpack(void)
 static void report(const char *unpack_status)
 {
        struct command *cmd;
-       packet_write(1, "unpack %s\n",
-                    unpack_status ? unpack_status : "ok");
+       struct strbuf buf = STRBUF_INIT;
+
+       packet_buf_write(&buf, "unpack %s\n",
+                        unpack_status ? unpack_status : "ok");
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (!cmd->error_string)
-                       packet_write(1, "ok %s\n",
-                                    cmd->ref_name);
+                       packet_buf_write(&buf, "ok %s\n",
+                                        cmd->ref_name);
                else
-                       packet_write(1, "ng %s %s\n",
-                                    cmd->ref_name, cmd->error_string);
+                       packet_buf_write(&buf, "ng %s %s\n",
+                                        cmd->ref_name, cmd->error_string);
        }
-       packet_flush(1);
+       packet_buf_flush(&buf);
+
+       if (use_sideband)
+               send_sideband(1, 1, buf.buf, buf.len, use_sideband);
+       else
+               safe_write(1, buf.buf, buf.len);
+       strbuf_release(&buf);
 }
 
 static int delete_only(struct command *cmd)
@@ -658,10 +761,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        else if (0 <= receive_unpack_limit)
                unpack_limit = receive_unpack_limit;
 
-       capabilities_to_send = (prefer_ofs_delta) ?
-               " report-status delete-refs ofs-delta " :
-               " report-status delete-refs ";
-
        if (advertise_refs || !stateless_rpc) {
                add_alternate_refs();
                write_head_info();
@@ -695,5 +794,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                if (auto_update_server_info)
                        update_server_info(0);
        }
+       if (use_sideband)
+               packet_flush(1);
        return 0;
 }
similarity index 99%
rename from builtin-reflog.c
rename to builtin/reflog.c
index 749821078df129cf13de34ebbd40a8cb7a38e00e..64e45bd8137bef2b6cfe1f8a3da79e2ff6f8fc47 100644 (file)
@@ -530,16 +530,14 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
        int i, status, do_all;
        int explicit_expiry = 0;
 
+       default_reflog_expire_unreachable = now - 30 * 24 * 3600;
+       default_reflog_expire = now - 90 * 24 * 3600;
        git_config(reflog_expire_config, NULL);
 
        save_commit_buffer = 0;
        do_all = status = 0;
        memset(&cb, 0, sizeof(cb));
 
-       if (!default_reflog_expire_unreachable)
-               default_reflog_expire_unreachable = now - 30 * 24 * 3600;
-       if (!default_reflog_expire)
-               default_reflog_expire = now - 90 * 24 * 3600;
        cb.expire_total = default_reflog_expire;
        cb.expire_unreachable = default_reflog_expire_unreachable;
 
similarity index 100%
rename from builtin-remote.c
rename to builtin/remote.c
similarity index 100%
rename from builtin-replace.c
rename to builtin/replace.c
similarity index 100%
rename from builtin-rerere.c
rename to builtin/rerere.c
similarity index 88%
rename from builtin-reset.c
rename to builtin/reset.c
index 0f5022eed24f980f6fedee49f8602fefa6fe85e4..2c3a69adc674bb3f539d29b2d29116e89f887066 100644 (file)
 #include "cache-tree.h"
 
 static const char * const git_reset_usage[] = {
-       "git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]",
+       "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]",
        "git reset [--mixed] <commit> [--] <paths>...",
        NULL
 };
 
-enum reset_type { MIXED, SOFT, HARD, MERGE, NONE };
-static const char *reset_type_names[] = { "mixed", "soft", "hard", "merge", NULL };
+enum reset_type { MIXED, SOFT, HARD, MERGE, KEEP, NONE };
+static const char *reset_type_names[] = {
+       "mixed", "soft", "hard", "merge", "keep", NULL
+};
 
 static char *args_to_str(const char **argv)
 {
@@ -71,6 +73,7 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet
        if (!quiet)
                opts.verbose_update = 1;
        switch (reset_type) {
+       case KEEP:
        case MERGE:
                opts.update = 1;
                break;
@@ -85,6 +88,16 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet
 
        read_cache_unmerged();
 
+       if (reset_type == KEEP) {
+               unsigned char head_sha1[20];
+               if (get_sha1("HEAD", head_sha1))
+                       return error("You do not have a valid HEAD.");
+               if (!fill_tree_descriptor(desc, head_sha1))
+                       return error("Failed to find tree of HEAD.");
+               nr++;
+               opts.fn = twoway_merge;
+       }
+
        if (!fill_tree_descriptor(desc + nr - 1, sha1))
                return error("Failed to find tree of %s.", sha1_to_hex(sha1));
        if (unpack_trees(nr, desc, &opts))
@@ -211,6 +224,14 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size)
                warning("Reflog action message too long: %.*s...", 50, buf);
 }
 
+static void die_if_unmerged_cache(int reset_type)
+{
+       if (is_merge() || read_cache() < 0 || unmerged_cache())
+               die("Cannot do a %s reset in the middle of a merge.",
+                   reset_type_names[reset_type]);
+
+}
+
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
        int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
@@ -229,6 +250,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                "reset HEAD, index and working tree", HARD),
                OPT_SET_INT(0, "merge", &reset_type,
                                "reset HEAD, index and working tree", MERGE),
+               OPT_SET_INT(0, "keep", &reset_type,
+                               "reset HEAD but keep local changes", KEEP),
                OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
                OPT_END()
        };
@@ -304,7 +327,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        if (reset_type == NONE)
                reset_type = MIXED; /* by default */
 
-       if (reset_type == HARD || reset_type == MERGE)
+       if (reset_type != SOFT && reset_type != MIXED)
                setup_work_tree();
 
        if (reset_type == MIXED && is_bare_repository())
@@ -314,12 +337,18 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        /* Soft reset does not touch the index file nor the working tree
         * at all, but requires them in a good order.  Other resets reset
         * the index file to the tree object we are switching to. */
-       if (reset_type == SOFT) {
-               if (is_merge() || read_cache() < 0 || unmerged_cache())
-                       die("Cannot do a soft reset in the middle of a merge.");
+       if (reset_type == SOFT)
+               die_if_unmerged_cache(reset_type);
+       else {
+               int err;
+               if (reset_type == KEEP)
+                       die_if_unmerged_cache(reset_type);
+               err = reset_index_file(sha1, reset_type, quiet);
+               if (reset_type == KEEP)
+                       err = err || reset_index_file(sha1, MIXED, quiet);
+               if (err)
+                       die("Could not reset index file to revision '%s'.", rev);
        }
-       else if (reset_index_file(sha1, reset_type, quiet))
-               die("Could not reset index file to revision '%s'.", rev);
 
        /* Any resets update HEAD to the head being switched to,
         * saving the previous head in ORIG_HEAD before. */
similarity index 98%
rename from builtin-rev-list.c
rename to builtin/rev-list.c
index c924b3a2c76c1f9a7f5531504825ed1b5456d41a..5679170e82ed644d4c3eb4f71f26aa0ac9acce24 100644 (file)
@@ -371,8 +371,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
            revs.diff)
                usage(rev_list_usage);
 
-       save_commit_buffer = revs.verbose_header ||
-               revs.grep_filter.pattern_list;
+       save_commit_buffer = (revs.verbose_header ||
+                             revs.grep_filter.pattern_list ||
+                             revs.grep_filter.header_list);
        if (bisect_list)
                revs.limited = 1;
 
similarity index 98%
rename from builtin-rev-parse.c
rename to builtin/rev-parse.c
index a8c5043dedd785b8fc43c0921edd01e2adac65e1..8fbf9d0db6f40aa8c7cb61d72d0f44446de46826 100644 (file)
@@ -455,6 +455,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
        if (argc > 1 && !strcmp("--sq-quote", argv[1]))
                return cmd_sq_quote(argc - 2, argv + 2);
 
+       if (argc == 2 && !strcmp("--local-env-vars", argv[1])) {
+               int i;
+               for (i = 0; local_repo_env[i]; i++)
+                       printf("%s\n", local_repo_env[i]);
+               return 0;
+       }
+
        if (argc > 1 && !strcmp("-h", argv[1]))
                usage(builtin_rev_parse_usage);
 
@@ -637,6 +644,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        if (!strcmp(arg, "--git-dir")) {
                                const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
                                static char cwd[PATH_MAX];
+                               int len;
                                if (gitdir) {
                                        puts(gitdir);
                                        continue;
@@ -647,7 +655,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                }
                                if (!getcwd(cwd, PATH_MAX))
                                        die_errno("unable to get current working directory");
-                               printf("%s/.git\n", cwd);
+                               len = strlen(cwd);
+                               printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
                                continue;
                        }
                        if (!strcmp(arg, "--is-inside-git-dir")) {
similarity index 92%
rename from builtin-revert.c
rename to builtin/revert.c
index 8ac86f09434d1dad830c924de8cadcef6705e1e5..eff52687a87b45b766e064eede7c2c35b6d133d2 100644 (file)
@@ -38,6 +38,7 @@ static const char * const cherry_pick_usage[] = {
 static int edit, no_replay, no_commit, mainline, signoff;
 static enum { REVERT, CHERRY_PICK } action;
 static struct commit *commit;
+static const char *commit_name;
 static int allow_rerere_auto;
 
 static const char *me;
@@ -49,7 +50,6 @@ 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 *arg;
        int noop;
        struct option options[] = {
                OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
@@ -64,19 +64,13 @@ static void parse_args(int argc, const char **argv)
 
        if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1)
                usage_with_options(usage_str, options);
-       arg = argv[0];
 
-       if (get_sha1(arg, sha1))
-               die ("Cannot find '%s'", arg);
-       commit = (struct commit *)parse_object(sha1);
+       commit_name = argv[0];
+       if (get_sha1(commit_name, sha1))
+               die ("Cannot find '%s'", commit_name);
+       commit = lookup_commit_reference(sha1);
        if (!commit)
-               die ("Could not find %s", sha1_to_hex(sha1));
-       if (commit->object.type == OBJ_TAG) {
-               commit = (struct commit *)
-                       deref_tag((struct object *)commit, arg, strlen(arg));
-       }
-       if (commit->object.type != OBJ_COMMIT)
-               die ("'%s' does not point to a commit", arg);
+               exit(1);
 }
 
 static char *get_oneline(const char *message)
@@ -204,25 +198,27 @@ static void set_author_ident_env(const char *message)
                        sha1_to_hex(commit->object.sha1));
 }
 
-static char *help_msg(const unsigned char *sha1)
+static char *help_msg(const char *name)
 {
-       static char helpbuf[1024];
+       struct strbuf helpbuf = STRBUF_INIT;
        char *msg = getenv("GIT_CHERRY_PICK_HELP");
 
        if (msg)
                return msg;
 
-       strcpy(helpbuf, "  After resolving the conflicts,\n"
-              "mark the corrected paths with 'git add <paths>' "
-              "or 'git rm <paths>' and commit the result.");
+       strbuf_addstr(&helpbuf, "  After resolving the conflicts,\n"
+               "mark the corrected paths with 'git add <paths>' or 'git rm <paths>'\n"
+               "and commit the result");
 
        if (action == CHERRY_PICK) {
-               sprintf(helpbuf + strlen(helpbuf),
-                       "\nWhen commiting, use the option "
-                       "'-c %s' to retain authorship and message.",
-                       find_unique_abbrev(sha1, DEFAULT_ABBREV));
+               strbuf_addf(&helpbuf, " with: \n"
+                       "\n"
+                       "        git commit -c %s\n",
+                       name);
        }
-       return helpbuf;
+       else
+               strbuf_addch(&helpbuf, '.');
+       return strbuf_detach(&helpbuf, NULL);
 }
 
 static struct tree *empty_tree(void)
@@ -409,7 +405,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                if (commit_lock_file(&msg_file) < 0)
                        die ("Error wrapping up %s", defmsg);
                fprintf(stderr, "Automatic %s failed.%s\n",
-                       me, help_msg(commit->object.sha1));
+                       me, help_msg(commit_name));
                rerere(allow_rerere_auto);
                exit(1);
        }
similarity index 100%
rename from builtin-rm.c
rename to builtin/rm.c
similarity index 67%
rename from builtin-send-pack.c
rename to builtin/send-pack.c
index 76c72065de73ea3f0da4665c0a47a64610e2ead2..481602d8ae73612226bcc758f2aedea7f964779c 100644 (file)
@@ -7,6 +7,7 @@
 #include "remote.h"
 #include "send-pack.h"
 #include "quote.h"
+#include "transport.h"
 
 static const char send_pack_usage[] =
 "git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
@@ -169,156 +170,6 @@ static int receive_status(int in, struct ref *refs)
        return ret;
 }
 
-static void update_tracking_ref(struct remote *remote, struct ref *ref)
-{
-       struct refspec rs;
-
-       if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
-               return;
-
-       rs.src = ref->name;
-       rs.dst = NULL;
-
-       if (!remote_find_tracking(remote, &rs)) {
-               if (args.verbose)
-                       fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
-               if (ref->deletion) {
-                       delete_ref(rs.dst, NULL, 0);
-               } else
-                       update_ref("update by push", rs.dst,
-                                       ref->new_sha1, NULL, 0, 0);
-               free(rs.dst);
-       }
-}
-
-#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
-
-static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
-{
-       fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
-       if (from)
-               fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
-       else
-               fputs(prettify_refname(to->name), stderr);
-       if (msg) {
-               fputs(" (", stderr);
-               fputs(msg, stderr);
-               fputc(')', stderr);
-       }
-       fputc('\n', stderr);
-}
-
-static const char *status_abbrev(unsigned char sha1[20])
-{
-       return find_unique_abbrev(sha1, DEFAULT_ABBREV);
-}
-
-static void print_ok_ref_status(struct ref *ref)
-{
-       if (ref->deletion)
-               print_ref_status('-', "[deleted]", ref, NULL, NULL);
-       else if (is_null_sha1(ref->old_sha1))
-               print_ref_status('*',
-                       (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
-                         "[new branch]"),
-                       ref, ref->peer_ref, NULL);
-       else {
-               char quickref[84];
-               char type;
-               const char *msg;
-
-               strcpy(quickref, status_abbrev(ref->old_sha1));
-               if (ref->nonfastforward) {
-                       strcat(quickref, "...");
-                       type = '+';
-                       msg = "forced update";
-               } else {
-                       strcat(quickref, "..");
-                       type = ' ';
-                       msg = NULL;
-               }
-               strcat(quickref, status_abbrev(ref->new_sha1));
-
-               print_ref_status(type, quickref, ref, ref->peer_ref, msg);
-       }
-}
-
-static int print_one_push_status(struct ref *ref, const char *dest, int count)
-{
-       if (!count)
-               fprintf(stderr, "To %s\n", dest);
-
-       switch(ref->status) {
-       case REF_STATUS_NONE:
-               print_ref_status('X', "[no match]", ref, NULL, NULL);
-               break;
-       case REF_STATUS_REJECT_NODELETE:
-               print_ref_status('!', "[rejected]", ref, NULL,
-                               "remote does not support deleting refs");
-               break;
-       case REF_STATUS_UPTODATE:
-               print_ref_status('=', "[up to date]", ref,
-                               ref->peer_ref, NULL);
-               break;
-       case REF_STATUS_REJECT_NONFASTFORWARD:
-               print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                               "non-fast-forward");
-               break;
-       case REF_STATUS_REMOTE_REJECT:
-               print_ref_status('!', "[remote rejected]", ref,
-                               ref->deletion ? NULL : ref->peer_ref,
-                               ref->remote_status);
-               break;
-       case REF_STATUS_EXPECTING_REPORT:
-               print_ref_status('!', "[remote failure]", ref,
-                               ref->deletion ? NULL : ref->peer_ref,
-                               "remote failed to report status");
-               break;
-       case REF_STATUS_OK:
-               print_ok_ref_status(ref);
-               break;
-       }
-
-       return 1;
-}
-
-static void print_push_status(const char *dest, struct ref *refs)
-{
-       struct ref *ref;
-       int n = 0;
-
-       if (args.verbose) {
-               for (ref = refs; ref; ref = ref->next)
-                       if (ref->status == REF_STATUS_UPTODATE)
-                               n += print_one_push_status(ref, dest, n);
-       }
-
-       for (ref = refs; ref; ref = ref->next)
-               if (ref->status == REF_STATUS_OK)
-                       n += print_one_push_status(ref, dest, n);
-
-       for (ref = refs; ref; ref = ref->next) {
-               if (ref->status != REF_STATUS_NONE &&
-                   ref->status != REF_STATUS_UPTODATE &&
-                   ref->status != REF_STATUS_OK)
-                       n += print_one_push_status(ref, dest, n);
-       }
-}
-
-static int refs_pushed(struct ref *ref)
-{
-       for (; ref; ref = ref->next) {
-               switch(ref->status) {
-               case REF_STATUS_NONE:
-               case REF_STATUS_UPTODATE:
-                       break;
-               default:
-                       return 1;
-               }
-       }
-       return 0;
-}
-
 static void print_helper_status(struct ref *ref)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -372,6 +223,14 @@ static void print_helper_status(struct ref *ref)
        strbuf_release(&buf);
 }
 
+static int sideband_demux(int in, int out, void *data)
+{
+       int *fd = data;
+       int ret = recv_sideband("send-pack", fd[0], out);
+       close(out);
+       return ret;
+}
+
 int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
@@ -382,18 +241,22 @@ int send_pack(struct send_pack_args *args,
        struct strbuf req_buf = STRBUF_INIT;
        struct ref *ref;
        int new_refs;
-       int ask_for_status_report = 0;
        int allow_deleting_refs = 0;
-       int expect_status_report = 0;
+       int status_report = 0;
+       int use_sideband = 0;
+       unsigned cmds_sent = 0;
        int ret;
+       struct async demux;
 
        /* Does the other end support the reporting? */
        if (server_supports("report-status"))
-               ask_for_status_report = 1;
+               status_report = 1;
        if (server_supports("delete-refs"))
                allow_deleting_refs = 1;
        if (server_supports("ofs-delta"))
                args->use_ofs_delta = 1;
+       if (server_supports("side-band-64k"))
+               use_sideband = 1;
 
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -426,28 +289,30 @@ int send_pack(struct send_pack_args *args,
                if (!ref->deletion)
                        new_refs++;
 
-               if (!args->dry_run) {
+               if (args->dry_run) {
+                       ref->status = REF_STATUS_OK;
+               } else {
                        char *old_hex = sha1_to_hex(ref->old_sha1);
                        char *new_hex = sha1_to_hex(ref->new_sha1);
 
-                       if (ask_for_status_report) {
-                               packet_buf_write(&req_buf, "%s %s %s%c%s",
+                       if (!cmds_sent && (status_report || use_sideband)) {
+                               packet_buf_write(&req_buf, "%s %s %s%c%s%s",
                                        old_hex, new_hex, ref->name, 0,
-                                       "report-status");
-                               ask_for_status_report = 0;
-                               expect_status_report = 1;
+                                       status_report ? " report-status" : "",
+                                       use_sideband ? " side-band-64k" : "");
                        }
                        else
                                packet_buf_write(&req_buf, "%s %s %s",
                                        old_hex, new_hex, ref->name);
+                       ref->status = status_report ?
+                               REF_STATUS_EXPECTING_REPORT :
+                               REF_STATUS_OK;
+                       cmds_sent++;
                }
-               ref->status = expect_status_report ?
-                       REF_STATUS_EXPECTING_REPORT :
-                       REF_STATUS_OK;
        }
 
        if (args->stateless_rpc) {
-               if (!args->dry_run) {
+               if (!args->dry_run && cmds_sent) {
                        packet_buf_flush(&req_buf);
                        send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
                }
@@ -457,25 +322,49 @@ int send_pack(struct send_pack_args *args,
        }
        strbuf_release(&req_buf);
 
-       if (new_refs && !args->dry_run) {
+       if (use_sideband && cmds_sent) {
+               memset(&demux, 0, sizeof(demux));
+               demux.proc = sideband_demux;
+               demux.data = fd;
+               demux.out = -1;
+               if (start_async(&demux))
+                       die("receive-pack: unable to fork off sideband demultiplexer");
+               in = demux.out;
+       }
+
+       if (new_refs && cmds_sent) {
                if (pack_objects(out, remote_refs, extra_have, args) < 0) {
                        for (ref = remote_refs; ref; ref = ref->next)
                                ref->status = REF_STATUS_NONE;
+                       if (use_sideband)
+                               finish_async(&demux);
                        return -1;
                }
        }
-       if (args->stateless_rpc && !args->dry_run)
+       if (args->stateless_rpc && cmds_sent)
                packet_flush(out);
 
-       if (expect_status_report)
+       if (status_report && cmds_sent)
                ret = receive_status(in, remote_refs);
        else
                ret = 0;
        if (args->stateless_rpc)
                packet_flush(out);
 
+       if (use_sideband && cmds_sent) {
+               if (finish_async(&demux)) {
+                       error("error in sideband demultiplexer");
+                       ret = -1;
+               }
+               close(demux.out);
+       }
+
        if (ret < 0)
                return ret;
+
+       if (args->porcelain)
+               return 0;
+
        for (ref = remote_refs; ref; ref = ref->next) {
                switch (ref->status) {
                case REF_STATUS_NONE:
@@ -489,37 +378,6 @@ int send_pack(struct send_pack_args *args,
        return 0;
 }
 
-static void verify_remote_names(int nr_heads, const char **heads)
-{
-       int i;
-
-       for (i = 0; i < nr_heads; i++) {
-               const char *local = heads[i];
-               const char *remote = strrchr(heads[i], ':');
-
-               if (*local == '+')
-                       local++;
-
-               /* A matching refspec is okay.  */
-               if (remote == local && remote[1] == '\0')
-                       continue;
-
-               remote = remote ? (remote + 1) : local;
-               switch (check_ref_format(remote)) {
-               case 0: /* ok */
-               case CHECK_REF_FORMAT_ONELEVEL:
-                       /* ok but a single level -- that is fine for
-                        * a match pattern.
-                        */
-               case CHECK_REF_FORMAT_WILDCARD:
-                       /* ok but ends with a pattern-match character */
-                       continue;
-               }
-               die("remote part of refspec is not a valid name in %s",
-                   heads[i]);
-       }
-}
-
 int cmd_send_pack(int argc, const char **argv, const char *prefix)
 {
        int i, nr_refspecs = 0;
@@ -536,6 +394,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        int send_all = 0;
        const char *receivepack = "git-receive-pack";
        int flags;
+       int nonfastforward = 0;
 
        argv++;
        for (i = 1; i < argc; i++, argv++) {
@@ -628,7 +487,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL,
                         &extra_have);
 
-       verify_remote_names(nr_refspecs, refspecs);
+       transport_verify_remote_names(nr_refspecs, refspecs);
 
        local_refs = get_local_heads();
 
@@ -657,15 +516,15 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        ret |= finish_connect(conn);
 
        if (!helper_status)
-               print_push_status(dest, remote_refs);
+               transport_print_push_status(dest, remote_refs, args.verbose, 0, &nonfastforward);
 
        if (!args.dry_run && remote) {
                struct ref *ref;
                for (ref = remote_refs; ref; ref = ref->next)
-                       update_tracking_ref(remote, ref);
+                       transport_update_tracking_ref(remote, ref, args.verbose);
        }
 
-       if (!ret && !refs_pushed(remote_refs))
+       if (!ret && !transport_refs_pushed(remote_refs))
                fprintf(stderr, "Everything up-to-date\n");
 
        return ret;
similarity index 94%
rename from builtin-shortlog.c
rename to builtin/shortlog.c
index b3b055f68ce59b6b91ef6949bd8c4bd0bed68b55..06320f5285988365b8340e110427866e68536b47 100644 (file)
@@ -295,6 +295,8 @@ parse_done:
        if (!nongit && !rev.pending.nr && isatty(0))
                add_head_to_pending(&rev);
        if (rev.pending.nr == 0) {
+               if (isatty(0))
+                       fprintf(stderr, "(reading log message from standard input)\n");
                read_from_stdin(&log);
        }
        else
@@ -304,9 +306,19 @@ parse_done:
        return 0;
 }
 
+static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s,
+                                    const struct shortlog *log)
+{
+       int col = strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap);
+       if (col != log->wrap)
+               strbuf_addch(sb, '\n');
+}
+
 void shortlog_output(struct shortlog *log)
 {
        int i, j;
+       struct strbuf sb = STRBUF_INIT;
+
        if (log->sort_by_number)
                qsort(log->list.items, log->list.nr, sizeof(struct string_list_item),
                        compare_by_number);
@@ -321,9 +333,9 @@ void shortlog_output(struct shortlog *log)
                                const char *msg = onelines->items[j].string;
 
                                if (log->wrap_lines) {
-                                       int col = print_wrapped_text(msg, log->in1, log->in2, log->wrap);
-                                       if (col != log->wrap)
-                                               putchar('\n');
+                                       strbuf_reset(&sb);
+                                       add_wrapped_shortlog_msg(&sb, msg, log);
+                                       fwrite(sb.buf, sb.len, 1, stdout);
                                }
                                else
                                        printf("      %s\n", msg);
@@ -337,6 +349,7 @@ void shortlog_output(struct shortlog *log)
                log->list.items[i].util = NULL;
        }
 
+       strbuf_release(&sb);
        log->list.strdup_strings = 1;
        string_list_clear(&log->list, 1);
        clear_mailmap(&log->mailmap);
similarity index 98%
rename from builtin-show-branch.c
rename to builtin/show-branch.c
index 9f13caa76d3b147993b2cf39397d2f5761cfca22..e20fcf3e935dfafb4e30f24990aa974c8b2f5927 100644 (file)
@@ -6,7 +6,7 @@
 #include "parse-options.h"
 
 static const char* show_branch_usage[] = {
-    "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [<rev> | <glob>]...",
+    "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [<rev> | <glob>]...",
     "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]",
     NULL
 };
@@ -567,7 +567,7 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
                        return config_error_nonbool(var);
                /*
                 * default_arg is now passed to parse_options(), so we need to
-                * mimick the real argv a bit better.
+                * mimic the real argv a bit better.
                 */
                if (!default_num) {
                        default_alloc = 20;
@@ -661,7 +661,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                            "show remote-tracking and local branches"),
                OPT_BOOLEAN('r', "remotes", &all_remotes,
                            "show remote-tracking branches"),
-               OPT_BOOLEAN(0, "color", &showbranch_use_color,
+               OPT__COLOR(&showbranch_use_color,
                            "color '*!+-' corresponding to the branch"),
                { OPTION_INTEGER, 0, "more", &extra, "n",
                            "show <n> more commits after the common ancestor",
similarity index 100%
rename from builtin-show-ref.c
rename to builtin/show-ref.c
similarity index 100%
rename from builtin-stripspace.c
rename to builtin/stripspace.c
similarity index 100%
rename from builtin-tag.c
rename to builtin/tag.c
similarity index 100%
rename from builtin-tar-tree.c
rename to builtin/tar-tree.c
similarity index 100%
rename from builtin-unpack-file.c
rename to builtin/unpack-file.c
similarity index 100%
rename from builtin-update-ref.c
rename to builtin/update-ref.c
similarity index 94%
rename from builtin-var.c
rename to builtin/var.c
index 22805181900c3cd33da0b92a2560b19a0cf68842..70fdb4dec7e8b0c56ded90ee7f086cc9f16293a1 100644 (file)
@@ -6,7 +6,7 @@
 #include "cache.h"
 #include "exec_cmd.h"
 
-static const char var_usage[] = "git var [-l | <variable>]";
+static const char var_usage[] = "git var (-l | <variable>)";
 
 static const char *editor(int flag)
 {
@@ -20,7 +20,7 @@ static const char *editor(int flag)
 
 static const char *pager(int flag)
 {
-       const char *pgm = git_pager();
+       const char *pgm = git_pager(1);
 
        if (!pgm)
                pgm = "cat";
similarity index 100%
rename from builtin-verify-pack.c
rename to builtin/verify-pack.c
similarity index 100%
rename from builtin-verify-tag.c
rename to builtin/verify-tag.c
similarity index 100%
rename from builtin-write-tree.c
rename to builtin/write-tree.c
diff --git a/cache.h b/cache.h
index d478eff1f323f25a474cf019e0de2254c5ff0360..2928107bb135004e11f6a11a625067545680b62a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -387,6 +387,18 @@ static inline enum object_type object_type(unsigned int mode)
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
 #define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
 #define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
+#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
+#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
+#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
+
+/*
+ * Repository-local GIT_* environment variables
+ * The array is NULL-terminated to simplify its usage in contexts such
+ * environment creation or simple walk of the list.
+ * The number of non-NULL entries is available as a macro.
+ */
+#define LOCAL_REPO_ENV_SIZE 8
+extern const char *const local_repo_env[LOCAL_REPO_ENV_SIZE + 1];
 
 extern int is_bare_repository_cfg;
 extern int is_bare_repository(void);
@@ -641,6 +653,10 @@ int git_mkstemp(char *path, size_t n, const char *template);
 
 int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
 
+/* set default permissions by passing mode arguments to open(2) */
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
+int git_mkstemp_mode(char *pattern, int mode);
+
 /*
  * NOTE NOTE NOTE!!
  *
@@ -675,6 +691,7 @@ int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, const char *prefix_list);
 char *strip_path_suffix(const char *path, const char *suffix);
 int daemon_avoid_alias(const char *path);
+int offset_1st_component(const char *path);
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
@@ -775,7 +792,7 @@ extern const char *git_committer_info(int);
 extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 extern const char *fmt_name(const char *name, const char *email);
 extern const char *git_editor(void);
-extern const char *git_pager(void);
+extern const char *git_pager(int stdout_is_tty);
 
 struct checkout {
        const char *base_dir;
@@ -877,6 +894,7 @@ struct ref {
 extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
 
 #define CONNECT_VERBOSE       (1u << 0)
+extern char *git_getpass(const char *prompt);
 extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
 extern int finish_connect(struct child_process *conn);
 extern int path_match(const char *path, int nr, char **match);
diff --git a/color.c b/color.c
index 62977f4808ae339fdfe797e16b4eb28dc6abb85d..bcf4e2c192c31136729aabcc2575a94f809d2980 100644 (file)
--- a/color.c
+++ b/color.c
@@ -47,7 +47,7 @@ void color_parse_mem(const char *value, int value_len, const char *var,
 {
        const char *ptr = value;
        int len = value_len;
-       int attr = -1;
+       unsigned int attr = 0;
        int fg = -2;
        int bg = -2;
 
@@ -56,7 +56,7 @@ void color_parse_mem(const char *value, int value_len, const char *var,
                return;
        }
 
-       /* [fg [bg]] [attr] */
+       /* [fg [bg]] [attr]... */
        while (len > 0) {
                const char *word = ptr;
                int val, wordlen = 0;
@@ -85,19 +85,27 @@ void color_parse_mem(const char *value, int value_len, const char *var,
                        goto bad;
                }
                val = parse_attr(word, wordlen);
-               if (val < 0 || attr != -1)
+               if (0 <= val)
+                       attr |= (1 << val);
+               else
                        goto bad;
-               attr = val;
        }
 
-       if (attr >= 0 || fg >= 0 || bg >= 0) {
+       if (attr || fg >= 0 || bg >= 0) {
                int sep = 0;
+               int i;
 
                *dst++ = '\033';
                *dst++ = '[';
-               if (attr >= 0) {
-                       *dst++ = '0' + attr;
-                       sep++;
+
+               for (i = 0; attr; i++) {
+                       unsigned bit = (1 << i);
+                       if (!(attr & bit))
+                               continue;
+                       attr &= ~bit;
+                       if (sep++)
+                               *dst++ = ';';
+                       *dst++ = '0' + i;
                }
                if (fg >= 0) {
                        if (sep++)
@@ -138,6 +146,9 @@ int git_config_colorbool(const char *var, const char *value, int stdout_is_tty)
                        goto auto_color;
        }
 
+       if (!var)
+               return -1;
+
        /* Missing or explicit false to turn off colorization */
        if (!git_config_bool(var, value))
                return 0;
diff --git a/color.h b/color.h
index 3cb4b7fc890880b0fcf19a11c6bc7de6b10d6e8d..5c264b0ce3b95edb5f86cc003d2aca04033d098f 100644 (file)
--- a/color.h
+++ b/color.h
@@ -1,8 +1,20 @@
 #ifndef COLOR_H
 #define COLOR_H
 
-/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
-#define COLOR_MAXLEN 24
+/*  2 + (2 * num_attrs) + 8 + 1 + 8 + 'm' + NUL */
+/* "\033[1;2;4;5;7;38;5;2xx;48;5;2xxm\0" */
+/*
+ * The maximum length of ANSI color sequence we would generate:
+ * - leading ESC '['            2
+ * - attr + ';'                 2 * 8 (e.g. "1;")
+ * - fg color + ';'             9 (e.g. "38;5;2xx;")
+ * - fg color + ';'             9 (e.g. "48;5;2xx;")
+ * - terminating 'm' NUL        2
+ *
+ * The above overcounts attr (we only use 5 not 8) and one semicolon
+ * but it is close enough.
+ */
+#define COLOR_MAXLEN 40
 
 /*
  * IMPORTANT: Due to the way these color codes are emulated on Windows,
 #define GIT_COLOR_BLUE         "\033[34m"
 #define GIT_COLOR_MAGENTA      "\033[35m"
 #define GIT_COLOR_CYAN         "\033[36m"
+#define GIT_COLOR_BOLD_RED     "\033[1;31m"
+#define GIT_COLOR_BOLD_GREEN   "\033[1;32m"
+#define GIT_COLOR_BOLD_YELLOW  "\033[1;33m"
+#define GIT_COLOR_BOLD_BLUE    "\033[1;34m"
+#define GIT_COLOR_BOLD_MAGENTA "\033[1;35m"
+#define GIT_COLOR_BOLD_CYAN    "\033[1;36m"
 #define GIT_COLOR_BG_RED       "\033[41m"
+#define GIT_COLOR_BG_GREEN     "\033[42m"
+#define GIT_COLOR_BG_YELLOW    "\033[43m"
+#define GIT_COLOR_BG_BLUE      "\033[44m"
+#define GIT_COLOR_BG_MAGENTA   "\033[45m"
+#define GIT_COLOR_BG_CYAN      "\033[46m"
 
 /*
  * This variable stores the value of color.ui
index ab65f77ab99500d99d24a9b7266669f37bb02cb2..c5bfb39b3949430cd06093afdbc85ecc22f8c9c7 100644 (file)
@@ -140,6 +140,22 @@ int mingw_open (const char *filename, int oflags, ...)
        return fd;
 }
 
+#undef fopen
+FILE *mingw_fopen (const char *filename, const char *otype)
+{
+       if (!strcmp(filename, "/dev/null"))
+               filename = "nul";
+       return fopen(filename, otype);
+}
+
+#undef freopen
+FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
+{
+       if (filename && !strcmp(filename, "/dev/null"))
+               filename = "nul";
+       return freopen(filename, otype, stream);
+}
+
 /*
  * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
  * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
index e254fb4e068c3248a1aac33d70e40b620cd91088..e81e752ed2716ac9fb4a6847f97db84764694b4c 100644 (file)
@@ -170,6 +170,12 @@ int link(const char *oldpath, const char *newpath);
 int mingw_open (const char *filename, int oflags, ...);
 #define open mingw_open
 
+FILE *mingw_fopen (const char *filename, const char *otype);
+#define fopen mingw_fopen
+
+FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream);
+#define freopen mingw_freopen
+
 char *mingw_getcwd(char *pointer, int len);
 #define getcwd mingw_getcwd
 
index 34d4b49818b0896b9db19b2b1387f142cbbbd42b..11361195925c674423309d40f343c88f58b7bc1e 100644 (file)
@@ -2,7 +2,7 @@
 
 char *gitmkdtemp(char *template)
 {
-       if (!mktemp(template) || mkdir(template, 0700))
+       if (!*mktemp(template) || mkdir(template, 0700))
                return NULL;
        return template;
 }
diff --git a/compat/mkstemps.c b/compat/mkstemps.c
deleted file mode 100644 (file)
index 14179c8..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "../git-compat-util.h"
-
-/* Adapted from libiberty's mkstemp.c. */
-
-#undef TMP_MAX
-#define TMP_MAX 16384
-
-int gitmkstemps(char *pattern, int suffix_len)
-{
-       static const char letters[] =
-               "abcdefghijklmnopqrstuvwxyz"
-               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-               "0123456789";
-       static const int num_letters = 62;
-       uint64_t value;
-       struct timeval tv;
-       char *template;
-       size_t len;
-       int fd, count;
-
-       len = strlen(pattern);
-
-       if (len < 6 + suffix_len) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       /*
-        * Replace pattern's XXXXXX characters with randomness.
-        * Try TMP_MAX different filenames.
-        */
-       gettimeofday(&tv, NULL);
-       value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
-       template = &pattern[len - 6 - suffix_len];
-       for (count = 0; count < TMP_MAX; ++count) {
-               uint64_t v = value;
-               /* Fill in the random bits. */
-               template[0] = letters[v % num_letters]; v /= num_letters;
-               template[1] = letters[v % num_letters]; v /= num_letters;
-               template[2] = letters[v % num_letters]; v /= num_letters;
-               template[3] = letters[v % num_letters]; v /= num_letters;
-               template[4] = letters[v % num_letters]; v /= num_letters;
-               template[5] = letters[v % num_letters]; v /= num_letters;
-
-               fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, 0600);
-               if (fd > 0)
-                       return fd;
-               /*
-                * Fatal error (EPERM, ENOSPC etc).
-                * It doesn't make sense to loop.
-                */
-               if (errno != EEXIST)
-                       break;
-               /*
-                * This is a random value.  It is only necessary that
-                * the next TMP_MAX values generated by adding 7777 to
-                * VALUE are different with (module 2^32).
-                */
-               value += 7777;
-       }
-       /* We return the null string if we can't find a unique file name.  */
-       pattern[0] = '\0';
-       errno = EINVAL;
-       return -1;
-}
index 5fc1670bee94880b717df2e513a4d15678383ee0..0f949fc4250b5de4a3545931506fc48373ad8c6b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
  *
- * DISCLAMER: The implementation is Git-specific, it is subset of original
+ * DISCLAIMER: The implementation is Git-specific, it is subset of original
  * Pthreads API, without lots of other features that Git doesn't use.
  * Git also makes sure that the passed arguments are valid, so there's
  * no need for double-checking.
index 20054e4d0fd4cf94288593726be179d07d19271c..9ae991ac42544716599ff8bf3ebaaa376c8119e4 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -152,6 +152,28 @@ static enum protocol get_protocol(const char *name)
 #define STR_(s)        # s
 #define STR(s) STR_(s)
 
+static void get_host_and_port(char **host, const char **port)
+{
+       char *colon, *end;
+
+       if (*host[0] == '[') {
+               end = strchr(*host + 1, ']');
+               if (end) {
+                       *end = 0;
+                       end++;
+                       (*host)++;
+               } else
+                       end = *host;
+       } else
+               end = *host;
+       colon = strchr(end, ':');
+
+       if (colon) {
+               *colon = 0;
+               *port = colon + 1;
+       }
+}
+
 #ifndef NO_IPV6
 
 static const char *ai_name(const struct addrinfo *ai)
@@ -170,30 +192,14 @@ static const char *ai_name(const struct addrinfo *ai)
 static int git_tcp_connect_sock(char *host, int flags)
 {
        int sockfd = -1, saved_errno = 0;
-       char *colon, *end;
        const char *port = STR(DEFAULT_GIT_PORT);
        struct addrinfo hints, *ai0, *ai;
        int gai;
        int cnt = 0;
 
-       if (host[0] == '[') {
-               end = strchr(host + 1, ']');
-               if (end) {
-                       *end = 0;
-                       end++;
-                       host++;
-               } else
-                       end = host;
-       } else
-               end = host;
-       colon = strchr(end, ':');
-
-       if (colon) {
-               *colon = 0;
-               port = colon + 1;
-               if (!*port)
-                       port = "<none>";
-       }
+       get_host_and_port(&host, &port);
+       if (!*port)
+               port = "<none>";
 
        memset(&hints, 0, sizeof(hints));
        hints.ai_socktype = SOCK_STREAM;
@@ -251,30 +257,15 @@ static int git_tcp_connect_sock(char *host, int flags)
 static int git_tcp_connect_sock(char *host, int flags)
 {
        int sockfd = -1, saved_errno = 0;
-       char *colon, *end;
-       char *port = STR(DEFAULT_GIT_PORT), *ep;
+       const char *port = STR(DEFAULT_GIT_PORT);
+       char *ep;
        struct hostent *he;
        struct sockaddr_in sa;
        char **ap;
        unsigned int nport;
        int cnt;
 
-       if (host[0] == '[') {
-               end = strchr(host + 1, ']');
-               if (end) {
-                       *end = 0;
-                       end++;
-                       host++;
-               } else
-                       end = host;
-       } else
-               end = host;
-       colon = strchr(end, ':');
-
-       if (colon) {
-               *colon = 0;
-               port = colon + 1;
-       }
+       get_host_and_port(&host, &port);
 
        if (flags & CONNECT_VERBOSE)
                fprintf(stderr, "Looking up %s ... ", host);
@@ -406,26 +397,10 @@ static int git_use_proxy(const char *host)
 static void git_proxy_connect(int fd[2], char *host)
 {
        const char *port = STR(DEFAULT_GIT_PORT);
-       char *colon, *end;
        const char *argv[4];
        struct child_process proxy;
 
-       if (host[0] == '[') {
-               end = strchr(host + 1, ']');
-               if (end) {
-                       *end = 0;
-                       end++;
-                       host++;
-               } else
-                       end = host;
-       } else
-               end = host;
-       colon = strchr(end, ':');
-
-       if (colon) {
-               *colon = 0;
-               port = colon + 1;
-       }
+       get_host_and_port(&host, &port);
 
        argv[0] = git_proxy_command;
        argv[1] = host;
@@ -504,7 +479,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
 
        /*
         * Don't do destructive transforms with git:// as that
-        * protocol code does '[]' dewrapping of its own.
+        * protocol code does '[]' unwrapping of its own.
         */
        if (host[0] == '[') {
                end = strchr(host + 1, ']');
@@ -607,18 +582,8 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                *arg++ = host;
        }
        else {
-               /* remove these from the environment */
-               const char *env[] = {
-                       ALTERNATE_DB_ENVIRONMENT,
-                       DB_ENVIRONMENT,
-                       GIT_DIR_ENVIRONMENT,
-                       GIT_WORK_TREE_ENVIRONMENT,
-                       GRAFT_ENVIRONMENT,
-                       INDEX_ENVIRONMENT,
-                       NO_REPLACE_OBJECTS_ENVIRONMENT,
-                       NULL
-               };
-               conn->env = env;
+               /* remove repo-local variables from the environment */
+               conn->env = local_repo_env;
                conn->use_shell = 1;
        }
        *arg++ = cmd.buf;
@@ -647,3 +612,40 @@ int finish_connect(struct child_process *conn)
        free(conn);
        return code;
 }
+
+char *git_getpass(const char *prompt)
+{
+       char *askpass;
+       struct child_process pass;
+       const char *args[3];
+       static struct strbuf buffer = STRBUF_INIT;
+
+       askpass = getenv("GIT_ASKPASS");
+
+       if (!askpass || !(*askpass))
+               return getpass(prompt);
+
+       args[0] = askpass;
+       args[1] = prompt;
+       args[2] = NULL;
+
+       memset(&pass, 0, sizeof(pass));
+       pass.argv = args;
+       pass.out = -1;
+
+       if (start_command(&pass))
+               exit(1);
+
+       strbuf_reset(&buffer);
+       if (strbuf_read(&buffer, pass.out, 20) < 0)
+               die("failed to read password from %s\n", askpass);
+
+       close(pass.out);
+
+       if (finish_command(&pass))
+               exit(1);
+
+       strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
+
+       return buffer.buf;
+}
index fe93747c93a7b65f4657b56ef3962d64b48e3eb7..733ac39a32f6fd0b4f1546d429ac55c4ad6699b6 100755 (executable)
@@ -250,7 +250,9 @@ __git_refs ()
                        refs="${cur%/*}"
                        ;;
                *)
-                       if [ -e "$dir/HEAD" ]; then echo HEAD; fi
+                       for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
+                               if [ -e "$dir/$i" ]; then echo $i; fi
+                       done
                        format="refname:short"
                        refs="refs/tags refs/heads refs/remotes"
                        ;;
similarity index 100%
rename from git-notes.sh
rename to contrib/examples/git-notes.sh
index e7c48144e62a0829a5f817c5debaee28516b351d..c1ea643ace920e83a5577948553d4c9d1046abec 100755 (executable)
@@ -802,7 +802,7 @@ class P4Submit(Command):
         self.oldWorkingDirectory = os.getcwd()
 
         chdir(self.clientPath)
-        print "Syncronizing p4 checkout..."
+        print "Synchronizing p4 checkout..."
         p4_system("sync ...")
 
         self.check()
@@ -967,9 +967,8 @@ class P4Sync(Command):
         elif file["type"] == "symlink":
             mode = "120000"
             # p4 print on a symlink contains "target\n", so strip it off
-            last = contents.pop()
-            last = last[:-1]
-            contents.append(last)
+            data = ''.join(contents)
+            contents = [data[:-1]]
 
         if self.isWindows and file["type"].endswith("text"):
             mangled = []
index 5782d80e2683cc6dce84c4a88e1052e0e2a04120..3a5da4ab00f2e29c3244611f40d776293e796e7d 100755 (executable)
@@ -344,7 +344,7 @@ sub parsekeyvaluepair
 
 Key and value strings may be enclosed in quotes, in which case
 whitespace inside the quotes is preserved. Additionally, an equal
-sign may be included in the key by preceeding it with a backslash.
+sign may be included in the key by preceding it with a backslash.
 For example:
 
  "key1 "=value1
index 27acce58bc4bec60a394f03db1f6e60e1e4cfc3e..4f8fcb7bbb00b66f1eaa354119784e6ef57e1eb4 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -241,7 +241,7 @@ struct filter_params {
        const char *cmd;
 };
 
-static int filter_buffer(int fd, void *data)
+static int filter_buffer(int in, int out, void *data)
 {
        /*
         * Spawn cmd and feed the buffer contents through its stdin.
@@ -255,7 +255,7 @@ static int filter_buffer(int fd, void *data)
        child_process.argv = argv;
        child_process.use_shell = 1;
        child_process.in = -1;
-       child_process.out = fd;
+       child_process.out = out;
 
        if (start_command(&child_process))
                return error("cannot fork to run external filter %s", params->cmd);
@@ -292,6 +292,7 @@ static int apply_filter(const char *path, const char *src, size_t len,
        memset(&async, 0, sizeof(async));
        async.proc = filter_buffer;
        async.data = &params;
+       async.out = -1;
        params.src = src;
        params.size = len;
        params.cmd = cmd;
index 6c2bd977131752e05d3ac545af0d977d6d7ca672..7d9e1c03e88d7785f4ff0d534563ba5778fbc244 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -407,7 +407,7 @@ static void parse_host_and_port(char *hostport, char **host,
 
                end = strchr(hostport, ']');
                if (!end)
-                       die("Invalid reqeuest ('[' without ']')");
+                       die("Invalid request ('[' without ']')");
                *end = '\0';
                *host = hostport + 1;
                if (!end[1])
@@ -420,7 +420,7 @@ static void parse_host_and_port(char *hostport, char **host,
                *host = hostport;
                *port = strrchr(hostport, ':');
                if (*port) {
-                       *port = '\0';
+                       **port = '\0';
                        ++*port;
                }
        }
index d7e13cb177a3c345eb076a9ffede87c6e6afa367..c9f6e05badf7b752188dcb5fa28a9bef53521dee 100644 (file)
@@ -55,6 +55,27 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
        return 0;
 }
 
+/*
+ * Has a file changed or has a submodule new commits or a dirty work tree?
+ *
+ * Return 1 when changes are detected, 0 otherwise. If the DIRTY_SUBMODULES
+ * option is set, the caller does not only want to know if a submodule is
+ * modified at all but wants to know all the conditions that are met (new
+ * commits, untracked content and/or modified content).
+ */
+static int match_stat_with_submodule(struct diff_options *diffopt,
+                                     struct cache_entry *ce, struct stat *st,
+                                     unsigned ce_option, unsigned *dirty_submodule)
+{
+       int changed = ce_match_stat(ce, st, ce_option);
+       if (S_ISGITLINK(ce->ce_mode)
+           && !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)
+           && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) {
+               *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+       }
+       return changed;
+}
+
 int run_diff_files(struct rev_info *revs, unsigned int option)
 {
        int entries, i;
@@ -177,15 +198,9 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                                       ce->sha1, ce->name, 0);
                        continue;
                }
-               changed = ce_match_stat(ce, &st, ce_option);
-               if (S_ISGITLINK(ce->ce_mode)
-                   && !DIFF_OPT_TST(&revs->diffopt, IGNORE_SUBMODULES)
-                   && (!changed || (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
-                   && is_submodule_modified(ce->name)) {
-                       changed = 1;
-                       dirty_submodule = 1;
-               }
-               if (!changed) {
+               changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
+                                                   ce_option, &dirty_submodule);
+               if (!changed && !dirty_submodule) {
                        ce_mark_uptodate(ce);
                        if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
                                continue;
@@ -240,14 +255,8 @@ static int get_stat_data(struct cache_entry *ce,
                        }
                        return -1;
                }
-               changed = ce_match_stat(ce, &st, 0);
-               if (S_ISGITLINK(ce->ce_mode)
-                   && !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)
-                   && (!changed || (diffopt->output_format & DIFF_FORMAT_PATCH))
-                   && is_submodule_modified(ce->name)) {
-                       changed = 1;
-                       *dirty_submodule = 1;
-               }
+               changed = match_stat_with_submodule(diffopt, ce, &st,
+                                                   0, dirty_submodule);
                if (changed) {
                        mode = ce_mode_from_stat(ce, st.st_mode);
                        sha1 = null_sha1;
@@ -322,7 +331,7 @@ static int show_modified(struct rev_info *revs,
        }
 
        oldmode = old->ce_mode;
-       if (mode == oldmode && !hashcmp(sha1, old->sha1) &&
+       if (mode == oldmode && !hashcmp(sha1, old->sha1) && !dirty_submodule &&
            !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
                return 0;
 
@@ -510,9 +519,12 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
 int index_differs_from(const char *def, int diff_flags)
 {
        struct rev_info rev;
+       struct setup_revision_opt opt;
 
        init_revisions(&rev, NULL);
-       setup_revisions(0, NULL, &rev, def);
+       memset(&opt, 0, sizeof(opt));
+       opt.def = def;
+       setup_revisions(0, NULL, &rev, &opt);
        DIFF_OPT_SET(&rev.diffopt, QUICK);
        DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
        rev.diffopt.flags |= diff_flags;
diff --git a/diff.c b/diff.c
index 381cc8d4fd69ca31fb8fc8af31422160e3ec1fd3..f5d93e9694d2f5e78bfa3d8dc886968d7cdd5867 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2032,7 +2032,7 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
        char *data = xmalloc(100), *dirty = "";
 
        /* Are we looking at the work tree? */
-       if (!s->sha1_valid && s->dirty_submodule)
+       if (s->dirty_submodule)
                dirty = "-dirty";
 
        len = snprintf(data, 100,
@@ -2628,6 +2628,12 @@ int diff_setup_done(struct diff_options *options)
         */
        if (options->pickaxe)
                DIFF_OPT_SET(options, RECURSIVE);
+       /*
+        * When patches are generated, submodules diffed against the work tree
+        * must be checked for dirtiness too so it can be shown in the output
+        */
+       if (options->output_format & DIFF_FORMAT_PATCH)
+               DIFF_OPT_SET(options, DIRTY_SUBMODULES);
 
        if (options->detect_rename && options->rename_limit < 0)
                options->rename_limit = diff_rename_limit_default;
@@ -2826,6 +2832,15 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_SET(options, FOLLOW_RENAMES);
        else if (!strcmp(arg, "--color"))
                DIFF_OPT_SET(options, COLOR_DIFF);
+       else if (!prefixcmp(arg, "--color=")) {
+               int value = git_config_colorbool(NULL, arg+8, -1);
+               if (value == 0)
+                       DIFF_OPT_CLR(options, COLOR_DIFF);
+               else if (value > 0)
+                       DIFF_OPT_SET(options, COLOR_DIFF);
+               else
+                       return error("option `color' expects \"always\", \"auto\", or \"never\"");
+       }
        else if (!strcmp(arg, "--no-color"))
                DIFF_OPT_CLR(options, COLOR_DIFF);
        else if (!strcmp(arg, "--color-words")) {
@@ -2893,6 +2908,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                ;
        else if (!prefixcmp(arg, "--output=")) {
                options->file = fopen(arg + strlen("--output="), "w");
+               if (!options->file)
+                       die_errno("Could not open '%s'", arg + strlen("--output="));
                options->close_file = 1;
        } else
                return 0;
@@ -3075,7 +3092,8 @@ int diff_unmodified_pair(struct diff_filepair *p)
         * dealing with a change.
         */
        if (one->sha1_valid && two->sha1_valid &&
-           !hashcmp(one->sha1, two->sha1))
+           !hashcmp(one->sha1, two->sha1) &&
+           !one->dirty_submodule && !two->dirty_submodule)
                return 1; /* no change */
        if (!one->sha1_valid && !two->sha1_valid)
                return 1; /* both look at the same file on the filesystem. */
@@ -3210,6 +3228,8 @@ static void diff_resolve_rename_copy(void)
                }
                else if (hashcmp(p->one->sha1, p->two->sha1) ||
                         p->one->mode != p->two->mode ||
+                        p->one->dirty_submodule ||
+                        p->two->dirty_submodule ||
                         is_null_sha1(p->one->sha1))
                        p->status = DIFF_STATUS_MODIFIED;
                else {
@@ -3520,6 +3540,29 @@ void diff_flush(struct diff_options *options)
                separator++;
        }
 
+       if (output_format & DIFF_FORMAT_NO_OUTPUT &&
+           DIFF_OPT_TST(options, EXIT_WITH_STATUS) &&
+           DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
+               /*
+                * run diff_flush_patch for the exit status. setting
+                * options->file to /dev/null should be safe, becaue we
+                * aren't supposed to produce any output anyway.
+                */
+               if (options->close_file)
+                       fclose(options->file);
+               options->file = fopen("/dev/null", "w");
+               if (!options->file)
+                       die_errno("Could not open /dev/null");
+               options->close_file = 1;
+               for (i = 0; i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       if (check_pair_status(p))
+                               diff_flush_patch(p, options);
+                       if (options->found_changes)
+                               break;
+               }
+       }
+
        if (output_format & DIFF_FORMAT_PATCH) {
                if (separator) {
                        putc(options->line_termination, options->file);
@@ -3642,7 +3685,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
                struct diff_filepair *p = q->queue[i];
 
                /*
-                * 1. Entries that come from stat info dirtyness
+                * 1. Entries that come from stat info dirtiness
                 *    always have both sides (iow, not create/delete),
                 *    one side of the object name is unknown, with
                 *    the same mode and size.  Keep the ones that
diff --git a/diff.h b/diff.h
index 2ef3341fb0852fc8958fa5c5eacab69ee68c0ad9..6a71013dc63fc0912fd4f3d27f70ae909917f1f6 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -69,6 +69,8 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
 #define DIFF_OPT_ALLOW_TEXTCONV      (1 << 21)
 #define DIFF_OPT_DIFF_FROM_CONTENTS  (1 << 22)
 #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_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
index 66687c3fe5ea4552cff2b864b73696460ca40b1e..fcd00bf27aee4e1f2823f05ab3dba1d3c70a509d 100644 (file)
@@ -42,7 +42,9 @@ struct diff_filespec {
 #define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
        unsigned should_free : 1; /* data should be free()'ed */
        unsigned should_munmap : 1; /* data should be munmap()'ed */
-       unsigned dirty_submodule : 1;  /* For submodules: its work tree is dirty */
+       unsigned dirty_submodule : 2;  /* For submodules: its work tree is dirty */
+#define DIRTY_SUBMODULE_UNTRACKED 1
+#define DIRTY_SUBMODULE_MODIFIED  2
 
        struct userdiff_driver *driver;
        /* data should be considered "binary"; -1 means "don't know yet" */
diff --git a/dir.c b/dir.c
index 67c3af6a1a91e2acaa873587d6df5318d2fb9ba8..cb83332a261f97026f9c6273afb162245944dec7 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -594,13 +594,29 @@ static int simplify_away(const char *path, int pathlen, const struct path_simpli
        return 0;
 }
 
-static int in_pathspec(const char *path, int len, const struct path_simplify *simplify)
+/*
+ * This function tells us whether an excluded path matches a
+ * list of "interesting" pathspecs. That is, whether a path matched
+ * by any of the pathspecs could possibly be ignored by excluding
+ * the specified path. This can happen if:
+ *
+ *   1. the path is mentioned explicitly in the pathspec
+ *
+ *   2. the path is a directory prefix of some element in the
+ *      pathspec
+ */
+static int exclude_matches_pathspec(const char *path, int len,
+               const struct path_simplify *simplify)
 {
        if (simplify) {
                for (; simplify->path; simplify++) {
                        if (len == simplify->len
                            && !memcmp(path, simplify->path, len))
                                return 1;
+                       if (len < simplify->len
+                           && simplify->path[len] == '/'
+                           && !memcmp(path, simplify->path, len))
+                               return 1;
                }
        }
        return 0;
@@ -678,7 +694,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
 {
        int exclude = excluded(dir, path, &dtype);
        if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
-           && in_pathspec(path, *len, simplify))
+           && exclude_matches_pathspec(path, *len, simplify))
                dir_add_ignored(dir, path, *len);
 
        /*
@@ -1044,7 +1060,7 @@ int remove_path(const char *name)
                slash = dirs + (slash - name);
                do {
                        *slash = '\0';
-               } while (rmdir(dirs) && (slash = strrchr(dirs, '/')));
+               } while (rmdir(dirs) == 0 && (slash = strrchr(dirs, '/')));
                free(dirs);
        }
        return 0;
index 739ec2704031f0dd9d4a380e07f001c09742a7be..876c5e53410250b5f2744671e39907c234771af3 100644 (file)
@@ -63,6 +63,23 @@ static char *work_tree;
 static const char *git_dir;
 static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
 
+/*
+ * Repository-local GIT_* environment variables
+ * Remember to update local_repo_env_size in cache.h when
+ * the size of the list changes
+ */
+const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {
+       ALTERNATE_DB_ENVIRONMENT,
+       CONFIG_ENVIRONMENT,
+       DB_ENVIRONMENT,
+       GIT_DIR_ENVIRONMENT,
+       GIT_WORK_TREE_ENVIRONMENT,
+       GRAFT_ENVIRONMENT,
+       INDEX_ENVIRONMENT,
+       NO_REPLACE_OBJECTS_ENVIRONMENT,
+       NULL
+};
+
 static void setup_git_env(void)
 {
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
index 408e4e55e1c58931444c772d35d23b505bf3e2ea..b2c07c70ce26312d81f499d3acf90dab919f1d24 100644 (file)
@@ -28,7 +28,7 @@ const char *system_path(const char *path)
            !(prefix = strip_path_suffix(argv0_path, BINDIR)) &&
            !(prefix = strip_path_suffix(argv0_path, "git"))) {
                prefix = PREFIX;
-               fprintf(stderr, "RUNTIME_PREFIX requested, "
+               trace_printf("RUNTIME_PREFIX requested, "
                                "but prefix computation failed.  "
                                "Using static fallback '%s'.\n", prefix);
        }
index b477dc6a8f8104cecf21b68375b7c5cf7ab8842c..309f2c58a2ba431a82a56f757482a5093cc8257d 100644 (file)
@@ -164,12 +164,11 @@ Format of STDIN stream:
 
 struct object_entry
 {
+       struct pack_idx_entry idx;
        struct object_entry *next;
-       uint32_t offset;
        uint32_t type : TYPE_BITS,
                pack_id : PACK_ID_BITS,
                depth : DEPTH_BITS;
-       unsigned char sha1[20];
 };
 
 struct object_entry_pool
@@ -192,7 +191,7 @@ struct mark_set
 struct last_object
 {
        struct strbuf data;
-       uint32_t offset;
+       off_t offset;
        unsigned int depth;
        unsigned no_swap : 1;
 };
@@ -280,7 +279,7 @@ struct recent_command
 
 /* Configured limits on output */
 static unsigned long max_depth = 10;
-static off_t max_packsize = (1LL << 32) - 1;
+static off_t max_packsize;
 static uintmax_t big_file_threshold = 512 * 1024 * 1024;
 static int force_update;
 static int pack_compression_level = Z_DEFAULT_COMPRESSION;
@@ -313,9 +312,10 @@ static struct atom_str **atom_table;
 
 /* The .pack file being generated */
 static unsigned int pack_id;
+static struct sha1file *pack_file;
 static struct packed_git *pack_data;
 static struct packed_git **all_packs;
-static unsigned long pack_size;
+static off_t pack_size;
 
 /* Table of objects we've written. */
 static unsigned int object_entry_alloc = 5000;
@@ -521,7 +521,7 @@ static struct object_entry *new_object(unsigned char *sha1)
                alloc_objects(object_entry_alloc);
 
        e = blocks->next_free++;
-       hashcpy(e->sha1, sha1);
+       hashcpy(e->idx.sha1, sha1);
        return e;
 }
 
@@ -530,7 +530,7 @@ static struct object_entry *find_object(unsigned char *sha1)
        unsigned int h = sha1[0] << 8 | sha1[1];
        struct object_entry *e;
        for (e = object_table[h]; e; e = e->next)
-               if (!hashcmp(sha1, e->sha1))
+               if (!hashcmp(sha1, e->idx.sha1))
                        return e;
        return NULL;
 }
@@ -542,7 +542,7 @@ static struct object_entry *insert_object(unsigned char *sha1)
        struct object_entry *p = NULL;
 
        while (e) {
-               if (!hashcmp(sha1, e->sha1))
+               if (!hashcmp(sha1, e->idx.sha1))
                        return e;
                p = e;
                e = e->next;
@@ -550,7 +550,7 @@ static struct object_entry *insert_object(unsigned char *sha1)
 
        e = new_object(sha1);
        e->next = NULL;
-       e->offset = 0;
+       e->idx.offset = 0;
        if (p)
                p->next = e;
        else
@@ -839,11 +839,12 @@ static void start_packfile(void)
        p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
        strcpy(p->pack_name, tmpfile);
        p->pack_fd = pack_fd;
+       pack_file = sha1fd(pack_fd, p->pack_name);
 
        hdr.hdr_signature = htonl(PACK_SIGNATURE);
        hdr.hdr_version = htonl(2);
        hdr.hdr_entries = 0;
-       write_or_die(p->pack_fd, &hdr, sizeof(hdr));
+       sha1write(pack_file, &hdr, sizeof(hdr));
 
        pack_data = p;
        pack_size = sizeof(hdr);
@@ -853,67 +854,30 @@ static void start_packfile(void)
        all_packs[pack_id] = p;
 }
 
-static int oecmp (const void *a_, const void *b_)
-{
-       struct object_entry *a = *((struct object_entry**)a_);
-       struct object_entry *b = *((struct object_entry**)b_);
-       return hashcmp(a->sha1, b->sha1);
-}
-
-static char *create_index(void)
+static const char *create_index(void)
 {
-       static char tmpfile[PATH_MAX];
-       git_SHA_CTX ctx;
-       struct sha1file *f;
-       struct object_entry **idx, **c, **last, *e;
+       const char *tmpfile;
+       struct pack_idx_entry **idx, **c, **last;
+       struct object_entry *e;
        struct object_entry_pool *o;
-       uint32_t array[256];
-       int i, idx_fd;
 
-       /* Build the sorted table of object IDs. */
-       idx = xmalloc(object_count * sizeof(struct object_entry*));
+       /* Build the table of object IDs. */
+       idx = xmalloc(object_count * sizeof(*idx));
        c = idx;
        for (o = blocks; o; o = o->next_pool)
                for (e = o->next_free; e-- != o->entries;)
                        if (pack_id == e->pack_id)
-                               *c++ = e;
+                               *c++ = &e->idx;
        last = idx + object_count;
        if (c != last)
                die("internal consistency error creating the index");
-       qsort(idx, object_count, sizeof(struct object_entry*), oecmp);
 
-       /* Generate the fan-out array. */
-       c = idx;
-       for (i = 0; i < 256; i++) {
-               struct object_entry **next = c;
-               while (next < last) {
-                       if ((*next)->sha1[0] != i)
-                               break;
-                       next++;
-               }
-               array[i] = htonl(next - idx);
-               c = next;
-       }
-
-       idx_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
-                            "pack/tmp_idx_XXXXXX");
-       f = sha1fd(idx_fd, tmpfile);
-       sha1write(f, array, 256 * sizeof(int));
-       git_SHA1_Init(&ctx);
-       for (c = idx; c != last; c++) {
-               uint32_t offset = htonl((*c)->offset);
-               sha1write(f, &offset, 4);
-               sha1write(f, (*c)->sha1, sizeof((*c)->sha1));
-               git_SHA1_Update(&ctx, (*c)->sha1, 20);
-       }
-       sha1write(f, pack_data->sha1, sizeof(pack_data->sha1));
-       sha1close(f, NULL, CSUM_FSYNC);
+       tmpfile = write_idx_file(NULL, idx, object_count, pack_data->sha1);
        free(idx);
-       git_SHA1_Final(pack_data->sha1, &ctx);
        return tmpfile;
 }
 
-static char *keep_pack(char *curr_index_name)
+static char *keep_pack(const char *curr_index_name)
 {
        static char name[PATH_MAX];
        static const char *keep_msg = "fast-import";
@@ -935,6 +899,7 @@ static char *keep_pack(char *curr_index_name)
                 get_object_directory(), sha1_to_hex(pack_data->sha1));
        if (move_temp_to_file(curr_index_name, name))
                die("cannot store index file");
+       free((void *)curr_index_name);
        return name;
 }
 
@@ -957,15 +922,17 @@ static void end_packfile(void)
 
        clear_delta_base_cache();
        if (object_count) {
+               unsigned char cur_pack_sha1[20];
                char *idx_name;
                int i;
                struct branch *b;
                struct tag *t;
 
                close_pack_windows(pack_data);
+               sha1close(pack_file, cur_pack_sha1, 0);
                fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
                                    pack_data->pack_name, object_count,
-                                   NULL, 0);
+                                   cur_pack_sha1, pack_size);
                close(pack_data->pack_fd);
                idx_name = keep_pack(create_index());
 
@@ -1013,29 +980,6 @@ static void cycle_packfile(void)
        start_packfile();
 }
 
-static size_t encode_header(
-       enum object_type type,
-       uintmax_t size,
-       unsigned char *hdr)
-{
-       int n = 1;
-       unsigned char c;
-
-       if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
-               die("bad type %d", type);
-
-       c = (type << 4) | (size & 15);
-       size >>= 4;
-       while (size) {
-               *hdr++ = c | 0x80;
-               c = size & 0x7f;
-               size >>= 7;
-               n++;
-       }
-       *hdr = c;
-       return n;
-}
-
 static int store_object(
        enum object_type type,
        struct strbuf *dat,
@@ -1063,25 +1007,21 @@ static int store_object(
        e = insert_object(sha1);
        if (mark)
                insert_mark(mark, e);
-       if (e->offset) {
+       if (e->idx.offset) {
                duplicate_count_by_type[type]++;
                return 1;
        } else if (find_sha1_pack(sha1, packed_git)) {
                e->type = type;
                e->pack_id = MAX_PACK_ID;
-               e->offset = 1; /* just not zero! */
+               e->idx.offset = 1; /* just not zero! */
                duplicate_count_by_type[type]++;
                return 1;
        }
 
-       if (last && last->data.buf && last->depth < max_depth) {
+       if (last && last->data.buf && last->depth < max_depth && dat->len > 20) {
                delta = diff_delta(last->data.buf, last->data.len,
                        dat->buf, dat->len,
-                       &deltalen, 0);
-               if (delta && deltalen >= dat->len) {
-                       free(delta);
-                       delta = NULL;
-               }
+                       &deltalen, dat->len - 20);
        } else
                delta = NULL;
 
@@ -1101,7 +1041,7 @@ static int store_object(
        deflateEnd(&s);
 
        /* Determine if we should auto-checkpoint. */
-       if ((pack_size + 60 + s.total_out) > max_packsize
+       if ((max_packsize && (pack_size + 60 + s.total_out) > max_packsize)
                || (pack_size + 60 + s.total_out) < pack_size) {
 
                /* This new object needs to *not* have the current pack_id. */
@@ -1127,36 +1067,40 @@ static int store_object(
 
        e->type = type;
        e->pack_id = pack_id;
-       e->offset = pack_size;
+       e->idx.offset = pack_size;
        object_count++;
        object_count_by_type[type]++;
 
+       crc32_begin(pack_file);
+
        if (delta) {
-               unsigned long ofs = e->offset - last->offset;
+               off_t ofs = e->idx.offset - last->offset;
                unsigned pos = sizeof(hdr) - 1;
 
                delta_count_by_type[type]++;
                e->depth = last->depth + 1;
 
-               hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr);
-               write_or_die(pack_data->pack_fd, hdr, hdrlen);
+               hdrlen = encode_in_pack_object_header(OBJ_OFS_DELTA, deltalen, hdr);
+               sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
 
                hdr[pos] = ofs & 127;
                while (ofs >>= 7)
                        hdr[--pos] = 128 | (--ofs & 127);
-               write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos);
+               sha1write(pack_file, hdr + pos, sizeof(hdr) - pos);
                pack_size += sizeof(hdr) - pos;
        } else {
                e->depth = 0;
-               hdrlen = encode_header(type, dat->len, hdr);
-               write_or_die(pack_data->pack_fd, hdr, hdrlen);
+               hdrlen = encode_in_pack_object_header(type, dat->len, hdr);
+               sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
        }
 
-       write_or_die(pack_data->pack_fd, out, s.total_out);
+       sha1write(pack_file, out, s.total_out);
        pack_size += s.total_out;
 
+       e->idx.crc32 = crc32_end(pack_file);
+
        free(out);
        free(delta);
        if (last) {
@@ -1165,18 +1109,23 @@ static int store_object(
                } else {
                        strbuf_swap(&last->data, dat);
                }
-               last->offset = e->offset;
+               last->offset = e->idx.offset;
                last->depth = e->depth;
        }
        return 0;
 }
 
-static void truncate_pack(off_t to)
+static void truncate_pack(off_t to, git_SHA_CTX *ctx)
 {
        if (ftruncate(pack_data->pack_fd, to)
         || lseek(pack_data->pack_fd, to, SEEK_SET) != to)
                die_errno("cannot truncate pack to skip duplicate");
        pack_size = to;
+
+       /* yes this is a layering violation */
+       pack_file->total = to;
+       pack_file->offset = 0;
+       pack_file->ctx = *ctx;
 }
 
 static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
@@ -1189,16 +1138,21 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        unsigned long hdrlen;
        off_t offset;
        git_SHA_CTX c;
+       git_SHA_CTX pack_file_ctx;
        z_stream s;
        int status = Z_OK;
 
        /* Determine if we should auto-checkpoint. */
-       if ((pack_size + 60 + len) > max_packsize
+       if ((max_packsize && (pack_size + 60 + len) > max_packsize)
                || (pack_size + 60 + len) < pack_size)
                cycle_packfile();
 
        offset = pack_size;
 
+       /* preserve the pack_file SHA1 ctx in case we have to truncate later */
+       sha1flush(pack_file);
+       pack_file_ctx = pack_file->ctx;
+
        hdrlen = snprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
        if (out_sz <= hdrlen)
                die("impossibly large object header");
@@ -1206,10 +1160,12 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        git_SHA1_Init(&c);
        git_SHA1_Update(&c, out_buf, hdrlen);
 
+       crc32_begin(pack_file);
+
        memset(&s, 0, sizeof(s));
        deflateInit(&s, pack_compression_level);
 
-       hdrlen = encode_header(OBJ_BLOB, len, out_buf);
+       hdrlen = encode_in_pack_object_header(OBJ_BLOB, len, out_buf);
        if (out_sz <= hdrlen)
                die("impossibly large object header");
 
@@ -1233,7 +1189,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
 
                if (!s.avail_out || status == Z_STREAM_END) {
                        size_t n = s.next_out - out_buf;
-                       write_or_die(pack_data->pack_fd, out_buf, n);
+                       sha1write(pack_file, out_buf, n);
                        pack_size += n;
                        s.next_out = out_buf;
                        s.avail_out = out_sz;
@@ -1259,22 +1215,23 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        if (mark)
                insert_mark(mark, e);
 
-       if (e->offset) {
+       if (e->idx.offset) {
                duplicate_count_by_type[OBJ_BLOB]++;
-               truncate_pack(offset);
+               truncate_pack(offset, &pack_file_ctx);
 
        } else if (find_sha1_pack(sha1, packed_git)) {
                e->type = OBJ_BLOB;
                e->pack_id = MAX_PACK_ID;
-               e->offset = 1; /* just not zero! */
+               e->idx.offset = 1; /* just not zero! */
                duplicate_count_by_type[OBJ_BLOB]++;
-               truncate_pack(offset);
+               truncate_pack(offset, &pack_file_ctx);
 
        } else {
                e->depth = 0;
                e->type = OBJ_BLOB;
                e->pack_id = pack_id;
-               e->offset = offset;
+               e->idx.offset = offset;
+               e->idx.crc32 = crc32_end(pack_file);
                object_count++;
                object_count_by_type[OBJ_BLOB]++;
        }
@@ -1317,6 +1274,7 @@ static void *gfi_unpack_entry(
                 * the newly written data.
                 */
                close_pack_windows(p);
+               sha1flush(pack_file);
 
                /* We have to offer 20 bytes additional on the end of
                 * the packfile as the core unpacker code assumes the
@@ -1326,7 +1284,7 @@ static void *gfi_unpack_entry(
                 */
                p->pack_size = pack_size + 20;
        }
-       return unpack_entry(p, oe->offset, &type, sizep);
+       return unpack_entry(p, oe->idx.offset, &type, sizep);
 }
 
 static const char *get_mode(const char *str, uint16_t *modep)
@@ -1457,7 +1415,7 @@ static void store_tree(struct tree_entry *root)
        if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) {
                mktree(t, 0, &old_tree);
                lo.data = old_tree;
-               lo.offset = le->offset;
+               lo.offset = le->idx.offset;
                lo.depth = t->delta_depth;
        }
 
@@ -1715,7 +1673,7 @@ static void dump_marks_helper(FILE *f,
                for (k = 0; k < 1024; k++) {
                        if (m->data.marked[k])
                                fprintf(f, ":%" PRIuMAX " %s\n", base + k,
-                                       sha1_to_hex(m->data.marked[k]->sha1));
+                                       sha1_to_hex(m->data.marked[k]->idx.sha1));
                }
        }
 }
@@ -1798,7 +1756,7 @@ static void read_marks(void)
                        e = insert_object(sha1);
                        e->type = type;
                        e->pack_id = MAX_PACK_ID;
-                       e->offset = 1; /* just not zero! */
+                       e->idx.offset = 1; /* just not zero! */
                }
                insert_mark(mark, e);
        }
@@ -2183,7 +2141,7 @@ static void file_change_m(struct branch *b)
        if (*p == ':') {
                char *x;
                oe = find_mark(strtoumax(p + 1, &x, 10));
-               hashcpy(sha1, oe->sha1);
+               hashcpy(sha1, oe->idx.sha1);
                p = x;
        } else if (!prefixcmp(p, "inline")) {
                inline_data = 1;
@@ -2316,7 +2274,7 @@ static void note_change_n(struct branch *b, unsigned char old_fanout)
        if (*p == ':') {
                char *x;
                oe = find_mark(strtoumax(p + 1, &x, 10));
-               hashcpy(sha1, oe->sha1);
+               hashcpy(sha1, oe->idx.sha1);
                p = x;
        } else if (!prefixcmp(p, "inline")) {
                inline_data = 1;
@@ -2339,7 +2297,7 @@ static void note_change_n(struct branch *b, unsigned char old_fanout)
                struct object_entry *commit_oe = find_mark(commit_mark);
                if (commit_oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", commit_mark);
-               hashcpy(commit_sha1, commit_oe->sha1);
+               hashcpy(commit_sha1, commit_oe->idx.sha1);
        } else if (!get_sha1(p, commit_sha1)) {
                unsigned long size;
                char *buf = read_object_with_reference(commit_sha1,
@@ -2446,7 +2404,7 @@ static int parse_from(struct branch *b)
                struct object_entry *oe = find_mark(idnum);
                if (oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", idnum);
-               hashcpy(b->sha1, oe->sha1);
+               hashcpy(b->sha1, oe->idx.sha1);
                if (oe->pack_id != MAX_PACK_ID) {
                        unsigned long size;
                        char *buf = gfi_unpack_entry(oe, &size);
@@ -2481,7 +2439,7 @@ static struct hash_list *parse_merge(unsigned int *count)
                        struct object_entry *oe = find_mark(idnum);
                        if (oe->type != OBJ_COMMIT)
                                die("Mark :%" PRIuMAX " not a commit", idnum);
-                       hashcpy(n->sha1, oe->sha1);
+                       hashcpy(n->sha1, oe->idx.sha1);
                } else if (!get_sha1(from, n->sha1)) {
                        unsigned long size;
                        char *buf = read_object_with_reference(n->sha1,
@@ -2639,7 +2597,7 @@ static void parse_new_tag(void)
                from_mark = strtoumax(from + 1, NULL, 10);
                oe = find_mark(from_mark);
                type = oe->type;
-               hashcpy(sha1, oe->sha1);
+               hashcpy(sha1, oe->idx.sha1);
        } else if (!get_sha1(from, sha1)) {
                unsigned long size;
                char *buf;
@@ -2891,6 +2849,17 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                pack_compression_seen = 1;
                return 0;
        }
+       if (!strcmp(k, "pack.indexversion")) {
+               pack_idx_default_version = git_config_int(k, v);
+               if (pack_idx_default_version > 2)
+                       die("bad pack.indexversion=%"PRIu32,
+                           pack_idx_default_version);
+               return 0;
+       }
+       if (!strcmp(k, "pack.packsizelimit")) {
+               max_packsize = git_config_ulong(k, v);
+               return 0;
+       }
        if (!strcmp(k, "core.bigfilethreshold")) {
                long n = git_config_int(k, v);
                big_file_threshold = 0 < n ? n : 0;
index cd43c3491260cb2aa51f0d19fd18ab66e4ad8217..21f1330a5bf26c955051ce9cf263289d90c7667b 100755 (executable)
@@ -957,6 +957,28 @@ sub coalesce_overlapping_hunks {
        return @out;
 }
 
+sub reassemble_patch {
+       my $head = shift;
+       my @patch;
+
+       # Include everything in the header except the beginning of the diff.
+       push @patch, (grep { !/^[-+]{3}/ } @$head);
+
+       # Then include any headers from the hunk lines, which must
+       # come before any actual hunk.
+       while (@_ && $_[0] !~ /^@/) {
+               push @patch, shift;
+       }
+
+       # Then begin the diff.
+       push @patch, grep { /^[-+]{3}/ } @$head;
+
+       # And then the actual hunks.
+       push @patch, @_;
+
+       return @patch;
+}
+
 sub color_diff {
        return map {
                colored((/^@/  ? $fraginfo_color :
@@ -1453,7 +1475,7 @@ sub patch_update_file {
 
        if (@result) {
                my $fh;
-               my @patch = (@{$head->{TEXT}}, @result);
+               my @patch = reassemble_patch($head->{TEXT}, @result);
                my $apply_routine = $patch_mode_flavour{APPLY};
                &$apply_routine(@patch);
                refresh();
index 3c08d53161faa72744ab64a1391d85cf9243f006..1056075545ad3e5e42626d167c91c1deada6186b 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -15,6 +15,8 @@ q,quiet         be quiet
 s,signoff       add a Signed-off-by line to the commit message
 u,utf8          recode into utf8 (default)
 k,keep          pass -k flag to git-mailinfo
+keep-cr         pass --keep-cr flag to git-mailsplit for mbox format
+no-keep-cr      do not pass --keep-cr flag to git-mailsplit independent of am.keepcr
 c,scissors      strip everything before a scissors line
 whitespace=     pass it through git-apply
 ignore-space-change pass it through git-apply
@@ -217,12 +219,12 @@ check_patch_format () {
 split_patches () {
        case "$patch_format" in
        mbox)
-               case "$rebasing" in
-               '')
-                       keep_cr= ;;
-               ?*)
-                       keep_cr=--keep-cr ;;
-               esac
+               if test -n "$rebasing" || test t = "$keepcr"
+               then
+                   keep_cr=--keep-cr
+               else
+                   keep_cr=
+               fi
                git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" ||
                clean_abort
                ;;
@@ -291,13 +293,18 @@ split_patches () {
 
 prec=4
 dotest="$GIT_DIR/rebase-apply"
-sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
+sign= utf8=t keep= keepcr= skip= interactive= resolved= rebasing= abort=
 resolvemsg= resume= scissors= no_inbody_headers=
 git_apply_opt=
 committer_date_is_author_date=
 ignore_date=
 allow_rerere_autoupdate=
 
+if test "$(git config --bool --get am.keepcr)" = true
+then
+    keepcr=t
+fi
+
 while test $# != 0
 do
        case "$1" in
@@ -348,6 +355,10 @@ do
                allow_rerere_autoupdate="$1" ;;
        -q|--quiet)
                GIT_QUIET=t ;;
+       --keep-cr)
+               keepcr=t ;;
+       --no-keep-cr)
+               keepcr=f ;;
        --)
                shift; break ;;
        *)
@@ -453,6 +464,7 @@ else
        echo "$sign" >"$dotest/sign"
        echo "$utf8" >"$dotest/utf8"
        echo "$keep" >"$dotest/keep"
+       echo "$keepcr" >"$dotest/keepcr"
        echo "$scissors" >"$dotest/scissors"
        echo "$no_inbody_headers" >"$dotest/no_inbody_headers"
        echo "$GIT_QUIET" >"$dotest/quiet"
@@ -496,6 +508,12 @@ if test "$(cat "$dotest/keep")" = t
 then
        keep=-k
 fi
+case "$(cat "$dotest/keepcr")" in
+t)
+       keepcr=--keep-cr ;;
+f)
+       keepcr=--no-keep-cr ;;
+esac
 case "$(cat "$dotest/scissors")" in
 t)
        scissors=--scissors ;;
@@ -575,6 +593,7 @@ do
                        echo "Patch is empty.  Was it split wrong?"
                        stop_here $this
                }
+               rm -f "$dotest/original-commit"
                if test -f "$dotest/rebasing" &&
                        commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
                                -e q "$dotest/$msgnum") &&
@@ -582,6 +601,7 @@ do
                then
                        git cat-file commit "$commit" |
                        sed -e '1,/^$/d' >"$dotest/msg-clean"
+                       echo "$commit" > "$dotest/original-commit"
                else
                        {
                                sed -n '/^Subject/ s/Subject: //p' "$dotest/info"
@@ -663,10 +683,7 @@ do
                [eE]*) git_editor "$dotest/final-commit"
                       action=again ;;
                [vV]*) action=again
-                      : ${GIT_PAGER=$(git var GIT_PAGER)}
-                      : ${LESS=-FRSX}
-                      export LESS
-                      $GIT_PAGER "$dotest/patch" ;;
+                      git_pager "$dotest/patch" ;;
                *)     action=again ;;
                esac
            done
@@ -768,6 +785,10 @@ do
        git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
        stop_here $this
 
+       if test -f "$dotest/original-commit"; then
+               echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
+       fi
+
        if test -x "$GIT_DIR"/hooks/post-applypatch
        then
                "$GIT_DIR"/hooks/post-applypatch
@@ -776,6 +797,12 @@ do
        go_next
 done
 
-git gc --auto
+if test -s "$dotest"/rewritten; then
+    git notes copy --for-rewrite=rebase < "$dotest"/rewritten
+    if test -x "$GIT_DIR"/hooks/post-rewrite; then
+       "$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
+    fi
+fi
 
 rm -fr "$dotest"
+git gc --auto
index 4853bf7a0d2d3cb99dee3f3fc371a48209c9efe2..9e03eee4586ca3b7476b56f66e9dcf6ffe3088cf 100755 (executable)
@@ -29,7 +29,7 @@ use IPC::Open2;
 $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
 
-our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r);
+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);
 my (%conv_author_name, %conv_author_email);
 
 sub usage(;$) {
@@ -40,7 +40,7 @@ Usage: git cvsimport     # fetch/update GIT from CVS
        [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
        [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
        [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
-       [-r remote] [CVS_module]
+       [-r remote] [-R] [CVS_module]
 END
        exit(1);
 }
@@ -110,7 +110,7 @@ sub read_repo_config {
        }
 }
 
-my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:";
+my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R";
 read_repo_config($opts);
 Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
 
@@ -659,6 +659,11 @@ if ($opt_A) {
        write_author_info("$git_dir/cvs-authors");
 }
 
+# open .git/cvs-revisions, if requested
+open my $revision_map, '>>', "$git_dir/cvs-revisions"
+    or die "Can't open $git_dir/cvs-revisions for appending: $!\n"
+       if defined $opt_R;
+
 
 #
 # run cvsps into a file unless we are getting
@@ -742,7 +747,7 @@ sub write_tree () {
 }
 
 my ($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
-my (@old,@new,@skipped,%ignorebranch);
+my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
 
 # commits that cvsps cannot place anywhere...
 $ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
@@ -825,6 +830,11 @@ sub commit {
        system('git' , 'update-ref', "$remote/$branch", $cid) == 0
                or die "Cannot write branch $branch for update: $!\n";
 
+       if ($revision_map) {
+               print $revision_map "@$_ $cid\n" for @commit_revisions;
+       }
+       @commit_revisions = ();
+
        if ($tag) {
                my ($xtag) = $tag;
                $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
@@ -959,6 +969,7 @@ while (<CVS>) {
                    push(@skipped, $fn);
                    next;
                }
+               push @commit_revisions, [$fn, $rev];
                print "Fetching $fn   v $rev\n" if $opt_v;
                my ($tmpname, $size) = $cvs->file($fn,$rev);
                if ($size == -1) {
@@ -981,7 +992,9 @@ while (<CVS>) {
                unlink($tmpname);
        } elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
                my $fn = $1;
+               my $rev = $2;
                $fn =~ s#^/+##;
+               push @commit_revisions, [$fn, $rev];
                push(@old,$fn);
                print "Delete $fn\n" if $opt_v;
        } elsif ($state == 9 and /^\s*$/) {
old mode 100755 (executable)
new mode 100644 (file)
index 38331a861106c63bf5f421dbe03f4aafe949812e..1a4729f7bb29205fb7bc251887dcd4f5237f1659 100755 (executable)
@@ -38,10 +38,10 @@ test -z "$(git ls-files -u)" || die_conflict
 test -f "$GIT_DIR/MERGE_HEAD" && die_merge
 
 strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
-log_arg= verbosity=
+log_arg= verbosity= progress=
 merge_args=
 curr_branch=$(git symbolic-ref -q HEAD)
-curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
+curr_branch_short="${curr_branch#refs/heads/}"
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
 while :
 do
@@ -50,6 +50,8 @@ do
                verbosity="$verbosity -q" ;;
        -v|--verbose)
                verbosity="$verbosity -v" ;;
+       --progress)
+               progress=--progress ;;
        -n|--no-stat|--no-summary)
                diffstat=--no-stat ;;
        --stat|--summary)
@@ -214,7 +216,7 @@ test true = "$rebase" && {
        done
 }
 orig_head=$(git rev-parse -q --verify HEAD)
-git fetch $verbosity --update-head-ok "$@" || exit 1
+git fetch $verbosity $progress --update-head-ok "$@" || exit 1
 
 curr_head=$(git rev-parse -q --verify HEAD)
 if test -n "$orig_head" && test "$curr_head" != "$orig_head"
index 3e4fd1456f1ebb4aabb61de6d7f13f820ae2abdc..415ae72dbc298d7723cdd23b1396c8a6d24dbe08 100755 (executable)
@@ -96,6 +96,13 @@ AUTHOR_SCRIPT="$DOTEST"/author-script
 # command is processed, this file is deleted.
 AMEND="$DOTEST"/amend
 
+# For the post-rewrite hook, we make a list of rewritten commits and
+# their new sha1s.  The rewritten-pending list keeps the sha1s of
+# commits that have been processed, but not committed yet,
+# e.g. because they are waiting for a 'squash' command.
+REWRITTEN_LIST="$DOTEST"/rewritten-list
+REWRITTEN_PENDING="$DOTEST"/rewritten-pending
+
 PRESERVE_MERGES=
 STRATEGY=
 ONTO=
@@ -198,6 +205,7 @@ make_patch () {
 }
 
 die_with_patch () {
+       echo "$1" > "$DOTEST"/stopped-sha
        make_patch "$1"
        git rerere
        die "$2"
@@ -348,6 +356,7 @@ pick_one_preserving_merges () {
                                printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
                                die_with_patch $sha1 "Error redoing merge $sha1"
                        fi
+                       echo "$sha1 $(git rev-parse HEAD^0)" >> "$REWRITTEN_LIST"
                        ;;
                *)
                        output git cherry-pick "$@" ||
@@ -425,6 +434,26 @@ die_failed_squash() {
        die_with_patch $1 ""
 }
 
+flush_rewritten_pending() {
+       test -s "$REWRITTEN_PENDING" || return
+       newsha1="$(git rev-parse HEAD^0)"
+       sed "s/$/ $newsha1/" < "$REWRITTEN_PENDING" >> "$REWRITTEN_LIST"
+       rm -f "$REWRITTEN_PENDING"
+}
+
+record_in_rewritten() {
+       oldsha1="$(git rev-parse $1)"
+       echo "$oldsha1" >> "$REWRITTEN_PENDING"
+
+       case "$(peek_next_command)" in
+           squash|s|fixup|f)
+               ;;
+           *)
+               flush_rewritten_pending
+               ;;
+       esac
+}
+
 do_next () {
        rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit
        read command sha1 rest < "$TODO"
@@ -438,6 +467,7 @@ do_next () {
                mark_action_done
                pick_one $sha1 ||
                        die_with_patch $sha1 "Could not apply $sha1... $rest"
+               record_in_rewritten $sha1
                ;;
        reword|r)
                comment_for_reflog reword
@@ -445,7 +475,8 @@ do_next () {
                mark_action_done
                pick_one $sha1 ||
                        die_with_patch $sha1 "Could not apply $sha1... $rest"
-               git commit --amend
+               git commit --amend --no-post-rewrite
+               record_in_rewritten $sha1
                ;;
        edit|e)
                comment_for_reflog edit
@@ -453,6 +484,7 @@ do_next () {
                mark_action_done
                pick_one $sha1 ||
                        die_with_patch $sha1 "Could not apply $sha1... $rest"
+               echo "$1" > "$DOTEST"/stopped-sha
                make_patch $sha1
                git rev-parse --verify HEAD > "$AMEND"
                warn "Stopped at $sha1... $rest"
@@ -509,6 +541,7 @@ do_next () {
                        rm -f "$SQUASH_MSG" "$FIXUP_MSG"
                        ;;
                esac
+               record_in_rewritten $sha1
                ;;
        *)
                warn "Unknown command: $command $sha1 $rest"
@@ -537,6 +570,15 @@ do_next () {
                test ! -f "$DOTEST"/verbose ||
                        git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
        } &&
+       {
+               git notes copy --for-rewrite=rebase < "$REWRITTEN_LIST" ||
+               true # we don't care if this copying failed
+       } &&
+       if test -x "$GIT_DIR"/hooks/post-rewrite &&
+               test -s "$REWRITTEN_LIST"; then
+               "$GIT_DIR"/hooks/post-rewrite rebase < "$REWRITTEN_LIST"
+               true # we don't care if this hook failed
+       fi &&
        rm -rf "$DOTEST" &&
        git gc --auto &&
        warn "Successfully rebased and updated $HEADNAME."
@@ -571,7 +613,12 @@ skip_unnecessary_picks () {
                esac
                echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
        done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
-       mv -f "$TODO".new "$TODO" ||
+       mv -f "$TODO".new "$TODO" &&
+       case "$(peek_next_command)" in
+       squash|s|fixup|f)
+               record_in_rewritten "$ONTO"
+               ;;
+       esac ||
        die "Could not skip unnecessary pick commands"
 }
 
@@ -685,6 +732,7 @@ first and then run 'git rebase --continue' again."
                                test -n "$amend" && git reset --soft $amend
                                die "Could not commit staged changes."
                        }
+                       record_in_rewritten "$(cat "$DOTEST"/stopped-sha)"
                fi
 
                require_clean_work_tree
index fb4fef7b1d6f7abb08fca562ecaad6e36f671768..e0eb9568f3d12a068d46196d2dc896dde4d35fd3 100755 (executable)
@@ -79,6 +79,7 @@ continue_merge () {
                then
                        printf "Committed: %0${prec}d " $msgnum
                fi
+               echo "$cmt $(git rev-parse HEAD^0)" >> "$dotest/rewritten"
        else
                if test -z "$GIT_QUIET"
                then
@@ -151,6 +152,11 @@ move_to_original_branch () {
 
 finish_rb_merge () {
        move_to_original_branch
+       git notes copy --for-rewrite=rebase < "$dotest"/rewritten
+       if test -x "$GIT_DIR"/hooks/post-rewrite &&
+               test -s "$dotest"/rewritten; then
+               "$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
+       fi
        rm -r "$dotest"
        say All done.
 }
index 630ceddf0356429f7ff71d280ee1056a2eb939c6..8fd15f6df4926a89812437e04735bd924ee7088b 100755 (executable)
@@ -65,11 +65,11 @@ if [ -z "$branch" ]; then
        status=1
 fi
 
-echo "The following changes since commit $baserev:"
-git shortlog --max-count=1 $baserev | sed -e 's/^\(.\)/  \1/'
+git show -s --format='The following changes since commit %H:
 
-echo "are available in the git repository at:"
-echo
+  %s (%ci)
+
+are available in the git repository at:' $baserev
 echo "  $url $branch"
 echo
 
index e05455f74c7e23c28cae41b68fa80df87c633ce9..d612ae8729e0a15f501214b6f27f4fc32a66e9fd 100755 (executable)
@@ -47,9 +47,9 @@ git send-email [options] <file | directory | rev-list options >
 
   Composing:
     --from                  <str>  * Email From:
-    --to                    <str>  * Email To:
-    --cc                    <str>  * Email Cc:
-    --bcc                   <str>  * Email Bcc:
+    --[no-]to               <str>  * Email To:
+    --[no-]cc               <str>  * Email Cc:
+    --[no-]bcc              <str>  * Email Bcc:
     --subject               <str>  * Email "Subject:"
     --in-reply-to           <str>  * Email "In-Reply-To:"
     --annotate                     * Review each patch that will be sent in an editor.
@@ -135,7 +135,7 @@ sub unique_email_list(@);
 sub cleanup_compose_files();
 
 # Variables we fill in automatically, or via prompting:
-my (@to,@cc,@initial_cc,@bcclist,@xh,
+my (@to,$no_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
        $initial_reply_to,$initial_subject,@files,
        $author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
@@ -261,8 +261,11 @@ my $rc = GetOptions("sender|from=s" => \$sender,
                     "in-reply-to=s" => \$initial_reply_to,
                    "subject=s" => \$initial_subject,
                    "to=s" => \@to,
+                   "no-to" => \$no_to,
                    "cc=s" => \@initial_cc,
+                   "no-cc" => \$no_cc,
                    "bcc=s" => \@bcclist,
+                   "no-bcc" => \$no_bcc,
                    "chain-reply-to!" => \$chain_reply_to,
                    "smtp-server=s" => \$smtp_server,
                    "smtp-server-port=s" => \$smtp_server_port,
@@ -305,6 +308,9 @@ sub read_config {
 
        foreach my $setting (keys %config_settings) {
                my $target = $config_settings{$setting};
+               next if $setting eq "to" and defined $no_to;
+               next if $setting eq "cc" and defined $no_cc;
+               next if $setting eq "bcc" and defined $no_bcc;
                if (ref($target) eq "ARRAY") {
                        unless (@$target) {
                                my @values = Git::config(@repo, "$prefix.$setting");
old mode 100755 (executable)
new mode 100644 (file)
index d56426d..6131670
@@ -107,6 +107,19 @@ git_editor() {
        eval "$GIT_EDITOR" '"$@"'
 }
 
+git_pager() {
+       if test -t 1
+       then
+               GIT_PAGER=$(git var GIT_PAGER)
+       else
+               GIT_PAGER=cat
+       fi
+       : ${LESS=-FRSX}
+       export LESS
+
+       eval "$GIT_PAGER" '"$@"'
+}
+
 sane_grep () {
        GREP_OPTIONS= LC_ALL=C grep "$@"
 }
@@ -128,7 +141,7 @@ cd_to_toplevel () {
 }
 
 require_work_tree () {
-       test $(git rev-parse --is-inside-work-tree) = true ||
+       test "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = true ||
        die "fatal: $0 cannot be used without a working tree."
 }
 
@@ -159,6 +172,13 @@ get_author_ident_from_commit () {
        LANG=C LC_ALL=C sed -ne "$pick_author_script"
 }
 
+# Clear repo-local GIT_* environment variables. Useful when switching to
+# another repository (e.g. when entering a submodule). See also the env
+# list in git_connect()
+clear_local_git_env() {
+       unset $(git rev-parse --local-env-vars)
+}
+
 # Make sure we are in a valid repository of a vintage we understand,
 # if we require to be in a git repository.
 if test -z "$NONGIT_OK"
index 3a0685f1893098e8f5c877f509183c8434e7c028..aa47e541ee4fe55254edc3fb59ef534ba4d5be66 100755 (executable)
@@ -151,6 +151,7 @@ save_stash () {
                        ;;
                -*)
                        echo "error: unknown option for 'stash save': $1"
+                       echo "       To provide a message, use git stash save -- '$1'"
                        usage
                        ;;
                *)
@@ -221,6 +222,7 @@ show_stash () {
 }
 
 apply_stash () {
+       applied_stash=
        unstash_index=
 
        while test $# != 0
@@ -242,6 +244,9 @@ apply_stash () {
        if test $# = 0
        then
                have_stash || die 'Nothing to apply'
+               applied_stash="$ref_stash@{0}"
+       else
+               applied_stash="$*"
        fi
 
        # stash records the work tree, and is a merge between the
@@ -415,8 +420,7 @@ pop)
        shift
        if apply_stash "$@"
        then
-               test -z "$unstash_index" || shift
-               drop_stash "$@"
+               drop_stash "$applied_stash"
        fi
        ;;
 branch)
index 5a9d3c0b305cc57e45b8f15b0d2c522bb8bb3285..2dd372a21d82a109774e80e014f9959485202b3e 100755 (executable)
@@ -222,7 +222,7 @@ cmd_add()
 
                module_clone "$path" "$realrepo" "$reference" || exit
                (
-                       unset GIT_DIR
+                       clear_local_git_env
                        cd "$path" &&
                        # ash fails to wordsplit ${branch:+-b "$branch"...}
                        case "$branch" in
@@ -278,7 +278,7 @@ cmd_foreach()
                        name=$(module_name "$path")
                        (
                                prefix="$prefix$path/"
-                               unset GIT_DIR
+                               clear_local_git_env
                                cd "$path" &&
                                eval "$@" &&
                                if test -n "$recursive"
@@ -434,7 +434,7 @@ cmd_update()
                        module_clone "$path" "$url" "$reference"|| exit
                        subsha1=
                else
-                       subsha1=$(unset GIT_DIR; cd "$path" &&
+                       subsha1=$(clear_local_git_env; cd "$path" &&
                                git rev-parse --verify HEAD) ||
                        die "Unable to find current revision in submodule path '$path'"
                fi
@@ -454,7 +454,7 @@ cmd_update()
 
                        if test -z "$nofetch"
                        then
-                               (unset GIT_DIR; cd "$path" &&
+                               (clear_local_git_env; cd "$path" &&
                                        git-fetch) ||
                                die "Unable to fetch in submodule path '$path'"
                        fi
@@ -477,14 +477,14 @@ cmd_update()
                                ;;
                        esac
 
-                       (unset GIT_DIR; cd "$path" && $command "$sha1") ||
+                       (clear_local_git_env; cd "$path" && $command "$sha1") ||
                        die "Unable to $action '$sha1' in submodule path '$path'"
                        say "Submodule path '$path': $msg '$sha1'"
                fi
 
                if test -n "$recursive"
                then
-                       (unset GIT_DIR; cd "$path" && cmd_update $orig_args) ||
+                       (clear_local_git_env; cd "$path" && cmd_update $orig_args) ||
                        die "Failed to recurse into submodule path '$path'"
                fi
        done
@@ -492,7 +492,7 @@ cmd_update()
 
 set_name_rev () {
        revname=$( (
-               unset GIT_DIR
+               clear_local_git_env
                cd "$1" && {
                        git describe "$2" 2>/dev/null ||
                        git describe --tags "$2" 2>/dev/null ||
@@ -762,7 +762,7 @@ cmd_status()
                else
                        if test -z "$cached"
                        then
-                               sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
+                               sha1=$(clear_local_git_env; cd "$path" && git rev-parse --verify HEAD)
                                set_name_rev "$path" "$sha1"
                        fi
                        say "+$sha1 $displaypath$revname"
@@ -772,7 +772,7 @@ cmd_status()
                then
                        (
                                prefix="$displaypath/"
-                               unset GIT_DIR
+                               clear_local_git_env
                                cd "$path" &&
                                cmd_status $orig_args
                        ) ||
@@ -823,7 +823,7 @@ cmd_sync()
                if test -e "$path"/.git
                then
                (
-                       unset GIT_DIR
+                       clear_local_git_env
                        cd "$path"
                        remote=$(get_default_remote)
                        say "Synchronizing submodule url for '$name'"
index 265852f4596bfe5aeca12be06f78631320b8ebb4..2c86ea2e384e4b3ecf9f6c2a856c52a0203ce489 100755 (executable)
@@ -36,11 +36,13 @@ $ENV{TZ} = 'UTC';
 $| = 1; # unbuffer STDOUT
 
 sub fatal (@) { print STDERR "@_\n"; exit 1 }
-require SVN::Core; # use()-ing this causes segfaults for me... *shrug*
-require SVN::Ra;
-require SVN::Delta;
-if ($SVN::Core::VERSION lt '1.1.0') {
-       fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)";
+sub _req_svn {
+       require SVN::Core; # use()-ing this causes segfaults for me... *shrug*
+       require SVN::Ra;
+       require SVN::Delta;
+       if ($SVN::Core::VERSION lt '1.1.0') {
+               fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)";
+       }
 }
 my $can_compress = eval { require Compress::Zlib; 1};
 push @Git::SVN::Ra::ISA, 'SVN::Ra';
@@ -349,6 +351,7 @@ information.
 }
 
 sub version {
+       ::_req_svn();
        print "git-svn version $VERSION (svn $SVN::Core::VERSION)\n";
        exit 0;
 }
@@ -367,7 +370,6 @@ sub do_git_init_db {
                command_noisy(@init_db);
                $_repository = Git->repository(Repository => ".git");
        }
-       command_noisy('config', 'core.autocrlf', 'false');
        my $set;
        my $pfx = "svn-remote.$Git::SVN::default_repo_id";
        foreach my $i (keys %icv) {
@@ -730,6 +732,8 @@ sub cmd_branch {
                $src=~s/^http:/https:/;
        }
 
+       ::_req_svn();
+
        my $ctx = SVN::Client->new(
                auth    => Git::SVN::Ra::_auth_providers(),
                log_msg => sub {
@@ -1098,6 +1102,7 @@ sub cmd_info {
        if ($@) {
                $result .= "Repository Root: (offline)\n";
        }
+       ::_req_svn();
        $result .= "Repository UUID: $uuid\n" unless $diff_status eq "A" &&
                ($SVN::Core::VERSION le '1.5.4' || $file_type ne "dir");
        $result .= "Revision: " . ($diff_status eq "A" ? 0 : $rev) . "\n";
@@ -2993,7 +2998,7 @@ sub find_extra_svk_parents {
        for my $ticket ( @tickets ) {
                my ($uuid, $path, $rev) = split /:/, $ticket;
                if ( $uuid eq $self->ra_uuid ) {
-                       my $url = $self->rewrite_root || $self->{url};
+                       my $url = $self->{url};
                        my $repos_root = $url;
                        my $branch_from = $path;
                        $branch_from =~ s{^/}{};
@@ -3201,7 +3206,7 @@ sub find_extra_svn_parents {
        # are now marked as merge, we can add the tip as a parent.
        my @merges = split "\n", $mergeinfo;
        my @merge_tips;
-       my $url = $self->rewrite_root || $self->{url};
+       my $url = $self->{url};
        my $uuid = $self->ra_uuid;
        my %ranges;
        for my $merge ( @merges ) {
@@ -3273,7 +3278,7 @@ sub find_extra_svn_parents {
                                        "$new_parents[$i]..$new_parents[$j]",
                                       );
                                if ( !$revs ) {
-                                       undef($new_parents[$i]);
+                                       undef($new_parents[$j]);
                                }
                        }
                }
@@ -3966,18 +3971,25 @@ sub username {
 
 sub _read_password {
        my ($prompt, $realm) = @_;
-       print STDERR $prompt;
-       STDERR->flush;
-       require Term::ReadKey;
-       Term::ReadKey::ReadMode('noecho');
        my $password = '';
-       while (defined(my $key = Term::ReadKey::ReadKey(0))) {
-               last if $key =~ /[\012\015]/; # \n\r
-               $password .= $key;
+       if (exists $ENV{GIT_ASKPASS}) {
+               open(PH, "-|", $ENV{GIT_ASKPASS}, $prompt);
+               $password = <PH>;
+               $password =~ s/[\012\015]//; # \n\r
+               close(PH);
+       } else {
+               print STDERR $prompt;
+               STDERR->flush;
+               require Term::ReadKey;
+               Term::ReadKey::ReadMode('noecho');
+               while (defined(my $key = Term::ReadKey::ReadKey(0))) {
+                       last if $key =~ /[\012\015]/; # \n\r
+                       $password .= $key;
+               }
+               Term::ReadKey::ReadMode('restore');
+               print STDERR "\n";
+               STDERR->flush;
        }
-       Term::ReadKey::ReadMode('restore');
-       print STDERR "\n";
-       STDERR->flush;
        $password;
 }
 
@@ -4859,6 +4871,8 @@ sub new {
        $url =~ s!/+$!!;
        return $RA if ($RA && $RA->{url} eq $url);
 
+       ::_req_svn();
+
        SVN::_Core::svn_config_ensure($config_dir, undef);
        my ($baton, $callbacks) = SVN::Core::auth_open_helper(_auth_providers);
        my $config = SVN::Core::config_get_config($config_dir);
@@ -5459,7 +5473,12 @@ sub git_svn_log_cmd {
 
 # adapted from pager.c
 sub config_pager {
-       chomp(my $pager = command_oneline(qw(var GIT_PAGER)));
+       if (! -t *STDOUT) {
+               $ENV{GIT_PAGER_IN_USE} = 'false';
+               $pager = undef;
+               return;
+       }
+       chomp($pager = command_oneline(qw(var GIT_PAGER)));
        if ($pager eq 'cat') {
                $pager = undef;
        }
@@ -5467,7 +5486,7 @@ sub config_pager {
 }
 
 sub run_pager {
-       return unless -t *STDOUT && defined $pager;
+       return unless defined $pager;
        pipe my ($rfd, $wfd) or return;
        defined(my $pid = fork) or ::fatal "Can't fork: $!";
        if (!$pid) {
diff --git a/git.c b/git.c
index 4c3028c098c03c842d2a635d054d3c27653dd511..6bae30545b85f19eb51c4b055f303f70909f0cf2 100644 (file)
--- a/git.c
+++ b/git.c
@@ -54,6 +54,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 {
        int handled = 0;
 
+       if (!getenv("GIT_ASKPASS") && getenv("SSH_ASKPASS"))
+               setenv("GIT_ASKPASS", getenv("SSH_ASKPASS"), 1);
+
        while (*argc > 0) {
                const char *cmd = (*argv)[0];
                if (cmd[0] != '-')
@@ -317,7 +320,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, RUN_SETUP | USE_PAGER },
+               { "grep", cmd_grep, USE_PAGER },
                { "hash-object", cmd_hash_object },
                { "help", cmd_help },
                { "index-pack", cmd_index_pack },
@@ -343,6 +346,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "mktree", cmd_mktree, RUN_SETUP },
                { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
                { "name-rev", cmd_name_rev, RUN_SETUP },
+               { "notes", cmd_notes, RUN_SETUP },
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
                { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
                { "patch-id", cmd_patch_id },
@@ -527,7 +531,7 @@ int main(int argc, const char **argv)
                        break;
                if (was_alias) {
                        fprintf(stderr, "Expansion of alias '%s' failed; "
-                               "'%s' is not a git-command\n",
+                               "'%s' is not a git command\n",
                                cmd, argv[0]);
                        exit(1);
                }
index 6c2c8e12598ca9ba17e7cc3d74812a8b7883d59a..ad6a04c464075c31afe3c67222f0bdeabc76f569 100644 (file)
@@ -312,12 +312,16 @@ If you want to have one URL for both gitweb and your http://
 repositories, you can configure apache like this:
 
 <VirtualHost *:80>
-    ServerName git.example.org
-    DocumentRoot /pub/git
-    SetEnv     GITWEB_CONFIG   /etc/gitweb.conf
+    ServerName         git.example.org
+    DocumentRoot       /pub/git
+    SetEnv                     GITWEB_CONFIG   /etc/gitweb.conf
+
+    # turning on mod rewrite
     RewriteEngine on
+
     # make the front page an internal rewrite to the gitweb script
     RewriteRule ^/$  /cgi-bin/gitweb.cgi
+
     # make access for "dumb clients" work
     RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI}  [L,PT]
 </VirtualHost>
@@ -343,6 +347,63 @@ something like the following in your gitweb.conf (or gitweb_config.perl) file:
   $home_link = "/";
 
 
+Webserver configuration with multiple projects' root
+----------------------------------------------------
+
+If you want to use gitweb with several project roots you can edit your apache
+virtual host and gitweb.conf configuration files like this :
+
+virtual host configuration :
+
+<VirtualHost *:80>
+    ServerName                 git.example.org
+    DocumentRoot               /pub/git
+    SetEnv                             GITWEB_CONFIG   /etc/gitweb.conf
+
+    # turning on mod rewrite
+    RewriteEngine on
+
+    # make the front page an internal rewrite to the gitweb script
+    RewriteRule ^/$            /cgi-bin/gitweb.cgi [QSA,L,PT]
+
+    # look for a public_git folder in unix users' home
+    # http://git.example.org/~<user>/
+    RewriteRule ^/\~([^\/]+)(/|/gitweb.cgi)?$  /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT]
+
+    # http://git.example.org/+<user>/
+    #RewriteRule ^/\+([^\/]+)(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT]
+
+    # http://git.example.org/user/<user>/
+    #RewriteRule ^/user/([^\/]+)/(gitweb.cgi)?$        /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT]
+
+    # defined list of project roots
+    RewriteRule ^/scm(/|/gitweb.cgi)?$         /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/pub/scm/,L,PT]
+    RewriteRule ^/var(/|/gitweb.cgi)?$         /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/var/git/,L,PT]
+
+    # make access for "dumb clients" work
+    RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI}  [L,PT]
+</VirtualHost>
+
+gitweb.conf configuration :
+
+$projectroot = $ENV{'GITWEB_PROJECTROOT'} || "/pub/git";
+
+These configurations enable two things. First, each unix user (<user>) of the
+server will be able to browse through gitweb git repositories found in
+~/public_git/ with the following url : http://git.example.org/~<user>/
+
+If you do not want this feature on your server just remove the second rewrite rule.
+
+If you already use mod_userdir in your virtual host or you don't want to use
+the '~' as first character just comment or remove the second rewrite rule and
+uncomment one of the following according to what you want.
+
+Second, repositories found in /pub/scm/ and /var/git/ will be accesible
+through http://git.example.org/scm/ and http://git.example.org/var/.
+You can add as many project roots as you want by adding rewrite rules like the
+third and the fourth.
+
+
 PATH_INFO usage
 -----------------------
 If you enable PATH_INFO usage in gitweb by putting
index 1f6978ac1f3ca2f915c5b87d8b196ee1e0e52aca..a2d2283ec921acac7b068537eb85f63c16de9556 100755 (executable)
@@ -454,7 +454,11 @@ sub gitweb_get_feature {
                $feature{$name}{'sub'},
                $feature{$name}{'override'},
                @{$feature{$name}{'default'}});
-       if (!$override) { return @defaults; }
+       # project specific override is possible only if we have project
+       our $git_dir; # global variable, declared later
+       if (!$override || !defined $git_dir) {
+               return @defaults;
+       }
        if (!defined $sub) {
                warn "feature $name is not overridable";
                return @defaults;
@@ -550,11 +554,14 @@ sub filter_snapshot_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;
-} else {
-       our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
-       do $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM;
+       die $@ if $@;
+} elsif (-e $GITWEB_CONFIG_SYSTEM) {
+       do $GITWEB_CONFIG_SYSTEM;
+       die $@ if $@;
 }
 
 # Get loadavg of system, to compare against $maxload.
@@ -1143,6 +1150,7 @@ sub validate_refname {
 # in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
 sub to_utf8 {
        my $str = shift;
+       return undef unless defined $str;
        if (utf8::valid($str)) {
                utf8::decode($str);
                return $str;
@@ -1155,6 +1163,7 @@ sub to_utf8 {
 # correct, but quoted slashes look too horrible in bookmarks
 sub esc_param {
        my $str = shift;
+       return undef unless defined $str;
        $str =~ s/([^A-Za-z0-9\-_.~()\/:@ ]+)/CGI::escape($1)/eg;
        $str =~ s/ /\+/g;
        return $str;
@@ -1163,6 +1172,7 @@ sub esc_param {
 # quote unsafe chars in whole URL, so some charactrs cannot be quoted
 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/ /\+/g;
@@ -1174,6 +1184,8 @@ sub esc_html {
        my $str = shift;
        my %opts = @_;
 
+       return undef unless defined $str;
+
        $str = to_utf8($str);
        $str = $cgi->escapeHTML($str);
        if ($opts{'-nbsp'}) {
@@ -1188,6 +1200,8 @@ sub esc_path {
        my $str = shift;
        my %opts = @_;
 
+       return undef unless defined $str;
+
        $str = to_utf8($str);
        $str = $cgi->escapeHTML($str);
        if ($opts{'-nbsp'}) {
@@ -2202,6 +2216,9 @@ sub config_to_multi {
 sub git_get_project_config {
        my ($key, $type) = @_;
 
+       # do we have project
+       return unless (defined $project && defined $git_dir);
+
        # key sanity check
        return unless ($key);
        $key =~ s/^gitweb\.//;
@@ -3372,7 +3389,7 @@ sub git_footer_html {
              "</html>";
 }
 
-# die_error(<http_status_code>, <error_message>)
+# die_error(<http_status_code>, <error_message>[, <detailed_html_description>])
 # Example: die_error(404, 'Hash not found')
 # By convention, use the following status codes (as defined in RFC 2616):
 # 400: Invalid or missing CGI parameters, or
@@ -3387,7 +3404,7 @@ sub git_footer_html {
 #      or down for maintenance).  Generally, this is a temporary state.
 sub die_error {
        my $status = shift || 500;
-       my $error = shift || "Internal server error";
+       my $error = esc_html(shift) || "Internal Server Error";
        my $extra = shift;
 
        my %http_responses = (
diff --git a/graph.c b/graph.c
index 6746d422a98ed010489d4ce74b26a8a4600b183e..e6bbcaa8c4655add3ecaca578e948355795e36ca 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -80,12 +80,12 @@ static char column_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_BLUE,
        GIT_COLOR_MAGENTA,
        GIT_COLOR_CYAN,
-       GIT_COLOR_BOLD GIT_COLOR_RED,
-       GIT_COLOR_BOLD GIT_COLOR_GREEN,
-       GIT_COLOR_BOLD GIT_COLOR_YELLOW,
-       GIT_COLOR_BOLD GIT_COLOR_BLUE,
-       GIT_COLOR_BOLD GIT_COLOR_MAGENTA,
-       GIT_COLOR_BOLD GIT_COLOR_CYAN,
+       GIT_COLOR_BOLD_RED,
+       GIT_COLOR_BOLD_GREEN,
+       GIT_COLOR_BOLD_YELLOW,
+       GIT_COLOR_BOLD_BLUE,
+       GIT_COLOR_BOLD_MAGENTA,
+       GIT_COLOR_BOLD_CYAN,
 };
 
 #define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
diff --git a/grep.c b/grep.c
index a0864f1cbbe5fcc6f28eb57a08ebfd9a76c87da6..fdc42062687fa6b8bb77c8d09ed58aee9107692d 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -11,8 +11,8 @@ void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field fie
        p->no = 0;
        p->token = GREP_PATTERN_HEAD;
        p->field = field;
-       *opt->pattern_tail = p;
-       opt->pattern_tail = &p->next;
+       *opt->header_tail = p;
+       opt->header_tail = &p->next;
        p->next = NULL;
 }
 
@@ -184,9 +184,26 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
 void compile_grep_patterns(struct grep_opt *opt)
 {
        struct grep_pat *p;
-
-       if (opt->all_match)
-               opt->extended = 1;
+       struct grep_expr *header_expr = NULL;
+
+       if (opt->header_list) {
+               p = opt->header_list;
+               header_expr = compile_pattern_expr(&p);
+               if (p)
+                       die("incomplete pattern expression: %s", p->pattern);
+               for (p = opt->header_list; p; p = p->next) {
+                       switch (p->token) {
+                       case GREP_PATTERN: /* atom */
+                       case GREP_PATTERN_HEAD:
+                       case GREP_PATTERN_BODY:
+                               compile_regexp(p, opt);
+                               break;
+                       default:
+                               opt->extended = 1;
+                               break;
+                       }
+               }
+       }
 
        for (p = opt->pattern_list; p; p = p->next) {
                switch (p->token) {
@@ -201,7 +218,9 @@ void compile_grep_patterns(struct grep_opt *opt)
                }
        }
 
-       if (!opt->extended)
+       if (opt->all_match || header_expr)
+               opt->extended = 1;
+       else if (!opt->extended)
                return;
 
        /* Then bundle them up in an expression.
@@ -212,6 +231,21 @@ void compile_grep_patterns(struct grep_opt *opt)
                opt->pattern_expression = compile_pattern_expr(&p);
        if (p)
                die("incomplete pattern expression: %s", p->pattern);
+
+       if (!header_expr)
+               return;
+
+       if (opt->pattern_expression) {
+               struct grep_expr *z;
+               z = xcalloc(1, sizeof(*z));
+               z->node = GREP_NODE_OR;
+               z->u.binary.left = opt->pattern_expression;
+               z->u.binary.right = header_expr;
+               opt->pattern_expression = z;
+       } else {
+               opt->pattern_expression = header_expr;
+       }
+       opt->all_match = 1;
 }
 
 static void free_pattern_expr(struct grep_expr *x)
@@ -270,9 +304,28 @@ static int word_char(char ch)
        return isalnum(ch) || ch == '_';
 }
 
+static void output_color(struct grep_opt *opt, const void *data, size_t size,
+                        const char *color)
+{
+       if (opt->color && color && color[0]) {
+               opt->output(opt, color, strlen(color));
+               opt->output(opt, data, size);
+               opt->output(opt, GIT_COLOR_RESET, strlen(GIT_COLOR_RESET));
+       } else
+               opt->output(opt, data, size);
+}
+
+static void output_sep(struct grep_opt *opt, char sign)
+{
+       if (opt->null_following_name)
+               opt->output(opt, "\0", 1);
+       else
+               output_color(opt, &sign, 1, opt->color_sep);
+}
+
 static void show_name(struct grep_opt *opt, const char *name)
 {
-       opt->output(opt, name, strlen(name));
+       output_color(opt, name, strlen(name), opt->color_filename);
        opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
 }
 
@@ -510,31 +563,31 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
                      const char *name, unsigned lno, char sign)
 {
        int rest = eol - bol;
-       char sign_str[1];
+       char *line_color = NULL;
 
-       sign_str[0] = sign;
        if (opt->pre_context || opt->post_context) {
                if (opt->last_shown == 0) {
-                       if (opt->show_hunk_mark)
-                               opt->output(opt, "--\n", 3);
-                       else
+                       if (opt->show_hunk_mark) {
+                               output_color(opt, "--", 2, opt->color_sep);
+                               opt->output(opt, "\n", 1);
+                       } else
                                opt->show_hunk_mark = 1;
-               } else if (lno > opt->last_shown + 1)
-                       opt->output(opt, "--\n", 3);
+               } else if (lno > opt->last_shown + 1) {
+                       output_color(opt, "--", 2, opt->color_sep);
+                       opt->output(opt, "\n", 1);
+               }
        }
        opt->last_shown = lno;
 
-       if (opt->null_following_name)
-               sign_str[0] = '\0';
        if (opt->pathname) {
-               opt->output(opt, name, strlen(name));
-               opt->output(opt, sign_str, 1);
+               output_color(opt, name, strlen(name), opt->color_filename);
+               output_sep(opt, sign);
        }
        if (opt->linenum) {
                char buf[32];
                snprintf(buf, sizeof(buf), "%d", lno);
-               opt->output(opt, buf, strlen(buf));
-               opt->output(opt, sign_str, 1);
+               output_color(opt, buf, strlen(buf), opt->color_lineno);
+               output_sep(opt, sign);
        }
        if (opt->color) {
                regmatch_t match;
@@ -542,25 +595,28 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
                int ch = *eol;
                int eflags = 0;
 
+               if (sign == ':')
+                       line_color = opt->color_selected;
+               else if (sign == '-')
+                       line_color = opt->color_context;
+               else if (sign == '=')
+                       line_color = opt->color_function;
                *eol = '\0';
                while (next_match(opt, bol, eol, ctx, &match, eflags)) {
                        if (match.rm_so == match.rm_eo)
                                break;
 
-                       opt->output(opt, bol, match.rm_so);
-                       opt->output(opt, opt->color_match,
-                                   strlen(opt->color_match));
-                       opt->output(opt, bol + match.rm_so,
-                                   (int)(match.rm_eo - match.rm_so));
-                       opt->output(opt, GIT_COLOR_RESET,
-                                   strlen(GIT_COLOR_RESET));
+                       output_color(opt, bol, match.rm_so, line_color);
+                       output_color(opt, bol + match.rm_so,
+                                    match.rm_eo - match.rm_so,
+                                    opt->color_match);
                        bol += match.rm_eo;
                        rest -= match.rm_eo;
                        eflags = REG_NOTBOL;
                }
                *eol = ch;
        }
-       opt->output(opt, bol, rest);
+       output_color(opt, bol, rest, line_color);
        opt->output(opt, "\n", 1);
 }
 
@@ -823,7 +879,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                                return 1;
                        if (binary_match_only) {
                                opt->output(opt, "Binary file ", 12);
-                               opt->output(opt, name, strlen(name));
+                               output_color(opt, name, strlen(name),
+                                            opt->color_filename);
                                opt->output(opt, " matches\n", 9);
                                return 1;
                        }
@@ -882,9 +939,9 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
         */
        if (opt->count && count) {
                char buf[32];
-               opt->output(opt, name, strlen(name));
-               snprintf(buf, sizeof(buf), "%c%u\n",
-                        opt->null_following_name ? '\0' : ':', count);
+               output_color(opt, name, strlen(name), opt->color_filename);
+               output_sep(opt, ':');
+               snprintf(buf, sizeof(buf), "%u\n", count);
                opt->output(opt, buf, strlen(buf));
        }
        return !!last_hit;
diff --git a/grep.h b/grep.h
index 970308799664fe6a3871c0b8364c13e43cf96e1f..89342e5b47f6d63dd546e738e4cbf023c447b382 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -59,6 +59,8 @@ struct grep_expr {
 struct grep_opt {
        struct grep_pat *pattern_list;
        struct grep_pat **pattern_tail;
+       struct grep_pat *header_list;
+       struct grep_pat **header_tail;
        struct grep_expr *pattern_expression;
        const char *prefix;
        int prefix_length;
@@ -84,7 +86,13 @@ struct grep_opt {
        int color;
        int max_depth;
        int funcname;
+       char color_context[COLOR_MAXLEN];
+       char color_filename[COLOR_MAXLEN];
+       char color_function[COLOR_MAXLEN];
+       char color_lineno[COLOR_MAXLEN];
        char color_match[COLOR_MAXLEN];
+       char color_selected[COLOR_MAXLEN];
+       char color_sep[COLOR_MAXLEN];
        int regflags;
        unsigned pre_context;
        unsigned post_context;
diff --git a/help.c b/help.c
index 9da97d7462040d3935e7eaa95b1167357b38a943..7f4928e45954465d0401964289952aec4bd59e2a 100644 (file)
--- a/help.c
+++ b/help.c
@@ -350,7 +350,7 @@ const char *help_unknown_cmd(const char *cmd)
                return assumed;
        }
 
-       fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd);
+       fprintf(stderr, "git: '%s' is not a git command. See 'git --help'.\n", cmd);
 
        if (SIMILAR_ENOUGH(best_similarity)) {
                fprintf(stderr, "\nDid you mean %s?\n",
index ffd0ad7e295d7341776bb7b6407602cdb2997ef3..762c750d7af3651287c147034d3dead469453e7c 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "exec_cmd.h"
+#include "http.h"
 #include "walker.h"
 
 static const char http_fetch_usage[] = "git http-fetch "
@@ -69,7 +70,8 @@ int main(int argc, const char **argv)
                url = rewritten_url;
        }
 
-       walker = get_http_walker(url, NULL);
+       http_init(NULL);
+       walker = get_http_walker(url);
        walker->get_tree = get_tree;
        walker->get_history = get_history;
        walker->get_all = get_all;
@@ -89,6 +91,7 @@ int main(int argc, const char **argv)
        }
 
        walker_free(walker);
+       http_cleanup();
 
        free(rewritten_url);
 
index 432b20f2d9a750263d930683e770413ac5328935..415b1ab0a7f0a98e3a16f82c39bbcc9b04f85ac2 100644 (file)
@@ -1965,7 +1965,7 @@ int main(int argc, char **argv)
                }
 
                if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
-                       if (push_verbosely || 1)
+                       if (push_verbosely)
                                fprintf(stderr, "'%s': up-to-date\n", ref->name);
                        if (helper_status)
                                printf("ok %s up to date\n", ref->name);
index 700bc13112d65dfe8cf89af17522e28cf0d76e26..ef99ae647ae02995495c71455eef785bdeca1789 100644 (file)
@@ -543,17 +543,30 @@ static int fetch_ref(struct walker *walker, struct ref *ref)
 
 static void cleanup(struct walker *walker)
 {
-       http_cleanup();
+       struct walker_data *data = walker->data;
+       struct alt_base *alt, *alt_next;
+
+       if (data) {
+               alt = data->alt;
+               while (alt) {
+                       alt_next = alt->next;
+
+                       free(alt->base);
+                       free(alt);
+
+                       alt = alt_next;
+               }
+               free(data);
+               walker->data = NULL;
+       }
 }
 
-struct walker *get_http_walker(const char *url, struct remote *remote)
+struct walker *get_http_walker(const char *url)
 {
        char *s;
        struct walker_data *data = xmalloc(sizeof(struct walker_data));
        struct walker *walker = xmalloc(sizeof(struct walker));
 
-       http_init(remote);
-
        data->alt = xmalloc(sizeof(*data->alt));
        data->alt->base = xmalloc(strlen(url) + 1);
        strcpy(data->alt->base, url);
diff --git a/http.c b/http.c
index deab59551dad9a0d2c2e86d75071fa561e4cbf1a..4814217c6401faa1fd8f13f0288758f57b5e3755 100644 (file)
--- a/http.c
+++ b/http.c
@@ -204,7 +204,7 @@ static void init_curl_http_auth(CURL *result)
        if (user_name) {
                struct strbuf up = STRBUF_INIT;
                if (!user_pass)
-                       user_pass = xstrdup(getpass("Password: "));
+                       user_pass = xstrdup(git_getpass("Password: "));
                strbuf_addf(&up, "%s:%s", user_name, user_pass);
                curl_easy_setopt(result, CURLOPT_USERPWD,
                                 strbuf_detach(&up, NULL));
@@ -219,7 +219,7 @@ static int has_cert_password(void)
                return 0;
        /* Only prompt the user once. */
        ssl_cert_password_required = -1;
-       ssl_cert_password = getpass("Certificate Password: ");
+       ssl_cert_password = git_getpass("Certificate Password: ");
        if (ssl_cert_password != NULL) {
                ssl_cert_password = xstrdup(ssl_cert_password);
                return 1;
index ba72fa4b6e2fcebc74e611ed1ca200a37b9f339d..aeb2985b831c982f10c3905f5053136520fcc4fe 100644 (file)
@@ -27,6 +27,9 @@
 #include "run-command.h"
 #ifdef NO_OPENSSL
 typedef void *SSL;
+#else
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
 #endif
 
 struct store_conf {
@@ -91,7 +94,6 @@ struct msg_data {
        char *data;
        int len;
        unsigned char flags;
-       unsigned int crlf:1;
 };
 
 static const char imap_send_usage[] = "git imap-send < <mbox>";
@@ -140,6 +142,20 @@ struct imap_server_conf {
        int use_ssl;
        int ssl_verify;
        int use_html;
+       char *auth_method;
+};
+
+static struct imap_server_conf server = {
+       NULL,   /* name */
+       NULL,   /* tunnel */
+       NULL,   /* host */
+       0,      /* port */
+       NULL,   /* user */
+       NULL,   /* pass */
+       0,      /* use_ssl */
+       1,      /* ssl_verify */
+       0,      /* use_html */
+       NULL,   /* auth_method */
 };
 
 struct imap_store_conf {
@@ -214,6 +230,7 @@ enum CAPABILITY {
        LITERALPLUS,
        NAMESPACE,
        STARTTLS,
+       AUTH_CRAM_MD5,
 };
 
 static const char *cap_list[] = {
@@ -222,6 +239,7 @@ static const char *cap_list[] = {
        "LITERAL+",
        "NAMESPACE",
        "STARTTLS",
+       "AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -949,6 +967,87 @@ static void imap_close_store(struct store *ctx)
        free(ctx);
 }
 
+#ifndef NO_OPENSSL
+
+/*
+ * hexchar() and cram() functions are based on the code from the isync
+ * project (http://isync.sf.net/).
+ */
+static char hexchar(unsigned int b)
+{
+       return b < 10 ? '0' + b : 'a' + (b - 10);
+}
+
+#define ENCODED_SIZE(n) (4*((n+2)/3))
+static char *cram(const char *challenge_64, const char *user, const char *pass)
+{
+       int i, resp_len, encoded_len, decoded_len;
+       HMAC_CTX hmac;
+       unsigned char hash[16];
+       char hex[33];
+       char *response, *response_64, *challenge;
+
+       /*
+        * length of challenge_64 (i.e. base-64 encoded string) is a good
+        * enough upper bound for challenge (decoded result).
+        */
+       encoded_len = strlen(challenge_64);
+       challenge = xmalloc(encoded_len);
+       decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
+                                     (unsigned char *)challenge_64, encoded_len);
+       if (decoded_len < 0)
+               die("invalid challenge %s", challenge_64);
+       HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
+       HMAC_Update(&hmac, (unsigned char *)challenge, decoded_len);
+       HMAC_Final(&hmac, hash, NULL);
+       HMAC_CTX_cleanup(&hmac);
+
+       hex[32] = 0;
+       for (i = 0; i < 16; i++) {
+               hex[2 * i] = hexchar((hash[i] >> 4) & 0xf);
+               hex[2 * i + 1] = hexchar(hash[i] & 0xf);
+       }
+
+       /* response: "<user> <digest in hex>" */
+       resp_len = strlen(user) + 1 + strlen(hex) + 1;
+       response = xmalloc(resp_len);
+       sprintf(response, "%s %s", user, hex);
+
+       response_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);
+       encoded_len = EVP_EncodeBlock((unsigned char *)response_64,
+                                     (unsigned char *)response, resp_len);
+       if (encoded_len < 0)
+               die("EVP_EncodeBlock error");
+       response_64[encoded_len] = '\0';
+       return (char *)response_64;
+}
+
+#else
+
+static char *cram(const char *challenge_64, const char *user, const char *pass)
+{
+       die("If you want to use CRAM-MD5 authenticate method, "
+           "you have to build git-imap-send with OpenSSL library.");
+}
+
+#endif
+
+static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)
+{
+       int ret;
+       char *response;
+
+       response = cram(prompt, server.user, server.pass);
+
+       ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
+       if (ret != strlen(response))
+               return error("IMAP error: sending response failed\n");
+
+       free(response);
+
+       return 0;
+}
+
 static struct store *imap_open_store(struct imap_server_conf *srvc)
 {
        struct imap_store *ctx;
@@ -1108,7 +1207,7 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
                if (!srvc->pass) {
                        char prompt[80];
                        sprintf(prompt, "Password (%s@%s): ", srvc->user, srvc->host);
-                       arg = getpass(prompt);
+                       arg = git_getpass(prompt);
                        if (!arg) {
                                perror("getpass");
                                exit(1);
@@ -1130,9 +1229,34 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
                if (!imap->buf.sock.ssl)
                        imap_warn("*** IMAP Warning *** Password is being "
                                  "sent in the clear\n");
-               if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
-                       fprintf(stderr, "IMAP error: LOGIN failed\n");
-                       goto bail;
+
+               if (srvc->auth_method) {
+                       struct imap_cmd_cb cb;
+
+                       if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+                               if (!CAP(AUTH_CRAM_MD5)) {
+                                       fprintf(stderr, "You specified"
+                                               "CRAM-MD5 as authentication method, "
+                                               "but %s doesn't support it.\n", srvc->host);
+                                       goto bail;
+                               }
+                               /* CRAM-MD5 */
+
+                               memset(&cb, 0, sizeof(cb));
+                               cb.cont = auth_cram_md5;
+                               if (imap_exec(ctx, &cb, "AUTHENTICATE CRAM-MD5") != RESP_OK) {
+                                       fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
+                                       goto bail;
+                               }
+                       } else {
+                               fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+                               goto bail;
+                       }
+               } else {
+                       if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+                               fprintf(stderr, "IMAP error: LOGIN failed\n");
+                               goto bail;
+                       }
                }
        } /* !preauth */
 
@@ -1162,6 +1286,44 @@ static int imap_make_flags(int flags, char *buf)
        return d;
 }
 
+static void lf_to_crlf(struct msg_data *msg)
+{
+       char *new;
+       int i, j, lfnum = 0;
+
+       if (msg->data[0] == '\n')
+               lfnum++;
+       for (i = 1; i < msg->len; i++) {
+               if (msg->data[i - 1] != '\r' && msg->data[i] == '\n')
+                       lfnum++;
+       }
+
+       new = xmalloc(msg->len + lfnum);
+       if (msg->data[0] == '\n') {
+               new[0] = '\r';
+               new[1] = '\n';
+               i = 1;
+               j = 2;
+       } else {
+               new[0] = msg->data[0];
+               i = 1;
+               j = 1;
+       }
+       for ( ; i < msg->len; i++) {
+               if (msg->data[i] != '\n') {
+                       new[j++] = msg->data[i];
+                       continue;
+               }
+               if (msg->data[i - 1] != '\r')
+                       new[j++] = '\r';
+               /* otherwise it already had CR before */
+               new[j++] = '\n';
+       }
+       msg->len += lfnum;
+       free(msg->data);
+       msg->data = new;
+}
+
 static int imap_store_msg(struct store *gctx, struct msg_data *data)
 {
        struct imap_store *ctx = (struct imap_store *)gctx;
@@ -1171,6 +1333,7 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data)
        int ret, d;
        char flagstr[128];
 
+       lf_to_crlf(data);
        memset(&cb, 0, sizeof(cb));
 
        cb.dlen = data->len;
@@ -1310,18 +1473,6 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
        return 1;
 }
 
-static struct imap_server_conf server = {
-       NULL,   /* name */
-       NULL,   /* tunnel */
-       NULL,   /* host */
-       0,      /* port */
-       NULL,   /* user */
-       NULL,   /* pass */
-       0,      /* use_ssl */
-       1,      /* ssl_verify */
-       0,      /* use_html */
-};
-
 static char *imap_folder;
 
 static int git_imap_config(const char *key, const char *val, void *cb)
@@ -1361,6 +1512,9 @@ static int git_imap_config(const char *key, const char *val, void *cb)
                server.port = git_config_int(key, val);
        else if (!strcmp("tunnel", key))
                server.tunnel = xstrdup(val);
+       else if (!strcmp("authmethod", key))
+               server.auth_method = xstrdup(val);
+
        return 0;
 }
 
index 0173abeef52c8dc3930a5c36420ff073ba8ddc87..4105bf3549560acc7bd187b8a16fff2a5e739d09 100644 (file)
@@ -2,7 +2,7 @@
 #define LEVENSHTEIN_H
 
 int levenshtein(const char *string1, const char *string2,
-       int swap_penalty, int substition_penalty,
+       int swap_penalty, int substitution_penalty,
        int insertion_penalty, int deletion_penalty);
 
 #endif
index 4c7f11ba84c67089dce7d725d87a4dd32a245c7f..82c7742e41f4645e7d6086bb277437a84ff4f03b 100644 (file)
@@ -63,8 +63,6 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
                        int flag, int marker_size)
 {
        xmparam_t xmp;
-       int style = 0;
-       int favor = (flag >> 1) & 03;
 
        if (buffer_is_binary(orig->ptr, orig->size) ||
            buffer_is_binary(src1->ptr, src1->size) ||
@@ -79,15 +77,13 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
        }
 
        memset(&xmp, 0, sizeof(xmp));
+       xmp.level = XDL_MERGE_ZEALOUS;
+       xmp.favor= (flag >> 1) & 03;
        if (git_xmerge_style >= 0)
-               style = git_xmerge_style;
+               xmp.style = git_xmerge_style;
        if (marker_size > 0)
                xmp.marker_size = marker_size;
-       return xdl_merge(orig,
-                        src1, name1,
-                        src2, name2,
-                        &xmp, XDL_MERGE_FLAGS(XDL_MERGE_ZEALOUS, style, favor),
-                        result);
+       return xdl_merge(orig, src1, name1, src2, name2, &xmp, result);
 }
 
 static int ll_union_merge(const struct ll_merge_driver *drv_unused,
@@ -98,44 +94,11 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
                          mmfile_t *src2, const char *name2,
                          int flag, int marker_size)
 {
-       char *src, *dst;
-       long size;
-       int status, saved_style;
-
-       /* We have to force the RCS "merge" style */
-       saved_style = git_xmerge_style;
-       git_xmerge_style = 0;
-       status = ll_xdl_merge(drv_unused, result, path_unused,
-                             orig, src1, NULL, src2, NULL,
-                             flag, marker_size);
-       git_xmerge_style = saved_style;
-       if (status <= 0)
-               return status;
-       size = result->size;
-       src = dst = result->ptr;
-       while (size) {
-               char ch;
-               if ((marker_size < size) &&
-                   (*src == '<' || *src == '=' || *src == '>')) {
-                       int i;
-                       ch = *src;
-                       for (i = 0; i < marker_size; i++)
-                               if (src[i] != ch)
-                                       goto not_a_marker;
-                       if (src[marker_size] != '\n')
-                               goto not_a_marker;
-                       src += marker_size + 1;
-                       size -= marker_size + 1;
-                       continue;
-               }
-       not_a_marker:
-               do {
-                       ch = *src++;
-                       *dst++ = ch;
-                       size--;
-               } while (ch != '\n' && size);
-       }
-       result->size = dst - result->ptr;
+       /* Use union favor */
+       flag = (flag & 1) | (XDL_MERGE_FAVOR_UNION << 1);
+       return ll_xdl_merge(drv_unused, result, path_unused,
+                           orig, src1, NULL, src2, NULL,
+                           flag, marker_size);
        return 0;
 }
 
index 27afcf697238a48c01dd49996f5263cd72a52eac..d3ae969f608b50a469aa60b2e925558a6bb437e5 100644 (file)
@@ -514,6 +514,16 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
                        return 0;
                else if (opt->combine_merges)
                        return do_diff_combined(opt, commit);
+               else if (opt->first_parent_only) {
+                       /*
+                        * Generate merge log entry only for the first
+                        * parent, showing summary diff of the others
+                        * we merged _in_.
+                        */
+                       diff_tree_sha1(parents->item->object.sha1, sha1, "", &opt->diffopt);
+                       log_tree_diff_flush(opt);
+                       return !opt->loginfo;
+               }
 
                /* If we show individual diffs, show the parent info */
                log->parent = parents->item;
index cb53b01c19159e66ef265bde7feceabebab42232..195ebf974435b0b08cd2be0f54c137218034008b 100644 (file)
@@ -599,23 +599,6 @@ struct merge_file_info
                 merge:1;
 };
 
-static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
-{
-       unsigned long size;
-       enum object_type type;
-
-       if (!hashcmp(sha1, null_sha1)) {
-               mm->ptr = xstrdup("");
-               mm->size = 0;
-               return;
-       }
-
-       mm->ptr = read_sha1_file(sha1, &type, &size);
-       if (!mm->ptr || type != OBJ_BLOB)
-               die("unable to read blob object %s", sha1_to_hex(sha1));
-       mm->size = size;
-}
-
 static int merge_3way(struct merge_options *o,
                      mmbuffer_t *result_buf,
                      struct diff_filespec *one,
@@ -653,9 +636,9 @@ static int merge_3way(struct merge_options *o,
                name2 = xstrdup(mkpath("%s", branch2));
        }
 
-       fill_mm(one->sha1, &orig);
-       fill_mm(a->sha1, &src1);
-       fill_mm(b->sha1, &src2);
+       read_mmblob(&orig, one->sha1);
+       read_mmblob(&src1, a->sha1);
+       read_mmblob(&src2, b->sha1);
 
        merge_status = ll_merge(result_buf, a->path, &orig,
                                &src1, name1, &src2, name2,
diff --git a/notes.c b/notes.c
index 023adce982c668f39b01652e525b54fd512d5603..e425e198278bfb5c6a039dc88825568f1518e875 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -1,10 +1,12 @@
 #include "cache.h"
-#include "commit.h"
 #include "notes.h"
-#include "refs.h"
+#include "blob.h"
+#include "tree.h"
 #include "utf8.h"
 #include "strbuf.h"
 #include "tree-walk.h"
+#include "string-list.h"
+#include "refs.h"
 
 /*
  * Use a non-balancing simple 16-tree structure with struct int_node as
@@ -25,10 +27,10 @@ struct int_node {
 /*
  * Leaf nodes come in two variants, note entries and subtree entries,
  * distinguished by the LSb of the leaf node pointer (see above).
- * As a note entry, the key is the SHA1 of the referenced commit, and the
+ * As a note entry, the key is the SHA1 of the referenced object, and the
  * value is the SHA1 of the note object.
  * As a subtree entry, the key is the prefix SHA1 (w/trailing NULs) of the
- * referenced commit, using the last byte of the key to store the length of
+ * referenced object, using the last byte of the key to store the length of
  * the prefix. The value is the SHA1 of the tree object containing the notes
  * subtree.
  */
@@ -37,6 +39,21 @@ struct leaf_node {
        unsigned char val_sha1[20];
 };
 
+/*
+ * A notes tree may contain entries that are not notes, and that do not follow
+ * the naming conventions of notes. There are typically none/few of these, but
+ * we still need to keep track of them. Keep a simple linked list sorted alpha-
+ * betically on the non-note path. The list is populated when parsing tree
+ * objects in load_subtree(), and the non-notes are correctly written back into
+ * the tree objects produced by write_notes_tree().
+ */
+struct non_note {
+       struct non_note *next; /* grounded (last->next == NULL) */
+       char *path;
+       unsigned int mode;
+       unsigned char sha1[20];
+};
+
 #define PTR_TYPE_NULL     0
 #define PTR_TYPE_INTERNAL 1
 #define PTR_TYPE_NOTE     2
@@ -46,17 +63,18 @@ struct leaf_node {
 #define CLR_PTR_TYPE(ptr)       ((void *) ((uintptr_t) (ptr) & ~3))
 #define SET_PTR_TYPE(ptr, type) ((void *) ((uintptr_t) (ptr) | (type)))
 
-#define GET_NIBBLE(n, sha1) (((sha1[n >> 1]) >> ((~n & 0x01) << 2)) & 0x0f)
+#define GET_NIBBLE(n, sha1) (((sha1[(n) >> 1]) >> ((~(n) & 0x01) << 2)) & 0x0f)
 
 #define SUBTREE_SHA1_PREFIXCMP(key_sha1, subtree_sha1) \
        (memcmp(key_sha1, subtree_sha1, subtree_sha1[19]))
 
-static struct int_node root_node;
+struct notes_tree default_notes_tree;
 
-static int initialized;
+static struct string_list display_notes_refs;
+static struct notes_tree **display_notes_trees;
 
-static void load_subtree(struct leaf_node *subtree, struct int_node *node,
-               unsigned int n);
+static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
+               struct int_node *node, unsigned int n);
 
 /*
  * Search the tree until the appropriate location for the given key is found:
@@ -73,7 +91,7 @@ static void load_subtree(struct leaf_node *subtree, struct int_node *node,
  *      - an unused leaf node (NULL)
  *      In any case, set *tree and *n, and return pointer to the tree location.
  */
-static void **note_tree_search(struct int_node **tree,
+static void **note_tree_search(struct notes_tree *t, struct int_node **tree,
                unsigned char *n, const unsigned char *key_sha1)
 {
        struct leaf_node *l;
@@ -85,27 +103,27 @@ static void **note_tree_search(struct int_node **tree,
                if (!SUBTREE_SHA1_PREFIXCMP(key_sha1, l->key_sha1)) {
                        /* unpack tree and resume search */
                        (*tree)->a[0] = NULL;
-                       load_subtree(l, *tree, *n);
+                       load_subtree(t, l, *tree, *n);
                        free(l);
-                       return note_tree_search(tree, n, key_sha1);
+                       return note_tree_search(t, tree, n, key_sha1);
                }
        }
 
        i = GET_NIBBLE(*n, key_sha1);
        p = (*tree)->a[i];
-       switch(GET_PTR_TYPE(p)) {
+       switch (GET_PTR_TYPE(p)) {
        case PTR_TYPE_INTERNAL:
                *tree = CLR_PTR_TYPE(p);
                (*n)++;
-               return note_tree_search(tree, n, key_sha1);
+               return note_tree_search(t, tree, n, key_sha1);
        case PTR_TYPE_SUBTREE:
                l = (struct leaf_node *) CLR_PTR_TYPE(p);
                if (!SUBTREE_SHA1_PREFIXCMP(key_sha1, l->key_sha1)) {
                        /* unpack tree and resume search */
                        (*tree)->a[i] = NULL;
-                       load_subtree(l, *tree, *n);
+                       load_subtree(t, l, *tree, *n);
                        free(l);
-                       return note_tree_search(tree, n, key_sha1);
+                       return note_tree_search(t, tree, n, key_sha1);
                }
                /* fall through */
        default:
@@ -118,10 +136,11 @@ static void **note_tree_search(struct int_node **tree,
  * Search to the tree location appropriate for the given key:
  * If a note entry with matching key, return the note entry, else return NULL.
  */
-static struct leaf_node *note_tree_find(struct int_node *tree, unsigned char n,
+static struct leaf_node *note_tree_find(struct notes_tree *t,
+               struct int_node *tree, unsigned char n,
                const unsigned char *key_sha1)
 {
-       void **p = note_tree_search(&tree, &n, key_sha1);
+       void **p = note_tree_search(t, &tree, &n, key_sha1);
        if (GET_PTR_TYPE(*p) == PTR_TYPE_NOTE) {
                struct leaf_node *l = (struct leaf_node *) CLR_PTR_TYPE(*p);
                if (!hashcmp(key_sha1, l->key_sha1))
@@ -130,55 +149,12 @@ static struct leaf_node *note_tree_find(struct int_node *tree, unsigned char n,
        return NULL;
 }
 
-/* Create a new blob object by concatenating the two given blob objects */
-static int concatenate_notes(unsigned char *cur_sha1,
-               const unsigned char *new_sha1)
-{
-       char *cur_msg, *new_msg, *buf;
-       unsigned long cur_len, new_len, buf_len;
-       enum object_type cur_type, new_type;
-       int ret;
-
-       /* read in both note blob objects */
-       new_msg = read_sha1_file(new_sha1, &new_type, &new_len);
-       if (!new_msg || !new_len || new_type != OBJ_BLOB) {
-               free(new_msg);
-               return 0;
-       }
-       cur_msg = read_sha1_file(cur_sha1, &cur_type, &cur_len);
-       if (!cur_msg || !cur_len || cur_type != OBJ_BLOB) {
-               free(cur_msg);
-               free(new_msg);
-               hashcpy(cur_sha1, new_sha1);
-               return 0;
-       }
-
-       /* we will separate the notes by a newline anyway */
-       if (cur_msg[cur_len - 1] == '\n')
-               cur_len--;
-
-       /* concatenate cur_msg and new_msg into buf */
-       buf_len = cur_len + 1 + new_len;
-       buf = (char *) xmalloc(buf_len);
-       memcpy(buf, cur_msg, cur_len);
-       buf[cur_len] = '\n';
-       memcpy(buf + cur_len + 1, new_msg, new_len);
-
-       free(cur_msg);
-       free(new_msg);
-
-       /* create a new blob object from buf */
-       ret = write_sha1_file(buf, buf_len, "blob", cur_sha1);
-       free(buf);
-       return ret;
-}
-
 /*
  * To insert a leaf_node:
  * Search to the tree location appropriate for the given leaf_node's key:
  * - If location is unused (NULL), store the tweaked pointer directly there
  * - If location holds a note entry that matches the note-to-be-inserted, then
- *   concatenate the two notes.
+ *   combine the two notes (by calling the given combine_notes function).
  * - If location holds a note entry that matches the subtree-to-be-inserted,
  *   then unpack the subtree-to-be-inserted into the location.
  * - If location holds a matching subtree entry, unpack the subtree at that
@@ -186,16 +162,17 @@ static int concatenate_notes(unsigned char *cur_sha1,
  * - Else, create a new int_node, holding both the node-at-location and the
  *   node-to-be-inserted, and store the new int_node into the location.
  */
-static void note_tree_insert(struct int_node *tree, unsigned char n,
-               struct leaf_node *entry, unsigned char type)
+static void note_tree_insert(struct notes_tree *t, struct int_node *tree,
+               unsigned char n, struct leaf_node *entry, unsigned char type,
+               combine_notes_fn combine_notes)
 {
        struct int_node *new_node;
        struct leaf_node *l;
-       void **p = note_tree_search(&tree, &n, entry->key_sha1);
+       void **p = note_tree_search(t, &tree, &n, entry->key_sha1);
 
        assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */
        l = (struct leaf_node *) CLR_PTR_TYPE(*p);
-       switch(GET_PTR_TYPE(*p)) {
+       switch (GET_PTR_TYPE(*p)) {
        case PTR_TYPE_NULL:
                assert(!*p);
                *p = SET_PTR_TYPE(entry, type);
@@ -208,12 +185,11 @@ static void note_tree_insert(struct int_node *tree, unsigned char n,
                                if (!hashcmp(l->val_sha1, entry->val_sha1))
                                        return;
 
-                               if (concatenate_notes(l->val_sha1,
-                                               entry->val_sha1))
-                                       die("failed to concatenate note %s "
-                                           "into note %s for commit %s",
-                                           sha1_to_hex(entry->val_sha1),
+                               if (combine_notes(l->val_sha1, entry->val_sha1))
+                                       die("failed to combine notes %s and %s"
+                                           " for object %s",
                                            sha1_to_hex(l->val_sha1),
+                                           sha1_to_hex(entry->val_sha1),
                                            sha1_to_hex(l->key_sha1));
                                free(entry);
                                return;
@@ -223,7 +199,7 @@ static void note_tree_insert(struct int_node *tree, unsigned char n,
                        if (!SUBTREE_SHA1_PREFIXCMP(l->key_sha1,
                                                    entry->key_sha1)) {
                                /* unpack 'entry' */
-                               load_subtree(entry, tree, n);
+                               load_subtree(t, entry, tree, n);
                                free(entry);
                                return;
                        }
@@ -234,9 +210,10 @@ static void note_tree_insert(struct int_node *tree, unsigned char n,
                if (!SUBTREE_SHA1_PREFIXCMP(entry->key_sha1, l->key_sha1)) {
                        /* unpack 'l' and restart insert */
                        *p = NULL;
-                       load_subtree(l, tree, n);
+                       load_subtree(t, l, tree, n);
                        free(l);
-                       note_tree_insert(tree, n, entry, type);
+                       note_tree_insert(t, tree, n, entry, type,
+                                        combine_notes);
                        return;
                }
                break;
@@ -246,9 +223,83 @@ static void note_tree_insert(struct int_node *tree, unsigned char n,
        assert(GET_PTR_TYPE(*p) == PTR_TYPE_NOTE ||
               GET_PTR_TYPE(*p) == PTR_TYPE_SUBTREE);
        new_node = (struct int_node *) xcalloc(sizeof(struct int_node), 1);
-       note_tree_insert(new_node, n + 1, l, GET_PTR_TYPE(*p));
+       note_tree_insert(t, new_node, n + 1, l, GET_PTR_TYPE(*p),
+                        combine_notes);
        *p = SET_PTR_TYPE(new_node, PTR_TYPE_INTERNAL);
-       note_tree_insert(new_node, n + 1, entry, type);
+       note_tree_insert(t, new_node, n + 1, entry, type, combine_notes);
+}
+
+/*
+ * How to consolidate an int_node:
+ * If there are > 1 non-NULL entries, give up and return non-zero.
+ * Otherwise replace the int_node at the given index in the given parent node
+ * with the only entry (or a NULL entry if no entries) from the given tree,
+ * and return 0.
+ */
+static int note_tree_consolidate(struct int_node *tree,
+       struct int_node *parent, unsigned char index)
+{
+       unsigned int i;
+       void *p = NULL;
+
+       assert(tree && parent);
+       assert(CLR_PTR_TYPE(parent->a[index]) == tree);
+
+       for (i = 0; i < 16; i++) {
+               if (GET_PTR_TYPE(tree->a[i]) != PTR_TYPE_NULL) {
+                       if (p) /* more than one entry */
+                               return -2;
+                       p = tree->a[i];
+               }
+       }
+
+       /* replace tree with p in parent[index] */
+       parent->a[index] = p;
+       free(tree);
+       return 0;
+}
+
+/*
+ * To remove a leaf_node:
+ * Search to the tree location appropriate for the given leaf_node's key:
+ * - If location does not hold a matching entry, abort and do nothing.
+ * - Replace the matching leaf_node with a NULL entry (and free the leaf_node).
+ * - Consolidate int_nodes repeatedly, while walking up the tree towards root.
+ */
+static void note_tree_remove(struct notes_tree *t, struct int_node *tree,
+               unsigned char n, struct leaf_node *entry)
+{
+       struct leaf_node *l;
+       struct int_node *parent_stack[20];
+       unsigned char i, j;
+       void **p = note_tree_search(t, &tree, &n, entry->key_sha1);
+
+       assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */
+       if (GET_PTR_TYPE(*p) != PTR_TYPE_NOTE)
+               return; /* type mismatch, nothing to remove */
+       l = (struct leaf_node *) CLR_PTR_TYPE(*p);
+       if (hashcmp(l->key_sha1, entry->key_sha1))
+               return; /* key mismatch, nothing to remove */
+
+       /* we have found a matching entry */
+       free(l);
+       *p = SET_PTR_TYPE(NULL, PTR_TYPE_NULL);
+
+       /* consolidate this tree level, and parent levels, if possible */
+       if (!n)
+               return; /* cannot consolidate top level */
+       /* first, build stack of ancestors between root and current node */
+       parent_stack[0] = t->root;
+       for (i = 0; i < n; i++) {
+               j = GET_NIBBLE(i, entry->key_sha1);
+               parent_stack[i + 1] = CLR_PTR_TYPE(parent_stack[i]->a[j]);
+       }
+       assert(i == n && parent_stack[i] == tree);
+       /* next, unwind stack until note_tree_consolidate() is done */
+       while (i > 0 &&
+              !note_tree_consolidate(parent_stack[i], parent_stack[i - 1],
+                                     GET_NIBBLE(i - 1, entry->key_sha1)))
+               i--;
 }
 
 /* Free the entire notes data contained in the given tree */
@@ -257,7 +308,7 @@ static void note_tree_free(struct int_node *tree)
        unsigned int i;
        for (i = 0; i < 16; i++) {
                void *p = tree->a[i];
-               switch(GET_PTR_TYPE(p)) {
+               switch (GET_PTR_TYPE(p)) {
                case PTR_TYPE_INTERNAL:
                        note_tree_free(CLR_PTR_TYPE(p));
                        /* fall through */
@@ -274,7 +325,7 @@ static void note_tree_free(struct int_node *tree)
  * - hex_len  - Length of above segment. Must be multiple of 2 between 0 and 40
  * - sha1     - Partial SHA1 value is written here
  * - sha1_len - Max #bytes to store in sha1, Must be >= hex_len / 2, and < 20
- * Returns -1 on error (invalid arguments or invalid SHA1 (not in hex format).
+ * Returns -1 on error (invalid arguments or invalid SHA1 (not in hex format)).
  * Otherwise, returns number of bytes written to sha1 (i.e. hex_len / 2).
  * Pads sha1 with NULs up to sha1_len (not included in returned length).
  */
@@ -296,14 +347,67 @@ static int get_sha1_hex_segment(const char *hex, unsigned int hex_len,
        return len;
 }
 
-static void load_subtree(struct leaf_node *subtree, struct int_node *node,
-               unsigned int n)
+static int non_note_cmp(const struct non_note *a, const struct non_note *b)
+{
+       return strcmp(a->path, b->path);
+}
+
+static void add_non_note(struct notes_tree *t, const char *path,
+               unsigned int mode, const unsigned char *sha1)
+{
+       struct non_note *p = t->prev_non_note, *n;
+       n = (struct non_note *) xmalloc(sizeof(struct non_note));
+       n->next = NULL;
+       n->path = xstrdup(path);
+       n->mode = mode;
+       hashcpy(n->sha1, sha1);
+       t->prev_non_note = n;
+
+       if (!t->first_non_note) {
+               t->first_non_note = n;
+               return;
+       }
+
+       if (non_note_cmp(p, n) < 0)
+               ; /* do nothing  */
+       else if (non_note_cmp(t->first_non_note, n) <= 0)
+               p = t->first_non_note;
+       else {
+               /* n sorts before t->first_non_note */
+               n->next = t->first_non_note;
+               t->first_non_note = n;
+               return;
+       }
+
+       /* n sorts equal or after p */
+       while (p->next && non_note_cmp(p->next, n) <= 0)
+               p = p->next;
+
+       if (non_note_cmp(p, n) == 0) { /* n ~= p; overwrite p with n */
+               assert(strcmp(p->path, n->path) == 0);
+               p->mode = n->mode;
+               hashcpy(p->sha1, n->sha1);
+               free(n);
+               t->prev_non_note = p;
+               return;
+       }
+
+       /* n sorts between p and p->next */
+       n->next = p->next;
+       p->next = n;
+}
+
+static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
+               struct int_node *node, unsigned int n)
 {
-       unsigned char commit_sha1[20];
+       unsigned char object_sha1[20];
        unsigned int prefix_len;
        void *buf;
        struct tree_desc desc;
        struct name_entry entry;
+       int len, path_len;
+       unsigned char type;
+       struct leaf_node *l;
 
        buf = fill_tree_descriptor(&desc, subtree->val_sha1);
        if (!buf)
@@ -312,86 +416,721 @@ static void load_subtree(struct leaf_node *subtree, struct int_node *node,
 
        prefix_len = subtree->key_sha1[19];
        assert(prefix_len * 2 >= n);
-       memcpy(commit_sha1, subtree->key_sha1, prefix_len);
+       memcpy(object_sha1, subtree->key_sha1, prefix_len);
        while (tree_entry(&desc, &entry)) {
-               int len = get_sha1_hex_segment(entry.path, strlen(entry.path),
-                               commit_sha1 + prefix_len, 20 - prefix_len);
+               path_len = strlen(entry.path);
+               len = get_sha1_hex_segment(entry.path, path_len,
+                               object_sha1 + prefix_len, 20 - prefix_len);
                if (len < 0)
-                       continue; /* entry.path is not a SHA1 sum. Skip */
+                       goto handle_non_note; /* entry.path is not a SHA1 */
                len += prefix_len;
 
                /*
-                * If commit SHA1 is complete (len == 20), assume note object
-                * If commit SHA1 is incomplete (len < 20), assume note subtree
+                * If object SHA1 is complete (len == 20), assume note object
+                * If object SHA1 is incomplete (len < 20), and current
+                * component consists of 2 hex chars, assume note subtree
                 */
                if (len <= 20) {
-                       unsigned char type = PTR_TYPE_NOTE;
-                       struct leaf_node *l = (struct leaf_node *)
+                       type = PTR_TYPE_NOTE;
+                       l = (struct leaf_node *)
                                xcalloc(sizeof(struct leaf_node), 1);
-                       hashcpy(l->key_sha1, commit_sha1);
+                       hashcpy(l->key_sha1, object_sha1);
                        hashcpy(l->val_sha1, entry.sha1);
                        if (len < 20) {
-                               if (!S_ISDIR(entry.mode))
-                                       continue; /* entry cannot be subtree */
+                               if (!S_ISDIR(entry.mode) || path_len != 2)
+                                       goto handle_non_note; /* not subtree */
                                l->key_sha1[19] = (unsigned char) len;
                                type = PTR_TYPE_SUBTREE;
                        }
-                       note_tree_insert(node, n, l, type);
+                       note_tree_insert(t, node, n, l, type,
+                                        combine_notes_concatenate);
+               }
+               continue;
+
+handle_non_note:
+               /*
+                * Determine full path for this non-note entry:
+                * The filename is already found in entry.path, but the
+                * directory part of the path must be deduced from the subtree
+                * containing this entry. We assume here that the overall notes
+                * tree follows a strict byte-based progressive fanout
+                * structure (i.e. using 2/38, 2/2/36, etc. fanouts, and not
+                * e.g. 4/36 fanout). This means that if a non-note is found at
+                * path "dead/beef", the following code will register it as
+                * being found on "de/ad/beef".
+                * On the other hand, if you use such non-obvious non-note
+                * paths in the middle of a notes tree, you deserve what's
+                * coming to you ;). Note that for non-notes that are not
+                * SHA1-like at the top level, there will be no problems.
+                *
+                * To conclude, it is strongly advised to make sure non-notes
+                * have at least one non-hex character in the top-level path
+                * component.
+                */
+               {
+                       char non_note_path[PATH_MAX];
+                       char *p = non_note_path;
+                       const char *q = sha1_to_hex(subtree->key_sha1);
+                       int i;
+                       for (i = 0; i < prefix_len; i++) {
+                               *p++ = *q++;
+                               *p++ = *q++;
+                               *p++ = '/';
+                       }
+                       strcpy(p, entry.path);
+                       add_non_note(t, non_note_path, entry.mode, entry.sha1);
+               }
+       }
+       free(buf);
+}
+
+/*
+ * Determine optimal on-disk fanout for this part of the notes tree
+ *
+ * Given a (sub)tree and the level in the internal tree structure, determine
+ * whether or not the given existing fanout should be expanded for this
+ * (sub)tree.
+ *
+ * Values of the 'fanout' variable:
+ * - 0: No fanout (all notes are stored directly in the root notes tree)
+ * - 1: 2/38 fanout
+ * - 2: 2/2/36 fanout
+ * - 3: 2/2/2/34 fanout
+ * etc.
+ */
+static unsigned char determine_fanout(struct int_node *tree, unsigned char n,
+               unsigned char fanout)
+{
+       /*
+        * The following is a simple heuristic that works well in practice:
+        * For each even-numbered 16-tree level (remember that each on-disk
+        * fanout level corresponds to _two_ 16-tree levels), peek at all 16
+        * entries at that tree level. If all of them are either int_nodes or
+        * subtree entries, then there are likely plenty of notes below this
+        * level, so we return an incremented fanout.
+        */
+       unsigned int i;
+       if ((n % 2) || (n > 2 * fanout))
+               return fanout;
+       for (i = 0; i < 16; i++) {
+               switch (GET_PTR_TYPE(tree->a[i])) {
+               case PTR_TYPE_SUBTREE:
+               case PTR_TYPE_INTERNAL:
+                       continue;
+               default:
+                       return fanout;
+               }
+       }
+       return fanout + 1;
+}
+
+static void construct_path_with_fanout(const unsigned char *sha1,
+               unsigned char fanout, char *path)
+{
+       unsigned int i = 0, j = 0;
+       const char *hex_sha1 = sha1_to_hex(sha1);
+       assert(fanout < 20);
+       while (fanout) {
+               path[i++] = hex_sha1[j++];
+               path[i++] = hex_sha1[j++];
+               path[i++] = '/';
+               fanout--;
+       }
+       strcpy(path + i, hex_sha1 + j);
+}
+
+static int for_each_note_helper(struct notes_tree *t, struct int_node *tree,
+               unsigned char n, unsigned char fanout, int flags,
+               each_note_fn fn, void *cb_data)
+{
+       unsigned int i;
+       void *p;
+       int ret = 0;
+       struct leaf_node *l;
+       static char path[40 + 19 + 1];  /* hex SHA1 + 19 * '/' + NUL */
+
+       fanout = determine_fanout(tree, n, fanout);
+       for (i = 0; i < 16; i++) {
+redo:
+               p = tree->a[i];
+               switch (GET_PTR_TYPE(p)) {
+               case PTR_TYPE_INTERNAL:
+                       /* recurse into int_node */
+                       ret = for_each_note_helper(t, CLR_PTR_TYPE(p), n + 1,
+                               fanout, flags, fn, cb_data);
+                       break;
+               case PTR_TYPE_SUBTREE:
+                       l = (struct leaf_node *) CLR_PTR_TYPE(p);
+                       /*
+                        * Subtree entries in the note tree represent parts of
+                        * the note tree that have not yet been explored. There
+                        * is a direct relationship between subtree entries at
+                        * level 'n' in the tree, and the 'fanout' variable:
+                        * Subtree entries at level 'n <= 2 * fanout' should be
+                        * preserved, since they correspond exactly to a fanout
+                        * directory in the on-disk structure. However, subtree
+                        * entries at level 'n > 2 * fanout' should NOT be
+                        * preserved, but rather consolidated into the above
+                        * notes tree level. We achieve this by unconditionally
+                        * unpacking subtree entries that exist below the
+                        * threshold level at 'n = 2 * fanout'.
+                        */
+                       if (n <= 2 * fanout &&
+                           flags & FOR_EACH_NOTE_YIELD_SUBTREES) {
+                               /* invoke callback with subtree */
+                               unsigned int path_len =
+                                       l->key_sha1[19] * 2 + fanout;
+                               assert(path_len < 40 + 19);
+                               construct_path_with_fanout(l->key_sha1, fanout,
+                                                          path);
+                               /* Create trailing slash, if needed */
+                               if (path[path_len - 1] != '/')
+                                       path[path_len++] = '/';
+                               path[path_len] = '\0';
+                               ret = fn(l->key_sha1, l->val_sha1, path,
+                                        cb_data);
+                       }
+                       if (n > fanout * 2 ||
+                           !(flags & FOR_EACH_NOTE_DONT_UNPACK_SUBTREES)) {
+                               /* unpack subtree and resume traversal */
+                               tree->a[i] = NULL;
+                               load_subtree(t, l, tree, n);
+                               free(l);
+                               goto redo;
+                       }
+                       break;
+               case PTR_TYPE_NOTE:
+                       l = (struct leaf_node *) CLR_PTR_TYPE(p);
+                       construct_path_with_fanout(l->key_sha1, fanout, path);
+                       ret = fn(l->key_sha1, l->val_sha1, path, cb_data);
+                       break;
+               }
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+struct tree_write_stack {
+       struct tree_write_stack *next;
+       struct strbuf buf;
+       char path[2]; /* path to subtree in next, if any */
+};
+
+static inline int matches_tree_write_stack(struct tree_write_stack *tws,
+               const char *full_path)
+{
+       return  full_path[0] == tws->path[0] &&
+               full_path[1] == tws->path[1] &&
+               full_path[2] == '/';
+}
+
+static void write_tree_entry(struct strbuf *buf, unsigned int mode,
+               const char *path, unsigned int path_len, const
+               unsigned char *sha1)
+{
+       strbuf_addf(buf, "%o %.*s%c", mode, path_len, path, '\0');
+       strbuf_add(buf, sha1, 20);
+}
+
+static void tree_write_stack_init_subtree(struct tree_write_stack *tws,
+               const char *path)
+{
+       struct tree_write_stack *n;
+       assert(!tws->next);
+       assert(tws->path[0] == '\0' && tws->path[1] == '\0');
+       n = (struct tree_write_stack *)
+               xmalloc(sizeof(struct tree_write_stack));
+       n->next = NULL;
+       strbuf_init(&n->buf, 256 * (32 + 40)); /* assume 256 entries per tree */
+       n->path[0] = n->path[1] = '\0';
+       tws->next = n;
+       tws->path[0] = path[0];
+       tws->path[1] = path[1];
+}
+
+static int tree_write_stack_finish_subtree(struct tree_write_stack *tws)
+{
+       int ret;
+       struct tree_write_stack *n = tws->next;
+       unsigned char s[20];
+       if (n) {
+               ret = tree_write_stack_finish_subtree(n);
+               if (ret)
+                       return ret;
+               ret = write_sha1_file(n->buf.buf, n->buf.len, tree_type, s);
+               if (ret)
+                       return ret;
+               strbuf_release(&n->buf);
+               free(n);
+               tws->next = NULL;
+               write_tree_entry(&tws->buf, 040000, tws->path, 2, s);
+               tws->path[0] = tws->path[1] = '\0';
+       }
+       return 0;
+}
+
+static int write_each_note_helper(struct tree_write_stack *tws,
+               const char *path, unsigned int mode,
+               const unsigned char *sha1)
+{
+       size_t path_len = strlen(path);
+       unsigned int n = 0;
+       int ret;
+
+       /* Determine common part of tree write stack */
+       while (tws && 3 * n < path_len &&
+              matches_tree_write_stack(tws, path + 3 * n)) {
+               n++;
+               tws = tws->next;
+       }
+
+       /* tws point to last matching tree_write_stack entry */
+       ret = tree_write_stack_finish_subtree(tws);
+       if (ret)
+               return ret;
+
+       /* Start subtrees needed to satisfy path */
+       while (3 * n + 2 < path_len && path[3 * n + 2] == '/') {
+               tree_write_stack_init_subtree(tws, path + 3 * n);
+               n++;
+               tws = tws->next;
+       }
+
+       /* There should be no more directory components in the given path */
+       assert(memchr(path + 3 * n, '/', path_len - (3 * n)) == NULL);
+
+       /* Finally add given entry to the current tree object */
+       write_tree_entry(&tws->buf, mode, path + 3 * n, path_len - (3 * n),
+                        sha1);
+
+       return 0;
+}
+
+struct write_each_note_data {
+       struct tree_write_stack *root;
+       struct non_note *next_non_note;
+};
+
+static int write_each_non_note_until(const char *note_path,
+               struct write_each_note_data *d)
+{
+       struct non_note *n = d->next_non_note;
+       int cmp, ret;
+       while (n && (!note_path || (cmp = strcmp(n->path, note_path)) <= 0)) {
+               if (note_path && cmp == 0)
+                       ; /* do nothing, prefer note to non-note */
+               else {
+                       ret = write_each_note_helper(d->root, n->path, n->mode,
+                                                    n->sha1);
+                       if (ret)
+                               return ret;
                }
+               n = n->next;
+       }
+       d->next_non_note = n;
+       return 0;
+}
+
+static int write_each_note(const unsigned char *object_sha1,
+               const unsigned char *note_sha1, char *note_path,
+               void *cb_data)
+{
+       struct write_each_note_data *d =
+               (struct write_each_note_data *) cb_data;
+       size_t note_path_len = strlen(note_path);
+       unsigned int mode = 0100644;
+
+       if (note_path[note_path_len - 1] == '/') {
+               /* subtree entry */
+               note_path_len--;
+               note_path[note_path_len] = '\0';
+               mode = 040000;
        }
+       assert(note_path_len <= 40 + 19);
+
+       /* Weave non-note entries into note entries */
+       return  write_each_non_note_until(note_path, d) ||
+               write_each_note_helper(d->root, note_path, mode, note_sha1);
+}
+
+struct note_delete_list {
+       struct note_delete_list *next;
+       const unsigned char *sha1;
+};
+
+static int prune_notes_helper(const unsigned char *object_sha1,
+               const unsigned char *note_sha1, char *note_path,
+               void *cb_data)
+{
+       struct note_delete_list **l = (struct note_delete_list **) cb_data;
+       struct note_delete_list *n;
+
+       if (has_sha1_file(object_sha1))
+               return 0; /* nothing to do for this note */
+
+       /* failed to find object => prune this note */
+       n = (struct note_delete_list *) xmalloc(sizeof(*n));
+       n->next = *l;
+       n->sha1 = object_sha1;
+       *l = n;
+       return 0;
+}
+
+int combine_notes_concatenate(unsigned char *cur_sha1,
+               const unsigned char *new_sha1)
+{
+       char *cur_msg = NULL, *new_msg = NULL, *buf;
+       unsigned long cur_len, new_len, buf_len;
+       enum object_type cur_type, new_type;
+       int ret;
+
+       /* read in both note blob objects */
+       if (!is_null_sha1(new_sha1))
+               new_msg = read_sha1_file(new_sha1, &new_type, &new_len);
+       if (!new_msg || !new_len || new_type != OBJ_BLOB) {
+               free(new_msg);
+               return 0;
+       }
+       if (!is_null_sha1(cur_sha1))
+               cur_msg = read_sha1_file(cur_sha1, &cur_type, &cur_len);
+       if (!cur_msg || !cur_len || cur_type != OBJ_BLOB) {
+               free(cur_msg);
+               free(new_msg);
+               hashcpy(cur_sha1, new_sha1);
+               return 0;
+       }
+
+       /* we will separate the notes by a newline anyway */
+       if (cur_msg[cur_len - 1] == '\n')
+               cur_len--;
+
+       /* concatenate cur_msg and new_msg into buf */
+       buf_len = cur_len + 1 + new_len;
+       buf = (char *) xmalloc(buf_len);
+       memcpy(buf, cur_msg, cur_len);
+       buf[cur_len] = '\n';
+       memcpy(buf + cur_len + 1, new_msg, new_len);
+       free(cur_msg);
+       free(new_msg);
+
+       /* create a new blob object from buf */
+       ret = write_sha1_file(buf, buf_len, blob_type, cur_sha1);
        free(buf);
+       return ret;
+}
+
+int combine_notes_overwrite(unsigned char *cur_sha1,
+               const unsigned char *new_sha1)
+{
+       hashcpy(cur_sha1, new_sha1);
+       return 0;
+}
+
+int combine_notes_ignore(unsigned char *cur_sha1,
+               const unsigned char *new_sha1)
+{
+       return 0;
+}
+
+static int string_list_add_one_ref(const char *path, const unsigned char *sha1,
+                                  int flag, void *cb)
+{
+       struct string_list *refs = cb;
+       if (!unsorted_string_list_has_string(refs, path))
+               string_list_append(path, refs);
+       return 0;
+}
+
+void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
+{
+       if (has_glob_specials(glob)) {
+               for_each_glob_ref(string_list_add_one_ref, glob, list);
+       } else {
+               unsigned char sha1[20];
+               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);
+       }
+}
+
+void string_list_add_refs_from_colon_sep(struct string_list *list,
+                                        const char *globs)
+{
+       struct strbuf globbuf = STRBUF_INIT;
+       struct strbuf **split;
+       int i;
+
+       strbuf_addstr(&globbuf, globs);
+       split = strbuf_split(&globbuf, ':');
+
+       for (i = 0; split[i]; i++) {
+               if (!split[i]->len)
+                       continue;
+               if (split[i]->buf[split[i]->len-1] == ':')
+                       strbuf_setlen(split[i], split[i]->len-1);
+               string_list_add_refs_by_glob(list, split[i]->buf);
+       }
+
+       strbuf_list_free(split);
+       strbuf_release(&globbuf);
+}
+
+static int string_list_add_refs_from_list(struct string_list_item *item,
+                                         void *cb)
+{
+       struct string_list *list = cb;
+       string_list_add_refs_by_glob(list, item->string);
+       return 0;
+}
+
+static int notes_display_config(const char *k, const char *v, void *cb)
+{
+       int *load_refs = cb;
+
+       if (*load_refs && !strcmp(k, "notes.displayref")) {
+               if (!v)
+                       config_error_nonbool(k);
+               string_list_add_refs_by_glob(&display_notes_refs, v);
+       }
+
+       return 0;
 }
 
-static void initialize_notes(const char *notes_ref_name)
+static const char *default_notes_ref(void)
 {
-       unsigned char sha1[20], commit_sha1[20];
+       const char *notes_ref = NULL;
+       if (!notes_ref)
+               notes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);
+       if (!notes_ref)
+               notes_ref = notes_ref_name; /* value of core.notesRef config */
+       if (!notes_ref)
+               notes_ref = GIT_NOTES_DEFAULT_REF;
+       return notes_ref;
+}
+
+void init_notes(struct notes_tree *t, const char *notes_ref,
+               combine_notes_fn combine_notes, int flags)
+{
+       unsigned char sha1[20], object_sha1[20];
        unsigned mode;
        struct leaf_node root_tree;
 
-       if (!notes_ref_name || read_ref(notes_ref_name, commit_sha1) ||
-           get_tree_entry(commit_sha1, "", sha1, &mode))
+       if (!t)
+               t = &default_notes_tree;
+       assert(!t->initialized);
+
+       if (!notes_ref)
+               notes_ref = default_notes_ref();
+
+       if (!combine_notes)
+               combine_notes = combine_notes_concatenate;
+
+       t->root = (struct int_node *) xcalloc(sizeof(struct int_node), 1);
+       t->first_non_note = NULL;
+       t->prev_non_note = NULL;
+       t->ref = notes_ref ? xstrdup(notes_ref) : NULL;
+       t->combine_notes = combine_notes;
+       t->initialized = 1;
+       t->dirty = 0;
+
+       if (flags & NOTES_INIT_EMPTY || !notes_ref ||
+           read_ref(notes_ref, object_sha1))
                return;
+       if (get_tree_entry(object_sha1, "", sha1, &mode))
+               die("Failed to read notes tree referenced by %s (%s)",
+                   notes_ref, object_sha1);
 
        hashclr(root_tree.key_sha1);
        hashcpy(root_tree.val_sha1, sha1);
-       load_subtree(&root_tree, &root_node, 0);
+       load_subtree(t, &root_tree, t->root, 0);
 }
 
-static unsigned char *lookup_notes(const unsigned char *commit_sha1)
+struct load_notes_cb_data {
+       int counter;
+       struct notes_tree **trees;
+};
+
+static int load_one_display_note_ref(struct string_list_item *item,
+                                    void *cb_data)
 {
-       struct leaf_node *found = note_tree_find(&root_node, 0, commit_sha1);
-       if (found)
-               return found->val_sha1;
-       return NULL;
+       struct load_notes_cb_data *c = cb_data;
+       struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
+       init_notes(t, item->string, combine_notes_ignore, 0);
+       c->trees[c->counter++] = t;
+       return 0;
 }
 
-void free_notes(void)
+struct notes_tree **load_notes_trees(struct string_list *refs)
 {
-       note_tree_free(&root_node);
-       memset(&root_node, 0, sizeof(struct int_node));
-       initialized = 0;
+       struct notes_tree **trees;
+       struct load_notes_cb_data cb_data;
+       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);
+       trees[cb_data.counter] = NULL;
+       return trees;
+}
+
+void init_display_notes(struct display_notes_opt *opt)
+{
+       char *display_ref_env;
+       int load_config_refs = 0;
+       display_notes_refs.strdup_strings = 1;
+
+       assert(!display_notes_trees);
+
+       if (!opt || !opt->suppress_default_notes) {
+               string_list_append(default_notes_ref(), &display_notes_refs);
+               display_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT);
+               if (display_ref_env) {
+                       string_list_add_refs_from_colon_sep(&display_notes_refs,
+                                                           display_ref_env);
+                       load_config_refs = 0;
+               } else
+                       load_config_refs = 1;
+       }
+
+       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,
+                                    &display_notes_refs);
+
+       display_notes_trees = load_notes_trees(&display_notes_refs);
+       string_list_clear(&display_notes_refs, 0);
+}
+
+void add_note(struct notes_tree *t, const unsigned char *object_sha1,
+               const unsigned char *note_sha1, combine_notes_fn combine_notes)
+{
+       struct leaf_node *l;
+
+       if (!t)
+               t = &default_notes_tree;
+       assert(t->initialized);
+       t->dirty = 1;
+       if (!combine_notes)
+               combine_notes = t->combine_notes;
+       l = (struct leaf_node *) xmalloc(sizeof(struct leaf_node));
+       hashcpy(l->key_sha1, object_sha1);
+       hashcpy(l->val_sha1, note_sha1);
+       note_tree_insert(t, t->root, 0, l, PTR_TYPE_NOTE, combine_notes);
+}
+
+void remove_note(struct notes_tree *t, const unsigned char *object_sha1)
+{
+       struct leaf_node l;
+
+       if (!t)
+               t = &default_notes_tree;
+       assert(t->initialized);
+       t->dirty = 1;
+       hashcpy(l.key_sha1, object_sha1);
+       hashclr(l.val_sha1);
+       note_tree_remove(t, t->root, 0, &l);
 }
 
-void get_commit_notes(const struct commit *commit, struct strbuf *sb,
-               const char *output_encoding, int flags)
+const unsigned char *get_note(struct notes_tree *t,
+               const unsigned char *object_sha1)
+{
+       struct leaf_node *found;
+
+       if (!t)
+               t = &default_notes_tree;
+       assert(t->initialized);
+       found = note_tree_find(t, t->root, 0, object_sha1);
+       return found ? found->val_sha1 : NULL;
+}
+
+int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,
+               void *cb_data)
+{
+       if (!t)
+               t = &default_notes_tree;
+       assert(t->initialized);
+       return for_each_note_helper(t, t->root, 0, 0, flags, fn, cb_data);
+}
+
+int write_notes_tree(struct notes_tree *t, unsigned char *result)
+{
+       struct tree_write_stack root;
+       struct write_each_note_data cb_data;
+       int ret;
+
+       if (!t)
+               t = &default_notes_tree;
+       assert(t->initialized);
+
+       /* Prepare for traversal of current notes tree */
+       root.next = NULL; /* last forward entry in list is grounded */
+       strbuf_init(&root.buf, 256 * (32 + 40)); /* assume 256 entries */
+       root.path[0] = root.path[1] = '\0';
+       cb_data.root = &root;
+       cb_data.next_non_note = t->first_non_note;
+
+       /* Write tree objects representing current notes tree */
+       ret = for_each_note(t, FOR_EACH_NOTE_DONT_UNPACK_SUBTREES |
+                               FOR_EACH_NOTE_YIELD_SUBTREES,
+                       write_each_note, &cb_data) ||
+               write_each_non_note_until(NULL, &cb_data) ||
+               tree_write_stack_finish_subtree(&root) ||
+               write_sha1_file(root.buf.buf, root.buf.len, tree_type, result);
+       strbuf_release(&root.buf);
+       return ret;
+}
+
+void prune_notes(struct notes_tree *t)
+{
+       struct note_delete_list *l = NULL;
+
+       if (!t)
+               t = &default_notes_tree;
+       assert(t->initialized);
+
+       for_each_note(t, 0, prune_notes_helper, &l);
+
+       while (l) {
+               remove_note(t, l->sha1);
+               l = l->next;
+       }
+}
+
+void free_notes(struct notes_tree *t)
+{
+       if (!t)
+               t = &default_notes_tree;
+       if (t->root)
+               note_tree_free(t->root);
+       free(t->root);
+       while (t->first_non_note) {
+               t->prev_non_note = t->first_non_note->next;
+               free(t->first_non_note->path);
+               free(t->first_non_note);
+               t->first_non_note = t->prev_non_note;
+       }
+       free(t->ref);
+       memset(t, 0, sizeof(struct notes_tree));
+}
+
+void format_note(struct notes_tree *t, const unsigned char *object_sha1,
+               struct strbuf *sb, const char *output_encoding, int flags)
 {
        static const char utf8[] = "utf-8";
-       unsigned char *sha1;
+       const unsigned char *sha1;
        char *msg, *msg_p;
        unsigned long linelen, msglen;
        enum object_type type;
 
-       if (!initialized) {
-               const char *env = getenv(GIT_NOTES_REF_ENVIRONMENT);
-               if (env)
-                       notes_ref_name = getenv(GIT_NOTES_REF_ENVIRONMENT);
-               else if (!notes_ref_name)
-                       notes_ref_name = GIT_NOTES_DEFAULT_REF;
-               initialize_notes(notes_ref_name);
-               initialized = 1;
-       }
+       if (!t)
+               t = &default_notes_tree;
+       if (!t->initialized)
+               init_notes(t, NULL, NULL, 0);
 
-       sha1 = lookup_notes(commit->object.sha1);
+       sha1 = get_note(t, object_sha1);
        if (!sha1)
                return;
 
@@ -415,8 +1154,18 @@ void get_commit_notes(const struct commit *commit, struct strbuf *sb,
        if (msglen && msg[msglen - 1] == '\n')
                msglen--;
 
-       if (flags & NOTES_SHOW_HEADER)
-               strbuf_addstr(sb, "\nNotes:\n");
+       if (flags & NOTES_SHOW_HEADER) {
+               const char *ref = t->ref;
+               if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {
+                       strbuf_addstr(sb, "\nNotes:\n");
+               } else {
+                       if (!prefixcmp(ref, "refs/"))
+                               ref += 5;
+                       if (!prefixcmp(ref, "notes/"))
+                               ref += 6;
+                       strbuf_addf(sb, "\nNotes (%s):\n", ref);
+               }
+       }
 
        for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
                linelen = strchrnul(msg_p, '\n') - msg_p;
@@ -429,3 +1178,31 @@ void get_commit_notes(const struct commit *commit, struct strbuf *sb,
 
        free(msg);
 }
+
+void format_display_notes(const unsigned char *object_sha1,
+                         struct strbuf *sb, const char *output_encoding, int flags)
+{
+       int i;
+       assert(display_notes_trees);
+       for (i = 0; display_notes_trees[i]; i++)
+               format_note(display_notes_trees[i], object_sha1, sb,
+                           output_encoding, flags);
+}
+
+int copy_note(struct notes_tree *t,
+             const unsigned char *from_obj, const unsigned char *to_obj,
+             int force, combine_notes_fn combine_fn)
+{
+       const unsigned char *note = get_note(t, from_obj);
+       const unsigned char *existing_note = get_note(t, to_obj);
+
+       if (!force && existing_note)
+               return 1;
+
+       if (note)
+               add_note(t, to_obj, note, combine_fn);
+       else if (existing_note)
+               add_note(t, to_obj, null_sha1, combine_fn);
+
+       return 0;
+}
diff --git a/notes.h b/notes.h
index a1421e351ae0a6f97824b150140e49ddd5d3414d..ee65bd1a2430d4af53f03fd2cc9faa916863408c 100644 (file)
--- a/notes.h
+++ b/notes.h
 #ifndef NOTES_H
 #define NOTES_H
 
-/* Free (and de-initialize) the internal notes tree structure */
-void free_notes(void);
+/*
+ * Function type for combining two notes annotating the same object.
+ *
+ * When adding a new note annotating the same object as an existing note, it is
+ * up to the caller to decide how to combine the two notes. The decision is
+ * made by passing in a function of the following form. The function accepts
+ * two SHA1s -- of the existing note and the new note, respectively. The
+ * function then combines the notes in whatever way it sees fit, and writes the
+ * resulting SHA1 into the first SHA1 argument (cur_sha1). A non-zero return
+ * value indicates failure.
+ *
+ * The two given SHA1s must both be non-NULL and different from each other.
+ *
+ * The default combine_notes function (you get this when passing NULL) is
+ * combine_notes_concatenate(), which appends the contents of the new note to
+ * the contents of the existing note.
+ */
+typedef int combine_notes_fn(unsigned char *cur_sha1, const unsigned char *new_sha1);
 
+/* Common notes combinators */
+int combine_notes_concatenate(unsigned char *cur_sha1, const unsigned char *new_sha1);
+int combine_notes_overwrite(unsigned char *cur_sha1, const unsigned char *new_sha1);
+int combine_notes_ignore(unsigned char *cur_sha1, const unsigned char *new_sha1);
+
+/*
+ * Notes tree object
+ *
+ * Encapsulates the internal notes tree structure associated with a notes ref.
+ * Whenever a struct notes_tree pointer is required below, you may pass NULL in
+ * order to use the default/internal notes tree. E.g. you only need to pass a
+ * non-NULL value if you need to refer to several different notes trees
+ * simultaneously.
+ */
+extern struct notes_tree {
+       struct int_node *root;
+       struct non_note *first_non_note, *prev_non_note;
+       char *ref;
+       combine_notes_fn *combine_notes;
+       int initialized;
+       int dirty;
+} default_notes_tree;
+
+/*
+ * Flags controlling behaviour of notes tree initialization
+ *
+ * Default behaviour is to initialize the notes tree from the tree object
+ * specified by the given (or default) notes ref.
+ */
+#define NOTES_INIT_EMPTY 1
+
+/*
+ * Initialize the given notes_tree with the notes tree structure at the given
+ * ref. If given ref is NULL, the value of the $GIT_NOTES_REF environment
+ * variable is used, and if that is missing, the default notes ref is used
+ * ("refs/notes/commits").
+ *
+ * If you need to re-intialize a notes_tree structure (e.g. when switching from
+ * one notes ref to another), you must first de-initialize the notes_tree
+ * structure by calling free_notes(struct notes_tree *).
+ *
+ * If you pass t == NULL, the default internal notes_tree will be initialized.
+ *
+ * The combine_notes function that is passed becomes the default combine_notes
+ * function for the given notes_tree. If NULL is passed, the default
+ * combine_notes function is combine_notes_concatenate().
+ *
+ * Precondition: The notes_tree structure is zeroed (this can be achieved with
+ * memset(t, 0, sizeof(struct notes_tree)))
+ */
+void init_notes(struct notes_tree *t, const char *notes_ref,
+               combine_notes_fn combine_notes, int flags);
+
+/*
+ * Add the given note object to the given notes_tree structure
+ *
+ * IMPORTANT: The changes made by add_note() to the given notes_tree structure
+ * are not persistent until a subsequent call to write_notes_tree() returns
+ * zero.
+ */
+void add_note(struct notes_tree *t, const unsigned char *object_sha1,
+               const unsigned char *note_sha1, combine_notes_fn combine_notes);
+
+/*
+ * Remove the given note object from the given notes_tree structure
+ *
+ * IMPORTANT: The changes made by remove_note() to the given notes_tree
+ * structure are not persistent until a subsequent call to write_notes_tree()
+ * returns zero.
+ */
+void remove_note(struct notes_tree *t, const unsigned char *object_sha1);
+
+/*
+ * Get the note object SHA1 containing the note data for the given object
+ *
+ * Return NULL if the given object has no notes.
+ */
+const unsigned char *get_note(struct notes_tree *t,
+               const unsigned char *object_sha1);
+
+/*
+ * Copy a note from one object to another in the given notes_tree.
+ *
+ * Fails if the to_obj already has a note unless 'force' is true.
+ */
+int copy_note(struct notes_tree *t,
+             const unsigned char *from_obj, const unsigned char *to_obj,
+             int force, combine_notes_fn combine_fn);
+
+/*
+ * Flags controlling behaviour of for_each_note()
+ *
+ * Default behaviour of for_each_note() is to traverse every single note object
+ * in the given notes tree, unpacking subtree entries along the way.
+ * The following flags can be used to alter the default behaviour:
+ *
+ * - DONT_UNPACK_SUBTREES causes for_each_note() NOT to unpack and recurse into
+ *   subtree entries while traversing the notes tree. This causes notes within
+ *   those subtrees NOT to be passed to the callback. Use this flag if you
+ *   don't want to traverse _all_ notes, but only want to traverse the parts
+ *   of the notes tree that have already been unpacked (this includes at least
+ *   all notes that have been added/changed).
+ *
+ * - YIELD_SUBTREES causes any subtree entries that are encountered to be
+ *   passed to the callback, before recursing into them. Subtree entries are
+ *   not note objects, but represent intermediate directories in the notes
+ *   tree. When passed to the callback, subtree entries will have a trailing
+ *   slash in their path, which the callback may use to differentiate between
+ *   note entries and subtree entries. Note that already-unpacked subtree
+ *   entries are not part of the notes tree, and will therefore not be yielded.
+ *   If this flag is used together with DONT_UNPACK_SUBTREES, for_each_note()
+ *   will yield the subtree entry, but not recurse into it.
+ */
+#define FOR_EACH_NOTE_DONT_UNPACK_SUBTREES 1
+#define FOR_EACH_NOTE_YIELD_SUBTREES 2
+
+/*
+ * Invoke the specified callback function for each note in the given notes_tree
+ *
+ * If the callback returns nonzero, the note walk is aborted, and the return
+ * value from the callback is returned from for_each_note(). Hence, a zero
+ * return value from for_each_note() indicates that all notes were walked
+ * successfully.
+ *
+ * IMPORTANT: The callback function is NOT allowed to change the notes tree.
+ * In other words, the following functions can NOT be invoked (on the current
+ * notes tree) from within the callback:
+ * - add_note()
+ * - remove_note()
+ * - free_notes()
+ */
+typedef int each_note_fn(const unsigned char *object_sha1,
+               const unsigned char *note_sha1, char *note_path,
+               void *cb_data);
+int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,
+               void *cb_data);
+
+/*
+ * Write the given notes_tree structure to the object database
+ *
+ * Creates a new tree object encapsulating the current state of the given
+ * notes_tree, and stores its SHA1 into the 'result' argument.
+ *
+ * Returns zero on success, non-zero on failure.
+ *
+ * IMPORTANT: Changes made to the given notes_tree are not persistent until
+ * this function has returned zero. Please also remember to create a
+ * corresponding commit object, and update the appropriate notes ref.
+ */
+int write_notes_tree(struct notes_tree *t, unsigned char *result);
+
+/*
+ * Remove all notes annotating non-existing objects from the given notes tree
+ *
+ * All notes in the given notes_tree that are associated with objects that no
+ * longer exist in the database, are removed from the notes tree.
+ *
+ * IMPORTANT: The changes made by prune_notes() to the given notes_tree
+ * structure are not persistent until a subsequent call to write_notes_tree()
+ * returns zero.
+ */
+void prune_notes(struct notes_tree *t);
+
+/*
+ * Free (and de-initialize) the given notes_tree structure
+ *
+ * IMPORTANT: Changes made to the given notes_tree since the last, successful
+ * call to write_notes_tree() will be lost.
+ */
+void free_notes(struct notes_tree *t);
+
+/* Flags controlling how notes are formatted */
 #define NOTES_SHOW_HEADER 1
 #define NOTES_INDENT 2
 
-void get_commit_notes(const struct commit *commit, struct strbuf *sb,
-               const char *output_encoding, int flags);
+/*
+ * Fill the given strbuf with the notes associated with the given object.
+ *
+ * If the given notes_tree structure is not initialized, it will be auto-
+ * initialized to the default value (see documentation for init_notes() above).
+ * If the given notes_tree is NULL, the internal/default notes_tree will be
+ * used instead.
+ *
+ * 'flags' is a bitwise combination of the above formatting flags.
+ */
+void format_note(struct notes_tree *t, const unsigned char *object_sha1,
+               struct strbuf *sb, const char *output_encoding, int flags);
+
+
+struct string_list;
+
+struct display_notes_opt {
+       int suppress_default_notes:1;
+       struct string_list *extra_notes_refs;
+};
+
+/*
+ * Load the notes machinery for displaying several notes trees.
+ *
+ * If 'opt' is not NULL, then it specifies additional settings for the
+ * displaying:
+ *
+ * - suppress_default_notes indicates that the notes from
+ *   core.notesRef and notes.displayRef should not be loaded.
+ *
+ * - extra_notes_refs may contain a list of globs (in the same style
+ *   as notes.displayRef) where notes should be loaded from.
+ */
+void init_display_notes(struct display_notes_opt *opt);
+
+/*
+ * Append notes for the given 'object_sha1' from all trees set up by
+ * init_display_notes() to 'sb'.  The 'flags' are a bitwise
+ * combination of
+ *
+ * - NOTES_SHOW_HEADER: add a 'Notes (refname):' header
+ *
+ * - NOTES_INDENT: indent the notes by 4 places
+ *
+ * You *must* call init_display_notes() before using this function.
+ */
+void format_display_notes(const unsigned char *object_sha1,
+                         struct strbuf *sb, const char *output_encoding, int flags);
+
+/*
+ * Load the notes tree from each ref listed in 'refs'.  The output is
+ * an array of notes_tree*, terminated by a NULL.
+ */
+struct notes_tree **load_notes_trees(struct string_list *refs);
+
+/*
+ * Add all refs that match 'glob' to the 'list'.
+ */
+void string_list_add_refs_by_glob(struct string_list *list, const char *glob);
+
+/*
+ * Add all refs from a colon-separated glob list 'globs' to the end of
+ * 'list'.  Empty components are ignored.  This helper is used to
+ * parse GIT_NOTES_DISPLAY_REF style environment variables.
+ */
+void string_list_add_refs_from_colon_sep(struct string_list *list,
+                                        const char *globs);
 
 #endif
index 9f47cf9961212242bc01082944e83fb54da2a515..a905ca4486754f099a30f90a2fcd22d0c771a070 100644 (file)
@@ -253,3 +253,30 @@ char *index_pack_lockfile(int ip_out)
        }
        return NULL;
 }
+
+/*
+ * The per-object header is a pretty dense thing, which is
+ *  - first byte: low four bits are "size", then three bits of "type",
+ *    and the high bit is "size continues".
+ *  - each byte afterwards: low seven bits are size continuation,
+ *    with the high bit being "size continues"
+ */
+int encode_in_pack_object_header(enum object_type type, uintmax_t size, unsigned char *hdr)
+{
+       int n = 1;
+       unsigned char c;
+
+       if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
+               die("bad type %d", type);
+
+       c = (type << 4) | (size & 15);
+       size >>= 4;
+       while (size) {
+               *hdr++ = c | 0x80;
+               c = size & 0x7f;
+               size >>= 7;
+               n++;
+       }
+       *hdr = c;
+       return n;
+}
diff --git a/pack.h b/pack.h
index b759a23d4d022c4acef893ec35a5dd4e2324715c..d268c014c9eb7040bd65125b13d68edce670274b 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -60,6 +60,7 @@ extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off
 extern int verify_pack(struct packed_git *);
 extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
 extern char *index_pack_lockfile(int fd);
+extern int encode_in_pack_object_header(enum object_type, uintmax_t, unsigned char *);
 
 #define PH_ERROR_EOF           (-1)
 #define PH_ERROR_PACK_SIGNATURE        (-2)
diff --git a/pager.c b/pager.c
index 2c7e8ecb3c0860b00113e348008415ca28a7ff72..dac358f047550a07dd8d90b2e410feab3eebd534 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -48,11 +48,11 @@ static void wait_for_pager_signal(int signo)
        raise(signo);
 }
 
-const char *git_pager(void)
+const char *git_pager(int stdout_is_tty)
 {
        const char *pager;
 
-       if (!isatty(1))
+       if (!stdout_is_tty)
                return NULL;
 
        pager = getenv("GIT_PAGER");
@@ -73,7 +73,7 @@ const char *git_pager(void)
 
 void setup_pager(void)
 {
-       const char *pager = git_pager();
+       const char *pager = git_pager(isatty(1));
 
        if (!pager)
                return;
index d218122af5c2c0cddd857fce0ae5064bf32f6387..c83035d013d3f50e9367f2b07d49cf17a1e22ff6 100644 (file)
@@ -2,6 +2,7 @@
 #include "parse-options.h"
 #include "cache.h"
 #include "commit.h"
+#include "color.h"
 
 static int parse_options_usage(const char * const *usagestr,
                               const struct option *opts);
@@ -599,6 +600,21 @@ int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
        return 0;
 }
 
+int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
+                           int unset)
+{
+       int value;
+
+       if (!arg)
+               arg = unset ? "never" : (const char *)opt->defval;
+       value = git_config_colorbool(NULL, arg, -1);
+       if (value < 0)
+               return opterror(opt,
+                       "expects \"always\", \"auto\", or \"never\"", 0);
+       *(int *)opt->value = value;
+       return 0;
+}
+
 int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
                           int unset)
 {
index 0c996916b6044989f0e2945881c7c12f7292d5c1..9429f7e36112b7b9cf52d4b3b68b007938396322 100644 (file)
@@ -135,6 +135,10 @@ struct option {
          PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
 #define OPT_FILENAME(s, l, v, h)    { OPTION_FILENAME, (s), (l), (v), \
                                       "FILE", (h) }
+#define OPT_COLOR_FLAG(s, l, v, h) \
+       { OPTION_CALLBACK, (s), (l), (v), "when", (h), PARSE_OPT_OPTARG, \
+               parse_opt_color_flag_cb, (intptr_t)"always" }
+
 
 /* parse_options() will filter out the processed options and leave the
  * non-option arguments in argv[].
@@ -187,6 +191,7 @@ extern int parse_options_end(struct parse_opt_ctx_t *ctx);
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
 extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
+extern int parse_opt_color_flag_cb(const struct option *, const char *, int);
 extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
 extern int parse_opt_with_commit(const struct option *, const char *, int);
 extern int parse_opt_tertiary(const struct option *, const char *, int);
@@ -203,5 +208,7 @@ extern int parse_opt_tertiary(const struct option *, const char *, int);
        { OPTION_CALLBACK, 0, "abbrev", (var), "n", \
          "use <n> digits to display SHA-1s", \
          PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
+#define OPT__COLOR(var, h) \
+       OPT_COLOR_FLAG(0, "color", (var), (h))
 
 #endif
diff --git a/path.c b/path.c
index 79aa104712364a8c18964feecd4c8079449a78cf..b4c8d917229a4187f36a76f43603fc036e65632e 100644 (file)
--- a/path.c
+++ b/path.c
@@ -157,6 +157,85 @@ int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
        return mkstemps(path, suffix_len);
 }
 
+/* Adapted from libiberty's mkstemp.c. */
+
+#undef TMP_MAX
+#define TMP_MAX 16384
+
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
+{
+       static const char letters[] =
+               "abcdefghijklmnopqrstuvwxyz"
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+               "0123456789";
+       static const int num_letters = 62;
+       uint64_t value;
+       struct timeval tv;
+       char *template;
+       size_t len;
+       int fd, count;
+
+       len = strlen(pattern);
+
+       if (len < 6 + suffix_len) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /*
+        * Replace pattern's XXXXXX characters with randomness.
+        * Try TMP_MAX different filenames.
+        */
+       gettimeofday(&tv, NULL);
+       value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
+       template = &pattern[len - 6 - suffix_len];
+       for (count = 0; count < TMP_MAX; ++count) {
+               uint64_t v = value;
+               /* Fill in the random bits. */
+               template[0] = letters[v % num_letters]; v /= num_letters;
+               template[1] = letters[v % num_letters]; v /= num_letters;
+               template[2] = letters[v % num_letters]; v /= num_letters;
+               template[3] = letters[v % num_letters]; v /= num_letters;
+               template[4] = letters[v % num_letters]; v /= num_letters;
+               template[5] = letters[v % num_letters]; v /= num_letters;
+
+               fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode);
+               if (fd > 0)
+                       return fd;
+               /*
+                * Fatal error (EPERM, ENOSPC etc).
+                * It doesn't make sense to loop.
+                */
+               if (errno != EEXIST)
+                       break;
+               /*
+                * This is a random value.  It is only necessary that
+                * the next TMP_MAX values generated by adding 7777 to
+                * VALUE are different with (module 2^32).
+                */
+               value += 7777;
+       }
+       /* We return the null string if we can't find a unique file name.  */
+       pattern[0] = '\0';
+       return -1;
+}
+
+int git_mkstemp_mode(char *pattern, int mode)
+{
+       /* mkstemp is just mkstemps with no suffix */
+       return git_mkstemps_mode(pattern, 0, mode);
+}
+
+int gitmkstemps(char *pattern, int suffix_len)
+{
+       return git_mkstemps_mode(pattern, suffix_len, 0600);
+}
+
 int validate_headref(const char *path)
 {
        struct stat st;
@@ -336,7 +415,7 @@ char *enter_repo(char *path, int strict)
 
        if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
            validate_headref("HEAD") == 0) {
-               setenv(GIT_DIR_ENVIRONMENT, ".", 1);
+               set_git_dir(".");
                check_repository_format();
                return path;
        }
@@ -610,7 +689,7 @@ int daemon_avoid_alias(const char *p)
        /*
         * This resurrects the belts and suspenders paranoia check by HPA
         * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
-        * does not do getcwd() based path canonicalizations.
+        * does not do getcwd() based path canonicalization.
         *
         * sl becomes true immediately after seeing '/' and continues to
         * be true as long as dots continue after that without intervening
@@ -649,3 +728,10 @@ int daemon_avoid_alias(const char *p)
                }
        }
 }
+
+int offset_1st_component(const char *path)
+{
+       if (has_dos_drive_prefix(path))
+               return 2 + is_dir_sep(path[2]);
+       return is_dir_sep(path[0]);
+}
index e8df55d2f290210ba4cf7ae8c91639f2a34c834e..1926dc9a4b929c025456227526a93424d4972948 100644 (file)
@@ -204,14 +204,14 @@ sub repository {
                        $dir = $opts{Directory};
 
                        unless (-d "$dir/refs" and -d "$dir/objects" and -e "$dir/HEAD") {
-                               # Mimick git-rev-parse --git-dir error message:
+                               # Mimic git-rev-parse --git-dir error message:
                                throw Error::Simple("fatal: Not a git repository: $dir");
                        }
                        my $search = Git->repository(Repository => $dir);
                        try {
                                $search->command('symbolic-ref', 'HEAD');
                        } catch Git::Error::Command with {
-                               # Mimick git-rev-parse --git-dir error message:
+                               # Mimic git-rev-parse --git-dir error message:
                                throw Error::Simple("fatal: Not a git repository: $dir");
                        }
 
@@ -842,7 +842,7 @@ sub _open_hash_and_insert_object_if_needed {
 
        ($self->{hash_object_pid}, $self->{hash_object_in},
         $self->{hash_object_out}, $self->{hash_object_ctx}) =
-               command_bidi_pipe(qw(hash-object -w --stdin-paths));
+               command_bidi_pipe(qw(hash-object -w --stdin-paths --no-filters));
 }
 
 sub _close_hash_and_insert_object {
index d493cade26890d3e16ea072ced8e0f95679f5670..6ba3da89b7d29ef35a6ac9215b4f3e0abe7a582f 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -775,8 +775,9 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                }
                return 0;       /* unknown %g placeholder */
        case 'N':
-               get_commit_notes(commit, sb, git_log_output_encoding ?
-                            git_log_output_encoding : git_commit_encoding, 0);
+               format_display_notes(commit->object.sha1, sb,
+                           git_log_output_encoding ? git_log_output_encoding
+                                                   : git_commit_encoding, 0);
                return 1;
        }
 
@@ -1095,8 +1096,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
                strbuf_addch(sb, '\n');
 
        if (context->show_notes)
-               get_commit_notes(commit, sb, encoding,
-                                NOTES_SHOW_HEADER | NOTES_INDENT);
+               format_display_notes(commit->object.sha1, sb, encoding,
+                                    NOTES_SHOW_HEADER | NOTES_INDENT);
 
        free(reencoded);
 }
diff --git a/refs.c b/refs.c
index 503a8c2bd0fa7e4fb825d543e007995701150948..0f24c8d5d9fa897c0b72c529c9fd2ccbcc7bd4d9 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -6,6 +6,7 @@
 
 /* ISSYMREF=01 and ISPACKED=02 are public interfaces */
 #define REF_KNOWS_PEELED 04
+#define REF_BROKEN 010
 
 struct ref_list {
        struct ref_list *next;
@@ -275,8 +276,10 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
                                list = get_ref_dir(ref, list);
                                continue;
                        }
-                       if (!resolve_ref(ref, sha1, 1, &flag))
+                       if (!resolve_ref(ref, sha1, 1, &flag)) {
                                hashclr(sha1);
+                               flag |= REF_BROKEN;
+                       }
                        list = add_ref(ref, sha1, flag, list, NULL);
                }
                free(ref);
@@ -539,10 +542,10 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
 {
        if (strncmp(base, entry->name, trim))
                return 0;
-       /* Is this a "negative ref" that represents a deleted ref? */
-       if (is_null_sha1(entry->sha1))
-               return 0;
+
        if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
+               if (entry->flag & REF_BROKEN)
+                       return 0; /* ignore dangling symref */
                if (!has_sha1_file(entry->sha1)) {
                        error("%s does not point to a valid object!", entry->name);
                        return 0;
@@ -695,7 +698,6 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
 {
        struct strbuf real_pattern = STRBUF_INIT;
        struct ref_filter filter;
-       const char *has_glob_specials;
        int ret;
 
        if (!prefix && prefixcmp(pattern, "refs/"))
@@ -704,9 +706,8 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
                strbuf_addstr(&real_pattern, prefix);
        strbuf_addstr(&real_pattern, pattern);
 
-       has_glob_specials = strpbrk(pattern, "?*[");
-       if (!has_glob_specials) {
-               /* Append impiled '/' '*' if not present. */
+       if (!has_glob_specials(pattern)) {
+               /* Append implied '/' '*' if not present. */
                if (real_pattern.buf[real_pattern.len - 1] != '/')
                        strbuf_addch(&real_pattern, '/');
                /* No need to check for '*', there is none. */
@@ -1574,7 +1575,7 @@ int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs,
 {
        const char *logfile;
        FILE *logfp;
-       char buf[1024];
+       struct strbuf sb = STRBUF_INIT;
        int ret = 0;
 
        logfile = git_path("logs/%s", ref);
@@ -1587,24 +1588,24 @@ int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs,
                if (fstat(fileno(logfp), &statbuf) ||
                    statbuf.st_size < ofs ||
                    fseek(logfp, -ofs, SEEK_END) ||
-                   fgets(buf, sizeof(buf), logfp)) {
+                   strbuf_getwholeline(&sb, logfp, '\n')) {
                        fclose(logfp);
+                       strbuf_release(&sb);
                        return -1;
                }
        }
 
-       while (fgets(buf, sizeof(buf), logfp)) {
+       while (!strbuf_getwholeline(&sb, logfp, '\n')) {
                unsigned char osha1[20], nsha1[20];
                char *email_end, *message;
                unsigned long timestamp;
-               int len, tz;
+               int tz;
 
                /* old SP new SP name <email> SP time TAB msg LF */
-               len = strlen(buf);
-               if (len < 83 || buf[len-1] != '\n' ||
-                   get_sha1_hex(buf, osha1) || buf[40] != ' ' ||
-                   get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ' ||
-                   !(email_end = strchr(buf + 82, '>')) ||
+               if (sb.len < 83 || sb.buf[sb.len - 1] != '\n' ||
+                   get_sha1_hex(sb.buf, osha1) || sb.buf[40] != ' ' ||
+                   get_sha1_hex(sb.buf + 41, nsha1) || sb.buf[81] != ' ' ||
+                   !(email_end = strchr(sb.buf + 82, '>')) ||
                    email_end[1] != ' ' ||
                    !(timestamp = strtoul(email_end + 2, &message, 10)) ||
                    !message || message[0] != ' ' ||
@@ -1618,11 +1619,13 @@ int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs,
                        message += 6;
                else
                        message += 7;
-               ret = fn(osha1, nsha1, buf+82, timestamp, tz, message, cb_data);
+               ret = fn(osha1, nsha1, sb.buf + 82, timestamp, tz, message,
+                        cb_data);
                if (ret)
                        break;
        }
        fclose(logfp);
+       strbuf_release(&sb);
        return ret;
 }
 
diff --git a/refs.h b/refs.h
index f7648b9bd3b719936024678246d0603028e72aa7..4a18b083f52a15e5216d583644e590d666cae097 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -28,6 +28,11 @@ extern int for_each_replace_ref(each_ref_fn, void *);
 extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
 extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);
 
+static inline const char *has_glob_specials(const char *pattern)
+{
+       return strpbrk(pattern, "?*[");
+}
+
 /* can be used to learn about broken ref and symref */
 extern int for_each_rawref(each_ref_fn, void *);
 
index a904164e425097380781c1ecd9bb13f4feec0de6..b76bfcb3d3cdbbee2e3279a6696c7d6b526176d7 100644 (file)
@@ -10,7 +10,6 @@
 
 static struct remote *remote;
 static const char *url;
-static struct walker *walker;
 
 struct options {
        int verbosity;
@@ -22,12 +21,6 @@ struct options {
 };
 static struct options options;
 
-static void init_walker(void)
-{
-       if (!walker)
-               walker = get_http_walker(url, remote);
-}
-
 static int set_option(const char *name, const char *value)
 {
        if (!strcmp(name, "verbosity")) {
@@ -119,7 +112,6 @@ static struct discovery* discover_refs(const char *service)
        }
        refs_url = strbuf_detach(&buffer, NULL);
 
-       init_walker();
        http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
 
        /* try again with "plain" url (no ? or & appended) */
@@ -184,13 +176,13 @@ static struct discovery* discover_refs(const char *service)
        return last;
 }
 
-static int write_discovery(int fd, void *data)
+static int write_discovery(int in, int out, void *data)
 {
        struct discovery *heads = data;
        int err = 0;
-       if (write_in_full(fd, heads->buf, heads->len) != heads->len)
+       if (write_in_full(out, heads->buf, heads->len) != heads->len)
                err = 1;
-       close(fd);
+       close(out);
        return err;
 }
 
@@ -202,6 +194,7 @@ static struct ref *parse_git_refs(struct discovery *heads)
        memset(&async, 0, sizeof(async));
        async.proc = write_discovery;
        async.data = heads;
+       async.out = -1;
 
        if (start_async(&async))
                die("cannot start thread to parse advertised refs");
@@ -249,9 +242,8 @@ static struct ref *parse_info_refs(struct discovery *heads)
                i++;
        }
 
-       init_walker();
        ref = alloc_ref("HEAD");
-       if (!walker->fetch_ref(walker, ref) &&
+       if (!http_fetch_ref(url, ref) &&
            !resolve_remote_symref(ref, refs)) {
                ref->next = refs;
                refs = ref;
@@ -501,7 +493,6 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
        struct child_process client;
        int err = 0;
 
-       init_walker();
        memset(&client, 0, sizeof(client));
        client.in = -1;
        client.out = -1;
@@ -553,6 +544,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
 
 static int fetch_dumb(int nr_heads, struct ref **to_fetch)
 {
+       struct walker *walker;
        char **targets = xmalloc(nr_heads * sizeof(char*));
        int ret, i;
 
@@ -561,13 +553,14 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
        for (i = 0; i < nr_heads; i++)
                targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
 
-       init_walker();
+       walker = get_http_walker(url);
        walker->get_all = 1;
        walker->get_tree = 1;
        walker->get_history = 1;
        walker->get_verbosely = options.verbosity >= 3;
        walker->get_recover = 0;
        ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
+       walker_free(walker);
 
        for (i = 0; i < nr_heads; i++)
                free(targets[i]);
@@ -810,6 +803,8 @@ int main(int argc, const char **argv)
                url = remote->url[0];
        }
 
+       http_init(remote);
+
        do {
                if (strbuf_getline(&buf, stdin, '\n') == EOF)
                        break;
@@ -855,5 +850,8 @@ int main(int argc, const char **argv)
                }
                strbuf_reset(&buf);
        } while (1);
+
+       http_cleanup();
+
        return 0;
 }
index d1d3e753955146cadfaf6da274487a4a369f0521..a59f74f76c293efa783103eaf3d167c97b3768ea 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -364,7 +364,7 @@ static int find_conflict(struct string_list *conflict)
 static int merge(const char *name, const char *path)
 {
        int ret;
-       mmfile_t cur, base, other;
+       mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
        mmbuffer_t result = {NULL, 0};
 
        if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0)
@@ -372,8 +372,10 @@ static int merge(const char *name, const char *path)
 
        if (read_mmfile(&cur, rerere_path(name, "thisimage")) ||
                        read_mmfile(&base, rerere_path(name, "preimage")) ||
-                       read_mmfile(&other, rerere_path(name, "postimage")))
-               return 1;
+                       read_mmfile(&other, rerere_path(name, "postimage"))) {
+               ret = 1;
+               goto out;
+       }
        ret = ll_merge(&result, path, &base, &cur, "", &other, "", 0);
        if (!ret) {
                FILE *f = fopen(path, "w");
@@ -387,6 +389,7 @@ static int merge(const char *name, const char *path)
                                     strerror(errno));
        }
 
+out:
        free(cur.ptr);
        free(base.ptr);
        free(other.ptr);
index 3ba6d991f6e9789949c314c2981dfc6b208a6f66..f4b8b383153b2330be1729fa29488bab1848e019 100644 (file)
@@ -12,6 +12,7 @@
 #include "patch-ids.h"
 #include "decorate.h"
 #include "log-tree.h"
+#include "string-list.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -547,6 +548,9 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
                        right_count++;
        }
 
+       if (!left_count || !right_count)
+               return;
+
        left_first = left_count < right_count;
        init_patch_ids(&ids);
        if (revs->diffopt.nr_paths) {
@@ -823,6 +827,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
 
        revs->grep_filter.status_only = 1;
        revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list);
+       revs->grep_filter.header_tail = &(revs->grep_filter.header_list);
        revs->grep_filter.regflags = REG_NEWLINE;
 
        diff_setup(&revs->diffopt);
@@ -1187,9 +1192,29 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--show-notes")) {
                revs->show_notes = 1;
                revs->show_notes_given = 1;
+       } else if (!prefixcmp(arg, "--show-notes=")) {
+               struct strbuf buf = STRBUF_INIT;
+               revs->show_notes = 1;
+               revs->show_notes_given = 1;
+               if (!revs->notes_opt.extra_notes_refs)
+                       revs->notes_opt.extra_notes_refs = xcalloc(1, sizeof(struct string_list));
+               if (!prefixcmp(arg+13, "refs/"))
+                       /* happy */;
+               else if (!prefixcmp(arg+13, "notes/"))
+                       strbuf_addstr(&buf, "refs/");
+               else
+                       strbuf_addstr(&buf, "refs/notes/");
+               strbuf_addstr(&buf, arg+13);
+               string_list_append(strbuf_detach(&buf, NULL),
+                                  revs->notes_opt.extra_notes_refs);
        } else if (!strcmp(arg, "--no-notes")) {
                revs->show_notes = 0;
                revs->show_notes_given = 1;
+       } else if (!strcmp(arg, "--standard-notes")) {
+               revs->show_notes_given = 1;
+               revs->notes_opt.suppress_default_notes = 0;
+       } else if (!strcmp(arg, "--no-standard-notes")) {
+               revs->notes_opt.suppress_default_notes = 1;
        } else if (!strcmp(arg, "--oneline")) {
                revs->verbose_header = 1;
                get_commit_format("oneline", revs);
@@ -1328,9 +1353,9 @@ static void append_prune_data(const char ***prune_data, const char **av)
  * Returns the number of arguments left that weren't recognized
  * (which are also moved to the head of the argument list)
  */
-int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
+int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
 {
-       int i, flags, left, seen_dashdash, read_from_stdin;
+       int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
        const char **prune_data = NULL;
 
        /* First, search for "--" */
@@ -1456,16 +1481,20 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        append_prune_data(&prune_data, argv + i);
                        break;
                }
+               else
+                       got_rev_arg = 1;
        }
 
        if (prune_data)
                revs->prune_data = get_pathspec(revs->prefix, prune_data);
 
        if (revs->def == NULL)
-               revs->def = def;
+               revs->def = opt ? opt->def : NULL;
+       if (opt && opt->tweak)
+               opt->tweak(revs, opt);
        if (revs->show_merge)
                prepare_show_merge(revs);
-       if (revs->def && !revs->pending.nr) {
+       if (revs->def && !revs->pending.nr && !got_rev_arg) {
                unsigned char sha1[20];
                struct object *object;
                unsigned mode;
@@ -1496,11 +1525,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                if (!revs->full_diff)
                        diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
        }
-       if (revs->combine_merges) {
+       if (revs->combine_merges)
                revs->ignore_merges = 0;
-               if (revs->dense_combined_merges && !revs->diffopt.output_format)
-                       revs->diffopt.output_format = DIFF_FORMAT_PATCH;
-       }
        revs->diffopt.abbrev = revs->abbrev;
        if (diff_setup_done(&revs->diffopt) < 0)
                die("diff_setup_done failed");
@@ -1801,7 +1827,7 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)
 
 static int commit_match(struct commit *commit, struct rev_info *opt)
 {
-       if (!opt->grep_filter.pattern_list)
+       if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
                return 1;
        return grep_buffer(&opt->grep_filter,
                           NULL, /* we say nothing, not even filename */
index a14deefc252bd641fba5e16f7859b4a985a72578..568f1c98de844dbadcebf1d583bffc24e6daa677 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "parse-options.h"
 #include "grep.h"
+#include "notes.h"
 
 #define SEEN           (1u<<0)
 #define UNINTERESTING   (1u<<1)
@@ -20,6 +21,7 @@
 
 struct rev_info;
 struct log_info;
+struct string_list;
 
 struct rev_info {
        /* Starting list */
@@ -126,6 +128,9 @@ struct rev_info {
        struct reflog_walk_info *reflog_info;
        struct decoration children;
        struct decoration merge_simplification;
+
+       /* notes-specific options: which refs to show */
+       struct display_notes_opt notes_opt;
 };
 
 #define REV_TREE_SAME          0
@@ -137,8 +142,13 @@ struct rev_info {
 typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
 extern volatile show_early_output_fn_t show_early_output;
 
+struct setup_revision_opt {
+       const char *def;
+       void (*tweak)(struct rev_info *, struct setup_revision_opt *);
+};
+
 extern void init_revisions(struct rev_info *revs, const char *prefix);
-extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
+extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *);
 extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
                                 const struct option *options,
                                 const char * const usagestr[]);
index 2feb493951322617692085998ac8507cdba9dd30..c8d53795ec3ae9b8b0a73b3d6900c1a1f212d2c2 100644 (file)
@@ -67,19 +67,21 @@ static int child_notifier = -1;
 
 static void notify_parent(void)
 {
-       write(child_notifier, "", 1);
+       ssize_t unused;
+       unused = write(child_notifier, "", 1);
 }
 
 static NORETURN void die_child(const char *err, va_list params)
 {
        char msg[4096];
+       ssize_t unused;
        int len = vsnprintf(msg, sizeof(msg), err, params);
        if (len > sizeof(msg))
                len = sizeof(msg);
 
-       write(child_err, "fatal: ", 7);
-       write(child_err, msg, len);
-       write(child_err, "\n", 1);
+       unused = write(child_err, "fatal: ", 7);
+       unused = write(child_err, msg, len);
+       unused = write(child_err, "\n", 1);
        exit(128);
 }
 
@@ -233,6 +235,9 @@ fail_pipe:
                else if (need_err) {
                        dup2(fderr[1], 2);
                        close_pair(fderr);
+               } else if (cmd->err > 1) {
+                       dup2(cmd->err, 2);
+                       close(cmd->err);
                }
 
                if (cmd->no_stdout)
@@ -325,6 +330,8 @@ fail_pipe:
                fherr = open("/dev/null", O_RDWR);
        else if (need_err)
                fherr = dup(fderr[1]);
+       else if (cmd->err > 2)
+               fherr = dup(cmd->err);
 
        if (cmd->no_stdout)
                fhout = open("/dev/null", O_RDWR);
@@ -394,6 +401,8 @@ fail_pipe:
 
        if (need_err)
                close(fderr[1]);
+       else if (cmd->err)
+               close(cmd->err);
 
        return 0;
 }
@@ -444,17 +453,51 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
 static unsigned __stdcall run_thread(void *data)
 {
        struct async *async = data;
-       return async->proc(async->fd_for_proc, async->data);
+       return async->proc(async->proc_in, async->proc_out, async->data);
 }
 #endif
 
 int start_async(struct async *async)
 {
-       int pipe_out[2];
+       int need_in, need_out;
+       int fdin[2], fdout[2];
+       int proc_in, proc_out;
 
-       if (pipe(pipe_out) < 0)
-               return error("cannot create pipe: %s", strerror(errno));
-       async->out = pipe_out[0];
+       need_in = async->in < 0;
+       if (need_in) {
+               if (pipe(fdin) < 0) {
+                       if (async->out > 0)
+                               close(async->out);
+                       return error("cannot create pipe: %s", strerror(errno));
+               }
+               async->in = fdin[1];
+       }
+
+       need_out = async->out < 0;
+       if (need_out) {
+               if (pipe(fdout) < 0) {
+                       if (need_in)
+                               close_pair(fdin);
+                       else if (async->in)
+                               close(async->in);
+                       return error("cannot create pipe: %s", strerror(errno));
+               }
+               async->out = fdout[0];
+       }
+
+       if (need_in)
+               proc_in = fdin[0];
+       else if (async->in)
+               proc_in = async->in;
+       else
+               proc_in = -1;
+
+       if (need_out)
+               proc_out = fdout[1];
+       else if (async->out)
+               proc_out = async->out;
+       else
+               proc_out = -1;
 
 #ifndef WIN32
        /* Flush stdio before fork() to avoid cloning buffers */
@@ -463,24 +506,47 @@ int start_async(struct async *async)
        async->pid = fork();
        if (async->pid < 0) {
                error("fork (async) failed: %s", strerror(errno));
-               close_pair(pipe_out);
-               return -1;
+               goto error;
        }
        if (!async->pid) {
-               close(pipe_out[0]);
-               exit(!!async->proc(pipe_out[1], async->data));
+               if (need_in)
+                       close(fdin[1]);
+               if (need_out)
+                       close(fdout[0]);
+               exit(!!async->proc(proc_in, proc_out, async->data));
        }
-       close(pipe_out[1]);
+
+       if (need_in)
+               close(fdin[0]);
+       else if (async->in)
+               close(async->in);
+
+       if (need_out)
+               close(fdout[1]);
+       else if (async->out)
+               close(async->out);
 #else
-       async->fd_for_proc = pipe_out[1];
+       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));
-               close_pair(pipe_out);
-               return -1;
+               goto error;
        }
 #endif
        return 0;
+
+error:
+       if (need_in)
+               close_pair(fdin);
+       else if (async->in)
+               close(async->in);
+
+       if (need_out)
+               close_pair(fdout);
+       else if (async->out)
+               close(async->out);
+       return -1;
 }
 
 int finish_async(struct async *async)
index 967ba8cc09786934724132b629587419f195b245..94619f52d95888b320664b7f19db3eeb7d6d8cca 100644 (file)
@@ -18,7 +18,7 @@ struct child_process {
         * - Specify > 0 to set a channel to a particular FD as follows:
         *     .in: a readable FD, becomes child's stdin
         *     .out: a writable FD, becomes child's stdout/stderr
-        *     .err > 0 not supported
+        *     .err: a writable FD, becomes child's stderr
         *   The specified FD is closed by start_command(), even in case
         *   of errors!
         */
@@ -66,17 +66,20 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
  */
 struct async {
        /*
-        * proc writes to fd and closes it;
+        * proc reads from in; closes it before return
+        * proc writes to out; closes it before return
         * returns 0 on success, non-zero on failure
         */
-       int (*proc)(int fd, void *data);
+       int (*proc)(int in, int out, void *data);
        void *data;
+       int in;         /* caller writes here and closes it */
        int out;        /* caller reads from here and closes it */
 #ifndef WIN32
        pid_t pid;
 #else
        HANDLE tid;
-       int fd_for_proc;
+       int proc_in;
+       int proc_out;
 #endif
 };
 
index 28141ac913f6558b81ff0b1b21a1e1b5ead43fa0..60b4ba66eb8cac3378326378dc4e0cbdb88162ac 100644 (file)
@@ -4,6 +4,7 @@
 struct send_pack_args {
        unsigned verbose:1,
                quiet:1,
+               porcelain:1,
                send_mirror:1,
                force_update:1,
                use_thin_pack:1,
diff --git a/setup.c b/setup.c
index 710e2f3008c79c08cdc507288881c9a58311283a..5716d90b57574d045114f4aaad1bdf36fd79ed89 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -18,14 +18,15 @@ const char *prefix_path(const char *prefix, int len, const char *path)
        if (normalize_path_copy(sanitized, sanitized))
                goto error_out;
        if (is_absolute_path(orig)) {
-               size_t len, total;
+               size_t root_len, len, total;
                const char *work_tree = get_git_work_tree();
                if (!work_tree)
                        goto error_out;
                len = strlen(work_tree);
+               root_len = offset_1st_component(work_tree);
                total = strlen(sanitized) + 1;
                if (strncmp(sanitized, work_tree, len) ||
-                   (sanitized[len] != '\0' && sanitized[len] != '/')) {
+                   (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
                error_out:
                        die("'%s' is outside repository", orig);
                }
@@ -206,7 +207,7 @@ int is_inside_work_tree(void)
 }
 
 /*
- * set_work_tree() is only ever called if you set GIT_DIR explicitely.
+ * set_work_tree() is only ever called if you set GIT_DIR explicitly.
  * The old behaviour (which we retain here) is to set the work tree root
  * to the cwd, unless overridden by the config, the command line, or
  * GIT_WORK_TREE.
@@ -321,7 +322,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
        static char cwd[PATH_MAX+1];
        const char *gitdirenv;
        const char *gitfile_dir;
-       int len, offset, ceil_offset;
+       int len, offset, ceil_offset, root_len;
 
        /*
         * Let's assume that we are in a git repository.
@@ -403,10 +404,11 @@ const char *setup_git_directory_gently(int *nongit_ok)
                        if (!work_tree_env)
                                inside_work_tree = 0;
                        if (offset != len) {
-                               cwd[offset] = '\0';
-                               setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
+                               root_len = offset_1st_component(cwd);
+                               cwd[offset > root_len ? offset : root_len] = '\0';
+                               set_git_dir(cwd);
                        } else
-                               setenv(GIT_DIR_ENVIRONMENT, ".", 1);
+                               set_git_dir(".");
                        check_repository_format_gently(nongit_ok);
                        return NULL;
                }
@@ -427,7 +429,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
        inside_git_dir = 0;
        if (!work_tree_env)
                inside_work_tree = 1;
-       git_work_tree_cfg = xstrndup(cwd, offset);
+       root_len = offset_1st_component(cwd);
+       git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
        if (check_repository_format_gently(nongit_ok))
                return NULL;
        if (offset == len)
index 657825e14ef78c19649779fe89e1d09ae672901a..a08a9d08808bdb2f4a138d7e6f602b61fc093c1b 100644 (file)
@@ -35,13 +35,6 @@ static size_t sz_fmt(size_t s) { return s; }
 
 const unsigned char null_sha1[20];
 
-static inline int offset_1st_component(const char *path)
-{
-       if (has_dos_drive_prefix(path))
-               return 2 + (path[2] == '/');
-       return *path == '/';
-}
-
 int safe_create_leading_directories(char *path)
 {
        char *pos = path + offset_1st_component(path);
@@ -2206,7 +2199,7 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
        }
 
 out:
-       if (set_shared_perm(filename, (S_IFREG|0444)))
+       if (adjust_shared_perm(filename))
                return error("unable to set permission to '%s'", filename);
        return 0;
 }
@@ -2262,7 +2255,7 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
        }
        memcpy(buffer, filename, dirlen);
        strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
-       fd = mkstemp(buffer);
+       fd = git_mkstemp_mode(buffer, 0444);
        if (fd < 0 && dirlen && errno == ENOENT) {
                /* Make sure the directory exists */
                memcpy(buffer, filename, dirlen);
@@ -2272,7 +2265,7 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
 
                /* Try again */
                strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX");
-               fd = mkstemp(buffer);
+               fd = git_mkstemp_mode(buffer, 0444);
        }
        return fd;
 }
@@ -2281,9 +2274,10 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
                              void *buf, unsigned long len, time_t mtime)
 {
        int fd, ret;
-       size_t size;
-       unsigned char *compressed;
+       unsigned char compressed[4096];
        z_stream stream;
+       git_SHA_CTX c;
+       unsigned char parano_sha1[20];
        char *filename;
        static char tmpfile[PATH_MAX];
 
@@ -2301,36 +2295,40 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
        deflateInit(&stream, zlib_compression_level);
-       size = 8 + deflateBound(&stream, len+hdrlen);
-       compressed = xmalloc(size);
-
-       /* Compress it */
        stream.next_out = compressed;
-       stream.avail_out = size;
+       stream.avail_out = sizeof(compressed);
+       git_SHA1_Init(&c);
 
        /* First header.. */
        stream.next_in = (unsigned char *)hdr;
        stream.avail_in = hdrlen;
        while (deflate(&stream, 0) == Z_OK)
                /* nothing */;
+       git_SHA1_Update(&c, hdr, hdrlen);
 
        /* Then the data itself.. */
        stream.next_in = buf;
        stream.avail_in = len;
-       ret = deflate(&stream, Z_FINISH);
+       do {
+               unsigned char *in0 = stream.next_in;
+               ret = deflate(&stream, Z_FINISH);
+               git_SHA1_Update(&c, in0, stream.next_in - in0);
+               if (write_buffer(fd, compressed, stream.next_out - compressed) < 0)
+                       die("unable to write sha1 file");
+               stream.next_out = compressed;
+               stream.avail_out = sizeof(compressed);
+       } while (ret == Z_OK);
+
        if (ret != Z_STREAM_END)
                die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
-
        ret = deflateEnd(&stream);
        if (ret != Z_OK)
                die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
+       git_SHA1_Final(parano_sha1, &c);
+       if (hashcmp(sha1, parano_sha1) != 0)
+               die("confused by unstable object source data for %s", sha1_to_hex(sha1));
 
-       size = stream.total_out;
-
-       if (write_buffer(fd, compressed, size) < 0)
-               die("unable to write sha1 file");
        close_sha1_file(fd);
-       free(compressed);
 
        if (mtime) {
                struct utimbuf utb;
@@ -2434,6 +2432,8 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
        return ret;
 }
 
+#define SMALL_FILE_SIZE (32*1024)
+
 int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
             enum object_type type, const char *path)
 {
@@ -2448,6 +2448,14 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
                else
                        ret = -1;
                strbuf_release(&sbuf);
+       } else if (size <= SMALL_FILE_SIZE) {
+               char *buf = xmalloc(size);
+               if (size == read_in_full(fd, buf, size))
+                       ret = index_mem(sha1, buf, size, write_object, type,
+                                       path);
+               else
+                       ret = error("short read %s", strerror(errno));
+               free(buf);
        } else if (size) {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
                ret = index_mem(sha1, buf, size, write_object, type, path);
index 77299257bf3aa91079d5b883c6676afa6fd2d01c..bf924178380c42a50b2fd7a02ff869dcc3ddff6a 100644 (file)
@@ -280,8 +280,7 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
                                *ref = xstrdup(r);
                        if (!warn_ambiguous_refs)
                                break;
-               } else if ((flag & REF_ISSYMREF) &&
-                          (len != 4 || strcmp(str, "HEAD")))
+               } else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD"))
                        warning("ignoring dangling symref %s.", fullref);
        }
        free(last_branch);
@@ -993,13 +992,15 @@ static void diagnose_invalid_index_path(int stage,
        pos = cache_name_pos(filename, namelen);
        if (pos < 0)
                pos = -pos - 1;
-       ce = active_cache[pos];
-       if (ce_namelen(ce) == namelen &&
-           !memcmp(ce->name, filename, namelen))
-               die("Path '%s' is in the index, but not at stage %d.\n"
-                   "Did you mean ':%d:%s'?",
-                   filename, stage,
-                   ce_stage(ce), filename);
+       if (pos < active_nr) {
+               ce = active_cache[pos];
+               if (ce_namelen(ce) == namelen &&
+                   !memcmp(ce->name, filename, namelen))
+                       die("Path '%s' is in the index, but not at stage %d.\n"
+                           "Did you mean ':%d:%s'?",
+                           filename, stage,
+                           ce_stage(ce), filename);
+       }
 
        /* Confusion between relative and absolute filenames? */
        fullnamelen = namelen + strlen(prefix);
@@ -1009,13 +1010,15 @@ static void diagnose_invalid_index_path(int stage,
        pos = cache_name_pos(fullname, fullnamelen);
        if (pos < 0)
                pos = -pos - 1;
-       ce = active_cache[pos];
-       if (ce_namelen(ce) == fullnamelen &&
-           !memcmp(ce->name, fullname, fullnamelen))
-               die("Path '%s' is in the index, but not '%s'.\n"
-                   "Did you mean ':%d:%s'?",
-                   fullname, filename,
-                   ce_stage(ce), fullname);
+       if (pos < active_nr) {
+               ce = active_cache[pos];
+               if (ce_namelen(ce) == fullnamelen &&
+                   !memcmp(ce->name, fullname, fullnamelen))
+                       die("Path '%s' is in the index, but not '%s'.\n"
+                           "Did you mean ':%d:%s'?",
+                           fullname, filename,
+                           ce_stage(ce), fullname);
+       }
 
        if (!lstat(filename, &st))
                die("Path '%s' exists on disk, but not in the index.", filename);
index 7d70c4f7bfe2749953726fecb27144a9588a326f..b3b8bc147909f81be6a387318ad0601dae50460f 100644 (file)
@@ -5,6 +5,7 @@
 #include "commit.h"
 #include "revision.h"
 #include "run-command.h"
+#include "diffcore.h"
 
 static int add_submodule_odb(const char *path)
 {
@@ -85,13 +86,21 @@ void show_submodule_summary(FILE *f, const char *path,
                        message = "(revision walker failed)";
        }
 
+       if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
+               fprintf(f, "Submodule %s contains untracked content\n", path);
+       if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
+               fprintf(f, "Submodule %s contains modified content\n", path);
+
+       if (!hashcmp(one, two)) {
+               strbuf_release(&sb);
+               return;
+       }
+
        strbuf_addf(&sb, "Submodule %s %s..", path,
                        find_unique_abbrev(one, DEFAULT_ABBREV));
        if (!fast_backward && !fast_forward)
                strbuf_addch(&sb, '.');
        strbuf_addf(&sb, "%s", find_unique_abbrev(two, DEFAULT_ABBREV));
-       if (dirty_submodule)
-               strbuf_add(&sb, "-dirty", 6);
        if (message)
                strbuf_addf(&sb, " %s\n", message);
        else
@@ -121,17 +130,24 @@ void show_submodule_summary(FILE *f, const char *path,
        strbuf_release(&sb);
 }
 
-int is_submodule_modified(const char *path)
+unsigned is_submodule_modified(const char *path, int ignore_untracked)
 {
-       int len;
+       int i;
+       ssize_t len;
        struct child_process cp;
        const char *argv[] = {
                "status",
                "--porcelain",
                NULL,
+               NULL,
        };
-       char *env[4];
+       const char *env[LOCAL_REPO_ENV_SIZE + 3];
        struct strbuf buf = STRBUF_INIT;
+       unsigned dirty_submodule = 0;
+       const char *line, *next_line;
+
+       for (i = 0; i < LOCAL_REPO_ENV_SIZE; i++)
+               env[i] = local_repo_env[i];
 
        strbuf_addf(&buf, "%s/.git/", path);
        if (!is_directory(buf.buf)) {
@@ -143,16 +159,17 @@ int is_submodule_modified(const char *path)
        strbuf_reset(&buf);
 
        strbuf_addf(&buf, "GIT_WORK_TREE=%s", path);
-       env[0] = strbuf_detach(&buf, NULL);
+       env[i++] = strbuf_detach(&buf, NULL);
        strbuf_addf(&buf, "GIT_DIR=%s/.git", path);
-       env[1] = strbuf_detach(&buf, NULL);
-       strbuf_addf(&buf, "GIT_INDEX_FILE");
-       env[2] = strbuf_detach(&buf, NULL);
-       env[3] = NULL;
+       env[i++] = strbuf_detach(&buf, NULL);
+       env[i] = NULL;
+
+       if (ignore_untracked)
+               argv[2] = "-uno";
 
        memset(&cp, 0, sizeof(cp));
        cp.argv = argv;
-       cp.env = (const char *const *)env;
+       cp.env = env;
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.out = -1;
@@ -160,14 +177,32 @@ int is_submodule_modified(const char *path)
                die("Could not run git status --porcelain");
 
        len = strbuf_read(&buf, cp.out, 1024);
+       line = buf.buf;
+       while (len > 2) {
+               if ((line[0] == '?') && (line[1] == '?')) {
+                       dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
+                       if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
+                               break;
+               } else {
+                       dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
+                       if (ignore_untracked ||
+                           (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED))
+                               break;
+               }
+               next_line = strchr(line, '\n');
+               if (!next_line)
+                       break;
+               next_line++;
+               len -= (next_line - line);
+               line = next_line;
+       }
        close(cp.out);
 
        if (finish_command(&cp))
                die("git status --porcelain failed");
 
-       free(env[0]);
-       free(env[1]);
-       free(env[2]);
+       for (i = LOCAL_REPO_ENV_SIZE; env[i]; i++)
+               free((char *)env[i]);
        strbuf_release(&buf);
-       return len != 0;
+       return dirty_submodule;
 }
index 233696555e913d20b6a6c7c21942586c93595541..dbda270873171c41f20c0f07b70773ce67c6a303 100644 (file)
@@ -5,6 +5,6 @@ void show_submodule_summary(FILE *f, const char *path,
                unsigned char one[20], unsigned char two[20],
                unsigned dirty_submodule,
                const char *del, const char *add, const char *reset);
-int is_submodule_modified(const char *path);
+unsigned is_submodule_modified(const char *path, int ignore_untracked);
 
 #endif
index bd09390d3208d7eac362cd9cf45f7dde623c4ae6..25c559bb49d04586c69242cd7ef03713f0939e9d 100644 (file)
@@ -27,6 +27,8 @@ pre-clean:
 
 clean:
        $(RM) -r 'trash directory'.* test-results
+       $(RM) t????/cvsroot/CVSROOT/?*
+       $(RM) -r valgrind/bin
 
 aggregate-results-and-cleanup: $(T)
        $(MAKE) aggregate-results
index 28aff887b5a92ec5919f4005010ef64f85d908e9..da4b8d5a6fbf18adac103a5a6dd26ea3498c178f 100644 (file)
@@ -131,3 +131,32 @@ stop_httpd() {
        "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
                -f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop
 }
+
+test_http_push_nonff() {
+       REMOTE_REPO=$1
+       LOCAL_REPO=$2
+       BRANCH=$3
+
+       test_expect_success 'non-fast-forward push fails' '
+               cd "$REMOTE_REPO" &&
+               HEAD=$(git rev-parse --verify HEAD) &&
+
+               cd "$LOCAL_REPO" &&
+               git checkout $BRANCH &&
+               echo "changed" > path2 &&
+               git commit -a -m path2 --amend &&
+
+               !(git push -v origin >output 2>&1) &&
+               (cd "$REMOTE_REPO" &&
+                test $HEAD = $(git rev-parse --verify HEAD))
+       '
+
+       test_expect_success 'non-fast-forward push show ref status' '
+               grep "^ ! \[rejected\][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" output
+       '
+
+       test_expect_success 'non-fast-forward push shows help message' '
+               grep "To prevent you from losing history, non-fast-forward updates were rejected" \
+                       output
+       '
+}
old mode 100755 (executable)
new mode 100644 (file)
index 75a3ee2..ce36f34
@@ -1,3 +1,5 @@
+: included from t2016 and others
+
 . ./test-lib.sh
 
 if ! test_have_prereq PERL; then
index 5386504790deea55d127f053f7b714cd121a2d57..675773479a8c6a1791ae01eb47654f4433c30ee3 100755 (executable)
@@ -167,6 +167,25 @@ test_expect_success 'init with --template (blank)' '
        ! test -f template-blank/.git/info/exclude
 '
 
+test_expect_success 'init with init.templatedir set' '
+       mkdir templatedir-source &&
+       echo Content >templatedir-source/file &&
+       (
+               HOME="`pwd`" &&
+               export HOME &&
+               test_config="${HOME}/.gitconfig" &&
+               git config -f "$test_config"  init.templatedir "${HOME}/templatedir-source" &&
+               mkdir templatedir-set &&
+               cd templatedir-set &&
+               unset GIT_CONFIG_NOGLOBAL &&
+               unset GIT_TEMPLATE_DIR &&
+               NO_SET_GIT_TEMPLATE_DIR=t &&
+               export NO_SET_GIT_TEMPLATE_DIR &&
+               git init
+       ) &&
+       test_cmp templatedir-source/file templatedir-set/.git/file
+'
+
 test_expect_success 'init --bare/--shared overrides system/global config' '
        (
                HOME="`pwd`" &&
index 89282ccf7a1a73d4b5ee085c4236b1204dc502c8..41df6bcf279a1abc4462e63866076094cfbdedd8 100755 (executable)
@@ -108,13 +108,17 @@ $test_case 'merge (case change)' '
 
 '
 
-$test_case 'add (with different case)' '
+
+
+test_expect_failure 'add (with different case)' '
 
        git reset --hard initial &&
        rm camelcase &&
        echo 1 >CamelCase &&
        git add CamelCase &&
-       test $(git ls-files | grep -i camelcase | wc -l) = 1
+       camel=$(git ls-files | grep -i camelcase) &&
+       test $(echo "$camel" | wc -l) = 1 &&
+       test "z$(git cat-file blob :$camel)" = z1
 
 '
 
index fd98e445bf2e74284709df54d2cd2d3f0006b19c..dd32432d626e4f3d192c2bbe4824772025bb08b1 100755 (executable)
@@ -65,10 +65,6 @@ test_expect_success "Can't use --path with --stdin-paths" '
        echo example | test_must_fail git hash-object --stdin-paths --path=foo
 '
 
-test_expect_success "Can't use --stdin-paths with --no-filters" '
-       echo example | test_must_fail git hash-object --stdin-paths --no-filters
-'
-
 test_expect_success "Can't use --path with --no-filters" '
        test_must_fail git hash-object --no-filters --path=foo
 '
@@ -141,6 +137,20 @@ test_expect_success 'check that --no-filters option works' '
        git config --unset core.autocrlf
 '
 
+test_expect_success 'check that --no-filters option works with --stdin-paths' '
+       echo fooQ | tr Q "\\015" >file0 &&
+       cp file0 file1 &&
+       echo "file0 -crlf" >.gitattributes &&
+       echo "file1 crlf" >>.gitattributes &&
+       git config core.autocrlf true &&
+       file0_sha=$(git hash-object file0) &&
+       file1_sha=$(git hash-object file1) &&
+       test "$file0_sha" != "$file1_sha" &&
+       nofilters_file1=$(echo "file1" | git hash-object --stdin-paths --no-filters) &&
+       test "$file0_sha" = "$nofilters_file1" &&
+       git config --unset core.autocrlf
+'
+
 pop_repo
 
 for args in "-w --stdin" "--stdin -w"; do
diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh
new file mode 100755 (executable)
index 0000000..cc30be4
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Matthieu Moy
+#
+
+test_description='Test repository with default ACL'
+
+# Create the test repo with restrictive umask
+# => this must come before . ./test-lib.sh
+umask 077
+
+. ./test-lib.sh
+
+# We need an arbitrary other user give permission to using ACLs. root
+# 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
+fi
+
+modebits () {
+       ls -l "$1" | sed -e 's|^\(..........\).*|\1|'
+}
+
+check_perms_and_acl () {
+       actual=$(modebits "$1") &&
+       case "$actual" in
+       -r--r-----*)
+               : happy
+               ;;
+       *)
+               echo "Got permission '$actual', expected '-r--r-----'"
+               false
+               ;;
+       esac &&
+       getfacl "$1" > actual &&
+       grep -q "user:root:rwx" actual &&
+       grep -q "user:${LOGNAME}:rwx" actual &&
+       grep -q "mask::r--" actual &&
+       grep -q "group::---" actual || false
+}
+
+dirs_to_set="./ .git/ .git/objects/ .git/objects/pack/"
+
+test_expect_success 'Setup test repo' '
+       setfacl -m u:root:rwx          $dirs_to_set &&
+       setfacl -d -m u:"$LOGNAME":rwx $dirs_to_set &&
+       setfacl -d -m u:root:rwx       $dirs_to_set &&
+
+       touch file.txt &&
+       git add file.txt &&
+       git commit -m "init"
+'
+
+test_expect_success 'Objects creation does not break ACLs with restrictive umask' '
+       # SHA1 for empty blob
+       check_perms_and_acl .git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+'
+
+test_expect_success 'git gc does not break ACLs with restrictive umask' '
+       git gc &&
+       check_perms_and_acl .git/objects/pack/*.pack
+'
+
+test_done
index 80af6b9b7ea50652dff6804b589d134207e9258f..25046c42081c8412b56c6b739dfd3a67ab700810 100755 (executable)
@@ -214,4 +214,45 @@ test_expect_success 'delete' '
 
 '
 
+test_expect_success 'rewind2' '
+
+       test_tick && git reset --hard HEAD~2 &&
+       loglen=$(wc -l <.git/logs/refs/heads/master) &&
+       test $loglen = 4
+
+'
+
+test_expect_success '--expire=never' '
+
+       git reflog expire --verbose \
+               --expire=never \
+               --expire-unreachable=never \
+               --all &&
+       loglen=$(wc -l <.git/logs/refs/heads/master) &&
+       test $loglen = 4
+
+'
+
+test_expect_success 'gc.reflogexpire=never' '
+
+       git config gc.reflogexpire never &&
+       git config gc.reflogexpireunreachable never &&
+       git reflog expire --verbose --all &&
+       loglen=$(wc -l <.git/logs/refs/heads/master) &&
+       test $loglen = 4
+'
+
+test_expect_success 'gc.reflogexpire=false' '
+
+       git config gc.reflogexpire false &&
+       git config gc.reflogexpireunreachable false &&
+       git reflog expire --verbose --all &&
+       loglen=$(wc -l <.git/logs/refs/heads/master) &&
+       test $loglen = 4 &&
+
+       git config --unset gc.reflogexpire &&
+       git config --unset gc.reflogexpireunreachable
+
+'
+
 test_done
index c18ed8edf994f3d701ab1d01c2e05b2585174d31..ba25ff354d6fc4998237b1145737faf6c836966e 100755 (executable)
@@ -64,4 +64,13 @@ test_expect_success 'using --date= shows reflog date (oneline)' '
        test_cmp expect actual
 '
 
+: >expect
+test_expect_success 'empty reflog file' '
+       git branch empty &&
+       : >.git/logs/refs/heads/empty &&
+
+       git log -g empty >actual &&
+       test_cmp expect actual
+'
+
 test_done
index a22632f483e2068642aa09a5bc28fde01d2294b9..49cae3ed524d70ae39114b35a95e7070f1a4e575 100755 (executable)
@@ -66,12 +66,12 @@ tagger T A Gger <tagger@example.com> 1234567890 -0000
 This is an invalid tag.
 EOF
 
-test_expect_failure 'tag pointing to nonexistent' '
-       tag=$(git hash-object -w --stdin < invalid-tag) &&
+test_expect_success 'tag pointing to nonexistent' '
+       tag=$(git hash-object -t tag -w --stdin < invalid-tag) &&
        echo $tag > .git/refs/tags/invalid &&
-       git fsck --tags 2>out &&
+       test_must_fail git fsck --tags >out &&
        cat out &&
-       grep "could not load tagged object" out &&
+       grep "broken link" out &&
        rm .git/refs/tags/invalid
 '
 
@@ -84,12 +84,12 @@ tagger T A Gger <tagger@example.com> 1234567890 -0000
 This is an invalid tag.
 EOF
 
-test_expect_failure 'tag pointing to something else than its type' '
-       tag=$(git hash-object -w --stdin < wrong-tag) &&
+test_expect_success 'tag pointing to something else than its type' '
+       tag=$(git hash-object -t tag -w --stdin < wrong-tag) &&
        echo $tag > .git/refs/tags/wrong &&
-       git fsck --tags 2>out &&
+       test_must_fail git fsck --tags 2>out &&
        cat out &&
-       grep "some sane error message" out &&
+       grep "error in tag.*broken links" out &&
        rm .git/refs/tags/wrong
 '
 
diff --git a/t/t1509-root-worktree.sh b/t/t1509-root-worktree.sh
new file mode 100755 (executable)
index 0000000..5322a3b
--- /dev/null
@@ -0,0 +1,249 @@
+#!/bin/sh
+
+test_description='Test Git when git repository is located at root
+
+This test requires write access in root. Do not bother if you do not
+have a throwaway chroot or VM.
+
+Script t1509/prepare-chroot.sh may help you setup chroot, then you
+can chroot in and execute this test from there.
+'
+
+. ./test-lib.sh
+
+test_cmp_val() {
+       echo "$1" > expected
+       echo "$2" > result
+       test_cmp expected result
+}
+
+test_vars() {
+       test_expect_success "$1: gitdir" '
+               test_cmp_val "'"$2"'" "$(git rev-parse --git-dir)"
+       '
+
+       test_expect_success "$1: worktree" '
+               test_cmp_val "'"$3"'" "$(git rev-parse --show-toplevel)"
+       '
+
+       test_expect_success "$1: prefix" '
+               test_cmp_val "'"$4"'" "$(git rev-parse --show-prefix)"
+       '
+}
+
+test_foobar_root() {
+       test_expect_success 'add relative' '
+               test -z "$(cd / && git ls-files)" &&
+               git add foo/foome &&
+               git add foo/bar/barme &&
+               git add me &&
+               ( cd / && git ls-files --stage ) > result &&
+               test_cmp /ls.expected result &&
+               rm "$(git rev-parse --git-dir)/index"
+       '
+
+       test_expect_success 'add absolute' '
+               test -z "$(cd / && git ls-files)" &&
+               git add /foo/foome &&
+               git add /foo/bar/barme &&
+               git add /me &&
+               ( cd / && git ls-files --stage ) > result &&
+               test_cmp /ls.expected result &&
+               rm "$(git rev-parse --git-dir)/index"
+       '
+
+}
+
+test_foobar_foo() {
+       test_expect_success 'add relative' '
+               test -z "$(cd / && git ls-files)" &&
+               git add foome &&
+               git add bar/barme &&
+               git add ../me &&
+               ( cd / && git ls-files --stage ) > result &&
+               test_cmp /ls.expected result &&
+               rm "$(git rev-parse --git-dir)/index"
+       '
+
+       test_expect_success 'add absolute' '
+               test -z "$(cd / && git ls-files)" &&
+               git add /foo/foome &&
+               git add /foo/bar/barme &&
+               git add /me &&
+               ( cd / && git ls-files --stage ) > result &&
+               test_cmp /ls.expected result &&
+               rm "$(git rev-parse --git-dir)/index"
+       '
+}
+
+test_foobar_foobar() {
+       test_expect_success 'add relative' '
+               test -z "$(cd / && git ls-files)" &&
+               git add ../foome &&
+               git add barme &&
+               git add ../../me &&
+               ( cd / && git ls-files --stage ) > result &&
+               test_cmp /ls.expected result &&
+               rm "$(git rev-parse --git-dir)/index"
+       '
+
+       test_expect_success 'add absolute' '
+               test -z "$(cd / && git ls-files)" &&
+               git add /foo/foome &&
+               git add /foo/bar/barme &&
+               git add /me &&
+               ( cd / && git ls-files --stage ) > result &&
+               test_cmp /ls.expected result &&
+               rm "$(git rev-parse --git-dir)/index"
+       '
+}
+
+if ! test_have_prereq POSIXPERM || ! [ -w / ]; then
+       say "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"
+       test_done
+fi
+
+if [ "$UID" = 0 ]; then
+       say "No you can't run this with root"
+       test_done
+fi
+
+ONE_SHA1=d00491fd7e5bb6fa28c517a0bb32b8b506539d4d
+
+test_expect_success 'setup' '
+       rm -rf /foo
+       mkdir /foo &&
+       mkdir /foo/bar &&
+       echo 1 > /foo/foome &&
+       echo 1 > /foo/bar/barme &&
+       echo 1 > /me
+'
+
+say "GIT_DIR absolute, GIT_WORK_TREE set"
+
+test_expect_success 'go to /' 'cd /'
+
+cat >ls.expected <<EOF
+100644 $ONE_SHA1 0     foo/bar/barme
+100644 $ONE_SHA1 0     foo/foome
+100644 $ONE_SHA1 0     me
+EOF
+
+export GIT_DIR="$TRASH_DIRECTORY/.git"
+export GIT_WORK_TREE=/
+
+test_vars 'abs gitdir, root' "$GIT_DIR" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /foo' 'cd /foo'
+
+test_vars 'abs gitdir, foo' "$GIT_DIR" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+
+test_vars 'abs gitdir, foo/bar' "$GIT_DIR" "/" "foo/bar/"
+test_foobar_foobar
+
+say "GIT_DIR relative, GIT_WORK_TREE set"
+
+test_expect_success 'go to /' 'cd /'
+
+export GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git"
+export GIT_WORK_TREE=/
+
+test_vars 'rel gitdir, root' "$GIT_DIR" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /foo' 'cd /foo'
+
+export GIT_DIR="../$TRASH_DIRECTORY/.git"
+export GIT_WORK_TREE=/
+
+test_vars 'rel gitdir, foo' "$TRASH_DIRECTORY/.git" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+
+export GIT_DIR="../../$TRASH_DIRECTORY/.git"
+export GIT_WORK_TREE=/
+
+test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
+test_foobar_foobar
+
+say "GIT_DIR relative, GIT_WORK_TREE relative"
+
+test_expect_success 'go to /' 'cd /'
+
+export GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git"
+export GIT_WORK_TREE=.
+
+test_vars 'rel gitdir, root' "$GIT_DIR" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /' 'cd /foo'
+
+export GIT_DIR="../$TRASH_DIRECTORY/.git"
+export GIT_WORK_TREE=..
+
+test_vars 'rel gitdir, foo' "$TRASH_DIRECTORY/.git" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+
+export GIT_DIR="../../$TRASH_DIRECTORY/.git"
+export GIT_WORK_TREE=../..
+
+test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
+test_foobar_foobar
+
+say ".git at root"
+
+unset GIT_DIR
+unset GIT_WORK_TREE
+
+test_expect_success 'go to /' 'cd /'
+test_expect_success 'setup' '
+       rm -rf /.git
+       echo "Initialized empty Git repository in /.git/" > expected &&
+       git init > result &&
+       test_cmp expected result
+'
+
+test_vars 'auto gitdir, root' ".git" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /foo' 'cd /foo'
+test_vars 'auto gitdir, foo' "/.git" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+test_vars 'auto gitdir, foo/bar' "/.git" "/" "foo/bar/"
+test_foobar_foobar
+
+test_expect_success 'cleanup' 'rm -rf /.git'
+
+say "auto bare gitdir"
+
+# DESTROYYYYY!!!!!
+test_expect_success 'setup' '
+       rm -rf /refs /objects /info /hooks
+       rm /*
+       cd / &&
+       echo "Initialized empty Git repository in /" > expected &&
+       git init --bare > result &&
+       test_cmp expected result
+'
+
+test_vars 'auto gitdir, root' "." "" ""
+
+test_expect_success 'go to /foo' 'cd /foo'
+
+test_vars 'auto gitdir, root' "/" "" ""
+
+test_done
diff --git a/t/t1509/excludes b/t/t1509/excludes
new file mode 100644 (file)
index 0000000..d4d21d3
--- /dev/null
@@ -0,0 +1,14 @@
+*.o
+*~
+*.bak
+*.c
+*.h
+.git
+contrib
+Documentation
+git-gui
+gitk-git
+gitweb
+t/t4013
+t/t5100
+t/t5515
diff --git a/t/t1509/prepare-chroot.sh b/t/t1509/prepare-chroot.sh
new file mode 100755 (executable)
index 0000000..c5334a8
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+die() {
+       echo >&2 "$@"
+       exit 1
+}
+
+xmkdir() {
+       while [ -n "$1" ]; do
+               [ -d "$1" ] || mkdir "$1" || die "Unable to mkdir $1"
+               shift
+       done
+}
+
+R="$1"
+
+[ -n "$R" ] || die "Usage: prepare-chroot.sh <root>"
+[ -x git ] || die "This script needs to be executed at git source code's top directory"
+[ -x /bin/busybox ] || die "You need busybox"
+
+xmkdir "$R" "$R/bin" "$R/etc" "$R/lib" "$R/dev"
+[ -c "$R/dev/null" ] || die "/dev/null is missing. Do mknod $R/dev/null c 1 3 && chmod 666 $R/dev/null"
+echo "root:x:0:0:root:/:/bin/sh" > "$R/etc/passwd"
+echo "$(id -nu):x:$(id -u):$(id -g)::$(pwd)/t:/bin/sh" >> "$R/etc/passwd"
+echo "root::0:root" > "$R/etc/group"
+echo "$(id -ng)::$(id -g):$(id -nu)" >> "$R/etc/group"
+
+[ -x "$R/bin/busybox" ] || cp /bin/busybox "$R/bin/busybox"
+[ -x "$R/bin/sh" ] || ln -s /bin/busybox "$R/bin/sh"
+[ -x "$R/bin/su" ] || ln -s /bin/busybox "$R/bin/su"
+
+mkdir -p "$R$(pwd)"
+rsync --exclude-from t/t1509/excludes -Ha . "$R$(pwd)"
+ldd git | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+       mkdir -p "$R$(dirname $i)"
+       cp "$i" "$R/$i"
+done
+echo "Execute this in root: 'chroot $R /bin/su - $(id -nu)'"
index 4d1c2e9e099918b5ff4e2e9458e702546f4c9605..2144184d790055ab12dfbb53ec10f519de598f72 100755 (executable)
@@ -66,6 +66,14 @@ test_expect_success 'git checkout -p HEAD^' '
        verify_state dir/foo parent parent
 '
 
+test_expect_success 'git checkout -p handles deletion' '
+       set_state dir/foo work index &&
+       rm dir/foo &&
+       (echo n; echo y) | git checkout -p &&
+       verify_saved_state bar &&
+       verify_state dir/foo index index
+'
+
 # The idea in the rest is that bar sorts first, so we always say 'y'
 # first and if the path limiter fails it'll apply to bar instead of
 # dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
index 912075063b9946d38a9ff72621cb80bcf2c05399..2ad2819a345af53ff6ab0b7c28fa313f1a0a5956 100755 (executable)
@@ -176,4 +176,9 @@ test_expect_success 'add -u resolves unmerged paths' '
 
 '
 
+test_expect_success '"add -u non-existent" should fail' '
+       test_must_fail git add -u non-existent &&
+       ! (git ls-files | grep "non-existent")
+'
+
 test_done
diff --git a/t/t2204-add-ignored.sh b/t/t2204-add-ignored.sh
new file mode 100755 (executable)
index 0000000..24afdab
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='giving ignored paths to git add'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       mkdir sub dir dir/sub &&
+       echo sub >.gitignore &&
+       echo ign >>.gitignore &&
+       for p in . sub dir dir/sub
+       do
+               >"$p/ign" &&
+               >"$p/file" || exit 1
+       done
+'
+
+for i in file dir/file dir 'd*'
+do
+       test_expect_success "no complaints for unignored $i" '
+               rm -f .git/index &&
+               git add "$i" &&
+               git ls-files "$i" >out &&
+               test -s out
+       '
+done
+
+for i in ign dir/ign dir/sub dir/sub/*ign sub/file sub sub/*
+do
+       test_expect_success "complaints for ignored $i" '
+               rm -f .git/index &&
+               test_must_fail git add "$i" 2>err &&
+               git ls-files "$i" >out &&
+               ! test -s out &&
+               grep -e "Use -f if" err &&
+               cat err
+       '
+
+       test_expect_success "complaints for ignored $i with unignored file" '
+               rm -f .git/index &&
+               test_must_fail git add "$i" file 2>err &&
+               git ls-files "$i" >out &&
+               ! test -s out &&
+               grep -e "Use -f if" err &&
+               cat err
+       '
+done
+
+for i in sub sub/*
+do
+       test_expect_success "complaints for ignored $i in dir" '
+               rm -f .git/index &&
+               (
+                       cd dir &&
+                       test_must_fail git add "$i" 2>err &&
+                       git ls-files "$i" >out &&
+                       ! test -s out &&
+                       grep -e "Use -f if" err &&
+                       cat err
+               )
+       '
+done
+
+for i in ign file
+do
+       test_expect_success "complaints for ignored $i in sub" '
+               rm -f .git/index &&
+               (
+                       cd sub &&
+                       test_must_fail git add "$i" 2>err &&
+                       git ls-files "$i" >out &&
+                       ! test -s out &&
+                       grep -e "Use -f if" err &&
+                       cat err
+               )
+       '
+done
+
+test_done
index f4066cbc090a8fd0f6a528eed65d16d705c1bb18..a7d8187169a36f708bd7dc39d97604dd9cc21565 100755 (executable)
@@ -11,9 +11,11 @@ line.
 '
 . ./test-lib.sh
 
-touch foo bar
-git update-index --add foo bar
-git commit -m "add foo bar"
+test_expect_success 'setup' '
+       touch foo bar &&
+       git update-index --add foo bar &&
+       git commit -m "add foo bar"
+'
 
 test_expect_success \
     'git ls-files --error-unmatch should fail with unmatched path.' \
index 5d9604b8155401a2e345a69a5d030cffd7d16818..a4a0b1d6c58a7ae2762de6a907b4138238e95a48 100755 (executable)
@@ -8,15 +8,16 @@ test_description='Test commit notes'
 . ./test-lib.sh
 
 cat > fake_editor.sh << \EOF
+#!/bin/sh
 echo "$MSG" > "$1"
 echo "$MSG" >& 2
 EOF
 chmod a+x fake_editor.sh
-VISUAL=./fake_editor.sh
-export VISUAL
+GIT_EDITOR=./fake_editor.sh
+export GIT_EDITOR
 
 test_expect_success 'cannot annotate non-existing HEAD' '
-       (MSG=3 && export MSG && test_must_fail git notes edit)
+       (MSG=3 && export MSG && test_must_fail git notes add)
 '
 
 test_expect_success setup '
@@ -32,18 +33,18 @@ test_expect_success setup '
 
 test_expect_success 'need valid notes ref' '
        (MSG=1 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&
-        test_must_fail git notes edit) &&
+        test_must_fail git notes add) &&
        (MSG=2 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&
         test_must_fail git notes show)
 '
 
-test_expect_success 'refusing to edit in refs/heads/' '
+test_expect_success 'refusing to add notes in refs/heads/' '
        (MSG=1 GIT_NOTES_REF=refs/heads/bogus &&
         export MSG GIT_NOTES_REF &&
-        test_must_fail git notes edit)
+        test_must_fail git notes add)
 '
 
-test_expect_success 'refusing to edit in refs/remotes/' '
+test_expect_success 'refusing to edit notes in refs/remotes/' '
        (MSG=1 GIT_NOTES_REF=refs/remotes/bogus &&
         export MSG GIT_NOTES_REF &&
         test_must_fail git notes edit)
@@ -56,8 +57,35 @@ test_expect_success 'handle empty notes gracefully' '
 
 test_expect_success 'create notes' '
        git config core.notesRef refs/notes/commits &&
-       MSG=b1 git notes edit &&
-       test ! -f .git/new-notes &&
+       MSG=b4 git notes add &&
+       test ! -f .git/NOTES_EDITMSG &&
+       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+       test b4 = $(git notes show) &&
+       git show HEAD^ &&
+       test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'edit existing notes' '
+       MSG=b3 git notes edit &&
+       test ! -f .git/NOTES_EDITMSG &&
+       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+       test b3 = $(git notes show) &&
+       git show HEAD^ &&
+       test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'cannot add note where one exists' '
+       ! MSG=b2 git notes add &&
+       test ! -f .git/NOTES_EDITMSG &&
+       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+       test b3 = $(git notes show) &&
+       git show HEAD^ &&
+       test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'can overwrite existing note with "git notes add -f"' '
+       MSG=b1 git notes add -f &&
+       test ! -f .git/NOTES_EDITMSG &&
        test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
        test b1 = $(git notes show) &&
        git show HEAD^ &&
@@ -80,6 +108,7 @@ test_expect_success 'show notes' '
        git log -1 > output &&
        test_cmp expect output
 '
+
 test_expect_success 'create multi-line notes (setup)' '
        : > a3 &&
        git add a3 &&
@@ -87,7 +116,7 @@ test_expect_success 'create multi-line notes (setup)' '
        git commit -m 3rd &&
        MSG="b3
 c3c3c3c3
-d3d3d3" git notes edit
+d3d3d3" git notes add
 '
 
 cat > expect-multiline << EOF
@@ -110,19 +139,16 @@ test_expect_success 'show multi-line notes' '
        git log -2 > output &&
        test_cmp expect-multiline output
 '
-test_expect_success 'create -m and -F notes (setup)' '
+test_expect_success 'create -F notes (setup)' '
        : > a4 &&
        git add a4 &&
        test_tick &&
        git commit -m 4th &&
        echo "xyzzy" > note5 &&
-       git notes edit -m spam -F note5 -m "foo
-bar
-baz"
+       git notes add -F note5
 '
 
-whitespace="    "
-cat > expect-m-and-F << EOF
+cat > expect-F << EOF
 commit 15023535574ded8b1a89052b32673f84cf9582b8
 Author: A U Thor <author@example.com>
 Date:   Thu Apr 7 15:16:13 2005 -0700
@@ -130,21 +156,15 @@ Date:   Thu Apr 7 15:16:13 2005 -0700
     4th
 
 Notes:
-    spam
-$whitespace
     xyzzy
-$whitespace
-    foo
-    bar
-    baz
 EOF
 
-printf "\n" >> expect-m-and-F
-cat expect-multiline >> expect-m-and-F
+printf "\n" >> expect-F
+cat expect-multiline >> expect-F
 
-test_expect_success 'show -m and -F notes' '
+test_expect_success 'show -F notes' '
        git log -3 > output &&
-       test_cmp expect-m-and-F output
+       test_cmp expect-F output
 '
 
 cat >expect << EOF
@@ -164,13 +184,7 @@ test_expect_success 'git log --pretty=raw does not show notes' '
 cat >>expect <<EOF
 
 Notes:
-    spam
-$whitespace
     xyzzy
-$whitespace
-    foo
-    bar
-    baz
 EOF
 test_expect_success 'git log --show-notes' '
        git log -1 --pretty=raw --show-notes >output &&
@@ -179,17 +193,17 @@ test_expect_success 'git log --show-notes' '
 
 test_expect_success 'git log --no-notes' '
        git log -1 --no-notes >output &&
-       ! grep spam output
+       ! grep xyzzy output
 '
 
 test_expect_success 'git format-patch does not show notes' '
        git format-patch -1 --stdout >output &&
-       ! grep spam output
+       ! grep xyzzy output
 '
 
 test_expect_success 'git format-patch --show-notes does show notes' '
        git format-patch --show-notes -1 --stdout >output &&
-       grep spam output
+       grep xyzzy output
 '
 
 for pretty in \
@@ -202,8 +216,794 @@ do
        esac
        test_expect_success "git show $pretty does$not show notes" '
                git show $p >output &&
-               eval "$negate grep spam output"
+               eval "$negate grep xyzzy output"
        '
 done
 
+test_expect_success 'create -m notes (setup)' '
+       : > a5 &&
+       git add a5 &&
+       test_tick &&
+       git commit -m 5th &&
+       git notes add -m spam -m "foo
+bar
+baz"
+'
+
+whitespace="    "
+cat > expect-m << EOF
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:17:13 2005 -0700
+
+    5th
+
+Notes:
+    spam
+$whitespace
+    foo
+    bar
+    baz
+EOF
+
+printf "\n" >> expect-m
+cat expect-F >> expect-m
+
+test_expect_success 'show -m notes' '
+       git log -4 > output &&
+       test_cmp expect-m output
+'
+
+test_expect_success 'remove note with add -f -F /dev/null (setup)' '
+       git notes add -f -F /dev/null
+'
+
+cat > expect-rm-F << EOF
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:17:13 2005 -0700
+
+    5th
+EOF
+
+printf "\n" >> expect-rm-F
+cat expect-F >> expect-rm-F
+
+test_expect_success 'verify note removal with -F /dev/null' '
+       git log -4 > output &&
+       test_cmp expect-rm-F output &&
+       ! git notes show
+'
+
+test_expect_success 'do not create empty note with -m "" (setup)' '
+       git notes add -m ""
+'
+
+test_expect_success 'verify non-creation of note with -m ""' '
+       git log -4 > output &&
+       test_cmp expect-rm-F output &&
+       ! git notes show
+'
+
+cat > expect-combine_m_and_F << EOF
+foo
+
+xyzzy
+
+bar
+
+zyxxy
+
+baz
+EOF
+
+test_expect_success 'create note with combination of -m and -F' '
+       echo "xyzzy" > note_a &&
+       echo "zyxxy" > note_b &&
+       git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" &&
+       git notes show > output &&
+       test_cmp expect-combine_m_and_F output
+'
+
+test_expect_success 'remove note with "git notes remove" (setup)' '
+       git notes remove HEAD^ &&
+       git notes remove
+'
+
+cat > expect-rm-remove << EOF
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:17:13 2005 -0700
+
+    5th
+
+commit 15023535574ded8b1a89052b32673f84cf9582b8
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:16:13 2005 -0700
+
+    4th
+EOF
+
+printf "\n" >> expect-rm-remove
+cat expect-multiline >> expect-rm-remove
+
+test_expect_success 'verify note removal with "git notes remove"' '
+       git log -4 > output &&
+       test_cmp expect-rm-remove output &&
+       ! git notes show HEAD^
+'
+
+cat > expect << EOF
+c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75
+c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
+EOF
+
+test_expect_success 'list notes with "git notes list"' '
+       git notes list > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'list notes with "git notes"' '
+       git notes > output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+c18dc024e14f08d18d14eea0d747ff692d66d6a3
+EOF
+
+test_expect_success 'list specific note with "git notes list <object>"' '
+       git notes list HEAD^^ > output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+EOF
+
+test_expect_success 'listing non-existing notes fails' '
+       test_must_fail git notes list HEAD > output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+Initial set of notes
+
+More notes appended with git notes append
+EOF
+
+test_expect_success 'append to existing note with "git notes append"' '
+       git notes add -m "Initial set of notes" &&
+       git notes append -m "More notes appended with git notes append" &&
+       git notes show > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'appending empty string does not change existing note' '
+       git notes append -m "" &&
+       git notes show > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'git notes append == add when there is no existing note' '
+       git notes remove HEAD &&
+       test_must_fail git notes list HEAD &&
+       git notes append -m "Initial set of notes
+
+More notes appended with git notes append" &&
+       git notes show > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'appending empty string to non-existing note does not create note' '
+       git notes remove HEAD &&
+       test_must_fail git notes list HEAD &&
+       git notes append -m "" &&
+       test_must_fail git notes list HEAD
+'
+
+test_expect_success 'create other note on a different notes ref (setup)' '
+       : > a6 &&
+       git add a6 &&
+       test_tick &&
+       git commit -m 6th &&
+       GIT_NOTES_REF="refs/notes/other" git notes add -m "other note"
+'
+
+cat > expect-other << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+
+Notes (other):
+    other note
+EOF
+
+cat > expect-not-other << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+EOF
+
+test_expect_success 'Do not show note on other ref by default' '
+       git log -1 > output &&
+       test_cmp expect-not-other output
+'
+
+test_expect_success 'Do show note when ref is given in GIT_NOTES_REF' '
+       GIT_NOTES_REF="refs/notes/other" git log -1 > output &&
+       test_cmp expect-other output
+'
+
+test_expect_success 'Do show note when ref is given in core.notesRef config' '
+       git config core.notesRef "refs/notes/other" &&
+       git log -1 > output &&
+       test_cmp expect-other output
+'
+
+test_expect_success 'Do not show note when core.notesRef is overridden' '
+       GIT_NOTES_REF="refs/notes/wrong" git log -1 > output &&
+       test_cmp expect-not-other output
+'
+
+cat > expect-both << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+
+Notes:
+    order test
+
+Notes (other):
+    other note
+
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:17:13 2005 -0700
+
+    5th
+
+Notes:
+    replacement for deleted note
+EOF
+
+test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '
+       GIT_NOTES_REF=refs/notes/commits git notes add \
+               -m"replacement for deleted note" HEAD^ &&
+       GIT_NOTES_REF=refs/notes/commits git notes add -m"order test" &&
+       git config --unset core.notesRef &&
+       git config notes.displayRef "refs/notes/*" &&
+       git log -2 > output &&
+       test_cmp expect-both output
+'
+
+test_expect_success 'core.notesRef is implicitly in notes.displayRef' '
+       git config core.notesRef refs/notes/commits &&
+       git config notes.displayRef refs/notes/other &&
+       git log -2 > output &&
+       test_cmp expect-both output
+'
+
+test_expect_success 'notes.displayRef can be given more than once' '
+       git config --unset core.notesRef &&
+       git config notes.displayRef refs/notes/commits &&
+       git config --add notes.displayRef refs/notes/other &&
+       git log -2 > output &&
+       test_cmp expect-both output
+'
+
+cat > expect-both-reversed << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+
+Notes (other):
+    other note
+
+Notes:
+    order test
+EOF
+
+test_expect_success 'notes.displayRef respects order' '
+       git config core.notesRef refs/notes/other &&
+       git config --unset-all notes.displayRef &&
+       git config notes.displayRef refs/notes/commits &&
+       git log -1 > output &&
+       test_cmp expect-both-reversed output
+'
+
+test_expect_success 'GIT_NOTES_DISPLAY_REF works' '
+       git config --unset-all core.notesRef &&
+       git config --unset-all notes.displayRef &&
+       GIT_NOTES_DISPLAY_REF=refs/notes/commits:refs/notes/other \
+               git log -2 > output &&
+       test_cmp expect-both output
+'
+
+cat > expect-none << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:17:13 2005 -0700
+
+    5th
+EOF
+
+test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '
+       git config notes.displayRef "refs/notes/*" &&
+       GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log -2 > output &&
+       test_cmp expect-none output
+'
+
+test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' '
+       GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log --show-notes=* -2 > output &&
+       test_cmp expect-both output
+'
+
+cat > expect-commits << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:18:13 2005 -0700
+
+    6th
+
+Notes:
+    order test
+EOF
+
+test_expect_success '--no-standard-notes' '
+       git log --no-standard-notes --show-notes=commits -1 > output &&
+       test_cmp expect-commits output
+'
+
+test_expect_success '--standard-notes' '
+       git log --no-standard-notes --show-notes=commits \
+               --standard-notes -2 > output &&
+       test_cmp expect-both output
+'
+
+test_expect_success '--show-notes=ref accumulates' '
+       git log --show-notes=other --show-notes=commits \
+                --no-standard-notes -1 > output &&
+       test_cmp expect-both-reversed output
+'
+
+test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
+       git config core.notesRef refs/notes/other &&
+       echo "Note on a tree" > expect
+       git notes add -m "Note on a tree" HEAD: &&
+       git notes show HEAD: > actual &&
+       test_cmp expect actual &&
+       echo "Note on a blob" > expect
+       filename=$(git ls-tree --name-only HEAD | head -n1) &&
+       git notes add -m "Note on a blob" HEAD:$filename &&
+       git notes show HEAD:$filename > actual &&
+       test_cmp expect actual &&
+       echo "Note on a tag" > expect
+       git tag -a -m "This is an annotated tag" foobar HEAD^ &&
+       git notes add -m "Note on a tag" foobar &&
+       git notes show foobar > actual &&
+       test_cmp expect actual
+'
+
+cat > expect << EOF
+commit 2ede89468182a62d0bde2583c736089bcf7d7e92
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:19:13 2005 -0700
+
+    7th
+
+Notes (other):
+    other note
+EOF
+
+test_expect_success 'create note from other note with "git notes add -C"' '
+       : > a7 &&
+       git add a7 &&
+       test_tick &&
+       git commit -m 7th &&
+       git notes add -C $(git notes list HEAD^) &&
+       git log -1 > actual &&
+       test_cmp expect actual &&
+       test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+'
+
+test_expect_success 'create note from non-existing note with "git notes add -C" fails' '
+       : > a8 &&
+       git add a8 &&
+       test_tick &&
+       git commit -m 8th &&
+       test_must_fail git notes add -C deadbeef &&
+       test_must_fail git notes list HEAD
+'
+
+cat > expect << EOF
+commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:21:13 2005 -0700
+
+    9th
+
+Notes (other):
+    yet another note
+EOF
+
+test_expect_success 'create note from other note with "git notes add -c"' '
+       : > a9 &&
+       git add a9 &&
+       test_tick &&
+       git commit -m 9th &&
+       MSG="yet another note" git notes add -c $(git notes list HEAD^^) &&
+       git log -1 > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'create note from non-existing note with "git notes add -c" fails' '
+       : > a10 &&
+       git add a10 &&
+       test_tick &&
+       git commit -m 10th &&
+       test_must_fail MSG="yet another note" git notes add -c deadbeef &&
+       test_must_fail git notes list HEAD
+'
+
+cat > expect << EOF
+commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:21:13 2005 -0700
+
+    9th
+
+Notes (other):
+    yet another note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'append to note from other note with "git notes append -C"' '
+       git notes append -C $(git notes list HEAD^) HEAD^ &&
+       git log -1 HEAD^ > actual &&
+       test_cmp expect actual
+'
+
+cat > expect << EOF
+commit ffed603236bfa3891c49644257a83598afe8ae5a
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:22:13 2005 -0700
+
+    10th
+
+Notes (other):
+    other note
+EOF
+
+test_expect_success 'create note from other note with "git notes append -c"' '
+       MSG="other note" git notes append -c $(git notes list HEAD^) &&
+       git log -1 > actual &&
+       test_cmp expect actual
+'
+
+cat > expect << EOF
+commit ffed603236bfa3891c49644257a83598afe8ae5a
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:22:13 2005 -0700
+
+    10th
+
+Notes (other):
+    other note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'append to note from other note with "git notes append -c"' '
+       MSG="yet another note" git notes append -c $(git notes list HEAD) &&
+       git log -1 > actual &&
+       test_cmp expect actual
+'
+
+cat > expect << EOF
+commit 6352c5e33dbcab725fe0579be16aa2ba8eb369be
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:23:13 2005 -0700
+
+    11th
+
+Notes (other):
+    other note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'copy note with "git notes copy"' '
+       : > a11 &&
+       git add a11 &&
+       test_tick &&
+       git commit -m 11th &&
+       git notes copy HEAD^ HEAD &&
+       git log -1 > actual &&
+       test_cmp expect actual &&
+       test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+'
+
+test_expect_success 'prevent overwrite with "git notes copy"' '
+       test_must_fail git notes copy HEAD~2 HEAD &&
+       git log -1 > actual &&
+       test_cmp expect actual &&
+       test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+'
+
+cat > expect << EOF
+commit 6352c5e33dbcab725fe0579be16aa2ba8eb369be
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:23:13 2005 -0700
+
+    11th
+
+Notes (other):
+    yet another note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'allow overwrite with "git notes copy -f"' '
+       git notes copy -f HEAD~2 HEAD &&
+       git log -1 > actual &&
+       test_cmp expect actual &&
+       test "$(git notes list HEAD)" = "$(git notes list HEAD~2)"
+'
+
+test_expect_success 'cannot copy note from object without notes' '
+       : > a12 &&
+       git add a12 &&
+       test_tick &&
+       git commit -m 12th &&
+       : > a13 &&
+       git add a13 &&
+       test_tick &&
+       git commit -m 13th &&
+       test_must_fail git notes copy HEAD^ HEAD
+'
+
+cat > expect << EOF
+commit e5d4fb5698d564ab8c73551538ecaf2b0c666185
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:25:13 2005 -0700
+
+    13th
+
+Notes (other):
+    yet another note
+$whitespace
+    yet another note
+
+commit 7038787dfe22a14c3867ce816dbba39845359719
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:24:13 2005 -0700
+
+    12th
+
+Notes (other):
+    other note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'git notes copy --stdin' '
+       (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+       echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
+       git notes copy --stdin &&
+       git log -2 > output &&
+       test_cmp expect output &&
+       test "$(git notes list HEAD)" = "$(git notes list HEAD~2)" &&
+       test "$(git notes list HEAD^)" = "$(git notes list HEAD~3)"
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:26:13 2005 -0700
+
+    14th
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
+       test_commit 14th &&
+       test_commit 15th &&
+       (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+       echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
+       git notes copy --for-rewrite=foo &&
+       git log -2 > output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    yet another note
+$whitespace
+    yet another note
+
+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:26:13 2005 -0700
+
+    14th
+
+Notes (other):
+    other note
+$whitespace
+    yet another note
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (enabled)' '
+       git config notes.rewriteMode overwrite &&
+       git config notes.rewriteRef "refs/notes/*" &&
+       (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+       echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
+       git notes copy --for-rewrite=foo &&
+       git log -2 > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'git notes copy --for-rewrite (disabled)' '
+       git config notes.rewrite.bar false &&
+       echo $(git rev-parse HEAD~3) $(git rev-parse HEAD) |
+       git notes copy --for-rewrite=bar &&
+       git log -2 > output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    a fresh note
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (overwrite)' '
+       git notes add -f -m"a fresh note" HEAD^ &&
+       echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+       git notes copy --for-rewrite=foo &&
+       git log -1 > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'git notes copy --for-rewrite (ignore)' '
+       git config notes.rewriteMode ignore &&
+       echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+       git notes copy --for-rewrite=foo &&
+       git log -1 > output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    a fresh note
+    another fresh note
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (append)' '
+       git notes add -f -m"another fresh note" HEAD^ &&
+       git config notes.rewriteMode concatenate &&
+       echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+       git notes copy --for-rewrite=foo &&
+       git log -1 > output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    a fresh note
+    another fresh note
+    append 1
+    append 2
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (append two to one)' '
+       git notes add -f -m"append 1" HEAD^ &&
+       git notes add -f -m"append 2" HEAD^^ &&
+       (echo $(git rev-parse HEAD^) $(git rev-parse HEAD);
+       echo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |
+       git notes copy --for-rewrite=foo &&
+       git log -1 > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'git notes copy --for-rewrite (append empty)' '
+       git notes remove HEAD^ &&
+       echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+       git notes copy --for-rewrite=foo &&
+       git log -1 > output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    replacement note 1
+EOF
+
+test_expect_success 'GIT_NOTES_REWRITE_MODE works' '
+       git notes add -f -m"replacement note 1" HEAD^ &&
+       echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+       GIT_NOTES_REWRITE_MODE=overwrite git notes copy --for-rewrite=foo &&
+       git log -1 > output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:27:13 2005 -0700
+
+    15th
+
+Notes (other):
+    replacement note 2
+EOF
+
+test_expect_success 'GIT_NOTES_REWRITE_REF works' '
+       git config notes.rewriteMode overwrite &&
+       git notes add -f -m"replacement note 2" HEAD^ &&
+       git config --unset-all notes.rewriteRef &&
+       echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+       GIT_NOTES_REWRITE_REF=refs/notes/commits:refs/notes/other \
+               git notes copy --for-rewrite=foo &&
+       git log -1 > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '
+       git config notes.rewriteRef refs/notes/other &&
+       git notes add -f -m"replacement note 3" HEAD^ &&
+       echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+       GIT_NOTES_REWRITE_REF= git notes copy --for-rewrite=foo &&
+       git log -1 > output &&
+       test_cmp expect output
+'
 test_done
index edc4bc884147f2be2d433a7b57f27c524aa933c7..75ec18778e1be732593ae130aa257eca3290e36f 100755 (executable)
@@ -95,12 +95,12 @@ INPUT_END
 test_expect_success 'test notes in 2/38-fanout' 'test_sha1_based "s|^..|&/|"'
 test_expect_success 'verify notes in 2/38-fanout' 'verify_notes'
 
-test_expect_success 'test notes in 4/36-fanout' 'test_sha1_based "s|^....|&/|"'
-test_expect_success 'verify notes in 4/36-fanout' 'verify_notes'
-
 test_expect_success 'test notes in 2/2/36-fanout' 'test_sha1_based "s|^\(..\)\(..\)|\1/\2/|"'
 test_expect_success 'verify notes in 2/2/36-fanout' 'verify_notes'
 
+test_expect_success 'test notes in 2/2/2/34-fanout' 'test_sha1_based "s|^\(..\)\(..\)\(..\)|\1/\2/\3/|"'
+test_expect_success 'verify notes in 2/2/2/34-fanout' 'verify_notes'
+
 test_same_notes () {
        (
                start_note_commit &&
@@ -128,14 +128,17 @@ INPUT_END
        git fast-import --quiet
 }
 
-test_expect_success 'test same notes in 4/36-fanout and 2/38-fanout' 'test_same_notes "s|^..|&/|" "s|^....|&/|"'
-test_expect_success 'verify same notes in 4/36-fanout and 2/38-fanout' 'verify_notes'
+test_expect_success 'test same notes in no fanout and 2/38-fanout' 'test_same_notes "s|^..|&/|" ""'
+test_expect_success 'verify same notes in no fanout and 2/38-fanout' 'verify_notes'
+
+test_expect_success 'test same notes in no fanout and 2/2/36-fanout' 'test_same_notes "s|^\(..\)\(..\)|\1/\2/|" ""'
+test_expect_success 'verify same notes in no fanout and 2/2/36-fanout' 'verify_notes'
 
 test_expect_success 'test same notes in 2/38-fanout and 2/2/36-fanout' 'test_same_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^..|&/|"'
 test_expect_success 'verify same notes in 2/38-fanout and 2/2/36-fanout' 'verify_notes'
 
-test_expect_success 'test same notes in 4/36-fanout and 2/2/36-fanout' 'test_same_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^....|&/|"'
-test_expect_success 'verify same notes in 4/36-fanout and 2/2/36-fanout' 'verify_notes'
+test_expect_success 'test same notes in 2/2/2/34-fanout and 2/2/36-fanout' 'test_same_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^\(..\)\(..\)\(..\)|\1/\2/\3/|"'
+test_expect_success 'verify same notes in 2/2/2/34-fanout and 2/2/36-fanout' 'verify_notes'
 
 test_concatenated_notes () {
        (
@@ -176,13 +179,16 @@ verify_concatenated_notes () {
     test_cmp expect output
 }
 
-test_expect_success 'test notes in 4/36-fanout concatenated with 2/38-fanout' 'test_concatenated_notes "s|^..|&/|" "s|^....|&/|"'
-test_expect_success 'verify notes in 4/36-fanout concatenated with 2/38-fanout' 'verify_concatenated_notes'
+test_expect_success 'test notes in no fanout concatenated with 2/38-fanout' 'test_concatenated_notes "s|^..|&/|" ""'
+test_expect_success 'verify notes in no fanout concatenated with 2/38-fanout' 'verify_concatenated_notes'
+
+test_expect_success 'test notes in no fanout concatenated with 2/2/36-fanout' 'test_concatenated_notes "s|^\(..\)\(..\)|\1/\2/|" ""'
+test_expect_success 'verify notes in no fanout concatenated with 2/2/36-fanout' 'verify_concatenated_notes'
 
 test_expect_success 'test notes in 2/38-fanout concatenated with 2/2/36-fanout' 'test_concatenated_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^..|&/|"'
 test_expect_success 'verify notes in 2/38-fanout concatenated with 2/2/36-fanout' 'verify_concatenated_notes'
 
-test_expect_success 'test notes in 4/36-fanout concatenated with 2/2/36-fanout' 'test_concatenated_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^....|&/|"'
-test_expect_success 'verify notes in 4/36-fanout concatenated with 2/2/36-fanout' 'verify_concatenated_notes'
+test_expect_success 'test notes in 2/2/36-fanout concatenated with 2/2/2/34-fanout' 'test_concatenated_notes "s|^\(..\)\(..\)\(..\)|\1/\2/\3/|" "s|^\(..\)\(..\)|\1/\2/|"'
+test_expect_success 'verify notes in 2/2/36-fanout concatenated with 2/2/2/34-fanout' 'verify_concatenated_notes'
 
 test_done
index 256687ffb53aef91666561bd91e0188ff62d8690..1709e8c00b859ae4f8ce91c7920a9411ac4acbce 100755 (executable)
@@ -131,6 +131,17 @@ data <<EOF
 another non-note with SHA1-like name
 EOF
 
+M 644 inline de/adbeefdeadbeefdeadbeefdeadbeefdeadbeef
+data <<EOF
+This is actually a valid note, albeit to a non-existing object.
+It is needed in order to trigger the "mishandling" of the dead/beef non-note.
+EOF
+
+M 644 inline dead/beef
+data <<EOF
+yet another non-note with SHA1-like name
+EOF
+
 INPUT_END
        git fast-import --quiet <input &&
        git config core.notesRef refs/notes/commits
@@ -158,6 +169,9 @@ EXPECT_END
 cat >expect_nn3 <<EXPECT_END
 another non-note with SHA1-like name
 EXPECT_END
+cat >expect_nn4 <<EXPECT_END
+yet another non-note with SHA1-like name
+EXPECT_END
 
 test_expect_success "verify contents of non-notes" '
 
@@ -166,7 +180,27 @@ test_expect_success "verify contents of non-notes" '
        git cat-file -p refs/notes/commits:deadbeef > actual_nn2 &&
        test_cmp expect_nn2 actual_nn2 &&
        git cat-file -p refs/notes/commits:de/adbeef > actual_nn3 &&
-       test_cmp expect_nn3 actual_nn3
+       test_cmp expect_nn3 actual_nn3 &&
+       git cat-file -p refs/notes/commits:dead/beef > actual_nn4 &&
+       test_cmp expect_nn4 actual_nn4
+'
+
+test_expect_success "git-notes preserves non-notes" '
+
+       test_tick &&
+       git notes add -f -m "foo bar"
+'
+
+test_expect_success "verify contents of non-notes after git-notes" '
+
+       git cat-file -p refs/notes/commits:foobar/non-note.txt > actual_nn1 &&
+       test_cmp expect_nn1 actual_nn1 &&
+       git cat-file -p refs/notes/commits:deadbeef > actual_nn2 &&
+       test_cmp expect_nn2 actual_nn2 &&
+       git cat-file -p refs/notes/commits:de/adbeef > actual_nn3 &&
+       test_cmp expect_nn3 actual_nn3 &&
+       git cat-file -p refs/notes/commits:dead/beef > actual_nn4 &&
+       test_cmp expect_nn4 actual_nn4
 '
 
 test_done
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
new file mode 100755 (executable)
index 0000000..b1ea64b
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
+
+. ./test-lib.sh
+
+test_expect_success 'creating many notes with git-notes' '
+       num_notes=300 &&
+       i=0 &&
+       while test $i -lt $num_notes
+       do
+               i=$(($i + 1)) &&
+               test_tick &&
+               echo "file for commit #$i" > file &&
+               git add file &&
+               git commit -q -m "commit #$i" &&
+               git notes add -m "note #$i" || return 1
+       done
+'
+
+test_expect_success 'many notes created correctly with git-notes' '
+       git log | grep "^    " > output &&
+       i=300 &&
+       while test $i -gt 0
+       do
+               echo "    commit #$i" &&
+               echo "    note #$i" &&
+               i=$(($i - 1));
+       done > expect &&
+       test_cmp expect output
+'
+
+test_expect_success 'many notes created with git-notes triggers fanout' '
+       # Expect entire notes tree to have a fanout == 1
+       git ls-tree -r --name-only refs/notes/commits |
+       while read path
+       do
+               case "$path" in
+               ??/??????????????????????????????????????)
+                       : true
+                       ;;
+               *)
+                       echo "Invalid path \"$path\"" &&
+                       return 1
+                       ;;
+               esac
+       done
+'
+
+test_expect_success 'deleting most notes with git-notes' '
+       num_notes=250 &&
+       i=0 &&
+       git rev-list HEAD |
+       while read sha1
+       do
+               i=$(($i + 1)) &&
+               if test $i -gt $num_notes
+               then
+                       break
+               fi &&
+               test_tick &&
+               git notes remove "$sha1"
+       done
+'
+
+test_expect_success 'most notes deleted correctly with git-notes' '
+       git log HEAD~250 | grep "^    " > output &&
+       i=50 &&
+       while test $i -gt 0
+       do
+               echo "    commit #$i" &&
+               echo "    note #$i" &&
+               i=$(($i - 1));
+       done > expect &&
+       test_cmp expect output
+'
+
+test_expect_success 'deleting most notes triggers fanout consolidation' '
+       # Expect entire notes tree to have a fanout == 0
+       git ls-tree -r --name-only refs/notes/commits |
+       while read path
+       do
+               case "$path" in
+               ????????????????????????????????????????)
+                       : true
+                       ;;
+               *)
+                       echo "Invalid path \"$path\"" &&
+                       return 1
+                       ;;
+               esac
+       done
+'
+
+test_done
diff --git a/t/t3306-notes-prune.sh b/t/t3306-notes-prune.sh
new file mode 100755 (executable)
index 0000000..a0ed035
--- /dev/null
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+test_description='Test git notes prune'
+
+. ./test-lib.sh
+
+test_expect_success 'setup: create a few commits with notes' '
+
+       : > file1 &&
+       git add file1 &&
+       test_tick &&
+       git commit -m 1st &&
+       git notes add -m "Note #1" &&
+       : > file2 &&
+       git add file2 &&
+       test_tick &&
+       git commit -m 2nd &&
+       git notes add -m "Note #2" &&
+       : > file3 &&
+       git add file3 &&
+       test_tick &&
+       git commit -m 3rd &&
+       git notes add -m "Note #3"
+'
+
+cat > expect <<END_OF_LOG
+commit 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:15:13 2005 -0700
+
+    3rd
+
+Notes:
+    Note #3
+
+commit 08341ad9e94faa089d60fd3f523affb25c6da189
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:14:13 2005 -0700
+
+    2nd
+
+Notes:
+    Note #2
+
+commit ab5f302035f2e7aaf04265f08b42034c23256e1f
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:13:13 2005 -0700
+
+    1st
+
+Notes:
+    Note #1
+END_OF_LOG
+
+test_expect_success 'verify commits and notes' '
+
+       git log > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'remove some commits' '
+
+       git reset --hard HEAD~2 &&
+       git reflog expire --expire=now HEAD &&
+       git gc --prune=now
+'
+
+test_expect_success 'verify that commits are gone' '
+
+       ! git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       ! git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+       git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_expect_success 'verify that notes are still present' '
+
+       git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+       git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_expect_success 'prune notes' '
+
+       git notes prune
+'
+
+test_expect_success 'verify that notes are gone' '
+
+       ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       ! git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+       git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_done
index 4314ad2d66d06b411e4bc0c9ee7b07553fc35ac2..dbf7dfba9b55e906d44a35d7b11ca2ceaad7e6f5 100755 (executable)
@@ -151,4 +151,21 @@ test_expect_success 'Rebase a commit that sprinkles CRs in' '
        git diff --exit-code file-with-cr:CR HEAD:CR
 '
 
+test_expect_success 'rebase can copy notes' '
+       git config notes.rewrite.rebase true &&
+       git config notes.rewriteRef "refs/notes/*" &&
+       test_commit n1 &&
+       test_commit n2 &&
+       test_commit n3 &&
+       git notes add -m"a note" n3 &&
+       git rebase --onto n1 n2 &&
+       test "a note" = "$(git notes show HEAD)"
+'
+
+test_expect_success 'rebase -m can copy notes' '
+       git reset --hard n3 &&
+       git rebase -m --onto n1 n2 &&
+       test "a note" = "$(git notes show HEAD)"
+'
+
 test_done
index 4e3513709eb121769f87501c1862c996184a6d05..19668c2c9206c5dfe63a5992bc7d011a9cdb4083 100755 (executable)
@@ -553,4 +553,28 @@ test_expect_success 'reword' '
        git show HEAD~2 | grep "C changed"
 '
 
+test_expect_success 'rebase -i can copy notes' '
+       git config notes.rewrite.rebase true &&
+       git config notes.rewriteRef "refs/notes/*" &&
+       test_commit n1 &&
+       test_commit n2 &&
+       test_commit n3 &&
+       git notes add -m"a note" n3 &&
+       git rebase --onto n1 n2 &&
+       test "a note" = "$(git notes show HEAD)"
+'
+
+cat >expect <<EOF
+an earlier note
+a note
+EOF
+
+test_expect_success 'rebase -i can copy notes over a fixup' '
+       git reset --hard n3 &&
+       git notes add -m"an earlier note" n2 &&
+       GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 &&
+       git notes show > output &&
+       test_cmp expect output
+'
+
 test_done
diff --git a/t/t3417-rebase-whitespace-fix.sh b/t/t3417-rebase-whitespace-fix.sh
new file mode 100755 (executable)
index 0000000..220a740
--- /dev/null
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='git rebase --whitespace=fix
+
+This test runs git rebase --whitespace=fix and make sure that it works.
+'
+
+. ./test-lib.sh
+
+# prepare initial revision of "file" with a blank line at the end
+cat >file <<EOF
+a
+b
+c
+
+EOF
+
+# expected contents in "file" after rebase
+cat >expect-first <<EOF
+a
+b
+c
+EOF
+
+# prepare second revision of "file"
+cat >second <<EOF
+a
+b
+c
+
+d
+e
+f
+
+
+
+
+EOF
+
+# expected contents in second revision after rebase
+cat >expect-second <<EOF
+a
+b
+c
+
+d
+e
+f
+EOF
+
+test_expect_success 'blank line at end of file; extend at end of file' '
+       git commit --allow-empty -m "Initial empty commit" &&
+       git add file && git commit -m first &&
+       mv second file &&
+       git add file && git commit -m second &&
+       git rebase --whitespace=fix HEAD^^ &&
+       git diff --exit-code HEAD^:file expect-first &&
+       test_cmp file expect-second
+'
+
+# prepare third revision of "file"
+sed -e's/Z//' >third <<EOF
+a
+b
+c
+
+d
+e
+f
+    Z
+ Z
+h
+i
+j
+k
+l
+EOF
+
+sed -e's/ //g' <third >expect-third
+
+test_expect_success 'two blanks line at end of file; extend at end of file' '
+       cp third file && git add file && git commit -m third &&
+       git rebase --whitespace=fix HEAD^^ &&
+       git diff --exit-code HEAD^:file expect-second &&
+       test_cmp file expect-third
+'
+
+test_expect_success 'same, but do not remove trailing spaces' '
+       git config core.whitespace "-blank-at-eol" &&
+       git reset --hard HEAD^ &&
+       cp third file && git add file && git commit -m third &&
+       git rebase --whitespace=fix HEAD^^
+       git diff --exit-code HEAD^:file expect-second &&
+       test_cmp file third
+'
+
+sed -e's/Z//' >beginning <<EOF
+a
+                   Z
+       Z
+EOF
+
+cat >expect-beginning <<EOF
+a
+
+
+1
+2
+3
+4
+5
+EOF
+
+test_expect_success 'at beginning of file' '
+       git config core.whitespace "blank-at-eol" &&
+       cp beginning file &&
+       git commit -m beginning file &&
+       for i in 1 2 3 4 5; do
+               echo $i
+       done >> file &&
+       git commit -m more file &&
+       git rebase --whitespace=fix HEAD^^ &&
+       test_cmp file expect-beginning
+'
+
+test_done
index 76b1bb45456a18a8c1c33256695396cc2b65a3a9..0aaf0ad84b05e1ee17e9789811ea06f2d2798556 100755 (executable)
@@ -271,4 +271,12 @@ test_expect_success 'choking "git rm" should not let it die with cruft' '
        test "$status" != 0
 '
 
+test_expect_success 'rm removes subdirectories recursively' '
+       mkdir -p dir/subdir/subsubdir &&
+       echo content >dir/subdir/subsubdir/file &&
+       git add dir/subdir/subsubdir/file &&
+       git rm -f dir/subdir/subsubdir/file &&
+       ! test -d dir
+'
+
 test_done
index 85eb0fbf96a65ad958422da02ca4975fe687da95..525c9a8fdfb4953612e6946994c8e555f676bfd2 100755 (executable)
@@ -255,4 +255,9 @@ test_expect_success 'git add to resolve conflicts on otherwise ignored path' '
        git add track-this
 '
 
+test_expect_success '"add non-existent" should fail' '
+       test_must_fail git add non-existent &&
+       ! (git ls-files | grep "non-existent")
+'
+
 test_done
index 6fb027ba57eeb328ac48ec78ff5f685fd94a6f4b..8eb47942e2d7f9624058b3347f011db591a13434 100755 (executable)
@@ -22,10 +22,12 @@ check_verify_failure () {
 ###########################################################
 # first create a commit, so we have a valid object/type
 # for the tag.
-echo Hello >A
-git update-index --add A
-git commit -m "Initial commit"
-head=$(git rev-parse --verify HEAD)
+test_expect_success 'setup' '
+       echo Hello >A &&
+       git update-index --add A &&
+       git commit -m "Initial commit" &&
+       head=$(git rev-parse --verify HEAD)
+'
 
 ############################################################
 #  1. length check
index 5514f74b30aa74fe2bf214e90f9ad8f4da2876e4..476e5ec038f3c9fd2cad4607c3819f54dabd04ad 100755 (executable)
@@ -194,6 +194,15 @@ test_expect_success 'pop -q is quiet' '
        test ! -s output.out
 '
 
+test_expect_success 'pop -q --index works and is quiet' '
+       echo foo > file &&
+       git add file &&
+       git stash save --quiet &&
+       git stash pop -q --index > output.out 2>&1 &&
+       test foo = "$(git show :file)" &&
+       test ! -s output.out
+'
+
 test_expect_success 'drop -q is quiet' '
        git stash &&
        git stash drop -q > output.out 2>&1 &&
index 8e3694ed5b80a87602d6533f0aa28307bd7b3d1b..dae635851666a7f5ccf685b89d64f31d5dbdd2fd 100755 (executable)
@@ -204,6 +204,9 @@ log --root --patch-with-stat --summary master
 log --root -c --patch-with-stat --summary master
 # improved by Timo's patch
 log --root --cc --patch-with-stat --summary master
+log -p --first-parent master
+log -m -p --first-parent master
+log -m -p master
 log -SF master
 log -SF -p master
 log --decorate --all
@@ -235,6 +238,9 @@ show initial
 show --root initial
 show side
 show master
+show -c master
+show -m master
+show --first-parent master
 show --stat side
 show --stat --summary side
 show --patch-with-stat side
diff --git a/t/t4013/diff.log_-m_-p_--first-parent_master b/t/t4013/diff.log_-m_-p_--first-parent_master
new file mode 100644 (file)
index 0000000..7a0073f
--- /dev/null
@@ -0,0 +1,100 @@
+$ git log -m -p --first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_-m_-p_master b/t/t4013/diff.log_-m_-p_master
new file mode 100644 (file)
index 0000000..9ca62a0
--- /dev/null
@@ -0,0 +1,200 @@
+$ git log -m -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index 7289e35..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,4 +1,8 @@
+ A
+ B
++C
++D
++E
++F
+ 1
+ 2
+diff --git a/file0 b/file0
+index f4615da..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -1,6 +1,9 @@
+ 1
+ 2
+ 3
++4
++5
++6
+ A
+ B
+ C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+diff --git a/file3 b/file3
+deleted file mode 100644
+index 7289e35..0000000
+--- a/file3
++++ /dev/null
+@@ -1,4 +0,0 @@
+-A
+-B
+-1
+-2
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_-p_--first-parent_master b/t/t4013/diff.log_-p_--first-parent_master
new file mode 100644 (file)
index 0000000..3fc896d
--- /dev/null
@@ -0,0 +1,78 @@
+$ git log -p --first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.show_--first-parent_master b/t/t4013/diff.show_--first-parent_master
new file mode 100644 (file)
index 0000000..3dcbe47
--- /dev/null
@@ -0,0 +1,30 @@
+$ git show --first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+$
diff --git a/t/t4013/diff.show_-c_master b/t/t4013/diff.show_-c_master
new file mode 100644 (file)
index 0000000..81aba8d
--- /dev/null
@@ -0,0 +1,36 @@
+$ git show -c master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --combined dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+  A
+  B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --combined file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+  1
+  2
+  3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.show_-m_master b/t/t4013/diff.show_-m_master
new file mode 100644 (file)
index 0000000..4ea2ee4
--- /dev/null
@@ -0,0 +1,93 @@
+$ git show -m master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index 7289e35..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,4 +1,8 @@
+ A
+ B
++C
++D
++E
++F
+ 1
+ 2
+diff --git a/file0 b/file0
+index f4615da..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -1,6 +1,9 @@
+ 1
+ 2
+ 3
++4
++5
++6
+ A
+ B
+ C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+diff --git a/file3 b/file3
+deleted file mode 100644
+index 7289e35..0000000
+--- a/file3
++++ /dev/null
+@@ -1,4 +0,0 @@
+-A
+-B
+-1
+-2
+$
index f2a2aaa2b9c7fd84634bb74febb3f0f5ac1793e1..c7b625642dd57f4bcb6f7c5aa240cc9c32e15af8 100755 (executable)
@@ -143,6 +143,58 @@ test_expect_success 'configuration headers and command line headers' '
        grep "^ *S. E. Cipient <scipient@example.com>\$" patch7
 '
 
+test_expect_success 'command line To: header' '
+
+       git config --unset-all format.headers &&
+       git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
+       grep "^To: R. E. Cipient <rcipient@example.com>\$" patch8
+'
+
+test_expect_success 'configuration To: header' '
+
+       git config format.to "R. E. Cipient <rcipient@example.com>" &&
+       git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
+       grep "^To: R. E. Cipient <rcipient@example.com>\$" patch9
+'
+
+test_expect_success '--no-to overrides config.to' '
+
+       git config --replace-all format.to \
+               "R. E. Cipient <rcipient@example.com>" &&
+       git format-patch --no-to --stdout master..side |
+       sed -e "/^\$/q" >patch10 &&
+       ! grep "^To: R. E. Cipient <rcipient@example.com>\$" patch10
+'
+
+test_expect_success '--no-to and --to replaces config.to' '
+
+       git config --replace-all format.to \
+               "Someone <someone@out.there>" &&
+       git format-patch --no-to --to="Someone Else <else@out.there>" \
+               --stdout master..side |
+       sed -e "/^\$/q" >patch11 &&
+       ! grep "^To: Someone <someone@out.there>\$" patch11 &&
+       grep "^To: Someone Else <else@out.there>\$" patch11
+'
+
+test_expect_success '--no-cc overrides config.cc' '
+
+       git config --replace-all format.cc \
+               "C. E. Cipient <rcipient@example.com>" &&
+       git format-patch --no-cc --stdout master..side |
+       sed -e "/^\$/q" >patch12 &&
+       ! grep "^Cc: C. E. Cipient <rcipient@example.com>\$" patch12
+'
+
+test_expect_success '--no-add-headers overrides config.headers' '
+
+       git config --replace-all format.headers \
+               "Header1: B. E. Cipient <rcipient@example.com>" &&
+       git format-patch --no-add-headers --stdout master..side |
+       sed -e "/^\$/q" >patch13 &&
+       ! grep "^Header1: B. E. Cipient <rcipient@example.com>\$" patch13
+'
+
 test_expect_success 'multiple files' '
 
        rm -rf patches/ &&
index 60dd2014d5ae5d5e9e168b8b60278d90ef93cc53..0391a5827ea8ba196b7796b7df818f8ac860c387 100755 (executable)
@@ -5,6 +5,9 @@ test_description='Return value of diffs'
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+       echo "1 " >a &&
+       git add . &&
+       git commit -m zeroth &&
        echo 1 >a &&
        git add . &&
        git commit -m first &&
@@ -13,6 +16,18 @@ test_expect_success 'setup' '
        git commit -a -m second
 '
 
+test_expect_success 'git diff --quiet -w  HEAD^^ HEAD^' '
+       git diff --quiet -w HEAD^^ HEAD^
+'
+
+test_expect_success 'git diff --quiet HEAD^^ HEAD^' '
+       test_must_fail git diff --quiet HEAD^^ HEAD^
+'
+
+test_expect_success 'git diff --quiet -w  HEAD^ HEAD' '
+       test_must_fail git diff --quiet -w HEAD^ HEAD
+'
+
 test_expect_success 'git diff-tree HEAD^ HEAD' '
        git diff-tree --exit-code HEAD^ HEAD
        test $? = 1
index 5ade44c043ca6577b2e331b152515359128dbd32..d5ccdd0cf8061e797e88185bfddb0864f73291dd 100755 (executable)
@@ -8,14 +8,13 @@ test_description='Test diff/status color escape codes'
 
 color()
 {
-       git config diff.color.new "$1" &&
-       test "`git config --get-color diff.color.new`" = "\e$2"
+       actual=$(git config --get-color no.such.slot "$1") &&
+       test "$actual" = "\e$2"
 }
 
 invalid_color()
 {
-       git config diff.color.new "$1" &&
-       test -z "`git config --get-color diff.color.new 2>/dev/null`"
+       test_must_fail git config --get-color no.such.slot "$1"
 }
 
 test_expect_success 'reset' '
@@ -42,6 +41,14 @@ test_expect_success 'fg bg attr' '
        color "blue red ul" "[4;34;41m"
 '
 
+test_expect_success 'fg bg attr...' '
+       color "blue bold dim ul blink reverse" "[1;2;4;5;7;34m"
+'
+
+test_expect_success 'long color specification' '
+       color "254 255 bold dim ul blink reverse" "[1;2;4;5;7;38;5;254;48;5;255m"
+'
+
 test_expect_success '256 colors' '
        color "254 bold 255" "[1;38;5;254;48;5;255m"
 '
index 464305405ac715411b9cc5faabf55d116f0c6ec7..11b19972ca89f20faaa35b8a2c9fcd7f97181f78 100755 (executable)
@@ -201,7 +201,7 @@ test_expect_success 'submodule contains untracked content' "
        echo new > sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
        diff actual - <<-EOF
-Submodule sm1 $head6..$head6-dirty:
+Submodule sm1 contains untracked content
 EOF
 "
 
@@ -209,7 +209,8 @@ test_expect_success 'submodule contains untracked and modifed content' "
        echo new > sm1/foo6 &&
        git diff-index -p --submodule=log HEAD >actual &&
        diff actual - <<-EOF
-Submodule sm1 $head6..$head6-dirty:
+Submodule sm1 contains untracked content
+Submodule sm1 contains modified content
 EOF
 "
 
@@ -217,7 +218,7 @@ test_expect_success 'submodule contains modifed content' "
        rm -f sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
        diff actual - <<-EOF
-Submodule sm1 $head6..$head6-dirty:
+Submodule sm1 contains modified content
 EOF
 "
 
@@ -235,7 +236,8 @@ test_expect_success 'modified submodule contains untracked content' "
        echo new > sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
        diff actual - <<-EOF
-Submodule sm1 $head6..$head8-dirty:
+Submodule sm1 contains untracked content
+Submodule sm1 $head6..$head8:
   > change
 EOF
 "
@@ -244,7 +246,9 @@ test_expect_success 'modified submodule contains untracked and modifed content'
        echo modification >> sm1/foo6 &&
        git diff-index -p --submodule=log HEAD >actual &&
        diff actual - <<-EOF
-Submodule sm1 $head6..$head8-dirty:
+Submodule sm1 contains untracked content
+Submodule sm1 contains modified content
+Submodule sm1 $head6..$head8:
   > change
 EOF
 "
@@ -253,7 +257,8 @@ test_expect_success 'modified submodule contains modifed content' "
        rm -f sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
        diff actual - <<-EOF
-Submodule sm1 $head6..$head8-dirty:
+Submodule sm1 contains modified content
+Submodule sm1 $head6..$head8:
   > change
 EOF
 "
index ad4cc1a7576d41131d291426d80c329ff838aa26..9692f16f3581f261c4c10a29f03751990adb7897 100755 (executable)
@@ -20,23 +20,25 @@ EOF
 cat file1 >file2
 cat file1 >file4
 
-git update-index --add --remove file1 file2 file4
-git commit -m 'Initial Version' 2>/dev/null
-
-git checkout -b binary
-perl -pe 'y/x/\000/' <file1 >file3
-cat file3 >file4
-git add file2
-perl -pe 'y/\000/v/' <file3 >file1
-rm -f file2
-git update-index --add --remove file1 file2 file3 file4
-git commit -m 'Second Version'
-
-git diff-tree -p master binary >B.diff
-git diff-tree -p -C master binary >C.diff
-
-git diff-tree -p --binary master binary >BF.diff
-git diff-tree -p --binary -C master binary >CF.diff
+test_expect_success 'setup' "
+       git update-index --add --remove file1 file2 file4 &&
+       git commit -m 'Initial Version' 2>/dev/null &&
+
+       git checkout -b binary &&
+       perl -pe 'y/x/\000/' <file1 >file3 &&
+       cat file3 >file4 &&
+       git add file2 &&
+       perl -pe 'y/\000/v/' <file3 >file1 &&
+       rm -f file2 &&
+       git update-index --add --remove file1 file2 file3 file4 &&
+       git commit -m 'Second Version' &&
+
+       git diff-tree -p master binary >B.diff &&
+       git diff-tree -p -C master binary >C.diff &&
+
+       git diff-tree -p --binary master binary >BF.diff &&
+       git diff-tree -p --binary -C master binary >CF.diff
+"
 
 test_expect_success 'stat binary diff -- should not fail.' \
        'git checkout master
index 0e3ce3611d9e83ab290ce034f2439961864ce30a..c617c2a33d8e8ac1dc7e049f9056ca6025fbf852 100755 (executable)
@@ -134,4 +134,13 @@ test_expect_success 'two lines' '
 
 '
 
+test_expect_success 'apply patch with 3 context lines matching at end' '
+       { echo a; echo b; echo c; echo d; } >file &&
+       git add file &&
+       echo e >>file &&
+       git diff >patch &&
+       >file &&
+       test_must_fail git apply patch
+'
+
 test_done
index ca26397590f3d79455c41894203fbff7bb6a9c3c..fb9ad247bf76c07a8b6dbbb0d6bf1ab830041770 100755 (executable)
@@ -261,4 +261,174 @@ test_expect_success 'blank but not empty at EOF' '
        grep "new blank line at EOF" error
 '
 
+test_expect_success 'applying beyond EOF requires one non-blank context line' '
+       { echo; echo; echo; echo; } >one &&
+       git add one &&
+       { echo b; } >>one &&
+       git diff -- one >patch &&
+
+       git checkout one &&
+       { echo a; echo; } >one &&
+       cp one expect &&
+       test_must_fail git apply --whitespace=fix patch &&
+       test_cmp one expect &&
+       test_must_fail git apply --ignore-space-change --whitespace=fix patch &&
+       test_cmp one expect
+'
+
+test_expect_success 'tons of blanks at EOF should not apply' '
+       for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
+               echo; echo; echo; echo;
+       done >one &&
+       git add one &&
+       echo a >>one &&
+       git diff -- one >patch &&
+
+       >one &&
+       test_must_fail git apply --whitespace=fix patch &&
+       test_must_fail git apply --ignore-space-change --whitespace=fix patch
+'
+
+test_expect_success 'missing blank line at end with --whitespace=fix' '
+       echo a >one &&
+       echo >>one &&
+       git add one &&
+       echo b >>one &&
+       cp one expect &&
+       git diff -- one >patch &&
+       echo a >one &&
+       cp one saved-one &&
+       test_must_fail git apply patch &&
+       git apply --whitespace=fix patch &&
+       test_cmp one expect &&
+       mv saved-one one &&
+       git apply --ignore-space-change --whitespace=fix patch &&
+       test_cmp one expect
+'
+
+test_expect_success 'two missing blank lines at end with --whitespace=fix' '
+       { echo a; echo; echo b; echo c; } >one &&
+       cp one no-blank-lines &&
+       { echo; echo; } >>one &&
+       git add one &&
+       echo d >>one &&
+       cp one expect &&
+       echo >>one &&
+       git diff -- one >patch &&
+       cp no-blank-lines one &&
+       test_must_fail git apply patch &&
+       git apply --whitespace=fix patch &&
+       test_cmp one expect &&
+       mv no-blank-lines one &&
+       test_must_fail git apply patch &&
+       git apply --ignore-space-change --whitespace=fix patch &&
+       test_cmp one expect
+'
+
+test_expect_success 'shrink file with tons of missing blanks at end of file' '
+       { echo a; echo b; echo c; } >one &&
+       cp one no-blank-lines &&
+       for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
+               echo; echo; echo; echo;
+       done >>one &&
+       git add one &&
+       echo a >one &&
+       cp one expect &&
+       git diff -- one >patch &&
+       cp no-blank-lines one &&
+       test_must_fail git apply patch &&
+       git apply --whitespace=fix patch &&
+       test_cmp one expect &&
+       mv no-blank-lines one &&
+       git apply --ignore-space-change --whitespace=fix patch &&
+       test_cmp one expect
+'
+
+test_expect_success 'missing blanks at EOF must only match blank lines' '
+       { echo a; echo b; } >one &&
+       git add one &&
+       { echo c; echo d; } >>one &&
+       git diff -- one >patch &&
+
+       echo a >one &&
+       test_must_fail git apply patch
+       test_must_fail git apply --whitespace=fix patch &&
+       test_must_fail git apply --ignore-space-change --whitespace=fix patch
+'
+
+sed -e's/Z//' >one <<EOF
+a
+b
+c
+                     Z
+EOF
+
+test_expect_success 'missing blank line should match context line with spaces' '
+       git add one &&
+       echo d >>one &&
+       git diff -- one >patch &&
+       { echo a; echo b; echo c; } >one &&
+       cp one expect &&
+       { echo; echo d; } >>expect &&
+       git add one &&
+
+       git apply --whitespace=fix patch &&
+       test_cmp one expect
+'
+
+sed -e's/Z//' >one <<EOF
+a
+b
+c
+                     Z
+EOF
+
+test_expect_success 'same, but with the --ignore-space-option' '
+       git add one &&
+       echo d >>one &&
+       cp one expect &&
+       git diff -- one >patch &&
+       { echo a; echo b; echo c; } >one &&
+       git add one &&
+
+       git checkout-index -f one &&
+       git apply --ignore-space-change --whitespace=fix patch &&
+       test_cmp one expect
+'
+
+test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
+       git config core.whitespace cr-at-eol &&
+       printf "a\r\n" >one &&
+       printf "b\r\n" >>one &&
+       printf "c\r\n" >>one &&
+       cp one save-one &&
+       printf "                 \r\n" >>one
+       git add one &&
+       printf "d\r\n" >>one &&
+       cp one expect &&
+       git diff -- one >patch &&
+       mv save-one one &&
+
+       git apply --ignore-space-change --whitespace=fix patch &&
+       test_cmp one expect
+'
+
+test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
+       git config --unset core.whitespace &&
+       printf "a\r\n" >one &&
+       printf "b\r\n" >>one &&
+       printf "c\r\n" >>one &&
+       cp one save-one &&
+       printf "                 \r\n" >>one
+       git add one &&
+       cp one expect &&
+       printf "d\r\n" >>one &&
+       git diff -- one >patch &&
+       mv save-one one &&
+       echo d >>expect &&
+
+       git apply --ignore-space-change --whitespace=fix patch &&
+       test_cmp one expect
+'
+
 test_done
index bb402c3780356d1feab4e8b7c9b9624495d3e176..70856d07ed113b731d149bde73fe7b4eb25a72f2 100755 (executable)
@@ -8,40 +8,42 @@ test_description='git rerere
 
 . ./test-lib.sh
 
-cat > a1 << EOF
-Some title
-==========
-Whether 'tis nobler in the mind to suffer
-The slings and arrows of outrageous fortune,
-Or to take arms against a sea of troubles,
-And by opposing end them? To die: to sleep;
-No more; and by a sleep to say we end
-The heart-ache and the thousand natural shocks
-That flesh is heir to, 'tis a consummation
-Devoutly to be wish'd.
-EOF
-
-git add a1
-git commit -q -a -m initial
-
-git checkout -b first
-cat >> a1 << EOF
-Some title
-==========
-To die, to sleep;
-To sleep: perchance to dream: ay, there's the rub;
-For in that sleep of death what dreams may come
-When we have shuffled off this mortal coil,
-Must give us pause: there's the respect
-That makes calamity of so long life;
-EOF
-git commit -q -a -m first
-
-git checkout -b second master
-git show first:a1 |
-sed -e 's/To die, t/To die! T/' -e 's/Some title/Some Title/' > a1
-echo "* END *" >>a1
-git commit -q -a -m second
+test_expect_success 'setup' "
+       cat > a1 <<- EOF &&
+       Some title
+       ==========
+       Whether 'tis nobler in the mind to suffer
+       The slings and arrows of outrageous fortune,
+       Or to take arms against a sea of troubles,
+       And by opposing end them? To die: to sleep;
+       No more; and by a sleep to say we end
+       The heart-ache and the thousand natural shocks
+       That flesh is heir to, 'tis a consummation
+       Devoutly to be wish'd.
+       EOF
+
+       git add a1 &&
+       git commit -q -a -m initial &&
+
+       git checkout -b first &&
+       cat >> a1 <<- EOF &&
+       Some title
+       ==========
+       To die, to sleep;
+       To sleep: perchance to dream: ay, there's the rub;
+       For in that sleep of death what dreams may come
+       When we have shuffled off this mortal coil,
+       Must give us pause: there's the respect
+       That makes calamity of so long life;
+       EOF
+       git commit -q -a -m first &&
+
+       git checkout -b second master &&
+       git show first:a1 |
+       sed -e 's/To die, t/To die! T/' -e 's/Some title/Some Title/' > a1 &&
+       echo '* END *' >>a1 &&
+       git commit -q -a -m second
+"
 
 test_expect_success 'nothing recorded without rerere' '
        (rm -rf .git/rr-cache; git config rerere.enabled false) &&
diff --git a/t/t4253-am-keep-cr-dos.sh b/t/t4253-am-keep-cr-dos.sh
new file mode 100755 (executable)
index 0000000..735e55d
--- /dev/null
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Stefan-W. Hahn
+#
+
+test_description='git-am mbox with dos line ending.
+
+'
+. ./test-lib.sh
+
+# Three patches which will be added as files with dos line ending.
+
+cat >file1 <<\EOF
+line 1
+EOF
+
+cat >file1a <<\EOF
+line 1
+line 4
+EOF
+
+cat >file2 <<\EOF
+line 1
+line 2
+EOF
+
+cat >file3 <<\EOF
+line 1
+line 2
+line 3
+EOF
+
+test_expect_success 'setup repository with dos files' '
+       append_cr <file1 >file &&
+       git add file &&
+       git commit -m Initial &&
+       git tag initial &&
+       append_cr <file2 >file &&
+       git commit -a -m Second &&
+       append_cr <file3 >file &&
+       git commit -a -m Third
+'
+
+test_expect_success 'am with dos files without --keep-cr' '
+       git checkout -b dosfiles initial &&
+       git format-patch -k initial..master &&
+       test_must_fail git am -k -3 000*.patch &&
+       git am --abort &&
+       rm -rf .git/rebase-apply 000*.patch
+'
+
+test_expect_success 'am with dos files with --keep-cr' '
+       git checkout -b dosfiles-keep-cr initial &&
+       git format-patch -k --stdout initial..master | git am --keep-cr -k -3 &&
+       git diff --exit-code master
+'
+
+test_expect_success 'am with dos files config am.keepcr' '
+       git config am.keepcr 1 &&
+       git checkout -b dosfiles-conf-keepcr initial &&
+       git format-patch -k --stdout initial..master | git am -k -3 &&
+       git diff --exit-code master
+'
+
+test_expect_success 'am with dos files config am.keepcr overriden by --no-keep-cr' '
+       git config am.keepcr 1 &&
+       git checkout -b dosfiles-conf-keepcr-override initial &&
+       git format-patch -k initial..master &&
+       test_must_fail git am -k -3 --no-keep-cr 000*.patch &&
+       git am --abort &&
+       rm -rf .git/rebase-apply 000*.patch
+'
+
+test_expect_success 'am with dos files with --keep-cr continue' '
+       git checkout -b dosfiles-keep-cr-continue initial &&
+       git format-patch -k initial..master &&
+       append_cr <file1a >file &&
+       git commit -m "different patch" file &&
+       test_must_fail git am --keep-cr -k -3 000*.patch &&
+       append_cr <file2 >file &&
+       git add file &&
+       git am -3 --resolved &&
+       git diff --exit-code master
+'
+
+test_expect_success 'am with unix files config am.keepcr overriden by --no-keep-cr' '
+       git config am.keepcr 1 &&
+       git checkout -b unixfiles-conf-keepcr-override initial &&
+       cp -f file1 file &&
+       git commit -m "line ending to unix" file &&
+       git format-patch -k initial..master &&
+       git am -k -3 --no-keep-cr 000*.patch &&
+       git diff --exit-code -w master
+'
+
+test_done
index 9577238685acecd1b5d530ffa3f4f03eef25b9b6..4abb3d5c6c4884806cdaabcc0b01acb9c1263af2 100644 (file)
@@ -1,2 +1,2 @@
-- a list
+  - a list
   - of stuff
index 3c6687abecf6b74839315c5e3dc09f361154a68c..e2ed13dba2705b15d6a1f623589acce134749fab 100755 (executable)
@@ -148,6 +148,38 @@ test_expect_success 'gc --prune=<date>' '
 
 '
 
+test_expect_success 'gc --prune=never' '
+
+       add_blob &&
+       git gc --prune=never &&
+       test -f $BLOB_FILE &&
+       git gc --prune=now &&
+       test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc respects gc.pruneExpire=never' '
+
+       git config gc.pruneExpire never &&
+       add_blob &&
+       git gc &&
+       test -f $BLOB_FILE &&
+       git config gc.pruneExpire now &&
+       git gc &&
+       test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'prune --expire=never' '
+
+       add_blob &&
+       git prune --expire=never &&
+       test -f $BLOB_FILE &&
+       git prune &&
+       test ! -f $BLOB_FILE
+
+'
+
 test_expect_success 'gc: prune old objects after local clone' '
        add_blob &&
        test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
index 325714e5299a5b59157c3741e7fa0d98d70b9990..17bcb0b04096eabb29c13a025fd2afe8d1e4623b 100755 (executable)
@@ -17,23 +17,22 @@ test_expect_success setup '
        commit1=$(echo modify | git commit-tree $tree1 -p $commit0) &&
        git update-ref refs/heads/master $commit0 &&
        git update-ref refs/heads/tofail $commit1 &&
-       git clone ./. victim &&
-       GIT_DIR=victim/.git git config receive.denyCurrentBranch warn &&
-       GIT_DIR=victim/.git git update-ref refs/heads/tofail $commit1 &&
+       git clone --bare ./. victim.git &&
+       GIT_DIR=victim.git git update-ref refs/heads/tofail $commit1 &&
        git update-ref refs/heads/master $commit1 &&
        git update-ref refs/heads/tofail $commit0
 '
 
-cat >victim/.git/hooks/pre-receive <<'EOF'
+cat >victim.git/hooks/pre-receive <<'EOF'
 #!/bin/sh
 printf %s "$@" >>$GIT_DIR/pre-receive.args
 cat - >$GIT_DIR/pre-receive.stdin
 echo STDOUT pre-receive
 echo STDERR pre-receive >&2
 EOF
-chmod u+x victim/.git/hooks/pre-receive
+chmod u+x victim.git/hooks/pre-receive
 
-cat >victim/.git/hooks/update <<'EOF'
+cat >victim.git/hooks/update <<'EOF'
 #!/bin/sh
 echo "$@" >>$GIT_DIR/update.args
 read x; printf %s "$x" >$GIT_DIR/update.stdin
@@ -41,77 +40,77 @@ echo STDOUT update $1
 echo STDERR update $1 >&2
 test "$1" = refs/heads/master || exit
 EOF
-chmod u+x victim/.git/hooks/update
+chmod u+x victim.git/hooks/update
 
-cat >victim/.git/hooks/post-receive <<'EOF'
+cat >victim.git/hooks/post-receive <<'EOF'
 #!/bin/sh
 printf %s "$@" >>$GIT_DIR/post-receive.args
 cat - >$GIT_DIR/post-receive.stdin
 echo STDOUT post-receive
 echo STDERR post-receive >&2
 EOF
-chmod u+x victim/.git/hooks/post-receive
+chmod u+x victim.git/hooks/post-receive
 
-cat >victim/.git/hooks/post-update <<'EOF'
+cat >victim.git/hooks/post-update <<'EOF'
 #!/bin/sh
 echo "$@" >>$GIT_DIR/post-update.args
 read x; printf %s "$x" >$GIT_DIR/post-update.stdin
 echo STDOUT post-update
 echo STDERR post-update >&2
 EOF
-chmod u+x victim/.git/hooks/post-update
+chmod u+x victim.git/hooks/post-update
 
 test_expect_success push '
-       test_must_fail git send-pack --force ./victim/.git \
+       test_must_fail git send-pack --force ./victim.git \
                master tofail >send.out 2>send.err
 '
 
 test_expect_success 'updated as expected' '
-       test $(GIT_DIR=victim/.git git rev-parse master) = $commit1 &&
-       test $(GIT_DIR=victim/.git git rev-parse tofail) = $commit1
+       test $(GIT_DIR=victim.git git rev-parse master) = $commit1 &&
+       test $(GIT_DIR=victim.git git rev-parse tofail) = $commit1
 '
 
 test_expect_success 'hooks ran' '
-       test -f victim/.git/pre-receive.args &&
-       test -f victim/.git/pre-receive.stdin &&
-       test -f victim/.git/update.args &&
-       test -f victim/.git/update.stdin &&
-       test -f victim/.git/post-receive.args &&
-       test -f victim/.git/post-receive.stdin &&
-       test -f victim/.git/post-update.args &&
-       test -f victim/.git/post-update.stdin
+       test -f victim.git/pre-receive.args &&
+       test -f victim.git/pre-receive.stdin &&
+       test -f victim.git/update.args &&
+       test -f victim.git/update.stdin &&
+       test -f victim.git/post-receive.args &&
+       test -f victim.git/post-receive.stdin &&
+       test -f victim.git/post-update.args &&
+       test -f victim.git/post-update.stdin
 '
 
 test_expect_success 'pre-receive hook input' '
        (echo $commit0 $commit1 refs/heads/master;
         echo $commit1 $commit0 refs/heads/tofail
-       ) | test_cmp - victim/.git/pre-receive.stdin
+       ) | test_cmp - victim.git/pre-receive.stdin
 '
 
 test_expect_success 'update hook arguments' '
        (echo refs/heads/master $commit0 $commit1;
         echo refs/heads/tofail $commit1 $commit0
-       ) | test_cmp - victim/.git/update.args
+       ) | test_cmp - victim.git/update.args
 '
 
 test_expect_success 'post-receive hook input' '
        echo $commit0 $commit1 refs/heads/master |
-       test_cmp - victim/.git/post-receive.stdin
+       test_cmp - victim.git/post-receive.stdin
 '
 
 test_expect_success 'post-update hook arguments' '
        echo refs/heads/master |
-       test_cmp - victim/.git/post-update.args
+       test_cmp - victim.git/post-update.args
 '
 
 test_expect_success 'all hook stdin is /dev/null' '
-       ! test -s victim/.git/update.stdin &&
-       ! test -s victim/.git/post-update.stdin
+       ! test -s victim.git/update.stdin &&
+       ! test -s victim.git/post-update.stdin
 '
 
 test_expect_success 'all *-receive hook args are empty' '
-       ! test -s victim/.git/pre-receive.args &&
-       ! test -s victim/.git/post-receive.args
+       ! test -s victim.git/pre-receive.args &&
+       ! test -s victim.git/post-receive.args
 '
 
 test_expect_success 'send-pack produced no output' '
@@ -119,20 +118,21 @@ test_expect_success 'send-pack produced no output' '
 '
 
 cat <<EOF >expect
-STDOUT pre-receive
-STDERR pre-receive
-STDOUT update refs/heads/master
-STDERR update refs/heads/master
-STDOUT update refs/heads/tofail
-STDERR update refs/heads/tofail
-STDOUT post-receive
-STDERR post-receive
-STDOUT post-update
-STDERR post-update
+remote: STDOUT pre-receive
+remote: STDERR pre-receive
+remote: STDOUT update refs/heads/master
+remote: STDERR update refs/heads/master
+remote: STDOUT update refs/heads/tofail
+remote: STDERR update refs/heads/tofail
+remote: error: hook declined to update refs/heads/tofail
+remote: STDOUT post-receive
+remote: STDERR post-receive
+remote: STDOUT post-update
+remote: STDERR post-update
 EOF
 test_expect_success 'send-pack stderr contains hook messages' '
-       grep ^STD send.err >actual &&
-       test_cmp - actual <expect
+       grep ^remote: send.err | sed "s/ *\$//" >actual &&
+       test_cmp expect actual
 '
 
 test_done
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
new file mode 100755 (executable)
index 0000000..f0f91f1
--- /dev/null
@@ -0,0 +1,183 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Thomas Rast
+#
+
+test_description='Test the post-rewrite hook.'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit A foo A &&
+       test_commit B foo B &&
+       test_commit C foo C &&
+       test_commit D foo D
+'
+
+mkdir .git/hooks
+
+cat >.git/hooks/post-rewrite <<EOF
+#!/bin/sh
+echo \$@ > "$TRASH_DIRECTORY"/post-rewrite.args
+cat > "$TRASH_DIRECTORY"/post-rewrite.data
+EOF
+chmod u+x .git/hooks/post-rewrite
+
+clear_hook_input () {
+       rm -f post-rewrite.args post-rewrite.data
+}
+
+verify_hook_input () {
+       test_cmp "$TRASH_DIRECTORY"/post-rewrite.args expected.args &&
+       test_cmp "$TRASH_DIRECTORY"/post-rewrite.data expected.data
+}
+
+test_expect_success 'git commit --amend' '
+       clear_hook_input &&
+       echo "D new message" > newmsg &&
+       oldsha=$(git rev-parse HEAD^0) &&
+       git commit -Fnewmsg --amend &&
+       echo amend > expected.args &&
+       echo $oldsha $(git rev-parse HEAD^0) > expected.data &&
+       verify_hook_input
+'
+
+test_expect_success 'git commit --amend --no-post-rewrite' '
+       clear_hook_input &&
+       echo "D new message again" > newmsg &&
+       git commit --no-post-rewrite -Fnewmsg --amend &&
+       test ! -f post-rewrite.args &&
+       test ! -f post-rewrite.data
+'
+
+test_expect_success 'git rebase' '
+       git reset --hard D &&
+       clear_hook_input &&
+       test_must_fail git rebase --onto A B &&
+       echo C > foo &&
+       git add foo &&
+       git rebase --continue &&
+       echo rebase >expected.args &&
+       cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+       verify_hook_input
+'
+
+test_expect_success 'git rebase --skip' '
+       git reset --hard D &&
+       clear_hook_input &&
+       test_must_fail git rebase --onto A B &&
+       test_must_fail git rebase --skip &&
+       echo D > foo &&
+       git add foo &&
+       git rebase --continue &&
+       echo rebase >expected.args &&
+       cat >expected.data <<EOF &&
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+       verify_hook_input
+'
+
+test_expect_success 'git rebase -m' '
+       git reset --hard D &&
+       clear_hook_input &&
+       test_must_fail git rebase -m --onto A B &&
+       echo C > foo &&
+       git add foo &&
+       git rebase --continue &&
+       echo rebase >expected.args &&
+       cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+       verify_hook_input
+'
+
+test_expect_success 'git rebase -m --skip' '
+       git reset --hard D &&
+       clear_hook_input &&
+       test_must_fail git rebase --onto A B &&
+       test_must_fail git rebase --skip &&
+       echo D > foo &&
+       git add foo &&
+       git rebase --continue &&
+       echo rebase >expected.args &&
+       cat >expected.data <<EOF &&
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+       verify_hook_input
+'
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+# Helper to work around the lack of one-shot exporting for
+# test_must_fail (as it is a shell function)
+test_fail_interactive_rebase () {
+       (
+               FAKE_LINES="$1" &&
+               shift &&
+               export FAKE_LINES &&
+               test_must_fail git rebase -i "$@"
+       )
+}
+
+test_expect_success 'git rebase -i (unchanged)' '
+       git reset --hard D &&
+       clear_hook_input &&
+       test_fail_interactive_rebase "1 2" --onto A B &&
+       echo C > foo &&
+       git add foo &&
+       git rebase --continue &&
+       echo rebase >expected.args &&
+       cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+       verify_hook_input
+'
+
+test_expect_success 'git rebase -i (skip)' '
+       git reset --hard D &&
+       clear_hook_input &&
+       test_fail_interactive_rebase "2" --onto A B &&
+       echo D > foo &&
+       git add foo &&
+       git rebase --continue &&
+       echo rebase >expected.args &&
+       cat >expected.data <<EOF &&
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+       verify_hook_input
+'
+
+test_expect_success 'git rebase -i (squash)' '
+       git reset --hard D &&
+       clear_hook_input &&
+       test_fail_interactive_rebase "1 squash 2" --onto A B &&
+       echo C > foo &&
+       git add foo &&
+       git rebase --continue &&
+       echo rebase >expected.args &&
+       cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+       verify_hook_input
+'
+
+test_expect_success 'git rebase -i (fixup without conflict)' '
+       git reset --hard D &&
+       clear_hook_input &&
+       FAKE_LINES="1 fixup 2" git rebase -i B &&
+       echo rebase >expected.args &&
+       cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+       verify_hook_input
+'
+
+test_done
index a82c5ffa1c608f45786fe37531ffe93008a3570b..26920502093a98d4b5721f783e006de9d999792a 100755 (executable)
@@ -507,15 +507,15 @@ test_expect_success 'remote prune to cause a dangling symref' '
        (
                cd seven &&
                git remote prune origin
-       ) 2>err &&
+       ) >err 2>&1 &&
        grep "has become dangling" err &&
 
-       : And the dangling symref will not cause other annoying errors
+       : And the dangling symref will not cause other annoying errors &&
        (
                cd seven &&
                git branch -a
        ) 2>err &&
-       ! grep "points nowhere" err
+       ! grep "points nowhere" err &&
        (
                cd seven &&
                test_must_fail git branch nomore origin
index 169af1edde557f054ea76b8de681c6dd74e436f2..721821ec92e476ed9a16222bc49b72f2fd9b2c68 100755 (executable)
@@ -341,6 +341,13 @@ test_expect_success 'fetch into the current branch with --update-head-ok' '
 
 '
 
+test_expect_success 'fetch --dry-run' '
+
+       rm -f .git/FETCH_HEAD &&
+       git fetch --dry-run . &&
+       ! test -f .git/FETCH_HEAD
+'
+
 test_expect_success "should be able to fetch with duplicate refspecs" '
         mkdir dups &&
         cd dups &&
index 0f04b2e8949dfec46fbc42af8347c9f3e6d302a7..2de98e6561607b87bceef66c20ad9055d04878c3 100755 (executable)
@@ -660,4 +660,54 @@ test_expect_success 'push with branches containing #' '
        git checkout master
 '
 
+test_expect_success 'push --porcelain' '
+       mk_empty &&
+       echo >.git/foo  "To testrepo" &&
+       echo >>.git/foo "*      refs/heads/master:refs/remotes/origin/master    [new branch]"  &&
+       echo >>.git/foo "Done" &&
+       git push >.git/bar --porcelain  testrepo refs/heads/master:refs/remotes/origin/master &&
+       (
+               cd testrepo &&
+               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+               test "z$r" = "z$the_commit" &&
+               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+       ) &&
+       test_cmp .git/foo .git/bar
+'
+
+test_expect_success 'push --porcelain bad url' '
+       mk_empty &&
+       test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/master:refs/remotes/origin/master &&
+       test_must_fail grep -q Done .git/bar
+'
+
+test_expect_success 'push --porcelain rejected' '
+       mk_empty &&
+       git push testrepo refs/heads/master:refs/remotes/origin/master &&
+       (cd testrepo &&
+               git reset --hard origin/master^
+               git config receive.denyCurrentBranch true) &&
+
+       echo >.git/foo  "To testrepo"  &&
+       echo >>.git/foo "!      refs/heads/master:refs/heads/master     [remote rejected] (branch is currently checked out)" &&
+
+       test_must_fail git push >.git/bar --porcelain  testrepo refs/heads/master:refs/heads/master &&
+       test_cmp .git/foo .git/bar
+'
+
+test_expect_success 'push --porcelain --dry-run rejected' '
+       mk_empty &&
+       git push testrepo refs/heads/master:refs/remotes/origin/master &&
+       (cd testrepo &&
+               git reset --hard origin/master
+               git config receive.denyCurrentBranch true) &&
+
+       echo >.git/foo  "To testrepo"  &&
+       echo >>.git/foo "!      refs/heads/master^:refs/heads/master    [rejected] (non-fast-forward)" &&
+       echo >>.git/foo "Done" &&
+
+       test_must_fail git push >.git/bar --porcelain  --dry-run testrepo refs/heads/master^:refs/heads/master &&
+       test_cmp .git/foo .git/bar
+'
+
 test_done
index 83e2e8ab800a2f664085b7350272727748512e42..1b06691bb41586b3bc564841b720ce710f927a1c 100755 (executable)
@@ -4,8 +4,6 @@ test_description='pull options'
 
 . ./test-lib.sh
 
-D=`pwd`
-
 test_expect_success 'setup' '
        mkdir parent &&
        (cd parent && git init &&
@@ -13,48 +11,83 @@ test_expect_success 'setup' '
         git commit -m one)
 '
 
-cd "$D"
-
 test_expect_success 'git pull -q' '
        mkdir clonedq &&
-       cd clonedq &&
-       git pull -q "$D/parent" >out 2>err &&
-       test ! -s out
+       (cd clonedq && git init &&
+       git pull -q "../parent" >out 2>err &&
+       test ! -s err &&
+       test ! -s out)
 '
 
-cd "$D"
-
 test_expect_success 'git pull' '
        mkdir cloned &&
-       cd cloned &&
-       git pull "$D/parent" >out 2>err &&
-       test -s out
+       (cd cloned && git init &&
+       git pull "../parent" >out 2>err &&
+       test -s err &&
+       test ! -s out)
 '
-cd "$D"
 
 test_expect_success 'git pull -v' '
        mkdir clonedv &&
-       cd clonedv &&
-       git pull -v "$D/parent" >out 2>err &&
-       test -s out
+       (cd clonedv && git init &&
+       git pull -v "../parent" >out 2>err &&
+       test -s err &&
+       test ! -s out)
 '
 
-cd "$D"
-
 test_expect_success 'git pull -v -q' '
        mkdir clonedvq &&
-       cd clonedvq &&
-       git pull -v -q "$D/parent" >out 2>err &&
-       test ! -s out
+       (cd clonedvq && git init &&
+       git pull -v -q "../parent" >out 2>err &&
+       test ! -s out &&
+       test ! -s err)
 '
 
-cd "$D"
-
 test_expect_success 'git pull -q -v' '
        mkdir clonedqv &&
-       cd clonedqv &&
-       git pull -q -v "$D/parent" >out 2>err &&
-       test -s out
+       (cd clonedqv && git init &&
+       git pull -q -v "../parent" >out 2>err &&
+       test ! -s out &&
+       test -s err)
+'
+
+test_expect_success 'git pull --force' '
+       mkdir clonedoldstyle &&
+       (cd clonedoldstyle && git init &&
+       cat >>.git/config <<-\EOF &&
+       [remote "one"]
+               url = ../parent
+               fetch = refs/heads/master:refs/heads/mirror
+       [remote "two"]
+               url = ../parent
+               fetch = refs/heads/master:refs/heads/origin
+       [branch "master"]
+               remote = two
+               merge = refs/heads/master
+       EOF
+       git pull two &&
+       test_commit A &&
+       git branch -f origin &&
+       git pull --all --force
+       )
+'
+
+test_expect_success 'git pull --all' '
+       mkdir clonedmulti &&
+       (cd clonedmulti && git init &&
+       cat >>.git/config <<-\EOF &&
+       [remote "one"]
+               url = ../parent
+               fetch = refs/heads/*:refs/remotes/one/*
+       [remote "two"]
+               url = ../parent
+               fetch = refs/heads/*:refs/remotes/two/*
+       [branch "master"]
+               remote = one
+               merge = refs/heads/master
+       EOF
+       git pull --all
+       )
 '
 
 test_done
index bb18f8bfc4c9cd7e602633ce4abf5a3cf9ae0e4a..37fe87541127887742530a8f8859f1dd369d3f34 100755 (executable)
@@ -137,6 +137,9 @@ test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
 
 '
 
+test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+       "$ROOT_PATH"/test_repo_clone master
+
 stop_httpd
 
 test_done
index 53f54a2789557bcfd4e9843b50fe28189fa6f356..795dc2bcdf98e582dd2f05d901b791ab4225ce3b 100755 (executable)
@@ -88,26 +88,8 @@ test_expect_success 'used receive-pack service' '
        test_cmp exp act
 '
 
-test_expect_success 'non-fast-forward push fails' '
-       cd "$ROOT_PATH"/test_repo_clone &&
-       git checkout master &&
-       echo "changed" > path2 &&
-       git commit -a -m path2 --amend &&
-
-       HEAD=$(git rev-parse --verify HEAD) &&
-       !(git push -v origin >output 2>&1) &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
-        test $HEAD != $(git rev-parse --verify HEAD))
-'
-
-test_expect_success 'non-fast-forward push show ref status' '
-       grep "^ ! \[rejected\][ ]*master -> master (non-fast-forward)$" output
-'
-
-test_expect_success 'non-fast-forward push shows help message' '
-       grep "To prevent you from losing history, non-fast-forward updates were rejected" \
-               output
-'
+test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+       "$ROOT_PATH"/test_repo_clone master
 
 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
        # create a dissimilarly-named remote ref so that git is unable to match the
old mode 100755 (executable)
new mode 100644 (file)
index f55627b..985d517
@@ -1,3 +1,5 @@
+: included from 6002 and others
+
 [ -d .git/refs/tags ] || mkdir -p .git/refs/tags
 
 :> sed.script
index 6291307cd03e4e374e640dc82ddc8f26dbb8ff1d..5034dd1352c275beca4d5dae805cad6573a89aca 100755 (executable)
@@ -64,6 +64,10 @@ cp new1.txt test.txt
 test_expect_success "merge without conflict" \
        "git merge-file test.txt orig.txt new2.txt"
 
+cp new1.txt test.txt
+test_expect_success "merge without conflict (--quiet)" \
+       "git merge-file --quiet test.txt orig.txt new2.txt"
+
 cp new1.txt test2.txt
 test_expect_success "merge without conflict (missing LF at EOF)" \
        "git merge-file test2.txt orig.txt new2.txt"
@@ -211,4 +215,41 @@ test_expect_success '"diff3 -m" style output (2)' '
        test_cmp expect actual
 '
 
+cat >expect <<\EOF
+Dominus regit me,
+<<<<<<<<<< new8.txt
+et nihil mihi deerit;
+
+
+
+
+In loco pascuae ibi me collocavit;
+super aquam refectionis educavit me.
+||||||||||
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+==========
+et nihil mihi deerit,
+
+
+
+
+In loco pascuae ibi me collocavit --
+super aquam refectionis educavit me,
+>>>>>>>>>> new9.txt
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam TU mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+
+test_expect_success 'marker size' '
+       test_must_fail git merge-file -p --marker-size=10 \
+               new8.txt new5.txt new9.txt >actual &&
+       test_cmp expect actual
+'
+
 test_done
index c51865fdbc0a6fd98cca4a4accd35b302e5fd739..3b042aacd63f77651fdaf3d10b65f4fc85669a75 100755 (executable)
@@ -567,6 +567,11 @@ test_expect_success 'skipping away from skipped commit' '
        test "$para3" = "$PARA_HASH3"
 '
 
+test_expect_success 'erroring out when using bad path parameters' '
+       test_must_fail git bisect start $PARA_HASH7 $HASH1 -- foobar 2> error.txt &&
+       grep "bad path parameters" error.txt
+'
+
 #
 #
 test_done
index 7144f815c07a46e4f39a0f739fb964467fd27a16..e249c3ed4176b4934ae57541ab8b671801968eae 100755 (executable)
@@ -353,7 +353,7 @@ test_expect_success 'log grep (4)' '
 '
 
 test_expect_success 'log grep (5)' '
-       git log --author=Thor -F --grep=Thu --pretty=tformat:%s >actual &&
+       git log --author=Thor -F --pretty=tformat:%s >actual &&
        ( echo third ; echo initial ) >expect &&
        test_cmp expect actual
 '
@@ -364,6 +364,14 @@ test_expect_success 'log grep (6)' '
        test_cmp expect actual
 '
 
+test_expect_success 'log --grep --author implicitly uses all-match' '
+       # grep matches initial and second but not third
+       # author matches only initial and third
+       git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
+       echo initial >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'grep with CE_VALID file' '
        git update-index --assume-unchanged t/t &&
        rm t/t &&
@@ -434,4 +442,89 @@ test_expect_success 'grep -Fi' '
        test_cmp expected actual
 '
 
+test_expect_success 'outside of git repository' '
+       rm -fr non &&
+       mkdir -p non/git/sub &&
+       echo hello >non/git/file1 &&
+       echo world >non/git/sub/file2 &&
+       echo ".*o*" >non/git/.gitignore &&
+       {
+               echo file1:hello &&
+               echo sub/file2:world
+       } >non/expect.full &&
+       echo file2:world >non/expect.sub
+       (
+               GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd non/git &&
+               test_must_fail git grep o &&
+               git grep --no-index o >../actual.full &&
+               test_cmp ../expect.full ../actual.full
+               cd sub &&
+               test_must_fail git grep o &&
+               git grep --no-index o >../../actual.sub &&
+               test_cmp ../../expect.sub ../../actual.sub
+       )
+'
+
+test_expect_success 'inside git repository but with --no-index' '
+       rm -fr is &&
+       mkdir -p is/git/sub &&
+       echo hello >is/git/file1 &&
+       echo world >is/git/sub/file2 &&
+       echo ".*o*" >is/git/.gitignore &&
+       {
+               echo file1:hello &&
+               echo sub/file2:world
+       } >is/expect.full &&
+       : >is/expect.empty &&
+       echo file2:world >is/expect.sub
+       (
+               cd is/git &&
+               git init &&
+               test_must_fail git grep o >../actual.full &&
+               test_cmp ../expect.empty ../actual.full &&
+               git grep --no-index o >../actual.full &&
+               test_cmp ../expect.full ../actual.full &&
+               cd sub &&
+               test_must_fail git grep o >../../actual.sub &&
+               test_cmp ../../expect.empty ../../actual.sub &&
+               git grep --no-index o >../../actual.sub &&
+               test_cmp ../../expect.sub ../../actual.sub
+       )
+'
+
+test_expect_success 'setup double-dash tests' '
+cat >double-dash <<EOF &&
+--
+->
+other
+EOF
+git add double-dash
+'
+
+cat >expected <<EOF
+double-dash:->
+EOF
+test_expect_success 'grep -- pattern' '
+       git grep -- "->" >actual &&
+       test_cmp expected actual
+'
+test_expect_success 'grep -- pattern -- pathspec' '
+       git grep -- "->" -- double-dash >actual &&
+       test_cmp expected actual
+'
+test_expect_success 'grep -e pattern -- path' '
+       git grep -e "->" -- double-dash >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+double-dash:--
+EOF
+test_expect_success 'grep -e -- -- path' '
+       git grep -e -- -- double-dash >actual &&
+       test_cmp expected actual
+'
+
 test_done
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
new file mode 100755 (executable)
index 0000000..d9202d5
--- /dev/null
@@ -0,0 +1,176 @@
+#!/bin/sh
+
+test_description='Test automatic use of a pager.'
+
+. ./test-lib.sh
+
+rm -f stdout_is_tty
+test_expect_success 'set up terminal for tests' '
+       if test -t 1
+       then
+               : > stdout_is_tty
+       elif
+               test_have_prereq PERL &&
+               "$PERL_PATH" "$TEST_DIRECTORY"/t7006/test-terminal.perl \
+                       sh -c "test -t 1"
+       then
+               : > test_terminal_works
+       fi
+'
+
+if test -e stdout_is_tty
+then
+       test_terminal() { "$@"; }
+       test_set_prereq TTY
+elif test -e test_terminal_works
+then
+       test_terminal() {
+               "$PERL_PATH" "$TEST_DIRECTORY"/t7006/test-terminal.perl "$@"
+       }
+       test_set_prereq TTY
+else
+       say no usable terminal, so skipping some tests
+fi
+
+unset GIT_PAGER GIT_PAGER_IN_USE
+git config --unset core.pager
+PAGER='cat > paginated.out'
+export PAGER
+
+test_expect_success 'setup' '
+       test_commit initial
+'
+
+rm -f paginated.out
+test_expect_success TTY 'some commands use a pager' '
+       test_terminal git log &&
+       test -e paginated.out
+'
+
+rm -f paginated.out
+test_expect_success TTY 'some commands do not use a pager' '
+       test_terminal git rev-list HEAD &&
+       ! test -e paginated.out
+'
+
+rm -f paginated.out
+test_expect_success 'no pager when stdout is a pipe' '
+       git log | cat &&
+       ! test -e paginated.out
+'
+
+rm -f paginated.out
+test_expect_success 'no pager when stdout is a regular file' '
+       git log > file &&
+       ! test -e paginated.out
+'
+
+rm -f paginated.out
+test_expect_success TTY 'git --paginate rev-list uses a pager' '
+       test_terminal git --paginate rev-list HEAD &&
+       test -e paginated.out
+'
+
+rm -f file paginated.out
+test_expect_success 'no pager even with --paginate when stdout is a pipe' '
+       git --paginate log | cat &&
+       ! test -e paginated.out
+'
+
+rm -f paginated.out
+test_expect_success TTY 'no pager with --no-pager' '
+       test_terminal git --no-pager log &&
+       ! test -e paginated.out
+'
+
+# A colored commit log will begin with an appropriate ANSI escape
+# for the first color; the text "commit" comes later.
+colorful() {
+       read firstline < $1
+       ! expr "$firstline" : "^[a-zA-Z]" >/dev/null
+}
+
+rm -f colorful.log colorless.log
+test_expect_success 'tests can detect color' '
+       git log --no-color > colorless.log &&
+       git log --color > colorful.log &&
+       ! colorful colorless.log &&
+       colorful colorful.log
+'
+
+rm -f colorless.log
+git config color.ui auto
+test_expect_success 'no color when stdout is a regular file' '
+       git log > colorless.log &&
+       ! colorful colorless.log
+'
+
+rm -f paginated.out
+git config color.ui auto
+test_expect_success TTY 'color when writing to a pager' '
+       TERM=vt100 test_terminal git log &&
+       colorful paginated.out
+'
+
+rm -f colorful.log
+git config color.ui auto
+test_expect_success 'color when writing to a file intended for a pager' '
+       TERM=vt100 GIT_PAGER_IN_USE=true git log > colorful.log &&
+       colorful colorful.log
+'
+
+unset PAGER GIT_PAGER
+git config --unset core.pager
+test_expect_success 'determine default pager' '
+       less=$(git var GIT_PAGER) &&
+       test -n "$less"
+'
+
+if expr "$less" : '^[a-z]*$' > /dev/null && test_have_prereq TTY
+then
+       test_set_prereq SIMPLEPAGER
+fi
+
+unset PAGER GIT_PAGER
+git config --unset core.pager
+rm -f default_pager_used
+test_expect_success SIMPLEPAGER 'default pager is used by default' '
+       cat > $less <<-EOF &&
+       #!$SHELL_PATH
+       wc > default_pager_used
+       EOF
+       chmod +x $less &&
+       PATH=.:$PATH test_terminal git log &&
+       test -e default_pager_used
+'
+
+unset GIT_PAGER
+git config --unset core.pager
+rm -f PAGER_used
+test_expect_success TTY 'PAGER overrides default pager' '
+       PAGER="wc > PAGER_used" &&
+       export PAGER &&
+       test_terminal git log &&
+       test -e PAGER_used
+'
+
+unset GIT_PAGER
+rm -f core.pager_used
+test_expect_success TTY 'core.pager overrides PAGER' '
+       PAGER=wc &&
+       export PAGER &&
+       git config core.pager "wc > core.pager_used" &&
+       test_terminal git log &&
+       test -e core.pager_used
+'
+
+rm -f GIT_PAGER_used
+test_expect_success TTY 'GIT_PAGER overrides core.pager' '
+       git config core.pager wc &&
+       GIT_PAGER="wc > GIT_PAGER_used" &&
+       export GIT_PAGER &&
+       test_terminal git log &&
+       test -e GIT_PAGER_used
+'
+
+test_done
diff --git a/t/t7006/test-terminal.perl b/t/t7006/test-terminal.perl
new file mode 100755 (executable)
index 0000000..73ff809
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use IO::Pty;
+use File::Copy;
+
+# Run @$argv in the background with stdout redirected to $out.
+sub start_child {
+       my ($argv, $out) = @_;
+       my $pid = fork;
+       if (not defined $pid) {
+               die "fork failed: $!"
+       } elsif ($pid == 0) {
+               open STDOUT, ">&", $out;
+               close $out;
+               exec(@$argv) or die "cannot exec '$argv->[0]': $!"
+       }
+       return $pid;
+}
+
+# Wait for $pid to finish.
+sub finish_child {
+       # Simplified from wait_or_whine() in run-command.c.
+       my ($pid) = @_;
+
+       my $waiting = waitpid($pid, 0);
+       if ($waiting < 0) {
+               die "waitpid failed: $!";
+       } elsif ($? & 127) {
+               my $code = $? & 127;
+               warn "died of signal $code";
+               return $code - 128;
+       } else {
+               return $? >> 8;
+       }
+}
+
+sub xsendfile {
+       my ($out, $in) = @_;
+
+       # Note: the real sendfile() cannot read from a terminal.
+
+       # It is unspecified by POSIX whether reads
+       # from a disconnected terminal will return
+       # EIO (as in AIX 4.x, IRIX, and Linux) or
+       # end-of-file.  Either is fine.
+       copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
+}
+
+if ($#ARGV < 1) {
+       die "usage: test-terminal program args";
+}
+my $master = new IO::Pty;
+my $slave = $master->slave;
+my $pid = start_child(\@ARGV, $slave);
+close $slave;
+xsendfile(\*STDOUT, $master);
+exit(finish_child($pid));
index afb55b3a463f79be83c0e3cc4a8aff8a0c6676be..1eef93c2b292c8ec649f12c826587365e5a0d0e6 100755 (executable)
@@ -11,21 +11,26 @@ test_expect_success 'setup non-bare' '
        git commit -a -m two
 '
 
-test_expect_success 'hard reset requires a worktree' '
+test_expect_success '"hard" reset requires a worktree' '
        (cd .git &&
         test_must_fail git reset --hard)
 '
 
-test_expect_success 'merge reset requires a worktree' '
+test_expect_success '"merge" reset requires a worktree' '
        (cd .git &&
         test_must_fail git reset --merge)
 '
 
-test_expect_success 'mixed reset is ok' '
+test_expect_success '"keep" reset requires a worktree' '
+       (cd .git &&
+        test_must_fail git reset --keep)
+'
+
+test_expect_success '"mixed" reset is ok' '
        (cd .git && git reset)
 '
 
-test_expect_success 'soft reset is ok' '
+test_expect_success '"soft" reset is ok' '
        (cd .git && git reset --soft)
 '
 
@@ -40,19 +45,23 @@ test_expect_success 'setup bare' '
        cd bare.git
 '
 
-test_expect_success 'hard reset is not allowed in bare' '
+test_expect_success '"hard" reset is not allowed in bare' '
        test_must_fail git reset --hard HEAD^
 '
 
-test_expect_success 'merge reset is not allowed in bare' '
+test_expect_success '"merge" reset is not allowed in bare' '
        test_must_fail git reset --merge HEAD^
 '
 
-test_expect_success 'mixed reset is not allowed in bare' '
+test_expect_success '"keep" reset is not allowed in bare' '
+       test_must_fail git reset --keep HEAD^
+'
+
+test_expect_success '"mixed" reset is not allowed in bare' '
        test_must_fail git reset --mixed HEAD^
 '
 
-test_expect_success 'soft reset is allowed in bare' '
+test_expect_success '"soft" reset is allowed in bare' '
        git reset --soft HEAD^ &&
        test "`git show --pretty=format:%s | head -n 1`" = "one"
 '
index 8704d0019655d591785c0cf0eadb7d846c6b4469..70cdd8e618c648f7ee6550997d68c40d912c8db9 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2009 Christian Couder
 #
 
-test_description='Tests for "git reset --merge"'
+test_description='Tests for "git reset" with "--merge" and "--keep" options'
 
 . ./test-lib.sh
 
@@ -43,6 +43,30 @@ test_expect_success 'reset --merge is ok when switching back' '
     test -z "$(git diff --cached)"
 '
 
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     C       C     C    D     --keep   D       D     D
+# file2:     C       D     D    D     --keep   C       D     D
+test_expect_success 'reset --keep is ok with changes in file it does not touch' '
+    git reset --hard second &&
+    cat file1 >file2 &&
+    git reset --keep HEAD^ &&
+    ! grep 4 file1 &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+    test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --keep is ok when switching back' '
+    git reset --keep second &&
+    grep 4 file1 &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)"
+'
+
 # The next test will test the following:
 #
 #           working index HEAD target         working index HEAD
@@ -74,6 +98,18 @@ test_expect_success 'reset --merge is ok again when switching back (1)' '
     test -z "$(git diff --cached)"
 '
 
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     B       B     C    D     --keep   (disallowed)
+test_expect_success 'reset --keep fails with changes in index in files it touches' '
+    git reset --hard second &&
+    echo "line 5" >> file1 &&
+    git add file1 &&
+    test_must_fail git reset --keep HEAD^
+'
+
 # The next test will test the following:
 #
 #           working index HEAD target         working index HEAD
@@ -100,6 +136,30 @@ test_expect_success 'reset --merge is ok again when switching back (2)' '
     test -z "$(git diff --cached)"
 '
 
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     C       C     C    D     --keep   D       D     D
+# file2:     C       C     D    D     --keep   C       D     D
+test_expect_success 'reset --keep keeps changes it does not touch' '
+    git reset --hard second &&
+    echo "line 4" >> file2 &&
+    git add file2 &&
+    git reset --keep HEAD^ &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+    test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --keep keeps changes when switching back' '
+    git reset --keep second &&
+    grep 4 file2 &&
+    grep 4 file1 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)"
+'
+
 # The next test will test the following:
 #
 #           working index HEAD target         working index HEAD
@@ -116,6 +176,22 @@ test_expect_success 'reset --merge fails with changes in file it touches' '
     grep file1 err.log | grep "not uptodate"
 '
 
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     A       B     B    C     --keep   (disallowed)
+test_expect_success 'reset --keep fails with changes in file it touches' '
+    git reset --hard second &&
+    echo "line 5" >> file1 &&
+    test_tick &&
+    git commit -m "add line 5" file1 &&
+    sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+    mv file3 file1 &&
+    test_must_fail git reset --keep HEAD^ 2>err.log &&
+    grep file1 err.log | grep "not uptodate"
+'
+
 test_expect_success 'setup 3 different branches' '
     git reset --hard second &&
     git branch branch1 &&
@@ -152,6 +228,18 @@ test_expect_success '"reset --merge HEAD^" is ok with pending merge' '
     test -z "$(git diff)"
 '
 
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     X       U     B    C     --keep   (disallowed)
+test_expect_success '"reset --keep HEAD^" fails with pending merge' '
+    git reset --hard third &&
+    test_must_fail git merge branch1 &&
+    test_must_fail git reset --keep HEAD^ 2>err.log &&
+    grep "middle of a merge" err.log
+'
+
 # The next test will test the following:
 #
 #           working index HEAD target         working index HEAD
@@ -166,7 +254,19 @@ test_expect_success '"reset --merge HEAD" is ok with pending merge' '
     test -z "$(git diff)"
 '
 
-test_expect_success '--merge with added/deleted' '
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     X       U     B    B     --keep   (disallowed)
+test_expect_success '"reset --keep HEAD" fails with pending merge' '
+    git reset --hard third &&
+    test_must_fail git merge branch1 &&
+    test_must_fail git reset --keep HEAD 2>err.log &&
+    grep "middle of a merge" err.log
+'
+
+test_expect_success '--merge is ok with added/deleted merge' '
     git reset --hard third &&
     rm -f file2 &&
     test_must_fail git merge branch3 &&
@@ -180,4 +280,16 @@ test_expect_success '--merge with added/deleted' '
     git diff --exit-code --cached
 '
 
+test_expect_success '--keep fails with added/deleted merge' '
+    git reset --hard third &&
+    rm -f file2 &&
+    test_must_fail git merge branch3 &&
+    ! test -f file2 &&
+    test -f file3 &&
+    git diff --exit-code file3 &&
+    git diff --exit-code branch3 file3 &&
+    test_must_fail git reset --keep HEAD 2>err.log &&
+    grep "middle of a merge" err.log
+'
+
 test_done
index de896c948d17bb55967fd04449cbb45c248fd6ab..ce421ad5ac4b3a17d0e347a7d86a386e2b14547b 100755 (executable)
@@ -44,26 +44,32 @@ A B C D soft   A B D
 A B C D mixed  A D D
 A B C D hard   D D D
 A B C D merge  XXXXX
+A B C D keep   XXXXX
 A B C C soft   A B C
 A B C C mixed  A C C
 A B C C hard   C C C
 A B C C merge  XXXXX
+A B C C keep   A C C
 B B C D soft   B B D
 B B C D mixed  B D D
 B B C D hard   D D D
 B B C D merge  D D D
+B B C D keep   XXXXX
 B B C C soft   B B C
 B B C C mixed  B C C
 B B C C hard   C C C
 B B C C merge  C C C
+B B C C keep   B C C
 B C C D soft   B C D
 B C C D mixed  B D D
 B C C D hard   D D D
 B C C D merge  XXXXX
+B C C D keep   XXXXX
 B C C C soft   B C C
 B C C C mixed  B C C
 B C C C hard   C C C
 B C C C merge  B C C
+B C C C keep   B C C
 EOF
 
 test_expect_success 'setting up branches to test with unmerged entries' '
@@ -104,10 +110,12 @@ X U B C soft   XXXXX
 X U B C mixed  X C C
 X U B C hard   C C C
 X U B C merge  C C C
+X U B C keep   XXXXX
 X U B B soft   XXXXX
 X U B B mixed  X B B
 X U B B hard   B B B
 X U B B merge  B B B
+X U B B keep   XXXXX
 EOF
 
 test_done
index 6442f710be8bcaea11931044d52deb5b75d8f7e0..d20ed61b481539b25c054c49bd82aa6fb9b3a981 100755 (executable)
@@ -166,19 +166,31 @@ test_expect_success 'checkout -m with merge conflict' '
        ! test -s current
 '
 
-test_expect_success 'checkout to detach HEAD' '
+test_expect_success 'checkout to detach HEAD (with advice declined)' '
 
+       git config advice.detachedHead false &&
        git checkout -f renamer && git clean -f &&
        git checkout renamer^ 2>messages &&
-       (cat >messages.expect <<EOF
-Note: moving to '\''renamer^'\'' which isn'\''t a local branch
-If you want to create a new branch from this checkout, you may do so
-(now or later) by using -b with the checkout command again. Example:
-  git checkout -b <new_branch_name>
-HEAD is now at 7329388... Initial A one, A two
-EOF
-) &&
-       test_cmp messages.expect messages &&
+       grep "HEAD is now at 7329388" messages &&
+       test 1 -eq $(wc -l <messages) &&
+       H=$(git rev-parse --verify HEAD) &&
+       M=$(git show-ref -s --verify refs/heads/master) &&
+       test "z$H" = "z$M" &&
+       if git symbolic-ref HEAD >/dev/null 2>&1
+       then
+               echo "OOPS, HEAD is still symbolic???"
+               false
+       else
+               : happy
+       fi
+'
+
+test_expect_success 'checkout to detach HEAD' '
+       git config advice.detachedHead true &&
+       git checkout -f renamer && git clean -f &&
+       git checkout renamer^ 2>messages &&
+       grep "HEAD is now at 7329388" messages &&
+       test 1 -lt $(wc -l <messages) &&
        H=$(git rev-parse --verify HEAD) &&
        M=$(git show-ref -s --verify refs/heads/master) &&
        test "z$H" = "z$M" &&
index 8e2449d24409bab14558f83617f651c3f7255627..1382a8e58a14c8fcc92889aad492d5883fd64f2b 100755 (executable)
@@ -28,6 +28,8 @@ test_expect_success 'setup a submodule tree' '
        git commit -m upstream
        git clone . super &&
        git clone super submodule &&
+       git clone super rebasing &&
+       git clone super merging &&
        (cd super &&
         git submodule add ../submodule submodule &&
         test_tick &&
@@ -45,6 +47,16 @@ test_expect_success 'setup a submodule tree' '
         ) &&
         git add submodule &&
         git commit -m "submodule update"
+       ) &&
+       (cd super &&
+        git submodule add ../rebasing rebasing &&
+        test_tick &&
+        git commit -m "rebasing"
+       ) &&
+       (cd super &&
+        git submodule add ../merging merging &&
+        test_tick &&
+        git commit -m "rebasing"
        )
 '
 
@@ -177,21 +189,17 @@ test_expect_success 'submodule update - checkout in .git/config' '
 
 test_expect_success 'submodule init picks up rebase' '
        (cd super &&
-        git config submodule.rebasing.url git://non-existing/git &&
-        git config submodule.rebasing.path does-not-matter &&
-        git config submodule.rebasing.update rebase &&
+        git config -f .gitmodules submodule.rebasing.update rebase &&
         git submodule init rebasing &&
-        test "rebase" = $(git config submodule.rebasing.update)
+        test "rebase" = "$(git config submodule.rebasing.update)"
        )
 '
 
 test_expect_success 'submodule init picks up merge' '
        (cd super &&
-        git config submodule.merging.url git://non-existing/git &&
-        git config submodule.merging.path does-not-matter &&
-        git config submodule.merging.update merge &&
+        git config -f .gitmodules submodule.merging.update merge &&
         git submodule init merging &&
-        test "merge" = $(git config submodule.merging.update)
+        test "merge" = "$(git config submodule.merging.update)"
        )
 '
 
index 7940901d47fd457cda77ee333aa40145433be4d4..8297cb4f1e6e2d903dfbf6fde825d2c787082e58 100755 (executable)
@@ -425,4 +425,16 @@ test_expect_success 'amend using the message from a commit named with tag' '
 
 '
 
+test_expect_success 'amend can copy notes' '
+
+       git config notes.rewrite.amend true &&
+       git config notes.rewriteRef "refs/notes/*" &&
+       test_commit foo &&
+       git notes add -m"a note" &&
+       test_tick &&
+       git commit --amend -m"new foo" &&
+       test "$(git notes show)" = "a note"
+
+'
+
 test_done
index 253c3343190e88349f6aca1109e7439e3cf2a06e..aeec1f6142e622ffc7b0ef8a68fe7734c02d24d0 100755 (executable)
@@ -34,7 +34,7 @@ test_expect_success 'status with modified file in submodule' '
        (cd sub && git reset --hard) &&
        echo "changed" >sub/foo &&
        git status >output &&
-       grep "modified:   sub" output
+       grep "modified:   sub (modified content)" output
 '
 
 test_expect_success 'status with modified file in submodule (porcelain)' '
@@ -49,7 +49,7 @@ test_expect_success 'status with modified file in submodule (porcelain)' '
 test_expect_success 'status with added file in submodule' '
        (cd sub && git reset --hard && echo >foo && git add foo) &&
        git status >output &&
-       grep "modified:   sub" output
+       grep "modified:   sub (modified content)" output
 '
 
 test_expect_success 'status with added file in submodule (porcelain)' '
@@ -64,7 +64,12 @@ test_expect_success 'status with untracked file in submodule' '
        (cd sub && git reset --hard) &&
        echo "content" >sub/new-file &&
        git status >output &&
-       grep "modified:   sub" output
+       grep "modified:   sub (untracked content)" output
+'
+
+test_expect_success 'status -uno with untracked file in submodule' '
+       git status -uno >output &&
+       grep "^nothing to commit" output
 '
 
 test_expect_success 'status with untracked file in submodule (porcelain)' '
@@ -74,6 +79,84 @@ test_expect_success 'status with untracked file in submodule (porcelain)' '
        EOF
 '
 
+test_expect_success 'status with added and untracked file in submodule' '
+       (cd sub && git reset --hard && echo >foo && git add foo) &&
+       echo "content" >sub/new-file &&
+       git status >output &&
+       grep "modified:   sub (modified content, untracked content)" output
+'
+
+test_expect_success 'status with added and untracked file in submodule (porcelain)' '
+       (cd sub && git reset --hard && echo >foo && git add foo) &&
+       echo "content" >sub/new-file &&
+       git status --porcelain >output &&
+       diff output - <<-\EOF
+        M sub
+       EOF
+'
+
+test_expect_success 'status with modified file in modified submodule' '
+       (cd sub && git reset --hard) &&
+       rm sub/new-file &&
+       (cd sub && echo "next change" >foo && git commit -m "next change" foo) &&
+       echo "changed" >sub/foo &&
+       git status >output &&
+       grep "modified:   sub (new commits, modified content)" output
+'
+
+test_expect_success 'status with modified file in modified submodule (porcelain)' '
+       (cd sub && git reset --hard) &&
+       echo "changed" >sub/foo &&
+       git status --porcelain >output &&
+       diff output - <<-\EOF
+        M sub
+       EOF
+'
+
+test_expect_success 'status with added file in modified submodule' '
+       (cd sub && git reset --hard && echo >foo && git add foo) &&
+       git status >output &&
+       grep "modified:   sub (new commits, modified content)" output
+'
+
+test_expect_success 'status with added file in modified submodule (porcelain)' '
+       (cd sub && git reset --hard && echo >foo && git add foo) &&
+       git status --porcelain >output &&
+       diff output - <<-\EOF
+        M sub
+       EOF
+'
+
+test_expect_success 'status with untracked file in modified submodule' '
+       (cd sub && git reset --hard) &&
+       echo "content" >sub/new-file &&
+       git status >output &&
+       grep "modified:   sub (new commits, untracked content)" output
+'
+
+test_expect_success 'status with untracked file in modified submodule (porcelain)' '
+       git status --porcelain >output &&
+       diff output - <<-\EOF
+        M sub
+       EOF
+'
+
+test_expect_success 'status with added and untracked file in modified submodule' '
+       (cd sub && git reset --hard && echo >foo && git add foo) &&
+       echo "content" >sub/new-file &&
+       git status >output &&
+       grep "modified:   sub (new commits, modified content, untracked content)" output
+'
+
+test_expect_success 'status with added and untracked file in modified submodule (porcelain)' '
+       (cd sub && git reset --hard && echo >foo && git add foo) &&
+       echo "content" >sub/new-file &&
+       git status --porcelain >output &&
+       diff output - <<-\EOF
+        M sub
+       EOF
+'
+
 test_expect_success 'rm submodule contents' '
        rm -rf sub/* sub/.git
 '
index 3bbddd03cbfcf5cbdff6ed2987d68da9402ed993..230143cf318705fb01e61f10072a096e86186934 100755 (executable)
@@ -11,7 +11,15 @@ test_expect_success setup '
        echo B B B B B >two &&
        echo C C C C C >tres &&
        echo ABC >mouse &&
-       git add one two tres mouse &&
+       for i in 1 2 3 4 5 6 7 8 9
+       do
+               echo $i
+       done >nine_lines &&
+       for i in 1 2 3 4 5 6 7 8 9 a
+       do
+               echo $i
+       done >ten_lines &&
+       git add one two tres mouse nine_lines ten_lines &&
        test_tick &&
        GIT_AUTHOR_NAME=Initial git commit -m Initial &&
 
@@ -167,4 +175,14 @@ test_expect_success 'blame -L with invalid end' '
        grep "has only 2 lines" errors
 '
 
+test_expect_success 'indent of line numbers, nine lines' '
+       git blame nine_lines >actual &&
+       test $(grep -c "  " actual) = 0
+'
+
+test_expect_success 'indent of line numbers, ten lines' '
+       git blame ten_lines >actual &&
+       test $(grep -c "  " actual) = 9
+'
+
 test_done
index c09f37528811f9395d63e3f3acd1f598307983a2..640b3d2bb41636e271d0d55aabeade38ded612bb 100755 (executable)
@@ -852,4 +852,70 @@ test_expect_success 'no warning with sendemail.chainreplyto = true' '
        ! grep "no-chain-reply-to" errors
 '
 
+test_expect_success 'sendemail.to works' '
+       git config --replace-all sendemail.to "Somebody <somebody@ex.com>" &&
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               $patches $patches >stdout &&
+       grep "To: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success '--no-to overrides sendemail.to' '
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --no-to \
+               --to=nobody@example.com \
+               $patches $patches >stdout &&
+       grep "To: nobody@example.com" stdout &&
+       ! grep "To: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success 'sendemail.cc works' '
+       git config --replace-all sendemail.cc "Somebody <somebody@ex.com>" &&
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               $patches $patches >stdout &&
+       grep "Cc: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success '--no-cc overrides sendemail.cc' '
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --no-cc \
+               --cc=bodies@example.com \
+               --to=nobody@example.com \
+               $patches $patches >stdout &&
+       grep "Cc: bodies@example.com" stdout &&
+       ! grep "Cc: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success 'sendemail.bcc works' '
+       git config --replace-all sendemail.bcc "Other <other@ex.com>" &&
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --smtp-server relay.example.com \
+               $patches $patches >stdout &&
+       grep "RCPT TO:<other@ex.com>" stdout
+'
+
+test_expect_success '--no-bcc overrides sendemail.bcc' '
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --no-bcc \
+               --bcc=bodies@example.com \
+               --to=nobody@example.com \
+               --smtp-server relay.example.com \
+               $patches $patches >stdout &&
+       grep "RCPT TO:<bodies@example.com>" stdout &&
+       ! grep "RCPT TO:<other@ex.com>" stdout
+'
+
 test_done
index 95741cbbac6bf2e59531bbe1f9527ce4596fa904..a9a558d292231bd4b372552df0364c63ece25299 100755 (executable)
@@ -7,9 +7,10 @@ test_description='git svn info'
 . ./lib-git-svn.sh
 
 # Tested with: svn, version 1.4.4 (r25188)
+# Tested with: svn, version 1.6.[12345689]
 v=`svn_cmd --version | sed -n -e 's/^svn, version \(1\.[0-9]*\.[0-9]*\).*$/\1/p'`
 case $v in
-1.[45].*)
+1.[456].*)
        ;;
 *)
        say "skipping svn-info test (SVN version: $v not supported)"
index 53581425c4b5cd8e7b35b41605511d52d005e8d6..24c2421bfc1acd7248fc8094ad76096f12579992 100755 (executable)
@@ -11,6 +11,7 @@ test_expect_success 'load svk depot' "
        svnadmin load -q '$rawsvnrepo' \
          < '$TEST_DIRECTORY/t9150/svk-merge.dump' &&
        git svn init --minimize-url -R svkmerge \
+         --rewrite-root=http://svn.example.org \
          -T trunk -b branches '$svnrepo' &&
        git svn fetch --all
        "
index 3569c620964d40e1f2461e8e1a5ad22be7be0939..250c651eaecf60103ee442bcfa2a6c65250320ec 100755 (executable)
@@ -11,6 +11,7 @@ test_expect_success 'load svn dump' "
        svnadmin load -q '$rawsvnrepo' \
          < '$TEST_DIRECTORY/t9151/svn-mergeinfo.dump' &&
        git svn init --minimize-url -R svnmerge \
+         --rewrite-root=http://svn.example.org \
          -T trunk -b branches '$svnrepo' &&
        git svn fetch --all
        "
@@ -33,6 +34,21 @@ test_expect_success 'svn non-merge merge commits did not become git merge commit
        [ -z "$bad_non_merges" ]
        '
 
+test_expect_success 'commit made to merged branch is reachable from the merge' '
+       before_commit=$(git rev-list --all --grep="trunk commit before merging trunk to b2")
+       merge_commit=$(git rev-list --all --grep="Merge trunk to b2")
+       not_reachable=$(git rev-list -1 $before_commit --not $merge_commit)
+       [ -z "$not_reachable" ]
+       '
+
+test_expect_success 'merging two branches in one commit is detected correctly' '
+       f1_commit=$(git rev-list --all --grep="make f1 branch from trunk")
+       f2_commit=$(git rev-list --all --grep="make f2 branch from trunk")
+       merge_commit=$(git rev-list --all --grep="Merge f1 and f2 to trunk")
+       not_reachable=$(git rev-list -1 $f1_commit $f2_commit --not $merge_commit)
+       [ -z "$not_reachable" ]
+       '
+
 test_expect_failure 'everything got merged in the end' '
        unmerged=$(git rev-list --all --not master)
        [ -z "$unmerged" ]
index 3d73f140f86ceb59dc556f81c4af09db00e62cdc..e1e138cb1a73cfa312cf4d375d77da90418cc7b5 100644 (file)
@@ -156,6 +156,89 @@ svn merge ../branches/right --accept postpone
 i=$(commit $i "non-merge right to trunk 2")
 cd ..
 
+say "Branching b1 from trunk"
+svn update
+svn cp trunk branches/b1
+i=$(commit $i "make b1 branch from trunk")
+
+say "Branching b2 from trunk"
+svn update
+svn cp trunk branches/b2
+i=$(commit $i "make b2 branch from trunk")
+
+say "Make a commit to b2"
+svn update
+cd branches/b2
+echo "b2" > b2file
+svn add b2file
+i=$(commit $i "b2 update 1")
+cd ../..
+
+say "Make a commit to b1"
+svn update
+cd branches/b1
+echo "b1" > b1file
+svn add b1file
+i=$(commit $i "b1 update 1")
+cd ../..
+
+say "Merge b1 to trunk"
+svn update
+cd trunk
+svn merge ../branches/b1/ --accept postpone
+i=$(commit $i "Merge b1 to trunk")
+cd ..
+
+say "Make a commit to trunk before merging trunk to b2"
+svn update
+cd trunk
+echo "trunk" > trunkfile
+svn add trunkfile
+i=$(commit $i "trunk commit before merging trunk to b2")
+cd ..
+
+say "Merge trunk to b2"
+svn update
+cd branches/b2
+svn merge ../../trunk/ --accept postpone
+i=$(commit $i "Merge trunk to b2")
+cd ../..
+
+say "Merge b2 to trunk"
+svn update
+cd trunk
+svn merge ../branches/b2/ --accept postpone
+svn resolved b1file
+svn resolved trunkfile
+i=$(commit $i "Merge b2 to trunk")
+cd ..
+
+say "Creating f1 from trunk with a new file"
+svn update
+svn cp trunk branches/f1
+cd branches/f1
+echo "f1" > f1file
+svn add f1file
+cd ../..
+i=$(commit $i "make f1 branch from trunk with a new file")
+
+say "Creating f2 from trunk with a new file"
+svn update
+svn cp trunk branches/f2
+cd branches/f2
+echo "f2" > f2file
+svn add f2file
+cd ../..
+i=$(commit $i "make f2 branch from trunk with a new file")
+
+say "Merge f1 and f2 to trunk in one go"
+svn update
+cd trunk
+svn merge ../branches/f1/ --accept postpone
+svn merge ../branches/f2/ --accept postpone
+i=$(commit $i "Merge f1 and f2 to trunk")
+cd ..
+
 say "Adding subdirectory to LEFT"
 svn update
 cd branches/left
@@ -174,8 +257,8 @@ cd ..
 
 say "Make PARTIAL branch"
 svn update
-i=$(commit $i "make partial branch")
 svn cp trunk/subdir branches/partial
+i=$(commit $i "make partial branch")
 
 say "Make a commit to PARTIAL"
 svn update
@@ -194,13 +277,13 @@ cd ../../
 
 say "Tagging trunk"
 svn update
-i=$(commit $i "tagging v1.0")
 svn cp trunk tags/v1.0
+i=$(commit $i "tagging v1.0")
 
 say "Branching BUGFIX from v1.0"
 svn update
-i=$(commit $i "make bugfix branch from tag")
 svn cp tags/v1.0 branches/bugfix
+i=$(commit $i "make bugfix branch from tag")
 
 say "Make a commit to BUGFIX"
 svn update
index ebf386ebd59372004d0bcf8440e8f2feb02ad5e6..47cafcf528d87119756e42666125bc75f9aea6e5 100644 (file)
@@ -1633,13 +1633,427 @@ PROPS-END
 
 
 Revision-number: 25
+Prop-content-length: 129
+Content-length: 129
+
+K 7
+svn:log
+V 31
+(r25) make b1 branch from trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:18:56.084589Z
+PROPS-END
+
+Node-path: branches/b1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 24
+Node-copyfrom-path: trunk
+
+
+Revision-number: 26
+Prop-content-length: 129
+Content-length: 129
+
+K 7
+svn:log
+V 31
+(r26) make b2 branch from trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:18:59.076940Z
+PROPS-END
+
+Node-path: branches/b2
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 25
+Node-copyfrom-path: trunk
+
+
+Revision-number: 27
+Prop-content-length: 115
+Content-length: 115
+
+K 7
+svn:log
+V 17
+(r27) b2 update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:01.095762Z
+PROPS-END
+
+Node-path: branches/b2/b2file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 5edbdd57cba621eb3c6e601bf563b4dc
+Text-content-sha1: 9d4b38049776bd0a2074d67cad23f8eaed35a3b3
+Content-length: 13
+
+PROPS-END
+b2
+
+
+Revision-number: 28
+Prop-content-length: 115
+Content-length: 115
+
+K 7
+svn:log
+V 17
+(r28) b1 update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:03.097465Z
+PROPS-END
+
+Node-path: branches/b1/b1file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 08778dfd9ac4f603231896aba7aad523
+Text-content-sha1: b551771aa4ad5b14123fc3bd98d89db2bc0edd4f
+Content-length: 13
+
+PROPS-END
+b1
+
+
+Revision-number: 29
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 23
+(r29) Merge b1 to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:06.073175Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 118
+Content-length: 118
+
+K 13
+svn:mergeinfo
+V 83
+/branches/b1:25-28
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/b1file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 28
+Node-copyfrom-path: branches/b1/b1file
+Text-copy-source-md5: 08778dfd9ac4f603231896aba7aad523
+Text-copy-source-sha1: b551771aa4ad5b14123fc3bd98d89db2bc0edd4f
+
+
+Revision-number: 30
+Prop-content-length: 143
+Content-length: 143
+
+K 7
+svn:log
+V 45
+(r30) trunk commit before merging trunk to b2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:08.096353Z
+PROPS-END
+
+Node-path: trunk/trunkfile
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: edf45fe5c98c5367733b39bbb2bb20d9
+Text-content-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239
+Content-length: 16
+
+PROPS-END
+trunk
+
+
+Revision-number: 31
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 23
+(r31) Merge trunk to b2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:11.081541Z
+PROPS-END
+
+Node-path: branches/b2
+Node-kind: dir
+Node-action: change
+Prop-content-length: 131
+Content-length: 131
+
+K 13
+svn:mergeinfo
+V 96
+/branches/b1:25-28
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+/trunk:26-30
+PROPS-END
+
+
+Node-path: branches/b2/b1file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 30
+Node-copyfrom-path: trunk/b1file
+Text-copy-source-md5: 08778dfd9ac4f603231896aba7aad523
+Text-copy-source-sha1: b551771aa4ad5b14123fc3bd98d89db2bc0edd4f
+
+
+Node-path: branches/b2/trunkfile
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 30
+Node-copyfrom-path: trunk/trunkfile
+Text-copy-source-md5: edf45fe5c98c5367733b39bbb2bb20d9
+Text-copy-source-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239
+
+
+Revision-number: 32
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 23
+(r32) Merge b2 to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:14.117939Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 138
+Content-length: 138
+
+K 13
+svn:mergeinfo
+V 102
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/b2file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 31
+Node-copyfrom-path: branches/b2/b2file
+Text-copy-source-md5: 5edbdd57cba621eb3c6e601bf563b4dc
+Text-copy-source-sha1: 9d4b38049776bd0a2074d67cad23f8eaed35a3b3
+
+
+Revision-number: 33
+Prop-content-length: 145
+Content-length: 145
+
+K 7
+svn:log
+V 47
+(r33) make f1 branch from trunk with a new file
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:17.105832Z
+PROPS-END
+
+Node-path: branches/f1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 32
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/f1/f1file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 2b1abc6b6c5c0018851f9f8e6475563b
+Text-content-sha1: aece6dfba588900e00d95601d22b4408d49580af
+Content-length: 13
+
+PROPS-END
+f1
+
+
+Revision-number: 34
+Prop-content-length: 145
+Content-length: 145
+
+K 7
+svn:log
+V 47
+(r34) make f2 branch from trunk with a new file
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:20.110057Z
+PROPS-END
+
+Node-path: branches/f2
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 33
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/f2/f2file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 575c5638d60271457e54ab7d07309502
+Text-content-sha1: 1c49a440c352f3473efa9512255033b94dc7def0
+Content-length: 13
+
+PROPS-END
+f2
+
+
+Revision-number: 35
+Prop-content-length: 128
+Content-length: 128
+
+K 7
+svn:log
+V 30
+(r35) Merge f1 and f2 to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:24.081490Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 173
+Content-length: 173
+
+K 13
+svn:mergeinfo
+V 137
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/f1:33-34
+/branches/f2:34
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/f1file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 34
+Node-copyfrom-path: branches/f1/f1file
+Text-copy-source-md5: 2b1abc6b6c5c0018851f9f8e6475563b
+Text-copy-source-sha1: aece6dfba588900e00d95601d22b4408d49580af
+
+
+Node-path: trunk/f2file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 34
+Node-copyfrom-path: branches/f2/f2file
+Text-copy-source-md5: 575c5638d60271457e54ab7d07309502
+Text-copy-source-sha1: 1c49a440c352f3473efa9512255033b94dc7def0
+
+
+Revision-number: 36
 Prop-content-length: 135
 Content-length: 135
 
 K 7
 svn:log
 V 37
-(r25) add subdirectory to left branch
+(r36) add subdirectory to left branch
 K 10
 svn:author
 V 3
@@ -1647,7 +2061,7 @@ adm
 K 8
 svn:date
 V 27
-2010-01-19T04:14:46.052649Z
+2010-02-22T06:19:26.113516Z
 PROPS-END
 
 Node-path: branches/left/subdir
@@ -1672,14 +2086,14 @@ PROPS-END
 Yeehaw
 
 
-Revision-number: 26
+Revision-number: 37
 Prop-content-length: 123
 Content-length: 123
 
 K 7
 svn:log
 V 25
-(r26) merge left to trunk
+(r37) merge left to trunk
 K 10
 svn:author
 V 3
@@ -1687,19 +2101,23 @@ adm
 K 8
 svn:date
 V 27
-2010-01-19T04:14:49.040783Z
+2010-02-22T06:19:29.073699Z
 PROPS-END
 
 Node-path: trunk
 Node-kind: dir
 Node-action: change
-Prop-content-length: 99
-Content-length: 99
+Prop-content-length: 173
+Content-length: 173
 
 K 13
 svn:mergeinfo
-V 64
-/branches/left:2-25
+V 137
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/f1:33-34
+/branches/f2:34
+/branches/left:2-36
 /branches/left-sub:4-19
 /branches/right:2-22
 PROPS-END
@@ -1708,18 +2126,18 @@ PROPS-END
 Node-path: trunk/subdir
 Node-kind: dir
 Node-action: add
-Node-copyfrom-rev: 25
+Node-copyfrom-rev: 36
 Node-copyfrom-path: branches/left/subdir
 
 
-Revision-number: 27
-Prop-content-length: 118
-Content-length: 118
+Revision-number: 38
+Prop-content-length: 123
+Content-length: 123
 
 K 7
 svn:log
-V 20
-(r28) partial update
+V 25
+(r38) make partial branch
 K 10
 svn:author
 V 3
@@ -1727,16 +2145,34 @@ adm
 K 8
 svn:date
 V 27
-2010-01-19T04:14:53.049037Z
+2010-02-22T06:19:32.072243Z
 PROPS-END
 
 Node-path: branches/partial
 Node-kind: dir
 Node-action: add
-Node-copyfrom-rev: 26
+Node-copyfrom-rev: 37
 Node-copyfrom-path: trunk/subdir
 
 
+Revision-number: 39
+Prop-content-length: 118
+Content-length: 118
+
+K 7
+svn:log
+V 20
+(r39) partial update
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:34.097961Z
+PROPS-END
+
 Node-path: branches/partial/palindromes
 Node-kind: file
 Node-action: add
@@ -1750,14 +2186,14 @@ PROPS-END
 racecar
 
 
-Revision-number: 28
+Revision-number: 40
 Prop-content-length: 126
 Content-length: 126
 
 K 7
 svn:log
 V 28
-(r29) merge partial to trunk
+(r40) merge partial to trunk
 K 10
 svn:author
 V 3
@@ -1765,21 +2201,25 @@ adm
 K 8
 svn:date
 V 27
-2010-01-19T04:14:56.041526Z
+2010-02-22T06:19:37.080211Z
 PROPS-END
 
 Node-path: trunk/subdir
 Node-kind: dir
 Node-action: change
-Prop-content-length: 142
-Content-length: 142
+Prop-content-length: 246
+Content-length: 246
 
 K 13
 svn:mergeinfo
-V 106
-/branches/left/subdir:2-25
+V 210
+/branches/b1/subdir:25-28
+/branches/b2/subdir:26-31
+/branches/f1/subdir:33-34
+/branches/f2/subdir:34
+/branches/left/subdir:2-36
 /branches/left-sub/subdir:4-19
-/branches/partial:27
+/branches/partial:38-39
 /branches/right/subdir:2-22
 PROPS-END
 
@@ -1787,20 +2227,20 @@ PROPS-END
 Node-path: trunk/subdir/palindromes
 Node-kind: file
 Node-action: add
-Node-copyfrom-rev: 27
+Node-copyfrom-rev: 39
 Node-copyfrom-path: branches/partial/palindromes
 Text-copy-source-md5: 5d1c2024fb5efc4eef812856df1b080c
 Text-copy-source-sha1: 5f8509ddd14c91a52864dd1447344e706f9bbc69
 
 
-Revision-number: 29
-Prop-content-length: 131
-Content-length: 131
+Revision-number: 41
+Prop-content-length: 116
+Content-length: 116
 
 K 7
 svn:log
-V 33
-(r31) make bugfix branch from tag
+V 18
+(r41) tagging v1.0
 K 10
 svn:author
 V 3
@@ -1808,24 +2248,24 @@ adm
 K 8
 svn:date
 V 27
-2010-01-19T04:15:00.039761Z
+2010-02-22T06:19:40.083460Z
 PROPS-END
 
 Node-path: tags/v1.0
 Node-kind: dir
 Node-action: add
-Node-copyfrom-rev: 28
+Node-copyfrom-rev: 40
 Node-copyfrom-path: trunk
 
 
-Revision-number: 30
-Prop-content-length: 120
-Content-length: 120
+Revision-number: 42
+Prop-content-length: 131
+Content-length: 131
 
 K 7
 svn:log
-V 22
-(r32) commit to bugfix
+V 33
+(r42) make bugfix branch from tag
 K 10
 svn:author
 V 3
@@ -1833,16 +2273,34 @@ adm
 K 8
 svn:date
 V 27
-2010-01-19T04:15:03.043218Z
+2010-02-22T06:19:43.118075Z
 PROPS-END
 
 Node-path: branches/bugfix
 Node-kind: dir
 Node-action: add
-Node-copyfrom-rev: 29
+Node-copyfrom-rev: 41
 Node-copyfrom-path: tags/v1.0
 
 
+Revision-number: 43
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 22
+(r43) commit to bugfix
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:45.079536Z
+PROPS-END
+
 Node-path: branches/bugfix/subdir/palindromes
 Node-kind: file
 Node-action: change
@@ -1855,14 +2313,14 @@ racecar
 kayak
 
 
-Revision-number: 31
+Revision-number: 44
 Prop-content-length: 125
 Content-length: 125
 
 K 7
 svn:log
 V 27
-(r33) Merge BUGFIX to TRUNK
+(r44) Merge BUGFIX to TRUNK
 K 10
 svn:author
 V 3
@@ -1870,41 +2328,49 @@ adm
 K 8
 svn:date
 V 27
-2010-01-19T04:15:06.043723Z
+2010-02-22T06:19:48.078914Z
 PROPS-END
 
 Node-path: trunk
 Node-kind: dir
 Node-action: change
-Prop-content-length: 133
-Content-length: 133
+Prop-content-length: 210
+Content-length: 210
 
 K 13
 svn:mergeinfo
-V 98
-/branches/bugfix:30
-/branches/left:2-25
+V 174
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/bugfix:42-43
+/branches/f1:33-34
+/branches/f2:34
+/branches/left:2-36
 /branches/left-sub:4-19
 /branches/right:2-22
-/tags/v1.0:29
+/tags/v1.0:41
 PROPS-END
 
 
 Node-path: trunk/subdir
 Node-kind: dir
 Node-action: change
-Prop-content-length: 190
-Content-length: 190
+Prop-content-length: 297
+Content-length: 297
 
 K 13
 svn:mergeinfo
-V 154
-/branches/bugfix/subdir:30
-/branches/left/subdir:2-25
+V 261
+/branches/b1/subdir:25-28
+/branches/b2/subdir:26-31
+/branches/bugfix/subdir:42-43
+/branches/f1/subdir:33-34
+/branches/f2/subdir:34
+/branches/left/subdir:2-36
 /branches/left-sub/subdir:4-19
-/branches/partial:27
+/branches/partial:38-39
 /branches/right/subdir:2-22
-/tags/v1.0/subdir:29
+/tags/v1.0/subdir:41
 PROPS-END
 
 
index 4327eb8baa2b00b0833fdbceac27bb8291370bda..daef2d6c230fb617395feeff6cf330b74ad57832 100755 (executable)
@@ -226,7 +226,7 @@ test_expect_success 'gitcvs.ext.enabled = true' \
   'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
    GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
    GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
-   diff -q cvswork cvswork2'
+   test_cmp cvswork cvswork2'
 
 rm -fr cvswork2
 test_expect_success 'gitcvs.ext.enabled = false' \
@@ -247,7 +247,7 @@ test_expect_success 'gitcvs.dbname' \
   'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
    GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
    GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
-   diff -q cvswork cvswork2 &&
+   test_cmp cvswork cvswork2 &&
    test -f "$SERVERDIR/gitcvs.ext.master.sqlite" &&
    cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs.ext.master.sqlite"'
 
@@ -257,7 +257,7 @@ test_expect_success 'gitcvs.ext.dbname' \
    GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
    GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
    GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
-   diff -q cvswork cvswork2 &&
+   test_cmp cvswork cvswork2 &&
    test -f "$SERVERDIR/gitcvs1.ext.master.sqlite" &&
    test ! -f "$SERVERDIR/gitcvs2.ext.master.sqlite" &&
    cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs1.ext.master.sqlite"'
@@ -282,7 +282,7 @@ test_expect_success 'cvs update (create new file)' \
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
    test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.1/" &&
-   diff -q testfile1 ../testfile1'
+   test_cmp testfile1 ../testfile1'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (update existing file)' \
@@ -293,7 +293,7 @@ test_expect_success 'cvs update (update existing file)' \
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
    test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.2/" &&
-   diff -q testfile1 ../testfile1'
+   test_cmp testfile1 ../testfile1'
 
 cd "$WORKDIR"
 #TODO: cvsserver doesn't support update w/o -d
@@ -322,7 +322,7 @@ test_expect_success 'cvs update (subdirectories)' \
    (for dir in A A/B A/B/C A/D E; do
       filename="file_in_$(echo $dir|sed -e "s#/# #g")" &&
       if test "$(echo $(grep -v ^D $dir/CVS/Entries|cut -d/ -f2,3,5))" = "$filename/1.1/" &&
-           diff -q "$dir/$filename" "../$dir/$filename"; then
+       test_cmp "$dir/$filename" "../$dir/$filename"; then
         :
       else
         echo >failure
@@ -349,7 +349,7 @@ test_expect_success 'cvs update (re-add deleted file)' \
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
    test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
-   diff -q testfile1 ../testfile1'
+   test_cmp testfile1 ../testfile1'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (merge)' \
@@ -366,7 +366,7 @@ test_expect_success 'cvs update (merge)' \
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
    test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
-   diff -q merge ../merge &&
+   test_cmp merge ../merge &&
    ( echo Line 0; cat merge ) >merge.tmp &&
    mv merge.tmp merge &&
    cd "$WORKDIR" &&
@@ -377,7 +377,7 @@ test_expect_success 'cvs update (merge)' \
    cd cvswork &&
    sleep 1 && touch merge &&
    GIT_CONFIG="$git_config" cvs -Q update &&
-   diff -q merge ../expected'
+   test_cmp merge ../expected'
 
 cd "$WORKDIR"
 
@@ -402,13 +402,13 @@ test_expect_success 'cvs update (conflict merge)' \
    git push gitcvs.git >/dev/null &&
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
-   diff -q merge ../expected.C'
+   test_cmp merge ../expected.C'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (-C)' \
   'cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update -C &&
-   diff -q merge ../merge'
+   test_cmp merge ../merge'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (merge no-op)' \
@@ -420,7 +420,7 @@ test_expect_success 'cvs update (merge no-op)' \
     cd cvswork &&
     sleep 1 && touch merge &&
     GIT_CONFIG="$git_config" cvs -Q update &&
-    diff -q merge ../merge'
+    test_cmp merge ../merge'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (-p)' '
index 2fc7fdb124583f86d5be622510f29ceca1dd3e09..63b6b060e64948771319294251838394264c84a9 100755 (executable)
@@ -591,13 +591,21 @@ test_debug 'cat gitweb.log'
 # ----------------------------------------------------------------------
 # gitweb config and repo config
 
-cat >>gitweb_config.perl <<EOF
-
-\$feature{'blame'}{'override'} = 1;
-\$feature{'snapshot'}{'override'} = 1;
-\$feature{'avatar'}{'override'} = 1;
+cat >>gitweb_config.perl <<\EOF
+
+# turn on override for each overridable feature
+foreach my $key (keys %feature) {
+       if ($feature{$key}{'sub'}) {
+               $feature{$key}{'override'} = 1;
+       }
+}
 EOF
 
+test_expect_success \
+       'config override: projects list (implicit)' \
+       'gitweb_run'
+test_debug 'cat gitweb.log'
+
 test_expect_success \
        'config override: tree view, features not overridden in repo config' \
        'gitweb_run "p=.git;a=tree"'
index d196cc5ca93b07bcf20e4629455ea7f03b0907c7..2487da1296da1a2508526f58a2e45fa750c49c8e 100755 (executable)
@@ -15,9 +15,10 @@ code and message.'
 # ----------------------------------------------------------------------
 # snapshot settings
 
-test_commit \
-       'SnapshotTests' \
-       'i can has snapshot?'
+test_expect_success 'setup' "
+       test_commit 'SnapshotTests' 'i can has snapshot?'
+"
+
 
 cat >>gitweb_config.perl <<\EOF
 $feature{'snapshot'}{'override'} = 0;
index 363345faef7b1eb209c548914b94460d9475cb13..b572ce3ab7f55e1258aa92b37e10a0d067c9d01d 100755 (executable)
@@ -47,13 +47,20 @@ EOF
 
 test_expect_success 'import a trivial module' '
 
-       git cvsimport -a -z 0 -C module-git module &&
+       git cvsimport -a -R -z 0 -C module-git module &&
        test_cmp module-cvs/o_fortuna module-git/o_fortuna
 
 '
 
 test_expect_success 'pack refs' 'cd module-git && git gc && cd ..'
 
+test_expect_success 'initial import has correct .git/cvs-revisions' '
+
+       (cd module-git &&
+        git log --format="o_fortuna 1.1 %H" -1) > expected &&
+       test_cmp expected module-git/.git/cvs-revisions
+'
+
 test_expect_success 'update cvs module' '
 
        cd module-cvs &&
@@ -86,13 +93,21 @@ EOF
 test_expect_success 'update git module' '
 
        cd module-git &&
-       git cvsimport -a -z 0 module &&
+       git cvsimport -a -R -z 0 module &&
        git merge origin &&
        cd .. &&
        test_cmp module-cvs/o_fortuna module-git/o_fortuna
 
 '
 
+test_expect_success 'update has correct .git/cvs-revisions' '
+
+       (cd module-git &&
+        git log --format="o_fortuna 1.1 %H" -1 HEAD^ &&
+        git log --format="o_fortuna 1.2 %H" -1 HEAD) > expected &&
+       test_cmp expected module-git/.git/cvs-revisions
+'
+
 test_expect_success 'update cvs module' '
 
        cd module-cvs &&
@@ -107,13 +122,22 @@ test_expect_success 'cvsimport.module config works' '
 
        cd module-git &&
                git config cvsimport.module module &&
-               git cvsimport -a -z0 &&
+               git cvsimport -a -R -z0 &&
                git merge origin &&
        cd .. &&
        test_cmp module-cvs/tick module-git/tick
 
 '
 
+test_expect_success 'second update has correct .git/cvs-revisions' '
+
+       (cd module-git &&
+        git log --format="o_fortuna 1.1 %H" -1 HEAD^^ &&
+        git log --format="o_fortuna 1.2 %H" -1 HEAD^
+        git log --format="tick 1.1 %H" -1 HEAD) > expected &&
+       test_cmp expected module-git/.git/cvs-revisions
+'
+
 test_expect_success 'import from a CVS working tree' '
 
        $CVS co -d import-from-wt module &&
@@ -126,6 +150,12 @@ test_expect_success 'import from a CVS working tree' '
 
 '
 
+test_expect_success 'no .git/cvs-revisions created by default' '
+
+       ! test -e import-from-wt/.git/cvs-revisions
+
+'
+
 test_expect_success 'test entire HEAD' 'test_cmp_branch_tree master'
 
 test_done
index afd3053f96b789a73274217b384d245583500c04..c582964b0d26bedcc69b4f7cc787c4deccfab6b9 100644 (file)
@@ -54,6 +54,10 @@ unset GIT_OBJECT_DIRECTORY
 unset GIT_CEILING_DIRECTORIES
 unset SHA1_FILE_DIRECTORIES
 unset SHA1_FILE_DIRECTORY
+unset GIT_NOTES_REF
+unset GIT_NOTES_DISPLAY_REF
+unset GIT_NOTES_REWRITE_REF
+unset GIT_NOTES_REWRITE_MODE
 GIT_MERGE_VERBOSITY=5
 export GIT_MERGE_VERBOSITY
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
@@ -65,6 +69,8 @@ GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
 # CDPATH into the environment
 unset CDPATH
 
+unset GREP_OPTIONS
+
 case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
        1|2|true)
                echo "* warning: Some tests will not work if GIT_TRACE" \
index fe476cb6185a389671c014b4ea067184f51465d0..92713d16da5431e9c8395967ab0e116402dfed67 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This program can either change modification time of the given
  * file(s) or just print it. The program does not change atime nor
- * ctime (their values are explicitely preserved).
+ * ctime (their values are explicitly preserved).
  *
  * The mtime can be changed to an absolute value:
  *
index 107742891f6e68d4b103f61a425a585770b0d155..2638781c5b89024294f3c4c32861b275e339ad1d 100644 (file)
@@ -171,7 +171,7 @@ static struct child_process *get_helper(struct transport *transport)
                } else if (!strcmp(capname, "connect")) {
                        data->connect = 1;
                } else if (mandatory) {
-                       die("Unknown madatory capability %s. This remote "
+                       die("Unknown mandatory capability %s. This remote "
                            "helper probably needs newer version of Git.\n",
                            capname);
                }
@@ -279,9 +279,8 @@ static void standard_options(struct transport *t)
        char buf[16];
        int n;
        int v = t->verbose;
-       int no_progress = v < 0 || (!t->progress && !isatty(2));
 
-       set_helper_option(t, "progress", !no_progress ? "true" : "false");
+       set_helper_option(t, "progress", t->progress ? "true" : "false");
 
        n = snprintf(buf, sizeof(buf), "%d", v + 1);
        if (n >= sizeof(buf))
@@ -576,7 +575,6 @@ static int push_refs(struct transport *transport,
        if (buf.len == 0)
                return 0;
 
-       transport->verbose = flags & TRANSPORT_PUSH_VERBOSE ? 1 : 0;
        standard_options(transport);
 
        if (flags & TRANSPORT_PUSH_DRY_RUN) {
index 3846aacb476b552cefddd8774b9a055e353b6ebc..8ce39364a1e58b338bc45a4eb524b637ce3a8881 100644 (file)
@@ -526,7 +526,7 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.include_tag = data->options.followtags;
        args.verbose = (transport->verbose > 0);
        args.quiet = (transport->verbose < 0);
-       args.no_progress = args.quiet || (!transport->progress && !isatty(2));
+       args.no_progress = !transport->progress;
        args.depth = data->options.depth;
 
        for (i = 0; i < nr_heads; i++)
@@ -573,7 +573,7 @@ static int push_had_errors(struct ref *ref)
        return 0;
 }
 
-static int refs_pushed(struct ref *ref)
+int transport_refs_pushed(struct ref *ref)
 {
        for (; ref; ref = ref->next) {
                switch(ref->status) {
@@ -587,7 +587,7 @@ static int refs_pushed(struct ref *ref)
        return 0;
 }
 
-static void update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)
+void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)
 {
        struct refspec rs;
 
@@ -609,8 +609,6 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref, int verb
        }
 }
 
-#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
-
 static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg, int porcelain)
 {
        if (porcelain) {
@@ -623,7 +621,7 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str
                else
                        fprintf(stdout, "%s\n", summary);
        } else {
-               fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+               fprintf(stderr, " %c %-*s ", flag, TRANSPORT_SUMMARY_WIDTH, summary);
                if (from)
                        fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
                else
@@ -675,7 +673,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
 static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)
 {
        if (!count)
-               fprintf(stderr, "To %s\n", dest);
+               fprintf(porcelain ? stdout : stderr, "To %s\n", dest);
 
        switch(ref->status) {
        case REF_STATUS_NONE:
@@ -711,8 +709,8 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
        return 1;
 }
 
-static void print_push_status(const char *dest, struct ref *refs,
-                             int verbose, int porcelain, int * nonfastforward)
+void transport_print_push_status(const char *dest, struct ref *refs,
+                                 int verbose, int porcelain, int *nonfastforward)
 {
        struct ref *ref;
        int n = 0;
@@ -738,7 +736,7 @@ static void print_push_status(const char *dest, struct ref *refs,
        }
 }
 
-static void verify_remote_names(int nr_heads, const char **heads)
+void transport_verify_remote_names(int nr_heads, const char **heads)
 {
        int i;
 
@@ -788,9 +786,10 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
        args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
        args.use_thin_pack = data->options.thin;
-       args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
-       args.quiet = !!(flags & TRANSPORT_PUSH_QUIET);
+       args.verbose = (transport->verbose > 0);
+       args.quiet = (transport->verbose < 0);
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
+       args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
 
        ret = send_pack(&args, data->fd, data->conn, remote_refs,
                        &data->extra_have);
@@ -872,6 +871,21 @@ static int is_file(const char *url)
        return S_ISREG(buf.st_mode);
 }
 
+static int isurlschemechar(int first_flag, int ch)
+{
+       /*
+        * The set of valid URL schemes, as per STD66 (RFC3986) is
+        * '[A-Za-z][A-Za-z0-9+.-]*'. But use sightly looser check
+        * of '[A-Za-z0-9][A-Za-z0-9+.-]*' because earlier version
+        * of check used '[A-Za-z0-9]+' so not to break any remote
+        * helpers.
+        */
+       int alphanumeric, special;
+       alphanumeric = ch > 0 && isalnum(ch);
+       special = ch == '+' || ch == '-' || ch == '.';
+       return alphanumeric || (!first_flag && special);
+}
+
 static int is_url(const char *url)
 {
        const char *url2, *first_slash;
@@ -896,7 +910,7 @@ static int is_url(const char *url)
         */
        url2 = url;
        while (url2 < first_slash - 1) {
-               if (!isalnum((unsigned char)*url2))
+               if (!isurlschemechar(url2 == url, (unsigned char)*url2))
                        return 0;
                url2++;
        }
@@ -915,9 +929,12 @@ struct transport *transport_get(struct remote *remote, const char *url)
        const char *helper;
        struct transport *ret = xcalloc(1, sizeof(*ret));
 
+       ret->progress = isatty(2);
+
        if (!remote)
                die("No remote provided to transport_get()");
 
+       ret->got_remote_refs = 0;
        ret->remote = remote;
        helper = remote->foreign_vcs;
 
@@ -929,7 +946,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
        if (url) {
                const char *p = url;
 
-               while (isalnum(*p))
+               while (isurlschemechar(p == url, *p))
                        p++;
                if (!prefixcmp(p, "::"))
                        helper = xstrndup(url, p - url);
@@ -1013,12 +1030,31 @@ int transport_set_option(struct transport *transport,
        return 1;
 }
 
+void transport_set_verbosity(struct transport *transport, int verbosity,
+       int force_progress)
+{
+       if (verbosity >= 2)
+               transport->verbose = verbosity <= 3 ? verbosity : 3;
+       if (verbosity < 0)
+               transport->verbose = -1;
+
+       /**
+        * Rules used to determine whether to report progress (processing aborts
+        * when a rule is satisfied):
+        *
+        *   1. Report progress, if force_progress is 1 (ie. --progress).
+        *   2. Don't report progress, if verbosity < 0 (ie. -q/--quiet ).
+        *   3. Report progress if isatty(2) is 1.
+        **/
+       transport->progress = force_progress || (verbosity >= 0 && isatty(2));
+}
+
 int transport_push(struct transport *transport,
                   int refspec_nr, const char **refspec, int flags,
                   int *nonfastforward)
 {
        *nonfastforward = 0;
-       verify_remote_names(refspec_nr, refspec);
+       transport_verify_remote_names(refspec_nr, refspec);
 
        if (transport->push) {
                /* Maybe FIXME. But no important transport uses this case. */
@@ -1031,11 +1067,11 @@ int transport_push(struct transport *transport,
                        transport->get_refs_list(transport, 1);
                struct ref *local_refs = get_local_heads();
                int match_flags = MATCH_REFS_NONE;
-               int verbose = flags & TRANSPORT_PUSH_VERBOSE;
-               int quiet = flags & TRANSPORT_PUSH_QUIET;
+               int verbose = (transport->verbose > 0);
+               int quiet = (transport->verbose < 0);
                int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;
                int pretend = flags & TRANSPORT_PUSH_DRY_RUN;
-               int ret, err;
+               int push_ret, ret, err;
 
                if (flags & TRANSPORT_PUSH_ALL)
                        match_flags |= MATCH_REFS_ALL;
@@ -1051,13 +1087,12 @@ int transport_push(struct transport *transport,
                        flags & TRANSPORT_PUSH_MIRROR,
                        flags & TRANSPORT_PUSH_FORCE);
 
-               ret = transport->push_refs(transport, remote_refs, flags);
+               push_ret = transport->push_refs(transport, remote_refs, flags);
                err = push_had_errors(remote_refs);
-
-               ret |= err;
+               ret = push_ret | err;
 
                if (!quiet || err)
-                       print_push_status(transport->url, remote_refs,
+                       transport_print_push_status(transport->url, remote_refs,
                                        verbose | porcelain, porcelain,
                                        nonfastforward);
 
@@ -1067,11 +1102,14 @@ int transport_push(struct transport *transport,
                if (!(flags & TRANSPORT_PUSH_DRY_RUN)) {
                        struct ref *ref;
                        for (ref = remote_refs; ref; ref = ref->next)
-                               update_tracking_ref(transport->remote, ref, verbose);
+                               transport_update_tracking_ref(transport->remote, ref, verbose);
                }
 
-               if (!quiet && !ret && !refs_pushed(remote_refs))
+               if (porcelain && !push_ret)
+                       puts("Done");
+               else if (!quiet && !ret && !transport_refs_pushed(remote_refs))
                        fprintf(stderr, "Everything up-to-date\n");
+
                return ret;
        }
        return 1;
@@ -1079,8 +1117,10 @@ int transport_push(struct transport *transport,
 
 const struct ref *transport_get_remote_refs(struct transport *transport)
 {
-       if (!transport->remote_refs)
+       if (!transport->got_remote_refs) {
                transport->remote_refs = transport->get_refs_list(transport, 0);
+               transport->got_remote_refs = 1;
+       }
 
        return transport->remote_refs;
 }
index 7cea5cc7234185b1c37ada818dfa1a9114621ad2..c59d97388e6fdb1dccd4dca0d41dccc3d129f8fb 100644 (file)
@@ -19,6 +19,12 @@ struct transport {
        void *data;
        const struct ref *remote_refs;
 
+       /**
+        * Indicates whether we already called get_refs_list(); set by
+        * transport.c::transport_get_remote_refs().
+        */
+       unsigned got_remote_refs : 1;
+
        /**
         * Returns 0 if successful, positive if the option is not
         * recognized or is inapplicable, and negative if the option
@@ -74,7 +80,12 @@ struct transport {
        int (*disconnect)(struct transport *connection);
        char *pack_lockfile;
        signed verbose : 3;
-       /* Force progress even if stderr is not a tty */
+       /**
+        * Transports should not set this directly, and should use this
+        * value without having to check isatty(2), -q/--quiet
+        * (transport->verbose < 0), etc. - checking has already been done
+        * in transport_set_verbosity().
+        **/
        unsigned progress : 1;
        /*
         * If transport is at least potentially smart, this points to
@@ -88,10 +99,10 @@ struct transport {
 #define TRANSPORT_PUSH_FORCE 2
 #define TRANSPORT_PUSH_DRY_RUN 4
 #define TRANSPORT_PUSH_MIRROR 8
-#define TRANSPORT_PUSH_VERBOSE 16
-#define TRANSPORT_PUSH_PORCELAIN 32
-#define TRANSPORT_PUSH_QUIET 64
-#define TRANSPORT_PUSH_SET_UPSTREAM 128
+#define TRANSPORT_PUSH_PORCELAIN 16
+#define TRANSPORT_PUSH_SET_UPSTREAM 32
+
+#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
@@ -122,6 +133,8 @@ struct transport *transport_get(struct remote *, const char *);
  **/
 int transport_set_option(struct transport *transport, const char *name,
                         const char *value);
+void transport_set_verbosity(struct transport *transport, int verbosity,
+       int force_progress);
 
 int transport_push(struct transport *connection,
                   int refspec_nr, const char **refspec, int flags,
@@ -142,4 +155,14 @@ int transport_connect(struct transport *transport, const char *name,
 /* Transport methods defined outside transport.c */
 int transport_helper_init(struct transport *transport, const char *name);
 
+/* common methods used by transport.c and builtin-send-pack.c */
+void transport_verify_remote_names(int nr_heads, const char **heads);
+
+void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose);
+
+int transport_refs_pushed(struct ref *ref);
+
+void transport_print_push_status(const char *dest, struct ref *refs,
+                 int verbose, int porcelain, int *nonfastforward);
+
 #endif
index 08796c23228fbfb6eb255c479f0196768ee83b27..67a9a0c5a5bf7d7125765679318cfcd68c160da7 100644 (file)
@@ -441,6 +441,7 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
 
        if (name[0] == '\0') {
                hashcpy(sha1, root);
+               free(tree);
                return 0;
        }
 
index df151813f9c12a681dcac85608f5ff2262c12879..dc464d78b35659705ffb0cd233b80ab27e24e8bc 100644 (file)
@@ -105,12 +105,12 @@ static void show_edge(struct commit *commit)
        fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
 }
 
-static int do_rev_list(int fd, void *create_full_pack)
+static int do_rev_list(int in, int out, void *create_full_pack)
 {
        int i;
        struct rev_info revs;
 
-       pack_pipe = xfdopen(fd, "w");
+       pack_pipe = xfdopen(out, "w");
        init_revisions(&revs, NULL);
        revs.tag_objects = 1;
        revs.tree_objects = 1;
@@ -162,8 +162,9 @@ static void create_pack_file(void)
        int arg = 0;
 
        if (shallow_nr) {
+               memset(&rev_list, 0, sizeof(rev_list));
                rev_list.proc = do_rev_list;
-               rev_list.data = 0;
+               rev_list.out = -1;
                if (start_async(&rev_list))
                        die("git upload-pack: unable to fork git-rev-list");
                argv[arg++] = "pack-objects";
diff --git a/utf8.c b/utf8.c
index ab326ac83e0d9e81b06abff58b00a98341adcd36..84cfc72e6db144880febad0a4ffa64800919fb6d 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -280,22 +280,11 @@ int is_utf8(const char *text)
        return 1;
 }
 
-static inline void strbuf_write(struct strbuf *sb, const char *buf, int len)
+static void strbuf_addchars(struct strbuf *sb, int c, size_t n)
 {
-       if (sb)
-               strbuf_insert(sb, sb->len, buf, len);
-       else
-               fwrite(buf, len, 1, stdout);
-}
-
-static void print_spaces(struct strbuf *buf, int count)
-{
-       static const char s[] = "                    ";
-       while (count >= sizeof(s)) {
-               strbuf_write(buf, s, sizeof(s) - 1);
-               count -= sizeof(s) - 1;
-       }
-       strbuf_write(buf, s, count);
+       strbuf_grow(sb, n);
+       memset(sb->buf + sb->len, c, n);
+       strbuf_setlen(sb, sb->len + n);
 }
 
 static void strbuf_add_indented_text(struct strbuf *buf, const char *text,
@@ -307,8 +296,8 @@ static void strbuf_add_indented_text(struct strbuf *buf, const char *text,
                const char *eol = strchrnul(text, '\n');
                if (*eol == '\n')
                        eol++;
-               print_spaces(buf, indent);
-               strbuf_write(buf, text, eol - text);
+               strbuf_addchars(buf, ' ', indent);
+               strbuf_add(buf, text, eol - text);
                text = eol;
                indent = indent2;
        }
@@ -335,16 +324,21 @@ static size_t display_mode_esc_sequence_len(const char *s)
  * consumed (and no extra indent is necessary for the first line).
  */
 int strbuf_add_wrapped_text(struct strbuf *buf,
-               const char *text, int indent, int indent2, int width)
+               const char *text, int indent1, int indent2, int width)
 {
-       int w = indent, assume_utf8 = is_utf8(text);
-       const char *bol = text, *space = NULL;
+       int indent, w, assume_utf8 = 1;
+       const char *bol, *space, *start = text;
+       size_t orig_len = buf->len;
 
        if (width <= 0) {
-               strbuf_add_indented_text(buf, text, indent, indent2);
+               strbuf_add_indented_text(buf, text, indent1, indent2);
                return 1;
        }
 
+retry:
+       bol = text;
+       w = indent = indent1;
+       space = NULL;
        if (indent < 0) {
                w = -indent;
                space = text;
@@ -366,8 +360,8 @@ int strbuf_add_wrapped_text(struct strbuf *buf,
                                if (space)
                                        start = space;
                                else
-                                       print_spaces(buf, indent);
-                               strbuf_write(buf, start, text - start);
+                                       strbuf_addchars(buf, ' ', indent);
+                               strbuf_add(buf, start, text - start);
                                if (!c)
                                        return w;
                                space = text;
@@ -376,40 +370,41 @@ int strbuf_add_wrapped_text(struct strbuf *buf,
                                else if (c == '\n') {
                                        space++;
                                        if (*space == '\n') {
-                                               strbuf_write(buf, "\n", 1);
+                                               strbuf_addch(buf, '\n');
                                                goto new_line;
                                        }
                                        else if (!isalnum(*space))
                                                goto new_line;
                                        else
-                                               strbuf_write(buf, " ", 1);
+                                               strbuf_addch(buf, ' ');
                                }
                                w++;
                                text++;
                        }
                        else {
 new_line:
-                               strbuf_write(buf, "\n", 1);
+                               strbuf_addch(buf, '\n');
                                text = bol = space + isspace(*space);
                                space = NULL;
                                w = indent = indent2;
                        }
                        continue;
                }
-               if (assume_utf8)
+               if (assume_utf8) {
                        w += utf8_width(&text, NULL);
-               else {
+                       if (!text) {
+                               assume_utf8 = 0;
+                               text = start;
+                               strbuf_setlen(buf, orig_len);
+                               goto retry;
+                       }
+               } else {
                        w++;
                        text++;
                }
        }
 }
 
-int print_wrapped_text(const char *text, int indent, int indent2, int width)
-{
-       return strbuf_add_wrapped_text(NULL, text, indent, indent2, width);
-}
-
 int is_encoding_utf8(const char *name)
 {
        if (!name)
diff --git a/utf8.h b/utf8.h
index c9738d83d991d89bbdd4c5a6442fcad2fdaaa4df..ebc4d2fa85113c971ab4c4eaa52537a688a03745 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -8,7 +8,6 @@ int utf8_strwidth(const char *string);
 int is_utf8(const char *text);
 int is_encoding_utf8(const char *name);
 
-int print_wrapped_text(const char *text, int indent, int indent2, int len);
 int strbuf_add_wrapped_text(struct strbuf *buf,
                const char *text, int indent, int indent2, int width);
 
index 8a149e11084eeec4501b5b2c5d22e5266f4852e7..95e576548474e942addcf1978f215720dd2f6e96 100644 (file)
--- a/walker.h
+++ b/walker.h
@@ -34,6 +34,6 @@ int walker_fetch(struct walker *impl, int targets, char **target,
 
 void walker_free(struct walker *walker);
 
-struct walker *get_http_walker(const char *url, struct remote *remote);
+struct walker *get_http_walker(const char *url);
 
 #endif /* WALKER_H */
index c5075c9c61ca97923e233622061da8365641b6c4..09feb1f7373367866668f3ac7c121e49bdde8096 100644 (file)
@@ -7,9 +7,15 @@
 # @@BUILD_DIR@@ and @@PROG@@.
 
 GIT_EXEC_PATH='@@BUILD_DIR@@'
-GIT_TEMPLATE_DIR='@@BUILD_DIR@@/templates/blt'
+if test -n "$NO_SET_GIT_TEMPLATE_DIR"
+then
+       unset GIT_TEMPLATE_DIR
+else
+       GIT_TEMPLATE_DIR='@@BUILD_DIR@@/templates/blt'
+       export GIT_TEMPLATE_DIR
+fi
 GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'
 PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH"
-export GIT_EXEC_PATH GIT_TEMPLATE_DIR GITPERLLIB PATH
+export GIT_EXEC_PATH GITPERLLIB PATH
 
 exec "${GIT_EXEC_PATH}/@@PROG@@" "$@"
index 0e3e20a3fd38f6f99da44483ee0bb9753f2b217a..9c71b21242773f52ca560d7e5e5e52123674d334 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -204,6 +204,16 @@ int xmkstemp(char *template)
        return fd;
 }
 
+int xmkstemp_mode(char *template, int mode)
+{
+       int fd;
+
+       fd = git_mkstemp_mode(template, mode);
+       if (fd < 0)
+               die_errno("Unable to create temporary file");
+       return fd;
+}
+
 /*
  * zlib wrappers to make sure we don't silently miss errors
  * at init time.
@@ -267,10 +277,14 @@ int git_inflate(z_streamp strm, int flush)
 int odb_mkstemp(char *template, size_t limit, const char *pattern)
 {
        int fd;
-
+       /*
+        * we let the umask do its job, don't try to be more
+        * restrictive except to remove write permission.
+        */
+       int mode = 0444;
        snprintf(template, limit, "%s/%s",
                 get_object_directory(), pattern);
-       fd = mkstemp(template);
+       fd = git_mkstemp_mode(template, mode);
        if (0 <= fd)
                return fd;
 
@@ -279,7 +293,7 @@ int odb_mkstemp(char *template, size_t limit, const char *pattern)
        snprintf(template, limit, "%s/%s",
                 get_object_directory(), pattern);
        safe_create_leading_directories(template);
-       return xmkstemp(template);
+       return xmkstemp_mode(template, mode);
 }
 
 int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
index 5807fc3211a3aa8f886694776fe8c86b5bc5eb59..8ca59a2d2abec0c9c4629cd4ae7340fcd79a0c7e 100644 (file)
@@ -78,7 +78,8 @@ static void wt_status_print_cached_header(struct wt_status *s)
 }
 
 static void wt_status_print_dirty_header(struct wt_status *s,
-                                        int has_deleted)
+                                        int has_deleted,
+                                        int has_dirty_submodules)
 {
        const char *c = color(WT_STATUS_HEADER, s);
 
@@ -90,6 +91,8 @@ static void wt_status_print_dirty_header(struct wt_status *s,
        else
                color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" to update what will be committed)");
        color_fprintf_ln(s->fp, c, "#   (use \"git checkout -- <file>...\" to discard changes in working directory)");
+       if (has_dirty_submodules)
+               color_fprintf_ln(s->fp, c, "#   (commit or discard the untracked or modified content in submodules)");
        color_fprintf_ln(s->fp, c, "#");
 }
 
@@ -144,6 +147,7 @@ static void wt_status_print_change_data(struct wt_status *s,
        char *two_name;
        const char *one, *two;
        struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
+       struct strbuf extra = STRBUF_INIT;
 
        one_name = two_name = it->string;
        switch (change_type) {
@@ -153,6 +157,17 @@ static void wt_status_print_change_data(struct wt_status *s,
                        one_name = d->head_path;
                break;
        case WT_STATUS_CHANGED:
+               if (d->new_submodule_commits || d->dirty_submodule) {
+                       strbuf_addstr(&extra, " (");
+                       if (d->new_submodule_commits)
+                               strbuf_addf(&extra, "new commits, ");
+                       if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
+                               strbuf_addf(&extra, "modified content, ");
+                       if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
+                               strbuf_addf(&extra, "untracked content, ");
+                       strbuf_setlen(&extra, extra.len - 2);
+                       strbuf_addch(&extra, ')');
+               }
                status = d->worktree_status;
                break;
        }
@@ -189,6 +204,10 @@ static void wt_status_print_change_data(struct wt_status *s,
        default:
                die("bug: unhandled diff status %c", status);
        }
+       if (extra.len) {
+               color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "%s", extra.buf);
+               strbuf_release(&extra);
+       }
        fprintf(s->fp, "\n");
        strbuf_release(&onebuf);
        strbuf_release(&twobuf);
@@ -218,6 +237,9 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
                }
                if (!d->worktree_status)
                        d->worktree_status = p->status;
+               d->dirty_submodule = p->two->dirty_submodule;
+               if (S_ISGITLINK(p->two->mode))
+                       d->new_submodule_commits = !!hashcmp(p->one->sha1, p->two->sha1);
        }
 }
 
@@ -281,6 +303,9 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
        init_revisions(&rev, NULL);
        setup_revisions(0, NULL, &rev, NULL);
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+       DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
+       if (!s->show_untracked_files)
+               DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
        rev.diffopt.format_callback = wt_status_collect_changed_cb;
        rev.diffopt.format_callback_data = s;
        rev.prune_data = s->pathspec;
@@ -290,10 +315,13 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 static void wt_status_collect_changes_index(struct wt_status *s)
 {
        struct rev_info rev;
+       struct setup_revision_opt opt;
 
        init_revisions(&rev, NULL);
-       setup_revisions(0, NULL, &rev,
-               s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
+       memset(&opt, 0, sizeof(opt));
+       opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
+       setup_revisions(0, NULL, &rev, &opt);
+
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = wt_status_collect_updated_cb;
        rev.diffopt.format_callback_data = s;
@@ -418,33 +446,39 @@ static void wt_status_print_updated(struct wt_status *s)
  *  0 : no change
  *  1 : some change but no delete
  */
-static int wt_status_check_worktree_changes(struct wt_status *s)
+static int wt_status_check_worktree_changes(struct wt_status *s,
+                                            int *dirty_submodules)
 {
        int i;
        int changes = 0;
 
+       *dirty_submodules = 0;
+
        for (i = 0; i < s->change.nr; i++) {
                struct wt_status_change_data *d;
                d = s->change.items[i].util;
                if (!d->worktree_status ||
                    d->worktree_status == DIFF_STATUS_UNMERGED)
                        continue;
-               changes = 1;
+               if (!changes)
+                       changes = 1;
+               if (d->dirty_submodule)
+                       *dirty_submodules = 1;
                if (d->worktree_status == DIFF_STATUS_DELETED)
-                       return -1;
+                       changes = -1;
        }
        return changes;
 }
 
 static void wt_status_print_changed(struct wt_status *s)
 {
-       int i;
-       int worktree_changes = wt_status_check_worktree_changes(s);
+       int i, dirty_submodules;
+       int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules);
 
        if (!worktree_changes)
                return;
 
-       wt_status_print_dirty_header(s, worktree_changes < 0);
+       wt_status_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
 
        for (i = 0; i < s->change.nr; i++) {
                struct wt_status_change_data *d;
@@ -512,11 +546,15 @@ static void wt_status_print_untracked(struct wt_status *s)
 static void wt_status_print_verbose(struct wt_status *s)
 {
        struct rev_info rev;
+       struct setup_revision_opt opt;
 
        init_revisions(&rev, NULL);
        DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
-       setup_revisions(0, NULL, &rev,
-               s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
+
+       memset(&opt, 0, sizeof(opt));
+       opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
+       setup_revisions(0, NULL, &rev, &opt);
+
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
        rev.diffopt.detect_rename = 1;
        rev.diffopt.file = s->fp;
index c60f40a34a89ccdacdc864203d4441cf3e20bcc5..91206739f318233811dbd594d5ff37aa77c06eef 100644 (file)
@@ -25,6 +25,8 @@ struct wt_status_change_data {
        int index_status;
        int stagemask;
        char *head_path;
+       unsigned dirty_submodule       : 2;
+       unsigned new_submodule_commits : 1;
 };
 
 struct wt_status {
index 01f14fb50f7cf1387898a0c8db44f966ce07b720..ca5e3fbae8184e7114413ec65fe815e01ad6b2a8 100644 (file)
@@ -218,6 +218,23 @@ int read_mmfile(mmfile_t *ptr, const char *filename)
        return 0;
 }
 
+void read_mmblob(mmfile_t *ptr, const unsigned char *sha1)
+{
+       unsigned long size;
+       enum object_type type;
+
+       if (!hashcmp(sha1, null_sha1)) {
+               ptr->ptr = xstrdup("");
+               ptr->size = 0;
+               return;
+       }
+
+       ptr->ptr = read_sha1_file(sha1, &type, &size);
+       if (!ptr->ptr || type != OBJ_BLOB)
+               die("unable to read blob object %s", sha1_to_hex(sha1));
+       ptr->size = size;
+}
+
 #define FIRST_FEW_BYTES 8000
 int buffer_is_binary(const char *ptr, unsigned long size)
 {
index 55572c39a10dee336355f816b324946fc087d6e7..abba70c16bb31fae0df999241830d0c8df8bfbb3 100644 (file)
@@ -18,6 +18,7 @@ int parse_hunk_header(char *line, int len,
                      int *ob, int *on,
                      int *nb, int *nn);
 int read_mmfile(mmfile_t *ptr, const char *filename);
+void read_mmblob(mmfile_t *ptr, const unsigned char *sha1);
 int buffer_is_binary(const char *ptr, unsigned long size);
 
 extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
index 3f6229edbeb21bb1ca0c423d88390f3b5a05b5a2..a71763ade835fd93ef291106298647408ada08e9 100644 (file)
@@ -56,17 +56,14 @@ extern "C" {
 #define XDL_MERGE_EAGER 1
 #define XDL_MERGE_ZEALOUS 2
 #define XDL_MERGE_ZEALOUS_ALNUM 3
-#define XDL_MERGE_LEVEL_MASK 0x0f
 
 /* merge favor modes */
 #define XDL_MERGE_FAVOR_OURS 1
 #define XDL_MERGE_FAVOR_THEIRS 2
-#define XDL_MERGE_FAVOR(flags) (((flags)>>4) & 3)
-#define XDL_MERGE_FLAGS(level, style, favor) ((level)|(style)|((favor)<<4))
+#define XDL_MERGE_FAVOR_UNION 3
 
 /* merge output styles */
-#define XDL_MERGE_DIFF3 0x8000
-#define XDL_MERGE_STYLE_MASK 0x8000
+#define XDL_MERGE_DIFF3 1
 
 typedef struct s_mmfile {
        char *ptr;
@@ -117,13 +114,16 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 typedef struct s_xmparam {
        xpparam_t xpp;
        int marker_size;
+       int level;
+       int favor;
+       int style;
 } xmparam_t;
 
 #define DEFAULT_CONFLICT_MARKER_SIZE 7
 
 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
                mmfile_t *mf2, const char *name2,
-               xmparam_t const *xmp, int flags, mmbuffer_t *result);
+               xmparam_t const *xmp, mmbuffer_t *result);
 
 #ifdef __cplusplus
 }
index 8cbe45e6755487dbe3759398375a11d05f6d91bc..87cafa7021468d00a11c72dfdc1df05f1ede0fea 100644 (file)
@@ -28,6 +28,7 @@ typedef struct s_xdmerge {
         * 0 = conflict,
         * 1 = no conflict, take first,
         * 2 = no conflict, take second.
+        * 3 = no conflict, take both.
         */
        int mode;
        /*
@@ -230,14 +231,19 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
                        size = fill_conflict_hunk(xe1, name1, xe2, name2,
                                                  size, i, style, m, dest,
                                                  marker_size);
-               else if (m->mode == 1)
-                       size += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0,
+               else if (m->mode & 3) {
+                       /* Before conflicting part */
+                       size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
                                              dest ? dest + size : NULL);
-               else if (m->mode == 2)
-                       size += xdl_recs_copy(xe2, m->i2 - m->i1 + i,
-                                             m->i1 + m->chg2 - i, 0,
-                                             dest ? dest + size : NULL);
-               else
+                       /* Postimage from side #1 */
+                       if (m->mode & 1)
+                               size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+                                                     dest ? dest + size : NULL);
+                       /* Postimage from side #2 */
+                       if (m->mode & 2)
+                               size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+                                                     dest ? dest + size : NULL);
+               } else
                        continue;
                i = m->i1 + m->chg1;
        }
@@ -394,13 +400,13 @@ static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
  */
 static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
                xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
-               int flags, xmparam_t const *xmp, mmbuffer_t *result) {
+               xmparam_t const *xmp, mmbuffer_t *result) {
        xdmerge_t *changes, *c;
        xpparam_t const *xpp = &xmp->xpp;
        int i0, i1, i2, chg0, chg1, chg2;
-       int level = flags & XDL_MERGE_LEVEL_MASK;
-       int style = flags & XDL_MERGE_STYLE_MASK;
-       int favor = XDL_MERGE_FAVOR(flags);
+       int level = xmp->level;
+       int style = xmp->style;
+       int favor = xmp->favor;
 
        if (style == XDL_MERGE_DIFF3) {
                /*
@@ -550,7 +556,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 
 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
                mmfile_t *mf2, const char *name2,
-               xmparam_t const *xmp, int flags, mmbuffer_t *result) {
+               xmparam_t const *xmp, mmbuffer_t *result) {
        xdchange_t *xscr1, *xscr2;
        xdfenv_t xe1, xe2;
        int status;
@@ -587,7 +593,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
        } else {
                status = xdl_do_merge(&xe1, xscr1, name1,
                                      &xe2, xscr2, name2,
-                                     flags, xmp, result);
+                                     xmp, result);
        }
        xdl_free_script(xscr1);
        xdl_free_script(xscr2);