Merge branch 'fc/branch-upstream-color'
authorJunio C Hamano <gitster@pobox.com>
Fri, 19 Apr 2013 20:31:24 +0000 (13:31 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 19 Apr 2013 20:31:24 +0000 (13:31 -0700)
Add more colors to "git branch -vv" output.

* fc/branch-upstream-color:
branch: colour upstream branches

53 files changed:
Documentation/RelNotes/1.8.3.txt
Documentation/config.txt
Documentation/git-daemon.txt
Documentation/git-format-patch.txt
Documentation/git-send-email.txt
Documentation/glossary-content.txt
Documentation/merge-strategies.txt
builtin/checkout.c
builtin/fmt-merge-msg.c
builtin/help.c
builtin/log.c
builtin/merge-tree.c
builtin/show-branch.c
bundle.c
config.mak.uname
contrib/completion/git-completion.bash
contrib/remote-helpers/git-remote-bzr
contrib/remote-helpers/git-remote-hg
diff.c
git-rebase--am.sh
git-send-email.perl
help.c
http-backend.c
http-push.c
http.c
http.h
perl/Git.pm
remote-curl.c
sha1_file.c
submodule.c
submodule.h
t/lib-httpd/apache.conf
t/t4014-format-patch.sh
t/t4300-merge-tree.sh
t/t5004-archive-corner-cases.sh
t/t5404-tracking-branches.sh
t/t5505-remote.sh
t/t5516-fetch-push.sh
t/t5517-push-mirror.sh
t/t5519-push-alternates.sh
t/t5531-deep-submodule-push.sh
t/t5550-http-fetch.sh
t/t5551-http-fetch.sh
t/t5570-git-daemon.sh
t/t5704-bundle.sh
t/t6200-fmt-merge-msg.sh
t/t7406-submodule-update.sh
t/t9400-git-cvsserver-server.sh
t/t9401-git-cvsserver-crlf.sh
t/t9700/test.pl
t/t9902-completion.sh
t/t9903-bash-prompt.sh
t/test-lib.sh
index bcbcf1569651d5aff9d0af738db7b912285954dd..5031abfe4a7bed4c785ba62245dd9e0dbc91fed7 100644 (file)
@@ -32,6 +32,18 @@ Updates since v1.8.2
 
 UI, Workflows & Features
 
+ * The "--annotate" option to "git send-email" can be turned on (or
+   off) by default with sendemail.annotate configuration variable (you
+   can use --no-annotate from the command line to override it).
+
+ * The "--cover-letter" option to "git format-patch" can be turned on
+   (or off) by default with format.coverLetter configuration
+   variable. By setting it to 'auto', you can turn it on only for a
+   series with two or more patches.
+
+ * The bash completion support (in contrib/) learned that cherry-pick
+   takes a few more options than it already knew about.
+
  * "git help" learned "-g" option to show the list of guides just like
    list of commands are given with "-a".
 
@@ -67,6 +79,9 @@ UI, Workflows & Features
    ref by specifying a raw object name from the command line when the
    server side supports this feature.
 
+ * Output from "git log --graph" works better with submodule log
+   output now.
+
  * "git count-objects -v" learned to report leftover temporary
    packfiles and other garbage in the object store.
 
@@ -116,6 +131,9 @@ Performance, Internal Implementation, etc.
 
  * Updates for building under msvc.
 
+ * The stack footprint of some codepaths that access an object from a
+   pack has been shrunk.
+
  * The logic to coalesce the same lines removed from the parents in
    the output from "diff -c/--cc" has been updated, but with an O(n^2)
    complexity, so this might turn out to be undesirable.
@@ -150,7 +168,7 @@ Performance, Internal Implementation, etc.
    conflicts have been applied.
 
 
-Also contains minor documentation updates and code clean-ups.
+Also contains various documentation updates and code clean-ups.
 
 
 Fixes since v1.8.2
@@ -160,6 +178,37 @@ Unless otherwise noted, all the fixes since v1.8.2 in the maintenance
 track are contained in this release (see release notes to them for
 details).
 
+ * Smart-capable HTTP servers were not restricted via the
+   GIT_NAMESPACE mechanism when talking with commit-walker clients,
+   like they do when talking with smart HTTP clients.
+   (merge 6130f86 jk/http-dumb-namespaces later to maint).
+
+ * "git merge-tree" did not omit a merge result that is identical to
+   "our" side in certain cases.
+   (merge aacecc3 jk/merge-tree-added-identically later to maint).
+
+ * Perl scripts like "git-svn" closed (not redirecting to /dev/null)
+   the standard error stream, which is not a very smart thing to do.
+   Later open may return file descriptor #2 for unrelated purpose, and
+   error reporting code may write into them.
+   (merge a749c0b tr/perl-keep-stderr-open later to maint).
+
+ * "git show-branch" was not prepared to show a very long run of
+   ancestor operators e.g. foobar^2~2^2^2^2...^2~4 correctly.
+   (merge aaa07e3 jk/show-branch-strbuf later to maint).
+
+ * "git diff --diff-algorithm algo" is also understood as "git diff
+   --diff-algorithm=algo".
+   (merge 0895c6d jk/diff-algo-finishing-touches later to maint).
+
+ * The new core.commentchar configuration was not applied to a few
+   places.
+   (merge 89c3bbd rt/commentchar-fmt-merge-msg later to maint).
+
+ * "git bundle" did not like a bundle created using a commit without
+   any message as its one of the prerequistes.
+   (merge 5446e33 lf/bundle-with-tip-wo-message later to maint).
+
  * "git log -S/-G" started paying attention to textconv filter, but
    there was no way to disable this.  Make it honor --no-textconv
    option.
index 426d02c9a0d5fbb543b7789ad32979a1919ab2c2..29559c88af2e5cc2aeffdc29626b6710446bc10f 100644 (file)
@@ -1110,6 +1110,11 @@ format.signoff::
     the rights to submit this work under the same open source license.
     Please see the 'SubmittingPatches' document for further discussion.
 
+format.coverLetter::
+       A boolean that controls whether to generate a cover-letter when
+       format-patch is invoked, but in addition can be set to "auto", to
+       generate a cover-letter only when there's more than one patch.
+
 filter.<driver>.clean::
        The command which is used to convert the content of a worktree
        file to a blob upon checkin.  See linkgit:gitattributes[5] for
@@ -1461,6 +1466,14 @@ http.sslCAPath::
        with when fetching or pushing over HTTPS. Can be overridden
        by the 'GIT_SSL_CAPATH' environment variable.
 
+http.sslTry::
+       Attempt to use AUTH SSL/TLS and encrypted data transfers
+       when connecting via regular FTP protocol. This might be needed
+       if the FTP server requires it for security reasons or you wish
+       to connect securely whenever remote FTP server supports it.
+       Default is false since it might trigger certificate verification
+       errors on misconfigured servers.
+
 http.maxRequests::
        How many HTTP requests to launch in parallel. Can be overridden
        by the 'GIT_HTTP_MAX_REQUESTS' environment variable. Default is 5.
@@ -2017,6 +2030,7 @@ sendemail.<identity>.*::
 
 sendemail.aliasesfile::
 sendemail.aliasfiletype::
+sendemail.annotate::
 sendemail.bcc::
 sendemail.cc::
 sendemail.cccmd::
index 77da564134f92e692e078888d7f87bd6710e821f..bfb106cccd5880f0cd40b03059aa79a5569acb63 100644 (file)
@@ -147,6 +147,13 @@ OPTIONS
 Giving these options is an error when used with `--inetd`; use
 the facility of inet daemon to achieve the same before spawning
 'git daemon' if needed.
++
+Like many programs that switch user id, the daemon does not reset
+environment variables such as `$HOME` when it runs git programs,
+e.g. `upload-pack` and `receive-pack`. When using this option, you
+may also want to set and export `HOME` to point at the home
+directory of `<user>` before starting the daemon, and make sure any
+Git configuration files in that directory are readable by `<user>`.
 
 --enable=<service>::
 --disable=<service>::
index 3a62f50edae9cdc772cb1009cf839b18e0079a9c..39118774afbbd730e4464f44e24a05af91432c30 100644 (file)
@@ -20,7 +20,7 @@ SYNOPSIS
                   [--ignore-if-in-upstream]
                   [--subject-prefix=Subject-Prefix] [(--reroll-count|-v) <n>]
                   [--to=<email>] [--cc=<email>]
-                  [--cover-letter] [--quiet] [--notes[=<ref>]]
+                  [--[no-]cover-letter] [--quiet] [--notes[=<ref>]]
                   [<common diff options>]
                   [ <since> | <revision range> ]
 
@@ -195,7 +195,7 @@ will want to ensure that threading is disabled for `git send-email`.
        `Cc:`, and custom) headers added so far from config or command
        line.
 
---cover-letter::
+--[no-]cover-letter::
        In addition to the patches, generate a cover letter file
        containing the shortlog and the overall diffstat.  You can
        fill in a description in the file before sending it out.
@@ -260,6 +260,7 @@ attachments, and sign off patches with configuration variables.
        cc = <email>
        attach [ = mime-boundary-string ]
        signoff = true
+       coverletter = auto
 ------------
 
 
index 0cffef8aa51cc5c2f34393e39e45e84c4997b195..40a9a9abc14f175738986b244207655b296cc31d 100644 (file)
@@ -45,8 +45,9 @@ Composing
 ~~~~~~~~~
 
 --annotate::
-       Review and edit each patch you're about to send. See the
-       CONFIGURATION section for 'sendemail.multiedit'.
+       Review and edit each patch you're about to send. Default is the value
+       of 'sendemail.annotate'. See the CONFIGURATION section for
+       'sendemail.multiedit'.
 
 --bcc=<address>::
        Specify a "Bcc:" value for each email. Default is the value of
index eb7ba84f1f65a316f3b1b3637983487b699a0a2c..2478a3963cc774f1bf73c56660a2d4c096ad4d94 100644 (file)
@@ -100,9 +100,22 @@ to point at the new commit.
 
 [[def_detached_HEAD]]detached HEAD::
        Normally the <<def_HEAD,HEAD>> stores the name of a
-       <<def_branch,branch>>.  However, Git also allows you to <<def_checkout,check out>>
-       an arbitrary <<def_commit,commit>> that isn't necessarily the tip of any
-       particular branch.  In this case HEAD is said to be "detached".
+       <<def_branch,branch>>, and commands that operate on the
+       history HEAD represents operate on the history leading to the
+       tip of the branch the HEAD points at.  However, Git also
+       allows you to <<def_checkout,check out>> an arbitrary
+       <<def_commit,commit>> that isn't necessarily the tip of any
+       particular branch.  The HEAD in such a state is called
+       "detached".
++
+Note that commands that operate on the history of the current branch
+(e.g. `git commit` to build a new history on top of it) still work
+while the HEAD is detached. They update the HEAD to point at the tip
+of the updated history without affecting any branch.  Commands that
+update or inquire information _about_ the current branch (e.g. `git
+branch --set-upstream-to` that sets what remote tracking branch the
+current branch integrates with) obviously do not work, as there is no
+(real) current branch to ask about in this state.
 
 [[def_dircache]]dircache::
        You are *waaaaay* behind. See <<def_index,index>>.
index 66db80296f505a9bd830c746f82bb54e3f49f313..49a9a7d53f5836ecf2c5bcadb30f0d314414afe0 100644 (file)
@@ -48,6 +48,12 @@ patience;;
        this when the branches to be merged have diverged wildly.
        See also linkgit:git-diff[1] `--patience`.
 
+diff-algorithm=[patience|minimal|histogram|myers];;
+       Tells 'merge-recursive' to use a different diff algorithm, which
+       can help avoid mismerges that occur due to unimportant matching
+       lines (such as braces from distinct functions).  See also
+       linkgit:git-diff[1] `--diff-algorithm`.
+
 ignore-space-change;;
 ignore-all-space;;
 ignore-space-at-eol;;
