Merge branch 'maint'
authorJunio C Hamano <junkio@cox.net>
Fri, 20 Oct 2006 04:28:12 +0000 (21:28 -0700)
committerJunio C Hamano <junkio@cox.net>
Fri, 20 Oct 2006 04:28:12 +0000 (21:28 -0700)
* maint:
git-apply: prepare for upcoming GNU diff -u format change.

23 files changed:
Documentation/diff-options.txt
Documentation/git-grep.txt
Documentation/git-rebase.txt
archive-zip.c
builtin-apply.c
builtin-grep.c
combine-diff.c
diff.c
diff.h
git-bisect.sh
git-fetch.sh
git-merge.sh
git-rebase.sh
git-resolve.sh
git-send-email.perl
git-svnimport.perl
gitweb/README
gitweb/gitweb.perl
grep.c
grep.h
revision.c
t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
t/t5000-tar-tree.sh
index 7b7b9e8ce92db7fbb2072b75a613b073b0c5a4e6..e112172ca57da75ce6c2dd447cc97c6aa2b6499e 100644 (file)
        The width of the filename part can be controlled by
        giving another width to it separated by a comma.
 
+--numstat::
+       Similar to \--stat, but shows number of added and
+       deleted lines in decimal notation and pathname without
+       abbreviation, to make it more machine friendly.
+
 --summary::
        Output a condensed summary of extended header information
        such as creations, renames and mode changes.
