Merge branch 'jh/notes'
authorJunio C Hamano <gitster@pobox.com>
Mon, 15 Mar 2010 07:52:06 +0000 (00:52 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 15 Mar 2010 07:52:06 +0000 (00:52 -0700)
* jh/notes: (33 commits)
Documentation: fix a few typos in git-notes.txt
notes: fix malformed tree entry
builtin-notes: Minor (mostly parse_options-related) fixes
builtin-notes: Add "copy" subcommand for copying notes between objects
builtin-notes: Misc. refactoring of argc and exit value handling
builtin-notes: Add -c/-C options for reusing notes
builtin-notes: Refactor handling of -F option to allow combining -m and -F
builtin-notes: Deprecate the -m/-F options for "git notes edit"
builtin-notes: Add "append" subcommand for appending to note objects
builtin-notes: Add "add" subcommand for adding notes to objects
builtin-notes: Add --message/--file aliases for -m/-F options
builtin-notes: Add "list" subcommand for listing note objects
Documentation: Generalize git-notes docs to 'objects' instead of 'commits'
builtin-notes: Add "prune" subcommand for removing notes for missing objects
Notes API: prune_notes(): Prune notes that belong to non-existing objects
t3305: Verify that removing notes triggers automatic fanout consolidation
builtin-notes: Add "remove" subcommand for removing existing notes
Teach builtin-notes to remove empty notes
Teach notes code to properly preserve non-notes in the notes tree
t3305: Verify that adding many notes with git-notes triggers increased fanout
...

Conflicts:
Makefile

1  2 
Makefile
builtin/notes.c
git.c
t/t3301-notes.sh
diff --combined Makefile
index 8fdc421073bf449c559d8ac0c962058db372dc02,1f95f93f76d5e098cde7015398fcad9ce69d9ab8..7c616f8b0a1405b3e5950ec72dc464e1a76a15b6
+++ b/Makefile
@@@ -180,6 -180,9 +180,6 @@@ all:
  # If not set it defaults to the bare 'wish'. If it is set to the empty
  # string then NO_TCLTK will be forced (this is used by configure script).
  #
 -# Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit
 -# parallel delta searching when packing objects.
 -#
  # Define INTERNAL_QSORT to use Git's implementation of qsort(), which
  # is a simplified version of the merge sort used in glibc. This is
  # recommended if Git triggers O(n^2) behavior in your platform's qsort().
  #   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
@@@ -282,6 -278,29 +282,6 @@@ pathsep = 
  # JavaScript minifier invocation that can function as filter
  JSMIN =
  
 -# default configuration for gitweb
 -GITWEB_CONFIG = gitweb_config.perl
 -GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
 -GITWEB_HOME_LINK_STR = projects
 -GITWEB_SITENAME =
 -GITWEB_PROJECTROOT = /pub/git
 -GITWEB_PROJECT_MAXDEPTH = 2007
 -GITWEB_EXPORT_OK =
 -GITWEB_STRICT_EXPORT =
 -GITWEB_BASE_URL =
 -GITWEB_LIST =
 -GITWEB_HOMETEXT = indextext.html
 -GITWEB_CSS = gitweb.css
 -GITWEB_LOGO = git-logo.png
 -GITWEB_FAVICON = git-favicon.png
 -ifdef JSMIN
 -GITWEB_JS = gitweb.min.js
 -else
 -GITWEB_JS = gitweb.js
 -endif
 -GITWEB_SITE_HEADER =
 -GITWEB_SITE_FOOTER =
 -
  export prefix bindir sharedir sysconfdir
  
  CC = gcc
@@@ -308,7 -327,7 +308,7 @@@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__pow
  # 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
@@@ -318,13 -337,11 +318,13 @@@ COMPAT_CFLAGS 
  COMPAT_OBJS =
  LIB_H =
  LIB_OBJS =
 +PROGRAM_OBJS =
  PROGRAMS =
  SCRIPT_PERL =
  SCRIPT_PYTHON =
  SCRIPT_SH =
 -TEST_PROGRAMS =
 +SCRIPT_LIB =
 +TEST_PROGRAMS_NEED_X =
  
  SCRIPT_SH += git-am.sh
  SCRIPT_SH += git-bisect.sh
@@@ -335,21 -352,19 +335,20 @@@ SCRIPT_SH += git-merge-octopus.s
  SCRIPT_SH += git-merge-one-file.sh
  SCRIPT_SH += git-merge-resolve.sh
  SCRIPT_SH += git-mergetool.sh
- SCRIPT_SH += git-notes.sh
 -SCRIPT_SH += git-mergetool--lib.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
@@@ -370,35 -385,16 +369,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
@@@ -454,7 -450,6 +453,7 @@@ LIB_H += blob.
  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
@@@ -466,7 -461,6 +465,7 @@@ LIB_H += delta.
  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
@@@ -509,8 -503,7 +508,8 @@@ LIB_H += tree-walk.
  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
@@@ -624,95 -617,96 +623,96 @@@ LIB_OBJS += ws.
  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-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
 +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 =
  ifeq ($(uname_S),Linux)
        NO_STRLCPY = YesPlease
        NO_MKSTEMPS = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
  endif
  ifeq ($(uname_S),GNU/kFreeBSD)
        NO_STRLCPY = YesPlease
        NO_MKSTEMPS = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
  endif
  ifeq ($(uname_S),UnixWare)
        CC = cc
@@@ -785,6 -781,7 +785,6 @@@ ifeq ($(uname_S),Darwin
                NO_STRLCPY = YesPlease
        endif
        NO_MEMMEM = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
        USE_ST_TIMESPEC = YesPlease
  endif
  ifeq ($(uname_S),SunOS)
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
        NO_REGEX = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
        ifeq ($(uname_R),5.7)
                NEEDS_RESOLV = YesPlease
                NO_IPV6 = YesPlease
@@@ -852,6 -850,7 +852,6 @@@ ifeq ($(uname_S),FreeBSD
        BASIC_LDFLAGS += -L/usr/local/lib
        DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
        USE_ST_TIMESPEC = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
        ifeq ($(shell expr "$(uname_R)" : '4\.'),2)
                PTHREAD_LIBS = -pthread
                NO_UINTMAX_T = YesPlease
@@@ -865,6 -864,7 +865,6 @@@ ifeq ($(uname_S),OpenBSD
        NEEDS_LIBICONV = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
 -      THREADED_DELTA_SEARCH = YesPlease
  endif
  ifeq ($(uname_S),NetBSD)
        ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
        endif
        BASIC_CFLAGS += -I/usr/pkg/include
        BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
 -      THREADED_DELTA_SEARCH = YesPlease
        USE_ST_TIMESPEC = YesPlease
        NO_MKSTEMPS = YesPlease
  endif
@@@ -886,7 -887,9 +886,7 @@@ ifeq ($(uname_S),AIX
        INTERNAL_QSORT = UnfortunatelyYes
        NEEDS_LIBICONV=YesPlease
        BASIC_CFLAGS += -D_LARGE_FILES
 -      ifneq ($(shell expr "$(uname_V)" : '[1234]'),1)
 -              THREADED_DELTA_SEARCH = YesPlease
 -      else
 +      ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
                NO_PTHREADS = YesPlease
        endif
  endif
@@@ -912,6 -915,7 +912,6 @@@ ifeq ($(uname_S),IRIX
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH = /usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
  endif
  ifeq ($(uname_S),IRIX64)
        NO_SETENV=YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
  endif
  ifeq ($(uname_S),HP-UX)
        NO_IPV6=YesPlease
@@@ -979,6 -984,7 +979,6 @@@ ifeq ($(uname_S),Windows
        NO_CURL = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
  
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
        COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
        BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
        EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
 +      PTHREAD_LIBS =
        lib =
  ifndef DEBUG
        BASIC_CFLAGS += -GL -Os -MT
@@@ -1030,12 -1035,12 +1030,12 @@@ ifneq (,$(findstring MINGW,$(uname_S))
        NO_REGEX = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
 -      COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
 +      COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch -Icompat/win32
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
                compat/win32/pthread.o
        EXTLIBS += -lws2_32
 +      PTHREAD_LIBS =
        X = .exe
  ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
        htmldir=doc/git/html/
@@@ -1052,14 -1057,6 +1052,14 @@@ endi
  -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)|'
        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
@@@ -1140,7 -1136,7 +1140,7 @@@ endi
  EXTLIBS += -lz
  
  ifndef NO_POSIX_ONLY_PROGRAMS
 -      PROGRAMS += git-daemon$X
 +      PROGRAM_OBJS += daemon.o
  endif
  ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
@@@ -1241,6 -1237,7 +1241,6 @@@ ifdef NO_MKDTEM
  endif
  ifdef NO_MKSTEMPS
        COMPAT_CFLAGS += -DNO_MKSTEMPS
 -      COMPAT_OBJS += compat/mkstemps.o
  endif
  ifdef NO_UNSETENV
        COMPAT_CFLAGS += -DNO_UNSETENV
@@@ -1306,12 -1303,10 +1306,12 @@@ endi
  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)
@@@ -1337,12 -1332,16 +1337,12 @@@ ifdef RUNTIME_PREFI
  endif
  
  ifdef NO_PTHREADS
 -      THREADED_DELTA_SEARCH =
        BASIC_CFLAGS += -DNO_PTHREADS
  else
        EXTLIBS += $(PTHREAD_LIBS)
 -endif
 -
 -ifdef THREADED_DELTA_SEARCH
 -      BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
        LIB_OBJS += thread-utils.o
  endif
 +
  ifdef DIR_HAS_BSD_GROUP_SEMANTICS
        COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
  endif
@@@ -1453,7 -1452,7 +1453,7 @@@ export TAR INSTALL DESTDIR SHELL_PAT
  
  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
@@@ -1487,8 -1486,8 +1487,8 @@@ 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: ALL_CFLAGS += \
        '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
        '-DGIT_MAN_PATH="$(mandir_SQ)"' \
        '-DGIT_INFO_PATH="$(infodir_SQ)"'
@@@ -1504,25 -1503,17 +1504,25 @@@ common-cmds.h: ./generate-cmdlist.sh co
  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
  
@@@ -1545,11 -1536,6 +1545,11 @@@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % 
        chmod +x $@+ && \
        mv $@+ $@
  
 +
 +.PHONY: gitweb
 +gitweb:
 +      $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
 +
  ifdef JSMIN
  OTHER_PROGRAMS += gitweb/gitweb.cgi   gitweb/gitweb.min.js
  gitweb/gitweb.cgi: gitweb/gitweb.perl gitweb/gitweb.min.js
  OTHER_PROGRAMS += gitweb/gitweb.cgi
  gitweb/gitweb.cgi: gitweb/gitweb.perl
  endif
 -      $(QUIET_GEN)$(RM) $@ $@+ && \
 -      sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
 -          -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
 -          -e 's|++GIT_BINDIR++|$(bindir)|g' \
 -          -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
 -          -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
 -          -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
 -          -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
 -          -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
 -          -e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
 -          -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
 -          -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
 -          -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
 -          -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
 -          -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
 -          -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
 -          -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
 -          -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
 -          -e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \
 -          -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
 -          -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
 -          $< >$@+ && \
 -      chmod +x $@+ && \
 -      mv $@+ $@
 +      $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 +
 +ifdef JSMIN
 +gitweb/gitweb.min.js: gitweb/gitweb.js
 +      $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 +endif # JSMIN
 +
  
  git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css gitweb/gitweb.js
        $(QUIET_GEN)$(RM) $@ $@+ && \
@@@ -1590,6 -1593,12 +1590,6 @@@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git
        mv $@+ $@
  endif # NO_PERL
  
 -
 -ifdef JSMIN
 -gitweb/gitweb.min.js: gitweb/gitweb.js
 -      $(QUIET_GEN)$(JSMIN) <$< >$@
 -endif # JSMIN
 -
  ifndef NO_PYTHON
  $(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS
  $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
@@@ -1632,140 -1641,19 +1632,140 @@@ 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) $<
 +$(ASM_OBJ): %.o: %.S GIT-CFLAGS $(missing_dep_dirs)
 +      $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $<
 +endif
 +
  %.s: %.c GIT-CFLAGS FORCE
        $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
 -%.o: %.S GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(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: ALL_CFLAGS += \
        '-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: ALL_CFLAGS += \
        -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
  
  config.s config.o: ALL_CFLAGS += -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
  http.s http.o: ALL_CFLAGS += -DGIT_USER_AGENT='"git/$(GIT_VERSION)"'
  
  ifdef NO_EXPAT
 -http-walker.o: http.h
  http-walker.s http-walker.o: ALL_CFLAGS += -DNO_EXPAT
  endif
  
@@@ -1783,6 -1672,10 +1783,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)
@@@ -1800,9 -1693,18 +1800,9 @@@ $(REMOTE_CURL_PRIMARY): remote-curl.o h
        $(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)
  
@@@ -1868,6 -1770,24 +1868,6 @@@ GIT-GUI-VARS: FORC
              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)
@@@ -1885,8 -1805,6 +1885,8 @@@ bin-wrappers/%: wrap-for-bin.s
  
  export NO_SVN_TESTS
  
 +### Testing rules
 +
  test: all
        $(MAKE) -C t/ all
  
@@@ -1898,7 -1816,9 +1898,7 @@@ test-delta$X: diff-delta.o patch-delta.
  
  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)
@@@ -1944,7 -1864,6 +1944,7 @@@ install: al
        $(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
@@@ -2063,11 -1982,10 +2063,11 @@@ distclean: clea
  
  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
@@@ -2097,13 -2015,12 +2097,13 @@@ endi
  ### 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" || \
                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; \
diff --combined builtin/notes.c
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..feb710ac4ada96339e42fe5838cf137e9ae4f12e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,455 @@@
++/*
++ * 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"
++
++static const char * const git_notes_usage[] = {
++      "git notes [list [<object>]]",
++      "git notes add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
++      "git notes copy [-f] <from-object> <to-object>",
++      "git notes append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
++      "git notes edit [<object>]",
++      "git notes show [<object>]",
++      "git notes remove [<object>]",
++      "git notes prune",
++      NULL
++};
++
++static const char note_template[] =
++      "\n"
++      "#\n"
++      "# Write/edit the notes for the following object:\n"
++      "#\n";
++
++struct msg_arg {
++      int given;
++      int use_editor;
++      struct strbuf buf;
++};
++
++static int list_each_note(const unsigned char *object_sha1,
++              const unsigned char *note_sha1, char *note_path,
++              void *cb_data)
++{
++      printf("%s %s\n", sha1_to_hex(note_sha1), sha1_to_hex(object_sha1));
++      return 0;
++}
++
++static void write_note_data(int fd, const unsigned char *sha1)
++{
++      unsigned long size;
++      enum object_type type;
++      char *buf = read_sha1_file(sha1, &type, &size);
++      if (buf) {
++              if (size)
++                      write_or_die(fd, buf, size);
++              free(buf);
++      }
++}
++
++static void write_commented_object(int fd, const unsigned char *object)
++{
++      const char *show_args[5] =
++              {"show", "--stat", "--no-notes", sha1_to_hex(object), NULL};
++      struct child_process show;
++      struct strbuf buf = STRBUF_INIT;
++      FILE *show_out;
++
++      /* Invoke "git show --stat --no-notes $object" */
++      memset(&show, 0, sizeof(show));
++      show.argv = show_args;
++      show.no_stdin = 1;
++      show.out = -1;
++      show.err = 0;
++      show.git_cmd = 1;
++      if (start_command(&show))
++              die("unable to start 'show' for object '%s'",
++                  sha1_to_hex(object));
++
++      /* Open the output as FILE* so strbuf_getline() can be used. */
++      show_out = xfdopen(show.out, "r");
++      if (show_out == NULL)
++              die_errno("can't fdopen 'show' output fd");
++
++      /* Prepend "# " to each output line and write result to 'fd' */
++      while (strbuf_getline(&buf, show_out, '\n') != EOF) {
++              write_or_die(fd, "# ", 2);
++              write_or_die(fd, buf.buf, buf.len);
++              write_or_die(fd, "\n", 1);
++      }
++      strbuf_release(&buf);
++      if (fclose(show_out))
++              die_errno("failed to close pipe to 'show' for object '%s'",
++                        sha1_to_hex(object));
++      if (finish_command(&show))
++              die("failed to finish 'show' for object '%s'",
++                  sha1_to_hex(object));
++}
++
++static void create_note(const unsigned char *object, struct msg_arg *msg,
++                      int append_only, const unsigned char *prev,
++                      unsigned char *result)
++{
++      char *path = NULL;
++
++      if (msg->use_editor || !msg->given) {
++              int fd;
++
++              /* write the template message before editing: */
++              path = git_pathdup("NOTES_EDITMSG");
++              fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
++              if (fd < 0)
++                      die_errno("could not create file '%s'", path);
++
++              if (msg->given)
++                      write_or_die(fd, msg->buf.buf, msg->buf.len);
++              else if (prev && !append_only)
++                      write_note_data(fd, prev);
++              write_or_die(fd, note_template, strlen(note_template));
++
++              write_commented_object(fd, object);
++
++              close(fd);
++              strbuf_reset(&(msg->buf));
++
++              if (launch_editor(path, &(msg->buf), NULL)) {
++                      die("Please supply the note contents using either -m" \
++                          " or -F option");
++              }
++              stripspace(&(msg->buf), 1);
++      }
++
++      if (prev && append_only) {
++              /* Append buf to previous note contents */
++              unsigned long size;
++              enum object_type type;
++              char *prev_buf = read_sha1_file(prev, &type, &size);
++
++              strbuf_grow(&(msg->buf), size + 1);
++              if (msg->buf.len && prev_buf && size)
++                      strbuf_insert(&(msg->buf), 0, "\n", 1);
++              if (prev_buf && size)
++                      strbuf_insert(&(msg->buf), 0, prev_buf, size);
++              free(prev_buf);
++      }
++
++      if (!msg->buf.len) {
++              fprintf(stderr, "Removing note for object %s\n",
++                      sha1_to_hex(object));
++              hashclr(result);
++      } else {
++              if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) {
++                      error("unable to write note object");
++                      if (path)
++                              error("The note contents has been left in %s",
++                                    path);
++                      exit(128);
++              }
++      }
++
++      if (path) {
++              unlink_or_warn(path);
++              free(path);
++      }
++}
++
++static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
++{
++      struct msg_arg *msg = opt->value;
++
++      strbuf_grow(&(msg->buf), strlen(arg) + 2);
++      if (msg->buf.len)
++              strbuf_addch(&(msg->buf), '\n');
++      strbuf_addstr(&(msg->buf), arg);
++      stripspace(&(msg->buf), 0);
++
++      msg->given = 1;
++      return 0;
++}
++
++static int parse_file_arg(const struct option *opt, const char *arg, int unset)
++{
++      struct msg_arg *msg = opt->value;
++
++      if (msg->buf.len)
++              strbuf_addch(&(msg->buf), '\n');
++      if (!strcmp(arg, "-")) {
++              if (strbuf_read(&(msg->buf), 0, 1024) < 0)
++                      die_errno("cannot read '%s'", arg);
++      } else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0)
++              die_errno("could not open or read '%s'", arg);
++      stripspace(&(msg->buf), 0);
++
++      msg->given = 1;
++      return 0;
++}
++
++static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
++{
++      struct msg_arg *msg = opt->value;
++      char *buf;
++      unsigned char object[20];
++      enum object_type type;
++      unsigned long len;
++
++      if (msg->buf.len)
++              strbuf_addch(&(msg->buf), '\n');
++
++      if (get_sha1(arg, object))
++              die("Failed to resolve '%s' as a valid ref.", arg);
++      if (!(buf = read_sha1_file(object, &type, &len)) || !len) {
++              free(buf);
++              die("Failed to read object '%s'.", arg);;
++      }
++      strbuf_add(&(msg->buf), buf, len);
++      free(buf);
++
++      msg->given = 1;
++      return 0;
++}
++
++static int parse_reedit_arg(const struct option *opt, const char *arg, int unset)
++{
++      struct msg_arg *msg = opt->value;
++      msg->use_editor = 1;
++      return parse_reuse_arg(opt, arg, unset);
++}
++
++int commit_notes(struct notes_tree *t, const char *msg)
++{
++      struct commit_list *parent;
++      unsigned char tree_sha1[20], prev_commit[20], new_commit[20];
++      struct strbuf buf = STRBUF_INIT;
++
++      if (!t)
++              t = &default_notes_tree;
++      if (!t->initialized || !t->ref || !*t->ref)
++              die("Cannot commit uninitialized/unreferenced notes tree");
++
++      /* 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;
++}
++
++int cmd_notes(int argc, const char **argv, const char *prefix)
++{
++      struct notes_tree *t;
++      unsigned char object[20], from_obj[20], new_note[20];
++      const unsigned char *note;
++      const char *object_ref;
++      char logmsg[100];
++
++      int list = 0, add = 0, copy = 0, append = 0, edit = 0, show = 0,
++          remove = 0, prune = 0, force = 0;
++      int given_object = 0, i = 1, retval = 0;
++      struct msg_arg msg = { 0, 0, STRBUF_INIT };
++      struct option options[] = {
++              OPT_GROUP("Notes contents options"),
++              { OPTION_CALLBACK, 'm', "message", &msg, "MSG",
++                      "note contents as a string", PARSE_OPT_NONEG,
++                      parse_msg_arg},
++              { OPTION_CALLBACK, 'F', "file", &msg, "FILE",
++                      "note contents in a file", PARSE_OPT_NONEG,
++                      parse_file_arg},
++              { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT",
++                      "reuse and edit specified note object", PARSE_OPT_NONEG,
++                      parse_reedit_arg},
++              { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT",
++                      "reuse specified note object", PARSE_OPT_NONEG,
++                      parse_reuse_arg},
++              OPT_GROUP("Other options"),
++              OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
++              OPT_END()
++      };
++
++      git_config(git_default_config, NULL);
++
++      argc = parse_options(argc, argv, prefix, options, git_notes_usage, 0);
++
++      if (argc && !strcmp(argv[0], "list"))
++              list = 1;
++      else if (argc && !strcmp(argv[0], "add"))
++              add = 1;
++      else if (argc && !strcmp(argv[0], "copy"))
++              copy = 1;
++      else if (argc && !strcmp(argv[0], "append"))
++              append = 1;
++      else if (argc && !strcmp(argv[0], "edit"))
++              edit = 1;
++      else if (argc && !strcmp(argv[0], "show"))
++              show = 1;
++      else if (argc && !strcmp(argv[0], "remove"))
++              remove = 1;
++      else if (argc && !strcmp(argv[0], "prune"))
++              prune = 1;
++      else if (!argc) {
++              list = 1; /* Default to 'list' if no other subcommand given */
++              i = 0;
++      }
++
++      if (list + add + copy + append + edit + show + remove + prune != 1)
++              usage_with_options(git_notes_usage, options);
++
++      if (msg.given && !(add || append || edit)) {
++              error("cannot use -m/-F/-c/-C options with %s subcommand.",
++                    argv[0]);
++              usage_with_options(git_notes_usage, options);
++      }
++
++      if (msg.given && edit) {
++              fprintf(stderr, "The -m/-F/-c/-C options have been deprecated "
++                      "for the 'edit' subcommand.\n"
++                      "Please use 'git notes add -f -m/-F/-c/-C' instead.\n");
++      }
++
++      if (force && !(add || copy)) {
++              error("cannot use -f option with %s subcommand.", argv[0]);
++              usage_with_options(git_notes_usage, options);
++      }
++
++      if (copy) {
++              const char *from_ref;
++              if (argc < 3) {
++                      error("too few parameters");
++                      usage_with_options(git_notes_usage, options);
++              }
++              from_ref = argv[i++];
++              if (get_sha1(from_ref, from_obj))
++                      die("Failed to resolve '%s' as a valid ref.", from_ref);
++      }
++
++      given_object = argc > i;
++      object_ref = given_object ? argv[i++] : "HEAD";
++
++      if (argc > i || (prune && given_object)) {
++              error("too many parameters");
++              usage_with_options(git_notes_usage, options);
++      }
++
++      if (get_sha1(object_ref, object))
++              die("Failed to resolve '%s' as a valid ref.", object_ref);
++
++      init_notes(NULL, NULL, NULL, 0);
++      t = &default_notes_tree;
++
++      if (prefixcmp(t->ref, "refs/notes/"))
++              die("Refusing to %s notes in %s (outside of refs/notes/)",
++                  argv[0], t->ref);
++
++      note = get_note(t, object);
++
++      /* list command */
++
++      if (list) {
++              if (given_object) {
++                      if (note) {
++                              puts(sha1_to_hex(note));
++                              goto end;
++                      }
++              } else {
++                      retval = for_each_note(t, 0, list_each_note, NULL);
++                      goto end;
++              }
++      }
++
++      /* show command */
++
++      if ((list || show) && !note) {
++              error("No note found for object %s.", sha1_to_hex(object));
++              retval = 1;
++              goto end;
++      } else if (show) {
++              const char *show_args[3] = {"show", sha1_to_hex(note), NULL};
++              retval = execv_git_cmd(show_args);
++              goto end;
++      }
++
++      /* add/append/edit/remove/prune command */
++
++      if ((add || copy) && note) {
++              if (!force) {
++                      error("Cannot %s notes. Found existing notes for object"
++                            " %s. Use '-f' to overwrite existing notes",
++                            argv[0], sha1_to_hex(object));
++                      retval = 1;
++                      goto end;
++              }
++              fprintf(stderr, "Overwriting existing notes for object %s\n",
++                      sha1_to_hex(object));
++      }
++
++      if (remove) {
++              msg.given = 1;
++              msg.use_editor = 0;
++              strbuf_reset(&(msg.buf));
++      }
++
++      if (prune) {
++              hashclr(new_note);
++              prune_notes(t);
++              goto commit;
++      } else if (copy) {
++              const unsigned char *from_note = get_note(t, from_obj);
++              if (!from_note) {
++                      error("Missing notes on source object %s. Cannot copy.",
++                            sha1_to_hex(from_obj));
++                      retval = 1;
++                      goto end;
++              }
++              hashcpy(new_note, from_note);
++      } else
++              create_note(object, &msg, append, note, new_note);
++
++      if (is_null_sha1(new_note))
++              remove_note(t, object);
++      else
++              add_note(t, object, new_note, combine_notes_overwrite);
++
++commit:
++      snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
++               is_null_sha1(new_note) ? "removed" : "added", argv[0]);
++      commit_notes(t, logmsg);
++
++end:
++      free_notes(t);
++      strbuf_release(&(msg.buf));
++      return retval;
++}
diff --combined git.c
index f09948eed952aa14614ad3c17475f63ad02fbc6e,32f76e15ea732d2d784fb8ee5076801f0b57cce6..6bae30545b85f19eb51c4b055f303f70909f0cf2
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -54,9 -54,6 +54,9 @@@ static int handle_options(const char **
  {
        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] != '-')