index f8033f446e8f9ef1807aa28c2f9eca4e821c42c9..eb51872347d9b7d3e81c37d9ddb4272a99f2b75f 100644 (file)
@@ -729,7 +729,7 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
                        "If you want to keep them by creating a new branch, "
                        "this may be a good time\nto do so with:\n\n"
                        " git branch new_branch_name %s\n\n"),
-                       sha1_to_hex(commit->object.sha1));
+                       find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
 }
 
 /*
index 265a9253bfd7cacd730789212fdb5284fcd270ac..1c040708696c8d6e81023b3261966fa751f14ad4 100644 (file)
@@ -287,10 +287,10 @@ static void credit_people(struct strbuf *out,
        const char *me;
 
        if (kind == 'a') {
-               label = "\n# By ";
+               label = "By";
                me = git_author_info(IDENT_NO_DATE);
        } else {
-               label = "\n# Via ";
+               label = "Via";
                me = git_committer_info(IDENT_NO_DATE);
        }
 
@@ -300,7 +300,7 @@ static void credit_people(struct strbuf *out,
             (me = skip_prefix(me, them->items->string)) != NULL &&
             skip_prefix(me, " <")))
                return;
-       strbuf_addstr(out, label);
+       strbuf_addf(out, "\n%c %s ", comment_line_char, label);
        add_people_count(out, them);
 }
 
@@ -503,14 +503,18 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
                } else {
                        if (tag_number == 2) {
                                struct strbuf tagline = STRBUF_INIT;
-                               strbuf_addf(&tagline, "\n# %s\n",
-                                           origins.items[first_tag].string);
+                               strbuf_addch(&tagline, '\n');
+                               strbuf_add_commented_lines(&tagline,
+                                               origins.items[first_tag].string,
+                                               strlen(origins.items[first_tag].string));
                                strbuf_insert(&tagbuf, 0, tagline.buf,
                                              tagline.len);
                                strbuf_release(&tagline);
                        }
-                       strbuf_addf(&tagbuf, "\n# %s\n",
-                                   origins.items[i].string);
+                       strbuf_addch(&tagbuf, '\n');
+                       strbuf_add_commented_lines(&tagbuf,
+                                       origins.items[i].string,
+                                       strlen(origins.items[i].string));
                        fmt_tag_signature(&tagbuf, &sig, buf, len);
                }
                strbuf_release(&sig);
index 034c36c254c3d4321ce6ef97adfe59dcae73bb2e..062957f629af09db81d78b9cb0be1ce60fd75ec4 100644 (file)
@@ -419,13 +419,13 @@ static struct {
        const char *name;
        const char *help;
 } common_guides[] = {
-       { "attributes", "Defining attributes per path" },
-       { "glossary", "A Git glossary" },
-       { "ignore", "Specifies intentionally untracked files to ignore" },
-       { "modules", "Defining submodule properties" },
-       { "revisions", "Specifying revisions and ranges for Git" },
-       { "tutorial", "A tutorial introduction to Git (for version 1.5.1 or newer)" },
-       { "workflows", "An overview of recommended workflows with Git"},
+       { "attributes", N_("Defining attributes per path") },
+       { "glossary", N_("A Git glossary") },
+       { "ignore", N_("Specifies intentionally untracked files to ignore") },
+       { "modules", N_("Defining submodule properties") },
+       { "revisions", N_("Specifying revisions and ranges for Git") },
+       { "tutorial", N_("A tutorial introduction to Git (for version 1.5.1 or newer)") },
+       { "workflows", N_("An overview of recommended workflows with Git") },
 };
 
 static void list_common_guides_help(void)
index 0f318107e5732a08bc6f979633829ea958d8f9b0..ad46f72950f3e4fda292da25f936e48c59705410 100644 (file)
@@ -100,9 +100,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
        int quiet = 0, source = 0, mailmap = 0;
 
        const struct option builtin_log_options[] = {
-               OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")),
-               OPT_BOOLEAN(0, "source", &source, N_("show source")),
-               OPT_BOOLEAN(0, "use-mailmap", &mailmap, N_("Use mail map file")),
+               OPT_BOOL(0, "quiet", &quiet, N_("suppress diff output")),
+               OPT_BOOL(0, "source", &source, N_("show source")),
+               OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
                { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
                  PARSE_OPT_OPTARG, decorate_callback},
                OPT_END()
@@ -622,6 +622,14 @@ static void add_header(const char *value)
 static int thread;
 static int do_signoff;
 static const char *signature = git_version_string;
+static int config_cover_letter;
+
+enum {
+       COVER_UNSET,
+       COVER_OFF,
+       COVER_ON,
+       COVER_AUTO
+};
 
 static int git_format_config(const char *var, const char *value, void *cb)
 {
@@ -683,6 +691,14 @@ static int git_format_config(const char *var, const char *value, void *cb)
        }
        if (!strcmp(var, "format.signature"))
                return git_config_string(&signature, var, value);
+       if (!strcmp(var, "format.coverletter")) {
+               if (value && !strcasecmp(value, "auto")) {
+                       config_cover_letter = COVER_AUTO;
+                       return 0;
+               }
+               config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
+               return 0;
+       }
 
        return git_log_config(var, value, cb);
 }
@@ -794,9 +810,37 @@ static void add_branch_description(struct strbuf *buf, const char *branch_name)
        }
 }
 
+static char *find_branch_name(struct rev_info *rev)
+{
+       int i, positive = -1;
+       unsigned char branch_sha1[20];
+       const unsigned char *tip_sha1;
+       const char *ref;
+       char *full_ref, *branch = NULL;
+
+       for (i = 0; i < rev->cmdline.nr; i++) {
+               if (rev->cmdline.rev[i].flags & UNINTERESTING)
+                       continue;
+               if (positive < 0)
+                       positive = i;
+               else
+                       return NULL;
+       }
+       if (positive < 0)
+               return NULL;
+       ref = rev->cmdline.rev[positive].name;
+       tip_sha1 = rev->cmdline.rev[positive].item->sha1;
+       if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
+           !prefixcmp(full_ref, "refs/heads/") &&
+           !hashcmp(tip_sha1, branch_sha1))
+               branch = xstrdup(full_ref + strlen("refs/heads/"));
+       free(full_ref);
+       return branch;
+}
+
 static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              struct commit *origin,
-                             int nr, struct commit **list, struct commit *head,
+                             int nr, struct commit **list,
                              const char *branch_name,
                              int quiet)
 {
@@ -810,6 +854,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        struct diff_options opts;
        int need_8bit_cte = 0;
        struct pretty_print_context pp = {0};
+       struct commit *head = list[0];
 
        if (rev->commit_format != CMIT_FMT_EMAIL)
                die(_("Cover letter needs email format"));
@@ -827,6 +872,9 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
                if (has_non_ascii(list[i]->buffer))
                        need_8bit_cte = 1;
 
+       if (!branch_name)
+               branch_name = find_branch_name(rev);
+
        msg = body;
        pp.fmt = CMIT_FMT_EMAIL;
        pp.date_mode = DATE_RFC2822;
@@ -1033,45 +1081,6 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
-static char *find_branch_name(struct rev_info *rev)
-{
-       int i, positive = -1;
-       unsigned char branch_sha1[20];
-       const unsigned char *tip_sha1;
-       const char *ref;
-       char *full_ref, *branch = NULL;
-
-       for (i = 0; i < rev->cmdline.nr; i++) {
-               if (rev->cmdline.rev[i].flags & UNINTERESTING)
-                       continue;
-               if (positive < 0)
-                       positive = i;
-               else
-                       return NULL;
-       }
-       if (0 <= positive) {
-               ref = rev->cmdline.rev[positive].name;
-               tip_sha1 = rev->cmdline.rev[positive].item->sha1;
-       } else if (!rev->cmdline.nr && rev->pending.nr == 1 &&
-                  !strcmp(rev->pending.objects[0].name, "HEAD")) {
-               /*
-                * No actual ref from command line, but "HEAD" from
-                * rev->def was added in setup_revisions()
-                * e.g. format-patch --cover-letter -12
-                */
-               ref = "HEAD";
-               tip_sha1 = rev->pending.objects[0].item->sha1;
-       } else {
-               return NULL;
-       }
-       if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
-           !prefixcmp(full_ref, "refs/heads/") &&
-           !hashcmp(tip_sha1, branch_sha1))
-               branch = xstrdup(full_ref + strlen("refs/heads/"));
-       free(full_ref);
-       return branch;
-}
-
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
        struct commit *commit;
@@ -1083,10 +1092,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int start_number = -1;
        int just_numbers = 0;
        int ignore_if_in_upstream = 0;
-       int cover_letter = 0;
+       int cover_letter = -1;
        int boundary_count = 0;
        int no_binary_diff = 0;
-       struct commit *origin = NULL, *head = NULL;
+       struct commit *origin = NULL;
        const char *in_reply_to = NULL;
        struct patch_ids ids;
        struct strbuf buf = STRBUF_INIT;
@@ -1101,12 +1110,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
                            N_("use [PATCH] even with multiple patches"),
                            PARSE_OPT_NOARG, no_numbered_callback },
