Merge branch 'cc/bisect-doc'
authorJunio C Hamano <gitster@pobox.com>
Mon, 30 Nov 2009 22:43:51 +0000 (14:43 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 30 Nov 2009 22:43:51 +0000 (14:43 -0800)
266 files changed:
.gitignore
Documentation/RelNotes-1.6.5.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.6.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/fetch-options.txt
Documentation/git-add.txt
Documentation/git-apply.txt
Documentation/git-commit.txt
Documentation/git-cvsserver.txt
Documentation/git-describe.txt
Documentation/git-difftool.txt
Documentation/git-fast-import.txt
Documentation/git-fetch.txt
Documentation/git-filter-branch.txt
Documentation/git-format-patch.txt
Documentation/git-http-backend.txt [new file with mode: 0644]
Documentation/git-http-push.txt
Documentation/git-ls-files.txt
Documentation/git-mailinfo.txt
Documentation/git-mergetool.txt
Documentation/git-notes.txt [new file with mode: 0644]
Documentation/git-pack-objects.txt
Documentation/git-push.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-receive-pack.txt
Documentation/git-remote-helpers.txt
Documentation/git-remote.txt
Documentation/git-replace.txt
Documentation/git-reset.txt
Documentation/git-send-email.txt
Documentation/git-send-pack.txt
Documentation/git-show-ref.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-update-index.txt
Documentation/git-var.txt
Documentation/git.txt
Documentation/gitcore-tutorial.txt
Documentation/githooks.txt
Documentation/gitworkflows.txt
Documentation/glossary-content.txt
Documentation/howto/maintain-git.txt
Documentation/howto/revert-branch-rebase.txt
Documentation/howto/update-hook-example.txt
Documentation/merge-config.txt
Documentation/merge-options.txt
Documentation/merge-strategies.txt
Documentation/pretty-formats.txt
Documentation/pull-fetch-param.txt
Documentation/rev-list-options.txt
Documentation/technical/api-history-graph.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-capabilities.txt [new file with mode: 0644]
Documentation/technical/protocol-common.txt [new file with mode: 0644]
Documentation/user-manual.txt
Makefile
advice.c
advice.h
builtin-apply.c
builtin-blame.c
builtin-check-ref-format.c
builtin-commit-tree.c
builtin-commit.c
builtin-describe.c
builtin-diff-tree.c
builtin-fetch--tool.c [deleted file]
builtin-fetch-pack.c
builtin-fetch.c
builtin-fsck.c
builtin-grep.c
builtin-log.c
builtin-ls-files.c
builtin-ls-tree.c
builtin-mailinfo.c
builtin-mailsplit.c
builtin-merge-ours.c
builtin-merge-recursive.c
builtin-merge.c
builtin-mv.c
builtin-pack-objects.c
builtin-prune-packed.c
builtin-push.c
builtin-read-tree.c
builtin-receive-pack.c
builtin-reflog.c
builtin-remote.c
builtin-rerere.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-send-pack.c
builtin-shortlog.c
builtin-show-ref.c
builtin-stripspace.c
builtin-tar-tree.c
builtin-update-index.c
builtin-upload-archive.c
builtin.h
bundle.c
cache.h
color.h
combine-diff.c
command-list.txt
commit.c
commit.h
compat/bswap.h
compat/mingw.c
compat/mingw.h
compat/vcbuild/scripts/clink.pl
compat/win32mmap.c
config.c
configure.ac
connect.c
contrib/buildsystems/engine.pl
contrib/completion/git-completion.bash
contrib/examples/builtin-fetch--tool.c [new file with mode: 0644]
contrib/examples/git-merge.sh
contrib/examples/git-resolve.sh
contrib/fast-import/git-p4
contrib/hooks/post-receive-email
daemon.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-break.c
diffcore-rename.c
editor.c
environment.c
fast-import.c
fetch-pack.h
fsck.h
git-add--interactive.perl
git-am.sh
git-bisect.sh
git-compat-util.h
git-filter-branch.sh
git-instaweb.sh
git-merge-octopus.sh
git-merge-one-file.sh
git-mergetool--lib.sh
git-notes.sh [new file with mode: 0755]
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-send-email.perl
git-sh-setup.sh
git-submodule.sh
git-svn.perl
git.c
gitweb/gitweb.perl
graph.h
grep.c
grep.h
http-backend.c [new file with mode: 0644]
http-fetch.c
http-push.c
http.c
http.h
ident.c
imap-send.c
index-pack.c
merge-recursive.c
notes.c [new file with mode: 0644]
notes.h [new file with mode: 0644]
pack-redundant.c
pager.c
path.c
pkt-line.c
pkt-line.h
pretty.c
refs.c
refs.h
remote-curl.c
remote.c
remote.h
revision.c
revision.h
send-pack.h
show-index.c
sideband.c
strbuf.h
submodule.c
t/README
t/gitweb-lib.sh
t/lib-httpd/apache.conf
t/t1001-read-tree-m-2way.sh
t/t1200-tutorial.sh
t/t2300-cd-to-toplevel.sh
t/t3003-ls-files-exclude.sh
t/t3101-ls-tree-dirname.sh
t/t3301-notes.sh [new file with mode: 0755]
t/t3302-notes-index-expensive.sh [new file with mode: 0755]
t/t3303-notes-subtrees.sh [new file with mode: 0755]
t/t3409-rebase-preserve-merges.sh
t/t3701-add-interactive.sh
t/t4014-format-patch.sh
t/t4034-diff-words.sh
t/t4107-apply-ignore-whitespace.sh
t/t4128-apply-root.sh
t/t4201-shortlog.sh
t/t4202-log.sh
t/t5100-mailinfo.sh
t/t5100/info0015 [new file with mode: 0644]
t/t5100/info0015--no-inbody-headers [new file with mode: 0644]
t/t5100/info0016 [new file with mode: 0644]
t/t5100/info0016--no-inbody-headers [new file with mode: 0644]
t/t5100/msg0015 [new file with mode: 0644]
t/t5100/msg0015--no-inbody-headers [new file with mode: 0644]
t/t5100/msg0016 [new file with mode: 0644]
t/t5100/msg0016--no-inbody-headers [new file with mode: 0644]
t/t5100/patch0015 [new file with mode: 0644]
t/t5100/patch0015--no-inbody-headers [new file with mode: 0644]
t/t5100/patch0016 [new file with mode: 0644]
t/t5100/patch0016--no-inbody-headers [new file with mode: 0644]
t/t5100/sample.mbox
t/t5505-remote.sh
t/t5506-remote-groups.sh
t/t5510-fetch.sh
t/t5514-fetch-multiple.sh [new file with mode: 0755]
t/t5518-fetch-exit-status.sh
t/t5540-http-push.sh
t/t5541-http-push.sh [new file with mode: 0755]
t/t5550-http-fetch.sh
t/t5551-http-fetch.sh [new file with mode: 0755]
t/t5560-http-backend.sh [new file with mode: 0755]
t/t6017-rev-list-stdin.sh [new file with mode: 0755]
t/t6028-merge-up-to-date.sh
t/t6050-replace.sh
t/t6120-describe.sh
t/t7002-grep.sh
t/t7003-filter-branch.sh
t/t7005-editor.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7501-commit.sh
t/t7509-commit.sh [new file with mode: 0755]
t/t7600-merge.sh
t/t9001-send-email.sh
t/t9115-git-svn-dcommit-funky-renames.sh
t/t9130-git-svn-authors-file.sh
t/t9146-git-svn-empty-dirs.sh [new file with mode: 0755]
t/t9150-svk-mergetickets.sh
t/t9151-svn-mergeinfo.sh
t/t9151/make-svnmerge-dump
t/t9151/svn-mergeinfo.dump
t/t9300-fast-import.sh
t/t9400-git-cvsserver-server.sh
t/t9401-git-cvsserver-crlf.sh
t/t9501-gitweb-standalone-http-status.sh
t/t9502-gitweb-standalone-parse-output.sh [new file with mode: 0755]
t/t9700-perl-git.sh
t/t9700/test.pl
t/test-lib.sh
templates/hooks--pre-commit.sample
transport-helper.c
transport.c
transport.h
unpack-file.c
unpack-trees.c
upload-pack.c
usage.c
utf8.c
utf8.h
var.c
index f0d2e96b888d0a457dbd61ecb6c347c4dbb07f8a..ac02a580daf07150b03194a5a0de522ac1e3415d 100644 (file)
-GIT-BUILD-OPTIONS
-GIT-CFLAGS
-GIT-GUI-VARS
-GIT-VERSION-FILE
-git
-git-add
-git-add--interactive
-git-am
-git-annotate
-git-apply
-git-archimport
-git-archive
-git-bisect
-git-bisect--helper
-git-blame
-git-branch
-git-bundle
-git-cat-file
-git-check-attr
-git-check-ref-format
-git-checkout
-git-checkout-index
-git-cherry
-git-cherry-pick
-git-clean
-git-clone
-git-commit
-git-commit-tree
-git-config
-git-count-objects
-git-cvsexportcommit
-git-cvsimport
-git-cvsserver
-git-daemon
-git-diff
-git-diff-files
-git-diff-index
-git-diff-tree
-git-difftool
-git-difftool--helper
-git-describe
-git-fast-export
-git-fast-import
-git-fetch
-git-fetch--tool
-git-fetch-pack
-git-filter-branch
-git-fmt-merge-msg
-git-for-each-ref
-git-format-patch
-git-fsck
-git-fsck-objects
-git-gc
-git-get-tar-commit-id
-git-grep
-git-hash-object
-git-help
-git-http-fetch
-git-http-push
-git-imap-send
-git-index-pack
-git-init
-git-init-db
-git-instaweb
-git-log
-git-lost-found
-git-ls-files
-git-ls-remote
-git-ls-tree
-git-mailinfo
-git-mailsplit
-git-merge
-git-merge-base
-git-merge-index
-git-merge-file
-git-merge-tree
-git-merge-octopus
-git-merge-one-file
-git-merge-ours
-git-merge-recursive
-git-merge-resolve
-git-merge-subtree
-git-mergetool
-git-mergetool--lib
-git-mktag
-git-mktree
-git-name-rev
-git-mv
-git-pack-redundant
-git-pack-objects
-git-pack-refs
-git-parse-remote
-git-patch-id
-git-peek-remote
-git-prune
-git-prune-packed
-git-pull
-git-push
-git-quiltimport
-git-read-tree
-git-rebase
-git-rebase--interactive
-git-receive-pack
-git-reflog
-git-relink
-git-remote
-git-remote-curl
-git-repack
-git-replace
-git-repo-config
-git-request-pull
-git-rerere
-git-reset
-git-rev-list
-git-rev-parse
-git-revert
-git-rm
-git-send-email
-git-send-pack
-git-sh-setup
-git-shell
-git-shortlog
-git-show
-git-show-branch
-git-show-index
-git-show-ref
-git-stage
-git-stash
-git-status
-git-stripspace
-git-submodule
-git-svn
-git-symbolic-ref
-git-tag
-git-tar-tree
-git-unpack-file
-git-unpack-objects
-git-update-index
-git-update-ref
-git-update-server-info
-git-upload-archive
-git-upload-pack
-git-var
-git-verify-pack
-git-verify-tag
-git-web--browse
-git-whatchanged
-git-write-tree
-git-core-*/?*
-gitk-wish
-gitweb/gitweb.cgi
-test-chmtime
-test-ctype
-test-date
-test-delta
-test-dump-cache-tree
-test-genrandom
-test-match-trees
-test-parse-options
-test-path-utils
-test-sha1
-test-sigchain
-common-cmds.h
+/GIT-BUILD-OPTIONS
+/GIT-CFLAGS
+/GIT-GUI-VARS
+/GIT-VERSION-FILE
+/git
+/git-add
+/git-add--interactive
+/git-am
+/git-annotate
+/git-apply
+/git-archimport
+/git-archive
+/git-bisect
+/git-bisect--helper
+/git-blame
+/git-branch
+/git-bundle
+/git-cat-file
+/git-check-attr
+/git-check-ref-format
+/git-checkout
+/git-checkout-index
+/git-cherry
+/git-cherry-pick
+/git-clean
+/git-clone
+/git-commit
+/git-commit-tree
+/git-config
+/git-count-objects
+/git-cvsexportcommit
+/git-cvsimport
+/git-cvsserver
+/git-daemon
+/git-diff
+/git-diff-files
+/git-diff-index
+/git-diff-tree
+/git-difftool
+/git-difftool--helper
+/git-describe
+/git-fast-export
+/git-fast-import
+/git-fetch
+/git-fetch--tool
+/git-fetch-pack
+/git-filter-branch
+/git-fmt-merge-msg
+/git-for-each-ref
+/git-format-patch
+/git-fsck
+/git-fsck-objects
+/git-gc
+/git-get-tar-commit-id
+/git-grep
+/git-hash-object
+/git-help
+/git-http-backend
+/git-http-fetch
+/git-http-push
+/git-imap-send
+/git-index-pack
+/git-init
+/git-init-db
+/git-instaweb
+/git-log
+/git-lost-found
+/git-ls-files
+/git-ls-remote
+/git-ls-tree
+/git-mailinfo
+/git-mailsplit
+/git-merge
+/git-merge-base
+/git-merge-index
+/git-merge-file
+/git-merge-tree
+/git-merge-octopus
+/git-merge-one-file
+/git-merge-ours
+/git-merge-recursive
+/git-merge-resolve
+/git-merge-subtree
+/git-mergetool
+/git-mergetool--lib
+/git-mktag
+/git-mktree
+/git-name-rev
+/git-mv
+/git-notes
+/git-pack-redundant
+/git-pack-objects
+/git-pack-refs
+/git-parse-remote
+/git-patch-id
+/git-peek-remote
+/git-prune
+/git-prune-packed
+/git-pull
+/git-push
+/git-quiltimport
+/git-read-tree
+/git-rebase
+/git-rebase--interactive
+/git-receive-pack
+/git-reflog
+/git-relink
+/git-remote
+/git-remote-curl
+/git-repack
+/git-replace
+/git-repo-config
+/git-request-pull
+/git-rerere
+/git-reset
+/git-rev-list
+/git-rev-parse
+/git-revert
+/git-rm
+/git-send-email
+/git-send-pack
+/git-sh-setup
+/git-shell
+/git-shortlog
+/git-show
+/git-show-branch
+/git-show-index
+/git-show-ref
+/git-stage
+/git-stash
+/git-status
+/git-stripspace
+/git-submodule
+/git-svn
+/git-symbolic-ref
+/git-tag
+/git-tar-tree
+/git-unpack-file
+/git-unpack-objects
+/git-update-index
+/git-update-ref
+/git-update-server-info
+/git-upload-archive
+/git-upload-pack
+/git-var
+/git-verify-pack
+/git-verify-tag
+/git-web--browse
+/git-whatchanged
+/git-write-tree
+/git-core-*/?*
+/gitk-git/gitk-wish
+/gitweb/gitweb.cgi
+/test-chmtime
+/test-ctype
+/test-date
+/test-delta
+/test-dump-cache-tree
+/test-genrandom
+/test-match-trees
+/test-parse-options
+/test-path-utils
+/test-sha1
+/test-sigchain
+/common-cmds.h
 *.tar.gz
 *.dsc
 *.deb
-git.spec
+/git.spec
 *.exe
 *.[aos]
 *.py[co]
 *+
-config.mak
-autom4te.cache
-config.cache
-config.log
-config.status
-config.mak.autogen
-config.mak.append
-configure
-tags
-TAGS
-cscope*
+/config.mak
+/autom4te.cache
+/config.cache
+/config.log
+/config.status
+/config.mak.autogen
+/config.mak.append
+/configure
+/tags
+/TAGS
+/cscope*
 *.obj
 *.lib
 *.sln
@@ -189,5 +191,5 @@ cscope*
 *.user
 *.idb
 *.pdb
-Debug/
-Release/
+/Debug/
+/Release/
diff --git a/Documentation/RelNotes-1.6.5.3.txt b/Documentation/RelNotes-1.6.5.3.txt
new file mode 100644 (file)
index 0000000..b2fad1b
--- /dev/null
@@ -0,0 +1,63 @@
+Git v1.6.5.3 Release Notes
+==========================
+
+Fixes since v1.6.5.2
+--------------------
+
+ * info/grafts file didn't ignore trailing CR at the end of lines.
+
+ * Packages generated on newer FC were unreadable by older versions of
+   RPM as the new default is to use stronger hash.
+
+ * output from "git blame" was unreadable when the file ended in an
+   incomplete line.
+
+ * "git add -i/-p" didn't handle deletion of empty files correctly.
+
+ * "git clone" takes up to two parameters, but did not complain when
+   given more arguments than necessary and silently ignored them.
+
+ * "git cvsimport" did not read files given as command line arguments
+   correctly when it is run from a subdirectory.
+
+ * "git diff --color-words -U0" didn't work correctly.
+
+ * The handling of blank lines at the end of file by "git diff/apply
+   --whitespace" was inconsistent with the other kinds of errors.
+   They are now colored, warned against, and fixed the same way as others.
+
+ * There was no way to allow blank lines at the end of file without
+   allowing extra blanks at the end of lines.  You can use blank-at-eof
+   and blank-at-eol whitespace error class to specify them separately.
+   The old trailing-space error class is now a short-hand to set both.
+
+ * "-p" option to "git format-patch" was supposed to suppress diffstat
+   generation, but it was broken since 1.6.1.
+
+ * "git imap-send" did not compile cleanly with newer OpenSSL.
+
+ * "git help -a" outside of a git repository was broken.
+
+ * "git ls-files -i" was supposed to be inverse of "git ls-files" without -i
+   with respect to exclude patterns, but it was broken since 1.6.5.2.
+
+ * "git ls-remote" outside of a git repository over http was broken.
+
+ * "git rebase -i" gave bogus error message when the command word was
+   misspelled.
+
+ * "git receive-pack" that is run in response to "git push" did not run
+   garbage collection nor update-server-info, but in larger hosting sites,
+   these almost always need to be run.  To help site administrators, the
+   command now runs "gc --auto" and "u-s-i" by setting receive.autogc
+   and receive.updateserverinfo configuration variables, respectively.
+
+ * Release notes spelled the package name with incorrect capitalization.
+
+ * "gitweb" did not escape non-ascii characters correctly in the URL.
+
+ * "gitweb" showed "patch" link even for merge commits.
+
+ * "gitweb" showed incorrect links for blob line numbers in pathinfo mode.
+
+Other minor documentation updates are included.
index abf34e6ace56c8675a8c47d9e1b624bd6c6e481b..6163b4aad33ba07f41546a2464ea542e434c01a6 100644 (file)
-GIT v1.6.6 Release Notes
+Git v1.6.6 Release Notes
 ========================
 
-In this release, "git fsck" defaults to "git fsck --full" and checks
-packfiles, and because of this it will take much longer to complete
-than before.  If you prefer a quicker check only on loose objects (the
-old default), you can say "git fsck --no-full".  This has been
-supported by 1.5.4 and newer versions of git, so it is safe to write
-it in your script even if you use slightly older git on some of your
-machines.
+Notes on behaviour change
+-------------------------
+
+ * In this release, "git fsck" defaults to "git fsck --full" and
+   checks packfiles, and because of this it will take much longer to
+   complete than before.  If you prefer a quicker check only on loose
+   objects (the old default), you can say "git fsck --no-full".  This
+   has been supported by 1.5.4 and newer versions of git, so it is
+   safe to write it in your script even if you use slightly older git
+   on some of your machines.
+
+Preparing yourselves for compatibility issues in 1.7.0
+------------------------------------------------------
+
+In git 1.7.0, which is planned to be the release after 1.6.6, there will
+be a handful of behaviour changes that will break backward compatibility.
+
+These changes were discussed long time ago and existing behaviours have
+been identified as more problematic to the userbase than keeping them for
+the sake of backward compatibility.
+
+When necessary, transition strategy for existing users has been designed
+not to force them running around setting configuration variables and
+updating their scripts in order to keep the traditional behaviour on the
+day their sysadmin decides to install the new version of git.  When we
+switched from "git-foo" to "git foo" in 1.6.0, even though the change had
+been advertised and the transition guide had been provided for a very long
+time, the users procrastinated during the entire transtion period, and
+ended up panicking on the day their sysadmins updated their git.
+
+For changes decided to be in 1.7.0, we have been much louder to strongly
+discourage such procrastination.  If you have been using recent versions
+of git, you would have already seen warnings issued when you exercised
+features whose behaviour will change, with the instruction on how to keep
+the existing behaviour if you choose to.  You hopefully should be well
+prepared already.
+
+Of course, we have also given "this and that will change in 1.7.0; prepare
+yourselves" warnings in the release notes and announcement messages.
+Let's see how well users will fare this time.
+
+ * "git push" into a branch that is currently checked out (i.e. pointed by
+   HEAD in a repository that is not bare) will be refused by default.
+
+   Similarly, "git push $there :$killed" to delete the branch $killed
+   in a remote repository $there, when $killed branch is the current
+   branch pointed at by its HEAD, will be refused by default.
+
+   Setting the configuration variables receive.denyCurrentBranch and
+   receive.denyDeleteCurrent to 'ignore' in the receiving repository
+   can be used to override these safety features.  Versions of git
+   since 1.6.2 have issued a loud warning when you tried to do them
+   without setting the configuration, so repositories of people who
+   still need to be able to perform such a push should already been
+   future proofed.
+
+   Please refer to:
+
+   http://git.or.cz/gitwiki/GitFaq#non-bare
+   http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+   for more details on the reason why this change is needed and the
+   transition process that already took place so far.
+
+ * "git send-email" will not make deep threads by default when sending a
+   patch series with more than two messages.  All messages will be sent as
+   a reply to the first message, i.e. cover letter.  It has been possible
+   to configure send-email to do this by setting sendemail.chainreplyto
+   configuration variable to false.  The only thing the new release will
+   do is to change the default when you haven't configured that variable.
+
+ * "git status" will not be "git commit --dry-run".  This change does not
+   affect you if you run the command without pathspec.
+
+   Nobody sane found the current behaviour of "git status Makefile" useful
+   nor meaningful, and it confused users.  "git commit --dry-run" has been
+   provided as a way to get the current behaviour of this command since
+   1.6.5.
+
+ * "git diff" traditionally treated various "ignore whitespace" options
+   only as a way to filter the patch output.  "git diff --exit-code -b"
+   exited with non-zero status even if all changes were about changing the
+   ammount of whitespace and nothing else.  and "git diff -b" showed the
+   "diff --git" header line for such a change without patch text.
+
+   In 1.7.0, the "ignore whitespaces" will affect the semantics of the
+   diff operation itself.  A change that does not affect anything but
+   whitespaces will be reported with zero exit status when run with
+   --exit-code, and there will not be "diff --git" header for such a
+   change.
 
-In git 1.7.0, which is planned to be the release after 1.6.6, "git
-push" into a branch that is currently checked out will be refused by
-default.
-
-You can choose what should happen upon such a push by setting the
-configuration variable receive.denyCurrentBranch in the receiving
-repository.
-
-Also, "git push $there :$killed" to delete the branch $killed in a remote
-repository $there, when $killed branch is the current branch pointed at by
-its HEAD, will be refused by default.
-
-You can choose what should happen upon such a push by setting the
-configuration variable receive.denyDeleteCurrent in the receiving
-repository.
-
-To ease the transition plan, the receiving repository of such a
-push running this release will issue a big warning when the
-configuration variable is missing.  Please refer to:
-
-  http://git.or.cz/gitwiki/GitFaq#non-bare
-  http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
-
-for more details on the reason why this change is needed and the
-transition plan.
 
 Updates since v1.6.5
 --------------------
 
 (subsystems)
 
+ * various git-gui updates including new translations, wm states, etc.
+
+ * git-svn updates.
+
+ * "git fetch" over http learned a new mode that is different from the
+   traditional "dumb commit walker".
+
 (portability)
 
+ * imap-send can be built on mingw port.
+
 (performance)
 
+ * "git diff -B" has smaller memory footprint.
+
 (usability, bells and whistles)
 
  * The object replace mechanism can be bypassed with --no-replace-objects
    global option given to the "git" program.
 
+ * In configuration files, a few variables that name paths can begin with ~/
+   and ~username/ and they are expanded as expected.
+
+ * "git subcmd -h" now shows short usage help for many more subcommands.
+
+ * "git bisect reset" can reset to an arbitrary commit.
+
+ * "git checkout frotz" when there is no local branch "frotz" but there
+   is only one remote tracking branch "frotz" is taken as a request to
+   start the named branch at the corresponding remote tracking branch.
+
+ * "git describe" can be told to add "-dirty" suffix with "--dirty" option.
+
+ * "git diff" learned --submodule option to show a list of one-line logs
+   instead of differences between the commit object names.
+
+ * "git fetch" learned --all and --multiple options, to run fetch from
+   many repositories, and --prune option to remove remote tracking
+   branches that went stale.  These make "git remote update" and "git
+   remote prune" less necessary (there is no plan to remove "remote
+   update" nor "remote prune", though).
+
  * "git fsck" by default checks the packfiles (i.e. "--full" is the
    default); you can turn it off with "git fsck --no-full".
 
+ * "git grep" can use -F (fixed strings) and -i (ignore case) together.
+
  * import-tars contributed fast-import frontend learned more types of
    compressed tarballs.
 
@@ -59,11 +152,31 @@ Updates since v1.6.5
 
  * "git log --decorate" shows the location of HEAD as well.
 
+ * "git log" and "git rev-list" learned to take revs and pathspecs from
+   the standard input with the new "--stdin" option.
+
+ * "--pretty=format" option to "log" family of commands learned:
+
+   . to wrap text with the "%w()" specifier.
+   . to show reflog information with "%g[sdD]" specifier.
+
+ * "git notes" command to annotate existing commits.
+
+ * "git merge" (and "git pull") learned --ff-only option to make it fail
+   if the merge does not result in a fast-forward.
+
+ * "git mergetool" learned to use p4merge.
+
  * "git rebase -i" learned "reword" that acts like "edit" but immediately
    starts an editor to tweak the log message without returning control to
    the shell, which is done by "edit" to give an opportunity to tweak the
    contents.
 
+ * In "git submodule add <repository> <path>", <path> is now optional and
+   inferred from <repository> the same way "git clone <repository>" does.
+
+ * "git svn" learned to read SVN 1.5+ and SVK merge tickets.
+
  * Author names shown in gitweb output are links to search commits by the
    author.
 
@@ -76,33 +189,8 @@ Fixes since v1.6.5
 All of the fixes in v1.6.5.X maintenance series are included in this
 release, unless otherwise noted.
 
- * "git apply" and "git diff" (including patch output from "git log -p")
-   now flags trailing blank lines as whitespace errors correctly (only
-   "apply --whitespace=fix" stripped them but "apply --whitespace=warn"
-   did not even warn).
-
- * Two whitespace error classes, 'blank-at-eof' and 'blank-at-eol', have
-   been introduced (settable by core.whitespace configuration variable and
-   whitespace attribute).  The 'trailing-space' whitespace error class has
-   become a short-hand to cover both of these and there is no behaviour
-   change for existing set-ups.
-
- * "git cvsimport" did not work well when it is fed filenames from the
-   command line and is not started at the top of the work tree.  We should
-   backport this by merging f6fdbb6 (cvsimport: fix relative argument
-   filenames, 2009-10-19).
-
- * The way gitweb escapes its CGI parameters were broken especially when
-   the parameter was a UTF-8 string.  We may want to backport this to
-   1.6.5.X series by merging 452e225 (gitweb: fix esc_param, 2009-10-13).
-
- * gitweb used to show 'patch' link for merge commits but the output from
-   it is not usable to feed "git am" with.  We may want to backport this
-   to 1.6.5.X series by merging 1655c98 (gitweb: Do not show 'patch' link
-   for merge commits, 2009-10-09).
-
 ---
 exec >/var/tmp/1
 echo O=$(git describe master)
-O=v1.6.5.2-73-g9b12444
+O=v1.6.6-rc0-62-g7fc9d15
 git shortlog --no-merges $O..master --not maint
index d1e2120e15b37a14eec5b3d1de9752da8f6ad7af..a1e36d7e423e01de796ac5f866536280618199d1 100644 (file)
@@ -126,12 +126,20 @@ advice.*::
                Directions on how to stage/unstage/add shown in the
                output of linkgit:git-status[1] and the template shown
                when writing commit messages. Default: true.
+       commitBeforeMerge::
+               Advice shown when linkgit:git-merge[1] refuses to
+               merge to avoid overwritting local changes.
+               Default: true.
 --
 
 core.fileMode::
        If false, the executable bit differences between the index and
        the working copy are ignored; useful on broken filesystems like FAT.
-       See linkgit:git-update-index[1]. True by default.
+       See linkgit:git-update-index[1].
++
+The default is true, except linkgit:git-clone[1] or linkgit:git-init[1]
+will probe and set core.fileMode false if appropriate when the
+repository is created.
 
 core.ignoreCygwinFSTricks::
        This option is only used by Cygwin implementation of Git. If false,
@@ -144,6 +152,18 @@ core.ignoreCygwinFSTricks::
        is true, in which case ignoreCygwinFSTricks is ignored as Cygwin's
        POSIX emulation is required to support core.filemode.
 
+core.ignorecase::
+       If true, this option enables various workarounds to enable
+       git to work better on filesystems that are not case sensitive,
+       like FAT. For example, if a directory listing finds
+       "makefile" when git expects "Makefile", git will assume
+       it is really the same file, and continue to remember it as
+       "Makefile".
++
+The default is false, except linkgit:git-clone[1] or linkgit:git-init[1]
+will probe and set core.ignorecase true if appropriate when the repository
+is created.
+
 core.trustctime::
        If false, the ctime differences between the index and the
        working copy are ignored; useful when the inode change time
@@ -169,9 +189,10 @@ core.autocrlf::
        writing to the filesystem.  The variable can be set to
        'input', in which case the conversion happens only while
        reading from the filesystem but files are written out with
-       `LF` at the end of lines.  Currently, which paths to consider
-       "text" (i.e. be subjected to the autocrlf mechanism) is
-       decided purely based on the contents.
+       `LF` at the end of lines.  A file is considered
+       "text" (i.e. be subjected to the autocrlf mechanism) based on
+       the file's `crlf` attribute, or if `crlf` is unspecified,
+       based on the file's contents.  See linkgit:gitattributes[5].
 
 core.safecrlf::
        If true, makes git check if converting `CRLF` as controlled by
@@ -223,7 +244,11 @@ core.symlinks::
        contain the link text. linkgit:git-update-index[1] and
        linkgit:git-add[1] will not change the recorded type to regular
        file. Useful on filesystems like FAT that do not support
-       symbolic links. True by default.
+       symbolic links.
++
+The default is true, except linkgit:git-clone[1] or linkgit:git-init[1]
+will probe and set core.symlinks false if appropriate when the repository
+is created.
 
 core.gitProxy::
        A "proxy command" to execute (as 'command host port') instead
@@ -380,16 +405,15 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.
 core.excludesfile::
        In addition to '.gitignore' (per-directory) and
        '.git/info/exclude', git looks into this file for patterns
-       of files which are not meant to be tracked.  See
-       linkgit:gitignore[5].
+       of files which are not meant to be tracked.  "{tilde}/" is expanded
+       to the value of `$HOME` and "{tilde}user/" to the specified user's
+       home directory.  See linkgit:gitignore[5].
 
 core.editor::
        Commands such as `commit` and `tag` that lets you edit
        messages by launching an editor uses the value of this
        variable when it is set, and the environment variable
-       `GIT_EDITOR` is not set.  The order of preference is
-       `GIT_EDITOR` environment, `core.editor`, `VISUAL` and
-       `EDITOR` environment variables and then finally `vi`.
+       `GIT_EDITOR` is not set.  See linkgit:git-var[1].
 
 core.pager::
        The command that git will use to paginate output.  Can
@@ -458,6 +482,19 @@ On some file system/operating system combinations, this is unreliable.
 Set this config setting to 'rename' there; However, This will remove the
 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.
++
+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
+given ref itself does not exist, it is not an error, but means that no
+notes should be printed.
++
+This setting defaults to "refs/notes/commits", and can be overridden by
+the `GIT_NOTES_REF` environment variable.
+
 add.ignore-errors::
        Tells 'git-add' to continue adding files when some files cannot be
        added due to indexing errors. Equivalent to the '--ignore-errors'
@@ -598,10 +635,10 @@ color.diff.<slot>::
        Use customized color for diff colorization.  `<slot>` specifies
        which part of the patch to use the specified color, and is one
        of `plain` (context text), `meta` (metainformation), `frag`
-       (hunk header), `old` (removed lines), `new` (added lines),
-       `commit` (commit headers), or `whitespace` (highlighting
-       whitespace errors). The values of these variables may be specified as
-       in color.branch.<slot>.
+       (hunk header), 'func' (function in hunk header), `old` (removed lines),
+       `new` (added lines), `commit` (commit headers), or `whitespace`
+       (highlighting whitespace errors). The values of these variables may be
+       specified as in color.branch.<slot>.
 
 color.grep::
        When set to `always`, always highlight matches.  When `false` (or
@@ -670,6 +707,8 @@ color.ui::
 
 commit.template::
        Specify a file to use as the template for new commit messages.
+       "{tilde}/" is expanded to the value of `$HOME` and "{tilde}user/" to the
+       specified user's home directory.
 
 diff.autorefreshindex::
        When using 'git-diff' to compare with work tree
@@ -1093,6 +1132,14 @@ http.maxRequests::
        How many HTTP requests to launch in parallel. Can be overridden
        by the 'GIT_HTTP_MAX_REQUESTS' environment variable. Default is 5.
 
+http.postBuffer::
+       Maximum size in bytes of the buffer used by smart HTTP
+       transports when POSTing data to the remote system.
+       For requests larger than this buffer size, HTTP/1.1 and
+       Transfer-Encoding: chunked is used to avoid creating a
+       massive pack file locally.  Default is 1 MiB, which is
+       sufficient for most requests.
+
 http.lowSpeedLimit, http.lowSpeedTime::
        If the HTTP transfer speed is less than 'http.lowSpeedLimit'
        for longer than 'http.lowSpeedTime' seconds, the transfer is aborted.
@@ -1360,7 +1407,7 @@ receive.denyCurrentBranch::
 
 receive.denyNonFastForwards::
        If set to true, git-receive-pack will deny a ref update which is
-       not a fast forward. Use this to prevent such an update via a push,
+       not a fast-forward. Use this to prevent such an update via a push,
        even if that push is forced. This configuration variable is
        set when initializing a shared repository.
 
@@ -1394,7 +1441,13 @@ remote.<name>.mirror::
 
 remote.<name>.skipDefaultUpdate::
        If true, this remote will be skipped by default when updating
-       using the update subcommand of linkgit:git-remote[1].
+       using linkgit:git-fetch[1] or the `update` subcommand of
+       linkgit:git-remote[1].
+
+remote.<name>.skipFetchAll::
+       If true, this remote will be skipped by default when updating
+       using linkgit:git-fetch[1] or the `update` subcommand of
+       linkgit:git-remote[1].
 
 remote.<name>.receivepack::
        The default program to execute on the remote side when pushing.  See
index e26b84706f8f29de26036ceec691e8cd6aac4661..8707d0e7404543d0565d72566a8db5946d9467ee 100644 (file)
@@ -14,7 +14,8 @@ endif::git-format-patch[]
 
 ifdef::git-format-patch[]
 -p::
-       Generate patches without diffstat.
+--no-stat::
+       Generate plain patches without any diffstats.
 endif::git-format-patch[]
 
 ifndef::git-format-patch[]
@@ -27,33 +28,40 @@ endif::git-format-patch[]
 -U<n>::
 --unified=<n>::
        Generate diffs with <n> lines of context instead of
-       the usual three. Implies "-p".
+       the usual three.
+ifndef::git-format-patch[]
+       Implies `-p`.
+endif::git-format-patch[]
 
+ifndef::git-format-patch[]
 --raw::
        Generate the raw format.
        {git-diff-core? This is the default.}
+endif::git-format-patch[]
 
+ifndef::git-format-patch[]
 --patch-with-raw::
-       Synonym for "-p --raw".
+       Synonym for `-p --raw`.
+endif::git-format-patch[]
 
 --patience::
        Generate a diff using the "patience diff" algorithm.
 
 --stat[=width[,name-width]]::
        Generate a diffstat.  You can override the default
-       output width for 80-column terminal by "--stat=width".
+       output width for 80-column terminal by `--stat=width`.
        The width of the filename part can be controlled by
        giving another width to it separated by a comma.
 
 --numstat::
-       Similar to \--stat, but shows number of added and
+       Similar to `\--stat`, but shows number of added and
        deleted lines in decimal notation and pathname without
        abbreviation, to make it more machine friendly.  For
        binary files, outputs two `-` instead of saying
        `0 0`.
 
 --shortstat::
-       Output only the last line of the --stat format containing total
+       Output only the last line of the `--stat` format containing total
        number of modified files, as well as number of added and deleted
        lines.
 
@@ -61,24 +69,39 @@ endif::git-format-patch[]
        Output the distribution of relative amount of changes (number of lines added or
        removed) for each sub-directory. Directories with changes below
        a cut-off percent (3% by default) are not shown. The cut-off percent
-       can be set with "--dirstat=limit". Changes in a child directory is not
-       counted for the parent directory, unless "--cumulative" is used.
+       can be set with `--dirstat=limit`. Changes in a child directory is not
+       counted for the parent directory, unless `--cumulative` is used.
 
 --dirstat-by-file[=limit]::
-       Same as --dirstat, but counts changed files instead of lines.
+       Same as `--dirstat`, but counts changed files instead of lines.
 
 --summary::
        Output a condensed summary of extended header information
        such as creations, renames and mode changes.
 
+ifndef::git-format-patch[]
 --patch-with-stat::
-       Synonym for "-p --stat".
-       {git-format-patch? This is the default.}
+       Synonym for `-p --stat`.
+endif::git-format-patch[]
+
+ifndef::git-format-patch[]
 
 -z::
-       NUL-line termination on output.  This affects the --raw
-       output field terminator.  Also output from commands such
-       as "git-log" will be delimited with NUL between commits.
+ifdef::git-log[]
+       Separate the commits with NULs instead of with new newlines.
++
+Also, when `--raw` or `--numstat` has been given, do not munge
+pathnames and use NULs as output field terminators.
+endif::git-log[]
+ifndef::git-log[]
+       When `--raw` or `--numstat` has been given, do not munge
+       pathnames and use NULs as output field terminators.
+endif::git-log[]
++
+Without this option, each pathname output will have TAB, LF, double quotes,
+and backslash characters replaced with `\t`, `\n`, `\"`, and `\\`,
+respectively, and the pathname will be enclosed in double quotes if
+any of those replacements occurred.
 
 --name-only::
        Show only names of changed files.
@@ -117,16 +140,19 @@ The regex can also be set via a diff driver or configuration option, see
 linkgit:gitattributes[1] or linkgit:git-config[1].  Giving it explicitly
 overrides any diff driver or configuration setting.  Diff drivers
 override configuration settings.
+endif::git-format-patch[]
 
 --no-renames::
        Turn off rename detection, even when the configuration
        file gives the default to do so.
 
+ifndef::git-format-patch[]
 --check::
        Warn if changes introduce trailing whitespace
        or an indent that uses a space before a tab. Exits with
        non-zero status if problems are found. Not compatible with
        --exit-code.
+endif::git-format-patch[]
 
 --full-index::
        Instead of the first handful of characters, show the full
@@ -134,16 +160,16 @@ override configuration settings.
        line when generating patch format output.
 
 --binary::
-       In addition to --full-index, output "binary diff" that
-       can be applied with "git apply".
+       In addition to `--full-index`, output a binary diff that
+       can be applied with `git-apply`.
 
 --abbrev[=<n>]::
        Instead of showing the full 40-byte hexadecimal object
        name in diff-raw format output and diff-tree header
        lines, show only a partial prefix.  This is
-       independent of --full-index option above, which controls
+       independent of the `--full-index` option above, which controls
        the diff-patch output format.  Non default number of
-       digits can be specified with --abbrev=<n>.
+       digits can be specified with `--abbrev=<n>`.
 
 -B::
        Break complete rewrite changes into pairs of delete and create.
@@ -154,6 +180,7 @@ override configuration settings.
 -C::
        Detect copies as well as renames.  See also `--find-copies-harder`.
 
+ifndef::git-format-patch[]
 --diff-filter=[ACDMRTUXB*]::
        Select only files that are Added (`A`), Copied (`C`),
        Deleted (`D`), Modified (`M`), Renamed (`R`), have their
@@ -165,6 +192,7 @@ override configuration settings.
        paths are selected if there is any file that matches
        other criteria in the comparison; if there is no file
        that matches other criteria, nothing is selected.
+endif::git-format-patch[]
 
 --find-copies-harder::
        For performance reasons, by default, `-C` option finds copies only
@@ -176,12 +204,13 @@ override configuration settings.
        `-C` option has the same effect.
 
 -l<num>::
-       -M and -C options require O(n^2) processing time where n
+       The `-M` and `-C` options require O(n^2) processing time where n
        is the number of potential rename/copy targets.  This
        option prevents rename/copy detection from running if
        the number of rename/copy targets exceeds the specified
        number.
 
+ifndef::git-format-patch[]
 -S<string>::
        Look for differences that introduce or remove an instance of
        <string>. Note that this is different than the string simply
@@ -189,18 +218,20 @@ override configuration settings.
        linkgit:gitdiffcore[7] for more details.
 
 --pickaxe-all::
-       When -S finds a change, show all the changes in that
+       When `-S` finds a change, show all the changes in that
        changeset, not just the files that contain the change
        in <string>.
 
 --pickaxe-regex::
        Make the <string> not a plain string but an extended POSIX
        regex to match.
+endif::git-format-patch[]
 
 -O<orderfile>::
        Output the patch in the order specified in the
        <orderfile>, which has one shell glob pattern per line.
 
+ifndef::git-format-patch[]
 -R::
        Swap two inputs; that is, show differences from index or
        on-disk file to tree contents.
@@ -212,6 +243,7 @@ override configuration settings.
        not in a subdirectory (e.g. in a bare repository), you
        can name which subdirectory to make the output relative
        to by giving a <path> as an argument.
+endif::git-format-patch[]
 
 -a::
 --text::
@@ -236,13 +268,15 @@ override configuration settings.
        Show the context between diff hunks, up to the specified number
        of lines, thereby fusing hunks that are close to each other.
 
+ifndef::git-format-patch[]
 --exit-code::
        Make the program exit with codes similar to diff(1).
        That is, it exits with 1 if there were differences and
        0 means no differences.
 
 --quiet::
-       Disable all output of the program. Implies --exit-code.
+       Disable all output of the program. Implies `--exit-code`.
+endif::git-format-patch[]
 
 --ext-diff::
        Allow an external diff helper to be executed. If you set an
index 28868747dac778dd1c527f3e40446edd739a5ba9..ab6419fe6e44c7008cbff4e1fae48a70cfb82ac0 100644 (file)
@@ -1,3 +1,6 @@
+--all::
+       Fetch all remotes.
+
 -a::
 --append::
        Append ref names and object names of fetched refs to the
@@ -9,6 +12,11 @@
        `git clone` with `--depth=<depth>` option (see linkgit:git-clone[1])
        by the specified number of commits.
 
+ifndef::git-pull[]
+--dry-run::
+       Show what would be done, without making any changes.
+endif::git-pull[]
+
 -f::
 --force::
        When 'git-fetch' is used with `<rbranch>:<lbranch>`
 --keep::
        Keep downloaded pack.
 
+ifndef::git-pull[]
+--multiple::
+       Allow several <repository> and <group> arguments to be
+       specified. No <refspec>s may be specified.
+
+--prune::
+       After fetching, remove any remote tracking branches which
+       no longer exist on the remote.
+endif::git-pull[]
+
 ifdef::git-pull[]
 --no-tags::
 endif::git-pull[]
index 45ebf87ca3c31bce8645a0130625f4b653929e5c..e93e606f458356275964d0d7b438ca772abba47c 100644 (file)
@@ -76,10 +76,10 @@ OPTIONS
        work tree and add them to the index. This gives the user a chance
        to review the difference before adding modified contents to the
        index.
-
-       This effectively runs ``add --interactive``, but bypasses the
-       initial command menu and directly jumps to `patch` subcommand.
-       See ``Interactive mode'' for details.
++
+This effectively runs `add --interactive`, but bypasses the
+initial command menu and directly jumps to the `patch` subcommand.
+See ``Interactive mode'' for details.
 
 -e, \--edit::
        Open the diff vs. the index in an editor and let the user
index 5ee8c91f2d0a376bf755f0802825ae184cea2a28..c2528a76545a154eb42a430b59caf26e7aad3bef 100644 (file)
@@ -3,7 +3,7 @@ git-apply(1)
 
 NAME
 ----
-git-apply - Apply a patch on a git index file and/or a working tree
+git-apply - Apply a patch to files and/or to the index
 
 
 SYNOPSIS
@@ -20,8 +20,11 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Reads supplied 'diff' output and applies it on a git index file
-and a work tree.
+Reads the supplied diff output (i.e. "a patch") and applies it to files.
+With the `--index` option the patch is also applied to the index, and
+with the `--cache` option the patch is only applied to the index.
+Without these options, the command applies the patch only to files,
+and does not require them to be in a git repository.
 
 OPTIONS
 -------
@@ -34,7 +37,7 @@ OPTIONS
        input.  Turns off "apply".
 
 --numstat::
-       Similar to \--stat, but shows the number of added and
+       Similar to `--stat`, but shows the number of added and
        deleted lines in decimal notation and the pathname without
        abbreviation, to make it more machine friendly.  For
        binary files, outputs two `-` instead of saying
@@ -48,22 +51,22 @@ OPTIONS
 
 --check::
        Instead of applying the patch, see if the patch is
-       applicable to the current work tree and/or the index
+       applicable to the current working tree and/or the index
        file and detects errors.  Turns off "apply".
 
 --index::
-       When --check is in effect, or when applying the patch
+       When `--check` is in effect, or when applying the patch
        (which is the default when none of the options that
        disables it is in effect), make sure the patch is
        applicable to what the current index file records.  If
-       the file to be patched in the work tree is not
+       the file to be patched in the working tree is not
        up-to-date, it is flagged as an error.  This flag also
        causes the index file to be updated.
 
 --cached::
        Apply a patch without touching the working tree. Instead take the
        cached data, apply the patch, and store the result in the index
-       without using the working tree. This implies '--index'.
+       without using the working tree. This implies `--index`.
 
 --build-fake-ancestor=<file>::
        Newer 'git-diff' output has embedded 'index information'
@@ -87,11 +90,13 @@ the information is read from the current index instead.
        rejected hunks in corresponding *.rej files.
 
 -z::
-       When showing the index information, do not munge paths,
-       but use NUL terminated machine readable format.  Without
-       this flag, the pathnames output will have TAB, LF, and
-       backslash characters replaced with `\t`, `\n`, and `\\`,
-       respectively.
+       When `--numstat` has been given, do not munge pathnames,
+       but use a NUL-terminated machine-readable format.
++
+Without this option, each pathname output will have TAB, LF, double quotes,
+and backslash characters replaced with `\t`, `\n`, `\"`, and `\\`,
+respectively, and the pathname will be enclosed in double quotes if
+any of those replacements occurred.
 
 -p<n>::
        Remove <n> leading slashes from traditional diff paths. The
@@ -107,8 +112,8 @@ the information is read from the current index instead.
        By default, 'git-apply' expects that the patch being
        applied is a unified diff with at least one line of context.
        This provides good safety measures, but breaks down when
-       applying a diff generated with --unified=0. To bypass these
-       checks use '--unidiff-zero'.
+       applying a diff generated with `--unified=0`. To bypass these
+       checks use `--unidiff-zero`.
 +
 Note, for the reasons stated above usage of context-free patches is
 discouraged.
@@ -144,7 +149,7 @@ discouraged.
        be useful when importing patchsets, where you want to include certain
        files or directories.
 +
-When --exclude and --include patterns are used, they are examined in the
+When `--exclude` and `--include` patterns are used, they are examined in the
 order they appear on the command line, and the first match determines if a
 patch to each path is used.  A patch to a path that does not match any
 include/exclude pattern is used by default if there is no include pattern
@@ -227,13 +232,13 @@ Submodules
 If the patch contains any changes to submodules then 'git-apply'
 treats these changes as follows.
 
-If --index is specified (explicitly or implicitly), then the submodule
+If `--index` is specified (explicitly or implicitly), then the submodule
 commits must match the index exactly for the patch to apply.  If any
 of the submodules are checked-out, then these check-outs are completely
 ignored, i.e., they are not required to be up-to-date or clean and they
 are not updated.
 
-If --index is not specified, then the submodule commits in the patch
+If `--index` is not specified, then the submodule commits in the patch
 are ignored and only the absence or presence of the corresponding
 subdirectory is checked and (if possible) updated.
 
index 0578a40d841348b5ae484e1fe15ce637ecc4b830..d227cec9ba566caa098c36e8c91b19a8a27fcae5 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run]
-          [(-c | -C) <commit>] [-F <file> | -m <msg>]
+          [(-c | -C) <commit>] [-F <file> | -m <msg>] [--reset-author]
           [--allow-empty] [--no-verify] [-e] [--author=<author>]
           [--cleanup=<mode>] [--] [[-i | -o ]<file>...]
 
@@ -69,6 +69,11 @@ OPTIONS
        Like '-C', but with '-c' the editor is invoked, so that
        the user can further edit the commit message.
 
+--reset-author::
+       When used with -C/-c/--amend options, declare that the
+       authorship of the resulting commit now belongs of the committer.
+       This also renews the author timestamp.
+
 -F <file>::
 --file=<file>::
        Take the commit message from the given file.  Use '-' to
@@ -323,7 +328,7 @@ ENVIRONMENT AND CONFIGURATION VARIABLES
 The editor used to edit the commit log message will be chosen from the
 GIT_EDITOR environment variable, the core.editor configuration variable, the
 VISUAL environment variable, or the EDITOR environment variable (in that
-order).
+order).  See linkgit:git-var[1] for details.
 
 HOOKS
 -----
index 785779e22122156bdc8c58a94d36edb66a8ee266..99a7c14700ffa06090534682283320fef5001815 100644 (file)
@@ -182,10 +182,9 @@ Database Backend
 ----------------
 
 'git-cvsserver' uses one database per git head (i.e. CVS module) to
-store information about the repository for faster access. The
-database doesn't contain any persistent data and can be completely
-regenerated from the git repository at any time. The database
-needs to be updated (i.e. written to) after every commit.
+store information about the repository to maintain consistent
+CVS revision numbers. The database needs to be
+updated (i.e. written to) after every commit.
 
 If the commit is done directly by using `git` (as opposed to
 using 'git-cvsserver') the update will need to happen on the
@@ -204,6 +203,18 @@ write so it might not be enough to grant the users using
 'git-cvsserver' write access to the database file without granting
 them write access to the directory, too.
 
+The database can not be reliably regenerated in a
+consistent form after the branch it is tracking has changed.
+Example: For merged branches, 'git-cvsserver' only tracks
+one branch of development, and after a 'git-merge' an
+incrementally updated database may track a different branch
+than a database regenerated from scratch, causing inconsistent
+CVS revision numbers. `git-cvsserver` has no way of knowing which
+branch it would have picked if it had been run incrementally
+pre-merge. So if you have to fully or partially (from old
+backup) regenerate the database, you should be suspicious
+of pre-existing CVS sandboxes.
+
 You can configure the database backend with the following
 configuration variables:
 
index 2f979167819f0e4500b998b41092e18ef053cead..78b9808aa3a2f5fe2435ea188ac4e73508c2720a 100644 (file)
@@ -8,7 +8,9 @@ git-describe - Show the most recent tag that is reachable from a commit
 
 SYNOPSIS
 --------
+[verse]
 'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] <committish>...
+'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]
 
 DESCRIPTION
 -----------
@@ -27,6 +29,11 @@ OPTIONS
 <committish>...::
        Committish object names to describe.
 
+--dirty[=<mark>]::
+       Describe the working tree.
+       It means describe HEAD and appends <mark> (`-dirty` by
+       default) if the working tree is dirty.
+
 --all::
        Instead of using only the annotated tags, use any ref
        found in `.git/refs/`.  This option enables matching
index 96a6c51a4b83207fa23fc8f09cb02f57b1085ea4..8e9aed67d7f68c0234ee47dc147736bf3b8e5c4f 100644 (file)
@@ -31,7 +31,7 @@ OPTIONS
        Use the diff tool specified by <tool>.
        Valid merge tools are:
        kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff,
-       ecmerge, diffuse, opendiff and araxis.
+       ecmerge, diffuse, opendiff, p4merge and araxis.
 +
 If a diff tool is not specified, 'git-difftool'
 will use the configuration variable `diff.tool`.  If the
index c2f483a8d2aed8dc017f3172e2d5fff4bed2c450..288032c7b88f2f43a2965d5bdbffc8472f0ca281 100644 (file)
@@ -316,7 +316,7 @@ change to the project.
        data
        ('from' SP <committish> LF)?
        ('merge' SP <committish> LF)?
-       (filemodify | filedelete | filecopy | filerename | filedeleteall)*
+       (filemodify | filedelete | filecopy | filerename | filedeleteall | notemodify)*
        LF?
 ....
 
@@ -339,14 +339,13 @@ commit message use a 0 length data.  Commit messages are free-form
 and are not interpreted by Git.  Currently they must be encoded in
 UTF-8, as fast-import does not permit other encodings to be specified.
 
-Zero or more `filemodify`, `filedelete`, `filecopy`, `filerename`
-and `filedeleteall` commands
+Zero or more `filemodify`, `filedelete`, `filecopy`, `filerename`,
+`filedeleteall` and `notemodify` commands
 may be included to update the contents of the branch prior to
 creating the commit.  These commands may be supplied in any order.
 However it is recommended that a `filedeleteall` command precede
-all `filemodify`, `filecopy` and `filerename` commands in the same
-commit, as `filedeleteall`
-wipes the branch clean (see below).
+all `filemodify`, `filecopy`, `filerename` and `notemodify` commands in
+the same commit, as `filedeleteall` wipes the branch clean (see below).
 
 The `LF` after the command is optional (it used to be required).
 
@@ -595,6 +594,40 @@ more memory per active branch (less than 1 MiB for even most large
 projects); so frontends that can easily obtain only the affected
 paths for a commit are encouraged to do so.
 
+`notemodify`
+^^^^^^^^^^^^
+Included in a `commit` command to add a new note (annotating a given
+commit) or change the content of an existing note.  This command has
+two different means of specifying the content of the note.
+
+External data format::
+       The data content for the note was already supplied by a prior
+       `blob` command.  The frontend just needs to connect it to the
+       commit that is to be annotated.
++
+....
+       'N' SP <dataref> SP <committish> LF
+....
++
+Here `<dataref>` can be either a mark reference (`:<idnum>`)
+set by a prior `blob` command, or a full 40-byte SHA-1 of an
+existing Git blob object.
+
+Inline data format::
+       The data content for the note has not been supplied yet.
+       The frontend wants to supply it as part of this modify
+       command.
++
+....
+       'N' SP 'inline' SP <committish> LF
+       data
+....
++
+See below for a detailed description of the `data` command.
+
+In both formats `<committish>` is any of the commit specification
+expressions also accepted by `from` (see above).
+
 `mark`
 ~~~~~~
 Arranges for fast-import to save a reference to the current object, allowing
index f2483d624ead24031ef3cf320a0c151cc6f6cb2b..9b9e5686e4d4e1ca41bba1c5a50715463975cad1 100644 (file)
@@ -10,11 +10,17 @@ SYNOPSIS
 --------
 'git fetch' <options> <repository> <refspec>...
 
+'git fetch' <options> <group>
+
+'git fetch' --multiple <options> [<repository> | <group>]...
+
+'git fetch' --all <options>
+
 
 DESCRIPTION
 -----------
-Fetches named heads or tags from another repository, along with
-the objects necessary to complete them.
+Fetches named heads or tags from one or more other repositories,
+along with the objects necessary to complete them.
 
 The ref names and their object names of fetched refs are stored
 in `.git/FETCH_HEAD`.  This information is left for a later merge
@@ -28,6 +34,10 @@ pointed by remote tags that it does not yet have, then fetch
 those missing tags.  If the other end has tags that point at
 branches you are not interested in, you will not get them.
 
+'git fetch' can fetch from either a single named repository, or
+or from several repositories at once if <group> is given and
+there is a remotes.<group> entry in the configuration file.
+(See linkgit:git-config[1]).
 
 OPTIONS
 -------
index 2b40babb6ba1c5eb99ae2cc7748854f5e4dcd864..394a77a35f2019f5c86f9e90e94b88cf56da04ae 100644 (file)
@@ -159,7 +159,18 @@ to other tags will be rewritten to point to the underlying commit.
 --subdirectory-filter <directory>::
        Only look at the history which touches the given subdirectory.
        The result will contain that directory (and only that) as its
-       project root.
+       project root.  Implies --remap-to-ancestor.
+
+--remap-to-ancestor::
+       Rewrite refs to the nearest rewritten ancestor instead of
+       ignoring them.
++
+Normally, positive refs on the command line are only changed if the
+commit they point to was rewritten.  However, you can limit the extent
+of this rewriting by using linkgit:rev-list[1] arguments, e.g., path
+limiters.  Refs pointing to such excluded commits would then normally
+be ignored.  With this option, they are instead rewritten to point at
+the nearest ancestor that was not excluded.
 
 --prune-empty::
        Some kind of filters will generate empty commits, that left the tree
index 687e66759817bc2f349e8a5a89aa2c6731237b23..f1fd0df08ae3067f8f5b41797e5575d465a859af 100644 (file)
@@ -43,28 +43,28 @@ There are two ways to specify which commits to operate on.
 
 The first rule takes precedence in the case of a single <commit>.  To
 apply the second rule, i.e., format everything since the beginning of
-history up until <commit>, use the '\--root' option: "git format-patch
-\--root <commit>".  If you want to format only <commit> itself, you
-can do this with "git format-patch -1 <commit>".
+history up until <commit>, use the '\--root' option: `git format-patch
+\--root <commit>`.  If you want to format only <commit> itself, you
+can do this with `git format-patch -1 <commit>`.
 
 By default, each output file is numbered sequentially from 1, and uses the
 first line of the commit message (massaged for pathname safety) as
-the filename. With the --numbered-files option, the output file names
+the filename. With the `--numbered-files` option, the output file names
 will only be numbers, without the first line of the commit appended.
 The names of the output files are printed to standard
-output, unless the --stdout option is specified.
+output, unless the `--stdout` option is specified.
 
-If -o is specified, output files are created in <dir>.  Otherwise
+If `-o` is specified, output files are created in <dir>.  Otherwise
 they are created in the current working directory.
 
 By default, the subject of a single patch is "[PATCH] First Line" and
 the subject when multiple patches are output is "[PATCH n/m] First
-Line". To force 1/1 to be added for a single patch, use -n.  To omit
-patch numbers from the subject, use -N
+Line". To force 1/1 to be added for a single patch, use `-n`.  To omit
+patch numbers from the subject, use `-N`.
 
-If given --thread, 'git-format-patch' will generate In-Reply-To and
-References headers to make the second and subsequent patch mails appear
-as replies to the first mail; this also generates a Message-Id header to
+If given `--thread`, `git-format-patch` will generate `In-Reply-To` and
+`References` headers to make the second and subsequent patch mails appear
+as replies to the first mail; this also generates a `Message-Id` header to
 reference.
 
 OPTIONS
@@ -112,7 +112,7 @@ include::diff-options.txt[]
 --attach[=<boundary>]::
        Create multipart/mixed attachment, the first part of
        which is the commit message and the patch itself in the
-       second part, with "Content-Disposition: attachment".
+       second part, with `Content-Disposition: attachment`.
 
 --no-attach::
        Disable the creation of an attachment, overriding the
@@ -121,13 +121,13 @@ include::diff-options.txt[]
 --inline[=<boundary>]::
        Create multipart/mixed attachment, the first part of
        which is the commit message and the patch itself in the
-       second part, with "Content-Disposition: inline".
+       second part, with `Content-Disposition: inline`.
 
 --thread[=<style>]::
 --no-thread::
-       Controls addition of In-Reply-To and References headers to
+       Controls addition of `In-Reply-To` and `References` headers to
        make the second and subsequent mails appear as replies to the
-       first.  Also controls generation of the Message-Id header to
+       first.  Also controls generation of the `Message-Id` header to
        reference.
 +
 The optional <style> argument can be either `shallow` or `deep`.
@@ -136,16 +136,16 @@ series, where the head is chosen from the cover letter, the
 `\--in-reply-to`, and the first patch mail, in this order.  'deep'
 threading makes every mail a reply to the previous one.
 +
-The default is --no-thread, unless the 'format.thread' configuration
-is set.  If --thread is specified without a style, it defaults to the
+The default is `--no-thread`, unless the 'format.thread' configuration
+is set.  If `--thread` is specified without a style, it defaults to the
 style specified by 'format.thread' if any, or else `shallow`.
 +
 Beware that the default for 'git send-email' is to thread emails
-itself.  If you want 'git format-patch' to take care of hreading, you
-will want to ensure that threading is disabled for 'git send-email'.
+itself.  If you want `git format-patch` to take care of threading, you
+will want to ensure that threading is disabled for `git send-email`.
 
 --in-reply-to=Message-Id::
-       Make the first mail (or all the mails with --no-thread) appear as a
+       Make the first mail (or all the mails with `--no-thread`) appear as a
        reply to the given Message-Id, which avoids breaking threads to
        provide a new patch series.
 
@@ -160,16 +160,16 @@ will want to ensure that threading is disabled for 'git send-email'.
        Instead of the standard '[PATCH]' prefix in the subject
        line, instead use '[<Subject-Prefix>]'. This
        allows for useful naming of a patch series, and can be
-       combined with the --numbered option.
+       combined with the `--numbered` option.
 
 --cc=<email>::
-       Add a "Cc:" header to the email headers. This is in addition
+       Add a `Cc:` header to the email headers. This is in addition
        to any configured headers, and may be used multiple times.
 
 --add-header=<header>::
        Add an arbitrary header to the email headers.  This is in addition
        to any configured headers, and may be used multiple times.
-       For example, --add-header="Organization: git-foo"
+       For example, `--add-header="Organization: git-foo"`
 
 --cover-letter::
        In addition to the patches, generate a cover letter file
diff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt
new file mode 100644 (file)
index 0000000..67aec06
--- /dev/null
@@ -0,0 +1,178 @@
+git-http-backend(1)
+===================
+
+NAME
+----
+git-http-backend - Server side implementation of Git over HTTP
+
+SYNOPSIS
+--------
+[verse]
+'git-http-backend'
+
+DESCRIPTION
+-----------
+A simple CGI program to serve the contents of a Git repository to Git
+clients accessing the repository over http:// and https:// protocols.
+The program supports clients fetching using both the smart HTTP protcol
+and the backwards-compatible dumb HTTP protocol, as well as clients
+pushing using the smart HTTP protocol.
+
+By default, only the `upload-pack` service is enabled, which serves
+'git-fetch-pack' and 'git-ls-remote' clients, which are invoked from
+'git-fetch', 'git-pull', and 'git-clone'.  If the client is authenticated,
+the `receive-pack` service is enabled, which serves 'git-send-pack'
+clients, which is invoked from 'git-push'.
+
+SERVICES
+--------
+These services can be enabled/disabled using the per-repository
+configuration file:
+
+http.getanyfile::
+       This serves older Git clients which are unable to use the
+       upload pack service.  When enabled, clients are able to read
+       any file within the repository, including objects that are
+       no longer reachable from a branch but are still present.
+       It is enabled by default, but a repository can disable it
+       by setting this configuration item to `false`.
+
+http.uploadpack::
+       This serves 'git-fetch-pack' and 'git-ls-remote' clients.
+       It is enabled by default, but a repository can disable it
+       by setting this configuration item to `false`.
+
+http.receivepack::
+       This serves 'git-send-pack' clients, allowing push.  It is
+       disabled by default for anonymous users, and enabled by
+       default for users authenticated by the web server.  It can be
+       disabled by setting this item to `false`, or enabled for all
+       users, including anonymous users, by setting it to `true`.
+
+URL TRANSLATION
+---------------
+To determine the location of the repository on disk, 'git-http-backend'
+concatenates the environment variables PATH_INFO, which is set
+automatically by the web server, and GIT_PROJECT_ROOT, which must be set
+manually in the web server configuration.  If GIT_PROJECT_ROOT is not
+set, 'git-http-backend' reads PATH_TRANSLATED, which is also set
+automatically by the web server.
+
+EXAMPLES
+--------
+All of the following examples map 'http://$hostname/git/foo/bar.git'
+to '/var/www/git/foo/bar.git'.
+
+Apache 2.x::
+       Ensure mod_cgi, mod_alias, and mod_env are enabled, set
+       GIT_PROJECT_ROOT (or DocumentRoot) appropriately, and
+       create a ScriptAlias to the CGI:
++
+----------------------------------------------------------------
+SetEnv GIT_PROJECT_ROOT /var/www/git
+ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
+----------------------------------------------------------------
++
+To enable anonymous read access but authenticated write access,
+require authorization with a LocationMatch directive:
++
+----------------------------------------------------------------
+<LocationMatch "^/git/.*/git-receive-pack$">
+       AuthType Basic
+       AuthName "Git Access"
+       Require group committers
+       ...
+</LocationMatch>
+----------------------------------------------------------------
++
+To require authentication for both reads and writes, use a Location
+directive around the repository, or one of its parent directories:
++
+----------------------------------------------------------------
+<Location /git/private>
+       AuthType Basic
+       AuthName "Private Git Access"
+       Require group committers
+       ...
+</Location>
+----------------------------------------------------------------
++
+To serve gitweb at the same url, use a ScriptAliasMatch to only
+those URLs that 'git-http-backend' can handle, and forward the
+rest to gitweb:
++
+----------------------------------------------------------------
+ScriptAliasMatch \
+       "(?x)^/git/(.*/(HEAD | \
+                       info/refs | \
+                       objects/(info/[^/]+ | \
+                                [0-9a-f]{2}/[0-9a-f]{38} | \
+                                pack/pack-[0-9a-f]{40}\.(pack|idx)) | \
+                       git-(upload|receive)-pack))$" \
+       /usr/libexec/git-core/git-http-backend/$1
+
+ScriptAlias /git/ /var/www/cgi-bin/gitweb.cgi/
+----------------------------------------------------------------
+
+Accelerated static Apache 2.x::
+       Similar to the above, but Apache can be used to return static
+       files that are stored on disk.  On many systems this may
+       be more efficient as Apache can ask the kernel to copy the
+       file contents from the file system directly to the network:
++
+----------------------------------------------------------------
+SetEnv GIT_PROJECT_ROOT /var/www/git
+
+AliasMatch ^/git/(.*/objects/[0-9a-f]{2}/[0-9a-f]{38})$          /var/www/git/$1
+AliasMatch ^/git/(.*/objects/pack/pack-[0-9a-f]{40}.(pack|idx))$ /var/www/git/$1
+ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
+----------------------------------------------------------------
++
+This can be combined with the gitweb configuration:
++
+----------------------------------------------------------------
+SetEnv GIT_PROJECT_ROOT /var/www/git
+
+AliasMatch ^/git/(.*/objects/[0-9a-f]{2}/[0-9a-f]{38})$          /var/www/git/$1
+AliasMatch ^/git/(.*/objects/pack/pack-[0-9a-f]{40}.(pack|idx))$ /var/www/git/$1
+ScriptAliasMatch \
+       "(?x)^/git/(.*/(HEAD | \
+                       info/refs | \
+                       objects/info/[^/]+ | \
+                       git-(upload|receive)-pack))$" \
+       /usr/libexec/git-core/git-http-backend/$1
+ScriptAlias /git/ /var/www/cgi-bin/gitweb.cgi/
+----------------------------------------------------------------
+
+
+ENVIRONMENT
+-----------
+'git-http-backend' relies upon the CGI environment variables set
+by the invoking web server, including:
+
+* PATH_INFO (if GIT_PROJECT_ROOT is set, otherwise PATH_TRANSLATED)
+* REMOTE_USER
+* REMOTE_ADDR
+* CONTENT_TYPE
+* QUERY_STRING
+* REQUEST_METHOD
+
+The backend process sets GIT_COMMITTER_NAME to '$REMOTE_USER' and
+GIT_COMMITTER_EMAIL to '$\{REMOTE_USER}@http.$\{REMOTE_ADDR\}',
+ensuring that any reflogs created by 'git-receive-pack' contain some
+identifying information of the remote user who performed the push.
+
+All CGI environment variables are available to each of the hooks
+invoked by the 'git-receive-pack'.
+
+Author
+------
+Written by Shawn O. Pearce <spearce@spearce.org>.
+
+Documentation
+--------------
+Documentation by Shawn O. Pearce <spearce@spearce.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
index aef383e0b142bd603b77620cad720c102d70c4b7..ddf7a18dc42ef4d5f8ad44aed89ee5b17bb0e9a6 100644 (file)
@@ -82,11 +82,11 @@ destination side.
 
 Without '--force', the <src> ref is stored at the remote only if
 <dst> does not exist, or <dst> is a proper subset (i.e. an
-ancestor) of <src>.  This check, known as "fast forward check",
+ancestor) of <src>.  This check, known as "fast-forward check",
 is performed in order to avoid accidentally overwriting the
 remote ref and lose other peoples' commits from there.
 
-With '--force', the fast forward check is disabled for all refs.
+With '--force', the fast-forward check is disabled for all refs.
 
 Optionally, a <ref> parameter can be prefixed with a plus '+' sign
 to disable the fast-forward check only on that ref.
index 021066e95d88624d2ec6c90a9218a4fd70ec17b7..625723e41fa072523fa850e37534677cfe8af631 100644 (file)
@@ -48,8 +48,10 @@ OPTIONS
 
 -i::
 --ignored::
-       Show ignored files in the output.
-       Note that this also reverses any exclude list present.
+       Show only ignored files in the output. When showing files in the
+       index, print only those matched by an exclude pattern. When
+       showing "other" files, show only those matched by an exclude
+       pattern.
 
 -s::
 --stage::
index 996c3fcc6cde0b7ce1acf6bfb3815bac4dd6176c..b81ac98cf0d95ab961b228e514c8fdabc152a21e 100644 (file)
@@ -8,7 +8,7 @@ git-mailinfo - Extracts patch and authorship from a single e-mail message
 
 SYNOPSIS
 --------
-'git mailinfo' [-k] [-u | --encoding=<encoding> | -n] [--scissors] <msg> <patch>
+'git mailinfo' [-k|-b] [-u | --encoding=<encoding> | -n] [--scissors] <msg> <patch>
 
 
 DESCRIPTION
@@ -32,6 +32,11 @@ OPTIONS
        munging, and is most useful when used to read back
        'git-format-patch -k' output.
 
+-b::
+       When -k is not in effect, all leading strings bracketed with '['
+       and ']' pairs are stripped.  This option limits the stripping to
+       only the pairs whose bracketed string contains the word "PATCH".
+
 -u::
        The commit log message, author name and author email are
        taken from the e-mail, and after minimally decoding MIME
index 68ed6c095664b8f06635ba7eaa85b85086b24280..4a6f7f3a2d189c40e4abfe4f74178f79d1c563fd 100644 (file)
@@ -27,7 +27,7 @@ OPTIONS
        Use the merge resolution program specified by <tool>.
        Valid merge tools are:
        kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge,
-       diffuse, tortoisemerge, opendiff and araxis.
+       diffuse, tortoisemerge, opendiff, p4merge and araxis.
 +
 If a merge resolution program is not specified, 'git-mergetool'
 will use the configuration variable `merge.tool`.  If the
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
new file mode 100644 (file)
index 0000000..94cceb1
--- /dev/null
@@ -0,0 +1,60 @@
+git-notes(1)
+============
+
+NAME
+----
+git-notes - Add/inspect commit notes
+
+SYNOPSIS
+--------
+[verse]
+'git-notes' (edit [-F <file> | -m <msg>] | show) [commit]
+
+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:".
+
+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".
+
+
+SUBCOMMANDS
+-----------
+
+edit::
+       Edit the notes for a given commit (defaults to HEAD).
+
+show::
+       Show the notes for a given commit (defaults to HEAD).
+
+
+OPTIONS
+-------
+-m <msg>::
+       Use the given note message (instead of prompting).
+       If multiple `-m` (or `-F`) options are given, their
+       values are concatenated as separate paragraphs.
+
+-F <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.
+
+
+Author
+------
+Written by Johannes Schindelin <johannes.schindelin@gmx.de>
+
+Documentation
+-------------
+Documentation by Johannes Schindelin
+
+GIT
+---
+Part of the linkgit:git[7] suite
index 2e4992970e84a1789cb97d35969976d12e5a30e1..f54d433d36b50184bd83f217c310935d1bbb541f 100644 (file)
@@ -9,8 +9,9 @@ git-pack-objects - Create a packed archive of objects
 SYNOPSIS
 --------
 [verse]
-'git pack-objects' [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty]
-       [--local] [--incremental] [--window=N] [--depth=N] [--all-progress]
+'git pack-objects' [-q | --progress | --all-progress] [--all-progress-implied]
+       [--no-reuse-delta] [--delta-base-offset] [--non-empty]
+       [--local] [--incremental] [--window=N] [--depth=N]
        [--revs [--unpacked | --all]*] [--stdout | base-name]
        [--keep-true-parents] < object-list
 
@@ -137,7 +138,7 @@ base-name::
 
 --all-progress::
        When --stdout is specified then progress report is
-       displayed during the object count and deltification phases
+       displayed during the object count and compression phases
        but inhibited during the write-out phase. The reason is
        that in some cases the output stream is directly linked
        to another command which may wish to display progress
@@ -146,6 +147,11 @@ base-name::
        report for the write-out phase as well even if --stdout is
        used.
 
+--all-progress-implied::
+       This is used to imply --all-progress whenever progress display
+       is activated.  Unlike --all-progress this flag doesn't actually
+       force any progress display by itself.
+
 -q::
        This flag makes the command not to report its progress
        on the standard error stream.
index 37c88953d1a08c0498c794aeb4d0d38eac8a36db..52c0538df528ecbb5b755a2e75fc1715762bfc00 100644 (file)
@@ -50,9 +50,9 @@ updated.
 +
 The object referenced by <src> is used to update the <dst> reference
 on the remote side, but by default this is only allowed if the
-update can fast forward <dst>.  By having the optional leading `{plus}`,
+update can fast-forward <dst>.  By having the optional leading `{plus}`,
 you can tell git to update the <dst> ref even when the update is not a
-fast forward.  This does *not* attempt to merge <src> into <dst>.  See
+fast-forward.  This does *not* attempt to merge <src> into <dst>.  See
 EXAMPLES below for details.
 +
 `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
@@ -60,7 +60,7 @@ EXAMPLES below for details.
 Pushing an empty <src> allows you to delete the <dst> ref from
 the remote repository.
 +
-The special refspec `:` (or `{plus}:` to allow non-fast forward updates)
+The special refspec `:` (or `{plus}:` to allow non-fast-forward updates)
 directs git to push "matching" branches: for every branch that exists on
 the local side, the remote side is updated if a branch of the same name
 already exists on the remote side.  This is the default operation mode
@@ -176,10 +176,10 @@ summary::
        For a successfully pushed ref, the summary shows the old and new
        values of the ref in a form suitable for using as an argument to
        `git log` (this is `<old>..<new>` in most cases, and
-       `<old>...<new>` for forced non-fast forward updates). For a
+       `<old>...<new>` for forced non-fast-forward updates). For a
        failed update, more details are given for the failure.
        The string `rejected` indicates that git did not try to send the
-       ref at all (typically because it is not a fast forward). The
+       ref at all (typically because it is not a fast-forward). The
        string `remote rejected` indicates that the remote end refused
        the update; this rejection is typically caused by a hook on the
        remote side. The string `remote failure` indicates that the
@@ -347,9 +347,9 @@ git push origin :experimental::
 
 git push origin {plus}dev:master::
        Update the origin repository's master branch with the dev branch,
-       allowing non-fast forward updates.  *This can leave unreferenced
+       allowing non-fast-forward updates.  *This can leave unreferenced
        commits dangling in the origin repository.*  Consider the
-       following situation, where a fast forward is not possible:
+       following situation, where a fast-forward is not possible:
 +
 ----
            o---o---o---A---B  origin/master
index 4a932b08c6ed4f1de46d51fd92046d5190f831a7..a10ce4ba40cc509a0f3a8f39551310fdd9231e71 100644 (file)
@@ -144,7 +144,7 @@ Two Tree Merge
 Typically, this is invoked as `git read-tree -m $H $M`, where $H
 is the head commit of the current repository, and $M is the head
 of a foreign tree, which is simply ahead of $H (i.e. we are in a
-fast forward situation).
+fast-forward situation).
 
 When two trees are specified, the user is telling 'git-read-tree'
 the following:
index 33e0ef1f6d48c22eddb2b1a7273065a4269924ae..ca5e1e8653be7a1d2b0911ec572ff0a85300ebb9 100644 (file)
@@ -228,13 +228,23 @@ OPTIONS
        Use merging strategies to rebase.  When the recursive (default) merge
        strategy is used, this allows rebase to be aware of renames on the
        upstream side.
++
+Note that a rebase merge works by replaying each commit from the working
+branch on top of the <upstream> branch.  Because of this, when a merge
+conflict happens, the side reported as 'ours' is the so-far rebased
+series, starting with <upstream>, and 'theirs' is the working branch.  In
+other words, the sides are swapped.
 
 -s <strategy>::
 --strategy=<strategy>::
        Use the given merge strategy.
-       If there is no `-s` option, a built-in list of strategies
-       is used instead ('git-merge-recursive' when merging a single
-       head, 'git-merge-octopus' otherwise).  This implies --merge.
+       If there is no `-s` option 'git-merge-recursive' is used
+       instead.  This implies --merge.
++
+Because 'git-rebase' replays each commit from the working branch
+on top of the <upstream> branch using the given strategy, using
+the 'ours' strategy simply discards all patches from the <branch>,
+which makes little sense.
 
 -q::
 --quiet::
index 514f03c97903aa0be41a4a8f0df236ccb68280b0..cb5f4052806d81b04d4a096d3697e0230d13e8ed 100644 (file)
@@ -20,7 +20,7 @@ The UI for the protocol is on the 'git-send-pack' side, and the
 program pair is meant to be used to push updates to remote
 repository.  For pull operations, see linkgit:git-fetch-pack[1].
 
-The command allows for creation and fast forwarding of sha1 refs
+The command allows for creation and fast-forwarding of sha1 refs
 (heads/tags) on the remote end (strictly speaking, it is the
 local end 'git-receive-pack' runs, but to the user who is sitting at
 the send-pack end, it is updating the remote.  Confused?)
index 173ee232f2cd24231ab95e3337da555be27a4746..8beb42dbb9952059650d51d2e7a5ca2efe9591fb 100644 (file)
@@ -34,15 +34,51 @@ Commands are given by the caller on the helper's standard input, one per line.
        value of the ref. A space-separated list of attributes follows
        the name; unrecognized attributes are ignored. After the
        complete list, outputs a blank line.
++
+If 'push' is supported this may be called as 'list for-push'
+to obtain the current refs prior to sending one or more 'push'
+commands to the helper.
+
+'option' <name> <value>::
+       Set the transport helper option <name> to <value>.  Outputs a
+       single line containing one of 'ok' (option successfully set),
+       'unsupported' (option not recognized) or 'error <msg>'
+       (option <name> is supported but <value> is not correct
+       for it).  Options should be set before other commands,
+       and may how those commands behave.
++
+Supported if the helper has the "option" capability.
 
 'fetch' <sha1> <name>::
-       Fetches the given object, writing the necessary objects to the
-       database. Outputs a blank line when the fetch is
-       complete. Only objects which were reported in the ref list
-       with a sha1 may be fetched this way.
+       Fetches the given object, writing the necessary objects
+       to the database.  Fetch commands are sent in a batch, one
+       per line, and the batch is terminated with a blank line.
+       Outputs a single blank line when all fetch commands in the
+       same batch are complete. Only objects which were reported
+       in the ref list with a sha1 may be fetched this way.
++
+Optionally may output a 'lock <file>' line indicating a file under
+GIT_DIR/objects/pack which is keeping a pack until refs can be
+suitably updated.
 +
 Supported if the helper has the "fetch" capability.
 
+'push' +<src>:<dst>::
+       Pushes the given <src> commit or branch locally to the
+       remote branch described by <dst>.  A batch sequence of
+       one or more push commands is terminated with a blank line.
++
+Zero or more protocol options may be entered after the last 'push'
+command, before the batch's terminating blank line.
++
+When the push is complete, outputs one or more 'ok <dst>' or
+'error <dst> <why>?' lines to indicate success or failure of
+each pushed ref.  The status report output is terminated by
+a blank line.  The option field <why> may be quoted in a C
+style string if it contains an LF.
++
+Supported if the helper has the "push" capability.
+
 If a fatal error occurs, the program writes the error message to
 stderr and exits. The caller should expect that a suitable error
 message has been printed if the child closes the connection without
@@ -57,10 +93,49 @@ CAPABILITIES
 'fetch'::
        This helper supports the 'fetch' command.
 
+'option'::
+       This helper supports the option command.
+
+'push'::
+       This helper supports the 'push' command.
+
 REF LIST ATTRIBUTES
 -------------------
 
-None are defined yet, but the caller must accept any which are supplied.
+'for-push'::
+       The caller wants to use the ref list to prepare push
+       commands.  A helper might chose to acquire the ref list by
+       opening a different type of connection to the destination.
+
+OPTIONS
+-------
+'option verbosity' <N>::
+       Change the level of messages displayed by the helper.
+       When N is 0 the end-user has asked the process to be
+       quiet, and the helper should produce only error output.
+       N of 1 is the default level of verbosity, higher values
+       of N correspond to the number of -v flags passed on the
+       command line.
+
+'option progress' \{'true'|'false'\}::
+       Enable (or disable) progress messages displayed by the
+       transport helper during a command.
+
+'option depth' <depth>::
+       Deepen the history of a shallow repository.
+
+'option followtags' \{'true'|'false'\}::
+       If enabled the helper should automatically fetch annotated
+       tag objects if the object the tag points at was transferred
+       during the fetch command.  If the tag is not fetched by
+       the helper a second fetch command will usually be sent to
+       ask for the tag specifically.  Some helpers may be able to
+       use this option to avoid a second network connection.
+
+'option dry-run' \{'true'|'false'\}:
+       If true, pretend the operation completed successfully,
+       but don't actually change any repository data.  For most
+       helpers this only applies to the 'push', if supported.
 
 Documentation
 -------------
index 82a3d296736e515d4f99732e31369c8cdccc2cfd..c272c92d4bef4d451505634a412eaf3a10762d95 100644 (file)
@@ -13,10 +13,10 @@ SYNOPSIS
 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
 'git remote rename' <old> <new>
 'git remote rm' <name>
-'git remote set-head' <name> [-a | -d | <branch>]
-'git remote show' [-n] <name>
+'git remote set-head' <name> (-a | -d | <branch>)
+'git remote' [-v | --verbose] 'show' [-n] <name>
 'git remote prune' [-n | --dry-run] <name>
-'git remote update' [-p | --prune] [group | remote]...
+'git remote' [-v | --verbose] 'update' [-p | --prune] [group | remote]...
 
 DESCRIPTION
 -----------
@@ -30,6 +30,7 @@ OPTIONS
 -v::
 --verbose::
        Be a little more verbose and show remote url after name.
+       NOTE: This must be placed between `remote` and `subcommand`.
 
 
 COMMANDS
index 8adc1ef55c620d04fc10b8d6ccb0b9c90765a753..65a0da508a6be033e46850dc46b4fb700c93ad50 100644 (file)
@@ -17,31 +17,35 @@ DESCRIPTION
 Adds a 'replace' reference in `.git/refs/replace/`
 
 The name of the 'replace' reference is the SHA1 of the object that is
-replaced. The content of the replace reference is the SHA1 of the
+replaced. The content of the 'replace' reference is the SHA1 of the
 replacement object.
 
-Unless `-f` is given, the replace reference must not yet exist in
+Unless `-f` is given, the 'replace' reference must not yet exist in
 `.git/refs/replace/` directory.
 
-Replace references will be used by default by all git commands except
-those doing reachability traversal (prune, pack transfer and fsck).
+Replacement references will be used by default by all git commands
+except those doing reachability traversal (prune, pack transfer and
+fsck).
 
-It is possible to disable use of replacement refs for any command
-using the --no-replace-objects option just after "git".
+It is possible to disable use of replacement references for any
+command using the `--no-replace-objects` option just after 'git'.
 
-For example if commit "foo" has been replaced by commit "bar":
+For example if commit 'foo' has been replaced by commit 'bar':
 
 ------------------------------------------------
-$ git --no-replace-object cat-file commit foo
+$ git --no-replace-objects cat-file commit foo
 ------------------------------------------------
 
-show information about commit "foo", while:
+shows information about commit 'foo', while:
 
 ------------------------------------------------
 $ git cat-file commit foo
 ------------------------------------------------
 
-show information about commit "bar".
+shows information about commit 'bar'.
+
+The 'GIT_NO_REPLACE_OBJECTS' environment variable can be set to
+achieve the same effect as the `--no-replace-objects` option.
 
 OPTIONS
 -------
index 469cf6dbacb8de24b5dd0dd78d63dd9ecc8fbd01..2d27e405a39c77f1a7d7507db2f063a4819bec47 100644 (file)
@@ -150,7 +150,7 @@ Automatic merge failed; fix conflicts and then commit the result.
 $ git reset --hard                 <2>
 $ git pull . topic/branch          <3>
 Updating from 41223... to 13134...
-Fast forward
+Fast-forward
 $ git reset --hard ORIG_HEAD       <4>
 ------------
 +
@@ -161,7 +161,7 @@ right now, so you decide to do that later.
 which is a synonym for "git reset --hard HEAD" clears the mess
 from the index file and the working tree.
 <3> Merge a topic branch into the current branch, which resulted
-in a fast forward.
+in a fast-forward.
 <4> But you decided that the topic branch is not ready for public
 consumption yet.  "pull" or "merge" always leaves the original
 tip of the current branch in ORIG_HEAD, so resetting hard to it
index 767cf4d4bdcdfbb6e76b3680b0e89af2186d632a..8c482f40b98f81460da7230a8656a9a4a97d77df 100644 (file)
@@ -60,8 +60,8 @@ The --bcc option must be repeated for each user you want on the bcc list.
 The --cc option must be repeated for each user you want on the cc list.
 
 --compose::
-       Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
-       introductory message for the patch series.
+       Invoke a text editor (see GIT_EDITOR in linkgit:git-var[1])
+       to edit an introductory message for the patch series.
 +
 When '--compose' is used, git send-email will use the From, Subject, and
 In-Reply-To headers specified in the message. If the body of the message
@@ -108,9 +108,10 @@ Sending
 --envelope-sender=<address>::
        Specify the envelope sender used to send the emails.
        This is useful if your default address is not the address that is
-       subscribed to a list. If you use the sendmail binary, you must have
-       suitable privileges for the -f parameter. Default is the value of
-       the 'sendemail.envelopesender' configuration variable; if that is
+       subscribed to a list. In order to use the 'From' address, set the
+       value to "auto". If you use the sendmail binary, you must have
+       suitable privileges for the -f parameter.  Default is the value of the
+       'sendemail.envelopesender' configuration variable; if that is
        unspecified, choosing the envelope sender is left to your MTA.
 
 --smtp-encryption=<encryption>::
index 399821832c2a5cd6a718a7dc37a87e6b5bc0b213..5a04c6eaf78adb8c6ccff573d873392e19640fdd 100644 (file)
@@ -105,11 +105,11 @@ name. See linkgit:git-rev-parse[1].
 
 Without '--force', the <src> ref is stored at the remote only if
 <dst> does not exist, or <dst> is a proper subset (i.e. an
-ancestor) of <src>.  This check, known as "fast forward check",
+ancestor) of <src>.  This check, known as "fast-forward check",
 is performed in order to avoid accidentally overwriting the
 remote ref and lose other peoples' commits from there.
 
-With '--force', the fast forward check is disabled for all refs.
+With '--force', the fast-forward check is disabled for all refs.
 
 Optionally, a <ref> parameter can be prefixed with a plus '+' sign
 to disable the fast-forward check only on that ref.
index f4429bdc6863ce1d115b47282f2c04a4d9093d29..70f400b2664df422160195a9615d566152a1b877 100644 (file)
@@ -8,7 +8,7 @@ git-show-ref - List references in a local repository
 SYNOPSIS
 --------
 [verse]
-'git show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]
+'git show-ref' [-q|--quiet] [--verify] [--head] [-d|--dereference]
             [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags]
             [--heads] [--] <pattern>...
 'git show-ref' --exclude-existing[=<pattern>] < ref-list
@@ -30,7 +30,6 @@ the `.git` directory.
 OPTIONS
 -------
 
--h::
 --head::
 
        Show the HEAD reference.
index 5ccdd18c89381e81fc616facb22bd7fee6faf964..4ef70c42ebf512ee6dc946ca2ceee284b3211b54 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git submodule' [--quiet] add [-b branch]
-             [--reference <repository>] [--] <repository> <path>
+             [--reference <repository>] [--] <repository> [<path>]
 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
@@ -69,7 +69,11 @@ add::
        to the changeset to be committed next to the current
        project: the current project is termed the "superproject".
 +
-This requires two arguments: <repository> and <path>.
+This requires at least one argument: <repository>. The optional
+argument <path> is the relative location for the cloned submodule
+to exist in the superproject. If <path> is not given, the
+"humanish" part of the source repository is used ("repo" for
+"/path/to/repo.git" and "foo" for "host.xz:foo/.git").
 +
 <repository> is the URL of the new submodule's origin repository.
 This may be either an absolute URL, or (if it begins with ./
index 1812890a7e986b2632972390ea4646331c0c922b..4cdca0d87435b4995e5c051750a6dd02720006f8 100644 (file)
@@ -320,6 +320,13 @@ Any other arguments are passed directly to 'git log'
        directories.  The output is suitable for appending to
        the $GIT_DIR/info/exclude file.
 
+'mkdirs'::
+       Attempts to recreate empty directories that core git cannot track
+       based on information in $GIT_DIR/svn/<refname>/unhandled.log files.
+       Empty directories are automatically recreated when using
+       "git svn clone" and "git svn rebase", so "mkdirs" is intended
+       for use after commands like "git checkout" or "git reset".
+
 'commit-diff'::
        Commits the diff of two tree-ish arguments from the
        command-line.  This command does not rely on being inside an `git svn
@@ -735,6 +742,16 @@ merges you've made.  Furthermore, if you merge or pull from a git branch
 that is a mirror of an SVN branch, 'dcommit' may commit to the wrong
 branch.
 
+If you do merge, note the following rule: 'git svn dcommit' will
+attempt to commit on top of the SVN commit named in
+------------------------------------------------------------------------
+git log --grep=^git-svn-id: --first-parent -1
+------------------------------------------------------------------------
+You 'must' therefore ensure that the most recent commit of the branch
+you want to dcommit to is the 'first' parent of the merge.  Chaos will
+ensue otherwise, especially if the first parent is an older commit on
+the same SVN branch.
+
 'git clone' does not clone branches under the refs/remotes/ hierarchy or
 any 'git svn' metadata, or config.  So repositories created and managed with
 using 'git svn' should use 'rsync' for cloning, if cloning is to be done
index 25e0bbea86caf1234da1746d4a2082cfb80129bd..6052484ab9e28d5565067885b1c4ed5667e8e90f 100644 (file)
@@ -99,6 +99,10 @@ in the index e.g. when merging in a commit;
 thus, in case the assumed-untracked file is changed upstream,
 you will need to handle the situation manually.
 
+--really-refresh::
+       Like '--refresh', but checks stat information unconditionally,
+       without regard to the "assume unchanged" setting.
+
 -g::
 --again::
        Runs 'git-update-index' itself on the paths whose index
@@ -308,7 +312,7 @@ Configuration
 -------------
 
 The command honors `core.filemode` configuration variable.  If
-your repository is on an filesystem whose executable bits are
+your repository is on a filesystem whose executable bits are
 unreliable, this should be set to 'false' (see linkgit:git-config[1]).
 This causes the command to ignore differences in file modes recorded
 in the index and the file mode on the filesystem if they differ only on
index e2f4c0901bcb4bcc5361e400ff40d70062c77ae6..ef6aa81872eb9e67eaf8183cfd5359d97e6c772b 100644 (file)
@@ -36,6 +36,20 @@ GIT_AUTHOR_IDENT::
 GIT_COMMITTER_IDENT::
     The person who put a piece of code into git.
 
+GIT_EDITOR::
+    Text editor for use by git commands.  The value is meant to be
+    interpreted by the shell when it is used.  Examples: `~/bin/vi`,
+    `$SOME_ENVIRONMENT_VARIABLE`, `"C:\Program Files\Vim\gvim.exe"
+    --nofork`.  The order of preference is the `$GIT_EDITOR`
+    environment variable, then `core.editor` configuration, then
+    `$VISUAL`, then `$EDITOR`, and then finally 'vi'.
+
+GIT_PAGER::
+    Text viewer for use by git commands (e.g., 'less').  The value
+    is meant to be interpreted by the shell.  The order of preference
+    is the `$GIT_PAGER` environment variable, then `core.pager`
+    configuration, then `$PAGER`, and then finally 'less'.
+
 Diagnostics
 -----------
 You don't exist. Go away!::
index 0f536793dfc1929f661029b61a1057d2b7a95abc..8e577cc4fe2fd445ef238efc812d12b58156fec4 100644 (file)
@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.6.5.2/git.html[documentation for release 1.6.5.2]
+* link:v1.6.5.3/git.html[documentation for release 1.6.5.3]
 
 * release notes for
+  link:RelNotes-1.6.5.3.txt[1.6.5.3],
   link:RelNotes-1.6.5.2.txt[1.6.5.2],
   link:RelNotes-1.6.5.1.txt[1.6.5.1],
   link:RelNotes-1.6.5.txt[1.6.5].
index b3640c4e64494689841e598cf17741ea8620af12..e237394397199cbb9b72f1f7989d0b51667e9a6d 100644 (file)
@@ -185,7 +185,7 @@ object is. git will tell you that you have a "blob" object (i.e., just a
 regular file), and you can see the contents with
 
 ----------------
-$ git cat-file "blob" 557db03
+$ git cat-file blob 557db03
 ----------------
 
 which will print out "Hello World". The object `557db03` is nothing
@@ -993,7 +993,7 @@ would be different)
 
 ----------------
 Updating from ae3a2da... to a80b4aa....
-Fast forward (no commit created; -m option ignored)
+Fast-forward (no commit created; -m option ignored)
  example |    1 +
  hello   |    1 +
  2 files changed, 2 insertions(+), 0 deletions(-)
@@ -1003,7 +1003,7 @@ Because your branch did not contain anything more than what had
 already been merged into the `master` branch, the merge operation did
 not actually do a merge. Instead, it just updated the top of
 the tree of your branch to that of the `master` branch. This is
-often called 'fast forward' merge.
+often called 'fast-forward' merge.
 
 You can run `gitk \--all` again to see how the commit ancestry
 looks like, or run 'show-branch', which tells you this.
@@ -1186,9 +1186,9 @@ $ git show-branch
 * [master] Some fun.
  ! [mybranch] Some work.
 --
- + [mybranch] Some work.
 *  [master] Some fun.
-*+ [mybranch^] New day.
+ + [mybranch] Some work.
+*+ [master^] Initial commit
 ------------
 
 Now we are ready to experiment with the merge by hand.
@@ -1204,11 +1204,11 @@ $ mb=$(git merge-base HEAD mybranch)
 The command writes the commit object name of the common ancestor
 to the standard output, so we captured its output to a variable,
 because we will be using it in the next step.  By the way, the common
-ancestor commit is the "New day." commit in this case.  You can
+ancestor commit is the "Initial commit" commit in this case.  You can
 tell it by:
 
 ------------
-$ git name-rev $mb
+$ git name-rev --name-only --tags $mb
 my-first-tag
 ------------
 
@@ -1237,8 +1237,8 @@ inspect the index file with this command:
 ------------
 $ git ls-files --stage
 100644 7f8b141b65fdcee47321e399a2598a235a032422 0      example
-100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1      hello
-100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2      hello
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1      hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2      hello
 100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
 ------------
 
@@ -1253,8 +1253,8 @@ To look at only non-zero stages, use `\--unmerged` flag:
 
 ------------
 $ git ls-files --unmerged
-100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1      hello
-100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2      hello
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1      hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2      hello
 100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
 ------------
 
@@ -1283,8 +1283,8 @@ the working tree..  This can be seen if you run `ls-files
 ------------
 $ git ls-files --stage
 100644 7f8b141b65fdcee47321e399a2598a235a032422 0      example
-100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1      hello
-100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2      hello
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1      hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2      hello
 100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
 ------------
 
index 06e0f315c3bb5bb6ef3320b8b1922b76480f9646..4cc3d1387fbdb60a2134a2e399fa46140939fe2f 100644 (file)
@@ -229,7 +229,7 @@ from updating that ref.
 This hook can be used to prevent 'forced' update on certain refs by
 making sure that the object name is a commit object that is a
 descendant of the commit object named by the old object name.
-That is, to enforce a "fast forward only" policy.
+That is, to enforce a "fast-forward only" policy.
 
 It could also be used to log the old..new status.  However, it
 does not know the entire set of branches, so it would end up
index 2b021e3c155013965a383a2188fd1c380d379b62..065441df644b6efc2883bec87fee4941788312ef 100644 (file)
@@ -209,6 +209,121 @@ chance to see if their in-progress work will be compatible.  `git.git`
 has such an official throw-away integration branch called 'pu'.
 
 
+Branch management for a release
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Assuming you are using the merge approach discussed above, when you
+are releasing your project you will need to do some additional branch
+management work.
+
+A feature release is created from the 'master' branch, since 'master'
+tracks the commits that should go into the next feature release.
+
+The 'master' branch is supposed to be a superset of 'maint'. If this
+condition does not hold, then 'maint' contains some commits that
+are not included on 'master'. The fixes represented by those commits
+will therefore not be included in your feature release.
+
+To verify that 'master' is indeed a superset of 'maint', use git log:
+
+.Verify 'master' is a superset of 'maint'
+[caption="Recipe: "]
+=====================================
+`git log master..maint`
+=====================================
+
+This command should not list any commits.  Otherwise, check out
+'master' and merge 'maint' into it.
+
+Now you can proceed with the creation of the feature release. Apply a
+tag to the tip of 'master' indicating the release version:
+
+.Release tagging
+[caption="Recipe: "]
+=====================================
+`git tag -s -m "GIT X.Y.Z" vX.Y.Z master`
+=====================================
+
+You need to push the new tag to a public git server (see
+"DISTRIBUTED WORKFLOWS" below). This makes the tag available to
+others tracking your project. The push could also trigger a
+post-update hook to perform release-related items such as building
+release tarballs and preformatted documentation pages.
+
+Similarly, for a maintenance release, 'maint' is tracking the commits
+to be released. Therefore, in the steps above simply tag and push
+'maint' rather than 'master'.
+
+
+Maintenance branch management after a feature release
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After a feature release, you need to manage your maintenance branches.
+
+First, if you wish to continue to release maintenance fixes for the
+feature release made before the recent one, then you must create
+another branch to track commits for that previous release.
+
+To do this, the current maintenance branch is copied to another branch
+named with the previous release version number (e.g. maint-X.Y.(Z-1)
+where X.Y.Z is the current release).
+
+.Copy maint
+[caption="Recipe: "]
+=====================================
+`git branch maint-X.Y.(Z-1) maint`
+=====================================
+
+The 'maint' branch should now be fast-forwarded to the newly released
+code so that maintenance fixes can be tracked for the current release:
+
+.Update maint to new release
+[caption="Recipe: "]
+=====================================
+* `git checkout maint`
+* `git merge --ff-only master`
+=====================================
+
+If the merge fails because it is not a fast-forward, then it is
+possible some fixes on 'maint' were missed in the feature release.
+This will not happen if the content of the branches was verified as
+described in the previous section.
+
+
+Branch management for next and pu after a feature release
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After a feature release, the integration branch 'next' may optionally be
+rewound and rebuilt from the tip of 'master' using the surviving
+topics on 'next':
+
+.Rewind and rebuild next
+[caption="Recipe: "]
+=====================================
+* `git checkout next`
+* `git reset --hard master`
+* `git merge ai/topic_in_next1`
+* `git merge ai/topic_in_next2`
+* ...
+=====================================
+
+The advantage of doing this is that the history of 'next' will be
+clean. For example, some topics merged into 'next' may have initially
+looked promising, but were later found to be undesirable or premature.
+In such a case, the topic is reverted out of 'next' but the fact
+remains in the history that it was once merged and reverted. By
+recreating 'next', you give another incarnation of such topics a clean
+slate to retry, and a feature release is a good point in history to do
+so.
+
+If you do this, then you should make a public announcement indicating
+that 'next' was rewound and rebuilt.
+
+The same rewind and rebuild process may be followed for 'pu'. A public
+announcement is not necessary since 'pu' is a throw-away branch, as
+described above.
+
+
 DISTRIBUTED WORKFLOWS
 ---------------------
 
index 43d84d15e921435ba4629a7d8c18021d16f89223..1f029f8aa080c4de6323e8b4905a81fa7e8e2046 100644 (file)
@@ -124,7 +124,7 @@ to point at the new commit.
        An evil merge is a <<def_merge,merge>> that introduces changes that
        do not appear in any <<def_parent,parent>>.
 
-[[def_fast_forward]]fast forward::
+[[def_fast_forward]]fast-forward::
        A fast-forward is a special type of <<def_merge,merge>> where you have a
        <<def_revision,revision>> and you are "merging" another
        <<def_branch,branch>>'s changes that happen to be a descendant of what
@@ -220,7 +220,7 @@ to point at the new commit.
        conflict, manual intervention may be required to complete the
        merge.
 +
-As a noun: unless it is a <<def_fast_forward,fast forward>>, a
+As a noun: unless it is a <<def_fast_forward,fast-forward>>, a
 successful merge results in the creation of a new <<def_commit,commit>>
 representing the result of the merge, and having as
 <<def_parent,parents>> the tips of the merged <<def_branch,branches>>.
index 4357e269131fad960367534ae4161fe078fee30a..d527b307707c676e82a08f18cb9fdd7d3abcb228 100644 (file)
@@ -59,7 +59,7 @@ The policy.
    not yet pass the criteria set for 'next'.
 
  - The tips of 'master', 'maint' and 'next' branches will always
-   fast forward, to allow people to build their own
+   fast-forward, to allow people to build their own
    customization on top of them.
 
  - Usually 'master' contains all of 'maint', 'next' contains all
index e70d8a31e7b05e8efc70c6a56f476324065d57a6..8c32da6deb05b5da700a5bd0a4281bf862b23f2c 100644 (file)
@@ -85,7 +85,7 @@ Fortunately I did not have to; what I have in the current branch
 
 ------------------------------------------------
 $ git checkout master
-$ git merge revert-c99 ;# this should be a fast forward
+$ git merge revert-c99 ;# this should be a fast-forward
 Updating from 10d781b9caa4f71495c7b34963bef137216f86a8 to e3a693c...
  cache.h        |    8 ++++----
  commit.c       |    2 +-
@@ -95,7 +95,7 @@ Updating from 10d781b9caa4f71495c7b34963bef137216f86a8 to e3a693c...
  5 files changed, 8 insertions(+), 8 deletions(-)
 ------------------------------------------------
 
-There is no need to redo the test at this point.  We fast forwarded
+There is no need to redo the test at this point.  We fast-forwarded
 and we know 'master' matches 'revert-c99' exactly.  In fact:
 
 ------------------------------------------------
index 697d9188850e9a685045da5bd37844b02978752d..b7f8d416d65ca1cf17d6348e858a13f6ed72c7fe 100644 (file)
@@ -76,7 +76,7 @@ case "$1" in
     if expr "$2" : '0*$' >/dev/null; then
       info "The branch '$1' is new..."
     else
-      # updating -- make sure it is a fast forward
+      # updating -- make sure it is a fast-forward
       mb=$(git-merge-base "$2" "$3")
       case "$mb,$2" in
         "$2,$mb") info "Update is fast-forward" ;;
index c0f96e70708cab74a13ec8696db003289f99b455..a403155052299dd0aaafd6bdfe0fec92d0d0ac7c 100644 (file)
@@ -23,7 +23,7 @@ merge.tool::
        Controls which merge resolution program is used by
        linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
        "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff",
-       "diffuse", "ecmerge", "tortoisemerge", "araxis", and
+       "diffuse", "ecmerge", "tortoisemerge", "p4merge", "araxis" and
        "opendiff".  Any other value is treated is custom merge tool
        and there must be a corresponding mergetool.<tool>.cmd option.
 
index 48d04a5d8833a2c83f1dc754c798688b194de5b9..fec33943058ea3e139aff0b898ef55a677d4067e 100644 (file)
@@ -49,6 +49,11 @@ merge.
 With --no-squash perform the merge and commit the result. This
 option can be used to override --squash.
 
+--ff-only::
+       Refuse to merge and exit with a non-zero status unless the
+       current `HEAD` is already up-to-date or the merge can be
+       resolved as a fast-forward.
+
 -s <strategy>::
 --strategy=<strategy>::
        Use the given merge strategy; can be supplied more than
index 4365b7e8420fa96d6cbfa14a5aa49d956ba2de16..42910a3d5e0229f471c5bceb0c36f184d17ebb08 100644 (file)
@@ -29,8 +29,9 @@ octopus::
        pulling or merging more than one branch.
 
 ours::
-       This resolves any number of heads, but the result of the
-       merge is always the current branch head.  It is meant to
+       This resolves any number of heads, but the resulting tree of the
+       merge is always that of the current branch head, effectively
+       ignoring all changes from all other branches.  It is meant to
        be used to supersede old development history of side
        branches.
 
index 38b9904791466152a98ab3df7c5b84ac66dbac73..0683fb3a3d34e89c67c6e37f2ee96adcd2821ea0 100644 (file)
@@ -123,6 +123,7 @@ The placeholders are:
 - '%s': subject
 - '%f': sanitized subject line, suitable for a filename
 - '%b': body
+- '%N': commit notes
 - '%gD': reflog selector, e.g., `refs/stash@\{1\}`
 - '%gd': shortened reflog selector, e.g., `stash@\{1\}`
 - '%gs': reflog subject
@@ -134,6 +135,8 @@ The placeholders are:
 - '%m': left, right or boundary mark
 - '%n': newline
 - '%x00': print a byte from a hex code
+- '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of
+  linkgit:git-shortlog[1].
 
 NOTE: Some placeholders may depend on other options given to the
 revision traversal engine. For example, the `%g*` reflog options will
index f9811f24733bde97b76dc8e695bad82eace5586b..0551ebdfafb3f41dd0e31d3c74ce42fff33e38e1 100644 (file)
@@ -4,6 +4,13 @@
        (see the section <<URLS,GIT URLS>> below) or the name
        of a remote (see the section <<REMOTES,REMOTES>> below).
 
+ifndef::git-pull[]
+<group>::
+       A name referring to a list of repositories as the value
+       of remotes.<group> in the configuration file.
+       (See linkgit:git-config[1]).
+endif::git-pull[]
+
 <refspec>::
        The format of a <refspec> parameter is an optional plus
        `{plus}`, followed by the source ref <src>, followed
@@ -11,9 +18,9 @@
 +
 The remote ref that matches <src>
 is fetched, and if <dst> is not empty string, the local
-ref that matches it is fast forwarded using <src>.
+ref that matches it is fast-forwarded using <src>.
 If the optional plus `+` is used, the local ref
-is updated even if it does not result in a fast forward
+is updated even if it does not result in a fast-forward
 update.
 +
 [NOTE]
index bf66116d61af4f16cff59ed1563f38736a14ecce..1f57aed337e87e8bd2b175ef3fc807288a8256bf 100644 (file)
@@ -243,12 +243,23 @@ endif::git-rev-list[]
        Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed
        on the command line as '<commit>'.
 
-ifdef::git-rev-list[]
+ifndef::git-rev-list[]
+--bisect::
+
+       Pretend as if the bad bisection ref `$GIT_DIR/refs/bisect/bad`
+       was listed and as if it was followed by `--not` and the good
+       bisection refs `$GIT_DIR/refs/bisect/good-*` on the command
+       line.
+endif::git-rev-list[]
+
 --stdin::
 
        In addition to the '<commit>' listed on the command
-       line, read them from the standard input.
+       line, read them from the standard input. If a '--' separator is
+       seen, stop reading commits and start reading paths to limit the
+       result.
 
+ifdef::git-rev-list[]
 --quiet::
 
        Don't print anything to standard output.  This form
@@ -536,7 +547,11 @@ Bisection Helpers
 --bisect::
 
 Limit output to the one commit object which is roughly halfway between
-the included and excluded commits. Thus, if
+included and excluded commits. Note that the bad bisection ref
+`$GIT_DIR/refs/bisect/bad` is added to the included commits (if it
+exists) and the good bisection refs `$GIT_DIR/refs/bisect/good-*` are
+added to the excluded commits (if they exist). Thus, supposing there
+are no refs in `$GIT_DIR/refs/bisect/`, if
 
 -----------------------------------------------------------------------
        $ git rev-list --bisect foo ^bar ^baz
@@ -556,22 +571,24 @@ one.
 
 --bisect-vars::
 
-This calculates the same as `--bisect`, but outputs text ready
-to be eval'ed by the shell. These lines will assign the name of
-the midpoint revision to the variable `bisect_rev`, and the
-expected number of commits to be tested after `bisect_rev` is
-tested to `bisect_nr`, the expected number of commits to be
-tested if `bisect_rev` turns out to be good to `bisect_good`,
-the expected number of commits to be tested if `bisect_rev`
-turns out to be bad to `bisect_bad`, and the number of commits
-we are bisecting right now to `bisect_all`.
+This calculates the same as `--bisect`, except that refs in
+`$GIT_DIR/refs/bisect/` are not used, and except that this outputs
+text ready to be eval'ed by the shell. These lines will assign the
+name of the midpoint revision to the variable `bisect_rev`, and the
+expected number of commits to be tested after `bisect_rev` is tested
+to `bisect_nr`, the expected number of commits to be tested if
+`bisect_rev` turns out to be good to `bisect_good`, the expected
+number of commits to be tested if `bisect_rev` turns out to be bad to
+`bisect_bad`, and the number of commits we are bisecting right now to
+`bisect_all`.
 
 --bisect-all::
 
 This outputs all the commit objects between the included and excluded
 commits, ordered by their distance to the included and excluded
-commits. The farthest from them is displayed first. (This is the only
-one displayed by `--bisect`.)
+commits. Refs in `$GIT_DIR/refs/bisect/` are not used. The farthest
+from them is displayed first. (This is the only one displayed by
+`--bisect`.)
 +
 This is useful because it makes it easy to choose a good commit to
 test when you want to avoid to test some of them for some reason (they
index d66e61b1eca3d87ffcf14ee0e0447b6ccb75a2bb..d6fc90ac7eb210bd492057422748817a59cfc2a3 100644 (file)
@@ -11,9 +11,6 @@ Core functions:
 
 * `graph_init()` creates a new `struct git_graph`
 
-* `graph_release()` destroys a `struct git_graph`, and frees the memory
-  associated with it.
-
 * `graph_update()` moves the graph to a new commit.
 
 * `graph_next_line()` outputs the next line of the graph into a strbuf.  It
@@ -134,8 +131,6 @@ while ((commit = get_revision(opts)) != NULL) {
                        putchar(opts->diffopt.line_termination);
        }
 }
-
-graph_release(graph);
 ------------
 
 Sample output
index 9cd48b48597f9b7e822fc3d81e0bc556d6631b02..7950eeeda4447808c937b6ddcaa9ae1686e2ed5c 100644 (file)
-Pack transfer protocols
-=======================
-
-There are two Pack push-pull protocols.
-
-upload-pack (S) | fetch/clone-pack (C) protocol:
-
-       # Tell the puller what commits we have and what their names are
-       S: SHA1 name
-       S: ...
-       S: SHA1 name
-       S: # flush -- it's your turn
-       # Tell the pusher what commits we want, and what we have
-       C: want name
-       C: ..
-       C: want name
-       C: have SHA1
-       C: have SHA1
-       C: ...
-       C: # flush -- occasionally ask "had enough?"
-       S: NAK
-       C: have SHA1
-       C: ...
-       C: have SHA1
-       S: ACK
-       C: done
-       S: XXXXXXX -- packfile contents.
-
-send-pack | receive-pack protocol.
-
-       # Tell the pusher what commits we have and what their names are
-       C: SHA1 name
-       C: ...
-       C: SHA1 name
-       C: # flush -- it's your turn
-       # Tell the puller what the pusher has
-       S: old-SHA1 new-SHA1 name
-       S: old-SHA1 new-SHA1 name
-       S: ...
-       S: # flush -- done with the list
-       S: XXXXXXX --- packfile contents.
+Packfile transfer protocols
+===========================
+
+Git supports transferring data in packfiles over the ssh://, git:// and
+file:// transports.  There exist two sets of protocols, one for pushing
+data from a client to a server and another for fetching data from a
+server to a client.  All three transports (ssh, git, file) use the same
+protocol to transfer data.
+
+The processes invoked in the canonical Git implementation are 'upload-pack'
+on the server side and 'fetch-pack' on the client side for fetching data;
+then 'receive-pack' on the server and 'send-pack' on the client for pushing
+data.  The protocol functions to have a server tell a client what is
+currently on the server, then for the two to negotiate the smallest amount
+of data to send in order to fully update one or the other.
+
+Transports
+----------
+There are three transports over which the packfile protocol is
+initiated.  The Git transport is a simple, unauthenticated server that
+takes the command (almost always 'upload-pack', though Git
+servers can be configured to be globally writable, in which 'receive-
+pack' initiation is also allowed) with which the client wishes to
+communicate and executes it and connects it to the requesting
+process.
+
+In the SSH transport, the client just runs the 'upload-pack'
+or 'receive-pack' process on the server over the SSH protocol and then
+communicates with that invoked process over the SSH connection.
+
+The file:// transport runs the 'upload-pack' or 'receive-pack'
+process locally and communicates with it over a pipe.
+
+Git Transport
+-------------
+
+The Git transport starts off by sending the command and repository
+on the wire using the pkt-line format, followed by a NUL byte and a
+hostname paramater, terminated by a NUL byte.
+
+   0032git-upload-pack /project.git\0host=myserver.com\0
+
+--
+   git-proto-request = request-command SP pathname NUL [ host-parameter NUL ]
+   request-command   = "git-upload-pack" / "git-receive-pack" /
+                      "git-upload-archive"   ; case sensitive
+   pathname          = *( %x01-ff ) ; exclude NUL
+   host-parameter    = "host=" hostname [ ":" port ]
+--
+
+Only host-parameter is allowed in the git-proto-request. Clients
+MUST NOT attempt to send additional parameters. It is used for the
+git-daemon name based virtual hosting.  See --interpolated-path
+option to git daemon, with the %H/%CH format characters.
+
+Basically what the Git client is doing to connect to an 'upload-pack'
+process on the server side over the Git protocol is this:
+
+   $ echo -e -n \
+     "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" |
+     nc -v example.com 9418
+
+
+SSH Transport
+-------------
+
+Initiating the upload-pack or receive-pack processes over SSH is
+executing the binary on the server via SSH remote execution.
+It is basically equivalent to running this:
+
+   $ ssh git.example.com "git-upload-pack '/project.git'"
+
+For a server to support Git pushing and pulling for a given user over
+SSH, that user needs to be able to execute one or both of those
+commands via the SSH shell that they are provided on login.  On some
+systems, that shell access is limited to only being able to run those
+two commands, or even just one of them.
+
+In an ssh:// format URI, it's absolute in the URI, so the '/' after
+the host name (or port number) is sent as an argument, which is then
+read by the remote git-upload-pack exactly as is, so it's effectively
+an absolute path in the remote filesystem.
+
+       git clone ssh://user@example.com/project.git
+                   |
+                   v
+    ssh user@example.com "git-upload-pack '/project.git'"
+
+In a "user@host:path" format URI, its relative to the user's home
+directory, because the Git client will run:
+
+     git clone user@example.com:project.git
+                   |
+                   v
+  ssh user@example.com "git-upload-pack 'project.git'"
+
+The exception is if a '~' is used, in which case
+we execute it without the leading '/'.
+
+      ssh://user@example.com/~alice/project.git,
+                    |
+                    v
+   ssh user@example.com "git-upload-pack '~alice/project.git'"
+
+A few things to remember here:
+
+- The "command name" is spelled with dash (e.g. git-upload-pack), but
+  this can be overridden by the client;
+
+- The repository path is always quoted with single quotes.
+
+Fetching Data From a Server
+===========================
+
+When one Git repository wants to get data that a second repository
+has, the first can 'fetch' from the second.  This operation determines
+what data the server has that the client does not then streams that
+data down to the client in packfile format.
+
+
+Reference Discovery
+-------------------
+
+When the client initially connects the server will immediately respond
+with a listing of each reference it has (all branches and tags) along
+with the object name that each reference currently points to.
+
+   $ echo -e -n "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" |
+      nc -v example.com 9418
+   00887217a7c7e582c46cec22a130adf4b9d7d950fba0 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress include-tag
+   00441d3fcd5ced445d1abc402225c0b8a1299641f497 refs/heads/integration
+   003f7217a7c7e582c46cec22a130adf4b9d7d950fba0 refs/heads/master
+   003cb88d2441cac0977faf98efc80305012112238d9d refs/tags/v0.9
+   003c525128480b96c89e6418b1e40909bf6c5b2d580f refs/tags/v1.0
+   003fe92df48743b7bc7d26bcaabfddde0a1e20cae47c refs/tags/v1.0^{}
+   0000
+
+Server SHOULD terminate each non-flush line using LF ("\n") terminator;
+client MUST NOT complain if there is no terminator.
+
+The returned response is a pkt-line stream describing each ref and
+its current value.  The stream MUST be sorted by name according to
+the C locale ordering.
+
+If HEAD is a valid ref, HEAD MUST appear as the first advertised
+ref.  If HEAD is not a valid ref, HEAD MUST NOT appear in the
+advertisement list at all, but other refs may still appear.
+
+The stream MUST include capability declarations behind a NUL on the
+first ref. The peeled value of a ref (that is "ref^{}") MUST be
+immediately after the ref itself, if presented. A conforming server
+MUST peel the ref if its an annotated tag.
+
+----
+  advertised-refs  =  (no-refs / list-of-refs)
+                     flush-pkt
+
+  no-refs          =  PKT-LINE(zero-id SP "capabilities^{}"
+                     NUL capability-list LF)
+
+  list-of-refs     =  first-ref *other-ref
+  first-ref        =  PKT-LINE(obj-id SP refname
+                     NUL capability-list LF)
+
+  other-ref        =  PKT-LINE(other-tip / other-peeled)
+  other-tip        =  obj-id SP refname LF
+  other-peeled     =  obj-id SP refname "^{}" LF
+
+  capability-list  =  capability *(SP capability)
+  capability       =  1*(LC_ALPHA / DIGIT / "-" / "_")
+  LC_ALPHA         =  %x61-7A
+----
+
+Server and client MUST use lowercase for obj-id, both MUST treat obj-id
+as case-insensitive.
+
+See protocol-capabilities.txt for a list of allowed server capabilities
+and descriptions.
+
+Packfile Negotiation
+--------------------
+After reference and capabilities discovery, the client can decide
+to terminate the connection by sending a flush-pkt, telling the
+server it can now gracefully terminate (as happens with the ls-remote
+command) or it can enter the negotiation phase, where the client and
+server determine what the minimal packfile necessary for transport is.
+
+Once the client has the initial list of references that the server
+has, as well as the list of capabilities, it will begin telling the
+server what objects it wants and what objects it has, so the server
+can make a packfile that only contains the objects that the client needs.
+The client will also send a list of the capabilities it wants to be in
+effect, out of what the server said it could do with the first 'want' line.
+
+----
+  upload-request    =  want-list
+                      have-list
+                      compute-end
+
+  want-list         =  first-want
+                      *additional-want
+                      flush-pkt
+
+  first-want        =  PKT-LINE("want" SP obj-id SP capability-list LF)
+  additional-want   =  PKT-LINE("want" SP obj-id LF)
+
+  have-list         =  *have-line
+  have-line         =  PKT-LINE("have" SP obj-id LF)
+  compute-end       =  flush-pkt / PKT-LINE("done")
+----
+
+Clients MUST send all the obj-ids it wants from the reference
+discovery phase as 'want' lines. Clients MUST send at least one
+'want' command in the request body. Clients MUST NOT mention an
+obj-id in a 'want' command which did not appear in the response
+obtained through ref discovery.
+
+If client is requesting a shallow clone, it will now send a 'deepen'
+line with the depth it is requesting.
+
+Once all the "want"s (and optional 'deepen') are transferred,
+clients MUST send a flush-pkt. If the client has all the references
+on the server, client flushes and disconnects.
+
+TODO: shallow/unshallow response and document the deepen command in the ABNF.
+
+Now the client will send a list of the obj-ids it has using 'have'
+lines.  In multi_ack mode, the canonical implementation will send up
+to 32 of these at a time, then will send a flush-pkt.  The canonical
+implementation will skip ahead and send the next 32 immediately,
+so that there is always a block of 32 "in-flight on the wire" at a
+time.
+
+If the server reads 'have' lines, it then will respond by ACKing any
+of the obj-ids the client said it had that the server also has. The
+server will ACK obj-ids differently depending on which ack mode is
+chosen by the client.
+
+In multi_ack mode:
+
+  * the server will respond with 'ACK obj-id continue' for any common
+    commits.
+
+  * once the server has found an acceptable common base commit and is
+    ready to make a packfile, it will blindly ACK all 'have' obj-ids
+    back to the client.
+
+  * the server will then send a 'NACK' and then wait for another response
+    from the client - either a 'done' or another list of 'have' lines.
+
+In multi_ack_detailed mode:
+
+  * the server will differentiate the ACKs where it is signaling
+    that it is ready to send data with 'ACK obj-id ready' lines, and
+    signals the identified common commits with 'ACK obj-id common' lines.
+
+Without either multi_ack or multi_ack_detailed:
+
+ * upload-pack sends "ACK obj-id" on the first common object it finds.
+   After that it says nothing until the client gives it a "done".
+
+ * upload-pack sends "NAK" on a flush-pkt if no common object
+   has been found yet.  If one has been found, and thus an ACK
+   was already sent, its silent on the flush-pkt.
+
+After the client has gotten enough ACK responses that it can determine
+that the server has enough information to send an efficient packfile
+(in the canonical implementation, this is determined when it has received
+enough ACKs that it can color everything left in the --date-order queue
+as common with the server, or the --date-order queue is empty), or the
+client determines that it wants to give up (in the canonical implementation,
+this is determined when the client sends 256 'have' lines without getting
+any of them ACKed by the server - meaning there is nothing in common and
+the server should just send all it's objects), then the client will send
+a 'done' command.  The 'done' command signals to the server that the client
+is ready to receive it's packfile data.
+
+However, the 256 limit *only* turns on in the canonical client
+implementation if we have received at least one "ACK %s continue"
+during a prior round.  This helps to ensure that at least one common
+ancestor is found before we give up entirely.
+
+Once the 'done' line is read from the client, the server will either
+send a final 'ACK obj-id' or it will send a 'NAK'. The server only sends
+ACK after 'done' if there is at least one common base and multi_ack or
+multi_ack_detailed is enabled. The server always sends NAK after 'done'
+if there is no common base found.
+
+Then the server will start sending it's packfile data.
+
+----
+  server-response = *ack_multi ack / nak
+  ack_multi       = PKT-LINE("ACK" SP obj-id ack_status LF)
+  ack_status      = "continue" / "common" / "ready"
+  ack             = PKT-LINE("ACK SP obj-id LF)
+  nak             = PKT-LINE("NAK" LF)
+----
+
+A simple clone may look like this (with no 'have' lines):
+
+----
+   C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d\0multi_ack \
+     side-band-64k ofs-delta\n
+   C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n
+   C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n
+   C: 0032want 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n
+   C: 0032want 74730d410fcb6603ace96f1dc55ea6196122532d\n
+   C: 0000
+   C: 0009done\n
+
+   S: 0008NAK\n
+   S: [PACKFILE]
+----
+
+An incremental update (fetch) response might look like this:
+
+----
+   C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d\0multi_ack \
+     side-band-64k ofs-delta\n
+   C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n
+   C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n
+   C: 0000
+   C: 0032have 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n
+   C: [30 more have lines]
+   C: 0032have 74730d410fcb6603ace96f1dc55ea6196122532d\n
+   C: 0000
+
+   S: 003aACK 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01 continue\n
+   S: 003aACK 74730d410fcb6603ace96f1dc55ea6196122532d continue\n
+   S: 0008NAK\n
+
+   C: 0009done\n
+
+   S: 003aACK 74730d410fcb6603ace96f1dc55ea6196122532d\n
+   S: [PACKFILE]
+----
+
+
+Packfile Data
+-------------
+
+Now that the client and server have finished negotiation about what
+the minimal amount of data that needs to be sent to the client is, the server
+will construct and send the required data in packfile format.
+
+See pack-format.txt for what the packfile itself actually looks like.
+
+If 'side-band' or 'side-band-64k' capabilities have been specified by
+the client, the server will send the packfile data multiplexed.
+
+Each packet starting with the packet-line length of the amount of data
+that follows, followed by a single byte specifying the sideband the
+following data is coming in on.
+
+In 'side-band' mode, it will send up to 999 data bytes plus 1 control
+code, for a total of up to 1000 bytes in a pkt-line.  In 'side-band-64k'
+mode it will send up to 65519 data bytes plus 1 control code, for a
+total of up to 65520 bytes in a pkt-line.
+
+The sideband byte will be a '1', '2' or a '3'. Sideband '1' will contain
+packfile data, sideband '2' will be used for progress information that the
+client will generally print to stderr and sideband '3' is used for error
+information.
+
+If no 'side-band' capability was specified, the server will stream the
+entire packfile without multiplexing.
+
+
+Pushing Data To a Server
+========================
+
+Pushing data to a server will invoke the 'receive-pack' process on the
+server, which will allow the client to tell it which references it should
+update and then send all the data the server will need for those new
+references to be complete.  Once all the data is received and validated,
+the server will then update its references to what the client specified.
+
+Authentication
+--------------
+
+The protocol itself contains no authentication mechanisms.  That is to be
+handled by the transport, such as SSH, before the 'receive-pack' process is
+invoked.  If 'receive-pack' is configured over the Git transport, those
+repositories will be writable by anyone who can access that port (9418) as
+that transport is unauthenticated.
+
+Reference Discovery
+-------------------
+
+The reference discovery phase is done nearly the same way as it is in the
+fetching protocol. Each reference obj-id and name on the server is sent
+in packet-line format to the client, followed by a flush-pkt.  The only
+real difference is that the capability listing is different - the only
+possible values are 'report-status', 'delete-refs' and 'ofs-delta'.
+
+Reference Update Request and Packfile Transfer
+----------------------------------------------
+
+Once the client knows what references the server is at, it can send a
+list of reference update requests.  For each reference on the server
+that it wants to update, it sends a line listing the obj-id currently on
+the server, the obj-id the client would like to update it to and the name
+of the reference.
+
+This list is followed by a flush-pkt and then the packfile that should
+contain all the objects that the server will need to complete the new
+references.
+
+----
+  update-request    =  command-list [pack-file]
+
+  command-list      =  PKT-LINE(command NUL capability-list LF)
+                      *PKT-LINE(command LF)
+                      flush-pkt
+
+  command           =  create / delete / update
+  create            =  zero-id SP new-id  SP name
+  delete            =  old-id  SP zero-id SP name
+  update            =  old-id  SP new-id  SP name
+
+  old-id            =  obj-id
+  new-id            =  obj-id
+
+  pack-file         = "PACK" 28*(OCTET)
+----
+
+If the receiving end does not support delete-refs, the sending end MUST
+NOT ask for delete command.
+
+The pack-file MUST NOT be sent if the only command used is 'delete'.
+
+A pack-file MUST be sent if either create or update command is used,
+even if the server already has all the necessary objects.  In this
+case the client MUST send an empty pack-file.   The only time this
+is likely to happen is if the client is creating
+a new branch or a tag that points to an existing obj-id.
+
+The server will receive the packfile, unpack it, then validate each
+reference that is being updated that it hasn't changed while the request
+was being processed (the obj-id is still the same as the old-id), and
+it will run any update hooks to make sure that the update is acceptable.
+If all of that is fine, the server will then update the references.
+
+Report Status
+-------------
+
+After receiving the pack data from the sender, the receiver sends a
+report if 'report-status' capability is in effect.
+It is a short listing of what happened in that update.  It will first
+list the status of the packfile unpacking as either 'unpack ok' or
+'unpack [error]'.  Then it will list the status for each of the references
+that it tried to update.  Each line is either 'ok [refname]' if the
+update was successful, or 'ng [refname] [error]' if the update was not.
+
+----
+  report-status     = unpack-status
+                     1*(command-status)
+                     flush-pkt
+
+  unpack-status     = PKT-LINE("unpack" SP unpack-result LF)
+  unpack-result     = "ok" / error-msg
+
+  command-status    = command-ok / command-fail
+  command-ok        = PKT-LINE("ok" SP refname LF)
+  command-fail      = PKT-LINE("ng" SP refname SP error-msg LF)
+
+  error-msg         = 1*(OCTECT) ; where not "ok"
+----
+
+Updates can be unsuccessful for a number of reasons.  The reference can have
+changed since the reference discovery phase was originally sent, meaning
+someone pushed in the meantime.  The reference being pushed could be a
+non-fast-forward reference and the update hooks or configuration could be
+set to not allow that, etc.  Also, some references can be updated while others
+can be rejected.
+
+An example client/server communication might look like this:
+
+----
+   S: 007c74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/local\0report-status delete-refs ofs-delta\n
+   S: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe refs/heads/debug\n
+   S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/master\n
+   S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/team\n
+   S: 0000
+
+   C: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe 74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/debug\n
+   C: 003e74730d410fcb6603ace96f1dc55ea6196122532d 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/master\n
+   C: 0000
+   C: [PACKDATA]
+
+   S: 000aunpack ok\n
+   S: 0014ok refs/heads/debug\n
+   S: 0026ng refs/heads/master non-fast-forward\n
+----
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
new file mode 100644 (file)
index 0000000..1892d3e
--- /dev/null
@@ -0,0 +1,187 @@
+Git Protocol Capabilities
+=========================
+
+Servers SHOULD support all capabilities defined in this document.
+
+On the very first line of the initial server response of either
+receive-pack and upload-pack the first reference is followed by
+a NUL byte and then a list of space delimited server capabilities.
+These allow the server to declare what it can and cannot support
+to the client.
+
+Client will then send a space separated list of capabilities it wants
+to be in effect. The client MUST NOT ask for capabilities the server
+did not say it supports.
+
+Server MUST diagnose and abort if capabilities it does not understand
+was sent.  Server MUST NOT ignore capabilities that client requested
+and server advertised.  As a consequence of these rules, server MUST
+NOT advertise capabilities it does not understand.
+
+The 'report-status' and 'delete-refs' capabilities are sent and
+recognized by the receive-pack (push to server) process.
+
+The 'ofs-delta' capability is sent and recognized by both upload-pack
+and receive-pack protocols.
+
+All other capabilities are only recognized by the upload-pack (fetch
+from server) process.
+
+multi_ack
+---------
+
+The 'multi_ack' capability allows the server to return "ACK obj-id
+continue" as soon as it finds a commit that it can use as a common
+base, between the client's wants and the client's have set.
+
+By sending this early, the server can potentially head off the client
+from walking any further down that particular branch of the client's
+repository history.  The client may still need to walk down other
+branches, sending have lines for those, until the server has a
+complete cut across the DAG, or the client has said "done".
+
+Without multi_ack, a client sends have lines in --date-order until
+the server has found a common base.  That means the client will send
+have lines that are already known by the server to be common, because
+they overlap in time with another branch that the server hasn't found
+a common base on yet.
+
+For example suppose the client has commits in caps that the server
+doesn't and the server has commits in lower case that the client
+doesn't, as in the following diagram:
+
+       +---- u ---------------------- x
+      /              +----- y
+     /              /
+    a -- b -- c -- d -- E -- F
+       \
+       +--- Q -- R -- S
+
+If the client wants x,y and starts out by saying have F,S, the server
+doesn't know what F,S is.  Eventually the client says "have d" and
+the server sends "ACK d continue" to let the client know to stop
+walking down that line (so don't send c-b-a), but its not done yet,
+it needs a base for x. The client keeps going with S-R-Q, until a
+gets reached, at which point the server has a clear base and it all
+ends.
+
+Without multi_ack the client would have sent that c-b-a chain anyway,
+interleaved with S-R-Q.
+
+thin-pack
+---------
+
+This capability means that the server can send a 'thin' pack, a pack
+which does not contain base objects; if those base objects are available
+on client side. Client requests 'thin-pack' capability when it
+understands how to "thicken" it by adding required delta bases making
+it self-contained.
+
+Client MUST NOT request 'thin-pack' capability if it cannot turn a thin
+pack into a self-contained pack.
+
+
+side-band, side-band-64k
+------------------------
+
+This capability means that server can send, and client understand multiplexed
+progress reports and error info interleaved with the packfile itself.
+
+These two options are mutually exclusive. A modern client always
+favors 'side-band-64k'.
+
+Either mode indicates that the packfile data will be streamed broken
+up into packets of up to either 1000 bytes in the case of 'side_band',
+or 65520 bytes in the case of 'side_band_64k'. Each packet is made up
+of a leading 4-byte pkt-line length of how much data is in the packet,
+followed by a 1-byte stream code, followed by the actual data.
+
+The stream code can be one of:
+
+ 1 - pack data
+ 2 - progress messages
+ 3 - fatal error message just before stream aborts
+
+The "side-band-64k" capability came about as a way for newer clients
+that can handle much larger packets to request packets that are
+actually crammed nearly full, while maintaining backward compatibility
+for the older clients.
+
+Further, with side-band and its up to 1000-byte messages, it's actually
+999 bytes of payload and 1 byte for the stream code. With side-band-64k,
+same deal, you have up to 65519 bytes of data and 1 byte for the stream
+code.
+
+The client MUST send only maximum of one of "side-band" and "side-
+band-64k".  Server MUST diagnose it as an error if client requests
+both.
+
+ofs-delta
+---------
+
+Server can send, and client understand PACKv2 with delta refering to
+its base by position in pack rather than by an obj-id.  That is, they can
+send/read OBJ_OFS_DELTA (aka type 6) in a packfile.
+
+shallow
+-------
+
+This capability adds "deepen", "shallow" and "unshallow" commands to
+the  fetch-pack/upload-pack protocol so clients can request shallow
+clones.
+
+no-progress
+-----------
+
+The client was started with "git clone -q" or something, and doesn't
+want that side band 2.  Basically the client just says "I do not
+wish to receive stream 2 on sideband, so do not send it to me, and if
+you did, I will drop it on the floor anyway".  However, the sideband
+channel 3 is still used for error responses.
+
+include-tag
+-----------
+
+The 'include-tag' capability is about sending annotated tags if we are
+sending objects they point to.  If we pack an object to the client, and
+a tag object points exactly at that object, we pack the tag object too.
+In general this allows a client to get all new annotated tags when it
+fetches a branch, in a single network connection.
+
+Clients MAY always send include-tag, hardcoding it into a request when
+the server advertises this capability. The decision for a client to
+request include-tag only has to do with the client's desires for tag
+data, whether or not a server had advertised objects in the
+refs/tags/* namespace.
+
+Servers MUST pack the tags if their referrant is packed and the client
+has requested include-tags.
+
+Clients MUST be prepared for the case where a server has ignored
+include-tag and has not actually sent tags in the pack.  In such
+cases the client SHOULD issue a subsequent fetch to acquire the tags
+that include-tag would have otherwise given the client.
+
+The server SHOULD send include-tag, if it supports it, regardless
+of whether or not there are tags available.
+
+report-status
+-------------
+
+The upload-pack process can receive a 'report-status' capability,
+which tells it that the client wants a report of what happened after
+a packfile upload and reference update.  If the pushing client requests
+this capability, after unpacking and updating references the server
+will respond with whether the packfile unpacked successfully and if
+each reference was updated successfully.  If any of those were not
+successful, it will send back an error message.  See pack-protocol.txt
+for example messages.
+
+delete-refs
+-----------
+
+If the server sends back the 'delete-refs' capability, it means that
+it is capable of accepting an zero-id value as the target
+value of a reference update.  It is not sent back by the client, it
+simply informs the client that it can be sent zero-id values
+to delete references.
diff --git a/Documentation/technical/protocol-common.txt b/Documentation/technical/protocol-common.txt
new file mode 100644 (file)
index 0000000..d30a1b9
--- /dev/null
@@ -0,0 +1,96 @@
+Documentation Common to Pack and Http Protocols
+===============================================
+
+ABNF Notation
+-------------
+
+ABNF notation as described by RFC 5234 is used within the protocol documents,
+except the following replacement core rules are used:
+----
+  HEXDIG    =  DIGIT / "a" / "b" / "c" / "d" / "e" / "f"
+----
+
+We also define the following common rules:
+----
+  NUL       =  %x00
+  zero-id   =  40*"0"
+  obj-id    =  40*(HEXDIGIT)
+
+  refname  =  "HEAD"
+  refname /=  "refs/" <see discussion below>
+----
+
+A refname is a hierarchical octet string beginning with "refs/" and
+not violating the 'git-check-ref-format' command's validation rules.
+More specifically, they:
+
+. They can include slash `/` for hierarchical (directory)
+  grouping, but no slash-separated component can begin with a
+  dot `.`.
+
+. They must contain at least one `/`. This enforces the presence of a
+  category like `heads/`, `tags/` etc. but the actual names are not
+  restricted.
+
+. They cannot have two consecutive dots `..` anywhere.
+
+. They cannot have ASCII control characters (i.e. bytes whose
+  values are lower than \040, or \177 `DEL`), space, tilde `~`,
+  caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
+  or open bracket `[` anywhere.
+
+. They cannot end with a slash `/` nor a dot `.`.
+
+. They cannot end with the sequence `.lock`.
+
+. They cannot contain a sequence `@{`.
+
+. They cannot contain a `\\`.
+
+
+pkt-line Format
+---------------
+
+Much (but not all) of the payload is described around pkt-lines.
+
+A pkt-line is a variable length binary string.  The first four bytes
+of the line, the pkt-len, indicates the total length of the line,
+in hexadecimal.  The pkt-len includes the 4 bytes used to contain
+the length's hexadecimal representation.
+
+A pkt-line MAY contain binary data, so implementors MUST ensure
+pkt-line parsing/formatting routines are 8-bit clean.
+
+A non-binary line SHOULD BE terminated by an LF, which if present
+MUST be included in the total length.
+
+The maximum length of a pkt-line's data component is 65520 bytes.
+Implementations MUST NOT send pkt-line whose length exceeds 65524
+(65520 bytes of payload + 4 bytes of length data).
+
+Implementations SHOULD NOT send an empty pkt-line ("0004").
+
+A pkt-line with a length field of 0 ("0000"), called a flush-pkt,
+is a special case and MUST be handled differently than an empty
+pkt-line ("0004").
+
+----
+  pkt-line     =  data-pkt / flush-pkt
+
+  data-pkt     =  pkt-len pkt-payload
+  pkt-len      =  4*(HEXDIG)
+  pkt-payload  =  (pkt-len - 4)*(OCTET)
+
+  flush-pkt    = "0000"
+----
+
+Examples (as C-style strings):
+
+----
+  pkt-line          actual value
+  ---------------------------------
+  "0006a\n"         "a\n"
+  "0005a"           "a"
+  "000bfoobar\n"    "foobar\n"
+  "0004"            ""
+----
index 67ebffa568fd684afd08496281a530c658a217d9..b169836684a56aae176875effa5cc4e6a619de80 100644 (file)
@@ -1183,7 +1183,23 @@ $ git merge branchname
 -------------------------------------------------
 
 merges the development in the branch "branchname" into the current
-branch.  If there are conflicts--for example, if the same file is
+branch.
+
+A merge is made by combining the changes made in "branchname" and the
+changes made up to the latest commit in your current branch since
+their histories forked. The work tree is overwritten by the result of
+the merge when this combining is done cleanly, or overwritten by a
+half-merged results when this combining results in conflicts.
+Therefore, if you have uncommitted changes touching the same files as
+the ones impacted by the merge, Git will refuse to proceed. Most of
+the time, you will want to commit your changes before you can merge,
+and if you don't, then linkgit:git-stash[1] can take these changes
+away while you're doing the merge, and reapply them afterwards.
+
+If the changes are independant enough, Git will automatically complete
+the merge and commit the result (or reuse an existing commit in case
+of <<fast-forwards,fast-forward>>, see below). On the other hand,
+if there are conflicts--for example, if the same file is
 modified in two different ways in the remote branch and the local
 branch--then you are warned; the output may look something like this:
 
@@ -1384,7 +1400,7 @@ were merged.
 
 However, if the current branch is a descendant of the other--so every
 commit present in the one is already contained in the other--then git
-just performs a "fast forward"; the head of the current branch is moved
+just performs a "fast-forward"; the head of the current branch is moved
 forward to point at the head of the merged-in branch, without any new
 commits being created.
 
@@ -1679,7 +1695,7 @@ Sharing development with others
 Getting updates with git pull
 -----------------------------
 
-After you clone a repository and make a few changes of your own, you
+After you clone a repository and commit a few changes of your own, you
 may wish to check the original repository for updates and merge them
 into your own work.
 
@@ -1719,7 +1735,7 @@ producing a default commit message documenting the branch and
 repository that you pulled from.
 
 (But note that no such commit will be created in the case of a
-<<fast-forwards,fast forward>>; instead, your branch will just be
+<<fast-forwards,fast-forward>>; instead, your branch will just be
 updated to point to the latest commit from the upstream branch.)
 
 The `git pull` command can also be given "." as the "remote" repository,
@@ -1943,7 +1959,7 @@ $ git push ssh://yourserver.com/~you/proj.git master
 -------------------------------------------------
 
 As with `git fetch`, `git push` will complain if this does not result in a
-<<fast-forwards,fast forward>>; see the following section for details on
+<<fast-forwards,fast-forward>>; see the following section for details on
 handling this case.
 
 Note that the target of a "push" is normally a
@@ -1976,7 +1992,7 @@ details.
 What to do when a push fails
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-If a push would not result in a <<fast-forwards,fast forward>> of the
+If a push would not result in a <<fast-forwards,fast-forward>> of the
 remote branch, then it will fail with an error like:
 
 -------------------------------------------------
@@ -2115,7 +2131,7 @@ $ git checkout release && git pull
 
 Important note!  If you have any local changes in these branches, then
 this merge will create a commit object in the history (with no local
-changes git will simply do a "Fast forward" merge).  Many people dislike
+changes git will simply do a "fast-forward" merge).  Many people dislike
 the "noise" that this creates in the Linux history, so you should avoid
 doing this capriciously in the "release" branch, as these noisy commits
 will become part of the permanent history when you ask Linus to pull
@@ -2569,7 +2585,7 @@ them again with linkgit:git-am[1].
 Other tools
 -----------
 
-There are numerous other tools, such as StGIT, which exist for the
+There are numerous other tools, such as StGit, which exist for the
 purpose of maintaining a patch series.  These are outside of the scope of
 this manual.
 
@@ -2729,9 +2745,9 @@ In the previous example, when updating an existing branch, "git fetch"
 checks to make sure that the most recent commit on the remote
 branch is a descendant of the most recent commit on your copy of the
 branch before updating your copy of the branch to point at the new
-commit.  Git calls this process a <<fast-forwards,fast forward>>.
+commit.  Git calls this process a <<fast-forwards,fast-forward>>.
 
-A fast forward looks something like this:
+A fast-forward looks something like this:
 
 ................................................
  o--o--o--o <-- old head of the branch
@@ -4275,7 +4291,7 @@ You see, Git is actually the best tool to find out about the source of Git
 itself!
 
 [[glossary]]
-GIT Glossary
+Git Glossary
 ============
 
 include::glossary-content.txt[]
index 5d5976f94faa1b981f5c8ad9056ab4c5cc9dd3a6..4dba10e7f06d816741697356f402e2a2b5da3dbe 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -204,6 +204,18 @@ all::
 # memory allocators with the nedmalloc allocator written by Niall Douglas.
 #
 # Define NO_REGEX if you have no or inferior regex support in your C library.
+#
+# Define DEFAULT_PAGER to a sensible pager command (defaults to "less") if
+# you want to use something different.  The value will be interpreted by the
+# shell at runtime when it is used.
+#
+# Define DEFAULT_EDITOR to a sensible editor command (defaults to "vi") if you
+# want to use something different.  The value will be interpreted by the shell
+# if necessary when it is used.  Examples:
+#
+#   DEFAULT_EDITOR='~/bin/vi',
+#   DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR',
+#   DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork'
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -216,6 +228,12 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
 uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
 uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
 
+ifdef MSVC
+       # avoid the MingW and Cygwin configuration sections
+       uname_S := Windows
+       uname_O := Windows
+endif
+
 # CFLAGS and LDFLAGS are for the users to override from the command line.
 
 CFLAGS = -g -O2 -Wall
@@ -325,6 +343,7 @@ 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
@@ -358,6 +377,7 @@ EXTRA_PROGRAMS =
 PROGRAMS += $(EXTRA_PROGRAMS)
 PROGRAMS += git-fast-import$X
 PROGRAMS += git-hash-object$X
+PROGRAMS += git-imap-send$X
 PROGRAMS += git-index-pack$X
 PROGRAMS += git-merge-index$X
 PROGRAMS += git-merge-tree$X
@@ -369,6 +389,7 @@ PROGRAMS += git-show-index$X
 PROGRAMS += git-unpack-file$X
 PROGRAMS += git-upload-pack$X
 PROGRAMS += git-var$X
+PROGRAMS += git-http-backend$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.
@@ -437,6 +458,7 @@ LIB_H += ll-merge.h
 LIB_H += log-tree.h
 LIB_H += mailmap.h
 LIB_H += merge-recursive.h
+LIB_H += notes.h
 LIB_H += object.h
 LIB_H += pack.h
 LIB_H += pack-refs.h
@@ -522,6 +544,7 @@ LIB_OBJS += match-trees.o
 LIB_OBJS += merge-file.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += name-hash.o
+LIB_OBJS += notes.o
 LIB_OBJS += object.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-refs.o
@@ -601,7 +624,6 @@ 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--tool.o
 BUILTIN_OBJS += builtin-fetch-pack.o
 BUILTIN_OBJS += builtin-fetch.o
 BUILTIN_OBJS += builtin-fmt-merge-msg.o
@@ -783,12 +805,15 @@ ifeq ($(uname_O),Cygwin)
        NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
        NO_TRUSTABLE_FILEMODE = UnfortunatelyYes
        OLD_ICONV = UnfortunatelyYes
+       NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
        # There are conflicting reports about this.
        # On some boxes NO_MMAP is needed, and not so elsewhere.
        # Try commenting this out if you suspect MMAP is more efficient
        NO_MMAP = YesPlease
        NO_IPV6 = YesPlease
        X = .exe
+       COMPAT_OBJS += compat/cygwin.o
+       UNRELIABLE_FSTAT = UnfortunatelyYes
 endif
 ifeq ($(uname_S),FreeBSD)
        NEEDS_LIBICONV = YesPlease
@@ -898,15 +923,11 @@ ifeq ($(uname_S),HP-UX)
        NO_SYS_SELECT_H = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
 endif
-ifneq (,$(findstring CYGWIN,$(uname_S)))
-       COMPAT_OBJS += compat/cygwin.o
-       UNRELIABLE_FSTAT = UnfortunatelyYes
-endif
-ifdef MSVC
+ifeq ($(uname_S),Windows)
        GIT_VERSION := $(GIT_VERSION).MSVC
        pathsep = ;
        NO_PREAD = YesPlease
-       NO_OPENSSL = YesPlease
+       NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NO_IPV6 = YesPlease
@@ -936,6 +957,7 @@ ifdef MSVC
        NO_REGEX = YesPlease
        NO_CURL = YesPlease
        NO_PTHREADS = YesPlease
+       BLK_SHA1 = YesPlease
 
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
@@ -954,14 +976,13 @@ else
        BASIC_CFLAGS += -Zi -MTd
 endif
        X = .exe
-else
+endif
 ifneq (,$(findstring MINGW,$(uname_S)))
        pathsep = ;
        NO_PREAD = YesPlease
-       NO_OPENSSL = YesPlease
+       NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_SYMLINK_HEAD = YesPlease
-       NO_IPV6 = YesPlease
        NO_SETENV = YesPlease
        NO_UNSETENV = YesPlease
        NO_STRCASESTR = YesPlease
@@ -985,6 +1006,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
+       BLK_SHA1 = YesPlease
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o
@@ -1003,7 +1025,6 @@ else
        NO_PTHREADS = YesPlease
 endif
 endif
-endif
 
 -include config.mak.autogen
 -include config.mak
@@ -1082,7 +1103,6 @@ EXTLIBS += -lz
 
 ifndef NO_POSIX_ONLY_PROGRAMS
        PROGRAMS += git-daemon$X
-       PROGRAMS += git-imap-send$X
 endif
 ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
@@ -1370,6 +1390,22 @@ BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
        $(COMPAT_CFLAGS)
 LIB_OBJS += $(COMPAT_OBJS)
 
+# Quote for C
+
+ifdef DEFAULT_EDITOR
+DEFAULT_EDITOR_CQ = "$(subst ",\",$(subst \,\\,$(DEFAULT_EDITOR)))"
+DEFAULT_EDITOR_CQ_SQ = $(subst ','\'',$(DEFAULT_EDITOR_CQ))
+
+BASIC_CFLAGS += -DDEFAULT_EDITOR='$(DEFAULT_EDITOR_CQ_SQ)'
+endif
+
+ifdef DEFAULT_PAGER
+DEFAULT_PAGER_CQ = "$(subst ",\",$(subst \,\\,$(DEFAULT_PAGER)))"
+DEFAULT_PAGER_CQ_SQ = $(subst ','\'',$(DEFAULT_PAGER_CQ))
+
+BASIC_CFLAGS += -DDEFAULT_PAGER='$(DEFAULT_PAGER_CQ_SQ)'
+endif
+
 ALL_CFLAGS += $(BASIC_CFLAGS)
 ALL_LDFLAGS += $(BASIC_LDFLAGS)
 
@@ -1633,6 +1669,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
 # and the first level quoting from the shell that runs "echo".
 GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
+       @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
        @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
@@ -1806,7 +1843,10 @@ dist: git.spec git-archive$(X) configure
        gzip -f -9 $(GIT_TARNAME).tar
 
 rpm: dist
-       $(RPMBUILD) -ta $(GIT_TARNAME).tar.gz
+       $(RPMBUILD) \
+               --define "_source_filedigest_algorithm md5" \
+               --define "_binary_filedigest_algorithm md5" \
+               -ta $(GIT_TARNAME).tar.gz
 
 htmldocs = git-htmldocs-$(GIT_VERSION)
 manpages = git-manpages-$(GIT_VERSION)
@@ -1834,7 +1874,7 @@ distclean: clean
        $(RM) configure
 
 clean:
-       $(RM) *.o block-sha1/*.o arm/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
+       $(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
                $(LIB_FILE) $(XDIFF_LIB)
        $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
index ae4b1e81dfea0f63db10691ba0005c8888fd6dc7..cb666acc3cf8fdda777febbefac455f3667759f1 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -2,6 +2,7 @@
 
 int advice_push_nonfastforward = 1;
 int advice_status_hints = 1;
+int advice_commit_before_merge = 1;
 
 static struct {
        const char *name;
@@ -9,6 +10,7 @@ static struct {
 } advice_config[] = {
        { "pushnonfastforward", &advice_push_nonfastforward },
        { "statushints", &advice_status_hints },
+       { "commitbeforemerge", &advice_commit_before_merge },
 };
 
 int git_default_advice_config(const char *var, const char *value)
index e9df8e026c301767b12217ba0374860a15742f2e..3de5000d8cb35c1d2c31867b942a54b487bed836 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -3,6 +3,7 @@
 
 extern int advice_push_nonfastforward;
 extern int advice_status_hints;
+extern int advice_commit_before_merge;
 
 int git_default_advice_config(const char *var, const char *value);
 
index f667368d161609ea3c93fc6ebe2452b9cd0e557d..36e2f9dda5c85c346e31f45afa6d28b107679970 100644 (file)
@@ -823,12 +823,13 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch)
 
 static const char *stop_at_slash(const char *line, int llen)
 {
+       int nslash = p_value;
        int i;
 
        for (i = 0; i < llen; i++) {
                int ch = line[i];
-               if (ch == '/')
-                       return line + i;
+               if (ch == '/' && --nslash <= 0)
+                       return &line[i];
        }
        return NULL;
 }
index 7512773b401255e76aadd5be6e561432d7d60772..d4e25a595dadba43223e84b20759ab49343aa33a 100644 (file)
@@ -1604,6 +1604,9 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
                } while (ch != '\n' &&
                         cp < sb->final_buf + sb->final_buf_size);
        }
+
+       if (sb->final_buf_size && cp[-1] != '\n')
+               putchar('\n');
 }
 
 static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
@@ -1667,6 +1670,9 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
                } while (ch != '\n' &&
                         cp < sb->final_buf + sb->final_buf_size);
        }
+
+       if (sb->final_buf_size && cp[-1] != '\n')
+               putchar('\n');
 }
 
 static void output(struct scoreboard *sb, int option)
@@ -2352,6 +2358,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                        die_errno("cannot stat path '%s'", path);
        }
 
+       revs.disable_stdin = 1;
        setup_revisions(argc, argv, &revs, NULL);
        memset(&sb, 0, sizeof(sb));
 
index e3e7bdf52f0b1f597e6cfd3affcf75f41f183d75..b106c65d80dfc8fe794d46e34ea1ff78b04f3056 100644 (file)
@@ -7,6 +7,10 @@
 #include "builtin.h"
 #include "strbuf.h"
 
+static const char builtin_check_ref_format_usage[] =
+"git check-ref-format [--print] <refname>\n"
+"   or: git check-ref-format --branch <branchname-shorthand>";
+
 /*
  * Replace each run of adjacent slashes in src with a single slash,
  * and write the result to dst.
@@ -31,6 +35,9 @@ static void collapse_slashes(char *dst, const char *src)
 
 int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
 {
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage(builtin_check_ref_format_usage);
+
        if (argc == 3 && !strcmp(argv[1], "--branch")) {
                struct strbuf sb = STRBUF_INIT;
 
@@ -49,6 +56,6 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
                exit(0);
        }
        if (argc != 2)
-               usage("git check-ref-format refname");
+               usage(builtin_check_ref_format_usage);
        return !!check_ref_format(argv[1]);
 }
index 64670777312c71d45b10f72a1efe9fceae3e3b89..ddcb7a4bbbcd45dfd7437209f3187fff882e6e62 100644 (file)
@@ -105,7 +105,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
 
-       if (argc < 2)
+       if (argc < 2 || !strcmp(argv[1], "-h"))
                usage(commit_tree_usage);
        if (get_sha1(argv[1], tree_sha1))
                die("Not a valid object name %s", argv[1]);
index d525b894ec1211476707acb06bdebe25d5828dbb..e93a647c59f1f52a4b0eb92b6c84fd9cec0aad6a 100644 (file)
@@ -51,7 +51,7 @@ static const char *template_file;
 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;
+static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static char *untracked_files_arg;
 /*
  * The default commit message cleanup mode will remove the lines
@@ -91,8 +91,9 @@ static struct option builtin_commit_options[] = {
        OPT_FILENAME('F', "file", &logfile, "read log from file"),
        OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
        OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
-       OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
+       OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit"),
        OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
+       OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"),
        OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
        OPT_FILENAME('t', "template", &template_file, "use specified template file"),
        OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
@@ -381,7 +382,7 @@ static void determine_author_info(void)
        email = getenv("GIT_AUTHOR_EMAIL");
        date = getenv("GIT_AUTHOR_DATE");
 
-       if (use_message) {
+       if (use_message && !renew_authorship) {
                const char *a, *lb, *rb, *eol;
 
                a = strstr(use_message_buffer, "\nauthor ");
@@ -747,6 +748,9 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (force_author && !strchr(force_author, '>'))
                force_author = find_author_by_nickname(force_author);
 
+       if (force_author && renew_authorship)
+               die("Using both --reset-author and --author does not make sense");
+
        if (logfile || message.len || use_message)
                use_editor = 0;
        if (edit_flag)
@@ -780,6 +784,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
                use_message = edit_message;
        if (amend && !use_message)
                use_message = "HEAD";
+       if (!use_message && renew_authorship)
+               die("--reset-author can be used only with -C, -c or --amend.");
        if (use_message) {
                unsigned char sha1[20];
                static char utf8[] = "UTF-8";
@@ -999,7 +1005,7 @@ static int git_commit_config(const char *k, const char *v, void *cb)
        struct wt_status *s = cb;
 
        if (!strcmp(k, "commit.template"))
-               return git_config_string(&template_file, k, v);
+               return git_config_pathname(&template_file, k, v);
 
        return git_status_config(k, v, s);
 }
index eaa8a9d229c97ebaab9ee3aa09d2456f68cd172c..71be2a9364748668996696f6c74057dba43315b5 100644 (file)
@@ -5,12 +5,14 @@
 #include "builtin.h"
 #include "exec_cmd.h"
 #include "parse-options.h"
+#include "diff.h"
 
 #define SEEN           (1u<<0)
 #define MAX_TAGS       (FLAG_BITS - 1)
 
 static const char * const describe_usage[] = {
        "git describe [options] <committish>*",
+       "git describe [options] --dirty",
        NULL
 };
 
@@ -23,6 +25,13 @@ static int max_candidates = 10;
 static int found_names;
 static const char *pattern;
 static int always;
+static const char *dirty;
+
+/* diff-index command arguments to check if working tree is dirty. */
+static const char *diff_index_args[] = {
+       "diff-index", "--quiet", "HEAD", "--", NULL
+};
+
 
 struct commit_name {
        struct tag *tag;
@@ -96,8 +105,6 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
        if (!all) {
                if (!prio)
                        return 0;
-               if (!tags && prio < 2)
-                       return 0;
        }
        add_to_known_names(all ? path + 5 : path + 10, commit, prio, sha1);
        return 0;
@@ -184,6 +191,7 @@ static void describe(const char *arg, int last_one)
        struct possible_tag all_matches[MAX_TAGS];
        unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
        unsigned long seen_commits = 0;
+       unsigned int unannotated_cnt = 0;
 
        if (get_sha1(arg, sha1))
                die("Not a valid object name %s", arg);
@@ -192,13 +200,15 @@ static void describe(const char *arg, int last_one)
                die("%s is not a valid '%s' object", arg, commit_type);
 
        n = cmit->util;
-       if (n) {
+       if (n && (tags || all || n->prio == 2)) {
                /*
                 * Exact match to an existing ref.
                 */
                display_name(n);
                if (longformat)
                        show_suffix(0, n->tag ? n->tag->tagged->sha1 : sha1);
+               if (dirty)
+                       printf("%s", dirty);
                printf("\n");
                return;
        }
@@ -217,7 +227,9 @@ static void describe(const char *arg, int last_one)
                seen_commits++;
                n = c->util;
                if (n) {
-                       if (match_cnt < max_candidates) {
+                       if (!tags && !all && n->prio < 2) {
+                               unannotated_cnt++;
+                       } else if (match_cnt < max_candidates) {
                                struct possible_tag *t = &all_matches[match_cnt++];
                                t->name = n;
                                t->depth = seen_commits - 1;
@@ -256,10 +268,20 @@ static void describe(const char *arg, int last_one)
        if (!match_cnt) {
                const unsigned char *sha1 = cmit->object.sha1;
                if (always) {
-                       printf("%s\n", find_unique_abbrev(sha1, abbrev));
+                       printf("%s", find_unique_abbrev(sha1, abbrev));
+                       if (dirty)
+                               printf("%s", dirty);
+                       printf("\n");
                        return;
                }
-               die("cannot describe '%s'", sha1_to_hex(sha1));
+               if (unannotated_cnt)
+                       die("No annotated tags can describe '%s'.\n"
+                           "However, there were unannotated tags: try --tags.",
+                           sha1_to_hex(sha1));
+               else
+                       die("No tags can describe '%s'.\n"
+                           "Try --always, or create some tags.",
+                           sha1_to_hex(sha1));
        }
 
        qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
@@ -291,6 +313,8 @@ static void describe(const char *arg, int last_one)
        display_name(all_matches[0].name);
        if (abbrev)
                show_suffix(all_matches[0].depth, cmit->object.sha1);
+       if (dirty)
+               printf("%s", dirty);
        printf("\n");
 
        if (!last_one)
@@ -315,6 +339,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                           "only consider tags matching <pattern>"),
                OPT_BOOLEAN(0, "always",     &always,
                           "show abbreviated commit object as fallback"),
+               {OPTION_STRING, 0, "dirty",  &dirty, "mark",
+                          "append <mark> on dirty working tree (default: \"-dirty\")",
+                PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
                OPT_END(),
        };
 
@@ -355,7 +382,11 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                die("No names found, cannot describe anything.");
 
        if (argc == 0) {
+               if (dirty && !cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1, diff_index_args, prefix))
+                       dirty = NULL;
                describe("HEAD", 1);
+       } else if (dirty) {
+               die("--dirty is incompatible with committishes");
        } else {
                while (argc-- > 0) {
                        describe(*argv++, argc == 0);
index 79cedb72c44dca3edf400d86e401a93531b54407..2380c21951fb5fb8050ab1acf0e7f01f36ea5520 100644 (file)
@@ -104,6 +104,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        opt->abbrev = 0;
        opt->diff = 1;
+       opt->disable_stdin = 1;
        argc = setup_revisions(argc, argv, opt, NULL);
 
        while (--argc > 0) {
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
deleted file mode 100644 (file)
index 3dbdf7a..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "refs.h"
-#include "commit.h"
-#include "sigchain.h"
-
-static char *get_stdin(void)
-{
-       struct strbuf buf = STRBUF_INIT;
-       if (strbuf_read(&buf, 0, 1024) < 0) {
-               die_errno("error reading standard input");
-       }
-       return strbuf_detach(&buf, NULL);
-}
-
-static void show_new(enum object_type type, unsigned char *sha1_new)
-{
-       fprintf(stderr, "  %s: %s\n", typename(type),
-               find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
-}
-
-static int update_ref_env(const char *action,
-                     const char *refname,
-                     unsigned char *sha1,
-                     unsigned char *oldval)
-{
-       char msg[1024];
-       const char *rla = getenv("GIT_REFLOG_ACTION");
-
-       if (!rla)
-               rla = "(reflog update)";
-       if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
-               warning("reflog message too long: %.*s...", 50, msg);
-       return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
-}
-
-static int update_local_ref(const char *name,
-                           const char *new_head,
-                           const char *note,
-                           int verbose, int force)
-{
-       unsigned char sha1_old[20], sha1_new[20];
-       char oldh[41], newh[41];
-       struct commit *current, *updated;
-       enum object_type type;
-
-       if (get_sha1_hex(new_head, sha1_new))
-               die("malformed object name %s", new_head);
-
-       type = sha1_object_info(sha1_new, NULL);
-       if (type < 0)
-               die("object %s not found", new_head);
-
-       if (!*name) {
-               /* Not storing */
-               if (verbose) {
-                       fprintf(stderr, "* fetched %s\n", note);
-                       show_new(type, sha1_new);
-               }
-               return 0;
-       }
-
-       if (get_sha1(name, sha1_old)) {
-               const char *msg;
-       just_store:
-               /* new ref */
-               if (!strncmp(name, "refs/tags/", 10))
-                       msg = "storing tag";
-               else
-                       msg = "storing head";
-               fprintf(stderr, "* %s: storing %s\n",
-                       name, note);
-               show_new(type, sha1_new);
-               return update_ref_env(msg, name, sha1_new, NULL);
-       }
-
-       if (!hashcmp(sha1_old, sha1_new)) {
-               if (verbose) {
-                       fprintf(stderr, "* %s: same as %s\n", name, note);
-                       show_new(type, sha1_new);
-               }
-               return 0;
-       }
-
-       if (!strncmp(name, "refs/tags/", 10)) {
-               fprintf(stderr, "* %s: updating with %s\n", name, note);
-               show_new(type, sha1_new);
-               return update_ref_env("updating tag", name, sha1_new, NULL);
-       }
-
-       current = lookup_commit_reference(sha1_old);
-       updated = lookup_commit_reference(sha1_new);
-       if (!current || !updated)
-               goto just_store;
-
-       strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
-       strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
-
-       if (in_merge_bases(current, &updated, 1)) {
-               fprintf(stderr, "* %s: fast forward to %s\n",
-                       name, note);
-               fprintf(stderr, "  old..new: %s..%s\n", oldh, newh);
-               return update_ref_env("fast forward", name, sha1_new, sha1_old);
-       }
-       if (!force) {
-               fprintf(stderr,
-                       "* %s: not updating to non-fast forward %s\n",
-                       name, note);
-               fprintf(stderr,
-                       "  old...new: %s...%s\n", oldh, newh);
-               return 1;
-       }
-       fprintf(stderr,
-               "* %s: forcing update to non-fast forward %s\n",
-               name, note);
-       fprintf(stderr, "  old...new: %s...%s\n", oldh, newh);
-       return update_ref_env("forced-update", name, sha1_new, sha1_old);
-}
-
-static int append_fetch_head(FILE *fp,
-                            const char *head, const char *remote,
-                            const char *remote_name, const char *remote_nick,
-                            const char *local_name, int not_for_merge,
-                            int verbose, int force)
-{
-       struct commit *commit;
-       int remote_len, i, note_len;
-       unsigned char sha1[20];
-       char note[1024];
-       const char *what, *kind;
-
-       if (get_sha1(head, sha1))
-               return error("Not a valid object name: %s", head);
-       commit = lookup_commit_reference_gently(sha1, 1);
-       if (!commit)
-               not_for_merge = 1;
-
-       if (!strcmp(remote_name, "HEAD")) {
-               kind = "";
-               what = "";
-       }
-       else if (!strncmp(remote_name, "refs/heads/", 11)) {
-               kind = "branch";
-               what = remote_name + 11;
-       }
-       else if (!strncmp(remote_name, "refs/tags/", 10)) {
-               kind = "tag";
-               what = remote_name + 10;
-       }
-       else if (!strncmp(remote_name, "refs/remotes/", 13)) {
-               kind = "remote branch";
-               what = remote_name + 13;
-       }
-       else {
-               kind = "";
-               what = remote_name;
-       }
-
-       remote_len = strlen(remote);
-       for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)
-               ;
-       remote_len = i + 1;
-       if (4 < i && !strncmp(".git", remote + i - 3, 4))
-               remote_len = i - 3;
-
-       note_len = 0;
-       if (*what) {
-               if (*kind)
-                       note_len += sprintf(note + note_len, "%s ", kind);
-               note_len += sprintf(note + note_len, "'%s' of ", what);
-       }
-       note_len += sprintf(note + note_len, "%.*s", remote_len, remote);
-       fprintf(fp, "%s\t%s\t%s\n",
-               sha1_to_hex(commit ? commit->object.sha1 : sha1),
-               not_for_merge ? "not-for-merge" : "",
-               note);
-       return update_local_ref(local_name, head, note, verbose, force);
-}
-
-static char *keep;
-static void remove_keep(void)
-{
-       if (keep && *keep)
-               unlink(keep);
-}
-
-static void remove_keep_on_signal(int signo)
-{
-       remove_keep();
-       sigchain_pop(signo);
-       raise(signo);
-}
-
-static char *find_local_name(const char *remote_name, const char *refs,
-                            int *force_p, int *not_for_merge_p)
-{
-       const char *ref = refs;
-       int len = strlen(remote_name);
-
-       while (ref) {
-               const char *next;
-               int single_force, not_for_merge;
-
-               while (*ref == '\n')
-                       ref++;
-               if (!*ref)
-                       break;
-               next = strchr(ref, '\n');
-
-               single_force = not_for_merge = 0;
-               if (*ref == '+') {
-                       single_force = 1;
-                       ref++;
-               }
-               if (*ref == '.') {
-                       not_for_merge = 1;
-                       ref++;
-                       if (*ref == '+') {
-                               single_force = 1;
-                               ref++;
-                       }
-               }
-               if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
-                       const char *local_part = ref + len + 1;
-                       int retlen;
-
-                       if (!next)
-                               retlen = strlen(local_part);
-                       else
-                               retlen = next - local_part;
-                       *force_p = single_force;
-                       *not_for_merge_p = not_for_merge;
-                       return xmemdupz(local_part, retlen);
-               }
-               ref = next;
-       }
-       return NULL;
-}
-
-static int fetch_native_store(FILE *fp,
-                             const char *remote,
-                             const char *remote_nick,
-                             const char *refs,
-                             int verbose, int force)
-{
-       char buffer[1024];
-       int err = 0;
-
-       sigchain_push_common(remove_keep_on_signal);
-       atexit(remove_keep);
-
-       while (fgets(buffer, sizeof(buffer), stdin)) {
-               int len;
-               char *cp;
-               char *local_name;
-               int single_force, not_for_merge;
-
-               for (cp = buffer; *cp && !isspace(*cp); cp++)
-                       ;
-               if (*cp)
-                       *cp++ = 0;
-               len = strlen(cp);
-               if (len && cp[len-1] == '\n')
-                       cp[--len] = 0;
-               if (!strcmp(buffer, "failed"))
-                       die("Fetch failure: %s", remote);
-               if (!strcmp(buffer, "pack"))
-                       continue;
-               if (!strcmp(buffer, "keep")) {
-                       char *od = get_object_directory();
-                       int len = strlen(od) + strlen(cp) + 50;
-                       keep = xmalloc(len);
-                       sprintf(keep, "%s/pack/pack-%s.keep", od, cp);
-                       continue;
-               }
-
-               local_name = find_local_name(cp, refs,
-                                            &single_force, &not_for_merge);
-               if (!local_name)
-                       continue;
-               err |= append_fetch_head(fp,
-                                        buffer, remote, cp, remote_nick,
-                                        local_name, not_for_merge,
-                                        verbose, force || single_force);
-       }
-       return err;
-}
-
-static int parse_reflist(const char *reflist)
-{
-       const char *ref;
-
-       printf("refs='");
-       for (ref = reflist; ref; ) {
-               const char *next;
-               while (*ref && isspace(*ref))
-                       ref++;
-               if (!*ref)
-                       break;
-               for (next = ref; *next && !isspace(*next); next++)
-                       ;
-               printf("\n%.*s", (int)(next - ref), ref);
-               ref = next;
-       }
-       printf("'\n");
-
-       printf("rref='");
-       for (ref = reflist; ref; ) {
-               const char *next, *colon;
-               while (*ref && isspace(*ref))
-                       ref++;
-               if (!*ref)
-                       break;
-               for (next = ref; *next && !isspace(*next); next++)
-                       ;
-               if (*ref == '.')
-                       ref++;
-               if (*ref == '+')
-                       ref++;
-               colon = strchr(ref, ':');
-               putchar('\n');
-               printf("%.*s", (int)((colon ? colon : next) - ref), ref);
-               ref = next;
-       }
-       printf("'\n");
-       return 0;
-}
-
-static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,
-                               const char **refs)
-{
-       int i, matchlen, replacelen;
-       int found_one = 0;
-       const char *remote = *refs++;
-       numrefs--;
-
-       if (numrefs == 0) {
-               fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",
-                       remote);
-               printf("empty\n");
-       }
-
-       for (i = 0; i < numrefs; i++) {
-               const char *ref = refs[i];
-               const char *lref = ref;
-               const char *colon;
-               const char *tail;
-               const char *ls;
-               const char *next;
-
-               if (*lref == '+')
-                       lref++;
-               colon = strchr(lref, ':');
-               tail = lref + strlen(lref);
-               if (!(colon &&
-                     2 < colon - lref &&
-                     colon[-1] == '*' &&
-                     colon[-2] == '/' &&
-                     2 < tail - (colon + 1) &&
-                     tail[-1] == '*' &&
-                     tail[-2] == '/')) {
-                       /* not a glob */
-                       if (!found_one++)
-                               printf("explicit\n");
-                       printf("%s\n", ref);
-                       continue;
-               }
-
-               /* glob */
-               if (!found_one++)
-                       printf("glob\n");
-
-               /* lref to colon-2 is remote hierarchy name;
-                * colon+1 to tail-2 is local.
-                */
-               matchlen = (colon-1) - lref;
-               replacelen = (tail-1) - (colon+1);
-               for (ls = ls_remote_result; ls; ls = next) {
-                       const char *eol;
-                       unsigned char sha1[20];
-                       int namelen;
-
-                       while (*ls && isspace(*ls))
-                               ls++;
-                       next = strchr(ls, '\n');
-                       eol = !next ? (ls + strlen(ls)) : next;
-                       if (!memcmp("^{}", eol-3, 3))
-                               continue;
-                       if (eol - ls < 40)
-                               continue;
-                       if (get_sha1_hex(ls, sha1))
-                               continue;
-                       ls += 40;
-                       while (ls < eol && isspace(*ls))
-                               ls++;
-                       /* ls to next (or eol) is the name.
-                        * is it identical to lref to colon-2?
-                        */
-                       if ((eol - ls) <= matchlen ||
-                           strncmp(ls, lref, matchlen))
-                               continue;
-
-                       /* Yes, it is a match */
-                       namelen = eol - ls;
-                       if (lref != ref)
-                               putchar('+');
-                       printf("%.*s:%.*s%.*s\n",
-                              namelen, ls,
-                              replacelen, colon + 1,
-                              namelen - matchlen, ls + matchlen);
-               }
-       }
-       return 0;
-}
-
-static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_result)
-{
-       int err = 0;
-       int lrr_count = lrr_count, i, pass;
-       const char *cp;
-       struct lrr {
-               const char *line;
-               const char *name;
-               int namelen;
-               int shown;
-       } *lrr_list = lrr_list;
-
-       for (pass = 0; pass < 2; pass++) {
-               /* pass 0 counts and allocates, pass 1 fills... */
-               cp = ls_remote_result;
-               i = 0;
-               while (1) {
-                       const char *np;
-                       while (*cp && isspace(*cp))
-                               cp++;
-                       if (!*cp)
-                               break;
-                       np = strchrnul(cp, '\n');
-                       if (pass) {
-                               lrr_list[i].line = cp;
-                               lrr_list[i].name = cp + 41;
-                               lrr_list[i].namelen = np - (cp + 41);
-                       }
-                       i++;
-                       cp = np;
-               }
-               if (!pass) {
-                       lrr_count = i;
-                       lrr_list = xcalloc(lrr_count, sizeof(*lrr_list));
-               }
-       }
-
-       while (1) {
-               const char *next;
-               int rreflen;
-               int i;
-
-               while (*rref && isspace(*rref))
-                       rref++;
-               if (!*rref)
-                       break;
-               next = strchrnul(rref, '\n');
-               rreflen = next - rref;
-
-               for (i = 0; i < lrr_count; i++) {
-                       struct lrr *lrr = &(lrr_list[i]);
-
-                       if (rreflen == lrr->namelen &&
-                           !memcmp(lrr->name, rref, rreflen)) {
-                               if (!lrr->shown)
-                                       printf("%.*s\n",
-                                              sha1_only ? 40 : lrr->namelen + 41,
-                                              lrr->line);
-                               lrr->shown = 1;
-                               break;
-                       }
-               }
-               if (lrr_count <= i) {
-                       error("pick-rref: %.*s not found", rreflen, rref);
-                       err = 1;
-               }
-               rref = next;
-       }
-       free(lrr_list);
-       return err;
-}
-
-int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
-{
-       int verbose = 0;
-       int force = 0;
-       int sopt = 0;
-
-       while (1 < argc) {
-               const char *arg = argv[1];
-               if (!strcmp("-v", arg))
-                       verbose = 1;
-               else if (!strcmp("-f", arg))
-                       force = 1;
-               else if (!strcmp("-s", arg))
-                       sopt = 1;
-               else
-                       break;
-               argc--;
-               argv++;
-       }
-
-       if (argc <= 1)
-               return error("Missing subcommand");
-
-       if (!strcmp("append-fetch-head", argv[1])) {
-               int result;
-               FILE *fp;
-               char *filename;
-
-               if (argc != 8)
-                       return error("append-fetch-head takes 6 args");
-               filename = git_path("FETCH_HEAD");
-               fp = fopen(filename, "a");
-               if (!fp)
-                       return error("cannot open %s: %s\n", filename, strerror(errno));
-               result = append_fetch_head(fp, argv[2], argv[3],
-                                          argv[4], argv[5],
-                                          argv[6], !!argv[7][0],
-                                          verbose, force);
-               fclose(fp);
-               return result;
-       }
-       if (!strcmp("native-store", argv[1])) {
-               int result;
-               FILE *fp;
-               char *filename;
-
-               if (argc != 5)
-                       return error("fetch-native-store takes 3 args");
-               filename = git_path("FETCH_HEAD");
-               fp = fopen(filename, "a");
-               if (!fp)
-                       return error("cannot open %s: %s\n", filename, strerror(errno));
-               result = fetch_native_store(fp, argv[2], argv[3], argv[4],
-                                           verbose, force);
-               fclose(fp);
-               return result;
-       }
-       if (!strcmp("parse-reflist", argv[1])) {
-               const char *reflist;
-               if (argc != 3)
-                       return error("parse-reflist takes 1 arg");
-               reflist = argv[2];
-               if (!strcmp(reflist, "-"))
-                       reflist = get_stdin();
-               return parse_reflist(reflist);
-       }
-       if (!strcmp("pick-rref", argv[1])) {
-               const char *ls_remote_result;
-               if (argc != 4)
-                       return error("pick-rref takes 2 args");
-               ls_remote_result = argv[3];
-               if (!strcmp(ls_remote_result, "-"))
-                       ls_remote_result = get_stdin();
-               return pick_rref(sopt, argv[2], ls_remote_result);
-       }
-       if (!strcmp("expand-refs-wildcard", argv[1])) {
-               const char *reflist;
-               if (argc < 4)
-                       return error("expand-refs-wildcard takes at least 2 args");
-               reflist = argv[2];
-               if (!strcmp(reflist, "-"))
-                       reflist = get_stdin();
-               return expand_refs_wildcard(reflist, argc - 3, argv + 3);
-       }
-
-       return error("Unknown subcommand: %s", argv[1]);
-}
index 629735f54723a3bc3b24f8c91e9a6226ae28d742..8ed4a6feaac2868523e6516e02865132dedbc9f5 100644 (file)
@@ -157,6 +157,66 @@ static const unsigned char *get_rev(void)
        return commit->object.sha1;
 }
 
+enum ack_type {
+       NAK = 0,
+       ACK,
+       ACK_continue,
+       ACK_common,
+       ACK_ready
+};
+
+static void consume_shallow_list(int fd)
+{
+       if (args.stateless_rpc && args.depth > 0) {
+               /* If we sent a depth we will get back "duplicate"
+                * shallow and unshallow commands every time there
+                * is a block of have lines exchanged.
+                */
+               char line[1000];
+               while (packet_read_line(fd, line, sizeof(line))) {
+                       if (!prefixcmp(line, "shallow "))
+                               continue;
+                       if (!prefixcmp(line, "unshallow "))
+                               continue;
+                       die("git fetch-pack: expected shallow list");
+               }
+       }
+}
+
+static enum ack_type get_ack(int fd, unsigned char *result_sha1)
+{
+       static char line[1000];
+       int len = packet_read_line(fd, line, sizeof(line));
+
+       if (!len)
+               die("git fetch-pack: expected ACK/NAK, got EOF");
+       if (line[len-1] == '\n')
+               line[--len] = 0;
+       if (!strcmp(line, "NAK"))
+               return NAK;
+       if (!prefixcmp(line, "ACK ")) {
+               if (!get_sha1_hex(line+4, result_sha1)) {
+                       if (strstr(line+45, "continue"))
+                               return ACK_continue;
+                       if (strstr(line+45, "common"))
+                               return ACK_common;
+                       if (strstr(line+45, "ready"))
+                               return ACK_ready;
+                       return ACK;
+               }
+       }
+       die("git fetch_pack: expected ACK/NAK, got '%s'", line);
+}
+
+static void send_request(int fd, struct strbuf *buf)
+{
+       if (args.stateless_rpc) {
+               send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
+               packet_flush(fd);
+       } else
+               safe_write(fd, buf->buf, buf->len);
+}
+
 static int find_common(int fd[2], unsigned char *result_sha1,
                       struct ref *refs)
 {
@@ -165,7 +225,11 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        const unsigned char *sha1;
        unsigned in_vain = 0;
        int got_continue = 0;
+       struct strbuf req_buf = STRBUF_INIT;
+       size_t state_len = 0;
 
+       if (args.stateless_rpc && multi_ack == 1)
+               die("--stateless-rpc requires multi_ack_detailed");
        if (marked)
                for_each_ref(clear_marks, NULL);
        marked = 1;
@@ -175,6 +239,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        fetching = 0;
        for ( ; refs ; refs = refs->next) {
                unsigned char *remote = refs->old_sha1;
+               const char *remote_hex;
                struct object *o;
 
                /*
@@ -192,32 +257,42 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                        continue;
                }
 
-               if (!fetching)
-                       packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n",
-                                    sha1_to_hex(remote),
-                                    (multi_ack ? " multi_ack" : ""),
-                                    (use_sideband == 2 ? " side-band-64k" : ""),
-                                    (use_sideband == 1 ? " side-band" : ""),
-                                    (args.use_thin_pack ? " thin-pack" : ""),
-                                    (args.no_progress ? " no-progress" : ""),
-                                    (args.include_tag ? " include-tag" : ""),
-                                    (prefer_ofs_delta ? " ofs-delta" : ""));
-               else
-                       packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
+               remote_hex = sha1_to_hex(remote);
+               if (!fetching) {
+                       struct strbuf c = STRBUF_INIT;
+                       if (multi_ack == 2)     strbuf_addstr(&c, " multi_ack_detailed");
+                       if (multi_ack == 1)     strbuf_addstr(&c, " multi_ack");
+                       if (use_sideband == 2)  strbuf_addstr(&c, " side-band-64k");
+                       if (use_sideband == 1)  strbuf_addstr(&c, " side-band");
+                       if (args.use_thin_pack) strbuf_addstr(&c, " thin-pack");
+                       if (args.no_progress)   strbuf_addstr(&c, " no-progress");
+                       if (args.include_tag)   strbuf_addstr(&c, " include-tag");
+                       if (prefer_ofs_delta)   strbuf_addstr(&c, " ofs-delta");
+                       packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
+                       strbuf_release(&c);
+               } else
+                       packet_buf_write(&req_buf, "want %s\n", remote_hex);
                fetching++;
        }
+
+       if (!fetching) {
+               strbuf_release(&req_buf);
+               packet_flush(fd[1]);
+               return 1;
+       }
+
        if (is_repository_shallow())
-               write_shallow_commits(fd[1], 1);
+               write_shallow_commits(&req_buf, 1);
        if (args.depth > 0)
-               packet_write(fd[1], "deepen %d", args.depth);
-       packet_flush(fd[1]);
-       if (!fetching)
-               return 1;
+               packet_buf_write(&req_buf, "deepen %d", args.depth);
+       packet_buf_flush(&req_buf);
+       state_len = req_buf.len;
 
        if (args.depth > 0) {
                char line[1024];
                unsigned char sha1[20];
 
+               send_request(fd[1], &req_buf);
                while (packet_read_line(fd[0], line, sizeof(line))) {
                        if (!prefixcmp(line, "shallow ")) {
                                if (get_sha1_hex(line + 8, sha1))
@@ -239,45 +314,73 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                        }
                        die("expected shallow/unshallow, got %s", line);
                }
+       } else if (!args.stateless_rpc)
+               send_request(fd[1], &req_buf);
+
+       if (!args.stateless_rpc) {
+               /* If we aren't using the stateless-rpc interface
+                * we don't need to retain the headers.
+                */
+               strbuf_setlen(&req_buf, 0);
+               state_len = 0;
        }
 
        flushes = 0;
        retval = -1;
        while ((sha1 = get_rev())) {
-               packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
+               packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
                if (args.verbose)
                        fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
                in_vain++;
                if (!(31 & ++count)) {
                        int ack;
 
-                       packet_flush(fd[1]);
+                       packet_buf_flush(&req_buf);
+                       send_request(fd[1], &req_buf);
+                       strbuf_setlen(&req_buf, state_len);
                        flushes++;
 
                        /*
                         * We keep one window "ahead" of the other side, and
                         * will wait for an ACK only on the next one
                         */
-                       if (count == 32)
+                       if (!args.stateless_rpc && count == 32)
                                continue;
 
+                       consume_shallow_list(fd[0]);
                        do {
                                ack = get_ack(fd[0], result_sha1);
                                if (args.verbose && ack)
                                        fprintf(stderr, "got ack %d %s\n", ack,
                                                        sha1_to_hex(result_sha1));
-                               if (ack == 1) {
+                               switch (ack) {
+                               case ACK:
                                        flushes = 0;
                                        multi_ack = 0;
                                        retval = 0;
                                        goto done;
-                               } else if (ack == 2) {
+                               case ACK_common:
+                               case ACK_ready:
+                               case ACK_continue: {
                                        struct commit *commit =
                                                lookup_commit(result_sha1);
+                                       if (args.stateless_rpc
+                                        && ack == ACK_common
+                                        && !(commit->object.flags & COMMON)) {
+                                               /* We need to replay the have for this object
+                                                * on the next RPC request so the peer knows
+                                                * it is in common with us.
+                                                */
+                                               const char *hex = sha1_to_hex(result_sha1);
+                                               packet_buf_write(&req_buf, "have %s\n", hex);
+                                               state_len = req_buf.len;
+                                       }
                                        mark_common(commit, 0, 1);
                                        retval = 0;
                                        in_vain = 0;
                                        got_continue = 1;
+                                       break;
+                                       }
                                }
                        } while (ack);
                        flushes--;
@@ -289,20 +392,24 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                }
        }
 done:
-       packet_write(fd[1], "done\n");
+       packet_buf_write(&req_buf, "done\n");
+       send_request(fd[1], &req_buf);
        if (args.verbose)
                fprintf(stderr, "done\n");
        if (retval != 0) {
                multi_ack = 0;
                flushes++;
        }
+       strbuf_release(&req_buf);
+
+       consume_shallow_list(fd[0]);
        while (flushes || multi_ack) {
                int ack = get_ack(fd[0], result_sha1);
                if (ack) {
                        if (args.verbose)
                                fprintf(stderr, "got ack (%d) %s\n", ack,
                                        sha1_to_hex(result_sha1));
-                       if (ack == 1)
+                       if (ack == ACK)
                                return 0;
                        multi_ack = 1;
                        continue;
@@ -584,7 +691,12 @@ static struct ref *do_fetch_pack(int fd[2],
 
        if (is_repository_shallow() && !server_supports("shallow"))
                die("Server does not support shallow clients");
-       if (server_supports("multi_ack")) {
+       if (server_supports("multi_ack_detailed")) {
+               if (args.verbose)
+                       fprintf(stderr, "Server supports multi_ack_detailed\n");
+               multi_ack = 2;
+       }
+       else if (server_supports("multi_ack")) {
                if (args.verbose)
                        fprintf(stderr, "Server supports multi_ack\n");
                multi_ack = 1;
@@ -615,6 +727,8 @@ static struct ref *do_fetch_pack(int fd[2],
                         */
                        warning("no common commits");
 
+       if (args.stateless_rpc)
+               packet_flush(fd[1]);
        if (get_pack(fd, pack_lockfile))
                die("git fetch-pack: fetch failed.");
 
@@ -685,6 +799,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        struct ref *ref = NULL;
        char *dest = NULL, **heads;
        int fd[2];
+       char *pack_lockfile = NULL;
+       char **pack_lockfile_ptr = NULL;
        struct child_process *conn;
 
        nr_heads = 0;
@@ -734,6 +850,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                                args.no_progress = 1;
                                continue;
                        }
+                       if (!strcmp("--stateless-rpc", arg)) {
+                               args.stateless_rpc = 1;
+                               continue;
+                       }
+                       if (!strcmp("--lock-pack", arg)) {
+                               args.lock_pack = 1;
+                               pack_lockfile_ptr = &pack_lockfile;
+                               continue;
+                       }
                        usage(fetch_pack_usage);
                }
                dest = (char *)arg;
@@ -744,19 +869,27 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        if (!dest)
                usage(fetch_pack_usage);
 
-       conn = git_connect(fd, (char *)dest, args.uploadpack,
-                          args.verbose ? CONNECT_VERBOSE : 0);
-       if (conn) {
-               get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
-
-               ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
-               close(fd[0]);
-               close(fd[1]);
-               if (finish_connect(conn))
-                       ref = NULL;
+       if (args.stateless_rpc) {
+               conn = NULL;
+               fd[0] = 0;
+               fd[1] = 1;
        } else {
-               ref = NULL;
+               conn = git_connect(fd, (char *)dest, args.uploadpack,
+                                  args.verbose ? CONNECT_VERBOSE : 0);
        }
+
+       get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
+
+       ref = fetch_pack(&args, fd, conn, ref, dest,
+               nr_heads, heads, pack_lockfile_ptr);
+       if (pack_lockfile) {
+               printf("lock %s\n", pack_lockfile);
+               fflush(stdout);
+       }
+       close(fd[0]);
+       close(fd[1]);
+       if (finish_connect(conn))
+               ref = NULL;
        ret = !ref;
 
        if (!ret && nr_heads) {
@@ -809,6 +942,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
 
        if (args.depth > 0) {
                struct cache_time mtime;
+               struct strbuf sb = STRBUF_INIT;
                char *shallow = git_path("shallow");
                int fd;
 
@@ -826,12 +960,14 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
 
                fd = hold_lock_file_for_update(&lock, shallow,
                                               LOCK_DIE_ON_ERROR);
-               if (!write_shallow_commits(fd, 0)) {
+               if (!write_shallow_commits(&sb, 0)
+                || write_in_full(fd, sb.buf, sb.len) != sb.len) {
                        unlink_or_warn(shallow);
                        rollback_lock_file(&lock);
                } else {
                        commit_lock_file(&lock);
                }
+               strbuf_release(&sb);
        }
 
        reprepare_packed_git();
index a35a6f8cb8030bf9be98a001da26fddf0efdb912..5b7db616dcf5cc3bb178b2c4dbb989322c6b2374 100644 (file)
@@ -14,6 +14,9 @@
 
 static const char * const builtin_fetch_usage[] = {
        "git fetch [options] [<repository> <refspec>...]",
+       "git fetch [options] <group>",
+       "git fetch --multiple [options] [<repository> | <group>]...",
+       "git fetch --all [options]",
        NULL
 };
 
@@ -23,7 +26,7 @@ enum {
        TAGS_SET = 2
 };
 
-static int append, force, keep, update_head_ok, verbosity;
+static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -32,16 +35,24 @@ static struct transport *transport;
 
 static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
+       OPT_BOOLEAN(0, "all", &all,
+                   "fetch from all remotes"),
        OPT_BOOLEAN('a', "append", &append,
                    "append to .git/FETCH_HEAD instead of overwriting"),
        OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
                   "path to upload pack on remote end"),
        OPT_BOOLEAN('f', "force", &force,
                    "force overwrite of local branch"),
+       OPT_BOOLEAN('m', "multiple", &multiple,
+                   "fetch from multiple remotes"),
        OPT_SET_INT('t', "tags", &tags,
                    "fetch all tags and associated objects", TAGS_SET),
        OPT_SET_INT('n', NULL, &tags,
                    "do not fetch all tags (--no-tags)", TAGS_UNSET),
+       OPT_BOOLEAN('p', "prune", &prune,
+                   "prune tracking branches no longer on remote"),
+       OPT_BOOLEAN(0, "dry-run", &dry_run,
+                   "dry run"),
        OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
        OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
                    "allow updating of HEAD ref"),
@@ -178,6 +189,8 @@ static int s_update_ref(const char *action,
        char *rla = getenv("GIT_REFLOG_ACTION");
        static struct ref_lock *lock;
 
+       if (dry_run)
+               return 0;
        if (!rla)
                rla = default_rla.buf;
        snprintf(msg, sizeof(msg), "%s: %s", rla, action);
@@ -269,7 +282,7 @@ static int update_local_ref(struct ref *ref,
                strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
                strcat(quickref, "..");
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
-               r = s_update_ref("fast forward", ref, 1);
+               r = s_update_ref("fast-forward", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
                        SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
                        pretty_ref, r ? "  (unable to update local ref)" : "");
@@ -287,7 +300,7 @@ static int update_local_ref(struct ref *ref,
                        r ? "unable to update local ref" : "forced update");
                return r;
        } else {
-               sprintf(display, "! %-*s %-*s -> %s  (non fast forward)",
+               sprintf(display, "! %-*s %-*s -> %s  (non-fast-forward)",
                        SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
                        pretty_ref);
                return 1;
@@ -303,7 +316,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
        char note[1024];
        const char *what, *kind;
        struct ref *rm;
-       char *url, *filename = git_path("FETCH_HEAD");
+       char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
 
        fp = fopen(filename, "a");
        if (!fp)
@@ -485,11 +498,34 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
        return ret;
 }
 
+static int prune_refs(struct transport *transport, struct ref *ref_map)
+{
+       int result = 0;
+       struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
+       const char *dangling_msg = dry_run
+               ? "   (%s will become dangling)\n"
+               : "   (%s has become dangling)\n";
+
+       for (ref = stale_refs; ref; ref = ref->next) {
+               if (!dry_run)
+                       result |= delete_ref(ref->name, NULL, 0);
+               if (verbosity >= 0) {
+                       fprintf(stderr, " x %-*s %-*s -> %s\n",
+                               SUMMARY_WIDTH, "[deleted]",
+                               REFCOL_WIDTH, "(none)", prettify_refname(ref->name));
+                       warn_dangling_symref(stderr, dangling_msg, ref->name);
+               }
+       }
+       free_refs(stale_refs);
+       return result;
+}
+
 static int add_existing(const char *refname, const unsigned char *sha1,
                        int flag, void *cbdata)
 {
        struct string_list *list = (struct string_list *)cbdata;
-       string_list_insert(refname, list);
+       struct string_list_item *item = string_list_insert(refname, list);
+       item->util = (void *)sha1;
        return 0;
 }
 
@@ -615,9 +651,14 @@ static void check_not_current_branch(struct ref *ref_map)
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
+       struct string_list existing_refs = { NULL, 0, 0, 0 };
+       struct string_list_item *peer_item = NULL;
        struct ref *ref_map;
        struct ref *rm;
        int autotags = (transport->remote->fetch_tags == 1);
+
+       for_each_ref(add_existing, &existing_refs);
+
        if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
                tags = TAGS_SET;
        if (transport->remote->fetch_tags == -1)
@@ -627,7 +668,7 @@ static int do_fetch(struct transport *transport,
                die("Don't know how to fetch from %s", transport->url);
 
        /* if not appending, truncate FETCH_HEAD */
-       if (!append) {
+       if (!append && !dry_run) {
                char *filename = git_path("FETCH_HEAD");
                FILE *fp = fopen(filename, "w");
                if (!fp)
@@ -640,8 +681,13 @@ static int do_fetch(struct transport *transport,
                check_not_current_branch(ref_map);
 
        for (rm = ref_map; rm; rm = rm->next) {
-               if (rm->peer_ref)
-                       read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
+               if (rm->peer_ref) {
+                       peer_item = string_list_lookup(rm->peer_ref->name,
+                                                      &existing_refs);
+                       if (peer_item)
+                               hashcpy(rm->peer_ref->old_sha1,
+                                       peer_item->util);
+               }
        }
 
        if (tags == TAGS_DEFAULT && autotags)
@@ -650,6 +696,8 @@ static int do_fetch(struct transport *transport,
                free_refs(ref_map);
                return 1;
        }
+       if (prune)
+               prune_refs(transport, ref_map);
        free_refs(ref_map);
 
        /* if neither --no-tags nor --tags was specified, do automated tag
@@ -680,33 +728,100 @@ static void set_option(const char *name, const char *value)
                        name, transport->url);
 }
 
-int cmd_fetch(int argc, const char **argv, const char *prefix)
+static int get_one_remote_for_fetch(struct remote *remote, void *priv)
+{
+       struct string_list *list = priv;
+       if (!remote->skip_default_update)
+               string_list_append(remote->name, list);
+       return 0;
+}
+
+struct remote_group_data {
+       const char *name;
+       struct string_list *list;
+};
+
+static int get_remote_group(const char *key, const char *value, void *priv)
+{
+       struct remote_group_data *g = priv;
+
+       if (!prefixcmp(key, "remotes.") &&
+                       !strcmp(key + 8, g->name)) {
+               /* split list by white space */
+               int space = strcspn(value, " \t\n");
+               while (*value) {
+                       if (space > 1) {
+                               string_list_append(xstrndup(value, space),
+                                                  g->list);
+                       }
+                       value += space + (value[space] != '\0');
+                       space = strcspn(value, " \t\n");
+               }
+       }
+
+       return 0;
+}
+
+static int add_remote_or_group(const char *name, struct string_list *list)
+{
+       int prev_nr = list->nr;
+       struct remote_group_data g = { name, list };
+
+       git_config(get_remote_group, &g);
+       if (list->nr == prev_nr) {
+               struct remote *remote;
+               if (!remote_is_configured(name))
+                       return 0;
+               remote = remote_get(name);
+               string_list_append(remote->name, list);
+       }
+       return 1;
+}
+
+static int fetch_multiple(struct string_list *list)
+{
+       int i, result = 0;
+       const char *argv[] = { "fetch", NULL, NULL, NULL, NULL, NULL, NULL };
+       int argc = 1;
+
+       if (dry_run)
+               argv[argc++] = "--dry-run";
+       if (prune)
+               argv[argc++] = "--prune";
+       if (verbosity >= 2)
+               argv[argc++] = "-v";
+       if (verbosity >= 1)
+               argv[argc++] = "-v";
+       else if (verbosity < 0)
+               argv[argc++] = "-q";
+
+       for (i = 0; i < list->nr; i++) {
+               const char *name = list->items[i].string;
+               argv[argc] = name;
+               if (verbosity >= 0)
+                       printf("Fetching %s\n", name);
+               if (run_command_v_opt(argv, RUN_GIT_CMD)) {
+                       error("Could not fetch %s", name);
+                       result = 1;
+               }
+       }
+
+       return result;
+}
+
+static int fetch_one(struct remote *remote, int argc, const char **argv)
 {
-       struct remote *remote;
        int i;
        static const char **refs = NULL;
        int ref_nr = 0;
        int exit_code;
 
-       /* Record the command line for the reflog */
-       strbuf_addstr(&default_rla, "fetch");
-       for (i = 1; i < argc; i++)
-               strbuf_addf(&default_rla, " %s", argv[i]);
-
-       argc = parse_options(argc, argv, prefix,
-                            builtin_fetch_options, builtin_fetch_usage, 0);
-
-       if (argc == 0)
-               remote = remote_get(NULL);
-       else
-               remote = remote_get(argv[0]);
-
        if (!remote)
                die("Where do you want to fetch from today?");
 
        transport = transport_get(remote, remote->url[0]);
        if (verbosity >= 2)
-               transport->verbose = 1;
+               transport->verbose = verbosity <= 3 ? verbosity : 3;
        if (verbosity < 0)
                transport->verbose = -1;
        if (upload_pack)
@@ -716,10 +831,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (depth)
                set_option(TRANS_OPT_DEPTH, depth);
 
-       if (argc > 1) {
+       if (argc > 0) {
                int j = 0;
                refs = xcalloc(argc + 1, sizeof(const char *));
-               for (i = 1; i < argc; i++) {
+               for (i = 0; i < argc; i++) {
                        if (!strcmp(argv[i], "tag")) {
                                char *ref;
                                i++;
@@ -746,3 +861,57 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        transport = NULL;
        return exit_code;
 }
+
+int cmd_fetch(int argc, const char **argv, const char *prefix)
+{
+       int i;
+       struct string_list list = { NULL, 0, 0, 0 };
+       struct remote *remote;
+       int result = 0;
+
+       /* Record the command line for the reflog */
+       strbuf_addstr(&default_rla, "fetch");
+       for (i = 1; i < argc; i++)
+               strbuf_addf(&default_rla, " %s", argv[i]);
+
+       argc = parse_options(argc, argv, prefix,
+                            builtin_fetch_options, builtin_fetch_usage, 0);
+
+       if (all) {
+               if (argc == 1)
+                       die("fetch --all does not take a repository argument");
+               else if (argc > 1)
+                       die("fetch --all does not make sense with refspecs");
+               (void) for_each_remote(get_one_remote_for_fetch, &list);
+               result = fetch_multiple(&list);
+       } else if (argc == 0) {
+               /* No arguments -- use default remote */
+               remote = remote_get(NULL);
+               result = fetch_one(remote, argc, argv);
+       } else if (multiple) {
+               /* All arguments are assumed to be remotes or groups */
+               for (i = 0; i < argc; i++)
+                       if (!add_remote_or_group(argv[i], &list))
+                               die("No such remote or remote group: %s", argv[i]);
+               result = fetch_multiple(&list);
+       } else {
+               /* Single remote or group */
+               (void) add_remote_or_group(argv[0], &list);
+               if (list.nr > 1) {
+                       /* More than one remote */
+                       if (argc > 1)
+                               die("Fetching a group and specifying refspecs does not make sense");
+                       result = fetch_multiple(&list);
+               } else {
+                       /* Zero or one remotes */
+                       remote = remote_get(argv[0]);
+                       result = fetch_one(remote, argc-1, argv+1);
+               }
+       }
+
+       /* All names were strdup()ed or strndup()ed */
+       list.strdup_strings = 1;
+       string_list_clear(&list, 0);
+
+       return result;
+}
index 2d88e4570f8217beaf7f5400dc3c3a328222ebe8..0e5faae381ed94c26671fcae04ea96d3656d7627 100644 (file)
@@ -47,6 +47,7 @@ static void objreport(struct object *obj, const char *severity,
        fputs("\n", stderr);
 }
 
+__attribute__((format (printf, 2, 3)))
 static int objerror(struct object *obj, const char *err, ...)
 {
        va_list params;
@@ -57,6 +58,7 @@ static int objerror(struct object *obj, const char *err, ...)
        return -1;
 }
 
+__attribute__((format (printf, 3, 4)))
 static int fsck_error_func(struct object *obj, int type, const char *err, ...)
 {
        va_list params;
index 1df25b07b573301bba7fdcb3b13d1af9050f513e..a5b6719a1af014497399ea6ff4f1a6f3852a0570 100644 (file)
@@ -367,7 +367,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
                push_arg("-h");
        if (opt->regflags & REG_EXTENDED)
                push_arg("-E");
-       if (opt->regflags & REG_ICASE)
+       if (opt->ignore_case)
                push_arg("-i");
        if (opt->binary == GREP_BINARY_NOMATCH)
                push_arg("-I");
@@ -433,7 +433,11 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 
                if (opt->color_external && strlen(opt->color_external) > 0)
                        push_arg(opt->color_external);
+       } else {
+               unsetenv("GREP_COLOR");
+               unsetenv("GREP_COLORS");
        }
+       unsetenv("GREP_OPTIONS");
 
        hit = 0;
        argc = nr;
@@ -706,8 +710,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_GROUP(""),
                OPT_BOOLEAN('v', "invert-match", &opt.invert,
                        "show non-matching lines"),
-               OPT_BIT('i', "ignore-case", &opt.regflags,
-                       "case insensitive matching", REG_ICASE),
+               OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case,
+                       "case insensitive matching"),
                OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp,
                        "match patterns only at word boundaries"),
                OPT_SET_INT('a', "text", &opt.binary,
@@ -788,6 +792,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       /*
+        * 'git grep -h', unlike 'git grep -h <pattern>', is a request
+        * to show usage information and exit.
+        */
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(grep_usage, options);
+
        memset(&opt, 0, sizeof(opt));
        opt.prefix = prefix;
        opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
@@ -830,6 +841,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                external_grep_allowed = 0;
        if (!opt.pattern_list)
                die("no pattern given.");
+       if (!opt.fixed && opt.ignore_case)
+               opt.regflags |= REG_ICASE;
        if ((opt.regflags != REG_NEWLINE) && opt.fixed)
                die("cannot mix --fixed-strings and regexp");
        compile_grep_patterns(&opt);
index 207a36178bafcbd755789fefb35b74054d1ff2fa..33fa6ea6c855df904cfcf30f0b413cb8bca61fa9 100644 (file)
@@ -50,6 +50,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
        if (default_date_mode)
                rev->date_mode = parse_date_format(default_date_mode);
 
+       /*
+        * Check for -h before setup_revisions(), or "git log -h" will
+        * fail when run without a git directory.
+        */
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage(builtin_log_usage);
        argc = setup_revisions(argc, argv, rev, "HEAD");
 
        if (rev->diffopt.pickaxe || rev->diffopt.filter)
@@ -891,6 +897,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        struct patch_ids ids;
        char *add_signoff = NULL;
        struct strbuf buf = STRBUF_INIT;
+       int use_patch_format = 0;
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            "use [PATCH n/m] even with a single patch",
@@ -922,6 +929,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                            "don't output binary diffs"),
                OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
                            "don't include a patch matching a commit upstream"),
+               { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL,
+                 "show patch format instead of default (patch + stat)",
+                 PARSE_OPT_NONEG | PARSE_OPT_NOARG },
                OPT_GROUP("Messaging"),
                { OPTION_CALLBACK, 0, "add-header", NULL, "header",
                            "add email header", PARSE_OPT_NONEG,
@@ -1027,9 +1037,20 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (argc > 1)
                die ("unrecognized argument: %s", argv[1]);
 
-       if (!rev.diffopt.output_format
-               || rev.diffopt.output_format == DIFF_FORMAT_PATCH)
-               rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH;
+       if (rev.diffopt.output_format & DIFF_FORMAT_NAME)
+               die("--name-only does not make sense");
+       if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS)
+               die("--name-status does not make sense");
+       if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF)
+               die("--check does not make sense");
+
+       if (!use_patch_format &&
+               (!rev.diffopt.output_format ||
+                rev.diffopt.output_format == DIFF_FORMAT_PATCH))
+               rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY;
+
+       /* Always generate a patch */
+       rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 
        if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
                DIFF_OPT_SET(&rev.diffopt, BINARY);
@@ -1237,6 +1258,9 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                argv++;
        }
 
+       if (argc > 1 && !strcmp(argv[1], "-h"))
+               usage(cherry_usage);
+
        switch (argc) {
        case 4:
                limit = argv[3];
index c5c0407b0b824327b0bbc6ae50ae1a030761b2b9..c9a03e5427bbf1390aeefb9d48b4e362ca6a8e1b 100644 (file)
@@ -170,6 +170,10 @@ static void show_files(struct dir_struct *dir, const char *prefix)
        if (show_cached | show_stage) {
                for (i = 0; i < active_nr; i++) {
                        struct cache_entry *ce = active_cache[i];
+                       int dtype = ce_to_dtype(ce);
+                       if (dir->flags & DIR_SHOW_IGNORED &&
+                           !excluded(dir, ce->name, &dtype))
+                               continue;
                        if (show_unmerged && !ce_stage(ce))
                                continue;
                        if (ce->ce_flags & CE_UPDATE)
@@ -182,6 +186,10 @@ static void show_files(struct dir_struct *dir, const char *prefix)
                        struct cache_entry *ce = active_cache[i];
                        struct stat st;
                        int err;
+                       int dtype = ce_to_dtype(ce);
+                       if (dir->flags & DIR_SHOW_IGNORED &&
+                           !excluded(dir, ce->name, &dtype))
+                               continue;
                        if (ce->ce_flags & CE_UPDATE)
                                continue;
                        err = lstat(ce->name, &st);
index 22008dfa8fb94d971b6fb1bd7342ede02a8d3fd0..4484185afc4c144bc0d88d9c3832cbeb1515841b 100644 (file)
@@ -9,6 +9,7 @@
 #include "commit.h"
 #include "quote.h"
 #include "builtin.h"
+#include "parse-options.h"
 
 static int line_termination = '\n';
 #define LS_RECURSIVE 1
@@ -22,8 +23,10 @@ static const char **pathspec;
 static int chomp_prefix;
 static const char *ls_tree_prefix;
 
-static const char ls_tree_usage[] =
-       "git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] <tree-ish> [path...]";
+static const  char * const ls_tree_usage[] = {
+       "git ls-tree [<options>] <tree-ish> [path...]",
+       NULL
+};
 
 static int show_recursive(const char *base, int baselen, const char *pathname)
 {
@@ -117,76 +120,53 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 {
        unsigned char sha1[20];
        struct tree *tree;
+       int full_tree = 0;
+       const struct option ls_tree_options[] = {
+               OPT_BIT('d', NULL, &ls_options, "only show trees",
+                       LS_TREE_ONLY),
+               OPT_BIT('r', NULL, &ls_options, "recurse into subtrees",
+                       LS_RECURSIVE),
+               OPT_BIT('t', NULL, &ls_options, "show trees when recursing",
+                       LS_SHOW_TREES),
+               OPT_SET_INT('z', NULL, &line_termination,
+                           "terminate entries with NUL byte", 0),
+               OPT_BIT('l', "long", &ls_options, "include object size",
+                       LS_SHOW_SIZE),
+               OPT_BIT(0, "name-only", &ls_options, "list only filenames",
+                       LS_NAME_ONLY),
+               OPT_BIT(0, "name-status", &ls_options, "list only filenames",
+                       LS_NAME_ONLY),
+               OPT_SET_INT(0, "full-name", &chomp_prefix,
+                           "use full path names", 0),
+               OPT_BOOLEAN(0, "full-tree", &full_tree,
+                           "list entire tree; not just current directory "
+                           "(implies --full-name)"),
+               OPT__ABBREV(&abbrev),
+               OPT_END()
+       };
 
        git_config(git_default_config, NULL);
        ls_tree_prefix = prefix;
        if (prefix && *prefix)
                chomp_prefix = strlen(prefix);
-       while (1 < argc && argv[1][0] == '-') {
-               switch (argv[1][1]) {
-               case 'z':
-                       line_termination = 0;
-                       break;
-               case 'r':
-                       ls_options |= LS_RECURSIVE;
-                       break;
-               case 'd':
-                       ls_options |= LS_TREE_ONLY;
-                       break;
-               case 't':
-                       ls_options |= LS_SHOW_TREES;
-                       break;
-               case 'l':
-                       ls_options |= LS_SHOW_SIZE;
-                       break;
-               case '-':
-                       if (!strcmp(argv[1]+2, "name-only") ||
-                           !strcmp(argv[1]+2, "name-status")) {
-                               ls_options |= LS_NAME_ONLY;
-                               break;
-                       }
-                       if (!strcmp(argv[1]+2, "long")) {
-                               ls_options |= LS_SHOW_SIZE;
-                               break;
-                       }
-                       if (!strcmp(argv[1]+2, "full-name")) {
-                               chomp_prefix = 0;
-                               break;
-                       }
-                       if (!strcmp(argv[1]+2, "full-tree")) {
-                               ls_tree_prefix = prefix = NULL;
-                               chomp_prefix = 0;
-                               break;
-                       }
-                       if (!prefixcmp(argv[1]+2, "abbrev=")) {
-                               abbrev = strtoul(argv[1]+9, NULL, 10);
-                               if (abbrev && abbrev < MINIMUM_ABBREV)
-                                       abbrev = MINIMUM_ABBREV;
-                               else if (abbrev > 40)
-                                       abbrev = 40;
-                               break;
-                       }
-                       if (!strcmp(argv[1]+2, "abbrev")) {
-                               abbrev = DEFAULT_ABBREV;
-                               break;
-                       }
-                       /* otherwise fallthru */
-               default:
-                       usage(ls_tree_usage);
-               }
-               argc--; argv++;
+
+       argc = parse_options(argc, argv, prefix, ls_tree_options,
+                            ls_tree_usage, 0);
+       if (full_tree) {
+               ls_tree_prefix = prefix = NULL;
+               chomp_prefix = 0;
        }
        /* -d -r should imply -t, but -d by itself should not have to. */
        if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
            ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
                ls_options |= LS_SHOW_TREES;
 
-       if (argc < 2)
-               usage(ls_tree_usage);
-       if (get_sha1(argv[1], sha1))
-               die("Not a valid object name %s", argv[1]);
+       if (argc < 1)
+               usage_with_options(ls_tree_usage, ls_tree_options);
+       if (get_sha1(argv[0], sha1))
+               die("Not a valid object name %s", argv[0]);
 
-       pathspec = get_pathspec(prefix, argv + 2);
+       pathspec = get_pathspec(prefix, argv + 1);
        tree = parse_tree_indirect(sha1);
        if (!tree)
                die("not a tree object");
index c90cd312ac99fe9a2169664e86a089b5378cacbe..a50ac2256cdbacd76ed44a50804212be07f949db 100644 (file)
@@ -10,6 +10,7 @@
 static FILE *cmitmsg, *patchfile, *fin, *fout;
 
 static int keep_subject;
+static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
 static struct strbuf line = STRBUF_INIT;
 static struct strbuf name = STRBUF_INIT;
@@ -26,6 +27,7 @@ static struct strbuf charset = STRBUF_INIT;
 static int patch_lines;
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
+static int use_inbody_headers = 1;
 
 #define MAX_HDR_PARSED 10
 #define MAX_BOUNDARIES 5
@@ -220,35 +222,41 @@ static int is_multipart_boundary(const struct strbuf *line)
 
 static void cleanup_subject(struct strbuf *subject)
 {
-       char *pos;
-       size_t remove;
-       while (subject->len) {
-               switch (*subject->buf) {
+       size_t at = 0;
+
+       while (at < subject->len) {
+               char *pos;
+               size_t remove;
+
+               switch (subject->buf[at]) {
                case 'r': case 'R':
-                       if (subject->len <= 3)
+                       if (subject->len <= at + 3)
                                break;
-                       if (!memcmp(subject->buf + 1, "e:", 2)) {
-                               strbuf_remove(subject, 0, 3);
+                       if (!memcmp(subject->buf + at + 1, "e:", 2)) {
+                               strbuf_remove(subject, at, 3);
                                continue;
                        }
+                       at++;
                        break;
                case ' ': case '\t': case ':':
-                       strbuf_remove(subject, 0, 1);
+                       strbuf_remove(subject, at, 1);
                        continue;
                case '[':
-                       if ((pos = strchr(subject->buf, ']'))) {
-                               remove = pos - subject->buf;
-                               if (remove <= (subject->len - remove) * 2) {
-                                       strbuf_remove(subject, 0, remove + 1);
-                                       continue;
-                               }
-                       } else
-                               strbuf_remove(subject, 0, 1);
-                       break;
+                       pos = strchr(subject->buf + at, ']');
+                       if (!pos)
+                               break;
+                       remove = pos - subject->buf + at + 1;
+                       if (!keep_non_patch_brackets_in_subject ||
+                           (7 <= remove &&
+                            memmem(subject->buf + at, remove, "PATCH", 5)))
+                               strbuf_remove(subject, at, remove);
+                       else
+                               at += remove;
+                       continue;
                }
-               strbuf_trim(subject);
-               return;
+               break;
        }
+       strbuf_trim(subject);
 }
 
 static void cleanup_space(struct strbuf *sb)
@@ -774,10 +782,17 @@ static int handle_commit_msg(struct strbuf *line)
                strbuf_ltrim(line);
                if (!line->len)
                        return 0;
+       }
+
+       if (use_inbody_headers && still_looking) {
                still_looking = check_header(line, s_hdr_data, 0);
                if (still_looking)
                        return 0;
-       }
+       } else
+               /* Only trim the first (blank) line of the commit message
+                * when ignoring in-body headers.
+                */
+               still_looking = 0;
 
        /* normalize the log message to UTF-8. */
        if (metainfo_charset)
@@ -1006,7 +1021,7 @@ static int git_mailinfo_config(const char *var, const char *value, void *unused)
 }
 
 static const char mailinfo_usage[] =
-       "git mailinfo [-k] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info";
+       "git mailinfo [-k|-b] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info";
 
 int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
@@ -1023,6 +1038,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
        while (1 < argc && argv[1][0] == '-') {
                if (!strcmp(argv[1], "-k"))
                        keep_subject = 1;
+               else if (!strcmp(argv[1], "-b"))
+                       keep_non_patch_brackets_in_subject = 1;
                else if (!strcmp(argv[1], "-u"))
                        metainfo_charset = def_charset;
                else if (!strcmp(argv[1], "-n"))
@@ -1033,6 +1050,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
                        use_scissors = 1;
                else if (!strcmp(argv[1], "--no-scissors"))
                        use_scissors = 0;
+               else if (!strcmp(argv[1], "--no-inbody-headers"))
+                       use_inbody_headers = 0;
                else
                        usage(mailinfo_usage);
                argc--; argv++;
index dfe5b151e6e0030abf6248808d414b2fecc5fdb7..207e358ed19cecb8cf7b57d59a9149619909459d 100644 (file)
@@ -231,6 +231,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
                        continue;
                } else if ( arg[1] == 'f' ) {
                        nr = strtol(arg+2, NULL, 10);
+               } else if ( arg[1] == 'h' ) {
+                       usage(git_mailsplit_usage);
                } else if ( arg[1] == 'b' && !arg[2] ) {
                        allow_bare = 1;
                } else if (!strcmp(arg, "--keep-cr")) {
index 8f5bbaf402e020e308e7af9cedb7df1b2ec5a2b7..684411694f64ca216fb03430df2d2a0ec9ea93ff 100644 (file)
@@ -10,6 +10,9 @@
 #include "git-compat-util.h"
 #include "builtin.h"
 
+static const char builtin_merge_ours_usage[] =
+       "git merge-ours <base>... -- HEAD <remote>...";
+
 static const char *diff_index_args[] = {
        "diff-index", "--quiet", "--cached", "HEAD", "--", NULL
 };
@@ -17,6 +20,9 @@ static const char *diff_index_args[] = {
 
 int cmd_merge_ours(int argc, const char **argv, const char *prefix)
 {
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage(builtin_merge_ours_usage);
+
        /*
         * We need to exit with 2 if the index does not match our HEAD tree,
         * because the current index is what we will be committing as the
index d26a96e486f2a84fdfc54527d15b9acdf7ea34bb..710674c6b2cccec4f1e0baa477a11d78cda72833 100644 (file)
@@ -33,7 +33,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        }
 
        if (argc < 4)
-               die("Usage: %s <base>... -- <head> <remote> ...", argv[0]);
+               usagef("%s <base>... -- <head> <remote> ...", argv[0]);
 
        for (i = 1; i < argc; ++i) {
                if (!strcmp(argv[i], "--"))
index c69a3051f38eb70f148015471d0c2df1df8db33b..57eedd447d0f899a258998bb3039905a5a49babb 100644 (file)
@@ -43,6 +43,7 @@ static const char * const builtin_merge_usage[] = {
 
 static int show_diffstat = 1, option_log, squash;
 static int option_commit = 1, allow_fast_forward = 1;
+static int fast_forward_only;
 static int allow_trivial = 1, have_message;
 static struct strbuf merge_msg;
 static struct commit_list *remoteheads;
@@ -166,7 +167,9 @@ static struct option builtin_merge_options[] = {
        OPT_BOOLEAN(0, "commit", &option_commit,
                "perform a commit if the merge succeeds (default)"),
        OPT_BOOLEAN(0, "ff", &allow_fast_forward,
-               "allow fast forward (default)"),
+               "allow fast-forward (default)"),
+       OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
+               "abort if fast-forward is not possible"),
        OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
                "merge strategy to use", option_parse_strategy),
        OPT_CALLBACK('m', "message", &merge_msg, "message",
@@ -843,7 +846,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        const char *best_strategy = NULL, *wt_strategy = NULL;
        struct commit_list **remotes = &remoteheads;
 
-       setup_work_tree();
        if (file_exists(git_path("MERGE_HEAD")))
                die("You have not concluded your merge. (MERGE_HEAD exists)");
        if (read_cache_unmerged())
@@ -877,6 +879,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                option_commit = 0;
        }
 
+       if (!allow_fast_forward && fast_forward_only)
+               die("You cannot combine --no-ff with --ff-only.");
+
        if (!argc)
                usage_with_options(builtin_merge_usage,
                        builtin_merge_options);
@@ -1016,7 +1021,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                                hex,
                                find_unique_abbrev(remoteheads->item->object.sha1,
                                DEFAULT_ABBREV));
-               strbuf_addstr(&msg, "Fast forward");
+               strbuf_addstr(&msg, "Fast-forward");
                if (have_message)
                        strbuf_addstr(&msg,
                                " (no commit created; -m option ignored)");
@@ -1034,16 +1039,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        } else if (!remoteheads->next && common->next)
                ;
                /*
-                * We are not doing octopus and not fast forward.  Need
+                * We are not doing octopus and not fast-forward.  Need
                 * a real merge.
                 */
        else if (!remoteheads->next && !common->next && option_commit) {
                /*
-                * We are not doing octopus, not fast forward, and have
+                * We are not doing octopus, not fast-forward, and have
                 * only one common.
                 */
                refresh_cache(REFRESH_QUIET);
-               if (allow_trivial) {
+               if (allow_trivial && !fast_forward_only) {
                        /* See if it is really trivial. */
                        git_committer_info(IDENT_ERROR_ON_NO_NAME);
                        printf("Trying really trivial in-index merge...\n");
@@ -1082,6 +1087,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (fast_forward_only)
+               die("Not possible to fast-forward, aborting.");
+
        /* We are going to make a new commit. */
        git_committer_info(IDENT_ERROR_ON_NO_NAME);
 
index 1b20028c67dec2f9954cb107620bd863b4b3a264..f633d81424f5e41cb85ac660dbdc9f4913852673 100644 (file)
@@ -64,15 +64,15 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
 
-       newfd = hold_locked_index(&lock_file, 1);
-       if (read_cache() < 0)
-               die("index file corrupt");
-
        argc = parse_options(argc, argv, prefix, builtin_mv_options,
                             builtin_mv_usage, 0);
        if (--argc < 1)
                usage_with_options(builtin_mv_usage, builtin_mv_options);
 
+       newfd = hold_locked_index(&lock_file, 1);
+       if (read_cache() < 0)
+               die("index file corrupt");
+
        source = copy_pathspec(prefix, argv, argc, 0);
        modes = xcalloc(argc, sizeof(enum update_mode));
        dest_path = copy_pathspec(prefix, argv + argc, 1, 0);
index 4c91e944c21df3d5e02535115cdd6098ee49d86d..4429d53a1e82b81f0a82e34bf2d6ae463aeeadee 100644 (file)
@@ -24,6 +24,7 @@
 
 static const char pack_usage[] =
   "git pack-objects [{ -q | --progress | --all-progress }]\n"
+  "        [--all-progress-implied]\n"
   "        [--max-pack-size=N] [--local] [--incremental]\n"
   "        [--window=N] [--window-memory=N] [--depth=N]\n"
   "        [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset]\n"
@@ -2124,6 +2125,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 {
        int use_internal_rev_list = 0;
        int thin = 0;
+       int all_progress_implied = 0;
        uint32_t i;
        const char **rp_av;
        int rp_ac_alloc = 64;
@@ -2223,6 +2225,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        progress = 2;
                        continue;
                }
+               if (!strcmp("--all-progress-implied", arg)) {
+                       all_progress_implied = 1;
+                       continue;
+               }
                if (!strcmp("-q", arg)) {
                        progress = 0;
                        continue;
@@ -2326,6 +2332,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (keep_unreachable && unpack_unreachable)
                die("--keep-unreachable and --unpack-unreachable are incompatible.");
 
+       if (progress && all_progress_implied)
+               progress = 2;
+
        prepare_packed_git();
 
        if (progress)
index be99eb0ac4162411d63d10003fa80e12206b8cc6..f9463deec2f5a5875ee9870e67c990a2fea942db 100644 (file)
@@ -71,7 +71,7 @@ void prune_packed_objects(int opts)
 
 int cmd_prune_packed(int argc, const char **argv, const char *prefix)
 {
-       int opts = VERBOSE;
+       int opts = isatty(2) ? VERBOSE : 0;
        const struct option prune_packed_options[] = {
                OPT_BIT('n', "dry-run", &opts, "dry run", DRY_RUN),
                OPT_NEGBIT('q', "quiet", &opts, "be quiet", VERBOSE),
index 8631c06ed6cfdfe12286c5c72ee3f2b733be8986..356d7c1fd3a6fae9558e93eb4f240242a60da368 100644 (file)
@@ -66,7 +66,6 @@ static void setup_push_tracking(void)
 
 static void setup_default_push_refspecs(void)
 {
-       git_config(git_default_config, NULL);
        switch (push_default) {
        default:
        case PUSH_DEFAULT_MATCHING:
@@ -159,7 +158,7 @@ static int do_push(const char *repo, int flags)
                error("failed to push some refs to '%s'", url[i]);
                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 'non-fast forward'\n"
+                              "Merge the remote changes before pushing again.  See the 'non-fast-forward'\n"
                               "section of 'git push --help' for details.\n");
                }
                errs++;
@@ -173,7 +172,6 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        int tags = 0;
        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),
@@ -191,6 +189,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, options, push_usage, 0);
 
        if (tags)
index 14c836b1693317d5d834606e5613d7ceacb3189c..2a3a32cbfe83a0e0366d04a99bb49606f647f594 100644 (file)
@@ -108,11 +108,11 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 
        git_config(git_default_config, NULL);
 
-       newfd = hold_locked_index(&lock_file, 1);
-
        argc = parse_options(argc, argv, unused_prefix, read_tree_options,
                             read_tree_usage, 0);
 
+       newfd = hold_locked_index(&lock_file, 1);
+
        prefix_set = opts.prefix ? 1 : 0;
        if (1 < opts.merge + opts.reset + prefix_set)
                die("Which one? -m, --reset, or --prefix?");
index e8bde02c271ace4745e245b001652b3b96d798f5..78c0e69cdc9fe2d21e83947932cb353c0553452e 100644 (file)
@@ -341,9 +341,9 @@ static const char *update(struct command *cmd)
                                break;
                free_commit_list(bases);
                if (!ent) {
-                       error("denying non-fast forward %s"
+                       error("denying non-fast-forward %s"
                              " (you should pull first)", name);
-                       return "non-fast forward";
+                       return "non-fast-forward";
                }
        }
        if (run_update_hook(cmd)) {
@@ -627,6 +627,8 @@ static void add_alternate_refs(void)
 
 int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 {
+       int advertise_refs = 0;
+       int stateless_rpc = 0;
        int i;
        char *dir = NULL;
 
@@ -635,7 +637,15 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                const char *arg = *argv++;
 
                if (*arg == '-') {
-                       /* Do flag handling here */
+                       if (!strcmp(arg, "--advertise-refs")) {
+                               advertise_refs = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--stateless-rpc")) {
+                               stateless_rpc = 1;
+                               continue;
+                       }
+
                        usage(receive_pack_usage);
                }
                if (dir)
@@ -664,12 +674,16 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                " report-status delete-refs ofs-delta " :
                " report-status delete-refs ";
 
-       add_alternate_refs();
-       write_head_info();
-       clear_extra_refs();
+       if (advertise_refs || !stateless_rpc) {
+               add_alternate_refs();
+               write_head_info();
+               clear_extra_refs();
 
-       /* EOF */
-       packet_flush(1);
+               /* EOF */
+               packet_flush(1);
+       }
+       if (advertise_refs)
+               return 0;
 
        read_head_info();
        if (commands) {
index e23b5ef979d98e5e44693345856a13485695246f..749821078df129cf13de34ebbd40a8cb7a38e00e 100644 (file)
@@ -698,6 +698,9 @@ static const char reflog_usage[] =
 
 int cmd_reflog(int argc, const char **argv, const char *prefix)
 {
+       if (argc > 1 && !strcmp(argv[1], "-h"))
+               usage(reflog_usage);
+
        /* With no command, we default to showing it. */
        if (argc < 2 || *argv[1] == '-')
                return cmd_log_reflog(argc, argv, prefix);
index 0777dd719b41ec4c545b336bc5144a73d34001d7..79166262182a1cb51839e20b8abcef8f6752b59f 100644 (file)
@@ -12,10 +12,45 @@ static const char * const builtin_remote_usage[] = {
        "git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
        "git remote rename <old> <new>",
        "git remote rm <name>",
-       "git remote set-head <name> [-a | -d | <branch>]",
-       "git remote show [-n] <name>",
+       "git remote set-head <name> (-a | -d | <branch>)",
+       "git remote [-v | --verbose] show [-n] <name>",
        "git remote prune [-n | --dry-run] <name>",
-       "git remote [-v | --verbose] update [-p | --prune] [group]",
+       "git remote [-v | --verbose] update [-p | --prune] [group | remote]",
+       NULL
+};
+
+static const char * const builtin_remote_add_usage[] = {
+       "git remote add [<options>] <name> <url>",
+       NULL
+};
+
+static const char * const builtin_remote_rename_usage[] = {
+       "git remote rename <old> <new>",
+       NULL
+};
+
+static const char * const builtin_remote_rm_usage[] = {
+       "git remote rm <name>",
+       NULL
+};
+
+static const char * const builtin_remote_sethead_usage[] = {
+       "git remote set-head <name> (-a | -d | <branch>])",
+       NULL
+};
+
+static const char * const builtin_remote_show_usage[] = {
+       "git remote show [<options>] <name>",
+       NULL
+};
+
+static const char * const builtin_remote_prune_usage[] = {
+       "git remote prune [<options>] <name>",
+       NULL
+};
+
+static const char * const builtin_remote_update_usage[] = {
+       "git remote update [<options>] [<group> | <remote>]...",
        NULL
 };
 
@@ -70,7 +105,6 @@ static int add(int argc, const char **argv)
        int i;
 
        struct option options[] = {
-               OPT_GROUP("add specific options"),
                OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
                OPT_CALLBACK('t', "track", &track, "branch",
                        "branch(es) to track", opt_parse_track),
@@ -79,11 +113,11 @@ static int add(int argc, const char **argv)
                OPT_END()
        };
 
-       argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_add_usage,
                             0);
 
        if (argc < 2)
-               usage_with_options(builtin_remote_usage, options);
+               usage_with_options(builtin_remote_add_usage, options);
 
        name = argv[0];
        url = argv[1];
@@ -227,32 +261,10 @@ struct ref_states {
        int queried;
 };
 
-static int handle_one_branch(const char *refname,
-       const unsigned char *sha1, int flags, void *cb_data)
-{
-       struct ref_states *states = cb_data;
-       struct refspec refspec;
-
-       memset(&refspec, 0, sizeof(refspec));
-       refspec.dst = (char *)refname;
-       if (!remote_find_tracking(states->remote, &refspec)) {
-               struct string_list_item *item;
-               const char *name = abbrev_branch(refspec.src);
-               /* symbolic refs pointing nowhere were handled already */
-               if ((flags & REF_ISSYMREF) ||
-                   string_list_has_string(&states->tracked, name) ||
-                   string_list_has_string(&states->new, name))
-                       return 0;
-               item = string_list_append(name, &states->stale);
-               item->util = xstrdup(refname);
-       }
-       return 0;
-}
-
 static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
 {
        struct ref *fetch_map = NULL, **tail = &fetch_map;
-       struct ref *ref;
+       struct ref *ref, *stale_refs;
        int i;
 
        for (i = 0; i < states->remote->fetch_refspec_nr; i++)
@@ -268,11 +280,17 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
                else
                        string_list_append(abbrev_branch(ref->name), &states->tracked);
        }
+       stale_refs = get_stale_heads(states->remote, fetch_map);
+       for (ref = stale_refs; ref; ref = ref->next) {
+               struct string_list_item *item =
+                       string_list_append(abbrev_branch(ref->name), &states->stale);
+               item->util = xstrdup(ref->name);
+       }
+       free_refs(stale_refs);
        free_refs(fetch_map);
 
        sort_string_list(&states->new);
        sort_string_list(&states->tracked);
-       for_each_ref(handle_one_branch, states);
        sort_string_list(&states->stale);
 
        return 0;
@@ -540,7 +558,7 @@ static int mv(int argc, const char **argv)
        int i;
 
        if (argc != 3)
-               usage_with_options(builtin_remote_usage, options);
+               usage_with_options(builtin_remote_rename_usage, options);
 
        rename.old = argv[1];
        rename.new = argv[2];
@@ -681,7 +699,7 @@ static int rm(int argc, const char **argv)
        int i, result;
 
        if (argc != 2)
-               usage_with_options(builtin_remote_usage, options);
+               usage_with_options(builtin_remote_rm_usage, options);
 
        remote = remote_get(argv[1]);
        if (!remote)
@@ -953,7 +971,7 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data)
                status = "up to date";
                break;
        case PUSH_STATUS_FASTFORWARD:
-               status = "fast forwardable";
+               status = "fast-forwardable";
                break;
        case PUSH_STATUS_OUTOFDATE:
                status = "local out of date";
@@ -976,7 +994,6 @@ static int show(int argc, const char **argv)
 {
        int no_query = 0, result = 0, query_flag = 0;
        struct option options[] = {
-               OPT_GROUP("show specific options"),
                OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"),
                OPT_END()
        };
@@ -984,7 +1001,7 @@ static int show(int argc, const char **argv)
        struct string_list info_list = { NULL, 0, 0, 0 };
        struct show_info info;
 
-       argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
                             0);
 
        if (argc < 1)
@@ -1081,14 +1098,13 @@ static int set_head(int argc, const char **argv)
        char *head_name = NULL;
 
        struct option options[] = {
-               OPT_GROUP("set-head specific options"),
                OPT_BOOLEAN('a', "auto", &opt_a,
                            "set refs/remotes/<name>/HEAD according to remote"),
                OPT_BOOLEAN('d', "delete", &opt_d,
                            "delete refs/remotes/<name>/HEAD"),
                OPT_END()
        };
-       argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage,
                             0);
        if (argc)
                strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
@@ -1114,7 +1130,7 @@ static int set_head(int argc, const char **argv)
                if (delete_ref(buf.buf, NULL, REF_NODEREF))
                        result |= error("Could not delete %s", buf.buf);
        } else
-               usage_with_options(builtin_remote_usage, options);
+               usage_with_options(builtin_remote_sethead_usage, options);
 
        if (head_name) {
                unsigned char sha1[20];
@@ -1138,16 +1154,15 @@ static int prune(int argc, const char **argv)
 {
        int dry_run = 0, result = 0;
        struct option options[] = {
-               OPT_GROUP("prune specific options"),
                OPT__DRY_RUN(&dry_run),
                OPT_END()
        };
 
-       argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,
                             0);
 
        if (argc < 1)
-               usage_with_options(builtin_remote_usage, options);
+               usage_with_options(builtin_remote_prune_usage, options);
 
        for (; argc; argc--, argv++)
                result |= prune_remote(*argv, dry_run);
@@ -1182,95 +1197,62 @@ static int prune_remote(const char *remote, int dry_run)
 
                printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
                       abbrev_ref(refname, "refs/remotes/"));
-               warn_dangling_symref(dangling_msg, refname);
+               warn_dangling_symref(stdout, dangling_msg, refname);
        }
 
        free_remote_ref_states(&states);
        return result;
 }
 
-static int get_one_remote_for_update(struct remote *remote, void *priv)
+static int get_remote_default(const char *key, const char *value, void *priv)
 {
-       struct string_list *list = priv;
-       if (!remote->skip_default_update)
-               string_list_append(remote->name, list);
-       return 0;
-}
-
-static struct remote_group {
-       const char *name;
-       struct string_list *list;
-} remote_group;
-
-static int get_remote_group(const char *key, const char *value, void *num_hits)
-{
-       if (!prefixcmp(key, "remotes.") &&
-                       !strcmp(key + 8, remote_group.name)) {
-               /* split list by white space */
-               int space = strcspn(value, " \t\n");
-               while (*value) {
-                       if (space > 1) {
-                               string_list_append(xstrndup(value, space),
-                                               remote_group.list);
-                               ++*((int *)num_hits);
-                       }
-                       value += space + (value[space] != '\0');
-                       space = strcspn(value, " \t\n");
-               }
+       if (strcmp(key, "remotes.default") == 0) {
+               int *found = priv;
+               *found = 1;
        }
-
        return 0;
 }
 
 static int update(int argc, const char **argv)
 {
-       int i, result = 0, prune = 0;
-       struct string_list list = { NULL, 0, 0, 0 };
-       static const char *default_argv[] = { NULL, "default", NULL };
+       int i, prune = 0;
        struct option options[] = {
-               OPT_GROUP("update specific options"),
                OPT_BOOLEAN('p', "prune", &prune,
                            "prune remotes after fetching"),
                OPT_END()
        };
+       const char **fetch_argv;
+       int fetch_argc = 0;
+       int default_defined = 0;
+
+       fetch_argv = xmalloc(sizeof(char *) * (argc+5));
 
-       argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
                             PARSE_OPT_KEEP_ARGV0);
-       if (argc < 2) {
-               argc = 2;
-               argv = default_argv;
-       }
 
-       remote_group.list = &list;
-       for (i = 1; i < argc; i++) {
-               int groups_found = 0;
-               remote_group.name = argv[i];
-               result = git_config(get_remote_group, &groups_found);
-               if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) {
-                       struct remote *remote;
-                       if (!remote_is_configured(argv[i]))
-                               die("No such remote or remote group: %s",
-                                   argv[i]);
-                       remote = remote_get(argv[i]);
-                       string_list_append(remote->name, remote_group.list);
-               }
-       }
+       fetch_argv[fetch_argc++] = "fetch";
 
-       if (!result && !list.nr  && argc == 2 && !strcmp(argv[1], "default"))
-               result = for_each_remote(get_one_remote_for_update, &list);
+       if (prune)
+               fetch_argv[fetch_argc++] = "--prune";
+       if (verbose)
+               fetch_argv[fetch_argc++] = "-v";
+       if (argc < 2) {
+               fetch_argv[fetch_argc++] = "default";
+       } else {
+               fetch_argv[fetch_argc++] = "--multiple";
+               for (i = 1; i < argc; i++)
+                       fetch_argv[fetch_argc++] = argv[i];
+       }
 
-       for (i = 0; i < list.nr; i++) {
-               int err = fetch_remote(list.items[i].string);
-               result |= err;
-               if (!err && prune)
-                       result |= prune_remote(list.items[i].string, 0);
+       if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) {
+               git_config(get_remote_default, &default_defined);
+               if (!default_defined)
+                       fetch_argv[fetch_argc-1] = "--all";
        }
 
-       /* all names were strdup()ed or strndup()ed */
-       list.strdup_strings = 1;
-       string_list_clear(&list, 0);
+       fetch_argv[fetch_argc] = NULL;
 
-       return result;
+       return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
 }
 
 static int get_one_entry(struct remote *remote, void *priv)
@@ -1334,7 +1316,7 @@ static int show_all(void)
 int cmd_remote(int argc, const char **argv, const char *prefix)
 {
        struct option options[] = {
-               OPT__VERBOSE(&verbose),
+               OPT_BOOLEAN('v', "verbose", &verbose, "be verbose; must be placed before a subcommand"),
                OPT_END()
        };
        int result;
index adfb7b5f48597c19c23235b74cf1a0c779985dc6..343d6cde48266d63c0aa6826b973e55b48820745 100644 (file)
@@ -106,6 +106,9 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
        if (argc < 2)
                return rerere();
 
+       if (!strcmp(argv[1], "-h"))
+               usage(git_rerere_usage);
+
        fd = setup_rerere(&merge_rr);
        if (fd < 0)
                return 0;
index 42cc8d8872afbc1302f14e089db430014330f7c3..91b604289dd59101cb6477fa49dd9c342e5b39e5 100644 (file)
@@ -306,7 +306,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        struct rev_info revs;
        struct rev_list_info info;
        int i;
-       int read_from_stdin = 0;
        int bisect_list = 0;
        int bisect_show_vars = 0;
        int bisect_find_all = 0;
@@ -320,6 +319,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
        memset(&info, 0, sizeof(info));
        info.revs = &revs;
+       if (revs.bisect)
+               bisect_list = 1;
 
        quiet = DIFF_OPT_TST(&revs.diffopt, QUIET);
        for (i = 1 ; i < argc; i++) {
@@ -349,12 +350,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        bisect_show_vars = 1;
                        continue;
                }
-               if (!strcmp(arg, "--stdin")) {
-                       if (read_from_stdin++)
-                               die("--stdin given twice?");
-                       read_revisions_from_stdin(&revs);
-                       continue;
-               }
                usage(rev_list_usage);
 
        }
index 45bead65451e87a4b564e59fed3518a4de9198e9..37d02335212dea9672e2472970f66dc4ac95dfd1 100644 (file)
@@ -180,6 +180,12 @@ static int show_reference(const char *refname, const unsigned char *sha1, int fl
        return 0;
 }
 
+static int anti_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+       show_rev(REVERSED, sha1, refname);
+       return 0;
+}
+
 static void show_datestring(const char *flag, const char *datestr)
 {
        static char buffer[100];
@@ -426,6 +432,13 @@ static void die_no_single_rev(int quiet)
                die("Needed a single revision");
 }
 
+static const char builtin_rev_parse_usage[] =
+"git rev-parse --parseopt [options] -- [<args>...]\n"
+"   or: git rev-parse --sq-quote [<arg>...]\n"
+"   or: git rev-parse [options] [<arg>...]\n"
+"\n"
+"Run \"git rev-parse --parseopt -h\" for more information on the first usage.";
+
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
@@ -438,6 +451,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
        if (argc > 1 && !strcmp("--sq-quote", argv[1]))
                return cmd_sq_quote(argc - 2, argv + 2);
 
+       if (argc > 1 && !strcmp("-h", argv[1]))
+               usage(builtin_rev_parse_usage);
+
        prefix = setup_git_directory();
        git_config(git_default_config, NULL);
        for (i = 1; i < argc; i++) {
@@ -548,6 +564,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
+                       if (!strcmp(arg, "--bisect")) {
+                               for_each_ref_in("refs/bisect/bad", show_reference, NULL);
+                               for_each_ref_in("refs/bisect/good", anti_reference, NULL);
+                               continue;
+                       }
                        if (!strcmp(arg, "--branches")) {
                                for_each_branch_ref(show_reference, NULL);
                                continue;
index 37e528e28364fbde5a5ba31316aa7bf66d43586b..8fffdbf20058e9970af4b5e4a14349ecb4ff455c 100644 (file)
@@ -2,9 +2,11 @@
 #include "commit.h"
 #include "refs.h"
 #include "pkt-line.h"
+#include "sideband.h"
 #include "run-command.h"
 #include "remote.h"
 #include "send-pack.h"
+#include "quote.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"
@@ -38,7 +40,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
         */
        const char *argv[] = {
                "pack-objects",
-               "--all-progress",
+               "--all-progress-implied",
                "--revs",
                "--stdout",
                NULL,
@@ -59,7 +61,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
        memset(&po, 0, sizeof(po));
        po.argv = argv;
        po.in = -1;
-       po.out = fd;
+       po.out = args->stateless_rpc ? -1 : fd;
        po.git_cmd = 1;
        if (start_command(&po))
                die_errno("git pack-objects failed");
@@ -83,6 +85,20 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
        }
 
        close(po.in);
+
+       if (args->stateless_rpc) {
+               char *buf = xmalloc(LARGE_PACKET_MAX);
+               while (1) {
+                       ssize_t n = xread(po.out, buf, LARGE_PACKET_MAX);
+                       if (n <= 0)
+                               break;
+                       send_sideband(fd, -1, buf, n, LARGE_PACKET_MAX);
+               }
+               free(buf);
+               close(po.out);
+               po.out = -1;
+       }
+
        if (finish_command(&po))
                return error("pack-objects died with strange error");
        return 0;
@@ -246,7 +262,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count)
                break;
        case REF_STATUS_REJECT_NONFASTFORWARD:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                               "non-fast forward");
+                               "non-fast-forward");
                break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
@@ -303,6 +319,59 @@ static int refs_pushed(struct ref *ref)
        return 0;
 }
 
+static void print_helper_status(struct ref *ref)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       for (; ref; ref = ref->next) {
+               const char *msg = NULL;
+               const char *res;
+
+               switch(ref->status) {
+               case REF_STATUS_NONE:
+                       res = "error";
+                       msg = "no match";
+                       break;
+
+               case REF_STATUS_OK:
+                       res = "ok";
+                       break;
+
+               case REF_STATUS_UPTODATE:
+                       res = "ok";
+                       msg = "up to date";
+                       break;
+
+               case REF_STATUS_REJECT_NONFASTFORWARD:
+                       res = "error";
+                       msg = "non-fast forward";
+                       break;
+
+               case REF_STATUS_REJECT_NODELETE:
+               case REF_STATUS_REMOTE_REJECT:
+                       res = "error";
+                       break;
+
+               case REF_STATUS_EXPECTING_REPORT:
+               default:
+                       continue;
+               }
+
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "%s %s", res, ref->name);
+               if (ref->remote_status)
+                       msg = ref->remote_status;
+               if (msg) {
+                       strbuf_addch(&buf, ' ');
+                       quote_two_c_style(&buf, "", msg, 0);
+               }
+               strbuf_addch(&buf, '\n');
+
+               safe_write(1, buf.buf, buf.len);
+       }
+       strbuf_release(&buf);
+}
+
 int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
@@ -310,6 +379,7 @@ int send_pack(struct send_pack_args *args,
 {
        int in = fd[0];
        int out = fd[1];
+       struct strbuf req_buf = STRBUF_INIT;
        struct ref *ref;
        int new_refs;
        int ask_for_status_report = 0;
@@ -391,14 +461,14 @@ int send_pack(struct send_pack_args *args,
                        char *new_hex = sha1_to_hex(ref->new_sha1);
 
                        if (ask_for_status_report) {
-                               packet_write(out, "%s %s %s%c%s",
+                               packet_buf_write(&req_buf, "%s %s %s%c%s",
                                        old_hex, new_hex, ref->name, 0,
                                        "report-status");
                                ask_for_status_report = 0;
                                expect_status_report = 1;
                        }
                        else
-                               packet_write(out, "%s %s %s",
+                               packet_buf_write(&req_buf, "%s %s %s",
                                        old_hex, new_hex, ref->name);
                }
                ref->status = expect_status_report ?
@@ -406,7 +476,17 @@ int send_pack(struct send_pack_args *args,
                        REF_STATUS_OK;
        }
 
-       packet_flush(out);
+       if (args->stateless_rpc) {
+               if (!args->dry_run) {
+                       packet_buf_flush(&req_buf);
+                       send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
+               }
+       } else {
+               safe_write(out, req_buf.buf, req_buf.len);
+               packet_flush(out);
+       }
+       strbuf_release(&req_buf);
+
        if (new_refs && !args->dry_run) {
                if (pack_objects(out, remote_refs, extra_have, args) < 0) {
                        for (ref = remote_refs; ref; ref = ref->next)
@@ -414,11 +494,15 @@ int send_pack(struct send_pack_args *args,
                        return -1;
                }
        }
+       if (args->stateless_rpc && !args->dry_run)
+               packet_flush(out);
 
        if (expect_status_report)
                ret = receive_status(in, remote_refs);
        else
                ret = 0;
+       if (args->stateless_rpc)
+               packet_flush(out);
 
        if (ret < 0)
                return ret;
@@ -478,6 +562,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        struct extra_have_objects extra_have;
        struct ref *remote_refs, *local_refs;
        int ret;
+       int helper_status = 0;
        int send_all = 0;
        const char *receivepack = "git-receive-pack";
        int flags;
@@ -523,6 +608,14 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                                args.use_thin_pack = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--stateless-rpc")) {
+                               args.stateless_rpc = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--helper-status")) {
+                               helper_status = 1;
+                               continue;
+                       }
                        usage(send_pack_usage);
                }
                if (!dest) {
@@ -551,7 +644,14 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                }
        }
 
-       conn = git_connect(fd, dest, receivepack, args.verbose ? CONNECT_VERBOSE : 0);
+       if (args.stateless_rpc) {
+               conn = NULL;
+               fd[0] = 0;
+               fd[1] = 1;
+       } else {
+               conn = git_connect(fd, dest, receivepack,
+                       args.verbose ? CONNECT_VERBOSE : 0);
+       }
 
        memset(&extra_have, 0, sizeof(extra_have));
 
@@ -575,12 +675,16 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 
        ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
 
+       if (helper_status)
+               print_helper_status(remote_refs);
+
        close(fd[1]);
        close(fd[0]);
 
        ret |= finish_connect(conn);
 
-       print_push_status(dest, remote_refs);
+       if (!helper_status)
+               print_push_status(dest, remote_refs);
 
        if (!args.dry_run && remote) {
                struct ref *ref;
index 8aa63c7857fb3a704826bf223ddefb3e40f0eaf7..b3b055f68ce59b6b91ef6949bd8c4bd0bed68b55 100644 (file)
@@ -139,8 +139,12 @@ static void read_from_stdin(struct shortlog *log)
 void shortlog_add_commit(struct shortlog *log, struct commit *commit)
 {
        const char *author = NULL, *buffer;
+       struct strbuf buf = STRBUF_INIT;
+       struct strbuf ufbuf = STRBUF_INIT;
+       struct pretty_print_context ctx = {0};
 
-       buffer = commit->buffer;
+       pretty_print_commit(CMIT_FMT_RAW, commit, &buf, &ctx);
+       buffer = buf.buf;
        while (*buffer && *buffer != '\n') {
                const char *eol = strchr(buffer, '\n');
 
@@ -157,20 +161,19 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
                die("Missing author: %s",
                    sha1_to_hex(commit->object.sha1));
        if (log->user_format) {
-               struct strbuf buf = STRBUF_INIT;
                struct pretty_print_context ctx = {0};
                ctx.abbrev = DEFAULT_ABBREV;
                ctx.subject = "";
                ctx.after_subject = "";
                ctx.date_mode = DATE_NORMAL;
-               pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &buf, &ctx);
-               insert_one_record(log, author, buf.buf);
-               strbuf_release(&buf);
-               return;
-       }
-       if (*buffer)
+               pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &ufbuf, &ctx);
+               buffer = ufbuf.buf;
+       } else if (*buffer) {
                buffer++;
+       }
        insert_one_record(log, author, !*buffer ? "<none>" : buffer);
+       strbuf_release(&ufbuf);
+       strbuf_release(&buf);
 }
 
 static void get_from_rev(struct rev_info *rev, struct shortlog *log)
index c46550c9c01da8fe74145e16656a5163a7e4f07b..17ada88dfb9543ba782b84507b451fabfec28e10 100644 (file)
@@ -7,7 +7,7 @@
 #include "parse-options.h"
 
 static const char * const show_ref_usage[] = {
-       "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] ",
+       "git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] ",
        "git show-ref --exclude-existing[=pattern] < ref-list",
        NULL
 };
@@ -183,7 +183,10 @@ static const struct option show_ref_options[] = {
        OPT_BOOLEAN(0, "heads", &heads_only, "only show heads (can be combined with tags)"),
        OPT_BOOLEAN(0, "verify", &verify, "stricter reference checking, "
                    "requires exact ref path"),
-       OPT_BOOLEAN('h', "head", &show_head, "show the HEAD reference"),
+       { OPTION_BOOLEAN, 'h', NULL, &show_head, NULL,
+         "show the HEAD reference",
+         PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+       OPT_BOOLEAN(0, "head", &show_head, "show the HEAD reference"),
        OPT_BOOLEAN('d', "dereference", &deref_tags,
                    "dereference tags into object IDs"),
        { OPTION_CALLBACK, 's', "hash", &abbrev, "n",
@@ -201,6 +204,9 @@ static const struct option show_ref_options[] = {
 
 int cmd_show_ref(int argc, const char **argv, const char *prefix)
 {
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(show_ref_usage, show_ref_options);
+
        argc = parse_options(argc, argv, prefix, show_ref_options,
                             show_ref_usage, PARSE_OPT_NO_INTERNAL_HELP);
 
index 1fd2205d530ad510a18ff5a441ca7b5cb1cc34a6..4d3b93fedb5f2eca68edca15ca1e10cb60176d36 100644 (file)
@@ -73,9 +73,11 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
        struct strbuf buf = STRBUF_INIT;
        int strip_comments = 0;
 
-       if (argc > 1 && (!strcmp(argv[1], "-s") ||
+       if (argc == 2 && (!strcmp(argv[1], "-s") ||
                                !strcmp(argv[1], "--strip-comments")))
                strip_comments = 1;
+       else if (argc > 1)
+               usage("git stripspace [-s | --strip-comments] < <stream>");
 
        if (strbuf_read(&buf, 0, 1024) < 0)
                die_errno("could not read the input");
index 8b3a35e12da2cca8b632d4139bc2c5ba2409d322..3f1e7012db1af7d3695e6517956721a313229d65 100644 (file)
@@ -11,6 +11,9 @@ static const char tar_tree_usage[] =
 "git tar-tree [--remote=<repo>] <tree-ish> [basedir]\n"
 "*** Note that this command is now deprecated; use \"git archive\" instead.";
 
+static const char builtin_get_tar_commit_id_usage[] =
+"git get-tar-commit-id < <tarfile>";
+
 int cmd_tar_tree(int argc, const char **argv, const char *prefix)
 {
        /*
@@ -81,6 +84,9 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
        char *content = buffer + RECORDSIZE;
        ssize_t n;
 
+       if (argc != 1)
+               usage(builtin_get_tar_commit_id_usage);
+
        n = read_in_full(0, buffer, HEADERSIZE);
        if (n < HEADERSIZE)
                die("git get-tar-commit-id: read error");
index 92beaaf4b3ca5520a9af27bde7f15dda46d80197..a6b7f2d6361fc1e8ea9713082affc72362f08d76 100644 (file)
@@ -27,6 +27,7 @@ static int mark_valid_only;
 #define MARK_VALID 1
 #define UNMARK_VALID 2
 
+__attribute__((format (printf, 1, 2)))
 static void report(const char *fmt, ...)
 {
        va_list vp;
index c4cd1e1327f18ae27b74fac6dfd41938cd9dfe5d..73f788ef247febabbabe9bc0d21a890afa198cff 100644 (file)
@@ -67,6 +67,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix)
        return write_archive(sent_argc, sent_argv, prefix, 0);
 }
 
+__attribute__((format (printf, 1, 2)))
 static void error_clnt(const char *fmt, ...)
 {
        char buf[1024];
@@ -132,7 +133,6 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
 
        while (1) {
                struct pollfd pfd[2];
-               ssize_t processed[2] = { 0, 0 };
                int status;
 
                pfd[0].fd = fd1[0];
@@ -147,15 +147,14 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
                        }
                        continue;
                }
-               if (pfd[0].revents & POLLIN)
-                       /* Data stream ready */
-                       processed[0] = process_input(pfd[0].fd, 1);
                if (pfd[1].revents & POLLIN)
                        /* Status stream ready */
-                       processed[1] = process_input(pfd[1].fd, 2);
-               /* Always finish to read data when available */
-               if (processed[0] || processed[1])
-                       continue;
+                       if (process_input(pfd[1].fd, 2))
+                               continue;
+               if (pfd[0].revents & POLLIN)
+                       /* Data stream ready */
+                       if (process_input(pfd[0].fd, 1))
+                               continue;
 
                if (waitpid(writer, &status, 0) < 0)
                        error_clnt("%s", lostchild);
index a2174dc855ad89e486c57ae1c3a458cb1572edbb..c3f83c093f6d18dc21ff4112513abbc84fb7858c 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -48,7 +48,6 @@ extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_fast_export(int argc, const char **argv, const char *prefix);
 extern int cmd_fetch(int argc, const char **argv, const char *prefix);
 extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_fetch__tool(int argc, const char **argv, const char *prefix);
 extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
 extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
index df95e151e2e82b48e4f15f2180838bf2d178f923..ff97adcb891caf98dcc655e71e29eaec5dae0135 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -204,7 +204,6 @@ int create_bundle(struct bundle_header *header, const char *path,
        int i, ref_count = 0;
        char buffer[1024];
        struct rev_info revs;
-       int read_from_stdin = 0;
        struct child_process rls;
        FILE *rls_fout;
 
@@ -256,15 +255,8 @@ int create_bundle(struct bundle_header *header, const char *path,
        /* write references */
        argc = setup_revisions(argc, argv, &revs, NULL);
 
-       for (i = 1; i < argc; i++) {
-               if (!strcmp(argv[i], "--stdin")) {
-                       if (read_from_stdin++)
-                               die("--stdin given twice?");
-                       read_revisions_from_stdin(&revs);
-                       continue;
-               }
-               return error("unrecognized argument: %s'", argv[i]);
-       }
+       if (argc > 1)
+               return error("unrecognized argument: %s'", argv[1]);
 
        object_array_remove_duplicates(&revs.pending);
 
@@ -351,7 +343,7 @@ int create_bundle(struct bundle_header *header, const char *path,
 
        /* write pack */
        argv_pack[0] = "pack-objects";
-       argv_pack[1] = "--all-progress";
+       argv_pack[1] = "--all-progress-implied";
        argv_pack[2] = "--stdout";
        argv_pack[3] = "--thin";
        argv_pack[4] = NULL;
diff --git a/cache.h b/cache.h
index 71a731dbc988a8a22d0a2e1f8f3a5223f4c5d67a..bf468e52352c193b355222b718d9f5125c26052c 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -369,9 +369,12 @@ static inline enum object_type object_type(unsigned int mode)
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
 #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
+#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
+#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
+#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
 
 extern int is_bare_repository_cfg;
 extern int is_bare_repository(void);
@@ -568,6 +571,8 @@ enum object_creation_mode {
 
 extern enum object_creation_mode object_creation_mode;
 
+extern char *notes_ref_name;
+
 extern int grafts_replace_parents;
 
 #define GIT_REPO_VERSION 0
@@ -645,6 +650,7 @@ int set_shared_perm(const char *path, int mode);
 #define adjust_shared_perm(path) set_shared_perm((path), 0)
 int safe_create_leading_directories(char *path);
 int safe_create_leading_directories_const(const char *path);
+extern char *expand_user_path(const char *path);
 char *enter_repo(char *path, int strict);
 static inline int is_absolute_path(const char *path)
 {
@@ -657,6 +663,7 @@ const char *make_relative_path(const char *abs, const char *base);
 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);
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
@@ -751,6 +758,8 @@ extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
 extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 extern const char *fmt_name(const char *name, const char *email);
+extern const char *git_editor(void);
+extern const char *git_pager(void);
 
 struct checkout {
        const char *base_dir;
@@ -857,7 +866,6 @@ extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
 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);
-extern int get_ack(int fd, unsigned char *result_sha1);
 struct extra_have_objects {
        int nr, alloc;
        unsigned char (*array)[20];
@@ -903,6 +911,7 @@ extern unsigned long git_config_ulong(const char *, const char *);
 extern int git_config_bool_or_int(const char *, const char *, int *);
 extern int git_config_bool(const char *, const char *);
 extern int git_config_string(const char **, const char *, const char *);
+extern int git_config_pathname(const char **, const char *, const char *);
 extern int git_config_set(const char *, const char *);
 extern int git_config_set_multivar(const char *, const char *, const char *, int);
 extern int git_config_rename_section(const char *, const char *);
@@ -961,7 +970,9 @@ extern void *alloc_object_node(void);
 extern void alloc_report(void);
 
 /* trace.c */
+__attribute__((format (printf, 1, 2)))
 extern void trace_printf(const char *format, ...);
+__attribute__((format (printf, 2, 3)))
 extern void trace_argv_printf(const char **argv, const char *format, ...);
 
 /* convert.c */
diff --git a/color.h b/color.h
index 18abeb7c7dd47794d4f5fea4049b5719f03383fe..3cb4b7fc890880b0fcf19a11c6bc7de6b10d6e8d 100644 (file)
--- a/color.h
+++ b/color.h
@@ -4,6 +4,11 @@
 /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
 #define COLOR_MAXLEN 24
 
+/*
+ * IMPORTANT: Due to the way these color codes are emulated on Windows,
+ * write them only using printf(), fprintf(), and fputs(). In particular,
+ * do not use puts() or write().
+ */
 #define GIT_COLOR_NORMAL       ""
 #define GIT_COLOR_RESET                "\033[m"
 #define GIT_COLOR_BOLD         "\033[1m"
@@ -29,7 +34,9 @@ int git_color_default_config(const char *var, const char *value, void *cb);
 int git_config_colorbool(const char *var, const char *value, int stdout_is_tty);
 void color_parse(const char *value, const char *var, char *dst);
 void color_parse_mem(const char *value, int len, const char *var, char *dst);
+__attribute__((format (printf, 3, 4)))
 int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
+__attribute__((format (printf, 3, 4)))
 int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
 int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
 
index 5b63af1eeb71907f23117bc552fa7a4019f09722..61626912e3bca2571b41fd1256067470dc170cc1 100644 (file)
@@ -524,6 +524,7 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
        int i;
        unsigned long lno = 0;
        const char *c_frag = diff_get_color(use_color, DIFF_FRAGINFO);
+       const char *c_func = diff_get_color(use_color, DIFF_FUNCINFO);
        const char *c_new = diff_get_color(use_color, DIFF_FILE_NEW);
        const char *c_old = diff_get_color(use_color, DIFF_FILE_OLD);
        const char *c_plain = diff_get_color(use_color, DIFF_PLAIN);
@@ -588,7 +589,9 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
                                    comment_end = i;
                        }
                        if (comment_end)
-                               putchar(' ');
+                               printf("%s%s %s%s", c_reset,
+                                                   c_plain, c_reset,
+                                                   c_func);
                        for (i = 0; i < comment_end; i++)
                                putchar(hunk_comment[i]);
                }
index 59b0adc39b7c2d30103fe5d30b416b6d4ca64c31..cc5d48b3851cda643b3790ff326c04fdf1333d1c 100644 (file)
@@ -74,6 +74,7 @@ git-mktag                               plumbingmanipulators
 git-mktree                              plumbingmanipulators
 git-mv                                  mainporcelain common
 git-name-rev                            plumbinginterrogators
+git-notes                               mainporcelain
 git-pack-objects                        plumbingmanipulators
 git-pack-redundant                      plumbinginterrogators
 git-pack-refs                           ancillarymanipulators
index 6393e1b36218e5f8970b0c43092b07f5d94b0bce..632061c2c3669991b6526a5b6b452e3b54ee718c 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -5,6 +5,7 @@
 #include "utf8.h"
 #include "diff.h"
 #include "revision.h"
+#include "notes.h"
 
 int save_commit_buffer = 1;
 
@@ -199,7 +200,7 @@ struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
        return commit_graft[pos];
 }
 
-int write_shallow_commits(int fd, int use_pack_protocol)
+int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
 {
        int i, count = 0;
        for (i = 0; i < commit_graft_nr; i++)
@@ -208,12 +209,10 @@ int write_shallow_commits(int fd, int use_pack_protocol)
                                sha1_to_hex(commit_graft[i]->sha1);
                        count++;
                        if (use_pack_protocol)
-                               packet_write(fd, "shallow %s", hex);
+                               packet_buf_write(out, "shallow %s", hex);
                        else {
-                               if (write_in_full(fd, hex,  40) != 40)
-                                       break;
-                               if (write_str_in_full(fd, "\n") != 1)
-                                       break;
+                               strbuf_addstr(out, hex);
+                               strbuf_addch(out, '\n');
                        }
                }
        return count;
index 422f778f3f5b997ea1a3d8569cf6fa573481fff1..e5332efcfc9449e0f3af4d6c49be63adfeb138b1 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -139,7 +139,7 @@ extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
 
 extern int register_shallow(const unsigned char *sha1);
 extern int unregister_shallow(const unsigned char *sha1);
-extern int write_shallow_commits(int fd, int use_pack_protocol);
+extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol);
 extern int is_repository_shallow(void);
 extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
index 279e0b48b15e091fc0ca975c3efbd461ab02c149..f3b8c44181776a99c3eb79e15542104d67001c9d 100644 (file)
@@ -24,7 +24,7 @@ static inline uint32_t default_swab32(uint32_t val)
        if (__builtin_constant_p(x)) { \
                __res = default_swab32(x); \
        } else { \
-               __asm__("bswap %0" : "=r" (__res) : "0" (x)); \
+               __asm__("bswap %0" : "=r" (__res) : "0" ((uint32_t)(x))); \
        } \
        __res; })
 
index 6b5b5b2c7011a89c0415ebb89406c4a3ac53e9a9..0d73f15fa894cb0ded20f0cdd1f7fec3da1643f1 100644 (file)
@@ -903,19 +903,195 @@ char **make_augmented_environ(const char *const *vars)
        return env;
 }
 
-/* this is the first function to call into WS_32; initialize it */
-#undef gethostbyname
-struct hostent *mingw_gethostbyname(const char *host)
+/*
+ * Note, this isn't a complete replacement for getaddrinfo. It assumes
+ * that service contains a numerical port, or that it it is null. It
+ * does a simple search using gethostbyname, and returns one IPv4 host
+ * if one was found.
+ */
+static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
+                                  const struct addrinfo *hints,
+                                  struct addrinfo **res)
+{
+       struct hostent *h = gethostbyname(node);
+       struct addrinfo *ai;
+       struct sockaddr_in *sin;
+
+       if (!h)
+               return WSAGetLastError();
+
+       ai = xmalloc(sizeof(struct addrinfo));
+       *res = ai;
+       ai->ai_flags = 0;
+       ai->ai_family = AF_INET;
+       ai->ai_socktype = hints->ai_socktype;
+       switch (hints->ai_socktype) {
+       case SOCK_STREAM:
+               ai->ai_protocol = IPPROTO_TCP;
+               break;
+       case SOCK_DGRAM:
+               ai->ai_protocol = IPPROTO_UDP;
+               break;
+       default:
+               ai->ai_protocol = 0;
+               break;
+       }
+       ai->ai_addrlen = sizeof(struct sockaddr_in);
+       ai->ai_canonname = strdup(h->h_name);
+
+       sin = xmalloc(ai->ai_addrlen);
+       memset(sin, 0, ai->ai_addrlen);
+       sin->sin_family = AF_INET;
+       if (service)
+               sin->sin_port = htons(atoi(service));
+       sin->sin_addr = *(struct in_addr *)h->h_addr;
+       ai->ai_addr = (struct sockaddr *)sin;
+       ai->ai_next = 0;
+       return 0;
+}
+
+static void WSAAPI freeaddrinfo_stub(struct addrinfo *res)
+{
+       free(res->ai_canonname);
+       free(res->ai_addr);
+       free(res);
+}
+
+static int WSAAPI getnameinfo_stub(const struct sockaddr *sa, socklen_t salen,
+                                  char *host, DWORD hostlen,
+                                  char *serv, DWORD servlen, int flags)
+{
+       const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
+       if (sa->sa_family != AF_INET)
+               return EAI_FAMILY;
+       if (!host && !serv)
+               return EAI_NONAME;
+
+       if (host && hostlen > 0) {
+               struct hostent *ent = NULL;
+               if (!(flags & NI_NUMERICHOST))
+                       ent = gethostbyaddr((const char *)&sin->sin_addr,
+                                           sizeof(sin->sin_addr), AF_INET);
+
+               if (ent)
+                       snprintf(host, hostlen, "%s", ent->h_name);
+               else if (flags & NI_NAMEREQD)
+                       return EAI_NONAME;
+               else
+                       snprintf(host, hostlen, "%s", inet_ntoa(sin->sin_addr));
+       }
+
+       if (serv && servlen > 0) {
+               struct servent *ent = NULL;
+               if (!(flags & NI_NUMERICSERV))
+                       ent = getservbyport(sin->sin_port,
+                                           flags & NI_DGRAM ? "udp" : "tcp");
+
+               if (ent)
+                       snprintf(serv, servlen, "%s", ent->s_name);
+               else
+                       snprintf(serv, servlen, "%d", ntohs(sin->sin_port));
+       }
+
+       return 0;
+}
+
+static HMODULE ipv6_dll = NULL;
+static void (WSAAPI *ipv6_freeaddrinfo)(struct addrinfo *res);
+static int (WSAAPI *ipv6_getaddrinfo)(const char *node, const char *service,
+                                     const struct addrinfo *hints,
+                                     struct addrinfo **res);
+static int (WSAAPI *ipv6_getnameinfo)(const struct sockaddr *sa, socklen_t salen,
+                                     char *host, DWORD hostlen,
+                                     char *serv, DWORD servlen, int flags);
+/*
+ * gai_strerror is an inline function in the ws2tcpip.h header, so we
+ * don't need to try to load that one dynamically.
+ */
+
+static void socket_cleanup(void)
+{
+       WSACleanup();
+       if (ipv6_dll)
+               FreeLibrary(ipv6_dll);
+       ipv6_dll = NULL;
+       ipv6_freeaddrinfo = freeaddrinfo_stub;
+       ipv6_getaddrinfo = getaddrinfo_stub;
+       ipv6_getnameinfo = getnameinfo_stub;
+}
+
+static void ensure_socket_initialization(void)
 {
        WSADATA wsa;
+       static int initialized = 0;
+       const char *libraries[] = { "ws2_32.dll", "wship6.dll", NULL };
+       const char **name;
+
+       if (initialized)
+               return;
 
        if (WSAStartup(MAKEWORD(2,2), &wsa))
                die("unable to initialize winsock subsystem, error %d",
                        WSAGetLastError());
-       atexit((void(*)(void)) WSACleanup);
+
+       for (name = libraries; *name; name++) {
+               ipv6_dll = LoadLibrary(*name);
+               if (!ipv6_dll)
+                       continue;
+
+               ipv6_freeaddrinfo = (void (WSAAPI *)(struct addrinfo *))
+                       GetProcAddress(ipv6_dll, "freeaddrinfo");
+               ipv6_getaddrinfo = (int (WSAAPI *)(const char *, const char *,
+                                                  const struct addrinfo *,
+                                                  struct addrinfo **))
+                       GetProcAddress(ipv6_dll, "getaddrinfo");
+               ipv6_getnameinfo = (int (WSAAPI *)(const struct sockaddr *,
+                                                  socklen_t, char *, DWORD,
+                                                  char *, DWORD, int))
+                       GetProcAddress(ipv6_dll, "getnameinfo");
+               if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) {
+                       FreeLibrary(ipv6_dll);
+                       ipv6_dll = NULL;
+               } else
+                       break;
+       }
+       if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) {
+               ipv6_freeaddrinfo = freeaddrinfo_stub;
+               ipv6_getaddrinfo = getaddrinfo_stub;
+               ipv6_getnameinfo = getnameinfo_stub;
+       }
+
+       atexit(socket_cleanup);
+       initialized = 1;
+}
+
+#undef gethostbyname
+struct hostent *mingw_gethostbyname(const char *host)
+{
+       ensure_socket_initialization();
        return gethostbyname(host);
 }
 
+void mingw_freeaddrinfo(struct addrinfo *res)
+{
+       ipv6_freeaddrinfo(res);
+}
+
+int mingw_getaddrinfo(const char *node, const char *service,
+                     const struct addrinfo *hints, struct addrinfo **res)
+{
+       ensure_socket_initialization();
+       return ipv6_getaddrinfo(node, service, hints, res);
+}
+
+int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+                     char *host, DWORD hostlen, char *serv, DWORD servlen,
+                     int flags)
+{
+       ensure_socket_initialization();
+       return ipv6_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
+}
+
 int mingw_socket(int domain, int type, int protocol)
 {
        int sockfd;
@@ -1000,6 +1176,18 @@ int mingw_rename(const char *pold, const char *pnew)
        return -1;
 }
 
+/*
+ * Note that this doesn't return the actual pagesize, but
+ * the allocation granularity. If future Windows specific git code
+ * needs the real getpagesize function, we need to find another solution.
+ */
+int mingw_getpagesize(void)
+{
+       SYSTEM_INFO si;
+       GetSystemInfo(&si);
+       return si.dwAllocationGranularity;
+}
+
 struct passwd *getpwuid(int uid)
 {
        static char user_name[100];
index 5b5258bcebbbead7e4ed15a4dea7bf755739b98e..b3d299f5bc16810867b7768725b8d33ee999f6b5 100644 (file)
@@ -1,4 +1,5 @@
 #include <winsock2.h>
+#include <ws2tcpip.h>
 
 /*
  * things that are not available in header files
@@ -124,6 +125,27 @@ static inline int waitpid(pid_t pid, int *status, unsigned options)
        return -1;
 }
 
+#ifndef NO_OPENSSL
+#include <openssl/ssl.h>
+static inline int mingw_SSL_set_fd(SSL *ssl, int fd)
+{
+       return SSL_set_fd(ssl, _get_osfhandle(fd));
+}
+#define SSL_set_fd mingw_SSL_set_fd
+
+static inline int mingw_SSL_set_rfd(SSL *ssl, int fd)
+{
+       return SSL_set_rfd(ssl, _get_osfhandle(fd));
+}
+#define SSL_set_rfd mingw_SSL_set_rfd
+
+static inline int mingw_SSL_set_wfd(SSL *ssl, int fd)
+{
+       return SSL_set_wfd(ssl, _get_osfhandle(fd));
+}
+#define SSL_set_wfd mingw_SSL_set_wfd
+#endif
+
 /*
  * implementations of missing functions
  */
@@ -157,6 +179,18 @@ char *mingw_getenv(const char *name);
 struct hostent *mingw_gethostbyname(const char *host);
 #define gethostbyname mingw_gethostbyname
 
+void mingw_freeaddrinfo(struct addrinfo *res);
+#define freeaddrinfo mingw_freeaddrinfo
+
+int mingw_getaddrinfo(const char *node, const char *service,
+                     const struct addrinfo *hints, struct addrinfo **res);
+#define getaddrinfo mingw_getaddrinfo
+
+int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+                     char *host, DWORD hostlen, char *serv, DWORD servlen,
+                     int flags);
+#define getnameinfo mingw_getnameinfo
+
 int mingw_socket(int domain, int type, int protocol);
 #define socket mingw_socket
 
@@ -166,7 +200,7 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
 int mingw_rename(const char*, const char*);
 #define rename mingw_rename
 
-#ifdef USE_WIN32_MMAP
+#if defined(USE_WIN32_MMAP) || defined(_MSC_VER)
 int mingw_getpagesize(void);
 #define getpagesize mingw_getpagesize
 #endif
index f9528c0ea10db41f948e6d24b84125d2d54f6dda..8a2112f22f46b3066d75bd5472dfe05bf88836c8 100644 (file)
@@ -29,6 +29,9 @@
                push(@args, "zlib.lib");
        } elsif ("$arg" eq "-liconv") {
                push(@args, "iconv.lib");
+       } elsif ("$arg" eq "-lcrypto") {
+               push(@args, "libeay32.lib");
+               push(@args, "ssleay32.lib");
        } elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") {
                $arg =~ s/^-L/-LIBPATH:/;
                push(@args, $arg);
index 779d796cd5f017d4effbede360185a4690cf414e..1c5a14922f255af2c3b0e75e06925b748d3d7684 100644 (file)
@@ -1,17 +1,5 @@
 #include "../git-compat-util.h"
 
-/*
- * Note that this doesn't return the actual pagesize, but
- * the allocation granularity. If future Windows specific git code
- * needs the real getpagesize function, we need to find another solution.
- */
-int mingw_getpagesize(void)
-{
-       SYSTEM_INFO si;
-       GetSystemInfo(&si);
-       return si.dwAllocationGranularity;
-}
-
 void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
 {
        HANDLE hmap;
index c64406113686e8886c0719206ce57d73fee13b29..37385ce9d338ecbd2556cef9455000784ebab7e5 100644 (file)
--- a/config.c
+++ b/config.c
@@ -351,6 +351,16 @@ int git_config_string(const char **dest, const char *var, const char *value)
        return 0;
 }
 
+int git_config_pathname(const char **dest, const char *var, const char *value)
+{
+       if (!value)
+               return config_error_nonbool(var);
+       *dest = expand_user_path(value);
+       if (!*dest)
+               die("Failed to expand user dir in: '%s'", value);
+       return 0;
+}
+
 static int git_default_core_config(const char *var, const char *value)
 {
        /* This needs a better name */
@@ -467,6 +477,11 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.notesref")) {
+               notes_ref_name = xstrdup(value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.pager"))
                return git_config_string(&pager_program, var, value);
 
@@ -474,7 +489,7 @@ static int git_default_core_config(const char *var, const char *value)
                return git_config_string(&editor_program, var, value);
 
        if (!strcmp(var, "core.excludesfile"))
-               return git_config_string(&excludes_file, var, value);
+               return git_config_pathname(&excludes_file, var, value);
 
        if (!strcmp(var, "core.whitespace")) {
                if (!value)
index b09b8e446fbd52c55e6966c611b55ac1baa81f32..4625b8672bf5b0dcc977737c2c5086c5c1aa54a6 100644 (file)
@@ -68,6 +68,26 @@ else \
        GIT_CONF_APPEND_LINE(${PACKAGE}DIR=$withval); \
 fi \
 ])# GIT_PARSE_WITH
+#
+# GIT_PARSE_WITH_SET_MAKE_VAR(WITHNAME, VAR, HELP_TEXT)
+# ---------------------
+# Set VAR to the value specied by --with-WITHNAME.
+# No verification of arguments is performed, but warnings are issued
+# if either 'yes' or 'no' is specified.
+# HELP_TEXT is presented when --help is called.
+# This is a direct way to allow setting variables in the Makefile.
+AC_DEFUN([GIT_PARSE_WITH_SET_MAKE_VAR],
+[AC_ARG_WITH([$1],
+ [AS_HELP_STRING([--with-$1=VALUE], $3)],
+ if test -n "$withval"; then \
+  if test "$withval" = "yes" -o "$withval" = "no"; then \
+    AC_MSG_WARN([You likely do not want either 'yes' or 'no' as]
+                    [a value for $1 ($2).  Maybe you do...?]); \
+  fi; \
+  \
+  AC_MSG_NOTICE([Setting $2 to $withval]); \
+  GIT_CONF_APPEND_LINE($2=$withval); \
+ fi)])# GIT_PARSE_WITH_SET_MAKE_VAR
 
 dnl
 dnl GIT_CHECK_FUNC(FUNCTION, IFTRUE, IFFALSE)
@@ -226,6 +246,29 @@ GIT_PARSE_WITH(iconv))
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-index perspective.
 
+#
+# Allow user to set ETC_GITCONFIG variable
+GIT_PARSE_WITH_SET_MAKE_VAR(gitconfig, ETC_GITCONFIG,
+                       Use VALUE instead of /etc/gitconfig as the
+                       global git configuration file.
+                       If VALUE is not fully qualified it will be interpretted
+                       as a path relative to the computed prefix at runtime.)
+
+#
+# Allow user to set the default pager
+GIT_PARSE_WITH_SET_MAKE_VAR(pager, DEFAULT_PAGER,
+                       Use VALUE as the fall-back pager instead of 'less'.
+                       This is used by things like 'git log' when the user
+                       does not specify a pager to use through alternate
+                       methods. eg: /usr/bin/pager)
+#
+# Allow user to set the default editor
+GIT_PARSE_WITH_SET_MAKE_VAR(editor, DEFAULT_EDITOR,
+                       Use VALUE as the fall-back editor instead of 'vi'.
+                       This is used by things like 'git commit' when the user
+                       does not specify a preferred editor through other
+                       methods. eg: /usr/bin/editor)
+
 #
 # Define SHELL_PATH to provide path to shell.
 GIT_ARG_SET_PATH(shell)
index 7945e38ac1c99c09f7975ae99767bc9747ac3740..db965c9982f1f2ae7e08331d3fa1d40e194a6520 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -107,27 +107,6 @@ int server_supports(const char *feature)
                strstr(server_capabilities, feature) != NULL;
 }
 
-int get_ack(int fd, unsigned char *result_sha1)
-{
-       static char line[1000];
-       int len = packet_read_line(fd, line, sizeof(line));
-
-       if (!len)
-               die("git fetch-pack: expected ACK/NAK, got EOF");
-       if (line[len-1] == '\n')
-               line[--len] = 0;
-       if (!strcmp(line, "NAK"))
-               return 0;
-       if (!prefixcmp(line, "ACK ")) {
-               if (!get_sha1_hex(line+4, result_sha1)) {
-                       if (strstr(line+45, "continue"))
-                               return 2;
-                       return 1;
-               }
-       }
-       die("git fetch_pack: expected ACK/NAK, got '%s'", line);
-}
-
 int path_match(const char *path, int nr, char **match)
 {
        int i;
@@ -630,6 +609,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                        GIT_WORK_TREE_ENVIRONMENT,
                        GRAFT_ENVIRONMENT,
                        INDEX_ENVIRONMENT,
+                       NO_REPLACE_OBJECTS_ENVIRONMENT,
                        NULL
                };
                conn->env = env;
index 20bd061b3e5a909fcd8dec732915c499cdac1a7b..d506717bfd304a1aaf4a02ad262ee96a0e468771 100644 (file)
@@ -315,6 +315,9 @@ sub handleLinkLine
             $appout = shift @parts;
         } elsif ("$part" eq "-lz") {
             push(@libs, "zlib.lib");
+       } elsif ("$part" eq "-lcrypto") {
+            push(@libs, "libeay32.lib");
+            push(@libs, "ssleay32.lib");
         } elsif ($part =~ /^-/) {
             push(@lflags, $part);
         } elsif ($part =~ /\.(a|lib)$/) {
index e3ddecc995eb30391639983d9f6f0db7567d7b9a..11bf17a86cf472a8939ad965d2552660061fd240 100755 (executable)
 #    2) Added the following line to your .bashrc:
 #        source ~/.git-completion.sh
 #
-#    3) You may want to make sure the git executable is available
-#       in your PATH before this script is sourced, as some caching
-#       is performed while the script loads.  If git isn't found
-#       at source time then all lookups will be done on demand,
-#       which may be slightly slower.
-#
-#    4) Consider changing your PS1 to also show the current branch:
+#    3) Consider changing your PS1 to also show the current branch:
 #        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
 #
 #       The argument to __git_ps1 will be displayed only if you
@@ -324,12 +318,8 @@ __git_remotes ()
        done
 }
 
-__git_merge_strategies ()
+__git_list_merge_strategies ()
 {
-       if [ -n "${__git_merge_strategylist-}" ]; then
-               echo "$__git_merge_strategylist"
-               return
-       fi
        git merge -s help 2>&1 |
        sed -n -e '/[Aa]vailable strategies are: /,/^$/{
                s/\.$//
@@ -339,8 +329,17 @@ __git_merge_strategies ()
                p
        }'
 }
-__git_merge_strategylist=
-__git_merge_strategylist=$(__git_merge_strategies 2>/dev/null)
+
+__git_merge_strategies=
+# 'git merge -s help' (and thus detection of the merge strategy
+# list) fails, unfortunately, if run outside of any git working
+# tree.  __git_merge_strategies is set to the empty string in
+# that case, and the detection will be repeated the next time it
+# is needed.
+__git_compute_merge_strategies ()
+{
+       : ${__git_merge_strategies:=$(__git_list_merge_strategies)}
+}
 
 __git_complete_file ()
 {
@@ -474,27 +473,24 @@ __git_complete_remote_or_refspec ()
 
 __git_complete_strategy ()
 {
+       __git_compute_merge_strategies
        case "${COMP_WORDS[COMP_CWORD-1]}" in
        -s|--strategy)
-               __gitcomp "$(__git_merge_strategies)"
+               __gitcomp "$__git_merge_strategies"
                return 0
        esac
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --strategy=*)
-               __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
+               __gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}"
                return 0
                ;;
        esac
        return 1
 }
 
-__git_all_commands ()
+__git_list_all_commands ()
 {
-       if [ -n "${__git_all_commandlist-}" ]; then
-               echo "$__git_all_commandlist"
-               return
-       fi
        local i IFS=" "$'\n'
        for i in $(git help -a|egrep '^  [a-zA-Z0-9]')
        do
@@ -504,17 +500,18 @@ __git_all_commands ()
                esac
        done
 }
-__git_all_commandlist=
-__git_all_commandlist="$(__git_all_commands 2>/dev/null)"
 
-__git_porcelain_commands ()
+__git_all_commands=
+__git_compute_all_commands ()
+{
+       : ${__git_all_commands:=$(__git_list_all_commands)}
+}
+
+__git_list_porcelain_commands ()
 {
-       if [ -n "${__git_porcelain_commandlist-}" ]; then
-               echo "$__git_porcelain_commandlist"
-               return
-       fi
        local i IFS=" "$'\n'
-       for i in "help" $(__git_all_commands)
+       __git_compute_all_commands
+       for i in "help" $__git_all_commands
        do
                case $i in
                *--*)             : helper pattern;;
@@ -595,8 +592,13 @@ __git_porcelain_commands ()
                esac
        done
 }
-__git_porcelain_commandlist=
-__git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)"
+
+__git_porcelain_commands=
+__git_compute_porcelain_commands ()
+{
+       __git_compute_all_commands
+       : ${__git_porcelain_commands:=$(__git_list_porcelain_commands)}
+}
 
 __git_aliases ()
 {
@@ -953,7 +955,7 @@ _git_diff ()
 }
 
 __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
-                       tkdiff vimdiff gvimdiff xxdiff araxis
+                       tkdiff vimdiff gvimdiff xxdiff araxis p4merge
 "
 
 _git_difftool ()
@@ -1088,7 +1090,8 @@ _git_help ()
                return
                ;;
        esac
-       __gitcomp "$(__git_all_commands)
+       __git_compute_all_commands
+       __gitcomp "$__git_all_commands
                attributes cli core-tutorial cvs-migration
                diffcore gitk glossary hooks ignore modules
                repository-layout tutorial tutorial-2
@@ -1224,7 +1227,7 @@ _git_log ()
 
 __git_merge_options="
        --no-commit --no-stat --log --no-log --squash --strategy
-       --commit --stat --no-squash --ff --no-ff
+       --commit --stat --no-squash --ff --no-ff --ff-only
 "
 
 _git_merge ()
@@ -1444,7 +1447,8 @@ _git_config ()
                return
                ;;
        pull.twohead|pull.octopus)
-               __gitcomp "$(__git_merge_strategies)"
+               __git_compute_merge_strategies
+               __gitcomp "$__git_merge_strategies"
                return
                ;;
        color.branch|color.diff|color.interactive|\
@@ -1545,7 +1549,8 @@ _git_config ()
        pager.*)
                local pfx="${cur%.*}."
                cur="${cur#*.}"
-               __gitcomp "$(__git_all_commands)" "$pfx" "$cur"
+               __git_compute_all_commands
+               __gitcomp "$__git_all_commands" "$pfx" "$cur"
                return
                ;;
        remote.*.*)
@@ -2142,7 +2147,8 @@ _git ()
                        --help
                        "
                        ;;
-               *)     __gitcomp "$(__git_porcelain_commands) $(__git_aliases)" ;;
+               *)     __git_compute_porcelain_commands
+                      __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;;
                esac
                return
        fi
diff --git a/contrib/examples/builtin-fetch--tool.c b/contrib/examples/builtin-fetch--tool.c
new file mode 100644 (file)
index 0000000..cd10dbc
--- /dev/null
@@ -0,0 +1,574 @@
+#include "builtin.h"
+#include "cache.h"
+#include "refs.h"
+#include "commit.h"
+#include "sigchain.h"
+
+static char *get_stdin(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+       if (strbuf_read(&buf, 0, 1024) < 0) {
+               die_errno("error reading standard input");
+       }
+       return strbuf_detach(&buf, NULL);
+}
+
+static void show_new(enum object_type type, unsigned char *sha1_new)
+{
+       fprintf(stderr, "  %s: %s\n", typename(type),
+               find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
+}
+
+static int update_ref_env(const char *action,
+                     const char *refname,
+                     unsigned char *sha1,
+                     unsigned char *oldval)
+{
+       char msg[1024];
+       const char *rla = getenv("GIT_REFLOG_ACTION");
+
+       if (!rla)
+               rla = "(reflog update)";
+       if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
+               warning("reflog message too long: %.*s...", 50, msg);
+       return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
+}
+
+static int update_local_ref(const char *name,
+                           const char *new_head,
+                           const char *note,
+                           int verbose, int force)
+{
+       unsigned char sha1_old[20], sha1_new[20];
+       char oldh[41], newh[41];
+       struct commit *current, *updated;
+       enum object_type type;
+
+       if (get_sha1_hex(new_head, sha1_new))
+               die("malformed object name %s", new_head);
+
+       type = sha1_object_info(sha1_new, NULL);
+       if (type < 0)
+               die("object %s not found", new_head);
+
+       if (!*name) {
+               /* Not storing */
+               if (verbose) {
+                       fprintf(stderr, "* fetched %s\n", note);
+                       show_new(type, sha1_new);
+               }
+               return 0;
+       }
+
+       if (get_sha1(name, sha1_old)) {
+               const char *msg;
+       just_store:
+               /* new ref */
+               if (!strncmp(name, "refs/tags/", 10))
+                       msg = "storing tag";
+               else
+                       msg = "storing head";
+               fprintf(stderr, "* %s: storing %s\n",
+                       name, note);
+               show_new(type, sha1_new);
+               return update_ref_env(msg, name, sha1_new, NULL);
+       }
+
+       if (!hashcmp(sha1_old, sha1_new)) {
+               if (verbose) {
+                       fprintf(stderr, "* %s: same as %s\n", name, note);
+                       show_new(type, sha1_new);
+               }
+               return 0;
+       }
+
+       if (!strncmp(name, "refs/tags/", 10)) {
+               fprintf(stderr, "* %s: updating with %s\n", name, note);
+               show_new(type, sha1_new);
+               return update_ref_env("updating tag", name, sha1_new, NULL);
+       }
+
+       current = lookup_commit_reference(sha1_old);
+       updated = lookup_commit_reference(sha1_new);
+       if (!current || !updated)
+               goto just_store;
+
+       strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
+       strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
+
+       if (in_merge_bases(current, &updated, 1)) {
+               fprintf(stderr, "* %s: fast-forward to %s\n",
+                       name, note);
+               fprintf(stderr, "  old..new: %s..%s\n", oldh, newh);
+               return update_ref_env("fast-forward", name, sha1_new, sha1_old);
+       }
+       if (!force) {
+               fprintf(stderr,
+                       "* %s: not updating to non-fast-forward %s\n",
+                       name, note);
+               fprintf(stderr,
+                       "  old...new: %s...%s\n", oldh, newh);
+               return 1;
+       }
+       fprintf(stderr,
+               "* %s: forcing update to non-fast-forward %s\n",
+               name, note);
+       fprintf(stderr, "  old...new: %s...%s\n", oldh, newh);
+       return update_ref_env("forced-update", name, sha1_new, sha1_old);
+}
+
+static int append_fetch_head(FILE *fp,
+                            const char *head, const char *remote,
+                            const char *remote_name, const char *remote_nick,
+                            const char *local_name, int not_for_merge,
+                            int verbose, int force)
+{
+       struct commit *commit;
+       int remote_len, i, note_len;
+       unsigned char sha1[20];
+       char note[1024];
+       const char *what, *kind;
+
+       if (get_sha1(head, sha1))
+               return error("Not a valid object name: %s", head);
+       commit = lookup_commit_reference_gently(sha1, 1);
+       if (!commit)
+               not_for_merge = 1;
+
+       if (!strcmp(remote_name, "HEAD")) {
+               kind = "";
+               what = "";
+       }
+       else if (!strncmp(remote_name, "refs/heads/", 11)) {
+               kind = "branch";
+               what = remote_name + 11;
+       }
+       else if (!strncmp(remote_name, "refs/tags/", 10)) {
+               kind = "tag";
+               what = remote_name + 10;
+       }
+       else if (!strncmp(remote_name, "refs/remotes/", 13)) {
+               kind = "remote branch";
+               what = remote_name + 13;
+       }
+       else {
+               kind = "";
+               what = remote_name;
+       }
+
+       remote_len = strlen(remote);
+       for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)
+               ;
+       remote_len = i + 1;
+       if (4 < i && !strncmp(".git", remote + i - 3, 4))
+               remote_len = i - 3;
+
+       note_len = 0;
+       if (*what) {
+               if (*kind)
+                       note_len += sprintf(note + note_len, "%s ", kind);
+               note_len += sprintf(note + note_len, "'%s' of ", what);
+       }
+       note_len += sprintf(note + note_len, "%.*s", remote_len, remote);
+       fprintf(fp, "%s\t%s\t%s\n",
+               sha1_to_hex(commit ? commit->object.sha1 : sha1),
+               not_for_merge ? "not-for-merge" : "",
+               note);
+       return update_local_ref(local_name, head, note, verbose, force);
+}
+
+static char *keep;
+static void remove_keep(void)
+{
+       if (keep && *keep)
+               unlink(keep);
+}
+
+static void remove_keep_on_signal(int signo)
+{
+       remove_keep();
+       sigchain_pop(signo);
+       raise(signo);
+}
+
+static char *find_local_name(const char *remote_name, const char *refs,
+                            int *force_p, int *not_for_merge_p)
+{
+       const char *ref = refs;
+       int len = strlen(remote_name);
+
+       while (ref) {
+               const char *next;
+               int single_force, not_for_merge;
+
+               while (*ref == '\n')
+                       ref++;
+               if (!*ref)
+                       break;
+               next = strchr(ref, '\n');
+
+               single_force = not_for_merge = 0;
+               if (*ref == '+') {
+                       single_force = 1;
+                       ref++;
+               }
+               if (*ref == '.') {
+                       not_for_merge = 1;
+                       ref++;
+                       if (*ref == '+') {
+                               single_force = 1;
+                               ref++;
+                       }
+               }
+               if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
+                       const char *local_part = ref + len + 1;
+                       int retlen;
+
+                       if (!next)
+                               retlen = strlen(local_part);
+                       else
+                               retlen = next - local_part;
+                       *force_p = single_force;
+                       *not_for_merge_p = not_for_merge;
+                       return xmemdupz(local_part, retlen);
+               }
+               ref = next;
+       }
+       return NULL;
+}
+
+static int fetch_native_store(FILE *fp,
+                             const char *remote,
+                             const char *remote_nick,
+                             const char *refs,
+                             int verbose, int force)
+{
+       char buffer[1024];
+       int err = 0;
+
+       sigchain_push_common(remove_keep_on_signal);
+       atexit(remove_keep);
+
+       while (fgets(buffer, sizeof(buffer), stdin)) {
+               int len;
+               char *cp;
+               char *local_name;
+               int single_force, not_for_merge;
+
+               for (cp = buffer; *cp && !isspace(*cp); cp++)
+                       ;
+               if (*cp)
+                       *cp++ = 0;
+               len = strlen(cp);
+               if (len && cp[len-1] == '\n')
+                       cp[--len] = 0;
+               if (!strcmp(buffer, "failed"))
+                       die("Fetch failure: %s", remote);
+               if (!strcmp(buffer, "pack"))
+                       continue;
+               if (!strcmp(buffer, "keep")) {
+                       char *od = get_object_directory();
+                       int len = strlen(od) + strlen(cp) + 50;
+                       keep = xmalloc(len);
+                       sprintf(keep, "%s/pack/pack-%s.keep", od, cp);
+                       continue;
+               }
+
+               local_name = find_local_name(cp, refs,
+                                            &single_force, &not_for_merge);
+               if (!local_name)
+                       continue;
+               err |= append_fetch_head(fp,
+                                        buffer, remote, cp, remote_nick,
+                                        local_name, not_for_merge,
+                                        verbose, force || single_force);
+       }
+       return err;
+}
+
+static int parse_reflist(const char *reflist)
+{
+       const char *ref;
+
+       printf("refs='");
+       for (ref = reflist; ref; ) {
+               const char *next;
+               while (*ref && isspace(*ref))
+                       ref++;
+               if (!*ref)
+                       break;
+               for (next = ref; *next && !isspace(*next); next++)
+                       ;
+               printf("\n%.*s", (int)(next - ref), ref);
+               ref = next;
+       }
+       printf("'\n");
+
+       printf("rref='");
+       for (ref = reflist; ref; ) {
+               const char *next, *colon;
+               while (*ref && isspace(*ref))
+                       ref++;
+               if (!*ref)
+                       break;
+               for (next = ref; *next && !isspace(*next); next++)
+                       ;
+               if (*ref == '.')
+                       ref++;
+               if (*ref == '+')
+                       ref++;
+               colon = strchr(ref, ':');
+               putchar('\n');
+               printf("%.*s", (int)((colon ? colon : next) - ref), ref);
+               ref = next;
+       }
+       printf("'\n");
+       return 0;
+}
+
+static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,
+                               const char **refs)
+{
+       int i, matchlen, replacelen;
+       int found_one = 0;
+       const char *remote = *refs++;
+       numrefs--;
+
+       if (numrefs == 0) {
+               fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",
+                       remote);
+               printf("empty\n");
+       }
+
+       for (i = 0; i < numrefs; i++) {
+               const char *ref = refs[i];
+               const char *lref = ref;
+               const char *colon;
+               const char *tail;
+               const char *ls;
+               const char *next;
+
+               if (*lref == '+')
+                       lref++;
+               colon = strchr(lref, ':');
+               tail = lref + strlen(lref);
+               if (!(colon &&
+                     2 < colon - lref &&
+                     colon[-1] == '*' &&
+                     colon[-2] == '/' &&
+                     2 < tail - (colon + 1) &&
+                     tail[-1] == '*' &&
+                     tail[-2] == '/')) {
+                       /* not a glob */
+                       if (!found_one++)
+                               printf("explicit\n");
+                       printf("%s\n", ref);
+                       continue;
+               }
+
+               /* glob */
+               if (!found_one++)
+                       printf("glob\n");
+
+               /* lref to colon-2 is remote hierarchy name;
+                * colon+1 to tail-2 is local.
+                */
+               matchlen = (colon-1) - lref;
+               replacelen = (tail-1) - (colon+1);
+               for (ls = ls_remote_result; ls; ls = next) {
+                       const char *eol;
+                       unsigned char sha1[20];
+                       int namelen;
+
+                       while (*ls && isspace(*ls))
+                               ls++;
+                       next = strchr(ls, '\n');
+                       eol = !next ? (ls + strlen(ls)) : next;
+                       if (!memcmp("^{}", eol-3, 3))
+                               continue;
+                       if (eol - ls < 40)
+                               continue;
+                       if (get_sha1_hex(ls, sha1))
+                               continue;
+                       ls += 40;
+                       while (ls < eol && isspace(*ls))
+                               ls++;
+                       /* ls to next (or eol) is the name.
+                        * is it identical to lref to colon-2?
+                        */
+                       if ((eol - ls) <= matchlen ||
+                           strncmp(ls, lref, matchlen))
+                               continue;
+
+                       /* Yes, it is a match */
+                       namelen = eol - ls;
+                       if (lref != ref)
+                               putchar('+');
+                       printf("%.*s:%.*s%.*s\n",
+                              namelen, ls,
+                              replacelen, colon + 1,
+                              namelen - matchlen, ls + matchlen);
+               }
+       }
+       return 0;
+}
+
+static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_result)
+{
+       int err = 0;
+       int lrr_count = lrr_count, i, pass;
+       const char *cp;
+       struct lrr {
+               const char *line;
+               const char *name;
+               int namelen;
+               int shown;
+       } *lrr_list = lrr_list;
+
+       for (pass = 0; pass < 2; pass++) {
+               /* pass 0 counts and allocates, pass 1 fills... */
+               cp = ls_remote_result;
+               i = 0;
+               while (1) {
+                       const char *np;
+                       while (*cp && isspace(*cp))
+                               cp++;
+                       if (!*cp)
+                               break;
+                       np = strchrnul(cp, '\n');
+                       if (pass) {
+                               lrr_list[i].line = cp;
+                               lrr_list[i].name = cp + 41;
+                               lrr_list[i].namelen = np - (cp + 41);
+                       }
+                       i++;
+                       cp = np;
+               }
+               if (!pass) {
+                       lrr_count = i;
+                       lrr_list = xcalloc(lrr_count, sizeof(*lrr_list));
+               }
+       }
+
+       while (1) {
+               const char *next;
+               int rreflen;
+               int i;
+
+               while (*rref && isspace(*rref))
+                       rref++;
+               if (!*rref)
+                       break;
+               next = strchrnul(rref, '\n');
+               rreflen = next - rref;
+
+               for (i = 0; i < lrr_count; i++) {
+                       struct lrr *lrr = &(lrr_list[i]);
+
+                       if (rreflen == lrr->namelen &&
+                           !memcmp(lrr->name, rref, rreflen)) {
+                               if (!lrr->shown)
+                                       printf("%.*s\n",
+                                              sha1_only ? 40 : lrr->namelen + 41,
+                                              lrr->line);
+                               lrr->shown = 1;
+                               break;
+                       }
+               }
+               if (lrr_count <= i) {
+                       error("pick-rref: %.*s not found", rreflen, rref);
+                       err = 1;
+               }
+               rref = next;
+       }
+       free(lrr_list);
+       return err;
+}
+
+int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
+{
+       int verbose = 0;
+       int force = 0;
+       int sopt = 0;
+
+       while (1 < argc) {
+               const char *arg = argv[1];
+               if (!strcmp("-v", arg))
+                       verbose = 1;
+               else if (!strcmp("-f", arg))
+                       force = 1;
+               else if (!strcmp("-s", arg))
+                       sopt = 1;
+               else
+                       break;
+               argc--;
+               argv++;
+       }
+
+       if (argc <= 1)
+               return error("Missing subcommand");
+
+       if (!strcmp("append-fetch-head", argv[1])) {
+               int result;
+               FILE *fp;
+               char *filename;
+
+               if (argc != 8)
+                       return error("append-fetch-head takes 6 args");
+               filename = git_path("FETCH_HEAD");
+               fp = fopen(filename, "a");
+               if (!fp)
+                       return error("cannot open %s: %s\n", filename, strerror(errno));
+               result = append_fetch_head(fp, argv[2], argv[3],
+                                          argv[4], argv[5],
+                                          argv[6], !!argv[7][0],
+                                          verbose, force);
+               fclose(fp);
+               return result;
+       }
+       if (!strcmp("native-store", argv[1])) {
+               int result;
+               FILE *fp;
+               char *filename;
+
+               if (argc != 5)
+                       return error("fetch-native-store takes 3 args");
+               filename = git_path("FETCH_HEAD");
+               fp = fopen(filename, "a");
+               if (!fp)
+                       return error("cannot open %s: %s\n", filename, strerror(errno));
+               result = fetch_native_store(fp, argv[2], argv[3], argv[4],
+                                           verbose, force);
+               fclose(fp);
+               return result;
+       }
+       if (!strcmp("parse-reflist", argv[1])) {
+               const char *reflist;
+               if (argc != 3)
+                       return error("parse-reflist takes 1 arg");
+               reflist = argv[2];
+               if (!strcmp(reflist, "-"))
+                       reflist = get_stdin();
+               return parse_reflist(reflist);
+       }
+       if (!strcmp("pick-rref", argv[1])) {
+               const char *ls_remote_result;
+               if (argc != 4)
+                       return error("pick-rref takes 2 args");
+               ls_remote_result = argv[3];
+               if (!strcmp(ls_remote_result, "-"))
+                       ls_remote_result = get_stdin();
+               return pick_rref(sopt, argv[2], ls_remote_result);
+       }
+       if (!strcmp("expand-refs-wildcard", argv[1])) {
+               const char *reflist;
+               if (argc < 4)
+                       return error("expand-refs-wildcard takes at least 2 args");
+               reflist = argv[2];
+               if (!strcmp(reflist, "-"))
+                       reflist = get_stdin();
+               return expand_refs_wildcard(reflist, argc - 3, argv + 3);
+       }
+
+       return error("Unknown subcommand: %s", argv[1]);
+}
index e9588eec33ba5b64d186ff048bb040c18c57e6bc..500635fe4bb5a8f60bc76502ef15cd97bd273f74 100755 (executable)
@@ -14,7 +14,7 @@ summary              (synonym to --stat)
 log                  add list of one-line log to merge commit message
 squash               create a single commit instead of doing a merge
 commit               perform a commit if the merge succeeds (default)
-ff                   allow fast forward (default)
+ff                   allow fast-forward (default)
 s,strategy=          merge strategy to use
 m,message=           message to be used for the merge commit (if any)
 "
@@ -353,7 +353,7 @@ t,1,"$head",*)
        # Again the most common case of merging one remote.
        echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)"
        git update-index --refresh 2>/dev/null
-       msg="Fast forward"
+       msg="Fast-forward"
        if test -n "$have_message"
        then
                msg="$msg (no commit created; -m option ignored)"
@@ -365,11 +365,11 @@ t,1,"$head",*)
        exit 0
        ;;
 ?,1,?*"$LF"?*,*)
-       # We are not doing octopus and not fast forward.  Need a
+       # We are not doing octopus and not fast-forward.  Need a
        # real merge.
        ;;
 ?,1,*,)
-       # We are not doing octopus, not fast forward, and have only
+       # We are not doing octopus, not fast-forward, and have only
        # one common.
        git update-index --refresh 2>/dev/null
        case "$allow_trivial_merge" in
index 0ee1bd898ecbb725d13385408b4ed4bcb413f503..8f98142f77b5d6eda44b301082da20604a893db0 100755 (executable)
@@ -48,7 +48,7 @@ case "$common" in
 "$head")
        echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $merge)"
        git read-tree -u -m $head $merge || exit 1
-       git update-ref -m "resolve $merge_name: Fast forward" \
+       git update-ref -m "resolve $merge_name: Fast-forward" \
                HEAD "$merge" "$head"
        git diff-tree -p $head $merge | git apply --stat
        dropheads
index e710219ca52af3c90c28bb90273d062a26543864..48059d0aa74fe5f4c14bd74f599d449335ef8519 100755 (executable)
@@ -729,13 +729,10 @@ class P4Submit(Command):
             tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
             tmpFile.close()
             mtime = os.stat(fileName).st_mtime
-            defaultEditor = "vi"
-            if platform.system() == "Windows":
-                defaultEditor = "notepad"
             if os.environ.has_key("P4EDITOR"):
                 editor = os.environ.get("P4EDITOR")
             else:
-                editor = os.environ.get("EDITOR", defaultEditor);
+                editor = read_pipe("git var GIT_EDITOR")
             system(editor + " " + fileName)
 
             response = "y"
index 2a66063e44e2ca8e1742b59e046b3702721086cb..58a35c82870c54f844fd1154a82237891655f38f 100755 (executable)
@@ -315,8 +315,8 @@ generate_update_branch_email()
        # "remotes/" will be ignored as well.
 
        # List all of the revisions that were removed by this update, in a
-       # fast forward update, this list will be empty, because rev-list O
-       # ^N is empty.  For a non fast forward, O ^N is the list of removed
+       # fast-forward update, this list will be empty, because rev-list O
+       # ^N is empty.  For a non-fast-forward, O ^N is the list of removed
        # revisions
        fast_forward=""
        rev=""
@@ -411,7 +411,7 @@ generate_update_branch_email()
        # revision because the base is effectively a random revision at this
        # point - the user will be interested in what this revision changed
        # - including the undoing of previous revisions in the case of
-       # non-fast forward updates.
+       # non-fast-forward updates.
        echo ""
        echo "Summary of changes:"
        git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev
index 1b5ada6648da66d14dec6c399898916920582852..5783e2401108adb1fef6943ef80bd78dbc76ecad 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -77,6 +77,7 @@ static void logreport(int priority, const char *err, va_list params)
        }
 }
 
+__attribute__((format (printf, 1, 2)))
 static void logerror(const char *err, ...)
 {
        va_list params;
@@ -85,6 +86,7 @@ static void logerror(const char *err, ...)
        va_end(params);
 }
 
+__attribute__((format (printf, 1, 2)))
 static void loginfo(const char *err, ...)
 {
        va_list params;
@@ -101,53 +103,6 @@ static void NORETURN daemon_die(const char *err, va_list params)
        exit(1);
 }
 
-static int avoid_alias(char *p)
-{
-       int sl, ndot;
-
-       /*
-        * This resurrects the belts and suspenders paranoia check by HPA
-        * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
-        * does not do getcwd() based path canonicalizations.
-        *
-        * sl becomes true immediately after seeing '/' and continues to
-        * be true as long as dots continue after that without intervening
-        * non-dot character.
-        */
-       if (!p || (*p != '/' && *p != '~'))
-               return -1;
-       sl = 1; ndot = 0;
-       p++;
-
-       while (1) {
-               char ch = *p++;
-               if (sl) {
-                       if (ch == '.')
-                               ndot++;
-                       else if (ch == '/') {
-                               if (ndot < 3)
-                                       /* reject //, /./ and /../ */
-                                       return -1;
-                               ndot = 0;
-                       }
-                       else if (ch == 0) {
-                               if (0 < ndot && ndot < 3)
-                                       /* reject /.$ and /..$ */
-                                       return -1;
-                               return 0;
-                       }
-                       else
-                               sl = ndot = 0;
-               }
-               else if (ch == 0)
-                       return 0;
-               else if (ch == '/') {
-                       sl = 1;
-                       ndot = 0;
-               }
-       }
-}
-
 static char *path_ok(char *directory)
 {
        static char rpath[PATH_MAX];
@@ -157,7 +112,7 @@ static char *path_ok(char *directory)
 
        dir = directory;
 
-       if (avoid_alias(dir)) {
+       if (daemon_avoid_alias(dir)) {
                logerror("'%s': aliased", dir);
                return NULL;
        }
index 0c74ef5cbe9ae0b43d8eaeb3249e4f83c695920b..adf1c5fdee2bc836f03eaa89160573973bb46a67 100644 (file)
@@ -383,7 +383,7 @@ static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_op
  * For diffing, the index is more important, and we only have a
  * single tree.
  *
- * We're supposed to return how many index entries we want to skip.
+ * We're supposed to advance o->pos to skip what we have already processed.
  *
  * This wrapper makes it all more readable, and takes care of all
  * the fairly complex unpack_trees() semantic requirements, including
index 4ebc1dbd87d9b90ad2aaa5c7d4601761e2d1d724..aae8e7accc1ff955bd76c62b379b37f343f61cc4 100644 (file)
@@ -201,8 +201,8 @@ void diff_no_index(struct rev_info *revs,
                        return;
        }
        if (argc != i + 2)
-               die("git diff %s takes two paths",
-                   no_index ? "--no-index" : "[--no-index]");
+               usagef("git diff %s <path> <path>",
+                      no_index ? "--no-index" : "[--no-index]");
 
        diff_setup(&revs->diffopt);
        for (i = 1; i < argc - 2; ) {
diff --git a/diff.c b/diff.c
index 3ad1f8c31046ee3dae8a8e9bed6dc9f5a5929b78..d952686926e2e97aab3e369ad192027b4d7e676b 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -39,6 +39,7 @@ static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_GREEN,        /* NEW */
        GIT_COLOR_YELLOW,       /* COMMIT */
        GIT_COLOR_BG_RED,       /* WHITESPACE */
+       GIT_COLOR_NORMAL,       /* FUNCINFO */
 };
 
 static void diff_filespec_load_driver(struct diff_filespec *one);
@@ -60,6 +61,8 @@ static int parse_diff_color_slot(const char *var, int ofs)
                return DIFF_COMMIT;
        if (!strcasecmp(var+ofs, "whitespace"))
                return DIFF_WHITESPACE;
+       if (!strcasecmp(var+ofs, "func"))
+               return DIFF_FUNCINFO;
        die("bad config variable '%s'", var);
 }
 
@@ -295,12 +298,13 @@ static void emit_line_0(FILE *file, const char *set, const char *reset,
                nofirst = 0;
        }
 
-       fputs(set, file);
-
-       if (!nofirst)
-               fputc(first, file);
-       fwrite(line, len, 1, file);
-       fputs(reset, file);
+       if (len || !nofirst) {
+               fputs(set, file);
+               if (!nofirst)
+                       fputc(first, file);
+               fwrite(line, len, 1, file);
+               fputs(reset, file);
+       }
        if (has_trailing_carriage_return)
                fputc('\r', file);
        if (has_trailing_newline)
@@ -344,6 +348,42 @@ static void emit_add_line(const char *reset,
        }
 }
 
+static void emit_hunk_header(struct emit_callback *ecbdata,
+                            const char *line, int len)
+{
+       const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
+       const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
+       const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
+       const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
+       static const char atat[2] = { '@', '@' };
+       const char *cp, *ep;
+
+       /*
+        * As a hunk header must begin with "@@ -<old>, +<new> @@",
+        * it always is at least 10 bytes long.
+        */
+       if (len < 10 ||
+           memcmp(line, atat, 2) ||
+           !(ep = memmem(line + 2, len - 2, atat, 2))) {
+               emit_line(ecbdata->file, plain, reset, line, len);
+               return;
+       }
+       ep += 2; /* skip over @@ */
+
+       /* The hunk header in fraginfo color */
+       emit_line(ecbdata->file, frag, reset, line, ep - line);
+
+       /* blank before the func header */
+       for (cp = ep; ep - line < len; ep++)
+               if (*ep != ' ' && *ep != '\t')
+                       break;
+       if (ep != cp)
+               emit_line(ecbdata->file, plain, reset, cp, ep - cp);
+
+       if (ep < line + len)
+               emit_line(ecbdata->file, func, reset, ep, line + len - ep);
+}
+
 static struct diff_tempfile *claim_diff_tempfile(void) {
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
@@ -686,14 +726,18 @@ static void diff_words_show(struct diff_words_data *diff_words)
        diff_words->minus.text.size = diff_words->plus.text.size = 0;
 }
 
+/* In "color-words" mode, show word-diff of words accumulated in the buffer */
+static void diff_words_flush(struct emit_callback *ecbdata)
+{
+       if (ecbdata->diff_words->minus.text.size ||
+           ecbdata->diff_words->plus.text.size)
+               diff_words_show(ecbdata->diff_words);
+}
+
 static void free_diff_words_data(struct emit_callback *ecbdata)
 {
        if (ecbdata->diff_words) {
-               /* flush buffers */
-               if (ecbdata->diff_words->minus.text.size ||
-                               ecbdata->diff_words->plus.text.size)
-                       diff_words_show(ecbdata->diff_words);
-
+               diff_words_flush(ecbdata);
                free (ecbdata->diff_words->minus.text.ptr);
                free (ecbdata->diff_words->minus.orig);
                free (ecbdata->diff_words->plus.text.ptr);
@@ -773,11 +817,11 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        }
 
        if (line[0] == '@') {
+               if (ecbdata->diff_words)
+                       diff_words_flush(ecbdata);
                len = sane_truncate_line(ecbdata, line, len);
                find_lno(line, ecbdata);
-               emit_line(ecbdata->file,
-                         diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
-                         reset, line, len);
+               emit_hunk_header(ecbdata, line, len);
                if (line[len-1] != '\n')
                        putc('\n', ecbdata->file);
                return;
@@ -798,9 +842,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                                          &ecbdata->diff_words->plus);
                        return;
                }
-               if (ecbdata->diff_words->minus.text.size ||
-                   ecbdata->diff_words->plus.text.size)
-                       diff_words_show(ecbdata->diff_words);
+               diff_words_flush(ecbdata);
                line++;
                len--;
                emit_line(ecbdata->file, plain, reset, line, len);
diff --git a/diff.h b/diff.h
index 2740421cfe9985a57f61855dc6372e8402231e0e..15fcecdecd9b033700902de44138eafdb66932eb 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -130,6 +130,7 @@ enum color_diff {
        DIFF_FILE_NEW = 5,
        DIFF_COMMIT = 6,
        DIFF_WHITESPACE = 7,
+       DIFF_FUNCINFO = 8,
 };
 const char *diff_get_color(int diff_use_color, enum color_diff ix);
 #define diff_get_color_opt(o, ix) \
index d7097bb576cef8900e8e0218ba82e1cc7a87a567..3a7b60a037b2e3c869afe76a23b671cfd5311338 100644 (file)
@@ -69,7 +69,7 @@ static int should_break(struct diff_filespec *src,
                return 0; /* we do not break too small filepair */
 
        if (diffcore_count_changes(src, dst,
-                                  NULL, NULL,
+                                  &src->cnt_data, &dst->cnt_data,
                                   0,
                                   &src_copied, &literal_added))
                return 0;
@@ -204,12 +204,16 @@ void diffcore_break(int break_score)
                                dp->score = score;
                                dp->broken_pair = 1;
 
+                               diff_free_filespec_blob(p->one);
+                               diff_free_filespec_blob(p->two);
                                free(p); /* not diff_free_filepair(), we are
                                          * reusing one and two here.
                                          */
                                continue;
                        }
                }
+               diff_free_filespec_data(p->one);
+               diff_free_filespec_data(p->two);
                diff_q(&outq, p);
        }
        free(q->queue);
index 63ac998bfaf64da807bec0ae0bd57c391fb651fe..d6fd3cacd6de4757994c61903dd07e0c4d74a9e9 100644 (file)
@@ -523,10 +523,13 @@ void diffcore_rename(struct diff_options *options)
                        this_src.dst = i;
                        this_src.src = j;
                        record_if_better(m, &this_src);
+                       /*
+                        * Once we run estimate_similarity,
+                        * We do not need the text anymore.
+                        */
                        diff_free_filespec_blob(one);
+                       diff_free_filespec_blob(two);
                }
-               /* We do not need the text anymore */
-               diff_free_filespec_blob(two);
                dst_cnt++;
        }
 
index 4d469d076bcd58df3af16d98adc9120e34765944..615f5754d66ba7ea611a4837b1ff802f9f9de598 100644 (file)
--- a/editor.c
+++ b/editor.c
@@ -2,24 +2,38 @@
 #include "strbuf.h"
 #include "run-command.h"
 
-int launch_editor(const char *path, struct strbuf *buffer, const char *const *env)
+#ifndef DEFAULT_EDITOR
+#define DEFAULT_EDITOR "vi"
+#endif
+
+const char *git_editor(void)
 {
-       const char *editor, *terminal;
+       const char *editor = getenv("GIT_EDITOR");
+       const char *terminal = getenv("TERM");
+       int terminal_is_dumb = !terminal || !strcmp(terminal, "dumb");
 
-       editor = getenv("GIT_EDITOR");
        if (!editor && editor_program)
                editor = editor_program;
-       if (!editor)
+       if (!editor && !terminal_is_dumb)
                editor = getenv("VISUAL");
        if (!editor)
                editor = getenv("EDITOR");
 
-       terminal = getenv("TERM");
-       if (!editor && (!terminal || !strcmp(terminal, "dumb")))
-               return error("Terminal is dumb but no VISUAL nor EDITOR defined.");
+       if (!editor && terminal_is_dumb)
+               return NULL;
+
+       if (!editor)
+               editor = DEFAULT_EDITOR;
+
+       return editor;
+}
+
+int launch_editor(const char *path, struct strbuf *buffer, const char *const *env)
+{
+       const char *editor = git_editor();
 
        if (!editor)
-               editor = "vi";
+               return error("Terminal is dumb, but EDITOR unset");
 
        if (strcmp(editor, ":")) {
                size_t len = strlen(editor);
@@ -28,7 +42,7 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en
                const char *args[6];
                struct strbuf arg0 = STRBUF_INIT;
 
-               if (strcspn(editor, "$ \t'") != len) {
+               if (strcspn(editor, "|&;<>()$`\\\"' \t\n*?[#~=%") != len) {
                        /* there are specials */
                        strbuf_addf(&arg0, "%s \"$@\"", editor);
                        args[i++] = "sh";
index 5de683784039f20b55f58ef36cb04a11f6a10610..5171d9f9a4a30054983638e9e19e61f422724373 100644 (file)
@@ -49,6 +49,7 @@ enum push_default_type push_default = PUSH_DEFAULT_MATCHING;
 #define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS
 #endif
 enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
+char *notes_ref_name;
 int grafts_replace_parents = 1;
 
 /* Parallel index stat data preload? */
@@ -83,6 +84,8 @@ static void setup_git_env(void)
        git_graft_file = getenv(GRAFT_ENVIRONMENT);
        if (!git_graft_file)
                git_graft_file = git_pathdup("info/grafts");
+       if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
+               read_replace_refs = 0;
 }
 
 int is_bare_repository(void)
index 6faaaacb68999db294d20ecbb30772223bfc1b57..dd3c99d60d0ccf2506b08632c35c5ffc2bbc3255 100644 (file)
@@ -22,8 +22,8 @@ Format of STDIN stream:
     ('author' sp name sp '<' email '>' sp when lf)?
     'committer' sp name sp '<' email '>' sp when lf
     commit_msg
-    ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
-    ('merge' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)*
+    ('from' sp committish lf)?
+    ('merge' sp committish lf)*
     file_change*
     lf?;
   commit_msg ::= data;
@@ -41,15 +41,18 @@ Format of STDIN stream:
   file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
   file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
     data;
+  note_obm ::= 'N' sp (hexsha1 | idnum) sp committish lf;
+  note_inm ::= 'N' sp 'inline' sp committish lf
+    data;
 
   new_tag ::= 'tag' sp tag_str lf
-    'from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf
+    'from' sp committish lf
     ('tagger' sp name sp '<' email '>' sp when lf)?
     tag_msg;
   tag_msg ::= data;
 
   reset_branch ::= 'reset' sp ref_str lf
-    ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
+    ('from' sp committish lf)?
     lf?;
 
   checkpoint ::= 'checkpoint' lf
@@ -88,6 +91,7 @@ Format of STDIN stream:
      # stream formatting is: \, " and LF.  Otherwise these values
      # are UTF8.
      #
+  committish  ::= (ref_str | hexsha1 | sha1exp_str | idnum);
   ref_str     ::= ref;
   sha1exp_str ::= sha1exp;
   tag_str     ::= tag;
@@ -2006,6 +2010,80 @@ static void file_change_cr(struct branch *b, int rename)
                leaf.tree);
 }
 
+static void note_change_n(struct branch *b)
+{
+       const char *p = command_buf.buf + 2;
+       static struct strbuf uq = STRBUF_INIT;
+       struct object_entry *oe = oe;
+       struct branch *s;
+       unsigned char sha1[20], commit_sha1[20];
+       uint16_t inline_data = 0;
+
+       /* <dataref> or 'inline' */
+       if (*p == ':') {
+               char *x;
+               oe = find_mark(strtoumax(p + 1, &x, 10));
+               hashcpy(sha1, oe->sha1);
+               p = x;
+       } else if (!prefixcmp(p, "inline")) {
+               inline_data = 1;
+               p += 6;
+       } else {
+               if (get_sha1_hex(p, sha1))
+                       die("Invalid SHA1: %s", command_buf.buf);
+               oe = find_object(sha1);
+               p += 40;
+       }
+       if (*p++ != ' ')
+               die("Missing space after SHA1: %s", command_buf.buf);
+
+       /* <committish> */
+       s = lookup_branch(p);
+       if (s) {
+               hashcpy(commit_sha1, s->sha1);
+       } else if (*p == ':') {
+               uintmax_t commit_mark = strtoumax(p + 1, NULL, 10);
+               struct object_entry *commit_oe = find_mark(commit_mark);
+               if (commit_oe->type != OBJ_COMMIT)
+                       die("Mark :%" PRIuMAX " not a commit", commit_mark);
+               hashcpy(commit_sha1, commit_oe->sha1);
+       } else if (!get_sha1(p, commit_sha1)) {
+               unsigned long size;
+               char *buf = read_object_with_reference(commit_sha1,
+                       commit_type, &size, commit_sha1);
+               if (!buf || size < 46)
+                       die("Not a valid commit: %s", p);
+               free(buf);
+       } else
+               die("Invalid ref name or SHA1 expression: %s", p);
+
+       if (inline_data) {
+               static struct strbuf buf = STRBUF_INIT;
+
+               if (p != uq.buf) {
+                       strbuf_addstr(&uq, p);
+                       p = uq.buf;
+               }
+               read_next_command();
+               parse_data(&buf);
+               store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0);
+       } else if (oe) {
+               if (oe->type != OBJ_BLOB)
+                       die("Not a blob (actually a %s): %s",
+                               typename(oe->type), command_buf.buf);
+       } else {
+               enum object_type type = sha1_object_info(sha1, NULL);
+               if (type < 0)
+                       die("Blob not found: %s", command_buf.buf);
+               if (type != OBJ_BLOB)
+                       die("Not a blob (actually a %s): %s",
+                           typename(type), command_buf.buf);
+       }
+
+       tree_content_set(&b->branch_tree, sha1_to_hex(commit_sha1), sha1,
+               S_IFREG | 0644, NULL);
+}
+
 static void file_change_deleteall(struct branch *b)
 {
        release_tree_content_recursive(b->branch_tree.tree);
@@ -2175,6 +2253,8 @@ static void parse_new_commit(void)
                        file_change_cr(b, 1);
                else if (!prefixcmp(command_buf.buf, "C "))
                        file_change_cr(b, 0);
+               else if (!prefixcmp(command_buf.buf, "N "))
+                       note_change_n(b);
                else if (!strcmp("deleteall", command_buf.buf))
                        file_change_deleteall(b);
                else {
@@ -2405,6 +2485,9 @@ int main(int argc, const char **argv)
 
        git_extract_argv0_path(argv[0]);
 
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage(fast_import_usage);
+
        setup_git_directory();
        git_config(git_pack_config, NULL);
        if (!pack_compression_seen && core_compression_seen)
index 8bd9c32561e79d194d27fa10cc98a26aa2cb673c..fbe85ac05fd2d945d645365dc086460003fa7f27 100644 (file)
@@ -13,7 +13,8 @@ struct fetch_pack_args
                fetch_all:1,
                verbose:1,
                no_progress:1,
-               include_tag:1;
+               include_tag:1,
+               stateless_rpc:1;
 };
 
 struct ref *fetch_pack(struct fetch_pack_args *args,
diff --git a/fsck.h b/fsck.h
index 008456b675b6e5c8a5a07e0b8628a41e3acc733a..1e4f527318ea4b4d701a27e08d68f3551173f176 100644 (file)
--- a/fsck.h
+++ b/fsck.h
@@ -17,6 +17,7 @@ typedef int (*fsck_walk_func)(struct object *obj, int type, void *data);
 /* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
 typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);
 
+__attribute__((format (printf, 3, 4)))
 int fsck_error_function(struct object *obj, int type, const char *fmt, ...);
 
 /* descend in all linked child objects
index 69aeaf03ec65922d8a3e5e092fab3d4b6ffcb63e..f813ffdaa1526aa68b4ed8e7d4d04b48e270496a 100755 (executable)
@@ -731,14 +731,17 @@ sub parse_diff_header {
 
        my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };
        my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };
+       my $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };
 
        for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
-               my $dest = $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ?
-                       $mode : $head;
+               my $dest =
+                  $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode :
+                  $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :
+                  $head;
                push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
                push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
        }
-       return ($head, $mode);
+       return ($head, $mode, $deletion);
 }
 
 sub hunk_splittable {
@@ -987,8 +990,7 @@ sub edit_hunk_manually {
 EOF
        close $fh;
 
-       my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor")
-               || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+       chomp(my $editor = run_cmd_pipe(qw(git var GIT_EDITOR)));
        system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
 
        if ($? != 0) {
@@ -1206,7 +1208,7 @@ sub patch_update_file {
        my ($ix, $num);
        my $path = shift;
        my ($head, @hunk) = parse_diff($path);
-       ($head, my $mode) = parse_diff_header($head);
+       ($head, my $mode, my $deletion) = parse_diff_header($head);
        for (@{$head->{DISPLAY}}) {
                print;
        }
@@ -1214,6 +1216,9 @@ sub patch_update_file {
        if (@{$mode->{TEXT}}) {
                unshift @hunk, $mode;
        }
+       if (@{$deletion->{TEXT}} && !@hunk) {
+               @hunk = ($deletion);
+       }
 
        $num = scalar @hunk;
        $ix = 0;
@@ -1267,7 +1272,9 @@ sub patch_update_file {
                        print;
                }
                print colored $prompt_color, $patch_mode_flavour{VERB},
-                 ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' : ' this hunk'),
+                 ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' :
+                  $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' :
+                  ' this hunk'),
                  $patch_mode_flavour{TARGET},
                  " [y,n,q,a,d,/$other,?]? ";
                my $line = prompt_single_character;
index c132f50da5f3416e6177dd7010e6db3660bdb073..4838cdb9ede8765b37ab663aa59fb1b011a30b4c 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -205,7 +205,7 @@ check_patch_format () {
                        # and see if it looks like that they all begin with the
                        # header field names...
                        sed -n -e '/^$/q' -e '/^[       ]/d' -e p "$1" |
-                       LC_ALL=C egrep -v '^[!-9;-~]+:' >/dev/null ||
+                       sane_egrep -v '^[!-9;-~]+:' >/dev/null ||
                        patch_format=mbox
                fi
        } < "$1" || clean_abort
@@ -289,7 +289,7 @@ split_patches () {
 prec=4
 dotest="$GIT_DIR/rebase-apply"
 sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
-resolvemsg= resume= scissors=
+resolvemsg= resume= scissors= no_inbody_headers=
 git_apply_opt=
 committer_date_is_author_date=
 ignore_date=
@@ -322,7 +322,7 @@ do
        --abort)
                abort=t ;;
        --rebasing)
-               rebasing=t threeway=t keep=t scissors=f ;;
+               rebasing=t threeway=t keep=t scissors=f no_inbody_headers=t ;;
        -d|--dotest)
                die "-d option is no longer supported.  Do not use."
                ;;
@@ -448,6 +448,7 @@ else
        echo "$utf8" >"$dotest/utf8"
        echo "$keep" >"$dotest/keep"
        echo "$scissors" >"$dotest/scissors"
+       echo "$no_inbody_headers" >"$dotest/no_inbody_headers"
        echo "$GIT_QUIET" >"$dotest/quiet"
        echo 1 >"$dotest/next"
        if test -n "$rebasing"
@@ -495,6 +496,12 @@ t)
 f)
        scissors=--no-scissors ;;
 esac
+if test "$(cat "$dotest/no_inbody_headers")" = t
+then
+       no_inbody_headers=--no-inbody-headers
+else
+       no_inbody_headers=
+fi
 if test "$(cat "$dotest/quiet")" = t
 then
        GIT_QUIET=t
@@ -549,12 +556,12 @@ do
        # by the user, or the user can tell us to do so by --resolved flag.
        case "$resume" in
        '')
-               git mailinfo $keep $scissors $utf8 "$dotest/msg" "$dotest/patch" \
+               git mailinfo $keep $no_inbody_headers $scissors $utf8 "$dotest/msg" "$dotest/patch" \
                        <"$dotest/$msgnum" >"$dotest/info" ||
                        stop_here $this
 
                # skip pine's internal folder data
-               grep '^Author: Mail System Internal Data$' \
+               sane_grep '^Author: Mail System Internal Data$' \
                        <"$dotest"/info >/dev/null &&
                        go_next && continue
 
@@ -570,11 +577,12 @@ do
                        git cat-file commit "$commit" |
                        sed -e '1,/^$/d' >"$dotest/msg-clean"
                else
-                       SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
-                       case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
-
-                       (printf '%s\n\n' "$SUBJECT"; cat "$dotest/msg") |
-                               git stripspace > "$dotest/msg-clean"
+                       {
+                               sed -n '/^Subject/ s/Subject: //p' "$dotest/info"
+                               echo
+                               cat "$dotest/msg"
+                       } |
+                       git stripspace > "$dotest/msg-clean"
                fi
                ;;
        esac
@@ -649,7 +657,10 @@ do
                [eE]*) git_editor "$dotest/final-commit"
                       action=again ;;
                [vV]*) action=again
-                      LESS=-S ${PAGER:-less} "$dotest/patch" ;;
+                      : ${GIT_PAGER=$(git var GIT_PAGER)}
+                      : ${LESS=-FRSX}
+                      export LESS
+                      $GIT_PAGER "$dotest/patch" ;;
                *)     action=again ;;
                esac
            done
index 8b3c5858a901619eecd162229f92eb431b3e63f9..6e2acb8ef29e5003945bed17014a68b141ada454 100755 (executable)
@@ -300,8 +300,7 @@ bisect_visualize() {
                esac
        fi
 
-       not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
-       eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
+       eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
 bisect_reset() {
@@ -393,7 +392,7 @@ bisect_run () {
 
       cat "$GIT_DIR/BISECT_RUN"
 
-      if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
+      if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
                > /dev/null; then
          echo >&2 "bisect run cannot continue any more"
          exit $res
@@ -405,7 +404,7 @@ bisect_run () {
          exit $res
       fi
 
-      if grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
+      if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
          echo "bisect run success"
          exit 0;
       fi
index ef6080338480f509e720a484998c286ae1814b0a..5c596875c2e78a92031915948444971126d91a78 100644 (file)
@@ -189,6 +189,7 @@ extern char *gitbasename(char *);
 
 /* General helper functions */
 extern NORETURN void usage(const char *err);
+extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
index a480d6fc70a4ca738c8ead14052afc3c1f5e8b02..cb9d2022cc93f1f371264098828a52e0d6bf1d7c 100755 (executable)
@@ -125,6 +125,7 @@ filter_subdir=
 orig_namespace=refs/original/
 force=
 prune_empty=
+remap_to_ancestor=
 while :
 do
        case "$1" in
@@ -137,6 +138,11 @@ do
                force=t
                continue
                ;;
+       --remap-to-ancestor)
+               shift
+               remap_to_ancestor=t
+               continue
+               ;;
        --prune-empty)
                shift
                prune_empty=t
@@ -182,6 +188,7 @@ do
                ;;
        --subdirectory-filter)
                filter_subdir="$OPTARG"
+               remap_to_ancestor=t
                ;;
        --original)
                orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
@@ -257,15 +264,24 @@ git read-tree || die "Could not seed the index"
 # map old->new commit ids for rewriting parents
 mkdir ../map || die "Could not create map/ directory"
 
+# we need "--" only if there are no path arguments in $@
+nonrevs=$(git rev-parse --no-revs "$@") || exit
+test -z "$nonrevs" && dashdash=-- || dashdash=
+rev_args=$(git rev-parse --revs-only "$@")
+
 case "$filter_subdir" in
 "")
-       git rev-list --reverse --topo-order --default HEAD \
-               --parents --simplify-merges "$@"
+       eval set -- "$(git rev-parse --sq --no-revs "$@")"
        ;;
 *)
-       git rev-list --reverse --topo-order --default HEAD \
-               --parents --simplify-merges "$@" -- "$filter_subdir"
-esac > ../revs || die "Could not get the commits"
+       eval set -- "$(git rev-parse --sq --no-revs "$@" $dashdash \
+               "$filter_subdir")"
+       ;;
+esac
+
+git rev-list --reverse --topo-order --default HEAD \
+       --parents --simplify-merges $rev_args "$@" > ../revs ||
+       die "Could not get the commits"
 commits=$(wc -l <../revs | tr -d " ")
 
 test $commits -eq 0 && die "Found nothing to rewrite"
@@ -345,19 +361,19 @@ while read commit parents; do
                        die "could not write rewritten commit"
 done <../revs
 
-# In case of a subdirectory filter, it is possible that a specified head
-# is not in the set of rewritten commits, because it was pruned by the
-# revision walker.  Fix it by mapping these heads to the unique nearest
-# ancestor that survived the pruning.
+# If we are filtering for paths, as in the case of a subdirectory
+# filter, it is possible that a specified head is not in the set of
+# rewritten commits, because it was pruned by the revision walker.
+# Ancestor remapping fixes this by mapping these heads to the unique
+# nearest ancestor that survived the pruning.
 
-if test "$filter_subdir"
+if test "$remap_to_ancestor" = t
 then
        while read ref
        do
                sha1=$(git rev-parse "$ref"^0)
                test -f "$workdir"/../map/$sha1 && continue
-               ancestor=$(git rev-list --simplify-merges -1 \
-                               $ref -- "$filter_subdir")
+               ancestor=$(git rev-list --simplify-merges -1 "$ref" "$@")
                test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1
        done < "$tempdir"/heads
 fi
@@ -457,7 +473,7 @@ if [ "$filter_tag_name" ]; then
                                git mktag) ||
                                die "Could not create new tag object for $ref"
                        if git cat-file tag "$ref" | \
-                          grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
+                          sane_grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
                        then
                                warn "gpg signature stripped from tag object $sha1t"
                        fi
index 622a5f0eb25e3420c085d51fd85944ab1286147e..341930ca9d74867cf334952fc47bb3afe95552b7 100755 (executable)
@@ -41,7 +41,7 @@ resolve_full_httpd () {
        case "$httpd" in
        *apache2*|*lighttpd*)
                # ensure that the apache2/lighttpd command ends with "-f"
-               if ! echo "$httpd" | grep -- '-f *$' >/dev/null 2>&1
+               if ! echo "$httpd" | sane_grep -- '-f *$' >/dev/null 2>&1
                then
                        httpd="$httpd -f"
                fi
@@ -73,6 +73,11 @@ resolve_full_httpd () {
 }
 
 start_httpd () {
+       if test -f "$fqgitdir/pid"; then
+               say "Instance already running. Restarting..."
+               stop_httpd
+       fi
+
        # here $httpd should have a meaningful value
        resolve_full_httpd
 
@@ -297,8 +302,8 @@ EOF
 
        # check to see if Dennis Stosberg's mod_perl compatibility patch
        # (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
-       if test -f "$module_path/mod_perl.so" && grep 'MOD_PERL' \
-                               "$GIT_DIR/gitweb/gitweb.cgi" >/dev/null
+       if test -f "$module_path/mod_perl.so" &&
+          sane_grep 'MOD_PERL' "$GIT_DIR/gitweb/gitweb.cgi" >/dev/null
        then
                # favor mod_perl if available
                cat >> "$conf" <<EOF
@@ -316,7 +321,7 @@ EOF
                # plain-old CGI
                resolve_full_httpd
                list_mods=$(echo "$full_httpd" | sed "s/-f$/-l/")
-               $list_mods | grep 'mod_cgi\.c' >/dev/null 2>&1 || \
+               $list_mods | sane_grep 'mod_cgi\.c' >/dev/null 2>&1 || \
                if test -f "$module_path/mod_cgi.so"
                then
                        echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
index 1dadbb49666c6d796df76babbfd291a2de4357e4..825c52c2436d15edb12a2ce7850437e0e531e0e3 100755 (executable)
@@ -81,7 +81,7 @@ do
                # tree as the intermediate result of the merge.
                # We still need to count this as part of the parent set.
 
-               echo "Fast forwarding to: $SHA1"
+               echo "Fast-forwarding to: $SHA1"
                git read-tree -u -m $head $SHA1 || exit
                MRC=$SHA1 MRT=$(git write-tree)
                continue
index 9c2c1b7202462370509a7bdb391982ae32564bd6..d067894bf45fd0a50513e196ea2a5e671d901681 100755 (executable)
 # been handled already by git read-tree, but that one doesn't
 # do any merges that might change the tree layout.
 
+USAGE='<orig blob> <our blob> <their blob> <path>'
+USAGE="$USAGE <orig mode> <our mode> <their mode>"
+LONG_USAGE="Usage: git merge-one-file $USAGE
+
+Blob ids and modes should be empty for missing files."
+
+if ! test "$#" -eq 7
+then
+       echo "$LONG_USAGE"
+       exit 1
+fi
+
 case "${1:-.}${2:-.}${3:-.}" in
 #
 # Deleted in both or deleted in one and unchanged in the other
index bfb01f7842e6192e0b3fbc0e3f2ab96269578289..5b6278572a48569a806a5a976cdd08eb7c117fb7 100644 (file)
@@ -46,7 +46,7 @@ check_unchanged () {
 valid_tool () {
        case "$1" in
        kdiff3 | tkdiff | xxdiff | meld | opendiff | \
-       emerge | vimdiff | gvimdiff | ecmerge | diffuse | araxis)
+       emerge | vimdiff | gvimdiff | ecmerge | diffuse | araxis | p4merge)
                ;; # happy
        tortoisemerge)
                if ! merge_mode; then
@@ -130,6 +130,19 @@ run_merge_tool () {
                        "$merge_tool_path" "$LOCAL" "$REMOTE"
                fi
                ;;
+       p4merge)
+               if merge_mode; then
+                   touch "$BACKUP"
+                       if $base_present; then
+                               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
+                       else
+                               "$merge_tool_path" "$LOCAL" "$LOCAL" "$REMOTE" "$MERGED"
+                       fi
+                       check_unchanged
+               else
+                       "$merge_tool_path" "$LOCAL" "$REMOTE"
+               fi
+               ;;
        meld)
                if merge_mode; then
                        touch "$BACKUP"
@@ -323,17 +336,16 @@ guess_merge_tool () {
                else
                        tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
                fi
-               tools="$tools gvimdiff diffuse ecmerge araxis"
+               tools="$tools gvimdiff diffuse ecmerge p4merge araxis"
        fi
-       if echo "${VISUAL:-$EDITOR}" | grep emacs > /dev/null 2>&1; then
-               # $EDITOR is emacs so add emerge as a candidate
-               tools="$tools emerge vimdiff"
-       elif echo "${VISUAL:-$EDITOR}" | grep vim > /dev/null 2>&1; then
-               # $EDITOR is vim so add vimdiff as a candidate
+       case "${VISUAL:-$EDITOR}" in
+       *vim*)
                tools="$tools vimdiff emerge"
-       else
+               ;;
+       *)
                tools="$tools emerge vimdiff"
-       fi
+               ;;
+       esac
        echo >&2 "merge tool candidates: $tools"
 
        # Loop over each candidate and stop when a valid merge tool is found.
diff --git a/git-notes.sh b/git-notes.sh
new file mode 100755 (executable)
index 0000000..e642e47
--- /dev/null
@@ -0,0 +1,121 @@
+#!/bin/sh
+
+USAGE="(edit [-F <file> | -m <msg>] | show) [commit]"
+. git-sh-setup
+
+test -z "$1" && usage
+ACTION="$1"; shift
+
+test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="$(git config core.notesref)"
+test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="refs/notes/commits"
+
+MESSAGE=
+while test $# != 0
+do
+       case "$1" in
+       -m)
+               test "$ACTION" = "edit" || usage
+               shift
+               if test "$#" = "0"; then
+                       die "error: option -m needs an argument"
+               else
+                       if [ -z "$MESSAGE" ]; then
+                               MESSAGE="$1"
+                       else
+                               MESSAGE="$MESSAGE
+
+$1"
+                       fi
+                       shift
+               fi
+               ;;
+       -F)
+               test "$ACTION" = "edit" || usage
+               shift
+               if test "$#" = "0"; then
+                       die "error: option -F needs an argument"
+               else
+                       if [ -z "$MESSAGE" ]; then
+                               MESSAGE="$(cat "$1")"
+                       else
+                               MESSAGE="$MESSAGE
+
+$(cat "$1")"
+                       fi
+                       shift
+               fi
+               ;;
+       -*)
+               usage
+               ;;
+       *)
+               break
+               ;;
+       esac
+done
+
+COMMIT=$(git rev-parse --verify --default HEAD "$@") ||
+die "Invalid commit: $@"
+
+case "$ACTION" in
+edit)
+       if [ "${GIT_NOTES_REF#refs/notes/}" = "$GIT_NOTES_REF" ]; then
+               die "Refusing to edit notes in $GIT_NOTES_REF (outside of refs/notes/)"
+       fi
+
+       MSG_FILE="$GIT_DIR/new-notes-$COMMIT"
+       GIT_INDEX_FILE="$MSG_FILE.idx"
+       export GIT_INDEX_FILE
+
+       trap '
+               test -f "$MSG_FILE" && rm "$MSG_FILE"
+               test -f "$GIT_INDEX_FILE" && rm "$GIT_INDEX_FILE"
+       ' 0
+
+       CURRENT_HEAD=$(git show-ref "$GIT_NOTES_REF" | cut -f 1 -d ' ')
+       if [ -z "$CURRENT_HEAD" ]; then
+               PARENT=
+       else
+               PARENT="-p $CURRENT_HEAD"
+               git read-tree "$GIT_NOTES_REF" || die "Could not read index"
+       fi
+
+       if [ -z "$MESSAGE" ]; then
+               GIT_NOTES_REF= git log -1 $COMMIT | sed "s/^/#/" > "$MSG_FILE"
+               if [ ! -z "$CURRENT_HEAD" ]; then
+                       git cat-file blob :$COMMIT >> "$MSG_FILE" 2> /dev/null
+               fi
+               core_editor="$(git config core.editor)"
+               ${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} "$MSG_FILE"
+       else
+               echo "$MESSAGE" > "$MSG_FILE"
+       fi
+
+       grep -v ^# < "$MSG_FILE" | git stripspace > "$MSG_FILE".processed
+       mv "$MSG_FILE".processed "$MSG_FILE"
+       if [ -s "$MSG_FILE" ]; then
+               BLOB=$(git hash-object -w "$MSG_FILE") ||
+                       die "Could not write into object database"
+               git update-index --add --cacheinfo 0644 $BLOB $COMMIT ||
+                       die "Could not write index"
+       else
+               test -z "$CURRENT_HEAD" &&
+                       die "Will not initialise with empty tree"
+               git update-index --force-remove $COMMIT ||
+                       die "Could not update index"
+       fi
+
+       TREE=$(git write-tree) || die "Could not write tree"
+       NEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) ||
+               die "Could not annotate"
+       git update-ref -m "Annotate $COMMIT" \
+               "$GIT_NOTES_REF" $NEW_HEAD $CURRENT_HEAD
+;;
+show)
+       git rev-parse -q --verify "$GIT_NOTES_REF":$COMMIT > /dev/null ||
+               die "No note for commit $COMMIT."
+       git show "$GIT_NOTES_REF":$COMMIT
+;;
+*)
+       usage
+esac
index fc78592ae04283db2af633ec080f99c6404ceda7..bfeb4a0ff65fabaa49b212411ee2451cbf656f3d 100755 (executable)
@@ -16,7 +16,8 @@ cd_to_toplevel
 test -z "$(git ls-files -u)" ||
        die "You are in the middle of a conflicted merge."
 
-strategy_args= diffstat= no_commit= squash= no_ff= log_arg= verbosity=
+strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
+log_arg= verbosity=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
@@ -45,6 +46,8 @@ do
                no_ff=--ff ;;
        --no-ff)
                no_ff=--no-ff ;;
+       --ff-only)
+               ff_only=--ff-only ;;
        -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
                --strateg=*|--strategy=*|\
        -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
@@ -171,7 +174,7 @@ then
        # First update the working tree to match $curr_head.
 
        echo >&2 "Warning: fetch updated the current branch head."
-       echo >&2 "Warning: fast forwarding your working tree from"
+       echo >&2 "Warning: fast-forwarding your working tree from"
        echo >&2 "Warning: commit $orig_head."
        git update-index -q --refresh
        git read-tree -u -m "$orig_head" "$curr_head" ||
@@ -215,5 +218,5 @@ merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 test true = "$rebase" &&
        exec git-rebase $diffstat $strategy_args --onto $merge_head \
        ${oldremoteref:-$merge_head}
-exec git-merge $diffstat $no_commit $squash $no_ff $log_arg $strategy_args \
+exec git-merge $diffstat $no_commit $squash $no_ff $ff_only $log_arg $strategy_args \
        "$merge_name" HEAD $merge_head $verbosity
index 53ad248ee505091d8e11cb9eb82ebb9b017b4262..0bd3bf78b8563f29a24dbc7dcc4ef308b3381157 100755 (executable)
@@ -106,8 +106,8 @@ mark_action_done () {
        sed -e 1q < "$TODO" >> "$DONE"
        sed -e 1d < "$TODO" >> "$TODO".new
        mv -f "$TODO".new "$TODO"
-       count=$(grep -c '^[^#]' < "$DONE")
-       total=$(($count+$(grep -c '^[^#]' < "$TODO")))
+       count=$(sane_grep -c '^[^#]' < "$DONE")
+       total=$(($count+$(sane_grep -c '^[^#]' < "$TODO")))
        if test "$last_count" != "$count"
        then
                last_count=$count
@@ -147,7 +147,7 @@ die_abort () {
 }
 
 has_action () {
-       grep '^[^#]' "$1" >/dev/null
+       sane_grep '^[^#]' "$1" >/dev/null
 }
 
 pick_one () {
@@ -168,7 +168,7 @@ pick_one () {
                output git reset --hard $sha1
                test "a$1" = a-n && output git reset --soft $current_sha1
                sha1=$(git rev-parse --short $sha1)
-               output warn Fast forward to $sha1
+               output warn Fast-forward to $sha1
        else
                output git cherry-pick "$@"
        fi
@@ -248,9 +248,9 @@ pick_one_preserving_merges () {
        done
        case $fast_forward in
        t)
-               output warn "Fast forward to $sha1"
+               output warn "Fast-forward to $sha1"
                output git reset --hard $sha1 ||
-                       die "Cannot fast forward to $sha1"
+                       die "Cannot fast-forward to $sha1"
                ;;
        f)
                first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
@@ -744,7 +744,7 @@ first and then run 'git rebase --continue' again."
                        git rev-list $REVISIONS |
                        while read rev
                        do
-                               if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = ""
+                               if test -f "$REWRITTEN"/$rev -a "$(sane_grep "$rev" "$DOTEST"/not-cherry-picks)" = ""
                                then
                                        # Use -f2 because if rev-list is telling us this commit is
                                        # not worthwhile, we don't want to track its multiple heads,
@@ -752,7 +752,7 @@ first and then run 'git rebase --continue' again."
                                        # be rebasing on top of it
                                        git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$DROPPED"/$rev
                                        short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev)
-                                       grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO"
+                                       sane_grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO"
                                        rm "$REWRITTEN"/$rev
                                fi
                        done
index 6ec155cf03c98f2e075f298039037d0e099311b1..b121f4537ccb173d9f289734f1fe2e89b28d7562 100755 (executable)
@@ -467,7 +467,7 @@ orig_head=$branch
 mb=$(git merge-base "$onto" "$branch")
 if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
        # linear history?
-       ! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null
+       ! (git rev-list --parents "$onto".."$branch" | sane_grep " .* ") > /dev/null
 then
        if test -z "$force_rebase"
        then
@@ -496,7 +496,7 @@ then
 fi
 
 # If the $onto is a proper descendant of the tip of the branch, then
-# we just fast forwarded.
+# we just fast-forwarded.
 if test "$mb" = "$branch"
 then
        say "Fast-forwarded $branch_name to $onto_name."
index a0279de687064c762a4ee24dfe2ed1922afab53a..319b5356713b02a48508ee9af2d14781d8760e94 100755 (executable)
@@ -162,7 +162,8 @@ sub format_2822_time {
 
 # Handle interactive edition of files.
 my $multiedit;
-my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+my $editor = Git::command_oneline('var', 'GIT_EDITOR');
+
 sub do_edit {
        if (defined($multiedit) && !$multiedit) {
                map {
@@ -186,9 +187,11 @@ sub do_edit {
 my ($validate, $confirm);
 my (@suppress_cc);
 
+my $not_set_by_user = "true but not set by the user";
+
 my %config_bool_settings = (
     "thread" => [\$thread, 1],
-    "chainreplyto" => [\$chain_reply_to, 1],
+    "chainreplyto" => [\$chain_reply_to, $not_set_by_user],
     "suppressfrom" => [\$suppress_from, undef],
     "signedoffbycc" => [\$signed_off_by_cc, undef],
     "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
@@ -213,6 +216,19 @@ sub do_edit {
     "from" => \$sender,
 );
 
+# Help users prepare for 1.7.0
+sub chain_reply_to {
+       if (defined $chain_reply_to &&
+           $chain_reply_to eq $not_set_by_user) {
+               print STDERR
+                   "In git 1.7.0, the default will be changed to --no-chain-reply-to\n" .
+                   "Set sendemail.chainreplyto configuration variable to true if\n" .
+                   "you want to keep --chain-reply-to as your default.\n";
+               $chain_reply_to = 1;
+       }
+       return $chain_reply_to;
+}
+
 # Handle Uncouth Termination
 sub signal_handler {
 
@@ -861,7 +877,9 @@ sub send_message
 
        my @sendmail_parameters = ('-i', @recipients);
        my $raw_from = $sanitized_sender;
-       $raw_from = $envelope_sender if (defined $envelope_sender);
+       if (defined $envelope_sender && $envelope_sender ne "auto") {
+               $raw_from = $envelope_sender;
+       }
        $raw_from = extract_valid_address($raw_from);
        unshift (@sendmail_parameters,
                        '-f', $raw_from) if(defined $envelope_sender);
@@ -1156,7 +1174,7 @@ sub send_message
 
        # set up for the next message
        if ($thread && $message_was_sent &&
-               ($chain_reply_to || !defined $reply_to || length($reply_to) == 0)) {
+               (chain_reply_to() || !defined $reply_to || length($reply_to) == 0)) {
                $reply_to = $message_id;
                if (length $references > 0) {
                        $references .= "\n $message_id";
index c41c2f7439724adc3dd13b92c86b25d254fc23b8..dfcb8078f508d6cc26d312d61b64e0185e90b74b 100755 (executable)
@@ -99,19 +99,20 @@ set_reflog_action() {
 }
 
 git_editor() {
-       : "${GIT_EDITOR:=$(git config core.editor)}"
-       : "${GIT_EDITOR:=${VISUAL:-${EDITOR}}}"
-       case "$GIT_EDITOR,$TERM" in
-       ,dumb)
-               echo >&2 "No editor specified in GIT_EDITOR, core.editor, VISUAL,"
-               echo >&2 "or EDITOR. Tried to fall back to vi but terminal is dumb."
-               echo >&2 "Please set one of these variables to an appropriate"
-               echo >&2 "editor or run $0 with options that will not cause an"
-               echo >&2 "editor to be invoked (e.g., -m or -F for git-commit)."
-               exit 1
-               ;;
-       esac
-       eval "${GIT_EDITOR:=vi}" '"$@"'
+       if test -z "${GIT_EDITOR:+set}"
+       then
+               GIT_EDITOR="$(git var GIT_EDITOR)" || return $?
+       fi
+
+       eval "$GIT_EDITOR" '"$@"'
+}
+
+sane_grep () {
+       GREP_OPTIONS= LC_ALL=C grep "$@"
+}
+
+sane_egrep () {
+       GREP_OPTIONS= LC_ALL=C egrep "$@"
 }
 
 is_bare_repository () {
index 0462e529d934750fb63a5a897f135d60ac674b5e..77d223292c5b167a8a33b04b10b46426b68e0576 100755 (executable)
@@ -5,7 +5,7 @@
 # Copyright (c) 2007 Lars Hjemli
 
 dashless=$(basename "$0" | sed -e 's/-/ /')
-USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> <path>
+USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> [<path>]
    or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
    or: $dashless [--quiet] init [--] [<path>...]
    or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
@@ -57,7 +57,7 @@ resolve_relative_url ()
 #
 module_list()
 {
-       git ls-files --error-unmatch --stage -- "$@" | grep '^160000 '
+       git ls-files --error-unmatch --stage -- "$@" | sane_grep '^160000 '
 }
 
 #
@@ -160,6 +160,11 @@ cmd_add()
        repo=$1
        path=$2
 
+       if test -z "$path"; then
+               path=$(echo "$repo" |
+                       sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
+       fi
+
        if test -z "$repo" -o -z "$path"; then
                usage
        fi
@@ -567,7 +572,7 @@ cmd_summary() {
        cd_to_toplevel
        # Get modified modules cared by user
        modules=$(git $diff_cmd $cached --raw $head -- "$@" |
-               egrep '^:([0-7]* )?160000' |
+               sane_egrep '^:([0-7]* )?160000' |
                while read mod_src mod_dst sha1_src sha1_dst status name
                do
                        # Always show modules deleted or type-changed (blob<->module)
@@ -581,7 +586,7 @@ cmd_summary() {
        test -z "$modules" && return
 
        git $diff_cmd $cached --raw $head -- $modules |
-       egrep '^:([0-7]* )?160000' |
+       sane_egrep '^:([0-7]* )?160000' |
        cut -c2- |
        while read mod_src mod_dst sha1_src sha1_dst status name
        do
index 6a3b501d24ebaa50e297511ccf0b18e3bbd2bb16..957d44e63004cef131fca5c7dad75e7049d15842 100755 (executable)
@@ -168,6 +168,9 @@ BEGIN
                             'Create a .gitignore per svn:ignore',
                             { 'revision|r=i' => \$_revision
                             } ],
+       'mkdirs' => [ \&cmd_mkdirs ,
+                     "recreate empty directories after a checkout",
+                     { 'revision|r=i' => \$_revision } ],
         'propget' => [ \&cmd_propget,
                       'Print the value of a property on a file or directory',
                       { 'revision|r=i' => \$_revision } ],
@@ -274,7 +277,7 @@ BEGIN
 
 my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
 
-read_repo_config(\%opts);
+read_git_config(\%opts);
 if ($cmd && ($cmd eq 'log' || $cmd eq 'blame')) {
        Getopt::Long::Configure('pass_through');
 }
@@ -425,6 +428,7 @@ sub cmd_fetch {
        if (@_ > 1) {
                die "Usage: $0 fetch [--all] [--parent] [svn-remote]\n";
        }
+       $Git::SVN::no_reuse_existing = undef;
        if ($_fetch_parent) {
                my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
                unless ($gs) {
@@ -769,6 +773,7 @@ sub cmd_rebase {
                $_fetch_all ? $gs->fetch_all : $gs->fetch;
        }
        command_noisy(rebase_cmd(), $gs->refname);
+       $gs->mkemptydirs;
 }
 
 sub cmd_show_ignore {
@@ -830,6 +835,12 @@ sub cmd_create_ignore {
        });
 }
 
+sub cmd_mkdirs {
+       my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+       $gs ||= Git::SVN->new;
+       $gs->mkemptydirs($_revision);
+}
+
 sub canonicalize_path {
        my ($path) = @_;
        my $dot_slash_added = 0;
@@ -946,6 +957,7 @@ sub cmd_multi_init {
 }
 
 sub cmd_multi_fetch {
+       $Git::SVN::no_reuse_existing = undef;
        my $remotes = Git::SVN::read_all_remotes();
        foreach my $repo_id (sort keys %$remotes) {
                if ($remotes->{$repo_id}->{url}) {
@@ -1196,6 +1208,7 @@ sub post_fetch_checkout {
        command_noisy(qw/read-tree -m -u -v HEAD HEAD/);
        print STDERR "Checked out HEAD:\n  ",
                     $gs->full_url, " r", $gs->last_rev, "\n";
+       $gs->mkemptydirs($gs->last_rev);
 }
 
 sub complete_svn_url {
@@ -1321,9 +1334,8 @@ sub get_commit_entry {
        close $log_fh or croak $!;
 
        if ($_edit || ($type eq 'tree')) {
-               my $editor = $ENV{VISUAL} || $ENV{EDITOR} || 'vi';
-               # TODO: strip out spaces, comments, like git-commit.sh
-               system($editor, $commit_editmsg);
+               chomp(my $editor = command_oneline(qw(var GIT_EDITOR)));
+               system('sh', '-c', $editor.' "$@"', $editor, $commit_editmsg);
        }
        rename $commit_editmsg, $commit_msg or croak $!;
        {
@@ -1390,8 +1402,7 @@ sub load_authors {
 }
 
 # convert GetOpt::Long specs for use by git-config
-sub read_repo_config {
-       return unless -d $ENV{GIT_DIR};
+sub read_git_config {
        my $opts = shift;
        my @config_only;
        foreach my $o (keys %$opts) {
@@ -2725,6 +2736,37 @@ sub do_fetch {
        $self->make_log_entry($rev, \@parents, $ed);
 }
 
+sub mkemptydirs {
+       my ($self, $r) = @_;
+       my %empty_dirs = ();
+
+       open my $fh, '<', "$self->{dir}/unhandled.log" or return;
+       binmode $fh or croak "binmode: $!";
+       while (<$fh>) {
+               if (defined $r && /^r(\d+)$/) {
+                       last if $1 > $r;
+               } elsif (/^  \+empty_dir: (.+)$/) {
+                       $empty_dirs{$1} = 1;
+               } elsif (/^  \-empty_dir: (.+)$/) {
+                       delete $empty_dirs{$1};
+               }
+       }
+       close $fh;
+
+       my $strip = qr/\A\Q$self->{path}\E(?:\/|$)/;
+       foreach my $d (sort keys %empty_dirs) {
+               $d = uri_decode($d);
+               $d =~ s/$strip//;
+               next if -d $d;
+               if (-e _) {
+                       warn "$d exists but is not a directory\n";
+               } else {
+                       print "creating empty directory: $d\n";
+                       mkpath([$d]);
+               }
+       }
+}
+
 sub get_untracked {
        my ($self, $ed) = @_;
        my @out;
@@ -2950,8 +2992,11 @@ sub find_extra_svn_parents {
                        my $bottom_commit =
                                $gs->rev_map_get($bottom, $self->ra_uuid) ||
                                $gs->rev_map_get($bottom+1, $self->ra_uuid);
-                       my $top_commit =
-                               $gs->rev_map_get($top, $self->ra_uuid);
+                       my $top_commit;
+                       for (; !$top_commit && $top >= $bottom; --$top) {
+                               $top_commit =
+                                       $gs->rev_map_get($top, $self->ra_uuid);
+                       }
 
                        unless ($top_commit and $bottom_commit) {
                                warn "W:unknown path/rev in svn:mergeinfo "
@@ -3554,6 +3599,12 @@ sub uri_encode {
        $f
 }
 
+sub uri_decode {
+       my ($f) = @_;
+       $f =~ s#%([0-9a-fA-F]{2})#chr(hex($1))#eg;
+       $f
+}
+
 sub remove_username {
        $_[0] =~ s{^([^:]*://)[^@]+@}{$1};
 }
@@ -5172,10 +5223,8 @@ sub git_svn_log_cmd {
 
 # adapted from pager.c
 sub config_pager {
-       $pager ||= $ENV{GIT_PAGER} || $ENV{PAGER};
-       if (!defined $pager) {
-               $pager = 'less';
-       } elsif (length $pager == 0 || $pager eq 'cat') {
+       chomp(my $pager = command_oneline(qw(var GIT_PAGER)));
+       if ($pager eq 'cat') {
                $pager = undef;
        }
        $ENV{GIT_PAGER_IN_USE} = defined($pager);
diff --git a/git.c b/git.c
index bd2c5fe77b41f314af21df703364361b1601f3d1..11544cdb4037715b1e9edc14689896d99d6e3284 100644 (file)
--- a/git.c
+++ b/git.c
@@ -89,6 +89,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--no-replace-objects")) {
                        read_replace_refs = 0;
+                       setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
+                       if (envchanged)
+                               *envchanged = 1;
                } else if (!strcmp(cmd, "--git-dir")) {
                        if (*argc < 2) {
                                fprintf(stderr, "No directory given for --git-dir.\n" );
@@ -229,21 +232,24 @@ struct cmd_struct {
 
 static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 {
-       int status;
+       int status, help;
        struct stat st;
        const char *prefix;
 
        prefix = NULL;
-       if (p->option & RUN_SETUP)
-               prefix = setup_git_directory();
-
-       if (use_pager == -1 && p->option & RUN_SETUP)
-               use_pager = check_pager_config(p->cmd);
-       if (use_pager == -1 && p->option & USE_PAGER)
-               use_pager = 1;
+       help = argc == 2 && !strcmp(argv[1], "-h");
+       if (!help) {
+               if (p->option & RUN_SETUP)
+                       prefix = setup_git_directory();
+
+               if (use_pager == -1 && p->option & RUN_SETUP)
+                       use_pager = check_pager_config(p->cmd);
+               if (use_pager == -1 && p->option & USE_PAGER)
+                       use_pager = 1;
+       }
        commit_pager_choice();
 
-       if (p->option & NEED_WORK_TREE)
+       if (!help && p->option & NEED_WORK_TREE)
                setup_work_tree();
 
        trace_argv_printf(argv, "trace: built-in: git");
@@ -304,7 +310,6 @@ static void handle_internal_command(int argc, const char **argv)
                { "fast-export", cmd_fast_export, RUN_SETUP },
                { "fetch", cmd_fetch, RUN_SETUP },
                { "fetch-pack", cmd_fetch_pack, RUN_SETUP },
-               { "fetch--tool", cmd_fetch__tool, RUN_SETUP },
                { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
                { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
                { "format-patch", cmd_format_patch, RUN_SETUP },
index e4cbfc35a76f761dd6ad3957956d7624568047d0..f94536c680c05932595f0766fde1d87b5a8f2162 100755 (executable)
@@ -2020,16 +2020,27 @@ sub quote_command {
 
 # get HEAD ref of given project as hash
 sub git_get_head_hash {
-       my $project = shift;
+       return git_get_full_hash(shift, 'HEAD');
+}
+
+sub git_get_full_hash {
+       return git_get_hash(@_);
+}
+
+sub git_get_short_hash {
+       return git_get_hash(@_, '--short=7');
+}
+
+sub git_get_hash {
+       my ($project, $hash, @options) = @_;
        my $o_git_dir = $git_dir;
        my $retval = undef;
        $git_dir = "$projectroot/$project";
-       if (open my $fd, "-|", git_cmd(), "rev-parse", "--verify", "HEAD") {
-               my $head = <$fd>;
+       if (open my $fd, '-|', git_cmd(), 'rev-parse',
+           '--verify', '-q', @options, $hash) {
+               $retval = <$fd>;
+               chomp $retval if defined $retval;
                close $fd;
-               if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) {
-                       $retval = $1;
-               }
        }
        if (defined $o_git_dir) {
                $git_dir = $o_git_dir;
@@ -3363,22 +3374,18 @@ sub git_print_page_nav {
 }
 
 sub format_paging_nav {
-       my ($action, $hash, $head, $page, $has_next_link) = @_;
+       my ($action, $page, $has_next_link) = @_;
        my $paging_nav;
 
 
-       if ($hash ne $head || $page) {
-               $paging_nav .= $cgi->a({-href => href(action=>$action)}, "HEAD");
-       } else {
-               $paging_nav .= "HEAD";
-       }
-
        if ($page > 0) {
-               $paging_nav .= " &sdot; " .
+               $paging_nav .=
+                       $cgi->a({-href => href(-replay=>1, page=>undef)}, "first") .
+                       " &sdot; " .
                        $cgi->a({-href => href(-replay=>1, page=>$page-1),
                                 -accesskey => "p", -title => "Alt-p"}, "prev");
        } else {
-               $paging_nav .= " &sdot; prev";
+               $paging_nav .= "first &sdot; prev";
        }
 
        if ($has_next_link) {
@@ -4361,6 +4368,46 @@ sub git_project_list_body {
        print "</table>\n";
 }
 
+sub git_log_body {
+       # uses global variable $project
+       my ($commitlist, $from, $to, $refs, $extra) = @_;
+
+       $from = 0 unless defined $from;
+       $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
+
+       for (my $i = 0; $i <= $to; $i++) {
+               my %co = %{$commitlist->[$i]};
+               next if !%co;
+               my $commit = $co{'id'};
+               my $ref = format_ref_marker($refs, $commit);
+               my %ad = parse_date($co{'author_epoch'});
+               git_print_header_div('commit',
+                              "<span class=\"age\">$co{'age_string'}</span>" .
+                              esc_html($co{'title'}) . $ref,
+                              $commit);
+               print "<div class=\"title_text\">\n" .
+                     "<div class=\"log_link\">\n" .
+                     $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") .
+                     " | " .
+                     $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
+                     " | " .
+                     $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
+                     "<br/>\n" .
+                     "</div>\n";
+                     git_print_authorship(\%co, -tag => 'span');
+                     print "<br/>\n</div>\n";
+
+               print "<div class=\"log_body\">\n";
+               git_print_log($co{'comment'}, -final_empty_line=> 1);
+               print "</div>\n";
+       }
+       if ($extra) {
+               print "<div class=\"page_nav\">\n";
+               print "$extra\n";
+               print "</div>\n";
+       }
+}
+
 sub git_shortlog_body {
        # uses global variable $project
        my ($commitlist, $from, $to, $refs, $extra) = @_;
@@ -4407,7 +4454,8 @@ sub git_shortlog_body {
 
 sub git_history_body {
        # Warning: assumes constant type (blob or tree) during history
-       my ($commitlist, $from, $to, $refs, $hash_base, $ftype, $extra) = @_;
+       my ($commitlist, $from, $to, $refs, $extra,
+           $file_name, $file_hash, $ftype) = @_;
 
        $from = 0 unless defined $from;
        $to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist});
@@ -4441,7 +4489,7 @@ sub git_history_body {
                      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
 
                if ($ftype eq 'blob') {
-                       my $blob_current = git_get_hash_by_path($hash_base, $file_name);
+                       my $blob_current = $file_hash;
                        my $blob_parent  = git_get_hash_by_path($commit, $file_name);
                        if (defined $blob_current && defined $blob_parent &&
                                        $blob_current ne $blob_parent) {
@@ -5127,7 +5175,8 @@ sub git_blob {
                        chomp $line;
                        $nr++;
                        $line = untabify($line);
-                       printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
+                       printf "<div class=\"pre\"><a id=\"l%i\" href=\"" . href(-replay => 1)
+                               . "#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
                               $nr, $nr, $nr, esc_html($line, -nbsp=>1);
                }
        }
@@ -5247,6 +5296,43 @@ sub git_tree {
        git_footer_html();
 }
 
+sub snapshot_name {
+       my ($project, $hash) = @_;
+
+       # path/to/project.git  -> project
+       # path/to/project/.git -> project
+       my $name = to_utf8($project);
+       $name =~ s,([^/])/*\.git$,$1,;
+       $name = basename($name);
+       # sanitize name
+       $name =~ s/[[:cntrl:]]/?/g;
+
+       my $ver = $hash;
+       if ($hash =~ /^[0-9a-fA-F]+$/) {
+               # shorten SHA-1 hash
+               my $full_hash = git_get_full_hash($project, $hash);
+               if ($full_hash =~ /^$hash/ && length($hash) > 7) {
+                       $ver = git_get_short_hash($project, $hash);
+               }
+       } elsif ($hash =~ m!^refs/tags/(.*)$!) {
+               # tags don't need shortened SHA-1 hash
+               $ver = $1;
+       } else {
+               # branches and other need shortened SHA-1 hash
+               if ($hash =~ m!^refs/(?:heads|remotes)/(.*)$!) {
+                       $ver = $1;
+               }
+               $ver .= '-' . git_get_short_hash($project, $hash);
+       }
+       # in case of hierarchical branch names
+       $ver =~ s!/!.!g;
+
+       # name = project-version_string
+       $name = "$name-$ver";
+
+       return wantarray ? ($name, $name) : $name;
+}
+
 sub git_snapshot {
        my $format = $input_params{'snapshot_format'};
        if (!@snapshot_fmts) {
@@ -5264,28 +5350,27 @@ sub git_snapshot {
                die_error(403, "Unsupported snapshot format");
        }
 
-       if (!defined $hash) {
-               $hash = git_get_head_hash($project);
+       my $type = git_get_type("$hash^{}");
+       if (!$type) {
+               die_error(404, 'Object does not exist');
+       }  elsif ($type eq 'blob') {
+               die_error(400, 'Object is not a tree-ish');
        }
 
-       my $name = $project;
-       $name =~ s,([^/])/*\.git$,$1,;
-       $name = basename($name);
-       my $filename = to_utf8($name);
-       $name =~ s/\047/\047\\\047\047/g;
-       my $cmd;
-       $filename .= "-$hash$known_snapshot_formats{$format}{'suffix'}";
-       $cmd = quote_command(
+       my ($name, $prefix) = snapshot_name($project, $hash);
+       my $filename = "$name$known_snapshot_formats{$format}{'suffix'}";
+       my $cmd = quote_command(
                git_cmd(), 'archive',
                "--format=$known_snapshot_formats{$format}{'format'}",
-               "--prefix=$name/", $hash);
+               "--prefix=$prefix/", $hash);
        if (exists $known_snapshot_formats{$format}{'compressor'}) {
                $cmd .= ' | ' . quote_command(@{$known_snapshot_formats{$format}{'compressor'}});
        }
 
+       $filename =~ s/(["\\])/\\$1/g;
        print $cgi->header(
                -type => $known_snapshot_formats{$format}{'type'},
-               -content_disposition => 'inline; filename="' . "$filename" . '"',
+               -content_disposition => 'inline; filename="' . $filename . '"',
                -status => '200 OK');
 
        open my $fd, "-|", $cmd
@@ -5296,22 +5381,57 @@ sub git_snapshot {
        close $fd;
 }
 
-sub git_log {
+sub git_log_generic {
+       my ($fmt_name, $body_subr, $base, $parent, $file_name, $file_hash) = @_;
+
        my $head = git_get_head_hash($project);
-       if (!defined $hash) {
-               $hash = $head;
+       if (!defined $base) {
+               $base = $head;
        }
        if (!defined $page) {
                $page = 0;
        }
        my $refs = git_get_references();
 
-       my @commitlist = parse_commits($hash, 101, (100 * $page));
+       my $commit_hash = $base;
+       if (defined $parent) {
+               $commit_hash = "$parent..$base";
+       }
+       my @commitlist =
+               parse_commits($commit_hash, 101, (100 * $page),
+                             defined $file_name ? ($file_name, "--full-history") : ());
 
-       my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#commitlist >= 100);
+       my $ftype;
+       if (!defined $file_hash && defined $file_name) {
+               # some commits could have deleted file in question,
+               # and not have it in tree, but one of them has to have it
+               for (my $i = 0; $i < @commitlist; $i++) {
+                       $file_hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name);
+                       last if defined $file_hash;
+               }
+       }
+       if (defined $file_hash) {
+               $ftype = git_get_type($file_hash);
+       }
+       if (defined $file_name && !defined $ftype) {
+               die_error(500, "Unknown type of object");
+       }
+       my %co;
+       if (defined $file_name) {
+               %co = parse_commit($base)
+                       or die_error(404, "Unknown commit object");
+       }
 
-       my ($patch_max) = gitweb_get_feature('patches');
-       if ($patch_max) {
+
+       my $paging_nav = format_paging_nav($fmt_name, $page, $#commitlist >= 100);
+       my $next_link = '';
+       if ($#commitlist >= 100) {
+               $next_link =
+                       $cgi->a({-href => href(-replay=>1, page=>$page+1),
+                                -accesskey => "n", -title => "Alt-n"}, "next");
+       }
+       my $patch_max = gitweb_get_feature('patches');
+       if ($patch_max && !defined $file_name) {
                if ($patch_max < 0 || @commitlist <= $patch_max) {
                        $paging_nav .= " &sdot; " .
                                $cgi->a({-href => href(action=>"patches", -replay=>1)},
@@ -5320,50 +5440,26 @@ sub git_log {
        }
 
        git_header_html();
-       git_print_page_nav('log','', $hash,undef,undef, $paging_nav);
-
-       if (!@commitlist) {
-               my %co = parse_commit($hash);
-
-               git_print_header_div('summary', $project);
-               print "<div class=\"page_body\"> Last change $co{'age_string'}.<br/><br/></div>\n";
+       git_print_page_nav($fmt_name,'', $hash,$hash,$hash, $paging_nav);
+       if (defined $file_name) {
+               git_print_header_div('commit', esc_html($co{'title'}), $base);
+       } else {
+               git_print_header_div('summary', $project)
        }
-       my $to = ($#commitlist >= 99) ? (99) : ($#commitlist);
-       for (my $i = 0; $i <= $to; $i++) {
-               my %co = %{$commitlist[$i]};
-               next if !%co;
-               my $commit = $co{'id'};
-               my $ref = format_ref_marker($refs, $commit);
-               my %ad = parse_date($co{'author_epoch'});
-               git_print_header_div('commit',
-                              "<span class=\"age\">$co{'age_string'}</span>" .
-                              esc_html($co{'title'}) . $ref,
-                              $commit);
-               print "<div class=\"title_text\">\n" .
-                     "<div class=\"log_link\">\n" .
-                     $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") .
-                     " | " .
-                     $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
-                     " | " .
-                     $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
-                     "<br/>\n" .
-                     "</div>\n";
-                     git_print_authorship(\%co, -tag => 'span');
-                     print "<br/>\n</div>\n";
+       git_print_page_path($file_name, $ftype, $hash_base)
+               if (defined $file_name);
+
+       $body_subr->(\@commitlist, 0, 99, $refs, $next_link,
+                    $file_name, $file_hash, $ftype);
 
-               print "<div class=\"log_body\">\n";
-               git_print_log($co{'comment'}, -final_empty_line=> 1);
-               print "</div>\n";
-       }
-       if ($#commitlist >= 100) {
-               print "<div class=\"page_nav\">\n";
-               print $cgi->a({-href => href(-replay=>1, page=>$page+1),
-                              -accesskey => "n", -title => "Alt-n"}, "next");
-               print "</div>\n";
-       }
        git_footer_html();
 }
 
+sub git_log {
+       git_log_generic('log', \&git_log_body,
+                       $hash, $hash_parent);
+}
+
 sub git_commit {
        $hash ||= $hash_base || "HEAD";
        my %co = parse_commit($hash)
@@ -5900,70 +5996,9 @@ sub git_patches {
 }
 
 sub git_history {
-       if (!defined $hash_base) {
-               $hash_base = git_get_head_hash($project);
-       }
-       if (!defined $page) {
-               $page = 0;
-       }
-       my $ftype;
-       my %co = parse_commit($hash_base)
-           or die_error(404, "Unknown commit object");
-
-       my $refs = git_get_references();
-       my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-
-       my @commitlist = parse_commits($hash_base, 101, (100 * $page),
-                                      $file_name, "--full-history")
-           or die_error(404, "No such file or directory on given branch");
-
-       if (!defined $hash && defined $file_name) {
-               # some commits could have deleted file in question,
-               # and not have it in tree, but one of them has to have it
-               for (my $i = 0; $i <= @commitlist; $i++) {
-                       $hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name);
-                       last if defined $hash;
-               }
-       }
-       if (defined $hash) {
-               $ftype = git_get_type($hash);
-       }
-       if (!defined $ftype) {
-               die_error(500, "Unknown type of object");
-       }
-
-       my $paging_nav = '';
-       if ($page > 0) {
-               $paging_nav .=
-                       $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base,
-                                              file_name=>$file_name)},
-                               "first");
-               $paging_nav .= " &sdot; " .
-                       $cgi->a({-href => href(-replay=>1, page=>$page-1),
-                                -accesskey => "p", -title => "Alt-p"}, "prev");
-       } else {
-               $paging_nav .= "first";
-               $paging_nav .= " &sdot; prev";
-       }
-       my $next_link = '';
-       if ($#commitlist >= 100) {
-               $next_link =
-                       $cgi->a({-href => href(-replay=>1, page=>$page+1),
-                                -accesskey => "n", -title => "Alt-n"}, "next");
-               $paging_nav .= " &sdot; $next_link";
-       } else {
-               $paging_nav .= " &sdot; next";
-       }
-
-       git_header_html();
-       git_print_page_nav('history','', $hash_base,$co{'tree'},$hash_base, $paging_nav);
-       git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
-       git_print_page_path($file_name, $ftype, $hash_base);
-
-       git_history_body(\@commitlist, 0, 99,
-                        $refs, $hash_base, $ftype, $next_link);
-
-       git_footer_html();
+       git_log_generic('history', \&git_history_body,
+                       $hash_base, $hash_parent_base,
+                       $file_name, $hash);
 }
 
 sub git_search {
@@ -6227,44 +6262,8 @@ sub git_search_help {
 }
 
 sub git_shortlog {
-       my $head = git_get_head_hash($project);
-       if (!defined $hash) {
-               $hash = $head;
-       }
-       if (!defined $page) {
-               $page = 0;
-       }
-       my $refs = git_get_references();
-
-       my $commit_hash = $hash;
-       if (defined $hash_parent) {
-               $commit_hash = "$hash_parent..$hash";
-       }
-       my @commitlist = parse_commits($commit_hash, 101, (100 * $page));
-
-       my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, $#commitlist >= 100);
-       my $next_link = '';
-       if ($#commitlist >= 100) {
-               $next_link =
-                       $cgi->a({-href => href(-replay=>1, page=>$page+1),
-                                -accesskey => "n", -title => "Alt-n"}, "next");
-       }
-       my $patch_max = gitweb_check_feature('patches');
-       if ($patch_max) {
-               if ($patch_max < 0 || @commitlist <= $patch_max) {
-                       $paging_nav .= " &sdot; " .
-                               $cgi->a({-href => href(action=>"patches", -replay=>1)},
-                                       "patches");
-               }
-       }
-
-       git_header_html();
-       git_print_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav);
-       git_print_header_div('summary', $project);
-
-       git_shortlog_body(\@commitlist, 0, 99, $refs, $next_link);
-
-       git_footer_html();
+       git_log_generic('shortlog', \&git_shortlog_body,
+                       $hash, $hash_parent);
 }
 
 ## ......................................................................
diff --git a/graph.h b/graph.h
index bc30d687c0dbfc06d459948788784b3d8ab1b5ff..b82ae87a491454aa119476ac9609ad176e8c9406 100644 (file)
--- a/graph.h
+++ b/graph.h
@@ -6,7 +6,6 @@ struct git_graph;
 
 /*
  * Create a new struct git_graph.
- * The graph should be freed with graph_release() when no longer needed.
  */
 struct git_graph *graph_init(struct rev_info *opt);
 
diff --git a/grep.c b/grep.c
index 5d162dae6e43cdfcf6b20627df088a73cbfa98dc..bdadf2c0ccfafc18011beee3bc05754860392523 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -41,6 +41,7 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
        int err;
 
        p->word_regexp = opt->word_regexp;
+       p->ignore_case = opt->ignore_case;
 
        if (opt->fixed || is_fixed(p->pattern))
                p->fixed = 1;
@@ -262,9 +263,15 @@ static void show_name(struct grep_opt *opt, const char *name)
        printf("%s%c", name, opt->null_following_name ? '\0' : '\n');
 }
 
-static int fixmatch(const char *pattern, char *line, regmatch_t *match)
+
+static int fixmatch(const char *pattern, char *line, int ignore_case, regmatch_t *match)
 {
-       char *hit = strstr(line, pattern);
+       char *hit;
+       if (ignore_case)
+               hit = strcasestr(line, pattern);
+       else
+               hit = strstr(line, pattern);
+
        if (!hit) {
                match->rm_so = match->rm_eo = -1;
                return REG_NOMATCH;
@@ -326,7 +333,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
 
  again:
        if (p->fixed)
-               hit = !fixmatch(p->pattern, bol, pmatch);
+               hit = !fixmatch(p->pattern, bol, p->ignore_case, pmatch);
        else
                hit = !regexec(&p->regexp, bol, 1, pmatch, eflags);
 
diff --git a/grep.h b/grep.h
index f6eecc62c038f212fa4efc8648f13f61bf937452..75370f60d7091becd3eaddf3b7069c26fca8c125 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -32,6 +32,7 @@ struct grep_pat {
        enum grep_header_field field;
        regex_t regexp;
        unsigned fixed:1;
+       unsigned ignore_case:1;
        unsigned word_regexp:1;
 };
 
@@ -64,6 +65,7 @@ struct grep_opt {
        regex_t regexp;
        int linenum;
        int invert;
+       int ignore_case;
        int status_only;
        int name_only;
        int unmatch_name_only;
diff --git a/http-backend.c b/http-backend.c
new file mode 100644 (file)
index 0000000..f729488
--- /dev/null
@@ -0,0 +1,655 @@
+#include "cache.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include "object.h"
+#include "tag.h"
+#include "exec_cmd.h"
+#include "run-command.h"
+#include "string-list.h"
+
+static const char content_type[] = "Content-Type";
+static const char content_length[] = "Content-Length";
+static const char last_modified[] = "Last-Modified";
+static int getanyfile = 1;
+
+static struct string_list *query_params;
+
+struct rpc_service {
+       const char *name;
+       const char *config_name;
+       signed enabled : 2;
+};
+
+static struct rpc_service rpc_service[] = {
+       { "upload-pack", "uploadpack", 1 },
+       { "receive-pack", "receivepack", -1 },
+};
+
+static int decode_char(const char *q)
+{
+       int i;
+       unsigned char val = 0;
+       for (i = 0; i < 2; i++) {
+               unsigned char c = *q++;
+               val <<= 4;
+               if (c >= '0' && c <= '9')
+                       val += c - '0';
+               else if (c >= 'a' && c <= 'f')
+                       val += c - 'a' + 10;
+               else if (c >= 'A' && c <= 'F')
+                       val += c - 'A' + 10;
+               else
+                       return -1;
+       }
+       return val;
+}
+
+static char *decode_parameter(const char **query, int is_name)
+{
+       const char *q = *query;
+       struct strbuf out;
+
+       strbuf_init(&out, 16);
+       do {
+               unsigned char c = *q;
+
+               if (!c)
+                       break;
+               if (c == '&' || (is_name && c == '=')) {
+                       q++;
+                       break;
+               }
+
+               if (c == '%') {
+                       int val = decode_char(q + 1);
+                       if (0 <= val) {
+                               strbuf_addch(&out, val);
+                               q += 3;
+                               continue;
+                       }
+               }
+
+               if (c == '+')
+                       strbuf_addch(&out, ' ');
+               else
+                       strbuf_addch(&out, c);
+               q++;
+       } while (1);
+       *query = q;
+       return strbuf_detach(&out, NULL);
+}
+
+static struct string_list *get_parameters(void)
+{
+       if (!query_params) {
+               const char *query = getenv("QUERY_STRING");
+
+               query_params = xcalloc(1, sizeof(*query_params));
+               while (query && *query) {
+                       char *name = decode_parameter(&query, 1);
+                       char *value = decode_parameter(&query, 0);
+                       struct string_list_item *i;
+
+                       i = string_list_lookup(name, query_params);
+                       if (!i)
+                               i = string_list_insert(name, query_params);
+                       else
+                               free(i->util);
+                       i->util = value;
+               }
+       }
+       return query_params;
+}
+
+static const char *get_parameter(const char *name)
+{
+       struct string_list_item *i;
+       i = string_list_lookup(name, get_parameters());
+       return i ? i->util : NULL;
+}
+
+__attribute__((format (printf, 2, 3)))
+static void format_write(int fd, const char *fmt, ...)
+{
+       static char buffer[1024];
+
+       va_list args;
+       unsigned n;
+
+       va_start(args, fmt);
+       n = vsnprintf(buffer, sizeof(buffer), fmt, args);
+       va_end(args);
+       if (n >= sizeof(buffer))
+               die("protocol error: impossibly long line");
+
+       safe_write(fd, buffer, n);
+}
+
+static void http_status(unsigned code, const char *msg)
+{
+       format_write(1, "Status: %u %s\r\n", code, msg);
+}
+
+static void hdr_str(const char *name, const char *value)
+{
+       format_write(1, "%s: %s\r\n", name, value);
+}
+
+static void hdr_int(const char *name, uintmax_t value)
+{
+       format_write(1, "%s: %" PRIuMAX "\r\n", name, value);
+}
+
+static void hdr_date(const char *name, unsigned long when)
+{
+       const char *value = show_date(when, 0, DATE_RFC2822);
+       hdr_str(name, value);
+}
+
+static void hdr_nocache(void)
+{
+       hdr_str("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+       hdr_str("Pragma", "no-cache");
+       hdr_str("Cache-Control", "no-cache, max-age=0, must-revalidate");
+}
+
+static void hdr_cache_forever(void)
+{
+       unsigned long now = time(NULL);
+       hdr_date("Date", now);
+       hdr_date("Expires", now + 31536000);
+       hdr_str("Cache-Control", "public, max-age=31536000");
+}
+
+static void end_headers(void)
+{
+       safe_write(1, "\r\n", 2);
+}
+
+__attribute__((format (printf, 1, 2)))
+static NORETURN void not_found(const char *err, ...)
+{
+       va_list params;
+
+       http_status(404, "Not Found");
+       hdr_nocache();
+       end_headers();
+
+       va_start(params, err);
+       if (err && *err)
+               vfprintf(stderr, err, params);
+       va_end(params);
+       exit(0);
+}
+
+__attribute__((format (printf, 1, 2)))
+static NORETURN void forbidden(const char *err, ...)
+{
+       va_list params;
+
+       http_status(403, "Forbidden");
+       hdr_nocache();
+       end_headers();
+
+       va_start(params, err);
+       if (err && *err)
+               vfprintf(stderr, err, params);
+       va_end(params);
+       exit(0);
+}
+
+static void select_getanyfile(void)
+{
+       if (!getanyfile)
+               forbidden("Unsupported service: getanyfile");
+}
+
+static void send_strbuf(const char *type, struct strbuf *buf)
+{
+       hdr_int(content_length, buf->len);
+       hdr_str(content_type, type);
+       end_headers();
+       safe_write(1, buf->buf, buf->len);
+}
+
+static void send_local_file(const char *the_type, const char *name)
+{
+       const char *p = git_path("%s", name);
+       size_t buf_alloc = 8192;
+       char *buf = xmalloc(buf_alloc);
+       int fd;
+       struct stat sb;
+
+       fd = open(p, O_RDONLY);
+       if (fd < 0)
+               not_found("Cannot open '%s': %s", p, strerror(errno));
+       if (fstat(fd, &sb) < 0)
+               die_errno("Cannot stat '%s'", p);
+
+       hdr_int(content_length, sb.st_size);
+       hdr_str(content_type, the_type);
+       hdr_date(last_modified, sb.st_mtime);
+       end_headers();
+
+       for (;;) {
+               ssize_t n = xread(fd, buf, buf_alloc);
+               if (n < 0)
+                       die_errno("Cannot read '%s'", p);
+               if (!n)
+                       break;
+               safe_write(1, buf, n);
+       }
+       close(fd);
+       free(buf);
+}
+
+static void get_text_file(char *name)
+{
+       select_getanyfile();
+       hdr_nocache();
+       send_local_file("text/plain", name);
+}
+
+static void get_loose_object(char *name)
+{
+       select_getanyfile();
+       hdr_cache_forever();
+       send_local_file("application/x-git-loose-object", name);
+}
+
+static void get_pack_file(char *name)
+{
+       select_getanyfile();
+       hdr_cache_forever();
+       send_local_file("application/x-git-packed-objects", name);
+}
+
+static void get_idx_file(char *name)
+{
+       select_getanyfile();
+       hdr_cache_forever();
+       send_local_file("application/x-git-packed-objects-toc", name);
+}
+
+static int http_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "http.getanyfile")) {
+               getanyfile = git_config_bool(var, value);
+               return 0;
+       }
+
+       if (!prefixcmp(var, "http.")) {
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
+                       struct rpc_service *svc = &rpc_service[i];
+                       if (!strcmp(var + 5, svc->config_name)) {
+                               svc->enabled = git_config_bool(var, value);
+                               return 0;
+                       }
+               }
+       }
+
+       /* we are not interested in parsing any other configuration here */
+       return 0;
+}
+
+static struct rpc_service *select_service(const char *name)
+{
+       struct rpc_service *svc = NULL;
+       int i;
+
+       if (prefixcmp(name, "git-"))
+               forbidden("Unsupported service: '%s'", name);
+
+       for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
+               struct rpc_service *s = &rpc_service[i];
+               if (!strcmp(s->name, name + 4)) {
+                       svc = s;
+                       break;
+               }
+       }
+
+       if (!svc)
+               forbidden("Unsupported service: '%s'", name);
+
+       if (svc->enabled < 0) {
+               const char *user = getenv("REMOTE_USER");
+               svc->enabled = (user && *user) ? 1 : 0;
+       }
+       if (!svc->enabled)
+               forbidden("Service not enabled: '%s'", svc->name);
+       return svc;
+}
+
+static void inflate_request(const char *prog_name, int out)
+{
+       z_stream stream;
+       unsigned char in_buf[8192];
+       unsigned char out_buf[8192];
+       unsigned long cnt = 0;
+       int ret;
+
+       memset(&stream, 0, sizeof(stream));
+       ret = inflateInit2(&stream, (15 + 16));
+       if (ret != Z_OK)
+               die("cannot start zlib inflater, zlib err %d", ret);
+
+       while (1) {
+               ssize_t n = xread(0, in_buf, sizeof(in_buf));
+               if (n <= 0)
+                       die("request ended in the middle of the gzip stream");
+
+               stream.next_in = in_buf;
+               stream.avail_in = n;
+
+               while (0 < stream.avail_in) {
+                       int ret;
+
+                       stream.next_out = out_buf;
+                       stream.avail_out = sizeof(out_buf);
+
+                       ret = inflate(&stream, Z_NO_FLUSH);
+                       if (ret != Z_OK && ret != Z_STREAM_END)
+                               die("zlib error inflating request, result %d", ret);
+
+                       n = stream.total_out - cnt;
+                       if (write_in_full(out, out_buf, n) != n)
+                               die("%s aborted reading request", prog_name);
+                       cnt += n;
+
+                       if (ret == Z_STREAM_END)
+                               goto done;
+               }
+       }
+
+done:
+       inflateEnd(&stream);
+       close(out);
+}
+
+static void run_service(const char **argv)
+{
+       const char *encoding = getenv("HTTP_CONTENT_ENCODING");
+       const char *user = getenv("REMOTE_USER");
+       const char *host = getenv("REMOTE_ADDR");
+       char *env[3];
+       struct strbuf buf = STRBUF_INIT;
+       int gzipped_request = 0;
+       struct child_process cld;
+
+       if (encoding && !strcmp(encoding, "gzip"))
+               gzipped_request = 1;
+       else if (encoding && !strcmp(encoding, "x-gzip"))
+               gzipped_request = 1;
+
+       if (!user || !*user)
+               user = "anonymous";
+       if (!host || !*host)
+               host = "(none)";
+
+       memset(&env, 0, sizeof(env));
+       strbuf_addf(&buf, "GIT_COMMITTER_NAME=%s", user);
+       env[0] = strbuf_detach(&buf, NULL);
+
+       strbuf_addf(&buf, "GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
+       env[1] = strbuf_detach(&buf, NULL);
+       env[2] = NULL;
+
+       memset(&cld, 0, sizeof(cld));
+       cld.argv = argv;
+       cld.env = (const char *const *)env;
+       if (gzipped_request)
+               cld.in = -1;
+       cld.git_cmd = 1;
+       if (start_command(&cld))
+               exit(1);
+
+       close(1);
+       if (gzipped_request)
+               inflate_request(argv[0], cld.in);
+       else
+               close(0);
+
+       if (finish_command(&cld))
+               exit(1);
+       free(env[0]);
+       free(env[1]);
+       strbuf_release(&buf);
+}
+
+static int show_text_ref(const char *name, const unsigned char *sha1,
+       int flag, void *cb_data)
+{
+       struct strbuf *buf = cb_data;
+       struct object *o = parse_object(sha1);
+       if (!o)
+               return 0;
+
+       strbuf_addf(buf, "%s\t%s\n", sha1_to_hex(sha1), name);
+       if (o->type == OBJ_TAG) {
+               o = deref_tag(o, name, 0);
+               if (!o)
+                       return 0;
+               strbuf_addf(buf, "%s\t%s^{}\n", sha1_to_hex(o->sha1), name);
+       }
+       return 0;
+}
+
+static void get_info_refs(char *arg)
+{
+       const char *service_name = get_parameter("service");
+       struct strbuf buf = STRBUF_INIT;
+
+       hdr_nocache();
+
+       if (service_name) {
+               const char *argv[] = {NULL /* service name */,
+                       "--stateless-rpc", "--advertise-refs",
+                       ".", NULL};
+               struct rpc_service *svc = select_service(service_name);
+
+               strbuf_addf(&buf, "application/x-git-%s-advertisement",
+                       svc->name);
+               hdr_str(content_type, buf.buf);
+               end_headers();
+
+               packet_write(1, "# service=git-%s\n", svc->name);
+               packet_flush(1);
+
+               argv[0] = svc->name;
+               run_service(argv);
+
+       } else {
+               select_getanyfile();
+               for_each_ref(show_text_ref, &buf);
+               send_strbuf("text/plain", &buf);
+       }
+       strbuf_release(&buf);
+}
+
+static void get_info_packs(char *arg)
+{
+       size_t objdirlen = strlen(get_object_directory());
+       struct strbuf buf = STRBUF_INIT;
+       struct packed_git *p;
+       size_t cnt = 0;
+
+       select_getanyfile();
+       prepare_packed_git();
+       for (p = packed_git; p; p = p->next) {
+               if (p->pack_local)
+                       cnt++;
+       }
+
+       strbuf_grow(&buf, cnt * 53 + 2);
+       for (p = packed_git; p; p = p->next) {
+               if (p->pack_local)
+                       strbuf_addf(&buf, "P %s\n", p->pack_name + objdirlen + 6);
+       }
+       strbuf_addch(&buf, '\n');
+
+       hdr_nocache();
+       send_strbuf("text/plain; charset=utf-8", &buf);
+       strbuf_release(&buf);
+}
+
+static void check_content_type(const char *accepted_type)
+{
+       const char *actual_type = getenv("CONTENT_TYPE");
+
+       if (!actual_type)
+               actual_type = "";
+
+       if (strcmp(actual_type, accepted_type)) {
+               http_status(415, "Unsupported Media Type");
+               hdr_nocache();
+               end_headers();
+               format_write(1,
+                       "Expected POST with Content-Type '%s',"
+                       " but received '%s' instead.\n",
+                       accepted_type, actual_type);
+               exit(0);
+       }
+}
+
+static void service_rpc(char *service_name)
+{
+       const char *argv[] = {NULL, "--stateless-rpc", ".", NULL};
+       struct rpc_service *svc = select_service(service_name);
+       struct strbuf buf = STRBUF_INIT;
+
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "application/x-git-%s-request", svc->name);
+       check_content_type(buf.buf);
+
+       hdr_nocache();
+
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "application/x-git-%s-result", svc->name);
+       hdr_str(content_type, buf.buf);
+
+       end_headers();
+
+       argv[0] = svc->name;
+       run_service(argv);
+       strbuf_release(&buf);
+}
+
+static NORETURN void die_webcgi(const char *err, va_list params)
+{
+       char buffer[1000];
+
+       http_status(500, "Internal Server Error");
+       hdr_nocache();
+       end_headers();
+
+       vsnprintf(buffer, sizeof(buffer), err, params);
+       fprintf(stderr, "fatal: %s\n", buffer);
+       exit(0);
+}
+
+static char* getdir(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+       char *pathinfo = getenv("PATH_INFO");
+       char *root = getenv("GIT_PROJECT_ROOT");
+       char *path = getenv("PATH_TRANSLATED");
+
+       if (root && *root) {
+               if (!pathinfo || !*pathinfo)
+                       die("GIT_PROJECT_ROOT is set but PATH_INFO is not");
+               if (daemon_avoid_alias(pathinfo))
+                       die("'%s': aliased", pathinfo);
+               strbuf_addstr(&buf, root);
+               if (buf.buf[buf.len - 1] != '/')
+                       strbuf_addch(&buf, '/');
+               if (pathinfo[0] == '/')
+                       pathinfo++;
+               strbuf_addstr(&buf, pathinfo);
+               return strbuf_detach(&buf, NULL);
+       } else if (path && *path) {
+               return xstrdup(path);
+       } else
+               die("No GIT_PROJECT_ROOT or PATH_TRANSLATED from server");
+       return NULL;
+}
+
+static struct service_cmd {
+       const char *method;
+       const char *pattern;
+       void (*imp)(char *);
+} services[] = {
+       {"GET", "/HEAD$", get_text_file},
+       {"GET", "/info/refs$", get_info_refs},
+       {"GET", "/objects/info/alternates$", get_text_file},
+       {"GET", "/objects/info/http-alternates$", get_text_file},
+       {"GET", "/objects/info/packs$", get_info_packs},
+       {"GET", "/objects/[0-9a-f]{2}/[0-9a-f]{38}$", get_loose_object},
+       {"GET", "/objects/pack/pack-[0-9a-f]{40}\\.pack$", get_pack_file},
+       {"GET", "/objects/pack/pack-[0-9a-f]{40}\\.idx$", get_idx_file},
+
+       {"POST", "/git-upload-pack$", service_rpc},
+       {"POST", "/git-receive-pack$", service_rpc}
+};
+
+int main(int argc, char **argv)
+{
+       char *method = getenv("REQUEST_METHOD");
+       char *dir;
+       struct service_cmd *cmd = NULL;
+       char *cmd_arg = NULL;
+       int i;
+
+       git_extract_argv0_path(argv[0]);
+       set_die_routine(die_webcgi);
+
+       if (!method)
+               die("No REQUEST_METHOD from server");
+       if (!strcmp(method, "HEAD"))
+               method = "GET";
+       dir = getdir();
+
+       for (i = 0; i < ARRAY_SIZE(services); i++) {
+               struct service_cmd *c = &services[i];
+               regex_t re;
+               regmatch_t out[1];
+
+               if (regcomp(&re, c->pattern, REG_EXTENDED))
+                       die("Bogus regex in service table: %s", c->pattern);
+               if (!regexec(&re, dir, 1, out, 0)) {
+                       size_t n;
+
+                       if (strcmp(method, c->method)) {
+                               const char *proto = getenv("SERVER_PROTOCOL");
+                               if (proto && !strcmp(proto, "HTTP/1.1"))
+                                       http_status(405, "Method Not Allowed");
+                               else
+                                       http_status(400, "Bad Request");
+                               hdr_nocache();
+                               end_headers();
+                               return 0;
+                       }
+
+                       cmd = c;
+                       n = out[0].rm_eo - out[0].rm_so;
+                       cmd_arg = xmalloc(n);
+                       memcpy(cmd_arg, dir + out[0].rm_so + 1, n-1);
+                       cmd_arg[n-1] = '\0';
+                       dir[out[0].rm_so] = 0;
+                       break;
+               }
+               regfree(&re);
+       }
+
+       if (!cmd)
+               not_found("Request not supported: '%s'", dir);
+
+       setup_path();
+       if (!enter_repo(dir, 0))
+               not_found("Not a git repository: '%s'", dir);
+
+       git_config(http_config, NULL);
+       cmd->imp(cmd_arg);
+       return 0;
+}
index e8f44babd9d3a1cd86eba659b054aed6ac2c792a..ffd0ad7e295d7341776bb7b6407602cdb2997ef3 100644 (file)
@@ -1,6 +1,10 @@
 #include "cache.h"
+#include "exec_cmd.h"
 #include "walker.h"
 
+static const char http_fetch_usage[] = "git http-fetch "
+"[-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url";
+
 int main(int argc, const char **argv)
 {
        const char *prefix;
@@ -19,9 +23,7 @@ int main(int argc, const char **argv)
        int get_verbosely = 0;
        int get_recover = 0;
 
-       prefix = setup_git_directory();
-
-       git_config(git_default_config, NULL);
+       git_extract_argv0_path(argv[0]);
 
        while (arg < argc && argv[arg][0] == '-') {
                if (argv[arg][1] == 't') {
@@ -37,6 +39,8 @@ int main(int argc, const char **argv)
                } else if (argv[arg][1] == 'w') {
                        write_ref = &argv[arg + 1];
                        arg++;
+               } else if (argv[arg][1] == 'h') {
+                       usage(http_fetch_usage);
                } else if (!strcmp(argv[arg], "--recover")) {
                        get_recover = 1;
                } else if (!strcmp(argv[arg], "--stdin")) {
@@ -44,10 +48,8 @@ int main(int argc, const char **argv)
                }
                arg++;
        }
-       if (argc < arg + 2 - commits_on_stdin) {
-               usage("git http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
-               return 1;
-       }
+       if (argc != arg + 2 - commits_on_stdin)
+               usage(http_fetch_usage);
        if (commits_on_stdin) {
                commits = walker_targets_stdin(&commit_id, &write_ref);
        } else {
@@ -55,6 +57,11 @@ int main(int argc, const char **argv)
                commits = 1;
        }
        url = argv[arg];
+
+       prefix = setup_git_directory();
+
+       git_config(git_default_config, NULL);
+
        if (url && url[strlen(url)-1] != '/') {
                rewritten_url = xmalloc(strlen(url)+2);
                strcpy(rewritten_url, url);
index 00e83dcec1d973b069d4c75105aed96634b00994..432b20f2d9a750263d930683e770413ac5328935 100644 (file)
@@ -78,6 +78,7 @@ static int push_verbosely;
 static int push_all = MATCH_REFS_NONE;
 static int force_all;
 static int dry_run;
+static int helper_status;
 
 static struct object_list *objects;
 
@@ -407,10 +408,10 @@ static void start_put(struct transfer_request *request)
        curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &request->buffer);
 #endif
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+       curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
        curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
-       curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
        curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
 
        if (start_active_slot(slot)) {
@@ -604,7 +605,7 @@ static void finish_request(struct transfer_request *request)
                        preq = (struct http_pack_request *)request->userData;
 
                        if (preq) {
-                               if (finish_http_pack_request(preq) > 0)
+                               if (finish_http_pack_request(preq) == 0)
                                        fail = 0;
                                release_http_pack_request(preq);
                        }
@@ -1792,8 +1793,6 @@ int main(int argc, char **argv)
 
        git_extract_argv0_path(argv[0]);
 
-       setup_git_directory();
-
        repo = xcalloc(sizeof(*repo), 1);
 
        argv++;
@@ -1813,6 +1812,10 @@ int main(int argc, char **argv)
                                dry_run = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--helper-status")) {
+                               helper_status = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--verbose")) {
                                push_verbosely = 1;
                                http_is_verbose = 1;
@@ -1827,6 +1830,8 @@ int main(int argc, char **argv)
                                force_delete = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "-h"))
+                               usage(http_push_usage);
                }
                if (!repo->url) {
                        char *path = strstr(arg, "//");
@@ -1854,6 +1859,8 @@ int main(int argc, char **argv)
        if (delete_branch && nr_refspec != 1)
                die("You must specify only one branch name when deleting a remote branch");
 
+       setup_git_directory();
+
        memset(remote_dir_exists, -1, 256);
 
        /*
@@ -1911,9 +1918,12 @@ int main(int argc, char **argv)
 
        /* Remove a remote branch if -d or -D was specified */
        if (delete_branch) {
-               if (delete_remote_branch(refspec[0], force_delete) == -1)
+               if (delete_remote_branch(refspec[0], force_delete) == -1) {
                        fprintf(stderr, "Unable to delete remote branch %s\n",
                                refspec[0]);
+                       if (helper_status)
+                               printf("error %s cannot remove\n", refspec[0]);
+               }
                goto cleanup;
        }
 
@@ -1925,6 +1935,8 @@ int main(int argc, char **argv)
        }
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
+               if (helper_status)
+                       printf("error null no match\n");
                rc = 0;
                goto cleanup;
        }
@@ -1942,8 +1954,12 @@ int main(int argc, char **argv)
                if (is_null_sha1(ref->peer_ref->new_sha1)) {
                        if (delete_remote_branch(ref->name, 1) == -1) {
                                error("Could not remove %s", ref->name);
+                               if (helper_status)
+                                       printf("error %s cannot remove\n", ref->name);
                                rc = -4;
                        }
+                       else if (helper_status)
+                               printf("ok %s\n", ref->name);
                        new_refs++;
                        continue;
                }
@@ -1951,6 +1967,8 @@ int main(int argc, char **argv)
                if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
                        if (push_verbosely || 1)
                                fprintf(stderr, "'%s': up-to-date\n", ref->name);
+                       if (helper_status)
+                               printf("ok %s up to date\n", ref->name);
                        continue;
                }
 
@@ -1974,6 +1992,8 @@ int main(int argc, char **argv)
                                      "need to pull first?",
                                      ref->name,
                                      ref->peer_ref->name);
+                               if (helper_status)
+                                       printf("error %s non-fast forward\n", ref->name);
                                rc = -2;
                                continue;
                        }
@@ -1987,14 +2007,19 @@ int main(int argc, char **argv)
                if (strcmp(ref->name, ref->peer_ref->name))
                        fprintf(stderr, " using '%s'", ref->peer_ref->name);
                fprintf(stderr, "\n  from %s\n  to   %s\n", old_hex, new_hex);
-               if (dry_run)
+               if (dry_run) {
+                       if (helper_status)
+                               printf("ok %s\n", ref->name);
                        continue;
+               }
 
                /* Lock remote branch ref */
                ref_lock = lock_remote(ref->name, LOCK_TIME);
                if (ref_lock == NULL) {
                        fprintf(stderr, "Unable to lock remote branch %s\n",
                                ref->name);
+                       if (helper_status)
+                               printf("error %s lock error\n", ref->name);
                        rc = 1;
                        continue;
                }
@@ -2045,6 +2070,8 @@ int main(int argc, char **argv)
 
                if (!rc)
                        fprintf(stderr, "    done\n");
+               if (helper_status)
+                       printf("%s %s\n", !rc ? "ok" : "error", ref->name);
                unlock_remote(ref_lock);
                check_locks();
        }
diff --git a/http.c b/http.c
index 23b2a1932c7daebd585827da7effd1c5bbae5e69..ed6414a2aaa4e0f6cf7672a089f49060aad62bfb 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,9 +1,11 @@
 #include "http.h"
 #include "pack.h"
+#include "sideband.h"
 
 int data_received;
 int active_requests;
 int http_is_verbose;
+size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
 
 #ifdef USE_CURL_MULTI
 static int max_requests = -1;
@@ -97,8 +99,6 @@ size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf)
        return eltsize * nmemb;
 }
 
-static void finish_active_slot(struct active_request_slot *slot);
-
 #ifdef USE_CURL_MULTI
 static void process_curl_messages(void)
 {
@@ -174,6 +174,13 @@ static int http_options(const char *var, const char *value, void *cb)
        if (!strcmp("http.proxy", var))
                return git_config_string(&curl_http_proxy, var, value);
 
+       if (!strcmp("http.postbuffer", var)) {
+               http_post_buffer = git_config_int(var, value);
+               if (http_post_buffer < LARGE_PACKET_MAX)
+                       http_post_buffer = LARGE_PACKET_MAX;
+               return 0;
+       }
+
        /* Fall back on the default ones */
        return git_default_config(var, value, cb);
 }
@@ -638,7 +645,7 @@ void release_active_slot(struct active_request_slot *slot)
 #endif
 }
 
-static void finish_active_slot(struct active_request_slot *slot)
+void finish_active_slot(struct active_request_slot *slot)
 {
        closedown_active_slot(slot);
        curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
diff --git a/http.h b/http.h
index 4c4e99c2f6cae97110efe28986333fc5cbc4c095..f828e1d8067aeff3036c14a1c43e333902e46bd6 100644 (file)
--- a/http.h
+++ b/http.h
@@ -79,6 +79,7 @@ extern curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
 extern struct active_request_slot *get_active_slot(void);
 extern int start_active_slot(struct active_request_slot *slot);
 extern void run_active_slot(struct active_request_slot *slot);
+extern void finish_active_slot(struct active_request_slot *slot);
 extern void finish_all_active_slots(void);
 extern void release_active_slot(struct active_request_slot *slot);
 
@@ -94,6 +95,7 @@ extern void http_cleanup(void);
 extern int data_received;
 extern int active_requests;
 extern int http_is_verbose;
+extern size_t http_post_buffer;
 
 extern char curl_errorstr[CURL_ERROR_SIZE];
 
diff --git a/ident.c b/ident.c
index 99f1c85ea5f5c83247f7affd3d801ff5c14393a6..26409b2a1b191765706265c2aa6d2ae163ba5bab 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -205,7 +205,7 @@ const char *fmt_ident(const char *name, const char *email,
                if ((warn_on_no_name || error_on_no_name) &&
                    name == git_default_name && env_hint) {
                        fprintf(stderr, env_hint, au_env, co_env);
-                       env_hint = NULL; /* warn only once, for "git var -l" */
+                       env_hint = NULL; /* warn only once */
                }
                if (error_on_no_name)
                        die("empty ident %s <%s> not allowed", name, email);
index 3847fd151df6e9f41fdb0b95573882a7d9ac992a..de8114bac010ef095cf9de17671566e248366dcb 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "cache.h"
 #include "exec_cmd.h"
+#include "run-command.h"
 #ifdef NO_OPENSSL
 typedef void *SSL;
 #endif
@@ -93,6 +94,9 @@ struct msg_data {
        unsigned int crlf:1;
 };
 
+static const char imap_send_usage[] = "git imap-send < <mbox>";
+
+#undef DRV_OK
 #define DRV_OK          0
 #define DRV_MSG_BAD     -1
 #define DRV_BOX_BAD     -2
@@ -100,13 +104,16 @@ struct msg_data {
 
 static int Verbose, Quiet;
 
+__attribute__((format (printf, 1, 2)))
 static void imap_info(const char *, ...);
+__attribute__((format (printf, 1, 2)))
 static void imap_warn(const char *, ...);
 
 static char *next_arg(char **);
 
 static void free_generic_messages(struct message *);
 
+__attribute__((format (printf, 3, 4)))
 static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
 
 static int nfvasprintf(char **strp, const char *fmt, va_list ap)
@@ -123,9 +130,6 @@ static int nfvasprintf(char **strp, const char *fmt, va_list ap)
        return len;
 }
 
-static void arc4_init(void);
-static unsigned char arc4_getbyte(void);
-
 struct imap_server_conf {
        char *name;
        char *tunnel;
@@ -154,7 +158,7 @@ struct imap_list {
 };
 
 struct imap_socket {
-       int fd;
+       int fd[2];
        SSL *ssl;
 };
 
@@ -272,8 +276,12 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 #ifdef NO_OPENSSL
        fprintf(stderr, "SSL requested but SSL support not compiled in\n");
        return -1;
+#else
+#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
+       const SSL_METHOD *meth;
 #else
        SSL_METHOD *meth;
+#endif
        SSL_CTX *ctx;
        int ret;
 
@@ -304,8 +312,12 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
                ssl_socket_perror("SSL_new");
                return -1;
        }
-       if (!SSL_set_fd(sock->ssl, sock->fd)) {
-               ssl_socket_perror("SSL_set_fd");
+       if (!SSL_set_rfd(sock->ssl, sock->fd[0])) {
+               ssl_socket_perror("SSL_set_rfd");
+               return -1;
+       }
+       if (!SSL_set_wfd(sock->ssl, sock->fd[1])) {
+               ssl_socket_perror("SSL_set_wfd");
                return -1;
        }
 
@@ -327,11 +339,12 @@ static int socket_read(struct imap_socket *sock, char *buf, int len)
                n = SSL_read(sock->ssl, buf, len);
        else
 #endif
-               n = xread(sock->fd, buf, len);
+               n = xread(sock->fd[0], buf, len);
        if (n <= 0) {
                socket_perror("read", sock, n);
-               close(sock->fd);
-               sock->fd = -1;
+               close(sock->fd[0]);
+               close(sock->fd[1]);
+               sock->fd[0] = sock->fd[1] = -1;
        }
        return n;
 }
@@ -344,11 +357,12 @@ static int socket_write(struct imap_socket *sock, const char *buf, int len)
                n = SSL_write(sock->ssl, buf, len);
        else
 #endif
-               n = write_in_full(sock->fd, buf, len);
+               n = write_in_full(sock->fd[1], buf, len);
        if (n != len) {
                socket_perror("write", sock, n);
-               close(sock->fd);
-               sock->fd = -1;
+               close(sock->fd[0]);
+               close(sock->fd[1]);
+               sock->fd[0] = sock->fd[1] = -1;
        }
        return n;
 }
@@ -361,7 +375,8 @@ static void socket_shutdown(struct imap_socket *sock)
                SSL_free(sock->ssl);
        }
 #endif
-       close(sock->fd);
+       close(sock->fd[0]);
+       close(sock->fd[1]);
 }
 
 /* simple line buffering */
@@ -489,52 +504,6 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
        return ret;
 }
 
-static struct {
-       unsigned char i, j, s[256];
-} rs;
-
-static void arc4_init(void)
-{
-       int i, fd;
-       unsigned char j, si, dat[128];
-
-       if ((fd = open("/dev/urandom", O_RDONLY)) < 0 && (fd = open("/dev/random", O_RDONLY)) < 0) {
-               fprintf(stderr, "Fatal: no random number source available.\n");
-               exit(3);
-       }
-       if (read_in_full(fd, dat, 128) != 128) {
-               fprintf(stderr, "Fatal: cannot read random number source.\n");
-               exit(3);
-       }
-       close(fd);
-
-       for (i = 0; i < 256; i++)
-               rs.s[i] = i;
-       for (i = j = 0; i < 256; i++) {
-               si = rs.s[i];
-               j += si + dat[i & 127];
-               rs.s[i] = rs.s[j];
-               rs.s[j] = si;
-       }
-       rs.i = rs.j = 0;
-
-       for (i = 0; i < 256; i++)
-               arc4_getbyte();
-}
-
-static unsigned char arc4_getbyte(void)
-{
-       unsigned char si, sj;
-
-       rs.i++;
-       si = rs.s[rs.i];
-       rs.j += si;
-       sj = rs.s[rs.j];
-       rs.s[rs.i] = sj;
-       rs.s[rs.j] = si;
-       return rs.s[(si + sj) & 0xff];
-}
-
 static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
                                         struct imap_cmd_cb *cb,
                                         const char *fmt, va_list ap)
@@ -596,6 +565,7 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
        return cmd;
 }
 
+__attribute__((format (printf, 3, 4)))
 static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
                                       struct imap_cmd_cb *cb,
                                       const char *fmt, ...)
@@ -609,6 +579,7 @@ static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
        return ret;
 }
 
+__attribute__((format (printf, 3, 4)))
 static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
                     const char *fmt, ...)
 {
@@ -624,6 +595,7 @@ static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
        return get_cmd_result(ctx, cmdp);
 }
 
+__attribute__((format (printf, 3, 4)))
 static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,
                       const char *fmt, ...)
 {
@@ -914,7 +886,7 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
                                if (!strcmp("NO", arg)) {
                                        if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, "[TRYCREATE]", 11))) { /* SELECT, APPEND or UID COPY */
                                                p = strchr(cmdp->cmd, '"');
-                                               if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", strchr(p + 1, '"') - p + 1, p)) {
+                                               if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", (int)(strchr(p + 1, '"') - p + 1), p)) {
                                                        resp = RESP_BAD;
                                                        goto normal;
                                                }
@@ -960,7 +932,7 @@ static void imap_close_server(struct imap_store *ictx)
 {
        struct imap *imap = ictx->imap;
 
-       if (imap->buf.sock.fd != -1) {
+       if (imap->buf.sock.fd[0] != -1) {
                imap_exec(ictx, NULL, "LOGOUT");
                socket_shutdown(&imap->buf.sock);
        }
@@ -982,40 +954,35 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
        struct imap_store *ctx;
        struct imap *imap;
        char *arg, *rsp;
-       int s = -1, a[2], preauth;
-       pid_t pid;
+       int s = -1, preauth;
 
        ctx = xcalloc(sizeof(*ctx), 1);
 
        ctx->imap = imap = xcalloc(sizeof(*imap), 1);
-       imap->buf.sock.fd = -1;
+       imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
        imap->in_progress_append = &imap->in_progress;
 
        /* open connection to IMAP server */
 
        if (srvc->tunnel) {
-               imap_info("Starting tunnel '%s'... ", srvc->tunnel);
+               const char *argv[4];
+               struct child_process tunnel = {0};
 
-               if (socketpair(PF_UNIX, SOCK_STREAM, 0, a)) {
-                       perror("socketpair");
-                       exit(1);
-               }
+               imap_info("Starting tunnel '%s'... ", srvc->tunnel);
 
-               pid = fork();
-               if (pid < 0)
-                       _exit(127);
-               if (!pid) {
-                       if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1)
-                               _exit(127);
-                       close(a[0]);
-                       close(a[1]);
-                       execl("/bin/sh", "sh", "-c", srvc->tunnel, NULL);
-                       _exit(127);
-               }
+               argv[0] = "sh";
+               argv[1] = "-c";
+               argv[2] = srvc->tunnel;
+               argv[3] = NULL;
 
-               close(a[0]);
+               tunnel.argv = argv;
+               tunnel.in = -1;
+               tunnel.out = -1;
+               if (start_command(&tunnel))
+                       die("cannot start proxy %s", argv[0]);
 
-               imap->buf.sock.fd = a[1];
+               imap->buf.sock.fd[0] = tunnel.out;
+               imap->buf.sock.fd[1] = tunnel.in;
 
                imap_info("ok\n");
        } else {
@@ -1092,7 +1059,8 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
                        goto bail;
                }
 
-               imap->buf.sock.fd = s;
+               imap->buf.sock.fd[0] = s;
+               imap->buf.sock.fd[1] = dup(s);
 
                if (srvc->use_ssl &&
                    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
@@ -1198,88 +1166,20 @@ static int imap_make_flags(int flags, char *buf)
        return d;
 }
 
-#define TUIDL 8
-
-static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid)
+static int imap_store_msg(struct store *gctx, struct msg_data *data)
 {
        struct imap_store *ctx = (struct imap_store *)gctx;
        struct imap *imap = ctx->imap;
        struct imap_cmd_cb cb;
-       char *fmap, *buf;
        const char *prefix, *box;
-       int ret, i, j, d, len, extra, nocr;
-       int start, sbreak = 0, ebreak = 0;
-       char flagstr[128], tuid[TUIDL * 2 + 1];
+       int ret, d;
+       char flagstr[128];
 
        memset(&cb, 0, sizeof(cb));
 
-       fmap = data->data;
-       len = data->len;
-       nocr = !data->crlf;
-       extra = 0, i = 0;
-       if (!CAP(UIDPLUS) && uid) {
-       nloop:
-               start = i;
-               while (i < len)
-                       if (fmap[i++] == '\n') {
-                               extra += nocr;
-                               if (i - 2 + nocr == start) {
-                                       sbreak = ebreak = i - 2 + nocr;
-                                       goto mktid;
-                               }
-                               if (!memcmp(fmap + start, "X-TUID: ", 8)) {
-                                       extra -= (ebreak = i) - (sbreak = start) + nocr;
-                                       goto mktid;
-                               }
-                               goto nloop;
-                       }
-               /* invalid message */
-               free(fmap);
-               return DRV_MSG_BAD;
-       mktid:
-               for (j = 0; j < TUIDL; j++)
-                       sprintf(tuid + j * 2, "%02x", arc4_getbyte());
-               extra += 8 + TUIDL * 2 + 2;
-       }
-       if (nocr)
-               for (; i < len; i++)
-                       if (fmap[i] == '\n')
-                               extra++;
-
-       cb.dlen = len + extra;
-       buf = cb.data = xmalloc(cb.dlen);
-       i = 0;
-       if (!CAP(UIDPLUS) && uid) {
-               if (nocr) {
-                       for (; i < sbreak; i++)
-                               if (fmap[i] == '\n') {
-                                       *buf++ = '\r';
-                                       *buf++ = '\n';
-                               } else
-                                       *buf++ = fmap[i];
-               } else {
-                       memcpy(buf, fmap, sbreak);
-                       buf += sbreak;
-               }
-               memcpy(buf, "X-TUID: ", 8);
-               buf += 8;
-               memcpy(buf, tuid, TUIDL * 2);
-               buf += TUIDL * 2;
-               *buf++ = '\r';
-               *buf++ = '\n';
-               i = ebreak;
-       }
-       if (nocr) {
-               for (; i < len; i++)
-                       if (fmap[i] == '\n') {
-                               *buf++ = '\r';
-                               *buf++ = '\n';
-                       } else
-                               *buf++ = fmap[i];
-       } else
-               memcpy(buf, fmap + i, len - i);
-
-       free(fmap);
+       cb.dlen = data->len;
+       cb.data = xmalloc(cb.dlen);
+       memcpy(cb.data, data->data, data->len);
 
        d = 0;
        if (data->flags) {
@@ -1288,26 +1188,14 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid)
        }
        flagstr[d] = 0;
 
-       if (!uid) {
-               box = gctx->conf->trash;
-               prefix = ctx->prefix;
-               cb.create = 1;
-               if (ctx->trashnc)
-                       imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
-       } else {
-               box = gctx->name;
-               prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
-               cb.create = 0;
-       }
-       cb.ctx = uid;
+       box = gctx->name;
+       prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
+       cb.create = 0;
        ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr);
        imap->caps = imap->rcaps;
        if (ret != DRV_OK)
                return ret;
-       if (!uid)
-               ctx->trashnc = 0;
-       else
-               gctx->count++;
+       gctx->count++;
 
        return DRV_OK;
 }
@@ -1483,7 +1371,6 @@ int main(int argc, char **argv)
 {
        struct msg_data all_msgs, msg;
        struct store *ctx = NULL;
-       int uid = 0;
        int ofs = 0;
        int r;
        int total, n = 0;
@@ -1491,8 +1378,8 @@ int main(int argc, char **argv)
 
        git_extract_argv0_path(argv[0]);
 
-       /* init the random number generator */
-       arc4_init();
+       if (argc != 1)
+               usage(imap_send_usage);
 
        setup_git_directory_gently(&nongit_ok);
        git_config(git_imap_config, NULL);
@@ -1540,7 +1427,7 @@ int main(int argc, char **argv)
                        break;
                if (server.use_html)
                        wrap_in_html(&msg);
-               r = imap_store_msg(ctx, &msg, &uid);
+               r = imap_store_msg(ctx, &msg);
                if (r != DRV_OK)
                        break;
                n++;
index b4f82786592e2e34392191e823d645e17d955fcf..190f372dd81f85d76068b166cc1688487640fdba 100644 (file)
@@ -882,6 +882,9 @@ int main(int argc, char **argv)
 
        git_extract_argv0_path(argv[0]);
 
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage(index_pack_usage);
+
        /*
         * We wish to read the repository's config file if any, and
         * for that it is necessary to call setup_git_directory_gently().
index f55b7ebe11bbd54f00a84dd1eb9d34f60d74a1c3..a91208f295d00d73b8fc0f5cd6a92ac422d9eeb4 100644 (file)
@@ -3,6 +3,7 @@
  * Fredrik Kuivinen.
  * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
  */
+#include "advice.h"
 #include "cache.h"
 #include "cache-tree.h"
 #include "commit.h"
@@ -86,6 +87,7 @@ static void flush_output(struct merge_options *o)
        }
 }
 
+__attribute__((format (printf, 3, 4)))
 static void output(struct merge_options *o, int v, const char *fmt, ...)
 {
        int len;
@@ -170,7 +172,7 @@ static int git_merge_trees(int index_only,
        int rc;
        struct tree_desc t[3];
        struct unpack_trees_options opts;
-       static const struct unpack_trees_error_msgs msgs = {
+       struct unpack_trees_error_msgs msgs = {
                /* would_overwrite */
                "Your local changes to '%s' would be overwritten by merge.  Aborting.",
                /* not_uptodate_file */
@@ -182,6 +184,11 @@ static int git_merge_trees(int index_only,
                /* bind_overlap -- will not happen here */
                NULL,
        };
+       if (advice_commit_before_merge) {
+               msgs.would_overwrite = msgs.not_uptodate_file =
+                       "Your local changes to '%s' would be overwritten by merge.  Aborting.\n"
+                       "Please, commit your changes or stash them before you can merge.";
+       }
 
        memset(&opts, 0, sizeof(opts));
        if (index_only)
@@ -214,7 +221,8 @@ struct tree *write_tree_from_memory(struct merge_options *o)
                for (i = 0; i < active_nr; i++) {
                        struct cache_entry *ce = active_cache[i];
                        if (ce_stage(ce))
-                               output(o, 0, "%d %.*s", ce_stage(ce), ce_namelen(ce), ce->name);
+                               output(o, 0, "%d %.*s", ce_stage(ce),
+                                      (int)ce_namelen(ce), ce->name);
                }
                return NULL;
        }
diff --git a/notes.c b/notes.c
new file mode 100644 (file)
index 0000000..50a4672
--- /dev/null
+++ b/notes.c
@@ -0,0 +1,429 @@
+#include "cache.h"
+#include "commit.h"
+#include "notes.h"
+#include "refs.h"
+#include "utf8.h"
+#include "strbuf.h"
+#include "tree-walk.h"
+
+/*
+ * Use a non-balancing simple 16-tree structure with struct int_node as
+ * internal nodes, and struct leaf_node as leaf nodes. Each int_node has a
+ * 16-array of pointers to its children.
+ * The bottom 2 bits of each pointer is used to identify the pointer type
+ * - ptr & 3 == 0 - NULL pointer, assert(ptr == NULL)
+ * - ptr & 3 == 1 - pointer to next internal node - cast to struct int_node *
+ * - ptr & 3 == 2 - pointer to note entry - cast to struct leaf_node *
+ * - ptr & 3 == 3 - pointer to subtree entry - cast to struct leaf_node *
+ *
+ * The root node is a statically allocated struct int_node.
+ */
+struct int_node {
+       void *a[16];
+};
+
+/*
+ * 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
+ * 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
+ * the prefix. The value is the SHA1 of the tree object containing the notes
+ * subtree.
+ */
+struct leaf_node {
+       unsigned char key_sha1[20];
+       unsigned char val_sha1[20];
+};
+
+#define PTR_TYPE_NULL     0
+#define PTR_TYPE_INTERNAL 1
+#define PTR_TYPE_NOTE     2
+#define PTR_TYPE_SUBTREE  3
+
+#define GET_PTR_TYPE(ptr)       ((uintptr_t) (ptr) & 3)
+#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 SUBTREE_SHA1_PREFIXCMP(key_sha1, subtree_sha1) \
+       (memcmp(key_sha1, subtree_sha1, subtree_sha1[19]))
+
+static struct int_node root_node;
+
+static int initialized;
+
+static void load_subtree(struct leaf_node *subtree, struct int_node *node,
+               unsigned int n);
+
+/*
+ * Search the tree until the appropriate location for the given key is found:
+ * 1. Start at the root node, with n = 0
+ * 2. If a[0] at the current level is a matching subtree entry, unpack that
+ *    subtree entry and remove it; restart search at the current level.
+ * 3. Use the nth nibble of the key as an index into a:
+ *    - If a[n] is an int_node, recurse from #2 into that node and increment n
+ *    - If a matching subtree entry, unpack that subtree entry (and remove it);
+ *      restart search at the current level.
+ *    - Otherwise, we have found one of the following:
+ *      - a subtree entry which does not match the key
+ *      - a note entry which may or may not match the key
+ *      - 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,
+               unsigned char *n, const unsigned char *key_sha1)
+{
+       struct leaf_node *l;
+       unsigned char i;
+       void *p = (*tree)->a[0];
+
+       if (GET_PTR_TYPE(p) == 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[0] = NULL;
+                       load_subtree(l, *tree, *n);
+                       free(l);
+                       return note_tree_search(tree, n, key_sha1);
+               }
+       }
+
+       i = GET_NIBBLE(*n, key_sha1);
+       p = (*tree)->a[i];
+       switch(GET_PTR_TYPE(p)) {
+       case PTR_TYPE_INTERNAL:
+               *tree = CLR_PTR_TYPE(p);
+               (*n)++;
+               return note_tree_search(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);
+                       free(l);
+                       return note_tree_search(tree, n, key_sha1);
+               }
+               /* fall through */
+       default:
+               return &((*tree)->a[i]);
+       }
+}
+
+/*
+ * To find a leaf_node:
+ * 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,
+               const unsigned char *key_sha1)
+{
+       void **p = note_tree_search(&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))
+                       return l;
+       }
+       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.
+ * - 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
+ *   location, and restart the insert operation from that level.
+ * - 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)
+{
+       struct int_node *new_node;
+       struct leaf_node *l;
+       void **p = note_tree_search(&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)) {
+       case PTR_TYPE_NULL:
+               assert(!*p);
+               *p = SET_PTR_TYPE(entry, type);
+               return;
+       case PTR_TYPE_NOTE:
+               switch (type) {
+               case PTR_TYPE_NOTE:
+                       if (!hashcmp(l->key_sha1, entry->key_sha1)) {
+                               /* skip concatenation if l == entry */
+                               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),
+                                           sha1_to_hex(l->val_sha1),
+                                           sha1_to_hex(l->key_sha1));
+                               free(entry);
+                               return;
+                       }
+                       break;
+               case PTR_TYPE_SUBTREE:
+                       if (!SUBTREE_SHA1_PREFIXCMP(l->key_sha1,
+                                                   entry->key_sha1)) {
+                               /* unpack 'entry' */
+                               load_subtree(entry, tree, n);
+                               free(entry);
+                               return;
+                       }
+                       break;
+               }
+               break;
+       case PTR_TYPE_SUBTREE:
+               if (!SUBTREE_SHA1_PREFIXCMP(entry->key_sha1, l->key_sha1)) {
+                       /* unpack 'l' and restart insert */
+                       *p = NULL;
+                       load_subtree(l, tree, n);
+                       free(l);
+                       note_tree_insert(tree, n, entry, type);
+                       return;
+               }
+               break;
+       }
+
+       /* non-matching leaf_node */
+       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));
+       *p = SET_PTR_TYPE(new_node, PTR_TYPE_INTERNAL);
+       note_tree_insert(new_node, n + 1, entry, type);
+}
+
+/* Free the entire notes data contained in the given tree */
+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)) {
+               case PTR_TYPE_INTERNAL:
+                       note_tree_free(CLR_PTR_TYPE(p));
+                       /* fall through */
+               case PTR_TYPE_NOTE:
+               case PTR_TYPE_SUBTREE:
+                       free(CLR_PTR_TYPE(p));
+               }
+       }
+}
+
+/*
+ * Convert a partial SHA1 hex string to the corresponding partial SHA1 value.
+ * - hex      - Partial SHA1 segment in ASCII hex format
+ * - 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).
+ * 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).
+ */
+static int get_sha1_hex_segment(const char *hex, unsigned int hex_len,
+               unsigned char *sha1, unsigned int sha1_len)
+{
+       unsigned int i, len = hex_len >> 1;
+       if (hex_len % 2 != 0 || len > sha1_len)
+               return -1;
+       for (i = 0; i < len; i++) {
+               unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
+               if (val & ~0xff)
+                       return -1;
+               *sha1++ = val;
+               hex += 2;
+       }
+       for (; i < sha1_len; i++)
+               *sha1++ = 0;
+       return len;
+}
+
+static void load_subtree(struct leaf_node *subtree, struct int_node *node,
+               unsigned int n)
+{
+       unsigned char commit_sha1[20];
+       unsigned int prefix_len;
+       void *buf;
+       struct tree_desc desc;
+       struct name_entry entry;
+
+       buf = fill_tree_descriptor(&desc, subtree->val_sha1);
+       if (!buf)
+               die("Could not read %s for notes-index",
+                    sha1_to_hex(subtree->val_sha1));
+
+       prefix_len = subtree->key_sha1[19];
+       assert(prefix_len * 2 >= n);
+       memcpy(commit_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);
+               if (len < 0)
+                       continue; /* entry.path is not a SHA1 sum. Skip */
+               len += prefix_len;
+
+               /*
+                * If commit SHA1 is complete (len == 20), assume note object
+                * If commit SHA1 is incomplete (len < 20), assume note subtree
+                */
+               if (len <= 20) {
+                       unsigned char type = PTR_TYPE_NOTE;
+                       struct leaf_node *l = (struct leaf_node *)
+                               xcalloc(sizeof(struct leaf_node), 1);
+                       hashcpy(l->key_sha1, commit_sha1);
+                       hashcpy(l->val_sha1, entry.sha1);
+                       if (len < 20) {
+                               l->key_sha1[19] = (unsigned char) len;
+                               type = PTR_TYPE_SUBTREE;
+                       }
+                       note_tree_insert(node, n, l, type);
+               }
+       }
+       free(buf);
+}
+
+static void initialize_notes(const char *notes_ref_name)
+{
+       unsigned char sha1[20], commit_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))
+               return;
+
+       hashclr(root_tree.key_sha1);
+       hashcpy(root_tree.val_sha1, sha1);
+       load_subtree(&root_tree, &root_node, 0);
+}
+
+static unsigned char *lookup_notes(const unsigned char *commit_sha1)
+{
+       struct leaf_node *found = note_tree_find(&root_node, 0, commit_sha1);
+       if (found)
+               return found->val_sha1;
+       return NULL;
+}
+
+void free_notes(void)
+{
+       note_tree_free(&root_node);
+       memset(&root_node, 0, sizeof(struct int_node));
+       initialized = 0;
+}
+
+void get_commit_notes(const struct commit *commit, struct strbuf *sb,
+               const char *output_encoding, int flags)
+{
+       static const char utf8[] = "utf-8";
+       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;
+       }
+
+       sha1 = lookup_notes(commit->object.sha1);
+       if (!sha1)
+               return;
+
+       if (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||
+                       type != OBJ_BLOB) {
+               free(msg);
+               return;
+       }
+
+       if (output_encoding && *output_encoding &&
+                       strcmp(utf8, output_encoding)) {
+               char *reencoded = reencode_string(msg, output_encoding, utf8);
+               if (reencoded) {
+                       free(msg);
+                       msg = reencoded;
+                       msglen = strlen(msg);
+               }
+       }
+
+       /* we will end the annotation by a newline anyway */
+       if (msglen && msg[msglen - 1] == '\n')
+               msglen--;
+
+       if (flags & NOTES_SHOW_HEADER)
+               strbuf_addstr(sb, "\nNotes:\n");
+
+       for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
+               linelen = strchrnul(msg_p, '\n') - msg_p;
+
+               if (flags & NOTES_INDENT)
+                       strbuf_addstr(sb, "    ");
+               strbuf_add(sb, msg_p, linelen);
+               strbuf_addch(sb, '\n');
+       }
+
+       free(msg);
+}
diff --git a/notes.h b/notes.h
new file mode 100644 (file)
index 0000000..a1421e3
--- /dev/null
+++ b/notes.h
@@ -0,0 +1,13 @@
+#ifndef NOTES_H
+#define NOTES_H
+
+/* Free (and de-initialize) the internal notes tree structure */
+void free_notes(void);
+
+#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);
+
+#endif
index 69a7ab2e27d39ecabca5c0cc25c4e372caafb46c..21c61dbbe9bd5e2f9770109bc4a9daaf2a2f6cc4 100644 (file)
@@ -603,6 +603,9 @@ int main(int argc, char **argv)
 
        git_extract_argv0_path(argv[0]);
 
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage(pack_redundant_usage);
+
        setup_git_directory();
 
        for (i = 1; i < argc; i++) {
diff --git a/pager.c b/pager.c
index 86facec7b417b26a7dbd9b3c1338149fcecd5588..92c03f654abd0333bd0dd48b4aebf9ae42ac4de5 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -2,6 +2,10 @@
 #include "run-command.h"
 #include "sigchain.h"
 
+#ifndef DEFAULT_PAGER
+#define DEFAULT_PAGER "less"
+#endif
+
 /*
  * This is split up from the rest of git so that we can do
  * something different on Windows.
@@ -44,12 +48,14 @@ static void wait_for_pager_signal(int signo)
        raise(signo);
 }
 
-void setup_pager(void)
+const char *git_pager(void)
 {
-       const char *pager = getenv("GIT_PAGER");
+       const char *pager;
 
        if (!isatty(1))
-               return;
+               return NULL;
+
+       pager = getenv("GIT_PAGER");
        if (!pager) {
                if (!pager_program)
                        git_config(git_default_config, NULL);
@@ -58,8 +64,18 @@ void setup_pager(void)
        if (!pager)
                pager = getenv("PAGER");
        if (!pager)
-               pager = "less";
+               pager = DEFAULT_PAGER;
        else if (!*pager || !strcmp(pager, "cat"))
+               pager = NULL;
+
+       return pager;
+}
+
+void setup_pager(void)
+{
+       const char *pager = git_pager();
+
+       if (!pager)
                return;
 
        spawned_pager = 1; /* means we are emitting to terminal */
diff --git a/path.c b/path.c
index 047fdb0a1fe8151f5f275ca5333365df786a8abd..2ec950b27f1c3e4919ce7d1696360c5a49abb724 100644 (file)
--- a/path.c
+++ b/path.c
@@ -11,6 +11,7 @@
  * which is what it's designed for.
  */
 #include "cache.h"
+#include "strbuf.h"
 
 static char bad_path[] = "/bad-path/";
 
@@ -207,43 +208,49 @@ int validate_headref(const char *path)
        return -1;
 }
 
-static char *user_path(char *buf, char *path, int sz)
+static struct passwd *getpw_str(const char *username, size_t len)
 {
        struct passwd *pw;
-       char *slash;
-       int len, baselen;
+       char *username_z = xmalloc(len + 1);
+       memcpy(username_z, username, len);
+       username_z[len] = '\0';
+       pw = getpwnam(username_z);
+       free(username_z);
+       return pw;
+}
 
-       if (!path || path[0] != '~')
-               return NULL;
-       path++;
-       slash = strchr(path, '/');
-       if (path[0] == '/' || !path[0]) {
-               pw = getpwuid(getuid());
-       }
-       else {
-               if (slash) {
-                       *slash = 0;
-                       pw = getpwnam(path);
-                       *slash = '/';
+/*
+ * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,
+ * then it is a newly allocated string. Returns NULL on getpw failure or
+ * if path is NULL.
+ */
+char *expand_user_path(const char *path)
+{
+       struct strbuf user_path = STRBUF_INIT;
+       const char *first_slash = strchrnul(path, '/');
+       const char *to_copy = path;
+
+       if (path == NULL)
+               goto return_null;
+       if (path[0] == '~') {
+               const char *username = path + 1;
+               size_t username_len = first_slash - username;
+               if (username_len == 0) {
+                       const char *home = getenv("HOME");
+                       strbuf_add(&user_path, home, strlen(home));
+               } else {
+                       struct passwd *pw = getpw_str(username, username_len);
+                       if (!pw)
+                               goto return_null;
+                       strbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));
                }
-               else
-                       pw = getpwnam(path);
+               to_copy = first_slash;
        }
-       if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))
-               return NULL;
-       baselen = strlen(pw->pw_dir);
-       memcpy(buf, pw->pw_dir, baselen);
-       while ((1 < baselen) && (buf[baselen-1] == '/')) {
-               buf[baselen-1] = 0;
-               baselen--;
-       }
-       if (slash && slash[1]) {
-               len = strlen(slash);
-               if (sz <= baselen + len)
-                       return NULL;
-               memcpy(buf + baselen, slash, len + 1);
-       }
-       return buf;
+       strbuf_add(&user_path, to_copy, strlen(to_copy));
+       return strbuf_detach(&user_path, NULL);
+return_null:
+       strbuf_release(&user_path);
+       return NULL;
 }
 
 /*
@@ -291,8 +298,18 @@ char *enter_repo(char *path, int strict)
                if (PATH_MAX <= len)
                        return NULL;
                if (path[0] == '~') {
-                       if (!user_path(used_path, path, PATH_MAX))
+                       char *newpath = expand_user_path(path);
+                       if (!newpath || (PATH_MAX - 10 < strlen(newpath))) {
+                               free(newpath);
                                return NULL;
+                       }
+                       /*
+                        * Copy back into the static buffer. A pity
+                        * since newpath was not bounded, but other
+                        * branches of the if are limited by PATH_MAX
+                        * anyway.
+                        */
+                       strcpy(used_path, newpath); free(newpath);
                        strcpy(validated_path, path);
                        path = used_path;
                }
@@ -564,3 +581,50 @@ char *strip_path_suffix(const char *path, const char *suffix)
                return NULL;
        return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
 }
+
+int daemon_avoid_alias(const char *p)
+{
+       int sl, ndot;
+
+       /*
+        * This resurrects the belts and suspenders paranoia check by HPA
+        * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
+        * does not do getcwd() based path canonicalizations.
+        *
+        * sl becomes true immediately after seeing '/' and continues to
+        * be true as long as dots continue after that without intervening
+        * non-dot character.
+        */
+       if (!p || (*p != '/' && *p != '~'))
+               return -1;
+       sl = 1; ndot = 0;
+       p++;
+
+       while (1) {
+               char ch = *p++;
+               if (sl) {
+                       if (ch == '.')
+                               ndot++;
+                       else if (ch == '/') {
+                               if (ndot < 3)
+                                       /* reject //, /./ and /../ */
+                                       return -1;
+                               ndot = 0;
+                       }
+                       else if (ch == 0) {
+                               if (0 < ndot && ndot < 3)
+                                       /* reject /.$ and /..$ */
+                                       return -1;
+                               return 0;
+                       }
+                       else
+                               sl = ndot = 0;
+               }
+               else if (ch == 0)
+                       return 0;
+               else if (ch == '/') {
+                       sl = 1;
+                       ndot = 0;
+               }
+       }
+}
index b691abebd79b40f096b28b5dbcb433f01bc0e949..295ba2b16c8675d8c5c5d0a5ee6e61e044ce99dd 100644 (file)
@@ -42,17 +42,19 @@ void packet_flush(int fd)
        safe_write(fd, "0000", 4);
 }
 
+void packet_buf_flush(struct strbuf *buf)
+{
+       strbuf_add(buf, "0000", 4);
+}
+
 #define hex(a) (hexchar[(a) & 15])
-void packet_write(int fd, const char *fmt, ...)
+static char buffer[1000];
+static unsigned format_packet(const char *fmt, va_list args)
 {
-       static char buffer[1000];
        static char hexchar[] = "0123456789abcdef";
-       va_list args;
        unsigned n;
 
-       va_start(args, fmt);
        n = vsnprintf(buffer + 4, sizeof(buffer) - 4, fmt, args);
-       va_end(args);
        if (n >= sizeof(buffer)-4)
                die("protocol error: impossibly long line");
        n += 4;
@@ -60,9 +62,31 @@ void packet_write(int fd, const char *fmt, ...)
        buffer[1] = hex(n >> 8);
        buffer[2] = hex(n >> 4);
        buffer[3] = hex(n);
+       return n;
+}
+
+void packet_write(int fd, const char *fmt, ...)
+{
+       va_list args;
+       unsigned n;
+
+       va_start(args, fmt);
+       n = format_packet(fmt, args);
+       va_end(args);
        safe_write(fd, buffer, n);
 }
 
+void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
+{
+       va_list args;
+       unsigned n;
+
+       va_start(args, fmt);
+       n = format_packet(fmt, args);
+       va_end(args);
+       strbuf_add(buf, buffer, n);
+}
+
 static void safe_read(int fd, void *buffer, unsigned size)
 {
        ssize_t ret = read_in_full(fd, buffer, size);
@@ -72,15 +96,11 @@ static void safe_read(int fd, void *buffer, unsigned size)
                die("The remote end hung up unexpectedly");
 }
 
-int packet_read_line(int fd, char *buffer, unsigned size)
+static int packet_length(const char *linelen)
 {
        int n;
-       unsigned len;
-       char linelen[4];
+       int len = 0;
 
-       safe_read(fd, linelen, 4);
-
-       len = 0;
        for (n = 0; n < 4; n++) {
                unsigned char c = linelen[n];
                len <<= 4;
@@ -96,8 +116,20 @@ int packet_read_line(int fd, char *buffer, unsigned size)
                        len += c - 'A' + 10;
                        continue;
                }
-               die("protocol error: bad line length character");
+               return -1;
        }
+       return len;
+}
+
+int packet_read_line(int fd, char *buffer, unsigned size)
+{
+       int len;
+       char linelen[4];
+
+       safe_read(fd, linelen, 4);
+       len = packet_length(linelen);
+       if (len < 0)
+               die("protocol error: bad line length character: %.4s", linelen);
        if (!len)
                return 0;
        len -= 4;
@@ -107,3 +139,31 @@ int packet_read_line(int fd, char *buffer, unsigned size)
        buffer[len] = 0;
        return len;
 }
+
+int packet_get_line(struct strbuf *out,
+       char **src_buf, size_t *src_len)
+{
+       int len;
+
+       if (*src_len < 4)
+               return -1;
+       len = packet_length(*src_buf);
+       if (len < 0)
+               return -1;
+       if (!len) {
+               *src_buf += 4;
+               *src_len -= 4;
+               return 0;
+       }
+       if (*src_len < len)
+               return -2;
+
+       *src_buf += 4;
+       *src_len -= 4;
+       len -= 4;
+
+       strbuf_add(out, *src_buf, len);
+       *src_buf += len;
+       *src_len -= len;
+       return len;
+}
index 9df653f6f5afe720870658d7093bddbf3e66beaf..1e5dcfe87c568eaa7746091b1eb059834f4913bb 100644 (file)
@@ -2,14 +2,18 @@
 #define PKTLINE_H
 
 #include "git-compat-util.h"
+#include "strbuf.h"
 
 /*
  * Silly packetized line writing interface
  */
 void packet_flush(int fd);
 void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
+void packet_buf_flush(struct strbuf *buf);
+void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
 
 int packet_read_line(int fd, char *buffer, unsigned size);
+int packet_get_line(struct strbuf *out, char **src_buf, size_t *src_len);
 ssize_t safe_write(int, const void *, ssize_t);
 
 #endif
index da15cf2a80d08e38b4b0bb279e378b72dbe49cc7..5661cba5952e2e68dd067a8afebe144bc1ca63af 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -6,6 +6,7 @@
 #include "string-list.h"
 #include "mailmap.h"
 #include "log-tree.h"
+#include "notes.h"
 #include "color.h"
 #include "reflog-walk.h"
 
@@ -446,6 +447,7 @@ struct format_commit_context {
        const struct pretty_print_context *pretty_ctx;
        unsigned commit_header_parsed:1;
        unsigned commit_message_parsed:1;
+       size_t width, indent1, indent2;
 
        /* These offsets are relative to the start of the commit message. */
        struct chunk author;
@@ -459,6 +461,7 @@ struct format_commit_context {
        struct chunk abbrev_commit_hash;
        struct chunk abbrev_tree_hash;
        struct chunk abbrev_parent_hashes;
+       size_t wrap_start;
 };
 
 static int add_again(struct strbuf *sb, struct chunk *chunk)
@@ -596,6 +599,35 @@ static void format_decoration(struct strbuf *sb, const struct commit *commit)
                strbuf_addch(sb, ')');
 }
 
+static void strbuf_wrap(struct strbuf *sb, size_t pos,
+                       size_t width, size_t indent1, size_t indent2)
+{
+       struct strbuf tmp = STRBUF_INIT;
+
+       if (pos)
+               strbuf_add(&tmp, sb->buf, pos);
+       strbuf_add_wrapped_text(&tmp, sb->buf + pos,
+                               (int) indent1, (int) indent2, (int) width);
+       strbuf_swap(&tmp, sb);
+       strbuf_release(&tmp);
+}
+
+static void rewrap_message_tail(struct strbuf *sb,
+                               struct format_commit_context *c,
+                               size_t new_width, size_t new_indent1,
+                               size_t new_indent2)
+{
+       if (c->width == new_width && c->indent1 == new_indent1 &&
+           c->indent2 == new_indent2)
+               return;
+       if (c->wrap_start < sb->len)
+               strbuf_wrap(sb, c->wrap_start, c->width, c->indent1, c->indent2);
+       c->wrap_start = sb->len;
+       c->width = new_width;
+       c->indent1 = new_indent1;
+       c->indent2 = new_indent2;
+}
+
 static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                                void *context)
 {
@@ -646,6 +678,30 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                        return 3;
                } else
                        return 0;
+       case 'w':
+               if (placeholder[1] == '(') {
+                       unsigned long width = 0, indent1 = 0, indent2 = 0;
+                       char *next;
+                       const char *start = placeholder + 2;
+                       const char *end = strchr(start, ')');
+                       if (!end)
+                               return 0;
+                       if (end > start) {
+                               width = strtoul(start, &next, 10);
+                               if (*next == ',') {
+                                       indent1 = strtoul(next + 1, &next, 10);
+                                       if (*next == ',') {
+                                               indent2 = strtoul(next + 1,
+                                                                &next, 10);
+                                       }
+                               }
+                               if (*next != ')')
+                                       return 0;
+                       }
+                       rewrap_message_tail(sb, c, width, indent1, indent2);
+                       return end - placeholder + 1;
+               } else
+                       return 0;
        }
 
        /* these depend on the commit */
@@ -718,6 +774,10 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                        return 2;
                }
                return 0;       /* unknown %g placeholder */
+       case 'N':
+               get_commit_notes(commit, sb, git_log_output_encoding ?
+                            git_log_output_encoding : git_commit_encoding, 0);
+               return 1;
        }
 
        /* For the rest we have to parse the commit header. */
@@ -765,7 +825,9 @@ void format_commit_message(const struct commit *commit,
        memset(&context, 0, sizeof(context));
        context.commit = commit;
        context.pretty_ctx = pretty_ctx;
+       context.wrap_start = sb->len;
        strbuf_expand(sb, format, format_commit_item, &context);
+       rewrap_message_tail(sb, &context, 0, 0, 0);
 }
 
 static void pp_header(enum cmit_fmt fmt,
@@ -993,5 +1055,10 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
         */
        if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
+
+       if (fmt != CMIT_FMT_ONELINE)
+               get_commit_notes(commit, sb, encoding,
+                                NOTES_SHOW_HEADER | NOTES_INDENT);
+
        free(reencoded);
 }
diff --git a/refs.c b/refs.c
index 808f56bb27b6ece616f400154fe98c9cef12f8ba..3e73a0a36dbea4046e5b7b0550b2f4bb67539439 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -286,6 +286,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
 }
 
 struct warn_if_dangling_data {
+       FILE *fp;
        const char *refname;
        const char *msg_fmt;
 };
@@ -304,13 +305,13 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
        if (!resolves_to || strcmp(resolves_to, d->refname))
                return 0;
 
-       printf(d->msg_fmt, refname);
+       fprintf(d->fp, d->msg_fmt, refname);
        return 0;
 }
 
-void warn_dangling_symref(const char *msg_fmt, const char *refname)
+void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 {
-       struct warn_if_dangling_data data = { refname, msg_fmt };
+       struct warn_if_dangling_data data = { fp, refname, msg_fmt };
        for_each_rawref(warn_if_dangling_symref, &data);
 }
 
diff --git a/refs.h b/refs.h
index 777b5b7ca6c08ef63f44abbf0236fc180cb9c6d1..e141991851be60334ea23cc6055b329c36cd272f 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -29,7 +29,7 @@ extern int for_each_replace_ref(each_ref_fn, void *);
 /* can be used to learn about broken ref and symref */
 extern int for_each_rawref(each_ref_fn, void *);
 
-extern void warn_dangling_symref(const char *msg_fmt, const char *refname);
+extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
 
 /*
  * Extra refs will be listed by for_each_ref() before any actual refs
index ebdab3603ec767b42ce04e0de2c3deeb516f2525..a331bae6c8e95042dc2d136fdc1ef6b3d4463c53 100644 (file)
 #include "walker.h"
 #include "http.h"
 #include "exec_cmd.h"
+#include "run-command.h"
+#include "pkt-line.h"
+#include "sideband.h"
 
-static struct ref *get_refs(struct walker *walker, const char *url)
+static struct remote *remote;
+static const char *url;
+static struct walker *walker;
+
+struct options {
+       int verbosity;
+       unsigned long depth;
+       unsigned progress : 1,
+               followtags : 1,
+               dry_run : 1,
+               thin : 1;
+};
+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")) {
+               char *end;
+               int v = strtol(value, &end, 10);
+               if (value == end || *end)
+                       return -1;
+               options.verbosity = v;
+               return 0;
+       }
+       else if (!strcmp(name, "progress")) {
+               if (!strcmp(value, "true"))
+                       options.progress = 1;
+               else if (!strcmp(value, "false"))
+                       options.progress = 0;
+               else
+                       return -1;
+               return 0;
+       }
+       else if (!strcmp(name, "depth")) {
+               char *end;
+               unsigned long v = strtoul(value, &end, 10);
+               if (value == end || *end)
+                       return -1;
+               options.depth = v;
+               return 0;
+       }
+       else if (!strcmp(name, "followtags")) {
+               if (!strcmp(value, "true"))
+                       options.followtags = 1;
+               else if (!strcmp(value, "false"))
+                       options.followtags = 0;
+               else
+                       return -1;
+               return 0;
+       }
+       else if (!strcmp(name, "dry-run")) {
+               if (!strcmp(value, "true"))
+                       options.dry_run = 1;
+               else if (!strcmp(value, "false"))
+                       options.dry_run = 0;
+               else
+                       return -1;
+               return 0;
+       }
+       else {
+               return 1 /* unsupported */;
+       }
+}
+
+struct discovery {
+       const char *service;
+       char *buf_alloc;
+       char *buf;
+       size_t len;
+       unsigned proto_git : 1;
+};
+static struct discovery *last_discovery;
+
+static void free_discovery(struct discovery *d)
+{
+       if (d) {
+               if (d == last_discovery)
+                       last_discovery = NULL;
+               free(d->buf_alloc);
+               free(d);
+       }
+}
+
+static struct discovery* discover_refs(const char *service)
 {
        struct strbuf buffer = STRBUF_INIT;
-       char *data, *start, *mid;
-       char *ref_name;
+       struct discovery *last = last_discovery;
        char *refs_url;
-       int i = 0;
-       int http_ret;
+       int http_ret, is_http = 0;
 
-       struct ref *refs = NULL;
-       struct ref *ref = NULL;
-       struct ref *last_ref = NULL;
+       if (last && !strcmp(service, last->service))
+               return last;
+       free_discovery(last);
 
-       refs_url = xmalloc(strlen(url) + 11);
-       sprintf(refs_url, "%s/info/refs", url);
+       strbuf_addf(&buffer, "%s/info/refs", url);
+       if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) {
+               is_http = 1;
+               if (!strchr(url, '?'))
+                       strbuf_addch(&buffer, '?');
+               else
+                       strbuf_addch(&buffer, '&');
+               strbuf_addf(&buffer, "service=%s", service);
+       }
+       refs_url = strbuf_detach(&buffer, NULL);
 
+       init_walker();
        http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
        switch (http_ret) {
        case HTTP_OK:
@@ -33,10 +132,86 @@ static struct ref *get_refs(struct walker *walker, const char *url)
                die("HTTP request failed");
        }
 
-       data = buffer.buf;
+       last= xcalloc(1, sizeof(*last_discovery));
+       last->service = service;
+       last->buf_alloc = strbuf_detach(&buffer, &last->len);
+       last->buf = last->buf_alloc;
+
+       if (is_http && 5 <= last->len && last->buf[4] == '#') {
+               /* smart HTTP response; validate that the service
+                * pkt-line matches our request.
+                */
+               struct strbuf exp = STRBUF_INIT;
+
+               if (packet_get_line(&buffer, &last->buf, &last->len) <= 0)
+                       die("%s has invalid packet header", refs_url);
+               if (buffer.len && buffer.buf[buffer.len - 1] == '\n')
+                       strbuf_setlen(&buffer, buffer.len - 1);
+
+               strbuf_addf(&exp, "# service=%s", service);
+               if (strbuf_cmp(&exp, &buffer))
+                       die("invalid server response; got '%s'", buffer.buf);
+               strbuf_release(&exp);
+
+               /* The header can include additional metadata lines, up
+                * until a packet flush marker.  Ignore these now, but
+                * in the future we might start to scan them.
+                */
+               strbuf_reset(&buffer);
+               while (packet_get_line(&buffer, &last->buf, &last->len) > 0)
+                       strbuf_reset(&buffer);
+
+               last->proto_git = 1;
+       }
+
+       free(refs_url);
+       strbuf_release(&buffer);
+       last_discovery = last;
+       return last;
+}
+
+static int write_discovery(int fd, void *data)
+{
+       struct discovery *heads = data;
+       int err = 0;
+       if (write_in_full(fd, heads->buf, heads->len) != heads->len)
+               err = 1;
+       close(fd);
+       return err;
+}
+
+static struct ref *parse_git_refs(struct discovery *heads)
+{
+       struct ref *list = NULL;
+       struct async async;
+
+       memset(&async, 0, sizeof(async));
+       async.proc = write_discovery;
+       async.data = heads;
+
+       if (start_async(&async))
+               die("cannot start thread to parse advertised refs");
+       get_remote_heads(async.out, &list, 0, NULL, 0, NULL);
+       close(async.out);
+       if (finish_async(&async))
+               die("ref parsing thread failed");
+       return list;
+}
+
+static struct ref *parse_info_refs(struct discovery *heads)
+{
+       char *data, *start, *mid;
+       char *ref_name;
+       int i = 0;
+
+       struct ref *refs = NULL;
+       struct ref *ref = NULL;
+       struct ref *last_ref = NULL;
+
+       data = heads->buf;
        start = NULL;
        mid = data;
-       while (i < buffer.len) {
+       while (i < heads->len) {
                if (!start) {
                        start = &data[i];
                }
@@ -60,8 +235,7 @@ static struct ref *get_refs(struct walker *walker, const char *url)
                i++;
        }
 
-       strbuf_release(&buffer);
-
+       init_walker();
        ref = alloc_ref("HEAD");
        if (!walker->fetch_ref(walker, ref) &&
            !resolve_remote_symref(ref, refs)) {
@@ -71,17 +245,506 @@ static struct ref *get_refs(struct walker *walker, const char *url)
                free(ref);
        }
 
-       strbuf_release(&buffer);
-       free(refs_url);
        return refs;
 }
 
+static struct ref *get_refs(int for_push)
+{
+       struct discovery *heads;
+
+       if (for_push)
+               heads = discover_refs("git-receive-pack");
+       else
+               heads = discover_refs("git-upload-pack");
+
+       if (heads->proto_git)
+               return parse_git_refs(heads);
+       return parse_info_refs(heads);
+}
+
+static void output_refs(struct ref *refs)
+{
+       struct ref *posn;
+       for (posn = refs; posn; posn = posn->next) {
+               if (posn->symref)
+                       printf("@%s %s\n", posn->symref, posn->name);
+               else
+                       printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
+       }
+       printf("\n");
+       fflush(stdout);
+       free_refs(refs);
+}
+
+struct rpc_state {
+       const char *service_name;
+       const char **argv;
+       char *service_url;
+       char *hdr_content_type;
+       char *hdr_accept;
+       char *buf;
+       size_t alloc;
+       size_t len;
+       size_t pos;
+       int in;
+       int out;
+       struct strbuf result;
+       unsigned gzip_request : 1;
+};
+
+static size_t rpc_out(void *ptr, size_t eltsize,
+               size_t nmemb, void *buffer_)
+{
+       size_t max = eltsize * nmemb;
+       struct rpc_state *rpc = buffer_;
+       size_t avail = rpc->len - rpc->pos;
+
+       if (!avail) {
+               avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
+               if (!avail)
+                       return 0;
+               rpc->pos = 0;
+               rpc->len = avail;
+       }
+
+       if (max < avail)
+               avail = max;
+       memcpy(ptr, rpc->buf + rpc->pos, avail);
+       rpc->pos += avail;
+       return avail;
+}
+
+static size_t rpc_in(const void *ptr, size_t eltsize,
+               size_t nmemb, void *buffer_)
+{
+       size_t size = eltsize * nmemb;
+       struct rpc_state *rpc = buffer_;
+       write_or_die(rpc->in, ptr, size);
+       return size;
+}
+
+static int post_rpc(struct rpc_state *rpc)
+{
+       struct active_request_slot *slot;
+       struct slot_results results;
+       struct curl_slist *headers = NULL;
+       int use_gzip = rpc->gzip_request;
+       char *gzip_body = NULL;
+       int err = 0, large_request = 0;
+
+       /* Try to load the entire request, if we can fit it into the
+        * allocated buffer space we can use HTTP/1.0 and avoid the
+        * chunked encoding mess.
+        */
+       while (1) {
+               size_t left = rpc->alloc - rpc->len;
+               char *buf = rpc->buf + rpc->len;
+               int n;
+
+               if (left < LARGE_PACKET_MAX) {
+                       large_request = 1;
+                       use_gzip = 0;
+                       break;
+               }
+
+               n = packet_read_line(rpc->out, buf, left);
+               if (!n)
+                       break;
+               rpc->len += n;
+       }
+
+       slot = get_active_slot();
+       slot->results = &results;
+
+       curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
+       curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
+       curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
+       curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
+
+       headers = curl_slist_append(headers, rpc->hdr_content_type);
+       headers = curl_slist_append(headers, rpc->hdr_accept);
+
+       if (large_request) {
+               /* The request body is large and the size cannot be predicted.
+                * We must use chunked encoding to send it.
+                */
+               headers = curl_slist_append(headers, "Expect: 100-continue");
+               headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
+               curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
+               curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
+               if (options.verbosity > 1) {
+                       fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
+                       fflush(stderr);
+               }
+
+       } else if (use_gzip && 1024 < rpc->len) {
+               /* The client backend isn't giving us compressed data so
+                * we can try to deflate it ourselves, this may save on.
+                * the transfer time.
+                */
+               size_t size;
+               z_stream stream;
+               int ret;
+
+               memset(&stream, 0, sizeof(stream));
+               ret = deflateInit2(&stream, Z_BEST_COMPRESSION,
+                               Z_DEFLATED, (15 + 16),
+                               8, Z_DEFAULT_STRATEGY);
+               if (ret != Z_OK)
+                       die("cannot deflate request; zlib init error %d", ret);
+               size = deflateBound(&stream, rpc->len);
+               gzip_body = xmalloc(size);
+
+               stream.next_in = (unsigned char *)rpc->buf;
+               stream.avail_in = rpc->len;
+               stream.next_out = (unsigned char *)gzip_body;
+               stream.avail_out = size;
+
+               ret = deflate(&stream, Z_FINISH);
+               if (ret != Z_STREAM_END)
+                       die("cannot deflate request; zlib deflate error %d", ret);
+
+               ret = deflateEnd(&stream);
+               if (ret != Z_OK)
+                       die("cannot deflate request; zlib end error %d", ret);
+
+               size = stream.total_out;
+
+               headers = curl_slist_append(headers, "Content-Encoding: gzip");
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, size);
+
+               if (options.verbosity > 1) {
+                       fprintf(stderr, "POST %s (gzip %lu to %lu bytes)\n",
+                               rpc->service_name,
+                               (unsigned long)rpc->len, (unsigned long)size);
+                       fflush(stderr);
+               }
+       } else {
+               /* We know the complete request size in advance, use the
+                * more normal Content-Length approach.
+                */
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, rpc->buf);
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, rpc->len);
+               if (options.verbosity > 1) {
+                       fprintf(stderr, "POST %s (%lu bytes)\n",
+                               rpc->service_name, (unsigned long)rpc->len);
+                       fflush(stderr);
+               }
+       }
+
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
+       curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
+
+       slot->curl_result = curl_easy_perform(slot->curl);
+       finish_active_slot(slot);
+
+       if (results.curl_result != CURLE_OK) {
+               err |= error("RPC failed; result=%d, HTTP code = %ld",
+                       results.curl_result, results.http_code);
+       }
+
+       curl_slist_free_all(headers);
+       free(gzip_body);
+       return err;
+}
+
+static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
+{
+       const char *svc = rpc->service_name;
+       struct strbuf buf = STRBUF_INIT;
+       struct child_process client;
+       int err = 0;
+
+       init_walker();
+       memset(&client, 0, sizeof(client));
+       client.in = -1;
+       client.out = -1;
+       client.git_cmd = 1;
+       client.argv = rpc->argv;
+       if (start_command(&client))
+               exit(1);
+       if (heads)
+               write_or_die(client.in, heads->buf, heads->len);
+
+       rpc->alloc = http_post_buffer;
+       rpc->buf = xmalloc(rpc->alloc);
+       rpc->in = client.in;
+       rpc->out = client.out;
+       strbuf_init(&rpc->result, 0);
+
+       strbuf_addf(&buf, "%s/%s", url, svc);
+       rpc->service_url = strbuf_detach(&buf, NULL);
+
+       strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
+       rpc->hdr_content_type = strbuf_detach(&buf, NULL);
+
+       strbuf_addf(&buf, "Accept: application/x-%s-response", svc);
+       rpc->hdr_accept = strbuf_detach(&buf, NULL);
+
+       while (!err) {
+               int n = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
+               if (!n)
+                       break;
+               rpc->pos = 0;
+               rpc->len = n;
+               err |= post_rpc(rpc);
+       }
+       strbuf_read(&rpc->result, client.out, 0);
+
+       close(client.in);
+       close(client.out);
+       client.in = -1;
+       client.out = -1;
+
+       err |= finish_command(&client);
+       free(rpc->service_url);
+       free(rpc->hdr_content_type);
+       free(rpc->hdr_accept);
+       free(rpc->buf);
+       strbuf_release(&buf);
+       return err;
+}
+
+static int fetch_dumb(int nr_heads, struct ref **to_fetch)
+{
+       char **targets = xmalloc(nr_heads * sizeof(char*));
+       int ret, i;
+
+       if (options.depth)
+               die("dumb http transport does not support --depth");
+       for (i = 0; i < nr_heads; i++)
+               targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
+
+       init_walker();
+       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);
+
+       for (i = 0; i < nr_heads; i++)
+               free(targets[i]);
+       free(targets);
+
+       return ret ? error("Fetch failed.") : 0;
+}
+
+static int fetch_git(struct discovery *heads,
+       int nr_heads, struct ref **to_fetch)
+{
+       struct rpc_state rpc;
+       char *depth_arg = NULL;
+       const char **argv;
+       int argc = 0, i, err;
+
+       argv = xmalloc((15 + nr_heads) * sizeof(char*));
+       argv[argc++] = "fetch-pack";
+       argv[argc++] = "--stateless-rpc";
+       argv[argc++] = "--lock-pack";
+       if (options.followtags)
+               argv[argc++] = "--include-tag";
+       if (options.thin)
+               argv[argc++] = "--thin";
+       if (options.verbosity >= 3) {
+               argv[argc++] = "-v";
+               argv[argc++] = "-v";
+       }
+       if (!options.progress)
+               argv[argc++] = "--no-progress";
+       if (options.depth) {
+               struct strbuf buf = STRBUF_INIT;
+               strbuf_addf(&buf, "--depth=%lu", options.depth);
+               depth_arg = strbuf_detach(&buf, NULL);
+               argv[argc++] = depth_arg;
+       }
+       argv[argc++] = url;
+       for (i = 0; i < nr_heads; i++) {
+               struct ref *ref = to_fetch[i];
+               if (!ref->name || !*ref->name)
+                       die("cannot fetch by sha1 over smart http");
+               argv[argc++] = ref->name;
+       }
+       argv[argc++] = NULL;
+
+       memset(&rpc, 0, sizeof(rpc));
+       rpc.service_name = "git-upload-pack",
+       rpc.argv = argv;
+       rpc.gzip_request = 1;
+
+       err = rpc_service(&rpc, heads);
+       if (rpc.result.len)
+               safe_write(1, rpc.result.buf, rpc.result.len);
+       strbuf_release(&rpc.result);
+       free(argv);
+       free(depth_arg);
+       return err;
+}
+
+static int fetch(int nr_heads, struct ref **to_fetch)
+{
+       struct discovery *d = discover_refs("git-upload-pack");
+       if (d->proto_git)
+               return fetch_git(d, nr_heads, to_fetch);
+       else
+               return fetch_dumb(nr_heads, to_fetch);
+}
+
+static void parse_fetch(struct strbuf *buf)
+{
+       struct ref **to_fetch = NULL;
+       struct ref *list_head = NULL;
+       struct ref **list = &list_head;
+       int alloc_heads = 0, nr_heads = 0;
+
+       do {
+               if (!prefixcmp(buf->buf, "fetch ")) {
+                       char *p = buf->buf + strlen("fetch ");
+                       char *name;
+                       struct ref *ref;
+                       unsigned char old_sha1[20];
+
+                       if (strlen(p) < 40 || get_sha1_hex(p, old_sha1))
+                               die("protocol error: expected sha/ref, got %s'", p);
+                       if (p[40] == ' ')
+                               name = p + 41;
+                       else if (!p[40])
+                               name = "";
+                       else
+                               die("protocol error: expected sha/ref, got %s'", p);
+
+                       ref = alloc_ref(name);
+                       hashcpy(ref->old_sha1, old_sha1);
+
+                       *list = ref;
+                       list = &ref->next;
+
+                       ALLOC_GROW(to_fetch, nr_heads + 1, alloc_heads);
+                       to_fetch[nr_heads++] = ref;
+               }
+               else
+                       die("http transport does not support %s", buf->buf);
+
+               strbuf_reset(buf);
+               if (strbuf_getline(buf, stdin, '\n') == EOF)
+                       return;
+               if (!*buf->buf)
+                       break;
+       } while (1);
+
+       if (fetch(nr_heads, to_fetch))
+               exit(128); /* error already reported */
+       free_refs(list_head);
+       free(to_fetch);
+
+       printf("\n");
+       fflush(stdout);
+       strbuf_reset(buf);
+}
+
+static int push_dav(int nr_spec, char **specs)
+{
+       const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
+       int argc = 0, i;
+
+       argv[argc++] = "http-push";
+       argv[argc++] = "--helper-status";
+       if (options.dry_run)
+               argv[argc++] = "--dry-run";
+       if (options.verbosity > 1)
+               argv[argc++] = "--verbose";
+       argv[argc++] = url;
+       for (i = 0; i < nr_spec; i++)
+               argv[argc++] = specs[i];
+       argv[argc++] = NULL;
+
+       if (run_command_v_opt(argv, RUN_GIT_CMD))
+               die("git-%s failed", argv[0]);
+       free(argv);
+       return 0;
+}
+
+static int push_git(struct discovery *heads, int nr_spec, char **specs)
+{
+       struct rpc_state rpc;
+       const char **argv;
+       int argc = 0, i, err;
+
+       argv = xmalloc((10 + nr_spec) * sizeof(char*));
+       argv[argc++] = "send-pack";
+       argv[argc++] = "--stateless-rpc";
+       argv[argc++] = "--helper-status";
+       if (options.thin)
+               argv[argc++] = "--thin";
+       if (options.dry_run)
+               argv[argc++] = "--dry-run";
+       if (options.verbosity > 1)
+               argv[argc++] = "--verbose";
+       argv[argc++] = url;
+       for (i = 0; i < nr_spec; i++)
+               argv[argc++] = specs[i];
+       argv[argc++] = NULL;
+
+       memset(&rpc, 0, sizeof(rpc));
+       rpc.service_name = "git-receive-pack",
+       rpc.argv = argv;
+
+       err = rpc_service(&rpc, heads);
+       if (rpc.result.len)
+               safe_write(1, rpc.result.buf, rpc.result.len);
+       strbuf_release(&rpc.result);
+       free(argv);
+       return err;
+}
+
+static int push(int nr_spec, char **specs)
+{
+       struct discovery *heads = discover_refs("git-receive-pack");
+       int ret;
+
+       if (heads->proto_git)
+               ret = push_git(heads, nr_spec, specs);
+       else
+               ret = push_dav(nr_spec, specs);
+       free_discovery(heads);
+       return ret;
+}
+
+static void parse_push(struct strbuf *buf)
+{
+       char **specs = NULL;
+       int alloc_spec = 0, nr_spec = 0, i;
+
+       do {
+               if (!prefixcmp(buf->buf, "push ")) {
+                       ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
+                       specs[nr_spec++] = xstrdup(buf->buf + 5);
+               }
+               else
+                       die("http transport does not support %s", buf->buf);
+
+               strbuf_reset(buf);
+               if (strbuf_getline(buf, stdin, '\n') == EOF)
+                       return;
+               if (!*buf->buf)
+                       break;
+       } while (1);
+
+       if (push(nr_spec, specs))
+               exit(128); /* error already reported */
+       for (i = 0; i < nr_spec; i++)
+               free(specs[i]);
+       free(specs);
+
+       printf("\n");
+       fflush(stdout);
+}
+
 int main(int argc, const char **argv)
 {
-       struct remote *remote;
        struct strbuf buf = STRBUF_INIT;
-       const char *url;
-       struct walker *walker = NULL;
        int nongit;
 
        git_extract_argv0_path(argv[0]);
@@ -91,6 +754,10 @@ int main(int argc, const char **argv)
                return 1;
        }
 
+       options.verbosity = 1;
+       options.progress = !!isatty(2);
+       options.thin = 1;
+
        remote = remote_get(argv[1]);
 
        if (argc > 2) {
@@ -103,36 +770,40 @@ int main(int argc, const char **argv)
                if (strbuf_getline(&buf, stdin, '\n') == EOF)
                        break;
                if (!prefixcmp(buf.buf, "fetch ")) {
-                       char *obj = buf.buf + strlen("fetch ");
                        if (nongit)
                                die("Fetch attempted without a local repo");
-                       if (!walker)
-                               walker = get_http_walker(url, remote);
-                       walker->get_all = 1;
-                       walker->get_tree = 1;
-                       walker->get_history = 1;
-                       walker->get_verbosely = 0;
-                       walker->get_recover = 0;
-                       if (walker_fetch(walker, 1, &obj, NULL, NULL))
-                               die("Fetch failed.");
-                       printf("\n");
-                       fflush(stdout);
-               } else if (!strcmp(buf.buf, "list")) {
-                       struct ref *refs;
-                       struct ref *posn;
-                       if (!walker)
-                               walker = get_http_walker(url, remote);
-                       refs = get_refs(walker, url);
-                       for (posn = refs; posn; posn = posn->next) {
-                               if (posn->symref)
-                                       printf("@%s %s\n", posn->symref, posn->name);
-                               else
-                                       printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
-                       }
-                       printf("\n");
+                       parse_fetch(&buf);
+
+               } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
+                       int for_push = !!strstr(buf.buf + 4, "for-push");
+                       output_refs(get_refs(for_push));
+
+               } else if (!prefixcmp(buf.buf, "push ")) {
+                       parse_push(&buf);
+
+               } else if (!prefixcmp(buf.buf, "option ")) {
+                       char *name = buf.buf + strlen("option ");
+                       char *value = strchr(name, ' ');
+                       int result;
+
+                       if (value)
+                               *value++ = '\0';
+                       else
+                               value = "true";
+
+                       result = set_option(name, value);
+                       if (!result)
+                               printf("ok\n");
+                       else if (result < 0)
+                               printf("error invalid value\n");
+                       else
+                               printf("unsupported\n");
                        fflush(stdout);
+
                } else if (!strcmp(buf.buf, "capabilities")) {
                        printf("fetch\n");
+                       printf("option\n");
+                       printf("push\n");
                        printf("\n");
                        fflush(stdout);
                } else {
index 73d33f2584b061085fcaa0958c26b1c3d4665904..b979a9642b81b456ab92cdf57e8eda944f01f92e 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -6,6 +6,7 @@
 #include "revision.h"
 #include "dir.h"
 #include "tag.h"
+#include "string-list.h"
 
 static struct refspec s_tag_refspec = {
        0,
@@ -396,7 +397,8 @@ static int handle_config(const char *key, const char *value, void *cb)
                remote->mirror = git_config_bool(key, value);
        else if (!strcmp(subkey, ".skipdefaultupdate"))
                remote->skip_default_update = git_config_bool(key, value);
-
+       else if (!strcmp(subkey, ".skipfetchall"))
+               remote->skip_default_update = git_config_bool(key, value);
        else if (!strcmp(subkey, ".url")) {
                const char *v;
                if (git_config_string(&v, key, value))
@@ -734,29 +736,33 @@ int for_each_remote(each_remote_fn fn, void *priv)
 
 void ref_remove_duplicates(struct ref *ref_map)
 {
-       struct ref **posn;
-       struct ref *next;
-       for (; ref_map; ref_map = ref_map->next) {
+       struct string_list refs = { NULL, 0, 0, 0 };
+       struct string_list_item *item = NULL;
+       struct ref *prev = NULL, *next = NULL;
+       for (; ref_map; prev = ref_map, ref_map = next) {
+               next = ref_map->next;
                if (!ref_map->peer_ref)
                        continue;
-               posn = &ref_map->next;
-               while (*posn) {
-                       if ((*posn)->peer_ref &&
-                           !strcmp((*posn)->peer_ref->name,
-                                   ref_map->peer_ref->name)) {
-                               if (strcmp((*posn)->name, ref_map->name))
-                                       die("%s tracks both %s and %s",
-                                           ref_map->peer_ref->name,
-                                           (*posn)->name, ref_map->name);
-                               next = (*posn)->next;
-                               free((*posn)->peer_ref);
-                               free(*posn);
-                               *posn = next;
-                       } else {
-                               posn = &(*posn)->next;
-                       }
+
+               item = string_list_lookup(ref_map->peer_ref->name, &refs);
+               if (item) {
+                       if (strcmp(((struct ref *)item->util)->name,
+                                  ref_map->name))
+                               die("%s tracks both %s and %s",
+                                   ref_map->peer_ref->name,
+                                   ((struct ref *)item->util)->name,
+                                   ref_map->name);
+                       prev->next = ref_map->next;
+                       free(ref_map->peer_ref);
+                       free(ref_map);
+                       ref_map = prev; /* skip this; we freed it */
+                       continue;
                }
+
+               item = string_list_insert(ref_map->peer_ref->name, &refs);
+               item->util = ref_map;
        }
+       string_list_clear(&refs, 0);
 }
 
 int remote_has_url(struct remote *remote, const char *url)
@@ -1586,3 +1592,42 @@ struct ref *guess_remote_head(const struct ref *head,
 
        return list;
 }
+
+struct stale_heads_info {
+       struct remote *remote;
+       struct string_list *ref_names;
+       struct ref **stale_refs_tail;
+};
+
+static int get_stale_heads_cb(const char *refname,
+       const unsigned char *sha1, int flags, void *cb_data)
+{
+       struct stale_heads_info *info = cb_data;
+       struct refspec refspec;
+       memset(&refspec, 0, sizeof(refspec));
+       refspec.dst = (char *)refname;
+       if (!remote_find_tracking(info->remote, &refspec)) {
+               if (!((flags & REF_ISSYMREF) ||
+                   string_list_has_string(info->ref_names, refspec.src))) {
+                       struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
+                       hashcpy(ref->new_sha1, sha1);
+               }
+       }
+       return 0;
+}
+
+struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
+{
+       struct ref *ref, *stale_refs = NULL;
+       struct string_list ref_names = { NULL, 0, 0, 0 };
+       struct stale_heads_info info;
+       info.remote = remote;
+       info.ref_names = &ref_names;
+       info.stale_refs_tail = &stale_refs;
+       for (ref = fetch_map; ref; ref = ref->next)
+               string_list_append(ref->name, &ref_names);
+       sort_string_list(&ref_names);
+       for_each_ref(get_stale_heads_cb, &info);
+       string_list_clear(&ref_names, 0);
+       return stale_refs;
+}
index 5db842087da081abbad96c8f9eed5829140714d2..d0aba81ead1847e43a971362659abf1c1737c12f 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -154,4 +154,7 @@ struct ref *guess_remote_head(const struct ref *head,
                              const struct ref *refs,
                              int all);
 
+/* Return refs which no longer exist on remote */
+struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map);
+
 #endif
index 9fc4e8d3818f29261b1963f4995bc36f8af31179..a8a3c3a4bdf5bd9287cb820330e73e8c28b88564 100644 (file)
@@ -953,21 +953,59 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
        return 0;
 }
 
-void read_revisions_from_stdin(struct rev_info *revs)
+static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb, const char ***prune_data)
 {
-       char line[1000];
+       const char **prune = *prune_data;
+       int prune_nr;
+       int prune_alloc;
 
-       while (fgets(line, sizeof(line), stdin) != NULL) {
-               int len = strlen(line);
-               if (len && line[len - 1] == '\n')
-                       line[--len] = '\0';
+       /* count existing ones */
+       if (!prune)
+               prune_nr = 0;
+       else
+               for (prune_nr = 0; prune[prune_nr]; prune_nr++)
+                       ;
+       prune_alloc = prune_nr; /* not really, but we do not know */
+
+       while (strbuf_getwholeline(sb, stdin, '\n') != EOF) {
+               int len = sb->len;
+               if (len && sb->buf[len - 1] == '\n')
+                       sb->buf[--len] = '\0';
+               ALLOC_GROW(prune, prune_nr+1, prune_alloc);
+               prune[prune_nr++] = xstrdup(sb->buf);
+       }
+       if (prune) {
+               ALLOC_GROW(prune, prune_nr+1, prune_alloc);
+               prune[prune_nr] = NULL;
+       }
+       *prune_data = prune;
+}
+
+static void read_revisions_from_stdin(struct rev_info *revs, const char ***prune)
+{
+       struct strbuf sb;
+       int seen_dashdash = 0;
+
+       strbuf_init(&sb, 1000);
+       while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
+               int len = sb.len;
+               if (len && sb.buf[len - 1] == '\n')
+                       sb.buf[--len] = '\0';
                if (!len)
                        break;
-               if (line[0] == '-')
+               if (sb.buf[0] == '-') {
+                       if (len == 2 && sb.buf[1] == '-') {
+                               seen_dashdash = 1;
+                               break;
+                       }
                        die("options not supported in --stdin mode");
-               if (handle_revision_arg(line, revs, 0, 1))
-                       die("bad revision '%s'", line);
+               }
+               if (handle_revision_arg(sb.buf, revs, 0, 1))
+                       die("bad revision '%s'", sb.buf);
        }
+       if (seen_dashdash)
+               read_pathspec_from_stdin(revs, &sb, prune);
+       strbuf_release(&sb);
 }
 
 static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
@@ -994,7 +1032,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
            !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
            !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
-           !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk"))
+           !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
+           !strcmp(arg, "--bisect"))
        {
                unkv[(*unkc)++] = arg;
                return 1;
@@ -1218,6 +1257,44 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
        ctx->argc -= n;
 }
 
+static int for_each_bad_bisect_ref(each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in("refs/bisect/bad", fn, cb_data);
+}
+
+static int for_each_good_bisect_ref(each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in("refs/bisect/good", fn, cb_data);
+}
+
+static void append_prune_data(const char ***prune_data, const char **av)
+{
+       const char **prune = *prune_data;
+       int prune_nr;
+       int prune_alloc;
+
+       if (!prune) {
+               *prune_data = av;
+               return;
+       }
+
+       /* count existing ones */
+       for (prune_nr = 0; prune[prune_nr]; prune_nr++)
+               ;
+       prune_alloc = prune_nr; /* not really, but we do not know */
+
+       while (*av) {
+               ALLOC_GROW(prune, prune_nr+1, prune_alloc);
+               prune[prune_nr++] = *av;
+               av++;
+       }
+       if (prune) {
+               ALLOC_GROW(prune, prune_nr+1, prune_alloc);
+               prune[prune_nr] = NULL;
+       }
+       *prune_data = prune;
+}
+
 /*
  * Parse revision information, filling in the "rev_info" structure,
  * and removing the used arguments from the argument list.
@@ -1227,7 +1304,8 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
  */
 int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
 {
-       int i, flags, left, seen_dashdash;
+       int i, flags, left, seen_dashdash, read_from_stdin;
+       const char **prune_data = NULL;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -1238,13 +1316,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                argv[i] = NULL;
                argc = i;
                if (argv[i + 1])
-                       revs->prune_data = get_pathspec(revs->prefix, argv + i + 1);
+                       prune_data = argv + i + 1;
                seen_dashdash = 1;
                break;
        }
 
        /* Second, deal with arguments and options */
        flags = 0;
+       read_from_stdin = 0;
        for (left = i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if (*arg == '-') {
@@ -1259,6 +1338,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                handle_refs(revs, flags, for_each_branch_ref);
                                continue;
                        }
+                       if (!strcmp(arg, "--bisect")) {
+                               handle_refs(revs, flags, for_each_bad_bisect_ref);
+                               handle_refs(revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
+                               revs->bisect = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--tags")) {
                                handle_refs(revs, flags, for_each_tag_ref);
                                continue;
@@ -1283,6 +1368,16 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->no_walk = 0;
                                continue;
                        }
+                       if (!strcmp(arg, "--stdin")) {
+                               if (revs->disable_stdin) {
+                                       argv[left++] = arg;
+                                       continue;
+                               }
+                               if (read_from_stdin++)
+                                       die("--stdin given twice?");
+                               read_revisions_from_stdin(revs, &prune_data);
+                               continue;
+                       }
 
                        opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv);
                        if (opts > 0) {
@@ -1308,12 +1403,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        for (j = i; j < argc; j++)
                                verify_filename(revs->prefix, argv[j]);
 
-                       revs->prune_data = get_pathspec(revs->prefix,
-                                                       argv + i);
+                       append_prune_data(&prune_data, argv + i);
                        break;
                }
        }
 
+       if (prune_data)
+               revs->prune_data = get_pathspec(revs->prefix, prune_data);
+
        if (revs->def == NULL)
                revs->def = def;
        if (revs->show_merge)
index b6421a64321168237443ecb7e56e6b0765256c8b..d368003159738570220c0e4713efd91f83303339 100644 (file)
@@ -63,6 +63,7 @@ struct rev_info {
                        reverse:1,
                        reverse_output_stage:1,
                        cherry_pick:1,
+                       bisect:1,
                        first_parent_only:1;
 
        /* Diff flags */
@@ -83,6 +84,8 @@ struct rev_info {
                        use_terminator:1,
                        missing_newline:1,
                        date_mode_explicit:1;
+       unsigned int    disable_stdin:1;
+
        enum date_mode date_mode;
 
        unsigned int    abbrev;
@@ -128,8 +131,6 @@ struct rev_info {
 #define REV_TREE_DIFFERENT     3       /* Mixed changes */
 
 /* revision.c */
-void read_revisions_from_stdin(struct rev_info *revs);
-
 typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
 extern volatile show_early_output_fn_t show_early_output;
 
index 8b3cf028ed1c54f8a513ad0bcddc847ad47fe602..28141ac913f6558b81ff0b1b21a1e1b5ead43fa0 100644 (file)
@@ -8,7 +8,8 @@ struct send_pack_args {
                force_update:1,
                use_thin_pack:1,
                use_ofs_delta:1,
-               dry_run:1;
+               dry_run:1,
+               stateless_rpc:1;
 };
 
 int send_pack(struct send_pack_args *args,
index 45bb535773fd9c36f73502df9462f7de800009c8..63f9da53237d4233bede66a28e4bcf27d5b44af1 100644 (file)
@@ -1,6 +1,9 @@
 #include "cache.h"
 #include "pack.h"
 
+static const char show_index_usage[] =
+"git show-index < <packed archive index>";
+
 int main(int argc, char **argv)
 {
        int i;
@@ -8,6 +11,8 @@ int main(int argc, char **argv)
        unsigned int version;
        static unsigned int top_index[256];
 
+       if (argc != 1)
+               usage(show_index_usage);
        if (fread(top_index, 2 * 4, 1, stdin) != 1)
                die("unable to read header");
        if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {
index 899b1ff36619d0077dced9da77f538ab98396a83..d5ffa1c8919a6db750606c78a1b44d8618fa35a5 100644 (file)
@@ -135,9 +135,14 @@ ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet
                n = sz;
                if (packet_max - 5 < n)
                        n = packet_max - 5;
-               sprintf(hdr, "%04x", n + 5);
-               hdr[4] = band;
-               safe_write(fd, hdr, 5);
+               if (0 <= band) {
+                       sprintf(hdr, "%04x", n + 5);
+                       hdr[4] = band;
+                       safe_write(fd, hdr, 5);
+               } else {
+                       sprintf(hdr, "%04x", n + 4);
+                       safe_write(fd, hdr, 4);
+               }
                safe_write(fd, p, n);
                p += n;
                sz -= n;
index d05e056dd3916b64865ac9a0337ecbf31625f436..fa07ecf094bd3ef8997958fde29199f8fb6421b9 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -117,7 +117,7 @@ struct strbuf_expand_dict_entry {
 };
 extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
 
-__attribute__((format(printf,2,3)))
+__attribute__((format (printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
 
 extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
index 461faf0e000ee14cdd0bb3d40121e1d21833e30d..86aad653b7c113106e365a9d4e4c38b3e7737457 100644 (file)
@@ -38,7 +38,7 @@ void show_submodule_summary(FILE *f, const char *path,
                const char *del, const char *add, const char *reset)
 {
        struct rev_info rev;
-       struct commit *commit, *left = left, *right;
+       struct commit *commit, *left = left, *right = right;
        struct commit_list *merge_bases, *list;
        const char *message = NULL;
        struct strbuf sb = STRBUF_INIT;
index d8f6c7de6d27e27a33982e82893baa3bb5ffd7fd..4e1d7dd1833f43404a00b41d55800b9fe27d1807 100644 (file)
--- a/t/README
+++ b/t/README
@@ -75,6 +75,19 @@ appropriately before running "make".
        As the names depend on the tests' file names, it is safe to
        run the tests with this option in parallel.
 
+You can also set the GIT_TEST_INSTALLED environment variable to
+the bindir of an existing git installation to test that installation.
+You still need to have built this git sandbox, from which various
+test-* support programs, templates, and perl libraries are used.
+If your installed git is incomplete, it will silently test parts of
+your built version instead.
+
+When using GIT_TEST_INSTALLED, you can also set GIT_TEST_EXEC_PATH to
+override the location of the dashed-form subcommands (what
+GIT_EXEC_PATH would be used for during normal operation).
+GIT_TEST_EXEC_PATH defaults to `$GIT_TEST_INSTALLED/git --exec-path`.
+
+
 Skipping Tests
 --------------
 
index 845253274b72e18a72858104a59ea32c9da0a862..76d8b7b803b4134b65f2d4f624e29223dda2a7ee 100644 (file)
@@ -52,10 +52,24 @@ gitweb_run () {
        rm -f gitweb.log &&
        perl -- "$SCRIPT_NAME" \
                >gitweb.output 2>gitweb.log &&
+       perl -w -e '
+               open O, ">gitweb.headers";
+               while (<>) {
+                       print O;
+                       last if (/^\r$/ || /^$/);
+               }
+               open O, ">gitweb.body";
+               while (<>) {
+                       print O;
+               }
+               close O;
+       ' gitweb.output &&
        if grep '^[[]' gitweb.log >/dev/null 2>&1; then false; else true; fi
 
        # gitweb.log is left for debugging
-       # gitweb.output is used to parse http output
+       # gitweb.output is used to parse HTTP output
+       # gitweb.headers contains only HTTP headers
+       # gitweb.body contains body of message, without headers
 }
 
 . ./test-lib.sh
index 21aa42f1c613538ba26c0b613cc0d1b001bc52b9..0fe3fd0d012159f6abe7f7d8b006d3f8e59cbab3 100644 (file)
@@ -8,6 +8,28 @@ ErrorLog error.log
 <IfModule !mod_log_config.c>
        LoadModule log_config_module modules/mod_log_config.so
 </IfModule>
+<IfModule !mod_alias.c>
+       LoadModule alias_module modules/mod_alias.so
+</IfModule>
+<IfModule !mod_cgi.c>
+       LoadModule cgi_module modules/mod_cgi.so
+</IfModule>
+<IfModule !mod_env.c>
+       LoadModule env_module modules/mod_env.so
+</IfModule>
+
+Alias /dumb/ www/
+
+<Location /smart/>
+       SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+</Location>
+ScriptAlias /smart/ ${GIT_EXEC_PATH}/git-http-backend/
+<Directory ${GIT_EXEC_PATH}>
+       Options None
+</Directory>
+<Files ${GIT_EXEC_PATH}/git-http-backend>
+       Options ExecCGI
+</Files>
 
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
@@ -26,7 +48,7 @@ SSLEngine On
        LoadModule dav_fs_module modules/mod_dav_fs.so
 
        DAVLockDB DAVLock
-       <Location />
+       <Location /dumb/>
                Dav on
        </Location>
 </IfDefine>
index 271bc4e17f0c12cda550ffa4f54f1ad7555b3bed..c2d408b46120ef9d962272b652a68a83ba0852a5 100755 (executable)
@@ -5,7 +5,7 @@
 
 test_description='Two way merge with read-tree -m $H $M
 
-This test tries two-way merge (aka fast forward with carry forward).
+This test tries two-way merge (aka fast-forward with carry forward).
 
 There is the head (called H) and another commit (called M), which is
 simply ahead of H.  The index and the work tree contains a state that
@@ -51,7 +51,7 @@ check_cache_at () {
 }
 
 cat >bozbar-old <<\EOF
-This is a sample file used in two-way fast forward merge
+This is a sample file used in two-way fast-forward merge
 tests.  Its second line ends with a magic word bozbar
 which will be modified by the merged head to gnusto.
 It has some extra lines so that external tools can
@@ -300,7 +300,7 @@ test_expect_success \
      echo gnusto gnusto >bozbar &&
      if read_tree_twoway $treeH $treeM; then false; else :; fi'
 
-# This fails with straight two-way fast forward.
+# This fails with straight two-way fast-forward.
 test_expect_success \
     '22 - local change cache updated.' \
     'rm -f .git/index &&
index 67e637b7810190e1b7d12dab70cd41d83db0e442..238c2f1c08d0c27b71cdde8c7ddc2b3240ea1fde 100755 (executable)
@@ -7,14 +7,18 @@ test_description='A simple turial in the form of a test case'
 
 . ./test-lib.sh
 
-echo "Hello World" > hello
-echo "Silly example" > example
+test_expect_success 'blob'  '
+       echo "Hello World" > hello &&
+       echo "Silly example" > example &&
 
-git update-index --add hello example
+       git update-index --add hello example &&
 
-test_expect_success 'blob' "test blob = \"$(git cat-file -t 557db03)\""
+       test blob = "$(git cat-file -t 557db03)"
+'
 
-test_expect_success 'blob 557db03' "test \"Hello World\" = \"$(git cat-file blob 557db03)\""
+test_expect_success 'blob 557db03' '
+       test "Hello World" = "$(git cat-file blob 557db03)"
+'
 
 echo "It's a new day for git" >>hello
 cat > diff.expect << EOF
@@ -26,25 +30,35 @@ index 557db03..263414f 100644
  Hello World
 +It's a new day for git
 EOF
-git diff-files -p > diff.output
-test_expect_success 'git diff-files -p' 'cmp diff.expect diff.output'
-git diff > diff.output
-test_expect_success 'git diff' 'cmp diff.expect diff.output'
-
-tree=$(git write-tree 2>/dev/null)
 
-test_expect_success 'tree' "test 8988da15d077d4829fc51d8544c097def6644dbb = $tree"
+test_expect_success 'git diff-files -p' '
+       git diff-files -p > diff.output &&
+       test_cmp diff.expect diff.output
+'
 
-output="$(echo "Initial commit" | git commit-tree $(git write-tree) 2>&1 > .git/refs/heads/master)"
+test_expect_success 'git diff' '
+       git diff > diff.output &&
+       test_cmp diff.expect diff.output
+'
 
-git diff-index -p HEAD > diff.output
-test_expect_success 'git diff-index -p HEAD' 'cmp diff.expect diff.output'
+test_expect_success 'tree' '
+       tree=$(git write-tree 2>/dev/null)
+       test 8988da15d077d4829fc51d8544c097def6644dbb = $tree
+'
 
-git diff HEAD > diff.output
-test_expect_success 'git diff HEAD' 'cmp diff.expect diff.output'
+test_expect_success 'git diff-index -p HEAD' '
+       test_tick &&
+       tree=$(git write-tree) &&
+       commit=$(echo "Initial commit" | git commit-tree $tree) &&
+       git update-ref HEAD $commit &&
+       git diff-index -p HEAD > diff.output &&
+       test_cmp diff.expect diff.output
+'
 
-#rm hello
-#test_expect_success 'git read-tree --reset HEAD' "git read-tree --reset HEAD ; test \"hello: needs update\" = \"$(git update-index --refresh)\""
+test_expect_success 'git diff HEAD' '
+       git diff HEAD > diff.output &&
+       test_cmp diff.expect diff.output
+'
 
 cat > whatchanged.expect << EOF
 commit VARIABLE
@@ -69,39 +83,47 @@ index 0000000..557db03
 +Hello World
 EOF
 
-git whatchanged -p --root | \
-       sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
+test_expect_success 'git whatchanged -p --root' '
+       git whatchanged -p --root |
+               sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
                -e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
-> whatchanged.output
-test_expect_success 'git whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output'
-
-git tag my-first-tag
-test_expect_success 'git tag my-first-tag' 'cmp .git/refs/heads/master .git/refs/tags/my-first-tag'
+       > whatchanged.output &&
+       test_cmp whatchanged.expect whatchanged.output
+'
 
-# TODO: test git clone
+test_expect_success 'git tag my-first-tag' '
+       git tag my-first-tag &&
+       test_cmp .git/refs/heads/master .git/refs/tags/my-first-tag
+'
 
-git checkout -b mybranch
-test_expect_success 'git checkout -b mybranch' 'cmp .git/refs/heads/master .git/refs/heads/mybranch'
+test_expect_success 'git checkout -b mybranch' '
+       git checkout -b mybranch &&
+       test_cmp .git/refs/heads/master .git/refs/heads/mybranch
+'
 
 cat > branch.expect <<EOF
   master
 * mybranch
 EOF
 
-git branch > branch.output
-test_expect_success 'git branch' 'cmp branch.expect branch.output'
+test_expect_success 'git branch' '
+       git branch > branch.output &&
+       test_cmp branch.expect branch.output
+'
 
-git checkout mybranch
-echo "Work, work, work" >>hello
-git commit -m 'Some work.' -i hello
+test_expect_success 'git resolve now fails' '
+       git checkout mybranch &&
+       echo "Work, work, work" >>hello &&
+       test_tick &&
+       git commit -m "Some work." -i hello &&
 
-git checkout master
+       git checkout master &&
 
-echo "Play, play, play" >>hello
-echo "Lots of fun" >>example
-git commit -m 'Some fun.' -i hello example
+       echo "Play, play, play" >>hello &&
+       echo "Lots of fun" >>example &&
+       test_tick &&
+       git commit -m "Some fun." -i hello example &&
 
-test_expect_success 'git resolve now fails' '
        test_must_fail git merge -m "Merge work in mybranch" mybranch
 '
 
@@ -112,52 +134,132 @@ Play, play, play
 Work, work, work
 EOF
 
-git commit -m 'Merged "mybranch" changes.' -i hello
-
-test_done
-
 cat > show-branch.expect << EOF
-* [master] Merged "mybranch" changes.
+* [master] Merge work in mybranch
  ! [mybranch] Some work.
 --
--  [master] Merged "mybranch" changes.
+-  [master] Merge work in mybranch
 *+ [mybranch] Some work.
+*  [master^] Some fun.
 EOF
 
-git show-branch --topo-order master mybranch > show-branch.output
-test_expect_success 'git show-branch' 'cmp show-branch.expect show-branch.output'
-
-git checkout mybranch
+test_expect_success 'git show-branch' '
+       test_tick &&
+       git commit -m "Merge work in mybranch" -i hello &&
+       git show-branch --topo-order --more=1 master mybranch \
+               > show-branch.output &&
+       test_cmp show-branch.expect show-branch.output
+'
 
 cat > resolve.expect << EOF
-Updating from VARIABLE to VARIABLE
+Updating VARIABLE..VARIABLE
+FASTFORWARD (no commit created; -m option ignored)
  example |    1 +
  hello   |    1 +
  2 files changed, 2 insertions(+), 0 deletions(-)
 EOF
 
-git merge -s "Merge upstream changes." master | \
-       sed -e "1s/[0-9a-f]\{40\}/VARIABLE/g" >resolve.output
-test_expect_success 'git resolve' 'cmp resolve.expect resolve.output'
+test_expect_success 'git resolve' '
+       git checkout mybranch &&
+       git merge -m "Merge upstream changes." master |
+               sed -e "1s/[0-9a-f]\{7\}/VARIABLE/g" \
+               -e "s/^Fast[- ]forward /FASTFORWARD /" >resolve.output &&
+       test_cmp resolve.expect resolve.output
+'
 
 cat > show-branch2.expect << EOF
-! [master] Merged "mybranch" changes.
- * [mybranch] Merged "mybranch" changes.
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
+--
+-- [master] Merge work in mybranch
+EOF
+
+test_expect_success 'git show-branch (part 2)' '
+       git show-branch --topo-order master mybranch > show-branch2.output &&
+       test_cmp show-branch2.expect show-branch2.output
+'
+
+cat > show-branch3.expect << EOF
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
+--
+-- [master] Merge work in mybranch
++* [master^2] Some work.
++* [master^] Some fun.
+EOF
+
+test_expect_success 'git show-branch (part 3)' '
+       git show-branch --topo-order --more=2 master mybranch \
+               > show-branch3.output &&
+       test_cmp show-branch3.expect show-branch3.output
+'
+
+test_expect_success 'rewind to "Some fun." and "Some work."' '
+       git checkout mybranch &&
+       git reset --hard master^2 &&
+       git checkout master &&
+       git reset --hard master^
+'
+
+cat > show-branch4.expect << EOF
+* [master] Some fun.
+ ! [mybranch] Some work.
 --
--- [master] Merged "mybranch" changes.
+*  [master] Some fun.
+ + [mybranch] Some work.
+*+ [master^] Initial commit
+EOF
+
+test_expect_success 'git show-branch (part 4)' '
+       git show-branch --topo-order > show-branch4.output &&
+       test_cmp show-branch4.expect show-branch4.output
+'
+
+test_expect_success 'manual merge' '
+       mb=$(git merge-base HEAD mybranch) &&
+       git name-rev --name-only --tags $mb > name-rev.output &&
+       test "my-first-tag" = $(cat name-rev.output) &&
+
+       git read-tree -m -u $mb HEAD mybranch
+'
+
+cat > ls-files.expect << EOF
+100644 7f8b141b65fdcee47321e399a2598a235a032422 0      example
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1      hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2      hello
+100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
+EOF
+
+test_expect_success 'git ls-files --stage' '
+       git ls-files --stage > ls-files.output &&
+       test_cmp ls-files.expect ls-files.output
+'
+
+cat > ls-files-unmerged.expect << EOF
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1      hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2      hello
+100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
 EOF
 
-git show-branch --topo-order master mybranch > show-branch2.output
-test_expect_success 'git show-branch' 'cmp show-branch2.expect show-branch2.output'
+test_expect_success 'git ls-files --unmerged' '
+       git ls-files --unmerged > ls-files-unmerged.output &&
+       test_cmp ls-files-unmerged.expect ls-files-unmerged.output
+'
 
-# TODO: test git fetch
+test_expect_success 'git-merge-index' '
+       test_must_fail git merge-index git-merge-one-file hello
+'
 
-# TODO: test git push
+test_expect_success 'git ls-files --stage (part 2)' '
+       git ls-files --stage > ls-files.output2 &&
+       test_cmp ls-files.expect ls-files.output2
+'
 
 test_expect_success 'git repack' 'git repack'
 test_expect_success 'git prune-packed' 'git prune-packed'
 test_expect_success '-> only packed objects' '
-       ! find -type f .git/objects/[0-9a-f][0-9a-f]
+       git prune && # Remove conflict marked blobs
+       ! find .git/objects/[0-9a-f][0-9a-f] -type f
 '
 
 test_done
index 3b01ad2e4de152d23bbe8267d3b1a5cafd615e88..9965bc5c92445160895ae3ea426c5caf1b3458b3 100755 (executable)
@@ -8,7 +8,7 @@ test_cd_to_toplevel () {
        test_expect_success $3 "$2" '
                (
                        cd '"'$1'"' &&
-                       . git-sh-setup &&
+                       . "$(git --exec-path)"/git-sh-setup &&
                        cd_to_toplevel &&
                        [ "$(pwd -P)" = "$TOPLEVEL" ]
                )
index fc1e379321a5ccd278951b7aaa21f677f7cbc960..d5ec333131f92cd086924815a9f32ad1c8b50aae 100755 (executable)
@@ -29,4 +29,12 @@ test_expect_success 'add file to gitignore' '
 '
 check_all_output
 
+test_expect_success 'ls-files -i lists only tracked-but-ignored files' '
+       echo content >other-file &&
+       git add other-file &&
+       echo file >expect &&
+       git ls-files -i --exclude-standard >output &&
+       test_cmp expect output
+'
+
 test_done
index 51cb4a30f571772399697bc073153b7e956853bf..8be9fb411276e3c416374a813d691ff0455a8746 100755 (executable)
@@ -39,8 +39,8 @@ test_expect_success \
      tree=`git write-tree` &&
      echo $tree'
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
 test_output () {
     sed -e "s/ $_x40   / X     /" <current >check
     test_cmp expected check
@@ -141,4 +141,89 @@ test_expect_success 'ls-tree filter is leading path match' '
        test_output
 '
 
+test_expect_success 'ls-tree --full-name' '
+       (
+               cd path0 &&
+               git ls-tree --full-name $tree a
+       ) >current &&
+       cat >expected <<\EOF &&
+040000 tree X  path0/a
+EOF
+       test_output
+'
+
+test_expect_success 'ls-tree --full-tree' '
+       (
+               cd path1/b/c &&
+               git ls-tree --full-tree $tree
+       ) >current &&
+       cat >expected <<\EOF &&
+100644 blob X  1.txt
+100644 blob X  2.txt
+040000 tree X  path0
+040000 tree X  path1
+040000 tree X  path2
+040000 tree X  path3
+EOF
+       test_output
+'
+
+test_expect_success 'ls-tree --full-tree -r' '
+       (
+               cd path3/ &&
+               git ls-tree --full-tree -r $tree
+       ) >current &&
+       cat >expected <<\EOF &&
+100644 blob X  1.txt
+100644 blob X  2.txt
+100644 blob X  path0/a/b/c/1.txt
+100644 blob X  path1/b/c/1.txt
+100644 blob X  path2/1.txt
+100644 blob X  path3/1.txt
+100644 blob X  path3/2.txt
+EOF
+       test_output
+'
+
+test_expect_success 'ls-tree --abbrev=5' '
+       git ls-tree --abbrev=5 $tree >current &&
+       sed -e "s/ $_x05[0-9a-f]*       / X     /" <current >check &&
+       cat >expected <<\EOF &&
+100644 blob X  1.txt
+100644 blob X  2.txt
+040000 tree X  path0
+040000 tree X  path1
+040000 tree X  path2
+040000 tree X  path3
+EOF
+       test_cmp expected check
+'
+
+test_expect_success 'ls-tree --name-only' '
+       git ls-tree --name-only $tree >current
+       cat >expected <<\EOF &&
+1.txt
+2.txt
+path0
+path1
+path2
+path3
+EOF
+       test_output
+'
+
+test_expect_success 'ls-tree --name-only -r' '
+       git ls-tree --name-only -r $tree >current
+       cat >expected <<\EOF &&
+1.txt
+2.txt
+path0/a/b/c/1.txt
+path1/b/c/1.txt
+path2/1.txt
+path3/1.txt
+path3/2.txt
+EOF
+       test_output
+'
+
 test_done
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
new file mode 100755 (executable)
index 0000000..1e34f48
--- /dev/null
@@ -0,0 +1,150 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='Test commit notes'
+
+. ./test-lib.sh
+
+cat > fake_editor.sh << \EOF
+echo "$MSG" > "$1"
+echo "$MSG" >& 2
+EOF
+chmod a+x fake_editor.sh
+VISUAL=./fake_editor.sh
+export VISUAL
+
+test_expect_success 'cannot annotate non-existing HEAD' '
+       (MSG=3 && export MSG && test_must_fail git notes edit)
+'
+
+test_expect_success setup '
+       : > a1 &&
+       git add a1 &&
+       test_tick &&
+       git commit -m 1st &&
+       : > a2 &&
+       git add a2 &&
+       test_tick &&
+       git commit -m 2nd
+'
+
+test_expect_success 'need valid notes ref' '
+       (MSG=1 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&
+        test_must_fail git notes edit) &&
+       (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/' '
+       (MSG=1 GIT_NOTES_REF=refs/heads/bogus &&
+        export MSG GIT_NOTES_REF &&
+        test_must_fail git notes edit)
+'
+
+test_expect_success 'refusing to edit in refs/remotes/' '
+       (MSG=1 GIT_NOTES_REF=refs/remotes/bogus &&
+        export MSG GIT_NOTES_REF &&
+        test_must_fail git notes edit)
+'
+
+# 1 indicates caught gracefully by die, 128 means git-show barked
+test_expect_success 'handle empty notes gracefully' '
+       git notes show ; test 1 = $?
+'
+
+test_expect_success 'create notes' '
+       git config core.notesRef refs/notes/commits &&
+       MSG=b1 git notes edit &&
+       test ! -f .git/new-notes &&
+       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+       test b1 = $(git notes show) &&
+       git show HEAD^ &&
+       test_must_fail git notes show HEAD^
+'
+
+cat > expect << EOF
+commit 268048bfb8a1fb38e703baceb8ab235421bf80c5
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:14:13 2005 -0700
+
+    2nd
+
+Notes:
+    b1
+EOF
+
+test_expect_success 'show notes' '
+       ! (git cat-file commit HEAD | grep b1) &&
+       git log -1 > output &&
+       test_cmp expect output
+'
+test_expect_success 'create multi-line notes (setup)' '
+       : > a3 &&
+       git add a3 &&
+       test_tick &&
+       git commit -m 3rd &&
+       MSG="b3
+c3c3c3c3
+d3d3d3" git notes edit
+'
+
+cat > expect-multiline << EOF
+commit 1584215f1d29c65e99c6c6848626553fdd07fd75
+Author: A U Thor <author@example.com>
+Date:   Thu Apr 7 15:15:13 2005 -0700
+
+    3rd
+
+Notes:
+    b3
+    c3c3c3c3
+    d3d3d3
+EOF
+
+printf "\n" >> expect-multiline
+cat expect >> expect-multiline
+
+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)' '
+       : > a4 &&
+       git add a4 &&
+       test_tick &&
+       git commit -m 4th &&
+       echo "xyzzy" > note5 &&
+       git notes edit -m spam -F note5 -m "foo
+bar
+baz"
+'
+
+whitespace="    "
+cat > expect-m-and-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
+
+test_expect_success 'show -m and -F notes' '
+       git log -3 > output &&
+       test_cmp expect-m-and-F output
+'
+
+test_done
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
new file mode 100755 (executable)
index 0000000..ee84fc4
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='Test commit notes index (expensive!)'
+
+. ./test-lib.sh
+
+test -z "$GIT_NOTES_TIMING_TESTS" && {
+       say Skipping timing tests
+       test_done
+       exit
+}
+
+create_repo () {
+       number_of_commits=$1
+       nr=0
+       test -d .git || {
+       git init &&
+       (
+               while [ $nr -lt $number_of_commits ]; do
+                       nr=$(($nr+1))
+                       mark=$(($nr+$nr))
+                       notemark=$(($mark+1))
+                       test_tick &&
+                       cat <<INPUT_END &&
+commit refs/heads/master
+mark :$mark
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #$nr
+COMMIT
+
+M 644 inline file
+data <<EOF
+file in commit #$nr
+EOF
+
+blob
+mark :$notemark
+data <<EOF
+note for commit #$nr
+EOF
+
+INPUT_END
+
+                       echo "N :$notemark :$mark" >> note_commit
+               done &&
+               test_tick &&
+               cat <<INPUT_END &&
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes
+COMMIT
+
+INPUT_END
+
+               cat note_commit
+       ) |
+       git fast-import --quiet &&
+       git config core.notesRef refs/notes/commits
+       }
+}
+
+test_notes () {
+       count=$1 &&
+       git config core.notesRef refs/notes/commits &&
+       git log | grep "^    " > output &&
+       i=$count &&
+       while [ $i -gt 0 ]; do
+               echo "    commit #$i" &&
+               echo "    note for commit #$i" &&
+               i=$(($i-1));
+       done > expect &&
+       test_cmp expect output
+}
+
+cat > time_notes << \EOF
+       mode=$1
+       i=1
+       while [ $i -lt $2 ]; do
+               case $1 in
+               no-notes)
+                       GIT_NOTES_REF=non-existing; export GIT_NOTES_REF
+               ;;
+               notes)
+                       unset GIT_NOTES_REF
+               ;;
+               esac
+               git log >/dev/null
+               i=$(($i+1))
+       done
+EOF
+
+time_notes () {
+       for mode in no-notes notes
+       do
+               echo $mode
+               /usr/bin/time sh ../time_notes $mode $1
+       done
+}
+
+for count in 10 100 1000 10000; do
+
+       mkdir $count
+       (cd $count;
+
+       test_expect_success "setup $count" "create_repo $count"
+
+       test_expect_success 'notes work' "test_notes $count"
+
+       test_expect_success 'notes timing' "time_notes 100"
+       )
+done
+
+test_done
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
new file mode 100755 (executable)
index 0000000..edc4bc8
--- /dev/null
@@ -0,0 +1,188 @@
+#!/bin/sh
+
+test_description='Test commit notes organized in subtrees'
+
+. ./test-lib.sh
+
+number_of_commits=100
+
+start_note_commit () {
+       test_tick &&
+       cat <<INPUT_END
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes
+COMMIT
+
+from refs/notes/commits^0
+deleteall
+INPUT_END
+
+}
+
+verify_notes () {
+       git log | grep "^    " > output &&
+       i=$number_of_commits &&
+       while [ $i -gt 0 ]; do
+               echo "    commit #$i" &&
+               echo "    note for commit #$i" &&
+               i=$(($i-1));
+       done > expect &&
+       test_cmp expect output
+}
+
+test_expect_success "setup: create $number_of_commits commits" '
+
+       (
+               nr=0 &&
+               while [ $nr -lt $number_of_commits ]; do
+                       nr=$(($nr+1)) &&
+                       test_tick &&
+                       cat <<INPUT_END
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #$nr
+COMMIT
+
+M 644 inline file
+data <<EOF
+file in commit #$nr
+EOF
+
+INPUT_END
+
+               done &&
+               test_tick &&
+               cat <<INPUT_END
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+no notes
+COMMIT
+
+deleteall
+
+INPUT_END
+
+       ) |
+       git fast-import --quiet &&
+       git config core.notesRef refs/notes/commits
+'
+
+test_sha1_based () {
+       (
+               start_note_commit &&
+               nr=$number_of_commits &&
+               git rev-list refs/heads/master |
+               while read sha1; do
+                       note_path=$(echo "$sha1" | sed "$1")
+                       cat <<INPUT_END &&
+M 100644 inline $note_path
+data <<EOF
+note for commit #$nr
+EOF
+
+INPUT_END
+
+                       nr=$(($nr-1))
+               done
+       ) |
+       git fast-import --quiet
+}
+
+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_same_notes () {
+       (
+               start_note_commit &&
+               nr=$number_of_commits &&
+               git rev-list refs/heads/master |
+               while read sha1; do
+                       first_note_path=$(echo "$sha1" | sed "$1")
+                       second_note_path=$(echo "$sha1" | sed "$2")
+                       cat <<INPUT_END &&
+M 100644 inline $second_note_path
+data <<EOF
+note for commit #$nr
+EOF
+
+M 100644 inline $first_note_path
+data <<EOF
+note for commit #$nr
+EOF
+
+INPUT_END
+
+                       nr=$(($nr-1))
+               done
+       ) |
+       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 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_concatenated_notes () {
+       (
+               start_note_commit &&
+               nr=$number_of_commits &&
+               git rev-list refs/heads/master |
+               while read sha1; do
+                       first_note_path=$(echo "$sha1" | sed "$1")
+                       second_note_path=$(echo "$sha1" | sed "$2")
+                       cat <<INPUT_END &&
+M 100644 inline $second_note_path
+data <<EOF
+second note for commit #$nr
+EOF
+
+M 100644 inline $first_note_path
+data <<EOF
+first note for commit #$nr
+EOF
+
+INPUT_END
+
+                       nr=$(($nr-1))
+               done
+       ) |
+       git fast-import --quiet
+}
+
+verify_concatenated_notes () {
+    git log | grep "^    " > output &&
+    i=$number_of_commits &&
+    while [ $i -gt 0 ]; do
+        echo "    commit #$i" &&
+        echo "    first note for commit #$i" &&
+        echo "    second note for commit #$i" &&
+        i=$(($i-1));
+    done > expect &&
+    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 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_done
index 297d165476b93e18b18bf42bc81f4740cf18db9f..8f785e7957519eaa3dd1ef946c905054c4fa0e6c 100755 (executable)
@@ -32,14 +32,14 @@ export GIT_AUTHOR_EMAIL
 test_expect_success 'setup for merge-preserving rebase' \
        'echo First > A &&
        git add A &&
-       git-commit -m "Add A1" &&
+       git commit -m "Add A1" &&
        git checkout -b topic &&
        echo Second > B &&
        git add B &&
-       git-commit -m "Add B1" &&
+       git commit -m "Add B1" &&
        git checkout -f master &&
        echo Third >> A &&
-       git-commit -a -m "Modify A2" &&
+       git commit -a -m "Modify A2" &&
 
        git clone ./. clone1 &&
        cd clone1 &&
index 687bd7ab53397d3bc9d5150bc7cd290efa8ebdbc..d86bc81abfd954194c245e7e180a23f2b9b7c840 100755 (executable)
@@ -228,4 +228,21 @@ test_expect_success 'add first line works' '
        test_cmp expected diff
 '
 
+cat >expected <<EOF
+diff --git a/empty b/empty
+deleted file mode 100644
+index e69de29..0000000
+EOF
+
+test_expect_success 'deleting an empty file' '
+       git reset --hard &&
+       > empty &&
+       git add empty &&
+       git commit -m empty &&
+       rm empty &&
+       echo y | git add -p empty &&
+       git diff --cached >diff &&
+       test_cmp expected diff
+'
+
 test_done
index 531f5b795c197d942223db3cdc2d2030208e9beb..7f267f9ed1d888eeb4b3c780a048c7d09e29db3b 100755 (executable)
@@ -455,6 +455,27 @@ test_expect_success 'format-patch respects -U' '
 
 '
 
+cat > expect << EOF
+
+diff --git a/file b/file
+index 40f36c6..2dc5c23 100644
+--- a/file
++++ b/file
+@@ -14,3 +14,19 @@ C
+ D
+ E
+ F
++5
+EOF
+
+test_expect_success 'format-patch -p suppresses stat' '
+
+       git format-patch -p -2 &&
+       sed -e "1,/^$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+       test_cmp expect output
+
+'
+
 test_expect_success 'format-patch from a subdirectory (1)' '
        filename=$(
                rm -rf sub &&
@@ -515,4 +536,20 @@ test_expect_success 'format-patch --signoff' '
        grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 '
 
+echo "fatal: --name-only does not make sense" > expect.name-only
+echo "fatal: --name-status does not make sense" > expect.name-status
+echo "fatal: --check does not make sense" > expect.check
+
+test_expect_success 'options no longer allowed for format-patch' '
+       test_must_fail git format-patch --name-only 2> output &&
+       test_cmp expect.name-only output &&
+       test_must_fail git format-patch --name-status 2> output &&
+       test_cmp expect.name-status output &&
+       test_must_fail git format-patch --check 2> output &&
+       test_cmp expect.check output'
+
+test_expect_success 'format-patch --numstat should produce a patch' '
+       git format-patch --numstat --stdout master..side > output &&
+       test 6 = $(grep "^diff --git a/" output | wc -l)'
+
 test_done
index 4508effcaacd3dbc8fadb13c7be0d631f4c946f1..1c21276c55400c594f71bc01eba17b0d3820b420 100755 (executable)
@@ -8,6 +8,7 @@ test_expect_success setup '
 
        git config diff.color.old red
        git config diff.color.new green
+       git config diff.color.func magenta
 
 '
 
@@ -16,6 +17,7 @@ decrypt_color () {
                -e 's/.\[1m/<WHITE>/g' \
                -e 's/.\[31m/<RED>/g' \
                -e 's/.\[32m/<GREEN>/g' \
+               -e 's/.\[35m/<MAGENTA>/g' \
                -e 's/.\[36m/<BROWN>/g' \
                -e 's/.\[m/<RESET>/g'
 }
@@ -49,7 +51,7 @@ cat > expect <<\EOF
 <WHITE>+++ b/post<RESET>
 <BROWN>@@ -1,3 +1,7 @@<RESET>
 <RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
-<RESET>
+
 a = b + c<RESET>
 
 <GREEN>aa = a<RESET>
@@ -63,6 +65,26 @@ test_expect_success 'word diff with runs of whitespace' '
 
 '
 
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1 +1 @@<RESET>
+<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+<BROWN>@@ -3,0 +4,4 @@<RESET> <RESET><MAGENTA>a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
+
+test_expect_success 'word diff without context' '
+
+       word_diff --color-words --unified=0
+
+'
+
 cat > expect <<\EOF
 <WHITE>diff --git a/pre b/post<RESET>
 <WHITE>index 330b04f..5ed8eff 100644<RESET>
@@ -70,7 +92,7 @@ cat > expect <<\EOF
 <WHITE>+++ b/post<RESET>
 <BROWN>@@ -1,3 +1,7 @@<RESET>
 h(4),<GREEN>hh<RESET>[44]
-<RESET>
+
 a = b + c<RESET>
 
 <GREEN>aa = a<RESET>
@@ -106,7 +128,7 @@ cat > expect <<\EOF
 <WHITE>+++ b/post<RESET>
 <BROWN>@@ -1,3 +1,7 @@<RESET>
 h(4)<GREEN>,hh[44]<RESET>
-<RESET>
+
 a = b + c<RESET>
 
 <GREEN>aa = a<RESET>
@@ -148,7 +170,7 @@ cat > expect <<\EOF
 <WHITE>+++ b/post<RESET>
 <BROWN>@@ -1,3 +1,7 @@<RESET>
 h(4),<GREEN>hh[44<RESET>]
-<RESET>
+
 a = b + c<RESET>
 
 <GREEN>aa = a<RESET>
index 484654d6e49a170975a36efe9c27d7884c4bce5d..b04fc8fc12238c3326306ed0e055b7d67bf950b4 100755 (executable)
@@ -136,37 +136,37 @@ void print_int(int num) {
 EOF
 
 test_expect_success 'file creation' '
-       git-apply patch1.patch
+       git apply patch1.patch
 '
 
 test_expect_success 'patch2 fails (retab)' '
-       test_must_fail git-apply patch2.patch
+       test_must_fail git apply patch2.patch
 '
 
 test_expect_success 'patch2 applies with --ignore-whitespace' '
-       git-apply --ignore-whitespace patch2.patch
+       git apply --ignore-whitespace patch2.patch
 '
 
 test_expect_success 'patch2 reverse applies with --ignore-space-change' '
-       git-apply -R --ignore-space-change patch2.patch
+       git apply -R --ignore-space-change patch2.patch
 '
 
 git config apply.ignorewhitespace change
 
 test_expect_success 'patch2 applies (apply.ignorewhitespace = change)' '
-       git-apply patch2.patch
+       git apply patch2.patch
 '
 
 test_expect_success 'patch3 fails (missing string at EOL)' '
-       test_must_fail git-apply patch3.patch
+       test_must_fail git apply patch3.patch
 '
 
 test_expect_success 'patch4 fails (missing EOL at EOF)' '
-       test_must_fail git-apply patch4.patch
+       test_must_fail git apply patch4.patch
 '
 
 test_expect_success 'patch5 applies (leading whitespace)' '
-       git-apply patch5.patch
+       git apply patch5.patch
 '
 
 test_expect_success 'patches do not mangle whitespace' '
@@ -175,11 +175,11 @@ test_expect_success 'patches do not mangle whitespace' '
 
 test_expect_success 're-create file (with --ignore-whitespace)' '
        rm -f main.c &&
-       git-apply patch1.patch
+       git apply patch1.patch
 '
 
 test_expect_success 'patch5 fails (--no-ignore-whitespace)' '
-       test_must_fail git-apply --no-ignore-whitespace patch5.patch
+       test_must_fail git apply --no-ignore-whitespace patch5.patch
 '
 
 test_done
index 8f6aea48d84621ae3b7304636452c724a4bbe5b6..6cc741a634b0352c54fe8e5f61f1e99543909b8c 100755 (executable)
@@ -57,6 +57,23 @@ test_expect_success 'apply --directory (new file)' '
        test content = $(cat some/sub/dir/newfile)
 '
 
+cat > patch << EOF
+diff --git a/c/newfile2 b/c/newfile2
+new file mode 100644
+index 0000000..d95f3ad
+--- /dev/null
++++ b/c/newfile2
+@@ -0,0 +1 @@
++content
+EOF
+
+test_expect_success 'apply --directory -p (new file)' '
+       git reset --hard initial &&
+       git apply -p2 --directory=some/sub/dir/ --index patch &&
+       test content = $(git show :some/sub/dir/newfile2) &&
+       test content = $(cat some/sub/dir/newfile2)
+'
+
 cat > patch << EOF
 diff --git a/delfile b/delfile
 deleted file mode 100644
index 405b97119175a1c0fa75a9db30c6b1ab076cc44e..dd818f6fd61e8ca593c36467377d15d023eced25 100755 (executable)
@@ -52,4 +52,32 @@ GIT_DIR=non-existing git shortlog -w < log > out
 
 test_expect_success 'shortlog from non-git directory' 'test_cmp expect out'
 
+iconvfromutf8toiso88591() {
+       printf "%s" "$*" | iconv -f UTF-8 -t ISO-8859-1
+}
+
+DSCHO="Jöhännës \"Dschö\" Schindëlin"
+DSCHOE="$DSCHO <Johannes.Schindelin@gmx.de>"
+MSG1="set a1 to 2 and some non-ASCII chars: Ã„ßø"
+MSG2="set a1 to 3 and some non-ASCII chars: Ã¡Ã¦Ã¯"
+cat > expect << EOF
+$DSCHO (2):
+      $MSG1
+      $MSG2
+
+EOF
+
+test_expect_success 'shortlog encoding' '
+       git reset --hard "$commit" &&
+       git config --unset i18n.commitencoding &&
+       echo 2 > a1 &&
+       git commit --quiet -m "$MSG1" --author="$DSCHOE" a1 &&
+       git config i18n.commitencoding "ISO-8859-1" &&
+       echo 3 > a1 &&
+       git commit --quiet -m "$(iconvfromutf8toiso88591 "$MSG2")" \
+               --author="$(iconvfromutf8toiso88591 "$DSCHOE")" a1 &&
+       git config --unset i18n.commitencoding &&
+       git shortlog HEAD~2.. > out &&
+test_cmp expect out'
+
 test_done
index 1e952ca55b1ff146000153d720f11d1b7fa22707..779a5adf554d77464f2a326a2eb919e224993746 100755 (executable)
@@ -63,6 +63,27 @@ test_expect_success 'format' '
        test_cmp expect actual
 '
 
+cat > expect << EOF
+ This is
+  the sixth
+  commit.
+ This is
+  the fifth
+  commit.
+EOF
+
+test_expect_success 'format %w(12,1,2)' '
+
+       git log -2 --format="%w(12,1,2)This is the %s commit." > actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'format %w(,1,2)' '
+
+       git log -2 --format="%w(,1,2)This is%nthe %s%ncommit." > actual &&
+       test_cmp expect actual
+'
+
 cat > expect << EOF
 804a787 sixth
 394ef78 fifth
index 0279d07c83b1eb6cbcf2add5fc1afd8a27d63fbd..ebc36c1758372f484055b62080d3ce81ae7c69b4 100755 (executable)
@@ -11,7 +11,7 @@ test_expect_success 'split sample box' \
        'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
        last=`cat last` &&
        echo total is $last &&
-       test `cat last` = 14'
+       test `cat last` = 16'
 
 check_mailinfo () {
        mail=$1 opt=$2
@@ -30,6 +30,10 @@ do
                if test -f "$TEST_DIRECTORY"/t5100/msg$mail--scissors
                then
                        check_mailinfo $mail --scissors
+               fi &&
+               if test -f "$TEST_DIRECTORY"/t5100/msg$mail--no-inbody-headers
+               then
+                       check_mailinfo $mail --no-inbody-headers
                fi
        '
 done
diff --git a/t/t5100/info0015 b/t/t5100/info0015
new file mode 100644 (file)
index 0000000..0114f10
--- /dev/null
@@ -0,0 +1,5 @@
+Author: 
+Email: 
+Subject: check bogus body header (from)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0015--no-inbody-headers b/t/t5100/info0015--no-inbody-headers
new file mode 100644 (file)
index 0000000..c4d8d77
--- /dev/null
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: check bogus body header (from)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0016 b/t/t5100/info0016
new file mode 100644 (file)
index 0000000..38ccd0d
--- /dev/null
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: check bogus body header (date)
+Date: bogus 
+
diff --git a/t/t5100/info0016--no-inbody-headers b/t/t5100/info0016--no-inbody-headers
new file mode 100644 (file)
index 0000000..f4857d4
--- /dev/null
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: check bogus body header (date)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/msg0015 b/t/t5100/msg0015
new file mode 100644 (file)
index 0000000..9577238
--- /dev/null
@@ -0,0 +1,2 @@
+- a list
+  - of stuff
diff --git a/t/t5100/msg0015--no-inbody-headers b/t/t5100/msg0015--no-inbody-headers
new file mode 100644 (file)
index 0000000..be5115b
--- /dev/null
@@ -0,0 +1,3 @@
+From: bogosity
+  - a list
+  - of stuff
diff --git a/t/t5100/msg0016 b/t/t5100/msg0016
new file mode 100644 (file)
index 0000000..0d9adad
--- /dev/null
@@ -0,0 +1,2 @@
+and some content
+
diff --git a/t/t5100/msg0016--no-inbody-headers b/t/t5100/msg0016--no-inbody-headers
new file mode 100644 (file)
index 0000000..1063f51
--- /dev/null
@@ -0,0 +1,4 @@
+Date: bogus
+
+and some content
+
diff --git a/t/t5100/patch0015 b/t/t5100/patch0015
new file mode 100644 (file)
index 0000000..ad64848
--- /dev/null
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/patch0015--no-inbody-headers b/t/t5100/patch0015--no-inbody-headers
new file mode 100644 (file)
index 0000000..ad64848
--- /dev/null
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/patch0016 b/t/t5100/patch0016
new file mode 100644 (file)
index 0000000..ad64848
--- /dev/null
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/patch0016--no-inbody-headers b/t/t5100/patch0016--no-inbody-headers
new file mode 100644 (file)
index 0000000..ad64848
--- /dev/null
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
index 13fa4ae03b917148e1c9c771ba0672c001a53046..de1031241d99a66db0653875ea163785691d12d6 100644 (file)
@@ -650,3 +650,36 @@ index b0b5d8f..461c47e 100644
                convert_to_utf8(line, charset.buf);
 -- 
 1.6.4.1
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Subject: check bogus body header (from)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
+From: bogosity
+  - a list
+  - of stuff
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Subject: check bogus body header (date)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
+Date: bogus
+
+and some content
+
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
index 852ccb5d7d649a71eeab25eadce6940b9c2a6f63..fd166d9de356dafb000504506285a6f77fcc0a37 100755 (executable)
@@ -158,7 +158,7 @@ cat > test/expect << EOF
     another
     master
   Local refs configured for 'git push':
-    ahead  forces to master  (fast forwardable)
+    ahead  forces to master  (fast-forwardable)
     master pushes to another (up to date)
 EOF
 
@@ -365,6 +365,17 @@ test_expect_success 'update with arguments' '
 
 '
 
+test_expect_success 'update --prune' '
+
+       (cd one &&
+        git branch -m side2 side3) &&
+       (cd test &&
+        git remote update --prune &&
+        (cd ../one && git branch -m side3 side2)
+        git rev-parse refs/remotes/origin/side3 &&
+        test_must_fail git rev-parse refs/remotes/origin/side2)
+'
+
 cat > one/expect << EOF
   apis/master
   apis/side
index 2a1806b0b4427abb410d0310ba8c1ec52f8bde58..b7b7ddaa403d7a06c0203b89e484d56d20863f4a 100755 (executable)
@@ -51,7 +51,7 @@ test_expect_success 'nonexistant group produces error' '
        ! repo_fetched two
 '
 
-test_expect_success 'updating group updates all members' '
+test_expect_success 'updating group updates all members (remote update)' '
        mark group-all &&
        update_repos &&
        git config --add remotes.all one &&
@@ -61,7 +61,15 @@ test_expect_success 'updating group updates all members' '
        repo_fetched two
 '
 
-test_expect_success 'updating group does not update non-members' '
+test_expect_success 'updating group updates all members (fetch)' '
+       mark fetch-group-all &&
+       update_repos &&
+       git fetch all &&
+       repo_fetched one &&
+       repo_fetched two
+'
+
+test_expect_success 'updating group does not update non-members (remote update)' '
        mark group-some &&
        update_repos &&
        git config --add remotes.some one &&
@@ -70,6 +78,15 @@ test_expect_success 'updating group does not update non-members' '
        ! repo_fetched two
 '
 
+test_expect_success 'updating group does not update non-members (fetch)' '
+       mark fetch-group-some &&
+       update_repos &&
+       git config --add remotes.some one &&
+       git remote update some &&
+       repo_fetched one &&
+       ! repo_fetched two
+'
+
 test_expect_success 'updating remote name updates that remote' '
        mark remote-name &&
        update_repos &&
index d13c806624bcc8a404be97d61500e8e1d4614c6b..169af1edde557f054ea76b8de681c6dd74e436f2 100755 (executable)
@@ -341,4 +341,15 @@ test_expect_success 'fetch into the current branch with --update-head-ok' '
 
 '
 
+test_expect_success "should be able to fetch with duplicate refspecs" '
+        mkdir dups &&
+        cd dups &&
+        git init &&
+        git config branch.master.remote three &&
+        git config remote.three.url ../three/.git &&
+        git config remote.three.fetch +refs/heads/*:refs/remotes/origin/* &&
+        git config --add remote.three.fetch +refs/heads/*:refs/remotes/origin/* &&
+        git fetch three
+'
+
 test_done
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
new file mode 100755 (executable)
index 0000000..b737332
--- /dev/null
@@ -0,0 +1,154 @@
+#!/bin/sh
+
+test_description='fetch --all works correctly'
+
+. ./test-lib.sh
+
+setup_repository () {
+       mkdir "$1" && (
+       cd "$1" &&
+       git init &&
+       >file &&
+       git add file &&
+       test_tick &&
+       git commit -m "Initial" &&
+       git checkout -b side &&
+       >elif &&
+       git add elif &&
+       test_tick &&
+       git commit -m "Second" &&
+       git checkout master
+       )
+}
+
+test_expect_success setup '
+       setup_repository one &&
+       setup_repository two &&
+       (
+               cd two && git branch another
+       ) &&
+       git clone --mirror two three
+       git clone one test
+'
+
+cat > test/expect << EOF
+  one/master
+  one/side
+  origin/HEAD -> origin/master
+  origin/master
+  origin/side
+  three/another
+  three/master
+  three/side
+  two/another
+  two/master
+  two/side
+EOF
+
+test_expect_success 'git fetch --all' '
+       (cd test &&
+        git remote add one ../one &&
+        git remote add two ../two &&
+        git remote add three ../three &&
+        git fetch --all &&
+        git branch -r > output &&
+        test_cmp expect output)
+'
+
+test_expect_success 'git fetch --all should continue if a remote has errors' '
+       (git clone one test2 &&
+        cd test2 &&
+        git remote add bad ../non-existing &&
+        git remote add one ../one &&
+        git remote add two ../two &&
+        git remote add three ../three &&
+        test_must_fail git fetch --all &&
+        git branch -r > output &&
+        test_cmp ../test/expect output)
+'
+
+test_expect_success 'git fetch --all does not allow non-option arguments' '
+       (cd test &&
+        test_must_fail git fetch --all origin &&
+        test_must_fail git fetch --all origin master)
+'
+
+cat > expect << EOF
+  origin/HEAD -> origin/master
+  origin/master
+  origin/side
+  three/another
+  three/master
+  three/side
+EOF
+
+test_expect_success 'git fetch --multiple (but only one remote)' '
+       (git clone one test3 &&
+        cd test3 &&
+        git remote add three ../three &&
+        git fetch --multiple three &&
+        git branch -r > output &&
+        test_cmp ../expect output)
+'
+
+cat > expect << EOF
+  one/master
+  one/side
+  two/another
+  two/master
+  two/side
+EOF
+
+test_expect_success 'git fetch --multiple (two remotes)' '
+       (git clone one test4 &&
+        cd test4 &&
+        git remote rm origin &&
+        git remote add one ../one &&
+        git remote add two ../two &&
+        git fetch --multiple one two &&
+        git branch -r > output &&
+        test_cmp ../expect output)
+'
+
+test_expect_success 'git fetch --multiple (bad remote names)' '
+       (cd test4 &&
+        test_must_fail git fetch --multiple four)
+'
+
+
+test_expect_success 'git fetch --all (skipFetchAll)' '
+       (cd test4 &&
+        for b in $(git branch -r)
+        do
+               git branch -r -d $b || break
+        done &&
+        git remote add three ../three &&
+        git config remote.three.skipFetchAll true &&
+        git fetch --all &&
+        git branch -r > output &&
+        test_cmp ../expect output)
+'
+
+cat > expect << EOF
+  one/master
+  one/side
+  three/another
+  three/master
+  three/side
+  two/another
+  two/master
+  two/side
+EOF
+
+test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '
+       (cd test4 &&
+        for b in $(git branch -r)
+        do
+               git branch -r -d $b || break
+        done &&
+        git fetch --multiple one two three &&
+        git branch -r > output &&
+        test_cmp ../expect output)
+'
+
+test_done
index c6bc65faa06adeaced0733064fb09eb82add585e..c2060bb870f35a3819fabb19c97e921e0ae43349 100755 (executable)
@@ -22,7 +22,7 @@ test_expect_success setup '
        git commit -a -m next
 '
 
-test_expect_success 'non fast forward fetch' '
+test_expect_success 'non-fast-forward fetch' '
 
        test_must_fail git fetch . master:side
 
index f4a2cf6c171a6356804f61ac6f5707dd587515e5..bb18f8bfc4c9cd7e602633ce4abf5a3cf9ae0e4a 100755 (executable)
@@ -3,23 +3,22 @@
 # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
 #
 
-test_description='test http-push
+test_description='test WebDAV http-push
 
 This test runs various sanity checks on http-push.'
 
 . ./test-lib.sh
 
-ROOT_PATH="$PWD"
-LIB_HTTPD_DAV=t
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
-
 if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
 then
        say "skipping test, USE_CURL_MULTI is not defined"
        test_done
 fi
 
+LIB_HTTPD_DAV=t
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
 . "$TEST_DIRECTORY"/lib-httpd.sh
+ROOT_PATH="$PWD"
 start_httpd
 
 test_expect_success 'setup remote repository' '
@@ -36,16 +35,17 @@ test_expect_success 'setup remote repository' '
        cd test_repo.git &&
        git --bare update-server-info &&
        mv hooks/post-update.sample hooks/post-update &&
+       ORIG_HEAD=$(git rev-parse --verify HEAD) &&
        cd - &&
        mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
 '
 
 test_expect_success 'clone remote repository' '
        cd "$ROOT_PATH" &&
-       git clone $HTTPD_URL/test_repo.git test_repo_clone
+       git clone $HTTPD_URL/dumb/test_repo.git test_repo_clone
 '
 
-test_expect_failure 'push to remote repository with packed refs' '
+test_expect_success 'push to remote repository with packed refs' '
        cd "$ROOT_PATH"/test_repo_clone &&
        : >path2 &&
        git add path2 &&
@@ -57,11 +57,15 @@ test_expect_failure 'push to remote repository with packed refs' '
         test $HEAD = $(git rev-parse --verify HEAD))
 '
 
-test_expect_success ' push to remote repository with unpacked refs' '
+test_expect_success 'push already up-to-date' '
+       git push
+'
+
+test_expect_success 'push to remote repository with unpacked refs' '
        (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
         rm packed-refs &&
-        git update-ref refs/heads/master \
-               0c973ae9bd51902a28466f3850b543fa66a6aaf4) &&
+        git update-ref refs/heads/master $ORIG_HEAD &&
+        git --bare update-server-info) &&
        git push &&
        (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
         test $HEAD = $(git rev-parse --verify HEAD))
@@ -71,7 +75,7 @@ test_expect_success 'http-push fetches unpacked objects' '
        cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
                "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_unpacked.git &&
 
-       git clone $HTTPD_URL/test_repo_unpacked.git \
+       git clone $HTTPD_URL/dumb/test_repo_unpacked.git \
                "$ROOT_PATH"/fetch_unpacked &&
 
        # By reset, we force git to retrieve the object
@@ -80,14 +84,14 @@ test_expect_success 'http-push fetches unpacked objects' '
         git remote rm origin &&
         git reflog expire --expire=0 --all &&
         git prune &&
-        git push -f -v $HTTPD_URL/test_repo_unpacked.git master)
+        git push -f -v $HTTPD_URL/dumb/test_repo_unpacked.git master)
 '
 
 test_expect_success 'http-push fetches packed objects' '
        cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
                "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
 
-       git clone $HTTPD_URL/test_repo_packed.git \
+       git clone $HTTPD_URL/dumb/test_repo_packed.git \
                "$ROOT_PATH"/test_repo_clone_packed &&
 
        (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
@@ -100,7 +104,7 @@ test_expect_success 'http-push fetches packed objects' '
         git remote rm origin &&
         git reflog expire --expire=0 --all &&
         git prune &&
-        git push -f -v $HTTPD_URL/test_repo_packed.git master)
+        git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master)
 '
 
 test_expect_success 'create and delete remote branch' '
@@ -111,10 +115,7 @@ test_expect_success 'create and delete remote branch' '
        test_tick &&
        git commit -m dev &&
        git push origin dev &&
-       git fetch &&
        git push origin :dev &&
-       git branch -d -r origin/dev &&
-       git fetch &&
        test_must_fail git show-ref --verify refs/remotes/origin/dev
 '
 
diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh
new file mode 100755 (executable)
index 0000000..2a58d0c
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test smart pushing over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+       say 'skipping test, git built without http support'
+       test_done
+fi
+
+ROOT_PATH="$PWD"
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5541'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup remote repository' '
+       cd "$ROOT_PATH" &&
+       mkdir test_repo &&
+       cd test_repo &&
+       git init &&
+       : >path1 &&
+       git add path1 &&
+       test_tick &&
+       git commit -m initial &&
+       cd - &&
+       git clone --bare test_repo test_repo.git &&
+       cd test_repo.git &&
+       git config http.receivepack true &&
+       ORIG_HEAD=$(git rev-parse --verify HEAD) &&
+       cd - &&
+       mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
+'
+
+test_expect_success 'clone remote repository' '
+       cd "$ROOT_PATH" &&
+       git clone $HTTPD_URL/smart/test_repo.git test_repo_clone
+'
+
+test_expect_success 'push to remote repository' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       : >path2 &&
+       git add path2 &&
+       test_tick &&
+       git commit -m path2 &&
+       HEAD=$(git rev-parse --verify HEAD) &&
+       git push &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+        test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push already up-to-date' '
+       git push
+'
+
+test_expect_success 'create and delete remote branch' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       git checkout -b dev &&
+       : >path3 &&
+       git add path3 &&
+       test_tick &&
+       git commit -m dev &&
+       git push origin dev &&
+       git push origin :dev &&
+       test_must_fail git show-ref --verify refs/remotes/origin/dev
+'
+
+cat >exp <<EOF
+GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+EOF
+test_expect_success 'used receive-pack service' '
+       sed -e "
+               s/^.* \"//
+               s/\"//
+               s/ [1-9][0-9]*\$//
+               s/^GET /GET  /
+       " >act <"$HTTPD_ROOT_PATH"/access.log &&
+       test_cmp exp act
+'
+
+stop_httpd
+test_done
index 0e69324652026bdeeb319c36782fe1130b0510a1..8cfce969bcdac6e2091e635dad9c58ca616e5c3b 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='test fetching over http'
+test_description='test dumb fetching over http via static file'
 . ./test-lib.sh
 
 if test -n "$NO_CURL"; then
@@ -30,7 +30,7 @@ test_expect_success 'create http-accessible bare repository' '
 '
 
 test_expect_success 'clone http repository' '
-       git clone $HTTPD_URL/repo.git clone &&
+       git clone $HTTPD_URL/dumb/repo.git clone &&
        test_cmp file clone/file
 '
 
@@ -58,7 +58,13 @@ test_expect_success 'fetch packed objects' '
        cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
        git --bare repack &&
        git --bare prune-packed &&
-       git clone $HTTPD_URL/repo_pack.git
+       git clone $HTTPD_URL/dumb/repo_pack.git
+'
+
+test_expect_success 'did not use upload-pack service' '
+       grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
+       : >exp
+       test_cmp exp act
 '
 
 stop_httpd
diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh
new file mode 100755 (executable)
index 0000000..c0505ec
--- /dev/null
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='test smart fetching over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+       say 'skipping test, git built without http support'
+       test_done
+fi
+
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5551'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+       echo content >file &&
+       git add file &&
+       git commit -m one
+'
+
+test_expect_success 'create http-accessible bare repository' '
+       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+        git --bare init
+       ) &&
+       git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git push public master:master
+'
+
+cat >exp <<EOF
+> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
+> Accept: */*
+> Pragma: no-cache
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-advertisement
+> POST /smart/repo.git/git-upload-pack HTTP/1.1
+> Accept-Encoding: deflate, gzip
+> Content-Type: application/x-git-upload-pack-request
+> Accept: application/x-git-upload-pack-response
+> Content-Length: xxx
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-result
+EOF
+test_expect_success 'clone http repository' '
+       GIT_CURL_VERBOSE=1 git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
+       test_cmp file clone/file &&
+       tr '\''\015'\'' Q <err |
+       sed -e "
+               s/Q\$//
+               /^[*] /d
+               /^$/d
+               /^< $/d
+
+               /^[^><]/{
+                       s/^/> /
+               }
+
+               /^> User-Agent: /d
+               /^> Host: /d
+               /^> POST /,$ {
+                       /^> Accept: [*]\\/[*]/d
+               }
+               s/^> Content-Length: .*/> Content-Length: xxx/
+               /^> 00..want /d
+               /^> 00.*done/d
+
+               /^< Server: /d
+               /^< Expires: /d
+               /^< Date: /d
+               /^< Content-Length: /d
+               /^< Transfer-Encoding: /d
+       " >act &&
+       test_cmp exp act
+'
+
+test_expect_success 'fetch changes via http' '
+       echo content >>file &&
+       git commit -a -m two &&
+       git push public
+       (cd clone && git pull) &&
+       test_cmp file clone/file
+'
+
+cat >exp <<EOF
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'used upload-pack service' '
+       sed -e "
+               s/^.* \"//
+               s/\"//
+               s/ [1-9][0-9]*\$//
+               s/^GET /GET  /
+       " >act <"$HTTPD_ROOT_PATH"/access.log &&
+       test_cmp exp act
+'
+
+stop_httpd
+test_done
diff --git a/t/t5560-http-backend.sh b/t/t5560-http-backend.sh
new file mode 100755 (executable)
index 0000000..ed034bc
--- /dev/null
@@ -0,0 +1,260 @@
+#!/bin/sh
+
+test_description='test git-http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+       say 'skipping test, git built without http support'
+       test_done
+fi
+
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5560'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+find_file() {
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       find $1 -type f |
+       sed -e 1q
+}
+
+config() {
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" config $1 $2
+}
+
+GET() {
+       curl --include "$HTTPD_URL/smart/repo.git/$1" >out 2>/dev/null &&
+       tr '\015' Q <out |
+       sed '
+               s/Q$//
+               1q
+       ' >act &&
+       echo "HTTP/1.1 $2" >exp &&
+       test_cmp exp act
+}
+
+POST() {
+       curl --include --data "$2" \
+       --header "Content-Type: application/x-$1-request" \
+       "$HTTPD_URL/smart/repo.git/$1" >out 2>/dev/null &&
+       tr '\015' Q <out |
+       sed '
+               s/Q$//
+               1q
+       ' >act &&
+       echo "HTTP/1.1 $3" >exp &&
+       test_cmp exp act
+}
+
+log_div() {
+       echo >>"$HTTPD_ROOT_PATH"/access.log
+       echo "###  $1" >>"$HTTPD_ROOT_PATH"/access.log
+       echo "###" >>"$HTTPD_ROOT_PATH"/access.log
+}
+
+test_expect_success 'setup repository' '
+       echo content >file &&
+       git add file &&
+       git commit -m one &&
+
+       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+        git --bare init &&
+        : >objects/info/alternates &&
+        : >objects/info/http-alternates
+       ) &&
+       git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git push public master:master &&
+
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+        git repack -a -d
+       ) &&
+
+       echo other >file &&
+       git add file &&
+       git commit -m two &&
+       git push public master:master &&
+
+       LOOSE_URL=$(find_file objects/??) &&
+       PACK_URL=$(find_file objects/pack/*.pack) &&
+       IDX_URL=$(find_file objects/pack/*.idx)
+'
+
+get_static_files() {
+       GET HEAD "$1" &&
+       GET info/refs "$1" &&
+       GET objects/info/packs "$1" &&
+       GET objects/info/alternates "$1" &&
+       GET objects/info/http-alternates "$1" &&
+       GET $LOOSE_URL "$1" &&
+       GET $PACK_URL "$1" &&
+       GET $IDX_URL "$1"
+}
+
+test_expect_success 'direct refs/heads/master not found' '
+       log_div "refs/heads/master"
+       GET refs/heads/master "404 Not Found"
+'
+test_expect_success 'static file is ok' '
+       log_div "getanyfile default"
+       get_static_files "200 OK"
+'
+test_expect_success 'static file if http.getanyfile true is ok' '
+       log_div "getanyfile true"
+       config http.getanyfile true &&
+       get_static_files "200 OK"
+'
+test_expect_success 'static file if http.getanyfile false fails' '
+       log_div "getanyfile false"
+       config http.getanyfile false &&
+       get_static_files "403 Forbidden"
+'
+
+test_expect_success 'http.uploadpack default enabled' '
+       log_div "uploadpack default"
+       GET info/refs?service=git-upload-pack "200 OK"  &&
+       POST git-upload-pack 0000 "200 OK"
+'
+test_expect_success 'http.uploadpack true' '
+       log_div "uploadpack true"
+       config http.uploadpack true &&
+       GET info/refs?service=git-upload-pack "200 OK" &&
+       POST git-upload-pack 0000 "200 OK"
+'
+test_expect_success 'http.uploadpack false' '
+       log_div "uploadpack false"
+       config http.uploadpack false &&
+       GET info/refs?service=git-upload-pack "403 Forbidden" &&
+       POST git-upload-pack 0000 "403 Forbidden"
+'
+
+test_expect_success 'http.receivepack default disabled' '
+       log_div "receivepack default"
+       GET info/refs?service=git-receive-pack "403 Forbidden"  &&
+       POST git-receive-pack 0000 "403 Forbidden"
+'
+test_expect_success 'http.receivepack true' '
+       log_div "receivepack true"
+       config http.receivepack true &&
+       GET info/refs?service=git-receive-pack "200 OK" &&
+       POST git-receive-pack 0000 "200 OK"
+'
+test_expect_success 'http.receivepack false' '
+       log_div "receivepack false"
+       config http.receivepack false &&
+       GET info/refs?service=git-receive-pack "403 Forbidden" &&
+       POST git-receive-pack 0000 "403 Forbidden"
+'
+
+run_backend() {
+       REQUEST_METHOD=GET \
+       GIT_PROJECT_ROOT="$HTTPD_DOCUMENT_ROOT_PATH" \
+       PATH_INFO="$2" \
+       git http-backend >act.out 2>act.err
+}
+
+path_info() {
+       if test $1 = 0; then
+               run_backend "$2"
+       else
+               test_must_fail run_backend "$2" &&
+               echo "fatal: '$2': aliased" >exp.err &&
+               test_cmp exp.err act.err
+       fi
+}
+
+test_expect_success 'http-backend blocks bad PATH_INFO' '
+       config http.getanyfile true &&
+
+       run_backend 0 /repo.git/HEAD &&
+
+       run_backend 1 /repo.git/../HEAD &&
+       run_backend 1 /../etc/passwd &&
+       run_backend 1 ../etc/passwd &&
+       run_backend 1 /etc//passwd &&
+       run_backend 1 /etc/./passwd &&
+       run_backend 1 /etc/.../passwd &&
+       run_backend 1 //domain/data.txt
+'
+
+cat >exp <<EOF
+
+###  refs/heads/master
+###
+GET  /smart/repo.git/refs/heads/master HTTP/1.1 404 -
+
+###  getanyfile default
+###
+GET  /smart/repo.git/HEAD HTTP/1.1 200
+GET  /smart/repo.git/info/refs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/packs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 200
+GET  /smart/repo.git/$PACK_URL HTTP/1.1 200
+GET  /smart/repo.git/$IDX_URL HTTP/1.1 200
+
+###  getanyfile true
+###
+GET  /smart/repo.git/HEAD HTTP/1.1 200
+GET  /smart/repo.git/info/refs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/packs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 200
+GET  /smart/repo.git/$PACK_URL HTTP/1.1 200
+GET  /smart/repo.git/$IDX_URL HTTP/1.1 200
+
+###  getanyfile false
+###
+GET  /smart/repo.git/HEAD HTTP/1.1 403 -
+GET  /smart/repo.git/info/refs HTTP/1.1 403 -
+GET  /smart/repo.git/objects/info/packs HTTP/1.1 403 -
+GET  /smart/repo.git/objects/info/alternates HTTP/1.1 403 -
+GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 403 -
+GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 403 -
+GET  /smart/repo.git/$PACK_URL HTTP/1.1 403 -
+GET  /smart/repo.git/$IDX_URL HTTP/1.1 403 -
+
+###  uploadpack default
+###
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -
+
+###  uploadpack true
+###
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -
+
+###  uploadpack false
+###
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-upload-pack HTTP/1.1 403 -
+
+###  receivepack default
+###
+GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
+
+###  receivepack true
+###
+GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/repo.git/git-receive-pack HTTP/1.1 200 -
+
+###  receivepack false
+###
+GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
+EOF
+test_expect_success 'server request log matches test results' '
+       sed -e "
+               s/^.* \"//
+               s/\"//
+               s/ [1-9][0-9]*\$//
+               s/^GET /GET  /
+       " >act <"$HTTPD_ROOT_PATH"/access.log &&
+       test_cmp exp act
+'
+
+stop_httpd
+test_done
diff --git a/t/t6017-rev-list-stdin.sh b/t/t6017-rev-list-stdin.sh
new file mode 100755 (executable)
index 0000000..f1c32db
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, Junio C Hamano
+#
+
+test_description='log family learns --stdin'
+
+. ./test-lib.sh
+
+check () {
+       for cmd in rev-list "log --stat"
+       do
+               for i in "$@"
+               do
+                       printf "%s\n" $i
+               done >input &&
+               test_expect_success "check $cmd $*" '
+                       git $cmd $(cat input) >expect &&
+                       git $cmd --stdin <input >actual &&
+                       sed -e "s/^/input /" input &&
+                       sed -e "s/^/output /" expect &&
+                       test_cmp expect actual
+               '
+       done
+}
+
+them='1 2 3 4 5 6 7'
+
+test_expect_success setup '
+       (
+               for i in 0 $them
+               do
+                       for j in $them
+                       do
+                               echo $i.$j >file-$j &&
+                               git add file-$j || exit
+                       done &&
+                       test_tick &&
+                       git commit -m $i || exit
+               done &&
+               for i in $them
+               do
+                       git checkout -b side-$i master~$i &&
+                       echo updated $i >file-$i &&
+                       git add file-$i &&
+                       test_tick &&
+                       git commit -m side-$i || exit
+               done
+       )
+'
+
+check master
+check side-1 ^side-4
+check side-1 ^side-7 --
+check side-1 ^side-7 -- file-1
+check side-1 ^side-7 -- file-2
+check side-3 ^side-4 -- file-3
+check side-3 ^side-2
+check side-3 ^side-2 -- file-1
+
+test_done
index f8f3e3ff2c00df468f5703a4e0ac31f52e42e06d..a91644e3b2ac3490cfe49d5e67c9736197cd56a1 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='merge fast forward and up to date'
+test_description='merge fast-forward and up to date'
 
 . ./test-lib.sh
 
index d4818b430aad556cec959d8be221eb7548a55707..203ffdb17a914654d35416575b6797a2825ce4e6 100755 (executable)
@@ -77,6 +77,11 @@ test_expect_success 'test --no-replace-objects option' '
      git --no-replace-objects show $HASH2 | grep "A U Thor"
 '
 
+test_expect_success 'test GIT_NO_REPLACE_OBJECTS env variable' '
+     GIT_NO_REPLACE_OBJECTS=1 git cat-file commit $HASH2 | grep "author A U Thor" &&
+     GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor"
+'
+
 cat >tag.sig <<EOF
 object $HASH2
 type commit
@@ -202,6 +207,18 @@ test_expect_success 'fetch branch with replacement' '
      cd ..
 '
 
+test_expect_success 'bisect and replacements' '
+     git bisect start $HASH7 $HASH1 &&
+     test "$S" = "$(git rev-parse --verify HEAD)" &&
+     git bisect reset &&
+     GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
+     test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+     git bisect reset &&
+     git --no-replace-objects bisect start $HASH7 $HASH1 &&
+     test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+     git bisect reset
+'
+
 #
 #
 test_done
index f5a1b615f65ecac2becff67d47842e7aed3d6091..065deadc29eb3838f391ce758c4b188249dc87f9 100755 (executable)
@@ -92,12 +92,18 @@ check_describe A-* HEAD^
 check_describe D-* HEAD^^
 check_describe A-* HEAD^^2
 check_describe B HEAD^^2^
+check_describe D-* HEAD^^^
 
 check_describe c-* --tags HEAD
 check_describe c-* --tags HEAD^
 check_describe e-* --tags HEAD^^
 check_describe c-* --tags HEAD^^2
 check_describe B --tags HEAD^^2^
+check_describe e --tags HEAD^^^
+
+check_describe heads/master --all HEAD
+check_describe tags/c-* --all HEAD^
+check_describe tags/e --all HEAD^^^
 
 check_describe B-0-* --long HEAD^^2^
 check_describe A-3-* --long HEAD^^2
@@ -125,6 +131,20 @@ test_expect_success 'rename tag Q back to A' '
 test_expect_success 'pack tag refs' 'git pack-refs'
 check_describe A-* HEAD
 
+check_describe "A-*[0-9a-f]" --dirty
+
+test_expect_success 'set-up dirty work tree' '
+       echo >>file
+'
+
+check_describe "A-*[0-9a-f]-dirty" --dirty
+
+check_describe "A-*[0-9a-f].mod" --dirty=.mod
+
+test_expect_success 'describe --dirty HEAD' '
+       test_must_fail git describe --dirty HEAD
+'
+
 test_expect_success 'set-up matching pattern tests' '
        git tag -a -m test-annotated test-annotated &&
        echo >>file &&
index ae5290ab43e8b51b32331768371e2a67d700064d..abd14bf819f5c60fc1b9dc758c04974bd24b10a4 100755 (executable)
@@ -14,6 +14,7 @@ int main(int argc, const char **argv)
 {
        printf("Hello world.\n");
        return 0;
+       /* char ?? */
 }
 EOF
 
@@ -213,6 +214,11 @@ test_expect_success 'grep -e A --and --not -e B' '
        test_cmp expected actual
 '
 
+test_expect_success 'grep should ignore GREP_OPTIONS' '
+       GREP_OPTIONS=-v git grep " mmap bar\$" >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'grep -f, non-existent file' '
        test_must_fail git grep -f patterns
 '
@@ -411,4 +417,13 @@ test_expect_success 'grep from a subdirectory to search wider area (2)' '
        )
 '
 
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+EOF
+
+test_expect_success 'grep -Fi' '
+       git grep -Fi "CHAR *" >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 329c851685b1c663ee88d45a5d21d452a293fa8e..9503875e97e09ad94283e63ae298551453e5787a 100755 (executable)
@@ -288,4 +288,22 @@ test_expect_success 'Prune empty commits' '
        test_cmp expect actual
 '
 
+test_expect_success '--remap-to-ancestor with filename filters' '
+       git checkout master &&
+       git reset --hard A &&
+       test_commit add-foo foo 1 &&
+       git branch moved-foo &&
+       test_commit add-bar bar a &&
+       git branch invariant &&
+       orig_invariant=$(git rev-parse invariant) &&
+       git branch moved-bar &&
+       test_commit change-foo foo 2 &&
+       git filter-branch -f --remap-to-ancestor \
+               moved-foo moved-bar A..master \
+               -- -- foo &&
+       test $(git rev-parse moved-foo) = $(git rev-parse moved-bar) &&
+       test $(git rev-parse moved-foo) = $(git rev-parse master^) &&
+       test $orig_invariant = $(git rev-parse invariant)
+'
+
 test_done
index b647957d75fa0d0ce4d88c7c3c7243f31af38b4a..5257f4d261c2060b881d2649034232f76f4ed9b7 100755 (executable)
@@ -4,7 +4,21 @@ test_description='GIT_EDITOR, core.editor, and stuff'
 
 . ./test-lib.sh
 
-for i in GIT_EDITOR core_editor EDITOR VISUAL vi
+unset EDITOR VISUAL GIT_EDITOR
+
+test_expect_success 'determine default editor' '
+
+       vi=$(TERM=vt100 git var GIT_EDITOR) &&
+       test -n "$vi"
+
+'
+
+if ! expr "$vi" : '^[a-z]*$' >/dev/null
+then
+       vi=
+fi
+
+for i in GIT_EDITOR core_editor EDITOR VISUAL $vi
 do
        cat >e-$i.sh <<-EOF
        #!$SHELL_PATH
@@ -12,19 +26,18 @@ do
        EOF
        chmod +x e-$i.sh
 done
-unset vi
-mv e-vi.sh vi
-unset EDITOR VISUAL GIT_EDITOR
+
+if ! test -z "$vi"
+then
+       mv e-$vi.sh $vi
+fi
 
 test_expect_success setup '
 
-       msg="Hand edited" &&
+       msg="Hand-edited" &&
+       test_commit "$msg" &&
        echo "$msg" >expect &&
-       git add vi &&
-       test_tick &&
-       git commit -m "$msg" &&
-       git show -s --pretty=oneline |
-       sed -e "s/^[0-9a-f]* //" >actual &&
+       git show -s --format=%s > actual &&
        diff actual expect
 
 '
@@ -42,9 +55,19 @@ test_expect_success 'dumb should error out when falling back on vi' '
        fi
 '
 
+test_expect_success 'dumb should prefer EDITOR to VISUAL' '
+
+       EDITOR=./e-EDITOR.sh &&
+       VISUAL=./e-VISUAL.sh &&
+       export EDITOR VISUAL &&
+       git commit --amend &&
+       test "$(git show -s --format=%s)" = "Edited by EDITOR"
+
+'
+
 TERM=vt100
 export TERM
-for i in vi EDITOR VISUAL core_editor GIT_EDITOR
+for i in $vi EDITOR VISUAL core_editor GIT_EDITOR
 do
        echo "Edited by $i" >expect
        unset EDITOR VISUAL GIT_EDITOR
@@ -68,7 +91,7 @@ done
 
 unset EDITOR VISUAL GIT_EDITOR
 git config --unset-all core.editor
-for i in vi EDITOR VISUAL core_editor GIT_EDITOR
+for i in $vi EDITOR VISUAL core_editor GIT_EDITOR
 do
        echo "Edited by $i" >expect
        case "$i" in
index 0f2ccc6cf0123951d9bdbb880931868f29de5b4e..a0cc99ab9f5b262851a1075193f5529b5582fd0a 100755 (executable)
@@ -306,4 +306,20 @@ test_expect_success 'submodule <invalid-path> warns' '
 
 '
 
+test_expect_success 'add submodules without specifying an explicit path' '
+       mkdir repo &&
+       cd repo &&
+       git init &&
+       echo r >r &&
+       git add r &&
+       git commit -m "repo commit 1" &&
+       cd .. &&
+       git clone --bare repo/ bare.git &&
+       cd addtest &&
+       git submodule add "$submodurl/repo" &&
+       git config -f .gitmodules submodule.repo.path repo &&
+       git submodule add "$submodurl/bare.git" &&
+       git config -f .gitmodules submodule.bare.path bare
+'
+
 test_done
index 2d33d9efec3f2f601305e736e31368621acd0584..8e2449d24409bab14558f83617f651c3f7255627 100755 (executable)
@@ -14,8 +14,8 @@ submodule and "git submodule update --rebase/--merge" does not detach the HEAD.
 
 compare_head()
 {
-    sha_master=`git-rev-list --max-count=1 master`
-    sha_head=`git-rev-list --max-count=1 HEAD`
+    sha_master=`git rev-list --max-count=1 master`
+    sha_head=`git rev-list --max-count=1 HEAD`
 
     test "$sha_master" = "$sha_head"
 }
index d2de57679f9ee347a5e77e751ae7767f9980f84e..a603f6d21a61df197256ea91bfb38c1f4e45c5e8 100755 (executable)
@@ -86,7 +86,7 @@ chmod 755 editor
 
 test_expect_success \
        "amend commit" \
-       "VISUAL=./editor git commit --amend"
+       "EDITOR=./editor git commit --amend"
 
 test_expect_success \
        "passing -m and -F" \
@@ -107,7 +107,7 @@ chmod 755 editor
 test_expect_success \
        "editing message from other commit" \
        "echo 'hula hula' >file && \
-        VISUAL=./editor git commit -c HEAD^ -a"
+        EDITOR=./editor git commit -c HEAD^ -a"
 
 test_expect_success \
        "message from stdin" \
@@ -141,10 +141,10 @@ EOF
 test_expect_success \
        'editor not invoked if -F is given' '
         echo "moo" >file &&
-        VISUAL=./editor git commit -a -F msg &&
+        EDITOR=./editor git commit -a -F msg &&
         git show -s --pretty=format:"%s" | grep -q good &&
         echo "quack" >file &&
-        echo "Another good message." | VISUAL=./editor git commit -a -F - &&
+        echo "Another good message." | EDITOR=./editor git commit -a -F - &&
         git show -s --pretty=format:"%s" | grep -q good
         '
 # We could just check the head sha1, but checking each commit makes it
diff --git a/t/t7509-commit.sh b/t/t7509-commit.sh
new file mode 100755 (executable)
index 0000000..d52c060
--- /dev/null
@@ -0,0 +1,114 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Erick Mattos
+#
+
+test_description='git commit --reset-author'
+
+. ./test-lib.sh
+
+author_header () {
+       git cat-file commit "$1" |
+       sed -n -e '/^$/q' -e '/^author /p'
+}
+
+message_body () {
+       git cat-file commit "$1" |
+       sed -e '1,/^$/d'
+}
+
+test_expect_success '-C option copies authorship and message' '
+       echo "Initial" >foo &&
+       git add foo &&
+       test_tick &&
+       git commit -m "Initial Commit" --author Frigate\ \<flying@over.world\> &&
+       git tag Initial &&
+       echo "Test 1" >>foo &&
+       test_tick &&
+       git commit -a -C Initial &&
+       author_header Initial >expect &&
+       author_header HEAD >actual &&
+       test_cmp expect actual &&
+
+       message_body Initial >expect &&
+       message_body HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '-C option copies only the message with --reset-author' '
+       echo "Test 2" >>foo &&
+       test_tick &&
+       git commit -a -C Initial --reset-author &&
+       echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+       author_header HEAD >actual
+       test_cmp expect actual &&
+
+       message_body Initial >expect &&
+       message_body HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '-c option copies authorship and message' '
+       echo "Test 3" >>foo &&
+       test_tick &&
+       EDITOR=: VISUAL=: git commit -a -c Initial &&
+       author_header Initial >expect &&
+       author_header HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '-c option copies only the message with --reset-author' '
+       echo "Test 4" >>foo &&
+       test_tick &&
+       EDITOR=: VISUAL=: git commit -a -c Initial --reset-author &&
+       echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+       author_header HEAD >actual &&
+       test_cmp expect actual &&
+
+       message_body Initial >expect &&
+       message_body HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--amend option copies authorship' '
+       git checkout Initial &&
+       echo "Test 5" >>foo &&
+       test_tick &&
+       git commit -a --amend -m "amend test" &&
+       author_header Initial >expect &&
+       author_header HEAD >actual &&
+
+       echo "amend test" >expect &&
+       message_body HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--reset-author makes the commit ours even with --amend option' '
+       git checkout Initial &&
+       echo "Test 6" >>foo &&
+       test_tick &&
+       git commit -a --reset-author -m "Changed again" --amend &&
+       echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+       author_header HEAD >actual &&
+       test_cmp expect actual &&
+
+       echo "Changed again" >expect &&
+       message_body HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--reset-author and --author are mutually exclusive' '
+       git checkout Initial &&
+       echo "Test 7" >>foo &&
+       test_tick &&
+       test_must_fail git commit -a --reset-author --author="Xyzzy <frotz@nitfol.xz>"
+'
+
+test_expect_success '--reset-author should be rejected without -c/-C/--amend' '
+       git checkout Initial &&
+       echo "Test 7" >>foo &&
+       test_tick &&
+       test_must_fail git commit -a --reset-author -m done
+'
+
+test_done
index e5b210bc960c8433d6758f3932a86647208297ef..57f6d2bae7c63f13ee18e11737dbdcc0c080ab10 100755 (executable)
@@ -243,6 +243,16 @@ test_expect_success 'merge c0 with c1' '
 
 test_debug 'gitk --all'
 
+test_expect_success 'merge c0 with c1 with --ff-only' '
+       git reset --hard c0 &&
+       git merge --ff-only c1 &&
+       git merge --ff-only HEAD c0 c1 &&
+       verify_merge file result.1 &&
+       verify_head "$c1"
+'
+
+test_debug 'gitk --all'
+
 test_expect_success 'merge c1 with c2' '
        git reset --hard c1 &&
        test_tick &&
@@ -263,6 +273,14 @@ test_expect_success 'merge c1 with c2 and c3' '
 
 test_debug 'gitk --all'
 
+test_expect_success 'failing merges with --ff-only' '
+       git reset --hard c1 &&
+       test_tick &&
+       test_must_fail git merge --ff-only c2 &&
+       test_must_fail git merge --ff-only c3 &&
+       test_must_fail git merge --ff-only c2 c3
+'
+
 test_expect_success 'merge c0 with c1 (no-commit)' '
        git reset --hard c0 &&
        git merge --no-commit c1 &&
@@ -303,6 +321,17 @@ test_expect_success 'merge c0 with c1 (squash)' '
 
 test_debug 'gitk --all'
 
+test_expect_success 'merge c0 with c1 (squash, ff-only)' '
+       git reset --hard c0 &&
+       git merge --squash --ff-only c1 &&
+       verify_merge file result.1 &&
+       verify_head $c0 &&
+       verify_no_mergehead &&
+       verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
+'
+
+test_debug 'gitk --all'
+
 test_expect_success 'merge c1 with c2 (squash)' '
        git reset --hard c1 &&
        git merge --squash c2 &&
@@ -314,6 +343,13 @@ test_expect_success 'merge c1 with c2 (squash)' '
 
 test_debug 'gitk --all'
 
+test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
+       git reset --hard c1 &&
+       test_must_fail git merge --squash --ff-only c2
+'
+
+test_debug 'gitk --all'
+
 test_expect_success 'merge c1 with c2 and c3 (squash)' '
        git reset --hard c1 &&
        git merge --squash c2 c3 &&
@@ -432,6 +468,11 @@ test_expect_success 'combining --squash and --no-ff is refused' '
        test_must_fail git merge --no-ff --squash c1
 '
 
+test_expect_success 'combining --ff-only and --no-ff is refused' '
+       test_must_fail git merge --ff-only --no-ff c1 &&
+       test_must_fail git merge --no-ff --ff-only c1
+'
+
 test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
        git reset --hard c0 &&
        git config branch.master.mergeoptions "--no-ff" &&
index 84a7f03d46ac4a624afd7d6d0d3c6198e41219c1..fb51ab3dbea92be5f345517e35c2fcc432c59f45 100755 (executable)
@@ -95,6 +95,40 @@ test_expect_success \
     'Verify commandline' \
     'test_cmp expected commandline1'
 
+test_expect_success 'Send patches with --envelope-sender' '
+    clean_fake_sendmail &&
+     git send-email --envelope-sender="Patch Contributer <patch@example.com>" --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+'
+
+cat >expected <<\EOF
+!patch@example.com!
+!-i!
+!nobody@example.com!
+!author@example.com!
+!one@example.com!
+!two@example.com!
+EOF
+test_expect_success \
+    'Verify commandline' \
+    'test_cmp expected commandline1'
+
+test_expect_success 'Send patches with --envelope-sender=auto' '
+    clean_fake_sendmail &&
+     git send-email --envelope-sender=auto --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+'
+
+cat >expected <<\EOF
+!nobody@example.com!
+!-i!
+!nobody@example.com!
+!author@example.com!
+!one@example.com!
+!two@example.com!
+EOF
+test_expect_success \
+    'Verify commandline' \
+    'test_cmp expected commandline1'
+
 cat >expected-show-all-headers <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
@@ -769,4 +803,53 @@ test_expect_success 'threading but no chain-reply-to' '
        grep "In-Reply-To: " stdout
 '
 
+test_expect_success 'warning with an implicit --chain-reply-to' '
+       git send-email \
+       --dry-run \
+       --from="Example <nobody@example.com>" \
+       --to=nobody@example.com \
+       outdir/000?-*.patch 2>errors >out &&
+       grep "no-chain-reply-to" errors
+'
+
+test_expect_success 'no warning with an explicit --chain-reply-to' '
+       git send-email \
+       --dry-run \
+       --from="Example <nobody@example.com>" \
+       --to=nobody@example.com \
+       --chain-reply-to \
+       outdir/000?-*.patch 2>errors >out &&
+       ! grep "no-chain-reply-to" errors
+'
+
+test_expect_success 'no warning with an explicit --no-chain-reply-to' '
+       git send-email \
+       --dry-run \
+       --from="Example <nobody@example.com>" \
+       --to=nobody@example.com \
+       --no-chain-reply-to \
+       outdir/000?-*.patch 2>errors >out &&
+       ! grep "no-chain-reply-to" errors
+'
+
+test_expect_success 'no warning with sendemail.chainreplyto = false' '
+       git config sendemail.chainreplyto false &&
+       git send-email \
+       --dry-run \
+       --from="Example <nobody@example.com>" \
+       --to=nobody@example.com \
+       outdir/000?-*.patch 2>errors >out &&
+       ! grep "no-chain-reply-to" errors
+'
+
+test_expect_success 'no warning with sendemail.chainreplyto = true' '
+       git config sendemail.chainreplyto true &&
+       git send-email \
+       --dry-run \
+       --from="Example <nobody@example.com>" \
+       --to=nobody@example.com \
+       outdir/000?-*.patch 2>errors >out &&
+       ! grep "no-chain-reply-to" errors
+'
+
 test_done
index 9be7aefaeea0af4d988ca656b3df0008f04a58d3..767799e7a70b91ef6f4e3f4007529f1cb0fd919c 100755 (executable)
@@ -19,7 +19,7 @@ test_expect_success 'init and fetch repository' '
        '
 
 test_expect_success 'create file in existing ugly and empty dir' '
-       mkdir "#{bad_directory_name}" &&
+       mkdir -p "#{bad_directory_name}" &&
        echo hi > "#{bad_directory_name}/ foo" &&
        git update-index --add "#{bad_directory_name}/ foo" &&
        git commit -m "new file in ugly parent" &&
@@ -37,7 +37,7 @@ test_expect_success 'rename pretty file' '
        git update-index --add pretty &&
        git commit -m "pretty :x" &&
        git svn dcommit &&
-       mkdir regular_dir_name &&
+       mkdir -p regular_dir_name &&
        git mv pretty regular_dir_name/pretty &&
        git commit -m "moved pretty file" &&
        git svn dcommit
index f5abdb3c7fd24033f1538db51c957345ab86e341..134411e0a56142930a418ca15bd0902837c7bdc1 100755 (executable)
@@ -91,4 +91,27 @@ test_expect_success 'fetch continues after authors-file is fixed' '
        )
        '
 
+test_expect_success 'fresh clone with svn.authors-file in config' '
+       (
+               rm -r "$GIT_DIR" &&
+               test x = x"$(git config svn.authorsfile)" &&
+               HOME="`pwd`" &&
+               export HOME &&
+               test_config="$HOME"/.gitconfig &&
+               unset GIT_CONFIG_NOGLOBAL &&
+               unset GIT_DIR &&
+               unset GIT_CONFIG &&
+               git config --global \
+                 svn.authorsfile "$HOME"/svn-authors &&
+               test x"$HOME"/svn-authors = x"$(git config svn.authorsfile)" &&
+               git svn clone "$svnrepo" gitconfig.clone &&
+               cd gitconfig.clone &&
+               nr_ex=$(git log | grep "^Author:.*example.com" | wc -l) &&
+               nr_rev=$(git rev-list HEAD | wc -l) &&
+               test $nr_rev -eq $nr_ex
+       )
+'
+
+test_debug 'GIT_DIR=gitconfig.clone/.git git log'
+
 test_done
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
new file mode 100755 (executable)
index 0000000..70c52c1
--- /dev/null
@@ -0,0 +1,108 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn creates empty directories'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+       for i in a b c d d/e d/e/f "weird file name"
+       do
+               svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+       done
+'
+
+test_expect_success 'clone' 'git svn clone "$svnrepo" cloned'
+
+test_expect_success 'empty directories exist' '
+       (
+               cd cloned &&
+               for i in a b c d d/e d/e/f "weird file name"
+               do
+                       if ! test -d "$i"
+                       then
+                               echo >&2 "$i does not exist"
+                               exit 1
+                       fi
+               done
+       )
+'
+
+test_expect_success 'more emptiness' '
+       svn_cmd mkdir -m "bang bang"  "$svnrepo"/"! !"
+'
+
+test_expect_success 'git svn rebase creates empty directory' '
+       ( cd cloned && git svn rebase )
+       test -d cloned/"! !"
+'
+
+test_expect_success 'git svn mkdirs recreates empty directories' '
+       (
+               cd cloned &&
+               rm -r * &&
+               git svn mkdirs &&
+               for i in a b c d d/e d/e/f "weird file name" "! !"
+               do
+                       if ! test -d "$i"
+                       then
+                               echo >&2 "$i does not exist"
+                               exit 1
+                       fi
+               done
+       )
+'
+
+test_expect_success 'git svn mkdirs -r works' '
+       (
+               cd cloned &&
+               rm -r * &&
+               git svn mkdirs -r7 &&
+               for i in a b c d d/e d/e/f "weird file name"
+               do
+                       if ! test -d "$i"
+                       then
+                               echo >&2 "$i does not exist"
+                               exit 1
+                       fi
+               done
+
+               if test -d "! !"
+               then
+                       echo >&2 "$i should not exist"
+                       exit 1
+               fi
+
+               git svn mkdirs -r8 &&
+               if ! test -d "! !"
+               then
+                       echo >&2 "$i not exist"
+                       exit 1
+               fi
+       )
+'
+
+test_expect_success 'initialize trunk' '
+       for i in trunk trunk/a trunk/"weird file name"
+       do
+               svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+       done
+'
+
+test_expect_success 'clone trunk' 'git svn clone -s "$svnrepo" trunk'
+
+test_expect_success 'empty directories in trunk exist' '
+       (
+               cd trunk &&
+               for i in a "weird file name"
+               do
+                       if ! test -d "$i"
+                       then
+                               echo >&2 "$i does not exist"
+                               exit 1
+                       fi
+               done
+       )
+'
+
+test_done
index dd0c2bad24db26abdab7a91acee109978421ea3e..53581425c4b5cd8e7b35b41605511d52d005e8d6 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success 'load svk depot' "
 uuid=b48289b2-9c08-4d72-af37-0358a40b9c15
 
 test_expect_success 'svk merges were represented coming in' "
-       [ `git-cat-file commit HEAD | grep parent | wc -l` -eq 2 ]
+       [ `git cat-file commit HEAD | grep parent | wc -l` -eq 2 ]
        "
 
 test_done
index 9bee516358bd98d1b5c701f6aa0c4f3e4d95bd45..f57daf401ac7c36c1d9f2380200e0efad2400f70 100755 (executable)
@@ -15,7 +15,11 @@ test_expect_success 'load svn dump' "
        git svn fetch --all
        "
 
-test_expect_success 'svn merges were represented coming in' "
+test_expect_success 'represent svn merges without intervening commits' "
+       [ `git cat-file commit HEAD^1 | grep parent | wc -l` -eq 2 ]
+       "
+
+test_expect_success 'represent svn merges with intervening commits' "
        [ `git cat-file commit HEAD | grep parent | wc -l` -eq 2 ]
        "
 
index e35d64d5859076ac09ec719e3ff9cd6700936533..7e3da75f8602621f0f202fc812fad2feefccda28 100644 (file)
@@ -28,6 +28,10 @@ svn cp trunk branches/left
 
 echo "Committing BRANCH POINT"
 svn commit -m "make left branch"
+svn cp trunk branches/right
+
+echo "Committing other BRANCH POINT"
+svn commit -m "make right branch"
 cd branches/left/
 
 #$sm init
@@ -64,7 +68,33 @@ git cat-file blob b51ad431:Makefile > Makefile
 
 svn resolved Makefile
 
-svn commit -m "Merge trunk"
+svn commit -m "Merge trunk 1"
+
+# create commits on both branches
+
+cd ../branches/left
+git cat-file blob ff5ebe39:Makefile > Makefile
+echo "Committing BRANCH UPDATE 4"
+svn commit -m "left update 4"
+
+cd ../right
+git cat-file blob b5039db6:Makefile > Makefile
+echo "Committing other BRANCH UPDATE 1"
+svn commit -m "right update 1"
+
+# merge to trun again
+
+cd ../..
+svn update
+cd trunk
+
+svn merge ../branches/left --accept postpone
+
+git cat-file blob b51ad431:Makefile > Makefile
+
+svn resolved Makefile
+
+svn commit -m "Merge trunk 2"
 
 cd ../..
 
index 2153187c9b11f7f17cb2b397086b43d675fe290e..11a883fda96ec81f3ed9ba3a9cfc424cc41d8103 100644 (file)
@@ -1,6 +1,6 @@
 SVN-fs-dump-format-version: 2
 
-UUID: 1ce241d1-ba54-4eb9-bded-03057fe48a33
+UUID: 1530d5a2-a1dc-4438-8ad5-d95e96db8945
 
 Revision-number: 0
 Prop-content-length: 56
@@ -9,12 +9,12 @@ Content-length: 56
 K 8
 svn:date
 V 27
-2009-10-20T01:33:37.692723Z
+2009-11-12T20:29:38.812226Z
 PROPS-END
 
 Revision-number: 1
-Prop-content-length: 123
-Content-length: 123
+Prop-content-length: 127
+Content-length: 127
 
 K 7
 svn:log
@@ -22,12 +22,12 @@ V 24
 Setup trunk and branches
 K 10
 svn:author
-V 4
-samv
+V 8
+tallsopp
 K 8
 svn:date
 V 27
-2009-10-20T01:33:38.159933Z
+2009-11-12T20:29:39.045856Z
 PROPS-END
 
 Node-path: branches
@@ -49,8 +49,8 @@ PROPS-END
 
 
 Revision-number: 2
-Prop-content-length: 106
-Content-length: 106
+Prop-content-length: 110
+Content-length: 110
 
 K 7
 svn:log
@@ -58,12 +58,12 @@ V 8
 ancestor
 K 10
 svn:author
-V 4
-samv
+V 8
+tallsopp
 K 8
 svn:date
 V 27
-2009-10-20T01:33:39.160059Z
+2009-11-12T20:29:40.079587Z
 PROPS-END
 
 Node-path: trunk/Makefile
@@ -72,6 +72,7 @@ Node-action: add
 Prop-content-length: 10
 Text-content-length: 2401
 Text-content-md5: bfd8ff778d1492dc6758567373176a89
+Text-content-sha1: 103205ce331f7d64086dba497574734f78439590
 Content-length: 2411
 
 PROPS-END
@@ -155,8 +156,8 @@ backup: clean
 
 
 Revision-number: 3
-Prop-content-length: 115
-Content-length: 115
+Prop-content-length: 119
+Content-length: 119
 
 K 7
 svn:log
@@ -164,12 +165,12 @@ V 16
 make left branch
 K 10
 svn:author
-V 4
-samv
+V 8
+tallsopp
 K 8
 svn:date
 V 27
-2009-10-20T01:33:41.148192Z
+2009-11-12T20:29:42.084439Z
 PROPS-END
 
 Node-path: branches/left
@@ -177,27 +178,54 @@ Node-kind: dir
 Node-action: add
 Node-copyfrom-rev: 1
 Node-copyfrom-path: trunk
-Prop-content-length: 34
-Content-length: 34
 
-K 13
-svn:mergeinfo
-V 0
 
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/Makefile
+Text-copy-source-md5: bfd8ff778d1492dc6758567373176a89
+Text-copy-source-sha1: 103205ce331f7d64086dba497574734f78439590
+
+
+Revision-number: 4
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 17
+make right branch
+K 10
+svn:author
+V 8
+tallsopp
+K 8
+svn:date
+V 27
+2009-11-12T20:29:44.065452Z
 PROPS-END
 
+Node-path: branches/right
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
 
-Node-path: branches/left/Makefile
+Node-path: branches/right/Makefile
 Node-kind: file
 Node-action: add
 Node-copyfrom-rev: 2
 Node-copyfrom-path: trunk/Makefile
 Text-copy-source-md5: bfd8ff778d1492dc6758567373176a89
+Text-copy-source-sha1: 103205ce331f7d64086dba497574734f78439590
 
 
-Revision-number: 4
-Prop-content-length: 112
-Content-length: 112
+Revision-number: 5
+Prop-content-length: 116
+Content-length: 116
 
 K 7
 svn:log
@@ -205,12 +233,12 @@ V 13
 left update 1
 K 10
 svn:author
-V 4
-samv
+V 8
+tallsopp
 K 8
 svn:date
 V 27
-2009-10-20T01:33:42.148773Z
+2009-11-12T20:29:45.066262Z
 PROPS-END
 
 Node-path: branches/left/Makefile
@@ -218,6 +246,7 @@ Node-kind: file
 Node-action: change
 Text-content-length: 2465
 Text-content-md5: 16e38d9753b061731650561ce01b1195
+Text-content-sha1: 36da4b84ea9b64218ab48171dfc5c48ae025f38b
 Content-length: 2465
 
 # -DCOLLISION_CHECK if you believe that SHA1's
@@ -299,9 +328,9 @@ backup: clean
        cd .. ; tar czvf dircache.tar.gz dir-cache
 
 
-Revision-number: 5
-Prop-content-length: 111
-Content-length: 111
+Revision-number: 6
+Prop-content-length: 115
+Content-length: 115
 
 K 7
 svn:log
@@ -309,12 +338,12 @@ V 12
 trunk update
 K 10
 svn:author
-V 4
-samv
+V 8
+tallsopp
 K 8
 svn:date
 V 27
-2009-10-20T01:33:43.159959Z
+2009-11-12T20:29:46.278498Z
 PROPS-END
 
 Node-path: trunk/Makefile
@@ -322,6 +351,7 @@ Node-kind: file
 Node-action: change
 Text-content-length: 2521
 Text-content-md5: 0668418a621333f4aa8b6632cd63e2a0
+Text-content-sha1: 4f29afd038e52f45acb5ef8c41acfc70062a741a
 Content-length: 2521
 
 # -DCOLLISION_CHECK if you believe that SHA1's
@@ -406,9 +436,9 @@ backup: clean
        cd .. ; tar czvf dircache.tar.gz dir-cache
 
 
-Revision-number: 6
-Prop-content-length: 112
-Content-length: 112
+Revision-number: 7
+Prop-content-length: 116
+Content-length: 116
 
 K 7
 svn:log
@@ -416,12 +446,12 @@ V 13
 left update 2
 K 10
 svn:author
-V 4
-samv
+V 8
+tallsopp
 K 8
 svn:date
 V 27
-2009-10-20T01:33:44.164175Z
+2009-11-12T20:29:47.069090Z
 PROPS-END
 
 Node-path: branches/left/Makefile
@@ -429,6 +459,7 @@ Node-kind: file
 Node-action: change
 Text-content-length: 2529
 Text-content-md5: f6b197cc3f2e89a83e545d4bb003de73
+Text-content-sha1: 2f656677cfec0bceec85e53036ffb63e25126f8e
 Content-length: 2529
 
 # -DCOLLISION_CHECK if you believe that SHA1's
@@ -510,9 +541,9 @@ backup: clean
        cd .. ; tar czvf dircache.tar.gz dir-cache
 
 
-Revision-number: 7
-Prop-content-length: 112
-Content-length: 112
+Revision-number: 8
+Prop-content-length: 116
+Content-length: 116
 
 K 7
 svn:log
@@ -520,12 +551,12 @@ V 13
 left update 3
 K 10
 svn:author
-V 4
-samv
+V 8
+tallsopp
 K 8
 svn:date
 V 27
-2009-10-20T01:33:45.144214Z
+2009-11-12T20:29:48.053835Z
 PROPS-END
 
 Node-path: branches/left/Makefile
@@ -533,6 +564,7 @@ Node-kind: file
 Node-action: change
 Text-content-length: 2593
 Text-content-md5: 5ccff689fb290e00b85fe18ee50c54ba
+Text-content-sha1: a13de8e23f1483efca3e57b2b64b0ae6f740ce10
 Content-length: 2593
 
 # -DCOLLISION_CHECK if you believe that SHA1's
@@ -614,22 +646,22 @@ backup: clean
        cd .. ; tar czvf dircache.tar.gz dir-cache
 
 
-Revision-number: 8
-Prop-content-length: 110
-Content-length: 110
+Revision-number: 9
+Prop-content-length: 116
+Content-length: 116
 
 K 7
 svn:log
-V 11
-Merge trunk
+V 13
+Merge trunk 1
 K 10
 svn:author
-V 4
-samv
+V 8
+tallsopp
 K 8
 svn:date
 V 27
-2009-10-20T01:33:48.176135Z
+2009-11-12T20:29:51.098306Z
 PROPS-END
 
 Node-path: trunk
@@ -641,7 +673,7 @@ Content-length: 53
 K 13
 svn:mergeinfo
 V 18
-/branches/left:2-7
+/branches/left:2-8
 PROPS-END
 
 
@@ -650,6 +682,7 @@ Node-kind: file
 Node-action: change
 Text-content-length: 2713
 Text-content-md5: 0afbe34f244cd662b1f97d708c687f90
+Text-content-sha1: 46d9377d783e67a9b581da110352e799517c8a14
 Content-length: 2713
 
 # -DCOLLISION_CHECK if you believe that SHA1's
@@ -734,3 +767,244 @@ backup: clean
        cd .. ; tar czvf dircache.tar.gz dir-cache
 
 
+Revision-number: 10
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 13
+left update 4
+K 10
+svn:author
+V 8
+tallsopp
+K 8
+svn:date
+V 27
+2009-11-12T20:29:52.081644Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2529
+Text-content-md5: f6b197cc3f2e89a83e545d4bb003de73
+Text-content-sha1: 2f656677cfec0bceec85e53036ffb63e25126f8e
+Content-length: 2529
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+       cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+       check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+       install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+       $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+       $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+       $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+       $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+       $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+       $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+       $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+       $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+       $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+       $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+       $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+       $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+       $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+       $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+       rm -f *.o $(PROG)
+
+backup: clean
+       cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 11
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 14
+right update 1
+K 10
+svn:author
+V 8
+tallsopp
+K 8
+svn:date
+V 27
+2009-11-12T20:29:53.059636Z
+PROPS-END
+
+Node-path: branches/right/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2593
+Text-content-md5: 5ccff689fb290e00b85fe18ee50c54ba
+Text-content-sha1: a13de8e23f1483efca3e57b2b64b0ae6f740ce10
+Content-length: 2593
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG=   update-cache show-diff init-db write-tree read-tree commit-tree \
+       cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+       check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+       install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+       $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+       $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+       $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+       $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+       $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+       $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+       $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+       $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+       $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+       $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+       $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+       $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+       $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+       $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+       rm -f *.o $(PROG)
+
+backup: clean
+       cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 12
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 13
+Merge trunk 2
+K 10
+svn:author
+V 8
+tallsopp
+K 8
+svn:date
+V 27
+2009-11-12T20:29:56.083003Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 54
+Content-length: 54
+
+K 13
+svn:mergeinfo
+V 19
+/branches/left:2-11
+PROPS-END
+
+
index 821be7ce8d92f8ead1bcaa946260e8d715784612..b49815d10806a57461bb98ffd89031680f84715a 100755 (executable)
@@ -1088,4 +1088,170 @@ INPUT_END
 test_expect_success 'P: fail on blob mark in gitlink' '
     test_must_fail git fast-import <input'
 
+###
+### series Q (notes)
+###
+
+note1_data="Note for the first commit"
+note2_data="Note for the second commit"
+note3_data="Note for the third commit"
+
+test_tick
+cat >input <<INPUT_END
+blob
+mark :2
+data <<EOF
+$file2_data
+EOF
+
+commit refs/heads/notes-test
+mark :3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+first (:3)
+COMMIT
+
+M 644 :2 file2
+
+blob
+mark :4
+data $file4_len
+$file4_data
+commit refs/heads/notes-test
+mark :5
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+second (:5)
+COMMIT
+
+M 644 :4 file4
+
+commit refs/heads/notes-test
+mark :6
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+third (:6)
+COMMIT
+
+M 644 inline file5
+data <<EOF
+$file5_data
+EOF
+
+M 755 inline file6
+data <<EOF
+$file6_data
+EOF
+
+blob
+mark :7
+data <<EOF
+$note1_data
+EOF
+
+blob
+mark :8
+data <<EOF
+$note2_data
+EOF
+
+commit refs/notes/foobar
+mark :9
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes (:9)
+COMMIT
+
+N :7 :3
+N :8 :5
+N inline :6
+data <<EOF
+$note3_data
+EOF
+
+INPUT_END
+test_expect_success \
+       'Q: commit notes' \
+       'git fast-import <input &&
+        git whatchanged notes-test'
+test_expect_success \
+       'Q: verify pack' \
+       'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+commit1=$(git rev-parse notes-test~2)
+commit2=$(git rev-parse notes-test^)
+commit3=$(git rev-parse notes-test)
+
+cat >expect <<EOF
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+first (:3)
+EOF
+test_expect_success \
+       'Q: verify first commit' \
+       'git cat-file commit notes-test~2 | sed 1d >actual &&
+       test_cmp expect actual'
+
+cat >expect <<EOF
+parent $commit1
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+second (:5)
+EOF
+test_expect_success \
+       'Q: verify second commit' \
+       'git cat-file commit notes-test^ | sed 1d >actual &&
+       test_cmp expect actual'
+
+cat >expect <<EOF
+parent $commit2
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+third (:6)
+EOF
+test_expect_success \
+       'Q: verify third commit' \
+       'git cat-file commit notes-test | sed 1d >actual &&
+       test_cmp expect actual'
+
+cat >expect <<EOF
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+notes (:9)
+EOF
+test_expect_success \
+       'Q: verify notes commit' \
+       'git cat-file commit refs/notes/foobar | sed 1d >actual &&
+       test_cmp expect actual'
+
+cat >expect.unsorted <<EOF
+100644 blob $commit1
+100644 blob $commit2
+100644 blob $commit3
+EOF
+cat expect.unsorted | sort >expect
+test_expect_success \
+       'Q: verify notes tree' \
+       'git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]*   / /" >actual &&
+        test_cmp expect actual'
+
+echo "$note1_data" >expect
+test_expect_success \
+       'Q: verify note for first commit' \
+       'git cat-file blob refs/notes/foobar:$commit1 >actual && test_cmp expect actual'
+
+echo "$note2_data" >expect
+test_expect_success \
+       'Q: verify note for second commit' \
+       'git cat-file blob refs/notes/foobar:$commit2 >actual && test_cmp expect actual'
+
+echo "$note3_data" >expect
+test_expect_success \
+       'Q: verify note for third commit' \
+       'git cat-file blob refs/notes/foobar:$commit3 >actual && test_cmp expect actual'
+
 test_done
index 64f947d75bc0700fc75e8c7c87d97ec4f3e62e44..c2ec3cb4bd97693ef8f8f25064297f019053e03a 100755 (executable)
@@ -20,7 +20,7 @@ then
     say 'skipping git-cvsserver tests, cvs not found'
     test_done
 fi
-perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
+"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
     say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
     test_done
 }
index aca40c1b1ff0f957652f75383625367d85242c14..40637d6782c3ad5ffbff99d352206b8b2ef01b43 100755 (executable)
@@ -57,7 +57,7 @@ then
     say 'skipping git-cvsserver tests, perl not available'
     test_done
 fi
-perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
+"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
     say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
     test_done
 }
index d0ff21d426e294cd2120813e7094b017c6ecd766..0688a57e1d8cd252232095203af48013c2cbfc60 100755 (executable)
@@ -75,4 +75,43 @@ test_expect_success \
 test_debug 'cat gitweb.output'
 
 
+# ----------------------------------------------------------------------
+# snapshot hash ids
+
+test_expect_success 'snapshots: good tree-ish id' '
+       gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+       grep "Status: 200 OK" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+test_expect_success 'snapshots: bad tree-ish id' '
+       gitweb_run "p=.git;a=snapshot;h=frizzumFrazzum;sf=tgz" &&
+       grep "404 - Object does not exist" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+test_expect_success 'snapshots: bad tree-ish id (tagged object)' '
+       echo object > tag-object &&
+       git add tag-object &&
+       git commit -m "Object to be tagged" &&
+       git tag tagged-object `git hash-object tag-object` &&
+       gitweb_run "p=.git;a=snapshot;h=tagged-object;sf=tgz" &&
+       grep "400 - Object is not a tree-ish" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+test_expect_success 'snapshots: good object id' '
+       ID=`git rev-parse --verify HEAD` &&
+       gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" &&
+       grep "Status: 200 OK" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+test_expect_success 'snapshots: bad object id' '
+       gitweb_run "p=.git;a=snapshot;h=abcdef01234;sf=tgz" &&
+       grep "404 - Object does not exist" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+
 test_done
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh
new file mode 100755 (executable)
index 0000000..dd83890
--- /dev/null
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Mark Rada
+#
+
+test_description='gitweb as standalone script (parsing script output).
+
+This test runs gitweb (git web interface) as a CGI script from the
+commandline, and checks that it produces the correct output, either
+in the HTTP header or the actual script output.'
+
+
+. ./gitweb-lib.sh
+
+# ----------------------------------------------------------------------
+# snapshot file name and prefix
+
+cat >>gitweb_config.perl <<\EOF
+
+$known_snapshot_formats{'tar'} = {
+       'display' => 'tar',
+       'type' => 'application/x-tar',
+       'suffix' => '.tar',
+       'format' => 'tar',
+};
+
+$feature{'snapshot'}{'default'} = ['tar'];
+EOF
+
+# Call check_snapshot with the arguments "<basename> [<prefix>]"
+#
+# This will check that gitweb HTTP header contains proposed filename
+# as <basename> with '.tar' suffix added, and that generated tarfile
+# (gitweb message body) has <prefix> as prefix for al files in tarfile
+#
+# <prefix> default to <basename>
+check_snapshot () {
+       basename=$1
+       prefix=${2:-"$1"}
+       echo "basename=$basename"
+       grep "filename=.*$basename.tar" gitweb.headers >/dev/null 2>&1 &&
+       "$TAR" tf gitweb.body >file_list &&
+       ! grep -v "^$prefix/" file_list
+}
+
+test_expect_success setup '
+       test_commit first foo &&
+       git branch xx/test &&
+       FULL_ID=$(git rev-parse --verify HEAD) &&
+       SHORT_ID=$(git rev-parse --verify --short=7 HEAD)
+'
+test_debug '
+       echo "FULL_ID  = $FULL_ID"
+       echo "SHORT_ID = $SHORT_ID"
+'
+
+test_expect_success 'snapshot: full sha1' '
+       gitweb_run "p=.git;a=snapshot;h=$FULL_ID;sf=tar" &&
+       check_snapshot ".git-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: shortened sha1' '
+       gitweb_run "p=.git;a=snapshot;h=$SHORT_ID;sf=tar" &&
+       check_snapshot ".git-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: almost full sha1' '
+       ID=$(git rev-parse --short=30 HEAD) &&
+       gitweb_run "p=.git;a=snapshot;h=$ID;sf=tar" &&
+       check_snapshot ".git-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: HEAD' '
+       gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tar" &&
+       check_snapshot ".git-HEAD-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: short branch name (master)' '
+       gitweb_run "p=.git;a=snapshot;h=master;sf=tar" &&
+       ID=$(git rev-parse --verify --short=7 master) &&
+       check_snapshot ".git-master-$ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: short tag name (first)' '
+       gitweb_run "p=.git;a=snapshot;h=first;sf=tar" &&
+       ID=$(git rev-parse --verify --short=7 first) &&
+       check_snapshot ".git-first-$ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: full branch name (refs/heads/master)' '
+       gitweb_run "p=.git;a=snapshot;h=refs/heads/master;sf=tar" &&
+       ID=$(git rev-parse --verify --short=7 master) &&
+       check_snapshot ".git-master-$ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: full tag name (refs/tags/first)' '
+       gitweb_run "p=.git;a=snapshot;h=refs/tags/first;sf=tar" &&
+       check_snapshot ".git-first"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: hierarchical branch name (xx/test)' '
+       gitweb_run "p=.git;a=snapshot;h=xx/test;sf=tar" &&
+       ! grep "filename=.*/" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_done
index 4eb7d3f7f042f896106f208a7f8bb91925352b2a..8686086dde9dba742bd805c31f3e28c658b03a9b 100755 (executable)
@@ -11,7 +11,7 @@ if ! test_have_prereq PERL; then
        test_done
 fi
 
-perl -MTest::More -e 0 2>/dev/null || {
+"$PERL_PATH" -MTest::More -e 0 2>/dev/null || {
        say "Perl Test::More unavailable, skipping test"
        test_done
 }
@@ -48,6 +48,6 @@ test_expect_success \
 
 test_external_without_stderr \
     'Perl API' \
-    perl "$TEST_DIRECTORY"/t9700/test.pl
+    "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
 
 test_done
index 6c70aec020c9c6486ae984dc264edb1dd8b270cf..666722d9bf1050522a687f4af95792a8e0ec5d64 100755 (executable)
@@ -13,7 +13,7 @@
 BEGIN { use_ok('Git') }
 
 # set up
-our $abs_repo_dir = Cwd->cwd;
+our $abs_repo_dir = cwd();
 ok(our $r = Git->repository(Directory => "."), "open repository");
 
 # config
index f2ca5364722e9c85a23bdfdcf1e24122fd5e3a0f..ec3336aba5a65a468bc6ce71f33a9cca76dbfe0f 100644 (file)
@@ -30,7 +30,7 @@ TZ=UTC
 TERM=dumb
 export LANG LC_ALL PAGER TERM TZ
 EDITOR=:
-VISUAL=:
+unset VISUAL
 unset GIT_EDITOR
 unset AUTHOR_DATE
 unset AUTHOR_EMAIL
@@ -58,7 +58,7 @@ GIT_MERGE_VERBOSITY=5
 export GIT_MERGE_VERBOSITY
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
-export EDITOR VISUAL
+export EDITOR
 GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
 
 # Protect ourselves from common misconfiguration to export
@@ -207,8 +207,8 @@ trap 'die' EXIT
 test_set_editor () {
        FAKE_EDITOR="$1"
        export FAKE_EDITOR
-       VISUAL='"$FAKE_EDITOR"'
-       export VISUAL
+       EDITOR='"$FAKE_EDITOR"'
+       export EDITOR
 }
 
 test_tick () {
index 043970a751f0a85cfa99684be5d610b33b15a358..439eefda510ca8de9f55c63616f2113ac36c8b6b 100755 (executable)
@@ -7,6 +7,14 @@
 #
 # To enable this hook, rename this file to "pre-commit".
 
+if git-rev-parse --verify HEAD >/dev/null 2>&1
+then
+       against=HEAD
+else
+       # Initial commit: diff against an empty tree object
+       against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+fi
+
 # If you want to allow non-ascii filenames set this variable to true.
 allownonascii=$(git config hooks.allownonascii)
 
@@ -17,7 +25,7 @@ if [ "$allownonascii" != "true" ] &&
        # Note that the use of brackets around a tr range is ok here, (it's
        # even required, for portability to Solaris 10's /usr/bin/tr), since
        # the square bracket bytes happen to fall in the designated range.
-       test "$(git diff --cached --name-only --diff-filter=A -z |
+       test "$(git diff --cached --name-only --diff-filter=A -z $against |
          LC_ALL=C tr -d '[ -~]\0')"
 then
        echo "Error: Attempt to add a non-ascii file name."
@@ -35,12 +43,4 @@ then
        exit 1
 fi
 
-if git-rev-parse --verify HEAD >/dev/null 2>&1
-then
-       against=HEAD
-else
-       # Initial commit: diff against an empty tree object
-       against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
-fi
-
 exec git diff-index --check --cached $against --
index f57e84c67641b5a03ba8d9ed5182507bb54dcd15..5078c7100f16e9d7ac08aad05e3797ad5b6626bc 100644 (file)
@@ -1,16 +1,20 @@
 #include "cache.h"
 #include "transport.h"
-
+#include "quote.h"
 #include "run-command.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
+#include "quote.h"
 
 struct helper_data
 {
        const char *name;
        struct child_process *helper;
-       unsigned fetch : 1;
+       FILE *out;
+       unsigned fetch : 1,
+               option : 1,
+               push : 1;
 };
 
 static struct child_process *get_helper(struct transport *transport)
@@ -18,7 +22,6 @@ static struct child_process *get_helper(struct transport *transport)
        struct helper_data *data = transport->data;
        struct strbuf buf = STRBUF_INIT;
        struct child_process *helper;
-       FILE *file;
 
        if (data->helper)
                return data->helper;
@@ -39,15 +42,19 @@ static struct child_process *get_helper(struct transport *transport)
 
        write_str_in_full(helper->in, "capabilities\n");
 
-       file = xfdopen(helper->out, "r");
+       data->out = xfdopen(helper->out, "r");
        while (1) {
-               if (strbuf_getline(&buf, file, '\n') == EOF)
+               if (strbuf_getline(&buf, data->out, '\n') == EOF)
                        exit(128); /* child died, message supplied already */
 
                if (!*buf.buf)
                        break;
                if (!strcmp(buf.buf, "fetch"))
                        data->fetch = 1;
+               if (!strcmp(buf.buf, "option"))
+                       data->option = 1;
+               if (!strcmp(buf.buf, "push"))
+                       data->push = 1;
        }
        return data->helper;
 }
@@ -58,23 +65,104 @@ static int disconnect_helper(struct transport *transport)
        if (data->helper) {
                write_str_in_full(data->helper->in, "\n");
                close(data->helper->in);
+               fclose(data->out);
                finish_command(data->helper);
                free((char *)data->helper->argv[0]);
                free(data->helper->argv);
                free(data->helper);
                data->helper = NULL;
        }
+       free(data);
        return 0;
 }
 
+static const char *unsupported_options[] = {
+       TRANS_OPT_UPLOADPACK,
+       TRANS_OPT_RECEIVEPACK,
+       TRANS_OPT_THIN,
+       TRANS_OPT_KEEP
+       };
+static const char *boolean_options[] = {
+       TRANS_OPT_THIN,
+       TRANS_OPT_KEEP,
+       TRANS_OPT_FOLLOWTAGS
+       };
+
+static int set_helper_option(struct transport *transport,
+                         const char *name, const char *value)
+{
+       struct helper_data *data = transport->data;
+       struct child_process *helper = get_helper(transport);
+       struct strbuf buf = STRBUF_INIT;
+       int i, ret, is_bool = 0;
+
+       if (!data->option)
+               return 1;
+
+       for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
+               if (!strcmp(name, unsupported_options[i]))
+                       return 1;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
+               if (!strcmp(name, boolean_options[i])) {
+                       is_bool = 1;
+                       break;
+               }
+       }
+
+       strbuf_addf(&buf, "option %s ", name);
+       if (is_bool)
+               strbuf_addstr(&buf, value ? "true" : "false");
+       else
+               quote_c_style(value, &buf, NULL, 0);
+       strbuf_addch(&buf, '\n');
+
+       if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
+               die_errno("cannot send option to %s", data->name);
+
+       strbuf_reset(&buf);
+       if (strbuf_getline(&buf, data->out, '\n') == EOF)
+               exit(128); /* child died, message supplied already */
+
+       if (!strcmp(buf.buf, "ok"))
+               ret = 0;
+       else if (!prefixcmp(buf.buf, "error")) {
+               ret = -1;
+       } else if (!strcmp(buf.buf, "unsupported"))
+               ret = 1;
+       else {
+               warning("%s unexpectedly said: '%s'", data->name, buf.buf);
+               ret = 1;
+       }
+       strbuf_release(&buf);
+       return ret;
+}
+
+static void standard_options(struct transport *t)
+{
+       char buf[16];
+       int n;
+       int v = t->verbose;
+       int no_progress = v < 0 || (!t->progress && !isatty(1));
+
+       set_helper_option(t, "progress", !no_progress ? "true" : "false");
+
+       n = snprintf(buf, sizeof(buf), "%d", v + 1);
+       if (n >= sizeof(buf))
+               die("impossibly large verbosity value");
+       set_helper_option(t, "verbosity", buf);
+}
+
 static int fetch_with_fetch(struct transport *transport,
                            int nr_heads, const struct ref **to_fetch)
 {
-       struct child_process *helper = get_helper(transport);
-       FILE *file = xfdopen(helper->out, "r");
+       struct helper_data *data = transport->data;
        int i;
        struct strbuf buf = STRBUF_INIT;
 
+       standard_options(transport);
+
        for (i = 0; i < nr_heads; i++) {
                const struct ref *posn = to_fetch[i];
                if (posn->status & REF_STATUS_UPTODATE)
@@ -82,12 +170,30 @@ static int fetch_with_fetch(struct transport *transport,
 
                strbuf_addf(&buf, "fetch %s %s\n",
                            sha1_to_hex(posn->old_sha1), posn->name);
-               write_in_full(helper->in, buf.buf, buf.len);
-               strbuf_reset(&buf);
+       }
+
+       strbuf_addch(&buf, '\n');
+       if (write_in_full(data->helper->in, buf.buf, buf.len) != buf.len)
+               die_errno("cannot send fetch to %s", data->name);
 
-               if (strbuf_getline(&buf, file, '\n') == EOF)
+       while (1) {
+               strbuf_reset(&buf);
+               if (strbuf_getline(&buf, data->out, '\n') == EOF)
                        exit(128); /* child died, message supplied already */
+
+               if (!prefixcmp(buf.buf, "lock ")) {
+                       const char *name = buf.buf + 5;
+                       if (transport->pack_lockfile)
+                               warning("%s also locked %s", data->name, name);
+                       else
+                               transport->pack_lockfile = xstrdup(name);
+               }
+               else if (!buf.len)
+                       break;
+               else
+                       warning("%s unexpectedly said: '%s'", data->name, buf.buf);
        }
+       strbuf_release(&buf);
        return 0;
 }
 
@@ -111,23 +217,151 @@ static int fetch(struct transport *transport,
        return -1;
 }
 
+static int push_refs(struct transport *transport,
+               struct ref *remote_refs, int flags)
+{
+       int force_all = flags & TRANSPORT_PUSH_FORCE;
+       int mirror = flags & TRANSPORT_PUSH_MIRROR;
+       struct helper_data *data = transport->data;
+       struct strbuf buf = STRBUF_INIT;
+       struct child_process *helper;
+       struct ref *ref;
+
+       if (!remote_refs)
+               return 0;
+
+       helper = get_helper(transport);
+       if (!data->push)
+               return 1;
+
+       for (ref = remote_refs; ref; ref = ref->next) {
+               if (ref->peer_ref)
+                       hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+               else if (!mirror)
+                       continue;
+
+               ref->deletion = is_null_sha1(ref->new_sha1);
+               if (!ref->deletion &&
+                       !hashcmp(ref->old_sha1, ref->new_sha1)) {
+                       ref->status = REF_STATUS_UPTODATE;
+                       continue;
+               }
+
+               if (force_all)
+                       ref->force = 1;
+
+               strbuf_addstr(&buf, "push ");
+               if (!ref->deletion) {
+                       if (ref->force)
+                               strbuf_addch(&buf, '+');
+                       if (ref->peer_ref)
+                               strbuf_addstr(&buf, ref->peer_ref->name);
+                       else
+                               strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1));
+               }
+               strbuf_addch(&buf, ':');
+               strbuf_addstr(&buf, ref->name);
+               strbuf_addch(&buf, '\n');
+       }
+       if (buf.len == 0)
+               return 0;
+
+       transport->verbose = flags & TRANSPORT_PUSH_VERBOSE ? 1 : 0;
+       standard_options(transport);
+
+       if (flags & TRANSPORT_PUSH_DRY_RUN) {
+               if (set_helper_option(transport, "dry-run", "true") != 0)
+                       die("helper %s does not support dry-run", data->name);
+       }
+
+       strbuf_addch(&buf, '\n');
+       if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
+               exit(128);
+
+       ref = remote_refs;
+       while (1) {
+               char *refname, *msg;
+               int status;
+
+               strbuf_reset(&buf);
+               if (strbuf_getline(&buf, data->out, '\n') == EOF)
+                       exit(128); /* child died, message supplied already */
+               if (!buf.len)
+                       break;
+
+               if (!prefixcmp(buf.buf, "ok ")) {
+                       status = REF_STATUS_OK;
+                       refname = buf.buf + 3;
+               } else if (!prefixcmp(buf.buf, "error ")) {
+                       status = REF_STATUS_REMOTE_REJECT;
+                       refname = buf.buf + 6;
+               } else
+                       die("expected ok/error, helper said '%s'\n", buf.buf);
+
+               msg = strchr(refname, ' ');
+               if (msg) {
+                       struct strbuf msg_buf = STRBUF_INIT;
+                       const char *end;
+
+                       *msg++ = '\0';
+                       if (!unquote_c_style(&msg_buf, msg, &end))
+                               msg = strbuf_detach(&msg_buf, NULL);
+                       else
+                               msg = xstrdup(msg);
+                       strbuf_release(&msg_buf);
+
+                       if (!strcmp(msg, "no match")) {
+                               status = REF_STATUS_NONE;
+                               free(msg);
+                               msg = NULL;
+                       }
+                       else if (!strcmp(msg, "up to date")) {
+                               status = REF_STATUS_UPTODATE;
+                               free(msg);
+                               msg = NULL;
+                       }
+                       else if (!strcmp(msg, "non-fast forward")) {
+                               status = REF_STATUS_REJECT_NONFASTFORWARD;
+                               free(msg);
+                               msg = NULL;
+                       }
+               }
+
+               if (ref)
+                       ref = find_ref_by_name(ref, refname);
+               if (!ref)
+                       ref = find_ref_by_name(remote_refs, refname);
+               if (!ref) {
+                       warning("helper reported unexpected status of %s", refname);
+                       continue;
+               }
+
+               ref->status = status;
+               ref->remote_status = msg;
+       }
+       strbuf_release(&buf);
+       return 0;
+}
+
 static struct ref *get_refs_list(struct transport *transport, int for_push)
 {
+       struct helper_data *data = transport->data;
        struct child_process *helper;
        struct ref *ret = NULL;
        struct ref **tail = &ret;
        struct ref *posn;
        struct strbuf buf = STRBUF_INIT;
-       FILE *file;
 
        helper = get_helper(transport);
 
-       write_str_in_full(helper->in, "list\n");
+       if (data->push && for_push)
+               write_str_in_full(helper->in, "list for-push\n");
+       else
+               write_str_in_full(helper->in, "list\n");
 
-       file = xfdopen(helper->out, "r");
        while (1) {
                char *eov, *eon;
-               if (strbuf_getline(&buf, file, '\n') == EOF)
+               if (strbuf_getline(&buf, data->out, '\n') == EOF)
                        exit(128); /* child died, message supplied already */
 
                if (!*buf.buf)
@@ -161,8 +395,10 @@ int transport_helper_init(struct transport *transport, const char *name)
        data->name = name;
 
        transport->data = data;
+       transport->set_option = set_helper_option;
        transport->get_refs_list = get_refs_list;
        transport->fetch = fetch;
+       transport->push_refs = push_refs;
        transport->disconnect = disconnect_helper;
        return 0;
 }
index 298dc46ec5ede0b972ec9be46556ffbec17df253..7362ec09b2cbc6752489286a8280c16d3519f163 100644 (file)
@@ -349,35 +349,6 @@ static int rsync_transport_push(struct transport *transport,
        return result;
 }
 
-#ifndef NO_CURL
-static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
-{
-       const char **argv;
-       int argc;
-
-       if (flags & TRANSPORT_PUSH_MIRROR)
-               return error("http transport does not support mirror mode");
-
-       argv = xmalloc((refspec_nr + 12) * sizeof(char *));
-       argv[0] = "http-push";
-       argc = 1;
-       if (flags & TRANSPORT_PUSH_ALL)
-               argv[argc++] = "--all";
-       if (flags & TRANSPORT_PUSH_FORCE)
-               argv[argc++] = "--force";
-       if (flags & TRANSPORT_PUSH_DRY_RUN)
-               argv[argc++] = "--dry-run";
-       if (flags & TRANSPORT_PUSH_VERBOSE)
-               argv[argc++] = "--verbose";
-       argv[argc++] = transport->url;
-       while (refspec_nr--)
-               argv[argc++] = *refspec++;
-       argv[argc] = NULL;
-       return !!run_command_v_opt(argv, RUN_GIT_CMD);
-}
-
-#endif
-
 struct bundle_transport_data {
        int fd;
        struct bundle_header header;
@@ -668,7 +639,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
                break;
        case REF_STATUS_REJECT_NONFASTFORWARD:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                                                "non-fast forward", porcelain);
+                                                "non-fast-forward", porcelain);
                break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
@@ -760,6 +731,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
                                 NULL);
        }
 
+       memset(&args, 0, sizeof(args));
        args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
        args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
        args.use_thin_pack = data->thin;
@@ -829,8 +801,6 @@ struct transport *transport_get(struct remote *remote, const char *url)
                transport_helper_init(ret, "curl");
 #ifdef NO_CURL
                error("git was compiled without libcurl support.");
-#else
-               ret->push = curl_transport_push;
 #endif
 
        } else if (is_local(url) && is_file(url)) {
index c14da6f1e53ece9fa9ac2e68b73674c54329be5f..e4e6177e2632b4a764703c11ef0a8033eafea037 100644 (file)
@@ -25,7 +25,7 @@ struct transport {
 
        int (*disconnect)(struct transport *connection);
        char *pack_lockfile;
-       signed verbose : 2;
+       signed verbose : 3;
        /* Force progress even if the output is not a tty */
        unsigned progress : 1;
 };
index ac9cbf7cd8ed1367151de0e8b96668f498b7f1a1..e9d8934691822ad81a4c32481d82790e9d7ca5b0 100644 (file)
@@ -28,7 +28,7 @@ int main(int argc, char **argv)
 
        git_extract_argv0_path(argv[0]);
 
-       if (argc != 2)
+       if (argc != 2 || !strcmp(argv[1], "-h"))
                usage("git unpack-file <sha1>");
        if (get_sha1(argv[1], sha1))
                die("Not a valid object name %s", argv[1]);
index 720f7a161651126872b7357173c10d69d1db7636..dd5999c3562219b7993420b22257f4088ab82b8d 100644 (file)
@@ -277,6 +277,17 @@ static int unpack_nondirectories(int n, unsigned long mask,
        return 0;
 }
 
+static int unpack_failed(struct unpack_trees_options *o, const char *message)
+{
+       discard_index(&o->result);
+       if (!o->gently) {
+               if (message)
+                       return error("%s", message);
+               return -1;
+       }
+       return -1;
+}
+
 static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
 {
        struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
@@ -294,7 +305,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
                        int cmp = compare_entry(ce, info, p);
                        if (cmp < 0) {
                                if (unpack_index_entry(ce, o) < 0)
-                                       return -1;
+                                       return unpack_failed(o, NULL);
                                continue;
                        }
                        if (!cmp) {
@@ -352,17 +363,6 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
        return mask;
 }
 
-static int unpack_failed(struct unpack_trees_options *o, const char *message)
-{
-       discard_index(&o->result);
-       if (!o->gently) {
-               if (message)
-                       return error("%s", message);
-               return -1;
-       }
-       return -1;
-}
-
 /*
  * N-way merge "len" trees.  Returns 0 on success, -1 on failure to manipulate the
  * resulting index, -2 on failure to reflect the changes to the work tree.
@@ -617,7 +617,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,
                         * found "foo/." in the working tree.
                         * This is tricky -- if we have modified
                         * files that are in "foo/" we would lose
-                        * it.
+                        * them.
                         */
                        ret = verify_clean_subdirectory(ce, action, o);
                        if (ret < 0)
@@ -895,7 +895,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
  * Two-way merge.
  *
  * The rule is to "carry forward" what is in the index without losing
- * information across a "fast forward", favoring a successful merge
+ * information across a "fast-forward", favoring a successful merge
  * over a merge failure when it makes sense.  For details of the
  * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
  *
index 38ddac2e8607f4cc6a801eeb49ba755dd89ee81c..6bfb500eb4116bc74d6088b5b0621e89453fab3d 100644 (file)
@@ -39,6 +39,8 @@ static unsigned int timeout;
  */
 static int use_sideband;
 static int debug_fd;
+static int advertise_refs;
+static int stateless_rpc;
 
 static void reset_timeout(void)
 {
@@ -308,6 +310,23 @@ static void create_pack_file(void)
                        }
                        continue;
                }
+               if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
+                       /* Status ready; we ship that in the side-band
+                        * or dump to the standard error.
+                        */
+                       sz = xread(pack_objects.err, progress,
+                                 sizeof(progress));
+                       if (0 < sz)
+                               send_client_data(2, progress, sz);
+                       else if (sz == 0) {
+                               close(pack_objects.err);
+                               pack_objects.err = -1;
+                       }
+                       else
+                               goto fail;
+                       /* give priority to status messages */
+                       continue;
+               }
                if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
                        /* Data ready; we keep the last byte to ourselves
                         * in case we detect broken rev-list, so that we
@@ -345,21 +364,6 @@ static void create_pack_file(void)
                        if (sz < 0)
                                goto fail;
                }
-               if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
-                       /* Status ready; we ship that in the side-band
-                        * or dump to the standard error.
-                        */
-                       sz = xread(pack_objects.err, progress,
-                                 sizeof(progress));
-                       if (0 < sz)
-                               send_client_data(2, progress, sz);
-                       else if (sz == 0) {
-                               close(pack_objects.err);
-                               pack_objects.err = -1;
-                       }
-                       else
-                               goto fail;
-               }
        }
 
        if (finish_command(&pack_objects)) {
@@ -498,7 +502,7 @@ static int get_common_commits(void)
 {
        static char line[1000];
        unsigned char sha1[20];
-       char hex[41], last_hex[41];
+       char last_hex[41];
 
        save_commit_buffer = 0;
 
@@ -509,25 +513,30 @@ static int get_common_commits(void)
                if (!len) {
                        if (have_obj.nr == 0 || multi_ack)
                                packet_write(1, "NAK\n");
+                       if (stateless_rpc)
+                               exit(0);
                        continue;
                }
                strip(line, len);
                if (!prefixcmp(line, "have ")) {
                        switch (got_sha1(line+5, sha1)) {
                        case -1: /* they have what we do not */
-                               if (multi_ack && ok_to_give_up())
-                                       packet_write(1, "ACK %s continue\n",
-                                                    sha1_to_hex(sha1));
+                               if (multi_ack && ok_to_give_up()) {
+                                       const char *hex = sha1_to_hex(sha1);
+                                       if (multi_ack == 2)
+                                               packet_write(1, "ACK %s ready\n", hex);
+                                       else
+                                               packet_write(1, "ACK %s continue\n", hex);
+                               }
                                break;
                        default:
-                               memcpy(hex, sha1_to_hex(sha1), 41);
-                               if (multi_ack) {
-                                       const char *msg = "ACK %s continue\n";
-                                       packet_write(1, msg, hex);
-                                       memcpy(last_hex, hex, 41);
-                               }
+                               memcpy(last_hex, sha1_to_hex(sha1), 41);
+                               if (multi_ack == 2)
+                                       packet_write(1, "ACK %s common\n", last_hex);
+                               else if (multi_ack)
+                                       packet_write(1, "ACK %s continue\n", last_hex);
                                else if (have_obj.nr == 1)
-                                       packet_write(1, "ACK %s\n", hex);
+                                       packet_write(1, "ACK %s\n", last_hex);
                                break;
                        }
                        continue;
@@ -587,7 +596,9 @@ static void receive_needs(void)
                    get_sha1_hex(line+5, sha1_buf))
                        die("git upload-pack: protocol error, "
                            "expected to get sha, not '%s'", line);
-               if (strstr(line+45, "multi_ack"))
+               if (strstr(line+45, "multi_ack_detailed"))
+                       multi_ack = 2;
+               else if (strstr(line+45, "multi_ack"))
                        multi_ack = 1;
                if (strstr(line+45, "thin-pack"))
                        use_thin_pack = 1;
@@ -681,7 +692,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
 {
        static const char *capabilities = "multi_ack thin-pack side-band"
                " side-band-64k ofs-delta shallow no-progress"
-               " include-tag";
+               " include-tag multi_ack_detailed";
        struct object *o = parse_object(sha1);
 
        if (!o)
@@ -705,12 +716,32 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
        return 0;
 }
 
+static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+       struct object *o = parse_object(sha1);
+       if (!o)
+               die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
+       if (!(o->flags & OUR_REF)) {
+               o->flags |= OUR_REF;
+               nr_our_refs++;
+       }
+       return 0;
+}
+
 static void upload_pack(void)
 {
-       reset_timeout();
-       head_ref(send_ref, NULL);
-       for_each_ref(send_ref, NULL);
-       packet_flush(1);
+       if (advertise_refs || !stateless_rpc) {
+               reset_timeout();
+               head_ref(send_ref, NULL);
+               for_each_ref(send_ref, NULL);
+               packet_flush(1);
+       } else {
+               head_ref(mark_our_ref, NULL);
+               for_each_ref(mark_our_ref, NULL);
+       }
+       if (advertise_refs)
+               return;
+
        receive_needs();
        if (want_obj.nr) {
                get_common_commits();
@@ -732,6 +763,14 @@ int main(int argc, char **argv)
 
                if (arg[0] != '-')
                        break;
+               if (!strcmp(arg, "--advertise-refs")) {
+                       advertise_refs = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--stateless-rpc")) {
+                       stateless_rpc = 1;
+                       continue;
+               }
                if (!strcmp(arg, "--strict")) {
                        strict = 1;
                        continue;
diff --git a/usage.c b/usage.c
index c488f3afcd6d612e786c13c86241b2a01eb97a2a..79856a2b9f5bc4603252cb10b471a0815416a617 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -7,14 +7,14 @@
 
 static void report(const char *prefix, const char *err, va_list params)
 {
-       char msg[1024];
+       char msg[4096];
        vsnprintf(msg, sizeof(msg), err, params);
        fprintf(stderr, "%s%s\n", prefix, msg);
 }
 
-static NORETURN void usage_builtin(const char *err)
+static NORETURN void usage_builtin(const char *err, va_list params)
 {
-       fprintf(stderr, "usage: %s\n", err);
+       report("usage: ", err, params);
        exit(129);
 }
 
@@ -36,7 +36,7 @@ static void warn_builtin(const char *warn, va_list params)
 
 /* If we are in a dlopen()ed .so write to a global variable would segfault
  * (ugh), so keep things static. */
-static NORETURN_PTR void (*usage_routine)(const char *err) = usage_builtin;
+static NORETURN_PTR void (*usage_routine)(const char *err, va_list params) = usage_builtin;
 static NORETURN_PTR void (*die_routine)(const char *err, va_list params) = die_builtin;
 static void (*error_routine)(const char *err, va_list params) = error_builtin;
 static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
@@ -46,9 +46,18 @@ void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list param
        die_routine = routine;
 }
 
+void usagef(const char *err, ...)
+{
+       va_list params;
+
+       va_start(params, err);
+       usage_routine(err, params);
+       va_end(params);
+}
+
 void usage(const char *err)
 {
-       usage_routine(err);
+       usagef("%s", err);
 }
 
 void die(const char *err, ...)
diff --git a/utf8.c b/utf8.c
index db706ac4ed6f033091cfaeb7b23c07ca7fd71675..7ddff23fa77fbadf7723bca03d24ad5b8f2baca2 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -1,4 +1,5 @@
 #include "git-compat-util.h"
+#include "strbuf.h"
 #include "utf8.h"
 
 /* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
@@ -279,14 +280,52 @@ int is_utf8(const char *text)
        return 1;
 }
 
-static void print_spaces(int count)
+static inline void strbuf_write(struct strbuf *sb, const char *buf, int len)
+{
+       if (sb)
+               strbuf_insert(sb, sb->len, buf, len);
+       else
+               fwrite(buf, len, 1, stdout);
+}
+
+static void print_spaces(struct strbuf *buf, int count)
 {
        static const char s[] = "                    ";
        while (count >= sizeof(s)) {
-               fwrite(s, sizeof(s) - 1, 1, stdout);
+               strbuf_write(buf, s, sizeof(s) - 1);
                count -= sizeof(s) - 1;
        }
-       fwrite(s, count, 1, stdout);
+       strbuf_write(buf, s, count);
+}
+
+static void strbuf_add_indented_text(struct strbuf *buf, const char *text,
+                                    int indent, int indent2)
+{
+       if (indent < 0)
+               indent = 0;
+       while (*text) {
+               const char *eol = strchrnul(text, '\n');
+               if (*eol == '\n')
+                       eol++;
+               print_spaces(buf, indent);
+               strbuf_write(buf, text, eol - text);
+               text = eol;
+               indent = indent2;
+       }
+}
+
+static size_t display_mode_esc_sequence_len(const char *s)
+{
+       const char *p = s;
+       if (*p++ != '\033')
+               return 0;
+       if (*p++ != '[')
+               return 0;
+       while (isdigit(*p) || *p == ';')
+               p++;
+       if (*p++ != 'm')
+               return 0;
+       return p - s;
 }
 
 /*
@@ -295,36 +334,62 @@ static void print_spaces(int count)
  * If indent is negative, assume that already -indent columns have been
  * consumed (and no extra indent is necessary for the first line).
  */
-int print_wrapped_text(const char *text, int indent, int indent2, int width)
+int strbuf_add_wrapped_text(struct strbuf *buf,
+               const char *text, int indent, int indent2, int width)
 {
        int w = indent, assume_utf8 = is_utf8(text);
        const char *bol = text, *space = NULL;
 
+       if (width <= 0) {
+               strbuf_add_indented_text(buf, text, indent, indent2);
+               return 1;
+       }
+
        if (indent < 0) {
                w = -indent;
                space = text;
        }
 
        for (;;) {
-               char c = *text;
+               char c;
+               size_t skip;
+
+               while ((skip = display_mode_esc_sequence_len(text)))
+                       text += skip;
+
+               c = *text;
                if (!c || isspace(c)) {
                        if (w < width || !space) {
                                const char *start = bol;
+                               if (!c && text == start)
+                                       return w;
                                if (space)
                                        start = space;
                                else
-                                       print_spaces(indent);
-                               fwrite(start, text - start, 1, stdout);
+                                       print_spaces(buf, indent);
+                               strbuf_write(buf, start, text - start);
                                if (!c)
                                        return w;
-                               else if (c == '\t')
-                                       w |= 0x07;
                                space = text;
+                               if (c == '\t')
+                                       w |= 0x07;
+                               else if (c == '\n') {
+                                       space++;
+                                       if (*space == '\n') {
+                                               strbuf_write(buf, "\n", 1);
+                                               goto new_line;
+                                       }
+                                       else if (!isalnum(*space))
+                                               goto new_line;
+                                       else
+                                               strbuf_write(buf, " ", 1);
+                               }
                                w++;
                                text++;
                        }
                        else {
-                               putchar('\n');
+new_line:
+                               strbuf_write(buf, "\n", 1);
                                text = bol = space + isspace(*space);
                                space = NULL;
                                w = indent = indent2;
@@ -340,6 +405,11 @@ int print_wrapped_text(const char *text, int indent, int indent2, int width)
        }
 }
 
+int print_wrapped_text(const char *text, int indent, int indent2, int width)
+{
+       return strbuf_add_wrapped_text(NULL, text, indent, indent2, width);
+}
+
 int is_encoding_utf8(const char *name)
 {
        if (!name)
diff --git a/utf8.h b/utf8.h
index 2f1b14ff49ef3c73bee6f298ba396b96120c34b7..ae30ae4c6e501e4766db93c94253fd404cd29357 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -10,6 +10,8 @@ int is_utf8(const char *text);
 int is_encoding_utf8(const char *name);
 
 int print_wrapped_text(const char *text, int indent, int indent2, int len);
+int strbuf_add_wrapped_text(struct strbuf *buf,
+               const char *text, int indent, int indent2, int width);
 
 #ifndef NO_ICONV
 char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);
diff --git a/var.c b/var.c
index 125c0d1676abae6de29de14061d1a24fd842712d..d9892f85ce954883427ac4f61d9de9d591c0827c 100644 (file)
--- a/var.c
+++ b/var.c
@@ -8,6 +8,25 @@
 
 static const char var_usage[] = "git var [-l | <variable>]";
 
+static const char *editor(int flag)
+{
+       const char *pgm = git_editor();
+
+       if (!pgm && flag & IDENT_ERROR_ON_NO_NAME)
+               die("Terminal is dumb, but EDITOR unset");
+
+       return pgm;
+}
+
+static const char *pager(int flag)
+{
+       const char *pgm = git_pager();
+
+       if (!pgm)
+               pgm = "cat";
+       return pgm;
+}
+
 struct git_var {
        const char *name;
        const char *(*read)(int);
@@ -15,14 +34,19 @@ struct git_var {
 static struct git_var git_vars[] = {
        { "GIT_COMMITTER_IDENT", git_committer_info },
        { "GIT_AUTHOR_IDENT",   git_author_info },
+       { "GIT_EDITOR", editor },
+       { "GIT_PAGER", pager },
        { "", NULL },
 };
 
 static void list_vars(void)
 {
        struct git_var *ptr;
+       const char *val;
+
        for (ptr = git_vars; ptr->read; ptr++)
-               printf("%s=%s\n", ptr->name, ptr->read(IDENT_WARN_ON_NO_NAME));
+               if ((val = ptr->read(0)))
+                       printf("%s=%s\n", ptr->name, val);
 }
 
 static const char *read_var(const char *var)