Merge branch 'bw/grep-recurse-submodules'
"git grep" has been taught to optionally recurse into submodules. * bw/grep-recurse-submodules: grep: search history of moved submodules grep: enable recurse-submodules to work on <tree> objects grep: optionally recurse into submodules grep: add submodules as a grep source type submodules: load gitmodules file from commit sha1 submodules: add helper to determine if a submodule is initialized submodules: add helper to determine if a submodule is populated real_path: canonicalize directory separators in root parts real_path: have callers use real_pathdup and strbuf_realpath real_path: create real_pathdup real_path: convert real_path_internal to strbuf_realpath real_path: resolve symlinks by hand
This commit is contained in:
commit
55d128ae06
@ -26,6 +26,7 @@ SYNOPSIS
|
|||||||
[--threads <num>]
|
[--threads <num>]
|
||||||
[-f <file>] [-e] <pattern>
|
[-f <file>] [-e] <pattern>
|
||||||
[--and|--or|--not|(|)|-e <pattern>...]
|
[--and|--or|--not|(|)|-e <pattern>...]
|
||||||
|
[--recurse-submodules] [--parent-basename <basename>]
|
||||||
[ [--[no-]exclude-standard] [--cached | --no-index | --untracked] | <tree>...]
|
[ [--[no-]exclude-standard] [--cached | --no-index | --untracked] | <tree>...]
|
||||||
[--] [<pathspec>...]
|
[--] [<pathspec>...]
|
||||||
|
|
||||||
@ -88,6 +89,19 @@ OPTIONS
|
|||||||
mechanism. Only useful when searching files in the current directory
|
mechanism. Only useful when searching files in the current directory
|
||||||
with `--no-index`.
|
with `--no-index`.
|
||||||
|
|
||||||
|
--recurse-submodules::
|
||||||
|
Recursively search in each submodule that has been initialized and
|
||||||
|
checked out in the repository. When used in combination with the
|
||||||
|
<tree> option the prefix of all submodule output will be the name of
|
||||||
|
the parent project's <tree> object.
|
||||||
|
|
||||||
|
--parent-basename <basename>::
|
||||||
|
For internal use only. In order to produce uniform output with the
|
||||||
|
--recurse-submodules option, this option can be used to provide the
|
||||||
|
basename of a parent's <tree> object to a submodule so the submodule
|
||||||
|
can prefix its output with the parent's name rather than the SHA1 of
|
||||||
|
the submodule.
|
||||||
|
|
||||||
-a::
|
-a::
|
||||||
--text::
|
--text::
|
||||||
Process binary files as if they were text.
|
Process binary files as if they were text.
|
||||||
|
239
abspath.c
239
abspath.c
@ -11,46 +11,81 @@ int is_directory(const char *path)
|
|||||||
return (!stat(path, &st) && S_ISDIR(st.st_mode));
|
return (!stat(path, &st) && S_ISDIR(st.st_mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* removes the last path component from 'path' except if 'path' is root */
|
||||||
|
static void strip_last_component(struct strbuf *path)
|
||||||
|
{
|
||||||
|
size_t offset = offset_1st_component(path->buf);
|
||||||
|
size_t len = path->len;
|
||||||
|
|
||||||
|
/* Find start of the last component */
|
||||||
|
while (offset < len && !is_dir_sep(path->buf[len - 1]))
|
||||||
|
len--;
|
||||||
|
/* Skip sequences of multiple path-separators */
|
||||||
|
while (offset < len && is_dir_sep(path->buf[len - 1]))
|
||||||
|
len--;
|
||||||
|
|
||||||
|
strbuf_setlen(path, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get (and remove) the next component in 'remaining' and place it in 'next' */
|
||||||
|
static void get_next_component(struct strbuf *next, struct strbuf *remaining)
|
||||||
|
{
|
||||||
|
char *start = NULL;
|
||||||
|
char *end = NULL;
|
||||||
|
|
||||||
|
strbuf_reset(next);
|
||||||
|
|
||||||
|
/* look for the next component */
|
||||||
|
/* Skip sequences of multiple path-separators */
|
||||||
|
for (start = remaining->buf; is_dir_sep(*start); start++)
|
||||||
|
; /* nothing */
|
||||||
|
/* Find end of the path component */
|
||||||
|
for (end = start; *end && !is_dir_sep(*end); end++)
|
||||||
|
; /* nothing */
|
||||||
|
|
||||||
|
strbuf_add(next, start, end - start);
|
||||||
|
/* remove the component from 'remaining' */
|
||||||
|
strbuf_remove(remaining, 0, end - remaining->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copies root part from remaining to resolved, canonicalizing it on the way */
|
||||||
|
static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)
|
||||||
|
{
|
||||||
|
int offset = offset_1st_component(remaining->buf);
|
||||||
|
|
||||||
|
strbuf_reset(resolved);
|
||||||
|
strbuf_add(resolved, remaining->buf, offset);
|
||||||
|
#ifdef GIT_WINDOWS_NATIVE
|
||||||
|
convert_slashes(resolved->buf);
|
||||||
|
#endif
|
||||||
|
strbuf_remove(remaining, 0, offset);
|
||||||
|
}
|
||||||
|
|
||||||
/* We allow "recursive" symbolic links. Only within reason, though. */
|
/* We allow "recursive" symbolic links. Only within reason, though. */
|
||||||
#define MAXDEPTH 5
|
#define MAXSYMLINKS 5
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the real path (i.e., absolute path, with symlinks resolved
|
* Return the real path (i.e., absolute path, with symlinks resolved
|
||||||
* and extra slashes removed) equivalent to the specified path. (If
|
* and extra slashes removed) equivalent to the specified path. (If
|
||||||
* you want an absolute path but don't mind links, use
|
* you want an absolute path but don't mind links, use
|
||||||
* absolute_path().) The return value is a pointer to a static
|
* absolute_path().) Places the resolved realpath in the provided strbuf.
|
||||||
* buffer.
|
|
||||||
*
|
*
|
||||||
* The input and all intermediate paths must be shorter than MAX_PATH.
|
|
||||||
* The directory part of path (i.e., everything up to the last
|
* The directory part of path (i.e., everything up to the last
|
||||||
* dir_sep) must denote a valid, existing directory, but the last
|
* dir_sep) must denote a valid, existing directory, but the last
|
||||||
* component need not exist. If die_on_error is set, then die with an
|
* component need not exist. If die_on_error is set, then die with an
|
||||||
* informative error message if there is a problem. Otherwise, return
|
* informative error message if there is a problem. Otherwise, return
|
||||||
* NULL on errors (without generating any output).
|
* NULL on errors (without generating any output).
|
||||||
*
|
|
||||||
* If path is our buffer, then return path, as it's already what the
|
|
||||||
* user wants.
|
|
||||||
*/
|
*/
|
||||||
static const char *real_path_internal(const char *path, int die_on_error)
|
char *strbuf_realpath(struct strbuf *resolved, const char *path,
|
||||||
|
int die_on_error)
|
||||||
{
|
{
|
||||||
static struct strbuf sb = STRBUF_INIT;
|
struct strbuf remaining = STRBUF_INIT;
|
||||||
|
struct strbuf next = STRBUF_INIT;
|
||||||
|
struct strbuf symlink = STRBUF_INIT;
|
||||||
char *retval = NULL;
|
char *retval = NULL;
|
||||||
|
int num_symlinks = 0;
|
||||||
/*
|
|
||||||
* If we have to temporarily chdir(), store the original CWD
|
|
||||||
* here so that we can chdir() back to it at the end of the
|
|
||||||
* function:
|
|
||||||
*/
|
|
||||||
struct strbuf cwd = STRBUF_INIT;
|
|
||||||
|
|
||||||
int depth = MAXDEPTH;
|
|
||||||
char *last_elem = NULL;
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
/* We've already done it */
|
|
||||||
if (path == sb.buf)
|
|
||||||
return path;
|
|
||||||
|
|
||||||
if (!*path) {
|
if (!*path) {
|
||||||
if (die_on_error)
|
if (die_on_error)
|
||||||
die("The empty string is not a valid path");
|
die("The empty string is not a valid path");
|
||||||
@ -58,86 +93,134 @@ static const char *real_path_internal(const char *path, int die_on_error)
|
|||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
strbuf_reset(&sb);
|
strbuf_addstr(&remaining, path);
|
||||||
strbuf_addstr(&sb, path);
|
get_root_part(resolved, &remaining);
|
||||||
|
|
||||||
while (depth--) {
|
if (!resolved->len) {
|
||||||
if (!is_directory(sb.buf)) {
|
/* relative path; can use CWD as the initial resolved path */
|
||||||
char *last_slash = find_last_dir_sep(sb.buf);
|
if (strbuf_getcwd(resolved)) {
|
||||||
if (last_slash) {
|
|
||||||
last_elem = xstrdup(last_slash + 1);
|
|
||||||
strbuf_setlen(&sb, last_slash - sb.buf + 1);
|
|
||||||
} else {
|
|
||||||
last_elem = xmemdupz(sb.buf, sb.len);
|
|
||||||
strbuf_reset(&sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sb.len) {
|
|
||||||
if (!cwd.len && strbuf_getcwd(&cwd)) {
|
|
||||||
if (die_on_error)
|
|
||||||
die_errno("Could not get current working directory");
|
|
||||||
else
|
|
||||||
goto error_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chdir(sb.buf)) {
|
|
||||||
if (die_on_error)
|
|
||||||
die_errno("Could not switch to '%s'",
|
|
||||||
sb.buf);
|
|
||||||
else
|
|
||||||
goto error_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (strbuf_getcwd(&sb)) {
|
|
||||||
if (die_on_error)
|
if (die_on_error)
|
||||||
die_errno("Could not get current working directory");
|
die_errno("unable to get current working directory");
|
||||||
else
|
else
|
||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (last_elem) {
|
/* Iterate over the remaining path components */
|
||||||
if (sb.len && !is_dir_sep(sb.buf[sb.len - 1]))
|
while (remaining.len > 0) {
|
||||||
strbuf_addch(&sb, '/');
|
get_next_component(&next, &remaining);
|
||||||
strbuf_addstr(&sb, last_elem);
|
|
||||||
free(last_elem);
|
if (next.len == 0) {
|
||||||
last_elem = NULL;
|
continue; /* empty component */
|
||||||
|
} else if (next.len == 1 && !strcmp(next.buf, ".")) {
|
||||||
|
continue; /* '.' component */
|
||||||
|
} else if (next.len == 2 && !strcmp(next.buf, "..")) {
|
||||||
|
/* '..' component; strip the last path component */
|
||||||
|
strip_last_component(resolved);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) {
|
/* append the next component and resolve resultant path */
|
||||||
struct strbuf next_sb = STRBUF_INIT;
|
if (!is_dir_sep(resolved->buf[resolved->len - 1]))
|
||||||
ssize_t len = strbuf_readlink(&next_sb, sb.buf, 0);
|
strbuf_addch(resolved, '/');
|
||||||
if (len < 0) {
|
strbuf_addbuf(resolved, &next);
|
||||||
|
|
||||||
|
if (lstat(resolved->buf, &st)) {
|
||||||
|
/* error out unless this was the last component */
|
||||||
|
if (errno != ENOENT || remaining.len) {
|
||||||
if (die_on_error)
|
if (die_on_error)
|
||||||
die_errno("Invalid symlink '%s'",
|
die_errno("Invalid path '%s'",
|
||||||
sb.buf);
|
resolved->buf);
|
||||||
else
|
else
|
||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
strbuf_swap(&sb, &next_sb);
|
} else if (S_ISLNK(st.st_mode)) {
|
||||||
strbuf_release(&next_sb);
|
ssize_t len;
|
||||||
} else
|
strbuf_reset(&symlink);
|
||||||
break;
|
|
||||||
|
if (num_symlinks++ > MAXSYMLINKS) {
|
||||||
|
if (die_on_error)
|
||||||
|
die("More than %d nested symlinks "
|
||||||
|
"on path '%s'", MAXSYMLINKS, path);
|
||||||
|
else
|
||||||
|
goto error_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = strbuf_readlink(&symlink, resolved->buf,
|
||||||
|
st.st_size);
|
||||||
|
if (len < 0) {
|
||||||
|
if (die_on_error)
|
||||||
|
die_errno("Invalid symlink '%s'",
|
||||||
|
resolved->buf);
|
||||||
|
else
|
||||||
|
goto error_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_absolute_path(symlink.buf)) {
|
||||||
|
/* absolute symlink; set resolved to root */
|
||||||
|
get_root_part(resolved, &symlink);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* relative symlink
|
||||||
|
* strip off the last component since it will
|
||||||
|
* be replaced with the contents of the symlink
|
||||||
|
*/
|
||||||
|
strip_last_component(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if there are still remaining components to resolve
|
||||||
|
* then append them to symlink
|
||||||
|
*/
|
||||||
|
if (remaining.len) {
|
||||||
|
strbuf_addch(&symlink, '/');
|
||||||
|
strbuf_addbuf(&symlink, &remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* use the symlink as the remaining components that
|
||||||
|
* need to be resloved
|
||||||
|
*/
|
||||||
|
strbuf_swap(&symlink, &remaining);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = sb.buf;
|
retval = resolved->buf;
|
||||||
|
|
||||||
error_out:
|
error_out:
|
||||||
free(last_elem);
|
strbuf_release(&remaining);
|
||||||
if (cwd.len && chdir(cwd.buf))
|
strbuf_release(&next);
|
||||||
die_errno("Could not change back to '%s'", cwd.buf);
|
strbuf_release(&symlink);
|
||||||
strbuf_release(&cwd);
|
|
||||||
|
if (!retval)
|
||||||
|
strbuf_reset(resolved);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *real_path(const char *path)
|
const char *real_path(const char *path)
|
||||||
{
|
{
|
||||||
return real_path_internal(path, 1);
|
static struct strbuf realpath = STRBUF_INIT;
|
||||||
|
return strbuf_realpath(&realpath, path, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *real_path_if_valid(const char *path)
|
const char *real_path_if_valid(const char *path)
|
||||||
{
|
{
|
||||||
return real_path_internal(path, 0);
|
static struct strbuf realpath = STRBUF_INIT;
|
||||||
|
return strbuf_realpath(&realpath, path, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *real_pathdup(const char *path)
|
||||||
|
{
|
||||||
|
struct strbuf realpath = STRBUF_INIT;
|
||||||
|
char *retval = NULL;
|
||||||
|
|
||||||
|
if (strbuf_realpath(&realpath, path, 0))
|
||||||
|
retval = strbuf_detach(&realpath, NULL);
|
||||||
|
|
||||||
|
strbuf_release(&realpath);
|
||||||
|
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
386
builtin/grep.c
386
builtin/grep.c
@ -18,12 +18,22 @@
|
|||||||
#include "quote.h"
|
#include "quote.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
#include "pathspec.h"
|
#include "pathspec.h"
|
||||||
|
#include "submodule.h"
|
||||||
|
#include "submodule-config.h"
|
||||||
|
|
||||||
static char const * const grep_usage[] = {
|
static char const * const grep_usage[] = {
|
||||||
N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
|
N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *super_prefix;
|
||||||
|
static int recurse_submodules;
|
||||||
|
static struct argv_array submodule_options = ARGV_ARRAY_INIT;
|
||||||
|
static const char *parent_basename;
|
||||||
|
|
||||||
|
static int grep_submodule_launch(struct grep_opt *opt,
|
||||||
|
const struct grep_source *gs);
|
||||||
|
|
||||||
#define GREP_NUM_THREADS_DEFAULT 8
|
#define GREP_NUM_THREADS_DEFAULT 8
|
||||||
static int num_threads;
|
static int num_threads;
|
||||||
|
|
||||||
@ -174,7 +184,10 @@ static void *run(void *arg)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
opt->output_priv = w;
|
opt->output_priv = w;
|
||||||
hit |= grep_source(opt, &w->source);
|
if (w->source.type == GREP_SOURCE_SUBMODULE)
|
||||||
|
hit |= grep_submodule_launch(opt, &w->source);
|
||||||
|
else
|
||||||
|
hit |= grep_source(opt, &w->source);
|
||||||
grep_source_clear_data(&w->source);
|
grep_source_clear_data(&w->source);
|
||||||
work_done(w);
|
work_done(w);
|
||||||
}
|
}
|
||||||
@ -300,6 +313,10 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
|
|||||||
if (opt->relative && opt->prefix_length) {
|
if (opt->relative && opt->prefix_length) {
|
||||||
quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
|
quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
|
||||||
strbuf_insert(&pathbuf, 0, filename, tree_name_len);
|
strbuf_insert(&pathbuf, 0, filename, tree_name_len);
|
||||||
|
} else if (super_prefix) {
|
||||||
|
strbuf_add(&pathbuf, filename, tree_name_len);
|
||||||
|
strbuf_addstr(&pathbuf, super_prefix);
|
||||||
|
strbuf_addstr(&pathbuf, filename + tree_name_len);
|
||||||
} else {
|
} else {
|
||||||
strbuf_addstr(&pathbuf, filename);
|
strbuf_addstr(&pathbuf, filename);
|
||||||
}
|
}
|
||||||
@ -328,10 +345,13 @@ static int grep_file(struct grep_opt *opt, const char *filename)
|
|||||||
{
|
{
|
||||||
struct strbuf buf = STRBUF_INIT;
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
|
||||||
if (opt->relative && opt->prefix_length)
|
if (opt->relative && opt->prefix_length) {
|
||||||
quote_path_relative(filename, opt->prefix, &buf);
|
quote_path_relative(filename, opt->prefix, &buf);
|
||||||
else
|
} else {
|
||||||
|
if (super_prefix)
|
||||||
|
strbuf_addstr(&buf, super_prefix);
|
||||||
strbuf_addstr(&buf, filename);
|
strbuf_addstr(&buf, filename);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef NO_PTHREADS
|
#ifndef NO_PTHREADS
|
||||||
if (num_threads) {
|
if (num_threads) {
|
||||||
@ -378,31 +398,310 @@ static void run_pager(struct grep_opt *opt, const char *prefix)
|
|||||||
exit(status);
|
exit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)
|
static void compile_submodule_options(const struct grep_opt *opt,
|
||||||
|
const struct pathspec *pathspec,
|
||||||
|
int cached, int untracked,
|
||||||
|
int opt_exclude, int use_index,
|
||||||
|
int pattern_type_arg)
|
||||||
|
{
|
||||||
|
struct grep_pat *pattern;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (recurse_submodules)
|
||||||
|
argv_array_push(&submodule_options, "--recurse-submodules");
|
||||||
|
|
||||||
|
if (cached)
|
||||||
|
argv_array_push(&submodule_options, "--cached");
|
||||||
|
if (!use_index)
|
||||||
|
argv_array_push(&submodule_options, "--no-index");
|
||||||
|
if (untracked)
|
||||||
|
argv_array_push(&submodule_options, "--untracked");
|
||||||
|
if (opt_exclude > 0)
|
||||||
|
argv_array_push(&submodule_options, "--exclude-standard");
|
||||||
|
|
||||||
|
if (opt->invert)
|
||||||
|
argv_array_push(&submodule_options, "-v");
|
||||||
|
if (opt->ignore_case)
|
||||||
|
argv_array_push(&submodule_options, "-i");
|
||||||
|
if (opt->word_regexp)
|
||||||
|
argv_array_push(&submodule_options, "-w");
|
||||||
|
switch (opt->binary) {
|
||||||
|
case GREP_BINARY_NOMATCH:
|
||||||
|
argv_array_push(&submodule_options, "-I");
|
||||||
|
break;
|
||||||
|
case GREP_BINARY_TEXT:
|
||||||
|
argv_array_push(&submodule_options, "-a");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (opt->allow_textconv)
|
||||||
|
argv_array_push(&submodule_options, "--textconv");
|
||||||
|
if (opt->max_depth != -1)
|
||||||
|
argv_array_pushf(&submodule_options, "--max-depth=%d",
|
||||||
|
opt->max_depth);
|
||||||
|
if (opt->linenum)
|
||||||
|
argv_array_push(&submodule_options, "-n");
|
||||||
|
if (!opt->pathname)
|
||||||
|
argv_array_push(&submodule_options, "-h");
|
||||||
|
if (!opt->relative)
|
||||||
|
argv_array_push(&submodule_options, "--full-name");
|
||||||
|
if (opt->name_only)
|
||||||
|
argv_array_push(&submodule_options, "-l");
|
||||||
|
if (opt->unmatch_name_only)
|
||||||
|
argv_array_push(&submodule_options, "-L");
|
||||||
|
if (opt->null_following_name)
|
||||||
|
argv_array_push(&submodule_options, "-z");
|
||||||
|
if (opt->count)
|
||||||
|
argv_array_push(&submodule_options, "-c");
|
||||||
|
if (opt->file_break)
|
||||||
|
argv_array_push(&submodule_options, "--break");
|
||||||
|
if (opt->heading)
|
||||||
|
argv_array_push(&submodule_options, "--heading");
|
||||||
|
if (opt->pre_context)
|
||||||
|
argv_array_pushf(&submodule_options, "--before-context=%d",
|
||||||
|
opt->pre_context);
|
||||||
|
if (opt->post_context)
|
||||||
|
argv_array_pushf(&submodule_options, "--after-context=%d",
|
||||||
|
opt->post_context);
|
||||||
|
if (opt->funcname)
|
||||||
|
argv_array_push(&submodule_options, "-p");
|
||||||
|
if (opt->funcbody)
|
||||||
|
argv_array_push(&submodule_options, "-W");
|
||||||
|
if (opt->all_match)
|
||||||
|
argv_array_push(&submodule_options, "--all-match");
|
||||||
|
if (opt->debug)
|
||||||
|
argv_array_push(&submodule_options, "--debug");
|
||||||
|
if (opt->status_only)
|
||||||
|
argv_array_push(&submodule_options, "-q");
|
||||||
|
|
||||||
|
switch (pattern_type_arg) {
|
||||||
|
case GREP_PATTERN_TYPE_BRE:
|
||||||
|
argv_array_push(&submodule_options, "-G");
|
||||||
|
break;
|
||||||
|
case GREP_PATTERN_TYPE_ERE:
|
||||||
|
argv_array_push(&submodule_options, "-E");
|
||||||
|
break;
|
||||||
|
case GREP_PATTERN_TYPE_FIXED:
|
||||||
|
argv_array_push(&submodule_options, "-F");
|
||||||
|
break;
|
||||||
|
case GREP_PATTERN_TYPE_PCRE:
|
||||||
|
argv_array_push(&submodule_options, "-P");
|
||||||
|
break;
|
||||||
|
case GREP_PATTERN_TYPE_UNSPECIFIED:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pattern = opt->pattern_list; pattern != NULL;
|
||||||
|
pattern = pattern->next) {
|
||||||
|
switch (pattern->token) {
|
||||||
|
case GREP_PATTERN:
|
||||||
|
argv_array_pushf(&submodule_options, "-e%s",
|
||||||
|
pattern->pattern);
|
||||||
|
break;
|
||||||
|
case GREP_AND:
|
||||||
|
case GREP_OPEN_PAREN:
|
||||||
|
case GREP_CLOSE_PAREN:
|
||||||
|
case GREP_NOT:
|
||||||
|
case GREP_OR:
|
||||||
|
argv_array_push(&submodule_options, pattern->pattern);
|
||||||
|
break;
|
||||||
|
/* BODY and HEAD are not used by git-grep */
|
||||||
|
case GREP_PATTERN_BODY:
|
||||||
|
case GREP_PATTERN_HEAD:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Limit number of threads for child process to use.
|
||||||
|
* This is to prevent potential fork-bomb behavior of git-grep as each
|
||||||
|
* submodule process has its own thread pool.
|
||||||
|
*/
|
||||||
|
argv_array_pushf(&submodule_options, "--threads=%d",
|
||||||
|
(num_threads + 1) / 2);
|
||||||
|
|
||||||
|
/* Add Pathspecs */
|
||||||
|
argv_array_push(&submodule_options, "--");
|
||||||
|
for (i = 0; i < pathspec->nr; i++)
|
||||||
|
argv_array_push(&submodule_options,
|
||||||
|
pathspec->items[i].original);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Launch child process to grep contents of a submodule
|
||||||
|
*/
|
||||||
|
static int grep_submodule_launch(struct grep_opt *opt,
|
||||||
|
const struct grep_source *gs)
|
||||||
|
{
|
||||||
|
struct child_process cp = CHILD_PROCESS_INIT;
|
||||||
|
int status, i;
|
||||||
|
const char *end_of_base;
|
||||||
|
const char *name;
|
||||||
|
struct work_item *w = opt->output_priv;
|
||||||
|
|
||||||
|
end_of_base = strchr(gs->name, ':');
|
||||||
|
if (gs->identifier && end_of_base)
|
||||||
|
name = end_of_base + 1;
|
||||||
|
else
|
||||||
|
name = gs->name;
|
||||||
|
|
||||||
|
prepare_submodule_repo_env(&cp.env_array);
|
||||||
|
argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT);
|
||||||
|
|
||||||
|
/* Add super prefix */
|
||||||
|
argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
|
||||||
|
super_prefix ? super_prefix : "",
|
||||||
|
name);
|
||||||
|
argv_array_push(&cp.args, "grep");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add basename of parent project
|
||||||
|
* When performing grep on a tree object the filename is prefixed
|
||||||
|
* with the object's name: 'tree-name:filename'. In order to
|
||||||
|
* provide uniformity of output we want to pass the name of the
|
||||||
|
* parent project's object name to the submodule so the submodule can
|
||||||
|
* prefix its output with the parent's name and not its own SHA1.
|
||||||
|
*/
|
||||||
|
if (gs->identifier && end_of_base)
|
||||||
|
argv_array_pushf(&cp.args, "--parent-basename=%.*s",
|
||||||
|
(int) (end_of_base - gs->name),
|
||||||
|
gs->name);
|
||||||
|
|
||||||
|
/* Add options */
|
||||||
|
for (i = 0; i < submodule_options.argc; i++) {
|
||||||
|
/*
|
||||||
|
* If there is a tree identifier for the submodule, add the
|
||||||
|
* rev after adding the submodule options but before the
|
||||||
|
* pathspecs. To do this we listen for the '--' and insert the
|
||||||
|
* sha1 before pushing the '--' onto the child process argv
|
||||||
|
* array.
|
||||||
|
*/
|
||||||
|
if (gs->identifier &&
|
||||||
|
!strcmp("--", submodule_options.argv[i])) {
|
||||||
|
argv_array_push(&cp.args, sha1_to_hex(gs->identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
argv_array_push(&cp.args, submodule_options.argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cp.git_cmd = 1;
|
||||||
|
cp.dir = gs->path;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Capture output to output buffer and check the return code from the
|
||||||
|
* child process. A '0' indicates a hit, a '1' indicates no hit and
|
||||||
|
* anything else is an error.
|
||||||
|
*/
|
||||||
|
status = capture_command(&cp, &w->out, 0);
|
||||||
|
if (status && (status != 1)) {
|
||||||
|
/* flush the buffer */
|
||||||
|
write_or_die(1, w->out.buf, w->out.len);
|
||||||
|
die("process for submodule '%s' failed with exit code: %d",
|
||||||
|
gs->name, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* invert the return code to make a hit equal to 1 */
|
||||||
|
return !status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prep grep structures for a submodule grep
|
||||||
|
* sha1: the sha1 of the submodule or NULL if using the working tree
|
||||||
|
* filename: name of the submodule including tree name of parent
|
||||||
|
* path: location of the submodule
|
||||||
|
*/
|
||||||
|
static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1,
|
||||||
|
const char *filename, const char *path)
|
||||||
|
{
|
||||||
|
if (!is_submodule_initialized(path))
|
||||||
|
return 0;
|
||||||
|
if (!is_submodule_populated(path)) {
|
||||||
|
/*
|
||||||
|
* If searching history, check for the presense of the
|
||||||
|
* submodule's gitdir before skipping the submodule.
|
||||||
|
*/
|
||||||
|
if (sha1) {
|
||||||
|
const struct submodule *sub =
|
||||||
|
submodule_from_path(null_sha1, path);
|
||||||
|
if (sub)
|
||||||
|
path = git_path("modules/%s", sub->name);
|
||||||
|
|
||||||
|
if (!(is_directory(path) && is_git_directory(path)))
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_PTHREADS
|
||||||
|
if (num_threads) {
|
||||||
|
add_work(opt, GREP_SOURCE_SUBMODULE, filename, path, sha1);
|
||||||
|
return 0;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
struct work_item w;
|
||||||
|
int hit;
|
||||||
|
|
||||||
|
grep_source_init(&w.source, GREP_SOURCE_SUBMODULE,
|
||||||
|
filename, path, sha1);
|
||||||
|
strbuf_init(&w.out, 0);
|
||||||
|
opt->output_priv = &w;
|
||||||
|
hit = grep_submodule_launch(opt, &w.source);
|
||||||
|
|
||||||
|
write_or_die(1, w.out.buf, w.out.len);
|
||||||
|
|
||||||
|
grep_source_clear(&w.source);
|
||||||
|
strbuf_release(&w.out);
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
|
||||||
|
int cached)
|
||||||
{
|
{
|
||||||
int hit = 0;
|
int hit = 0;
|
||||||
int nr;
|
int nr;
|
||||||
|
struct strbuf name = STRBUF_INIT;
|
||||||
|
int name_base_len = 0;
|
||||||
|
if (super_prefix) {
|
||||||
|
name_base_len = strlen(super_prefix);
|
||||||
|
strbuf_addstr(&name, super_prefix);
|
||||||
|
}
|
||||||
|
|
||||||
read_cache();
|
read_cache();
|
||||||
|
|
||||||
for (nr = 0; nr < active_nr; nr++) {
|
for (nr = 0; nr < active_nr; nr++) {
|
||||||
const struct cache_entry *ce = active_cache[nr];
|
const struct cache_entry *ce = active_cache[nr];
|
||||||
if (!S_ISREG(ce->ce_mode))
|
strbuf_setlen(&name, name_base_len);
|
||||||
|
strbuf_addstr(&name, ce->name);
|
||||||
|
|
||||||
|
if (S_ISREG(ce->ce_mode) &&
|
||||||
|
match_pathspec(pathspec, name.buf, name.len, 0, NULL,
|
||||||
|
S_ISDIR(ce->ce_mode) ||
|
||||||
|
S_ISGITLINK(ce->ce_mode))) {
|
||||||
|
/*
|
||||||
|
* If CE_VALID is on, we assume worktree file and its
|
||||||
|
* cache entry are identical, even if worktree file has
|
||||||
|
* been modified, so use cache version instead
|
||||||
|
*/
|
||||||
|
if (cached || (ce->ce_flags & CE_VALID) ||
|
||||||
|
ce_skip_worktree(ce)) {
|
||||||
|
if (ce_stage(ce) || ce_intent_to_add(ce))
|
||||||
|
continue;
|
||||||
|
hit |= grep_sha1(opt, ce->oid.hash, ce->name,
|
||||||
|
0, ce->name);
|
||||||
|
} else {
|
||||||
|
hit |= grep_file(opt, ce->name);
|
||||||
|
}
|
||||||
|
} else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
|
||||||
|
submodule_path_match(pathspec, name.buf, NULL)) {
|
||||||
|
hit |= grep_submodule(opt, NULL, ce->name, ce->name);
|
||||||
|
} else {
|
||||||
continue;
|
continue;
|
||||||
if (!ce_path_match(ce, pathspec, NULL))
|
|
||||||
continue;
|
|
||||||
/*
|
|
||||||
* If CE_VALID is on, we assume worktree file and its cache entry
|
|
||||||
* are identical, even if worktree file has been modified, so use
|
|
||||||
* cache version instead
|
|
||||||
*/
|
|
||||||
if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) {
|
|
||||||
if (ce_stage(ce) || ce_intent_to_add(ce))
|
|
||||||
continue;
|
|
||||||
hit |= grep_sha1(opt, ce->oid.hash, ce->name, 0,
|
|
||||||
ce->name);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
hit |= grep_file(opt, ce->name);
|
|
||||||
if (ce_stage(ce)) {
|
if (ce_stage(ce)) {
|
||||||
do {
|
do {
|
||||||
nr++;
|
nr++;
|
||||||
@ -413,6 +712,8 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
|
|||||||
if (hit && opt->status_only)
|
if (hit && opt->status_only)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strbuf_release(&name);
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,12 +725,22 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
|||||||
enum interesting match = entry_not_interesting;
|
enum interesting match = entry_not_interesting;
|
||||||
struct name_entry entry;
|
struct name_entry entry;
|
||||||
int old_baselen = base->len;
|
int old_baselen = base->len;
|
||||||
|
struct strbuf name = STRBUF_INIT;
|
||||||
|
int name_base_len = 0;
|
||||||
|
if (super_prefix) {
|
||||||
|
strbuf_addstr(&name, super_prefix);
|
||||||
|
name_base_len = name.len;
|
||||||
|
}
|
||||||
|
|
||||||
while (tree_entry(tree, &entry)) {
|
while (tree_entry(tree, &entry)) {
|
||||||
int te_len = tree_entry_len(&entry);
|
int te_len = tree_entry_len(&entry);
|
||||||
|
|
||||||
if (match != all_entries_interesting) {
|
if (match != all_entries_interesting) {
|
||||||
match = tree_entry_interesting(&entry, base, tn_len, pathspec);
|
strbuf_addstr(&name, base->buf + tn_len);
|
||||||
|
match = tree_entry_interesting(&entry, &name,
|
||||||
|
0, pathspec);
|
||||||
|
strbuf_setlen(&name, name_base_len);
|
||||||
|
|
||||||
if (match == all_entries_not_interesting)
|
if (match == all_entries_not_interesting)
|
||||||
break;
|
break;
|
||||||
if (match == entry_not_interesting)
|
if (match == entry_not_interesting)
|
||||||
@ -441,8 +752,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
|||||||
if (S_ISREG(entry.mode)) {
|
if (S_ISREG(entry.mode)) {
|
||||||
hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
|
hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
|
||||||
check_attr ? base->buf + tn_len : NULL);
|
check_attr ? base->buf + tn_len : NULL);
|
||||||
}
|
} else if (S_ISDIR(entry.mode)) {
|
||||||
else if (S_ISDIR(entry.mode)) {
|
|
||||||
enum object_type type;
|
enum object_type type;
|
||||||
struct tree_desc sub;
|
struct tree_desc sub;
|
||||||
void *data;
|
void *data;
|
||||||
@ -458,12 +768,18 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
|||||||
hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
|
hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
|
||||||
check_attr);
|
check_attr);
|
||||||
free(data);
|
free(data);
|
||||||
|
} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
|
||||||
|
hit |= grep_submodule(opt, entry.oid->hash, base->buf,
|
||||||
|
base->buf + tn_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
strbuf_setlen(base, old_baselen);
|
strbuf_setlen(base, old_baselen);
|
||||||
|
|
||||||
if (hit && opt->status_only)
|
if (hit && opt->status_only)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strbuf_release(&name);
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,6 +803,10 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
|
|||||||
if (!data)
|
if (!data)
|
||||||
die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
|
die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
|
||||||
|
|
||||||
|
/* Use parent's name as base when recursing submodules */
|
||||||
|
if (recurse_submodules && parent_basename)
|
||||||
|
name = parent_basename;
|
||||||
|
|
||||||
len = name ? strlen(name) : 0;
|
len = name ? strlen(name) : 0;
|
||||||
strbuf_init(&base, PATH_MAX + len + 1);
|
strbuf_init(&base, PATH_MAX + len + 1);
|
||||||
if (len) {
|
if (len) {
|
||||||
@ -513,6 +833,12 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
|
|||||||
for (i = 0; i < nr; i++) {
|
for (i = 0; i < nr; i++) {
|
||||||
struct object *real_obj;
|
struct object *real_obj;
|
||||||
real_obj = deref_tag(list->objects[i].item, NULL, 0);
|
real_obj = deref_tag(list->objects[i].item, NULL, 0);
|
||||||
|
|
||||||
|
/* load the gitmodules file for this rev */
|
||||||
|
if (recurse_submodules) {
|
||||||
|
submodule_free();
|
||||||
|
gitmodules_config_sha1(real_obj->oid.hash);
|
||||||
|
}
|
||||||
if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path)) {
|
if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path)) {
|
||||||
hit = 1;
|
hit = 1;
|
||||||
if (opt->status_only)
|
if (opt->status_only)
|
||||||
@ -651,6 +977,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||||||
N_("search in both tracked and untracked files")),
|
N_("search in both tracked and untracked files")),
|
||||||
OPT_SET_INT(0, "exclude-standard", &opt_exclude,
|
OPT_SET_INT(0, "exclude-standard", &opt_exclude,
|
||||||
N_("ignore files specified via '.gitignore'"), 1),
|
N_("ignore files specified via '.gitignore'"), 1),
|
||||||
|
OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
|
||||||
|
N_("recursivley search in each submodule")),
|
||||||
|
OPT_STRING(0, "parent-basename", &parent_basename,
|
||||||
|
N_("basename"),
|
||||||
|
N_("prepend parent project's basename to output")),
|
||||||
OPT_GROUP(""),
|
OPT_GROUP(""),
|
||||||
OPT_BOOL('v', "invert-match", &opt.invert,
|
OPT_BOOL('v', "invert-match", &opt.invert,
|
||||||
N_("show non-matching lines")),
|
N_("show non-matching lines")),
|
||||||
@ -755,6 +1086,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||||||
init_grep_defaults();
|
init_grep_defaults();
|
||||||
git_config(grep_cmd_config, NULL);
|
git_config(grep_cmd_config, NULL);
|
||||||
grep_init(&opt, prefix);
|
grep_init(&opt, prefix);
|
||||||
|
super_prefix = get_super_prefix();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there is no -- then the paths must exist in the working
|
* If there is no -- then the paths must exist in the working
|
||||||
@ -872,6 +1204,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||||||
pathspec.max_depth = opt.max_depth;
|
pathspec.max_depth = opt.max_depth;
|
||||||
pathspec.recursive = 1;
|
pathspec.recursive = 1;
|
||||||
|
|
||||||
|
if (recurse_submodules) {
|
||||||
|
gitmodules_config();
|
||||||
|
compile_submodule_options(&opt, &pathspec, cached, untracked,
|
||||||
|
opt_exclude, use_index,
|
||||||
|
pattern_type_arg);
|
||||||
|
}
|
||||||
|
|
||||||
if (show_in_pager && (cached || list.nr))
|
if (show_in_pager && (cached || list.nr))
|
||||||
die(_("--open-files-in-pager only works on the worktree"));
|
die(_("--open-files-in-pager only works on the worktree"));
|
||||||
|
|
||||||
@ -895,6 +1234,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recurse_submodules && (!use_index || untracked))
|
||||||
|
die(_("option not supported with --recurse-submodules."));
|
||||||
|
|
||||||
if (!show_in_pager && !opt.status_only)
|
if (!show_in_pager && !opt.status_only)
|
||||||
setup_pager();
|
setup_pager();
|
||||||
|
|
||||||
|
@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
|
|||||||
{
|
{
|
||||||
int reinit;
|
int reinit;
|
||||||
int exist_ok = flags & INIT_DB_EXIST_OK;
|
int exist_ok = flags & INIT_DB_EXIST_OK;
|
||||||
char *original_git_dir = xstrdup(real_path(git_dir));
|
char *original_git_dir = real_pathdup(git_dir);
|
||||||
|
|
||||||
if (real_git_dir) {
|
if (real_git_dir) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
|
|||||||
argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
|
argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
|
||||||
|
|
||||||
if (real_git_dir && !is_absolute_path(real_git_dir))
|
if (real_git_dir && !is_absolute_path(real_git_dir))
|
||||||
real_git_dir = xstrdup(real_path(real_git_dir));
|
real_git_dir = real_pathdup(real_git_dir);
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
int mkdir_tried = 0;
|
int mkdir_tried = 0;
|
||||||
@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
|
|||||||
const char *git_dir_parent = strrchr(git_dir, '/');
|
const char *git_dir_parent = strrchr(git_dir, '/');
|
||||||
if (git_dir_parent) {
|
if (git_dir_parent) {
|
||||||
char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
|
char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
|
||||||
git_work_tree_cfg = xstrdup(real_path(rel));
|
git_work_tree_cfg = real_pathdup(rel);
|
||||||
free(rel);
|
free(rel);
|
||||||
}
|
}
|
||||||
if (!git_work_tree_cfg)
|
if (!git_work_tree_cfg)
|
||||||
|
5
cache.h
5
cache.h
@ -1064,8 +1064,11 @@ static inline int is_absolute_path(const char *path)
|
|||||||
return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
|
return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
|
||||||
}
|
}
|
||||||
int is_directory(const char *);
|
int is_directory(const char *);
|
||||||
|
char *strbuf_realpath(struct strbuf *resolved, const char *path,
|
||||||
|
int die_on_error);
|
||||||
const char *real_path(const char *path);
|
const char *real_path(const char *path);
|
||||||
const char *real_path_if_valid(const char *path);
|
const char *real_path_if_valid(const char *path);
|
||||||
|
char *real_pathdup(const char *path);
|
||||||
const char *absolute_path(const char *path);
|
const char *absolute_path(const char *path);
|
||||||
const char *remove_leading_path(const char *in, const char *prefix);
|
const char *remove_leading_path(const char *in, const char *prefix);
|
||||||
const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
|
const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
|
||||||
@ -1691,6 +1694,8 @@ extern int git_default_config(const char *, const char *, void *);
|
|||||||
extern int git_config_from_file(config_fn_t fn, const char *, void *);
|
extern int git_config_from_file(config_fn_t fn, const char *, void *);
|
||||||
extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
|
extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
|
||||||
const char *name, const char *buf, size_t len, void *data);
|
const char *name, const char *buf, size_t len, void *data);
|
||||||
|
extern int git_config_from_blob_sha1(config_fn_t fn, const char *name,
|
||||||
|
const unsigned char *sha1, void *data);
|
||||||
extern void git_config_push_parameter(const char *text);
|
extern void git_config_push_parameter(const char *text);
|
||||||
extern int git_config_from_parameters(config_fn_t fn, void *data);
|
extern int git_config_from_parameters(config_fn_t fn, void *data);
|
||||||
extern void git_config(config_fn_t fn, void *);
|
extern void git_config(config_fn_t fn, void *);
|
||||||
|
8
config.c
8
config.c
@ -1236,10 +1236,10 @@ int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_typ
|
|||||||
return do_config_from(&top, fn, data);
|
return do_config_from(&top, fn, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int git_config_from_blob_sha1(config_fn_t fn,
|
int git_config_from_blob_sha1(config_fn_t fn,
|
||||||
const char *name,
|
const char *name,
|
||||||
const unsigned char *sha1,
|
const unsigned char *sha1,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
enum object_type type;
|
enum object_type type;
|
||||||
char *buf;
|
char *buf;
|
||||||
|
@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
git_work_tree_initialized = 1;
|
git_work_tree_initialized = 1;
|
||||||
work_tree = xstrdup(real_path(new_work_tree));
|
work_tree = real_pathdup(new_work_tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *get_git_work_tree(void)
|
const char *get_git_work_tree(void)
|
||||||
|
2
git.c
2
git.c
@ -434,7 +434,7 @@ static struct cmd_struct commands[] = {
|
|||||||
{ "fsck-objects", cmd_fsck, RUN_SETUP },
|
{ "fsck-objects", cmd_fsck, RUN_SETUP },
|
||||||
{ "gc", cmd_gc, RUN_SETUP },
|
{ "gc", cmd_gc, RUN_SETUP },
|
||||||
{ "get-tar-commit-id", cmd_get_tar_commit_id },
|
{ "get-tar-commit-id", cmd_get_tar_commit_id },
|
||||||
{ "grep", cmd_grep, RUN_SETUP_GENTLY },
|
{ "grep", cmd_grep, RUN_SETUP_GENTLY | SUPPORT_SUPER_PREFIX },
|
||||||
{ "hash-object", cmd_hash_object },
|
{ "hash-object", cmd_hash_object },
|
||||||
{ "help", cmd_help },
|
{ "help", cmd_help },
|
||||||
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
|
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
|
||||||
|
16
grep.c
16
grep.c
@ -1735,12 +1735,23 @@ void grep_source_init(struct grep_source *gs, enum grep_source_type type,
|
|||||||
case GREP_SOURCE_FILE:
|
case GREP_SOURCE_FILE:
|
||||||
gs->identifier = xstrdup(identifier);
|
gs->identifier = xstrdup(identifier);
|
||||||
break;
|
break;
|
||||||
|
case GREP_SOURCE_SUBMODULE:
|
||||||
|
if (!identifier) {
|
||||||
|
gs->identifier = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* FALL THROUGH
|
||||||
|
* If the identifier is non-NULL (in the submodule case) it
|
||||||
|
* will be a SHA1 that needs to be copied.
|
||||||
|
*/
|
||||||
case GREP_SOURCE_SHA1:
|
case GREP_SOURCE_SHA1:
|
||||||
gs->identifier = xmalloc(20);
|
gs->identifier = xmalloc(20);
|
||||||
hashcpy(gs->identifier, identifier);
|
hashcpy(gs->identifier, identifier);
|
||||||
break;
|
break;
|
||||||
case GREP_SOURCE_BUF:
|
case GREP_SOURCE_BUF:
|
||||||
gs->identifier = NULL;
|
gs->identifier = NULL;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1760,6 +1771,7 @@ void grep_source_clear_data(struct grep_source *gs)
|
|||||||
switch (gs->type) {
|
switch (gs->type) {
|
||||||
case GREP_SOURCE_FILE:
|
case GREP_SOURCE_FILE:
|
||||||
case GREP_SOURCE_SHA1:
|
case GREP_SOURCE_SHA1:
|
||||||
|
case GREP_SOURCE_SUBMODULE:
|
||||||
free(gs->buf);
|
free(gs->buf);
|
||||||
gs->buf = NULL;
|
gs->buf = NULL;
|
||||||
gs->size = 0;
|
gs->size = 0;
|
||||||
@ -1831,8 +1843,10 @@ static int grep_source_load(struct grep_source *gs)
|
|||||||
return grep_source_load_sha1(gs);
|
return grep_source_load_sha1(gs);
|
||||||
case GREP_SOURCE_BUF:
|
case GREP_SOURCE_BUF:
|
||||||
return gs->buf ? 0 : -1;
|
return gs->buf ? 0 : -1;
|
||||||
|
case GREP_SOURCE_SUBMODULE:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
die("BUG: invalid grep_source type");
|
die("BUG: invalid grep_source type to load");
|
||||||
}
|
}
|
||||||
|
|
||||||
void grep_source_load_driver(struct grep_source *gs)
|
void grep_source_load_driver(struct grep_source *gs)
|
||||||
|
1
grep.h
1
grep.h
@ -161,6 +161,7 @@ struct grep_source {
|
|||||||
GREP_SOURCE_SHA1,
|
GREP_SOURCE_SHA1,
|
||||||
GREP_SOURCE_FILE,
|
GREP_SOURCE_FILE,
|
||||||
GREP_SOURCE_BUF,
|
GREP_SOURCE_BUF,
|
||||||
|
GREP_SOURCE_SUBMODULE,
|
||||||
} type;
|
} type;
|
||||||
void *identifier;
|
void *identifier;
|
||||||
|
|
||||||
|
13
setup.c
13
setup.c
@ -256,8 +256,10 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)
|
|||||||
strbuf_addbuf(&path, &data);
|
strbuf_addbuf(&path, &data);
|
||||||
strbuf_addstr(sb, real_path(path.buf));
|
strbuf_addstr(sb, real_path(path.buf));
|
||||||
ret = 1;
|
ret = 1;
|
||||||
} else
|
} else {
|
||||||
strbuf_addstr(sb, gitdir);
|
strbuf_addstr(sb, gitdir);
|
||||||
|
}
|
||||||
|
|
||||||
strbuf_release(&data);
|
strbuf_release(&data);
|
||||||
strbuf_release(&path);
|
strbuf_release(&path);
|
||||||
return ret;
|
return ret;
|
||||||
@ -692,7 +694,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
|
|||||||
/* --work-tree is set without --git-dir; use discovered one */
|
/* --work-tree is set without --git-dir; use discovered one */
|
||||||
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
|
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
|
||||||
if (offset != cwd->len && !is_absolute_path(gitdir))
|
if (offset != cwd->len && !is_absolute_path(gitdir))
|
||||||
gitdir = xstrdup(real_path(gitdir));
|
gitdir = real_pathdup(gitdir);
|
||||||
if (chdir(cwd->buf))
|
if (chdir(cwd->buf))
|
||||||
die_errno("Could not come back to cwd");
|
die_errno("Could not come back to cwd");
|
||||||
return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
|
return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
|
||||||
@ -800,11 +802,12 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
|
|||||||
/* Keep entry but do not canonicalize it */
|
/* Keep entry but do not canonicalize it */
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
const char *real_path = real_path_if_valid(ceil);
|
char *real_path = real_pathdup(ceil);
|
||||||
if (!real_path)
|
if (!real_path) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
free(item->string);
|
free(item->string);
|
||||||
item->string = xstrdup(real_path);
|
item->string = real_path;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,7 +284,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,
|
|||||||
struct strbuf pathbuf = STRBUF_INIT;
|
struct strbuf pathbuf = STRBUF_INIT;
|
||||||
|
|
||||||
if (!is_absolute_path(entry) && relative_base) {
|
if (!is_absolute_path(entry) && relative_base) {
|
||||||
strbuf_addstr(&pathbuf, real_path(relative_base));
|
strbuf_realpath(&pathbuf, relative_base, 1);
|
||||||
strbuf_addch(&pathbuf, '/');
|
strbuf_addch(&pathbuf, '/');
|
||||||
}
|
}
|
||||||
strbuf_addstr(&pathbuf, entry);
|
strbuf_addstr(&pathbuf, entry);
|
||||||
|
@ -379,7 +379,7 @@ static int parse_config(const char *var, const char *value, void *data)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
|
int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
|
||||||
unsigned char *gitmodules_sha1,
|
unsigned char *gitmodules_sha1,
|
||||||
struct strbuf *rev)
|
struct strbuf *rev)
|
||||||
{
|
{
|
||||||
|
@ -29,6 +29,9 @@ const struct submodule *submodule_from_name(const unsigned char *commit_or_tree,
|
|||||||
const char *name);
|
const char *name);
|
||||||
const struct submodule *submodule_from_path(const unsigned char *commit_or_tree,
|
const struct submodule *submodule_from_path(const unsigned char *commit_or_tree,
|
||||||
const char *path);
|
const char *path);
|
||||||
|
extern int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
|
||||||
|
unsigned char *gitmodules_sha1,
|
||||||
|
struct strbuf *rev);
|
||||||
void submodule_free(void);
|
void submodule_free(void);
|
||||||
|
|
||||||
#endif /* SUBMODULE_CONFIG_H */
|
#endif /* SUBMODULE_CONFIG_H */
|
||||||
|
58
submodule.c
58
submodule.c
@ -199,6 +199,56 @@ void gitmodules_config(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gitmodules_config_sha1(const unsigned char *commit_sha1)
|
||||||
|
{
|
||||||
|
struct strbuf rev = STRBUF_INIT;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
|
||||||
|
if (gitmodule_sha1_from_commit(commit_sha1, sha1, &rev)) {
|
||||||
|
git_config_from_blob_sha1(submodule_config, rev.buf,
|
||||||
|
sha1, NULL);
|
||||||
|
}
|
||||||
|
strbuf_release(&rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine if a submodule has been initialized at a given 'path'
|
||||||
|
*/
|
||||||
|
int is_submodule_initialized(const char *path)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
const struct submodule *module = NULL;
|
||||||
|
|
||||||
|
module = submodule_from_path(null_sha1, path);
|
||||||
|
|
||||||
|
if (module) {
|
||||||
|
char *key = xstrfmt("submodule.%s.url", module->name);
|
||||||
|
char *value = NULL;
|
||||||
|
|
||||||
|
ret = !git_config_get_string(key, &value);
|
||||||
|
|
||||||
|
free(value);
|
||||||
|
free(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine if a submodule has been populated at a given 'path'
|
||||||
|
*/
|
||||||
|
int is_submodule_populated(const char *path)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
char *gitdir = xstrfmt("%s/.git", path);
|
||||||
|
|
||||||
|
if (resolve_gitdir(gitdir))
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
free(gitdir);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int parse_submodule_update_strategy(const char *value,
|
int parse_submodule_update_strategy(const char *value,
|
||||||
struct submodule_update_strategy *dst)
|
struct submodule_update_strategy *dst)
|
||||||
{
|
{
|
||||||
@ -1333,7 +1383,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
|
|||||||
/* If it is an actual gitfile, it doesn't need migration. */
|
/* If it is an actual gitfile, it doesn't need migration. */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
real_old_git_dir = xstrdup(real_path(old_git_dir));
|
real_old_git_dir = real_pathdup(old_git_dir);
|
||||||
|
|
||||||
sub = submodule_from_path(null_sha1, path);
|
sub = submodule_from_path(null_sha1, path);
|
||||||
if (!sub)
|
if (!sub)
|
||||||
@ -1342,7 +1392,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
|
|||||||
new_git_dir = git_path("modules/%s", sub->name);
|
new_git_dir = git_path("modules/%s", sub->name);
|
||||||
if (safe_create_leading_directories_const(new_git_dir) < 0)
|
if (safe_create_leading_directories_const(new_git_dir) < 0)
|
||||||
die(_("could not create directory '%s'"), new_git_dir);
|
die(_("could not create directory '%s'"), new_git_dir);
|
||||||
real_new_git_dir = xstrdup(real_path(new_git_dir));
|
real_new_git_dir = real_pathdup(new_git_dir);
|
||||||
|
|
||||||
if (!prefix)
|
if (!prefix)
|
||||||
prefix = get_super_prefix();
|
prefix = get_super_prefix();
|
||||||
@ -1379,8 +1429,8 @@ void absorb_git_dir_into_superproject(const char *prefix,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Is it already absorbed into the superprojects git dir? */
|
/* Is it already absorbed into the superprojects git dir? */
|
||||||
real_sub_git_dir = xstrdup(real_path(sub_git_dir));
|
real_sub_git_dir = real_pathdup(sub_git_dir);
|
||||||
real_common_git_dir = xstrdup(real_path(get_git_common_dir()));
|
real_common_git_dir = real_pathdup(get_git_common_dir());
|
||||||
if (!skip_prefix(real_sub_git_dir, real_common_git_dir, &v))
|
if (!skip_prefix(real_sub_git_dir, real_common_git_dir, &v))
|
||||||
relocate_single_git_dir_into_superproject(prefix, path);
|
relocate_single_git_dir_into_superproject(prefix, path);
|
||||||
|
|
||||||
|
@ -38,6 +38,9 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
|
|||||||
const char *path);
|
const char *path);
|
||||||
int submodule_config(const char *var, const char *value, void *cb);
|
int submodule_config(const char *var, const char *value, void *cb);
|
||||||
void gitmodules_config(void);
|
void gitmodules_config(void);
|
||||||
|
extern void gitmodules_config_sha1(const unsigned char *commit_sha1);
|
||||||
|
extern int is_submodule_initialized(const char *path);
|
||||||
|
extern int is_submodule_populated(const char *path);
|
||||||
int parse_submodule_update_strategy(const char *value,
|
int parse_submodule_update_strategy(const char *value,
|
||||||
struct submodule_update_strategy *dst);
|
struct submodule_update_strategy *dst);
|
||||||
const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
|
const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
|
||||||
|
241
t/t7814-grep-recurse-submodules.sh
Executable file
241
t/t7814-grep-recurse-submodules.sh
Executable file
@ -0,0 +1,241 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='Test grep recurse-submodules feature
|
||||||
|
|
||||||
|
This test verifies the recurse-submodules feature correctly greps across
|
||||||
|
submodules.
|
||||||
|
'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success 'setup directory structure and submodule' '
|
||||||
|
echo "foobar" >a &&
|
||||||
|
mkdir b &&
|
||||||
|
echo "bar" >b/b &&
|
||||||
|
git add a b &&
|
||||||
|
git commit -m "add a and b" &&
|
||||||
|
git init submodule &&
|
||||||
|
echo "foobar" >submodule/a &&
|
||||||
|
git -C submodule add a &&
|
||||||
|
git -C submodule commit -m "add a" &&
|
||||||
|
git submodule add ./submodule &&
|
||||||
|
git commit -m "added submodule"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep correctly finds patterns in a submodule' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
a:foobar
|
||||||
|
b/b:bar
|
||||||
|
submodule/a:foobar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --recurse-submodules >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep and basic pathspecs' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
submodule/a:foobar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e. --recurse-submodules -- submodule >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep and nested submodules' '
|
||||||
|
git init submodule/sub &&
|
||||||
|
echo "foobar" >submodule/sub/a &&
|
||||||
|
git -C submodule/sub add a &&
|
||||||
|
git -C submodule/sub commit -m "add a" &&
|
||||||
|
git -C submodule submodule add ./sub &&
|
||||||
|
git -C submodule add sub &&
|
||||||
|
git -C submodule commit -m "added sub" &&
|
||||||
|
git add submodule &&
|
||||||
|
git commit -m "updated submodule" &&
|
||||||
|
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
a:foobar
|
||||||
|
b/b:bar
|
||||||
|
submodule/a:foobar
|
||||||
|
submodule/sub/a:foobar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --recurse-submodules >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep and multiple patterns' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
a:foobar
|
||||||
|
submodule/a:foobar
|
||||||
|
submodule/sub/a:foobar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --and -e "foo" --recurse-submodules >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep and multiple patterns' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
b/b:bar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --and --not -e "foo" --recurse-submodules >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'basic grep tree' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
HEAD:a:foobar
|
||||||
|
HEAD:b/b:bar
|
||||||
|
HEAD:submodule/a:foobar
|
||||||
|
HEAD:submodule/sub/a:foobar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --recurse-submodules HEAD >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep tree HEAD^' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
HEAD^:a:foobar
|
||||||
|
HEAD^:b/b:bar
|
||||||
|
HEAD^:submodule/a:foobar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --recurse-submodules HEAD^ >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep tree HEAD^^' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
HEAD^^:a:foobar
|
||||||
|
HEAD^^:b/b:bar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --recurse-submodules HEAD^^ >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep tree and pathspecs' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
HEAD:submodule/a:foobar
|
||||||
|
HEAD:submodule/sub/a:foobar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --recurse-submodules HEAD -- submodule >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep tree and pathspecs' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
HEAD:submodule/a:foobar
|
||||||
|
HEAD:submodule/sub/a:foobar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --recurse-submodules HEAD -- "submodule*a" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep tree and more pathspecs' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
HEAD:submodule/a:foobar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --recurse-submodules HEAD -- "submodul?/a" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep tree and more pathspecs' '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
HEAD:submodule/sub/a:foobar
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git grep -e "bar" --recurse-submodules HEAD -- "submodul*/sub/a" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success !MINGW 'grep recurse submodule colon in name' '
|
||||||
|
git init parent &&
|
||||||
|
test_when_finished "rm -rf parent" &&
|
||||||
|
echo "foobar" >"parent/fi:le" &&
|
||||||
|
git -C parent add "fi:le" &&
|
||||||
|
git -C parent commit -m "add fi:le" &&
|
||||||
|
|
||||||
|
git init "su:b" &&
|
||||||
|
test_when_finished "rm -rf su:b" &&
|
||||||
|
echo "foobar" >"su:b/fi:le" &&
|
||||||
|
git -C "su:b" add "fi:le" &&
|
||||||
|
git -C "su:b" commit -m "add fi:le" &&
|
||||||
|
|
||||||
|
git -C parent submodule add "../su:b" "su:b" &&
|
||||||
|
git -C parent commit -m "add submodule" &&
|
||||||
|
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
fi:le:foobar
|
||||||
|
su:b/fi:le:foobar
|
||||||
|
EOF
|
||||||
|
git -C parent grep -e "foobar" --recurse-submodules >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
HEAD:fi:le:foobar
|
||||||
|
HEAD:su:b/fi:le:foobar
|
||||||
|
EOF
|
||||||
|
git -C parent grep -e "foobar" --recurse-submodules HEAD >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep history with moved submoules' '
|
||||||
|
git init parent &&
|
||||||
|
test_when_finished "rm -rf parent" &&
|
||||||
|
echo "foobar" >parent/file &&
|
||||||
|
git -C parent add file &&
|
||||||
|
git -C parent commit -m "add file" &&
|
||||||
|
|
||||||
|
git init sub &&
|
||||||
|
test_when_finished "rm -rf sub" &&
|
||||||
|
echo "foobar" >sub/file &&
|
||||||
|
git -C sub add file &&
|
||||||
|
git -C sub commit -m "add file" &&
|
||||||
|
|
||||||
|
git -C parent submodule add ../sub dir/sub &&
|
||||||
|
git -C parent commit -m "add submodule" &&
|
||||||
|
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
dir/sub/file:foobar
|
||||||
|
file:foobar
|
||||||
|
EOF
|
||||||
|
git -C parent grep -e "foobar" --recurse-submodules >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
git -C parent mv dir/sub sub-moved &&
|
||||||
|
git -C parent commit -m "moved submodule" &&
|
||||||
|
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
file:foobar
|
||||||
|
sub-moved/file:foobar
|
||||||
|
EOF
|
||||||
|
git -C parent grep -e "foobar" --recurse-submodules >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
HEAD^:dir/sub/file:foobar
|
||||||
|
HEAD^:file:foobar
|
||||||
|
EOF
|
||||||
|
git -C parent grep -e "foobar" --recurse-submodules HEAD^ >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_incompatible_with_recurse_submodules ()
|
||||||
|
{
|
||||||
|
test_expect_success "--recurse-submodules and $1 are incompatible" "
|
||||||
|
test_must_fail git grep -e. --recurse-submodules $1 2>actual &&
|
||||||
|
test_i18ngrep 'not supported with --recurse-submodules' actual
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_incompatible_with_recurse_submodules --untracked
|
||||||
|
test_incompatible_with_recurse_submodules --no-index
|
||||||
|
|
||||||
|
test_done
|
@ -1214,7 +1214,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,
|
|||||||
const struct ref *extra;
|
const struct ref *extra;
|
||||||
struct alternate_refs_data *cb = data;
|
struct alternate_refs_data *cb = data;
|
||||||
|
|
||||||
other = xstrdup(real_path(e->path));
|
other = real_pathdup(e->path);
|
||||||
len = strlen(other);
|
len = strlen(other);
|
||||||
|
|
||||||
while (other[len-1] == '/')
|
while (other[len-1] == '/')
|
||||||
|
28
tree-walk.c
28
tree-walk.c
@ -1004,6 +1004,19 @@ static enum interesting do_match(const struct name_entry *entry,
|
|||||||
*/
|
*/
|
||||||
if (ps->recursive && S_ISDIR(entry->mode))
|
if (ps->recursive && S_ISDIR(entry->mode))
|
||||||
return entry_interesting;
|
return entry_interesting;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When matching against submodules with
|
||||||
|
* wildcard characters, ensure that the entry
|
||||||
|
* at least matches up to the first wild
|
||||||
|
* character. More accurate matching can then
|
||||||
|
* be performed in the submodule itself.
|
||||||
|
*/
|
||||||
|
if (ps->recursive && S_ISGITLINK(entry->mode) &&
|
||||||
|
!ps_strncmp(item, match + baselen,
|
||||||
|
entry->path,
|
||||||
|
item->nowildcard_len - baselen))
|
||||||
|
return entry_interesting;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -1040,6 +1053,21 @@ match_wildcards:
|
|||||||
strbuf_setlen(base, base_offset + baselen);
|
strbuf_setlen(base, base_offset + baselen);
|
||||||
return entry_interesting;
|
return entry_interesting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When matching against submodules with
|
||||||
|
* wildcard characters, ensure that the entry
|
||||||
|
* at least matches up to the first wild
|
||||||
|
* character. More accurate matching can then
|
||||||
|
* be performed in the submodule itself.
|
||||||
|
*/
|
||||||
|
if (ps->recursive && S_ISGITLINK(entry->mode) &&
|
||||||
|
!ps_strncmp(item, match, base->buf + base_offset,
|
||||||
|
item->nowildcard_len)) {
|
||||||
|
strbuf_setlen(base, base_offset + baselen);
|
||||||
|
return entry_interesting;
|
||||||
|
}
|
||||||
|
|
||||||
strbuf_setlen(base, base_offset + baselen);
|
strbuf_setlen(base, base_offset + baselen);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list,
|
|||||||
return wt;
|
return wt;
|
||||||
|
|
||||||
arg = prefix_filename(prefix, strlen(prefix), arg);
|
arg = prefix_filename(prefix, strlen(prefix), arg);
|
||||||
path = xstrdup(real_path(arg));
|
path = real_pathdup(arg);
|
||||||
for (; *list; list++)
|
for (; *list; list++)
|
||||||
if (!fspathcmp(path, real_path((*list)->path)))
|
if (!fspathcmp(path, real_path((*list)->path)))
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user