Merge branch 'va/i18n'
authorJunio C Hamano <gitster@pobox.com>
Mon, 8 Aug 2016 21:48:38 +0000 (14:48 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 8 Aug 2016 21:48:38 +0000 (14:48 -0700)
More i18n marking.

* va/i18n:
i18n: config: unfold error messages marked for translation
i18n: notes: mark comment for translation

89 files changed:
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/githooks.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-capabilities.txt
builtin/blame.c
builtin/cat-file.c
builtin/clone.c
builtin/commit.c
builtin/fetch.c
builtin/fsck.c
builtin/index-pack.c
builtin/pack-objects.c
builtin/push.c
builtin/receive-pack.c
builtin/rev-list.c
builtin/revert.c
builtin/submodule--helper.c
builtin/worktree.c
cache.h
compat/mingw.h
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
fetch-pack.c
git-rebase--interactive.sh
git-submodule.sh
git-svn.perl
grep.c
grep.h
http-walker.c
http.c
list.h [new file with mode: 0644]
notes-merge.c
pack-check.c
pack.h
parse-options-cb.c
parse-options.h
revision.c
send-pack.c
send-pack.h
sha1_file.c
submodule-config.c
t/helper/test-submodule-config.c
t/lib-git-svn.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
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/t4130-apply-criss-cross-rename.sh
t/t4202-log.sh
t/t4205-log-pretty-formats.sh
t/t5510-fetch.sh
t/t5545-push-options.sh [new file with mode: 0755]
t/t7060-wtstatus.sh
t/t7411-submodule-config.sh
t/t7512-status-help.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 f9dfc27b333bc2d94cadf4652f1466ad27a4ccba..a9fb0736d65068db0d093faf950d98af6ce74d1e 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,27 @@ 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.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -156,7 +177,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 +225,27 @@ 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.
+
 
 Also contains various documentation updates and code clean-ups.
 
@@ -285,7 +326,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 +348,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 +400,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,7 +410,6 @@ 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
@@ -414,15 +440,71 @@ notes for details).
    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.
+   (merge deb9c15 rs/notes-merge-no-toctou later to maint).
+
+ * "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.
+   (merge ec9d224 nd/pack-ofs-4gb-limit later to maint).
+
+ * 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().
+   (merge fab6027 ew/daemon-socket-keepalive later to maint).
+
+ * 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.
+   (merge 259f22a ew/find-perl-on-freebsd-in-local later to maint).
+
+ * "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).
+
  * 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 406621f sb/submodule-deinit-all later to maint).
+   (merge 55cbe18 rs/submodule-config-code-cleanup later to maint).
+   (merge 280abfd sb/pack-protocol-doc-nak 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..28003a5
--- /dev/null
@@ -0,0 +1,58 @@
+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.
+
+Also contains minor documentation updates and code clean-ups.
index 8b1aee4b3bbb57e95fd660b57d589742dcd211b3..bc1c433c4e0584a095f79196997426780521b377 100644 (file)
@@ -2427,8 +2427,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 +2488,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..ec514f6cd5cc0f019b469d91fc6af365ce0a64a9 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
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 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 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 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 ab66cde2c2cf0f535678469cdf6eb6e37cd3113b..2e7b3030c67a4e83f902166533998461df444aa3 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;
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 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 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 a2f8cfdec0d4034c20f3307c390cec9dec6d4a18..92e2e5f7a8190c546f2c0cc9bc9724a04d330052 100644 (file)
@@ -342,15 +342,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 +416,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 +493,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;
 
        /*
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 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 c8020981678cdfbe88dfc4c0751dfda17b1d11f5..e8128fc5d60579f461d60897fdd6483b3653d75c 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;
@@ -1515,7 +1516,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;
 
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 22958a8d6ffbf3ca937b5aac97bb9f616de70130..17fed2f43ac6f7564785e2074e7f130bd625c231 100644 (file)
@@ -205,6 +205,7 @@ 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
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..6a187bc11bc9b8b141f70f81ae256c27921ff00d 100644 (file)
@@ -1136,6 +1136,7 @@ _git_clone ()
                        --depth
                        --single-branch
                        --branch
+                       --recurse-submodules
                        "
                return
                ;;
@@ -2693,7 +2694,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 +2706,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 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 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 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');
 }
diff --git a/grep.c b/grep.c
index 394c8569db26bc0ab386ff50a76c78611f78ffde..58d599e6475cd1472c824743ec60b00361a396c3 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 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..e81dd132d483aa02540bac7d1f55b5e98b1ad736 100644 (file)
--- a/http.c
+++ b/http.c
@@ -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/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 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);
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);
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);
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..3a842ac2d1aa351e5182f0c67b74aee1bd67d021 100644 (file)
@@ -260,6 +260,7 @@ 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;
@@ -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 d5e11217f523018008b3c4861d5d70068de14c72..cb571ac6e8ed0657e39b346b41961caa8cc825be 100644 (file)
@@ -2281,7 +2281,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);
index 1753e017557c385563269e6bf5417f0bf214b3b2..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,16 +431,11 @@ 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;
@@ -450,6 +444,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
        parameter.overwrite = 0;
        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 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>
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 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 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 &&
        (
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 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 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 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 b233e3ee5e4a22e150f1ba274d825102e711ef9f..4ba48b05960203f9c5ed3360e99382bc945ae7e4 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 19cbc3939cdb3d6a84c0dbd88a4e43847d228363..3175ec6ddb6d6e40f8e2cfcbbb0b4acf5f0b5e98 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,