Merge branch 'js/am-3-merge-recursive-direct'
authorJunio C Hamano <gitster@pobox.com>
Wed, 10 Aug 2016 19:33:20 +0000 (12:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 10 Aug 2016 19:33:20 +0000 (12:33 -0700)
"git am -3" calls "git merge-recursive" when it needs to fall back
to a three-way merge; this call has been turned into an internal
subroutine call instead of spawning a separate subprocess.

* js/am-3-merge-recursive-direct:
merge-recursive: flush output buffer even when erroring out
merge_trees(): ensure that the callers release output buffer
merge-recursive: offer an option to retain the output in 'obuf'
merge-recursive: write the commit title in one go
merge-recursive: flush output buffer before printing error messages
am -3: use merge_recursive() directly again
merge-recursive: switch to returning errors instead of dying
merge-recursive: handle return values indicating errors
merge-recursive: allow write_tree_from_memory() to error out
merge-recursive: avoid returning a wholesale struct
merge_recursive: abort properly upon errors
prepare the builtins for a libified merge_recursive()
merge-recursive: clarify code in was_tracked()
die(_("BUG")): avoid translating bug messages
die("bug"): report bugs consistently
t5520: verify that `pull --rebase` shows the helpful advice when failing

128 files changed:
.mailmap
Documentation/RelNotes/2.10.0.txt
Documentation/RelNotes/2.9.3.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-push.txt
Documentation/git-svn.txt
Documentation/git-worktree.txt
Documentation/gitattributes.txt
Documentation/githooks.txt
Documentation/gitmodules.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/technical/api-hashmap.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-capabilities.txt
Makefile
builtin/am.c
builtin/blame.c
builtin/cat-file.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/fsck.c
builtin/index-pack.c
builtin/log.c
builtin/notes.c
builtin/pack-objects.c
builtin/push.c
builtin/receive-pack.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/submodule--helper.c
builtin/worktree.c
cache.h
commit-slab.h
compat/mingw.h
compat/nedmalloc/nedmalloc.c
config.c
config.mak.uname
connected.c
connected.h
contrib/completion/git-completion.bash
contrib/git-jump/README
contrib/git-jump/git-jump
contrib/persistent-https/Makefile
contrib/subtree/git-subtree.sh
contrib/subtree/t/t7900-subtree.sh
daemon.c
date.c
diff.c
diffcore-rename.c
fetch-pack.c
git-difftool.perl
git-rebase--interactive.sh
git-sh-setup.sh
git-submodule.sh
git-svn.perl
gitweb/gitweb.perl
grep.c
grep.h
http-push.c
http-walker.c
http.c
ident.c
list.h [new file with mode: 0644]
log-tree.c
mru.c [new file with mode: 0644]
mru.h [new file with mode: 0644]
notes-merge.c
pack-check.c
pack-write.c
pack.h
pager.c
parse-options-cb.c
parse-options.h
refs.c
remote.c
remote.h
revision.c
send-pack.c
send-pack.h
sequencer.c
sha1_file.c
shallow.c
submodule-config.c
t/helper/test-run-command.c
t/helper/test-submodule-config.c
t/lib-git-svn.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/perf/p5303-many-packs.sh [new file with mode: 0755]
t/t0006-date.sh
t/t0021-conversion.sh
t/t1050-large.sh
t/t2028-worktree-move.sh [new file with mode: 0755]
t/t3404-rebase-interactive.sh
t/t3415-rebase-autosquash.sh
t/t3700-add.sh
t/t4014-format-patch.sh
t/t4130-apply-criss-cross-rename.sh
t/t4202-log.sh
t/t4205-log-pretty-formats.sh
t/t4207-log-decoration-colors.sh
t/t5510-fetch.sh
t/t5533-push-cas.sh
t/t5545-push-options.sh [new file with mode: 0755]
t/t7006-pager.sh
t/t7060-wtstatus.sh
t/t7063-status-untracked-cache.sh
t/t7411-submodule-config.sh
t/t7512-status-help.sh
t/t7800-difftool.sh
t/t9100-git-svn-basic.sh
t/t9115-git-svn-dcommit-funky-renames.sh
t/t9118-git-svn-funky-branch-names.sh
t/t9120-git-svn-clone-with-percent-escapes.sh
t/t9142-git-svn-shallow-clone.sh
t/t9158-git-svn-mergeinfo.sh
t/t9160-git-svn-preserve-empty-dirs.sh
templates/hooks--pre-receive.sample [new file with mode: 0644]
transport.c
transport.h
worktree.c
worktree.h
wt-status.c
index a714e69a11f59576a24487d05f8422f792edd9f0..9441a54b0d5b89ea2c795f8f6ad8a99dfbe8b432 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -33,6 +33,7 @@ Cheng Renquan <crquan@gmail.com>
 Chris Shoemaker <c.shoemaker@cox.net>
 Chris Wright <chrisw@sous-sol.org> <chrisw@osdl.org>
 Cord Seele <cowose@gmail.com> <cowose@googlemail.com>
+Christian Couder <chriscool@tuxfamily.org> <christian.couder@gmail.com>
 Christian Stimming <stimming@tuhh.de> <chs@ckiste.goetheallee>
 Csaba Henk <csaba@gluster.com> <csaba@lowlife.hu>
 Dan Johnson <computerdruid@gmail.com>
index f9dfc27b333bc2d94cadf4652f1466ad27a4ccba..8abb67881e8215343880e89f345515db84eeed36 100644 (file)
@@ -34,7 +34,7 @@ UI, Workflows & Features
 
  * "upload-pack" allows a custom "git pack-objects" replacement when
    responding to "fetch/clone" via the uploadpack.packObjectsHook.
-   (merge 20b20a2 jk/upload-pack-hook later to maint).
+   (merge b738396 jk/upload-pack-hook later to maint).
 
  * Teach format-patch and mailsplit (hence "am") how a line that
    happens to begin with "From " in the e-mail message is quoted with
@@ -84,6 +84,32 @@ UI, Workflows & Features
  * A new configuration variable core.sshCommand has been added to
    specify what value for GIT_SSH_COMMAND to use per repository.
 
+ * "git worktree prune" protected worktrees that are marked as
+   "locked" by creating a file in a known location.  "git worktree"
+   command learned a dedicated command pair to create and remove such
+   a file, so that the users do not have to do this with editor.
+
+ * A handful of "git svn" updates.
+
+ * "git push" learned to accept and pass extra options to the
+   receiving end so that hooks can read and react to them.
+
+ * "git status" learned to suggest "merge --abort" during a conflicted
+   merge, just like it already suggests "rebase --abort" during a
+   conflicted rebase.
+   (merge b0a61ab mm/status-suggest-merge-abort later to maint).
+
+ * "git jump" script (in contrib/) has been updated a bit.
+   (merge a91e692 jk/git-jump later to maint).
+
+ * "git push" and "git clone" learned to give better progress meters
+   to the end user who is waiting on the terminal.
+
+ * An entry "git log --decorate" for the tip of the current branch is
+   shown as "HEAD -> name" (where "name" is the name of the branch);
+   paint the arrow in the same color as "HEAD", not in the color for
+   commits.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -156,7 +182,6 @@ Performance, Internal Implementation, Development Support etc.
 
  * Improve the look of the way "git fetch" reports what happened to
    each ref that was fetched.
-   (merge bc437d1 nd/fetch-ref-summary later to maint).
 
  * The .c/.h sources are marked as such in our .gitattributes file so
    that "git diff -W" and friends would work better.
@@ -205,6 +230,48 @@ Performance, Internal Implementation, Development Support etc.
  * The API to iterate over all the refs (i.e. for_each_ref(), etc.)
    has been revamped.
 
+ * A few tests that specifically target "git rebase -i" have been
+   added.
+
+ * Dumb http transport on the client side has been optimized.
+   (merge ecba195 ew/http-walker later to maint).
+
+ * Users of the parse_options_concat() API function need to allocate
+   extra slots in advance and fill them with OPT_END() when they want
+   to decide the set of supported options dynamically, which makes the
+   code error-prone and hard to read.  This has been corrected by tweaking
+   the API to allocate and return a new copy of "struct option" array.
+   (merge 023ff39 jk/parse-options-concat later to maint).
+
+ * "git fetch" exchanges batched have/ack messages between the sender
+   and the receiver, initially doubling every time and then falling
+   back to enlarge the window size linearly.  The "smart http"
+   transport, being an half-duplex protocol, outgrows the preset limit
+   too quickly and becomes inefficient when interacting with a large
+   repository.  The internal mechanism learned to grow the window size
+   more aggressively when working with the "smart http" transport.
+
+ * Tests for "git svn" have been taught to reuse the lib-httpd test
+   infrastructure when testing the subversion integration that
+   interacts with subversion repositories served over the http://
+   protocol.
+   (merge a8a5d25 ew/git-svn-http-tests later to maint).
+
+ * "git pack-objects" has a few options that tell it not to pack
+   objects found in certain packfiles, which require it to scan .idx
+   files of all available packs.  The codepaths involved in these
+   operations have been optimized for a common case of not having any
+   non-local pack and/or any .kept pack.
+
+ * The t3700 test about "add --chmod=-x" have been made a bit more
+   robust and generally cleaned up.
+   (merge 766cdc4 ib/t3700-add-chmod-x-updates later to maint).
+
+ * The build procedure learned PAGER_ENV knob that lists what default
+   environment variable settings to export for popular pagers.  This
+   mechanism is used to tweak the default settings to MORE on FreeBSD.
+   (merge 995bc22 ew/build-time-pager-tweaks later to maint).
+
 
 Also contains various documentation updates and code clean-ups.
 
@@ -285,7 +352,6 @@ notes for details).
    bogus offset value to the caller.  Use a more benign looking
    +0000 instead and let "git log" going in such a case, instead
    of aborting.
-   (merge bab7483 jk/tzoffset-fix later to maint).
 
  * One among four invocations of readlink(1) in our test suite has
    been rewritten so that the test can run on systems without the
@@ -308,58 +374,46 @@ notes for details).
    finds its subject line did not ignore leading blank lines, as is
    commonly done by other codepaths.  Make it ignore leading blank
    lines to match.
-   (merge 054a5ae js/find-commit-subject-ignore-leading-blanks later to maint).
 
  * For a long time, we carried an in-code comment that said our
    colored output would work only when we use fprintf/fputs on
    Windows, which no longer is the case for the past few years.
-   (merge 3d0a833 js/color-on-windows-comment later to maint).
 
  * "gc.autoPackLimit" when set to 1 should not trigger a repacking
    when there is only one pack, but the code counted poorly and did
    so.
-   (merge 5f4e3bf ew/gc-auto-pack-limit-fix later to maint).
 
  * Add a test to specify the desired behaviour that currently is not
    available in "git rebase -Xsubtree=...".
-   (merge 5f35900 dg/subtree-rebase-test later to maint).
 
  * More mark-up updates to typeset strings that are expected to
    literally typed by the end user in fixed-width font.
-   (merge 661c3e9 mm/doc-tt later to maint).
 
  * "git commit --amend --allow-empty-message -S" for a commit without
    any message body could have misidentified where the header of the
    commit object ends.
-   (merge 3324dd8 js/sign-empty-commit-fix later to maint).
 
  * "git rebase -i --autostash" did not restore the auto-stashed change
    when the operation was aborted.
-   (merge 33ba9c6 ps/rebase-i-auto-unstash-upon-abort later to maint).
 
  * Git does not know what the contents in the index should be for a
    path added with "git add -N" yet, so "git grep --cached" should not
    show hits (or show lack of hits, with -L) in such a path, but that
    logic does not apply to "git grep", i.e. searching in the working
    tree files.  But we did so by mistake, which has been corrected.
-   (merge b8e47d1 nd/ita-cleanup later to maint).
 
  * "git blame -M" missed a single line that was moved within the file.
-   (merge 17a07e2 dk/blame-move-no-reason-for-1-line-context later to maint).
 
  * Fix recently introduced codepaths that are involved in parallel
    submodule operations, which gave up on reading too early, and
    could have wasted CPU while attempting to write under a corner
    case condition.
-   (merge d751dd1 sb/submodule-parallel-fetch later to maint).
 
  * "git grep -i" has been taught to fold case in non-ascii locales
    correctly.
-   (merge 695f95b nd/icase later to maint).
 
  * A test that unconditionally used "mktemp" learned that the command
    is not necessarily available everywhere.
-   (merge c578a09 ak/lazy-prereq-mktemp later to maint).
 
  * There are certain house-keeping tasks that need to be performed at
    the very beginning of any Git program, and programs that are not
@@ -372,7 +426,6 @@ notes for details).
 
  * The test framework learned a new helper test_match_signal to
    check an exit code from getting killed by an expected signal.
-   (merge 03c39b3 jk/test-match-signal later to maint).
 
  * General code clean-up around a helper function to write a
    single-liner to a file.
@@ -383,23 +436,19 @@ notes for details).
    which was not gender-neutral and also inconsistent with the rest of
    the system where outside stuff is usuall called "theirs" in
    contrast to "ours".
-   (merge 715a51b js/am-call-theirs-theirs-in-fallback-3way later to maint).
 
  * "git blame file" allowed the lineage of lines in the uncommitted,
    unadded contents of "file" to be inspected, but it refused when
    "file" did not appear in the current commit.  When "file" was
    created by renaming an existing file (but the change has not been
    committed), this restriction was unnecessarily tight.
-   (merge c66b470 mh/blame-worktree later to maint).
 
  * "git add -N dir/file && git write-tree" produced an incorrect tree
    when there are other paths in the same directory that sorts after
    "file".
-   (merge 6d6a782 nd/cache-tree-ita later to maint).
 
  * "git fetch http://user:pass@host/repo..." scrubbed the userinfo
    part, but "git push" didn't.
-   (merge 68f3c07 jk/push-scrub-url later to maint).
 
  * "git merge" with renormalization did not work well with
    merge-recursive, due to "safer crlf" conversion kicking in when it
@@ -412,17 +461,95 @@ notes for details).
 
  * An age old bug that caused "git diff --ignore-space-at-eol"
    misbehave has been fixed.
