Merge branch 'jk/test-helper-v-output-fix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 6 Mar 2018 22:54:05 +0000 (14:54 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 6 Mar 2018 22:54:05 +0000 (14:54 -0800)
Test framework update.

* jk/test-helper-v-output-fix:
t: send verbose test-helper output to fd 4

92 files changed:
Documentation/CodingGuidelines
Documentation/RelNotes/2.17.0.txt
Documentation/config.txt
Documentation/fetch-options.txt
Documentation/git-am.txt
Documentation/git-fetch.txt
Documentation/git-interpret-trailers.txt
Documentation/git-rebase.txt
Documentation/git-remote.txt
Documentation/git-status.txt
Documentation/git-tag.txt
Documentation/git-update-index.txt
Documentation/git-worktree.txt
Documentation/merge-options.txt
Documentation/technical/api-submodule-config.txt
Makefile
apply.c
builtin/am.c
builtin/archive.c
builtin/blame.c
builtin/check-ignore.c
builtin/describe.c
builtin/fetch.c
builtin/merge.c
builtin/mv.c
builtin/rev-list.c
builtin/tag.c
builtin/worktree.c
color.c
color.h
config.mak.uname
contrib/completion/git-completion.bash
contrib/subtree/git-subtree.txt
dir.c
dir.h
fetch-pack.c
fsmonitor.c
fsmonitor.h
git-add--interactive.perl
git-rebase--am.sh
git-rebase--interactive.sh
git-rebase--merge.sh
git-rebase.sh
git-send-email.perl
git-sh-i18n.sh
git.c
grep.c
log-tree.c
name-hash.c
packfile.c
quote.c
remote-curl.c
remote.c
remote.h
revision.c
revision.h
send-pack.c
sequencer.c
setup.c
sha1-lookup.c
sha1-lookup.h
submodule-config.c
t/README
t/helper/test-hashmap.c
t/t0002-gitfile.sh
t/t0008-ignores.sh
t/t0050-filesystem.sh
t/t0205-gettext-poison.sh
t/t1300-repo-config.sh
t/t1506-rev-parse-diagnosis.sh
t/t2025-worktree-add.sh
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3408-rebase-multi-line.sh
t/t4135-apply-weird-filenames.sh
t/t4150-am.sh
t/t5510-fetch.sh
t/t5526-fetch-submodules.sh
t/t5545-push-options.sh
t/t6015-rev-list-show-all-parents.sh [deleted file]
t/t6120-describe.sh
t/t6200-fmt-merge-msg.sh
t/t6300-for-each-ref.sh
t/t7001-mv.sh
t/t7004-tag.sh
t/t7063-status-untracked-cache.sh
t/t7409-submodule-detached-work-tree.sh
t/t7519-status-fsmonitor.sh
t/t7600-merge.sh
t/test-lib-functions.sh
t/test-lib.sh
unpack-trees.c
index c4cb5ff0d477938b8fd49749c3589c5afbb04221..48aa4edfbdd180e1c6d874b6bb61ea5fc8e32ef5 100644 (file)
@@ -386,6 +386,11 @@ For C programs:
  - Use Git's gettext wrappers to make the user interface
    translatable. See "Marking strings for translation" in po/README.
 
+ - Variables and functions local to a given source file should be marked
+   with "static". Variables that are visible to other source files
+   must be declared with "extern" in header files. However, function
+   declarations should not use "extern", as that is already the default.
+
 For Perl programs:
 
  - Most of the C guidelines above apply.
index db87393f4ad81bff608487448ba4ece2b3ebf438..ae7c70857494a0957dd292a9f854c03797221204 100644 (file)
@@ -8,7 +8,6 @@ UI, Workflows & Features
 
  * "diff" family of commands learned "--find-object=<object-id>" option
    to limit the findings to changes that involve the named object.
-   (merge 4d8c51aa19 sb/diff-blobfind-pickaxe later to maint).
 
  * "git format-patch" learned to give 72-cols to diffstat, which is
    consistent with other line length limits the subcommand uses for
@@ -20,10 +19,17 @@ UI, Workflows & Features
 
  * "git rebase" learned to take "--allow-empty-message" option.
 
+ * "git am" has learned the "--quit" option, in addition to the
+   existing "--abort" option; having the pair mirrors a few other
+   commands like "rebase" and "cherry-pick".
+
+ * "git worktree add" learned to run the post-checkout hook, just like
+   "git clone" runs it upon the initial checkout.
+
+
 Performance, Internal Implementation, Development Support etc.
 
  * More perf tests for threaded grep
-   (merge 7b31b55db1 ab/perf-grep-threads later to maint).
 
  * "perf" test output can be sent to codespeed server.
    (merge 19cf57a92e cc/codespeed later to maint).
@@ -79,6 +85,10 @@ Performance, Internal Implementation, Development Support etc.
 
  * More tests for wildmatch functions.
 
+ * The code to binary search starting from a fan-out table (which is
+   how the packfile is indexed with object names) has been refactored
+   into a reusable helper.
+
 Also contains various documentation updates and code clean-ups.
 
 
@@ -87,44 +97,35 @@ Fixes since v2.16
 
  * An old regression in "git describe --all $annotated_tag^0" has been
    fixed.
-   (merge 1bba00130a dk/describe-all-output-fix later to maint).
 
  * "git status" after moving a path in the working tree (hence making
    it appear "removed") and then adding with the -N option (hence
    making that appear "added") detected it as a rename, but did not
    report the  old and new pathnames correctly.
-   (merge 176ea74793 nd/ita-wt-renames-in-status later to maint).
 
  * "git svn dcommit" did not take into account the fact that a
    svn+ssh:// URL with a username@ (typically used for pushing) refers
    to the same SVN repository without the username@ and failed when
    svn.pushmergeinfo option is set.
-   (merge 8aaed892fd jm/svn-pushmergeinfo-fix later to maint).
 
  * API clean-up around revision traversal.
-   (merge 6fcec2f9ae rs/lose-leak-pending later to maint).
 
  * "git merge -Xours/-Xtheirs" learned to use our/their version when
    resolving a conflicting updates to a symbolic link.
-   (merge fd48b46474 jc/merge-symlink-ours-theirs later to maint).
 
  * "git clone $there $here" is allowed even when here directory exists
    as long as it is an empty directory, but the command incorrectly
    removed it upon a failure of the operation.
-   (merge d45420c1c8 jk/abort-clone-with-existing-dest later to maint).
 
  * "git commit --fixup" did not allow "-m<message>" option to be used
    at the same time; allow it to annotate resulting commit with more
    text.
-   (merge 30884c9afc ab/commit-m-with-fixup later to maint).
 
  * When resetting the working tree files recursively, the working tree
    of submodules are now also reset to match.
-   (merge 7dcc1f4df8 sb/submodule-update-reset-fix later to maint).
 
  * "git stash -- <pathspec>" incorrectly blew away untracked files in
    the directory that matched the pathspec, which has been corrected.
-   (merge bba067d2fa tg/stash-with-pathspec-fix later to maint).
 
  * Instead of maintaining home-grown email address parsing code, ship
    a copy of reasonably recent Mail::Address to be used as a fallback
@@ -134,7 +135,6 @@ Fixes since v2.16
  * "git add -p" was taught to ignore local changes to submodules as
    they do not interfere with the partial addition of regular changes
    anyway.
-   (merge 12434efc1d nd/add-i-ignore-submodules later to maint).
 
  * Avoid showing a warning message in the middle of a line of "git
    diff" output.
@@ -178,11 +178,59 @@ Fixes since v2.16
  * Test fixes.
    (merge 63b1a175ee sg/test-i18ngrep later to maint).
 
+ * Some bugs around "untracked cache" feature have been fixed.  This
+   will notice corrupt data in the untracked cache left by old and
+   buggy code and issue a warning---the index can be fixed by clearing
+   the untracked cache from it.
+   (merge 0cacebf099 nd/fix-untracked-cache-invalidation later to maint).
+   (merge 7bf0be7501 ab/untracked-cache-invalidation-docs later to maint).
+
+ * "git blame HEAD COPYING" in a bare repository failed to run, while
+   "git blame HEAD -- COPYING" run just fine.  This has been corrected.
+
+ * "git add" files in the same directory, but spelling the directory
+   path in different cases on case insensitive filesystem, corrupted
+   the name hash data structure and led to unexpected results.  This
+   has been corrected.
+   (merge c95525e90d bp/name-hash-dirname-fix later to maint).
+
+ * "git rebase -p" mangled log messages of a merge commit, which is
+   now fixed.
+   (merge ed5144d7eb js/fix-merge-arg-quoting-in-rebase-p later to maint).
+
+ * Some low level protocol codepath could crash when they get an
+   unexpected flush packet, which is now fixed.
+   (merge bb1356dc64 js/packet-read-line-check-null later to maint).
+
+ * "git check-ignore" with multiple paths got confused when one is a
+   file and the other is a directory, which has been fixed.
+   (merge d60771e930 rs/check-ignore-multi later to maint).
+
+ * "git describe $garbage" stopped giving any errors when the garbage
+   happens to be a string with 40 hexadecimal letters.
+   (merge a8e7a2bf0f sb/describe-blob later to maint).
+
+ * Code to unquote single-quoted string (used in the parser for
+   configuration files, etc.) did not diagnose bogus input correctly
+   and produced bogus results instead.
+   (merge ddbbf8eb25 jk/sq-dequote-on-bogus-input later to maint).
+
+ * Many places in "git apply" knew that "/dev/null" that signals
+   "there is no such file on this side of the diff" can be followed by
+   whitespace and garbage when parsing a patch, except for one, which
+   made an otherwise valid patch (e.g. ones from subversion) rejected.
+   (merge e454ad4bec tk/apply-dev-null-verify-name-fix later to maint).
+
+ * We no longer create any *.spec file, so "make clean" should not
+   remove it.
+   (merge 4321bdcabb tz/do-not-clean-spec-file later to maint).
+
+ * "git push" over http transport did not unquote the push-options
+   correctly.
+   (merge 90dce21eb0 jk/push-options-via-transport-fix later to maint).
+
  * Other minor doc, test and build updates and code cleanups.
    (merge e2a5a028c7 bw/oidmap-autoinit later to maint).
-   (merge f0a6068a9f ys/bisect-object-id-missing-conversion-fix later to maint).
-   (merge 30221a3389 as/read-tree-prefix-doc-fix later to maint).
-   (merge 9bd2ce5432 ab/doc-cat-file-e-still-shows-errors later to maint).
    (merge ec3b4b06f8 cl/t9001-cleanup later to maint).
    (merge e1b3f3dd38 ks/submodule-doc-updates later to maint).
    (merge fbac558a9b rs/describe-unique-abbrev later to maint).
@@ -197,3 +245,13 @@ Fixes since v2.16
    (merge b780e4407d jc/worktree-add-short-help later to maint).
    (merge ae239fc8e5 rs/cocci-strbuf-addf-to-addstr later to maint).
    (merge 2e22a85e5c nd/ignore-glob-doc-update later to maint).
+   (merge 3738031581 jk/gettext-poison later to maint).
+   (merge 54360a1956 rj/sparse-updates later to maint).
+   (merge 12e31a6b12 sg/doc-test-must-fail-args later to maint).
+   (merge 760f1ad101 bc/doc-interpret-trailers-grammofix later to maint).
+   (merge 4ccf461f56 bp/fsmonitor later to maint).
+   (merge a6119f82b1 jk/test-hashmap-updates later to maint).
+   (merge 5aea9fe6cc rd/typofix later to maint).
+   (merge e4e5da2796 sb/status-doc-fix later to maint).
+   (merge 7976e901c8 gs/test-unset-xdg-cache-home later to maint).
+   (merge d023df1ee6 tg/worktree-create-tracking later to maint).
index f57e9cf10ca485ae0147ee1d01639c94ce97fb56..ce9102cea83766be0ae8d2ed2867fd4c587d7a95 100644 (file)
@@ -1398,7 +1398,16 @@ fetch.unpackLimit::
 
 fetch.prune::
        If true, fetch will automatically behave as if the `--prune`
-       option was given on the command line.  See also `remote.<name>.prune`.
+       option was given on the command line.  See also `remote.<name>.prune`
+       and the PRUNING section of linkgit:git-fetch[1].
+
+fetch.pruneTags::
+       If true, fetch will automatically behave as if the
+       `refs/tags/*:refs/tags/*` refspec was provided when pruning,
+       if not set already. This allows for setting both this option
+       and `fetch.prune` to maintain a 1=1 mapping to upstream
+       refs. See also `remote.<name>.pruneTags` and the PRUNING
+       section of linkgit:git-fetch[1].
 
 fetch.output::
        Control how ref update status is printed. Valid values are
@@ -2945,6 +2954,15 @@ remote.<name>.prune::
        remote (as if the `--prune` option was given on the command line).
        Overrides `fetch.prune` settings, if any.
 
+remote.<name>.pruneTags::
+       When set to true, fetching from this remote by default will also
+       remove any local tags that no longer exist on the remote if pruning
+       is activated in general via `remote.<name>.prune`, `fetch.prune` or
+       `--prune`. Overrides `fetch.pruneTags` settings, if any.
++
+See also `remote.<name>.prune` and the PRUNING section of
+linkgit:git-fetch[1].
+
 remotes.<group>::
        The list of remotes which are fetched by "git remote update
        <group>".  See linkgit:git-remote[1].
@@ -3210,7 +3228,8 @@ submodule.active::
 
 submodule.recurse::
        Specifies if commands recurse into submodules by default. This
-       applies to all commands that have a `--recurse-submodules` option.
+       applies to all commands that have a `--recurse-submodules` option,
+       except `clone`.
        Defaults to false.
 
 submodule.fetchJobs::
index fb6bebbc618c3f71ab400ff4267264a0167a887a..8631e365f437fd85058bed3dbd0cebde15756ccc 100644 (file)
@@ -73,7 +73,22 @@ ifndef::git-pull[]
        are fetched due to an explicit refspec (either on the command
        line or in the remote configuration, for example if the remote
        was cloned with the --mirror option), then they are also
-       subject to pruning.
+       subject to pruning. Supplying `--prune-tags` is a shorthand for
+       providing the tag refspec.
++
+See the PRUNING section below for more details.
+
+-P::
+--prune-tags::
+       Before fetching, remove any local tags that no longer exist on
+       the remote if `--prune` is enabled. This option should be used
+       more carefully, unlike `--prune` it will remove any local
+       references (local tags) that have been created. This option is
+       a shorthand for providing the explicit tag refspec along with
+       `--prune`, see the discussion about that in its documentation.
++
+See the PRUNING section below for more details.
+
 endif::git-pull[]
 
 ifndef::git-pull[]
