]> rtime.felk.cvut.cz Git - git.git/commitdiff
Sync with 1.7.0.4
authorJunio C Hamano <gitster@pobox.com>
Wed, 31 Mar 2010 22:14:27 +0000 (15:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 31 Mar 2010 22:14:27 +0000 (15:14 -0700)
Signed-off-by: Junio C Hamano <gitster@pobox.com>
241 files changed:
.gitignore
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-cherry-pick.txt
Documentation/git-clone.txt
Documentation/git-cvsimport.txt
Documentation/git-format-patch.txt
Documentation/git-grep.txt
Documentation/git-hash-object.txt
Documentation/git-imap-send.txt
Documentation/git-init.txt
Documentation/git-log.txt
Documentation/git-mailsplit.txt
Documentation/git-merge-file.txt
Documentation/git-notes.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-reset.txt
Documentation/git-show-branch.txt
Documentation/githooks.txt
Documentation/merge-options.txt
Documentation/pretty-options.txt
Documentation/rev-list-options.txt
Documentation/technical/api-parse-options.txt
Makefile
RelNotes
abspath.c
builtin.h
builtin/add.c [moved from builtin-add.c with 100% similarity]
builtin/annotate.c [moved from builtin-annotate.c with 100% similarity]
builtin/apply.c [moved from builtin-apply.c with 100% 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 100% 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 100% 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 97% 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 100% similarity]
builtin/fetch.c [moved from builtin-fetch.c with 97% 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 92% 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 100% 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 99% 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 100% similarity]
builtin/push.c [moved from builtin-push.c with 93% 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 100% similarity]
builtin/reflog.c [moved from builtin-reflog.c with 100% 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 100% similarity]
builtin/rev-parse.c [moved from builtin-rev-parse.c with 99% similarity]
builtin/revert.c [moved from builtin-revert.c with 90% similarity]
builtin/rm.c [moved from builtin-rm.c with 100% similarity]
builtin/send-pack.c [moved from builtin-send-pack.c with 71% similarity]
builtin/shortlog.c [moved from builtin-shortlog.c with 99% similarity]
builtin/show-branch.c [moved from builtin-show-branch.c with 99% 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 100% 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
connect.c
contrib/examples/git-notes.sh [moved from git-notes.sh with 100% similarity]
contrib/fast-import/git-p4
diff-lib.c
diff.c
diff.h
diffcore.h
exec_cmd.c
fast-import.c
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-submodule.sh
git-svn.perl
git.c
gitweb/gitweb.perl
graph.c
grep.c
grep.h
http-fetch.c
http-push.c
http-walker.c
http.c
imap-send.c
ll-merge.c
log-tree.c
merge-recursive.c
notes.c
notes.h
pack-write.c
pack.h
parse-options.c
parse-options.h
path.c
perl/Git.pm
pretty.c
refs.c
refs.h
remote-curl.c
revision.c
revision.h
send-pack.h
setup.c
sha1_file.c
submodule.c
submodule.h
t/lib-httpd.sh
t/t0001-init.sh
t/t1007-hash-object.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/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/t3506-cherry-pick-ff.sh [new file with mode: 0755]
t/t3800-mktag.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/t4041-diff-submodule.sh
t/t4103-apply-binary.sh
t/t4200-rerere.sh
t/t4253-am-keep-cr-dos.sh [new file with mode: 0755]
t/t5407-post-rewrite-hook.sh [new file with mode: 0755]
t/t5510-fetch.sh
t/t5516-fetch-push.sh
t/t5540-http-push.sh
t/t5541-http-push.sh
t/t6023-merge-file.sh
t/t7002-grep.sh
t/t7103-reset-bare.sh
t/t7110-reset-merge.sh
t/t7111-reset-table.sh
t/t7401-submodule-summary.sh
t/t7501-commit.sh
t/t7506-status-submodule.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/t9501-gitweb-standalone-http-status.sh
t/t9600-cvsimport.sh
t/test-lib.sh
transport-helper.c
transport.c
transport.h
walker.h
wrap-for-bin.sh
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
diff --git a/Documentation/RelNotes-1.7.1.txt b/Documentation/RelNotes-1.7.1.txt
new file mode 100644 (file)
index 0000000..19aeef5
--- /dev/null
@@ -0,0 +1,71 @@
+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.
+
+ * "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 diff --submodule" notices and descries dirty submodules.
+
+ * "git for-each-ref" learned %(symref), %(symref:short) and %(flag)
+   tokens.
+
+ * "git hash-object --stdin-paths" can take "--no-filters" option now.
+
+ * "git init" can be told to look at init.templatedir configuration
+   variable (obviously that has to come from either /etc/gitconfig or
+   $HOME/.gitconfig).
+
+ * "git grep" learned "--no-index" option, to search inside contents that
+   are not managed by git.
+
+ * "git grep" learned --color=auto/always/never.
+
+ * "git grep" learned to paint filename and line-number in colors.
+
+ * "git log -p --first-parent -m" shows one-parent diff for merge
+   commits, instead of showing combined diff.
+
+ * "git merge-file" learned to use custom conflict marker size and also use
+   the "union merge" behaviour.
+
+ * "git notes" command has been rewritten in C and learned quite a
+   many commands and features to help you carry notes forward across
+   rebases and amends.
+
+ * "git request-pull" identifies the commit the request is relative to in
+   a more readable way.
+
+ * "git reset" learned "--keep" option that lets you discard commits
+   near the tip while preserving your local changes in a way similar
+   to how "git checkout branch" does.
+
+ * "git status" notices and descries dirty submodules.
+
+ * "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.3-310-g99f5b08
+git shortlog --no-merges ^maint $O..
index 437b4ac5eebb9a7eef600f2fd92ff1103c26e816..06b2f827b414651bde25165b4f42dc4160892d7a 100644 (file)
@@ -519,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.
 +
@@ -555,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'
@@ -683,9 +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>.
+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
@@ -1203,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].
@@ -1303,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.
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 23864df8da0694a0daefb81fe629e22a1deb06f1..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,6 +39,13 @@ 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
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 78f4714da0c226b9523f60f247f60ab13119c7c7..d71607a85d03062c76f7d17d4fd8e8b4168a279d 100644 (file)
@@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit
 
 SYNOPSIS
 --------
-'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] <commit>
+'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>
 
 DESCRIPTION
 -----------
@@ -70,6 +70,10 @@ effect to your index in a row.
 --signoff::
        Add Signed-off-by line at the end of the commit message.
 
+--ff::
+       If the current HEAD is the same as the parent of the
+       cherry-pick'ed commit, then a fast forward to this commit will
+       be performed.
 
 Author
 ------
index d15cb17d78784760097fc5137e526d12d10e90e2..dc7d3d17b151d05827925fdcd2806945e2e278cb 100644 (file)
@@ -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
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 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 ee506e67f09034591b4cd0215f72f61ff607d128..4b32322a67d9f3de8b555d3d9d03e7ddab14e0a7 100644 (file)
@@ -17,7 +17,7 @@ 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>...]
@@ -114,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`
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 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 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 49b6bd9d925f9150a4aaf2f2c4d7439503863d05..59dc8b197ecb4b6912c9ec29f83f21c405ef8634 100644 (file)
@@ -146,14 +146,21 @@ useful if you write an alias or script around 'git push'.
        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 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 b9c4154e7332fa59b370ce5be823a9cb6384227c..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>]...
@@ -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.
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 81c0e6f18498ae72e52e7c7ac0b55429143fabf1..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.
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 b07cd8a68146047ed3e7a5a17a420ad0ff5576f8..8a0f5c4d5b9a7c6c3617708014417b918c0ef034 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,16 @@ 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
@@ -332,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
@@ -366,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
@@ -431,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
@@ -442,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
@@ -484,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
@@ -598,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 =
@@ -1028,6 +1061,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)|'
@@ -1083,11 +1124,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
@@ -1107,7 +1149,7 @@ endif
 EXTLIBS += -lz
 
 ifndef NO_POSIX_ONLY_PROGRAMS
-       PROGRAMS += git-daemon$X
+       PROGRAM_OBJS += daemon.o
 endif
 ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
@@ -1273,10 +1315,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)
@@ -1418,7 +1462,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
@@ -1445,15 +1489,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)"'
@@ -1469,17 +1513,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
 
@@ -1589,28 +1641,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)
@@ -1620,10 +1792,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)
@@ -1641,18 +1809,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)
 
@@ -1718,24 +1877,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)
@@ -1753,6 +1894,8 @@ bin-wrappers/%: wrap-for-bin.sh
 
 export NO_SVN_TESTS
 
+### Testing rules
+
 test: all
        $(MAKE) -C t/ all
 
@@ -1764,9 +1907,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)
@@ -1812,6 +1953,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
@@ -1930,10 +2072,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
@@ -1963,12 +2106,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" || \
@@ -2006,9 +2150,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 9148d7c5c2d201bf9ad28a3487be0aeb7075721e..00e77229ddadcb8a13b10b693a96bd76a4cb9f33 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.7.0.4.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 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 100%
rename from builtin-add.c
rename to builtin/add.c
similarity index 100%
rename from builtin-annotate.c
rename to builtin/annotate.c
similarity index 100%
rename from builtin-apply.c
rename to builtin/apply.c
similarity index 100%
rename from builtin-archive.c
rename to builtin/archive.c
similarity index 100%
rename from builtin-blame.c
rename to builtin/blame.c
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 100%
rename from builtin-cat-file.c
rename to builtin/cat-file.c
similarity index 100%
rename from builtin-check-attr.c
rename to builtin/check-attr.c
similarity index 97%
rename from builtin-checkout.c
rename to builtin/checkout.c
index c5ab7835e1fe260b9ec61f216bf198011eabf6f7..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);
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 f4c73442cfba9483a826dcfbf68f5466d43e8351..8dd104ee0b247f5536d02d54a932acfcbaec603b 100644 (file)
@@ -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 */
@@ -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 100%
rename from builtin-fetch-pack.c
rename to builtin/fetch-pack.c
similarity index 97%
rename from builtin-fetch.c
rename to builtin/fetch.c
index bbc425b655e456d57e37118690a088351162ff3c..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()
@@ -203,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,
@@ -222,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;
@@ -237,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;
        }
@@ -247,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;
        }
@@ -269,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;
        }
@@ -282,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) {
@@ -293,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;
        }
@@ -392,7 +394,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                        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) {
@@ -513,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);
                }
@@ -844,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 92%
rename from builtin-grep.c
rename to builtin/grep.c
index 371db0aa9e6572c5b25ea679a510bc8784a25759..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;
 }
 
 /*
@@ -652,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)
 {
@@ -746,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"),
@@ -789,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",
@@ -831,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.
@@ -848,7 +886,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        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)
@@ -869,6 +913,10 @@ 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
@@ -940,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 76962e1b08ec6a7e52320c97cc3b54887cc67ef9..542ecc708bdb830bcdbbd48e3fb3d21021119630 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);
 
@@ -1230,6 +1281,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 100%
rename from builtin-mailinfo.c
rename to builtin/mailinfo.c
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 99%
rename from builtin-merge.c
rename to builtin/merge.c
index 3aaec7bed76af9efdfe5647be0da64373db2011e..c043066845e03e47093f88ca0b01c7bdef7bffdd 100644 (file)
@@ -667,7 +667,7 @@ static int count_unmerged_entries(void)
        return ret;
 }
 
-static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
+int checkout_fast_forward(const unsigned char *head, const unsigned char *remote)
 {
        struct tree *trees[MAX_UNPACK_TREES];
        struct unpack_trees_options opts;
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..52b72fc
--- /dev/null
@@ -0,0 +1,862 @@
+/*
+ * 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 [--ref <notes_ref>] [list [<object>]]",
+       "git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
+       "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>",
+       "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
+       "git notes [--ref <notes_ref>] edit [<object>]",
+       "git notes [--ref <notes_ref>] show [<object>]",
+       "git notes [--ref <notes_ref>] remove [<object>]",
+       "git notes [--ref <notes_ref>] prune",
+       NULL
+};
+
+static const char * const git_notes_list_usage[] = {
+       "git notes [list [<object>]]",
+       NULL
+};
+
+static const char * const git_notes_add_usage[] = {
+       "git notes add [<options>] [<object>]",
+       NULL
+};
+
+static const char * const git_notes_copy_usage[] = {
+       "git notes copy [<options>] <from-object> <to-object>",
+       "git notes copy --stdin [<from-object> <to-object>]...",
+       NULL
+};
+
+static const char * const git_notes_append_usage[] = {
+       "git notes append [<options>] [<object>]",
+       NULL
+};
+
+static const char * const git_notes_edit_usage[] = {
+       "git notes edit [<object>]",
+       NULL
+};
+
+static const char * const git_notes_show_usage[] = {
+       "git notes show [<object>]",
+       NULL
+};
+
+static const char * const git_notes_remove_usage[] = {
+       "git notes remove [<object>]",
+       NULL
+};
+
+static const char * const git_notes_prune_usage[] = {
+       "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;
+}
+
+static struct notes_tree *init_notes_check(const char *subcommand)
+{
+       struct notes_tree *t;
+       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/)",
+                   subcommand, t->ref);
+       return t;
+}
+
+static int list(int argc, const char **argv, const char *prefix)
+{
+       struct notes_tree *t;
+       unsigned char object[20];
+       const unsigned char *note;
+       int retval = -1;
+       struct option options[] = {
+               OPT_END()
+       };
+
+       if (argc)
+               argc = parse_options(argc, argv, prefix, options,
+                                    git_notes_list_usage, 0);
+
+       if (1 < argc) {
+               error("too many parameters");
+               usage_with_options(git_notes_list_usage, options);
+       }
+
+       t = init_notes_check("list");
+       if (argc) {
+               if (get_sha1(argv[0], object))
+                       die("Failed to resolve '%s' as a valid ref.", argv[0]);
+               note = get_note(t, object);
+               if (note) {
+                       puts(sha1_to_hex(note));
+                       retval = 0;
+               } else
+                       retval = error("No note found for object %s.",
+                                      sha1_to_hex(object));
+       } else
+               retval = for_each_note(t, 0, list_each_note, NULL);
+
+       free_notes(t);
+       return retval;
+}
+
+static int add(int argc, const char **argv, const char *prefix)
+{
+       int retval = 0, force = 0;
+       const char *object_ref;
+       struct notes_tree *t;
+       unsigned char object[20], new_note[20];
+       char logmsg[100];
+       const unsigned char *note;
+       struct msg_arg msg = { 0, 0, STRBUF_INIT };
+       struct option 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_BOOLEAN('f', "force", &force, "replace existing notes"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options, git_notes_add_usage,
+                            0);
+
+       if (1 < argc) {
+               error("too many parameters");
+               usage_with_options(git_notes_add_usage, options);
+       }
+
+       object_ref = argc ? argv[0] : "HEAD";
+
+       if (get_sha1(object_ref, object))
+               die("Failed to resolve '%s' as a valid ref.", object_ref);
+
+       t = init_notes_check("add");
+       note = get_note(t, object);
+
+       if (note) {
+               if (!force) {
+                       retval = error("Cannot add notes. Found existing notes "
+                                      "for object %s. Use '-f' to overwrite "
+                                      "existing notes", sha1_to_hex(object));
+                       goto out;
+               }
+               fprintf(stderr, "Overwriting existing notes for object %s\n",
+                       sha1_to_hex(object));
+       }
+
+       create_note(object, &msg, 0, note, new_note);
+
+       if (is_null_sha1(new_note))
+               remove_note(t, object);
+       else
+               add_note(t, object, new_note, combine_notes_overwrite);
+
+       snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
+                is_null_sha1(new_note) ? "removed" : "added", "add");
+       commit_notes(t, logmsg);
+out:
+       free_notes(t);
+       strbuf_release(&(msg.buf));
+       return retval;
+}
+
+static int copy(int argc, const char **argv, const char *prefix)
+{
+       int retval = 0, force = 0, from_stdin = 0;
+       const unsigned char *from_note, *note;
+       const char *object_ref;
+       unsigned char object[20], from_obj[20];
+       struct notes_tree *t;
+       const char *rewrite_cmd = NULL;
+       struct option options[] = {
+               OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
+               OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
+               OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command",
+                          "load rewriting config for <command> (implies "
+                          "--stdin)"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options, git_notes_copy_usage,
+                            0);
+
+       if (from_stdin || rewrite_cmd) {
+               if (argc) {
+                       error("too many parameters");
+                       usage_with_options(git_notes_copy_usage, options);
+               } else {
+                       return notes_copy_from_stdin(force, rewrite_cmd);
+               }
+       }
+
+       if (2 < argc) {
+               error("too many parameters");
+               usage_with_options(git_notes_copy_usage, options);
+       }
+
+       if (get_sha1(argv[0], from_obj))
+               die("Failed to resolve '%s' as a valid ref.", argv[0]);
+
+       object_ref = 1 < argc ? argv[1] : "HEAD";
+
+       if (get_sha1(object_ref, object))
+               die("Failed to resolve '%s' as a valid ref.", object_ref);
+
+       t = init_notes_check("copy");
+       note = get_note(t, object);
+
+       if (note) {
+               if (!force) {
+                       retval = error("Cannot copy notes. Found existing "
+                                      "notes for object %s. Use '-f' to "
+                                      "overwrite existing notes",
+                                      sha1_to_hex(object));
+                       goto out;
+               }
+               fprintf(stderr, "Overwriting existing notes for object %s\n",
+                       sha1_to_hex(object));
+       }
+
+       from_note = get_note(t, from_obj);
+       if (!from_note) {
+               retval = error("Missing notes on source object %s. Cannot "
+                              "copy.", sha1_to_hex(from_obj));
+               goto out;
+       }
+
+       add_note(t, object, from_note, combine_notes_overwrite);
+       commit_notes(t, "Notes added by 'git notes copy'");
+out:
+       free_notes(t);
+       return retval;
+}
+
+static int append_edit(int argc, const char **argv, const char *prefix)
+{
+       const char *object_ref;
+       struct notes_tree *t;
+       unsigned char object[20], new_note[20];
+       const unsigned char *note;
+       char logmsg[100];
+       const char * const *usage;
+       struct msg_arg msg = { 0, 0, STRBUF_INIT };
+       struct option 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_END()
+       };
+       int edit = !strcmp(argv[0], "edit");
+
+       usage = edit ? git_notes_edit_usage : git_notes_append_usage;
+       argc = parse_options(argc, argv, prefix, options, usage,
+                            PARSE_OPT_KEEP_ARGV0);
+
+       if (2 < argc) {
+               error("too many parameters");
+               usage_with_options(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");
+
+       object_ref = 1 < argc ? argv[1] : "HEAD";
+
+       if (get_sha1(object_ref, object))
+               die("Failed to resolve '%s' as a valid ref.", object_ref);
+
+       t = init_notes_check(argv[0]);
+       note = get_note(t, object);
+
+       create_note(object, &msg, !edit, note, new_note);
+
+       if (is_null_sha1(new_note))
+               remove_note(t, object);
+       else
+               add_note(t, object, new_note, combine_notes_overwrite);
+
+       snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
+                is_null_sha1(new_note) ? "removed" : "added", argv[0]);
+       commit_notes(t, logmsg);
+       free_notes(t);
+       strbuf_release(&(msg.buf));
+       return 0;
+}
+
+static int show(int argc, const char **argv, const char *prefix)
+{
+       const char *object_ref;
+       struct notes_tree *t;
+       unsigned char object[20];
+       const unsigned char *note;
+       int retval;
+       struct option options[] = {
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options, git_notes_show_usage,
+                            0);
+
+       if (1 < argc) {
+               error("too many parameters");
+               usage_with_options(git_notes_show_usage, options);
+       }
+
+       object_ref = argc ? argv[0] : "HEAD";
+
+       if (get_sha1(object_ref, object))
+               die("Failed to resolve '%s' as a valid ref.", object_ref);
+
+       t = init_notes_check("show");
+       note = get_note(t, object);
+
+       if (!note)
+               retval = error("No note found for object %s.",
+                              sha1_to_hex(object));
+       else {
+               const char *show_args[3] = {"show", sha1_to_hex(note), NULL};
+               retval = execv_git_cmd(show_args);
+       }
+       free_notes(t);
+       return retval;
+}
+
+static int remove_cmd(int argc, const char **argv, const char *prefix)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+       const char *object_ref;
+       struct notes_tree *t;
+       unsigned char object[20];
+
+       argc = parse_options(argc, argv, prefix, options,
+                            git_notes_remove_usage, 0);
+
+       if (1 < argc) {
+               error("too many parameters");
+               usage_with_options(git_notes_remove_usage, options);
+       }
+
+       object_ref = argc ? argv[0] : "HEAD";
+
+       if (get_sha1(object_ref, object))
+               die("Failed to resolve '%s' as a valid ref.", object_ref);
+
+       t = init_notes_check("remove");
+
+       fprintf(stderr, "Removing note for object %s\n", sha1_to_hex(object));
+       remove_note(t, object);
+
+       commit_notes(t, "Notes removed by 'git notes remove'");
+       free_notes(t);
+       return 0;
+}
+
+static int prune(int argc, const char **argv, const char *prefix)
+{
+       struct notes_tree *t;
+       struct option options[] = {
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options, git_notes_prune_usage,
+                            0);
+
+       if (argc) {
+               error("too many parameters");
+               usage_with_options(git_notes_prune_usage, options);
+       }
+
+       t = init_notes_check("prune");
+
+       prune_notes(t);
+       commit_notes(t, "Notes removed by 'git notes prune'");
+       free_notes(t);
+       return 0;
+}
+
+int cmd_notes(int argc, const char **argv, const char *prefix)
+{
+       int result;
+       const char *override_notes_ref = NULL;
+       struct option options[] = {
+               OPT_STRING(0, "ref", &override_notes_ref, "notes_ref",
+                          "use notes from <notes_ref>"),
+               OPT_END()
+       };
+
+       git_config(git_default_config, NULL);
+       argc = parse_options(argc, argv, prefix, options, git_notes_usage,
+                            PARSE_OPT_STOP_AT_NON_OPTION);
+
+       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 < 1 || !strcmp(argv[0], "list"))
+               result = list(argc, argv, prefix);
+       else if (!strcmp(argv[0], "add"))
+               result = add(argc, argv, prefix);
+       else if (!strcmp(argv[0], "copy"))
+               result = copy(argc, argv, prefix);
+       else if (!strcmp(argv[0], "append") || !strcmp(argv[0], "edit"))
+               result = append_edit(argc, argv, prefix);
+       else if (!strcmp(argv[0], "show"))
+               result = show(argc, argv, prefix);
+       else if (!strcmp(argv[0], "remove"))
+               result = remove_cmd(argc, argv, prefix);
+       else if (!strcmp(argv[0], "prune"))
+               result = prune(argc, argv, prefix);
+       else {
+               result = error("Unknown subcommand: %s", argv[0]);
+               usage_with_options(git_notes_usage, options);
+       }
+
+       return result ? 1 : 0;
+}
similarity index 98%
rename from builtin-pack-objects.c
rename to builtin/pack-objects.c
index 539e75d56f7a33fdb1971b51ee0681c43e9662b0..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);
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 100%
rename from builtin-prune.c
rename to builtin/prune.c
similarity index 93%
rename from builtin-push.c
rename to builtin/push.c
index f7bc2b292fb85725d9cc26ce09f2302aaa7167fe..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;
@@ -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 100%
rename from builtin-reflog.c
rename to builtin/reflog.c
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 a174a316102c6ed40b4cef24d7812220f6ac5d89..1283068fd24c371e39f0271d01dd6b3aac437d4b 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 [-q] <commit> [--] <paths>...",
        "git reset --patch [<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)
 {
@@ -72,6 +74,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;
@@ -86,6 +89,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))
@@ -212,6 +225,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;
@@ -230,6 +251,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()
        };
@@ -305,7 +328,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())
@@ -315,12 +338,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 100%
rename from builtin-rev-list.c
rename to builtin/rev-list.c
similarity index 99%
rename from builtin-rev-parse.c
rename to builtin/rev-parse.c
index b76f205e62f29a21fc4b0fedb4a981488ec7cb7f..8fbf9d0db6f40aa8c7cb61d72d0f44446de46826 100644 (file)
@@ -644,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;
@@ -654,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 90%
rename from builtin-revert.c
rename to builtin/revert.c
index eff52687a87b45b766e064eede7c2c35b6d133d2..9a3c14c329713f16d14147b81a4b8031028a4f82 100644 (file)
@@ -13,6 +13,7 @@
 #include "revision.h"
 #include "rerere.h"
 #include "merge-recursive.h"
+#include "refs.h"
 
 /*
  * This implements the builtins revert and cherry-pick.
@@ -35,7 +36,7 @@ static const char * const cherry_pick_usage[] = {
        NULL
 };
 
-static int edit, no_replay, no_commit, mainline, signoff;
+static int edit, no_replay, no_commit, mainline, signoff, allow_ff;
 static enum { REVERT, CHERRY_PICK } action;
 static struct commit *commit;
 static const char *commit_name;
@@ -60,8 +61,19 @@ static void parse_args(int argc, const char **argv)
                OPT_INTEGER('m', "mainline", &mainline, "parent number"),
                OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
                OPT_END(),
+               OPT_END(),
+               OPT_END(),
        };
 
+       if (action == CHERRY_PICK) {
+               struct option cp_extra[] = {
+                       OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"),
+                       OPT_END(),
+               };
+               if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
+                       die("program error");
+       }
+
        if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1)
                usage_with_options(usage_str, options);
 
@@ -244,6 +256,17 @@ static NORETURN void die_dirty_index(const char *me)
        }
 }
 
+static int fast_forward_to(const unsigned char *to, const unsigned char *from)
+{
+       struct ref_lock *ref_lock;
+
+       read_cache();
+       if (checkout_fast_forward(from, to))
+               exit(1); /* the callee should have complained already */
+       ref_lock = lock_any_ref_for_update("HEAD", from, 0);
+       return write_ref_sha1(ref_lock, to, "cherry-pick");
+}
+
 static int revert_or_cherry_pick(int argc, const char **argv)
 {
        unsigned char head[20];
@@ -251,7 +274,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        int i, index_fd, clean;
        char *oneline, *reencoded_message = NULL;
        const char *message, *encoding;
-       char *defmsg = git_pathdup("MERGE_MSG");
+       char *defmsg = NULL;
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
        static struct lock_file index_lock;
@@ -265,6 +288,17 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        if (action == REVERT && !no_replay)
                die("revert is incompatible with replay");
 
+       if (allow_ff) {
+               if (signoff)
+                       die("cherry-pick --ff cannot be used with --signoff");
+               if (no_commit)
+                       die("cherry-pick --ff cannot be used with --no-commit");
+               if (no_replay)
+                       die("cherry-pick --ff cannot be used with -x");
+               if (edit)
+                       die("cherry-pick --ff cannot be used with --edit");
+       }
+
        if (read_cache() < 0)
                die("git %s: failed to read the index", me);
        if (no_commit) {
@@ -284,8 +318,6 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        }
        discard_cache();
 
-       index_fd = hold_locked_index(&index_lock, 1);
-
        if (!commit->parents) {
                if (action == REVERT)
                        die ("Cannot revert a root commit");
@@ -314,6 +346,9 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        else
                parent = commit->parents->item;
 
+       if (allow_ff && !hashcmp(parent->object.sha1, head))
+               return fast_forward_to(commit->object.sha1, head);
+
        if (!(message = commit->buffer))
                die ("Cannot get commit message for %s",
                                sha1_to_hex(commit->object.sha1));
@@ -329,6 +364,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
         * reverse of it if we are revert.
         */
 
+       defmsg = git_pathdup("MERGE_MSG");
        msg_fd = hold_lock_file_for_update(&msg_file, defmsg,
                                           LOCK_DIE_ON_ERROR);
 
@@ -343,6 +379,8 @@ static int revert_or_cherry_pick(int argc, const char **argv)
 
        oneline = get_oneline(message);
 
+       index_fd = hold_locked_index(&index_lock, 1);
+
        if (action == REVERT) {
                char *oneline_body = strchr(oneline, ' ');
 
similarity index 100%
rename from builtin-rm.c
rename to builtin/rm.c
similarity index 71%
rename from builtin-send-pack.c
rename to builtin/send-pack.c
index 2183a470524048eabef1b0f31499c5d04aec5850..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;
@@ -510,6 +361,10 @@ int send_pack(struct send_pack_args *args,
 
        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:
@@ -523,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;
@@ -570,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++) {
@@ -662,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();
 
@@ -691,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 99%
rename from builtin-shortlog.c
rename to builtin/shortlog.c
index ecd2d45a00b1aacf9d3a19e2f934a14ca7e58c35..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
similarity index 99%
rename from builtin-show-branch.c
rename to builtin/show-branch.c
index 35a709e63066ad8bebc8a96dee0563bad43348b6..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
 };
@@ -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 100%
rename from builtin-var.c
rename to builtin/var.c
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 6e54993256befed7b77fc54559ff196d01292ef2..6dcb100a6cfc1b1d311602507bc3dcb483c1558d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -387,6 +387,9 @@ 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
@@ -688,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 *);
@@ -890,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);
@@ -1053,4 +1058,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
 
+/* builtin/merge.c */
+int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
+
 #endif /* CACHE_H */
diff --git a/color.c b/color.c
index e8bcac0a79a27ee093f3d5a92013780cf52eb9da..bcf4e2c192c31136729aabcc2575a94f809d2980 100644 (file)
--- a/color.c
+++ b/color.c
@@ -146,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 bcb28cf10f2cbef11d9dba0e5a7f3f0515cbbd70..5c264b0ce3b95edb5f86cc003d2aca04033d098f 100644 (file)
--- a/color.h
+++ b/color.h
 #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 59b18dc7ca51180426a0dbf11e651a93c5eedc81..30716903f5c3072588ddaae71f557f14d7806fde 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 beaff987c7bc36096d09b2557f5208c5606ce2dd..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;
@@ -637,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;
+}
similarity index 100%
rename from git-notes.sh
rename to contrib/examples/git-notes.sh
index cd96c6f81f6e054c3c5cdb04f810204aaa594e49..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()
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 99059231b4e9d7eac9eb2d63ce6baa4d43190b95..2daa732a360b2e846791156be5ceb33b85b4eac9 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")) {
@@ -3077,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. */
@@ -3212,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 {
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" */
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 74f08bd554ae4cab1dc9ee38272376e5cbec1200..309f2c58a2ba431a82a56f757482a5093cc8257d 100644 (file)
@@ -980,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,
@@ -1103,7 +1080,7 @@ static int store_object(
                delta_count_by_type[type]++;
                e->depth = last->depth + 1;
 
-               hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr);
+               hdrlen = encode_in_pack_object_header(OBJ_OFS_DELTA, deltalen, hdr);
                sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
 
@@ -1114,7 +1091,7 @@ static int store_object(
                pack_size += sizeof(hdr) - pos;
        } else {
                e->depth = 0;
-               hdrlen = encode_header(type, dat->len, hdr);
+               hdrlen = encode_in_pack_object_header(type, dat->len, hdr);
                sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
        }
@@ -1188,7 +1165,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        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");
 
index 9df951a597d6b9ec5f7f390c35a60adb942a3fdc..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"
@@ -765,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
@@ -773,5 +797,12 @@ do
        go_next
 done
 
+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 246a3a4b373dba87877204c90c9f808dfa93c45c..1a4729f7bb29205fb7bc251887dcd4f5237f1659 100755 (executable)
@@ -38,7 +38,7 @@ test -z "$(git ls-files -u)" || die_conflict
 test -f "$GIT_DIR/MERGE_HEAD" && die_merge
 
 strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
-log_arg= verbosity=
+log_arg= verbosity= progress=
 merge_args=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short="${curr_branch#refs/heads/}"
@@ -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..1d116bfae2175891905d40ba4b55b331fba63372 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"
@@ -222,8 +230,8 @@ do_with_author () {
 }
 
 pick_one () {
-       no_ff=
-       case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
+       ff=--ff
+       case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
        output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
        test -d "$REWRITTEN" &&
                pick_one_preserving_merges "$@" && return
@@ -232,16 +240,7 @@ pick_one () {
                output git cherry-pick "$@"
                return
        fi
-       parent_sha1=$(git rev-parse --verify $sha1^) ||
-               die "Could not get the parent of $sha1"
-       current_sha1=$(git rev-parse --verify HEAD)
-       if test -z "$no_ff" && test "$current_sha1" = "$parent_sha1"
-       then
-               output git reset --hard $sha1
-               output warn Fast-forward to $(git rev-parse --short $sha1)
-       else
-               output git cherry-pick "$@"
-       fi
+       output git cherry-pick $ff "$@"
 }
 
 pick_one_preserving_merges () {
@@ -348,6 +347,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 +425,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 +458,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 +466,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 +475,7 @@ do_next () {
                mark_action_done
                pick_one $sha1 ||
                        die_with_patch $sha1 "Could not apply $sha1... $rest"
+               echo "$sha1" > "$DOTEST"/stopped-sha
                make_patch $sha1
                git rev-parse --verify HEAD > "$AMEND"
                warn "Stopped at $sha1... $rest"
@@ -509,6 +532,7 @@ do_next () {
                        rm -f "$SQUASH_MSG" "$FIXUP_MSG"
                        ;;
                esac
+               record_in_rewritten $sha1
                ;;
        *)
                warn "Unknown command: $command $sha1 $rest"
@@ -537,6 +561,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 +604,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"
 }
 
@@ -687,6 +725,8 @@ first and then run 'git rebase --continue' again."
                        }
                fi
 
+               record_in_rewritten "$(cat "$DOTEST"/stopped-sha)"
+
                require_clean_work_tree
                do_rest
                ;;
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 e2082fd1492fe34e69c59f0a46cbaf7ddd2e2a8e..2dd372a21d82a109774e80e014f9959485202b3e 100755 (executable)
@@ -553,12 +553,17 @@ cmd_summary() {
 
        test $summary_limit = 0 && return
 
-       if rev=$(git rev-parse -q --verify "$1^0")
+       if rev=$(git rev-parse -q --verify --default HEAD ${1+"$1"})
        then
                head=$rev
-               shift
+               test $# = 0 || shift
+       elif test -z "$1" -o "$1" = "HEAD"
+       then
+               # before the first commit: compare with an empty tree
+               head=$(git hash-object -w -t tree --stdin </dev/null)
+               test -z "$1" || shift
        else
-               head=HEAD
+               head="HEAD"
        fi
 
        if [ -n "$files" ]
index 473a0b9d556fc7ead7088c4cb6c6f14c4a70995b..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);
diff --git a/git.c b/git.c
index c445d7bcc256c68fe53df4886bd7b7af81640056..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 },
index 9d4c58238ea87963221d948020e928d232c0d90d..c356e95f18f0f784584ce644c08e1966ca1be52f 100755 (executable)
@@ -1150,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;
@@ -1162,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;
@@ -1170,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;
@@ -1181,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'}) {
@@ -1195,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'}) {
@@ -3381,7 +3388,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
@@ -3396,7 +3403,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 90a063a985098976f831c37227496d612fae37c0..fdc42062687fa6b8bb77c8d09ed58aee9107692d 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -304,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);
 }
 
@@ -544,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;
@@ -576,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);
 }
 
@@ -857,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;
                        }
@@ -916,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 d35bc29bfd76f27c066f40dcb3f31078b4100059..89342e5b47f6d63dd546e738e4cbf023c447b382 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -86,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;
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 5631930bc3462c5d85d29e7e840f1cf24cd7111b..7107923a39247b5c18ad443dc2309971e44abdb4 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 {
@@ -139,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 {
@@ -213,6 +230,7 @@ enum CAPABILITY {
        LITERALPLUS,
        NAMESPACE,
        STARTTLS,
+       AUTH_CRAM_MD5,
 };
 
 static const char *cap_list[] = {
@@ -221,6 +239,7 @@ static const char *cap_list[] = {
        "LITERAL+",
        "NAMESPACE",
        "STARTTLS",
+       "AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -948,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;
@@ -1107,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);
@@ -1126,12 +1226,37 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
                        fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
                        goto bail;
                }
-               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->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;
+                       }
                }
        } /* !preauth */
 
@@ -1348,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)
@@ -1399,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 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)
index d218122af5c2c0cddd857fce0ae5064bf32f6387..8546d8526f311e2a2703c258a4da3f02f8650df4 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)
 {
@@ -643,3 +659,18 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
        *target = unset ? 2 : 1;
        return 0;
 }
+
+int parse_options_concat(struct option *dst, size_t dst_size, struct option *src)
+{
+       int i, j;
+
+       for (i = 0; i < dst_size; i++)
+               if (dst[i].type == OPTION_END)
+                       break;
+       for (j = 0; i < dst_size; i++, j++) {
+               dst[i] = src[j];
+               if (src[j].type == OPTION_END)
+                       return 0;
+       }
+       return -1;
+}
index 0c996916b6044989f0e2945881c7c12f7292d5c1..7581e931da13151473739036a89d9d19303eb18b 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[].
@@ -183,10 +187,12 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx,
 
 extern int parse_options_end(struct parse_opt_ctx_t *ctx);
 
+extern int parse_options_concat(struct option *dst, size_t, struct option *src);
 
 /*----- 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 +209,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 aaa9345ebca4140317d8509ba95ec61cff82192d..b4c8d917229a4187f36a76f43603fc036e65632e 100644 (file)
--- a/path.c
+++ b/path.c
@@ -415,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;
        }
@@ -728,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 970fe434ed02e1850b984ddcbdbff6c13a21083c..1926dc9a4b929c025456227526a93424d4972948 100644 (file)
@@ -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 a7518b6f0ddb505aa0133d5920a76a19f8e00a75..0f24c8d5d9fa897c0b72c529c9fd2ccbcc7bd4d9 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -698,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/"))
@@ -707,8 +706,7 @@ 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) {
+       if (!has_glob_specials(pattern)) {
                /* Append implied '/' '*' if not present. */
                if (real_pattern.buf[real_pattern.len - 1] != '/')
                        strbuf_addch(&real_pattern, '/');
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 d38812085151b6738e7ca95be10968b973bf13b4..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) */
@@ -250,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;
@@ -502,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;
@@ -554,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;
 
@@ -562,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]);
@@ -811,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;
@@ -856,5 +850,8 @@ int main(int argc, const char **argv)
                }
                strbuf_reset(&buf);
        } while (1);
+
+       http_cleanup();
+
        return 0;
 }
index 490b484084e875e9b6c5aea1168437bba4bb500a..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;
 
@@ -1191,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);
@@ -1332,7 +1353,7 @@ 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, got_rev_arg = 0;
        const char **prune_data = NULL;
@@ -1468,7 +1489,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                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 && !got_rev_arg) {
@@ -1502,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");
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 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 fac34f77a7800193e636f49c799908f629540de1..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);
                }
@@ -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 c23cc5e6e19a2d8c9a92161b0a5d62a5ef8e920b..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);
index 5d286e409ee2a9b12d6b64cb05394eca18fe3e97..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,21 @@ 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, i;
+       int i;
+       ssize_t len;
        struct child_process cp;
        const char *argv[] = {
                "status",
                "--porcelain",
                NULL,
+               NULL,
        };
        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];
@@ -151,6 +164,9 @@ int is_submodule_modified(const char *path)
        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 = env;
@@ -161,6 +177,25 @@ 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))
@@ -169,5 +204,5 @@ int is_submodule_modified(const char *path)
        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 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
+       '
+}
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 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/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 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 714626d2d61ea95465b838595fe9b5e32b8f55b1..1d6cd45b55a2ae2e0a7d6728b04112d1c0f43446 100755 (executable)
@@ -13,11 +13,11 @@ 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 '
@@ -33,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)
@@ -57,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^ &&
@@ -81,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 &&
@@ -88,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
@@ -111,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
@@ -131,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
@@ -165,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 &&
@@ -180,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 \
@@ -203,8 +216,805 @@ 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
+'
+
+cat > expect_list << EOF
+c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75
+c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
+4b6ad22357cc8a1296720574b8d2fbc22fab0671 bd1753200303d0a0344be813e504253b3d98e74d
+EOF
+
+test_expect_success '"git notes list" does not expand to "git notes list HEAD"' '
+       git notes list > output &&
+       test_cmp expect_list 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/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh
new file mode 100755 (executable)
index 0000000..e17ae71
--- /dev/null
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+test_description='test cherry-picking with --ff option'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo first > file1 &&
+       git add file1 &&
+       test_tick &&
+       git commit -m "first" &&
+       git tag first &&
+
+       git checkout -b other &&
+       echo second >> file1 &&
+       git add file1 &&
+       test_tick &&
+       git commit -m "second" &&
+       git tag second
+'
+
+test_expect_success 'cherry-pick using --ff fast forwards' '
+       git checkout master &&
+       git reset --hard first &&
+       test_tick &&
+       git cherry-pick --ff second &&
+       test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify second)"
+'
+
+test_expect_success 'cherry-pick not using --ff does not fast forwards' '
+       git checkout master &&
+       git reset --hard first &&
+       test_tick &&
+       git cherry-pick second &&
+       test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify second)"
+'
+
+#
+# We setup the following graph:
+#
+#            B---C
+#           /   /
+#      first---A
+#
+# (This has been taken from t3502-cherry-pick-merge.sh)
+#
+test_expect_success 'merge setup' '
+       git checkout master &&
+       git reset --hard first &&
+       echo new line >A &&
+       git add A &&
+       test_tick &&
+       git commit -m "add line to A" A &&
+       git tag A &&
+       git checkout -b side first &&
+       echo new line >B &&
+       git add B &&
+       test_tick &&
+       git commit -m "add line to B" B &&
+       git tag B &&
+       git checkout master &&
+       git merge side &&
+       git tag C &&
+       git checkout -b new A
+'
+
+test_expect_success 'cherry-pick a non-merge with --ff and -m should fail' '
+       git reset --hard A -- &&
+       test_must_fail git cherry-pick --ff -m 1 B &&
+       git diff --exit-code A --
+'
+
+test_expect_success 'cherry pick a merge with --ff but without -m should fail' '
+       git reset --hard A -- &&
+       test_must_fail git cherry-pick --ff C &&
+       git diff --exit-code A --
+'
+
+test_expect_success 'cherry pick with --ff a merge (1)' '
+       git reset --hard A -- &&
+       git cherry-pick --ff -m 1 C &&
+       git diff --exit-code C &&
+       test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify C)"
+'
+
+test_expect_success 'cherry pick with --ff a merge (2)' '
+       git reset --hard B -- &&
+       git cherry-pick --ff -m 2 C &&
+       git diff --exit-code C &&
+       test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify C)"
+'
+
+test_expect_success 'cherry pick a merge relative to nonexistent parent with --ff should fail' '
+       git reset --hard B -- &&
+       test_must_fail git cherry-pick --ff -m 3 C
+'
+
+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 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 843ef7f88c3025ebe0660a645024fcf0942386bd..d21c37f3a20c42b05044e45c5e5af71294c8420c 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 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 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
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
new file mode 100755 (executable)
index 0000000..552da65
--- /dev/null
@@ -0,0 +1,199 @@
+#!/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_expect_success 'git rebase -i (double edit)' '
+       git reset --hard D &&
+       clear_hook_input &&
+       FAKE_LINES="edit 1 edit 2" git rebase -i B &&
+       git rebase --continue &&
+       echo something > 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_done
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 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
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 af63d6ec6dfdf34b7a8ab4c19547d1d84cc184f2..e249c3ed4176b4934ae57541ab8b671801968eae 100755 (executable)
@@ -442,6 +442,58 @@ 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 &&
 --
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 d3c039f724c3a1247417b5fd29d6bd50a88f54be..cee319da0ae6ef9ec3b220adbb178e116713db2a 100755 (executable)
@@ -227,4 +227,11 @@ test_expect_success 'fail when using --files together with --cached' "
     test_must_fail git submodule summary --files --cached
 "
 
+test_expect_success 'should not fail in an empty repo' "
+    git init xyzzy &&
+    cd xyzzy &&
+    git submodule summary >output 2>&1 &&
+    test_cmp output /dev/null
+"
+
 test_done
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 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 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 a0e396a9522cec96443490fd77ff4abffb335287..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
index f822972020136b0fa04a95d7fcf26df40c81cfe6..2638781c5b89024294f3c4c32861b275e339ad1d 100644 (file)
@@ -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 08e4fa0354d64e2b931be68ed8da111b97273523..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,6 +929,8 @@ 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()");
 
@@ -930,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);
@@ -1014,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. */
@@ -1032,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;
@@ -1052,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);
 
@@ -1068,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;
index 6dd9ae182fe12c53860a6af02461ff819d59fd6e..c59d97388e6fdb1dccd4dca0d41dccc3d129f8fb 100644 (file)
@@ -80,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
@@ -94,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 *);
@@ -128,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,
@@ -148,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 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 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);