Code

Merge branch 'mg/doc-rev-parse-treepath-syntax' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 7 Jul 2010 17:34:04 +0000 (10:34 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 7 Jul 2010 17:34:04 +0000 (10:34 -0700)
* mg/doc-rev-parse-treepath-syntax:
  git-rev-parse.txt: Add more examples for caret and colon
  git-rev-parse.txt: Document ":path" specifier

26 files changed:
Documentation/RelNotes-1.7.1.1.txt
Documentation/git-gc.txt
Documentation/git-grep.txt
Documentation/git-rerere.txt
Documentation/git-send-email.txt
GIT-VERSION-GEN
block-sha1/sha1.c
builtin.h
builtin/notes.c
builtin/receive-pack.c
dir.c
git-am.sh
git-send-email.perl
notes.c
notes.h
perl/Git.pm
refs.c
t/t0006-date.sh
t/t3301-notes.sh
t/t3400-rebase.sh
t/t5516-fetch-push.sh
t/t7405-submodule-merge.sh
t/t9001-send-email.sh
test-date.c
unpack-trees.c
xdiff/xutils.c

index bfdb5ba0641feabea52b1a4a334e956eb5d97be6..3f6b3148a3091f6b6d635ff5778ad6a92087a0f7 100644 (file)
@@ -1,5 +1,5 @@
-Git v1.7.1.1 Release Notes (draft)
-==================================
+Git v1.7.1.1 Release Notes
+==========================
 
 Fixes since v1.7.1
 ------------------
@@ -17,11 +17,18 @@ Fixes since v1.7.1
 
  * We didn't recognize timezone "Z" as a synonym for "UTC" (75b37e70).
 
+ * In 1.7.0, read-tree and user commands that use the mechanism such as
+   checkout and merge were fixed to handle switching between branches one
+   of which has a file while the other has a directory at the same path
+   correctly even when there are some "confusing" pathnames in them.  But
+   the algorithm used for this fix was suboptimal and had a terrible
+   performance degradation especially in larger trees.
+
  * "git am -3" did not show diagnosis when the patch in the message was corrupt.
 
  * After "git apply --whitespace=fix" removed trailing blank lines in an
    patch in a patch series, it failed to apply later patches that depend
-   on the presense of such blank lines.
+   on the presence of such blank lines.
 
  * "git bundle --stdin" segfaulted.
 
@@ -57,10 +64,15 @@ Fixes since v1.7.1
  * "git merge --log" used to replace the custom message given by "-m" with
    the shortlog, instead of appending to it.
 
+ * "git notes copy" without any other argument segfaulted.
+
  * "git pull" accepted "--dry-run", gave it to underlying "git fetch" but
    ignored the option itself, resulting in a bogus attempt to merge
    unrelated commit.
 
+ * "git rebase" did not faithfully reproduce a malformed author ident, that
+   is often seen in a repository converted from foreign SCMs.
+
  * "git reset --hard" started from a wrong directory and a working tree in
    a nonstandard location is in use got confused.
 
@@ -68,6 +80,9 @@ Fixes since v1.7.1
    EHLO/HELO exchange, causing rejected connection from picky servers.
    It learned --smtp-domain option to solve this issue.
 
+ * "git send-email" did not declare a content-transfer-encoding and
+   content-type even when its payload needs to be sent in 8-bit.
+
  * "git show -C -C" and other corner cases lost diff metainfo output
    in 1.7.0.
 
@@ -79,10 +94,3 @@ Fixes since v1.7.1
  * "git status" showed excess "hints" even when advice.statusHints is set to false.
 
 And other minor fixes and documentation updates.
-
-
---
-exec >/var/tmp/1
-O=v1.7.1-195-gb2ebbd8
-echo O=$(git describe HEAD)
-git shortlog --no-merges HEAD ^$O
index 189573a3b3d459822b465d39db2f90001ffc98d3..cbe74d531af500c0e47e158830474907556a15db 100644 (file)
@@ -127,6 +127,13 @@ If you are expecting some objects to be collected and they aren't, check
 all of those locations and decide whether it makes sense in your case to
 remove those references.
 
+HOOKS
+-----
+
+The 'git gc --auto' command will run the 'pre-auto-gc' hook.  See
+linkgit:githooks[5] for more information.
+
+
 SEE ALSO
 --------
 linkgit:git-prune[1]
index 4b32322a67d9f3de8b555d3d9d03e7ddab14e0a7..912bddd7b67bb486a703814f116a73fa047e667c 100644 (file)
@@ -183,7 +183,7 @@ OPTIONS
 Examples
 --------
 
-git grep 'time_t' -- '*.[ch]'::
+git grep 'time_t' \-- '*.[ch]'::
        Looks for `time_t` in all tracked .c and .h files in the working
        directory and its subdirectories.
 
index acc220a00f0013d115366cfc8b7a48aeeef47711..db99d4786e06df1cd2c9352c877c45efcbfb10cc 100644 (file)
@@ -7,7 +7,7 @@ git-rerere - Reuse recorded resolution of conflicted merges
 
 SYNOPSIS
 --------
-'git rerere' ['clear'|'diff'|'status'|'gc']
+'git rerere' ['clear'|'forget' [<pathspec>]|'diff'|'status'|'gc']
 
 DESCRIPTION
 -----------
@@ -40,6 +40,11 @@ This resets the metadata used by rerere if a merge resolution is to be
 aborted.  Calling 'git am [--skip|--abort]' or 'git rebase [--skip|--abort]'
 will automatically invoke this command.
 
+'forget' <pathspec>::
+
+This resets the conflict resolutions which rerere has recorded for the current
+conflict in <pathspec>.  The <pathspec> is optional.
+
 'diff'::
 
 This displays diffs for the current state of the resolution.  It is
index 12622fc49a0825fa8423c46ddddc06ff89f292d4..c283084272090c28d5529d200a1147c9f57c639b 100644 (file)
@@ -101,6 +101,15 @@ See the CONFIGURATION section for 'sendemail.multiedit'.
 +
 The --to option must be repeated for each user you want on the to list.
 
+--8bit-encoding=<encoding>::
+       When encountering a non-ASCII message or subject that does not
+       declare its encoding, add headers/quoting to indicate it is
+       encoded in <encoding>.  Default is the value of the
+       'sendemail.assume8bitEncoding'; if that is unspecified, this
+       will be prompted for if any non-ASCII files are encountered.
++
+Note that no attempts whatsoever are made to validate the encoding.
+
 
 Sending
 ~~~~~~~
index 82fd72652b6d35837b8786005024263ac61630dd..d4cb58e5df10d560cf7fe8680ca81ec217036b23 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.1
+DEF_VER=v1.7.1.1
 
 LF='
 '
index d8934757a5e5e259f26c4a09f7ea5d10615df0c1..d880a5336ecbee175162a86a3336a783c79b0eb0 100644 (file)
@@ -236,13 +236,13 @@ void blk_SHA1_Init(blk_SHA_CTX *ctx)
 
 void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
 {
-       int lenW = ctx->size & 63;
+       unsigned int lenW = ctx->size & 63;
 
        ctx->size += len;
 
        /* Read the data into W and process blocks as they get full */
        if (lenW) {
-               int left = 64 - lenW;
+               unsigned int left = 64 - lenW;
                if (len < left)
                        left = len;
                memcpy(lenW + (char *)ctx->W, data, left);
@@ -269,8 +269,8 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
        int i;
 
        /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
-       padlen[0] = htonl(ctx->size >> 29);
-       padlen[1] = htonl(ctx->size << 3);
+       padlen[0] = htonl((uint32_t)(ctx->size >> 29));
+       padlen[1] = htonl((uint32_t)(ctx->size << 3));
 
        i = ctx->size & 63;
        blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));
index 322901f339a47486854d90048cbec198db8fce56..fdae0279ef3e4ba4d6b03f651d19e772a239ddc0 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -26,13 +26,13 @@ struct notes_rewrite_cfg {
        struct notes_tree **trees;
        const char *cmd;
        int enabled;
-       combine_notes_fn *combine;
+       combine_notes_fn combine;
        struct string_list *refs;
        int refs_from_env;
        int mode_from_env;
 };
 
-combine_notes_fn *parse_combine_notes_fn(const char *v);
+combine_notes_fn parse_combine_notes_fn(const char *v);
 struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
 int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
                          const unsigned char *from_obj, const unsigned char *to_obj);