-               OPT_BOOLEAN('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
-               OPT_BOOLEAN(0, "stdout", &use_stdout,
+               OPT_BOOL('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
+               OPT_BOOL(0, "stdout", &use_stdout,
                            N_("print patches to standard out")),
-               OPT_BOOLEAN(0, "cover-letter", &cover_letter,
+               OPT_BOOL(0, "cover-letter", &cover_letter,
                            N_("generate a cover letter")),
-               OPT_BOOLEAN(0, "numbered-files", &just_numbers,
+               OPT_BOOL(0, "numbered-files", &just_numbers,
                            N_("use simple number sequence for output file names")),
                OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"),
                            N_("use <sfx> instead of '.patch'")),
@@ -1280,28 +1289,36 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        }
 
        if (rev.pending.nr == 1) {
+               int check_head = 0;
+
                if (rev.max_count < 0 && !rev.show_root_diff) {
                        /*
                         * This is traditional behaviour of "git format-patch
                         * origin" that prepares what the origin side still
                         * does not have.
                         */
-                       unsigned char sha1[20];
-                       const char *ref;
-
                        rev.pending.objects[0].item->flags |= UNINTERESTING;
                        add_head_to_pending(&rev);
-                       ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
-                       if (ref && !prefixcmp(ref, "refs/heads/"))
-                               branch_name = xstrdup(ref + strlen("refs/heads/"));
-                       else
-                               branch_name = xstrdup(""); /* no branch */
+                       check_head = 1;
                }
                /*
                 * Otherwise, it is "format-patch -22 HEAD", and/or
                 * "format-patch --root HEAD".  The user wants
                 * get_revision() to do the usual traversal.
                 */
+
+               if (!strcmp(rev.pending.objects[0].name, "HEAD"))
+                       check_head = 1;
+
+               if (check_head) {
+                       unsigned char sha1[20];
+                       const char *ref;
+                       ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+                       if (ref && !prefixcmp(ref, "refs/heads/"))
+                               branch_name = xstrdup(ref + strlen("refs/heads/"));
+                       else
+                               branch_name = xstrdup(""); /* no branch */
+               }
        }
 
        /*
@@ -1310,29 +1327,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
         */
        rev.show_root_diff = 1;
 
-       if (cover_letter) {
-               /*
-                * NEEDSWORK:randomly pick one positive commit to show
-                * diffstat; this is often the tip and the command
-                * happens to do the right thing in most cases, but a
-                * complex command like "--cover-letter a b c ^bottom"
-                * picks "c" and shows diffstat between bottom..c
-                * which may not match what the series represents at
-                * all and totally broken.
-                */
-               int i;
-               for (i = 0; i < rev.pending.nr; i++) {
-                       struct object *o = rev.pending.objects[i].item;
-                       if (!(o->flags & UNINTERESTING))
-                               head = (struct commit *)o;
-               }
-               /* There is nothing to show; it is not an error, though. */
-               if (!head)
-                       return 0;
-               if (!branch_name)
-                       branch_name = find_branch_name(&rev);
-       }
-
        if (ignore_if_in_upstream) {
                /* Don't say anything if head and upstream are the same. */
                if (rev.pending.nr == 2) {
@@ -1364,11 +1358,21 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                list = xrealloc(list, nr * sizeof(list[0]));
                list[nr - 1] = commit;
        }
+       if (nr == 0)
+               /* nothing to do */
+               return 0;
        total = nr;
        if (!keep_subject && auto_number && total > 1)
                numbered = 1;
        if (numbered)
                rev.total = total + start_number - 1;
+       if (cover_letter == -1) {
+               if (config_cover_letter == COVER_AUTO)
+                       cover_letter = (total > 1);
+               else
+                       cover_letter = (config_cover_letter == COVER_ON);
+       }
+
        if (in_reply_to || thread || cover_letter)
                rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
        if (in_reply_to) {
@@ -1381,7 +1385,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                if (thread)
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, use_stdout,
-                                 origin, nr, list, head, branch_name, quiet);
+                                 origin, nr, list, branch_name, quiet);
                total++;
                start_number--;
        }
index bc912e399efa295615e9bfaac0de03904edff367..ed25d81b880eb830481d4e729d7e9acc3266de65 100644 (file)
@@ -155,6 +155,11 @@ static int same_entry(struct name_entry *a, struct name_entry *b)
                a->mode == b->mode;
 }
 
+static int both_empty(struct name_entry *a, struct name_entry *b)
+{
+       return !(a->sha1 || b->sha1);
+}
+
 static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
 {
        struct merge_list *res = xcalloc(1, sizeof(*res));
@@ -297,13 +302,10 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])
 static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
 {
        /* Same in both? */
-       if (same_entry(entry+1, entry+2)) {
-               if (entry[0].sha1) {
-                       /* Modified identically */
-                       resolve(info, NULL, entry+1);
-                       return mask;
-               }
-               /* "Both added the same" is left unresolved */
+       if (same_entry(entry+1, entry+2) || both_empty(entry+0, entry+2)) {
+               /* Modified, added or removed identically */
+               resolve(info, NULL, entry+1);
+               return mask;
        }
 
        if (same_entry(entry+0, entry+1)) {
@@ -319,12 +321,10 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
                 */
        }
 
-       if (same_entry(entry+0, entry+2)) {
-               if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) {
-                       /* We modified, they did not touch -- take ours */
-                       resolve(info, NULL, entry+1);
-                       return mask;
-               }
+       if (same_entry(entry+0, entry+2) || both_empty(entry+0, entry+2)) {
+               /* We added, modified or removed, they did not touch -- take ours */
+               resolve(info, NULL, entry+1);
+               return mask;
        }
 
        unresolved(info, entry);
index d208fd6c6821c729a713edb485a20bbe8d5e2692..90fc6b1b9d2a397ae43dfc4c0aba6bc192c9823c 100644 (file)
@@ -162,29 +162,28 @@ static void name_commits(struct commit_list *list,
                        nth = 0;
                        while (parents) {
                                struct commit *p = parents->item;
-                               char newname[1000], *en;
+                               struct strbuf newname = STRBUF_INIT;
                                parents = parents->next;
                                nth++;
                                if (p->util)
                                        continue;
-                               en = newname;
                                switch (n->generation) {
                                case 0:
-                                       en += sprintf(en, "%s", n->head_name);
+                                       strbuf_addstr(&newname, n->head_name);
                                        break;
                                case 1:
-                                       en += sprintf(en, "%s^", n->head_name);
+                                       strbuf_addf(&newname, "%s^", n->head_name);
                                        break;
                                default:
-                                       en += sprintf(en, "%s~%d",
-                                               n->head_name, n->generation);
+                                       strbuf_addf(&newname, "%s~%d",
+                                                   n->head_name, n->generation);
                                        break;
                                }
                                if (nth == 1)
-                                       en += sprintf(en, "^");
+                                       strbuf_addch(&newname, '^');
                                else
-                                       en += sprintf(en, "^%d", nth);
-                               name_commit(p, xstrdup(newname), 0);
+                                       strbuf_addf(&newname, "^%d", nth);
+                               name_commit(p, strbuf_detach(&newname, NULL), 0);
                                i++;
                                name_first_parent_chain(p);
                        }
index 505e07e93468b68454c7ec906f40eacea9746551..4b0e5cd51b82f916c26d3776f81f22cdb3045fa6 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -57,7 +57,7 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
                 * followed by SP and subject line.
                 */
                if (get_sha1_hex(buf.buf, sha1) ||
-                   (40 <= buf.len && !isspace(buf.buf[40])) ||
+                   (buf.len > 40 && !isspace(buf.buf[40])) ||
                    (!is_prereq && buf.len <= 40)) {
                        if (report_path)
                                error(_("unrecognized header: %s%s (%d)"),
index 9080054f7617017a67ca0ae8414f829a01d3d546..d78fd3df5b211130ba8ff77197a3f852c32aab86 100644 (file)
@@ -507,6 +507,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
                compat/win32/dirent.o
        EXTLIBS += -lws2_32
        PTHREAD_LIBS =
+       NATIVE_CRLF = YesPlease
        X = .exe
        SPARSE_FLAGS = -Wno-one-bit-signed-bitfield
 ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
index 2ba1461422c4e6c16e6c85ed3914536e1f4aa56e..6df62c217321b4f66fd230a1b95f2b3cfe2becd2 100644 (file)
@@ -53,19 +53,6 @@ __gitdir ()
        fi
 }
 
-__gitcomp_1 ()
-{
-       local c IFS=$' \t\n'
-       for c in $1; do
-               c="$c$2"
-               case $c in
-               --*=*|*.) ;;
-               *) c="$c " ;;
-               esac
-               printf '%s\n' "$c"
-       done
-}
-
 # The following function is based on code from:
 #
 #   bash_completion - programmable completion functions for bash 3.2+
@@ -195,8 +182,18 @@ _get_comp_words_by_ref ()
 }
 fi
 
-# Generates completion reply with compgen, appending a space to possible
-# completion words, if necessary.
+__gitcompadd ()
+{
+       local i=0
+       for x in $1; do
+               if [[ "$x" == "$3"* ]]; then
+                       COMPREPLY[i++]="$2$x$4"
+               fi
+       done
+}
+
+# Generates completion reply, appending a space to possible completion words,
+# if necessary.
 # It accepts 1 to 4 arguments:
 # 1: List of possible completion words.
 # 2: A prefix to be added to each possible completion word (optional).
@@ -208,19 +205,25 @@ __gitcomp ()
 
        case "$cur_" in
        --*=)
-               COMPREPLY=()
                ;;
        *)
-               local IFS=$'\n'
-               COMPREPLY=($(compgen -P "${2-}" \
-                       -W "$(__gitcomp_1 "${1-}" "${4-}")" \
-                       -- "$cur_"))
+               local c i=0 IFS=$' \t\n'
+               for c in $1; do
+                       c="$c${4-}"
+                       if [[ $c == "$cur_"* ]]; then
+                               case $c in
+                               --*=*|*.) ;;
+                               *) c="$c " ;;
+                               esac
+                               COMPREPLY[i++]="${2-}$c"
+                       fi
+               done
                ;;
        esac
 }
 
-# Generates completion reply with compgen from newline-separated possible
-# completion words by appending a space to all of them.
+# Generates completion reply from newline-separated possible completion words
+# by appending a space to all of them.
 # It accepts 1 to 4 arguments:
 # 1: List of possible completion words, separated by a single newline.
 # 2: A prefix to be added to each possible completion word (optional).
@@ -231,7 +234,7 @@ __gitcomp ()
 __gitcomp_nl ()
 {
        local IFS=$'\n'
-       COMPREPLY=($(compgen -P "${2-}" -S "${4- }" -W "$1" -- "${3-$cur}"))
+       __gitcompadd "$1" "${2-}" "${3-$cur}" "${4- }"
 }
 
 # Generates completion reply with compgen from newline-separated possible
@@ -614,7 +617,6 @@ __git_complete_remote_or_refspec ()
                        case "$cmd" in
                        push) no_complete_refspec=1 ;;
                        fetch)
-                               COMPREPLY=()
                                return
                                ;;
                        *) ;;
@@ -630,7 +632,6 @@ __git_complete_remote_or_refspec ()
                return
        fi
        if [ $no_complete_refspec = 1 ]; then
-               COMPREPLY=()
                return
        fi
        [ "$remote" = "." ] && remote=
@@ -951,7 +952,6 @@ _git_am ()
                        "
                return
        esac
-       COMPREPLY=()
 }
 
 _git_apply ()
@@ -971,7 +971,6 @@ _git_apply ()
                        "
                return
        esac
-       COMPREPLY=()
 }
 
 _git_add ()
@@ -1031,7 +1030,6 @@ _git_bisect ()
                __gitcomp_nl "$(__git_refs)"
                ;;
        *)
-               COMPREPLY=()
                ;;
        esac
 }
@@ -1124,9 +1122,14 @@ _git_cherry ()
 
 _git_cherry_pick ()
 {
+       local dir="$(__gitdir)"
+       if [ -f "$dir"/CHERRY_PICK_HEAD ]; then
+               __gitcomp "--continue --quit --abort"
+               return
+       fi
        case "$cur" in
        --*)
-               __gitcomp "--edit --no-commit"
+               __gitcomp "--edit --no-commit --signoff --strategy= --mainline"
                ;;
        *)
                __gitcomp_nl "$(__git_refs)"
@@ -1170,7 +1173,6 @@ _git_clone ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_commit ()
@@ -1354,7 +1356,6 @@ _git_fsck ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_gc ()
@@ -1365,7 +1366,6 @@ _git_gc ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_gitk ()
@@ -1442,7 +1442,6 @@ _git_init ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_ls_files ()
@@ -1578,7 +1577,6 @@ _git_mergetool ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_merge_base ()
@@ -1831,7 +1829,7 @@ _git_config ()
                local remote="${prev#remote.}"
                remote="${remote%.fetch}"
                if [ -z "$cur" ]; then
-                       COMPREPLY=("refs/heads/")
+                       __gitcompadd "refs/heads/" "" "" ""
                        return
                fi
                __gitcomp_nl "$(__git_refs_remotes "$remote")"
@@ -1891,7 +1889,6 @@ _git_config ()
                return
                ;;
        *.*)
-               COMPREPLY=()
                return
                ;;
        esac
@@ -2272,7 +2269,6 @@ _git_remote ()
                __gitcomp "$c"
                ;;
        *)
-               COMPREPLY=()
                ;;
        esac
 }
@@ -2388,8 +2384,6 @@ _git_stash ()
                *)
                        if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
                                __gitcomp "$subcommands"
-                       else
-                               COMPREPLY=()
                        fi
                        ;;
                esac
@@ -2402,14 +2396,12 @@ _git_stash ()
                        __gitcomp "--index --quiet"
                        ;;
                show,--*|drop,--*|branch,--*)
-                       COMPREPLY=()
                        ;;
                show,*|apply,*|drop,*|pop,*|branch,*)
                        __gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
                                        | sed -n -e 's/:.*//p')"
                        ;;
                *)
-                       COMPREPLY=()
                        ;;
                esac
        fi
@@ -2526,7 +2518,6 @@ _git_svn ()
                        __gitcomp "--revision= --parent"
                        ;;
                *)
-                       COMPREPLY=()
                        ;;
                esac
        fi
@@ -2551,13 +2542,10 @@ _git_tag ()
 
        case "$prev" in
        -m|-F)
-               COMPREPLY=()
                ;;
        -*|tag)
                if [ $f = 1 ]; then
                        __gitcomp_nl "$(__git_tags)"
-               else
-                       COMPREPLY=()
                fi
                ;;
        *)
index bd25e082027f90e614f3f265b94b49979d96e49f..aa7bc97beecc6af7adca5b6886d8e7c61f9173b1 100755 (executable)
@@ -249,7 +249,7 @@ def export_files(tree, files):
     return final
 
 def export_branch(branch, name):
-    global prefix, dirname
+    global prefix
 
     ref = '%s/heads/%s' % (prefix, name)
     tip = marks.get_tip(name)