index 12879e4029a7710e2d78bae476f7ad7d9b0fe830..6f6c34b0f4bc9ba18ec890dff1a6fe10af2fd68f 100644 (file)
@@ -16,7 +16,7 @@ SYNOPSIS
         [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
         [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
         [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort)
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch)
 
 DESCRIPTION
 -----------
@@ -167,6 +167,14 @@ default.   You can use `--no-utf8` to override this.
 --abort::
        Restore the original branch and abort the patching operation.
 
+--quit::
+       Abort the patching operation but keep HEAD and the index
+       untouched.
+
+--show-current-patch::
+       Show the patch being applied when "git am" is stopped because
+       of conflicts.
+
 DISCUSSION
 ----------
 
index b153aefa68c8dcaa5f3600d67c5ff0010ee899af..e3199355978880b21668bc1aff0e42f610c02bd6 100644 (file)
@@ -99,6 +99,93 @@ The latter use of the `remote.<repository>.fetch` values can be
 overridden by giving the `--refmap=<refspec>` parameter(s) on the
 command line.
 
+PRUNING
+-------
+
+Git has a default disposition of keeping data unless it's explicitly
+thrown away; this extends to holding onto local references to branches
+on remotes that have themselves deleted those branches.
+
+If left to accumulate, these stale references might make performance
+worse on big and busy repos that have a lot of branch churn, and
+e.g. make the output of commands like `git branch -a --contains
+<commit>` needlessly verbose, as well as impacting anything else
+that'll work with the complete set of known references.
+
+These remote-tracking references can be deleted as a one-off with
+either of:
+
+------------------------------------------------
+# While fetching
+$ git fetch --prune <name>
+
+# Only prune, don't fetch
+$ git remote prune <name>
+------------------------------------------------
+
+To prune references as part of your normal workflow without needing to
+remember to run that, set `fetch.prune` globally, or
+`remote.<name>.prune` per-remote in the config. See
+linkgit:git-config[1].
+
+Here's where things get tricky and more specific. The pruning feature
+doesn't actually care about branches, instead it'll prune local <->
+remote-references as a function of the refspec of the remote (see
+`<refspec>` and <<CRTB,CONFIGURED REMOTE-TRACKING BRANCHES>> above).
+
+Therefore if the refspec for the remote includes
+e.g. `refs/tags/*:refs/tags/*`, or you manually run e.g. `git fetch
+--prune <name> "refs/tags/*:refs/tags/*"` it won't be stale remote
+tracking branches that are deleted, but any local tag that doesn't
+exist on the remote.
+
+This might not be what you expect, i.e. you want to prune remote
+`<name>`, but also explicitly fetch tags from it, so when you fetch
+from it you delete all your local tags, most of which may not have
+come from the `<name>` remote in the first place.
+
+So be careful when using this with a refspec like
+`refs/tags/*:refs/tags/*`, or any other refspec which might map
+references from multiple remotes to the same local namespace.
+
+Since keeping up-to-date with both branches and tags on the remote is
+a common use-case the `--prune-tags` option can be supplied along with
+`--prune` to prune local tags that don't exist on the remote, and
+force-update those tags that differ. Tag pruning can also be enabled
+with `fetch.pruneTags` or `remote.<name>.pruneTags` in the config. See
+linkgit:git-config[1].
+
+The `--prune-tags` option is equivalent to having
+`refs/tags/*:refs/tags/*` declared in the refspecs of the remote. This
+can lead to some seemingly strange interactions:
+
+------------------------------------------------
+# These both fetch tags
+$ git fetch --no-tags origin 'refs/tags/*:refs/tags/*'
+$ git fetch --no-tags --prune-tags origin
+------------------------------------------------
+
+The reason it doesn't error out when provided without `--prune` or its
+config versions is for flexibility of the configured versions, and to
+maintain a 1=1 mapping between what the command line flags do, and
+what the configuration versions do.
+
+It's reasonable to e.g. configure `fetch.pruneTags=true` in
+`~/.gitconfig` to have tags pruned whenever `git fetch --prune` is
+run, without making every invocation of `git fetch` without `--prune`
+an error.
+
+Pruning tags with `--prune-tags` also works when fetching a URL
+instead of a named remote. These will all prune tags not found on
+origin:
+
+------------------------------------------------
+$ git fetch origin --prune --prune-tags
+$ git fetch origin --prune 'refs/tags/*:refs/tags/*'
+$ git fetch <url of origin> --prune --prune-tags
+$ git fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'
+------------------------------------------------
+
 OUTPUT
 ------
 
index 9dd19a1dd9f126fbf54f1dc628b30fbe05445cf3..ff446f15f79f270a8979d1a398450a50ba4a4e95 100644 (file)
@@ -51,7 +51,7 @@ with only spaces at the end of the commit message part, one blank line
 will be added before the new trailer.
 
 Existing trailers are extracted from the input message by looking for
-a group of one or more lines that (i) are all trailers, or (ii) contains at
+a group of one or more lines that (i) is all trailers, or (ii) contains at
 least one Git-generated or user-configured trailer and consists of at
 least 25% trailers.
 The group must be preceded by one or more empty (or whitespace-only) lines.
index d713951b86abaa3fbea48703f87cce5cf0330019..3277ca143273e01f5f4973ed351c8a5cb4b8e0fa 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
        [<upstream> [<branch>]]
 'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
        --root [<branch>]
-'git rebase' --continue | --skip | --abort | --quit | --edit-todo
+'git rebase' --continue | --skip | --abort | --quit | --edit-todo | --show-current-patch
 
 DESCRIPTION
 -----------
@@ -255,6 +255,11 @@ leave out at most one of A and B, in which case it defaults to HEAD.
 --edit-todo::
        Edit the todo list during an interactive rebase.
 
+--show-current-patch::
+       Show the current patch in an interactive rebase or when rebase
+       is stopped because of conflicts. This is the equivalent of
+       `git show REBASE_HEAD`.
+
 -m::
 --merge::
        Use merging strategies to rebase.  When the recursive (default) merge
index 577b969c1bda2bfd95fd28e3ffc4bbbc96c5d16c..4feddc0293bd7eb9827ce45f11519335a484f013 100644 (file)
@@ -172,10 +172,14 @@ With `-n` option, the remote heads are not queried first with
 
 'prune'::
 
-Deletes all stale remote-tracking branches under <name>.
-These stale branches have already been removed from the remote repository
-referenced by <name>, but are still locally available in
-"remotes/<name>".
+Deletes stale references associated with <name>. By default, stale
+remote-tracking branches under <name> are deleted, but depending on
+global configuration and the configuration of the remote we might even
+prune local tags that haven't been pushed there. Equivalent to `git
+fetch --prune <name>`, except that no new references will be fetched.
++
+See the PRUNING section of linkgit:git-fetch[1] for what it'll prune
+depending on various configuration.
 +
 With `--dry-run` option, report what branches will be pruned, but do not
 actually prune them.
@@ -189,7 +193,7 @@ remotes.default is not defined, all remotes which do not have the
 configuration parameter remote.<name>.skipDefaultUpdate set to true will
 be updated.  (See linkgit:git-config[1]).
 +
-With `--prune` option, prune all the remotes that are updated.
+With `--prune` option, run pruning against all the remotes that are updated.
 
 
 DISCUSSION
index 72bfb87f6658b209e9b45956e73e492e957971b7..f9c91c721e909291ba42c27c3fd521135b4869f0 100644 (file)
@@ -184,10 +184,10 @@ in which case `XY` are `!!`.
 
     X          Y     Meaning
     -------------------------------------------------
-              [MD]   not updated
+            [AMD]   not updated
     M        [ MD]   updated in index
     A        [ MD]   added to index
-    D         [ M]   deleted from index
+    D                deleted from index
     R        [ MD]   renamed in index
     C        [ MD]   copied in index
     [MARC]           index and work tree matches
index 956fc019f984bca1754a72dc0d7308b39a28445d..1d17101bac39d64cc3d4aff89cd588955dd00735 100644 (file)
@@ -9,7 +9,7 @@ git-tag - Create, list, delete or verify a tag object signed with GPG
 SYNOPSIS
 --------
 [verse]
-'git tag' [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>]
+'git tag' [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>] [-e]
        <tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
@@ -167,6 +167,12 @@ This option is only applicable when listing tags without annotation lines.
        Implies `-a` if none of `-a`, `-s`, or `-u <keyid>`
        is given.
 
+-e::
+--edit::
+       The message taken from file with `-F` and command line with
+       `-m` are usually used as the tag message unmodified.
+       This option lets you further edit the message taken from these sources.
+
 --cleanup=<mode>::
        This option sets how the tag message is cleaned up.
        The  '<mode>' can be one of 'verbatim', 'whitespace' and 'strip'.  The
index bdb0342593229e32425bf73d17ad6fcdd0dd3732..3897a59ee94bc424c2c66cb5d05bc0193fb6eaf2 100644 (file)
@@ -464,6 +464,32 @@ command reads the index; while when `--[no-|force-]untracked-cache`
 are used, the untracked cache is immediately added to or removed from
 the index.
 
+Before 2.17, the untracked cache had a bug where replacing a directory
+with a symlink to another directory could cause it to incorrectly show
+files tracked by git as untracked. See the "status: add a failing test
+showing a core.untrackedCache bug" commit to git.git. A workaround for
+that is (and this might work for other undiscovered bugs in the
+future):
+
+----------------
+$ git -c core.untrackedCache=false status
+----------------
+
+This bug has also been shown to affect non-symlink cases of replacing
+a directory with a file when it comes to the internal structures of
+the untracked cache, but no case has been reported where this resulted in
+wrong "git status" output.
+
+There are also cases where existing indexes written by git versions
+before 2.17 will reference directories that don't exist anymore,
+potentially causing many "could not open directory" warnings to be
+printed on "git status". These are new warnings for existing issues
+that were previously silently discarded.
+
+As with the bug described above the solution is to one-off do a "git
+status" run with `core.untrackedCache=false` to flush out the leftover
+bad data.
+
 File System Monitor
 -------------------
 
@@ -484,8 +510,8 @@ the `core.fsmonitor` configuration variable (see
 linkgit:git-config[1]) than using the `--fsmonitor` option to
 `git update-index` in each repository, especially if you want to do so
 across all repositories you use, because you can set the configuration
-variable to `true` (or `false`) in your `$HOME/.gitconfig` just once
-and have it affect all repositories you touch.
+variable in your `$HOME/.gitconfig` just once and have it affect all
+repositories you touch.
 
 When the `core.fsmonitor` configuration variable is changed, the
 file system monitor is added to or removed from the index the next time
index 41585f535dba760590f2d67ecdc96cc8dea3a57f..5ac3f68ab5396ade59c25f2083088c21f94f1f93 100644 (file)
@@ -52,10 +52,11 @@ is linked to the current repository, sharing everything except working
 directory specific files such as HEAD, index, etc. `-` may also be
 specified as `<commit-ish>`; it is synonymous with `@{-1}`.
 +
-If <commit-ish> is a branch name (call it `<branch>` and is not found,
+If <commit-ish> is a branch name (call it `<branch>`) and is not found,
 and neither `-b` nor `-B` nor `--detach` are used, but there does
 exist a tracking branch in exactly one remote (call it `<remote>`)
-with a matching name, treat as equivalent to
+with a matching name, treat as equivalent to:
++
 ------------
 $ git worktree add --track -b <branch> <path> <remote>/<branch>
 ------------
index 3888c3ff85e2dc5b137e4e3ed50e39327760a02a..63a3fc09548abe8d34faab98f183e1817b21b878 100644 (file)
@@ -35,7 +35,8 @@ set to `no` at the beginning of them.
 --no-ff::
        Create a merge commit even when the merge resolves as a
        fast-forward.  This is the default behaviour when merging an
-       annotated (and possibly signed) tag.
+       annotated (and possibly signed) tag that is not stored in
+       its natural place in 'refs/tags/' hierarchy.
 
 --ff-only::
        Refuse to merge and exit with a non-zero status unless the
index 3dce003fda008e61c7a91c85554f9a1cafe6106f..ee907c4a82a9127c0abc67b8a5dd215a3b0535d4 100644 (file)
@@ -4,7 +4,7 @@ submodule config cache API
 The submodule config cache API allows to read submodule
 configurations/information from specified revisions. Internally
 information is lazily read into a cache that is used to avoid
-unnecessary parsing of the same .gitmodule files. Lookups can be done by
+unnecessary parsing of the same .gitmodules files. Lookups can be done by
 submodule path or name.
 
 Usage
index c56fdc14ca154b7d80541569d9abf700ea419499..de4b8f0c02e2119f7252ff9e6b6c6d479e664a36 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2162,6 +2162,8 @@ gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \
 http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SPARSE_FLAGS += \
        -DCURL_DISABLE_TYPECHECK
 
+pack-revindex.sp: SPARSE_FLAGS += -Wno-memcpy-max-count
+
 ifdef NO_EXPAT
 http-walker.sp http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
 endif
@@ -2216,13 +2218,15 @@ $(VCSSVN_LIB): $(VCSSVN_OBJS)
 
 export DEFAULT_EDITOR DEFAULT_PAGER
 
-.PHONY: doc man html info pdf
-doc:
+.PHONY: doc man man-perl html info pdf
+doc: man-perl
        $(MAKE) -C Documentation all
 
-man:
+man: man-perl
        $(MAKE) -C Documentation man
 
+man-perl: perl/build/man/man3/Git.3pm
+
 html:
        $(MAKE) -C Documentation html
 
@@ -2620,7 +2624,7 @@ endif
        done && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
-.PHONY: install-gitweb install-doc install-man install-html install-info install-pdf
+.PHONY: install-gitweb install-doc install-man install-man-perl install-html install-info install-pdf
 .PHONY: quick-install-doc quick-install-man quick-install-html
 install-gitweb:
        $(MAKE) -C gitweb install
@@ -2631,7 +2635,7 @@ install-doc: install-man-perl
 install-man: install-man-perl
        $(MAKE) -C Documentation install-man
 
-install-man-perl: perl/build/man/man3/Git.3pm
+install-man-perl: man-perl
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mandir_SQ)/man3'
        (cd perl/build/man/man3 && $(TAR) cf - .) | \
        (cd '$(DESTDIR_SQ)$(mandir_SQ)/man3' && umask 022 && $(TAR) xof -)
@@ -2734,7 +2738,7 @@ clean: profile-clean coverage-clean
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
-       $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
+       $(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
        $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
diff --git a/apply.c b/apply.c
index 40a368b3153f90ff0061894d0af5db5b401a00d9..7c88d4ee71304825a475fc1e67ed38b48621cb3a 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -950,7 +950,7 @@ static int gitdiff_verify_name(struct apply_state *state,
                }
                free(another);
        } else {
-               if (!starts_with(line, "/dev/null\n"))
+               if (!is_dev_null(line))
                        return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
        }
 
index 6661edc162b770d40c62f3a03b7c0e59cb47ca51..1151b5c73aec81dbfca36a799cd4049429c75973 100644 (file)
@@ -1011,6 +1011,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
 
        if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
                die_errno(_("failed to create directory '%s'"), state->dir);
+       delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 
        if (split_mail(state, patch_format, paths, keep_cr) < 0) {
                am_destroy(state);
@@ -1110,6 +1111,7 @@ static void am_next(struct am_state *state)
 
        oidclr(&state->orig_commit);
        unlink(am_path(state, "original-commit"));
+       delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 
        if (!get_oid("HEAD", &head))
                write_state_text(state, "abort-safety", oid_to_hex(&head));
@@ -1441,6 +1443,8 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
 
        oidcpy(&state->orig_commit, &commit_oid);
        write_state_text(state, "original-commit", oid_to_hex(&commit_oid));
+       update_ref("am", "REBASE_HEAD", &commit_oid,
+                  NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
 
        return 0;
 }
@@ -1831,8 +1835,7 @@ static void am_run(struct am_state *state, int resume)
                        git_config_get_bool("advice.amworkdir", &advice_amworkdir);
 
                        if (advice_amworkdir)
-                               printf_ln(_("The copy of the patch that failed is found in: %s"),
-                                               am_path(state, "patch"));
+                               printf_ln(_("Use 'git am --show-current-patch' to see the failed patch"));
 
                        die_user_resolve(state);
                }
@@ -2121,6 +2124,34 @@ static void am_abort(struct am_state *state)
        am_destroy(state);
 }
 
+static int show_patch(struct am_state *state)
+{
+       struct strbuf sb = STRBUF_INIT;
+       const char *patch_path;
+       int len;
+
+       if (!is_null_oid(&state->orig_commit)) {
+               const char *av[4] = { "show", NULL, "--", NULL };
+               char *new_oid_str;
+               int ret;
+
+               av[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));
+               ret = run_command_v_opt(av, RUN_GIT_CMD);
+               free(new_oid_str);
+               return ret;
+       }
+
+       patch_path = am_path(state, msgnum(state));
+       len = strbuf_read_file(&sb, patch_path, 0);
+       if (len < 0)
+               die_errno(_("failed to read '%s'"), patch_path);
+
+       setup_pager();
+       write_in_full(1, sb.buf, sb.len);
+       strbuf_release(&sb);
+       return 0;
+}
+
 /**
  * parse_options() callback that validates and sets opt->value to the
  * PATCH_FORMAT_* enum value corresponding to `arg`.
@@ -2149,7 +2180,9 @@ enum resume_mode {
        RESUME_APPLY,
        RESUME_RESOLVED,
        RESUME_SKIP,
-       RESUME_ABORT
+       RESUME_ABORT,
+       RESUME_QUIT,
+       RESUME_SHOW_PATCH
 };
 
 static int git_am_config(const char *k, const char *v, void *cb)
@@ -2171,6 +2204,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
        int patch_format = PATCH_FORMAT_UNKNOWN;
        enum resume_mode resume = RESUME_FALSE;
        int in_progress;
+       int ret = 0;
 
        const char * const usage[] = {
                N_("git am [<options>] [(<mbox> | <Maildir>)...]"),
@@ -2249,6 +2283,12 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                OPT_CMDMODE(0, "abort", &resume,
                        N_("restore the original branch and abort the patching operation."),
                        RESUME_ABORT),
+               OPT_CMDMODE(0, "quit", &resume,
+                       N_("abort the patching operation but keep HEAD where it is."),
+                       RESUME_QUIT),
+               OPT_CMDMODE(0, "show-current-patch", &resume,
+                       N_("show the patch being applied."),
+                       RESUME_SHOW_PATCH),
                OPT_BOOL(0, "committer-date-is-author-date",
                        &state.committer_date_is_author_date,
                        N_("lie about committer date")),
@@ -2317,7 +2357,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                 * stray directories.
                 */
                if (file_exists(state.dir) && !state.rebasing) {
-                       if (resume == RESUME_ABORT) {
+                       if (resume == RESUME_ABORT || resume == RESUME_QUIT) {
                                am_destroy(&state);
                                am_state_release(&state);
                                return 0;
@@ -2359,11 +2399,18 @@ int cmd_am(int argc, const char **argv, const char *prefix)
        case RESUME_ABORT:
                am_abort(&state);
                break;
+       case RESUME_QUIT:
+               am_rerere_clear();
+               am_destroy(&state);
+               break;
+       case RESUME_SHOW_PATCH:
+               ret = show_patch(&state);
+               break;
        default:
                die("BUG: invalid resume value");
        }
 
        am_state_release(&state);
 
-       return 0;
+       return ret;
 }
index f863465a0fa137f3446071a0ec1653adad536fdc..73971d0dd20e7233d6cb1e6c16986ede25526939 100644 (file)
@@ -55,7 +55,7 @@ static int run_remote_archiver(int argc, const char **argv,
 
        buf = packet_read_line(fd[0], NULL);
        if (!buf)
-               die(_("git archive: expected ACK/NAK, got EOF"));
+               die(_("git archive: expected ACK/NAK, got a flush packet"));
        if (strcmp(buf, "ACK")) {
                if (starts_with(buf, "NACK "))
                        die(_("git archive: NACK %s"), buf + 5);
index 005f55aaa257fc34f81517650223a3c195c03178..9dcb367b90d99fc9ebbbf878f53cd5b4651d7864 100644 (file)
@@ -649,6 +649,15 @@ static int blame_move_callback(const struct option *option, const char *arg, int
        return 0;
 }
 
+static int is_a_rev(const char *name)
+{
+       struct object_id oid;
+
+       if (get_oid(name, &oid))
+               return 0;
+       return OBJ_NONE < sha1_object_info(oid.hash, NULL);
+}
+
 int cmd_blame(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
@@ -845,16 +854,15 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        } else {
                if (argc < 2)
                        usage_with_options(blame_opt_usage, options);
-               path = add_prefix(prefix, argv[argc - 1]);
-               if (argc == 3 && !file_exists(path)) { /* (2b) */
+               if (argc == 3 && is_a_rev(argv[argc - 1])) { /* (2b) */
                        path = add_prefix(prefix, argv[1]);
                        argv[1] = argv[2];
+               } else {        /* (2a) */
+                       if (argc == 2 && is_a_rev(argv[1]) && !get_git_work_tree())
+                               die("missing <path> to blame");
+                       path = add_prefix(prefix, argv[argc - 1]);
                }
                argv[argc - 1] = "--";
-
-               setup_work_tree();
-               if (!file_exists(path))
-                       die_errno("cannot stat path '%s'", path);
        }
 
        revs.disable_stdin = 1;
index 3e280b9c7aa9c93c8e7572a4fe3f7ef3ac92b3af..ec9a959e08d0e3dfa0d2b08529cacbe8d2e04c6e 100644 (file)
@@ -72,7 +72,7 @@ static int check_ignore(struct dir_struct *dir,
 {
        const char *full_path;
        char *seen;
-       int num_ignored = 0, dtype = DT_UNKNOWN, i;
+       int num_ignored = 0, i;
        struct exclude *exclude;
        struct pathspec pathspec;
 
@@ -104,6 +104,7 @@ static int check_ignore(struct dir_struct *dir,
                full_path = pathspec.items[i].match;
                exclude = NULL;
                if (!seen[i]) {
+                       int dtype = DT_UNKNOWN;
                        exclude = last_exclude_matching(dir, &the_index,
                                                        full_path, &dtype);
                }
index c4289847063ac02ed061f890ffd71cd329d67109..e4869df7b434845544dfcc0c37cae6a77cd42dad 100644 (file)
@@ -502,7 +502,7 @@ static void describe(const char *arg, int last_one)
 
        if (cmit)
                describe_commit(&oid, &sb);
-       else if (lookup_blob(&oid))
+       else if (sha1_object_info(oid.hash, NULL) == OBJ_BLOB)
                describe_blob(oid, &sb);
        else
                die(_("%s is neither a commit nor blob"), arg);
index 8ee998ea2ee8f736a49afe285d31b0be478cf15b..d32d94692c7eae8372c4a8abac45f172e6693247 100644 (file)
@@ -39,6 +39,10 @@ static int fetch_prune_config = -1; /* unspecified */
 static int prune = -1; /* unspecified */
 #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
 
+static int fetch_prune_tags_config = -1; /* unspecified */
+static int prune_tags = -1; /* unspecified */
+#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
+
 static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
 static int progress = -1;
 static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
@@ -66,6 +70,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
                return 0;
        }
 
+       if (!strcmp(k, "fetch.prunetags")) {
+               fetch_prune_tags_config = git_config_bool(k, v);
+               return 0;
+       }
+
        if (!strcmp(k, "submodule.recurse")) {
                int r = git_config_bool(k, v) ?
                        RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
@@ -128,6 +137,8 @@ static struct option builtin_fetch_options[] = {
                    N_("number of submodules fetched in parallel")),
        OPT_BOOL('p', "prune", &prune,
                 N_("prune remote-tracking branches no longer on remote")),
+       OPT_BOOL('P', "prune-tags", &prune_tags,
+                N_("prune local tags no longer on remote and clobber changed tags")),
        { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
                    N_("control recursive fetching of submodules"),
                    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
@@ -1220,6 +1231,8 @@ static void add_options_to_argv(struct argv_array *argv)
                argv_array_push(argv, "--dry-run");
        if (prune != -1)
                argv_array_push(argv, prune ? "--prune" : "--no-prune");
+       if (prune_tags != -1)
+               argv_array_push(argv, prune_tags ? "--prune-tags" : "--no-prune-tags");
        if (update_head_ok)
                argv_array_push(argv, "--update-head-ok");
        if (force)
@@ -1323,12 +1336,15 @@ static inline void fetch_one_setup_partial(struct remote *remote)
        return;
 }
 
-static int fetch_one(struct remote *remote, int argc, const char **argv)
+static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)
 {
        static const char **refs = NULL;
        struct refspec *refspec;
        int ref_nr = 0;
+       int j = 0;
        int exit_code;
+       int maybe_prune_tags;
+       int remote_via_config = remote_is_configured(remote, 0);
 
        if (!remote)
                die(_("No remote repository specified.  Please, specify either a URL or a\n"
@@ -1338,18 +1354,39 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
 
        if (prune < 0) {
                /* no command line request */
-               if (0 <= gtransport->remote->prune)
-                       prune = gtransport->remote->prune;
+               if (0 <= remote->prune)
+                       prune = remote->prune;
                else if (0 <= fetch_prune_config)
                        prune = fetch_prune_config;
                else
                        prune = PRUNE_BY_DEFAULT;
        }
 
+       if (prune_tags < 0) {
+               /* no command line request */
+               if (0 <= remote->prune_tags)
+                       prune_tags = remote->prune_tags;
+               else if (0 <= fetch_prune_tags_config)
+                       prune_tags = fetch_prune_tags_config;
+               else
+                       prune_tags = PRUNE_TAGS_BY_DEFAULT;
+       }
+
+       maybe_prune_tags = prune_tags_ok && prune_tags;
+       if (maybe_prune_tags && remote_via_config)
+               add_prune_tags_to_fetch_refspec(remote);
+
+       if (argc > 0 || (maybe_prune_tags && !remote_via_config)) {
+               size_t nr_alloc = st_add3(argc, maybe_prune_tags, 1);
+               refs = xcalloc(nr_alloc, sizeof(const char *));
+               if (maybe_prune_tags) {
+                       refs[j++] = xstrdup("refs/tags/*:refs/tags/*");
+                       ref_nr++;
+               }
+       }
+
        if (argc > 0) {
-               int j = 0;
                int i;
-               refs = xcalloc(st_add(argc, 1), sizeof(const char *));
                for (i = 0; i < argc; i++) {
                        if (!strcmp(argv[i], "tag")) {
                                i++;
@@ -1359,9 +1396,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
                                                    argv[i], argv[i]);
                        } else
                                refs[j++] = argv[i];
+                       ref_nr++;
                }
-               refs[j] = NULL;
-               ref_nr = j;
        }
 
        sigchain_push_common(unlock_pack_on_signal);
@@ -1380,6 +1416,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        struct string_list list = STRING_LIST_INIT_DUP;
        struct remote *remote = NULL;
        int result = 0;
+       int prune_tags_ok = 1;
        struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
 
        packet_trace_identity("fetch");
@@ -1446,6 +1483,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                } else {
                        /* Zero or one remotes */
                        remote = remote_get(argv[0]);
+                       prune_tags_ok = (argc == 1);
                        argc--;
                        argv++;
                }
@@ -1454,7 +1492,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (remote) {
                if (filter_options.choice || repository_format_partial_clone)
                        fetch_one_setup_partial(remote);
-               result = fetch_one(remote, argc, argv);
+               result = fetch_one(remote, argc, argv, prune_tags_ok);
        } else {
                if (filter_options.choice)
                        die(_("--filter can only be used with the remote configured in core.partialClone"));
index 92ba99a1a5efff8598251c2ed806bae12832ae0a..f598001db2584941a2b9b936bcd00bd0f8c22fc1 100644 (file)
@@ -33,6 +33,7 @@
 #include "sequencer.h"
 #include "string-list.h"
 #include "packfile.h"
+#include "tag.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -1125,6 +1126,43 @@ static struct commit_list *collect_parents(struct commit *head_commit,
        return remoteheads;
 }
 
+static int merging_a_throwaway_tag(struct commit *commit)
+{
+       char *tag_ref;
+       struct object_id oid;
+       int is_throwaway_tag = 0;
+
+       /* Are we merging a tag? */
+       if (!merge_remote_util(commit) ||
+           !merge_remote_util(commit)->obj ||
+           merge_remote_util(commit)->obj->type != OBJ_TAG)
+               return is_throwaway_tag;
+
+       /*
+        * Now we know we are merging a tag object.  Are we downstream
+        * and following the tags from upstream?  If so, we must have
+        * the tag object pointed at by "refs/tags/$T" where $T is the
+        * tagname recorded in the tag object.  We want to allow such
+        * a "just to catch up" merge to fast-forward.
+        *
+        * Otherwise, we are playing an integrator's role, making a
+        * merge with a throw-away tag from a contributor with
+        * something like "git pull $contributor $signed_tag".
+        * We want to forbid such a merge from fast-forwarding
+        * by default; otherwise we would not keep the signature
+        * anywhere.
+        */
+       tag_ref = xstrfmt("refs/tags/%s",
+                         ((struct tag *)merge_remote_util(commit)->obj)->tag);
+       if (!read_ref(tag_ref, &oid) &&
+           !oidcmp(&oid, &merge_remote_util(commit)->obj->oid))
+               is_throwaway_tag = 0;
+       else
+               is_throwaway_tag = 1;
+       free(tag_ref);
+       return is_throwaway_tag;
+}
+
 int cmd_merge(int argc, const char **argv, const char *prefix)
 {
        struct object_id result_tree, stash, head_oid;
@@ -1322,10 +1360,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                            oid_to_hex(&commit->object.oid));
                setenv(buf.buf, merge_remote_util(commit)->name, 1);
                strbuf_reset(&buf);
-               if (fast_forward != FF_ONLY &&
-                   merge_remote_util(commit) &&
-                   merge_remote_util(commit)->obj &&
-                   merge_remote_util(commit)->obj->type == OBJ_TAG)
+               if (fast_forward != FF_ONLY && merging_a_throwaway_tag(commit))
                        fast_forward = FF_NO;
        }
 
index cf3684d907a2fe3408581502c682d6290c790827..8ce6a2ddd4c5008c9d32d45dc21e11d97ff8b79d 100644 (file)
@@ -286,8 +286,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 
                pos = cache_name_pos(src, strlen(src));
                assert(pos >= 0);
-               if (!show_only)
-                       rename_cache_entry_at(pos, dst);
+               rename_cache_entry_at(pos, dst);
        }
 
        if (gitmodules_modified)
index 48300d9e11733396610e81fbb00ee7d17a7dba66..d320b6f1e3bcbcd429d5e2baa94b453035572db7 100644 (file)
@@ -134,7 +134,7 @@ static void show_commit(struct commit *commit, void *data)
        else
                putchar('\n');
 
-       if (revs->verbose_header && get_cached_commit_buffer(commit, NULL)) {
+       if (revs->verbose_header) {
                struct strbuf buf = STRBUF_INIT;
                struct pretty_print_context ctx = {0};
                ctx.abbrev = revs->abbrev;
index 8885e21ddc81e4d2686aaeb6fe4c1bbffc916ec1..26f8d5136528bb5e92e1f3b78bde7a37f4188f1e 100644 (file)
@@ -194,6 +194,7 @@ static int build_tag_object(struct strbuf *buf, int sign, struct object_id *resu
 
 struct create_tag_options {
        unsigned int message_given:1;
+       unsigned int use_editor:1;
        unsigned int sign;
        enum {
                CLEANUP_NONE,
@@ -224,7 +225,7 @@ static void create_tag(const struct object_id *object, const char *tag,
                    tag,
                    git_committer_info(IDENT_STRICT));
 
-       if (!opt->message_given) {
+       if (!opt->message_given || opt->use_editor) {
                int fd;
 
                /* write the template message before editing: */
@@ -233,7 +234,10 @@ static void create_tag(const struct object_id *object, const char *tag,
                if (fd < 0)
                        die_errno(_("could not create file '%s'"), path);
 
-               if (!is_null_oid(prev)) {
+               if (opt->message_given) {
+                       write_or_die(fd, buf->buf, buf->len);
+                       strbuf_reset(buf);
+               } else if (!is_null_oid(prev)) {
                        write_tag_body(fd, prev);
                } else {
                        struct strbuf buf = STRBUF_INIT;
@@ -372,6 +376,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
        struct ref_format format = REF_FORMAT_INIT;
        int icase = 0;
+       int edit_flag = 0;
        struct option options[] = {
                OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
                { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
@@ -386,6 +391,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                OPT_CALLBACK('m', "message", &msg, N_("message"),
                             N_("tag message"), parse_msg_arg),
                OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
+               OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
                OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
                OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"),
                        N_("how to strip spaces and #comments from message")),
@@ -524,6 +530,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                die(_("tag '%s' already exists"), tag);
 
        opt.message_given = msg.given || msgfile;
+       opt.use_editor = edit_flag;
 
        if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
                opt.cleanup_mode = CLEANUP_ALL;
index 9efdc224661b02192d06c6877e49c5585ddbcdca..4e7c98758fcfb93c19e3dbf58fc3167829e998b8 100644 (file)
@@ -345,9 +345,23 @@ static int add_worktree(const char *path, const char *refname,
         * Hook failure does not warrant worktree deletion, so run hook after
         * is_junk is cleared, but do return appropriate code when hook fails.
         */
-       if (!ret && opts->checkout)
-               ret = run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid),
-                                 oid_to_hex(&commit->object.oid), "1", NULL);
+       if (!ret && opts->checkout) {
+               const char *hook = find_hook("post-checkout");
+               if (hook) {
+                       const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
+                       cp.git_cmd = 0;
+                       cp.no_stdin = 1;
+                       cp.stdout_to_stderr = 1;
+                       cp.dir = path;
+                       cp.env = env;
+                       cp.argv = NULL;
+                       argv_array_pushl(&cp.args, absolute_path(hook),
+                                        oid_to_hex(&null_oid),
+                                        oid_to_hex(&commit->object.oid),
+                                        "1", NULL);
+                       ret = run_command(&cp);
+               }
+       }
 
        argv_array_clear(&child_env);
        strbuf_release(&sb);
diff --git a/color.c b/color.c
index d48dd947c987cdca13f103fd8b4a2e56eceab238..f277e72e4ce04815f71c949dfdf7c89c9462c5b7 100644 (file)
--- a/color.c
+++ b/color.c
@@ -161,11 +161,6 @@ int color_parse(const char *value, char *dst)
        return color_parse_mem(value, strlen(value), dst);
 }
 
-void color_set(char *dst, const char *color_bytes)
-{
-       xsnprintf(dst, COLOR_MAXLEN, "%s", color_bytes);
-}
-
 /*
  * Write the ANSI color codes for "c" to "out"; the string should
  * already have the ANSI escape code in it. "out" should have enough
@@ -399,8 +394,6 @@ static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
        return r;
 }
 
-
-
 int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
 {
        va_list args;
diff --git a/color.h b/color.h
index fd2b688dfbccbe4eef4c1f79b680db2a01e2f9ef..cd0bcedd084f3741fad55569b18ec15e12d75cf8 100644 (file)
--- a/color.h
+++ b/color.h
@@ -76,22 +76,46 @@ int git_color_config(const char *var, const char *value, void *cb);
 int git_color_default_config(const char *var, const char *value, void *cb);
 
 /*
- * Set the color buffer (which must be COLOR_MAXLEN bytes)
- * to the raw color bytes; this is useful for initializing
- * default color variables.
+ * Parse a config option, which can be a boolean or one of
+ * "never", "auto", "always". Return a constant of
+ * GIT_COLOR_NEVER for "never" or negative boolean,
+ * GIT_COLOR_ALWAYS for "always" or a positive boolean,
+ * and GIT_COLOR_AUTO for "auto".
  */
-void color_set(char *dst, const char *color_bytes);
-
 int git_config_colorbool(const char *var, const char *value);
+
+/*
+ * Return a boolean whether to use color, where the argument 'var' is
+ * one of GIT_COLOR_UNKNOWN, GIT_COLOR_NEVER, GIT_COLOR_ALWAYS, GIT_COLOR_AUTO.
+ */
 int want_color(int var);
+
+/*
+ * Translate a Git color from 'value' into a string that the terminal can
+ * interpret and store it into 'dst'. The Git color values are of the form
+ * "foreground [background] [attr]" where fore- and background can be a color
+ * name ("red"), a RGB code (#0xFF0000) or a 256-color-mode from the terminal.
+ */
 int color_parse(const char *value, char *dst);
 int color_parse_mem(const char *value, int len, char *dst);
+
+/*
+ * Output the formatted string in the specified color (and then reset to normal
+ * color so subsequent output is uncolored). Omits the color encapsulation if
+ * `color` is NULL. The `color_fprintf_ln` prints a new line after resetting
+ * the color.  The `color_print_strbuf` prints the contents of the given
+ * strbuf (BUG: but only up to its first NUL character).
+ */
 __attribute__((format (printf, 3, 4)))
 int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
 __attribute__((format (printf, 3, 4)))
 int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
 void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb);
 
+/*
+ * Check if the given color is GIT_COLOR_NIL that means "no color selected".
+ * The caller needs to replace the color with the actual desired color.
+ */
 int color_is_nil(const char *color);
 
 #endif /* COLOR_H */
index 685a80d13843800be3c633539b0a22e88652444a..6a1d0de0cc571f395eeeb8f149d4377a1a5e1602 100644 (file)
@@ -182,7 +182,6 @@ ifeq ($(uname_O),Cygwin)
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
        X = .exe
        UNRELIABLE_FSTAT = UnfortunatelyYes
-       SPARSE_FLAGS = -isystem /usr/include/w32api -Wno-one-bit-signed-bitfield
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        MMAP_PREVENTS_DELETE = UnfortunatelyYes
        COMPAT_OBJS += compat/cygwin.o
index 88813e91244d820b4aa2c0fb5e60a44db03c3e59..91536d831c8954b67efe738ace3fad7f8f2ee2d7 100644 (file)
@@ -439,7 +439,7 @@ __git_refs ()
                        track=""
                        ;;
                *)
-                       for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
+                       for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD; do
                                case "$i" in
                                $match*)
                                        if [ -e "$dir/$i" ]; then
@@ -1077,7 +1077,7 @@ _git_am ()
 {
        __git_find_repo_path
        if [ -d "$__git_repo_path"/rebase-apply ]; then
-               __gitcomp "--skip --continue --resolved --abort"
+               __gitcomp "--skip --continue --resolved --abort --quit --show-current-patch"
                return
        fi
        case "$cur" in
@@ -1468,7 +1468,7 @@ __git_fetch_recurse_submodules="yes on-demand no"
 __git_fetch_options="
        --quiet --verbose --append --upload-pack --force --keep --depth=
        --tags --no-tags --all --prune --dry-run --recurse-submodules=
-       --unshallow --update-shallow
+       --unshallow --update-shallow --prune-tags
 "
 
 _git_fetch ()
@@ -1992,11 +1992,11 @@ _git_rebase ()
 {
        __git_find_repo_path
        if [ -f "$__git_repo_path"/rebase-merge/interactive ]; then
-               __gitcomp "--continue --skip --abort --quit --edit-todo"
+               __gitcomp "--continue --skip --abort --quit --edit-todo --show-current-patch"
                return
        elif [ -d "$__git_repo_path"/rebase-apply ] || \
             [ -d "$__git_repo_path"/rebase-merge ]; then
-               __gitcomp "--continue --skip --abort --quit"
+               __gitcomp "--continue --skip --abort --quit --show-current-patch"
                return
        fi
        __git_complete_strategy && return
index 60d76cdddfc75f0c77d5798ac0b1adc6b0e93899..352deda69dcfd009ff025615dff07dd0a5d015d1 100644 (file)
@@ -28,7 +28,7 @@ as a subdirectory of your application.
 
 Subtrees are not to be confused with submodules, which are meant for
 the same task. Unlike submodules, subtrees do not need any special
-constructions (like .gitmodule files or gitlinks) be present in
+constructions (like .gitmodules files or gitlinks) be present in
 your repository, and do not force end-users of your
 repository to do anything special or to understand how subtrees
 work. A subtree is just a subdirectory that can be
diff --git a/dir.c b/dir.c
index 536416ff2df5f7c475deba4739d4edaf340ae898..6dd91be8185d4012e11495a9238636913fedeeef 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -771,7 +771,16 @@ static void invalidate_directory(struct untracked_cache *uc,
                                 struct untracked_cache_dir *dir)
 {
        int i;
-       uc->dir_invalidated++;
+
+       /*
+        * Invalidation increment here is just roughly correct. If
+        * untracked_nr or any of dirs[].recurse is non-zero, we
+        * should increment dir_invalidated too. But that's more
+        * expensive to do.
+        */
+       if (dir->valid)
+               uc->dir_invalidated++;
+
        dir->valid = 0;
        dir->untracked_nr = 0;
        for (i = 0; i < dir->dirs_nr; i++)
@@ -1770,7 +1779,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
        if (!de)
                return treat_path_fast(dir, untracked, cdir, istate, path,
                                       baselen, pathspec);
-       if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
+       if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git"))
                return path_none;
        strbuf_setlen(path, baselen);
        strbuf_addstr(path, de->d_name);
@@ -1806,24 +1815,19 @@ static int valid_cached_dir(struct dir_struct *dir,
         */
        refresh_fsmonitor(istate);
        if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
-               if (stat(path->len ? path->buf : ".", &st)) {
-                       invalidate_directory(dir->untracked, untracked);
+               if (lstat(path->len ? path->buf : ".", &st)) {
                        memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
                        return 0;
                }
                if (!untracked->valid ||
                        match_stat_data_racy(istate, &untracked->stat_data, &st)) {
-                       if (untracked->valid)
-                               invalidate_directory(dir->untracked, untracked);
                        fill_stat_data(&untracked->stat_data, &st);
                        return 0;
                }
        }
 
-       if (untracked->check_only != !!check_only) {
-               invalidate_directory(dir->untracked, untracked);
+       if (untracked->check_only != !!check_only)
                return 0;
-       }
 
        /*
         * prep_exclude will be called eventually on this directory,
@@ -1850,13 +1854,20 @@ static int open_cached_dir(struct cached_dir *cdir,
                           struct strbuf *path,
                           int check_only)
 {
+       const char *c_path;
+
        memset(cdir, 0, sizeof(*cdir));
        cdir->untracked = untracked;
        if (valid_cached_dir(dir, untracked, istate, path, check_only))
                return 0;
-       cdir->fdir = opendir(path->len ? path->buf : ".");
-       if (dir->untracked)
+       c_path = path->len ? path->buf : ".";
+       cdir->fdir = opendir(c_path);
+       if (!cdir->fdir)
+               warning_errno(_("could not open directory '%s'"), c_path);
+       if (dir->untracked) {
+               invalidate_directory(dir->untracked, untracked);
                dir->untracked->dir_opened++;
+       }
        if (!cdir->fdir)
                return -1;
        return 0;
@@ -2966,10 +2977,12 @@ static int invalidate_one_component(struct untracked_cache *uc,
 }
 
 void untracked_cache_invalidate_path(struct index_state *istate,
-                                    const char *path)
+                                    const char *path, int safe_path)
 {
        if (!istate->untracked || !istate->untracked->root)
                return;
+       if (!safe_path && !verify_path(path))
+               return;
        invalidate_one_component(istate->untracked, istate->untracked->root,
                                 path, strlen(path));
 }
@@ -2977,13 +2990,13 @@ void untracked_cache_invalidate_path(struct index_state *istate,
 void untracked_cache_remove_from_index(struct index_state *istate,
                                       const char *path)
 {
-       untracked_cache_invalidate_path(istate, path);
+       untracked_cache_invalidate_path(istate, path, 1);
 }
 
 void untracked_cache_add_to_index(struct index_state *istate,
                                  const char *path)
 {
-       untracked_cache_invalidate_path(istate, path);
+       untracked_cache_invalidate_path(istate, path, 1);
 }
 
 /* Update gitfile and core.worktree setting to connect work tree and git dir */
diff --git a/dir.h b/dir.h
index e7bb786a33967360750ad21446ebe65b3bea8b80..b0758b82a20017dd3ce29c54454678f026718078 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -350,7 +350,7 @@ static inline int dir_path_match(const struct dir_entry *ent,
 int cmp_dir_entry(const void *p1, const void *p2);
 int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
 
-void untracked_cache_invalidate_path(struct index_state *, const char *);
+void untracked_cache_invalidate_path(struct index_state *, const char *, int safe_path);
 void untracked_cache_remove_from_index(struct index_state *, const char *);
 void untracked_cache_add_to_index(struct index_state *, const char *);
 
index 8253d746e0c40492e05360fe6f27bfa85bb39fbc..d97461296d5692521cd038f9ffa0b5f5c3c3f3dd 100644 (file)
@@ -261,8 +261,8 @@ static enum ack_type get_ack(int fd, struct object_id *result_oid)
        char *line = packet_read_line(fd, &len);
        const char *arg;
 
-       if (!len)
-               die(_("git fetch-pack: expected ACK/NAK, got EOF"));
+       if (!line)
+               die(_("git fetch-pack: expected ACK/NAK, got a flush packet"));
        if (!strcmp(line, "NAK"))
                return NAK;
        if (skip_prefix(line, "ACK ", &arg)) {
index 0af7c4edba37fd9f6a8cee83cd2715c3fa08b488..6d7bcd5d0ed8f2d3f5abdea2f26c6be72909b657 100644 (file)
@@ -130,7 +130,7 @@ static void fsmonitor_refresh_callback(struct index_state *istate, const char *n
         * as it could be a new untracked file.
         */
        trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
-       untracked_cache_invalidate_path(istate, name);
+       untracked_cache_invalidate_path(istate, name, 0);
 }
 
 void refresh_fsmonitor(struct index_state *istate)
index cd3cc0ccf228c9d601621685042d51368a71c707..65f37436369cd934f2bdb3b627396c5f72a7cb03 100644 (file)
@@ -65,7 +65,7 @@ static inline void mark_fsmonitor_invalid(struct index_state *istate, struct cac
 {
        if (core_fsmonitor) {
                ce->ce_flags &= ~CE_FSMONITOR_VALID;
-               untracked_cache_invalidate_path(istate, ce->name);
+               untracked_cache_invalidate_path(istate, ce->name, 1);
                trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_invalid '%s'", ce->name);
        }
 }
index 964c3a75420db4751cf11125b68b6904112632f1..d9d8ff3090e914421ab67071117789f6b3554475 100755 (executable)
@@ -1184,7 +1184,13 @@ sub edit_hunk_loop {
 );
 
 sub help_patch_cmd {
-       print colored $help_color, __($help_patch_modes{$patch_mode}), "\n", __ <<EOF ;
+       local $_;
+       my $other = $_[0] . ",?";
+       print colored $help_color, __($help_patch_modes{$patch_mode}), "\n",
+               map { "$_\n" } grep {
+                       my $c = quotemeta(substr($_, 0, 1));
+                       $other =~ /,$c/
+               } split "\n", __ <<EOF ;
 g - select a hunk to go to
 / - search for a hunk matching the given regex
 j - leave this hunk undecided, see next undecided hunk
@@ -1302,39 +1308,39 @@ sub display_hunks {
 
 my %patch_update_prompt_modes = (
        stage => {
-               mode => N__("Stage mode change [y,n,q,a,d,/%s,?]? "),
-               deletion => N__("Stage deletion [y,n,q,a,d,/%s,?]? "),
-               hunk => N__("Stage this hunk [y,n,q,a,d,/%s,?]? "),
+               mode => N__("Stage mode change [y,n,q,a,d%s,?]? "),
+               deletion => N__("Stage deletion [y,n,q,a,d%s,?]? "),
+               hunk => N__("Stage this hunk [y,n,q,a,d%s,?]? "),
        },
        stash => {
-               mode => N__("Stash mode change [y,n,q,a,d,/%s,?]? "),
-               deletion => N__("Stash deletion [y,n,q,a,d,/%s,?]? "),
-               hunk => N__("Stash this hunk [y,n,q,a,d,/%s,?]? "),
+               mode => N__("Stash mode change [y,n,q,a,d%s,?]? "),
+               deletion => N__("Stash deletion [y,n,q,a,d%s,?]? "),
+               hunk => N__("Stash this hunk [y,n,q,a,d%s,?]? "),
        },
        reset_head => {
-               mode => N__("Unstage mode change [y,n,q,a,d,/%s,?]? "),
-               deletion => N__("Unstage deletion [y,n,q,a,d,/%s,?]? "),
-               hunk => N__("Unstage this hunk [y,n,q,a,d,/%s,?]? "),
+               mode => N__("Unstage mode change [y,n,q,a,d%s,?]? "),
+               deletion => N__("Unstage deletion [y,n,q,a,d%s,?]? "),
+               hunk => N__("Unstage this hunk [y,n,q,a,d%s,?]? "),
        },
        reset_nothead => {
-               mode => N__("Apply mode change to index [y,n,q,a,d,/%s,?]? "),
-               deletion => N__("Apply deletion to index [y,n,q,a,d,/%s,?]? "),
-               hunk => N__("Apply this hunk to index [y,n,q,a,d,/%s,?]? "),
+               mode => N__("Apply mode change to index [y,n,q,a,d%s,?]? "),
+               deletion => N__("Apply deletion to index [y,n,q,a,d%s,?]? "),
+               hunk => N__("Apply this hunk to index [y,n,q,a,d%s,?]? "),
        },
        checkout_index => {
-               mode => N__("Discard mode change from worktree [y,n,q,a,d,/%s,?]? "),
-               deletion => N__("Discard deletion from worktree [y,n,q,a,d,/%s,?]? "),
-               hunk => N__("Discard this hunk from worktree [y,n,q,a,d,/%s,?]? "),
+               mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
+               deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+               hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
        },
        checkout_head => {
-               mode => N__("Discard mode change from index and worktree [y,n,q,a,d,/%s,?]? "),
-               deletion => N__("Discard deletion from index and worktree [y,n,q,a,d,/%s,?]? "),
-               hunk => N__("Discard this hunk from index and worktree [y,n,q,a,d,/%s,?]? "),
+               mode => N__("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
+               deletion => N__("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
+               hunk => N__("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
        },
        checkout_nothead => {
-               mode => N__("Apply mode change to index and worktree [y,n,q,a,d,/%s,?]? "),
-               deletion => N__("Apply deletion to index and worktree [y,n,q,a,d,/%s,?]? "),
-               hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d,/%s,?]? "),
+               mode => N__("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
+               deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
+               hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
        },
 );
 
@@ -1390,7 +1396,7 @@ sub patch_update_file {
                        $other .= ',J';
                }
                if ($num > 1) {
-                       $other .= ',g';
+                       $other .= ',g,/';
                }
                for ($i = 0; $i < $num; $i++) {
                        if (!defined $hunk[$i]{USE}) {
@@ -1431,8 +1437,12 @@ sub patch_update_file {
                                }
                                next;
                        }
-                       elsif ($other =~ /g/ && $line =~ /^g(.*)/) {
+                       elsif ($line =~ /^g(.*)/) {
                                my $response = $1;
+                               unless ($other =~ /g/) {
+                                       error_msg __("No other hunks to goto\n");
+                                       next;
+                               }
                                my $no = $ix > 10 ? $ix - 10 : 0;
                                while ($response eq '') {
                                        $no = display_hunks(\@hunk, $no);
@@ -1478,6 +1488,10 @@ sub patch_update_file {
                        }
                        elsif ($line =~ m|^/(.*)|) {
                                my $regex = $1;
+                               unless ($other =~ m|/|) {
+                                       error_msg __("No other hunks to search\n");
+                                       next;
+                               }
                                if ($1 eq "") {
                                        print colored $prompt_color, __("search for regex? ");
                                        $regex = <STDIN>;
@@ -1546,7 +1560,11 @@ sub patch_update_file {
                                        next;
                                }
                        }
-                       elsif ($other =~ /s/ && $line =~ /^s/) {
+                       elsif ($line =~ /^s/) {
+                               unless ($other =~ /s/) {
+                                       error_msg __("Sorry, cannot split this hunk\n");
+                                       next;
+                               }
                                my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
                                if (1 < @split) {
                                        print colored $header_color, sprintf(
@@ -1558,7 +1576,11 @@ sub patch_update_file {
                                $num = scalar @hunk;
                                next;
                        }
-                       elsif ($other =~ /e/ && $line =~ /^e/) {
+                       elsif ($line =~ /^e/) {
+                               unless ($other =~ /e/) {
+                                       error_msg __("Sorry, cannot edit this hunk\n");
+                                       next;
+                               }
                                my $newhunk = edit_hunk_loop($head, \@hunk, $ix);
                                if (defined $newhunk) {
                                        splice @hunk, $ix, 1, $newhunk;
index 0f78051792fae93f7966c2a3f9fc670b4fca9188..be3f068922c5a3ba0d1112f3b8b1cf1228567d2e 100644 (file)
@@ -27,6 +27,9 @@ skip)
        move_to_original_branch
        return
        ;;
+show-current-patch)
+       exec git am --show-current-patch
+       ;;
 esac
 
 if test -z "$rebase_root"
index 81c5b42875771ab98ca86e718e14a5a393aa1a86..331c8dfeac3cac2fd2d9d9287d5c487669fe80a0 100644 (file)
@@ -199,12 +199,14 @@ make_patch () {
 
 die_with_patch () {
        echo "$1" > "$state_dir"/stopped-sha
+       git update-ref REBASE_HEAD "$1"
        make_patch "$1"
        die "$2"
 }
 
 exit_with_patch () {
        echo "$1" > "$state_dir"/stopped-sha
+       git update-ref REBASE_HEAD "$1"
        make_patch $1
        git rev-parse --verify HEAD > "$amend"
        gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
@@ -396,7 +398,7 @@ pick_one_preserving_merges () {
                                        --sq-quote "$gpg_sign_opt")} \
                                $allow_rerere_autoupdate "$merge_args" \
                                "$strategy_args" \
-                               -m $(git rev-parse --sq-quote "$msg_content") \
+                               -m "$(git rev-parse --sq-quote "$msg_content")" \
                                "$new_parents"
                        then
                                printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
@@ -844,6 +846,9 @@ To continue rebase after editing, run:
 
        exit
        ;;
+show-current-patch)
+       exec git show REBASE_HEAD --
+       ;;
 esac
 
 comment_for_reflog start
@@ -859,6 +864,7 @@ fi
 
 orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
 mkdir -p "$state_dir" || die "$(eval_gettext "Could not create temporary \$state_dir")"
+rm -f "$(git rev-parse --git-path REBASE_HEAD)"
 
 : > "$state_dir"/interactive || die "$(gettext "Could not mark as interactive")"
 write_basic_state
index 1831e589b8c94b3fbcee710c5742b71a3e7e8f9e..ceb715453cc9eba0b6e91abfd2ea3863e74f3e05 100644 (file)
@@ -58,6 +58,7 @@ call_merge () {
        echo "$msgnum" >"$state_dir/msgnum"
        cmt="$(cat "$state_dir/cmt.$msgnum")"
        echo "$cmt" > "$state_dir/current"
+       git update-ref REBASE_HEAD "$cmt"
        hd=$(git rev-parse --verify HEAD)
        cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
        eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
@@ -138,11 +139,15 @@ skip)
        finish_rb_merge
        return
        ;;
+show-current-patch)
+       exec git show REBASE_HEAD --
+       ;;
 esac
 
 mkdir -p "$state_dir"
 echo "$onto_name" > "$state_dir/onto_name"
 write_basic_state
+rm -f "$(git rev-parse --git-path REBASE_HEAD)"
 
 msgnum=0
 for cmt in $(git rev-list --reverse --no-merges "$revisions")
index b353c33d417a913c2b356b3c070b1dbbc033f162..a1f6e5de6a3ed1fe9a6217a136611682f3db6582 100755 (executable)
@@ -46,6 +46,7 @@ abort!             abort and check out the original branch
 skip!              skip current patch and continue
 edit-todo!         edit the todo list during an interactive rebase
 quit!              abort but keep HEAD where it is
+show-current-patch! show the patch file being applied or merged
 "
 . git-sh-setup
 set_reflog_action rebase
@@ -183,6 +184,7 @@ You can run "git stash pop" or "git stash drop" at any time.
 }
 
 finish_rebase () {
+       rm -f "$(git rev-parse --git-path REBASE_HEAD)"
        apply_autostash &&
        { git gc --auto || true; } &&
        rm -rf "$state_dir"
@@ -247,7 +249,7 @@ do
        --verify)
                ok_to_skip_pre_rebase=
                ;;
-       --continue|--skip|--abort|--quit|--edit-todo)
+       --continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
                test $total_argc -eq 2 || usage
                action=${1##--}
                ;;
@@ -417,6 +419,10 @@ quit)
 edit-todo)
        run_specific_rebase
        ;;
+show-current-patch)
+       run_specific_rebase
+       die "BUG: run_specific_rebase is not supposed to return here"
+       ;;
 esac
 
 # Make sure no rebase is in progress
index bbf4deaa0d42ef2a843c2240000d7e34dfff0b62..435c7c9e26d75e8f6a4f635753f248fcf8106790 100755 (executable)
@@ -379,6 +379,10 @@ sub signal_handler {
 die __("Cannot run git format-patch from outside a repository\n")
        if $format_patch and not $repo;
 
+die __("`batch-size` and `relogin` must be specified together " .
+       "(via command-line or configuration option)\n")
+       if defined $relogin_delay and not defined $batch_size;
+
 # Now, let's fill any that aren't set in with defaults:
 
 sub read_config {
index 1ef1889dbd806494b8659023a091920b07692035..9d065fb4bf9a308e64fa2d4aa5dff294b116f1a9 100644 (file)
@@ -17,15 +17,15 @@ export TEXTDOMAINDIR
 
 # First decide what scheme to use...
 GIT_INTERNAL_GETTEXT_SH_SCHEME=fallthrough
-if test -n "@@USE_GETTEXT_SCHEME@@"
+if test -n "$GIT_GETTEXT_POISON"
+then
+       GIT_INTERNAL_GETTEXT_SH_SCHEME=poison
+elif test -n "@@USE_GETTEXT_SCHEME@@"
 then
        GIT_INTERNAL_GETTEXT_SH_SCHEME="@@USE_GETTEXT_SCHEME@@"
 elif test -n "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
 then
        : no probing necessary
-elif test -n "$GIT_GETTEXT_POISON"
-then
-       GIT_INTERNAL_GETTEXT_SH_SCHEME=poison
 elif type gettext.sh >/dev/null 2>&1
 then
        # GNU libintl's gettext.sh
diff --git a/git.c b/git.c
index c870b9719c21b2db23ea7ab5d56fdeda483cb02e..96cd734f1282161eebde954f7e638cf8dd8c3516 100644 (file)
--- a/git.c
+++ b/git.c
@@ -5,11 +5,11 @@
 #include "run-command.h"
 
 const char git_usage_string[] =
-       "git [--version] [--help] [-C <path>] [-c name=value]\n"
-       "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
-       "           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]\n"
-       "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-       "           <command> [<args>]";
+       N_("git [--version] [--help] [-C <path>] [-c <name>=<value>]\n"
+          "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
+          "           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]\n"
+          "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
+          "           <command> [<args>]");
 
 const char git_more_info_string[] =
        N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -92,7 +92,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--git-dir")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No directory given for --git-dir.\n" );
+                               fprintf(stderr, _("no directory given for --git-dir\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
@@ -106,7 +106,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--namespace")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No namespace given for --namespace.\n" );
+                               fprintf(stderr, _("no namespace given for --namespace\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
@@ -120,7 +120,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--work-tree")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No directory given for --work-tree.\n" );
+                               fprintf(stderr, _("no directory given for --work-tree\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
@@ -134,7 +134,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--super-prefix")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No prefix given for --super-prefix.\n" );
+                               fprintf(stderr, _("no prefix given for --super-prefix\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1);
@@ -156,7 +156,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-c")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "-c expects a configuration string\n" );
+                               fprintf(stderr, _("-c expects a configuration string\n" ));
                                usage(git_usage_string);
                        }
                        git_config_push_parameter((*argv)[1]);
@@ -194,12 +194,12 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-C")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No directory given for -C.\n" );
+                               fprintf(stderr, _("no directory given for -C\n" ));
                                usage(git_usage_string);
                        }
                        if ((*argv)[1][0]) {
                                if (chdir((*argv)[1]))
-                                       die_errno("Cannot change to '%s'", (*argv)[1]);
+                                       die_errno("cannot change to '%s'", (*argv)[1]);
                                if (envchanged)
                                        *envchanged = 1;
                        }
@@ -209,7 +209,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        list_builtins();
                        exit(0);
                } else {
-                       fprintf(stderr, "Unknown option: %s\n", cmd);
+                       fprintf(stderr, _("unknown option: %s\n"), cmd);
                        usage(git_usage_string);
                }
 
@@ -247,7 +247,7 @@ static int handle_alias(int *argcp, const char ***argv)
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
 
-                       die_errno("While expanding alias '%s': '%s'",
+                       die_errno("while expanding alias '%s': '%s'",
                            alias_command, alias_string + 1);
                }
                count = split_cmdline(alias_string, &new_argv);
@@ -256,8 +256,8 @@ static int handle_alias(int *argcp, const char ***argv)
                            split_cmdline_strerror(count));
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
-                       die("alias '%s' changes environment variables\n"
-                                "You can use '!git' in the alias to do this.",
+                       die("alias '%s' changes environment variables.\n"
+                                "You can use '!git' in the alias to do this",
                                 alias_command);
                memmove(new_argv - option_count, new_argv,
                                count * sizeof(char *));
@@ -684,8 +684,8 @@ int cmd_main(int argc, const char **argv)
                if (errno != ENOENT)
                        break;
                if (was_alias) {
-                       fprintf(stderr, "Expansion of alias '%s' failed; "
-                               "'%s' is not a git command\n",
+                       fprintf(stderr, _("expansion of alias '%s' failed; "
+                                         "'%s' is not a git command\n"),
                                cmd, argv[0]);
                        exit(1);
                }
@@ -696,7 +696,7 @@ int cmd_main(int argc, const char **argv)
                        break;
        }
 
-       fprintf(stderr, "Failed to run command '%s': %s\n",
+       fprintf(stderr, _("failed to run command '%s': %s\n"),
                cmd, strerror(errno));
 
        return 1;
diff --git a/grep.c b/grep.c
index 3d7cd0e96f1ee160a66dd500c58d4026bf24e34c..834b8eb439297ff5af4a3da8b946a40ca68057f1 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -18,6 +18,11 @@ static void std_output(struct grep_opt *opt, const void *buf, size_t size)
        fwrite(buf, size, 1, stdout);
 }
 
+static void color_set(char *dst, const char *color_bytes)
+{
+       xsnprintf(dst, COLOR_MAXLEN, "%s", color_bytes);
+}
+
 /*
  * Initialize the grep_defaults template with hardcoded defaults.
  * We could let the compiler do this, but without C99 initializers
index fc0cc0d6d11cbec37b87004f3a56cc4c5495bc72..22b2fb6c581743392d9315b7a5d5ca4d2259aea9 100644 (file)
@@ -659,9 +659,6 @@ void show_log(struct rev_info *opt)
                show_mergetag(opt, commit);
        }
 
-       if (!get_cached_commit_buffer(commit, NULL))
-               return;
-
        if (opt->show_notes) {
                int raw;
                struct strbuf notebuf = STRBUF_INIT;
index ada66f066a002ac5ecb73f94a582d758c0a3a657..163849831c9f11316ce97c649b77c32cf2eed276 100644 (file)
@@ -699,12 +699,12 @@ void adjust_dirname_case(struct index_state *istate, char *name)
                if (*ptr == '/') {
                        struct dir_entry *dir;
 
-                       ptr++;
-                       dir = find_dir_entry(istate, name, ptr - name + 1);
+                       dir = find_dir_entry(istate, name, ptr - name);
                        if (dir) {
                                memcpy((void *)startPtr, dir->name + (startPtr - name), ptr - startPtr);
-                               startPtr = ptr;
+                               startPtr = ptr + 1;
                        }
+                       ptr++;
                }
        }
 }
index 7dbe8739d17d64fad79eac9e7477d00ea3d80df2..5d07f330c8a4bf0a748e3fc87ed70f5affafd681 100644 (file)
@@ -1722,11 +1722,8 @@ off_t find_pack_entry_one(const unsigned char *sha1,
 {
        const uint32_t *level1_ofs = p->index_data;
        const unsigned char *index = p->index_data;
-       unsigned hi, lo, stride;
-       static int debug_lookup = -1;
-
-       if (debug_lookup < 0)
-               debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
+       unsigned stride;
+       uint32_t result;
 
        if (!index) {
                if (open_pack_index(p))
@@ -1739,8 +1736,6 @@ off_t find_pack_entry_one(const unsigned char *sha1,
                index += 8;
        }
        index += 4 * 256;
-       hi = ntohl(level1_ofs[*sha1]);
-       lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
        if (p->index_version > 1) {
                stride = 20;
        } else {
@@ -1748,24 +1743,8 @@ off_t find_pack_entry_one(const unsigned char *sha1,
                index += 4;
        }
 
-       if (debug_lookup)
-               printf("%02x%02x%02x... lo %u hi %u nr %"PRIu32"\n",
-                      sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
-
-       while (lo < hi) {
-               unsigned mi = lo + (hi - lo) / 2;
-               int cmp = hashcmp(index + mi * stride, sha1);
-
-               if (debug_lookup)
-                       printf("lo %u hi %u rg %u mi %u\n",
-                              lo, hi, hi - lo, mi);
-               if (!cmp)
-                       return nth_packed_object_offset(p, mi);
-               if (cmp > 0)
-                       hi = mi;
-               else
-                       lo = mi+1;
-       }
+       if (bsearch_hash(sha1, level1_ofs, index, stride, &result))
+               return nth_packed_object_offset(p, result);
        return 0;
 }
 
diff --git a/quote.c b/quote.c
index 37d26868656fe8c8c65de41580ab5ae9e89981fc..c95dd2cafbaa85c9c443a229134842bf06ce3200 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -118,9 +118,15 @@ static char *sq_dequote_step(char *arg, char **next)
                                *next = NULL;
                        return arg;
                case '\\':
-                       c = *++src;
-                       if (need_bs_quote(c) && *++src == '\'') {
-                               *dst++ = c;
+                       /*
+                        * Allow backslashed characters outside of
+                        * single-quotes only if they need escaping,
+                        * and only if we resume the single-quoted part
+                        * afterward.
+                        */
+                       if (need_bs_quote(src[1]) && src[2] == '\'') {
+                               *dst++ = src[1];
+                               src += 2;
                                continue;
                        }
                /* Fallthrough */
index 6ec535243565d4b1a50e22868966b4aec33776f8..a7c4c9b5ff4822e36bfc43a59d113c624537297e 100644 (file)
@@ -13,6 +13,7 @@
 #include "credential.h"
 #include "sha1-array.h"
 #include "send-pack.h"
+#include "quote.h"
 
 static struct remote *remote;
 /* always ends with a trailing slash */
@@ -145,7 +146,15 @@ static int set_option(const char *name, const char *value)
                        return -1;
                return 0;
        } else if (!strcmp(name, "push-option")) {
-               string_list_append(&options.push_options, value);
+               if (*value != '"')
+                       string_list_append(&options.push_options, value);
+               else {
+                       struct strbuf unquoted = STRBUF_INIT;
+                       if (unquote_c_style(&unquoted, value, NULL) < 0)
+                               die("invalid quoting in push-option value");
+                       string_list_append_nodup(&options.push_options,
+                                                strbuf_detach(&unquoted, NULL));
+               }
                return 0;
 
 #if LIBCURL_VERSION_NUM >= 0x070a08
@@ -351,6 +360,8 @@ static struct discovery *discover_refs(const char *service, int for_push)
                 * pkt-line matches our request.
                 */
                line = packet_read_line_buf(&last->buf, &last->len, NULL);
+               if (!line)
+                       die("invalid server response; expected service, got flush packet");
 
                strbuf_reset(&exp);
                strbuf_addf(&exp, "# service=%s", service);
index 4e93753e1988afd4a01559951f96142c6dc2e73d..d670eed4c4ef8a09b5665bc5a447539bc49fda8f 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -22,6 +22,7 @@ static struct refspec s_tag_refspec = {
        "refs/tags/*"
 };
 
+/* See TAG_REFSPEC for the string version */
 const struct refspec *tag_refspec = &s_tag_refspec;
 
 struct counted_string {
@@ -103,6 +104,17 @@ static void add_fetch_refspec(struct remote *remote, const char *ref)
        remote->fetch_refspec[remote->fetch_refspec_nr++] = ref;
 }
 
+void add_prune_tags_to_fetch_refspec(struct remote *remote)
+{
+       int nr = remote->fetch_refspec_nr;
+       int bufsize = nr  + 1;
+       int size = sizeof(struct refspec);
+
+       remote->fetch = xrealloc(remote->fetch, size  * bufsize);
+       memcpy(&remote->fetch[nr], tag_refspec, size);
+       add_fetch_refspec(remote, xstrdup(TAG_REFSPEC));
+}
+
 static void add_url(struct remote *remote, const char *url)
 {
        ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
@@ -173,6 +185,7 @@ static struct remote *make_remote(const char *name, int len)
 
        ret = xcalloc(1, sizeof(struct remote));
        ret->prune = -1;  /* unspecified */
+       ret->prune_tags = -1;  /* unspecified */
        ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
        remotes[remotes_nr++] = ret;
        ret->name = xstrndup(name, len);
@@ -391,6 +404,8 @@ static int handle_config(const char *key, const char *value, void *cb)
                remote->skip_default_update = git_config_bool(key, value);
        else if (!strcmp(subkey, "prune"))
                remote->prune = git_config_bool(key, value);
+       else if (!strcmp(subkey, "prunetags"))
+               remote->prune_tags = git_config_bool(key, value);
        else if (!strcmp(subkey, "url")) {
                const char *v;
                if (git_config_string(&v, key, value))
index 1f6611be214363a4be363fad959135a9d123cee0..271afe1bab4d562ee6daf2d47071be765051237f 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -47,6 +47,7 @@ struct remote {
        int skip_default_update;
        int mirror;
        int prune;
+       int prune_tags;
 
        const char *receivepack;
        const char *uploadpack;
@@ -297,4 +298,8 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int
 extern int is_empty_cas(const struct push_cas_option *);
 void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
 
+#define TAG_REFSPEC "refs/tags/*:refs/tags/*"
+
+void add_prune_tags_to_fetch_refspec(struct remote *remote);
+
 #endif
index 5ce9b93baa72685893dd30b62c067bff1f18d636..5c1cb7277c2fef11c473fbb3f6a9c04e0d83ca70 100644 (file)
@@ -1065,14 +1065,9 @@ static int limit_list(struct rev_info *revs)
                        return -1;
                if (obj->flags & UNINTERESTING) {
                        mark_parents_uninteresting(commit);
-                       if (revs->show_all)
-                               p = &commit_list_insert(commit, p)->next;
                        slop = still_interesting(list, date, slop, &interesting_cache);
                        if (slop)
                                continue;
-                       /* If showing all, add the whole pending list to the end */
-                       if (revs->show_all)
-                               *p = list;
                        break;
                }
                if (revs->min_age != -1 && (commit->date > revs->min_age))
@@ -1864,8 +1859,6 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->dense = 1;
        } else if (!strcmp(arg, "--sparse")) {
                revs->dense = 0;
-       } else if (!strcmp(arg, "--show-all")) {
-               revs->show_all = 1;
        } else if (!strcmp(arg, "--in-commit-order")) {
                revs->tree_blobs_in_commit_order = 1;
        } else if (!strcmp(arg, "--remove-empty")) {
@@ -3094,8 +3087,6 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
                return commit_ignore;
        if (revs->unpacked && has_sha1_pack(commit->object.oid.hash))
                return commit_ignore;
-       if (revs->show_all)
-               return commit_show;
        if (commit->object.flags & UNINTERESTING)
                return commit_ignore;
        if (revs->min_age != -1 &&
@@ -3194,7 +3185,6 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
        enum commit_action action = get_commit_action(revs, commit);
 
        if (action == commit_show &&
-           !revs->show_all &&
            revs->prune && revs->dense && want_ancestry(revs)) {
                /*
                 * --full-diff on simplified parents is no good: it
index 3dee97bfb97a4abd636ad7055eb84cf5dea79558..b8c47b98e22562ef320197b4ddbc8f0c3ee40f98 100644 (file)
@@ -90,7 +90,6 @@ struct rev_info {
        unsigned int    dense:1,
                        prune:1,
                        no_walk:2,
-                       show_all:1,
                        remove_empty_trees:1,
                        simplify_history:1,
                        topo_order:1,
index 2112d3b27ad21e1d457b08a3aa71a811b5e8ee79..8d9190f5e7815c6b2f18afd266643a8c862e526e 100644 (file)
@@ -137,6 +137,8 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
 static int receive_unpack_status(int in)
 {
        const char *line = packet_read_line(in, NULL);
+       if (!line)
+               return error(_("unexpected flush packet while reading remote unpack status"));
        if (!skip_prefix(line, "unpack ", &line))
                return error(_("unable to parse remote unpack status: %s"), line);
        if (strcmp(line, "ok"))
index e9baaf59bd954279970367631c930fe051712cda..c24ecdb21fdaac2dd70a127b761e536f1eef196f 100644 (file)
@@ -2314,6 +2314,9 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)
        p = short_commit_name(commit);
        if (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)
                return -1;
+       if (update_ref("rebase", "REBASE_HEAD", &commit->object.oid,
+                      NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+               res |= error(_("could not update %s"), "REBASE_HEAD");
 
        strbuf_addf(&buf, "%s/patch", get_dir(opts));
        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
@@ -2565,6 +2568,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        unlink(rebase_path_author_script());
                        unlink(rebase_path_stopped_sha());
                        unlink(rebase_path_amend());
+                       delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
                }
                if (item->command <= TODO_SQUASH) {
                        if (is_rebase_i(opts))
diff --git a/setup.c b/setup.c
index c5d55dcee45ca811def540ca43c1e1eb57532a81..72877796420b06213665f5d357decb202e71fa91 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -119,7 +119,7 @@ char *prefix_path(const char *prefix, int len, const char *path)
 {
        char *r = prefix_path_gently(prefix, len, NULL, path);
        if (!r)
-               die("'%s' is outside repository", path);
+               die(_("'%s' is outside repository"), path);
        return r;
 }
 
@@ -160,7 +160,7 @@ int check_filename(const char *prefix, const char *arg)
                free(to_free);
                return 0; /* file does not exist */
        }
-       die_errno("failed to stat '%s'", arg);
+       die_errno(_("failed to stat '%s'"), arg);
 }
 
 static void NORETURN die_verify_filename(const char *prefix,
@@ -230,7 +230,7 @@ void verify_filename(const char *prefix,
                     int diagnose_misspelt_rev)
 {
        if (*arg == '-')
-               die("option '%s' must come before non-option arguments", arg);
+               die(_("option '%s' must come before non-option arguments"), arg);
        if (looks_like_pathspec(arg) || check_filename(prefix, arg))
                return;
        die_verify_filename(prefix, arg, diagnose_misspelt_rev);
@@ -385,14 +385,14 @@ void setup_work_tree(void)
                return;
 
        if (work_tree_config_is_bogus)
-               die("unable to set up work tree using invalid config");
+               die(_("unable to set up work tree using invalid config"));
 
        work_tree = get_git_work_tree();
        git_dir = get_git_dir();
        if (!is_absolute_path(git_dir))
                git_dir = real_path(get_git_dir());
        if (!work_tree || chdir(work_tree))
-               die("This operation must be run in a work tree");
+               die(_("this operation must be run in a work tree"));
 
        /*
         * Make sure subsequent git processes find correct worktree
@@ -530,17 +530,17 @@ void read_gitfile_error_die(int error_code, const char *path, const char *dir)
                /* non-fatal; follow return path */
                break;
        case READ_GITFILE_ERR_OPEN_FAILED:
-               die_errno("Error opening '%s'", path);
+               die_errno(_("error opening '%s'"), path);
        case READ_GITFILE_ERR_TOO_LARGE:
-               die("Too large to be a .git file: '%s'", path);
+               die(_("too large to be a .git file: '%s'"), path);
        case READ_GITFILE_ERR_READ_FAILED:
-               die("Error reading %s", path);
+               die(_("error reading %s"), path);
        case READ_GITFILE_ERR_INVALID_FORMAT:
-               die("Invalid gitfile format: %s", path);
+               die(_("invalid gitfile format: %s"), path);
        case READ_GITFILE_ERR_NO_PATH:
-               die("No path in gitfile: %s", path);
+               die(_("no path in gitfile: %s"), path);
        case READ_GITFILE_ERR_NOT_A_REPO:
-               die("Not a git repository: %s", dir);
+               die(_("not a git repository: %s"), dir);
        default:
                die("BUG: unknown error code");
        }
@@ -639,7 +639,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
        int offset;
 
        if (PATH_MAX - 40 < strlen(gitdirenv))
-               die("'$%s' too big", GIT_DIR_ENVIRONMENT);
+               die(_("'$%s' too big"), GIT_DIR_ENVIRONMENT);
 
        gitfile = (char*)read_gitfile(gitdirenv);
        if (gitfile) {
@@ -653,7 +653,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
                        free(gitfile);
                        return NULL;
                }
-               die("Not a git repository: '%s'", gitdirenv);
+               die(_("not a git repository: '%s'"), gitdirenv);
        }
 
        if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
@@ -682,12 +682,12 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
                else {
                        char *core_worktree;
                        if (chdir(gitdirenv))
-                               die_errno("Could not chdir to '%s'", gitdirenv);
+                               die_errno(_("cannot chdir to '%s'"), gitdirenv);
                        if (chdir(git_work_tree_cfg))
-                               die_errno("Could not chdir to '%s'", git_work_tree_cfg);
+                               die_errno(_("cannot chdir to '%s'"), git_work_tree_cfg);
                        core_worktree = xgetcwd();
                        if (chdir(cwd->buf))
-                               die_errno("Could not come back to cwd");
+                               die_errno(_("cannot come back to cwd"));
                        set_git_work_tree(core_worktree);
                        free(core_worktree);
                }
@@ -715,7 +715,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
        if (offset >= 0) {      /* cwd inside worktree? */
                set_git_dir(real_path(gitdirenv));
                if (chdir(worktree))
-                       die_errno("Could not chdir to '%s'", worktree);
+                       die_errno(_("cannot chdir to '%s'"), worktree);
                strbuf_addch(cwd, '/');
                free(gitfile);
                return cwd->buf + offset;
@@ -743,7 +743,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
                if (offset != cwd->len && !is_absolute_path(gitdir))
                        gitdir = to_free = real_pathdup(gitdir, 1);
                if (chdir(cwd->buf))
-                       die_errno("Could not come back to cwd");
+                       die_errno(_("cannot come back to cwd"));
                ret = setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
                free(to_free);
                return ret;
@@ -753,7 +753,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
        if (is_bare_repository_cfg > 0) {
                set_git_dir(offset == cwd->len ? gitdir : real_path(gitdir));
                if (chdir(cwd->buf))
-                       die_errno("Could not come back to cwd");
+                       die_errno(_("cannot come back to cwd"));
                return NULL;
        }
 
@@ -792,7 +792,7 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
 
                gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
                if (chdir(cwd->buf))
-                       die_errno("Could not come back to cwd");
+                       die_errno(_("cannot come back to cwd"));
                return setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
        }
 
@@ -800,7 +800,7 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
        inside_work_tree = 0;
        if (offset != cwd->len) {
                if (chdir(cwd->buf))
-                       die_errno("Cannot come back to cwd");
+                       die_errno(_("cannot come back to cwd"));
                root_len = offset_1st_component(cwd->buf);
                strbuf_setlen(cwd, offset > root_len ? offset : root_len);
                set_git_dir(cwd->buf);
@@ -813,9 +813,9 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
 static const char *setup_nongit(const char *cwd, int *nongit_ok)
 {
        if (!nongit_ok)
-               die(_("Not a git repository (or any of the parent directories): %s"), DEFAULT_GIT_DIR_ENVIRONMENT);
+               die(_("not a git repository (or any of the parent directories): %s"), DEFAULT_GIT_DIR_ENVIRONMENT);
        if (chdir(cwd))
-               die_errno(_("Cannot come back to cwd"));
+               die_errno(_("cannot come back to cwd"));
        *nongit_ok = 1;
        return NULL;
 }
@@ -824,7 +824,7 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_
 {
        struct stat buf;
        if (stat(path, &buf)) {
-               die_errno("failed to stat '%*s%s%s'",
+               die_errno(_("failed to stat '%*s%s%s'"),
                                prefix_len,
                                prefix ? prefix : "",
                                prefix ? "/" : "", path);
@@ -1066,13 +1066,13 @@ const char *setup_git_directory_gently(int *nongit_ok)
                break;
        case GIT_DIR_DISCOVERED:
                if (dir.len < cwd.len && chdir(dir.buf))
-                       die(_("Cannot change to '%s'"), dir.buf);
+                       die(_("cannot change to '%s'"), dir.buf);
                prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
                                                  &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_BARE:
                if (dir.len < cwd.len && chdir(dir.buf))
-                       die(_("Cannot change to '%s'"), dir.buf);
+                       die(_("cannot change to '%s'"), dir.buf);
                prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_HIT_CEILING:
@@ -1085,7 +1085,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
                        strbuf_release(&dir);
                        return NULL;
                }
-               die(_("Not a git repository (or any parent up to mount point %s)\n"
+               die(_("not a git repository (or any parent up to mount point %s)\n"
                      "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
                    dir.buf);
        default:
@@ -1169,7 +1169,7 @@ int git_config_perm(const char *var, const char *value)
        /* A filemode value was given: 0xxx */
 
        if ((i & 0600) != 0600)
-               die(_("Problem with core.sharedRepository filemode value "
+               die(_("problem with core.sharedRepository filemode value "
                    "(0%.3o).\nThe owner of files must always have "
                    "read and write permissions."), i);
 
@@ -1212,7 +1212,7 @@ void sanitize_stdfds(void)
        while (fd != -1 && fd < 2)
                fd = dup(fd);
        if (fd == -1)
-               die_errno("open /dev/null or dup failed");
+               die_errno(_("open /dev/null or dup failed"));
        if (fd > 2)
                close(fd);
 }
@@ -1227,12 +1227,12 @@ int daemonize(void)
                case 0:
                        break;
                case -1:
-                       die_errno("fork failed");
+                       die_errno(_("fork failed"));
                default:
                        exit(0);
        }
        if (setsid() == -1)
-               die_errno("setsid failed");
+               die_errno(_("setsid failed"));
        close(0);
        close(1);
        close(2);
index 4cf3ebd9212f6d5c9b9829373e58c34b83f0a548..8d0b1db3e27c99b80faa2867565de237581802f4 100644 (file)
@@ -99,3 +99,31 @@ int sha1_pos(const unsigned char *sha1, void *table, size_t nr,
        } while (lo < hi);
        return -lo-1;
 }
+
+int bsearch_hash(const unsigned char *sha1, const uint32_t *fanout_nbo,
+                const unsigned char *table, size_t stride, uint32_t *result)
+{
+       uint32_t hi, lo;
+
+       hi = ntohl(fanout_nbo[*sha1]);
+       lo = ((*sha1 == 0x0) ? 0 : ntohl(fanout_nbo[*sha1 - 1]));
+
+       while (lo < hi) {
+               unsigned mi = lo + (hi - lo) / 2;
+               int cmp = hashcmp(table + mi * stride, sha1);
+
+               if (!cmp) {
+                       if (result)
+                               *result = mi;
+                       return 1;
+               }
+               if (cmp > 0)
+                       hi = mi;
+               else
+                       lo = mi + 1;
+       }
+
+       if (result)
+               *result = lo;
+       return 0;
+}
index cf5314f402ce78f0d5ab2bd72ee7f334b6394e04..7678b23b36c291c7b1b6656e0c42fc14ea12da15 100644 (file)
@@ -7,4 +7,26 @@ extern int sha1_pos(const unsigned char *sha1,
                    void *table,
                    size_t nr,
                    sha1_access_fn fn);
+
+/*
+ * Searches for sha1 in table, using the given fanout table to determine the
+ * interval to search, then using binary search. Returns 1 if found, 0 if not.
+ *
+ * Takes the following parameters:
+ *
+ *  - sha1: the hash to search for
+ *  - fanout_nbo: a 256-element array of NETWORK-order 32-bit integers; the
+ *    integer at position i represents the number of elements in table whose
+ *    first byte is less than or equal to i
+ *  - table: a sorted list of hashes with optional extra information in between
+ *  - stride: distance between two consecutive elements in table (should be
+ *    GIT_MAX_RAWSZ or greater)
+ *  - result: if not NULL, this function stores the element index of the
+ *    position found (if the search is successful) or the index of the least
+ *    element that is greater than sha1 (if the search is not successful)
+ *
+ * This function does not verify the validity of the fanout table.
+ */
+int bsearch_hash(const unsigned char *sha1, const uint32_t *fanout_nbo,
+                const unsigned char *table, size_t stride, uint32_t *result);
 #endif
index 2aa8a1747f8586839aa3036fbbc59f6c716c6128..602ba8ca8b8455df9b34e2990397c838d542569f 100644 (file)
@@ -9,7 +9,7 @@
 /*
  * submodule cache lookup structure
  * There is one shared set of 'struct submodule' entries which can be
- * looked up by their sha1 blob id of the .gitmodule file and either
+ * looked up by their sha1 blob id of the .gitmodules file and either
  * using path or name as key.
  * for_path stores submodule entries with path as key
  * for_name stores submodule entries with name as key
@@ -91,7 +91,7 @@ static void submodule_cache_clear(struct submodule_cache *cache)
        /*
         * We iterate over the name hash here to be symmetric with the
         * allocation of struct submodule entries. Each is allocated by
-        * their .gitmodule blob sha1 and submodule name.
+        * their .gitmodules blob sha1 and submodule name.
         */
        hashmap_iter_init(&cache->for_name, &iter);
        while ((entry = hashmap_iter_next(&iter)))
index b3f7b449c366c2d7d7afd12139a54b7a588f9747..1a1361a8063b8e247b6a92b83b6e8876953f95bd 100644 (file)
--- a/t/README
+++ b/t/README
@@ -655,7 +655,7 @@ library for your script to use.
                test_expect_code 1 git merge "merge msg" B master
        '
 
- - test_must_fail <git-command>
+ - test_must_fail [<options>] <git-command>
 
    Run a git command and ensure it fails in a controlled way.  Use
    this instead of "! <git-command>".  When git-command dies due to a
@@ -663,11 +663,21 @@ library for your script to use.
    treats it as just another expected failure, which would let such a
    bug go unnoticed.
 
- - test_might_fail <git-command>
+   Accepts the following options:
+
+     ok=<signal-name>[,<...>]:
+       Don't treat an exit caused by the given signal as error.
+       Multiple signals can be specified as a comma separated list.
+       Currently recognized signal names are: sigpipe, success.
+       (Don't use 'success', use 'test_might_fail' instead.)
+
+ - test_might_fail [<options>] <git-command>
 
    Similar to test_must_fail, but tolerate success, too.  Use this
    instead of "<git-command> || :" to catch failures due to segv.
 
+   Accepts the same options as test_must_fail.
+
  - test_cmp <expected> <actual>
 
    Check whether the content of the <actual> file matches the
index 1145d5167115a761d319b1e205500e765cd090cd..9ae9281c071254019ccca3486b3a6762d9c0085f 100644 (file)
@@ -1,5 +1,6 @@
 #include "git-compat-util.h"
 #include "hashmap.h"
+#include "strbuf.h"
 
 struct test_entry
 {
@@ -29,11 +30,12 @@ static int test_entry_cmp(const void *cmp_data,
                return strcmp(e1->key, key ? key : e2->key);
 }
 
-static struct test_entry *alloc_test_entry(int hash, char *key, int klen,
-               char *value, int vlen)
+static struct test_entry *alloc_test_entry(unsigned int hash,
+                                          char *key, char *value)
 {
-       struct test_entry *entry = malloc(sizeof(struct test_entry) + klen
-                       + vlen + 2);
+       size_t klen = strlen(key);
+       size_t vlen = strlen(value);
+       struct test_entry *entry = xmalloc(st_add4(sizeof(*entry), klen, vlen, 2));
        hashmap_entry_init(entry, hash);
        memcpy(entry->key, key, klen + 1);
        memcpy(entry->key + klen + 1, value, vlen + 1);
@@ -85,11 +87,11 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
        unsigned int *hashes;
        unsigned int i, j;
 
-       entries = malloc(TEST_SIZE * sizeof(struct test_entry *));
-       hashes = malloc(TEST_SIZE * sizeof(int));
+       ALLOC_ARRAY(entries, TEST_SIZE);
+       ALLOC_ARRAY(hashes, TEST_SIZE);
        for (i = 0; i < TEST_SIZE; i++) {
-               snprintf(buf, sizeof(buf), "%i", i);
-               entries[i] = alloc_test_entry(0, buf, strlen(buf), "", 0);
+               xsnprintf(buf, sizeof(buf), "%i", i);
+               entries[i] = alloc_test_entry(0, buf, "");
                hashes[i] = hash(method, i, entries[i]->key);
        }
 
@@ -144,7 +146,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
  */
 int cmd_main(int argc, const char **argv)
 {
-       char line[1024];
+       struct strbuf line = STRBUF_INIT;
        struct hashmap map;
        int icase;
 
@@ -153,44 +155,42 @@ int cmd_main(int argc, const char **argv)
        hashmap_init(&map, test_entry_cmp, &icase, 0);
 
        /* process commands from stdin */
-       while (fgets(line, sizeof(line), stdin)) {
+       while (strbuf_getline(&line, stdin) != EOF) {
                char *cmd, *p1 = NULL, *p2 = NULL;
-               int l1 = 0, l2 = 0, hash = 0;
+               unsigned int hash = 0;
                struct test_entry *entry;
 
                /* break line into command and up to two parameters */
-               cmd = strtok(line, DELIM);
+               cmd = strtok(line.buf, DELIM);
                /* ignore empty lines */
                if (!cmd || *cmd == '#')
                        continue;
 
                p1 = strtok(NULL, DELIM);
                if (p1) {
-                       l1 = strlen(p1);
                        hash = icase ? strihash(p1) : strhash(p1);
                        p2 = strtok(NULL, DELIM);
-                       if (p2)
-                               l2 = strlen(p2);
                }
 
-               if (!strcmp("hash", cmd) && l1) {
+               if (!strcmp("hash", cmd) && p1) {
 
                        /* print results of different hash functions */
-                       printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1),
-                                       strihash(p1), memihash(p1, l1));
+                       printf("%u %u %u %u\n",
+                              strhash(p1), memhash(p1, strlen(p1)),
+                              strihash(p1), memihash(p1, strlen(p1)));
 
-               } else if (!strcmp("add", cmd) && l1 && l2) {
+               } else if (!strcmp("add", cmd) && p1 && p2) {
 
                        /* create entry with key = p1, value = p2 */
-                       entry = alloc_test_entry(hash, p1, l1, p2, l2);
+                       entry = alloc_test_entry(hash, p1, p2);
 
                        /* add to hashmap */
                        hashmap_add(&map, entry);
 
-               } else if (!strcmp("put", cmd) && l1 && l2) {
+               } else if (!strcmp("put", cmd) && p1 && p2) {
 
                        /* create entry with key = p1, value = p2 */
-                       entry = alloc_test_entry(hash, p1, l1, p2, l2);
+                       entry = alloc_test_entry(hash, p1, p2);
 
                        /* add / replace entry */
                        entry = hashmap_put(&map, entry);
@@ -199,7 +199,7 @@ int cmd_main(int argc, const char **argv)
                        puts(entry ? get_value(entry) : "NULL");
                        free(entry);
 
-               } else if (!strcmp("get", cmd) && l1) {
+               } else if (!strcmp("get", cmd) && p1) {
 
                        /* lookup entry in hashmap */
                        entry = hashmap_get_from_hash(&map, hash, p1);
@@ -212,7 +212,7 @@ int cmd_main(int argc, const char **argv)
                                entry = hashmap_get_next(&map, entry);
                        }
 
-               } else if (!strcmp("remove", cmd) && l1) {
+               } else if (!strcmp("remove", cmd) && p1) {
 
                        /* setup static key */
                        struct hashmap_entry key;
@@ -238,7 +238,7 @@ int cmd_main(int argc, const char **argv)
                        printf("%u %u\n", map.tablesize,
                               hashmap_get_size(&map));
 
-               } else if (!strcmp("intern", cmd) && l1) {
+               } else if (!strcmp("intern", cmd) && p1) {
 
                        /* test that strintern works */
                        const char *i1 = strintern(p1);
@@ -252,7 +252,7 @@ int cmd_main(int argc, const char **argv)
                        else
                                printf("%s\n", i1);
 
-               } else if (!strcmp("perfhashmap", cmd) && l1 && l2) {
+               } else if (!strcmp("perfhashmap", cmd) && p1 && p2) {
 
                        perf_hashmap(atoi(p1), atoi(p2));
 
@@ -263,6 +263,7 @@ int cmd_main(int argc, const char **argv)
                }
        }
 
+       strbuf_release(&line);
        hashmap_free(&map, 1);
        return 0;
 }
index 9670e8cbe6cb9a9faa3519b0f11dc16713496188..3691023d510a0d97bf1390b781afe1ac9fa270f4 100755 (executable)
@@ -10,15 +10,6 @@ objpath() {
        echo "$1" | sed -e 's|\(..\)|\1/|'
 }
 
-objck() {
-       p=$(objpath "$1")
-       if test ! -f "$REAL/objects/$p"
-       then
-               echo "Object not found: $REAL/objects/$p"
-               false
-       fi
-}
-
 test_expect_success 'initial setup' '
        REAL="$(pwd)/.real" &&
        mv .git "$REAL"
@@ -26,30 +17,14 @@ test_expect_success 'initial setup' '
 
 test_expect_success 'bad setup: invalid .git file format' '
        echo "gitdir $REAL" >.git &&
-       if git rev-parse 2>.err
-       then
-               echo "git rev-parse accepted an invalid .git file"
-               false
-       fi &&
-       if ! grep "Invalid gitfile format" .err
-       then
-               echo "git rev-parse returned wrong error"
-               false
-       fi
+       test_must_fail git rev-parse 2>.err &&
+       test_i18ngrep "invalid gitfile format" .err
 '
 
 test_expect_success 'bad setup: invalid .git file path' '
        echo "gitdir: $REAL.not" >.git &&
-       if git rev-parse 2>.err
-       then
-               echo "git rev-parse accepted an invalid .git file path"
-               false
-       fi &&
-       if ! grep "Not a git repository" .err
-       then
-               echo "git rev-parse returned wrong error"
-               false
-       fi
+       test_must_fail git rev-parse 2>.err &&
+       test_i18ngrep "not a git repository" .err
 '
 
 test_expect_success 'final setup + check rev-parse --git-dir' '
@@ -60,7 +35,7 @@ test_expect_success 'final setup + check rev-parse --git-dir' '
 test_expect_success 'check hash-object' '
        echo "foo" >bar &&
        SHA=$(cat bar | git hash-object -w --stdin) &&
-       objck $SHA
+       test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
 test_expect_success 'check cat-file' '
@@ -69,29 +44,21 @@ test_expect_success 'check cat-file' '
 '
 
 test_expect_success 'check update-index' '
-       if test -f "$REAL/index"
-       then
-               echo "Hmm, $REAL/index exists?"
-               false
-       fi &&
+       test_path_is_missing "$REAL/index" &&
        rm -f "$REAL/objects/$(objpath $SHA)" &&
        git update-index --add bar &&
-       if ! test -f "$REAL/index"
-       then
-               echo "$REAL/index not found"
-               false
-       fi &&
-       objck $SHA
+       test_path_is_file "$REAL/index" &&
+       test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
 test_expect_success 'check write-tree' '
        SHA=$(git write-tree) &&
-       objck $SHA
+       test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
 test_expect_success 'check commit-tree' '
        SHA=$(echo "commit bar" | git commit-tree $SHA) &&
-       objck $SHA
+       test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
 test_expect_success 'check rev-list' '
index d27f438bf410d06f37ec76a6ce040316218a8138..c03f155a357446338fa831e061dc58d8e5f4c657 100755 (executable)
@@ -307,7 +307,7 @@ test_expect_success_multi 'needs work tree' '' '
                cd .git &&
                test_check_ignore "foo" 128
        ) &&
-       stderr_contains "fatal: This operation must be run in a work tree"
+       stderr_contains "fatal: this operation must be run in a work tree"
 '
 
 ############################################################################
@@ -775,6 +775,26 @@ test_expect_success PIPE 'streaming support for --stdin' '
        echo "$response" | grep "^::    two"
 '
 
+test_expect_success 'existing file and directory' '
+       test_when_finished "rm one" &&
+       test_when_finished "rmdir top-level-dir" &&
+       >one &&
+       mkdir top-level-dir &&
+       git check-ignore one top-level-dir >actual &&
+       grep one actual &&
+       grep top-level-dir actual
+'
+
+test_expect_success 'existing directory and file' '
+       test_when_finished "rm one" &&
+       test_when_finished "rmdir top-level-dir" &&
+       >one &&
+       mkdir top-level-dir &&
+       git check-ignore top-level-dir one >actual &&
+       grep one actual &&
+       grep top-level-dir actual
+'
+
 ############################################################################
 #
 # test whitespace handling
index b29d749bb7b33406b2d433d96c35d252e305eed0..192c94eccd13c3b251cfc6910ad6ef175312ea13 100755 (executable)
@@ -80,7 +80,21 @@ test_expect_success 'merge (case change)' '
        git merge topic
 '
 
-
+test_expect_success CASE_INSENSITIVE_FS 'add directory (with different case)' '
+       git reset --hard initial &&
+       mkdir -p dir1/dir2 &&
+       echo >dir1/dir2/a &&
+       echo >dir1/dir2/b &&
+       git add dir1/dir2/a &&
+       git add dir1/DIR2/b &&
+       git ls-files >actual &&
+       cat >expected <<-\EOF &&
+               camelcase
+               dir1/dir2/a
+               dir1/dir2/b
+       EOF
+       test_cmp expected actual
+'
 
 test_expect_failure CASE_INSENSITIVE_FS 'add (with different case)' '
        git reset --hard initial &&
index 2361590d54856a1a284945e46fece2716487f624..438e778d6afa086e8823b2ac3d38c2a26f171dbd 100755 (executable)
@@ -7,10 +7,6 @@ test_description='Gettext Shell poison'
 
 . ./lib-gettext.sh
 
-test_expect_success GETTEXT_POISON "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
-    test -n "$GIT_INTERNAL_GETTEXT_SH_SCHEME"
-'
-
 test_expect_success GETTEXT_POISON 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is poison' '
     test "$GIT_INTERNAL_GETTEXT_SH_SCHEME" = "poison"
 '
index cbeb9bebeea67c3654c279b2c12fed4825840d8f..4f8e6f5fde3295cc91d2feb1fd85fa007e56016c 100755 (executable)
@@ -1206,6 +1206,29 @@ test_expect_success 'git -c is not confused by empty environment' '
        GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
 '
 
+sq="'"
+test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
+       cat >expect <<-\EOF &&
+       env.one one
+       env.two two
+       EOF
+       GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq} ${sq}env.two=two${sq}" \
+               git config --get-regexp "env.*" >actual &&
+       test_cmp expect actual &&
+
+       cat >expect <<-EOF &&
+       env.one one${sq}
+       env.two two
+       EOF
+       GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq$sq$sq ${sq}env.two=two${sq}" \
+               git config --get-regexp "env.*" >actual &&
+       test_cmp expect actual &&
+
+       test_must_fail env \
+               GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq ${sq}env.two=two${sq}" \
+               git config --get-regexp "env.*"
+'
+
 test_expect_success 'git config --edit works' '
        git config -f tmp test.value no &&
        echo test.value=yes >expect &&
index 79a0251efa6e049be4fffa41004e73b9950d2bd7..4ee009da666f22f6ff1ddce93dc1f30924c6cc26 100755 (executable)
@@ -157,7 +157,7 @@ test_expect_success 'relative path not found' '
 test_expect_success 'relative path outside worktree' '
        test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
        test -z "$(cat output)" &&
-       grep "outside repository" error
+       test_i18ngrep "outside repository" error
 '
 
 test_expect_success 'relative path when cwd is outside worktree' '
index 2b959449730e14dd4e650ce9115a463d71eedca4..d0d2e4f7ec3310ec51da7144fa87151129f393c0 100755 (executable)
@@ -451,32 +451,68 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '
 '
 
 post_checkout_hook () {
-       test_when_finished "rm -f .git/hooks/post-checkout" &&
-       mkdir -p .git/hooks &&
-       write_script .git/hooks/post-checkout <<-\EOF
-       echo $* >hook.actual
+       gitdir=${1:-.git}
+       test_when_finished "rm -f $gitdir/hooks/post-checkout" &&
+       mkdir -p $gitdir/hooks &&
+       write_script $gitdir/hooks/post-checkout <<-\EOF
+       {
+               echo $*
+               git rev-parse --git-dir --show-toplevel
+       } >hook.actual
        EOF
 }
 
 test_expect_success '"add" invokes post-checkout hook (branch)' '
        post_checkout_hook &&
-       printf "%s %s 1\n" $_z40 $(git rev-parse HEAD) >hook.expect &&
+       {
+               echo $_z40 $(git rev-parse HEAD) 1 &&
+               echo $(pwd)/.git/worktrees/gumby &&
+               echo $(pwd)/gumby
+       } >hook.expect &&
        git worktree add gumby &&
-       test_cmp hook.expect hook.actual
+       test_cmp hook.expect gumby/hook.actual
 '
 
 test_expect_success '"add" invokes post-checkout hook (detached)' '
        post_checkout_hook &&
-       printf "%s %s 1\n" $_z40 $(git rev-parse HEAD) >hook.expect &&
+       {
+               echo $_z40 $(git rev-parse HEAD) 1 &&
+               echo $(pwd)/.git/worktrees/grumpy &&
+               echo $(pwd)/grumpy
+       } >hook.expect &&
        git worktree add --detach grumpy &&
-       test_cmp hook.expect hook.actual
+       test_cmp hook.expect grumpy/hook.actual
 '
 
 test_expect_success '"add --no-checkout" suppresses post-checkout hook' '
        post_checkout_hook &&
        rm -f hook.actual &&
        git worktree add --no-checkout gloopy &&
-       test_path_is_missing hook.actual
+       test_path_is_missing gloopy/hook.actual
+'
+
+test_expect_success '"add" in other worktree invokes post-checkout hook' '
+       post_checkout_hook &&
+       {
+               echo $_z40 $(git rev-parse HEAD) 1 &&
+               echo $(pwd)/.git/worktrees/guppy &&
+               echo $(pwd)/guppy
+       } >hook.expect &&
+       git -C gloopy worktree add --detach ../guppy &&
+       test_cmp hook.expect guppy/hook.actual
+'
+
+test_expect_success '"add" in bare repo invokes post-checkout hook' '
+       rm -rf bare &&
+       git clone --bare . bare &&
+       {
+               echo $_z40 $(git --git-dir=bare rev-parse HEAD) 1 &&
+               echo $(pwd)/bare/worktrees/goozy &&
+               echo $(pwd)/goozy
+       } >hook.expect &&
+       post_checkout_hook bare &&
+       git -C bare worktree add --detach ../goozy &&
+       test_cmp hook.expect goozy/hook.actual
 '
 
 test_done
index 8ac58d5ea5e4b8b75deaa74f3d6bca29f37dbcb6..72d9564747adf2d37ea2e61a6d2e479096fe6508 100755 (executable)
@@ -277,4 +277,38 @@ EOF
        test_cmp From_.msg out
 '
 
+test_expect_success 'rebase--am.sh and --show-current-patch' '
+       test_create_repo conflict-apply &&
+       (
+               cd conflict-apply &&
+               test_commit init &&
+               echo one >>init.t &&
+               git commit -a -m one &&
+               echo two >>init.t &&
+               git commit -a -m two &&
+               git tag two &&
+               test_must_fail git rebase --onto init HEAD^ &&
+               GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
+               grep "show.*$(git rev-parse two)" stderr
+       )
+'
+
+test_expect_success 'rebase--merge.sh and --show-current-patch' '
+       test_create_repo conflict-merge &&
+       (
+               cd conflict-merge &&
+               test_commit init &&
+               echo one >>init.t &&
+               git commit -a -m one &&
+               echo two >>init.t &&
+               git commit -a -m two &&
+               git tag two &&
+               test_must_fail git rebase --merge --onto init HEAD^ &&
+               git rebase --show-current-patch >actual.patch &&
+               GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
+               grep "show.*REBASE_HEAD" stderr &&
+               test "$(git rev-parse REBASE_HEAD)" = "$(git rev-parse two)"
+       )
+'
+
 test_done
index ef2887bd852b15a0d1c1e6fb1ea9eff09664f27e..3b905406df79187f70c828f72e9e2dc187f1be57 100755 (executable)
@@ -225,6 +225,14 @@ test_expect_success 'stop on conflicting pick' '
        test 0 = $(grep -c "^[^#]" < .git/rebase-merge/git-rebase-todo)
 '
 
+test_expect_success 'show conflicted patch' '
+       GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
+       grep "show.*REBASE_HEAD" stderr &&
+       # the original stopped-sha1 is abbreviated
+       stopped_sha1="$(git rev-parse $(cat ".git/rebase-merge/stopped-sha"))" &&
+       test "$(git rev-parse REBASE_HEAD)" = "$stopped_sha1"
+'
+
 test_expect_success 'abort' '
        git rebase --abort &&
        test $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&
index 6b84e6042a6fcc9cf850a53ad2a885597fb178fc..e7292f5b9b938018f515f858f2dadc29162261ad 100755 (executable)
@@ -24,8 +24,23 @@ But otherwise with a sane description." &&
        >elif &&
        git add elif &&
        test_tick &&
-       git commit -m second
+       git commit -m second &&
 
+       git checkout -b side2 &&
+       >afile &&
+       git add afile &&
+       test_tick &&
+       git commit -m third &&
+       echo hello >afile &&
+       test_tick &&
+       git commit -a -m fourth &&
+       git checkout -b side-merge &&
+       git reset --hard HEAD^^ &&
+       git merge --no-ff -m "A merge commit log message that has a long
+summary that spills over multiple lines.
+
+But otherwise with a sane description." side2 &&
+       git branch side-merge-original
 '
 
 test_expect_success rebase '
@@ -36,6 +51,15 @@ test_expect_success rebase '
        git cat-file commit side@{1} | sed -e "1,/^\$/d" >expect &&
        test_cmp expect actual
 
+'
+test_expect_success rebasep '
+
+       git checkout side-merge &&
+       git rebase -p side &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+       git cat-file commit side-merge-original | sed -e "1,/^\$/d" >expect &&
+       test_cmp expect actual
+
 '
 
 test_done
index 27cb0009fb1ed52b749785c31081b3097fef0022..c7c688fcc4bbdfe97a5c595fddc96e2590e021a2 100755 (executable)
@@ -89,4 +89,21 @@ test_expect_success 'traditional, whitespace-damaged, colon in timezone' '
        test_cmp expected "post image.txt"
 '
 
+cat >diff-from-svn <<\EOF
+Index: Makefile
+===================================================================
+diff --git a/branches/Makefile
+deleted file mode 100644
+--- a/branches/Makefile        (revision 13)
++++ /dev/null  (nonexistent)
+@@ +1 0,0 @@
+-
+EOF
+
+test_expect_success 'apply handles a diff generated by Subversion' '
+       >Makefile &&
+       git apply -p2 diff-from-svn &&
+       test_path_is_missing Makefile
+'
+
 test_done
index 73b67b4280b99e0328e201e6b69c3d88b766ea84..1eccfb71d0c8e26f88708459f065dea622baf9d3 100755 (executable)
@@ -662,6 +662,11 @@ test_expect_success 'am pauses on conflict' '
        test -d .git/rebase-apply
 '
 
+test_expect_success 'am --show-current-patch' '
+       git am --show-current-patch >actual.patch &&
+       test_cmp .git/rebase-apply/0001 actual.patch
+'
+
 test_expect_success 'am --skip works' '
        echo goodbye >expected &&
        git am --skip &&
@@ -1045,4 +1050,16 @@ test_expect_success 'am works with multi-line in-body headers' '
        git cat-file commit HEAD | grep "^$LONG$"
 '
 
+test_expect_success 'am --quit keeps HEAD where it is' '
+       mkdir .git/rebase-apply &&
+       >.git/rebase-apply/last &&
+       >.git/rebase-apply/next &&
+       git rev-parse HEAD^ >.git/ORIG_HEAD &&
+       git rev-parse HEAD >expected &&
+       git am --quit &&
+       test_path_is_missing .git/rebase-apply &&
+       git rev-parse HEAD >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 3debc87d4aefb2d0d5c77881a9485c06a2908858..da9ac0055721237f177d3d475e56ddb38b25eff1 100755 (executable)
@@ -540,82 +540,232 @@ test_expect_success "should be able to fetch with duplicate refspecs" '
 set_config_tristate () {
        # var=$1 val=$2
        case "$2" in
-       unset)  test_unconfig "$1" ;;
-       *)      git config "$1" "$2" ;;
+       unset)
+               test_unconfig "$1"
+               ;;
+       *)
+               git config "$1" "$2"
+               key=$(echo $1 | sed -e 's/^remote\.origin/fetch/')
+               git_fetch_c="$git_fetch_c -c $key=$2"
+               ;;
        esac
 }
 
 test_configured_prune () {
-       fetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4
+       test_configured_prune_type "$@" "name"
+       test_configured_prune_type "$@" "link"
+}
 
-       test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4" '
+test_configured_prune_type () {
+       fetch_prune=$1
+       remote_origin_prune=$2
+       fetch_prune_tags=$3
+       remote_origin_prune_tags=$4
+       expected_branch=$5
+       expected_tag=$6
+       cmdline=$7
+       mode=$8
+
+       if test -z "$cmdline_setup"
+       then
+               test_expect_success 'setup cmdline_setup variable for subsequent test' '
+                       remote_url="file://$(git -C one config remote.origin.url)" &&
+                       remote_fetch="$(git -C one config remote.origin.fetch)" &&
+                       cmdline_setup="\"$remote_url\" \"$remote_fetch\""
+               '
+       fi
+
+       if test "$mode" = 'link'
+       then
+               new_cmdline=""
+
+               if test "$cmdline" = ""
+               then
+                       new_cmdline=$cmdline_setup
+               else
+                       new_cmdline=$(printf "%s" "$cmdline" | perl -pe 's[origin(?!/)]["'"$remote_url"'"]g')
+               fi
+
+               if test "$fetch_prune_tags" = 'true' ||
+                  test "$remote_origin_prune_tags" = 'true'
+               then
+                       if ! printf '%s' "$cmdline\n" | grep -q refs/remotes/origin/
+                       then
+                               new_cmdline="$new_cmdline refs/tags/*:refs/tags/*"
+                       fi
+               fi
+
+               cmdline="$new_cmdline"
+       fi
+
+       test_expect_success "$mode prune fetch.prune=$1 remote.origin.prune=$2 fetch.pruneTags=$3 remote.origin.pruneTags=$4${7:+ $7}; branch:$5 tag:$6" '
                # make sure a newbranch is there in . and also in one
                git branch -f newbranch &&
+               git tag -f newtag &&
                (
                        cd one &&
                        test_unconfig fetch.prune &&
+                       test_unconfig fetch.pruneTags &&
                        test_unconfig remote.origin.prune &&
-                       git fetch &&
-                       git rev-parse --verify refs/remotes/origin/newbranch
+                       test_unconfig remote.origin.pruneTags &&
+                       git fetch '"$cmdline_setup"' &&
+                       git rev-parse --verify refs/remotes/origin/newbranch &&
+                       git rev-parse --verify refs/tags/newtag
                ) &&
 
                # now remove it
                git branch -d newbranch &&
+               git tag -d newtag &&
 
                # then test
                (
                        cd one &&
+                       git_fetch_c="" &&
                        set_config_tristate fetch.prune $fetch_prune &&
+                       set_config_tristate fetch.pruneTags $fetch_prune_tags &&
                        set_config_tristate remote.origin.prune $remote_origin_prune &&
-
-                       git fetch $cmdline &&
-                       case "$expected" in
+                       set_config_tristate remote.origin.pruneTags $remote_origin_prune_tags &&
+
+                       if test "$mode" != "link"
+                       then
+                               git_fetch_c=""
+                       fi &&
+                       git$git_fetch_c fetch '"$cmdline"' &&
+                       case "$expected_branch" in
                        pruned)
                                test_must_fail git rev-parse --verify refs/remotes/origin/newbranch
                                ;;
                        kept)
                                git rev-parse --verify refs/remotes/origin/newbranch
                                ;;
+                       esac &&
+                       case "$expected_tag" in
+                       pruned)
+                               test_must_fail git rev-parse --verify refs/tags/newtag
+                               ;;
+                       kept)
+                               git rev-parse --verify refs/tags/newtag
+                               ;;
                        esac
                )
        '
 }
 
-test_configured_prune unset unset ""           kept
-test_configured_prune unset unset "--no-prune" kept
-test_configured_prune unset unset "--prune"    pruned
-
-test_configured_prune false unset ""           kept
-test_configured_prune false unset "--no-prune" kept
-test_configured_prune false unset "--prune"    pruned
-
-test_configured_prune true  unset ""           pruned
-test_configured_prune true  unset "--prune"    pruned
-test_configured_prune true  unset "--no-prune" kept
-
-test_configured_prune unset false ""           kept
-test_configured_prune unset false "--no-prune" kept
-test_configured_prune unset false "--prune"    pruned
-
-test_configured_prune false false ""           kept
-test_configured_prune false false "--no-prune" kept
-test_configured_prune false false "--prune"    pruned
-
-test_configured_prune true  false ""           kept
-test_configured_prune true  false "--prune"    pruned
-test_configured_prune true  false "--no-prune" kept
-
-test_configured_prune unset true  ""           pruned
-test_configured_prune unset true  "--no-prune" kept
-test_configured_prune unset true  "--prune"    pruned
-
-test_configured_prune false true  ""           pruned
-test_configured_prune false true  "--no-prune" kept
-test_configured_prune false true  "--prune"    pruned
-
-test_configured_prune true  true  ""           pruned
-test_configured_prune true  true  "--prune"    pruned
-test_configured_prune true  true  "--no-prune" kept
+# $1 config: fetch.prune
+# $2 config: remote.<name>.prune
+# $3 config: fetch.pruneTags
+# $4 config: remote.<name>.pruneTags
+# $5 expect: branch to be pruned?
+# $6 expect: tag to be pruned?
+# $7 git-fetch $cmdline:
+#
+#                     $1    $2    $3    $4    $5     $6     $7
+test_configured_prune unset unset unset unset kept   kept   ""
+test_configured_prune unset unset unset unset kept   kept   "--no-prune"
+test_configured_prune unset unset unset unset pruned kept   "--prune"
+test_configured_prune unset unset unset unset kept   pruned \
+       "--prune origin refs/tags/*:refs/tags/*"
+test_configured_prune unset unset unset unset pruned pruned \
+       "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*"
+
+test_configured_prune false unset unset unset kept   kept   ""
+test_configured_prune false unset unset unset kept   kept   "--no-prune"
+test_configured_prune false unset unset unset pruned kept   "--prune"
+
+test_configured_prune true  unset unset unset pruned kept   ""
+test_configured_prune true  unset unset unset pruned kept   "--prune"
+test_configured_prune true  unset unset unset kept   kept   "--no-prune"
+
+test_configured_prune unset false unset unset kept   kept   ""
+test_configured_prune unset false unset unset kept   kept   "--no-prune"
+test_configured_prune unset false unset unset pruned kept   "--prune"
+
+test_configured_prune false false unset unset kept   kept   ""
+test_configured_prune false false unset unset kept   kept   "--no-prune"
+test_configured_prune false false unset unset pruned kept   "--prune"
+test_configured_prune false false unset unset kept   pruned \
+       "--prune origin refs/tags/*:refs/tags/*"
+test_configured_prune false false unset unset pruned pruned \
+       "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*"
+
+test_configured_prune true  false unset unset kept   kept   ""
+test_configured_prune true  false unset unset pruned kept   "--prune"
+test_configured_prune true  false unset unset kept   kept   "--no-prune"
+
+test_configured_prune unset true  unset unset pruned kept   ""
+test_configured_prune unset true  unset unset kept   kept   "--no-prune"
+test_configured_prune unset true  unset unset pruned kept   "--prune"
+
+test_configured_prune false true  unset unset pruned kept   ""
+test_configured_prune false true  unset unset kept   kept   "--no-prune"
+test_configured_prune false true  unset unset pruned kept   "--prune"
+
+test_configured_prune true  true  unset unset pruned kept   ""
+test_configured_prune true  true  unset unset pruned kept   "--prune"
+test_configured_prune true  true  unset unset kept   kept   "--no-prune"
+test_configured_prune true  true  unset unset kept   pruned \
+       "--prune origin refs/tags/*:refs/tags/*"
+test_configured_prune true  true  unset unset pruned pruned \
+       "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*"
+
+# --prune-tags on its own does nothing, needs --prune as well, same
+# for for fetch.pruneTags without fetch.prune
+test_configured_prune unset unset unset unset kept kept     "--prune-tags"
+test_configured_prune unset unset true unset  kept kept     ""
+test_configured_prune unset unset unset true  kept kept     ""
+
+# These will prune the tags
+test_configured_prune unset unset unset unset pruned pruned "--prune --prune-tags"
+test_configured_prune true  unset true  unset pruned pruned ""
+test_configured_prune unset true  unset true  pruned pruned ""
+
+# remote.<name>.pruneTags overrides fetch.pruneTags, just like
+# remote.<name>.prune overrides fetch.prune if set.
+test_configured_prune true  unset true unset pruned pruned  ""
+test_configured_prune false true  false true  pruned pruned ""
+test_configured_prune true  false true  false kept   kept   ""
+
+# When --prune-tags is supplied it's ignored if an explicit refspec is
+# given, same for the configuration options.
+test_configured_prune unset unset unset unset pruned kept \
+       "--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*"
+test_configured_prune unset unset true  unset pruned kept \
+       "--prune origin +refs/heads/*:refs/remotes/origin/*"
+test_configured_prune unset unset unset true pruned  kept \
+       "--prune origin +refs/heads/*:refs/remotes/origin/*"
+
+# Pruning that also takes place if a file:// url replaces a named
+# remote. However, because there's no implicit
+# +refs/heads/*:refs/remotes/origin/* refspec and supplying it on the
+# command-line negates --prune-tags, the branches will not be pruned.
+test_configured_prune_type unset unset unset unset kept   kept   "origin --prune-tags" "name"
+test_configured_prune_type unset unset unset unset kept   kept   "origin --prune-tags" "link"
+test_configured_prune_type unset unset unset unset pruned pruned "origin --prune --prune-tags" "name"
+test_configured_prune_type unset unset unset unset kept   pruned "origin --prune --prune-tags" "link"
+test_configured_prune_type unset unset unset unset pruned pruned "--prune --prune-tags origin" "name"
+test_configured_prune_type unset unset unset unset kept   pruned "--prune --prune-tags origin" "link"
+test_configured_prune_type unset unset true  unset pruned pruned "--prune origin" "name"
+test_configured_prune_type unset unset true  unset kept   pruned "--prune origin" "link"
+test_configured_prune_type unset unset unset true  pruned pruned "--prune origin" "name"
+test_configured_prune_type unset unset unset true  kept   pruned "--prune origin" "link"
+test_configured_prune_type true  unset true  unset pruned pruned "origin" "name"
+test_configured_prune_type true  unset true  unset kept   pruned "origin" "link"
+test_configured_prune_type unset  true true  unset pruned pruned "origin" "name"
+test_configured_prune_type unset  true true  unset kept   pruned "origin" "link"
+test_configured_prune_type unset  true unset true  pruned pruned "origin" "name"
+test_configured_prune_type unset  true unset true  kept   pruned "origin" "link"
+
+# When all remote.origin.fetch settings are deleted a --prune
+# --prune-tags still implicitly supplies refs/tags/*:refs/tags/* so
+# tags, but not tracking branches, will be deleted.
+test_expect_success 'remove remote.origin.fetch "one"' '
+       (
+               cd one &&
+               git config --unset-all remote.origin.fetch
+       )
+'
+test_configured_prune_type unset unset unset unset kept pruned "origin --prune --prune-tags" "name"
+test_configured_prune_type unset unset unset unset kept pruned "origin --prune --prune-tags" "link"
 
 test_expect_success 'all boundary commits are excluded' '
        test_commit base &&
index a552ad4ead899fbb431c68bef558811d25cd8c63..74486c73b0bcc20ee69e49d5c1cc072994e4d7b0 100755 (executable)
@@ -485,7 +485,7 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea
        )
 '
 
-test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .gitmodule entry" '
+test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .gitmodules entry" '
        (
                cd downstream &&
                git fetch --recurse-submodules
index 463783789c8ccda6a483197f6626afbfca7d8ccf..b47a95871cac3dd8593b3b9262d238be3914ac62 100755 (executable)
@@ -217,17 +217,32 @@ test_expect_success 'invalid push option in config' '
        test_refs master HEAD@{1}
 '
 
+test_expect_success 'push options keep quoted characters intact (direct)' '
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions true &&
+       test_commit -C workbench one &&
+       git -C workbench push --push-option="\"embedded quotes\"" up master &&
+       echo "\"embedded quotes\"" >expect &&
+       test_cmp expect upstream/.git/hooks/pre-receive.push_options
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
-test_expect_success 'push option denied properly by http server' '
+# set up http repository for fetching/pushing, with push options config
+# bool set to $1
+mk_http_pair () {
        test_when_finished "rm -rf test_http_clone" &&
-       test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" &&
+       test_when_finished 'rm -rf "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git' &&
        mk_repo_pair &&
-       git -C upstream config receive.advertisePushOptions false &&
+       git -C upstream config receive.advertisePushOptions "$1" &&
        git -C upstream config http.receivepack true &&
        cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git &&
-       git clone "$HTTPD_URL"/smart/upstream test_http_clone &&
+       git clone "$HTTPD_URL"/smart/upstream test_http_clone
+}
+
+test_expect_success 'push option denied properly by http server' '
+       mk_http_pair false &&
        test_commit -C test_http_clone one &&
        test_must_fail git -C test_http_clone push --push-option=asdf origin master 2>actual &&
        test_i18ngrep "the receiving end does not support push options" actual &&
@@ -235,13 +250,7 @@ test_expect_success 'push option denied properly by http server' '
 '
 
 test_expect_success 'push options work properly across http' '
-       test_when_finished "rm -rf test_http_clone" &&
-       test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" &&
-       mk_repo_pair &&
-       git -C upstream config receive.advertisePushOptions true &&
-       git -C upstream config http.receivepack true &&
-       cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git &&
-       git clone "$HTTPD_URL"/smart/upstream test_http_clone &&
+       mk_http_pair true &&
 
        test_commit -C test_http_clone one &&
        git -C test_http_clone push origin master &&
@@ -260,6 +269,15 @@ test_expect_success 'push options work properly across http' '
        test_cmp expect actual
 '
 
+test_expect_success 'push options keep quoted characters intact (http)' '
+       mk_http_pair true &&
+
+       test_commit -C test_http_clone one &&
+       git -C test_http_clone push --push-option="\"embedded quotes\"" origin master &&
+       echo "\"embedded quotes\"" >expect &&
+       test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options
+'
+
 stop_httpd
 
 test_done
diff --git a/t/t6015-rev-list-show-all-parents.sh b/t/t6015-rev-list-show-all-parents.sh
deleted file mode 100755 (executable)
index 3c73c93..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/sh
-
-test_description='--show-all --parents does not rewrite TREESAME commits'
-
-. ./test-lib.sh
-
-test_expect_success 'set up --show-all --parents test' '
-       test_commit one foo.txt &&
-       commit1=$(git rev-list -1 HEAD) &&
-       test_commit two bar.txt &&
-       commit2=$(git rev-list -1 HEAD) &&
-       test_commit three foo.txt &&
-       commit3=$(git rev-list -1 HEAD)
-       '
-
-test_expect_success '--parents rewrites TREESAME parents correctly' '
-       echo $commit3 $commit1 > expected &&
-       echo $commit1 >> expected &&
-       git rev-list --parents HEAD -- foo.txt > actual &&
-       test_cmp expected actual
-       '
-
-test_expect_success '--parents --show-all does not rewrites TREESAME parents' '
-       echo $commit3 $commit2 > expected &&
-       echo $commit2 $commit1 >> expected &&
-       echo $commit1 >> expected &&
-       git rev-list --parents --show-all HEAD -- foo.txt > actual &&
-       test_cmp expected actual
-       '
-
-test_done
index a5d901502414f25616a474152ee0f5816465bd37..bae78c4e89e2402d5ab37e3b7cd094dde41d69b3 100755 (executable)
@@ -378,4 +378,12 @@ check_describe tags/A --all A
 check_describe tags/c --all c
 check_describe heads/branch_A --all --match='branch_*' branch_A
 
+test_expect_success 'describe complains about tree object' '
+       test_must_fail git describe HEAD^{tree}
+'
+
+test_expect_success 'describe complains about missing object' '
+       test_must_fail git describe $_z40
+'
+
 test_done
index 2e2fb0e9572f3d570311470aebf47da37974b8f8..a54a52aaa4e680bdbc97750c4ae4855a45dcdb80 100755 (executable)
@@ -512,7 +512,7 @@ test_expect_success 'merge-msg with "merging" an annotated tag' '
 
        test_when_finished "git reset --hard" &&
        annote=$(git rev-parse annote) &&
-       git merge --no-commit $annote &&
+       git merge --no-commit --no-ff $annote &&
        {
                cat <<-EOF
                Merge tag '\''$annote'\''
index c128dfc5790790de9edf1b4d2cfa8b028c1036bc..295d1475bde0151df65e0098e6e4a169193906ed 100755 (executable)
@@ -373,11 +373,8 @@ test_expect_success 'Quoting style: tcl' '
 
 for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do
        test_expect_success "more than one quoting style: $i" "
-               git for-each-ref $i 2>&1 | (read line &&
-               case \$line in
-               \"error: more than one quoting style\"*) : happy;;
-               *) false
-               esac)
+               test_must_fail git for-each-ref $i 2>err &&
+               grep '^error: more than one quoting style' err
        "
 done
 
index 6e5031f56fb4f211e0ba43911d4495b1bb0b5b62..d4e6485a26eef8985856413a1bf977c95f6ec345 100755 (executable)
@@ -38,6 +38,12 @@ test_expect_success \
     'git diff-tree -r -M --name-status  HEAD^ HEAD | \
     grep "^R100..*path1/COPYING..*path0/COPYING"'
 
+test_expect_success \
+    'mv --dry-run does not move file' \
+    'git mv -n path0/COPYING MOVED &&
+     test -f path0/COPYING &&
+     test ! -f MOVED'
+
 test_expect_success \
     'checking -k on non-existing file' \
     'git mv -k idontexist path0'
index a9af2de9960b345878ac0f85c33b1efd3e038d28..2aac77af701989dc16980268155d6e40500354bb 100755 (executable)
@@ -452,6 +452,21 @@ test_expect_success \
        test_cmp expect actual
 '
 
+get_tag_header annotated-tag-edit $commit commit $time >expect
+echo "An edited message" >>expect
+test_expect_success 'set up editor' '
+       write_script fakeeditor <<-\EOF
+       sed -e "s/A message/An edited message/g" <"$1" >"$1-"
+       mv "$1-" "$1"
+       EOF
+'
+test_expect_success \
+       'creating an annotated tag with -m message --edit should succeed' '
+       GIT_EDITOR=./fakeeditor git tag -m "A message" --edit annotated-tag-edit &&
+       get_tag_msg annotated-tag-edit >actual &&
+       test_cmp expect actual
+'
+
 cat >msgfile <<EOF
 Another message
 in a file.
@@ -465,6 +480,21 @@ test_expect_success \
        test_cmp expect actual
 '
 
+get_tag_header file-annotated-tag-edit $commit commit $time >expect
+sed -e "s/Another message/Another edited message/g" msgfile >>expect
+test_expect_success 'set up editor' '
+       write_script fakeeditor <<-\EOF
+       sed -e "s/Another message/Another edited message/g" <"$1" >"$1-"
+       mv "$1-" "$1"
+       EOF
+'
+test_expect_success \
+       'creating an annotated tag with -F messagefile --edit should succeed' '
+       GIT_EDITOR=./fakeeditor git tag -F msgfile --edit file-annotated-tag-edit &&
+       get_tag_msg file-annotated-tag-edit >actual &&
+       test_cmp expect actual
+'
+
 cat >inputmsg <<EOF
 A message from the
 standard input
index e5fb892f9575fda4baf0b2a0e6b31cf13a0d6c0b..46b947824fd68150cfaa6018355cd086c5a8759e 100755 (executable)
@@ -22,6 +22,12 @@ avoid_racy() {
        sleep 1
 }
 
+status_is_clean() {
+       >../status.expect &&
+       git status --porcelain >../status.actual &&
+       test_cmp ../status.expect ../status.actual
+}
+
 test_lazy_prereq UNTRACKED_CACHE '
        { git update-index --test-untracked-cache; ret=$?; } &&
        test $ret -ne 1
@@ -683,4 +689,85 @@ test_expect_success 'untracked cache survives a commit' '
        test_cmp ../before ../after
 '
 
+test_expect_success 'teardown worktree' '
+       cd ..
+'
+
+test_expect_success SYMLINKS 'setup worktree for symlink test' '
+       git init worktree-symlink &&
+       cd worktree-symlink &&
+       git config core.untrackedCache true &&
+       mkdir one two &&
+       touch one/file two/file &&
+       git add one/file two/file &&
+       git commit -m"first commit" &&
+       git rm -rf one &&
+       ln -s two one &&
+       git add one &&
+       git commit -m"second commit"
+'
+
+test_expect_success SYMLINKS '"status" after symlink replacement should be clean with UC=true' '
+       git checkout HEAD~ &&
+       status_is_clean &&
+       status_is_clean &&
+       git checkout master &&
+       avoid_racy &&
+       status_is_clean &&
+       status_is_clean
+'
+
+test_expect_success SYMLINKS '"status" after symlink replacement should be clean with UC=false' '
+       git config core.untrackedCache false &&
+       git checkout HEAD~ &&
+       status_is_clean &&
+       status_is_clean &&
+       git checkout master &&
+       avoid_racy &&
+       status_is_clean &&
+       status_is_clean
+'
+
+test_expect_success 'setup worktree for non-symlink test' '
+       git init worktree-non-symlink &&
+       cd worktree-non-symlink &&
+       git config core.untrackedCache true &&
+       mkdir one two &&
+       touch one/file two/file &&
+       git add one/file two/file &&
+       git commit -m"first commit" &&
+       git rm -rf one &&
+       cp two/file one &&
+       git add one &&
+       git commit -m"second commit"
+'
+
+test_expect_success '"status" after file replacement should be clean with UC=true' '
+       git checkout HEAD~ &&
+       status_is_clean &&
+       status_is_clean &&
+       git checkout master &&
+       avoid_racy &&
+       status_is_clean &&
+       test-dump-untracked-cache >../actual &&
+       grep -F "recurse valid" ../actual >../actual.grep &&
+       cat >../expect.grep <<EOF &&
+/ 0000000000000000000000000000000000000000 recurse valid
+/two/ 0000000000000000000000000000000000000000 recurse valid
+EOF
+       status_is_clean &&
+       test_cmp ../expect.grep ../actual.grep
+'
+
+test_expect_success '"status" after file replacement should be clean with UC=false' '
+       git config core.untrackedCache false &&
+       git checkout HEAD~ &&
+       status_is_clean &&
+       status_is_clean &&
+       git checkout master &&
+       avoid_racy &&
+       status_is_clean &&
+       status_is_clean
+'
+
 test_done
index c20717181e95fc74738bdf53aa3e0b0797d77d6a..fc018e3638a8bebebf0f1c689567e9867bfbbee8 100755 (executable)
@@ -6,7 +6,7 @@
 test_description='Test submodules on detached working tree
 
 This test verifies that "git submodule" initialization, update and addition works
-on detahced working trees
+on detached working trees
 '
 
 TEST_NO_CREATE_REPO=1
index eb2d13bbcf8abefd7af2be6f9cb3bf97e389ab15..756beb0d8eb466d78b235af363b6a36dde37c79e 100755 (executable)
@@ -314,4 +314,43 @@ test_expect_success 'splitting the index results in the same state' '
        test_cmp expect actual
 '
 
+test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR' '
+       test_create_repo dot-git &&
+       (
+               cd dot-git &&
+               mkdir -p .git/hooks &&
+               : >tracked &&
+               : >modified &&
+               mkdir dir1 &&
+               : >dir1/tracked &&
+               : >dir1/modified &&
+               mkdir dir2 &&
+               : >dir2/tracked &&
+               : >dir2/modified &&
+               write_integration_script &&
+               git config core.fsmonitor .git/hooks/fsmonitor-test &&
+               git update-index --untracked-cache &&
+               git update-index --fsmonitor &&
+               GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-before" \
+               git status &&
+               test-dump-untracked-cache >../before
+       ) &&
+       cat >>dot-git/.git/hooks/fsmonitor-test <<-\EOF &&
+       printf ".git\0"
+       printf ".git/index\0"
+       printf "dir1/.git\0"
+       printf "dir1/.git/index\0"
+       EOF
+       (
+               cd dot-git &&
+               GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-after" \
+               git status &&
+               test-dump-untracked-cache >../after
+       ) &&
+       grep "directory invalidation" trace-before >>before &&
+       grep "directory invalidation" trace-after >>after &&
+       # UNTR extension unchanged, dir invalidation count unchanged
+       test_cmp before after
+'
+
 test_done
index dfde6a675a8cd28297324627923a861bc7410ff2..6736d8d13139c946b07165d5bbad6c8d14617cbd 100755 (executable)
@@ -700,6 +700,42 @@ test_expect_success 'merge --no-ff --edit' '
        test_cmp expected actual
 '
 
+test_expect_success 'merge annotated/signed tag w/o tracking' '
+       test_when_finished "rm -rf dst; git tag -d anno1" &&
+       git tag -a -m "anno c1" anno1 c1 &&
+       git init dst &&
+       git rev-parse c1 >dst/expect &&
+       (
+               # c0 fast-forwards to c1 but because this repository
+               # is not a "downstream" whose refs/tags follows along
+               # tag from the "upstream", this pull defaults to --no-ff
+               cd dst &&
+               git pull .. c0 &&
+               git pull .. anno1 &&
+               git rev-parse HEAD^2 >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'merge annotated/signed tag w/ tracking' '
+       test_when_finished "rm -rf dst; git tag -d anno1" &&
+       git tag -a -m "anno c1" anno1 c1 &&
+       git init dst &&
+       git rev-parse c1 >dst/expect &&
+       (
+               # c0 fast-forwards to c1 and because this repository
+               # is a "downstream" whose refs/tags follows along
+               # tag from the "upstream", this pull defaults to --ff
+               cd dst &&
+               git remote add origin .. &&
+               git pull origin c0 &&
+               git fetch origin &&
+               git merge anno1 &&
+               git rev-parse HEAD >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_expect_success GPG 'merge --ff-only tag' '
        git reset --hard c0 &&
        git commit --allow-empty -m "A newer commit" &&
@@ -718,7 +754,7 @@ test_expect_success GPG 'merge --no-edit tag should skip editor' '
        git tag -f -s -m "A newer commit" signed &&
        git reset --hard c0 &&
 
-       EDITOR=false git merge --no-edit signed &&
+       EDITOR=false git merge --no-edit --no-ff signed &&
        git rev-parse signed^0 >expect &&
        git rev-parse HEAD^2 >actual &&
        test_cmp expect actual
index aabee13e5dd017c52d16e72f0e7f9bf973f60693..42a6781358f85cd43100078e7457e18962f6f07e 100644 (file)
@@ -610,6 +610,14 @@ list_contains () {
 #
 # Writing this as "! git checkout ../outerspace" is wrong, because
 # the failure could be due to a segv.  We want a controlled failure.
+#
+# Accepts the following options:
+#
+#   ok=<signal-name>[,<...>]:
+#     Don't treat an exit caused by the given signal as error.
+#     Multiple signals can be specified as a comma separated list.
+#     Currently recognized signal names are: sigpipe, success.
+#     (Don't use 'success', use 'test_might_fail' instead.)
 
 test_must_fail () {
        case "$1" in
@@ -656,6 +664,8 @@ test_must_fail () {
 #
 # Writing "git config --unset all.configuration || :" would be wrong,
 # because we want to notice if it fails due to segv.
+#
+# Accepts the same options as test_must_fail.
 
 test_might_fail () {
        test_must_fail ok=success "$@"
index 33f6ce26f69d442238c60619a5f70673d451a7fb..9535d2e0a936eb1b7a84686a9b702d3ca9fd680d 100644 (file)
@@ -116,6 +116,7 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
        my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
        print join("\n", @vars);
 ')
+unset XDG_CACHE_HOME
 unset XDG_CONFIG_HOME
 unset GITPERLLIB
 GIT_AUTHOR_EMAIL=author@example.com
index e6a15bbe44f24555a8edb9461c2af553de70cba0..c9f6e314d5cd830d63236ea88c5ff9c7baf418bb 100644 (file)
@@ -1528,7 +1528,7 @@ static void invalidate_ce_path(const struct cache_entry *ce,
        if (!ce)
                return;
        cache_tree_invalidate_path(o->src_index, ce->name);
-       untracked_cache_invalidate_path(o->src_index, ce->name);
+       untracked_cache_invalidate_path(o->src_index, ce->name, 1);
 }
 
 /*