index 26617546c8cadac493f06473d02f27f320713b28..f678f9cb520fa331de4899e372502cffccd9682c 100644 (file)
@@ -313,7 +313,7 @@ int commit_notes(struct notes_tree *t, const char *msg)
        return 0;
 }
 
-combine_notes_fn *parse_combine_notes_fn(const char *v)
+combine_notes_fn parse_combine_notes_fn(const char *v)
 {
        if (!strcasecmp(v, "overwrite"))
                return combine_notes_overwrite;
@@ -614,6 +614,10 @@ static int copy(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (argc < 2) {
+               error("too few parameters");
+               usage_with_options(git_notes_copy_usage, options);
+       }
        if (2 < argc) {
                error("too many parameters");
                usage_with_options(git_notes_copy_usage, options);
index 0559fcc871894548050e99c8c258561a0dcad5c9..7e4129ddc85e1408300947ff9c041524a1c10f48 100644 (file)
@@ -9,6 +9,7 @@
 #include "object.h"
 #include "remote.h"
 #include "transport.h"
+#include "string-list.h"
 
 static const char receive_pack_usage[] = "git receive-pack <git-dir>";
 
@@ -129,13 +130,12 @@ static void write_head_info(void)
 struct command {
        struct command *next;
        const char *error_string;
+       unsigned int skip_update;
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
        char ref_name[FLEX_ARRAY]; /* more */
 };
 
-static struct command *commands;
-
 static const char pre_receive_hook[] = "hooks/pre-receive";
 static const char post_receive_hook[] = "hooks/post-receive";
 
@@ -188,7 +188,7 @@ static int copy_to_sideband(int in, int out, void *arg)
        return 0;
 }
 
-static int run_receive_hook(const char *hook_name)
+static int run_receive_hook(struct command *commands, const char *hook_name)
 {
        static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
        struct command *cmd;
@@ -447,15 +447,15 @@ static const char *update(struct command *cmd)
 
 static char update_post_hook[] = "hooks/post-update";
 
-static void run_update_post_hook(struct command *cmd)
+static void run_update_post_hook(struct command *commands)
 {
-       struct command *cmd_p;
+       struct command *cmd;
        int argc;
        const char **argv;
        struct child_process proc;
 
-       for (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
-               if (cmd_p->error_string)
+       for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
+               if (cmd->error_string)
                        continue;
                argc++;
        }
@@ -464,12 +464,12 @@ static void run_update_post_hook(struct command *cmd)
        argv = xmalloc(sizeof(*argv) * (2 + argc));
        argv[0] = update_post_hook;
 
-       for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
+       for (argc = 1, cmd = commands; cmd; cmd = cmd->next) {
                char *p;
-               if (cmd_p->error_string)
+               if (cmd->error_string)
                        continue;
-               p = xmalloc(strlen(cmd_p->ref_name) + 1);
-               strcpy(p, cmd_p->ref_name);
+               p = xmalloc(strlen(cmd->ref_name) + 1);
+               strcpy(p, cmd->ref_name);
                argv[argc] = p;
                argc++;
        }
@@ -488,37 +488,92 @@ static void run_update_post_hook(struct command *cmd)
        }
 }
 
-static void execute_commands(const char *unpacker_error)
+static void check_aliased_update(struct command *cmd, struct string_list *list)
+{
+       struct string_list_item *item;
+       struct command *dst_cmd;
+       unsigned char sha1[20];
+       char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
+       int flag;
+
+       const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);
+
+       if (!(flag & REF_ISSYMREF))
+               return;
+
+       if ((item = string_list_lookup(dst_name, list)) == NULL)
+               return;
+
+       cmd->skip_update = 1;
+
+       dst_cmd = (struct command *) item->util;
+
+       if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&
+           !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))
+               return;
+
+       dst_cmd->skip_update = 1;
+
+       strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));
+       strcpy(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
+       strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));
+       strcpy(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
+       rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
+                " its target '%s' (%s..%s)",
+                cmd->ref_name, cmd_oldh, cmd_newh,
+                dst_cmd->ref_name, dst_oldh, dst_newh);
+
+       cmd->error_string = dst_cmd->error_string =
+               "inconsistent aliased update";
+}
+
+static void check_aliased_updates(struct command *commands)
+{
+       struct command *cmd;
+       struct string_list ref_list = { NULL, 0, 0, 0 };
+
+       for (cmd = commands; cmd; cmd = cmd->next) {
+               struct string_list_item *item =
+                       string_list_append(cmd->ref_name, &ref_list);
+               item->util = (void *)cmd;
+       }
+       sort_string_list(&ref_list);
+
+       for (cmd = commands; cmd; cmd = cmd->next)
+               check_aliased_update(cmd, &ref_list);
+
+       string_list_clear(&ref_list, 0);
+}
+
+static void execute_commands(struct command *commands, const char *unpacker_error)
 {
-       struct command *cmd = commands;
+       struct command *cmd;
        unsigned char sha1[20];
 
        if (unpacker_error) {
-               while (cmd) {
+               for (cmd = commands; cmd; cmd = cmd->next)
                        cmd->error_string = "n/a (unpacker error)";
-                       cmd = cmd->next;
-               }
                return;
        }
 
-       if (run_receive_hook(pre_receive_hook)) {
-               while (cmd) {
+       if (run_receive_hook(commands, pre_receive_hook)) {
+               for (cmd = commands; cmd; cmd = cmd->next)
                        cmd->error_string = "pre-receive hook declined";
-                       cmd = cmd->next;
-               }
                return;
        }
 
+       check_aliased_updates(commands);
+
        head_name = resolve_ref("HEAD", sha1, 0, NULL);
 
-       while (cmd) {
-               cmd->error_string = update(cmd);
-               cmd = cmd->next;
-       }
+       for (cmd = commands; cmd; cmd = cmd->next)
+               if (!cmd->skip_update)
+                       cmd->error_string = update(cmd);
 }
 
-static void read_head_info(void)
+static struct command *read_head_info(void)
 {
+       struct command *commands = NULL;
        struct command **p = &commands;
        for (;;) {
                static char line[1000];
@@ -548,15 +603,14 @@ static void read_head_info(void)
                        if (strstr(refname + reflen + 1, "side-band-64k"))
                                use_sideband = LARGE_PACKET_MAX;
                }
-               cmd = xmalloc(sizeof(struct command) + len - 80);
+               cmd = xcalloc(1, sizeof(struct command) + len - 80);
                hashcpy(cmd->old_sha1, old_sha1);
                hashcpy(cmd->new_sha1, new_sha1);
                memcpy(cmd->ref_name, line + 82, len - 81);
-               cmd->error_string = NULL;
-               cmd->next = NULL;
                *p = cmd;
                p = &cmd->next;
        }
+       return commands;
 }
 
 static const char *parse_pack_header(struct pack_header *hdr)