@@ -335,8 +335,10 @@ def export_branch(branch, name):
     marks.set_tip(name, revid)
 
 def export_tag(repo, name):
-    global tags
-    print "reset refs/tags/%s" % name
+    global tags, prefix
+
+    ref = '%s/tags/%s' % (prefix, name)
+    print "reset %s" % ref
     print "from :%u" % rev_to_mark(tags[name])
     print
 
@@ -649,6 +651,7 @@ def do_capabilities(parser):
     print "import"
     print "export"
     print "refspec refs/heads/*:%s/heads/*" % prefix
+    print "refspec refs/tags/*:%s/tags/*" % prefix
 
     path = os.path.join(dirname, 'marks-git')
 
index 328c2dc76dd33e47cfe69064757631e9717771e6..45f6c80d45ab9848d1197e34a00b8ac5977a05fc 100755 (executable)
@@ -326,6 +326,8 @@ def export_ref(repo, name, kind, head):
         else:
             modified, removed = get_filechanges(repo, c, parents[0])
 
+        desc += '\n'
+
         if mode == 'hg':
             extra_msg = ''
 
@@ -349,7 +351,6 @@ def export_ref(repo, name, kind, head):
                 else:
                     extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value))
 
-            desc += '\n'
             if extra_msg:
                 desc += '\n--HG--\n' + extra_msg
 