index d8af4d961b83e362b83f25b21d40f9d54ed4bc11..bfbece9864a3c6d8b38dd526e2483fea0fb0b58f 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
           [-v | --invert-match] [-h|-H] [--full-name]
           [-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
           [-n] [-l | --files-with-matches] [-L | --files-without-match]
-          [-c | --count]
+          [-c | --count] [--all-match]
           [-A <post-context>] [-B <pre-context>] [-C <context>]
           [-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...]
           [<tree>...]
@@ -96,6 +96,11 @@ OPTIONS
        higher precedence than `--or`.  `-e` has to be used for all
        patterns.
 
+--all-match::
+       When giving multiple pattern expressions combined with `--or`,
+       this flag is specified to limit the match to files that
+       have lines to match all of them.
+
 `<tree>...`::
        Search blobs in the trees for specified patterns.
 
@@ -111,6 +116,10 @@ git grep -e \'#define\' --and \( -e MAX_PATH -e PATH_MAX \)::
        Looks for a line that has `#define` and either `MAX_PATH` or
        `PATH_MAX`.
 
+git grep --all-match -e NODE -e Unexpected::
+       Looks for a line that has `NODE` or `Unexpected` in
+       files that have lines that match both.
+
 Author
 ------
 Originally written by Linus Torvalds <torvalds@osdl.org>, later
index 9d7bcaa38cc5c13e29c00ddd18c64202e98cb5f8..10f2924f4df1eb29c1baf4484d2837377b6cfcb3 100644 (file)
@@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a new head
 
 SYNOPSIS
 --------
-'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' [-v] [--merge] [--onto <newbase>] <upstream> [<branch>]
 
 'git-rebase' --continue | --skip | --abort
 
@@ -121,6 +121,9 @@ OPTIONS
        is used instead (`git-merge-recursive` when merging a single
        head, `git-merge-octopus` otherwise).  This implies --merge.
 
+-v, \--verbose::
+       Display a diffstat of what changed upstream since the last rebase.
+
 include::merge-strategies.txt[]
 
 NOTES
index 3ffdad68d130136312028b389da73af1a789232e..28e7352e98eaa3bcbc3c86ce772bae35a605ceb2 100644 (file)
@@ -145,6 +145,7 @@ static int write_zip_entry(const unsigned char *sha1,
 {
        struct zip_local_header header;
        struct zip_dir_header dirent;
+       unsigned long attr2;
        unsigned long compressed_size;
        unsigned long uncompressed_size;
        unsigned long crc;
@@ -172,12 +173,16 @@ static int write_zip_entry(const unsigned char *sha1,
 
        if (S_ISDIR(mode)) {
                method = 0;
+               attr2 = 16;
                result = READ_TREE_RECURSIVE;
                out = NULL;
                uncompressed_size = 0;
                compressed_size = 0;
-       } else if (S_ISREG(mode)) {
-               method = zlib_compression_level == 0 ? 0 : 8;
+       } else if (S_ISREG(mode) || S_ISLNK(mode)) {
+               method = 0;
+               attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) : 0;
+               if (S_ISREG(mode) && zlib_compression_level != 0)
+                       method = 8;
                result = 0;
                buffer = read_sha1_file(sha1, type, &size);
                if (!buffer)
@@ -213,8 +218,8 @@ static int write_zip_entry(const unsigned char *sha1,
        }
 
        copy_le32(dirent.magic, 0x02014b50);
-       copy_le16(dirent.creator_version, 0);
-       copy_le16(dirent.version, 20);
+       copy_le16(dirent.creator_version, S_ISLNK(mode) ? 0x0317 : 0);
+       copy_le16(dirent.version, 10);
        copy_le16(dirent.flags, 0);
        copy_le16(dirent.compression_method, method);
        copy_le16(dirent.mtime, zip_time);
@@ -227,7 +232,7 @@ static int write_zip_entry(const unsigned char *sha1,
        copy_le16(dirent.comment_length, 0);
        copy_le16(dirent.disk, 0);
        copy_le16(dirent.attr1, 0);
-       copy_le32(dirent.attr2, 0);
+       copy_le32(dirent.attr2, attr2);
        copy_le32(dirent.offset, zip_offset);
        memcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header));
        zip_dir_offset += sizeof(struct zip_dir_header);
@@ -236,7 +241,7 @@ static int write_zip_entry(const unsigned char *sha1,
        zip_dir_entries++;
 
        copy_le32(header.magic, 0x04034b50);
-       copy_le16(header.version, 20);
+       copy_le16(header.version, 10);
        copy_le16(header.flags, 0);
        copy_le16(header.compression_method, method);
        copy_le16(header.mtime, zip_time);
index 11a5277a69f4cb5c02fb8c9a9312ebafdc472028..11397f5504f98ccff47a90b228abc71b30327fb9 100644 (file)
@@ -360,7 +360,7 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
 static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
 {
        if (!orig_name && !isnull)
-               return find_name(line, NULL, 1, 0);
+               return find_name(line, NULL, 1, TERM_TAB);
 
        if (orig_name) {
                int len;
@@ -370,7 +370,7 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name,
                len = strlen(name);
                if (isnull)
                        die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
-               another = find_name(line, NULL, 1, 0);
+               another = find_name(line, NULL, 1, TERM_TAB);
                if (!another || memcmp(another, name, len))
                        die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
                free(another);
index 4205e5d38dea6dee2d815a7434f87eacd089e4a9..ad7dc00cde4e8e08ef35b313525781c135357df3 100644 (file)
@@ -596,6 +596,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                                            GREP_CLOSE_PAREN);
                        continue;
                }
+               if (!strcmp("--all-match", arg)) {
+                       opt.all_match = 1;
+                       continue;
+               }
                if (!strcmp("-e", arg)) {
                        if (1 < argc) {
                                append_grep_pattern(&opt, argv[1],
index 46d9121baf2ebb024f6b19993a9b75fa3b67951a..65c786807b3cca8408d5119d03a8e4e268bd3e76 100644 (file)
@@ -856,8 +856,10 @@ void diff_tree_combined(const unsigned char *sha1,
                /* show stat against the first parent even
                 * when doing combined diff.
                 */
-               if (i == 0 && opt->output_format & DIFF_FORMAT_DIFFSTAT)
-                       diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
+               int stat_opt = (opt->output_format &
+                               (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
+               if (i == 0 && stat_opt)
+                       diffopts.output_format = stat_opt;
                else
                        diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
                diff_tree_sha1(parent[i], sha1, "", &diffopts);
@@ -887,7 +889,8 @@ void diff_tree_combined(const unsigned char *sha1,
                        }
                        needsep = 1;
                }
-               else if (opt->output_format & DIFF_FORMAT_DIFFSTAT)
+               else if (opt->output_format &
+                        (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT))
                        needsep = 1;
                if (opt->output_format & DIFF_FORMAT_PATCH) {
                        if (needsep)
diff --git a/diff.c b/diff.c
index fb8243261cb3cc9165dbe990586d3865fad4ee61..33153787b8117396cf906e69e656849ac04f3257 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -795,6 +795,23 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
               set, total_files, adds, dels, reset);
 }
 
+static void show_numstat(struct diffstat_t* data, struct diff_options *options)
+{
+       int i;
+
+       for (i = 0; i < data->nr; i++) {
+               struct diffstat_file *file = data->files[i];
+
+               printf("%d\t%d\t", file->added, file->deleted);
+               if (options->line_termination &&
+                   quote_c_style(file->name, NULL, NULL, 0))
+                       quote_c_style(file->name, NULL, stdout, 0);
+               else
+                       fputs(file->name, stdout);
+               putchar(options->line_termination);
+       }
+}
+
 struct checkdiff_t {
        struct xdiff_emit_state xm;
        const char *filename;
@@ -1731,6 +1748,7 @@ int diff_setup_done(struct diff_options *options)
                                      DIFF_FORMAT_CHECKDIFF |
                                      DIFF_FORMAT_NO_OUTPUT))
                options->output_format &= ~(DIFF_FORMAT_RAW |
+                                           DIFF_FORMAT_NUMSTAT |
                                            DIFF_FORMAT_DIFFSTAT |
                                            DIFF_FORMAT_SUMMARY |
                                            DIFF_FORMAT_PATCH);
@@ -1740,7 +1758,9 @@ int diff_setup_done(struct diff_options *options)
         * recursive bits for other formats here.
         */
        if (options->output_format & (DIFF_FORMAT_PATCH |
+                                     DIFF_FORMAT_NUMSTAT |
                                      DIFF_FORMAT_DIFFSTAT |
+                                     DIFF_FORMAT_SUMMARY |
                                      DIFF_FORMAT_CHECKDIFF))
                options->recursive = 1;
        /*
@@ -1828,6 +1848,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--patch-with-raw")) {
                options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
        }
+       else if (!strcmp(arg, "--numstat")) {
+               options->output_format |= DIFF_FORMAT_NUMSTAT;
+       }
        else if (!strncmp(arg, "--stat", 6)) {
                char *end;
                int width = options->stat_width;
@@ -2602,7 +2625,7 @@ void diff_flush(struct diff_options *options)
                separator++;
        }
 
-       if (output_format & DIFF_FORMAT_DIFFSTAT) {
+       if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_NUMSTAT)) {
                struct diffstat_t diffstat;
 
                memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -2612,7 +2635,10 @@ void diff_flush(struct diff_options *options)
                        if (check_pair_status(p))
                                diff_flush_stat(p, options, &diffstat);
                }
-               show_stats(&diffstat, options);
+               if (output_format & DIFF_FORMAT_NUMSTAT)
+                       show_numstat(&diffstat, options);
+               if (output_format & DIFF_FORMAT_DIFFSTAT)
+                       show_stats(&diffstat, options);
                separator++;
        }
 
diff --git a/diff.h b/diff.h
index b48c9914e7e3802d17870bbc0fd68c454fded61c..ce3058e437d5f0142be0746a3e50a3c32045eecb 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -26,20 +26,21 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
 
 #define DIFF_FORMAT_RAW                0x0001
 #define DIFF_FORMAT_DIFFSTAT   0x0002
-#define DIFF_FORMAT_SUMMARY    0x0004
-#define DIFF_FORMAT_PATCH      0x0008
+#define DIFF_FORMAT_NUMSTAT    0x0004
+#define DIFF_FORMAT_SUMMARY    0x0008
+#define DIFF_FORMAT_PATCH      0x0010
 
 /* These override all above */
-#define DIFF_FORMAT_NAME       0x0010
-#define DIFF_FORMAT_NAME_STATUS        0x0020
-#define DIFF_FORMAT_CHECKDIFF  0x0040
+#define DIFF_FORMAT_NAME       0x0100
+#define DIFF_FORMAT_NAME_STATUS        0x0200
+#define DIFF_FORMAT_CHECKDIFF  0x0400
 
 /* Same as output_format = 0 but we know that -s flag was given
  * and we should not give default value to output_format.
  */
-#define DIFF_FORMAT_NO_OUTPUT  0x0080
+#define DIFF_FORMAT_NO_OUTPUT  0x0800
 
-#define DIFF_FORMAT_CALLBACK   0x0100
+#define DIFF_FORMAT_CALLBACK   0x1000
 
 struct diff_options {
        const char *filter;
@@ -170,6 +171,7 @@ extern void diffcore_std_no_resolve(struct diff_options *);
 "  --patch-with-raw\n" \
 "                output both a patch and the diff-raw format.\n" \
 "  --stat        show diffstat instead of patch.\n" \
+"  --numstat     show numeric diffstat instead of patch.\n" \
 "  --patch-with-stat\n" \
 "                output a patch and prepend its diffstat.\n" \
 "  --name-only   show only names of changed files.\n" \
index 06a8d26945a679b06438308ceb96c69cd76c43db..6da31e87a01af9b883958b3b0bae4304bfa94520 100755 (executable)
@@ -179,11 +179,12 @@ bisect_reset() {
         *)
            usage ;;
        esac
-       git checkout "$branch" &&
-       rm -fr "$GIT_DIR/refs/bisect"
-       rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
-       rm -f "$GIT_DIR/BISECT_LOG"
-       rm -f "$GIT_DIR/BISECT_NAMES"
+       if git checkout "$branch"; then
+               rm -fr "$GIT_DIR/refs/bisect"
+               rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
+               rm -f "$GIT_DIR/BISECT_LOG"
+               rm -f "$GIT_DIR/BISECT_NAMES"
+       fi
 }
 
 bisect_replay () {
index 79222fbb1a4bf3205f3225feafc96aa43aace87c..b15fc2b389f35e2b30b17eada4bcc711a12d2a11 100755 (executable)
@@ -129,22 +129,25 @@ append_fetch_head () {
     then
        headc_=$(git-rev-parse --verify "$head_^0") || exit
        echo "$headc_   $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
-       [ "$verbose" ] && echo >&2 "* committish: $head_"
-       [ "$verbose" ] && echo >&2 "  $note_"
     else
        echo "$head_    not-for-merge   $note_" >>"$GIT_DIR/FETCH_HEAD"
-       [ "$verbose" ] && echo >&2 "* non-commit: $head_"
-       [ "$verbose" ] && echo >&2 "  $note_"
-    fi
-    if test "$local_name_" != ""
-    then
-       # We are storing the head locally.  Make sure that it is
-       # a fast forward (aka "reverse push").
-       fast_forward_local "$local_name_" "$head_" "$note_"
     fi
+
+    update_local_ref "$local_name_" "$head_" "$note_"
 }
 
-fast_forward_local () {
+update_local_ref () {
+    # If we are storing the head locally make sure that it is
+    # a fast forward (aka "reverse push").
+
+    label_=$(git-cat-file -t $2)
+    newshort_=$(git-rev-parse --short $2)
+    if test -z "$1" ; then
+       [ "$verbose" ] && echo >&2 "* fetched $3"
+       [ "$verbose" ] && echo >&2 "  $label_: $newshort_"
+       return 0
+    fi
+    oldshort_=$(git-rev-parse --short "$1" 2>/dev/null)
     mkdir -p "$(dirname "$GIT_DIR/$1")"
     case "$1" in
     refs/tags/*)
@@ -154,13 +157,16 @@ fast_forward_local () {
        then
                if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
                then
-                       [ "$verbose" ] && echo >&2 "* $1: same as $3" ||:
+                       [ "$verbose" ] && echo >&2 "* $1: same as $3"
+                       [ "$verbose" ] && echo >&2 "  $label_: $newshort_" ||:
                else
                        echo >&2 "* $1: updating with $3"
+                       echo >&2 "  $label_: $newshort_"
                        git-update-ref -m "$rloga: updating tag" "$1" "$2"
                fi
        else
                echo >&2 "* $1: storing $3"
+               echo >&2 "  $label_: $newshort_"
                git-update-ref -m "$rloga: storing tag" "$1" "$2"
        fi
        ;;
@@ -178,31 +184,34 @@ fast_forward_local () {
                if test -n "$verbose"
                then
                        echo >&2 "* $1: same as $3"
+                       echo >&2 "  $label_: $newshort_"
                fi
                ;;
            *,$local)
                echo >&2 "* $1: fast forward to $3"
-               echo >&2 "  from $local to $2"
+               echo >&2 "  old..new: $oldshort_..$newshort_"
                git-update-ref -m "$rloga: fast-forward" "$1" "$2" "$local"
                ;;
            *)
                false
                ;;
            esac || {
-               echo >&2 "* $1: does not fast forward to $3;"
                case ",$force,$single_force," in
                *,t,*)
-                       echo >&2 "  forcing update."
+                       echo >&2 "* $1: forcing update to non-fast forward $3"
+                       echo >&2 "  old...new: $oldshort_...$newshort_"
                        git-update-ref -m "$rloga: forced-update" "$1" "$2" "$local"
                        ;;
                *)
-                       echo >&2 "  not updating."
+                       echo >&2 "* $1: not updating to non-fast forward $3"
+                       echo >&2 "  old...new: $oldshort_...$newshort_"
                        exit 1
                        ;;
                esac
            }
        else
            echo >&2 "* $1: storing $3"
+           echo >&2 "  $label_: $newshort_"
            git-update-ref -m "$rloga: storing head" "$1" "$2"
        fi
        ;;
index 5b34b4de99c33a99dfb841795baced8889f74a88..49c46d55df3491460864b3afd751258ec8ec97c7 100755 (executable)
@@ -197,7 +197,7 @@ f,*)
        ;;
 ?,1,"$head",*)
        # Again the most common case of merging one remote.
-       echo "Updating from $head to $1"
+       echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
        git-update-index --refresh 2>/dev/null
        new_head=$(git-rev-parse --verify "$1^0") &&
        git-read-tree -u -v -m $head "$new_head" &&
index a7373c0532fad447263e7199d0b8ec2908c683c9..546fa446fc3c6c63488b9ef38cb5bdc960953cb1 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano.
 #
 
-USAGE='[--onto <newbase>] <upstream> [<branch>]'
+USAGE='[-v] [--onto <newbase>] <upstream> [<branch>]'
 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
 same name.  When the --onto option is provided the new branch starts
 out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -39,6 +39,7 @@ strategy=recursive
 do_merge=
 dotest=$GIT_DIR/.dotest-merge
 prec=4
+verbose=
 
 continue_merge () {
        test -n "$prev_head" || die "prev_head must be defined"
@@ -190,6 +191,9 @@ do
                esac
                do_merge=t
                ;;
+       -v|--verbose)
+               verbose=t
+               ;;
        -*)
                usage
                ;;
@@ -273,6 +277,12 @@ then
        exit 0
 fi
 
+if test -n "$verbose"
+then
+       echo "Changes from $mb to $onto:"
+       git-diff-tree --stat --summary "$mb" "$onto"
+fi
+
 # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
 git-reset --hard "$onto"
 
@@ -286,7 +296,7 @@ fi
 
 if test -z "$do_merge"
 then
-       git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
+       git-format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
        git am --binary -3 -k --resolvemsg="$RESOLVEMSG" \
                --reflog-action=rebase
        exit $?
index 729ec65dc9e0ddebfe81fb4d837a5f9c83b537ee..36b90e38494eb79b4859cf59301b5a4e6ccccea1 100755 (executable)
@@ -46,7 +46,7 @@ case "$common" in
        exit 0
        ;;
 "$head")
-       echo "Updating from $head to $merge"
+       echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)"
        git-read-tree -u -m $head $merge || exit 1
        git-update-ref -m "resolve $merge_name: Fast forward" \
                HEAD "$merge" "$head"
index 3f50abaeb6901772b22e6ca2c1e87b4bd92f3b92..1c6d2cc7872ab82020d9dc32ce0086b1d3139e22 100755 (executable)
@@ -83,11 +83,12 @@ sub format_2822_time {
 my $compose_filename = ".msg.$$";
 
 # Variables we fill in automatically, or via prompting:
-my (@to,@cc,@initial_cc,@bcclist,
+my (@to,@cc,@initial_cc,@bcclist,@xh,
        $initial_reply_to,$initial_subject,@files,$from,$compose,$time);
 
 # Behavior modification variables
-my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc) = (1, 0, 0, 0);
+my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc,
+       $dry_run) = (1, 0, 0, 0, 0);
 my $smtp_server;
 
 # Example reply to:
@@ -116,6 +117,7 @@ sub format_2822_time {
                    "quiet" => \$quiet,
                    "suppress-from" => \$suppress_from,
                    "no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc,
+                   "dry-run" => \$dry_run,
         );
 
 # Verify the user input
@@ -409,6 +411,11 @@ sub send_message
            $gitversion = Git::version();
        }
 
+       my ($author_name) = ($from =~ /^(.*?)\s+</);
+       if ($author_name && $author_name =~ /\./ && $author_name !~ /^".*"$/) {
+               my ($name, $addr) = ($from =~ /^(.*?)(\s+<.*)/);
+               $from = "\"$name\"$addr";
+       }
        my $header = "From: $from
 To: $to
 Cc: $cc
@@ -422,8 +429,13 @@ sub send_message
                $header .= "In-Reply-To: $reply_to\n";
                $header .= "References: $references\n";
        }
+       if (@xh) {
+               $header .= join("\n", @xh) . "\n";
+       }
 
-       if ($smtp_server =~ m#^/#) {
+       if ($dry_run) {
+               # We don't want to send the email.
+       } elsif ($smtp_server =~ m#^/#) {
                my $pid = open my $sm, '|-';
                defined $pid or die $!;
                if (!$pid) {
@@ -472,15 +484,22 @@ sub send_message
 
        my $author_not_sender = undef;
        @cc = @initial_cc;
-       my $found_mbox = 0;
+       @xh = ();
+       my $input_format = undef;
        my $header_done = 0;
        $message = "";
        while(<F>) {
                if (!$header_done) {
-                       $found_mbox = 1, next if (/^From /);
+                       if (/^From /) {
+                               $input_format = 'mbox';
+                               next;
+                       }
                        chomp;
+                       if (!defined $input_format && /^[-A-Za-z]+:\s/) {
+                               $input_format = 'mbox';
+                       }
 
-                       if ($found_mbox) {
+                       if (defined $input_format && $input_format eq 'mbox') {
                                if (/^Subject:\s+(.*)$/) {
                                        $subject = $1;
 
@@ -495,6 +514,9 @@ sub send_message
                                                $2, $_) unless $quiet;
                                        push @cc, $2;
                                }
+                               elsif (/^[-A-Za-z]+:\s+\S/) {
+                                       push @xh, $_;
+                               }
 
                        } else {
                                # In the traditional
@@ -502,6 +524,7 @@ sub send_message
                                # line 1 = cc
                                # line 2 = subject
                                # So let's support that, too.
+                               $input_format = 'lots';
                                if (@cc == 0) {
                                        printf("(non-mbox) Adding cc: %s from line '%s'\n",
                                                $_, $_) unless $quiet;
index aca0e4f64ebb467cf5ba69dfbfaa55a4da6e6d3d..f6eff8e32adc92a072b33de9040e2154efd13a8a 100755 (executable)
@@ -193,6 +193,13 @@ sub ignore {
        }
 }
 
+sub dir_list {
+       my($self,$path,$rev) = @_;
+       my ($dirents,undef,$properties)
+           = $self->{'svn'}->get_dir($path,$rev,undef);
+       return $dirents;
+}
+
 package main;
 use URI;
 
@@ -342,35 +349,16 @@ ($$)
 
 open BRANCHES,">>", "$git_dir/svn2git";
 
-sub node_kind($$$) {
-       my ($branch, $path, $revision) = @_;
+sub node_kind($$) {
+       my ($svnpath, $revision) = @_;
        my $pool=SVN::Pool->new;
-       my $kind = $svn->{'svn'}->check_path(revert_split_path($branch,$path),$revision,$pool);
+       my $kind = $svn->{'svn'}->check_path($svnpath,$revision,$pool);
        $pool->clear;
        return $kind;
 }
 
-sub revert_split_path($$) {
-       my($branch,$path) = @_;
-
-       my $svnpath;
-       $path = "" if $path eq "/"; # this should not happen, but ...
-       if($branch eq "/") {
-               $svnpath = "$trunk_name/$path";
-       } elsif($branch =~ m#^/#) {
-               $svnpath = "$tag_name$branch/$path";
-       } else {
-               $svnpath = "$branch_name/$branch/$path";
-       }
-
-       $svnpath =~ s#/+$##;
-       return $svnpath;
-}
-
 sub get_file($$$) {
-       my($rev,$branch,$path) = @_;
-
-       my $svnpath = revert_split_path($branch,$path);
+       my($svnpath,$rev,$path) = @_;
 
        # now get it
        my ($name,$mode);
@@ -413,10 +401,9 @@ ($$$)
 }
 
 sub get_ignore($$$$$) {
-       my($new,$old,$rev,$branch,$path) = @_;
+       my($new,$old,$rev,$path,$svnpath) = @_;
 
        return unless $opt_I;
-       my $svnpath = revert_split_path($branch,$path);
        my $name = $svn->ignore("$svnpath",$rev);
        if ($path eq '/') {
                $path = $opt_I;
@@ -435,7 +422,7 @@ ($$$$$)
                close $F;
                unlink $name;
                push(@$new,['0644',$sha,$path]);
-       } else {
+       } elsif (defined $old) {
                push(@$old,$path);
        }
 }
@@ -480,6 +467,27 @@ ($$)
        return $therev;
 }
 
+sub expand_svndir($$$);
+
+sub expand_svndir($$$)
+{
+       my ($svnpath, $rev, $path) = @_;
+       my @list;
+       get_ignore(\@list, undef, $rev, $path, $svnpath);
+       my $dirents = $svn->dir_list($svnpath, $rev);
+       foreach my $p(keys %$dirents) {
+               my $kind = node_kind($svnpath.'/'.$p, $rev);
+               if ($kind eq $SVN::Node::file) {
+                       my $f = get_file($svnpath.'/'.$p, $rev, $path.'/'.$p);
+                       push(@list, $f) if $f;
+               } elsif ($kind eq $SVN::Node::dir) {
+                       push(@list,
+                            expand_svndir($svnpath.'/'.$p, $rev, $path.'/'.$p));
+               }
+       }
+       return @list;
+}
+
 sub copy_path($$$$$$$$) {
        # Somebody copied a whole subdirectory.
        # We need to find the index entries from the old version which the
@@ -488,8 +496,11 @@ ($$$$$$$$)
        my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_;
 
        my($srcbranch,$srcpath) = split_path($rev,$oldpath);
-       unless(defined $srcbranch) {
-               print "Path not found when copying from $oldpath @ $rev\n";
+       unless(defined $srcbranch && defined $srcpath) {
+               print "Path not found when copying from $oldpath @ $rev.\n".
+                       "Will try to copy from original SVN location...\n"
+                       if $opt_v;
+               push (@$new, expand_svndir($oldpath, $rev, $path));
                return;
        }
        my $therev = branch_rev($srcbranch, $rev);
@@ -503,7 +514,7 @@ ($$$$$$$$)
        }
        print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v;
        if ($node_kind eq $SVN::Node::dir) {
-                       $srcpath =~ s#/*$#/#;
+               $srcpath =~ s#/*$#/#;
        }
        
        my $pid = open my $f,'-|';
@@ -582,10 +593,12 @@ sub commit {
                if(defined $oldpath) {
                        my $p;
                        ($parent,$p) = split_path($revision,$oldpath);
-                       if($parent eq "/") {
-                               $parent = $opt_o;
-                       } else {
-                               $parent =~ s#^/##; # if it's a tag
+                       if(defined $parent) {
+                               if($parent eq "/") {
+                                       $parent = $opt_o;
+                               } else {
+                                       $parent =~ s#^/##; # if it's a tag
+                               }
                        }
                } else {
                        $parent = undef;
@@ -651,9 +664,10 @@ sub commit {
                                push(@old,$path); # remove any old stuff
                        }
                        if(($action->[0] eq "A") || ($action->[0] eq "R")) {
-                               my $node_kind = node_kind($branch,$path,$revision);
+                               my $node_kind = node_kind($action->[3], $revision);
                                if ($node_kind eq $SVN::Node::file) {
-                                       my $f = get_file($revision,$branch,$path);
+                                       my $f = get_file($action->[3],
+                                                        $revision, $path);
                                        if ($f) {
                                                push(@new,$f) if $f;
                                        } else {
@@ -668,19 +682,20 @@ sub commit {
                                                          \@new, \@parents);
                                        } else {
                                                get_ignore(\@new, \@old, $revision,
-                                                          $branch, $path);
+                                                          $path, $action->[3]);
                                        }
                                }
                        } elsif ($action->[0] eq "D") {
                                push(@old,$path);
                        } elsif ($action->[0] eq "M") {
-                               my $node_kind = node_kind($branch,$path,$revision);
+                               my $node_kind = node_kind($action->[3], $revision);
                                if ($node_kind eq $SVN::Node::file) {
-                                       my $f = get_file($revision,$branch,$path);
+                                       my $f = get_file($action->[3],
+                                                        $revision, $path);
                                        push(@new,$f) if $f;
                                } elsif ($node_kind eq $SVN::Node::dir) {
                                        get_ignore(\@new, \@old, $revision,
-                                                  $branch,$path);
+                                                  $path, $action->[3]);
                                }
                        } else {
                                die "$revision: unknown action '".$action->[0]."' for $path\n";
index 78e6fc05f3dbbc274f7f1523e861799a4161f379..e02e90f0429be0d2a69b76571101f20b8f75530f 100644 (file)
@@ -26,12 +26,26 @@ You can specify the following configuration variables when building GIT:
  * GITWEB_LOGO
    Points to the location where you put git-logo.png on your web server.
  * GITWEB_CONFIG
-   This file will be loaded using 'require'.  If the environment
+   This file will be loaded using 'require' and can be used to override any
+   of the options above as well as some other options - see the top of
+   'gitweb.cgi' for their full list and description.  If the environment
    $GITWEB_CONFIG is set when gitweb.cgi is executed the file in the
    environment variable will be loaded instead of the file
    specified when gitweb.cgi was created.
 
 
+Runtime gitweb configuration
+----------------------------
+
+You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
+(defaults to 'gitweb_config.perl' in the same directory as the CGI).
+See the top of 'gitweb.cgi' for the list of variables and some description.
+The most notable thing that is not configurable at compile time are the
+optional features, stored in the '%features' variable. You can find further
+description on how to reconfigure the default features setting in your
+`GITWEB_CONFIG` or per-project in `project.git/config` inside 'gitweb.cgi'.
+
+
 Webserver configuration
 -----------------------
 
index 0ec1eeffa1b688a3b0dc90eed24ba2de61756e2b..035e85e6e9c25623783ef36e2e91964b306de3b5 100755 (executable)
        #
        # use gitweb_check_feature(<feature>) to check if <feature> is enabled
 
+       # Enable the 'blame' blob view, showing the last commit that modified
+       # each line in the file. This can be very CPU-intensive.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'blame'}{'default'} = [1];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'blame'}{'override'} = 1;
+       # and in project config gitweb.blame = 0|1;
        'blame' => {
                'sub' => \&feature_blame,
                'override' => 0,
                'default' => [0]},
 
+       # Enable the 'snapshot' link, providing a compressed tarball of any
+       # tree. This can potentially generate high traffic if you have large
+       # project.
+
+       # To disable system wide have in $GITWEB_CONFIG
+       # $feature{'snapshot'}{'default'} = [undef];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'blame'}{'override'} = 1;
+       # and in project config gitweb.snapshot = none|gzip|bzip2;
        'snapshot' => {
                'sub' => \&feature_snapshot,
                'override' => 0,
                #         => [content-encoding, suffix, program]
                'default' => ['x-gzip', 'gz', 'gzip']},
 
+       # Enable the pickaxe search, which will list the commits that modified
+       # a given string in a file. This can be practical and quite faster
+       # alternative to 'blame', but still potentially CPU-intensive.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'pickaxe'}{'default'} = [1];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'pickaxe'}{'override'} = 1;
+       # and in project config gitweb.pickaxe = 0|1;
        'pickaxe' => {
                'sub' => \&feature_pickaxe,
                'override' => 0,
                'default' => [1]},
+
+       # Make gitweb use an alternative format of the URLs which can be
+       # more readable and natural-looking: project name is embedded
+       # directly in the path and the query string contains other
+       # auxiliary information. All gitweb installations recognize
+       # URL in either format; this configures in which formats gitweb
+       # generates links.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'pathinfo'}{'default'} = [1];
+       # Project specific override is not supported.
+
+       # Note that you will need to change the default location of CSS,
+       # favicon, logo and possibly other files to an absolute URL. Also,
+       # if gitweb.cgi serves as your indexfile, you will need to force
+       # $my_uri to contain the script name in your $GITWEB_CONFIG.
+       'pathinfo' => {
+               'override' => 0,
+               'default' => [0]},
 );
 
 sub gitweb_check_feature {
@@ -118,15 +163,13 @@ sub gitweb_check_feature {
                $feature{$name}{'override'},
                @{$feature{$name}{'default'}});
        if (!$override) { return @defaults; }
+       if (!defined $sub) {
+               warn "feature $name is not overrideable";
+               return @defaults;
+       }
        return $sub->(@defaults);
 }
 
-# To enable system wide have in $GITWEB_CONFIG
-# $feature{'blame'}{'default'} = [1];
-# To have project specific config enable override in $GITWEB_CONFIG
-# $feature{'blame'}{'override'} = 1;
-# and in project config gitweb.blame = 0|1;
-
 sub feature_blame {
        my ($val) = git_get_project_config('blame', '--bool');
 
@@ -139,12 +182,6 @@ sub feature_blame {
        return $_[0];
 }
 
-# To disable system wide have in $GITWEB_CONFIG
-# $feature{'snapshot'}{'default'} = [undef];
-# To have project specific config enable override in $GITWEB_CONFIG
-# $feature{'blame'}{'override'} = 1;
-# and in project config  gitweb.snapshot = none|gzip|bzip2
-
 sub feature_snapshot {
        my ($ctype, $suffix, $command) = @_;
 
@@ -168,12 +205,6 @@ sub gitweb_have_snapshot {
        return $have_snapshot;
 }
 
-# To enable system wide have in $GITWEB_CONFIG
-# $feature{'pickaxe'}{'default'} = [1];
-# To have project specific config enable override in $GITWEB_CONFIG
-# $feature{'pickaxe'}{'override'} = 1;
-# and in project config gitweb.pickaxe = 0|1;
-
 sub feature_pickaxe {
        my ($val) = git_get_project_config('pickaxe', '--bool');
 
@@ -381,6 +412,10 @@ sub evaluate_path_info {
 
 sub href(%) {
        my %params = @_;
+       my $href = $my_uri;
+
+       # XXX: Warning: If you touch this, check the search form for updating,
+       # too.
 
        my @mapping = (
                project => "p",
@@ -399,6 +434,19 @@ (%)
 
        $params{'project'} = $project unless exists $params{'project'};
 
+       my ($use_pathinfo) = gitweb_check_feature('pathinfo');
+       if ($use_pathinfo) {
+               # use PATH_INFO for project name
+               $href .= "/$params{'project'}" if defined $params{'project'};
+               delete $params{'project'};
+
+               # Summary just uses the project path URL
+               if (defined $params{'action'} && $params{'action'} eq 'summary') {
+                       delete $params{'action'};
+               }
+       }
+
+       # now encode the parameters explicitly
        my @result = ();
        for (my $i = 0; $i < @mapping; $i += 2) {
                my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]);
@@ -406,7 +454,9 @@ (%)
                        push @result, $symbol . "=" . esc_param($params{$name});
                }
        }
-       return "$my_uri?" . join(';', @result);
+       $href .= "?" . join(';', @result) if scalar @result;
+
+       return $href;
 }
 
 
@@ -1411,6 +1461,7 @@ sub git_header_html {
                }
                $cgi->param("a", "search");
                $cgi->param("h", $search_hash);
+               $cgi->param("p", $project);
                print $cgi->startform(-method => "get", -action => $my_uri) .
                      "<div class=\"search\">\n" .
                      $cgi->hidden(-name => "p") . "\n" .
diff --git a/grep.c b/grep.c
index c411ddd4d52e2aa68edd979733dd0b15f51d5f32..0fc078ec0ac42e39125c8e5a8f05a3b6ecae4fa3 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -34,7 +34,7 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
        }
 }
 
-static struct grep_expr *compile_pattern_expr(struct grep_pat **);
+static struct grep_expr *compile_pattern_or(struct grep_pat **);
 static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
 {
        struct grep_pat *p;
@@ -52,7 +52,7 @@ static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
                return x;
        case GREP_OPEN_PAREN:
                *list = p->next;
-               x = compile_pattern_expr(list);
+               x = compile_pattern_or(list);
                if (!x)
                        return NULL;
                if (!*list || (*list)->token != GREP_CLOSE_PAREN)
@@ -138,6 +138,9 @@ void compile_grep_patterns(struct grep_opt *opt)
 {
        struct grep_pat *p;
 
+       if (opt->all_match)
+               opt->extended = 1;
+
        for (p = opt->pattern_list; p; p = p->next) {
                switch (p->token) {
                case GREP_PATTERN: /* atom */
@@ -309,40 +312,63 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
        return hit;
 }
 
-static int match_expr_eval(struct grep_opt *opt,
+static int match_expr_eval(struct grep_opt *o,
                           struct grep_expr *x,
                           char *bol, char *eol,
-                          enum grep_context ctx)
+                          enum grep_context ctx,
+                          int collect_hits)
 {
+       int h = 0;
+
        switch (x->node) {
        case GREP_NODE_ATOM:
-               return match_one_pattern(opt, x->u.atom, bol, eol, ctx);
+               h = match_one_pattern(o, x->u.atom, bol, eol, ctx);
                break;
        case GREP_NODE_NOT:
-               return !match_expr_eval(opt, x->u.unary, bol, eol, ctx);
+               h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);
+               break;
        case GREP_NODE_AND:
-               return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) &&
-                       match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
+               if (!collect_hits)
+                       return (match_expr_eval(o, x->u.binary.left,
+                                               bol, eol, ctx, 0) &&
+                               match_expr_eval(o, x->u.binary.right,
+                                               bol, eol, ctx, 0));
+               h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
+               h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);
+               break;
        case GREP_NODE_OR:
-               return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) ||
-                       match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
+               if (!collect_hits)
+                       return (match_expr_eval(o, x->u.binary.left,
+                                               bol, eol, ctx, 0) ||
+                               match_expr_eval(o, x->u.binary.right,
+                                               bol, eol, ctx, 0));
+               h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
+               x->u.binary.left->hit |= h;
+               h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);
+               break;
+       default:
+               die("Unexpected node type (internal error) %d\n", x->node);
        }
-       die("Unexpected node type (internal error) %d\n", x->node);
+       if (collect_hits)
+               x->hit |= h;
+       return h;
 }
 
 static int match_expr(struct grep_opt *opt, char *bol, char *eol,
-                     enum grep_context ctx)
+                     enum grep_context ctx, int collect_hits)
 {
        struct grep_expr *x = opt->pattern_expression;
-       return match_expr_eval(opt, x, bol, eol, ctx);
+       return match_expr_eval(opt, x, bol, eol, ctx, collect_hits);
 }
 
 static int match_line(struct grep_opt *opt, char *bol, char *eol,
-                     enum grep_context ctx)
+                     enum grep_context ctx, int collect_hits)
 {
        struct grep_pat *p;
        if (opt->extended)
-               return match_expr(opt, bol, eol, ctx);
+               return match_expr(opt, bol, eol, ctx, collect_hits);
+
+       /* we do not call with collect_hits without being extended */
        for (p = opt->pattern_list; p; p = p->next) {
                if (match_one_pattern(opt, p, bol, eol, ctx))
                        return 1;
@@ -350,7 +376,8 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,
        return 0;
 }
 
-int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
+static int grep_buffer_1(struct grep_opt *opt, const char *name,
+                        char *buf, unsigned long size, int collect_hits)
 {
        char *bol = buf;
        unsigned long left = size;
@@ -386,7 +413,7 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
 
        while (left) {
                char *eol, ch;
-               int hit = 0;
+               int hit;
 
                eol = end_of_line(bol, &left);
                ch = *eol;
@@ -395,9 +422,12 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
                if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
                        ctx = GREP_CONTEXT_BODY;
 
-               hit = match_line(opt, bol, eol, ctx);
+               hit = match_line(opt, bol, eol, ctx, collect_hits);
                *eol = ch;
 
+               if (collect_hits)
+                       goto next_line;
+
                /* "grep -v -e foo -e bla" should list lines
                 * that do not have either, so inversion should
                 * be done outside.
@@ -477,6 +507,8 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
        }
 
        free(prev);
+       if (collect_hits)
+               return 0;
 
        if (opt->status_only)
                return 0;
@@ -496,3 +528,49 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
        return !!last_hit;
 }
 
+static void clr_hit_marker(struct grep_expr *x)
+{
+       /* All-hit markers are meaningful only at the very top level
+        * OR node.
+        */
+       while (1) {
+               x->hit = 0;
+               if (x->node != GREP_NODE_OR)
+                       return;
+               x->u.binary.left->hit = 0;
+               x = x->u.binary.right;
+       }
+}
+
+static int chk_hit_marker(struct grep_expr *x)
+{
+       /* Top level nodes have hit markers.  See if they all are hits */
+       while (1) {
+               if (x->node != GREP_NODE_OR)
+                       return x->hit;
+               if (!x->u.binary.left->hit)
+                       return 0;
+               x = x->u.binary.right;
+       }
+}
+
+int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
+{
+       /*
+        * we do not have to do the two-pass grep when we do not check
+        * buffer-wide "all-match".
+        */
+       if (!opt->all_match)
+               return grep_buffer_1(opt, name, buf, size, 0);
+
+       /* Otherwise the toplevel "or" terms hit a bit differently.
+        * We first clear hit markers from them.
+        */
+       clr_hit_marker(opt->pattern_expression);
+       grep_buffer_1(opt, name, buf, size, 1);
+
+       if (!chk_hit_marker(opt->pattern_expression))
+               return 0;
+
+       return grep_buffer_1(opt, name, buf, size, 0);
+}
diff --git a/grep.h b/grep.h
index af9098cfe8699680aeaa11790dad3e8d556ed395..d252dd25f81526d9b8663b4d3c9585d69a901397 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -35,6 +35,7 @@ enum grep_expr_node {
 
 struct grep_expr {
        enum grep_expr_node node;
+       unsigned hit;
        union {
                struct grep_pat *atom;
                struct grep_expr *unary;
@@ -59,6 +60,7 @@ struct grep_opt {
        unsigned count:1;
        unsigned word_regexp:1;
        unsigned fixed:1;
+       unsigned all_match:1;
 #define GREP_BINARY_DEFAULT    0
 #define GREP_BINARY_NOMATCH    1
 #define GREP_BINARY_TEXT       2
index 93f25130a05ccca3e3e6c65b750f256246ae16be..f1e0caaae3d2a96de6cf9bad12e8641c19d788dd 100644 (file)
@@ -732,6 +732,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        int i, flags, seen_dashdash, show_merge;
        const char **unrecognized = argv + 1;
        int left = 1;
+       int all_match = 0;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -967,6 +968,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                add_message_grep(revs, arg+7);
                                continue;
                        }
+                       if (!strcmp(arg, "--all-match")) {
+                               all_match = 1;
+                               continue;
+                       }
 
                        opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
                        if (opts > 0) {
@@ -1028,8 +1033,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        if (diff_setup_done(&revs->diffopt) < 0)
                die("diff_setup_done failed");
 
-       if (revs->grep_filter)
+       if (revs->grep_filter) {
+               revs->grep_filter->all_match = all_match;
                compile_grep_patterns(revs->grep_filter);
+       }
 
        return left;
 }
index ea48205537b60376b1fab346effb82548d48954e..58e5f74aeae880c69776bfa8e51bc9e5fe2463cf 100644 (file)
@@ -5,7 +5,7 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 
- create mode 040000 dir
+ create mode 100644 dir/sub
  create mode 100644 file0
  create mode 100644 file2
 $
index 278eb6670116d0036413a81fc129615974458e5d..cf08e9279c1b2dc61cb53cf1e4940143cb3bf05c 100755 (executable)
@@ -26,6 +26,7 @@ commit id embedding:
 
 . ./test-lib.sh
 TAR=${TAR:-tar}
+UNZIP=${UNZIP:-unzip}
 
 test_expect_success \
     'populate workdir' \
@@ -95,4 +96,38 @@ test_expect_success \
     'validate file contents with prefix' \
     'diff -r a c/prefix/a'
 
+test_expect_success \
+    'git-archive --format=zip' \
+    'git-archive --format=zip HEAD >d.zip'
+
+test_expect_success \
+    'extract ZIP archive' \
+    '(mkdir d && cd d && $UNZIP ../d.zip)'
+
+test_expect_success \
+    'validate filenames' \
+    '(cd d/a && find .) | sort >d.lst &&
+     diff a.lst d.lst'
+
+test_expect_success \
+    'validate file contents' \
+    'diff -r a d/a'
+
+test_expect_success \
+    'git-archive --format=zip with prefix' \
+    'git-archive --format=zip --prefix=prefix/ HEAD >e.zip'
+
+test_expect_success \
+    'extract ZIP archive with prefix' \
+    '(mkdir e && cd e && $UNZIP ../e.zip)'
+
+test_expect_success \
+    'validate filenames with prefix' \
+    '(cd e/prefix/a && find .) | sort >e.lst &&
+     diff a.lst e.lst'
+
+test_expect_success \
+    'validate file contents with prefix' \
+    'diff -r a e/prefix/a'
+
 test_done