Merge branch 'jc/grep-c'

* 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()
maint
Junio C Hamano 2007-09-14 15:17:07 -07:00
commit f6336167e9
10 changed files with 116 additions and 22 deletions

View File

@ -15,7 +15,8 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
Creates an archive of the specified format containing the tree 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. prepended to the filenames in the archive.


'git-archive' behaves differently when given a tree ID versus when 'git-archive' behaves differently when given a tree ID versus when
@ -31,7 +32,7 @@ OPTIONS
------- -------


--format=<fmt>:: --format=<fmt>::
Format of the resulting archive: 'tar', 'zip'... The default Format of the resulting archive: 'tar' or 'zip'. The default
is 'tar'. is 'tar'.


--list, -l:: --list, -l::

View File

@ -142,7 +142,7 @@ FILES
If not set explicitly with '--file', there are three files where If not set explicitly with '--file', there are three files where
git-config will search for configuration options: git-config will search for configuration options:


.git/config:: $GIT_DIR/config::
Repository specific configuration file. (The filename is Repository specific configuration file. (The filename is
of course relative to the repository root, not the working of course relative to the repository root, not the working
directory.) directory.)

View File

@ -95,7 +95,7 @@ static void update_callback(struct diff_queue_struct *q,
const char *path = p->one->path; const char *path = p->one->path;
switch (p->status) { switch (p->status) {
default: default:
die("unexpacted diff status %c", p->status); die("unexpected diff status %c", p->status);
case DIFF_STATUS_UNMERGED: case DIFF_STATUS_UNMERGED:
case DIFF_STATUS_MODIFIED: case DIFF_STATUS_MODIFIED:
case DIFF_STATUS_TYPE_CHANGED: case DIFF_STATUS_TYPE_CHANGED:

View File

@ -187,6 +187,78 @@ static int exec_grep(int argc, const char **argv)
else die("maximum number of args exceeded"); \ else die("maximum number of args exceeded"); \
} while (0) } 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) static int external_grep(struct grep_opt *opt, const char **paths, int cached)
{ {
int i, nr, argc, hit, len, status; 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); 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; hit = 0;
argc = nr; argc = nr;
for (i = 0; i < active_nr; i++) { for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i]; struct cache_entry *ce = active_cache[i];
char *name; char *name;
int kept;
if (!S_ISREG(ntohl(ce->ce_mode))) if (!S_ISREG(ntohl(ce->ce_mode)))
continue; continue;
if (!pathspec_matches(paths, ce->name)) 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; argv[argc++] = name;
if (argc < MAXARGS && !ce_stage(ce)) if (argc < MAXARGS && !ce_stage(ce))
continue; continue;
status = exec_grep(argc, argv); status = flush_grep(opt, argc, nr, argv, &kept);
if (0 < status) if (0 < status)
hit = 1; hit = 1;
argc = nr; argc = nr + kept;
if (ce_stage(ce)) { if (ce_stage(ce)) {
do { do {
i++; i++;
@ -296,7 +358,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
} }
} }
if (argc > nr) { if (argc > nr) {
status = exec_grep(argc, argv); status = flush_grep(opt, argc, nr, argv, NULL);
if (0 < status) if (0 < status)
hit = 1; hit = 1;
} }

View File

@ -298,6 +298,8 @@ int setup_diff_no_index(struct rev_info *revs,
revs->diffopt.nr_paths = 2; revs->diffopt.nr_paths = 2;
revs->diffopt.no_index = 1; revs->diffopt.no_index = 1;
revs->max_count = -2; revs->max_count = -2;
if (diff_setup_done(&revs->diffopt) < 0)
die("diff_setup_done failed");
return 0; return 0;
} }



2
diff.c
View File

@ -17,7 +17,7 @@
#endif #endif


static int diff_detect_rename_default; 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; static int diff_use_color_default;
int diff_auto_refresh_index = 1; int diff_auto_refresh_index = 1;



View File

@ -298,10 +298,25 @@ void diffcore_rename(struct diff_options *options)
else if (detect_rename == DIFF_DETECT_COPY) else if (detect_rename == DIFF_DETECT_COPY)
register_rename_src(p->one, 1, p->score); register_rename_src(p->one, 1, p->score);
} }
if (rename_dst_nr == 0 || rename_src_nr == 0 || if (rename_dst_nr == 0 || rename_src_nr == 0)
(0 < rename_limit && rename_limit < rename_dst_nr))
goto cleanup; /* nothing to do */ 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 /* We really want to cull the candidates list early
* with cheap tests in order to avoid doing deltas. * with cheap tests in order to avoid doing deltas.
* The first round matches up the up-to-date entries, * The first round matches up the up-to-date entries,

View File

@ -16,11 +16,12 @@ only the updates to dir/sub.'
test_expect_success setup ' test_expect_success setup '
echo initial >check && echo initial >check &&
echo initial >top && echo initial >top &&
echo initial >foo &&
mkdir dir1 dir2 && mkdir dir1 dir2 &&
echo initial >dir1/sub1 && echo initial >dir1/sub1 &&
echo initial >dir1/sub2 && echo initial >dir1/sub2 &&
echo initial >dir2/sub3 && echo initial >dir2/sub3 &&
git add check dir1 dir2 top && git add check dir1 dir2 top foo &&
test_tick test_tick
git-commit -m initial && 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 test_done

View File

@ -107,6 +107,10 @@ do
diff expected actual diff expected actual
' '


test_expect_failure "grep -c $L (no /dev/null)" '
git grep -c test $H | grep -q "/dev/null"
'

done done


test_done test_done

View 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 = wt_status_print_updated_cb;
rev.diffopt.format_callback_data = s; rev.diffopt.format_callback_data = s;
rev.diffopt.detect_rename = 1; rev.diffopt.detect_rename = 1;
rev.diffopt.rename_limit = 100;
wt_read_cache(s); wt_read_cache(s);
run_diff_index(&rev, 1); run_diff_index(&rev, 1);
} }