diff --git a/diff.c b/diff.c
index 0eb26535f52adf516961672628f2810e55a31a9d..f0b3e7cfe34a99c0e5fea85fcd00ec54e9b578d8 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2255,6 +2255,7 @@ static void builtin_diff(const char *name_a,
                const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
                const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
                show_submodule_summary(o->file, one ? one->path : two->path,
+                               line_prefix,
                                one->sha1, two->sha1, two->dirty_submodule,
                                meta, del, add, reset);
                return;
@@ -3596,8 +3597,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        else if (!strcmp(arg, "--histogram"))
                options->xdl_opts = DIFF_WITH_ALG(options, HISTOGRAM_DIFF);
-       else if (!prefixcmp(arg, "--diff-algorithm=")) {
-               long value = parse_algorithm_value(arg+17);
+       else if ((argcount = parse_long_opt("diff-algorithm", av, &optarg))) {
+               long value = parse_algorithm_value(optarg);
                if (value < 0)
                        return error("option diff-algorithm accepts \"myers\", "
                                     "\"minimal\", \"patience\" and \"histogram\"");
@@ -3605,6 +3606,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_XDL_CLR(options, NEED_MINIMAL);
                options->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
                options->xdl_opts |= value;
+               return argcount;
        }
 
        /* flags options */
index 97f31dc7af43bafe36d3222ba2f78da861a4e620..f84854f09a14b92790bad543cbe78c2662def9f7 100644 (file)
@@ -31,8 +31,8 @@ else
        rm -f "$GIT_DIR/rebased-patches"
 
        git format-patch -k --stdout --full-index --ignore-if-in-upstream \
-               --src-prefix=a/ --dst-prefix=b/ \
-               --no-renames $root_flag "$revisions" >"$GIT_DIR/rebased-patches"
+               --src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
+               $root_flag "$revisions" >"$GIT_DIR/rebased-patches"
        ret=$?
 
        if test 0 != $ret
index 729747281864155bee7ef009de3ab92cc7643a89..bd13cc812d2a0115edcdd2c3ec146665ddfc5e29 100755 (executable)
@@ -54,7 +54,7 @@ sub usage {
     --[no-]bcc              <str>  * Email Bcc:
     --subject               <str>  * Email "Subject:"
     --in-reply-to           <str>  * Email "In-Reply-To:"
-    --annotate                     * Review each patch that will be sent in an editor.
+    --[no-]annotate                * Review each patch that will be sent in an editor.
     --compose                      * Open an editor for introduction.
     --compose-encoding      <str>  * Encoding to assume for introduction.
     --8bit-encoding         <str>  * Encoding to assume 8bit mails if undeclared
@@ -212,7 +212,8 @@ sub do_edit {
     "signedoffbycc" => [\$signed_off_by_cc, undef],
     "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
     "validate" => [\$validate, 1],
-    "multiedit" => [\$multiedit, undef]
+    "multiedit" => [\$multiedit, undef],
+    "annotate" => [\$annotate, undef]
 );
 
 my %config_settings = (
@@ -304,7 +305,7 @@ sub signal_handler {
                    "smtp-debug:i" => \$debug_net_smtp,
                    "smtp-domain:s" => \$smtp_domain,
                    "identity=s" => \$identity,
-                   "annotate" => \$annotate,
+                   "annotate!" => \$annotate,
                    "compose" => \$compose,
                    "quiet" => \$quiet,
                    "cc-cmd=s" => \$cc_cmd,
diff --git a/help.c b/help.c
index 1dfa0b05827e6e75dc463092b2838cd1c991623c..02ba043319932411e90121d4147f25395782dfb5 100644 (file)
--- a/help.c
+++ b/help.c
@@ -397,6 +397,10 @@ const char *help_unknown_cmd(const char *cmd)
 
 int cmd_version(int argc, const char **argv, const char *prefix)
 {
+       /*
+        * The format of this string should be kept stable for compatibility
+        * with external projects that rely on the output of "git version".
+        */
        printf("git version %s\n", git_version_string);
        return 0;
 }
index 8144f3ad5eb68c789fd031921e6e702170310d68..6b85ffac271596cf469984a06d27423af7df2b35 100644 (file)
@@ -361,17 +361,19 @@ static void run_service(const char **argv)
 static int show_text_ref(const char *name, const unsigned char *sha1,
        int flag, void *cb_data)
 {
+       const char *name_nons = strip_namespace(name);
        struct strbuf *buf = cb_data;
        struct object *o = parse_object(sha1);
        if (!o)
                return 0;
 
-       strbuf_addf(buf, "%s\t%s\n", sha1_to_hex(sha1), name);
+       strbuf_addf(buf, "%s\t%s\n", sha1_to_hex(sha1), name_nons);
        if (o->type == OBJ_TAG) {
                o = deref_tag(o, name, 0);
                if (!o)
                        return 0;
-               strbuf_addf(buf, "%s\t%s^{}\n", sha1_to_hex(o->sha1), name);
+               strbuf_addf(buf, "%s\t%s^{}\n", sha1_to_hex(o->sha1),
+                           name_nons);
        }
        return 0;
 }
@@ -402,12 +404,40 @@ static void get_info_refs(char *arg)
 
        } else {
                select_getanyfile();
-               for_each_ref(show_text_ref, &buf);
+               for_each_namespaced_ref(show_text_ref, &buf);
                send_strbuf("text/plain", &buf);
        }
        strbuf_release(&buf);
 }
 
+static int show_head_ref(const char *name, const unsigned char *sha1,
+       int flag, void *cb_data)
+{
+       struct strbuf *buf = cb_data;
+
+       if (flag & REF_ISSYMREF) {
+               unsigned char sha1[20];
+               const char *target = resolve_ref_unsafe(name, sha1, 1, NULL);
+               const char *target_nons = strip_namespace(target);
+
+               strbuf_addf(buf, "ref: %s\n", target_nons);
+       } else {
+               strbuf_addf(buf, "%s\n", sha1_to_hex(sha1));
+       }
+
+       return 0;
+}
+
+static void get_head(char *arg)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       select_getanyfile();
+       head_ref_namespaced(show_head_ref, &buf);
+       send_strbuf("text/plain", &buf);
+       strbuf_release(&buf);
+}
+
 static void get_info_packs(char *arg)
 {
        size_t objdirlen = strlen(get_object_directory());
@@ -520,7 +550,7 @@ static struct service_cmd {
        const char *pattern;
        void (*imp)(char *);
 } services[] = {
-       {"GET", "/HEAD$", get_text_file},
+       {"GET", "/HEAD$", get_head},
        {"GET", "/info/refs$", get_info_refs},
        {"GET", "/objects/info/alternates$", get_text_file},
        {"GET", "/objects/info/http-alternates$", get_text_file},
index bd66f6ab6edd24946d2bc84e117a2b97417b1503..395a8cfc1055fb6febc9cee559d8943bb4d9e829 100644 (file)
@@ -1551,7 +1551,7 @@ static int remote_exists(const char *path)
                ret = 0;
                break;
        case HTTP_ERROR:
-               http_error(url, HTTP_ERROR);
+               error("unable to access '%s': %s", url, curl_errorstr);
        default:
                ret = -1;
        }
diff --git a/http.c b/http.c
index 8803c70825ac8aefb619b6c56d6015573eb5d227..92aba59082a4ed9aa0c92d8522793abcd332e279 100644 (file)
--- a/http.c
+++ b/http.c
@@ -31,6 +31,7 @@ static CURL *curl_default;
 char curl_errorstr[CURL_ERROR_SIZE];
 
 static int curl_ssl_verify = -1;
+static int curl_ssl_try;
 static const char *ssl_cert;
 #if LIBCURL_VERSION_NUM >= 0x070903
 static const char *ssl_key;
@@ -163,6 +164,10 @@ static int http_options(const char *var, const char *value, void *cb)
                        ssl_cert_password_required = 1;
                return 0;
        }
+       if (!strcmp("http.ssltry", var)) {
+               curl_ssl_try = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp("http.minsessions", var)) {
                min_curl_sessions = git_config_int(var, value);
 #ifndef USE_CURL_MULTI
@@ -282,7 +287,6 @@ static CURL *get_curl_handle(void)
 #endif
        if (ssl_cainfo != NULL)
                curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
-       curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
 
        if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
                curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
@@ -307,6 +311,11 @@ static CURL *get_curl_handle(void)
        if (curl_ftp_no_epsv)
                curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
 
+#ifdef CURLOPT_USE_SSL
+       if (curl_ssl_try)
+               curl_easy_setopt(result, CURLOPT_USE_SSL, CURLUSESSL_TRY);
+#endif
+
        if (curl_http_proxy) {
                curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
                curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
@@ -506,6 +515,7 @@ struct active_request_slot *get_active_slot(void)
        curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
+       curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
        if (http_auth.password)
                init_curl_http_auth(slot->curl);
 
@@ -761,6 +771,25 @@ char *get_remote_object_url(const char *url, const char *hex,
 
 int handle_curl_result(struct slot_results *results)
 {
+       /*
+        * If we see a failing http code with CURLE_OK, we have turned off
+        * FAILONERROR (to keep the server's custom error response), and should
+        * translate the code into failure here.
+        */
+       if (results->curl_result == CURLE_OK &&
+           results->http_code >= 400) {
+               results->curl_result = CURLE_HTTP_RETURNED_ERROR;
+               /*
+                * Normally curl will already have put the "reason phrase"
+                * from the server into curl_errorstr; unfortunately without
+                * FAILONERROR it is lost, so we can give only the numeric
+                * status code.
+                */
+               snprintf(curl_errorstr, sizeof(curl_errorstr),
+                        "The requested URL returned error: %ld",
+                        results->http_code);
+       }
+
        if (results->curl_result == CURLE_OK) {
                credential_approve(&http_auth);
                return HTTP_OK;
@@ -825,6 +854,8 @@ static int http_request(const char *url, struct strbuf *type,
        strbuf_addstr(&buf, "Pragma:");
        if (options & HTTP_NO_CACHE)
                strbuf_addstr(&buf, " no-cache");
+       if (options & HTTP_KEEP_ERROR)
+               curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
 
        headers = curl_slist_append(headers, buf.buf);
 
@@ -836,7 +867,8 @@ static int http_request(const char *url, struct strbuf *type,
                run_active_slot(slot);
                ret = handle_curl_result(&results);
        } else {
-               error("Unable to start HTTP request for %s", url);
+               snprintf(curl_errorstr, sizeof(curl_errorstr),
+                        "failed to start HTTP request");
                ret = HTTP_START_FAILED;
        }
 
@@ -862,6 +894,22 @@ static int http_request_reauth(const char *url,
        int ret = http_request(url, type, result, target, options);
        if (ret != HTTP_REAUTH)
                return ret;
+
+       /*
+        * If we are using KEEP_ERROR, the previous request may have
+        * put cruft into our output stream; we should clear it out before
+        * making our next request. We only know how to do this for
+        * the strbuf case, but that is enough to satisfy current callers.
+        */
+       if (options & HTTP_KEEP_ERROR) {
+               switch (target) {
+               case HTTP_REQUEST_STRBUF:
+                       strbuf_reset(result);
+                       break;
+               default:
+                       die("BUG: HTTP_KEEP_ERROR is only supported with strbufs");
+               }
+       }
        return http_request(url, type, result, target, options);
 }
 
@@ -903,15 +951,6 @@ static int http_get_file(const char *url, const char *filename, int options)
        return ret;
 }
 
-int http_error(const char *url, int ret)
-{
-       /* http_request has already handled HTTP_START_FAILED. */
-       if (ret != HTTP_START_FAILED)
-               error("%s while accessing %s", curl_errorstr, url);
-
-       return ret;
-}
-
 int http_fetch_ref(const char *base, struct ref *ref)
 {
        char *url;
diff --git a/http.h b/http.h
index 25d19313983f0d0a11ab50dffbb623b105c8960a..d77c1b54f24d142461b9e1a0d11cf6de041bc59f 100644 (file)
--- a/http.h
+++ b/http.h
 #define NO_CURL_IOCTL
 #endif
 
+/*
+ * CURLOPT_USE_SSL was known as CURLOPT_FTP_SSL up to 7.16.4,
+ * and the constants were known as CURLFTPSSL_*
+*/
+#if !defined(CURLOPT_USE_SSL) && defined(CURLOPT_FTP_SSL)
+#define CURLOPT_USE_SSL CURLOPT_FTP_SSL
+#define CURLUSESSL_TRY CURLFTPSSL_TRY
+#endif
+
 struct slot_results {
        CURLcode curl_result;
        long http_code;
@@ -118,6 +127,7 @@ extern char *get_remote_object_url(const char *url, const char *hex,
 
 /* Options for http_request_*() */
 #define HTTP_NO_CACHE          1
+#define HTTP_KEEP_ERROR                2
 
 /* Return values for http_request_*() */
 #define HTTP_OK                        0
@@ -134,12 +144,6 @@ extern char *get_remote_object_url(const char *url, const char *hex,
  */
 int http_get_strbuf(const char *url, struct strbuf *content_type, struct strbuf *result, int options);
 
-/*
- * Prints an error message using error() containing url and curl_errorstr,
- * and returns ret.
- */
-int http_error(const char *url, int ret);
-
 extern int http_fetch_ref(const char *base, struct ref *ref);
 
 /* Helpers for fetching packs */
index 650db90853c6ff2a769e981afd2954ffbb97469c..dc48159ccab1cf2f888a6169460c5fc60d0d1bab 100644 (file)
@@ -1489,12 +1489,12 @@ sub _command_common_pipe {
                if (not defined $pid) {
                        throw Error::Simple("open failed: $!");
                } elsif ($pid == 0) {
-                       if (defined $opts{STDERR}) {
-                               close STDERR;
-                       }
                        if ($opts{STDERR}) {
                                open (STDERR, '>&', $opts{STDERR})
                                        or die "dup failed: $!";
+                       } elsif (defined $opts{STDERR}) {
+                               open (STDERR, '>', '/dev/null')
+                                       or die "opening /dev/null failed: $!";
                        }
                        _cmd_exec($self, $cmd, @args);
                }
index 93a09a64c3b1689b0f29a26c6cdd601ef298b975..60eda6308197ad3d0b5957b9ae34e2dc15dfe5cc 100644 (file)
@@ -151,6 +151,33 @@ static void free_discovery(struct discovery *d)
        }
 }
 
+static int show_http_message(struct strbuf *type, struct strbuf *msg)
+{
+       const char *p, *eol;
+
+       /*
+        * We only show text/plain parts, as other types are likely
+        * to be ugly to look at on the user's terminal.
+        *
+        * TODO should handle "; charset=XXX", and re-encode into
+        * logoutputencoding
+        */
+       if (strcasecmp(type->buf, "text/plain"))
+               return -1;
+
+       strbuf_trim(msg);
+       if (!msg->len)
+               return -1;
+
+       p = msg->buf;
+       do {
+               eol = strchrnul(p, '\n');
+               fprintf(stderr, "remote: %.*s\n", (int)(eol - p), p);
+               p = eol + 1;
+       } while(*eol);
+       return 0;
+}
+
 static struct discovery* discover_refs(const char *service, int for_push)
 {
        struct strbuf exp = STRBUF_INIT;
@@ -176,18 +203,20 @@ static struct discovery* discover_refs(const char *service, int for_push)
        }
        refs_url = strbuf_detach(&buffer, NULL);
 
-       http_ret = http_get_strbuf(refs_url, &type, &buffer, HTTP_NO_CACHE);
+       http_ret = http_get_strbuf(refs_url, &type, &buffer,
+                                  HTTP_NO_CACHE | HTTP_KEEP_ERROR);
        switch (http_ret) {
        case HTTP_OK:
                break;
        case HTTP_MISSING_TARGET:
-               die("%s not found: did you run git update-server-info on the"
-                   " server?", refs_url);
+               show_http_message(&type, &buffer);
+               die("repository '%s' not found", url);
        case HTTP_NOAUTH:
-               die("Authentication failed");
+               show_http_message(&type, &buffer);
+               die("Authentication failed for '%s'", url);
        default:
-               http_error(refs_url, http_ret);
-               die("HTTP request failed");
+               show_http_message(&type, &buffer);
+               die("unable to access '%s': %s", url, curl_errorstr);
        }
 
        last= xcalloc(1, sizeof(*last_discovery));
index 0ed23981b363a07c6f4c5b930b1a5991d5c8a622..64228a26d0be873040ebde37e95e62d20f5ba957 100644 (file)
@@ -1648,50 +1648,6 @@ static off_t get_delta_base(struct packed_git *p,
        return base_offset;
 }
 
-/* forward declaration for a mutually recursive function */
-static int packed_object_info(struct packed_git *p, off_t offset,
-                             unsigned long *sizep, int *rtype);
-
-static int packed_delta_info(struct packed_git *p,
-                            struct pack_window **w_curs,
-                            off_t curpos,
-                            enum object_type type,
-                            off_t obj_offset,
-                            unsigned long *sizep)
-{
-       off_t base_offset;
-
-       base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
-       if (!base_offset)
-               return OBJ_BAD;
-       type = packed_object_info(p, base_offset, NULL, NULL);
-       if (type <= OBJ_NONE) {
-               struct revindex_entry *revidx;
-               const unsigned char *base_sha1;
-               revidx = find_pack_revindex(p, base_offset);
-               if (!revidx)
-                       return OBJ_BAD;
-               base_sha1 = nth_packed_object_sha1(p, revidx->nr);
-               mark_bad_packed_object(p, base_sha1);
-               type = sha1_object_info(base_sha1, NULL);
-               if (type <= OBJ_NONE)
-                       return OBJ_BAD;
-       }
-
-       /* We choose to only get the type of the base object and
-        * ignore potentially corrupt pack file that expects the delta
-        * based on a base with a wrong size.  This saves tons of
-        * inflate() calls.
-        */
-       if (sizep) {
-               *sizep = get_size_from_delta(p, w_curs, curpos);
-               if (*sizep == 0)
-                       type = OBJ_BAD;
-       }
-
-       return type;
-}
-
 int unpack_object_header(struct packed_git *p,
                         struct pack_window **w_curs,
                         off_t *curpos,
@@ -1718,6 +1674,25 @@ int unpack_object_header(struct packed_git *p,
        return type;
 }
 
+static int retry_bad_packed_offset(struct packed_git *p, off_t obj_offset)
+{
+       int type;
+       struct revindex_entry *revidx;
+       const unsigned char *sha1;
+       revidx = find_pack_revindex(p, obj_offset);
+       if (!revidx)
+               return OBJ_BAD;
+       sha1 = nth_packed_object_sha1(p, revidx->nr);
+       mark_bad_packed_object(p, sha1);
+       type = sha1_object_info(sha1, NULL);
+       if (type <= OBJ_NONE)
+               return OBJ_BAD;
+       return type;
+}
+
+
+#define POI_STACK_PREALLOC 64
+
 static int packed_object_info(struct packed_git *p, off_t obj_offset,
                              unsigned long *sizep, int *rtype)
 {
@@ -1725,31 +1700,89 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
        unsigned long size;
        off_t curpos = obj_offset;
        enum object_type type;
+       off_t small_poi_stack[POI_STACK_PREALLOC];
+       off_t *poi_stack = small_poi_stack;
+       int poi_stack_nr = 0, poi_stack_alloc = POI_STACK_PREALLOC;
 
        type = unpack_object_header(p, &w_curs, &curpos, &size);
+
        if (rtype)
                *rtype = type; /* representation type */
 
+       if (sizep) {
+               if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+                       off_t tmp_pos = curpos;
+                       off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
+                                                          type, obj_offset);
+                       if (!base_offset) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+                       *sizep = get_size_from_delta(p, &w_curs, tmp_pos);
+                       if (*sizep == 0) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+               } else {
+                       *sizep = size;
+               }
+       }
+
+       while (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+               off_t base_offset;
+               /* Push the object we're going to leave behind */
+               if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
+                       poi_stack_alloc = alloc_nr(poi_stack_nr);
+                       poi_stack = xmalloc(sizeof(off_t)*poi_stack_alloc);
+                       memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
+               } else {
+                       ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
+               }
+               poi_stack[poi_stack_nr++] = obj_offset;
+               /* If parsing the base offset fails, just unwind */
+               base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
+               if (!base_offset)
+                       goto unwind;
+               curpos = obj_offset = base_offset;
+               type = unpack_object_header(p, &w_curs, &curpos, &size);
+               if (type <= OBJ_NONE) {
+                       /* If getting the base itself fails, we first
+                        * retry the base, otherwise unwind */
+                       type = retry_bad_packed_offset(p, base_offset);
+                       if (type > OBJ_NONE)
+                               goto out;
+                       goto unwind;
+               }
+       }
+
        switch (type) {
-       case OBJ_OFS_DELTA:
-       case OBJ_REF_DELTA:
-               type = packed_delta_info(p, &w_curs, curpos,
-                                        type, obj_offset, sizep);
-               break;
+       case OBJ_BAD:
        case OBJ_COMMIT:
        case OBJ_TREE:
        case OBJ_BLOB:
        case OBJ_TAG:
-               if (sizep)
-                       *sizep = size;
                break;
        default:
                error("unknown object type %i at offset %"PRIuMAX" in %s",
                      type, (uintmax_t)obj_offset, p->pack_name);
                type = OBJ_BAD;
        }
+
+out:
+       if (poi_stack != small_poi_stack)
+               free(poi_stack);
        unuse_pack(&w_curs);
        return type;
+
+unwind:
+       while (poi_stack_nr) {
+               obj_offset = poi_stack[--poi_stack_nr];
+               type = retry_bad_packed_offset(p, obj_offset);
+               if (type > OBJ_NONE)
+                       goto out;
+       }
+       type = OBJ_BAD;
+       goto out;
 }
 
 static void *unpack_compressed_entry(struct packed_git *p,
@@ -1811,32 +1844,51 @@ static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
        return hash % MAX_DELTA_CACHE;
 }
 
-static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
+static struct delta_base_cache_entry *
+get_delta_base_cache_entry(struct packed_git *p, off_t base_offset)
 {
        unsigned long hash = pack_entry_hash(p, base_offset);
-       struct delta_base_cache_entry *ent = delta_base_cache + hash;
+       return delta_base_cache + hash;
+}
+
+static int eq_delta_base_cache_entry(struct delta_base_cache_entry *ent,
+                                    struct packed_git *p, off_t base_offset)
+{
        return (ent->data && ent->p == p && ent->base_offset == base_offset);
 }
 
+static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
+{
+       struct delta_base_cache_entry *ent;
+       ent = get_delta_base_cache_entry(p, base_offset);
+       return eq_delta_base_cache_entry(ent, p, base_offset);
+}
+
+static void clear_delta_base_cache_entry(struct delta_base_cache_entry *ent)
+{
+       ent->data = NULL;
+       ent->lru.next->prev = ent->lru.prev;
+       ent->lru.prev->next = ent->lru.next;
+       delta_base_cached -= ent->size;
+}
+
 static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
        unsigned long *base_size, enum object_type *type, int keep_cache)
 {
+       struct delta_base_cache_entry *ent;
        void *ret;
-       unsigned long hash = pack_entry_hash(p, base_offset);
-       struct delta_base_cache_entry *ent = delta_base_cache + hash;
 
-       ret = ent->data;
-       if (!ret || ent->p != p || ent->base_offset != base_offset)
+       ent = get_delta_base_cache_entry(p, base_offset);
+
+       if (!eq_delta_base_cache_entry(ent, p, base_offset))
                return unpack_entry(p, base_offset, type, base_size);
 
-       if (!keep_cache) {
-               ent->data = NULL;
-               ent->lru.next->prev = ent->lru.prev;
-               ent->lru.prev->next = ent->lru.next;
-               delta_base_cached -= ent->size;
-       } else {
+       ret = ent->data;
+
+       if (!keep_cache)
+               clear_delta_base_cache_entry(ent);
+       else
                ret = xmemdupz(ent->data, ent->size);
-       }
        *type = ent->type;
        *base_size = ent->size;
        return ret;
@@ -1900,68 +1952,6 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
 static void *read_object(const unsigned char *sha1, enum object_type *type,
                         unsigned long *size);
 
-static void *unpack_delta_entry(struct packed_git *p,
-                               struct pack_window **w_curs,
-                               off_t curpos,
-                               unsigned long delta_size,
-                               off_t obj_offset,
-                               enum object_type *type,
-                               unsigned long *sizep)
-{
-       void *delta_data, *result, *base;
-       unsigned long base_size;
-       off_t base_offset;
-
-       base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset);
-       if (!base_offset) {
-               error("failed to validate delta base reference "
-                     "at offset %"PRIuMAX" from %s",
-                     (uintmax_t)curpos, p->pack_name);
-               return NULL;
-       }
-       unuse_pack(w_curs);
-       base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0);
-       if (!base) {
-               /*
-                * We're probably in deep shit, but let's try to fetch
-                * the required base anyway from another pack or loose.
-                * This is costly but should happen only in the presence
-                * of a corrupted pack, and is better than failing outright.
-                */
-               struct revindex_entry *revidx;
-               const unsigned char *base_sha1;
-               revidx = find_pack_revindex(p, base_offset);
-               if (!revidx)
-                       return NULL;
-               base_sha1 = nth_packed_object_sha1(p, revidx->nr);
-               error("failed to read delta base object %s"
-                     " at offset %"PRIuMAX" from %s",
-                     sha1_to_hex(base_sha1), (uintmax_t)base_offset,
-                     p->pack_name);
-               mark_bad_packed_object(p, base_sha1);
-               base = read_object(base_sha1, type, &base_size);
-               if (!base)
-                       return NULL;
-       }
-
-       delta_data = unpack_compressed_entry(p, w_curs, curpos, delta_size);
-       if (!delta_data) {
-               error("failed to unpack compressed delta "
-                     "at offset %"PRIuMAX" from %s",
-                     (uintmax_t)curpos, p->pack_name);
-               free(base);
-               return NULL;
-       }
-       result = patch_delta(base, base_size,
-                            delta_data, delta_size,
-                            sizep);
-       if (!result)
-               die("failed to apply delta");
-       free(delta_data);
-       add_delta_base_cache(p, base_offset, base, base_size, *type);
-       return result;
-}
-
 static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
 {
        static FILE *log_file;
@@ -1982,48 +1972,179 @@ static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
 
 int do_check_packed_object_crc;
 
+#define UNPACK_ENTRY_STACK_PREALLOC 64
+struct unpack_entry_stack_ent {
+       off_t obj_offset;
+       off_t curpos;
+       unsigned long size;
+};
+
 void *unpack_entry(struct packed_git *p, off_t obj_offset,
-                  enum object_type *type, unsigned long *sizep)
+                  enum object_type *final_type, unsigned long *final_size)
 {
        struct pack_window *w_curs = NULL;
        off_t curpos = obj_offset;
-       void *data;
+       void *data = NULL;
+       unsigned long size;
+       enum object_type type;
+       struct unpack_entry_stack_ent small_delta_stack[UNPACK_ENTRY_STACK_PREALLOC];
+       struct unpack_entry_stack_ent *delta_stack = small_delta_stack;
+       int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
+       int base_from_cache = 0;
 
        if (log_pack_access)
                write_pack_access_log(p, obj_offset);
 
-       if (do_check_packed_object_crc && p->index_version > 1) {
-               struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
-               unsigned long len = revidx[1].offset - obj_offset;
-               if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
-                       const unsigned char *sha1 =
-                               nth_packed_object_sha1(p, revidx->nr);
-                       error("bad packed object CRC for %s",
-                             sha1_to_hex(sha1));
-                       mark_bad_packed_object(p, sha1);
-                       unuse_pack(&w_curs);
-                       return NULL;
+       /* PHASE 1: drill down to the innermost base object */
+       for (;;) {
+               off_t base_offset;
+               int i;
+               struct delta_base_cache_entry *ent;
+
+               if (do_check_packed_object_crc && p->index_version > 1) {
+                       struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
+                       unsigned long len = revidx[1].offset - obj_offset;
+                       if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
+                               const unsigned char *sha1 =
+                                       nth_packed_object_sha1(p, revidx->nr);
+                               error("bad packed object CRC for %s",
+                                     sha1_to_hex(sha1));
+                               mark_bad_packed_object(p, sha1);
+                               unuse_pack(&w_curs);
+                               return NULL;
+                       }
+               }
+
+               ent = get_delta_base_cache_entry(p, curpos);
+               if (eq_delta_base_cache_entry(ent, p, curpos)) {
+                       type = ent->type;
+                       data = ent->data;
+                       size = ent->size;
+                       clear_delta_base_cache_entry(ent);
+                       base_from_cache = 1;
+                       break;
                }
+
+               type = unpack_object_header(p, &w_curs, &curpos, &size);
+               if (type != OBJ_OFS_DELTA && type != OBJ_REF_DELTA)
+                       break;
+
+               base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
+               if (!base_offset) {
+                       error("failed to validate delta base reference "
+                             "at offset %"PRIuMAX" from %s",
+                             (uintmax_t)curpos, p->pack_name);
+                       /* bail to phase 2, in hopes of recovery */
+                       data = NULL;
+                       break;
+               }
+
+               /* push object, proceed to base */
+               if (delta_stack_nr >= delta_stack_alloc
+                   && delta_stack == small_delta_stack) {
+                       delta_stack_alloc = alloc_nr(delta_stack_nr);
+                       delta_stack = xmalloc(sizeof(*delta_stack)*delta_stack_alloc);
+                       memcpy(delta_stack, small_delta_stack,
+                              sizeof(*delta_stack)*delta_stack_nr);
+               } else {
+                       ALLOC_GROW(delta_stack, delta_stack_nr+1, delta_stack_alloc);
+               }
+               i = delta_stack_nr++;
+               delta_stack[i].obj_offset = obj_offset;
+               delta_stack[i].curpos = curpos;
+               delta_stack[i].size = size;
+
+               curpos = obj_offset = base_offset;
        }
 
-       *type = unpack_object_header(p, &w_curs, &curpos, sizep);
-       switch (*type) {
+       /* PHASE 2: handle the base */
+       switch (type) {
        case OBJ_OFS_DELTA:
        case OBJ_REF_DELTA:
-               data = unpack_delta_entry(p, &w_curs, curpos, *sizep,
-                                         obj_offset, type, sizep);
+               if (data)
+                       die("BUG in unpack_entry: left loop at a valid delta");
                break;
        case OBJ_COMMIT:
        case OBJ_TREE:
        case OBJ_BLOB:
        case OBJ_TAG:
-               data = unpack_compressed_entry(p, &w_curs, curpos, *sizep);
+               if (!base_from_cache)
+                       data = unpack_compressed_entry(p, &w_curs, curpos, size);
                break;
        default:
                data = NULL;
                error("unknown object type %i at offset %"PRIuMAX" in %s",
-                     *type, (uintmax_t)obj_offset, p->pack_name);
+                     type, (uintmax_t)obj_offset, p->pack_name);
+       }
+
+       /* PHASE 3: apply deltas in order */
+
+       /* invariants:
+        *   'data' holds the base data, or NULL if there was corruption
+        */
+       while (delta_stack_nr) {
+               void *delta_data;
+               void *base = data;
+               unsigned long delta_size, base_size = size;
+               int i;
+
+               data = NULL;
+
+               if (base)
+                       add_delta_base_cache(p, obj_offset, base, base_size, type);
+
+               if (!base) {
+                       /*
+                        * We're probably in deep shit, but let's try to fetch
+                        * the required base anyway from another pack or loose.
+                        * This is costly but should happen only in the presence
+                        * of a corrupted pack, and is better than failing outright.
+                        */
+                       struct revindex_entry *revidx;
+                       const unsigned char *base_sha1;
+                       revidx = find_pack_revindex(p, obj_offset);
+                       if (revidx) {
+                               base_sha1 = nth_packed_object_sha1(p, revidx->nr);
+                               error("failed to read delta base object %s"
+                                     " at offset %"PRIuMAX" from %s",
+                                     sha1_to_hex(base_sha1), (uintmax_t)obj_offset,
+                                     p->pack_name);
+                               mark_bad_packed_object(p, base_sha1);
+                               base = read_object(base_sha1, &type, &base_size);
+                       }
+               }
+
+               i = --delta_stack_nr;
+               obj_offset = delta_stack[i].obj_offset;
+               curpos = delta_stack[i].curpos;
+               delta_size = delta_stack[i].size;
+
+               if (!base)
+                       continue;
+
+               delta_data = unpack_compressed_entry(p, &w_curs, curpos, delta_size);
+
+               if (!delta_data) {
+                       error("failed to unpack compressed delta "
+                             "at offset %"PRIuMAX" from %s",
+                             (uintmax_t)curpos, p->pack_name);
+                       free(base);
+                       data = NULL;
+                       continue;
+               }
+
+               data = patch_delta(base, base_size,
+                                  delta_data, delta_size,
+                                  &size);
+               if (!data)
+                       die("failed to apply delta");
+
+               free (delta_data);
        }
+
+       *final_type = type;
+       *final_size = size;
+
        unuse_pack(&w_curs);
        return data;
 }
index 975bc87e48b33e70bd7c38e82b7f59e34bb81ca3..e728025f60dbf7f3df87b7104af6410832ddb653 100644 (file)
@@ -216,6 +216,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
 }
 
 static void print_submodule_summary(struct rev_info *rev, FILE *f,
+               const char *line_prefix,
                const char *del, const char *add, const char *reset)
 {
        static const char format[] = "  %m %s";
@@ -226,6 +227,7 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
                struct pretty_print_context ctx = {0};
                ctx.date_mode = rev->date_mode;
                strbuf_setlen(&sb, 0);
+               strbuf_addstr(&sb, line_prefix);
                if (commit->object.flags & SYMMETRIC_LEFT) {
                        if (del)
                                strbuf_addstr(&sb, del);
@@ -256,6 +258,7 @@ int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
 }
 
 void show_submodule_summary(FILE *f, const char *path,
+               const char *line_prefix,
                unsigned char one[20], unsigned char two[20],
                unsigned dirty_submodule, const char *meta,
                const char *del, const char *add, const char *reset)
@@ -280,16 +283,18 @@ void show_submodule_summary(FILE *f, const char *path,
                message = "(revision walker failed)";
 
        if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
-               fprintf(f, "Submodule %s contains untracked content\n", path);
+               fprintf(f, "%sSubmodule %s contains untracked content\n",
+                       line_prefix, path);
        if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-               fprintf(f, "Submodule %s contains modified content\n", path);
+               fprintf(f, "%sSubmodule %s contains modified content\n",
+                       line_prefix, path);
 
        if (!hashcmp(one, two)) {
                strbuf_release(&sb);
                return;
        }
 
-       strbuf_addf(&sb, "%sSubmodule %s %s..", meta, path,
+       strbuf_addf(&sb, "%s%sSubmodule %s %s..", line_prefix, meta, path,
                        find_unique_abbrev(one, DEFAULT_ABBREV));
        if (!fast_backward && !fast_forward)
                strbuf_addch(&sb, '.');
@@ -301,7 +306,7 @@ void show_submodule_summary(FILE *f, const char *path,
        fwrite(sb.buf, sb.len, 1, f);
 
        if (!message) /* only NULL if we succeeded in setting up the walk */
-               print_submodule_summary(&rev, f, del, add, reset);
+               print_submodule_summary(&rev, f, line_prefix, del, add, reset);
        if (left)
                clear_commit_marks(left, ~0);
        if (right)
index 3dc1b3fe891eaf5c758ee0a668f9f385ea99d5ba..c7ffc7c399d23781d823954d74bab4620debb628 100644 (file)
@@ -19,6 +19,7 @@ int parse_submodule_config_option(const char *var, const char *value);
 void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
 int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
 void show_submodule_summary(FILE *f, const char *path,
+               const char *line_prefix,
                unsigned char one[20], unsigned char two[20],
                unsigned dirty_submodule, const char *meta,
                const char *del, const char *add, const char *reset);
index 938b4cf803b3b4ea07be20338425f784b80cdf0c..ad8553719a4f476bd24aa0294d4c8103d55f9d90 100644 (file)
@@ -61,6 +61,11 @@ Alias /auth/dumb/ www/auth/dumb/
        SetEnv GIT_COMMITTER_NAME "Custom User"
        SetEnv GIT_COMMITTER_EMAIL custom@example.com
 </LocationMatch>
+<LocationMatch /smart_namespace/>
+       SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+       SetEnv GIT_HTTP_EXPORT_ALL
+       SetEnv GIT_NAMESPACE ns
+</LocationMatch>
 ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 ScriptAlias /broken_smart/ broken-smart-http.sh/
 <Directory ${GIT_EXEC_PATH}>
index 86ee0771077a9da0404a276873c81dd17600ceb4..58d418098d793b284bb4a7222cf9709c45b26c90 100755 (executable)
@@ -1284,4 +1284,37 @@ test_expect_success 'cover letter using branch description (6)' '
        grep hello actual >/dev/null
 '
 
+test_expect_success 'cover letter with nothing' '
+       git format-patch --stdout --cover-letter >actual &&
+       test_line_count = 0 actual
+'
+
+test_expect_success 'cover letter auto' '
+       mkdir -p tmp &&
+       test_when_finished "rm -rf tmp;
+               git config --unset format.coverletter" &&
+
+       git config format.coverletter auto &&
+       git format-patch -o tmp -1 >list &&
+       test_line_count = 1 list &&
+       git format-patch -o tmp -2 >list &&
+       test_line_count = 3 list
+'
+
+test_expect_success 'cover letter auto user override' '
+       mkdir -p tmp &&
+       test_when_finished "rm -rf tmp;
+               git config --unset format.coverletter" &&
+
+       git config format.coverletter auto &&
+       git format-patch -o tmp --cover-letter -1 >list &&
+       test_line_count = 2 list &&
+       git format-patch -o tmp --cover-letter -2 >list &&
+       test_line_count = 3 list &&
+       git format-patch -o tmp --no-cover-letter -1 >list &&
+       test_line_count = 1 list &&
+       git format-patch -o tmp --no-cover-letter -2 >list &&
+       test_line_count = 2 list
+'
+
 test_done
index d0b2a457b8bf30fc7ab472ff6b2576ff319432f7..bd43b3d8ef8b3e71f6985cfa96a940dccb4a0a54 100755 (executable)
@@ -26,8 +26,6 @@ EXPECTED
 
 test_expect_success 'file add !A, B' '
        cat >expected <<\EXPECTED &&
-added in local
-  our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
 EXPECTED
 
        git reset --hard initial &&
@@ -38,9 +36,6 @@ EXPECTED
 
 test_expect_success 'file add A, B (same)' '
        cat >expected <<\EXPECTED &&
-added in both
-  our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
-  their  100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
 EXPECTED
 
        git reset --hard initial &&
@@ -181,9 +176,6 @@ AAA" &&
 
 test_expect_success 'file remove A, !B' '
        cat >expected <<\EXPECTED &&
-removed in local
-  base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
-  their  100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
 EXPECTED
 
        git reset --hard initial &&
@@ -283,8 +275,6 @@ test_expect_success 'turn tree to file' '
        test_commit "make-file" "dir" "CCC" &&
        git merge-tree add-tree add-another-tree make-file >actual &&
        cat >expect <<-\EOF &&
-       added in local
-         our    100644 ba629238ca89489f2b350e196ca445e09d8bb834 dir/another
        removed in remote
          base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path
          our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path
index cdb7d7a7f9b59678e4079c61ae5104db51dc05a2..bfdb56a0694db0c6ed07aaa568c648909ac62660 100755 (executable)
@@ -28,7 +28,7 @@ check_dir() {
 }
 
 test_expect_success 'tar archive of empty tree is empty' '
-       git archive --format=tar HEAD >empty.tar &&
+       git archive --format=tar HEAD: >empty.tar &&
        make_dir extract &&
        "$TAR" xf empty.tar -C extract &&
        check_dir extract
index c24003565d635722f07333bb662c8e102d577c9e..2b8c0bac7db47ef7b37024ecea95ed0e37d5364f 100755 (executable)
@@ -36,7 +36,7 @@ test_expect_success 'prepare pushable branches' '
 '
 
 test_expect_success 'mixed-success push returns error' '
-       test_must_fail git push
+       test_must_fail git push origin :
 '
 
 test_expect_success 'check tracking branches updated correctly after push' '
index ccc55ebf4bf10d4782bf2ff32910ed6eeec040f6..6579a86724cec01b8c731ead2d4083dfe44293dd 100755 (executable)
@@ -345,7 +345,7 @@ test_expect_success 'fetch mirrors do not act as mirrors during push' '
        ) &&
        (cd mirror-fetch/child &&
         git branch -m renamed renamed2 &&
-        git push parent
+        git push parent :
        ) &&
        (cd mirror-fetch/parent &&
         git rev-parse --verify renamed &&
index 838e71dafea8388cbe18c82b26f58f074f56abbd..4691d51b8cde48dbb97ac5dfc1e3b75fe90f8da3 100755 (executable)
@@ -252,7 +252,7 @@ test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf
 test_expect_success 'push with matching heads' '
 
        mk_test testrepo heads/master &&
-       git push testrepo &&
+       git push testrepo &&
        check_push_result testrepo $the_commit heads/master
 
 '
@@ -281,7 +281,7 @@ test_expect_success 'push --force with matching heads' '
        mk_test testrepo heads/master &&
        git push testrepo : &&
        git commit --amend -massaged &&
-       git push --force testrepo &&
+       git push --force testrepo &&
        ! check_push_result testrepo $the_commit heads/master &&
        git reset --hard $the_commit
 
@@ -504,6 +504,7 @@ test_expect_success 'push with remote.pushdefault' '
        test_config remote.down.url down_repo &&
        test_config branch.master.remote up &&
        test_config remote.pushdefault down &&
+       test_config push.default matching &&
        git push &&
        check_push_result up_repo $the_first_commit heads/master &&
        check_push_result down_repo $the_commit heads/master
@@ -515,7 +516,7 @@ test_expect_success 'push with config remote.*.pushurl' '
        git checkout master &&
        test_config remote.there.url test2repo &&
        test_config remote.there.pushurl testrepo &&
-       git push there &&
+       git push there &&
        check_push_result testrepo $the_commit heads/master
 '
 
@@ -528,6 +529,7 @@ test_expect_success 'push with config branch.*.pushremote' '
        test_config remote.down.url down_repo &&
        test_config branch.master.remote up &&
        test_config branch.master.pushremote down &&
+       test_config push.default matching &&
        git push &&
        check_push_result up_repo $the_first_commit heads/master &&
        check_push_result side_repo $the_first_commit heads/master &&
@@ -541,7 +543,7 @@ test_expect_success 'push with dry-run' '
                cd testrepo &&
                old_commit=$(git show-ref -s --verify refs/heads/master)
        ) &&
-       git push --dry-run testrepo &&
+       git push --dry-run testrepo &&
        check_push_result testrepo $old_commit heads/master
 '
 
@@ -1022,7 +1024,7 @@ test_expect_success 'push --porcelain --dry-run rejected' '
 
 test_expect_success 'push --prune' '
        mk_test testrepo heads/master heads/second heads/foo heads/bar &&
-       git push --prune testrepo &&
+       git push --prune testrepo &&
        check_push_result testrepo $the_commit heads/master &&
        check_push_result testrepo $the_first_commit heads/second &&
        ! check_push_result testrepo $the_first_commit heads/foo heads/bar
index e2ad2605084194868fc23e23fec1636af622b47f..12a5dfb17e0a0bd4d0d9636ff13bf44999f78e36 100755 (executable)
@@ -256,7 +256,7 @@ test_expect_success 'remote.foo.mirror=no has no effect' '
                git branch keep master &&
                git push --mirror up &&
                git branch -D keep &&
-               git push up
+               git push up :
        ) &&
        (
                cd mirror &&
index c00c9b071d696038f63e8d613e11beab68eb547e..11fcd37700f372117145258f209e6c56ae04a1fc 100755 (executable)
@@ -40,7 +40,7 @@ test_expect_success 'alice works and pushes' '
                cd alice-work &&
                echo more >file &&
                git commit -a -m second &&
-               git push ../alice-pub
+               git push ../alice-pub :
        )
 '
 
@@ -57,7 +57,7 @@ test_expect_success 'bob fetches from alice, works and pushes' '
                git pull ../alice-pub master &&
                echo more bob >file &&
                git commit -a -m third &&
-               git push ../bob-pub
+               git push ../bob-pub :
        ) &&
 
        # Check that the second commit by Alice is not sent
@@ -86,7 +86,7 @@ test_expect_success 'alice works and pushes again' '
                cd alice-work &&
                echo more alice >file &&
                git commit -a -m fourth &&
-               git push ../alice-pub
+               git push ../alice-pub :
        )
 '
 
@@ -99,7 +99,7 @@ test_expect_success 'bob works and pushes' '
                cd bob-work &&
                echo yet more bob >file &&
                git commit -a -m fifth &&
-               git push ../bob-pub
+               git push ../bob-pub :
        )
 '
 
@@ -115,7 +115,7 @@ test_expect_success 'alice works and pushes yet again' '
                git commit -a -m sixth.2 &&
                echo more and more alice >>file &&
                git commit -a -m sixth.3 &&
-               git push ../alice-pub
+               git push ../alice-pub :
        )
 '
 
@@ -136,7 +136,7 @@ test_expect_success 'bob works and pushes again' '
                git hash-object -t commit -w commit &&
                echo even more bob >file &&
                git commit -a -m seventh &&
-               git push ../bob-pub
+               git push ../bob-pub :
        )
 '
 
index 1947c28c6466d46c79c7b1b093488c1620324172..8c16e045a0c585957a0be4e31b5628d1d5b6cb23 100755 (executable)
@@ -16,6 +16,7 @@ test_expect_success setup '
                (
                        cd gar/bage &&
                        git init &&
+                       git config push.default matching &&
                        >junk &&
                        git add junk &&
                        git commit -m "Initial junk"
index 80d20c876bbac0b00d388d511da555a29e827dbc..f7d0f146f0f69775dd3fa3ea06895e2bb1a74d55 100755 (executable)
@@ -13,6 +13,7 @@ LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
 start_httpd
 
 test_expect_success 'setup repository' '
+       git config push.default matching &&
        echo content1 >file &&
        git add file &&
        git commit -m one
index 47eb76921ddd53acb63573f9af75e41ab5067f5c..b23efbbfd9586670f1eeee114a1335b9dad4d97f 100755 (executable)
@@ -13,6 +13,7 @@ LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5551'}
 start_httpd
 
 test_expect_success 'setup repository' '
+       git config push.default matching &&
        echo content >file &&
        git add file &&
        git commit -m one
@@ -162,6 +163,30 @@ test_expect_success 'invalid Content-Type rejected' '
        grep "not valid:" actual
 '
 
+test_expect_success 'create namespaced refs' '
+       test_commit namespaced &&
+       git push public HEAD:refs/namespaces/ns/refs/heads/master &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+               symbolic-ref refs/namespaces/ns/HEAD refs/namespaces/ns/refs/heads/master
+'
+
+test_expect_success 'smart clone respects namespace' '
+       git clone "$HTTPD_URL/smart_namespace/repo.git" ns-smart &&
+       echo namespaced >expect &&
+       git --git-dir=ns-smart/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'dumb clone via http-backend respects namespace' '
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+               config http.getanyfile true &&
+       GIT_SMART_HTTP=0 git clone \
+               "$HTTPD_URL/smart_namespace/repo.git" ns-dumb &&
+       echo namespaced >expect &&
+       git --git-dir=ns-dumb/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
 test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
 
 test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
index a3a4e47e1d25379e981373e67124c9efe5c76213..f01edffa3c0babf76593fbd211f6606ff2036c74 100755 (executable)
@@ -8,6 +8,7 @@ LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-5570}
 start_git_daemon
 
 test_expect_success 'setup repository' '
+       git config push.default matching &&
        echo content >file &&
        git add file &&
        git commit -m one
index 9e43731fe504c46737c5294e4d508b5c20bbb663..a45c31692e17218988e6619742c366043e426039 100755 (executable)
@@ -58,4 +58,14 @@ test_expect_success 'ridiculously long subject in boundary' '
        grep "^-[0-9a-f]\\{40\\} " boundary
 '
 
+test_expect_success 'prerequisites with an empty commit message' '
+       : >file1 &&
+       git add file1 &&
+       test_tick &&
+       git commit --allow-empty-message -m "" &&
+       test_commit file2 &&
+       git bundle create bundle HEAD^.. &&
+       git bundle verify bundle
+'
+
 test_done
index f73eceabfbcd0a763b8908a8d829894a5e36055b..e7e945db098de4ca491bea5dac36df34bcb13ebd 100755 (executable)
@@ -175,6 +175,24 @@ test_expect_success 'merge.log=5 shows all 5 commits' '
        test_cmp expected actual
 '
 
+test_expect_success '--log=5 with custom comment character' '
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}
+
+       / By Another Author (3) and A U Thor (2)
+       / Via Another Committer
+       * left:
+         Left #5
+         Left #4
+         Left #3
+         Common #2
+         Common #1
+       EOF
+
+       git -c core.commentchar="/" fmt-merge-msg --log=5 <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'merge.log=0 disables shortlog' '
        echo "Merge branch ${apos}left${apos}" >expected
        git -c merge.log=0 fmt-merge-msg <.git/FETCH_HEAD >actual &&
index 2a0cfaac329c6c0974f205c81c45a0304a04897d..a4ffea0dbe743a1bbe4c5512037a6b1ffde1c313 100755 (executable)
@@ -596,14 +596,14 @@ test_expect_success 'submodule add places git-dir in superprojects git-dir recur
           git log > ../../../expected
          ) &&
          git commit -m "added subsubmodule" &&