@@ -643,7 +697,7 @@ static const char *unpack(void)
        }
 }
 
-static void report(const char *unpack_status)
+static void report(struct command *commands, const char *unpack_status)
 {
        struct command *cmd;
        struct strbuf buf = STRBUF_INIT;
@@ -667,12 +721,12 @@ static void report(const char *unpack_status)
        strbuf_release(&buf);
 }
 
-static int delete_only(struct command *cmd)
+static int delete_only(struct command *commands)
 {
-       while (cmd) {
+       struct command *cmd;
+       for (cmd = commands; cmd; cmd = cmd->next) {
                if (!is_null_sha1(cmd->new_sha1))
                        return 0;
-               cmd = cmd->next;
        }
        return 1;
 }
@@ -722,6 +776,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        int stateless_rpc = 0;
        int i;
        char *dir = NULL;
+       struct command *commands;
 
        argv++;
        for (i = 1; i < argc; i++) {
@@ -772,18 +827,17 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        if (advertise_refs)
                return 0;
 
-       read_head_info();
-       if (commands) {
+       if ((commands = read_head_info()) != NULL) {
                const char *unpack_status = NULL;
 
                if (!delete_only(commands))
                        unpack_status = unpack();
-               execute_commands(unpack_status);
+               execute_commands(commands, unpack_status);
                if (pack_lockfile)
                        unlink_or_warn(pack_lockfile);
                if (report_status)
-                       report(unpack_status);
-               run_receive_hook(post_receive_hook);
+                       report(commands, unpack_status);
+               run_receive_hook(commands, post_receive_hook);
                run_update_post_hook(commands);
                if (auto_gc) {
                        const char *argv_gc_auto[] = {
diff --git a/dir.c b/dir.c
index 5615f33af187f381f8c2dfe7ab53910fe165fd59..7f912c76f268e2f27857a67d565e77be0e43660d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -31,22 +31,22 @@ static int common_prefix(const char **pathspec)
        if (!slash)
                return 0;
 
+       /*
+        * The first 'prefix' characters of 'path' are common leading
+        * path components among the pathspecs we have seen so far,
+        * including the trailing slash.
+        */
        prefix = slash - path + 1;
        while ((next = *++pathspec) != NULL) {
-               int len = strlen(next);
-               if (len >= prefix && !memcmp(path, next, prefix))
+               int len, last_matching_slash = -1;
+               for (len = 0; len < prefix && next[len] == path[len]; len++)
+                       if (next[len] == '/')
+                               last_matching_slash = len;
+               if (len == prefix)
                        continue;
-               len = prefix - 1;
-               for (;;) {
-                       if (!len)
-                               return 0;
-                       if (next[--len] != '/')
-                               continue;
-                       if (memcmp(path, next, len+1))
-                               continue;
-                       prefix = len + 1;
-                       break;
-               }
+               if (last_matching_slash < 0)
+                       return 0;
+               prefix = last_matching_slash + 1;
        }
        return prefix;
 }
index 87ffae252b3f2ff88646d142ea9c2dfb38a28953..1df5b04146a118831f4b8d30652aa506c7e4b02e 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -593,7 +593,7 @@ do
                        echo "Patch is empty.  Was it split wrong?"
                        stop_here $this
                }
-               rm -f "$dotest/original-commit"
+               rm -f "$dotest/original-commit" "$dotest/author-script"
                if test -f "$dotest/rebasing" &&
                        commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
                                -e q "$dotest/$msgnum") &&
@@ -602,6 +602,7 @@ do
                        git cat-file commit "$commit" |
                        sed -e '1,/^$/d' >"$dotest/msg-clean"
                        echo "$commit" > "$dotest/original-commit"
+                       get_author_ident_from_commit "$commit" > "$dotest/author-script"
                else
                        {
                                sed -n '/^Subject/ s/Subject: //p' "$dotest/info"
@@ -613,9 +614,14 @@ do
                ;;
        esac
 
-       GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
-       GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
-       GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
+       if test -f "$dotest/author-script"
+       then
+               eval $(cat "$dotest/author-script")
+       else
+               GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
+               GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
+               GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
+       fi
 
        if test -z "$GIT_AUTHOR_EMAIL"
        then
index 111c981229bf2c0bc6afa4a22db011b68d93fdfa..6dab3bf6a74bb52c7294e33dbac5bf87b51a9fdf 100755 (executable)
@@ -54,6 +54,7 @@ git send-email [options] <file | directory | rev-list options >
     --in-reply-to           <str>  * Email "In-Reply-To:"
     --annotate                     * Review each patch that will be sent in an editor.
     --compose                      * Open an editor for introduction.
+    --8bit-encoding         <str>  * Encoding to assume 8bit mails if undeclared
 
   Sending:
     --envelope-sender       <str>  * Email envelope sender.
@@ -191,6 +192,7 @@ my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
 my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts, $smtp_domain);
 my ($validate, $confirm);
 my (@suppress_cc);
+my ($auto_8bit_encoding);
 
 my ($debug_net_smtp) = 0;              # Net::SMTP, see send_message()
 
@@ -222,6 +224,7 @@ my %config_settings = (
     "multiedit" => \$multiedit,
     "confirm"   => \$confirm,
     "from" => \$sender,
+    "assume8bitencoding" => \$auto_8bit_encoding,
 );
 
 # Help users prepare for 1.7.0
@@ -297,6 +300,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
                    "thread!" => \$thread,
                    "validate!" => \$validate,
                    "format-patch!" => \$format_patch,
+                   "8bit-encoding=s" => \$auto_8bit_encoding,
         );
 
 unless ($rc) {
@@ -669,6 +673,35 @@ sub ask {
        return undef;
 }
 
+my %broken_encoding;
+
+sub file_declares_8bit_cte($) {
+       my $fn = shift;
+       open (my $fh, '<', $fn);
+       while (my $line = <$fh>) {
+               last if ($line =~ /^$/);
+               return 1 if ($line =~ /^Content-Transfer-Encoding: .*8bit.*$/);
+       }
+       close $fh;
+       return 0;
+}
+
+foreach my $f (@files) {
+       next unless (body_or_subject_has_nonascii($f)
+                    && !file_declares_8bit_cte($f));
+       $broken_encoding{$f} = 1;
+}
+
+if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
+       print "The following files are 8bit, but do not declare " .
+               "a Content-Transfer-Encoding.\n";
+       foreach my $f (sort keys %broken_encoding) {
+               print "    $f\n";
+       }
+       $auto_8bit_encoding = ask("Which 8bit encoding should I declare [UTF-8]? ",
+                                 default => "UTF-8");
+}
+
 my $prompting = 0;
 if (!defined $sender) {
        $sender = $repoauthor || $repocommitter || '';
@@ -1221,6 +1254,18 @@ foreach my $t (@files) {
                        or die "(cc-cmd) failed to close pipe to '$cc_cmd'";
        }
 
+       if ($broken_encoding{$t} && !$has_content_type) {
+               $has_content_type = 1;
+               push @xh, "MIME-Version: 1.0",
+                       "Content-Type: text/plain; charset=$auto_8bit_encoding",
+                       "Content-Transfer-Encoding: 8bit";
+               $body_encoding = $auto_8bit_encoding;
+       }
+
+       if ($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {
+               $subject = quote_rfc2047($subject, $auto_8bit_encoding);
+       }
+
        if (defined $author and $author ne $sender) {
                $message = "From: $author\n\n$message";
                if (defined $author_encoding) {
@@ -1233,6 +1278,7 @@ foreach my $t (@files) {
                                }
                        }
                        else {
+                               $has_content_type = 1;
                                push @xh,
                                  'MIME-Version: 1.0',
                                  "Content-Type: text/plain; charset=$author_encoding",
@@ -1310,3 +1356,17 @@ sub file_has_nonascii {
        }
        return 0;
 }
+
+sub body_or_subject_has_nonascii {
+       my $fn = shift;
+       open(my $fh, '<', $fn)
+               or die "unable to open $fn: $!\n";
+       while (my $line = <$fh>) {
+               last if $line =~ /^$/;
+               return 1 if $line =~ /^Subject.*[^[:ascii:]]/;
+       }
+       while (my $line = <$fh>) {
+               return 1 if $line =~ /[^[:ascii:]]/;
+       }
+       return 0;
+}
diff --git a/notes.c b/notes.c
index e425e198278bfb5c6a039dc88825568f1518e875..cc92cf351a5d8abb7ad2890660d484644c359dfb 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -716,7 +716,7 @@ static int write_each_non_note_until(const char *note_path,
                struct write_each_note_data *d)
 {
        struct non_note *n = d->next_non_note;
-       int cmp, ret;
+       int cmp = 0, ret;
        while (n && (!note_path || (cmp = strcmp(n->path, note_path)) <= 0)) {
                if (note_path && cmp == 0)
                        ; /* do nothing, prefer note to non-note */
diff --git a/notes.h b/notes.h
index 9f59277c516f7ac78bcc66f91a7d7013b2a77f65..109bc8f78236d8067568e524ffaf4ae21ee6c027 100644 (file)
--- a/notes.h
+++ b/notes.h
@@ -18,7 +18,7 @@
  * combine_notes_concatenate(), which appends the contents of the new note to
  * the contents of the existing note.
  */
-typedef int combine_notes_fn(unsigned char *cur_sha1, const unsigned char *new_sha1);
+typedef int (*combine_notes_fn)(unsigned char *cur_sha1, const unsigned char *new_sha1);
 
 /* Common notes combinators */
 int combine_notes_concatenate(unsigned char *cur_sha1, const unsigned char *new_sha1);
@@ -38,7 +38,7 @@ extern struct notes_tree {
        struct int_node *root;
        struct non_note *first_non_note, *prev_non_note;
        char *ref;
-       combine_notes_fn *combine_notes;
+       combine_notes_fn combine_notes;
        int initialized;
        int dirty;
 } default_notes_tree;
index 1926dc9a4b929c025456227526a93424d4972948..6cb0dd19344ba1169b23b7e4949033f8a9088a00 100644 (file)
@@ -172,7 +172,7 @@ sub repository {
        }
 
        if (defined $opts{Directory}) {
-               -d $opts{Directory} or throw Error::Simple("Directory not found: $!");
+               -d $opts{Directory} or throw Error::Simple("Directory not found: $opts{Directory} $!");
 
                my $search = Git->repository(WorkingCopy => $opts{Directory});
                my $dir;
@@ -545,7 +545,7 @@ sub wc_chdir {
                or throw Error::Simple("bare repository");
 
        -d $self->wc_path().'/'.$subdir
-               or throw Error::Simple("subdir not found: $!");
+               or throw Error::Simple("subdir not found: $subdir $!");
        # Of course we will not "hold" the subdirectory so anyone
        # can delete it now and we will never know. But at least we tried.
 
diff --git a/refs.c b/refs.c
index d3db15a76cc46f6f6a31d4448816c09e6c48e543..d6307ae29c14d73654039bf8a92483302f4e3e15 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1086,6 +1086,15 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
        return ret;
 }
 
+/*
+ * People using contrib's git-new-workdir have .git/logs/refs ->
+ * /some/other/path/.git/logs/refs, and that may live on another device.
+ *
+ * IOW, to avoid cross device rename errors, the temporary renamed log must
+ * live into logs/refs.
+ */
+#define TMP_RENAMED_LOG  "logs/refs/.tmp-renamed-log"
+
 int rename_ref(const char *oldref, const char *newref, const char *logmsg)
 {
        static const char renamed_ref[] = "RENAMED-REF";
@@ -1119,8 +1128,8 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        if (write_ref_sha1(lock, orig_sha1, logmsg))
                return error("unable to save current sha1 in %s", renamed_ref);
 
-       if (log && rename(git_path("logs/%s", oldref), git_path("tmp-renamed-log")))
-               return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
+       if (log && rename(git_path("logs/%s", oldref), git_path(TMP_RENAMED_LOG)))
+               return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
                        oldref, strerror(errno));
 
        if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
@@ -1146,7 +1155,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        }
 
  retry:
-       if (log && rename(git_path("tmp-renamed-log"), git_path("logs/%s", newref))) {
+       if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newref))) {
                if (errno==EISDIR || errno==ENOTDIR) {
                        /*
                         * rename(a, b) when b is an existing
@@ -1159,7 +1168,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
                        }
                        goto retry;
                } else {
-                       error("unable to move logfile tmp-renamed-log to logs/%s: %s",
+                       error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
                                newref, strerror(errno));
                        goto rollback;
                }
@@ -1199,8 +1208,8 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
                error("unable to restore logfile %s from %s: %s",
                        oldref, newref, strerror(errno));
        if (!logmoved && log &&
-           rename(git_path("tmp-renamed-log"), git_path("logs/%s", oldref)))
-               error("unable to restore logfile %s from tmp-renamed-log: %s",
+           rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldref)))
+               error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s",
                        oldref, strerror(errno));
 
        return 1;
index 75b02af86d4d613fac91b3cec28044e3b7f6274e..3ea4f9eff923c963aeb9635be6d77dce99f3bf4a 100755 (executable)
@@ -28,8 +28,8 @@ check_show 31449600 '12 months ago'
 
 check_parse() {
        echo "$1 -> $2" >expect
-       test_expect_${3:-success} "parse date ($1)" "
-       test-date parse '$1' >actual &&
+       test_expect_${4:-success} "parse date ($1${3:+ TZ=$3})" "
+       TZ=${3:-$TZ} test-date parse '$1' >actual &&
        test_cmp expect actual
        "
 }
@@ -38,6 +38,7 @@ check_parse 2008 bad
 check_parse 2008-02 bad
 check_parse 2008-02-14 bad
 check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
+check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
 
 check_approxidate() {
        echo "$1 -> $2 +0000" >expect
index 64f32ad94dfe842746774596b88c91e873da864f..2d67a40fc16385b8bee37968f4058b094a174a98 100755 (executable)
@@ -1044,4 +1044,10 @@ test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '
        git log -1 > output &&
        test_cmp expect output
 '
+
+test_expect_success 'git notes copy diagnoses too many or too few parameters' '
+       test_must_fail git notes copy &&
+       test_must_fail git notes copy one two three
+'
+
 test_done
index dbf7dfba9b55e906d44a35d7b11ca2ceaad7e6f5..c41bcc7d635687f938312d4a6fbbf786738bca93 100755 (executable)
@@ -10,8 +10,9 @@ among other things.
 '
 . ./test-lib.sh
 
-GIT_AUTHOR_EMAIL=bogus_email_address
-export GIT_AUTHOR_EMAIL
+GIT_AUTHOR_NAME=author@name
+GIT_AUTHOR_EMAIL=bogus@email@address
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
 
 test_expect_success \
     'prepare repository with topic branches' \
@@ -80,6 +81,10 @@ test_expect_success \
     'the rebase operation should not have destroyed author information' \
     '! (git log | grep "Author:" | grep "<>")'
 
+test_expect_success \
+    'the rebase operation should not have destroyed author information (2)' \
+    "git log -1 | grep 'Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>'"
+
 test_expect_success 'HEAD was detached during rebase' '
      test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
 '
index 6a37a4d993df3fa4958a719fdfdb82f0dd2de623..b8f64e13865a0f5fe24d90e6864b58962e8d384c 100755 (executable)
@@ -660,6 +660,55 @@ test_expect_success 'push with branches containing #' '
        git checkout master
 '
 
+test_expect_success 'push into aliased refs (consistent)' '
+       mk_test heads/master &&
+       mk_child child1 &&
+       mk_child child2 &&
+       (
+               cd child1 &&
+               git branch foo &&
+               git symbolic-ref refs/heads/bar refs/heads/foo
+               git config receive.denyCurrentBranch false
+       ) &&
+       (
+               cd child2 &&
+               >path2 &&
+               git add path2 &&
+               test_tick &&
+               git commit -a -m child2 &&
+               git branch foo &&
+               git branch bar &&
+               git push ../child1 foo bar
+       )
+'
+
+test_expect_success 'push into aliased refs (inconsistent)' '
+       mk_test heads/master &&
+       mk_child child1 &&
+       mk_child child2 &&
+       (
+               cd child1 &&
+               git branch foo &&
+               git symbolic-ref refs/heads/bar refs/heads/foo
+               git config receive.denyCurrentBranch false
+       ) &&
+       (
+               cd child2 &&
+               >path2 &&
+               git add path2 &&
+               test_tick &&
+               git commit -a -m child2 &&
+               git branch foo &&
+               >path3 &&
+               git add path3 &&
+               test_tick &&
+               git commit -a -m child2 &&
+               git branch bar &&
+               test_must_fail git push ../child1 foo bar 2>stderr &&
+               grep "refusing inconsistent update" stderr
+       )
+'
+
 test_expect_success 'push --porcelain' '
        mk_empty &&
        echo >.git/foo  "To testrepo" &&
index 9a21f783d3a5fedaa56df26919ab8e0456872e90..4a7b8933f4bf1036ba1e044d6e2dc54d72084039 100755 (executable)
@@ -45,7 +45,7 @@ test_expect_success setup '
         git commit -m sub-b) &&
        git add sub &&
        test_tick &&
-       git commit -m b
+       git commit -m b &&
 
        git checkout -b c a &&
        git merge -s ours b &&
index 640b3d2bb41636e271d0d55aabeade38ded612bb..382ab6c98a932925db838b29d452b36cfea0dba5 100755 (executable)
@@ -918,4 +918,81 @@ test_expect_success '--no-bcc overrides sendemail.bcc' '
        ! grep "RCPT TO:<other@ex.com>" stdout
 '
 
+cat >email-using-8bit <<EOF
+From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+Message-Id: <bogus-message-id@example.com>
+From: author@example.com
+Date: Sat, 12 Jun 2010 15:53:58 +0200
+Subject: subject goes here
+
+Dieser deutsche Text enthält einen Umlaut!
+EOF
+
+cat >content-type-decl <<EOF
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+EOF
+
+test_expect_success 'asks about and fixes 8bit encodings' '
+       clean_fake_sendmail &&
+       echo |
+       git send-email --from=author@example.com --to=nobody@example.com \
+                       --smtp-server="$(pwd)/fake.sendmail" \
+                       email-using-8bit >stdout &&
+       grep "do not declare a Content-Transfer-Encoding" stdout &&
+       grep email-using-8bit stdout &&
+       grep "Which 8bit encoding" stdout &&
+       egrep "Content|MIME" msgtxt1 >actual &&
+       test_cmp actual content-type-decl
+'
+
+test_expect_success 'sendemail.8bitEncoding works' '
+       clean_fake_sendmail &&
+       git config sendemail.assume8bitEncoding UTF-8 &&
+       echo bogus |
+       git send-email --from=author@example.com --to=nobody@example.com \
+                       --smtp-server="$(pwd)/fake.sendmail" \
+                       email-using-8bit >stdout &&
+       egrep "Content|MIME" msgtxt1 >actual &&
+       test_cmp actual content-type-decl
+'
+
+test_expect_success '--8bit-encoding overrides sendemail.8bitEncoding' '
+       clean_fake_sendmail &&
+       git config sendemail.assume8bitEncoding "bogus too" &&
+       echo bogus |
+       git send-email --from=author@example.com --to=nobody@example.com \
+                       --smtp-server="$(pwd)/fake.sendmail" \
+                       --8bit-encoding=UTF-8 \
+                       email-using-8bit >stdout &&
+       egrep "Content|MIME" msgtxt1 >actual &&
+       test_cmp actual content-type-decl
+'
+
+cat >email-using-8bit <<EOF
+From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+Message-Id: <bogus-message-id@example.com>
+From: author@example.com
+Date: Sat, 12 Jun 2010 15:53:58 +0200
+Subject: Dieser Betreff enthält auch einen Umlaut!
+
+Nothing to see here.
+EOF
+
+cat >expected <<EOF
+Subject: =?UTF-8?q?Dieser=20Betreff=20enth=C3=A4lt=20auch=20einen=20Umlaut!?=
+EOF
+
+test_expect_success '--8bit-encoding also treats subject' '
+       clean_fake_sendmail &&
+       echo bogus |
+       git send-email --from=author@example.com --to=nobody@example.com \
+                       --smtp-server="$(pwd)/fake.sendmail" \
+                       --8bit-encoding=UTF-8 \
+                       email-using-8bit >stdout &&
+       grep "Subject" msgtxt1 >actual &&
+       test_cmp expected actual
+'
+
 test_done
index a9e705f79a148790f4de55c7918dc9c669382a4f..6bcd5b03c078bc532e074ef8e8775a51d0eef219 100644 (file)
@@ -20,13 +20,16 @@ static void parse_dates(char **argv, struct timeval *now)
 {
        for (; *argv; argv++) {
                char result[100];
-               time_t t;
+               unsigned long t;
+               int tz;
 
                result[0] = 0;
                parse_date(*argv, result, sizeof(result));
-               t = strtoul(result, NULL, 0);
-               printf("%s -> %s\n", *argv,
-                       t ? show_date(t, 0, DATE_ISO8601) : "bad");
+               if (sscanf(result, "%lu %d", &t, &tz) == 2)
+                       printf("%s -> %s\n",
+                              *argv, show_date(t, tz, DATE_ISO8601));
+               else
+                       printf("%s -> bad\n", *argv);
        }
 }
 
index 490cd5f6f4779cbff68405722b98e522cbeb3cde..e8f03f51541472ead12d55672ba4422f97a3c113 100644 (file)
@@ -520,9 +520,17 @@ static int find_cache_pos(struct traverse_info *info,
                const char *ce_name, *ce_slash;
                int cmp, ce_len;
 
-               if (!ce_in_traverse_path(ce, info))
+               if (ce->ce_flags & CE_UNPACKED) {
+                       /*
+                        * cache_bottom entry is already unpacked, so
+                        * we can never match it; don't check it
+                        * again.
+                        */
+                       if (pos == o->cache_bottom)
+                               ++o->cache_bottom;
                        continue;
-               if (ce->ce_flags & CE_UNPACKED)
+               }
+               if (!ce_in_traverse_path(ce, info))
                        continue;
                ce_name = ce->name + pfxlen;
                ce_slash = strchr(ce_name, '/');
index bc12f298953a4e72b323f73607278028ec4a2805..22f9bd692c2706136b4938bbcb2e0fc3dab212b8 100644 (file)
@@ -190,8 +190,10 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 {
        int i1, i2;
 
+       if (s1 == s2 && !memcmp(l1, l2, s1))
+               return 1;
        if (!(flags & XDF_WHITESPACE_FLAGS))
-               return s1 == s2 && !memcmp(l1, l2, s1);
+               return 0;
 
        i1 = 0;
        i2 = 0;