-   (merge 044fb19 js/ignore-space-at-eol later to maint).
+
+ * "git notes merge" had a code to see if a path exists (and fails if
+   it does) and then open the path for writing (when it doesn't).
+   Replace it with open with O_EXCL.
+
+ * "git pack-objects" and "git index-pack" mostly operate with off_t
+   when talking about the offset of objects in a packfile, but there
+   were a handful of places that used "unsigned long" to hold that
+   value, leading to an unintended truncation.
+
+ * Recent update to "git daemon" tries to enable the socket-level
+   KEEPALIVE, but when it is spawned via inetd, the standard input
+   file descriptor may not necessarily be connected to a socket.
+   Suppress an ENOTSOCK error from setsockopt().
+
+ * Recent FreeBSD stopped making perl available at /usr/bin/perl;
+   switch the default the built-in path to /usr/local/bin/perl on not
+   too ancient FreeBSD releases.
+
+ * "git commit --help" said "--no-verify" is only about skipping the
+   pre-commit hook, and failed to say that it also skipped the
+   commit-msg hook.
+   (merge def480f os/no-verify-skips-commit-msg-too later to maint).
+
+ * "git merge" in Git v2.9 was taught to forbid merging an unrelated
+   lines of history by default, but that is exactly the kind of thing
+   the "--rejoin" mode of "git subtree" (in contrib/) wants to do.
+   "git subtree" has been taught to use the "--allow-unrelated-histories"
+   option to override the default.
+   (merge 0f12c7d da/subtree-2.9-regression later to maint).
+
+ * The build procedure for "git persistent-https" helper (in contrib/)
+   has been updated so that it can be built with more recent versions
+   of Go.
+   (merge accb613 pm/build-persistent-https-with-recent-go later to maint).
+
+ * There is an optimization used in "git diff $treeA $treeB" to borrow
+   an already checked-out copy in the working tree when it is known to
+   be the same as the blob being compared, expecting that open/mmap of
+   such a file is faster than reading it from the object store, which
+   involves inflating and applying delta.  This however kicked in even
+   when the checked-out copy needs to go through the convert-to-git
+   conversion (including the clean filter), which defeats the whole
+   point of the optimization.  The optimization has been disabled when
+   the conversion is necessary.
+   (merge 06dec43 jk/diff-do-not-reuse-wtf-needs-cleaning later to maint).
+
+ * "git -c grep.patternType=extended log --basic-regexp" misbehaved
+   because the internal API to access the grep machinery was not
+   designed well.
+   (merge 8465541 jc/grep-commandline-vs-configuration later to maint).
+
+ * Windows port was failing some tests in t4130, due to the lack of
+   inum in the returned values by its lstat(2) emulation.
+   (merge 54956df js/t4130-rename-without-ino later to maint).
+
+ * The reflog output format is documented better, and a new format
+   --date=unix to report the seconds-since-epoch (without timezone)
+   has been added.
+   (merge 442f6fd jk/reflog-date later to maint).
+
+ * "git difftool <paths>..." started in a subdirectory failed to
+   interpret the paths relative to that directory, which has been
+   fixed.
+   (merge 32b8c58 jk/difftool-in-subdir later to maint).
+
+ * The characters in the label shown for tags/refs for commits in
+   "gitweb" output are now properly escaped for proper HTML output.
+   (merge 77947bb ab/gitweb-link-html-escape later to maint).
+
+ * FreeBSD can lie when asked mtime of a directory, which made the
+   untracked cache code to fall back to a slow-path, which in turn
+   caused tests in t7063 to fail because it wanted to verify the
+   behaviour of the fast-path.
+   (merge 6b7728d nd/fbsd-lazy-mtime later to maint).
+
+ * Squelch compiler warnings for netmalloc (in compat/) library.
+   (merge c6c9e18 js/nedmalloc-gcc6-warnings later to maint).
+
+ * A small memory leak in the command line parsing of "git blame"
+   has been plugged.
+
+ * The API documentation for hashmap was unclear if hashmap_entry
+   can be safely discarded without any other consideration.  State
+   that it is safe to do so.
+   (merge 54ba5a1 jc/hashmap-doc-init later to maint).
 
  * Other minor clean-ups and documentation updates
-   (merge e51b0df pb/commit-editmsg-path later to maint).
-   (merge b333d0d jk/send-pack-stdio later to maint).
-   (merge fcf0fe9 lf/sideband-returns-void later to maint).
-   (merge c2691e2 ah/unpack-trees-advice-messages later to maint).
-   (merge 82f6178 nd/doc-new-command later to maint).
-   (merge fa90ab4 js/t3404-grammo-fix later to maint).
-   (merge c61b2af lf/recv-sideband-cleanup later to maint).
-   (merge 31471ba rs/use-strbuf-addbuf later to maint).
-   (merge 503e224 nd/test-helpers later to maint).
-   (merge 16726cf jc/doc-diff-filter-exclude later to maint).
-   (merge fd2e7da rs/worktree-use-strbuf-absolute-path later to maint).
+   (merge 9d1ca1d jk/t4205-cleanup later to maint).
+   (merge 5f072e0 cp/completion-clone-recurse-submodules later to maint).
+   (merge bc57b9c rs/use-strbuf-addstr later to maint).
+   (merge 52db4b0 jh/clean-smudge-f-doc later to maint).
diff --git a/Documentation/RelNotes/2.9.3.txt b/Documentation/RelNotes/2.9.3.txt
new file mode 100644 (file)
index 0000000..bdcd446
--- /dev/null
@@ -0,0 +1,92 @@
+Git v2.9.3 Release Notes
+========================
+
+Fixes since v2.9.2
+------------------
+
+ * A helper function that takes the contents of a commit object and
+   finds its subject line did not ignore leading blank lines, as is
+   commonly done by other codepaths.  Make it ignore leading blank
+   lines to match.
+
+ * Git does not know what the contents in the index should be for a
+   path added with "git add -N" yet, so "git grep --cached" should not
+   show hits (or show lack of hits, with -L) in such a path, but that
+   logic does not apply to "git grep", i.e. searching in the working
+   tree files.  But we did so by mistake, which has been corrected.
+
+ * "git rebase -i --autostash" did not restore the auto-stashed change
+   when the operation was aborted.
+
+ * "git commit --amend --allow-empty-message -S" for a commit without
+   any message body could have misidentified where the header of the
+   commit object ends.
+
+ * More mark-up updates to typeset strings that are expected to
+   literally typed by the end user in fixed-width font.
+
+ * For a long time, we carried an in-code comment that said our
+   colored output would work only when we use fprintf/fputs on
+   Windows, which no longer is the case for the past few years.
+
+ * "gc.autoPackLimit" when set to 1 should not trigger a repacking
+   when there is only one pack, but the code counted poorly and did
+   so.
+
+ * One part of "git am" had an oddball helper function that called
+   stuff from outside "his" as opposed to calling what we have "ours",
+   which was not gender-neutral and also inconsistent with the rest of
+   the system where outside stuff is usuall called "theirs" in
+   contrast to "ours".
+
+ * The test framework learned a new helper test_match_signal to
+   check an exit code from getting killed by an expected signal.
+
+ * "git blame -M" missed a single line that was moved within the file.
+
+ * Fix recently introduced codepaths that are involved in parallel
+   submodule operations, which gave up on reading too early, and
+   could have wasted CPU while attempting to write under a corner
+   case condition.
+
+ * "git grep -i" has been taught to fold case in non-ascii locales
+   correctly.
+
+ * A test that unconditionally used "mktemp" learned that the command
+   is not necessarily available everywhere.
+
+ * "git blame file" allowed the lineage of lines in the uncommitted,
+   unadded contents of "file" to be inspected, but it refused when
+   "file" did not appear in the current commit.  When "file" was
+   created by renaming an existing file (but the change has not been
+   committed), this restriction was unnecessarily tight.
+
+ * "git add -N dir/file && git write-tree" produced an incorrect tree
+   when there are other paths in the same directory that sorts after
+   "file".
+
+ * "git fetch http://user:pass@host/repo..." scrubbed the userinfo
+   part, but "git push" didn't.
+
+ * An age old bug that caused "git diff --ignore-space-at-eol"
+   misbehave has been fixed.
+
+ * "git notes merge" had a code to see if a path exists (and fails if
+   it does) and then open the path for writing (when it doesn't).
+   Replace it with open with O_EXCL.
+
+ * "git pack-objects" and "git index-pack" mostly operate with off_t
+   when talking about the offset of objects in a packfile, but there
+   were a handful of places that used "unsigned long" to hold that
+   value, leading to an unintended truncation.
+
+ * Recent update to "git daemon" tries to enable the socket-level
+   KEEPALIVE, but when it is spawned via inetd, the standard input
+   file descriptor may not necessarily be connected to a socket.
+   Suppress an ENOTSOCK error from setsockopt().
+
+ * Recent FreeBSD stopped making perl available at /usr/bin/perl;
+   switch the default the built-in path to /usr/local/bin/perl on not
+   too ancient FreeBSD releases.
+
+Also contains minor documentation updates and code clean-ups.
index 8b1aee4b3bbb57e95fd660b57d589742dcd211b3..0bcb6790d6efbb2a812bc90b6f64e323cb3ec393 100644 (file)
@@ -1253,6 +1253,16 @@ format.attach::
        value as the boundary.  See the --attach option in
        linkgit:git-format-patch[1].
 
+format.from::
+       Provides the default value for the `--from` option to format-patch.
+       Accepts a boolean value, or a name and email address.  If false,
+       format-patch defaults to `--no-from`, using commit authors directly in
+       the "From:" field of patch mails.  If true, format-patch defaults to
+       `--from`, using your committer identity in the "From:" field of patch
+       mails and including a "From:" field in the body of the patch mail if
+       different.  If set to a non-boolean value, format-patch uses that
+       value instead of your committer identity.  Defaults to false.
+
 format.numbered::
        A boolean which can enable or disable sequence numbers in patch
        subjects.  It defaults to "auto" which enables it only if there
@@ -2427,8 +2437,13 @@ rebase.instructionFormat
 
 receive.advertiseAtomic::
        By default, git-receive-pack will advertise the atomic push
-       capability to its clients. If you don't want to this capability
-       to be advertised, set this variable to false.
+       capability to its clients. If you don't want to advertise this
+       capability, set this variable to false.
+
+receive.advertisePushOptions::
+       By default, git-receive-pack will advertise the push options
+       capability to its clients. If you don't want to advertise this
+       capability, set this variable to false.
 
 receive.autogc::
        By default, git-receive-pack will run "git-gc --auto" after
@@ -2483,6 +2498,15 @@ receive.fsck.skipList::
        can be safely ignored such as invalid committer email addresses.
        Note: corrupt objects cannot be skipped with this setting.
 
+receive.keepAlive::
+       After receiving the pack from the client, `receive-pack` may
+       produce no output (if `--quiet` was specified) while processing
+       the pack, causing some networks to drop the TCP connection.
+       With this option set, if `receive-pack` does not transmit
+       any data in this phase for `receive.keepAlive` seconds, it will
+       send a short keepalive packet.  The default is 5 seconds; set
+       to 0 to disable keepalives entirely.
+
 receive.unpackLimit::
        If the number of objects received in a push is below this
        limit then the objects will be unpacked into loose object
index 93c3527f0cf6f5bf43344f3cd4939497fd0fb5ef..47b77e693bca1675a4887bddb5147077d60805a0 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
           [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
-          [-u | --set-upstream]
+          [-u | --set-upstream] [--push-option=<string>]
           [--[no-]signed|--sign=(true|false|if-asked)]
           [--force-with-lease[=<refname>[:<expect>]]]
           [--no-verify] [<repository> [<refspec>...]]
@@ -156,6 +156,12 @@ already exists on the remote side.
        Either all refs are updated, or on error, no refs are updated.
        If the server does not support atomic pushes the push will fail.
 
+-o::
+--push-option::
+       Transmit the given string to the server, which passes them to
+       the pre-receive as well as the post-receive hook. The given string
+       must not contain a NUL or LF character.
+
 --receive-pack=<git-receive-pack>::
 --exec=<git-receive-pack>::
        Path to the 'git-receive-pack' program on the remote
@@ -198,10 +204,11 @@ branch we have for it.
 +
 `--force-with-lease=<refname>:<expect>` will protect the named ref (alone),
 if it is going to be updated, by requiring its current value to be
-the same as the specified value <expect> (which is allowed to be
+the same as the specified value `<expect>` (which is allowed to be
 different from the remote-tracking branch we have for the refname,
 or we do not even have to have such a remote-tracking branch when
-this form is used).
+this form is used).  If `<expect>` is the empty string, then the named ref
+must not already exist.
 +
 Note that all forms other than `--force-with-lease=<refname>:<expect>`
 that specifies the expected current value of the ref explicitly are
index 7e17cade7f0d2d0a80b079d51f5df600600078f5..5f9e65b0c4de117942babf0a14d255922d02b4ad 100644 (file)
@@ -625,6 +625,9 @@ config key: svn.authorsfile
        with the committer name as the first argument.  The program is
        expected to return a single line of the form "Name <email>",
        which will be treated as if included in the authors file.
++
+[verse]
+config key: svn.authorsProg
 
 -q::
 --quiet::
index 7c4cfb0885f44619761708cb2a2d12356fbe011e..0aeb020d026f59ad435c72e752d6f9c2f3cec73b 100644 (file)
@@ -11,7 +11,9 @@ SYNOPSIS
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
 'git worktree list' [--porcelain]
+'git worktree lock' [--reason <string>] <worktree>
 'git worktree prune' [-n] [-v] [--expire <expire>]
+'git worktree unlock' <worktree>
 
 DESCRIPTION
 -----------
@@ -38,9 +40,8 @@ section "DETAILS" for more information.
 
 If a linked working tree is stored on a portable device or network share
 which is not always mounted, you can prevent its administrative files from
-being pruned by creating a file named 'locked' alongside the other
-administrative files, optionally containing a plain text reason that
-pruning should be suppressed. See section "DETAILS" for more information.
+being pruned by issuing the `git worktree lock` command, optionally
+specifying `--reason` to explain why the working tree is locked.
 
 COMMANDS
 --------
@@ -62,10 +63,22 @@ each of the linked worktrees.  The output details include if the worktree is
 bare, the revision currently checked out, and the branch currently checked out
 (or 'detached HEAD' if none).
 
+lock::
+
+If a working tree is on a portable device or network share which
+is not always mounted, lock it to prevent its administrative
+files from being pruned automatically. This also prevents it from
+being moved or deleted. Optionally, specify a reason for the lock
+with `--reason`.
+
 prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
 
+unlock::
+
+Unlock a working tree, allowing it to be pruned, moved or deleted.
+
 OPTIONS
 -------
 
@@ -111,6 +124,18 @@ OPTIONS
 --expire <time>::
        With `prune`, only expire unused working trees older than <time>.
 
+--reason <string>::
+       With `lock`, an explanation why the working tree is locked.
+
+<worktree>::
+       Working trees can be identified by path, either relative or
+       absolute.
++
+If the last path components in the working tree's path is unique among
+working trees, it can be used to identify worktrees. For example if
+you only have to working trees at "/abc/def/ghi" and "/abc/def/ggg",
+then "ghi" or "def/ghi" is enough to point to the former working tree.
+
 DETAILS
 -------
 Each linked working tree has a private sub-directory in the repository's
@@ -151,7 +176,8 @@ instead.
 
 To prevent a $GIT_DIR/worktrees entry from being pruned (which
 can be useful in some situations, such as when the
-entry's working tree is stored on a portable device), add a file named
+entry's working tree is stored on a portable device), use the
+`git worktree lock` command, which adds a file named
 'locked' to the entry's directory. The file contains the reason in
 plain text. For example, if a linked working tree's `.git` file points
 to `/path/main/.git/worktrees/test-next` then a file named
@@ -227,8 +253,6 @@ performed manually, such as:
 - `remove` to remove a linked working tree and its administrative files (and
   warn if the working tree is dirty)
 - `mv` to move or rename a working tree and update its administrative files
-- `lock` to prevent automatic pruning of administrative files (for instance,
-  for a working tree on a portable device)
 
 GIT
 ---
index b40068bdfd14767fbfa659576f434944f4225b5e..34db3e219b285f13f8435ebd5307c2185fec15bf 100644 (file)
@@ -377,6 +377,11 @@ substitution.  For example:
        smudge = git-p4-filter --smudge %f
 ------------------------
 
+Note that "%f" is the name of the path that is being worked on. Depending
+on the version that is being filtered, the corresponding file on disk may
+not exist, or may have different contents. So, smudge and clean commands
+should not try to access the file on disk, but only act as filters on the
+content provided to them on standard input.
 
 Interaction between checkin/checkout attributes
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index d82e912e550ee0032c20a46c4238f7ce10003657..9565dc3fda47d7c8a7290132c347b5a6f0d2422e 100644 (file)
@@ -247,6 +247,15 @@ Both standard output and standard error output are forwarded to
 'git send-pack' on the other end, so you can simply `echo` messages
 for the user.
 
+The number of push options given on the command line of
+`git push --push-option=...` can be read from the environment
+variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are
+found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,...
+If it is negotiated to not use the push options phase, the
+environment variables will not be set. If the client selects
+to use push options, but doesn't transmit any, the count variable
+will be set to zero, `GIT_PUSH_OPTION_COUNT=0`.
+
 [[update]]
 update
 ~~~~~~
@@ -322,6 +331,15 @@ a sample script `post-receive-email` provided in the `contrib/hooks`
 directory in Git distribution, which implements sending commit
 emails.
 
+The number of push options given on the command line of
+`git push --push-option=...` can be read from the environment
+variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are
+found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,...
+If it is negotiated to not use the push options phase, the
+environment variables will not be set. If the client selects
+to use push options, but doesn't transmit any, the count variable
+will be set to zero, `GIT_PUSH_OPTION_COUNT=0`.
+
 [[post-update]]
 post-update
 ~~~~~~~~~~~
index 07cdd73ab2e68da4aaef30a523801f56610b176c..0849d281bb79b8a394a00f50fc09a10aba15d4f2 100644 (file)
@@ -79,6 +79,11 @@ submodule.<name>.ignore::
        "--ignore-submodule" option. The 'git submodule' commands are not
        affected by this setting.
 
+submodule.<name>.shallow::
+       When set to true, a clone of this submodule will be performed as a
+       shallow clone unless the user explicitely asks for a non-shallow
+       clone.
+
 
 EXAMPLES
 --------
index 29b19b992f2e652d6fc9258cbd64cad51f86c2ef..b95d67ec01f130417af9ff0d7bd0d2377008146c 100644 (file)
@@ -147,8 +147,14 @@ endif::git-rev-list[]
   "U" for a good signature with unknown validity and "N" for no signature
 - '%GS': show the name of the signer for a signed commit
 - '%GK': show the key used to sign a signed commit
-- '%gD': reflog selector, e.g., `refs/stash@{1}`
-- '%gd': shortened reflog selector, e.g., `stash@{1}`
+- '%gD': reflog selector, e.g., `refs/stash@{1}` or
+  `refs/stash@{2 minutes ago`}; the format follows the rules described
+  for the `-g` option. The portion before the `@` is the refname as
+  given on the command line (so `git log -g refs/heads/master` would
+  yield `refs/heads/master@{0}`).
+- '%gd': shortened reflog selector; same as `%gD`, but the refname
+  portion is shortened for human readability (so `refs/heads/master`
+  becomes just `master`).
 - '%gn': reflog identity name
 - '%gN': reflog identity name (respecting .mailmap, see
   linkgit:git-shortlog[1] or linkgit:git-blame[1])
index c5bd21812d63418e9011c32c96f5adc9bc193c90..a779c9dfec0ed962e7274577b1c53daa0ecff1d9 100644 (file)
@@ -252,10 +252,25 @@ list.
 +
 With `--pretty` format other than `oneline` (for obvious reasons),
 this causes the output to have two extra lines of information
-taken from the reflog.  By default, 'commit@\{Nth}' notation is
-used in the output.  When the starting commit is specified as
-'commit@\{now}', output also uses 'commit@\{timestamp}' notation
-instead.  Under `--pretty=oneline`, the commit message is
+taken from the reflog.  The reflog designator in the output may be shown
+as `ref@{Nth}` (where `Nth` is the reverse-chronological index in the
+reflog) or as `ref@{timestamp}` (with the timestamp for that entry),
+depending on a few rules:
++
+--
+1. If the starting point is specified as `ref@{Nth}`, show the index
+format.
++
+2. If the starting point was specified as `ref@{now}`, show the
+timestamp format.
++
+3. If neither was used, but `--date` was given on the command line, show
+the timestamp in the format requested by `--date`.
++
+4. Otherwise, show the index format.
+--
++
+Under `--pretty=oneline`, the commit message is
 prefixed with this information on the same line.
 This option cannot be combined with `--reverse`.
 See also linkgit:git-reflog[1].
@@ -274,6 +289,10 @@ ifdef::git-rev-list[]
        Try to speed up the traversal using the pack bitmap index (if
        one is available). Note that when traversing with `--objects`,
        trees and blobs will not have their associated path printed.
+
+--progress=<header>::
+       Show progress reports on stderr as objects are considered. The
+       `<header>` text will be printed with each progress update.
 endif::git-rev-list[]
 
 --
@@ -710,8 +729,8 @@ include::pretty-options.txt[]
        `iso-local`), the user's local time zone is used instead.
 +
 `--date=relative` shows dates relative to the current time,
-e.g. ``2 hours ago''. The `-local` option cannot be used with
-`--raw` or `--relative`.
+e.g. ``2 hours ago''. The `-local` option has no effect for
+`--date=relative`.
 +
 `--date=local` is an alias for `--date=default-local`.
 +
@@ -731,7 +750,18 @@ format, often found in email messages.
 +
 `--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.
 +
-`--date=raw` shows the date in the internal raw Git format `%s %z` format.
+`--date=raw` shows the date as seconds since the epoch (1970-01-01
+00:00:00 UTC), followed by a space, and then the timezone as an offset
+from UTC (a `+` or `-` with four digits; the first two are hours, and
+the second two are minutes). I.e., as if the timestamp were formatted
+with `strftime("%s %z")`).
+Note that the `-local` option does not affect the seconds-since-epoch
+value (which is always measured in UTC), but does switch the accompanying
+timezone value.
++
+`--date=unix` shows the date as a Unix epoch timestamp (seconds since
+1970).  As with `--raw`, this is always in UTC and therefore `-local`
+has no effect.
 +
 `--date=format:...` feeds the format `...` to your system `strftime`.
 Use `--date=format:%c` to show the date in your system locale's
index ad7a5bddd24d91ceda78d430fd86a204b21fd005..28f5a8b71574916820cdb1f1a023e27cb45ed6e6 100644 (file)
@@ -104,6 +104,11 @@ If `free_entries` is true, each hashmap_entry in the map is freed as well
 `entry` points to the entry to initialize.
 +
 `hash` is the hash code of the entry.
++
+The hashmap_entry structure does not hold references to external resources,
+and it is safe to just discard it once you are done with it (i.e. if
+your structure was allocated with xmalloc(), you can just free(3) it,
+and if it is on stack, you can just let it go out of scope).
 
 `void *hashmap_get(const struct hashmap *map, const void *key, const void *keydata)`::
 
index 8b363438021bf1ac635042b002976ad75816caed..736f3894a85602c10e0bf13fb74eb7f556207672 100644 (file)
@@ -307,7 +307,7 @@ In multi_ack mode:
     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
+  * the server will then send a 'NAK' and then wait for another response
     from the client - either a 'done' or another list of 'have' lines.
 
 In multi_ack_detailed mode:
@@ -454,7 +454,8 @@ 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'.
+possible values are 'report-status', 'delete-refs', 'ofs-delta' and
+'push-options'.
 
 Reference Update Request and Packfile Transfer
 ----------------------------------------------
@@ -465,9 +466,10 @@ 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.
+This list is followed by a flush-pkt. Then the push options are transmitted
+one per packet followed by another flush-pkt. After that the packfile that
+should contain all the objects that the server will need to complete the new
+references will be sent.
 
 ----
   update-request    =  *shallow ( command-list | push-cert ) [packfile]
index eaab6b4ac723c9f5d7dde9d70fe7e01d234e7734..4c28d3a8aea3312190d859e4accb70f9d28973bf 100644 (file)
@@ -253,6 +253,15 @@ atomic pushes. If the pushing client requests this capability, the server
 will update the refs in one atomic transaction. Either all refs are
 updated or none.
 
+push-options
+------------
+
+If the server sends the 'push-options' capability it is able to accept
+push options after the update commands have been sent, but before the
+packfile is streamed. If the pushing client requests this capability,
+the server will pass the options to the pre- and post- receive hooks
+that process this push request.
+
 allow-tip-sha1-in-want
 ----------------------
 
index 6a13386c27336b0f3054389674872a2c842f3290..d96ecb7141a12f1fe47246805c5d4e4e562a5c86 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -370,6 +370,14 @@ all::
 # Define HAVE_BSD_SYSCTL if your platform has a BSD-compatible sysctl function.
 #
 # Define HAVE_GETDELIM if your system has the getdelim() function.
+#
+# Define PAGER_ENV to a SP separated VAR=VAL pairs to define
+# default environment variables to be passed when a pager is spawned, e.g.
+#
+#    PAGER_ENV = LESS=FRX LV=-c
+#
+# to say "export LESS=FRX (and LV=-c) if the environment variable
+# LESS (and LV) is not set, respectively".
 
 GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -755,6 +763,7 @@ LIB_OBJS += merge.o
 LIB_OBJS += merge-blobs.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += mergesort.o
+LIB_OBJS += mru.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
@@ -1500,6 +1509,10 @@ ifeq ($(PYTHON_PATH),)
 NO_PYTHON = NoThanks
 endif
 
+ifndef PAGER_ENV
+PAGER_ENV = LESS=FRX LV=-c
+endif
+
 QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
 QUIET_SUBDIR1  =
 
@@ -1629,6 +1642,11 @@ ifdef DEFAULT_HELP_FORMAT
 BASIC_CFLAGS += -DDEFAULT_HELP_FORMAT='"$(DEFAULT_HELP_FORMAT)"'
 endif
 
+PAGER_ENV_SQ = $(subst ','\'',$(PAGER_ENV))
+PAGER_ENV_CQ = "$(subst ",\",$(subst \,\\,$(PAGER_ENV)))"
+PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ))
+BASIC_CFLAGS += -DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
+
 ALL_CFLAGS += $(BASIC_CFLAGS)
 ALL_LDFLAGS += $(BASIC_LDFLAGS)
 
@@ -1753,7 +1771,7 @@ common-cmds.h: $(wildcard Documentation/git-*.txt)
 
 SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
        $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
-       $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP)
+       $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV)
 define cmd_munge_script
 $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
@@ -1766,6 +1784,7 @@ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
     -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
     -e 's|@@SANE_TEXT_GREP@@|$(SANE_TEXT_GREP)|g' \
+    -e 's|@@PAGER_ENV@@|$(PAGER_ENV_SQ)|g' \
     $@.sh >$@+
 endef
 
@@ -2173,6 +2192,7 @@ GIT-BUILD-OPTIONS: FORCE
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
        @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
+       @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
 ifdef TEST_OUTPUT_DIRECTORY
        @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
 endif
index cfb79ea906f9c9911244845af2a984afe9a73843..739b34dcf25a1355246bed436ec7fdb4723d7ca7 100644 (file)
@@ -1822,6 +1822,8 @@ static void am_run(struct am_state *state, int resume)
                const char *mail = am_path(state, msgnum(state));
                int apply_status;
 
+               reset_ident_date();
+
                if (!file_exists(mail))
                        goto next;
 
index ab66cde2c2cf0f535678469cdf6eb6e37cd3113b..7ec782343002d27b1b7fdd7e61fada12e81eeaf1 100644 (file)
@@ -2633,6 +2633,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        case DATE_RAW:
                blame_date_width = sizeof("1161298804 -0700");
                break;
+       case DATE_UNIX:
+               blame_date_width = sizeof("1161298804");
+               break;
        case DATE_SHORT:
                blame_date_width = sizeof("2006-10-19");
                break;
@@ -2805,7 +2808,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        lno = prepare_lines(&sb);
 
        if (lno && !range_list.nr)
-               string_list_append(&range_list, xstrdup("1"));
+               string_list_append(&range_list, "1");
 
        anchor = 1;
        range_set_init(&ranges, range_list.nr);
index 618103fdeeb7f7b35911ebd12d0d811cc4a5297a..2dfe6265f7df6099b51645fa67dbeeb3f4f567a4 100644 (file)
@@ -131,7 +131,7 @@ struct expand_data {
        unsigned char sha1[20];
        enum object_type type;
        unsigned long size;
-       unsigned long disk_size;
+       off_t disk_size;
        const char *rest;
        unsigned char delta_base_sha1[20];
 
@@ -191,7 +191,7 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
                if (data->mark_query)
                        data->info.disk_sizep = &data->disk_size;
                else
-                       strbuf_addf(sb, "%lu", data->disk_size);
+                       strbuf_addf(sb, "%"PRIuMAX, (uintmax_t)data->disk_size);
        } else if (is_atom("rest", atom, len)) {
                if (data->mark_query)
                        data->split_on_whitespace = 1;
index 31ea247e3f262fb19595905869a7f2d874aa898e..f044a8c27f542c94c0b7f2458de1590e0d02fae2 100644 (file)
@@ -624,13 +624,13 @@ static void update_remote_refs(const struct ref *refs,
        const struct ref *rm = mapped_refs;
 
        if (check_connectivity) {
-               if (transport->progress)
-                       fprintf(stderr, _("Checking connectivity... "));
-               if (check_everything_connected_with_transport(iterate_ref_map,
-                                                             0, &rm, transport))
+               struct check_connected_options opt = CHECK_CONNECTED_INIT;
+
+               opt.transport = transport;
+               opt.progress = transport->progress;
+
+               if (check_connected(iterate_ref_map, &rm, &opt))
                        die(_("remote did not send all necessary objects"));
-               if (transport->progress)
-                       fprintf(stderr, _("done.\n"));
        }
 
        if (refs) {
index 1f6dbcd0d06a54eadc0d99a676adf3a9203aca61..77e3dc849419e697abe8f2f4c7d753cce17634a8 100644 (file)
@@ -1617,7 +1617,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
                OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
                OPT_BOOL('o', "only", &only, N_("commit only specified files")),
-               OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
+               OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")),
                OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
                OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
                            STATUS_FORMAT_SHORT),
index a991a53418506f18f439212f8cfe1f6f994ea571..6cbf73369b2f6cf41430d78e0a5e8ffa18de6d15 100644 (file)
@@ -25,7 +25,6 @@ static char term = '\n';
 static int use_global_config, use_system_config, use_local_config;
 static struct git_config_source given_config_source;
 static int actions, types;
-static const char *get_color_slot, *get_colorbool_slot;
 static int end_null;
 static int respect_includes = -1;
 static int show_origin;
index acd0cf1755eb5afec9dc179b8dc1a93050579023..164623bb6f2eb3ffad3eac59b8ec440b9cf60d9c 100644 (file)
@@ -729,7 +729,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                url = xstrdup("foreign");
 
        rm = ref_map;
-       if (check_everything_connected(iterate_ref_map, 0, &rm)) {
+       if (check_connected(iterate_ref_map, &rm, NULL)) {
                rc = error(_("%s did not send all necessary objects\n"), url);
                goto abort;
        }
@@ -866,6 +866,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 static int quickfetch(struct ref *ref_map)
 {
        struct ref *rm = ref_map;
+       struct check_connected_options opt = CHECK_CONNECTED_INIT;
 
        /*
         * If we are deepening a shallow clone we already have these
@@ -876,7 +877,8 @@ static int quickfetch(struct ref *ref_map)
         */
        if (depth)
                return -1;
-       return check_everything_connected(iterate_ref_map, 1, &rm);
+       opt.quiet = 1;
+       return check_connected(iterate_ref_map, &rm, &opt);
 }
 
 static int fetch_refs(struct transport *transport, struct ref *ref_map)
index e5658c320ee45c208d4087e2b9761f38e0aeca92..ac84e99f3a6c073409ab7514aacdad7e722c6255 100644 (file)
@@ -272,7 +272,7 @@ static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
 static void add_people_count(struct strbuf *out, struct string_list *people)
 {
        if (people->nr == 1)
-               strbuf_addf(out, "%s", people->items[0].string);
+               strbuf_addstr(out, people->items[0].string);
        else if (people->nr == 2)
                strbuf_addf(out, "%s (%d) and %s (%d)",
                            people->items[0].string,
index c6d17e63fd832d5b37ea93e523cfb774b563c452..2de272ea3659411b58d38d703a48790d55d6a12b 100644 (file)
@@ -377,6 +377,10 @@ static int fsck_sha1(const unsigned char *sha1)
 static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
                           unsigned long size, void *buffer, int *eaten)
 {
+       /*
+        * Note, buffer may be NULL if type is OBJ_BLOB. See
+        * verify_packfile(), data_valid variable for details.
+        */
        struct object *obj;
        obj = parse_object_buffer(sha1, type, size, buffer, eaten);
        if (!obj) {
index e8c71fc1d2e44ef9bd93c37ff714eeefc53664cf..1d2ea583a45507935ec2007d6c44f5e968a1aed5 100644 (file)
@@ -77,6 +77,7 @@ static int strict;
 static int do_fsck_object;
 static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
 static int verbose;
+static int show_resolving_progress;
 static int show_stat;
 static int check_self_contained_and_connected;
 
@@ -338,10 +339,10 @@ static void parse_pack_header(void)
        use(sizeof(struct pack_header));
 }
 
-static NORETURN void bad_object(unsigned long offset, const char *format,
+static NORETURN void bad_object(off_t offset, const char *format,
                       ...) __attribute__((format (printf, 2, 3)));
 
-static NORETURN void bad_object(unsigned long offset, const char *format, ...)
+static NORETURN void bad_object(off_t offset, const char *format, ...)
 {
        va_list params;
        char buf[1024];
@@ -349,7 +350,8 @@ static NORETURN void bad_object(unsigned long offset, const char *format, ...)
        va_start(params, format);
        vsnprintf(buf, sizeof(buf), format, params);
        va_end(params);
-       die(_("pack has bad object at offset %lu: %s"), offset, buf);
+       die(_("pack has bad object at offset %"PRIuMAX": %s"),
+           (uintmax_t)offset, buf);
 }
 
 static inline struct thread_local *get_thread_data(void)
@@ -429,7 +431,7 @@ static int is_delta_type(enum object_type type)
        return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
 }
 
-static void *unpack_entry_data(unsigned long offset, unsigned long size,
+static void *unpack_entry_data(off_t offset, unsigned long size,
                               enum object_type type, unsigned char *sha1)
 {
        static char fixed_buf[8192];
@@ -549,13 +551,13 @@ static void *unpack_data(struct object_entry *obj,
                         void *cb_data)
 {
        off_t from = obj[0].idx.offset + obj[0].hdr_size;
-       unsigned long len = obj[1].idx.offset - from;
+       off_t len = obj[1].idx.offset - from;
        unsigned char *data, *inbuf;
        git_zstream stream;
        int status;
 
        data = xmallocz(consume ? 64*1024 : obj->size);
-       inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
+       inbuf = xmalloc((len < 64*1024) ? (int)len : 64*1024);
 
        memset(&stream, 0, sizeof(stream));
        git_inflate_init(&stream);
@@ -563,15 +565,15 @@ static void *unpack_data(struct object_entry *obj,
        stream.avail_out = consume ? 64*1024 : obj->size;
 
        do {
-               ssize_t n = (len < 64*1024) ? len : 64*1024;
+               ssize_t n = (len < 64*1024) ? (ssize_t)len : 64*1024;
                n = xpread(get_thread_data()->pack_fd, inbuf, n, from);
                if (n < 0)
                        die_errno(_("cannot pread pack file"));
                if (!n)
-                       die(Q_("premature end of pack file, %lu byte missing",
-                              "premature end of pack file, %lu bytes missing",
-                              len),
-                           len);
+                       die(Q_("premature end of pack file, %"PRIuMAX" byte missing",
+                              "premature end of pack file, %"PRIuMAX" bytes missing",
+                              (unsigned int)len),
+                           (uintmax_t)len);
                from += n;
                len -= n;
                stream.next_in = inbuf;
@@ -1190,7 +1192,7 @@ static void resolve_deltas(void)
        qsort(ref_deltas, nr_ref_deltas, sizeof(struct ref_delta_entry),
              compare_ref_delta_entry);
 
-       if (verbose)
+       if (verbose || show_resolving_progress)
                progress = start_progress(_("Resolving deltas"),
                                          nr_ref_deltas + nr_ofs_deltas);
 
@@ -1625,6 +1627,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        struct pack_idx_option opts;
        unsigned char pack_sha1[20];
        unsigned foreign_nr = 1;        /* zero is a "good" value, assume bad */
+       int report_end_of_input = 0;
 
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(index_pack_usage);
@@ -1694,6 +1697,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                input_len = sizeof(*hdr);
                        } else if (!strcmp(arg, "-v")) {
                                verbose = 1;
+                       } else if (!strcmp(arg, "--show-resolving-progress")) {
+                               show_resolving_progress = 1;
+                       } else if (!strcmp(arg, "--report-end-of-input")) {
+                               report_end_of_input = 1;
                        } else if (!strcmp(arg, "-o")) {
                                if (index_name || (i+1) >= argc)
                                        usage(index_pack_usage);
@@ -1751,6 +1758,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                obj_stat = xcalloc(st_add(nr_objects, 1), sizeof(struct object_stat));
        ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry));
        parse_pack_objects(pack_sha1);
+       if (report_end_of_input)
+               write_in_full(2, "\0", 1);
        resolve_deltas();
        conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
        free(ofs_deltas);
index fd1652f52b32fc8bfb7bdc6e0191affafaa21c9c..1f116bea8c8873d652f573db2047c558fa51bbd3 100644 (file)
@@ -719,6 +719,7 @@ static void add_header(const char *value)
 static int thread;
 static int do_signoff;
 static int base_auto;
+static char *from;
 static const char *signature = git_version_string;
 static const char *signature_file;
 static int config_cover_letter;
@@ -807,6 +808,17 @@ static int git_format_config(const char *var, const char *value, void *cb)
                base_auto = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "format.from")) {
+               int b = git_config_maybe_bool(var, value);
+               free(from);
+               if (b < 0)
+                       from = xstrdup(value);
+               else if (b)
+                       from = xstrdup(git_committer_info(IDENT_NO_DATE));
+               else
+                       from = NULL;
+               return 0;
+       }
 
        return git_log_config(var, value, cb);
 }
@@ -1384,7 +1396,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        int reroll_count = -1;
        char *branch_name = NULL;
-       char *from = NULL;
        char *base_commit = NULL;
        struct base_tree_info bases;
 
index 05720517628270669d9e231c2030befd2d6325f6..f848b896927a34e884f8377ea9c98271237be5fc 100644 (file)
@@ -91,7 +91,7 @@ static const char * const git_notes_get_ref_usage[] = {
 };
 
 static const char note_template[] =
-       "\nWrite/edit the notes for the following object:\n";
+       N_("Write/edit the notes for the following object:");
 
 struct note_data {
        int given;
@@ -179,7 +179,8 @@ static void prepare_note_data(const unsigned char *object, struct note_data *d,
                        copy_obj_to_fd(fd, old_note);
 
                strbuf_addch(&buf, '\n');
-               strbuf_add_commented_lines(&buf, note_template, strlen(note_template));
+               strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
+               strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)));
                strbuf_addch(&buf, '\n');
                write_or_die(fd, buf.buf, buf.len);
 
index a2f8cfdec0d4034c20f3307c390cec9dec6d4a18..4a63398960c42d91821c4278a4cfbd11c1f1a7a4 100644 (file)
@@ -46,6 +46,7 @@ static int keep_unreachable, unpack_unreachable, include_tag;
 static unsigned long unpack_unreachable_expiration;
 static int pack_loose_unreachable;
 static int local;
+static int have_non_local_packs;
 static int incremental;
 static int ignore_packed_keep;
 static int allow_ofs_delta;
@@ -342,15 +343,15 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
 }
 
 /* Return 0 if we will bust the pack-size limit */
-static unsigned long write_reuse_object(struct sha1file *f, struct object_entry *entry,
-                                       unsigned long limit, int usable_delta)
+static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
+                               unsigned long limit, int usable_delta)
 {
        struct packed_git *p = entry->in_pack;
        struct pack_window *w_curs = NULL;
        struct revindex_entry *revidx;
        off_t offset;
        enum object_type type = entry->type;
-       unsigned long datalen;
+       off_t datalen;
        unsigned char header[10], dheader[10];
        unsigned hdrlen;
 
@@ -416,11 +417,12 @@ static unsigned long write_reuse_object(struct sha1file *f, struct object_entry
 }
 
 /* Return 0 if we will bust the pack-size limit */
-static unsigned long write_object(struct sha1file *f,
-                                 struct object_entry *entry,
-                                 off_t write_offset)
+static off_t write_object(struct sha1file *f,
+                         struct object_entry *entry,
+                         off_t write_offset)
 {
-       unsigned long limit, len;
+       unsigned long limit;
+       off_t len;
        int usable_delta, to_reuse;
 
        if (!pack_to_stdout)
@@ -492,7 +494,7 @@ static enum write_one_status write_one(struct sha1file *f,
                                       struct object_entry *e,
                                       off_t *offset)
 {
-       unsigned long size;
+       off_t size;
        int recursing;
 
        /*
@@ -977,6 +979,23 @@ static int want_object_in_pack(const unsigned char *sha1,
                                return 1;
                        if (incremental)
                                return 0;
+
+                       /*
+                        * When asked to do --local (do not include an
+                        * object that appears in a pack we borrow
+                        * from elsewhere) or --honor-pack-keep (do not
+                        * include an object that appears in a pack marked
+                        * with .keep), we need to make sure no copy of this
+                        * object come from in _any_ pack that causes us to
+                        * omit it, and need to complete this loop.  When
+                        * neither option is in effect, we know the object
+                        * we just found is going to be packed, so break
+                        * out of the loop to return 1 now.
+                        */
+                       if (!ignore_packed_keep &&
+                           (!local || !have_non_local_packs))
+                               break;
+
                        if (local && !p->pack_local)
                                return 0;
                        if (ignore_packed_keep && p->pack_local && p->pack_keep)
@@ -2783,6 +2802,28 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                progress = 2;
 
        prepare_packed_git();
+       if (ignore_packed_keep) {
+               struct packed_git *p;
+               for (p = packed_git; p; p = p->next)
+                       if (p->pack_local && p->pack_keep)
+                               break;
+               if (!p) /* no keep-able packs found */
+                       ignore_packed_keep = 0;
+       }
+       if (local) {
+               /*
+                * unlike ignore_packed_keep above, we do not want to
+                * unset "local" based on looking at packs, as it
+                * also covers non-local objects
+                */
+               struct packed_git *p;
+               for (p = packed_git; p; p = p->next) {
+                       if (!p->pack_local) {
+                               have_non_local_packs = 1;
+                               break;
+                       }
+               }
+       }
 
        if (progress)
                progress_state = start_progress(_("Counting objects"), 0);
index 4e9e4dbab23e5fb78239eadde724a63240e43505..3bb9d6b7e63b3e3082023c3d333c11757df6fbda 100644 (file)
@@ -353,7 +353,8 @@ static int push_with_options(struct transport *transport, int flags)
        return 1;
 }
 
-static int do_push(const char *repo, int flags)
+static int do_push(const char *repo, int flags,
+                  const struct string_list *push_options)
 {
        int i, errs;
        struct remote *remote = pushremote_get(repo);
@@ -376,6 +377,9 @@ static int do_push(const char *repo, int flags)
        if (remote->mirror)
                flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 
+       if (push_options->nr)
+               flags |= TRANSPORT_PUSH_OPTIONS;
+
        if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
                if (!strcmp(*refspec, "refs/tags/*"))
                        return error(_("--all and --tags are incompatible"));
@@ -406,13 +410,16 @@ static int do_push(const char *repo, int flags)
                for (i = 0; i < url_nr; i++) {
                        struct transport *transport =
                                transport_get(remote, url[i]);
+                       if (flags & TRANSPORT_PUSH_OPTIONS)
+                               transport->push_options = push_options;
                        if (push_with_options(transport, flags))
                                errs++;
                }
        } else {
                struct transport *transport =
                        transport_get(remote, NULL);
-
+               if (flags & TRANSPORT_PUSH_OPTIONS)
+                       transport->push_options = push_options;
                if (push_with_options(transport, flags))
                        errs++;
        }
@@ -500,6 +507,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        int push_cert = -1;
        int rc;
        const char *repo = NULL;        /* default repository */
+       static struct string_list push_options = STRING_LIST_INIT_DUP;
+       static struct string_list_item *item;
+
        struct option options[] = {
                OPT__VERBOSITY(&verbosity),
                OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")),
@@ -533,6 +543,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                  0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
                  PARSE_OPT_OPTARG, option_parse_push_signed },
                OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
+               OPT_STRING_LIST('o', "push-option", &push_options, N_("server-specific"), N_("option to transmit")),
                OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
                                TRANSPORT_FAMILY_IPV4),
                OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
@@ -563,7 +574,11 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                set_refspecs(argv + 1, argc - 1, repo);
        }
 
-       rc = do_push(repo, flags);
+       for_each_string_list_item(item, &push_options)
+               if (strchr(item->string, '\n'))
+                       die(_("push options must not have new line characters"));
+
+       rc = do_push(repo, flags, &push_options);
        if (rc == -1)
                usage_with_options(push_usage, options);
        else
index 15c323a7cdc97bce04203d0edcc425e3d74fc7c7..92e1213ecc05969f4601c7d3c48f6d96dcf72a0a 100644 (file)
@@ -44,10 +44,12 @@ static struct strbuf fsck_msg_types = STRBUF_INIT;
 static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
 static int advertise_atomic_push = 1;
+static int advertise_push_options;
 static int unpack_limit = 100;
 static int report_status;
 static int use_sideband;
 static int use_atomic;
+static int use_push_options;
 static int quiet;
 static int prefer_ofs_delta = 1;
 static int auto_update_server_info;
@@ -76,6 +78,13 @@ static long nonce_stamp_slop;
 static unsigned long nonce_stamp_slop_limit;
 static struct ref_transaction *transaction;
 
+static enum {
+       KEEPALIVE_NEVER = 0,
+       KEEPALIVE_AFTER_NUL,
+       KEEPALIVE_ALWAYS
+} use_keepalive;
+static int keepalive_in_sec = 5;
+
 static enum deny_action parse_deny_action(const char *var, const char *value)
 {
        if (value) {
@@ -193,6 +202,16 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (strcmp(var, "receive.advertisepushoptions") == 0) {
+               advertise_push_options = git_config_bool(var, value);
+               return 0;
+       }
+
+       if (strcmp(var, "receive.keepalive") == 0) {
+               keepalive_in_sec = git_config_int(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
@@ -211,6 +230,8 @@ static void show_ref(const char *path, const unsigned char *sha1)
                        strbuf_addstr(&cap, " ofs-delta");
                if (push_cert_nonce)
                        strbuf_addf(&cap, " push-cert=%s", push_cert_nonce);
+               if (advertise_push_options)
+                       strbuf_addstr(&cap, " push-options");
                strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
                packet_write(1, "%s %s%c%s\n",
                             sha1_to_hex(sha1), path, 0, cap.buf);
@@ -319,10 +340,60 @@ static void rp_error(const char *err, ...)
 static int copy_to_sideband(int in, int out, void *arg)
 {
        char data[128];
+       int keepalive_active = 0;
+
+       if (keepalive_in_sec <= 0)
+               use_keepalive = KEEPALIVE_NEVER;
+       if (use_keepalive == KEEPALIVE_ALWAYS)
+               keepalive_active = 1;
+
        while (1) {
-               ssize_t sz = xread(in, data, sizeof(data));
+               ssize_t sz;
+
+               if (keepalive_active) {
+                       struct pollfd pfd;
+                       int ret;
+
+                       pfd.fd = in;
+                       pfd.events = POLLIN;
+                       ret = poll(&pfd, 1, 1000 * keepalive_in_sec);
+
+                       if (ret < 0) {
+                               if (errno == EINTR)
+                                       continue;
+                               else
+                                       break;
+                       } else if (ret == 0) {
+                               /* no data; send a keepalive packet */
+                               static const char buf[] = "0005\1";
+                               write_or_die(1, buf, sizeof(buf) - 1);
+                               continue;
+                       } /* else there is actual data to read */
+               }
+
+               sz = xread(in, data, sizeof(data));
                if (sz <= 0)
                        break;
+
+               if (use_keepalive == KEEPALIVE_AFTER_NUL && !keepalive_active) {
+                       const char *p = memchr(data, '\0', sz);
+                       if (p) {
+                               /*
+                                * The NUL tells us to start sending keepalives. Make
+                                * sure we send any other data we read along
+                                * with it.
+                                */
+                               keepalive_active = 1;
+                               send_sideband(1, 2, data, p - data, use_sideband);
+                               send_sideband(1, 2, p + 1, sz - (p - data + 1), use_sideband);
+                               continue;
+                       }
+               }
+
+               /*
+                * Either we're not looking for a NUL signal, or we didn't see
+                * it yet; just pass along the data.
+                */
                send_sideband(1, 2, data, sz, use_sideband);
        }
        close(in);
@@ -550,8 +621,16 @@ static void prepare_push_cert_sha1(struct child_process *proc)
        }
 }
 
+struct receive_hook_feed_state {
+       struct command *cmd;
+       int skip_broken;
+       struct strbuf buf;
+       const struct string_list *push_options;
+};
+
 typedef int (*feed_fn)(void *, const char **, size_t *);
-static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state)
+static int run_and_feed_hook(const char *hook_name, feed_fn feed,
+                            struct receive_hook_feed_state *feed_state)
 {
        struct child_process proc = CHILD_PROCESS_INIT;
        struct async muxer;
@@ -567,6 +646,16 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
        proc.argv = argv;
        proc.in = -1;
        proc.stdout_to_stderr = 1;
+       if (feed_state->push_options) {
+               int i;
+               for (i = 0; i < feed_state->push_options->nr; i++)
+                       argv_array_pushf(&proc.env_array,
+                               "GIT_PUSH_OPTION_%d=%s", i,
+                               feed_state->push_options->items[i].string);
+               argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d",
+                                feed_state->push_options->nr);
+       } else
+               argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
 
        if (use_sideband) {
                memset(&muxer, 0, sizeof(muxer));
@@ -606,12 +695,6 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
        return finish_command(&proc);
 }
 
-struct receive_hook_feed_state {
-       struct command *cmd;
-       int skip_broken;
-       struct strbuf buf;
-};
-
 static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
 {
        struct receive_hook_feed_state *state = state_;
@@ -634,8 +717,10 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
        return 0;
 }
 
-static int run_receive_hook(struct command *commands, const char *hook_name,
-                           int skip_broken)
+static int run_receive_hook(struct command *commands,
+                           const char *hook_name,
+                           int skip_broken,
+                           const struct string_list *push_options)
 {
        struct receive_hook_feed_state state;
        int status;
@@ -646,6 +731,7 @@ static int run_receive_hook(struct command *commands, const char *hook_name,
        if (feed_receive_hook(&state, NULL, NULL))
                return 0;
        state.cmd = commands;
+       state.push_options = push_options;
        status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
        strbuf_release(&state.buf);
        return status;
@@ -737,7 +823,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
 {
        static struct lock_file shallow_lock;
        struct sha1_array extra = SHA1_ARRAY_INIT;
-       const char *alt_file;
+       struct check_connected_options opt = CHECK_CONNECTED_INIT;
        uint32_t mask = 1 << (cmd->index % 32);
        int i;
 
@@ -749,9 +835,8 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
                    !delayed_reachability_test(si, i))
                        sha1_array_append(&extra, si->shallow->sha1[i]);
 
-       setup_alternate_shallow(&shallow_lock, &alt_file, &extra);
-       if (check_shallow_connected(command_singleton_iterator,
-                                   0, cmd, alt_file)) {
+       setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
+       if (check_connected(command_singleton_iterator, cmd, &opt)) {
                rollback_lock_file(&shallow_lock);
                sha1_array_clear(&extra);
                return -1;
@@ -1160,8 +1245,8 @@ static void set_connectivity_errors(struct command *commands,
                if (shallow_update && si->shallow_ref[cmd->index])
                        /* to be checked in update_shallow_ref() */
                        continue;
-               if (!check_everything_connected(command_singleton_iterator,
-                                               0, &singleton))
+               if (!check_connected(command_singleton_iterator, &singleton,
+                                    NULL))
                        continue;
                cmd->error_string = "missing necessary objects";
        }
@@ -1316,11 +1401,15 @@ static void execute_commands_atomic(struct command *commands,
 
 static void execute_commands(struct command *commands,
                             const char *unpacker_error,
-                            struct shallow_info *si)
+                            struct shallow_info *si,
+                            const struct string_list *push_options)
 {
+       struct check_connected_options opt = CHECK_CONNECTED_INIT;
        struct command *cmd;
        unsigned char sha1[20];
        struct iterate_data data;
+       struct async muxer;
+       int err_fd = 0;
 
        if (unpacker_error) {
                for (cmd = commands; cmd; cmd = cmd->next)
@@ -1328,14 +1417,28 @@ static void execute_commands(struct command *commands,
                return;
        }
 
+       if (use_sideband) {
+               memset(&muxer, 0, sizeof(muxer));
+               muxer.proc = copy_to_sideband;
+               muxer.in = -1;
+               if (!start_async(&muxer))
+                       err_fd = muxer.in;
+               /* ...else, continue without relaying sideband */
+       }
+
        data.cmds = commands;
        data.si = si;
-       if (check_everything_connected(iterate_receive_command_list, 0, &data))
+       opt.err_fd = err_fd;
+       opt.progress = err_fd && !quiet;
+       if (check_connected(iterate_receive_command_list, &data, &opt))
                set_connectivity_errors(commands, si);
 
+       if (use_sideband)
+               finish_async(&muxer);
+
        reject_updates_to_hidden(commands);
 
-       if (run_receive_hook(commands, "pre-receive", 0)) {
+       if (run_receive_hook(commands, "pre-receive", 0, push_options)) {
                for (cmd = commands; cmd; cmd = cmd->next) {
                        if (!cmd->error_string)
                                cmd->error_string = "pre-receive hook declined";
@@ -1439,6 +1542,9 @@ static struct command *read_head_info(struct sha1_array *shallow)
                        if (advertise_atomic_push
                            && parse_feature_request(feature_list, "atomic"))
                                use_atomic = 1;
+                       if (advertise_push_options
+                           && parse_feature_request(feature_list, "push-options"))
+                               use_push_options = 1;
                }
 
                if (!strcmp(line, "push-cert")) {
@@ -1471,6 +1577,21 @@ static struct command *read_head_info(struct sha1_array *shallow)
        return commands;
 }
 
+static void read_push_options(struct string_list *options)
+{
+       while (1) {
+               char *line;
+               int len;
+
+               line = packet_read_line(0, &len);
+
+               if (!line)
+                       break;
+
+               string_list_append(options, line);
+       }
+}
+
 static const char *parse_pack_header(struct pack_header *hdr)
 {
        switch (read_pack_header(0, hdr)) {
@@ -1548,6 +1669,10 @@ static const char *unpack(int err_fd, struct shallow_info *si)
                                 (uintmax_t)getpid(),
                                 hostname);
 
+               if (!quiet && err_fd)
+                       argv_array_push(&child.args, "--show-resolving-progress");
+               if (use_sideband)
+                       argv_array_push(&child.args, "--report-end-of-input");
                if (fsck_objects)
                        argv_array_pushf(&child.args, "--strict%s",
                                fsck_msg_types.buf);
@@ -1577,6 +1702,7 @@ static const char *unpack_with_sideband(struct shallow_info *si)
        if (!use_sideband)
                return unpack(0, si);
 
+       use_keepalive = KEEPALIVE_AFTER_NUL;
        memset(&muxer, 0, sizeof(muxer));
        muxer.proc = copy_to_sideband;
        muxer.in = -1;
@@ -1756,6 +1882,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 
        if ((commands = read_head_info(&shallow)) != NULL) {
                const char *unpack_status = NULL;
+               struct string_list push_options = STRING_LIST_INIT_DUP;
+
+               if (use_push_options)
+                       read_push_options(&push_options);
 
                prepare_shallow_info(&si, &shallow);
                if (!si.nr_ours && !si.nr_theirs)
@@ -1764,13 +1894,18 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        unpack_status = unpack_with_sideband(&si);
                        update_shallow_info(commands, &si, &ref);
                }
-               execute_commands(commands, unpack_status, &si);
+               use_keepalive = KEEPALIVE_ALWAYS;
+               execute_commands(commands, unpack_status, &si,
+                                &push_options);
                if (pack_lockfile)
                        unlink_or_warn(pack_lockfile);
                if (report_status)
                        report(commands, unpack_status);
-               run_receive_hook(commands, "post-receive", 1);
+               run_receive_hook(commands, "post-receive", 1,
+                                &push_options);
                run_update_post_hook(commands);
+               if (push_options.nr)
+                       string_list_clear(&push_options, 0);
                if (auto_gc) {
                        const char *argv_gc_auto[] = {
                                "gc", "--auto", "--quiet", NULL,
index b82bcc3436014183084f48575f6c4568a0a94b5c..0ba82b1635b6380d9a7d9fd9a31471b8c8ac9f20 100644 (file)
@@ -9,6 +9,7 @@
 #include "log-tree.h"
 #include "graph.h"
 #include "bisect.h"
+#include "progress.h"
 
 static const char rev_list_usage[] =
 "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -49,12 +50,17 @@ static const char rev_list_usage[] =
 "    --bisect-all"
 ;
 
+static struct progress *progress;
+static unsigned progress_counter;
+
 static void finish_commit(struct commit *commit, void *data);
 static void show_commit(struct commit *commit, void *data)
 {
        struct rev_list_info *info = data;
        struct rev_info *revs = info->revs;
 
+       display_progress(progress, ++progress_counter);
+
        if (info->flags & REV_LIST_QUIET) {
                finish_commit(commit, data);
                return;
@@ -190,6 +196,7 @@ static void show_object(struct object *obj, const char *name, void *cb_data)
 {
        struct rev_list_info *info = cb_data;
        finish_object(obj, name, cb_data);
+       display_progress(progress, ++progress_counter);
        if (info->flags & REV_LIST_QUIET)
                return;
        show_object_with_name(stdout, obj, name);
@@ -276,6 +283,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        int bisect_show_vars = 0;
        int bisect_find_all = 0;
        int use_bitmap_index = 0;
+       const char *show_progress = NULL;
 
        git_config(git_default_config, NULL);
        init_revisions(&revs, prefix);
@@ -325,6 +333,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        test_bitmap_walk(&revs);
                        return 0;
                }
+               if (skip_prefix(arg, "--progress=", &arg)) {
+                       show_progress = arg;
+                       continue;
+               }
                usage(rev_list_usage);
 
        }
@@ -355,6 +367,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        if (bisect_list)
                revs.limited = 1;
 
+       if (show_progress)
+               progress = start_progress_delay(show_progress, 0, 0, 2);
+
        if (use_bitmap_index && !revs.prune) {
                if (revs.count && !revs.left_right && !revs.cherry_mark) {
                        uint32_t commit_count;
@@ -392,6 +407,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
        traverse_commit_list(&revs, show_commit, show_object, &info);
 
+       stop_progress(&progress);
+
        if (revs.count) {
                if (revs.left_right && revs.cherry_mark)
                        printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same);
index c961b74c5aaae41153b89f4e877437ba7f0d70c7..76cf05e2ade4348b8ad59b1541b58f671e53e1ce 100644 (file)
@@ -469,7 +469,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
                        (stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
                        PARSE_OPT_SHELL_EVAL);
 
-       strbuf_addf(&parsed, " --");
+       strbuf_addstr(&parsed, " --");
        sq_quote_argv(&parsed, argv, 0);
        puts(parsed.buf);
        return 0;
index 56a2c366698f838146bcf62d7b2c7a8a115287e1..4e693808b197780c1029aaf886478795b3a6d0a1 100644 (file)
@@ -76,7 +76,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
        const char * const * usage_str = revert_or_cherry_pick_usage(opts);
        const char *me = action_name(opts);
        int cmd = 0;
-       struct option options[] = {
+       struct option base_options[] = {
                OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
                OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
                OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
@@ -91,13 +91,9 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
                        N_("option for merge strategy"), option_parse_x),
                { OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"),
                  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-               OPT_END(),
-               OPT_END(),
-               OPT_END(),
-               OPT_END(),
-               OPT_END(),
-               OPT_END(),
+               OPT_END()
        };
+       struct option *options = base_options;
 
        if (opts->action == REPLAY_PICK) {
                struct option cp_extra[] = {
@@ -108,8 +104,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
                        OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
                        OPT_END(),
                };
-               if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
-                       die(_("program error"));
+               options = parse_options_concat(options, cp_extra);
        }
 
        argc = parse_options(argc, argv, NULL, options, usage_str,
index b22352b6e1e4c40d1a6e182f5c12abf6351f71fb..6f6d67a4694a83f18f86f5de2d9d64fd4e449a5e 100644 (file)
@@ -795,7 +795,7 @@ static int update_clone_task_finished(int result,
                suc->failed_clones[suc->failed_clones_nr++] = ce;
                return 0;
        } else {
-               idx = suc->current - suc->list.nr;
+               idx -= suc->list.nr;
                ce  = suc->failed_clones[idx];
                strbuf_addf(err, _("Failed to clone '%s' a second time, aborting"),
                            ce->name);
index cce555cbbc8a58b41789990402d756d7d4aa337a..5a41788edb7501ba2457b4db50f1b2472eeccc1e 100644 (file)
@@ -14,7 +14,9 @@
 static const char * const worktree_usage[] = {
        N_("git worktree add [<options>] <path> [<branch>]"),
        N_("git worktree list [<options>]"),
+       N_("git worktree lock [<options>] <path>"),
        N_("git worktree prune [<options>]"),
+       N_("git worktree unlock <path>"),
        NULL
 };
 
@@ -462,6 +464,66 @@ static int list(int ac, const char **av, const char *prefix)
        return 0;
 }
 
+static int lock_worktree(int ac, const char **av, const char *prefix)
+{
+       const char *reason = "", *old_reason;
+       struct option options[] = {
+               OPT_STRING(0, "reason", &reason, N_("string"),
+                          N_("reason for locking")),
+               OPT_END()
+       };
+       struct worktree **worktrees, *wt;
+
+       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       if (ac != 1)
+               usage_with_options(worktree_usage, options);
+
+       worktrees = get_worktrees();
+       wt = find_worktree(worktrees, prefix, av[0]);
+       if (!wt)
+               die(_("'%s' is not a working tree"), av[0]);
+       if (is_main_worktree(wt))
+               die(_("The main working tree cannot be locked or unlocked"));
+
+       old_reason = is_worktree_locked(wt);
+       if (old_reason) {
+               if (*old_reason)
+                       die(_("'%s' is already locked, reason: %s"),
+                           av[0], old_reason);
+               die(_("'%s' is already locked"), av[0]);
+       }
+
+       write_file(git_common_path("worktrees/%s/locked", wt->id),
+                  "%s", reason);
+       free_worktrees(worktrees);
+       return 0;
+}
+
+static int unlock_worktree(int ac, const char **av, const char *prefix)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+       struct worktree **worktrees, *wt;
+       int ret;
+
+       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       if (ac != 1)
+               usage_with_options(worktree_usage, options);
+
+       worktrees = get_worktrees();
+       wt = find_worktree(worktrees, prefix, av[0]);
+       if (!wt)
+               die(_("'%s' is not a working tree"), av[0]);
+       if (is_main_worktree(wt))
+               die(_("The main working tree cannot be locked or unlocked"));
+       if (!is_worktree_locked(wt))
+               die(_("'%s' is not locked"), av[0]);
+       ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+       free_worktrees(worktrees);
+       return ret;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
        struct option options[] = {
@@ -478,5 +540,9 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
                return prune(ac - 1, av + 1, prefix);
        if (!strcmp(av[1], "list"))
                return list(ac - 1, av + 1, prefix);
+       if (!strcmp(av[1], "lock"))
+               return lock_worktree(ac - 1, av + 1, prefix);
+       if (!strcmp(av[1], "unlock"))
+               return unlock_worktree(ac - 1, av + 1, prefix);
        usage_with_options(worktree_usage, options);
 }
diff --git a/cache.h b/cache.h
index 3855ddfbe659569a2156b8783d53f7838b6a940f..95a0bd397a9858cc3ea19778112dcc1dd036642d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1230,7 +1230,8 @@ struct date_mode {
                DATE_ISO8601_STRICT,
                DATE_RFC2822,
                DATE_STRFTIME,
-               DATE_RAW
+               DATE_RAW,
+               DATE_UNIX
        } type;
        const char *strftime_fmt;
        int local;
@@ -1269,6 +1270,7 @@ extern const char *ident_default_email(void);
 extern const char *git_editor(void);
 extern const char *git_pager(int stdout_is_tty);
 extern int git_ident_config(const char *, const char *, void *);
+extern void reset_ident_date(void);
 
 struct ident_split {
        const char *name_begin;
@@ -1377,6 +1379,13 @@ extern struct packed_git {
        char pack_name[FLEX_ARRAY]; /* more */
 } *packed_git;
 
+/*
+ * A most-recently-used ordered version of the packed_git list, which can
+ * be iterated instead of packed_git (and marked via mru_mark).
+ */
+struct mru;
+extern struct mru *packed_git_mru;
+
 struct pack_entry {
        off_t offset;
        unsigned char sha1[20];
@@ -1416,7 +1425,6 @@ extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t
 extern void close_pack_windows(struct packed_git *);
 extern void close_all_packs(void);
 extern void unuse_pack(struct pack_window **);
-extern void free_pack_by_name(const char *);
 extern void clear_delta_base_cache(void);
 extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
 
@@ -1515,7 +1523,7 @@ struct object_info {
        /* Request */
        enum object_type *typep;
        unsigned long *sizep;
-       unsigned long *disk_sizep;
+       off_t *disk_sizep;
        unsigned char *delta_base_sha1;
        struct strbuf *typename;
 
@@ -1566,10 +1574,18 @@ struct git_config_source {
        const char *blob;
 };
 
+enum config_origin_type {
+       CONFIG_ORIGIN_BLOB,
+       CONFIG_ORIGIN_FILE,
+       CONFIG_ORIGIN_STDIN,
+       CONFIG_ORIGIN_SUBMODULE_BLOB,
+       CONFIG_ORIGIN_CMDLINE
+};
+
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
-extern int git_config_from_mem(config_fn_t fn, const char *origin_type,
+extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
                                        const char *name, const char *buf, size_t len, void *data);
 extern void git_config_push_parameter(const char *text);
 extern int git_config_from_parameters(config_fn_t fn, void *data);
@@ -1713,7 +1729,7 @@ extern int ignore_untracked_cache_config;
 struct key_value_info {
        const char *filename;
        int linenr;
-       const char *origin_type;
+       enum config_origin_type origin_type;
        enum config_scope scope;
 };
 
index f84b449413d434b8145426503a98bcd4f9ad995f..006a50b5481b552278931f5aec33883d79891a80 100644 (file)
@@ -126,16 +126,16 @@ static MAYBE_UNUSED elemtype *slabname## _peek(struct slabname *s,        \
        return slabname##_at_peek(s, c, 0);                             \
 }                                                                      \
                                                                        \
-static int stat_ ##slabname## realloc
+struct slabname
 
 /*
- * Note that this seemingly redundant second declaration is required
+ * Note that this redundant forward declaration is required
  * to allow a terminating semicolon, which makes instantiations look
  * like function declarations.  I.e., the expansion of
  *
  *    define_commit_slab(indegree, int);
  *
- * ends in 'static int stat_indegreerealloc;'.  This would otherwise
+ * ends in 'struct indegree;'.  This would otherwise
  * be a syntax error according (at least) to ISO C.  It's hard to
  * catch because GCC silently parses it by default.
  */
index 233933ee86b2d069c7fca5b0dc39ea6c6badb20b..95e128fcfd45e98c091256265c7d9bda95ec733f 100644 (file)
@@ -73,6 +73,9 @@ typedef int pid_t;
 #ifndef ECONNABORTED
 #define ECONNABORTED WSAECONNABORTED
 #endif
+#ifndef ENOTSOCK
+#define ENOTSOCK WSAENOTSOCK
+#endif
 
 struct passwd {
        char *pw_name;
index a0a16eb1bbfb22c13f29c15d2fd68ccc9c7e01ad..2d4ef59013823d3c7caac8b705fbd4916cfe8fd6 100644 (file)
@@ -938,10 +938,10 @@ void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **
        void **ret;
        threadcache *tc;
        int mymspace;
-    size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
-    if(!adjustedsizes) return 0;
-    for(i=0; i<elems; i++)
-       adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
+       size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
+       if(!adjustedsizes) return 0;
+       for(i=0; i<elems; i++)
+               adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
        GetThreadCache(&p, &tc, &mymspace, 0);
        GETMSPACE(m, p, tc, mymspace, 0,
              ret=mspace_independent_comalloc(m, elems, adjustedsizes, chunks));
@@ -955,12 +955,11 @@ void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **
  */
 char *strdup(const char *s1)
 {
-       char *s2 = 0;
-       if (s1) {
-               size_t len = strlen(s1) + 1;
-               s2 = malloc(len);
+       size_t len = strlen(s1) + 1;
+       char *s2 = malloc(len);
+
+       if (s2)
                memcpy(s2, s1, len);
-       }
        return s2;
 }
 #endif
index bea937e4ecf8417a910882c1aafb263e792f2e1c..584cacf148336b90d9a5d87949b7594021ced42b 100644 (file)
--- a/config.c
+++ b/config.c
@@ -24,7 +24,7 @@ struct config_source {
                        size_t pos;
                } buf;
        } u;
-       const char *origin_type;
+       enum config_origin_type origin_type;
        const char *name;
        const char *path;
        int die_on_error;
@@ -245,6 +245,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
 
        memset(&source, 0, sizeof(source));
        source.prev = cf;
+       source.origin_type = CONFIG_ORIGIN_CMDLINE;
        cf = &source;
 
        /* sq_dequote will write over it */
@@ -453,6 +454,8 @@ static int git_parse_source(config_fn_t fn, void *data)
        int comment = 0;
        int baselen = 0;
        struct strbuf *var = &cf->var;
+       int error_return = 0;
+       char *error_msg = NULL;
 
        /* U+FEFF Byte Order Mark in UTF8 */
        const char *bomptr = utf8_bom;
@@ -507,10 +510,40 @@ static int git_parse_source(config_fn_t fn, void *data)
                if (get_value(fn, data, var) < 0)
                        break;
        }
+
+       switch (cf->origin_type) {
+       case CONFIG_ORIGIN_BLOB:
+               error_msg = xstrfmt(_("bad config line %d in blob %s"),
+                                     cf->linenr, cf->name);
+               break;
+       case CONFIG_ORIGIN_FILE:
+               error_msg = xstrfmt(_("bad config line %d in file %s"),
+                                     cf->linenr, cf->name);
+               break;
+       case CONFIG_ORIGIN_STDIN:
+               error_msg = xstrfmt(_("bad config line %d in standard input"),
+                                     cf->linenr);
+               break;
+       case CONFIG_ORIGIN_SUBMODULE_BLOB:
+               error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"),
+                                      cf->linenr, cf->name);
+               break;
+       case CONFIG_ORIGIN_CMDLINE:
+               error_msg = xstrfmt(_("bad config line %d in command line %s"),
+                                      cf->linenr, cf->name);
+               break;
+       default:
+               error_msg = xstrfmt(_("bad config line %d in %s"),
+                                     cf->linenr, cf->name);
+       }
+
        if (cf->die_on_error)
-               die(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
+               die("%s", error_msg);
        else
-               return error(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
+               error_return = error("%s", error_msg);
+
+       free(error_msg);
+       return error_return;
 }
 
 static int parse_unit_factor(const char *end, uintmax_t *val)
@@ -619,16 +652,47 @@ int git_parse_ulong(const char *value, unsigned long *ret)
 NORETURN
 static void die_bad_number(const char *name, const char *value)
 {
-       const char *reason = errno == ERANGE ?
-                            "out of range" :
-                            "invalid unit";
        if (!value)
                value = "";
 
-       if (cf && cf->origin_type && cf->name)
-               die(_("bad numeric config value '%s' for '%s' in %s %s: %s"),
-                   value, name, cf->origin_type, cf->name, reason);
-       die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason);
+       if (!(cf && cf->name))
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s': out of range")
+                   : _("bad numeric config value '%s' for '%s': invalid unit"),
+                   value, name);
+
+       switch (cf->origin_type) {
+       case CONFIG_ORIGIN_BLOB:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in blob %s: out of range")
+                   : _("bad numeric config value '%s' for '%s' in blob %s: invalid unit"),
+                   value, name, cf->name);
+       case CONFIG_ORIGIN_FILE:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in file %s: out of range")
+                   : _("bad numeric config value '%s' for '%s' in file %s: invalid unit"),
+                   value, name, cf->name);
+       case CONFIG_ORIGIN_STDIN:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in standard input: out of range")
+                   : _("bad numeric config value '%s' for '%s' in standard input: invalid unit"),
+                   value, name);
+       case CONFIG_ORIGIN_SUBMODULE_BLOB:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in submodule-blob %s: out of range")
+                   : _("bad numeric config value '%s' for '%s' in submodule-blob %s: invalid unit"),
+                   value, name, cf->name);
+       case CONFIG_ORIGIN_CMDLINE:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in command line %s: out of range")
+                   : _("bad numeric config value '%s' for '%s' in command line %s: invalid unit"),
+                   value, name, cf->name);
+       default:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in %s: out of range")
+                   : _("bad numeric config value '%s' for '%s' in %s: invalid unit"),
+                   value, name, cf->name);
+       }
 }
 
 int git_config_int(const char *name, const char *value)
@@ -1105,7 +1169,8 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
 }
 
 static int do_config_from_file(config_fn_t fn,
-               const char *origin_type, const char *name, const char *path, FILE *f,
+               const enum config_origin_type origin_type,
+               const char *name, const char *path, FILE *f,
                void *data)
 {
        struct config_source top;
@@ -1124,7 +1189,7 @@ static int do_config_from_file(config_fn_t fn,
 
 static int git_config_from_stdin(config_fn_t fn, void *data)
 {
-       return do_config_from_file(fn, "standard input", "", NULL, stdin, data);
+       return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, data);
 }
 
 int git_config_from_file(config_fn_t fn, const char *filename, void *data)
@@ -1135,14 +1200,14 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
        f = fopen(filename, "r");
        if (f) {
                flockfile(f);
-               ret = do_config_from_file(fn, "file", filename, filename, f, data);
+               ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data);
                funlockfile(f);
                fclose(f);
        }
        return ret;
 }
 
-int git_config_from_mem(config_fn_t fn, const char *origin_type,
+int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type,
                        const char *name, const char *buf, size_t len, void *data)
 {
        struct config_source top;
@@ -1179,7 +1244,7 @@ static int git_config_from_blob_sha1(config_fn_t fn,
                return error("reference '%s' does not point to a blob", name);
        }
 
-       ret = git_config_from_mem(fn, "blob", name, buf, size, data);
+       ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size, data);
        free(buf);
 
        return ret;
@@ -1390,12 +1455,12 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
        if (cf->name) {
                kv_info->filename = strintern(cf->name);
                kv_info->linenr = cf->linenr;
-               kv_info->origin_type = strintern(cf->origin_type);
+               kv_info->origin_type = cf->origin_type;
        } else {
                /* for values read from `git_config_from_parameters()` */
                kv_info->filename = NULL;
                kv_info->linenr = -1;
-               kv_info->origin_type = NULL;
+               kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
        }
        kv_info->scope = current_parsing_scope;
        si->util = kv_info;
@@ -2476,14 +2541,28 @@ int parse_config_key(const char *var,
 
 const char *current_config_origin_type(void)
 {
-       const char *type;
+       int type;
        if (current_config_kvi)
                type = current_config_kvi->origin_type;
        else if(cf)
                type = cf->origin_type;
        else
                die("BUG: current_config_origin_type called outside config callback");
-       return type ? type : "command line";
+
+       switch (type) {
+       case CONFIG_ORIGIN_BLOB:
+               return "blob";
+       case CONFIG_ORIGIN_FILE:
+               return "file";
+       case CONFIG_ORIGIN_STDIN:
+               return "standard input";
+       case CONFIG_ORIGIN_SUBMODULE_BLOB:
+               return "submodule-blob";
+       case CONFIG_ORIGIN_CMDLINE:
+               return "command line";
+       default:
+               die("BUG: unknown config origin type");
+       }
 }
 
 const char *current_config_name(void)
index 22958a8d6ffbf3ca937b5aac97bb9f616de70130..b232908f8c8c2eae84bd6ef8ab2a96ac45bf94a3 100644 (file)
@@ -205,9 +205,11 @@ ifeq ($(uname_S),FreeBSD)
                NO_STRTOUMAX = YesPlease
        endif
        PYTHON_PATH = /usr/local/bin/python
+       PERL_PATH = /usr/local/bin/perl
        HAVE_PATHS_H = YesPlease
        GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes
        HAVE_BSD_SYSCTL = YesPlease
+       PAGER_ENV = LESS=FRX LV=-c MORE=FRX
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
index bf1b12e7ecaf476304811cabc870d58fc7f3f1dd..8e3e4b1dc1271f0530d10469dc5ef4eeb46526ea 100644 (file)
@@ -4,10 +4,6 @@
 #include "connected.h"
 #include "transport.h"
 
-int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
-{
-       return check_everything_connected_with_transport(fn, quiet, cb_data, NULL);
-}
 /*
  * If we feed all the commits we want to verify to this command
  *
@@ -19,22 +15,27 @@ int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
  *
  * Returns 0 if everything is connected, non-zero otherwise.
  */
-static int check_everything_connected_real(sha1_iterate_fn fn,
-                                          int quiet,
-                                          void *cb_data,
-                                          struct transport *transport,
-                                          const char *shallow_file)
+int check_connected(sha1_iterate_fn fn, void *cb_data,
+                   struct check_connected_options *opt)
 {
        struct child_process rev_list = CHILD_PROCESS_INIT;
-       const char *argv[9];
+       struct check_connected_options defaults = CHECK_CONNECTED_INIT;
        char commit[41];
        unsigned char sha1[20];
-       int err = 0, ac = 0;
+       int err = 0;
        struct packed_git *new_pack = NULL;
+       struct transport *transport;
        size_t base_len;
 
-       if (fn(cb_data, sha1))
+       if (!opt)
+               opt = &defaults;
+       transport = opt->transport;
+
+       if (fn(cb_data, sha1)) {
+               if (opt->err_fd)
+                       close(opt->err_fd);
                return err;
+       }
 
        if (transport && transport->smart_options &&
            transport->smart_options->self_contained_and_connected &&
@@ -47,24 +48,28 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
                strbuf_release(&idx_file);
        }
 
-       if (shallow_file) {
-               argv[ac++] = "--shallow-file";
-               argv[ac++] = shallow_file;
+       if (opt->shallow_file) {
+               argv_array_push(&rev_list.args, "--shallow-file");
+               argv_array_push(&rev_list.args, opt->shallow_file);
        }
-       argv[ac++] = "rev-list";
-       argv[ac++] = "--objects";
-       argv[ac++] = "--stdin";
-       argv[ac++] = "--not";
-       argv[ac++] = "--all";
-       if (quiet)
-               argv[ac++] = "--quiet";
-       argv[ac] = NULL;
+       argv_array_push(&rev_list.args,"rev-list");
+       argv_array_push(&rev_list.args, "--objects");
+       argv_array_push(&rev_list.args, "--stdin");
+       argv_array_push(&rev_list.args, "--not");
+       argv_array_push(&rev_list.args, "--all");
+       argv_array_push(&rev_list.args, "--quiet");
+       if (opt->progress)
+               argv_array_pushf(&rev_list.args, "--progress=%s",
+                                _("Checking connectivity"));
 
-       rev_list.argv = argv;
        rev_list.git_cmd = 1;
        rev_list.in = -1;
        rev_list.no_stdout = 1;
-       rev_list.no_stderr = quiet;
+       if (opt->err_fd)
+               rev_list.err = opt->err_fd;
+       else
+               rev_list.no_stderr = opt->quiet;
+
        if (start_command(&rev_list))
                return error(_("Could not run 'git rev-list'"));
 
@@ -98,19 +103,3 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
        sigchain_pop(SIGPIPE);
        return finish_command(&rev_list) || err;
 }
-
-int check_everything_connected_with_transport(sha1_iterate_fn fn,
-                                             int quiet,
-                                             void *cb_data,
-                                             struct transport *transport)
-{
-       return check_everything_connected_real(fn, quiet, cb_data,
-                                              transport, NULL);
-}
-
-int check_shallow_connected(sha1_iterate_fn fn, int quiet, void *cb_data,
-                           const char *shallow_file)
-{
-       return check_everything_connected_real(fn, quiet, cb_data,
-                                              NULL, shallow_file);
-}
index 071d408f387b2afdbb642920054a7735c982200b..afa48cc0524764a65053f5bb1392142de3251609 100644 (file)
@@ -10,18 +10,43 @@ struct transport;
  */
 typedef int (*sha1_iterate_fn)(void *, unsigned char [20]);
 
+/*
+ * Named-arguments struct for check_connected. All arguments are
+ * optional, and can be left to defaults as set by CHECK_CONNECTED_INIT.
+ */
+struct check_connected_options {
+       /* Avoid printing any errors to stderr. */
+       int quiet;
+
+       /* --shallow-file to pass to rev-list sub-process */
+       const char *shallow_file;
+
+       /* Transport whose objects we are checking, if available. */
+       struct transport *transport;
+
+       /*
+        * If non-zero, send error messages to this descriptor rather
+        * than stderr. The descriptor is closed before check_connected
+        * returns.
+        */
+       int err_fd;
+
+       /* If non-zero, show progress as we traverse the objects. */
+       int progress;
+};
+
+#define CHECK_CONNECTED_INIT { 0 }
+
 /*
  * Make sure that our object store has all the commits necessary to
  * connect the ancestry chain to some of our existing refs, and all
  * the trees and blobs that these commits use.
  *
  * Return 0 if Ok, non zero otherwise (i.e. some missing objects)
+ *
+ * If "opt" is NULL, behaves as if CHECK_CONNECTED_INIT was passed.
  */
-extern int check_everything_connected(sha1_iterate_fn, int quiet, void *cb_data);
-extern int check_shallow_connected(sha1_iterate_fn, int quiet, void *cb_data,
-                                  const char *shallow_file);
-extern int check_everything_connected_with_transport(sha1_iterate_fn, int quiet,
-                                                    void *cb_data,
-                                                    struct transport *transport);
+int check_connected(sha1_iterate_fn fn, void *cb_data,
+                   struct check_connected_options *opt);
 
 #endif /* CONNECTED_H */
index 37888f4e570e7f268bdc3e735c63f7a136774aa3..c1b2135544bba888ccdeadf3075d86b76571f688 100644 (file)
@@ -1136,6 +1136,7 @@ _git_clone ()
                        --depth
                        --single-branch
                        --branch
+                       --recurse-submodules
                        "
                return
                ;;
@@ -1204,6 +1205,8 @@ _git_describe ()
 
 __git_diff_algorithms="myers minimal patience histogram"
 
+__git_diff_submodule_formats="log short"
+
 __git_diff_common_options="--stat --numstat --shortstat --summary
                        --patch-with-stat --name-only --name-status --color
                        --no-color --color-words --no-renames --check
@@ -1219,6 +1222,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
                        --dirstat --dirstat= --dirstat-by-file
                        --dirstat-by-file= --cumulative
                        --diff-algorithm=
+                       --submodule --submodule=
 "
 
 _git_diff ()
@@ -1230,6 +1234,10 @@ _git_diff ()
                __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
                return
                ;;
+       --submodule=*)
+               __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
+               return
+               ;;
        --*)
                __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
                        --base --ours --theirs --no-index
@@ -1493,6 +1501,14 @@ _git_log ()
                __gitcomp "full short no" "" "${cur##--decorate=}"
                return
                ;;
+       --diff-algorithm=*)
+               __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
+               return
+               ;;
+       --submodule=*)
+               __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
+               return
+               ;;
        --*)
                __gitcomp "
                        $__git_log_common_options
@@ -2181,6 +2197,7 @@ _git_config ()
                format.attach
                format.cc
                format.coverLetter
+               format.from
                format.headers
                format.numbered
                format.pretty
@@ -2455,6 +2472,10 @@ _git_show ()
                __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
                return
                ;;
+       --submodule=*)
+               __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
+               return
+               ;;
        --*)
                __gitcomp "--pretty= --format= --abbrev-commit --oneline
                        --show-signature
@@ -2693,7 +2714,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-       local subcommands="add list prune"
+       local subcommands="add list lock prune unlock"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
@@ -2705,6 +2726,9 @@ _git_worktree ()
                list,--*)
                        __gitcomp "--porcelain"
                        ;;
+               lock,--*)
+                       __gitcomp "--reason"
+                       ;;
                prune,--*)
                        __gitcomp "--dry-run --expire --verbose"
                        ;;
index 1cebc328cbfa1daffd1e0ce9f4fe1807140340d5..225e3f09547d07563a8da34b21677dce514659fa 100644 (file)
@@ -29,7 +29,7 @@ Obviously this trivial case isn't that interesting; you could just open
 `foo.c` yourself. But when you have many changes scattered across a
 project, you can use the editor's support to "jump" from point to point.
 
-Git-jump can generate three types of interesting lists:
+Git-jump can generate four types of interesting lists:
 
   1. The beginning of any diff hunks.
 
@@ -37,6 +37,8 @@ Git-jump can generate three types of interesting lists:
 
   3. Any grep matches.
 
+  4. Any whitespace errors detected by `git diff --check`.
+
 
 Using git-jump
 --------------
@@ -83,7 +85,7 @@ complete list of files and line numbers for each match.
 Limitations
 -----------
 
-This scripts was written and tested with vim. Given that the quickfix
+This script was written and tested with vim. Given that the quickfix
 format is the same as what gcc produces, I expect emacs users have a
 similar feature for iterating through the list, but I know nothing about
 how to activate it.
index dc90cd6379de4e9bf1eadc69560867601227b55d..427f206a45b326f2ee80af0fae7080d5f794c14a 100755 (executable)
@@ -12,6 +12,8 @@ diff: elements are diff hunks. Arguments are given to diff.
 merge: elements are merge conflicts. Arguments are ignored.
 
 grep: elements are grep hits. Arguments are given to grep.
+
+ws: elements are whitespace errors. Arguments are given to diff --check.
 EOF
 }
 
@@ -25,7 +27,7 @@ mode_diff() {
        perl -ne '
        if (m{^\+\+\+ (.*)}) { $file = $1; next }
        defined($file) or next;
-       if (m/^@@ .*\+(\d+)/) { $line = $1; next }
+       if (m/^@@ .*?\+(\d+)/) { $line = $1; next }
        defined($line) or next;
        if (/^ /) { $line++; next }
        if (/^[-+]\s*(.*)/) {
@@ -55,6 +57,10 @@ mode_grep() {
        '
 }
 
+mode_ws() {
+       git diff --check "$@"
+}
+
 if test $# -lt 1; then
        usage >&2
        exit 1
index 92baa3beeeaf924e06ceb6d8afb9e14e60f322e6..52b84ba3d4396e13c3aae3af4fe78aac5ddd55ca 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-BUILD_LABEL=$(shell date +"%s")
+BUILD_LABEL=$(shell cut -d" " -f3 ../../GIT-VERSION-FILE)
 TAR_OUT=$(shell go env GOOS)_$(shell go env GOARCH).tar.gz
 
 all: git-remote-persistent-https git-remote-persistent-https--proxy \
@@ -25,8 +25,10 @@ git-remote-persistent-http: git-remote-persistent-https
        ln -f -s git-remote-persistent-https git-remote-persistent-http
 
 git-remote-persistent-https:
+       case $$(go version) in \
+       "go version go"1.[0-5].*) EQ=" " ;; *) EQ="=" ;; esac && \
        go build -o git-remote-persistent-https \
-               -ldflags "-X main._BUILD_EMBED_LABEL $(BUILD_LABEL)"
+               -ldflags "-X main._BUILD_EMBED_LABEL$${EQ}$(BUILD_LABEL)"
 
 clean:
        rm -f git-remote-persistent-http* *.tar.gz
index 7a39b30ad09483ef9796d98ca78e52f652046582..dec085a235f4477c9079dc643c76db166f487761 100755 (executable)
@@ -4,8 +4,9 @@
 #
 # Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com>
 #
-if [ $# -eq 0 ]; then
-    set -- -h
+if test $# -eq 0
+then
+       set -- -h
 fi
 OPTS_SPEC="\
 git subtree add   --prefix=<prefix> <commit>
@@ -48,89 +49,144 @@ squash=
 message=
 prefix=
 
-debug()
-{
-       if [ -n "$debug" ]; then
+debug () {
+       if test -n "$debug"
+       then
                printf "%s\n" "$*" >&2
        fi
 }
 
-say()
-{
-       if [ -z "$quiet" ]; then
+say () {
+       if test -z "$quiet"
+       then
                printf "%s\n" "$*" >&2
        fi
 }
 
-progress()
-{
-       if [ -z "$quiet" ]; then
+progress () {
+       if test -z "$quiet"
+       then
                printf "%s\r" "$*" >&2
        fi
 }
 
-assert()
-{
-       if "$@"; then
-               :
-       else
+assert () {
+       if ! "$@"
+       then
                die "assertion failed: " "$@"
        fi
 }
 
 
-#echo "Options: $*"
-
-while [ $# -gt 0 ]; do
+while test $# -gt 0
+do
        opt="$1"
        shift
+
        case "$opt" in
-               -q) quiet=1 ;;
-               -d) debug=1 ;;
-               --annotate) annotate="$1"; shift ;;
-               --no-annotate) annotate= ;;
-               -b) branch="$1"; shift ;;
-               -P) prefix="${1%/}"; shift ;;
-               -m) message="$1"; shift ;;
-               --no-prefix) prefix= ;;
-               --onto) onto="$1"; shift ;;
-               --no-onto) onto= ;;
-               --rejoin) rejoin=1 ;;
-               --no-rejoin) rejoin= ;;
-               --ignore-joins) ignore_joins=1 ;;
-               --no-ignore-joins) ignore_joins= ;;
-               --squash) squash=1 ;;
-               --no-squash) squash= ;;
-               --) break ;;
-               *) die "Unexpected option: $opt" ;;
+       -q)
+               quiet=1
+               ;;
+       -d)
+               debug=1
+               ;;
+       --annotate)
+               annotate="$1"
+               shift
+               ;;
+       --no-annotate)
+               annotate=
+               ;;
+       -b)
+               branch="$1"
+               shift
+               ;;
+       -P)
+               prefix="${1%/}"
+               shift
+               ;;
+       -m)
+               message="$1"
+               shift
+               ;;
+       --no-prefix)
+               prefix=
+               ;;
+       --onto)
+               onto="$1"
+               shift
+               ;;
+       --no-onto)
+               onto=
+               ;;
+       --rejoin)
+               rejoin=1
+               ;;
+       --no-rejoin)
+               rejoin=
+               ;;
+       --ignore-joins)
+               ignore_joins=1
+               ;;
+       --no-ignore-joins)
+               ignore_joins=
+               ;;
+       --squash)
+               squash=1
+               ;;
+       --no-squash)
+               squash=
+               ;;
+       --)
+               break
+               ;;
+       *)
+               die "Unexpected option: $opt"
+               ;;
        esac
 done
 
 command="$1"
 shift
+
 case "$command" in
-       add|merge|pull) default= ;;
-       split|push) default="--default HEAD" ;;
-       *) die "Unknown command '$command'" ;;
+add|merge|pull)
+       default=
+       ;;
+split|push)
+       default="--default HEAD"
+       ;;
+*)
+       die "Unknown command '$command'"
+       ;;
 esac
 
-if [ -z "$prefix" ]; then
+if test -z "$prefix"
+then
        die "You must provide the --prefix option."
 fi
 
 case "$command" in
-       add) [ -e "$prefix" ] && 
-               die "prefix '$prefix' already exists." ;;
-       *)   [ -e "$prefix" ] || 
-               die "'$prefix' does not exist; use 'git subtree add'" ;;
+add)
+       test -e "$prefix" &&
+               die "prefix '$prefix' already exists."
+       ;;
+*)
+       test -e "$prefix" ||
+               die "'$prefix' does not exist; use 'git subtree add'"
+       ;;
 esac
 
 dir="$(dirname "$prefix/.")"
 
-if [ "$command" != "pull" -a "$command" != "add" -a "$command" != "push" ]; then
+if test "$command" != "pull" &&
+               test "$command" != "add" &&
+               test "$command" != "push"
+then
        revs=$(git rev-parse $default --revs-only "$@") || exit $?
-       dirs="$(git rev-parse --no-revs --no-flags "$@")" || exit $?
-       if [ -n "$dirs" ]; then
+       dirs=$(git rev-parse --no-revs --no-flags "$@") || exit $?
+       if test -n "$dirs"
+       then
                die "Error: Use --prefix instead of bare filenames."
        fi
 fi
@@ -142,78 +198,82 @@ debug "dir: {$dir}"
 debug "opts: {$*}"
 debug
 
-cache_setup()
-{
+cache_setup () {
        cachedir="$GIT_DIR/subtree-cache/$$"
-       rm -rf "$cachedir" || die "Can't delete old cachedir: $cachedir"
-       mkdir -p "$cachedir" || die "Can't create new cachedir: $cachedir"
-       mkdir -p "$cachedir/notree" || die "Can't create new cachedir: $cachedir/notree"
+       rm -rf "$cachedir" ||
+               die "Can't delete old cachedir: $cachedir"
+       mkdir -p "$cachedir" ||
+               die "Can't create new cachedir: $cachedir"
+       mkdir -p "$cachedir/notree" ||
+               die "Can't create new cachedir: $cachedir/notree"
        debug "Using cachedir: $cachedir" >&2
 }
 
-cache_get()
-{
-       for oldrev in $*; do
-               if [ -r "$cachedir/$oldrev" ]; then
+cache_get () {
+       for oldrev in "$@"
+       do
+               if test -r "$cachedir/$oldrev"
+               then
                        read newrev <"$cachedir/$oldrev"
                        echo $newrev
                fi
        done
 }
 
-cache_miss()
-{
-       for oldrev in $*; do
-               if [ ! -r "$cachedir/$oldrev" ]; then
+cache_miss () {
+       for oldrev in "$@"
+       do
+               if ! test -r "$cachedir/$oldrev"
+               then
                        echo $oldrev
                fi
        done
 }
 
-check_parents()
-{
-       missed=$(cache_miss $*)
-       for miss in $missed; do
-               if [ ! -r "$cachedir/notree/$miss" ]; then
+check_parents () {
+       missed=$(cache_miss "$@")
+       for miss in $missed
+       do
+               if ! test -r "$cachedir/notree/$miss"
+               then
                        debug "  incorrect order: $miss"
                fi
        done
 }
 
-set_notree()
-{
+set_notree () {
        echo "1" > "$cachedir/notree/$1"
 }
 
-cache_set()
-{
+cache_set () {
        oldrev="$1"
        newrev="$2"
-       if [ "$oldrev" != "latest_old" \
-            -a "$oldrev" != "latest_new" \
-            -a -e "$cachedir/$oldrev" ]; then
+       if test "$oldrev" != "latest_old" &&
+               test "$oldrev" != "latest_new" &&
+               test -e "$cachedir/$oldrev"
+       then
                die "cache for $oldrev already exists!"
        fi
        echo "$newrev" >"$cachedir/$oldrev"
 }
 
-rev_exists()
-{
-       if git rev-parse "$1" >/dev/null 2>&1; then
+rev_exists () {
+       if git rev-parse "$1" >/dev/null 2>&1
+       then
                return 0
        else
                return 1
        fi
 }
 
-rev_is_descendant_of_branch()
-{
+rev_is_descendant_of_branch () {
        newrev="$1"
        branch="$2"
-       branch_hash=$(git rev-parse $branch)
-       match=$(git rev-list -1 $branch_hash ^$newrev)
+       branch_hash=$(git rev-parse "$branch")
+       match=$(git rev-list -1 "$branch_hash" "^$newrev")
 
-       if [ -z "$match" ]; then
+       if test -z "$match"
+       then
                return 0
        else
                return 1
@@ -223,15 +283,14 @@ rev_is_descendant_of_branch()
 # if a commit doesn't have a parent, this might not work.  But we only want
 # to remove the parent from the rev-list, and since it doesn't exist, it won't
 # be there anyway, so do nothing in that case.
-try_remove_previous()
-{
-       if rev_exists "$1^"; then
+try_remove_previous () {
+       if rev_exists "$1^"
+       then
                echo "^$1^"
        fi
 }
 
-find_latest_squash()
-{
+find_latest_squash () {
        debug "Looking for latest squash ($dir)..."
        dir="$1"
        sq=
@@ -239,37 +298,43 @@ find_latest_squash()
        sub=
        git log --grep="^git-subtree-dir: $dir/*\$" \
                --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD |
-       while read a b junk; do
+       while read a b junk
+       do
                debug "$a $b $junk"
                debug "{{$sq/$main/$sub}}"
                case "$a" in
-                       START) sq="$b" ;;
-                       git-subtree-mainline:) main="$b" ;;
-                       git-subtree-split:)
-                               sub="$(git rev-parse "$b^0")" ||
-                                   die "could not rev-parse split hash $b from commit $sq"
-                               ;;
-                       END)
-                               if [ -n "$sub" ]; then
-                                       if [ -n "$main" ]; then
-                                               # a rejoin commit?
-                                               # Pretend its sub was a squash.
-                                               sq="$sub"
-                                       fi
-                                       debug "Squash found: $sq $sub"
-                                       echo "$sq" "$sub"
-                                       break
+               START)
+                       sq="$b"
+                       ;;
+               git-subtree-mainline:)
+                       main="$b"
+                       ;;
+               git-subtree-split:)
+                       sub="$(git rev-parse "$b^0")" ||
+                       die "could not rev-parse split hash $b from commit $sq"
+                       ;;
+               END)
+                       if test -n "$sub"
+                       then
+                               if test -n "$main"
+                               then
+                                       # a rejoin commit?
+                                       # Pretend its sub was a squash.
+                                       sq="$sub"
                                fi
-                               sq=
-                               main=
-                               sub=
-                               ;;
+                               debug "Squash found: $sq $sub"
+                               echo "$sq" "$sub"
+                               break
+                       fi
+                       sq=
+                       main=
+                       sub=
+                       ;;
                esac
        done
 }
 
-find_existing_splits()
-{
+find_existing_splits () {
        debug "Looking for prior splits..."
        dir="$1"
        revs="$2"
@@ -277,37 +342,43 @@ find_existing_splits()
        sub=
        git log --grep="^git-subtree-dir: $dir/*\$" \
                --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
-       while read a b junk; do
+       while read a b junk
+       do
                case "$a" in
-                       START) sq="$b" ;;
-                       git-subtree-mainline:) main="$b" ;;
-                       git-subtree-split:)
-                               sub="$(git rev-parse "$b^0")" ||
-                                   die "could not rev-parse split hash $b from commit $sq"
-                               ;;
-                       END)
-                               debug "  Main is: '$main'"
-                               if [ -z "$main" -a -n "$sub" ]; then
-                                       # squash commits refer to a subtree
-                                       debug "  Squash: $sq from $sub"
-                                       cache_set "$sq" "$sub"
-                               fi
-                               if [ -n "$main" -a -n "$sub" ]; then
-                                       debug "  Prior: $main -> $sub"
-                                       cache_set $main $sub
-                                       cache_set $sub $sub
-                                       try_remove_previous "$main"
-                                       try_remove_previous "$sub"
-                               fi
-                               main=
-                               sub=
-                               ;;
+               START)
+                       sq="$b"
+                       ;;
+               git-subtree-mainline:)
+                       main="$b"
+                       ;;
+               git-subtree-split:)
+                       sub="$(git rev-parse "$b^0")" ||
+                       die "could not rev-parse split hash $b from commit $sq"
+                       ;;
+               END)
+                       debug "  Main is: '$main'"
+                       if test -z "$main" -a -n "$sub"
+                       then
+                               # squash commits refer to a subtree
+                               debug "  Squash: $sq from $sub"
+                               cache_set "$sq" "$sub"
+                       fi
+                       if test -n "$main" -a -n "$sub"
+                       then
+                               debug "  Prior: $main -> $sub"
+                               cache_set $main $sub
+                               cache_set $sub $sub
+                               try_remove_previous "$main"
+                               try_remove_previous "$sub"
+                       fi
+                       main=
+                       sub=
+                       ;;
                esac
        done
 }
 
-copy_commit()
-{
+copy_commit () {
        # We're going to set some environment vars here, so
        # do it in a subshell to get rid of them safely later
        debug copy_commit "{$1}" "{$2}" "{$3}"
@@ -325,66 +396,69 @@ copy_commit()
                        GIT_COMMITTER_NAME \
                        GIT_COMMITTER_EMAIL \
                        GIT_COMMITTER_DATE
-               (printf "%s" "$annotate"; cat ) |
+               (
+                       printf "%s" "$annotate"
+                       cat
+               ) |
                git commit-tree "$2" $3  # reads the rest of stdin
        ) || die "Can't copy commit $1"
 }
 
-add_msg()
-{
+add_msg () {
        dir="$1"
        latest_old="$2"
        latest_new="$3"
-       if [ -n "$message" ]; then
+       if test -n "$message"
+       then
                commit_message="$message"
        else
                commit_message="Add '$dir/' from commit '$latest_new'"
        fi
        cat <<-EOF
                $commit_message
-               
+
                git-subtree-dir: $dir
                git-subtree-mainline: $latest_old
                git-subtree-split: $latest_new
        EOF
 }
 
-add_squashed_msg()
-{
-       if [ -n "$message" ]; then
+add_squashed_msg () {
+       if test -n "$message"
+       then
                echo "$message"
        else
                echo "Merge commit '$1' as '$2'"
        fi
 }
 
-rejoin_msg()
-{
+rejoin_msg () {
        dir="$1"
        latest_old="$2"
        latest_new="$3"
-       if [ -n "$message" ]; then
+       if test -n "$message"
+       then
                commit_message="$message"
        else
                commit_message="Split '$dir/' into commit '$latest_new'"
        fi
        cat <<-EOF
                $commit_message
-               
+
                git-subtree-dir: $dir
                git-subtree-mainline: $latest_old
                git-subtree-split: $latest_new
        EOF
 }
 
-squash_msg()
-{
+squash_msg () {
        dir="$1"
        oldsub="$2"
        newsub="$3"
        newsub_short=$(git rev-parse --short "$newsub")
-       
-       if [ -n "$oldsub" ]; then
+
+       if test -n "$oldsub"
+       then
                oldsub_short=$(git rev-parse --short "$oldsub")
                echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short"
                echo
@@ -393,41 +467,41 @@ squash_msg()
        else
                echo "Squashed '$dir/' content from commit $newsub_short"
        fi
-       
+
        echo
        echo "git-subtree-dir: $dir"
        echo "git-subtree-split: $newsub"
 }
 
-toptree_for_commit()
-{
+toptree_for_commit () {
        commit="$1"
        git log -1 --pretty=format:'%T' "$commit" -- || exit $?
 }
 
-subtree_for_commit()
-{
+subtree_for_commit () {
        commit="$1"
        dir="$2"
        git ls-tree "$commit" -- "$dir" |
-       while read mode type tree name; do
-               assert [ "$name" = "$dir" ]
-               assert [ "$type" = "tree" -o "$type" = "commit" ]
-               [ "$type" = "commit" ] && continue  # ignore submodules
+       while read mode type tree name
+       do
+               assert test "$name" = "$dir"
+               assert test "$type" = "tree" -o "$type" = "commit"
+               test "$type" = "commit" && continue  # ignore submodules
                echo $tree
                break
        done
 }
 
-tree_changed()
-{
+tree_changed () {
        tree=$1
        shift
-       if [ $# -ne 1 ]; then
+       if test $# -ne 1
+       then
                return 0   # weird parents, consider it changed
        else
                ptree=$(toptree_for_commit $1)
-               if [ "$ptree" != "$tree" ]; then
+               if test "$ptree" != "$tree"
+               then
                        return 0   # changed
                else
                        return 1   # not changed
@@ -435,118 +509,127 @@ tree_changed()
        fi
 }
 
-new_squash_commit()
-{
+new_squash_commit () {
        old="$1"
        oldsub="$2"
        newsub="$3"
        tree=$(toptree_for_commit $newsub) || exit $?
-       if [ -n "$old" ]; then
-               squash_msg "$dir" "$oldsub" "$newsub" | 
-                       git commit-tree "$tree" -p "$old" || exit $?
+       if test -n "$old"
+       then
+               squash_msg "$dir" "$oldsub" "$newsub" |
+               git commit-tree "$tree" -p "$old" || exit $?
        else
                squash_msg "$dir" "" "$newsub" |
-                       git commit-tree "$tree" || exit $?
+               git commit-tree "$tree" || exit $?
        fi
 }
 
-copy_or_skip()
-{
+copy_or_skip () {
        rev="$1"
        tree="$2"
        newparents="$3"
-       assert [ -n "$tree" ]
+       assert test -n "$tree"
 
        identical=
        nonidentical=
        p=
        gotparents=
-       for parent in $newparents; do
+       for parent in $newparents
+       do
                ptree=$(toptree_for_commit $parent) || exit $?
-               [ -z "$ptree" ] && continue
-               if [ "$ptree" = "$tree" ]; then
+               test -z "$ptree" && continue
+               if test "$ptree" = "$tree"
+               then
                        # an identical parent could be used in place of this rev.
                        identical="$parent"
                else
                        nonidentical="$parent"
                fi
-               
+
                # sometimes both old parents map to the same newparent;
                # eliminate duplicates
                is_new=1
-               for gp in $gotparents; do
-                       if [ "$gp" = "$parent" ]; then
+               for gp in $gotparents
+               do
+                       if test "$gp" = "$parent"
+                       then
                                is_new=
                                break
                        fi
                done
-               if [ -n "$is_new" ]; then
+               if test -n "$is_new"
+               then
                        gotparents="$gotparents $parent"
                        p="$p -p $parent"
                fi
        done
 
        copycommit=
-       if [ -n "$identical" ] && [ -n "$nonidentical" ]; then
+       if test -n "$identical" && test -n "$nonidentical"
+       then
                extras=$(git rev-list --count $identical..$nonidentical)
-               if [ "$extras" -ne 0 ]; then
+               if test "$extras" -ne 0
+               then
                        # we need to preserve history along the other branch
                        copycommit=1
                fi
        fi
-       if [ -n "$identical" ] && [ -z "$copycommit" ]; then
+       if test -n "$identical" && test -z "$copycommit"
+       then
                echo $identical
        else
-               copy_commit $rev $tree "$p" || exit $?
+               copy_commit "$rev" "$tree" "$p" || exit $?
        fi
 }
 
-ensure_clean()
-{
-       if ! git diff-index HEAD --exit-code --quiet 2>&1; then
+ensure_clean () {
+       if ! git diff-index HEAD --exit-code --quiet 2>&1
+       then
                die "Working tree has modifications.  Cannot add."
        fi
-       if ! git diff-index --cached HEAD --exit-code --quiet 2>&1; then
+       if ! git diff-index --cached HEAD --exit-code --quiet 2>&1
+       then
                die "Index has modifications.  Cannot add."
        fi
 }
 
-ensure_valid_ref_format()
-{
+ensure_valid_ref_format () {
        git check-ref-format "refs/heads/$1" ||
-           die "'$1' does not look like a ref"
+               die "'$1' does not look like a ref"
 }
 
-cmd_add()
-{
-       if [ -e "$dir" ]; then
+cmd_add () {
+       if test -e "$dir"
+       then
                die "'$dir' already exists.  Cannot add."
        fi
 
        ensure_clean
-       
-       if [ $# -eq 1 ]; then
-           git rev-parse -q --verify "$1^{commit}" >/dev/null ||
-           die "'$1' does not refer to a commit"
-
-           "cmd_add_commit" "$@"
-       elif [ $# -eq 2 ]; then
-           # Technically we could accept a refspec here but we're
-           # just going to turn around and add FETCH_HEAD under the
-           # specified directory.  Allowing a refspec might be
-           # misleading because we won't do anything with any other
-           # branches fetched via the refspec.
-           ensure_valid_ref_format "$2"
-
-           "cmd_add_repository" "$@"
+
+       if test $# -eq 1
+       then
+               git rev-parse -q --verify "$1^{commit}" >/dev/null ||
+                       die "'$1' does not refer to a commit"
+
+               cmd_add_commit "$@"
+
+       elif test $# -eq 2
+       then
+               # Technically we could accept a refspec here but we're
+               # just going to turn around and add FETCH_HEAD under the
+               # specified directory.  Allowing a refspec might be
+               # misleading because we won't do anything with any other
+               # branches fetched via the refspec.
+               ensure_valid_ref_format "$2"
+
+               cmd_add_repository "$@"
        else
-           say "error: parameters were '$@'"
-           die "Provide either a commit or a repository and commit."
+               say "error: parameters were '$@'"
+               die "Provide either a commit or a repository and commit."
        fi
 }
 
-cmd_add_repository()
-{
+cmd_add_repository () {
        echo "git fetch" "$@"
        repository=$1
        refspec=$2
@@ -556,60 +639,63 @@ cmd_add_repository()
        cmd_add_commit "$@"
 }
 
-cmd_add_commit()
-{
+cmd_add_commit () {
        revs=$(git rev-parse $default --revs-only "$@") || exit $?
        set -- $revs
        rev="$1"
-       
+
        debug "Adding $dir as '$rev'..."
        git read-tree --prefix="$dir" $rev || exit $?
        git checkout -- "$dir" || exit $?
        tree=$(git write-tree) || exit $?
-       
+
        headrev=$(git rev-parse HEAD) || exit $?
-       if [ -n "$headrev" -a "$headrev" != "$rev" ]; then
+       if test -n "$headrev" && test "$headrev" != "$rev"
+       then
                headp="-p $headrev"
        else
                headp=
        fi
-       
-       if [ -n "$squash" ]; then
+
+       if test -n "$squash"
+       then
                rev=$(new_squash_commit "" "" "$rev") || exit $?
                commit=$(add_squashed_msg "$rev" "$dir" |
-                        git commit-tree $tree $headp -p "$rev") || exit $?
+                       git commit-tree "$tree" $headp -p "$rev") || exit $?
        else
                revp=$(peel_committish "$rev") &&
-               commit=$(add_msg "$dir" "$headrev" "$rev" |
-                        git commit-tree $tree $headp -p "$revp") || exit $?
+               commit=$(add_msg "$dir" $headrev "$rev" |
+                       git commit-tree "$tree" $headp -p "$revp") || exit $?
        fi
        git reset "$commit" || exit $?
-       
+
        say "Added dir '$dir'"
 }
 
-cmd_split()
-{
+cmd_split () {
        debug "Splitting $dir..."
        cache_setup || exit $?
-       
-       if [ -n "$onto" ]; then
+
+       if test -n "$onto"
+       then
                debug "Reading history for --onto=$onto..."
                git rev-list $onto |
-               while read rev; do
+               while read rev
+               do
                        # the 'onto' history is already just the subdir, so
                        # any parent we find there can be used verbatim
                        debug "  cache: $rev"
-                       cache_set $rev $rev
+                       cache_set "$rev" "$rev"
                done
        fi
-       
-       if [ -n "$ignore_joins" ]; then
+
+       if test -n "$ignore_joins"
+       then
                unrevs=
        else
                unrevs="$(find_existing_splits "$dir" "$revs")"
        fi
-       
+
        # We can't restrict rev-list to only $dir here, because some of our
        # parents have the $dir contents the root, and those won't match.
        # (and rev-list --follow doesn't seem to solve this)
@@ -618,12 +704,14 @@ cmd_split()
        revcount=0
        createcount=0
        eval "$grl" |
-       while read rev parents; do
+       while read rev parents
+       do
                revcount=$(($revcount + 1))
                progress "$revcount/$revmax ($createcount)"
                debug "Processing commit: $rev"
-               exists=$(cache_get $rev)
-               if [ -n "$exists" ]; then
+               exists=$(cache_get "$rev")
+               if test -n "$exists"
+               then
                        debug "  prior: $exists"
                        continue
                fi
@@ -631,76 +719,89 @@ cmd_split()
                debug "  parents: $parents"
                newparents=$(cache_get $parents)
                debug "  newparents: $newparents"
-               
-               tree=$(subtree_for_commit $rev "$dir")
+
+               tree=$(subtree_for_commit "$rev" "$dir")
                debug "  tree is: $tree"
 
                check_parents $parents
-               
+
                # ugly.  is there no better way to tell if this is a subtree
                # vs. a mainline commit?  Does it matter?
-               if [ -z $tree ]; then
-                       set_notree $rev
-                       if [ -n "$newparents" ]; then
-                               cache_set $rev $rev
+               if test -z "$tree"
+               then
+                       set_notree "$rev"
+                       if test -n "$newparents"
+                       then
+                               cache_set "$rev" "$rev"
                        fi
                        continue
                fi
 
                newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
                debug "  newrev is: $newrev"
-               cache_set $rev $newrev
-               cache_set latest_new $newrev
-               cache_set latest_old $rev
+               cache_set "$rev" "$newrev"
+               cache_set latest_new "$newrev"
+               cache_set latest_old "$rev"
        done || exit $?
+
        latest_new=$(cache_get latest_new)
-       if [ -z "$latest_new" ]; then
+       if test -z "$latest_new"
+       then
                die "No new revisions were found"
        fi
-       
-       if [ -n "$rejoin" ]; then
+
+       if test -n "$rejoin"
+       then
                debug "Merging split branch into HEAD..."
                latest_old=$(cache_get latest_old)
                git merge -s ours \
-                       -m "$(rejoin_msg "$dir" $latest_old $latest_new)" \
-                       $latest_new >&2 || exit $?
-       fi
-       if [ -n "$branch" ]; then
-               if rev_exists "refs/heads/$branch"; then
-                       if ! rev_is_descendant_of_branch $latest_new $branch; then
+                       --allow-unrelated-histories \
+                       -m "$(rejoin_msg "$dir" "$latest_old" "$latest_new")" \
+                       "$latest_new" >&2 || exit $?
+       fi
+       if test -n "$branch"
+       then
+               if rev_exists "refs/heads/$branch"
+               then
+                       if ! rev_is_descendant_of_branch "$latest_new" "$branch"
+                       then
                                die "Branch '$branch' is not an ancestor of commit '$latest_new'."
                        fi
                        action='Updated'
                else
                        action='Created'
                fi
-               git update-ref -m 'subtree split' "refs/heads/$branch" $latest_new || exit $?
+               git update-ref -m 'subtree split' \
+                       "refs/heads/$branch" "$latest_new" || exit $?
                say "$action branch '$branch'"
        fi
-       echo $latest_new
+       echo "$latest_new"
        exit 0
 }
 
-cmd_merge()
-{
+cmd_merge () {
        revs=$(git rev-parse $default --revs-only "$@") || exit $?
        ensure_clean
-       
+
        set -- $revs
-       if [ $# -ne 1 ]; then
+       if test $# -ne 1
+       then
                die "You must provide exactly one revision.  Got: '$revs'"
        fi
        rev="$1"
-       
-       if [ -n "$squash" ]; then
+
+       if test -n "$squash"
+       then
                first_split="$(find_latest_squash "$dir")"
-               if [ -z "$first_split" ]; then
+               if test -z "$first_split"
+               then
                        die "Can't squash-merge: '$dir' was never added."
                fi
                set $first_split
                old=$1
                sub=$2
-               if [ "$sub" = "$rev" ]; then
+               if test "$sub" = "$rev"
+               then
                        say "Subtree is already at commit $rev."
                        exit 0
                fi
@@ -710,25 +811,29 @@ cmd_merge()
        fi
 
        version=$(git version)
-       if [ "$version" \< "git version 1.7" ]; then
-               if [ -n "$message" ]; then
-                       git merge -s subtree --message="$message" $rev
+       if test "$version" \< "git version 1.7"
+       then
+               if test -n "$message"
+               then
+                       git merge -s subtree --message="$message" "$rev"
                else
-                       git merge -s subtree $rev
+                       git merge -s subtree "$rev"
                fi
        else
-               if [ -n "$message" ]; then
-                       git merge -Xsubtree="$prefix" --message="$message" $rev
+               if test -n "$message"
+               then
+                       git merge -Xsubtree="$prefix" \
+                               --message="$message" "$rev"
                else
                        git merge -Xsubtree="$prefix" $rev
                fi
        fi
 }
 
-cmd_pull()
-{
-       if [ $# -ne 2 ]; then
-           die "You must provide <repository> <ref>"
+cmd_pull () {
+       if test $# -ne 2
+       then
+               die "You must provide <repository> <ref>"
        fi
        ensure_clean
        ensure_valid_ref_format "$2"
@@ -738,20 +843,21 @@ cmd_pull()
        cmd_merge "$@"
 }
 
-cmd_push()
-{
-       if [ $# -ne 2 ]; then
-           die "You must provide <repository> <ref>"
+cmd_push () {
+       if test $# -ne 2
+       then
+               die "You must provide <repository> <ref>"
        fi
        ensure_valid_ref_format "$2"
-       if [ -e "$dir" ]; then
-           repository=$1
-           refspec=$2
-           echo "git push using: " $repository $refspec
-           localrev=$(git subtree split --prefix="$prefix") || die
-           git push "$repository" $localrev:refs/heads/$refspec
+       if test -e "$dir"
+       then
+               repository=$1
+               refspec=$2
+               echo "git push using: " "$repository" "$refspec"
+               localrev=$(git subtree split --prefix="$prefix") || die
+               git push "$repository" "$localrev":"refs/heads/$refspec"
        else
-           die "'$dir' must already exist. Try 'git subtree add'."
+               die "'$dir' must already exist. Try 'git subtree add'."
        fi
 }
 
index 3bf96a9bb6b29757736d8f4250fae25664e298f0..9751cfe9e63d6510dd7af0b6be5f7028df49bf88 100755 (executable)
@@ -16,16 +16,16 @@ export TEST_DIRECTORY
 
 subtree_test_create_repo()
 {
-       test_create_repo "$1"
+       test_create_repo "$1" &&
        (
-               cd $1
+               cd "$1" &&
                git config log.date relative
        )
 }
 
 create()
 {
-       echo "$1" >"$1"
+       echo "$1" >"$1" &&
        git add "$1"
 }
 
@@ -71,12 +71,12 @@ join_commits()
 }
 
 test_create_commit() (
-       repo=$1
-       commit=$2
-       cd "$repo"
-       mkdir -p $(dirname "$commit") \
+       repo=$1 &&
+       commit=$2 &&
+       cd "$repo" &&
+       mkdir -p "$(dirname "$commit")" \
        || error "Could not create directory for commit"
-       echo "$commit" >"$commit"
+       echo "$commit" >"$commit" &&
        git add "$commit" || error "Could not add commit"
        git commit -m "$commit" || error "Could not commit"
 )
@@ -346,6 +346,22 @@ test_expect_success 'split sub dir/ with --rejoin' '
        )
  '
 
+next_test
+test_expect_success 'split sub dir/ with --rejoin from scratch' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       (
+               cd "$subtree_test_count" &&
+               mkdir "sub dir" &&
+               echo file >"sub dir"/file &&
+               git add "sub dir/file" &&
+               git commit -m"sub dir file" &&
+               split_hash=$(git subtree split --prefix="sub dir" --rejoin) &&
+               git subtree split --prefix="sub dir" --rejoin &&
+               check_equal "$(last_commit_message)" "Split '\''sub dir/'\'' into commit '\''$split_hash'\''"
+       )
+ '
+
 next_test
 test_expect_success 'split sub dir/ with --rejoin and --message' '
        subtree_test_create_repo "$subtree_test_count" &&
index e647254c196f8270691cd369b51ac5132add1259..425aad0507f48ca07b11faa05828ea841bdd302f 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -672,9 +672,11 @@ static void set_keep_alive(int sockfd)
 {
        int ka = 1;
 
-       if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0)
-               logerror("unable to set SO_KEEPALIVE on socket: %s",
-                       strerror(errno));
+       if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0) {
+               if (errno != ENOTSOCK)
+                       logerror("unable to set SO_KEEPALIVE on socket: %s",
+                               strerror(errno));
+       }
 }
 
 static int execute(void)
diff --git a/date.c b/date.c
index 4c7aa9ba853c0924d49f4b8c2cc06826fe08956c..a996331f5b33703c9f70844c6b49453ec39d16a7 100644 (file)
--- a/date.c
+++ b/date.c
@@ -177,6 +177,12 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
        struct tm *tm;
        static struct strbuf timebuf = STRBUF_INIT;
 
+       if (mode->type == DATE_UNIX) {
+               strbuf_reset(&timebuf);
+               strbuf_addf(&timebuf, "%lu", time);
+               return timebuf.buf;
+       }
+
        if (mode->local)
                tz = local_tzoffset(time);
 
@@ -792,6 +798,8 @@ static enum date_mode_type parse_date_type(const char *format, const char **end)
                return DATE_NORMAL;
        if (skip_prefix(format, "raw", end))
                return DATE_RAW;
+       if (skip_prefix(format, "unix", end))
+               return DATE_UNIX;
        if (skip_prefix(format, "format", end))
                return DATE_STRFTIME;
 
diff --git a/diff.c b/diff.c
index 7d0341988083dae44f8833875762b0b978bbafc0..b43d3dd2ecb7b2154ce6c445983e23ce89c69111 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2683,6 +2683,13 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
        if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1))
                return 0;
 
+       /*
+        * Similarly, if we'd have to convert the file contents anyway, that
+        * makes the optimization not worthwhile.
+        */
+       if (!want_file && would_convert_to_git(name))
+               return 0;
+
        len = strlen(name);
        pos = cache_name_pos(name, len);
        if (pos < 0)
index 58ac0a531be0722bc422eb008bfcb9695965996c..73d003a08ae72d0c8f18b16172bc09c86022eb8f 100644 (file)
@@ -541,7 +541,7 @@ void diffcore_rename(struct diff_options *options)
                                rename_dst_nr * rename_src_nr, 50, 1);
        }
 
-       mx = xcalloc(st_mult(num_create, NUM_CANDIDATE_PER_DST), sizeof(*mx));
+       mx = xcalloc(st_mult(NUM_CANDIDATE_PER_DST, num_create), sizeof(*mx));
        for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
                struct diff_filespec *two = rename_dst[i].two;
                struct diff_score *m;
index b501d5c320a5117020a934a50fce2a356b0c2f0e..85e77af61d05b492d6448fd4ad33abc2c348eaf1 100644 (file)
@@ -243,16 +243,21 @@ static void insert_one_alternate_ref(const struct ref *ref, void *unused)
 
 #define INITIAL_FLUSH 16
 #define PIPESAFE_FLUSH 32
-#define LARGE_FLUSH 1024
+#define LARGE_FLUSH 16384
 
 static int next_flush(struct fetch_pack_args *args, int count)
 {
-       int flush_limit = args->stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH;
-
-       if (count < flush_limit)
-               count <<= 1;
-       else
-               count += flush_limit;
+       if (args->stateless_rpc) {
+               if (count < LARGE_FLUSH)
+                       count <<= 1;
+               else
+                       count = count * 11 / 10;
+       } else {
+               if (count < PIPESAFE_FLUSH)
+                       count <<= 1;
+               else
+                       count += PIPESAFE_FLUSH;
+       }
        return count;
 }
 
index ebd13baa6e06cf7007fdd5c48713f06cf511721d..a5790d03a075884ffe93dbff782b28ec87263051 100755 (executable)
@@ -37,14 +37,6 @@ sub usage
        exit($exitcode);
 }
 
-sub find_worktree
-{
-       # Git->repository->wc_path() does not honor changes to the working
-       # tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree'
-       # config variable.
-       return Git::command_oneline('rev-parse', '--show-toplevel');
-}
-
 sub print_tool_help
 {
        # See the comment at the bottom of file_diff() for the reason behind
@@ -67,14 +59,14 @@ sub exit_cleanup
 
 sub use_wt_file
 {
-       my ($repo, $workdir, $file, $sha1) = @_;
+       my ($workdir, $file, $sha1) = @_;
        my $null_sha1 = '0' x 40;
 
        if (-l "$workdir/$file" || ! -e _) {
                return (0, $null_sha1);
        }
 
-       my $wt_sha1 = $repo->command_oneline('hash-object', "$workdir/$file");
+       my $wt_sha1 = Git::command_oneline('hash-object', "$workdir/$file");
        my $use = ($sha1 eq $null_sha1) || ($sha1 eq $wt_sha1);
        return ($use, $wt_sha1);
 }
@@ -83,20 +75,17 @@ sub changed_files
 {
        my ($repo_path, $index, $worktree) = @_;
        $ENV{GIT_INDEX_FILE} = $index;
-       $ENV{GIT_WORK_TREE} = $worktree;
-       my $must_unset_git_dir = 0;
-       if (not defined($ENV{GIT_DIR})) {
-               $must_unset_git_dir = 1;
-               $ENV{GIT_DIR} = $repo_path;
-       }
 
-       my @refreshargs = qw/update-index --really-refresh -q --unmerged/;
-       my @gitargs = qw/diff-files --name-only -z/;
+       my @gitargs = ('--git-dir', $repo_path, '--work-tree', $worktree);
+       my @refreshargs = (
+               @gitargs, 'update-index',
+               '--really-refresh', '-q', '--unmerged');
        try {
                Git::command_oneline(@refreshargs);
        } catch Git::Error::Command with {};
 
-       my $line = Git::command_oneline(@gitargs);
+       my @diffargs = (@gitargs, 'diff-files', '--name-only', '-z');
+       my $line = Git::command_oneline(@diffargs);
        my @files;
        if (defined $line) {
                @files = split('\0', $line);
@@ -105,26 +94,15 @@ sub changed_files
        }
 
        delete($ENV{GIT_INDEX_FILE});
-       delete($ENV{GIT_WORK_TREE});
-       delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
 
        return map { $_ => 1 } @files;
 }
 
 sub setup_dir_diff
 {
-       my ($repo, $workdir, $symlinks) = @_;
-
-       # Run the diff; exit immediately if no diff found
-       # 'Repository' and 'WorkingCopy' must be explicitly set to insure that
-       # if $GIT_DIR and $GIT_WORK_TREE are set in ENV, they are actually used
-       # by Git->repository->command*.
-       my $repo_path = $repo->repo_path();
-       my %repo_args = (Repository => $repo_path, WorkingCopy => $workdir);
-       my $diffrepo = Git->repository(%repo_args);
-
+       my ($workdir, $symlinks) = @_;
        my @gitargs = ('diff', '--raw', '--no-abbrev', '-z', @ARGV);
-       my $diffrtn = $diffrepo->command_oneline(@gitargs);
+       my $diffrtn = Git::command_oneline(@gitargs);
        exit(0) unless defined($diffrtn);
 
        # Build index info for left and right sides of the diff
@@ -176,12 +154,12 @@ sub setup_dir_diff
 
                if ($lmode eq $symlink_mode) {
                        $symlink{$src_path}{left} =
-                               $diffrepo->command_oneline('show', "$lsha1");
+                               Git::command_oneline('show', $lsha1);
                }
 
                if ($rmode eq $symlink_mode) {
                        $symlink{$dst_path}{right} =
-                               $diffrepo->command_oneline('show', "$rsha1");
+                               Git::command_oneline('show', $rsha1);
                }
 
                if ($lmode ne $null_mode and $status !~ /^C/) {
@@ -193,8 +171,8 @@ sub setup_dir_diff
                        if ($working_tree_dups{$dst_path}++) {
                                next;
                        }
-                       my ($use, $wt_sha1) = use_wt_file($repo, $workdir,
-                                                         $dst_path, $rsha1);
+                       my ($use, $wt_sha1) =
+                               use_wt_file($workdir, $dst_path, $rsha1);
                        if ($use) {
                                push @working_tree, $dst_path;
                                $wtindex .= "$rmode $wt_sha1\t$dst_path\0";
@@ -211,44 +189,34 @@ sub setup_dir_diff
        mkpath($ldir) or exit_cleanup($tmpdir, 1);
        mkpath($rdir) or exit_cleanup($tmpdir, 1);
 
-       # If $GIT_DIR is not set prior to calling 'git update-index' and
-       # 'git checkout-index', then those commands will fail if difftool
-       # is called from a directory other than the repo root.
-       my $must_unset_git_dir = 0;
-       if (not defined($ENV{GIT_DIR})) {
-               $must_unset_git_dir = 1;
-               $ENV{GIT_DIR} = $repo_path;
-       }
-
        # Populate the left and right directories based on each index file
        my ($inpipe, $ctx);
        $ENV{GIT_INDEX_FILE} = "$tmpdir/lindex";
        ($inpipe, $ctx) =
-               $repo->command_input_pipe(qw(update-index -z --index-info));
+               Git::command_input_pipe('update-index', '-z', '--index-info');
        print($inpipe $lindex);
-       $repo->command_close_pipe($inpipe, $ctx);
+       Git::command_close_pipe($inpipe, $ctx);
 
        my $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/");
        exit_cleanup($tmpdir, $rc) if $rc != 0;
 
        $ENV{GIT_INDEX_FILE} = "$tmpdir/rindex";
        ($inpipe, $ctx) =
-               $repo->command_input_pipe(qw(update-index -z --index-info));
+               Git::command_input_pipe('update-index', '-z', '--index-info');
        print($inpipe $rindex);
-       $repo->command_close_pipe($inpipe, $ctx);
+       Git::command_close_pipe($inpipe, $ctx);
 
        $rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/");
        exit_cleanup($tmpdir, $rc) if $rc != 0;
 
        $ENV{GIT_INDEX_FILE} = "$tmpdir/wtindex";
        ($inpipe, $ctx) =
-               $repo->command_input_pipe(qw(update-index --info-only -z --index-info));
+               Git::command_input_pipe('update-index', '--info-only', '-z', '--index-info');
        print($inpipe $wtindex);
-       $repo->command_close_pipe($inpipe, $ctx);
+       Git::command_close_pipe($inpipe, $ctx);
 
        # If $GIT_DIR was explicitly set just for the update/checkout
        # commands, then it should be unset before continuing.
-       delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
        delete($ENV{GIT_INDEX_FILE});
 
        # Changes in the working tree need special treatment since they are
@@ -415,9 +383,9 @@ sub dir_diff
        my $rc;
        my $error = 0;
        my $repo = Git->repository();
-       my $workdir = find_worktree();
-       my ($a, $b, $tmpdir, @worktree) =
-               setup_dir_diff($repo, $workdir, $symlinks);
+       my $repo_path = $repo->repo_path();
+       my $workdir = $repo->wc_path();
+       my ($a, $b, $tmpdir, @worktree) = setup_dir_diff($workdir, $symlinks);
 
        if (defined($extcmd)) {
                $rc = system($extcmd, $a, $b);
@@ -443,10 +411,10 @@ sub dir_diff
                next if ! -f "$b/$file";
 
                if (!$indices_loaded) {
-                       %wt_modified = changed_files($repo->repo_path(),
-                               "$tmpdir/wtindex", "$workdir");
-                       %tmp_modified = changed_files($repo->repo_path(),
-                               "$tmpdir/wtindex", "$b");
+                       %wt_modified = changed_files(
+                               $repo_path, "$tmpdir/wtindex", $workdir);
+                       %tmp_modified = changed_files(
+                               $repo_path, "$tmpdir/wtindex", $b);
                        $indices_loaded = 1;
                }
 
index ded459563816ebe36f4e467f596f0c84fa30ff57..e2da524f5a26206b3558f5a1d062695939c054e7 100644 (file)
@@ -121,7 +121,7 @@ mark_action_done () {
        sed -e 1q < "$todo" >> "$done"
        sed -e 1d < "$todo" >> "$todo".new
        mv -f "$todo".new "$todo"
-       new_count=$(git stripspace --strip-comments <"$done" | wc -l)
+       new_count=$(( $(git stripspace --strip-comments <"$done" | wc -l) ))
        echo $new_count >"$msgnum"
        total=$(($new_count + $(git stripspace --strip-comments <"$todo" | wc -l)))
        echo $total >"$end"
index 0c34aa62f6b1b33be67f4221290c9c5ce55beabe..a8a4576342e519932be07fca5cbe2ee2fc0ab73b 100644 (file)
@@ -163,9 +163,11 @@ git_pager() {
        else
                GIT_PAGER=cat
        fi
-       : "${LESS=-FRX}"
-       : "${LV=-c}"
-       export LESS LV
+       for vardef in @@PAGER_ENV@@
+       do
+               var=${vardef%%=*}
+               eval ": \"\${$vardef}\" && export $var"
+       done
 
        eval "$GIT_PAGER" '"$@"'
 }
index 4ec7546f1eb5617b85169cb6e926b9f490e1d6dc..c90dc335d145fa0a16efbe41873a525b596b806d 100755 (executable)
@@ -49,7 +49,7 @@ die_if_unmatched ()
 {
        if test "$1" = "#unmatched"
        then
-               exit 1
+               exit ${2:-1}
        fi
 }
 
@@ -312,11 +312,11 @@ cmd_foreach()
 
        {
                git submodule--helper list --prefix "$wt_prefix" ||
-               echo "#unmatched"
+               echo "#unmatched" $?
        } |
        while read mode sha1 stage sm_path
        do
-               die_if_unmatched "$mode"
+               die_if_unmatched "$mode" "$sha1"
                if test -e "$sm_path"/.git
                then
                        displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
@@ -379,8 +379,6 @@ cmd_init()
 #
 # Unregister submodules from .git/config and remove their work tree
 #
-# $@ = requested paths (use '.' to deinit all submodules)
-#
 cmd_deinit()
 {
        # parse $args after "submodule ... deinit".
@@ -423,11 +421,11 @@ cmd_deinit()
 
        {
                git submodule--helper list --prefix "$wt_prefix" "$@" ||
-               echo "#unmatched"
+               echo "#unmatched" $?
        } |
        while read mode sha1 stage sm_path
        do
-               die_if_unmatched "$mode"
+               die_if_unmatched "$mode" "$sha1"
                name=$(git submodule--helper name "$sm_path") || exit
 
                displaypath=$(git submodule--helper relative-path "$sm_path" "$wt_prefix")
@@ -581,12 +579,12 @@ cmd_update()
                ${depth:+--depth "$depth"} \
                ${recommend_shallow:+"$recommend_shallow"} \
                ${jobs:+$jobs} \
-               "$@" || echo "#unmatched"
+               "$@" || echo "#unmatched" $?
        } | {
        err=
        while read mode sha1 stage just_cloned sm_path
        do
-               die_if_unmatched "$mode"
+               die_if_unmatched "$mode" "$sha1"
 
                name=$(git submodule--helper name "$sm_path") || exit
                url=$(git config submodule."$name".url)
@@ -994,11 +992,11 @@ cmd_status()
 
        {
                git submodule--helper list --prefix "$wt_prefix" "$@" ||
-               echo "#unmatched"
+               echo "#unmatched" $?
        } |
        while read mode sha1 stage sm_path
        do
-               die_if_unmatched "$mode"
+               die_if_unmatched "$mode" "$sha1"
                name=$(git submodule--helper name "$sm_path") || exit
                url=$(git config submodule."$name".url)
                displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
@@ -1075,11 +1073,11 @@ cmd_sync()
        cd_to_toplevel
        {
                git submodule--helper list --prefix "$wt_prefix" "$@" ||
-               echo "#unmatched"
+               echo "#unmatched" $?
        } |
        while read mode sha1 stage sm_path
        do
-               die_if_unmatched "$mode"
+               die_if_unmatched "$mode" "$sha1"
                name=$(git submodule--helper name "$sm_path")
                url=$(git config -f .gitmodules --get submodule."$name".url)
 
index f609e54ce3b3d5a0f004de6d40390a9fdae4f6a9..4d41d220a0da3a2357d63dbb6ba59514bb3f0462 100755 (executable)
@@ -339,7 +339,7 @@ sub term_init {
                        die "failed to open $ENV{GIT_DIR}: $!\n";
                $ENV{GIT_DIR} = $1 if <$fh> =~ /^gitdir: (.+)$/;
        }
-} else {
+} elsif ($cmd) {
        my ($git_dir, $cdup);
        git_cmd_try {
                $git_dir = command_oneline([qw/rev-parse --git-dir/]);
@@ -356,7 +356,7 @@ sub term_init {
 
 my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
 
-read_git_config(\%opts);
+read_git_config(\%opts) if $ENV{GIT_DIR};
 if ($cmd && ($cmd eq 'log' || $cmd eq 'blame')) {
        Getopt::Long::Configure('pass_through');
 }
index 2fddf750fabf9ac2d079777ad7bd7953c2477f9c..33d701d8525fd9334e4a899a807c8f8f0164dcc5 100755 (executable)
@@ -2090,7 +2090,7 @@ sub format_ref_marker {
                                -href => href(
                                        action=>$dest_action,
                                        hash=>$dest
-                               )}, $name);
+                               )}, esc_html($name));
 
                        $markers .= " <span class=\"".esc_attr($class)."\" title=\"".esc_attr($ref)."\">" .
                                $link . "</span>";
diff --git a/grep.c b/grep.c
index 22cbb73f26c174939e51bae469f5fbb9e7b2417d..d7d00b87cb2a28332ccad4ea4ff676e3f2bc2611 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -163,17 +163,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
        color_set(opt->color_sep, def->color_sep);
 }
 
-void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
-{
-       if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED)
-               grep_set_pattern_type_option(pattern_type, opt);
-       else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
-               grep_set_pattern_type_option(opt->pattern_type_option, opt);
-       else if (opt->extended_regexp_option)
-               grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
-}
-
-void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
+static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
 {
        switch (pattern_type) {
        case GREP_PATTERN_TYPE_UNSPECIFIED:
@@ -205,6 +195,16 @@ void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct gr
        }
 }
 
+void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
+{
+       if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED)
+               grep_set_pattern_type_option(pattern_type, opt);
+       else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
+               grep_set_pattern_type_option(opt->pattern_type_option, opt);
+       else if (opt->extended_regexp_option)
+               grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
+}
+
 static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
                                        const char *origin, int no,
                                        enum grep_pat_token t,
diff --git a/grep.h b/grep.h
index cee4357b1738ed145cc06090e891147a7b4e9420..5856a23e4620773cbda2e2944f93946c2701f923 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -145,7 +145,6 @@ struct grep_opt {
 extern void init_grep_defaults(void);
 extern int grep_config(const char *var, const char *value, void *);
 extern void grep_init(struct grep_opt *, const char *prefix);
-void grep_set_pattern_type_option(enum grep_pattern_type, struct grep_opt *opt);
 void grep_commit_pattern_type(enum grep_pattern_type, struct grep_opt *opt);
 
 extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
index dacada9094efd0c929319f39ca74a908668863c7..704b1c837c9feaa1215e5fd9767c04275a4c1beb 100644 (file)
@@ -1137,7 +1137,7 @@ static void remote_ls(const char *path, int flags,
        ls.userData = userData;
        ls.userFunc = userFunc;
 
-       strbuf_addf(&out_buffer.buf, PROPFIND_ALL_REQUEST);
+       strbuf_addstr(&out_buffer.buf, PROPFIND_ALL_REQUEST);
 
        dav_headers = curl_slist_append(dav_headers, "Depth: 1");
        dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
index 2c721f0c30d786e043dec819dc421788311c7e4b..0b2425531a8120fb29f37ee473a1e6e974959605 100644 (file)
@@ -2,6 +2,7 @@
 #include "commit.h"
 #include "walker.h"
 #include "http.h"
+#include "list.h"
 
 struct alt_base {
        char *base;
@@ -23,7 +24,7 @@ struct object_request {
        struct alt_base *repo;
        enum object_request_state state;
        struct http_object_request *req;
-       struct object_request *next;
+       struct list_head node;
 };
 
 struct alternates_request {
@@ -41,7 +42,7 @@ struct walker_data {
        struct alt_base *alt;
 };
 
-static struct object_request *object_queue_head;
+static LIST_HEAD(object_queue_head);
 
 static void fetch_alternates(struct walker *walker, const char *base);
 
@@ -110,19 +111,10 @@ static void process_object_response(void *callback_data)
 
 static void release_object_request(struct object_request *obj_req)
 {
-       struct object_request *entry = object_queue_head;
-
        if (obj_req->req !=NULL && obj_req->req->localfile != -1)
                error("fd leakage in release: %d", obj_req->req->localfile);
-       if (obj_req == object_queue_head) {
-               object_queue_head = obj_req->next;
-       } else {
-               while (entry->next != NULL && entry->next != obj_req)
-                       entry = entry->next;
-               if (entry->next == obj_req)
-                       entry->next = entry->next->next;
-       }
 
+       list_del(&obj_req->node);
        free(obj_req);
 }
 
@@ -130,8 +122,10 @@ static void release_object_request(struct object_request *obj_req)
 static int fill_active_slot(struct walker *walker)
 {
        struct object_request *obj_req;
+       struct list_head *pos, *tmp, *head = &object_queue_head;
 
-       for (obj_req = object_queue_head; obj_req; obj_req = obj_req->next) {
+       list_for_each_safe(pos, tmp, head) {
+               obj_req = list_entry(pos, struct object_request, node);
                if (obj_req->state == WAITING) {
                        if (has_sha1_file(obj_req->sha1))
                                obj_req->state = COMPLETE;
@@ -148,7 +142,6 @@ static int fill_active_slot(struct walker *walker)
 static void prefetch(struct walker *walker, unsigned char *sha1)
 {
        struct object_request *newreq;
-       struct object_request *tail;
        struct walker_data *data = walker->data;
 
        newreq = xmalloc(sizeof(*newreq));
@@ -157,18 +150,9 @@ static void prefetch(struct walker *walker, unsigned char *sha1)
        newreq->repo = data->alt;
        newreq->state = WAITING;
        newreq->req = NULL;
-       newreq->next = NULL;
 
        http_is_verbose = walker->get_verbosely;
-
-       if (object_queue_head == NULL) {
-               object_queue_head = newreq;
-       } else {
-               tail = object_queue_head;
-               while (tail->next != NULL)
-                       tail = tail->next;
-               tail->next = newreq;
-       }
+       list_add_tail(&newreq->node, &object_queue_head);
 
 #ifdef USE_CURL_MULTI
        fill_active_slots();
@@ -447,15 +431,19 @@ static void abort_object_request(struct object_request *obj_req)
        release_object_request(obj_req);
 }
 
-static int fetch_object(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
+static int fetch_object(struct walker *walker, unsigned char *sha1)
 {
        char *hex = sha1_to_hex(sha1);
        int ret = 0;
-       struct object_request *obj_req = object_queue_head;
+       struct object_request *obj_req = NULL;
        struct http_object_request *req;
+       struct list_head *pos, *head = &object_queue_head;
 
-       while (obj_req != NULL && hashcmp(obj_req->sha1, sha1))
-               obj_req = obj_req->next;
+       list_for_each(pos, head) {
+               obj_req = list_entry(pos, struct object_request, node);
+               if (!hashcmp(obj_req->sha1, sha1))
+                       break;
+       }
        if (obj_req == NULL)
                return error("Couldn't find request for %s in the queue", hex);
 
@@ -488,6 +476,15 @@ static int fetch_object(struct walker *walker, struct alt_base *repo, unsigned c
                req->localfile = -1;
        }
 
+       /*
+        * we turned off CURLOPT_FAILONERROR to avoid losing a
+        * persistent connection and got CURLE_OK.
+        */
+       if (req->http_code == 404 && req->curl_result == CURLE_OK &&
+                       (starts_with(req->url, "http://") ||
+                        starts_with(req->url, "https://")))
+               req->curl_result = CURLE_HTTP_RETURNED_ERROR;
+
        if (obj_req->state == ABORTED) {
                ret = error("Request for %s aborted", hex);
        } else if (req->curl_result != CURLE_OK &&
@@ -518,7 +515,7 @@ static int fetch(struct walker *walker, unsigned char *sha1)
        struct walker_data *data = walker->data;
        struct alt_base *altbase = data->alt;
 
-       if (!fetch_object(walker, altbase, sha1))
+       if (!fetch_object(walker, sha1))
                return 0;
        while (altbase) {
                if (!http_fetch_pack(walker, altbase, sha1))
diff --git a/http.c b/http.c
index d8b2bec861b14dfa16dbe99f3ccc44463375b8b3..cd40b012f89397db67e851101ab38c2c57fd18c9 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1225,7 +1225,7 @@ void append_remote_object_url(struct strbuf *buf, const char *url,
 
        strbuf_addf(buf, "objects/%.*s/", 2, hex);
        if (!only_two_digit_prefix)
-               strbuf_addf(buf, "%s", hex+2);
+               strbuf_addstr(buf, hex + 2);
 }
 
 char *get_remote_object_url(const char *url, const char *hex,
@@ -1975,8 +1975,19 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
        unsigned char expn[4096];
        size_t size = eltsize * nmemb;
        int posn = 0;
-       struct http_object_request *freq =
-               (struct http_object_request *)data;
+       struct http_object_request *freq = data;
+       struct active_request_slot *slot = freq->slot;
+
+       if (slot) {
+               CURLcode c = curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE,
+                                               &slot->http_code);
+               if (c != CURLE_OK)
+                       die("BUG: curl_easy_getinfo for HTTP code failed: %s",
+                               curl_easy_strerror(c));
+               if (slot->http_code >= 400)
+                       return size;
+       }
+
        do {
                ssize_t retval = xwrite(freq->localfile,
                                        (char *) ptr + posn, size - posn);
@@ -2097,6 +2108,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
        freq->slot = get_active_slot();
 
        curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
+       curl_easy_setopt(freq->slot->curl, CURLOPT_FAILONERROR, 0);
        curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
        curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
        curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
diff --git a/ident.c b/ident.c
index 139c5289d03b7594af2a07e6e0364bb285ae0348..e20a772dde4230b0871ffe85a5204919402aaf94 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -184,6 +184,11 @@ static const char *ident_default_date(void)
        return git_default_date.buf;
 }
 
+void reset_ident_date(void)
+{
+       strbuf_reset(&git_default_date);
+}
+
 static int crud(unsigned char c)
 {
        return  c <= 32  ||
diff --git a/list.h b/list.h
new file mode 100644 (file)
index 0000000..a226a87
--- /dev/null
+++ b/list.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2002 Free Software Foundation, Inc.
+ * (originally part of the GNU C Library and Userspace RCU)
+ * Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+ *
+ * Copyright (C) 2009 Pierre-Marc Fournier
+ * Conversion to RCU list.
+ * Copyright (C) 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIST_H
+#define LIST_H 1
+
+/*
+ * The definitions of this file are adopted from those which can be
+ * found in the Linux kernel headers to enable people familiar with the
+ * latter find their way in these sources as well.
+ */
+
+/* Basic type for the double-link list. */
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+/* avoid conflicts with BSD-only sys/queue.h */
+#undef LIST_HEAD
+/* Define a variable with the head and tail of the list. */
+#define LIST_HEAD(name) \
+       struct list_head name = { &(name), &(name) }
+
+/* Initialize a new list head. */
+#define INIT_LIST_HEAD(ptr) \
+       (ptr)->next = (ptr)->prev = (ptr)
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+/* Add new element at the head of the list. */
+static inline void list_add(struct list_head *newp, struct list_head *head)
+{
+       head->next->prev = newp;
+       newp->next = head->next;
+       newp->prev = head;
+       head->next = newp;
+}
+
+/* Add new element at the tail of the list. */
+static inline void list_add_tail(struct list_head *newp, struct list_head *head)
+{
+       head->prev->next = newp;
+       newp->next = head;
+       newp->prev = head->prev;
+       head->prev = newp;
+}
+
+/* Remove element from list. */
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/* Remove element from list. */
+static inline void list_del(struct list_head *elem)
+{
+       __list_del(elem->prev, elem->next);
+}
+
+/* Remove element from list, initializing the element's list pointers. */
+static inline void list_del_init(struct list_head *elem)
+{
+       list_del(elem);
+       INIT_LIST_HEAD(elem);
+}
+
+/* Delete from list, add to another list as head. */
+static inline void list_move(struct list_head *elem, struct list_head *head)
+{
+       __list_del(elem->prev, elem->next);
+       list_add(elem, head);
+}
+
+/* Replace an old entry. */
+static inline void list_replace(struct list_head *old, struct list_head *newp)
+{
+       newp->next = old->next;
+       newp->prev = old->prev;
+       newp->prev->next = newp;
+       newp->next->prev = newp;
+}
+
+/* Join two lists. */
+static inline void list_splice(struct list_head *add, struct list_head *head)
+{
+       /* Do nothing if the list which gets added is empty. */
+       if (add != add->next) {
+               add->next->prev = head;
+               add->prev->next = head->next;
+               head->next->prev = add->prev;
+               head->next = add->next;
+       }
+}
+
+/* Get typed element from list at a given position. */
+#define list_entry(ptr, type, member) \
+       ((type *) ((char *) (ptr) - offsetof(type, member)))
+
+/* Get first entry from a list. */
+#define list_first_entry(ptr, type, member) \
+       list_entry((ptr)->next, type, member)
+
+/* Iterate forward over the elements of the list. */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/*
+ * Iterate forward over the elements list. The list elements can be
+ * removed from the list while doing this.
+ */
+#define list_for_each_safe(pos, p, head) \
+       for (pos = (head)->next, p = pos->next; \
+               pos != (head); \
+               pos = p, p = pos->next)
+
+/* Iterate backward over the elements of the list. */
+#define list_for_each_prev(pos, head) \
+       for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/*
+ * Iterate backwards over the elements list. The list elements can be
+ * removed from the list while doing this.
+ */
+#define list_for_each_prev_safe(pos, p, head) \
+       for (pos = (head)->prev, p = pos->prev; \
+               pos != (head); \
+               pos = p, p = pos->prev)
+
+static inline int list_empty(struct list_head *head)
+{
+       return head == head->next;
+}
+
+static inline void list_replace_init(struct list_head *old,
+                                    struct list_head *newp)
+{
+       struct list_head *head = old->next;
+
+       list_del(old);
+       list_add_tail(newp, head);
+       INIT_LIST_HEAD(old);
+}
+
+#endif /* LIST_H */
index d0062e6788e95620bb6e1a534661dbb70bdbb098..bfb735c84556f0c6f4d3f17dc4ed18774b8db817 100644 (file)
@@ -263,8 +263,6 @@ void format_decorations_extended(struct strbuf *sb,
 
                        if (current_and_HEAD &&
                            decoration->type == DECORATION_REF_HEAD) {
-                               strbuf_addstr(sb, color_reset);
-                               strbuf_addstr(sb, color_commit);
                                strbuf_addstr(sb, " -> ");
                                strbuf_addstr(sb, color_reset);
                                strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type));
diff --git a/mru.c b/mru.c
new file mode 100644 (file)
index 0000000..9dedae0
--- /dev/null
+++ b/mru.c
@@ -0,0 +1,50 @@
+#include "cache.h"
+#include "mru.h"
+
+void mru_append(struct mru *mru, void *item)
+{
+       struct mru_entry *cur = xmalloc(sizeof(*cur));
+       cur->item = item;
+       cur->prev = mru->tail;
+       cur->next = NULL;
+
+       if (mru->tail)
+               mru->tail->next = cur;
+       else
+               mru->head = cur;
+       mru->tail = cur;
+}
+
+void mru_mark(struct mru *mru, struct mru_entry *entry)
+{
+       /* If we're already at the front of the list, nothing to do */
+       if (mru->head == entry)
+               return;
+
+       /* Otherwise, remove us from our current slot... */
+       if (entry->prev)
+               entry->prev->next = entry->next;
+       if (entry->next)
+               entry->next->prev = entry->prev;
+       else
+               mru->tail = entry->prev;
+
+       /* And insert us at the beginning. */
+       entry->prev = NULL;
+       entry->next = mru->head;
+       if (mru->head)
+               mru->head->prev = entry;
+       mru->head = entry;
+}
+
+void mru_clear(struct mru *mru)
+{
+       struct mru_entry *p = mru->head;
+
+       while (p) {
+               struct mru_entry *to_free = p;
+               p = p->next;
+               free(to_free);
+       }
+       mru->head = mru->tail = NULL;
+}
diff --git a/mru.h b/mru.h
new file mode 100644 (file)
index 0000000..42e4aea
--- /dev/null
+++ b/mru.h
@@ -0,0 +1,45 @@
+#ifndef MRU_H
+#define MRU_H
+
+/**
+ * A simple most-recently-used cache, backed by a doubly-linked list.
+ *
+ * Usage is roughly:
+ *
+ *   // Create a list.  Zero-initialization is required.
+ *   static struct mru cache;
+ *   mru_append(&cache, item);
+ *   ...
+ *
+ *   // Iterate in MRU order.
+ *   struct mru_entry *p;
+ *   for (p = cache.head; p; p = p->next) {
+ *     if (matches(p->item))
+ *             break;
+ *   }
+ *
+ *   // Mark an item as used, moving it to the front of the list.
+ *   mru_mark(&cache, p);
+ *
+ *   // Reset the list to empty, cleaning up all resources.
+ *   mru_clear(&cache);
+ *
+ * Note that you SHOULD NOT call mru_mark() and then continue traversing the
+ * list; it reorders the marked item to the front of the list, and therefore
+ * you will begin traversing the whole list again.
+ */
+
+struct mru_entry {
+       void *item;
+       struct mru_entry *prev, *next;
+};
+
+struct mru {
+       struct mru_entry *head, *tail;
+};
+
+void mru_append(struct mru *mru, void *item);
+void mru_mark(struct mru *mru, struct mru_entry *entry);
+void mru_clear(struct mru *mru);
+
+#endif /* MRU_H */
index 1b58a14ebc30d47d942b868af1afdc8cc3195148..97fc42f64bcd7a46d4f7818b4ad96ee6622fbec1 100644 (file)
@@ -298,12 +298,8 @@ static void write_buf_to_worktree(const unsigned char *obj,
        char *path = git_pathdup(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
        if (safe_create_leading_directories_const(path))
                die_errno("unable to create directory for '%s'", path);
-       if (file_exists(path))
-               die("found existing file at '%s'", path);
 
-       fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0666);
-       if (fd < 0)
-               die_errno("failed to open '%s'", path);
+       fd = xopen(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
 
        while (size > 0) {
                long ret = write_in_full(fd, buf, size);
index 1da89a41cec9e605fc3fdfa8efaaf2489e501c88..d123846ea2be7c34360049864aeb3a88f505dfd1 100644 (file)
@@ -105,6 +105,8 @@ static int verify_packfile(struct packed_git *p,
                void *data;
                enum object_type type;
                unsigned long size;
+               off_t curpos;
+               int data_valid;
 
                if (p->index_version > 1) {
                        off_t offset = entries[i].offset;
@@ -116,8 +118,25 @@ static int verify_packfile(struct packed_git *p,
                                            sha1_to_hex(entries[i].sha1),
                                            p->pack_name, (uintmax_t)offset);
                }
-               data = unpack_entry(p, entries[i].offset, &type, &size);
-               if (!data)
+
+               curpos = entries[i].offset;
+               type = unpack_object_header(p, w_curs, &curpos, &size);
+               unuse_pack(w_curs);
+
+               if (type == OBJ_BLOB && big_file_threshold <= size) {
+                       /*
+                        * Let check_sha1_signature() check it with
+                        * the streaming interface; no point slurping
+                        * the data in-core only to discard.
+                        */
+                       data = NULL;
+                       data_valid = 0;
+               } else {
+                       data = unpack_entry(p, entries[i].offset, &type, &size);
+                       data_valid = 1;
+               }
+
+               if (data_valid && !data)
                        err = error("cannot unpack %s from %s at offset %"PRIuMAX"",
                                    sha1_to_hex(entries[i].sha1), p->pack_name,
                                    (uintmax_t)entries[i].offset);
index 33293ce2a6b8ea51516e42d01001b15a3016ca27..ea0b78813081236401efbf5f102e7f89a7d5ed7b 100644 (file)
@@ -354,7 +354,6 @@ void finish_tmp_packfile(struct strbuf *name_buffer,
                die_errno("unable to make temporary index file readable");
 
        strbuf_addf(name_buffer, "%s.pack", sha1_to_hex(sha1));
-       free_pack_by_name(name_buffer->buf);
 
        if (rename(pack_tmp_name, name_buffer->buf))
                die_errno("unable to rename temporary pack file");
diff --git a/pack.h b/pack.h
index 3223f5a0380f735509424bbdbad64097948ef85b..0e77429df5e53a753271843aa8abc16f38708ccc 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -74,6 +74,7 @@ struct pack_idx_entry {
 
 
 struct progress;
+/* Note, the data argument could be NULL if object type is blob */
 typedef int (*verify_fn)(const unsigned char*, enum object_type, unsigned long, void*, int*);
 
 extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1);
diff --git a/pager.c b/pager.c
index 4bc048148e043eabf1315bbcbae8ea4c6363b330..6470b8180df7e0de51a0d71ac47f8c078d226923 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -63,14 +63,38 @@ const char *git_pager(int stdout_is_tty)
        return pager;
 }
 
+static void setup_pager_env(struct argv_array *env)
+{
+       const char **argv;
+       int i;
+       char *pager_env = xstrdup(PAGER_ENV);
+       int n = split_cmdline(pager_env, &argv);
+
+       if (n < 0)
+               die("malformed build-time PAGER_ENV: %s",
+                       split_cmdline_strerror(n));
+
+       for (i = 0; i < n; i++) {
+               char *cp = strchr(argv[i], '=');
+
+               if (!cp)
+                       die("malformed build-time PAGER_ENV");
+
+               *cp = '\0';
+               if (!getenv(argv[i])) {
+                       *cp = '=';
+                       argv_array_push(env, argv[i]);
+               }
+       }
+       free(pager_env);
+       free(argv);
+}
+
 void prepare_pager_args(struct child_process *pager_process, const char *pager)
 {
        argv_array_push(&pager_process->args, pager);
        pager_process->use_shell = 1;
-       if (!getenv("LESS"))
-               argv_array_push(&pager_process->env_array, "LESS=FRX");
-       if (!getenv("LV"))
-               argv_array_push(&pager_process->env_array, "LV=-c");
+       setup_pager_env(&pager_process->env_array);
 }
 
 void setup_pager(void)
index ba5acf3111d809a3c77314fb2c0852854620cfae..9667bc75a08e8b64290f5f44c92b0bac35d1d0fa 100644 (file)
@@ -117,19 +117,24 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
-int parse_options_concat(struct option *dst, size_t dst_size, struct option *src)
+struct option *parse_options_concat(struct option *a, struct option *b)
 {
-       int i, j;
-
-       for (i = 0; i < dst_size; i++)
-               if (dst[i].type == OPTION_END)
-                       break;
-       for (j = 0; i < dst_size; i++, j++) {
-               dst[i] = src[j];
-               if (src[j].type == OPTION_END)
-                       return 0;
-       }
-       return -1;
+       struct option *ret;
+       size_t i, a_len = 0, b_len = 0;
+
+       for (i = 0; a[i].type != OPTION_END; i++)
+               a_len++;
+       for (i = 0; b[i].type != OPTION_END; i++)
+               b_len++;
+
+       ALLOC_ARRAY(ret, st_add3(a_len, b_len, 1));
+       for (i = 0; i < a_len; i++)
+               ret[i] = a[i];
+       for (i = 0; i < b_len; i++)
+               ret[a_len + i] = b[i];
+       ret[a_len + b_len] = b[b_len]; /* final OPTION_END */
+
+       return ret;
 }
 
 int parse_opt_string_list(const struct option *opt, const char *arg, int unset)
index ea4af92a5110554c450665e08fce3a97310e5640..78f8384c56b02cbabcc586b3ee082cb1cb12e764 100644 (file)
@@ -215,7 +215,7 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx,
 
 extern int parse_options_end(struct parse_opt_ctx_t *ctx);
 
-extern int parse_options_concat(struct option *dst, size_t, struct option *src);
+extern struct option *parse_options_concat(struct option *a, struct option *b);
 
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
diff --git a/refs.c b/refs.c
index 814cad316384f953be975cdb711e320b4f1290d3..b4e7cac7b26d0903c4ab7361430c38232c1d5e82 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -922,7 +922,7 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                        /* -2 for strlen("%.*s") - strlen("%s"); +1 for NUL */
                        total_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;
 
-               scanf_fmts = xmalloc(st_add(st_mult(nr_rules, sizeof(char *)), total_len));
+               scanf_fmts = xmalloc(st_add(st_mult(sizeof(char *), nr_rules), total_len));
 
                offset = 0;
                for (i = 0; i < nr_rules; i++) {
index a326e4e2516e2129e7a08bfc149786402ec160fb..d29850a81cdb09b41da5dc3e7284106672d85a4c 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1544,8 +1544,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                 * branch.
                 */
                if (ref->expect_old_sha1) {
-                       if (ref->expect_old_no_trackback ||
-                           oidcmp(&ref->old_oid, &ref->old_oid_expect))
+                       if (oidcmp(&ref->old_oid, &ref->old_oid_expect))
                                reject_reason = REF_STATUS_REJECT_STALE;
                        else
                                /* If the ref isn't stale then force the update. */
@@ -2294,6 +2293,8 @@ int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unse
        entry = add_cas_entry(cas, arg, colon - arg);
        if (!*colon)
                entry->use_tracking = 1;
+       else if (!colon[1])
+               hashclr(entry->expect);
        else if (get_sha1(colon + 1, entry->expect))
                return error("cannot parse expected object name '%s'", colon + 1);
        return 0;
@@ -2343,7 +2344,7 @@ static void apply_cas(struct push_cas_option *cas,
                if (!entry->use_tracking)
                        hashcpy(ref->old_oid_expect.hash, cas->entry[i].expect);
                else if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
-                       ref->expect_old_no_trackback = 1;
+                       oidclr(&ref->old_oid_expect);
                return;
        }
 
@@ -2353,7 +2354,7 @@ static void apply_cas(struct push_cas_option *cas,
 
        ref->expect_old_sha1 = 1;
        if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
-               ref->expect_old_no_trackback = 1;
+               oidclr(&ref->old_oid_expect);
 }
 
 void apply_push_cas(struct push_cas_option *cas,
index c21fd3788c78f28d57e7d76d472dc411986b34d1..924881169d9f6c5b9b09e2434d964f62e0e28d09 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -89,7 +89,6 @@ struct ref {
                force:1,
                forced_update:1,
                expect_old_sha1:1,
-               expect_old_no_trackback:1,
                deletion:1,
                matched:1;
 
index edba5b79bc1435483ee2555e1ba645743721ac5d..15873bf24d72175b026e28071e83b1db7a7ed004 100644 (file)
@@ -1973,16 +1973,16 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--grep-debug")) {
                revs->grep_filter.debug = 1;
        } else if (!strcmp(arg, "--basic-regexp")) {
-               grep_set_pattern_type_option(GREP_PATTERN_TYPE_BRE, &revs->grep_filter);
+               revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_BRE;
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
-               grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, &revs->grep_filter);
+               revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.regflags |= REG_ICASE;
                DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
-               grep_set_pattern_type_option(GREP_PATTERN_TYPE_FIXED, &revs->grep_filter);
+               revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;
        } else if (!strcmp(arg, "--perl-regexp")) {
-               grep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);
+               revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_PCRE;
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
        } else if (!strcmp(arg, "--invert-grep")) {
index 299d303848384935000e60505be414c218eca0cd..90f2ac51a741f14cecab51177120cc7c3f063096 100644 (file)
@@ -260,12 +260,13 @@ static int generate_push_cert(struct strbuf *req_buf,
                              const char *push_cert_nonce)
 {
        const struct ref *ref;
+       struct string_list_item *item;
        char *signing_key = xstrdup(get_signing_key());
        const char *cp, *np;
        struct strbuf cert = STRBUF_INIT;
        int update_seen = 0;
 
-       strbuf_addf(&cert, "certificate version 0.1\n");
+       strbuf_addstr(&cert, "certificate version 0.1\n");
        strbuf_addf(&cert, "pusher %s ", signing_key);
        datestamp(&cert);
        strbuf_addch(&cert, '\n');
@@ -276,6 +277,9 @@ static int generate_push_cert(struct strbuf *req_buf,
        }
        if (push_cert_nonce[0])
                strbuf_addf(&cert, "nonce %s\n", push_cert_nonce);
+       if (args->push_options)
+               for_each_string_list_item(item, args->push_options)
+                       strbuf_addf(&cert, "push-option %s\n", item->string);
        strbuf_addstr(&cert, "\n");
 
        for (ref = remote_refs; ref; ref = ref->next) {
@@ -370,6 +374,8 @@ int send_pack(struct send_pack_args *args,
        int agent_supported = 0;
        int use_atomic = 0;
        int atomic_supported = 0;
+       int use_push_options = 0;
+       int push_options_supported = 0;
        unsigned cmds_sent = 0;
        int ret;
        struct async demux;
@@ -392,6 +398,8 @@ int send_pack(struct send_pack_args *args,
                args->use_thin_pack = 0;
        if (server_supports("atomic"))
                atomic_supported = 1;
+       if (server_supports("push-options"))
+               push_options_supported = 1;
 
        if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) {
                int len;
@@ -418,6 +426,11 @@ int send_pack(struct send_pack_args *args,
 
        use_atomic = atomic_supported && args->atomic;
 
+       if (args->push_options && !push_options_supported)
+               die(_("the receiving end does not support push options"));
+
+       use_push_options = push_options_supported && args->push_options;
+
        if (status_report)
                strbuf_addstr(&cap_buf, " report-status");
        if (use_sideband)
@@ -426,6 +439,8 @@ int send_pack(struct send_pack_args *args,
                strbuf_addstr(&cap_buf, " quiet");
        if (use_atomic)
                strbuf_addstr(&cap_buf, " atomic");
+       if (use_push_options)
+               strbuf_addstr(&cap_buf, " push-options");
        if (agent_supported)
                strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
 
@@ -512,6 +527,18 @@ int send_pack(struct send_pack_args *args,
        strbuf_release(&req_buf);
        strbuf_release(&cap_buf);
 
+       if (use_push_options) {
+               struct string_list_item *item;
+               struct strbuf sb = STRBUF_INIT;
+
+               for_each_string_list_item(item, args->push_options)
+                       packet_buf_write(&sb, "%s", item->string);
+
+               write_or_die(out, sb.buf, sb.len);
+               packet_flush(out);
+               strbuf_release(&sb);
+       }
+
        if (use_sideband && cmds_sent) {
                memset(&demux, 0, sizeof(demux));
                demux.proc = sideband_demux;
index 57f222abccd7e77dad7e9a107e44971d16db79c2..67fc40f4ec1a0847fb16535334e07bf196e8028a 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef SEND_PACK_H
 #define SEND_PACK_H
 
+#include "string-list.h"
+
 /* Possible values for push_cert field in send_pack_args. */
 #define SEND_PACK_PUSH_CERT_NEVER 0
 #define SEND_PACK_PUSH_CERT_IF_ASKED 1
@@ -21,6 +23,7 @@ struct send_pack_args {
                push_cert:2,
                stateless_rpc:1,
                atomic:1;
+       const struct string_list *push_options;
 };
 
 struct option;
index ec50519df92eb50f74db8e7db05f35d0ebb913b2..2e9c7d0f03734c20f1fb9d5c6214a7960d80b112 100644 (file)
@@ -112,7 +112,7 @@ static void remove_sequencer_state(void)
 {
        struct strbuf seq_dir = STRBUF_INIT;
 
-       strbuf_addf(&seq_dir, "%s", git_path(SEQ_DIR));
+       strbuf_addstr(&seq_dir, git_path(SEQ_DIR));
        remove_dir_recursively(&seq_dir, 0);
        strbuf_release(&seq_dir);
 }
index 5085fe03f1461fc5620ad57e5cbb8d87e1ff44cc..02940f1920300c290e922174159fd7177844adda 100644 (file)
@@ -23,6 +23,7 @@
 #include "bulk-checkin.h"
 #include "streaming.h"
 #include "dir.h"
+#include "mru.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -59,14 +60,6 @@ static struct cached_object empty_tree = {
        0
 };
 
-/*
- * A pointer to the last packed_git in which an object was found.
- * When an object is sought, we look in this packfile first, because
- * objects that are looked up at similar times are often in the same
- * packfile as one another.
- */
-static struct packed_git *last_found_pack;
-
 static struct cached_object *find_cached_object(const unsigned char *sha1)
 {
        int i;
@@ -522,6 +515,9 @@ static size_t peak_pack_mapped;
 static size_t pack_mapped;
 struct packed_git *packed_git;
 
+static struct mru packed_git_mru_storage;
+struct mru *packed_git_mru = &packed_git_mru_storage;
+
 void pack_report(void)
 {
        fprintf(stderr,
@@ -891,36 +887,6 @@ void close_pack_index(struct packed_git *p)
        }
 }
 
-/*
- * This is used by git-repack in case a newly created pack happens to
- * contain the same set of objects as an existing one.  In that case
- * the resulting file might be different even if its name would be the
- * same.  It is best to close any reference to the old pack before it is
- * replaced on disk.  Of course no index pointers or windows for given pack
- * must subsist at this point.  If ever objects from this pack are requested
- * again, the new version of the pack will be reinitialized through
- * reprepare_packed_git().
- */
-void free_pack_by_name(const char *pack_name)
-{
-       struct packed_git *p, **pp = &packed_git;
-
-       while (*pp) {
-               p = *pp;
-               if (strcmp(pack_name, p->pack_name) == 0) {
-                       clear_delta_base_cache();
-                       close_pack(p);
-                       free(p->bad_object_sha1);
-                       *pp = p->next;
-                       if (last_found_pack == p)
-                               last_found_pack = NULL;
-                       free(p);
-                       return;
-               }
-               pp = &p->next;
-       }
-}
-
 static unsigned int get_max_fd_limit(void)
 {
 #ifdef RLIMIT_NOFILE
@@ -1385,6 +1351,15 @@ static void rearrange_packed_git(void)
        free(ary);
 }
 
+static void prepare_packed_git_mru(void)
+{
+       struct packed_git *p;
+
+       mru_clear(packed_git_mru);
+       for (p = packed_git; p; p = p->next)
+               mru_append(packed_git_mru, p);
+}
+
 static int prepare_packed_git_run_once = 0;
 void prepare_packed_git(void)
 {
@@ -1400,6 +1375,7 @@ void prepare_packed_git(void)
                alt->name[-1] = '/';
        }
        rearrange_packed_git();
+       prepare_packed_git_mru();
        prepare_packed_git_run_once = 1;
 }
 
@@ -2281,7 +2257,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
 
                if (do_check_packed_object_crc && p->index_version > 1) {
                        struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
-                       unsigned long len = revidx[1].offset - obj_offset;
+                       off_t len = revidx[1].offset - obj_offset;
                        if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
                                const unsigned char *sha1 =
                                        nth_packed_object_sha1(p, revidx->nr);
@@ -2604,21 +2580,15 @@ static int fill_pack_entry(const unsigned char *sha1,
  */
 static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 {
-       struct packed_git *p;
+       struct mru_entry *p;
 
        prepare_packed_git();
        if (!packed_git)
                return 0;
 
-       if (last_found_pack && fill_pack_entry(sha1, e, last_found_pack))
-               return 1;
-
-       for (p = packed_git; p; p = p->next) {
-               if (p == last_found_pack)
-                       continue; /* we already checked this one */
-
-               if (fill_pack_entry(sha1, e, p)) {
-                       last_found_pack = p;
+       for (p = packed_git_mru->head; p; p = p->next) {
+               if (fill_pack_entry(sha1, e, p->item)) {
+                       mru_mark(packed_git_mru, p);
                        return 1;
                }
        }
index 4d554caf8d751d7a7c8a3a4286a208e0959ab513..54e2db73349ef22d644552c6b122ae5ed6ba2ce1 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -389,7 +389,7 @@ static void paint_down(struct paint_info *info, const unsigned char *sha1,
        unsigned int i, nr;
        struct commit_list *head = NULL;
        int bitmap_nr = (info->nr_bits + 31) / 32;
-       size_t bitmap_size = st_mult(bitmap_nr, sizeof(uint32_t));
+       size_t bitmap_size = st_mult(sizeof(uint32_t), bitmap_nr);
        uint32_t *tmp = xmalloc(bitmap_size); /* to be freed before return */
        uint32_t *bitmap = paint_alloc(info);
        struct commit *c = lookup_commit_reference_gently(sha1, 1);
index 077db4054f7b38b31d9c6f11e2b589e9111d4a8c..d22a851d2705fc44c608cdde1c31bea40b454ebb 100644 (file)
@@ -371,9 +371,9 @@ static int parse_config(const char *var, const char *value, void *data)
 }
 
 static int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
-                                     unsigned char *gitmodules_sha1)
+                                     unsigned char *gitmodules_sha1,
+                                     struct strbuf *rev)
 {
-       struct strbuf rev = STRBUF_INIT;
        int ret = 0;
 
        if (is_null_sha1(commit_sha1)) {
@@ -381,11 +381,10 @@ static int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
                return 1;
        }
 
-       strbuf_addf(&rev, "%s:.gitmodules", sha1_to_hex(commit_sha1));
-       if (get_sha1(rev.buf, gitmodules_sha1) >= 0)
+       strbuf_addf(rev, "%s:.gitmodules", sha1_to_hex(commit_sha1));
+       if (get_sha1(rev->buf, gitmodules_sha1) >= 0)
                ret = 1;
 
-       strbuf_release(&rev);
        return ret;
 }
 
@@ -399,7 +398,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
 {
        struct strbuf rev = STRBUF_INIT;
        unsigned long config_size;
-       char *config;
+       char *config = NULL;
        unsigned char sha1[20];
        enum object_type type;
        const struct submodule *submodule = NULL;
@@ -420,8 +419,8 @@ static const struct submodule *config_from(struct submodule_cache *cache,
                return entry->config;
        }
 
-       if (!gitmodule_sha1_from_commit(commit_sha1, sha1))
-               return NULL;
+       if (!gitmodule_sha1_from_commit(commit_sha1, sha1, &rev))
+               goto out;
 
        switch (lookup_type) {
        case lookup_name:
@@ -432,24 +431,20 @@ static const struct submodule *config_from(struct submodule_cache *cache,
                break;
        }
        if (submodule)
-               return submodule;
+               goto out;
 
        config = read_sha1_file(sha1, &type, &config_size);
-       if (!config)
-               return NULL;
-
-       if (type != OBJ_BLOB) {
-               free(config);
-               return NULL;
-       }
+       if (!config || type != OBJ_BLOB)
+               goto out;
 
        /* fill the submodule config into the cache */
        parameter.cache = cache;
        parameter.commit_sha1 = commit_sha1;
        parameter.gitmodules_sha1 = sha1;
        parameter.overwrite = 0;
-       git_config_from_mem(parse_config, "submodule-blob", rev.buf,
+       git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
                        config, config_size, &parameter);
+       strbuf_release(&rev);
        free(config);
 
        switch (lookup_type) {
@@ -460,6 +455,11 @@ static const struct submodule *config_from(struct submodule_cache *cache,
        default:
                return NULL;
        }
+
+out:
+       strbuf_release(&rev);
+       free(config);
+       return submodule;
 }
 
 static const struct submodule *config_from_path(struct submodule_cache *cache,
index c71ea4f759bf15253841bda5dd95f0783d0f7ad6..d24d157379f30cafdeeb772e82bf5ee4c862272c 100644 (file)
@@ -26,7 +26,7 @@ static int parallel_next(struct child_process *cp,
                return 0;
 
        argv_array_pushv(&cp->args, d->argv);
-       strbuf_addf(err, "preloaded output of a child\n");
+       strbuf_addstr(err, "preloaded output of a child\n");
        number_callbacks++;
        return 1;
 }
@@ -36,7 +36,7 @@ static int no_job(struct child_process *cp,
                  void *cb,
                  void **task_cb)
 {
-       strbuf_addf(err, "no further jobs available\n");
+       strbuf_addstr(err, "no further jobs available\n");
        return 0;
 }
 
@@ -45,7 +45,7 @@ static int task_finished(int result,
                         void *pp_cb,
                         void *pp_task_cb)
 {
-       strbuf_addf(err, "asking for a quick stop\n");
+       strbuf_addstr(err, "asking for a quick stop\n");
        return 1;
 }
 
index 61049b87a07c638a9211b11484c503ffc56a1f9e..2f144d539a4822619a9383ded2d3adb270484624 100644 (file)
@@ -23,7 +23,7 @@ int cmd_main(int argc, const char **argv)
 
        arg++;
        my_argc--;
-       while (starts_with(arg[0], "--")) {
+       while (arg[0] && starts_with(arg[0], "--")) {
                if (!strcmp(arg[0], "--url"))
                        output_url = 1;
                if (!strcmp(arg[0], "--name"))
index fb8823224e9ed2452c32e847bf53dbc4da116f1b..688313ed5cc40e22b2412c57f98df06e4f3ac0ea 100644 (file)
@@ -65,81 +65,22 @@ svn_cmd () {
        svn "$orig_svncmd" --config-dir "$svnconf" "$@"
 }
 
-prepare_httpd () {
-       for d in \
-               "$SVN_HTTPD_PATH" \
-               /usr/sbin/apache2 \
-               /usr/sbin/httpd \
-       ; do
-               if test -f "$d"
-               then
-                       SVN_HTTPD_PATH="$d"
-                       break
-               fi
-       done
-       if test -z "$SVN_HTTPD_PATH"
-       then
-               echo >&2 '*** error: Apache not found'
-               return 1
-       fi
-       for d in \
-               "$SVN_HTTPD_MODULE_PATH" \
-               /usr/lib/apache2/modules \
-               /usr/libexec/apache2 \
-       ; do
-               if test -d "$d"
-               then
-                       SVN_HTTPD_MODULE_PATH="$d"
-                       break
-               fi
-       done
-       if test -z "$SVN_HTTPD_MODULE_PATH"
-       then
-               echo >&2 '*** error: Apache module dir not found'
-               return 1
-       fi
-       if test ! -f "$SVN_HTTPD_MODULE_PATH/mod_dav_svn.so"
-       then
-               echo >&2 '*** error: Apache module "mod_dav_svn" not found'
-               return 1
-       fi
-
-       repo_base_path="${1-svn}"
-       mkdir "$GIT_DIR"/logs
-
-       cat > "$GIT_DIR/httpd.conf" <<EOF
-ServerName "git svn test"
-ServerRoot "$GIT_DIR"
-DocumentRoot "$GIT_DIR"
-PidFile "$GIT_DIR/httpd.pid"
-LockFile logs/accept.lock
-Listen 127.0.0.1:$SVN_HTTPD_PORT
-LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
-LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
-<Location /$repo_base_path>
-       DAV svn
-       SVNPath "$rawsvnrepo"
-</Location>
-EOF
-}
-
-start_httpd () {
-       if test -z "$SVN_HTTPD_PORT"
-       then
-               echo >&2 'SVN_HTTPD_PORT is not defined!'
-               return
-       fi
-
-       prepare_httpd "$1" || return 1
-
-       "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
-       svnrepo="http://127.0.0.1:$SVN_HTTPD_PORT/$repo_base_path"
-}
-
-stop_httpd () {
-       test -z "$SVN_HTTPD_PORT" && return
-       test ! -f "$GIT_DIR/httpd.conf" && return
-       "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
+maybe_start_httpd () {
+       loc=${1-svn}
+
+       test_tristate GIT_SVN_TEST_HTTPD
+       case $GIT_SVN_TEST_HTTPD in
+       true)
+               . "$TEST_DIRECTORY"/lib-httpd.sh
+               LIB_HTTPD_SVN="$loc"
+               start_httpd
+               ;;
+       *)
+               stop_httpd () {
+                       : noop
+               }
+               ;;
+       esac
 }
 
 convert_to_rev_db () {
index ac2cbee250887759b67898e4a2ce9d9c2708b7bc..435a37465a702c35a5d53d8809f0ef700ae9ff83 100644 (file)
@@ -24,7 +24,7 @@
 #    LIB_HTTPD_MODULE_PATH       web server modules path
 #    LIB_HTTPD_PORT              listening port
 #    LIB_HTTPD_DAV               enable DAV
-#    LIB_HTTPD_SVN               enable SVN
+#    LIB_HTTPD_SVN               enable SVN at given location (e.g. "svn")
 #    LIB_HTTPD_SSL               enable SSL
 #
 # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
@@ -162,8 +162,10 @@ prepare_httpd() {
                if test -n "$LIB_HTTPD_SVN"
                then
                        HTTPD_PARA="$HTTPD_PARA -DSVN"
-                       rawsvnrepo="$HTTPD_ROOT_PATH/svnrepo"
-                       svnrepo="http://127.0.0.1:$LIB_HTTPD_PORT/svn"
+                       LIB_HTTPD_SVNPATH="$rawsvnrepo"
+                       svnrepo="http://127.0.0.1:$LIB_HTTPD_PORT/"
+                       svnrepo="$svnrepo$LIB_HTTPD_SVN"
+                       export LIB_HTTPD_SVN LIB_HTTPD_SVNPATH
                fi
        fi
 }
index 018a83a5a18431f120672b7cd8874bfb72e8d8fb..c3e631394f4a47f32e62e266431607861929f328 100644 (file)
@@ -208,8 +208,8 @@ RewriteRule ^/half-auth-complete/ - [E=AUTHREQUIRED:yes]
 <IfDefine SVN>
        LoadModule dav_svn_module modules/mod_dav_svn.so
 
-       <Location /svn>
+       <Location /${LIB_HTTPD_SVN}>
                DAV svn
-               SVNPath svnrepo
+               SVNPath "${LIB_HTTPD_SVNPATH}"
        </Location>
 </IfDefine>
diff --git a/t/perf/p5303-many-packs.sh b/t/perf/p5303-many-packs.sh
new file mode 100755 (executable)
index 0000000..3779851
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='performance with large numbers of packs'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+# A real many-pack situation would probably come from having a lot of pushes
+# over time. We don't know how big each push would be, but we can fake it by
+# just walking the first-parent chain and having every 5 commits be their own
+# "push". This isn't _entirely_ accurate, as real pushes would have some
+# duplicate objects due to thin-pack fixing, but it's a reasonable
+# approximation.
+#
+# And then all of the rest of the objects can go in a single packfile that
+# represents the state before any of those pushes (actually, we'll generate
+# that first because in such a setup it would be the oldest pack, and we sort
+# the packs by reverse mtime inside git).
+repack_into_n () {
+       rm -rf staging &&
+       mkdir staging &&
+
+       git rev-list --first-parent HEAD |
+       sed -n '1~5p' |
+       head -n "$1" |
+       perl -e 'print reverse <>' \
+       >pushes
+
+       # create base packfile
+       head -n 1 pushes |
+       git pack-objects --delta-base-offset --revs staging/pack
+
+       # and then incrementals between each pair of commits
+       last= &&
+       while read rev
+       do
+               if test -n "$last"; then
+                       {
+                               echo "$rev" &&
+                               echo "^$last"
+                       } |
+                       git pack-objects --delta-base-offset --revs \
+                               staging/pack || return 1
+               fi
+               last=$rev
+       done <pushes &&
+
+       # and install the whole thing
+       rm -f .git/objects/pack/* &&
+       mv staging/* .git/objects/pack/
+}
+
+# Pretend we just have a single branch and no reflogs, and that everything is
+# in objects/pack; that makes our fake pack-building via repack_into_n()
+# much simpler.
+test_expect_success 'simplify reachability' '
+       tip=$(git rev-parse --verify HEAD) &&
+       git for-each-ref --format="option no-deref%0adelete %(refname)" |
+       git update-ref --stdin &&
+       rm -rf .git/logs &&
+       git update-ref refs/heads/master $tip &&
+       git symbolic-ref HEAD refs/heads/master &&
+       git repack -ad
+'
+
+for nr_packs in 1 50 1000
+do
+       test_expect_success "create $nr_packs-pack scenario" '
+               repack_into_n $nr_packs
+       '
+
+       test_perf "rev-list ($nr_packs)" '
+               git rev-list --objects --all >/dev/null
+       '
+
+       # This simulates the interesting part of the repack, which is the
+       # actual pack generation, without smudging the on-disk setup
+       # between trials.
+       test_perf "repack ($nr_packs)" '
+               git pack-objects --keep-true-parents \
+                 --honor-pack-keep --non-empty --all \
+                 --reflog --indexed-objects --delta-base-offset \
+                 --stdout </dev/null >/dev/null
+       '
+done
+
+test_done
index 4c8cf58512513848d06f759d8e86860c5606dffa..c0c910867d75368832ce8b297e9dd82ee984a85a 100755 (executable)
@@ -46,7 +46,10 @@ check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 16:13:20 +0200'
 check_show short "$TIME" '2016-06-15'
 check_show default "$TIME" 'Wed Jun 15 16:13:20 2016 +0200'
 check_show raw "$TIME" '1466000000 +0200'
+check_show unix "$TIME" '1466000000'
 check_show iso-local "$TIME" '2016-06-15 14:13:20 +0000'
+check_show raw-local "$TIME" '1466000000 +0000'
+check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
index 7bac2bcf260794bbfeb795b210ed0c9eceb45937..e799e5954437e08df4a013f9796c5ec5a9bb077d 100755 (executable)
@@ -268,4 +268,15 @@ test_expect_success 'disable filter with empty override' '
        test_must_be_empty err
 '
 
+test_expect_success 'diff does not reuse worktree files that need cleaning' '
+       test_config filter.counter.clean "echo . >>count; sed s/^/clean:/" &&
+       echo "file filter=counter" >.gitattributes &&
+       test_commit one file &&
+       test_commit two file &&
+
+       >count &&
+       git diff-tree -p HEAD &&
+       test_line_count = 0 count
+'
+
 test_done
index f9f3d1391ff496da38e0ed25780d1fdae173c91d..096dbffecc3d51478b643bd2f4dee92f507e1c1a 100755 (executable)
@@ -177,10 +177,9 @@ test_expect_success 'zip achiving, deflate' '
        git archive --format=zip HEAD >/dev/null
 '
 
-test_expect_success 'fsck' '
-       test_must_fail git fsck 2>err &&
-       n=$(grep "error: attempting to allocate .* over limit" err | wc -l) &&
-       test "$n" -gt 1
+test_expect_success 'fsck large blobs' '
+       git fsck 2>err &&
+       test_must_be_empty err
 '
 
 test_done
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
new file mode 100755 (executable)
index 0000000..8298aaf
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='test git worktree move, remove, lock and unlock'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit init &&
+       git worktree add source &&
+       git worktree list --porcelain | grep "^worktree" >actual &&
+       cat <<-EOF >expected &&
+       worktree $(pwd)
+       worktree $(pwd)/source
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'lock main worktree' '
+       test_must_fail git worktree lock .
+'
+
+test_expect_success 'lock linked worktree' '
+       git worktree lock --reason hahaha source &&
+       echo hahaha >expected &&
+       test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock linked worktree from another worktree' '
+       rm .git/worktrees/source/locked &&
+       git worktree add elsewhere &&
+       git -C elsewhere worktree lock --reason hahaha ../source &&
+       echo hahaha >expected &&
+       test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice' '
+       test_must_fail git worktree lock source &&
+       echo hahaha >expected &&
+       test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice (from the locked worktree)' '
+       test_must_fail git -C source worktree lock . &&
+       echo hahaha >expected &&
+       test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'unlock main worktree' '
+       test_must_fail git worktree unlock .
+'
+
+test_expect_success 'unlock linked worktree' '
+       git worktree unlock source &&
+       test_path_is_missing .git/worktrees/source/locked
+'
+
+test_expect_success 'unlock worktree twice' '
+       test_must_fail git worktree unlock source &&
+       test_path_is_missing .git/worktrees/source/locked
+'
+
+test_done
index 3532c482fc5270675ccf6efe3a2d805d17a82466..197914bbd8f36097a6f25e85470a0b2725fac193 100755 (executable)
@@ -1281,4 +1281,12 @@ test_expect_success 'editor saves as CR/LF' '
        )
 '
 
+SQ="'"
+test_expect_success 'rebase -i --gpg-sign=<key-id>' '
+       set_fake_editor &&
+       FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" HEAD^ \
+               >out 2>err &&
+       grep "$SQ-S\"S I Gner\"$SQ" err
+'
+
 test_done
index 8f53e54ce4176fd0697e8af60a105d40dc161a12..48346f1cc0c12883029349e4999d7e6697b35adb 100755 (executable)
@@ -271,4 +271,37 @@ test_expect_success 'autosquash with custom inst format' '
        test 2 = $(git cat-file commit HEAD^ | grep squash | wc -l)
 '
 
+set_backup_editor () {
+       write_script backup-editor.sh <<-\EOF
+       cp "$1" .git/backup-"$(basename "$1")"
+       EOF
+       test_set_editor "$PWD/backup-editor.sh"
+}
+
+test_expect_failure 'autosquash with multiple empty patches' '
+       test_tick &&
+       git commit --allow-empty -m "empty" &&
+       test_tick &&
+       git commit --allow-empty -m "empty2" &&
+       test_tick &&
+       >fixup &&
+       git add fixup &&
+       git commit --fixup HEAD^^ &&
+       (
+               set_backup_editor &&
+               GIT_USE_REBASE_HELPER=false \
+               git rebase -i --force-rebase --autosquash HEAD~4 &&
+               grep empty2 .git/backup-git-rebase-todo
+       )
+'
+
+test_expect_success 'extra spaces after fixup!' '
+       base=$(git rev-parse HEAD) &&
+       test_commit to-fixup &&
+       git commit --allow-empty -m "fixup!  to-fixup" &&
+       git rebase -i --autosquash --keep-empty HEAD~2 &&
+       parent=$(git rev-parse HEAD^) &&
+       test $base = $parent
+'
+
 test_done
index 4865304ebb8560d40fd993dc13a47a2ed4bf9e1b..2978cb9d640d1e826e8fb9b96e99bc69ee86f09d 100755 (executable)
@@ -7,6 +7,20 @@ test_description='Test of git add, including the -- option.'
 
 . ./test-lib.sh
 
+# Test the file mode "$1" of the file "$2" in the index.
+test_mode_in_index () {
+       case "$(git ls-files -s "$2")" in
+       "$1 "*" $2")
+               echo pass
+               ;;
+       *)
+               echo fail
+               git ls-files -s "$2"
+               return 1
+               ;;
+       esac
+}
+
 test_expect_success \
     'Test of git add' \
     'touch foo && git add foo'
@@ -25,18 +39,12 @@ test_expect_success \
         echo foo >xfoo1 &&
         chmod 755 xfoo1 &&
         git add xfoo1 &&
-        case "$(git ls-files --stage xfoo1)" in
-        100644" "*xfoo1) echo pass;;
-        *) echo fail; git ls-files --stage xfoo1; (exit 1);;
-        esac'
+        test_mode_in_index 100644 xfoo1'
 
 test_expect_success 'git add: filemode=0 should not get confused by symlink' '
        rm -f xfoo1 &&
        test_ln_s_add foo xfoo1 &&
-       case "$(git ls-files --stage xfoo1)" in
-       120000" "*xfoo1) echo pass;;
-       *) echo fail; git ls-files --stage xfoo1; (exit 1);;
-       esac
+       test_mode_in_index 120000 xfoo1
 '
 
 test_expect_success \
@@ -45,28 +53,19 @@ test_expect_success \
         echo foo >xfoo2 &&
         chmod 755 xfoo2 &&
         git update-index --add xfoo2 &&
-        case "$(git ls-files --stage xfoo2)" in
-        100644" "*xfoo2) echo pass;;
-        *) echo fail; git ls-files --stage xfoo2; (exit 1);;
-        esac'
+        test_mode_in_index 100644 xfoo2'
 
 test_expect_success 'git add: filemode=0 should not get confused by symlink' '
        rm -f xfoo2 &&
        test_ln_s_add foo xfoo2 &&
-       case "$(git ls-files --stage xfoo2)" in
-       120000" "*xfoo2) echo pass;;
-       *) echo fail; git ls-files --stage xfoo2; (exit 1);;
-       esac
+       test_mode_in_index 120000 xfoo2
 '
 
 test_expect_success \
        'git update-index --add: Test that executable bit is not used...' \
        'git config core.filemode 0 &&
         test_ln_s_add xfoo2 xfoo3 &&   # runs git update-index --add
-        case "$(git ls-files --stage xfoo3)" in
-        120000" "*xfoo3) echo pass;;
-        *) echo fail; git ls-files --stage xfoo3; (exit 1);;
-        esac'
+        test_mode_in_index 120000 xfoo3'
 
 test_expect_success '.gitignore test setup' '
        echo "*.ig" >.gitignore &&
@@ -332,34 +331,22 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file out
        test_i18ncmp expect.err actual.err
 '
 
-test_expect_success 'git add --chmod=+x stages a non-executable file with +x' '
+test_expect_success 'git add --chmod=[+-]x stages correctly' '
+       rm -f foo1 &&
        echo foo >foo1 &&
        git add --chmod=+x foo1 &&
-       case "$(git ls-files --stage foo1)" in
-       100755" "*foo1) echo pass;;
-       *) echo fail; git ls-files --stage foo1; (exit 1);;
-       esac
-'
-
-test_expect_success 'git add --chmod=-x stages an executable file with -x' '
-       echo foo >xfoo1 &&
-       chmod 755 xfoo1 &&
-       git add --chmod=-x xfoo1 &&
-       case "$(git ls-files --stage xfoo1)" in
-       100644" "*xfoo1) echo pass;;
-       *) echo fail; git ls-files --stage xfoo1; (exit 1);;
-       esac
+       test_mode_in_index 100755 foo1 &&
+       git add --chmod=-x foo1 &&
+       test_mode_in_index 100644 foo1
 '
 
 test_expect_success POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
        git config core.filemode 1 &&
        git config core.symlinks 1 &&
+       rm -f foo2 &&
        echo foo >foo2 &&
        git add --chmod=+x foo2 &&
-       case "$(git ls-files --stage foo2)" in
-       100755" "*foo2) echo pass;;
-       *) echo fail; git ls-files --stage foo2; (exit 1);;
-       esac
+       test_mode_in_index 100755 foo2
 '
 
 test_done
index 1206c48392c36db04ef422813e76b370eeef20b2..b0579dd45242f64d129d3c10e8ae1c3de6cc5458 100755 (executable)
@@ -229,6 +229,46 @@ check_patch () {
        grep -e "^Subject:" "$1"
 }
 
+test_expect_success 'format.from=false' '
+
+       git -c format.from=false format-patch --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       ! grep "^From: C O Mitter <committer@example.com>\$" patch
+'
+
+test_expect_success 'format.from=true' '
+
+       git -c format.from=true format-patch --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       grep "^From: C O Mitter <committer@example.com>\$" patch
+'
+
+test_expect_success 'format.from with address' '
+
+       git -c format.from="F R Om <from@example.com>" format-patch --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       grep "^From: F R Om <from@example.com>\$" patch
+'
+
+test_expect_success '--no-from overrides format.from' '
+
+       git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       ! grep "^From: F R Om <from@example.com>\$" patch
+'
+
+test_expect_success '--from overrides format.from' '
+
+       git -c format.from="F R Om <from@example.com>" format-patch --from --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       ! grep "^From: F R Om <from@example.com>\$" patch
+'
+
 test_expect_success '--no-to overrides config.to' '
 
        git config --replace-all format.to \
index d173acde0f2c44031003144fda9770f4b1e726b4..f8a313bcb98c6e2b98982295773bdc8ac7e13256 100755 (executable)
@@ -13,9 +13,13 @@ create_file() {
 }
 
 test_expect_success 'setup' '
-       create_file file1 "File1 contents" &&
-       create_file file2 "File2 contents" &&
-       create_file file3 "File3 contents" &&
+       # Ensure that file sizes are different, because on Windows
+       # lstat() does not discover inode numbers, and we need
+       # other properties to discover swapped files
+       # (mtime is not always different, either).
+       create_file file1 "some content" &&
+       create_file file2 "some other content" &&
+       create_file file3 "again something else" &&
        git add file1 file2 file3 &&
        git commit -m 1
 '
index 803e1e6b8fc5633b4d33c5749226b1aa506a8152..e2db47c36e09e3580c53867909cd3e20bdf320f5 100755 (executable)
@@ -255,6 +255,20 @@ test_expect_success 'log -F -E --grep=<ere> uses ere' '
        test_cmp expect actual
 '
 
+test_expect_success 'log with grep.patternType configuration' '
+       >expect &&
+       git -c grep.patterntype=fixed \
+       log -1 --pretty=tformat:%s --grep=s.c.nd >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log with grep.patternType configuration and command line' '
+       echo second >expect &&
+       git -c grep.patterntype=fixed \
+       log -1 --pretty=tformat:%s --basic-regexp --grep=s.c.nd >actual &&
+       test_cmp expect actual
+'
+
 cat > expect <<EOF
 * Second
 * sixth
index d9f62425b052b4c136ce1f961d96b5063ce9a158..f5435fd250baf7415fb6a205f4497cb5c118de65 100755 (executable)
@@ -145,199 +145,199 @@ test_expect_success 'setup more commits' '
 
 test_expect_success 'left alignment formatting' '
        git log --pretty="tformat:%<(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-message two                            Z
-message one                            Z
-add bar                                Z
-$(commit_msg)                    Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       message two                            Z
+       message one                            Z
+       add bar                                Z
+       $(commit_msg)                    Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two                            Z
-message one                            Z
-add bar                                Z
-$(commit_msg)                    Z
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       message two                            Z
+       message one                            Z
+       add bar                                Z
+       $(commit_msg)                    Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting at the nth column' '
        git log --pretty="tformat:%h %<|(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1 message two                    Z
-$head2 message one                    Z
-$head3 add bar                        Z
-$head4 $(commit_msg)            Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1 message two                    Z
+       $head2 message one                    Z
+       $head3 add bar                        Z
+       $head4 $(commit_msg)            Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting at the nth column' '
        COLUMNS=50 git log --pretty="tformat:%h %<|(-10)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1 message two                    Z
-$head2 message one                    Z
-$head3 add bar                        Z
-$head4 $(commit_msg)            Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1 message two                    Z
+       $head2 message one                    Z
+       $head3 add bar                        Z
+       $head4 $(commit_msg)            Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting at the nth column. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %<|(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-$head1 message two                    Z
-$head2 message one                    Z
-$head3 add bar                        Z
-$head4 $(commit_msg)            Z
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       $head1 message two                    Z
+       $head2 message one                    Z
+       $head3 add bar                        Z
+       $head4 $(commit_msg)            Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with no padding' '
        git log --pretty="tformat:%<(1)%s" >actual &&
-       cat <<EOF >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with no padding. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(1)%s" >actual &&
-       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with trunc' '
        git log --pretty="tformat:%<(10,trunc)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-message ..
-message ..
-add bar  Z
-initial...
-EOF
+       qz_to_tab_space <<-\EOF >expected &&
+       message ..
+       message ..
+       add bar  Z
+       initial...
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with trunc. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message ..
-message ..
-add bar  Z
-initial...
-EOF
+       qz_to_tab_space <<-\EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       message ..
+       message ..
+       add bar  Z
+       initial...
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with ltrunc' '
        git log --pretty="tformat:%<(10,ltrunc)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-..sage two
-..sage one
-add bar  Z
-..${sample_utf8_part}lich
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       ..sage two
+       ..sage one
+       add bar  Z
+       ..${sample_utf8_part}lich
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with ltrunc. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,ltrunc)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-..sage two
-..sage one
-add bar  Z
-..${sample_utf8_part}lich
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       ..sage two
+       ..sage one
+       add bar  Z
+       ..${sample_utf8_part}lich
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with mtrunc' '
        git log --pretty="tformat:%<(10,mtrunc)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-mess.. two
-mess.. one
-add bar  Z
-init..lich
-EOF
+       qz_to_tab_space <<-\EOF >expected &&
+       mess.. two
+       mess.. one
+       add bar  Z
+       init..lich
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with mtrunc. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,mtrunc)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-mess.. two
-mess.. one
-add bar  Z
-init..lich
-EOF
+       qz_to_tab_space <<-\EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       mess.. two
+       mess.. one
+       add bar  Z
+       init..lich
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting' '
        git log --pretty="tformat:%>(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-Z                            message two
-Z                            message one
-Z                                add bar
-Z                    $(commit_msg)
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       Z                            message two
+       Z                            message one
+       Z                                add bar
+       Z                    $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-Z                            message two
-Z                            message one
-Z                                add bar
-Z                    $(commit_msg)
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       Z                            message two
+       Z                            message one
+       Z                                add bar
+       Z                    $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting at the nth column' '
        git log --pretty="tformat:%h %>|(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1                      message two
-$head2                      message one
-$head3                          add bar
-$head4              $(commit_msg)
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1                      message two
+       $head2                      message one
+       $head3                          add bar
+       $head4              $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting at the nth column' '
        COLUMNS=50 git log --pretty="tformat:%h %>|(-10)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1                      message two
-$head2                      message one
-$head3                          add bar
-$head4              $(commit_msg)
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1                      message two
+       $head2                      message one
+       $head3                          add bar
+       $head4              $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting at the nth column. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %>|(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-$head1                      message two
-$head2                      message one
-$head3                          add bar
-$head4              $(commit_msg)
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       $head1                      message two
+       $head2                      message one
+       $head3                          add bar
+       $head4              $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
@@ -345,110 +345,110 @@ EOF
 # as in previous test.
 test_expect_success 'right alignment formatting at the nth column with --graph. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --graph --pretty="tformat:%h %>|(40)%s" >actual &&
-       iconv -f utf-8 -t $test_encoding >expected <<EOF&&
-* $head1                    message two
-* $head2                    message one
-* $head3                        add bar
-* $head4            $(commit_msg)
-EOF
+       iconv -f utf-8 -t $test_encoding >expected <<-EOF &&
+       * $head1                    message two
+       * $head2                    message one
+       * $head3                        add bar
+       * $head4            $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting with no padding' '
        git log --pretty="tformat:%>(1)%s" >actual &&
-       cat <<EOF >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting with no padding and with --graph' '
        git log --graph --pretty="tformat:%>(1)%s" >actual &&
-       cat <<EOF >expected &&
-* message two
-* message one
-* add bar
-* $(commit_msg)
-EOF
+       cat <<-EOF >expected &&
+       * message two
+       * message one
+       * add bar
+       * $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting with no padding. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(1)%s" >actual &&
-       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'center alignment formatting' '
        git log --pretty="tformat:%><(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-Z             message two              Z
-Z             message one              Z
-Z               add bar                Z
-Z         $(commit_msg)          Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       Z             message two              Z
+       Z             message one              Z
+       Z               add bar                Z
+       Z         $(commit_msg)          Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'center alignment formatting. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-Z             message two              Z
-Z             message one              Z
-Z               add bar                Z
-Z         $(commit_msg)          Z
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       Z             message two              Z
+       Z             message one              Z
+       Z               add bar                Z
+       Z         $(commit_msg)          Z
+       EOF
        test_cmp expected actual
 '
 test_expect_success 'center alignment formatting at the nth column' '
        git log --pretty="tformat:%h %><|(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1           message two          Z
-$head2           message one          Z
-$head3             add bar            Z
-$head4       $(commit_msg)      Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1           message two          Z
+       $head2           message one          Z
+       $head3             add bar            Z
+       $head4       $(commit_msg)      Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'center alignment formatting at the nth column' '
        COLUMNS=70 git log --pretty="tformat:%h %><|(-30)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1           message two          Z
-$head2           message one          Z
-$head3             add bar            Z
-$head4       $(commit_msg)      Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1           message two          Z
+       $head2           message one          Z
+       $head3             add bar            Z
+       $head4       $(commit_msg)      Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'center alignment formatting at the nth column. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %><|(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-$head1           message two          Z
-$head2           message one          Z
-$head3             add bar            Z
-$head4       $(commit_msg)      Z
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       $head1           message two          Z
+       $head2           message one          Z
+       $head3             add bar            Z
+       $head4       $(commit_msg)      Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'center alignment formatting with no padding' '
        git log --pretty="tformat:%><(1)%s" >actual &&
-       cat <<EOF >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
@@ -457,34 +457,34 @@ EOF
 old_head1=$(git rev-parse --verify HEAD~0)
 test_expect_success 'center alignment formatting with no padding. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(1)%s" >actual &&
-       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left/right alignment formatting with stealing' '
        git commit --amend -m short --author "long long long <long@me.com>" &&
        git log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
-       cat <<EOF >expected &&
-short long  long long
-message ..   A U Thor
-add bar      A U Thor
-initial...   A U Thor
-EOF
+       cat <<-\EOF >expected &&
+       short long  long long
+       message ..   A U Thor
+       add bar      A U Thor
+       initial...   A U Thor
+       EOF
        test_cmp expected actual
 '
 test_expect_success 'left/right alignment formatting with stealing. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
-       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-short long  long long
-message ..   A U Thor
-add bar      A U Thor
-initial...   A U Thor
-EOF
+       cat <<-\EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       short long  long long
+       message ..   A U Thor
+       add bar      A U Thor
+       initial...   A U Thor
+       EOF
        test_cmp expected actual
 '
 
@@ -504,8 +504,10 @@ test_expect_success 'ISO and ISO-strict date formats display the same values' '
 '
 
 # get new digests (with no abbreviations)
-head1=$(git rev-parse --verify HEAD~0) &&
-head2=$(git rev-parse --verify HEAD~1) &&
+test_expect_success 'set up log decoration tests' '
+       head1=$(git rev-parse --verify HEAD~0) &&
+       head2=$(git rev-parse --verify HEAD~1)
+'
 
 test_expect_success 'log decoration properly follows tag chain' '
        git tag -a tag1 -m tag1 &&
@@ -513,22 +515,22 @@ test_expect_success 'log decoration properly follows tag chain' '
        git tag -d tag1 &&
        git commit --amend -m shorter &&
        git log --no-walk --tags --pretty="%H %d" --decorate=full >actual &&
-       cat <<EOF >expected &&
-$head1  (tag: refs/tags/tag2)
-$head2  (tag: refs/tags/message-one)
-$old_head1  (tag: refs/tags/message-two)
-EOF
+       cat <<-EOF >expected &&
+       $head1  (tag: refs/tags/tag2)
+       $head2  (tag: refs/tags/message-one)
+       $old_head1  (tag: refs/tags/message-two)
+       EOF
        sort actual >actual1 &&
        test_cmp expected actual1
 '
 
 test_expect_success 'clean log decoration' '
        git log --no-walk --tags --pretty="%H %D" --decorate=full >actual &&
-       cat >expected <<EOF &&
-$head1 tag: refs/tags/tag2
-$head2 tag: refs/tags/message-one
-$old_head1 tag: refs/tags/message-two
-EOF
+       cat >expected <<-EOF &&
+       $head1 tag: refs/tags/tag2
+       $head2 tag: refs/tags/message-one
+       $old_head1 tag: refs/tags/message-two
+       EOF
        sort actual >actual1 &&
        test_cmp expected actual1
 '
index f8008b6a3d2a5fc200665998288d121d1c35dd25..b972296f0634144defe5a7a083c5607893a7ca10 100755 (executable)
@@ -44,7 +44,7 @@ test_expect_success setup '
 '
 
 cat >expected <<EOF
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}${c_commit} ->\
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD ->\
  ${c_reset}${c_branch}master${c_reset}${c_commit},\
  ${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit},\
  ${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
index 6bd4853079ea3215edaf2c409d8ae0548f5efe3a..668c54be41eb4fda17e9b43000081c7b374990c2 100755 (executable)
@@ -688,7 +688,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '
        )
 '
 
-test_expect_success 'fetch aligned output' '
+test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '
        git clone . full-output &&
        test_commit looooooooooooong-tag &&
        (
@@ -703,7 +703,7 @@ test_expect_success 'fetch aligned output' '
        test_cmp expect actual
 '
 
-test_expect_success 'fetch compact output' '
+test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '
        git clone . compact &&
        test_commit extraaa &&
        (
index c7320121ecfa74dce8389d3790364501c8bb12b8..a2c9e7439f362d8f247c369ba515a55b26dee8ec 100755 (executable)
@@ -191,4 +191,42 @@ test_expect_success 'cover everything with default force-with-lease (allowed)' '
        test_cmp expect actual
 '
 
+test_expect_success 'new branch covered by force-with-lease' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               git branch branch master &&
+               git push --force-with-lease=branch origin branch
+       ) &&
+       git ls-remote dst refs/heads/branch >expect &&
+       git ls-remote src refs/heads/branch >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'new branch covered by force-with-lease (explicit)' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               git branch branch master &&
+               git push --force-with-lease=branch: origin branch
+       ) &&
+       git ls-remote dst refs/heads/branch >expect &&
+       git ls-remote src refs/heads/branch >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'new branch already exists' '
+       setup_srcdst_basic &&
+       (
+               cd src &&
+               git checkout -b branch master &&
+               test_commit F
+       ) &&
+       (
+               cd dst &&
+               git branch branch master &&
+               test_must_fail git push --force-with-lease=branch: origin branch
+       )
+'
+
 test_done
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
new file mode 100755 (executable)
index 0000000..ea813b9
--- /dev/null
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='pushing to a repository using push options'
+
+. ./test-lib.sh
+
+mk_repo_pair () {
+       rm -rf workbench upstream &&
+       test_create_repo upstream &&
+       test_create_repo workbench &&
+       (
+               cd upstream &&
+               git config receive.denyCurrentBranch warn &&
+               mkdir -p .git/hooks &&
+               cat >.git/hooks/pre-receive <<-'EOF' &&
+               #!/bin/sh
+               if test -n "$GIT_PUSH_OPTION_COUNT"; then
+                       i=0
+                       >hooks/pre-receive.push_options
+                       while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"; do
+                               eval "value=\$GIT_PUSH_OPTION_$i"
+                               echo $value >>hooks/pre-receive.push_options
+                               i=$((i + 1))
+                       done
+               fi
+               EOF
+               chmod u+x .git/hooks/pre-receive
+
+               cat >.git/hooks/post-receive <<-'EOF' &&
+               #!/bin/sh
+               if test -n "$GIT_PUSH_OPTION_COUNT"; then
+                       i=0
+                       >hooks/post-receive.push_options
+                       while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"; do
+                               eval "value=\$GIT_PUSH_OPTION_$i"
+                               echo $value >>hooks/post-receive.push_options
+                               i=$((i + 1))
+                       done
+               fi
+               EOF
+               chmod u+x .git/hooks/post-receive
+       ) &&
+       (
+               cd workbench &&
+               git remote add up ../upstream
+       )
+}
+
+# Compare the ref ($1) in upstream with a ref value from workbench ($2)
+# i.e. test_refs second HEAD@{2}
+test_refs () {
+       test $# = 2 &&
+       git -C upstream rev-parse --verify "$1" >expect &&
+       git -C workbench rev-parse --verify "$2" >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success 'one push option works for a single branch' '
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions true &&
+       (
+               cd workbench &&
+               test_commit one &&
+               git push --mirror up &&
+               test_commit two &&
+               git push --push-option=asdf up master
+       ) &&
+       test_refs master master &&
+       echo "asdf" >expect &&
+       test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+       test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'push option denied by remote' '
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions false &&
+       (
+               cd workbench &&
+               test_commit one &&
+               git push --mirror up &&
+               test_commit two &&
+               test_must_fail git push --push-option=asdf up master
+       ) &&
+       test_refs master HEAD@{1}
+'
+
+test_expect_success 'two push options work' '
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions true &&
+       (
+               cd workbench &&
+               test_commit one &&
+               git push --mirror up &&
+               test_commit two &&
+               git push --push-option=asdf --push-option="more structured text" up master
+       ) &&
+       test_refs master master &&
+       printf "asdf\nmore structured text\n" >expect &&
+       test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+       test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_done
index e4fc5c826c2c02d540c64ac88374f8ad85869023..c8dc665f2fdd0f0452779e6f8576395f8b3bdf11 100755 (executable)
@@ -49,6 +49,19 @@ test_expect_success TTY 'LESS and LV envvars are set for pagination' '
        grep ^LV= pager-env.out
 '
 
+test_expect_success !MINGW,TTY 'LESS and LV envvars set by git-sh-setup' '
+       (
+               sane_unset LESS LV &&
+               PAGER="env >pager-env.out; wc" &&
+               export PAGER &&
+               PATH="$(git --exec-path):$PATH" &&
+               export PATH &&
+               test_terminal sh -c ". git-sh-setup && git_pager"
+       ) &&
+       grep ^LESS= pager-env.out &&
+       grep ^LV= pager-env.out
+'
+
 test_expect_success TTY 'some commands do not use a pager' '
        rm -f paginated.out &&
        test_terminal git rev-list HEAD &&
index 44bf1d84af574509c5a8f6f05721d7c4f9719439..4d17363a926c8938c6c8d78e997a53ffb81b3c6c 100755 (executable)
@@ -34,6 +34,7 @@ test_expect_success 'M/D conflict does not segfault' '
 On branch side
 You have unmerged paths.
   (fix conflicts and run "git commit")
+  (use "git merge --abort" to abort the merge)
 
 Unmerged paths:
   (use "git add/rm <file>..." as appropriate to mark resolution)
@@ -138,6 +139,7 @@ test_expect_success 'status when conflicts with add and rm advice (deleted by th
 On branch master
 You have unmerged paths.
   (fix conflicts and run "git commit")
+  (use "git merge --abort" to abort the merge)
 
 Unmerged paths:
   (use "git add/rm <file>..." as appropriate to mark resolution)
@@ -171,6 +173,7 @@ test_expect_success 'status when conflicts with add and rm advice (both deleted)
 On branch conflict_second
 You have unmerged paths.
   (fix conflicts and run "git commit")
+  (use "git merge --abort" to abort the merge)
 
 Unmerged paths:
   (use "git add/rm <file>..." as appropriate to mark resolution)
@@ -195,6 +198,7 @@ test_expect_success 'status when conflicts with only rm advice (both deleted)' '
 On branch conflict_second
 You have unmerged paths.
   (fix conflicts and run "git commit")
+  (use "git merge --abort" to abort the merge)
 
 Changes to be committed:
 
index c23a21cb74d56426a9948c0a37db512b093eaa57..0667bd9dd3e8bc650bc4e47ac9fcde5d63ef0d11 100755 (executable)
@@ -4,6 +4,20 @@ test_description='test untracked cache'
 
 . ./test-lib.sh
 
+# On some filesystems (e.g. FreeBSD's ext2 and ufs) directory mtime
+# is updated lazily after contents in the directory changes, which
+# forces the untracked cache code to take the slow path.  A test
+# that wants to make sure that the fast path works correctly should
+# call this helper to make mtime of the containing directory in sync
+# with the reality before checking the fast path behaviour.
+#
+# See <20160803174522.5571-1-pclouds@gmail.com> if you want to know
+# more.
+
+sync_mtime () {
+       find . -type d -ls >/dev/null
+}
+
 avoid_racy() {
        sleep 1
 }
@@ -416,7 +430,8 @@ test_expect_success 'create/modify files, some of which are gitignored' '
        echo four >done/four && # four is gitignored at a higher level
        echo five >done/five && # five is not gitignored
        echo test >base && #we need to ensure that the root dir is touched
-       rm base
+       rm base &&
+       sync_mtime
 '
 
 test_expect_success 'test sparse status with untracked cache' '
index fc97c3314e2d89e11b4a846457f5e183f4fc42c0..400e2b1439a9e7fa329f78b8c5d600f34cf375d2 100755 (executable)
@@ -82,6 +82,17 @@ test_expect_success 'error in one submodule config lets continue' '
        )
 '
 
+test_expect_success 'error message contains blob reference' '
+       (cd super &&
+               sha1=$(git rev-parse HEAD) &&
+               test-submodule-config \
+                       HEAD b \
+                       HEAD submodule \
+                               2>actual_err &&
+               grep "submodule-blob $sha1:.gitmodules" actual_err >/dev/null
+       )
+'
+
 cat >super/expect_url <<EOF
 Submodule url: 'git@somewhere.else.net:a.git' for path 'b'
 Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
index 49d19a3b36d0649dfd01de29acdbf0fab2532e17..5c3db656dfaa0171598445720b62e63f13f67f44 100755 (executable)
@@ -29,6 +29,7 @@ test_expect_success 'status when conflicts unresolved' '
 On branch conflicts
 You have unmerged paths.
   (fix conflicts and run "git commit")
+  (use "git merge --abort" to abort the merge)
 
 Unmerged paths:
   (use "git add <file>..." to mark resolution)
index 42a2929835b5c77e5b3031ce56ad1243afb56971..2974900578d3f21b57d5773973344a1743e8e6e0 100755 (executable)
@@ -412,6 +412,20 @@ run_dir_diff_test 'difftool --dir-diff from subdirectory' '
        )
 '
 
+run_dir_diff_test 'difftool --dir-diff from subdirectory with GIT_DIR set' '
+       (
+               GIT_DIR=$(pwd)/.git &&
+               export GIT_DIR &&
+               GIT_WORK_TREE=$(pwd) &&
+               export GIT_WORK_TREE &&
+               cd sub &&
+               git difftool --dir-diff $symlinks --extcmd ls \
+                       branch -- sub >output &&
+               grep sub output &&
+               ! grep file output
+       )
+'
+
 run_dir_diff_test 'difftool --dir-diff when worktree file is missing' '
        test_when_finished git reset --hard &&
        rm file2 &&
index 28082b134fd54aafa38a4ac2964fd4c367e19fa2..92a3aa8063f810bd2b8df68f3f0f30c2faf3bdc6 100755 (executable)
@@ -8,8 +8,6 @@ GIT_SVN_LC_ALL=${LC_ALL:-$LANG}
 
 . ./lib-git-svn.sh
 
-say 'define NO_SVN_TESTS to skip git svn tests'
-
 case "$GIT_SVN_LC_ALL" in
 *.UTF-8)
        test_set_prereq UTF8
@@ -19,6 +17,27 @@ case "$GIT_SVN_LC_ALL" in
        ;;
 esac
 
+deepdir=nothing-above
+ceiling=$PWD
+
+test_expect_success 'git svn --version works anywhere' '
+       mkdir -p "$deepdir" && (
+               GIT_CEILING_DIRECTORIES="$ceiling" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd "$deepdir" &&
+               git svn --version
+       )
+'
+
+test_expect_success 'git svn help works anywhere' '
+       mkdir -p "$deepdir" && (
+               GIT_CEILING_DIRECTORIES="$ceiling" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd "$deepdir" &&
+               git svn help
+       )
+'
+
 test_expect_success \
     'initialize git svn' '
        mkdir import &&
index a87d3d3fc1e2d6dca0fc6280e33637d2589d3235..64bb495834698c8438c8b8dde488873072abc1fd 100755 (executable)
@@ -8,9 +8,10 @@ test_description='git svn dcommit can commit renames of files with ugly names'
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
-       svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9115/funky-names.dump &&
-       start_httpd gtk+
-       '
+       svnadmin load -q "$rawsvnrepo" <"$TEST_DIRECTORY"/t9115/funky-names.dump
+'
+
+maybe_start_httpd gtk+
 
 test_expect_success 'init and fetch repository' '
        git svn init "$svnrepo" &&
index ecb1fed147d206c7d1a3ac43f9d82110e9514e66..41a026637fa03174765d4e3facc3815bbf538e29 100755 (executable)
@@ -32,7 +32,7 @@ test_expect_success 'setup svnrepo' '
                        "$svnrepo/pr ject/branches/trailing_dotlock.lock" &&
        svn_cmd cp -m "reflog" "$svnrepo/pr ject/trunk" \
                        "$svnrepo/pr ject/branches/not-a@{0}reflog@" &&
-       start_httpd
+       maybe_start_httpd
        '
 
 # SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
index 59465b147eb9cdc7da1fb891ff695cbc4442209a..b28a1741e3f52296dcab35ed6d51fa01c2628652 100755 (executable)
@@ -15,7 +15,7 @@ test_expect_success 'setup svnrepo' '
        svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \
          "$svnrepo/pr ject/tags/v1" &&
        rm -rf project &&
-       start_httpd
+       maybe_start_httpd
 '
 
 test_expect_success 'test clone with percent escapes' '
index e21ee5f663ce8333625c5d9d483c42dde3394675..9ee23be64003ab22277c80a6c4b364c8cc36f902 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success 'setup test repository' '
                svn_cmd add foo &&
                svn_cmd commit -m "add foo"
        ) &&
-       start_httpd
+       maybe_start_httpd
 '
 
 test_expect_success 'clone trunk with "-r HEAD"' '
index 13f78f2682602014711714a09a8f675c3eed1ff0..a875b4510270aa8411ab5ee546223be49a1fd65b 100755 (executable)
@@ -7,8 +7,6 @@ test_description='git svn mergeinfo propagation'
 
 . ./lib-git-svn.sh
 
-say 'define NO_SVN_TESTS to skip git svn tests'
-
 test_expect_success 'initialize source svn repo' '
        svn_cmd mkdir -m x "$svnrepo"/trunk &&
        svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
index b4a443460476bdeb783963e5a3b488b7ef6f4ad2..0ede3cfedb2a7b5ac66238a61615395001885a94 100755 (executable)
@@ -11,7 +11,6 @@ local Git repository with placeholder files.'
 
 . ./lib-git-svn.sh
 
-say 'define NO_SVN_TESTS to skip git svn tests'
 GIT_REPO=git-svn-repo
 
 test_expect_success 'initialize source svn repo containing empty dirs' '
diff --git a/templates/hooks--pre-receive.sample b/templates/hooks--pre-receive.sample
new file mode 100644 (file)
index 0000000..a1fd29e
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to make use of push options.
+# The example simply echoes all push options that start with 'echoback='
+# and rejects all pushes when the "reject" push option is used.
+#
+# To enable this hook, rename this file to "pre-receive".
+
+if test -n "$GIT_PUSH_OPTION_COUNT"
+then
+       i=0
+       while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
+       do
+               eval "value=\$GIT_PUSH_OPTION_$i"
+               case "$value" in
+               echoback=*)
+                       echo "echo from the pre-receive-hook: ${value#*=}" >&2
+                       ;;
+               reject)
+                       exit 1
+               esac
+               i=$((i + 1))
+       done
+fi
index 04d945425a495b9e230a77fc47fcebd482cd97b4..c5772a14eeec3d5651796c8051443dd76583c05d 100644 (file)
@@ -513,6 +513,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
        args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
        args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
+       args.push_options = transport->push_options;
        args.url = transport->url;
 
        if (flags & TRANSPORT_PUSH_CERT_ALWAYS)
index c68140892c6258925104f7dda385e635fcf95e20..6fe3485325dfccfba3b018c8c25b89a6d4643a12 100644 (file)
@@ -48,6 +48,12 @@ struct transport {
         */
        unsigned cloning : 1;
 
+       /*
+        * These strings will be passed to the {pre, post}-receive hook,
+        * on the remote side, if both sides support the push options capability.
+        */
+       const struct string_list *push_options;
+
        /**
         * Returns 0 if successful, positive if the option is not
         * recognized or is inapplicable, and negative if the option
@@ -134,6 +140,7 @@ struct transport {
 #define TRANSPORT_PUSH_CERT_ALWAYS 2048
 #define TRANSPORT_PUSH_CERT_IF_ASKED 4096
 #define TRANSPORT_PUSH_ATOMIC 8192
+#define TRANSPORT_PUSH_OPTIONS 16384
 
 #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 #define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
index b819baf0cda97c9b596e83d8146f1b42891efb01..5acfe4cd64967d86462f3958433f3d2f844ead51 100644 (file)
@@ -13,6 +13,7 @@ void free_worktrees(struct worktree **worktrees)
                free(worktrees[i]->path);
                free(worktrees[i]->id);
                free(worktrees[i]->head_ref);
+               free(worktrees[i]->lock_reason);
                free(worktrees[i]);
        }
        free (worktrees);
@@ -98,6 +99,8 @@ static struct worktree *get_main_worktree(void)
        worktree->is_detached = is_detached;
        worktree->is_current = 0;
        add_head_info(&head_ref, worktree);
+       worktree->lock_reason = NULL;
+       worktree->lock_reason_valid = 0;
 
 done:
        strbuf_release(&path);
@@ -143,6 +146,8 @@ static struct worktree *get_linked_worktree(const char *id)
        worktree->is_detached = is_detached;
        worktree->is_current = 0;
        add_head_info(&head_ref, worktree);
+       worktree->lock_reason = NULL;
+       worktree->lock_reason_valid = 0;
 
 done:
        strbuf_release(&path);
@@ -214,6 +219,78 @@ const char *get_worktree_git_dir(const struct worktree *wt)
                return git_common_path("worktrees/%s", wt->id);
 }
 
+static struct worktree *find_worktree_by_suffix(struct worktree **list,
+                                               const char *suffix)
+{
+       struct worktree *found = NULL;
+       int nr_found = 0, suffixlen;
+
+       suffixlen = strlen(suffix);
+       if (!suffixlen)
+               return NULL;
+
+       for (; *list && nr_found < 2; list++) {
+               const char      *path    = (*list)->path;
+               int              pathlen = strlen(path);
+               int              start   = pathlen - suffixlen;
+
+               /* suffix must start at directory boundary */
+               if ((!start || (start > 0 && is_dir_sep(path[start - 1]))) &&
+                   !fspathcmp(suffix, path + start)) {
+                       found = *list;
+                       nr_found++;
+               }
+       }
+       return nr_found == 1 ? found : NULL;
+}
+
+struct worktree *find_worktree(struct worktree **list,
+                              const char *prefix,
+                              const char *arg)
+{
+       struct worktree *wt;
+       char *path;
+
+       if ((wt = find_worktree_by_suffix(list, arg)))
+               return wt;
+
+       arg = prefix_filename(prefix, strlen(prefix), arg);
+       path = xstrdup(real_path(arg));
+       for (; *list; list++)
+               if (!fspathcmp(path, real_path((*list)->path)))
+                       break;
+       free(path);
+       return *list;
+}
+
+int is_main_worktree(const struct worktree *wt)
+{
+       return !wt->id;
+}
+
+const char *is_worktree_locked(struct worktree *wt)
+{
+       assert(!is_main_worktree(wt));
+
+       if (!wt->lock_reason_valid) {
+               struct strbuf path = STRBUF_INIT;
+
+               strbuf_addstr(&path, worktree_git_path(wt, "locked"));
+               if (file_exists(path.buf)) {
+                       struct strbuf lock_reason = STRBUF_INIT;
+                       if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)
+                               die_errno(_("failed to read '%s'"), path.buf);
+                       strbuf_trim(&lock_reason);
+                       wt->lock_reason = strbuf_detach(&lock_reason, NULL);
+               } else
+                       wt->lock_reason = NULL;
+               wt->lock_reason_valid = 1;
+               strbuf_release(&path);
+       }
+
+       return wt->lock_reason;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
                              const char *target)
 {
index 13949093cc1610fcafbefb22d1f8b9441fbd4ade..90e1311fa73fa6bdde5249d68818eacb6dc8edd0 100644 (file)
@@ -5,10 +5,12 @@ struct worktree {
        char *path;
        char *id;
        char *head_ref;
+       char *lock_reason;      /* internal use */
        unsigned char head_sha1[20];
        int is_detached;
        int is_bare;
        int is_current;
+       int lock_reason_valid;
 };
 
 /* Functions for acting on the information about worktrees. */
@@ -29,6 +31,25 @@ extern struct worktree **get_worktrees(void);
  */
 extern const char *get_worktree_git_dir(const struct worktree *wt);
 
+/*
+ * Search a worktree that can be unambiguously identified by
+ * "arg". "prefix" must not be NULL.
+ */
+extern struct worktree *find_worktree(struct worktree **list,
+                                     const char *prefix,
+                                     const char *arg);
+
+/*
+ * Return true if the given worktree is the main one.
+ */
+extern int is_main_worktree(const struct worktree *wt);
+
+/*
+ * Return the reason string if the given worktree is locked or NULL
+ * otherwise.
+ */
+extern const char *is_worktree_locked(struct worktree *wt);
+
 /*
  * Free up the memory for worktree(s)
  */
index f8ae0c2dd05d4997d617c09a14b09c3ba347f587..6225a2d89f2c38bea6ed64295a28e7e07d19522c 100644 (file)
@@ -948,9 +948,12 @@ static void show_merge_in_progress(struct wt_status *s,
 {
        if (has_unmerged(s)) {
                status_printf_ln(s, color, _("You have unmerged paths."));
-               if (s->hints)
+               if (s->hints) {
                        status_printf_ln(s, color,
-                               _("  (fix conflicts and run \"git commit\")"));
+                                        _("  (fix conflicts and run \"git commit\")"));
+                       status_printf_ln(s, color,
+                                        _("  (use \"git merge --abort\" to abort the merge)"));
+               }
        } else {
                s-> commitable = 1;
                status_printf_ln(s, color,