-         git push
+         git push origin :
         ) &&
         (cd .git/modules/deeper/submodule/modules/subsubmodule &&
          git log > ../../../../../actual
         ) &&
         git add deeper/submodule &&
         git commit -m "update submodule" &&
-        git push &&
+        git push origin : &&
         test_cmp actual expected
        )
 '
index 9502f2438aeeb47c0881b80326a4152002fc23ad..043138631b8ba7fa21899e634383925feb6737e8 100755 (executable)
@@ -36,6 +36,7 @@ export CVSROOT CVS_SERVER
 
 rm -rf "$CVSWORK" "$SERVERDIR"
 test_expect_success 'setup' '
+  git config push.default matching &&
   echo >empty &&
   git add empty &&
   git commit -q -m "First Commit" &&
index 1c5bc84fa72492a820638e4e197b89ba13bc27f1..8c3db763013ff736e173a405de70f68cbdddaf9f 100755 (executable)
@@ -84,6 +84,7 @@ export CVSROOT CVS_SERVER
 
 rm -rf "$CVSWORK" "$SERVERDIR"
 test_expect_success 'setup' '
+    git config push.default matching &&
     echo "Simple text file" >textfile.c &&
     echo "File with embedded NUL: Q <- there" | q_to_nul > binfile.bin &&
     mkdir subdir &&
