Merge branch 'jc/grep-c'
authorJunio C Hamano <gitster@pobox.com>
Fri, 14 Sep 2007 22:17:07 +0000 (15:17 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 14 Sep 2007 22:17:07 +0000 (15:17 -0700)
* jc/grep-c:
Split grep arguments in a way that does not requires to add /dev/null.
Documentation/git-config.txt: AsciiDoc tweak to avoid leading dot
Add test to check recent fix to "git add -u"
Documentation/git-archive.txt: a couple of clarifications.
Fix the rename detection limit checking
diff --no-index: do not forget to run diff_setup_done()

Documentation/git-archive.txt
Documentation/git-config.txt
builtin-add.c
builtin-grep.c
diff-lib.c
diff.c
diffcore-rename.c
t/t2200-add-update.sh
t/t7002-grep.sh
wt-status.c
index f2080eb6ad35282107fd5500eb9cba9290497cb9..e1e2d60fef1b8fc85e30243f403bd11d80ca980b 100644 (file)
@@ -15,7 +15,8 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Creates an archive of the specified format containing the tree
-structure for the named tree.  If <prefix> is specified it is
+structure for the named tree, and writes it out to the standard
+output.  If <prefix> is specified it is
 prepended to the filenames in the archive.
 
 'git-archive' behaves differently when given a tree ID versus when
@@ -31,7 +32,7 @@ OPTIONS
 -------
 
 --format=<fmt>::
-       Format of the resulting archive: 'tar', 'zip'...  The default
+       Format of the resulting archive: 'tar' or 'zip'.  The default
        is 'tar'.
 
 --list, -l::
index 5b794f439990e3ddeb80adee31de44063ef44ee6..a592b61e2fe998525d0978547b3ea7e4ad2d72ac 100644 (file)
@@ -142,7 +142,7 @@ FILES
 If not set explicitly with '--file', there are three files where
 git-config will search for configuration options:
 
-.git/config::
+$GIT_DIR/config::
        Repository specific configuration file. (The filename is
        of course relative to the repository root, not the working
        directory.)
index 9847b7e019ece82f19e3e5decf8647eb921c4291..3d8b8b4f89514e0a8f7af1c2c7dc2f8ae372129e 100644 (file)
@@ -95,7 +95,7 @@ static void update_callback(struct diff_queue_struct *q,
                const char *path = p->one->path;
                switch (p->status) {
                default:
-                       die("unexpacted diff status %c", p->status);
+                       die("unexpected diff status %c", p->status);
                case DIFF_STATUS_UNMERGED:
                case DIFF_STATUS_MODIFIED:
                case DIFF_STATUS_TYPE_CHANGED:
index e13cb31f2b9dd63d335437fb5b1c9b0b06fbf188..c7b45c4d58dd718204e815363a77747d2d5aebb3 100644 (file)
@@ -187,6 +187,78 @@ static int exec_grep(int argc, const char **argv)
        else die("maximum number of args exceeded"); \
        } while (0)
 
+/*
+ * If you send a singleton filename to grep, it does not give
+ * the name of the file.  GNU grep has "-H" but we would want
+ * that behaviour in a portable way.
+ *
+ * So we keep two pathnames in argv buffer unsent to grep in
+ * the main loop if we need to do more than one grep.
+ */
+static int flush_grep(struct grep_opt *opt,
+                     int argc, int arg0, const char **argv, int *kept)
+{
+       int status;
+       int count = argc - arg0;
+       const char *kept_0 = NULL;
+
+       if (count <= 2) {
+               /*
+                * Because we keep at least 2 paths in the call from
+                * the main loop (i.e. kept != NULL), and MAXARGS is
+                * far greater than 2, this usually is a call to
+                * conclude the grep.  However, the user could attempt
+                * to overflow the argv buffer by giving too many
+                * options to leave very small number of real
+                * arguments even for the call in the main loop.
+                */
+               if (kept)
+                       die("insanely many options to grep");
+
+               /*
+                * If we have two or more paths, we do not have to do
+                * anything special, but we need to push /dev/null to
+                * get "-H" behaviour of GNU grep portably but when we
+                * are not doing "-l" nor "-L" nor "-c".
+                */
+               if (count == 1 &&
+                   !opt->name_only &&
+                   !opt->unmatch_name_only &&
+                   !opt->count) {
+                       argv[argc++] = "/dev/null";
+                       argv[argc] = NULL;
+               }
+       }
+
+       else if (kept) {
+               /*
+                * Called because we found many paths and haven't finished
+                * iterating over the cache yet.  We keep two paths
+                * for the concluding call.  argv[argc-2] and argv[argc-1]
+                * has the last two paths, so save the first one away,
+                * replace it with NULL while sending the list to grep,
+                * and recover them after we are done.
+                */
+               *kept = 2;
+               kept_0 = argv[argc-2];
+               argv[argc-2] = NULL;
+               argc -= 2;
+       }
+
+       status = exec_grep(argc, argv);
+
+       if (kept_0) {
+               /*
+                * Then recover them.  Now the last arg is beyond the
+                * terminating NULL which is at argc, and the second
+                * from the last is what we saved away in kept_0
+                */
+               argv[arg0++] = kept_0;
+               argv[arg0] = argv[argc+1];
+       }
+       return status;
+}
+
 static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 {
        int i, nr, argc, hit, len, status;
@@ -253,22 +325,12 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
                push_arg(p->pattern);
        }
 
-       /*
-        * To make sure we get the header printed out when we want it,
-        * add /dev/null to the paths to grep.  This is unnecessary
-        * (and wrong) with "-l" or "-L", which always print out the
-        * name anyway.
-        *
-        * GNU grep has "-H", but this is portable.
-        */
-       if (!opt->name_only && !opt->unmatch_name_only)
-               push_arg("/dev/null");
-
        hit = 0;
        argc = nr;
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
                char *name;
+               int kept;
                if (!S_ISREG(ntohl(ce->ce_mode)))
                        continue;
                if (!pathspec_matches(paths, ce->name))
@@ -283,10 +345,10 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
                argv[argc++] = name;
                if (argc < MAXARGS && !ce_stage(ce))
                        continue;
-               status = exec_grep(argc, argv);
+               status = flush_grep(opt, argc, nr, argv, &kept);
                if (0 < status)
                        hit = 1;
-               argc = nr;
+               argc = nr + kept;
                if (ce_stage(ce)) {
                        do {
                                i++;
@@ -296,7 +358,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
                }
        }
        if (argc > nr) {
-               status = exec_grep(argc, argv);
+               status = flush_grep(opt, argc, nr, argv, NULL);
                if (0 < status)
                        hit = 1;
        }
index f5568c3b36338a203a42ce1e46109152ad4642bb..da5571302df6ed418874fd4d7423853a7de5b52c 100644 (file)
@@ -298,6 +298,8 @@ int setup_diff_no_index(struct rev_info *revs,
        revs->diffopt.nr_paths = 2;
        revs->diffopt.no_index = 1;
        revs->max_count = -2;
+       if (diff_setup_done(&revs->diffopt) < 0)
+               die("diff_setup_done failed");
        return 0;
 }
 
diff --git a/diff.c b/diff.c
index 1aca5df522d1c5249c9d1ce8d030849d59a5eb6a..0ee9ea1c1b47b82710a3850e7e9f679e4486f7bd 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -17,7 +17,7 @@
 #endif
 
 static int diff_detect_rename_default;
-static int diff_rename_limit_default = -1;
+static int diff_rename_limit_default = 100;
 static int diff_use_color_default;
 int diff_auto_refresh_index = 1;
 
index 6bde4396f212833cc1d411e723d5215c086e7c2d..41b35c3a9e6935f6cd8563de732321e74e115765 100644 (file)
@@ -298,10 +298,25 @@ void diffcore_rename(struct diff_options *options)
                else if (detect_rename == DIFF_DETECT_COPY)
                        register_rename_src(p->one, 1, p->score);
        }
-       if (rename_dst_nr == 0 || rename_src_nr == 0 ||
-           (0 < rename_limit && rename_limit < rename_dst_nr))
+       if (rename_dst_nr == 0 || rename_src_nr == 0)
                goto cleanup; /* nothing to do */
 
+       /*
+        * This basically does a test for the rename matrix not
+        * growing larger than a "rename_limit" square matrix, ie:
+        *
+        *    rename_dst_nr * rename_src_nr > rename_limit * rename_limit
+        *
+        * but handles the potential overflow case specially (and we
+        * assume at least 32-bit integers)
+        */
+       if (rename_limit <= 0 || rename_limit > 32767)
+               rename_limit = 32767;
+       if (rename_dst_nr > rename_limit && rename_src_nr > rename_limit)
+               goto cleanup;
+       if (rename_dst_nr * rename_src_nr > rename_limit * rename_limit)
+               goto cleanup;
+
        /* We really want to cull the candidates list early
         * with cheap tests in order to avoid doing deltas.
         * The first round matches up the up-to-date entries,
index 61d08bb431ca18d6223e944cd4ca43fba6c1d332..eb1ced3c371ecaeab9d0dc14be888bf7df110483 100755 (executable)
@@ -16,11 +16,12 @@ only the updates to dir/sub.'
 test_expect_success setup '
        echo initial >check &&
        echo initial >top &&
+       echo initial >foo &&
        mkdir dir1 dir2 &&
        echo initial >dir1/sub1 &&
        echo initial >dir1/sub2 &&
        echo initial >dir2/sub3 &&
-       git add check dir1 dir2 top &&
+       git add check dir1 dir2 top foo &&
        test_tick
        git-commit -m initial &&
 
@@ -76,4 +77,12 @@ test_expect_success 'change gets noticed' '
 
 '
 
+test_expect_success 'replace a file with a symlink' '
+
+       rm foo &&
+       ln -s top foo &&
+       git add -u -- foo
+
+'
+
 test_done
index 6bfb899ed10606886ea3ce444f636b687bbfb66d..68b2b92879c5e187a33d34d3daf54bb3c131e40f 100755 (executable)
@@ -107,6 +107,10 @@ do
                diff expected actual
        '
 
+        test_expect_failure "grep -c $L (no /dev/null)" '
+               git grep -c test $H | grep -q "/dev/null"
+        '
+
 done
 
 test_done
index 52054201c2a8729e036a5d97337a5f85bcafc782..10ce6eedc7e8adbcc3b12e1987d2e16b532a4e07 100644 (file)
@@ -227,6 +227,7 @@ static void wt_status_print_updated(struct wt_status *s)
        rev.diffopt.format_callback = wt_status_print_updated_cb;
        rev.diffopt.format_callback_data = s;
        rev.diffopt.detect_rename = 1;
+       rev.diffopt.rename_limit = 100;
        wt_read_cache(s);
        run_diff_index(&rev, 1);
 }