@@@ -346,6 -343,7 +346,7 @@@ static void handle_internal_command(in
                { "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 },
@@@ -530,7 -528,7 +531,7 @@@ int main(int argc, const char **argv
                        break;
                if (was_alias) {
                        fprintf(stderr, "Expansion of alias '%s' failed; "
 -                              "'%s' is not a git-command\n",
 +                              "'%s' is not a git command\n",
                                cmd, argv[0]);
                        exit(1);
                }
diff --combined t/t3301-notes.sh
index 714626d2d61ea95465b838595fe9b5e32b8f55b1,90178f96d29597a886da1f64f95176116294554a..37b96871c54037399450dd8c982d83e47dda0184
@@@ -8,16 -8,15 +8,16 @@@ test_description='Test commit notes
  . ./test-lib.sh
  
  cat > fake_editor.sh << \EOF
 +#!/bin/sh
  echo "$MSG" > "$1"
  echo "$MSG" >& 2
  EOF
  chmod a+x fake_editor.sh
VISUAL=./fake_editor.sh
- export VISUAL
GIT_EDITOR=./fake_editor.sh
+ export GIT_EDITOR
  
  test_expect_success 'cannot annotate non-existing HEAD' '
-       (MSG=3 && export MSG && test_must_fail git notes edit)
+       (MSG=3 && export MSG && test_must_fail git notes add)
  '
  
  test_expect_success setup '
  
  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 -56,35 +57,35 @@@ test_expect_success 'handle empty note
  
  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 -107,7 +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 &&
        git commit -m 3rd &&
        MSG="b3
  c3c3c3c3
- d3d3d3" git notes edit
+ d3d3d3" git notes add
  '
  
  cat > expect-multiline << EOF
@@@ -111,19 -138,16 +139,16 @@@ test_expect_success 'show multi-line no
        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
      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 -183,7 +184,7 @@@ test_expect_success 'git log --pretty=r
  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 &&
  
  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 \
        esac
        test_expect_success "git show $pretty does$not show notes" '
                git show $p >output &&
-               eval "$negate grep spam output"
+               eval "$negate grep xyzzy output"
        '
  done
  
+ test_expect_success 'create -m notes (setup)' '
+       : > a5 &&
+       git add a5 &&
+       test_tick &&
+       git commit -m 5th &&
+       git notes add -m spam -m "foo
+ bar
+ baz"
+ '
+ whitespace="    "
+ cat > expect-m << EOF
+ commit bd1753200303d0a0344be813e504253b3d98e74d
+ Author: A U Thor <author@example.com>
+ Date:   Thu Apr 7 15:17:13 2005 -0700
+     5th
+ Notes:
+     spam
+ $whitespace
+     foo
+     bar
+     baz
+ EOF
+ printf "\n" >> expect-m
+ cat expect-F >> expect-m
+ test_expect_success 'show -m notes' '
+       git log -4 > output &&
+       test_cmp expect-m output
+ '
+ test_expect_success 'remove note with add -f -F /dev/null (setup)' '
+       git notes add -f -F /dev/null
+ '
+ cat > expect-rm-F << EOF
+ commit bd1753200303d0a0344be813e504253b3d98e74d
+ Author: A U Thor <author@example.com>
+ Date:   Thu Apr 7 15:17:13 2005 -0700
+     5th
+ EOF
+ printf "\n" >> expect-rm-F
+ cat expect-F >> expect-rm-F
+ test_expect_success 'verify note removal with -F /dev/null' '
+       git log -4 > output &&
+       test_cmp expect-rm-F output &&
+       ! git notes show
+ '
+ test_expect_success 'do not create empty note with -m "" (setup)' '
+       git notes add -m ""
+ '
+ test_expect_success 'verify non-creation of note with -m ""' '
+       git log -4 > output &&
+       test_cmp expect-rm-F output &&
+       ! git notes show
+ '
+ cat > expect-combine_m_and_F << EOF
+ foo
+ xyzzy
+ bar
+ zyxxy
+ baz
+ EOF
+ test_expect_success 'create note with combination of -m and -F' '
+       echo "xyzzy" > note_a &&
+       echo "zyxxy" > note_b &&
+       git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" &&
+       git notes show > output &&
+       test_cmp expect-combine_m_and_F output
+ '
+ test_expect_success 'remove note with "git notes remove" (setup)' '
+       git notes remove HEAD^ &&
+       git notes remove
+ '
+ cat > expect-rm-remove << EOF
+ commit bd1753200303d0a0344be813e504253b3d98e74d
+ Author: A U Thor <author@example.com>
+ Date:   Thu Apr 7 15:17:13 2005 -0700
+     5th
+ commit 15023535574ded8b1a89052b32673f84cf9582b8
+ Author: A U Thor <author@example.com>
+ Date:   Thu Apr 7 15:16:13 2005 -0700
+     4th
+ EOF
+ printf "\n" >> expect-rm-remove
+ cat expect-multiline >> expect-rm-remove
+ test_expect_success 'verify note removal with "git notes remove"' '
+       git log -4 > output &&
+       test_cmp expect-rm-remove output &&
+       ! git notes show HEAD^
+ '
+ cat > expect << EOF
+ c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75
+ c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
+ EOF
+ test_expect_success 'list notes with "git notes list"' '
+       git notes list > output &&
+       test_cmp expect output
+ '
+ test_expect_success 'list notes with "git notes"' '
+       git notes > output &&
+       test_cmp expect output
+ '
+ cat > expect << EOF
+ c18dc024e14f08d18d14eea0d747ff692d66d6a3
+ EOF
+ test_expect_success 'list specific note with "git notes list <object>"' '
+       git notes list HEAD^^ > output &&
+       test_cmp expect output
+ '
+ cat > expect << EOF
+ EOF
+ test_expect_success 'listing non-existing notes fails' '
+       test_must_fail git notes list HEAD > output &&
+       test_cmp expect output
+ '
+ cat > expect << EOF
+ Initial set of notes
+ More notes appended with git notes append
+ EOF
+ test_expect_success 'append to existing note with "git notes append"' '
+       git notes add -m "Initial set of notes" &&
+       git notes append -m "More notes appended with git notes append" &&
+       git notes show > output &&
+       test_cmp expect output
+ '
+ test_expect_success 'appending empty string does not change existing note' '
+       git notes append -m "" &&
+       git notes show > output &&
+       test_cmp expect output
+ '
+ test_expect_success 'git notes append == add when there is no existing note' '
+       git notes remove HEAD &&
+       test_must_fail git notes list HEAD &&
+       git notes append -m "Initial set of notes
+ More notes appended with git notes append" &&
+       git notes show > output &&
+       test_cmp expect output
+ '
+ test_expect_success 'appending empty string to non-existing note does not create note' '
+       git notes remove HEAD &&
+       test_must_fail git notes list HEAD &&
+       git notes append -m "" &&
+       test_must_fail git notes list HEAD
+ '
+ test_expect_success 'create other note on a different notes ref (setup)' '
+       : > a6 &&
+       git add a6 &&
+       test_tick &&
+       git commit -m 6th &&
+       GIT_NOTES_REF="refs/notes/other" git notes add -m "other note"
+ '
+ cat > expect-other << EOF
+ commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+ Author: A U Thor <author@example.com>
+ Date:   Thu Apr 7 15:18:13 2005 -0700
+     6th
+ Notes:
+     other 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
+ '
+ test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
+       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 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:
+     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:
+     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 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 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 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:
+     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
+ '
  test_done