index 0d4e366232c29bdde0e1059d431ae96afc259112..1140767b50706f03b0da5b2c8befdae6898e4136 100755 (executable)
@@ -45,7 +45,8 @@ BEGIN
 # Failure cases for config:
 # Save and restore STDERR; we will probably extract this into a
 # "dies_ok" method and possibly move the STDERR handling to Git.pm.
-open our $tmpstderr, ">&STDERR" or die "cannot save STDERR"; close STDERR;
+open our $tmpstderr, ">&STDERR" or die "cannot save STDERR";
+open STDERR, ">", "/dev/null" or die "cannot redirect STDERR to /dev/null";
 is($r->config("test.dupstring"), "value2", "config: multivar");
 eval { $r->config_bool("test.boolother") };
 ok($@, "config_bool: non-boolean values fail");
index adc1372b3c334b4305761f6c3922493c85a0822a..6d9d1418a041bd8b3dddb598ac5479c999047a06 100755 (executable)
@@ -69,6 +69,7 @@ run_completion ()
        local -a COMPREPLY _words
        local _cword
        _words=( $1 )
+       test "${1: -1}" == ' ' && _words+=('')
        (( _cword = ${#_words[@]} - 1 ))
        __git_wrap__git_main && print_comp
 }
@@ -104,6 +105,23 @@ test_gitcomp ()
        test_cmp expected out
 }
 
+# Test __gitcomp_nl
+# Arguments are:
+# 1: current word (cur)
+# -: the rest are passed to __gitcomp_nl
+test_gitcomp_nl ()
+{
+       local -a COMPREPLY &&
+       sed -e 's/Z$//' >expected &&
+       cur="$1" &&
+       shift &&
+       __gitcomp_nl "$@" &&
+       print_comp &&
+       test_cmp expected out
+}
+
+invalid_variable_name='${foo.bar}'
+
 test_expect_success '__gitcomp - trailing space - options' '
        test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message=
                --reset-author" <<-EOF
@@ -147,8 +165,51 @@ test_expect_success '__gitcomp - suffix' '
        EOF
 '
 
+test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
+       __gitcomp "$invalid_variable_name"
+'
+
+read -r -d "" refs <<-\EOF
+maint
+master
+next
+pu
+EOF
+
+test_expect_success '__gitcomp_nl - trailing space' '
+       test_gitcomp_nl "m" "$refs" <<-EOF
+       maint Z
+       master Z
+       EOF
+'
+
+test_expect_success '__gitcomp_nl - prefix' '
+       test_gitcomp_nl "--fixup=m" "$refs" "--fixup=" "m" <<-EOF
+       --fixup=maint Z
+       --fixup=master Z
+       EOF
+'
+
+test_expect_success '__gitcomp_nl - suffix' '
+       test_gitcomp_nl "branch.ma" "$refs" "branch." "ma" "." <<-\EOF
+       branch.maint.Z
+       branch.master.Z
+       EOF
+'
+
+test_expect_success '__gitcomp_nl - no suffix' '
+       test_gitcomp_nl "ma" "$refs" "" "ma" "" <<-\EOF
+       maintZ
+       masterZ
+       EOF
+'
+
+test_expect_success '__gitcomp_nl - doesnt fail because of invalid variable name' '
+       __gitcomp_nl "$invalid_variable_name"
+'
+
 test_expect_success 'basic' '
-       run_completion "git \"\"" &&
+       run_completion "git " &&
        # built-in
        grep -q "^add \$" out &&
        # script
@@ -271,7 +332,7 @@ test_expect_success 'complete tree filename with spaces' '
        EOF
 '
 
-test_expect_failure 'complete tree filename with metacharacters' '
+test_expect_success 'complete tree filename with metacharacters' '
        echo content >"name with \${meta}" &&
        git add . &&
        git commit -m meta &&
index 2101d914f2d34d28b05ce17982a24c0b008809af..e147a8d277af7948cf165ea93899e9020a961d7a 100755 (executable)
@@ -59,7 +59,7 @@ test_expect_success 'gitdir - .git directory in cwd' '
 '
 
 test_expect_success 'gitdir - .git directory in parent' '
-       echo "$TRASH_DIRECTORY/.git" > expected &&
+       echo "$(pwd -P)/.git" > expected &&
        (
                cd subdir/subsubdir &&
                __gitdir > "$actual"
@@ -77,7 +77,7 @@ test_expect_success 'gitdir - cwd is a .git directory' '
 '
 
 test_expect_success 'gitdir - parent is a .git directory' '
-       echo "$TRASH_DIRECTORY/.git" > expected &&
+       echo "$(pwd -P)/.git" > expected &&
        (
                cd .git/refs/heads &&
                __gitdir > "$actual"
@@ -115,7 +115,7 @@ test_expect_success 'gitdir - non-existing $GIT_DIR' '
 '
 
 test_expect_success 'gitdir - gitfile in cwd' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
+       echo "$(pwd -P)/otherrepo/.git" > expected &&
        echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
        test_when_finished "rm -f subdir/.git" &&
        (
@@ -126,7 +126,7 @@ test_expect_success 'gitdir - gitfile in cwd' '
 '
 
 test_expect_success 'gitdir - gitfile in parent' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
+       echo "$(pwd -P)/otherrepo/.git" > expected &&
        echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
        test_when_finished "rm -f subdir/.git" &&
        (
@@ -137,7 +137,7 @@ test_expect_success 'gitdir - gitfile in parent' '
 '
 
 test_expect_success SYMLINKS 'gitdir - resulting path avoids symlinks' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
+       echo "$(pwd -P)/otherrepo/.git" > expected &&
        mkdir otherrepo/dir &&
        test_when_finished "rm -rf otherrepo/dir" &&
        ln -s otherrepo/dir link &&
index debd8b475186591ec1d1ef38848fef1a213889f4..ca6bdef63d2ee9389729e5118f22461c887dc5ed 100644 (file)
@@ -600,14 +600,14 @@ then
 fi
 
 # Test repository
-test="trash directory.$(basename "$0" .sh)"
-test -n "$root" && test="$root/$test"
-case "$test" in
-/*) TRASH_DIRECTORY="$test" ;;
- *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$test" ;;
+TRASH_DIRECTORY="trash directory.$(basename "$0" .sh)"
+test -n "$root" && TRASH_DIRECTORY="$root/$TRASH_DIRECTORY"
+case "$TRASH_DIRECTORY" in
+/*) ;; # absolute path is good
+ *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;;
 esac
 test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
-rm -fr "$test" || {
+rm -fr "$TRASH_DIRECTORY" || {
        GIT_EXIT_OK=t
        echo >&5 "FATAL: Cannot prepare test area"
        exit 1
@@ -618,13 +618,13 @@ export HOME
 
 if test -z "$TEST_NO_CREATE_REPO"
 then
-       test_create_repo "$test"
+       test_create_repo "$TRASH_DIRECTORY"
 else
-       mkdir -p "$test"
+       mkdir -p "$TRASH_DIRECTORY"
 fi
 # Use -P to resolve symlinks in our working directory so that the cwd
 # in subprocesses like git equals our $PWD (for pathname comparisons).
-cd -P "$test" || exit 1
+cd -P "$TRASH_DIRECTORY" || exit 1
 
 this_test=${0##*/}
 this_test=${this_test%%-*}