Merge branch 'jl/submodule-mv'
"git mv A B" when moving a submodule A does "the right thing", inclusing relocating its working tree and adjusting the paths in the .gitmodules file. * jl/submodule-mv: (53 commits) rm: delete .gitmodules entry of submodules removed from the work tree mv: update the path entry in .gitmodules for moved submodules submodule.c: add .gitmodules staging helper functions mv: move submodules using a gitfile mv: move submodules together with their work trees rm: do not set a variable twice without intermediate reading. t6131 - skip tests if on case-insensitive file system parse_pathspec: accept :(icase)path syntax pathspec: support :(glob) syntax pathspec: make --literal-pathspecs disable pathspec magic pathspec: support :(literal) syntax for noglob pathspec kill limit_pathspec_to_literal() as it's only used by parse_pathspec() parse_pathspec: preserve prefix length via PATHSPEC_PREFIX_ORIGIN parse_pathspec: make sure the prefix part is wildcard-free rename field "raw" to "_raw" in struct pathspec tree-diff: remove the use of pathspec's raw[] in follow-rename codepath remove match_pathspec() in favor of match_pathspec_depth() remove init_pathspec() in favor of parse_pathspec() remove diff_tree_{setup,release}_paths convert common_prefix() to use struct pathspec ...
This commit is contained in:
commit
b02f5aeda6
@ -13,7 +13,7 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This script is used to move or rename a file, directory or symlink.
|
||||
Move or rename a file, directory or symlink.
|
||||
|
||||
git mv [-v] [-f] [-n] [-k] <source> <destination>
|
||||
git mv [-v] [-f] [-n] [-k] <source> ... <destination directory>
|
||||
@ -44,6 +44,14 @@ OPTIONS
|
||||
--verbose::
|
||||
Report the names of files as they are moved.
|
||||
|
||||
SUBMODULES
|
||||
----------
|
||||
Moving a submodule using a gitfile (which means they were cloned
|
||||
with a Git version 1.7.8 or newer) will update the gitfile and
|
||||
core.worktree setting to make the submodule work in the new location.
|
||||
It also will attempt to update the submodule.<name>.path setting in
|
||||
the linkgit:gitmodules[5] file and stage that file (unless -n is used).
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the linkgit:git[1] suite
|
||||
|
@ -134,14 +134,16 @@ use the following command:
|
||||
git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached
|
||||
----------------
|
||||
|
||||
Submodules
|
||||
~~~~~~~~~~
|
||||
SUBMODULES
|
||||
----------
|
||||
Only submodules using a gitfile (which means they were cloned
|
||||
with a Git version 1.7.8 or newer) will be removed from the work
|
||||
tree, as their repository lives inside the .git directory of the
|
||||
superproject. If a submodule (or one of those nested inside it)
|
||||
still uses a .git directory, `git rm` will fail - no matter if forced
|
||||
or not - to protect the submodule's history.
|
||||
or not - to protect the submodule's history. If it exists the
|
||||
submodule.<name> section in the linkgit:gitmodules[5] file will also
|
||||
be removed and that file will be staged (unless --cached or -n are used).
|
||||
|
||||
A submodule is considered up-to-date when the HEAD is the same as
|
||||
recorded in the index, no tracked files are modified and no untracked
|
||||
|
@ -457,10 +457,25 @@ help ...`.
|
||||
linkgit:git-replace[1] for more information.
|
||||
|
||||
--literal-pathspecs::
|
||||
Treat pathspecs literally, rather than as glob patterns. This is
|
||||
equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
|
||||
Treat pathspecs literally (i.e. no globbing, no pathspec magic).
|
||||
This is equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
|
||||
variable to `1`.
|
||||
|
||||
--glob-pathspecs:
|
||||
Add "glob" magic to all pathspec. This is equivalent to setting
|
||||
the `GIT_GLOB_PATHSPECS` environment variable to `1`. Disabling
|
||||
globbing on individual pathspecs can be done using pathspec
|
||||
magic ":(literal)"
|
||||
|
||||
--noglob-pathspecs:
|
||||
Add "literal" magic to all pathspec. This is equivalent to setting
|
||||
the `GIT_NOGLOB_PATHSPECS` environment variable to `1`. Enabling
|
||||
globbing on individual pathspecs can be done using pathspec
|
||||
magic ":(glob)"
|
||||
|
||||
--icase-pathspecs:
|
||||
Add "icase" magic to all pathspec. This is equivalent to setting
|
||||
the `GIT_ICASE_PATHSPECS` environment variable to `1`.
|
||||
|
||||
GIT COMMANDS
|
||||
------------
|
||||
@ -867,6 +882,18 @@ GIT_LITERAL_PATHSPECS::
|
||||
literal paths to Git (e.g., paths previously given to you by
|
||||
`git ls-tree`, `--raw` diff output, etc).
|
||||
|
||||
GIT_GLOB_PATHSPECS::
|
||||
Setting this variable to `1` will cause Git to treat all
|
||||
pathspecs as glob patterns (aka "glob" magic).
|
||||
|
||||
GIT_NOGLOB_PATHSPECS::
|
||||
Setting this variable to `1` will cause Git to treat all
|
||||
pathspecs as literal (aka "literal" magic).
|
||||
|
||||
GIT_ICASE_PATHSPECS::
|
||||
Setting this variable to `1` will cause Git to treat all
|
||||
pathspecs as case-insensitive.
|
||||
|
||||
|
||||
Discussion[[Discussion]]
|
||||
------------------------
|
||||
|
@ -322,10 +322,54 @@ and a close parentheses `)`, and the remainder is the pattern to match
|
||||
against the path.
|
||||
+
|
||||
The "magic signature" consists of an ASCII symbol that is not
|
||||
alphanumeric. Currently only the slash `/` is recognized as a
|
||||
"magic signature": it makes the pattern match from the root of
|
||||
the working tree, even when you are running the command from
|
||||
inside a subdirectory.
|
||||
alphanumeric.
|
||||
+
|
||||
--
|
||||
top `/`;;
|
||||
The magic word `top` (mnemonic: `/`) makes the pattern match
|
||||
from the root of the working tree, even when you are running
|
||||
the command from inside a subdirectory.
|
||||
|
||||
literal;;
|
||||
Wildcards in the pattern such as `*` or `?` are treated
|
||||
as literal characters.
|
||||
|
||||
icase;;
|
||||
Case insensitive match.
|
||||
|
||||
glob;;
|
||||
Git treats the pattern as a shell glob suitable for
|
||||
consumption by fnmatch(3) with the FNM_PATHNAME flag:
|
||||
wildcards in the pattern will not match a / in the pathname.
|
||||
For example, "Documentation/{asterisk}.html" matches
|
||||
"Documentation/git.html" but not "Documentation/ppc/ppc.html"
|
||||
or "tools/perf/Documentation/perf.html".
|
||||
+
|
||||
Two consecutive asterisks ("`**`") in patterns matched against
|
||||
full pathname may have special meaning:
|
||||
|
||||
- A leading "`**`" followed by a slash means match in all
|
||||
directories. For example, "`**/foo`" matches file or directory
|
||||
"`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar"
|
||||
matches file or directory "`bar`" anywhere that is directly
|
||||
under directory "`foo`".
|
||||
|
||||
- A trailing "/**" matches everything inside. For example,
|
||||
"abc/**" matches all files inside directory "abc", relative
|
||||
to the location of the `.gitignore` file, with infinite depth.
|
||||
|
||||
- A slash followed by two consecutive asterisks then a slash
|
||||
matches zero or more directories. For example, "`a/**/b`"
|
||||
matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on.
|
||||
|
||||
- Other consecutive asterisks are considered invalid.
|
||||
+
|
||||
Glob magic is incompatible with literal magic.
|
||||
--
|
||||
+
|
||||
Currently only the slash `/` is recognized as the "magic signature",
|
||||
but it is envisioned that we will support more types of magic in later
|
||||
versions of Git.
|
||||
+
|
||||
A pathspec with only a colon means "there is no pathspec". This form
|
||||
should not be combined with other pathspec.
|
||||
|
@ -8,6 +8,42 @@ Talk about
|
||||
* is_inside_git_dir()
|
||||
* is_inside_work_tree()
|
||||
* setup_work_tree()
|
||||
* get_pathspec()
|
||||
|
||||
(Dscho)
|
||||
|
||||
Pathspec
|
||||
--------
|
||||
|
||||
See glossary-context.txt for the syntax of pathspec. In memory, a
|
||||
pathspec set is represented by "struct pathspec" and is prepared by
|
||||
parse_pathspec(). This function takes several arguments:
|
||||
|
||||
- magic_mask specifies what features that are NOT supported by the
|
||||
following code. If a user attempts to use such a feature,
|
||||
parse_pathspec() can reject it early.
|
||||
|
||||
- flags specifies other things that the caller wants parse_pathspec to
|
||||
perform.
|
||||
|
||||
- prefix and args come from cmd_* functions
|
||||
|
||||
get_pathspec() is obsolete and should never be used in new code.
|
||||
|
||||
parse_pathspec() helps catch unsupported features and reject them
|
||||
politely. At a lower level, different pathspec-related functions may
|
||||
not support the same set of features. Such pathspec-sensitive
|
||||
functions are guarded with GUARD_PATHSPEC(), which will die in an
|
||||
unfriendly way when an unsupported feature is requested.
|
||||
|
||||
The command designers are supposed to make sure that GUARD_PATHSPEC()
|
||||
never dies. They have to make sure all unsupported features are caught
|
||||
by parse_pathspec(), not by GUARD_PATHSPEC. grepping GUARD_PATHSPEC()
|
||||
should give the designers all pathspec-sensitive codepaths and what
|
||||
features they support.
|
||||
|
||||
A similar process is applied when a new pathspec magic is added. The
|
||||
designer lifts the GUARD_PATHSPEC restriction in the functions that
|
||||
support the new magic. At the same time (s)he has to make sure this
|
||||
new feature will be caught at parse_pathspec() in commands that cannot
|
||||
handle the new magic in some cases. grepping parse_pathspec() should
|
||||
help.
|
||||
|
18
archive.c
18
archive.c
@ -151,7 +151,6 @@ int write_archive_entries(struct archiver_args *args,
|
||||
struct archiver_context context;
|
||||
struct unpack_trees_options opts;
|
||||
struct tree_desc t;
|
||||
struct pathspec pathspec;
|
||||
int err;
|
||||
|
||||
if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
|
||||
@ -186,10 +185,8 @@ int write_archive_entries(struct archiver_args *args,
|
||||
git_attr_set_direction(GIT_ATTR_INDEX, &the_index);
|
||||
}
|
||||
|
||||
init_pathspec(&pathspec, args->pathspec);
|
||||
err = read_tree_recursive(args->tree, "", 0, 0, &pathspec,
|
||||
err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
|
||||
write_archive_entry, &context);
|
||||
free_pathspec(&pathspec);
|
||||
if (err == READ_TREE_RECURSIVE)
|
||||
err = 0;
|
||||
return err;
|
||||
@ -222,7 +219,7 @@ static int path_exists(struct tree *tree, const char *path)
|
||||
struct pathspec pathspec;
|
||||
int ret;
|
||||
|
||||
init_pathspec(&pathspec, paths);
|
||||
parse_pathspec(&pathspec, 0, 0, "", paths);
|
||||
ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, NULL);
|
||||
free_pathspec(&pathspec);
|
||||
return ret != 0;
|
||||
@ -231,11 +228,18 @@ static int path_exists(struct tree *tree, const char *path)
|
||||
static void parse_pathspec_arg(const char **pathspec,
|
||||
struct archiver_args *ar_args)
|
||||
{
|
||||
ar_args->pathspec = pathspec = get_pathspec("", pathspec);
|
||||
/*
|
||||
* must be consistent with parse_pathspec in path_exists()
|
||||
* Also if pathspec patterns are dependent, we're in big
|
||||
* trouble as we test each one separately
|
||||
*/
|
||||
parse_pathspec(&ar_args->pathspec, 0,
|
||||
PATHSPEC_PREFER_FULL,
|
||||
"", pathspec);
|
||||
if (pathspec) {
|
||||
while (*pathspec) {
|
||||
if (**pathspec && !path_exists(ar_args->tree, *pathspec))
|
||||
die("path not found: %s", *pathspec);
|
||||
die(_("pathspec '%s' did not match any files"), *pathspec);
|
||||
pathspec++;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef ARCHIVE_H
|
||||
#define ARCHIVE_H
|
||||
|
||||
#include "pathspec.h"
|
||||
|
||||
struct archiver_args {
|
||||
const char *base;
|
||||
size_t baselen;
|
||||
@ -8,7 +10,7 @@ struct archiver_args {
|
||||
const unsigned char *commit_sha1;
|
||||
const struct commit *commit;
|
||||
time_t time;
|
||||
const char **pathspec;
|
||||
struct pathspec pathspec;
|
||||
unsigned int verbose : 1;
|
||||
unsigned int worktree_attributes : 1;
|
||||
unsigned int convert : 1;
|
||||
|
166
builtin/add.c
166
builtin/add.c
@ -166,14 +166,16 @@ static void update_callback(struct diff_queue_struct *q,
|
||||
}
|
||||
}
|
||||
|
||||
static void update_files_in_cache(const char *prefix, const char **pathspec,
|
||||
static void update_files_in_cache(const char *prefix,
|
||||
const struct pathspec *pathspec,
|
||||
struct update_callback_data *data)
|
||||
{
|
||||
struct rev_info rev;
|
||||
|
||||
init_revisions(&rev, prefix);
|
||||
setup_revisions(0, NULL, &rev, NULL);
|
||||
init_pathspec(&rev.prune_data, pathspec);
|
||||
if (pathspec)
|
||||
copy_pathspec(&rev.prune_data, pathspec);
|
||||
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
rev.diffopt.format_callback = update_callback;
|
||||
rev.diffopt.format_callback_data = data;
|
||||
@ -181,7 +183,8 @@ static void update_files_in_cache(const char *prefix, const char **pathspec,
|
||||
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
|
||||
}
|
||||
|
||||
int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
|
||||
int add_files_to_cache(const char *prefix,
|
||||
const struct pathspec *pathspec, int flags)
|
||||
{
|
||||
struct update_callback_data data;
|
||||
|
||||
@ -192,23 +195,21 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
|
||||
}
|
||||
|
||||
#define WARN_IMPLICIT_DOT (1u << 0)
|
||||
static char *prune_directory(struct dir_struct *dir, const char **pathspec,
|
||||
static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
|
||||
int prefix, unsigned flag)
|
||||
{
|
||||
char *seen;
|
||||
int i, specs;
|
||||
int i;
|
||||
struct dir_entry **src, **dst;
|
||||
|
||||
for (specs = 0; pathspec[specs]; specs++)
|
||||
/* nothing */;
|
||||
seen = xcalloc(specs, 1);
|
||||
seen = xcalloc(pathspec->nr, 1);
|
||||
|
||||
src = dst = dir->entries;
|
||||
i = dir->nr;
|
||||
while (--i >= 0) {
|
||||
struct dir_entry *entry = *src++;
|
||||
if (match_pathspec(pathspec, entry->name, entry->len,
|
||||
prefix, seen))
|
||||
if (match_pathspec_depth(pathspec, entry->name, entry->len,
|
||||
prefix, seen))
|
||||
*dst++ = entry;
|
||||
else if (flag & WARN_IMPLICIT_DOT)
|
||||
/*
|
||||
@ -222,72 +223,33 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec,
|
||||
warn_pathless_add();
|
||||
}
|
||||
dir->nr = dst - dir->entries;
|
||||
add_pathspec_matches_against_index(pathspec, seen, specs);
|
||||
add_pathspec_matches_against_index(pathspec, seen);
|
||||
return seen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks the index to see whether any path in pathspec refers to
|
||||
* something inside a submodule. If so, dies with an error message.
|
||||
*/
|
||||
static void treat_gitlinks(const char **pathspec)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!pathspec || !*pathspec)
|
||||
return;
|
||||
|
||||
for (i = 0; pathspec[i]; i++)
|
||||
pathspec[i] = check_path_for_gitlink(pathspec[i]);
|
||||
}
|
||||
|
||||
static void refresh(int verbose, const char **pathspec)
|
||||
static void refresh(int verbose, const struct pathspec *pathspec)
|
||||
{
|
||||
char *seen;
|
||||
int i, specs;
|
||||
int i;
|
||||
|
||||
for (specs = 0; pathspec[specs]; specs++)
|
||||
/* nothing */;
|
||||
seen = xcalloc(specs, 1);
|
||||
seen = xcalloc(pathspec->nr, 1);
|
||||
refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET,
|
||||
pathspec, seen, _("Unstaged changes after refreshing the index:"));
|
||||
for (i = 0; i < specs; i++) {
|
||||
for (i = 0; i < pathspec->nr; i++) {
|
||||
if (!seen[i])
|
||||
die(_("pathspec '%s' did not match any files"), pathspec[i]);
|
||||
die(_("pathspec '%s' did not match any files"),
|
||||
pathspec->items[i].match);
|
||||
}
|
||||
free(seen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes argv relative to prefix, via get_pathspec(), and then
|
||||
* runs die_if_path_beyond_symlink() on each path in the normalized
|
||||
* list.
|
||||
*/
|
||||
static const char **validate_pathspec(const char **argv, const char *prefix)
|
||||
{
|
||||
const char **pathspec = get_pathspec(prefix, argv);
|
||||
|
||||
if (pathspec) {
|
||||
const char **p;
|
||||
for (p = pathspec; *p; p++) {
|
||||
die_if_path_beyond_symlink(*p, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
return pathspec;
|
||||
}
|
||||
|
||||
int run_add_interactive(const char *revision, const char *patch_mode,
|
||||
const char **pathspec)
|
||||
const struct pathspec *pathspec)
|
||||
{
|
||||
int status, ac, pc = 0;
|
||||
int status, ac, i;
|
||||
const char **args;
|
||||
|
||||
if (pathspec)
|
||||
while (pathspec[pc])
|
||||
pc++;
|
||||
|
||||
args = xcalloc(sizeof(const char *), (pc + 5));
|
||||
args = xcalloc(sizeof(const char *), (pathspec->nr + 6));
|
||||
ac = 0;
|
||||
args[ac++] = "add--interactive";
|
||||
if (patch_mode)
|
||||
@ -295,11 +257,9 @@ int run_add_interactive(const char *revision, const char *patch_mode,
|
||||
if (revision)
|
||||
args[ac++] = revision;
|
||||
args[ac++] = "--";
|
||||
if (pc) {
|
||||
memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc);
|
||||
ac += pc;
|
||||
}
|
||||
args[ac] = NULL;
|
||||
for (i = 0; i < pathspec->nr; i++)
|
||||
/* pass original pathspec, to be re-parsed */
|
||||
args[ac++] = pathspec->items[i].original;
|
||||
|
||||
status = run_command_v_opt(args, RUN_GIT_CMD);
|
||||
free(args);
|
||||
@ -308,17 +268,23 @@ int run_add_interactive(const char *revision, const char *patch_mode,
|
||||
|
||||
int interactive_add(int argc, const char **argv, const char *prefix, int patch)
|
||||
{
|
||||
const char **pathspec = NULL;
|
||||
struct pathspec pathspec;
|
||||
|
||||
if (argc) {
|
||||
pathspec = validate_pathspec(argv, prefix);
|
||||
if (!pathspec)
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* git-add--interactive itself does not parse pathspec. It
|
||||
* simply passes the pathspec to other builtin commands. Let's
|
||||
* hope all of them support all magic, or we'll need to limit
|
||||
* the magic here.
|
||||
*/
|
||||
parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP,
|
||||
PATHSPEC_PREFER_FULL |
|
||||
PATHSPEC_SYMLINK_LEADING_PATH |
|
||||
PATHSPEC_PREFIX_ORIGIN,
|
||||
prefix, argv);
|
||||
|
||||
return run_add_interactive(NULL,
|
||||
patch ? "--patch" : NULL,
|
||||
pathspec);
|
||||
&pathspec);
|
||||
}
|
||||
|
||||
static int edit_patch(int argc, const char **argv, const char *prefix)
|
||||
@ -446,7 +412,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int exit_status = 0;
|
||||
int newfd;
|
||||
const char **pathspec;
|
||||
struct pathspec pathspec;
|
||||
struct dir_struct dir;
|
||||
int flags;
|
||||
int add_new_files;
|
||||
@ -527,14 +493,23 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
|
||||
return 0;
|
||||
}
|
||||
pathspec = validate_pathspec(argv, prefix);
|
||||
|
||||
if (read_cache() < 0)
|
||||
die(_("index file corrupt"));
|
||||
treat_gitlinks(pathspec);
|
||||
|
||||
/*
|
||||
* Check the "pathspec '%s' did not match any files" block
|
||||
* below before enabling new magic.
|
||||
*/
|
||||
parse_pathspec(&pathspec, 0,
|
||||
PATHSPEC_PREFER_FULL |
|
||||
PATHSPEC_SYMLINK_LEADING_PATH |
|
||||
PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE,
|
||||
prefix, argv);
|
||||
|
||||
if (add_new_files) {
|
||||
int baselen;
|
||||
struct pathspec empty_pathspec;
|
||||
|
||||
/* Set up the default git porcelain excludes */
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
@ -543,35 +518,49 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
setup_standard_excludes(&dir);
|
||||
}
|
||||
|
||||
memset(&empty_pathspec, 0, sizeof(empty_pathspec));
|
||||
/* This picks up the paths that are not tracked */
|
||||
baselen = fill_directory(&dir, implicit_dot ? NULL : pathspec);
|
||||
if (pathspec)
|
||||
seen = prune_directory(&dir, pathspec, baselen,
|
||||
baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec);
|
||||
if (pathspec.nr)
|
||||
seen = prune_directory(&dir, &pathspec, baselen,
|
||||
implicit_dot ? WARN_IMPLICIT_DOT : 0);
|
||||
}
|
||||
|
||||
if (refresh_only) {
|
||||
refresh(verbose, pathspec);
|
||||
refresh(verbose, &pathspec);
|
||||
goto finish;
|
||||
}
|
||||
if (implicit_dot && prefix)
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
|
||||
if (pathspec) {
|
||||
if (pathspec.nr) {
|
||||
int i;
|
||||
|
||||
if (!seen)
|
||||
seen = find_pathspecs_matching_against_index(pathspec);
|
||||
for (i = 0; pathspec[i]; i++) {
|
||||
if (!seen[i] && pathspec[i][0]
|
||||
&& !file_exists(pathspec[i])) {
|
||||
seen = find_pathspecs_matching_against_index(&pathspec);
|
||||
|
||||
/*
|
||||
* file_exists() assumes exact match
|
||||
*/
|
||||
GUARD_PATHSPEC(&pathspec,
|
||||
PATHSPEC_FROMTOP |
|
||||
PATHSPEC_LITERAL |
|
||||
PATHSPEC_GLOB |
|
||||
PATHSPEC_ICASE);
|
||||
|
||||
for (i = 0; i < pathspec.nr; i++) {
|
||||
const char *path = pathspec.items[i].match;
|
||||
if (!seen[i] &&
|
||||
((pathspec.items[i].magic &
|
||||
(PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
|
||||
!file_exists(path))) {
|
||||
if (ignore_missing) {
|
||||
int dtype = DT_UNKNOWN;
|
||||
if (is_excluded(&dir, pathspec[i], &dtype))
|
||||
dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
|
||||
if (is_excluded(&dir, path, &dtype))
|
||||
dir_add_ignored(&dir, path, pathspec.items[i].len);
|
||||
} else
|
||||
die(_("pathspec '%s' did not match any files"),
|
||||
pathspec[i]);
|
||||
pathspec.items[i].original);
|
||||
}
|
||||
}
|
||||
free(seen);
|
||||
@ -587,10 +576,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
*/
|
||||
update_data.implicit_dot = prefix;
|
||||
update_data.implicit_dot_len = strlen(prefix);
|
||||
pathspec = NULL;
|
||||
free_pathspec(&pathspec);
|
||||
memset(&pathspec, 0, sizeof(pathspec));
|
||||
}
|
||||
update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT;
|
||||
update_files_in_cache(prefix, pathspec, &update_data);
|
||||
update_files_in_cache(prefix, &pathspec, &update_data);
|
||||
|
||||
exit_status |= !!update_data.add_errors;
|
||||
if (add_new_files)
|
||||
|
@ -409,7 +409,7 @@ static struct origin *find_origin(struct scoreboard *sb,
|
||||
paths[0] = origin->path;
|
||||
paths[1] = NULL;
|
||||
|
||||
diff_tree_setup_paths(paths, &diff_opts);
|
||||
parse_pathspec(&diff_opts.pathspec, PATHSPEC_ALL_MAGIC, 0, "", paths);
|
||||
diff_setup_done(&diff_opts);
|
||||
|
||||
if (is_null_sha1(origin->commit->object.sha1))
|
||||
@ -459,7 +459,7 @@ static struct origin *find_origin(struct scoreboard *sb,
|
||||
}
|
||||
}
|
||||
diff_flush(&diff_opts);
|
||||
diff_tree_release_paths(&diff_opts);
|
||||
free_pathspec(&diff_opts.pathspec);
|
||||
if (porigin) {
|
||||
/*
|
||||
* Create a freestanding copy that is not part of
|
||||
@ -487,15 +487,12 @@ static struct origin *find_rename(struct scoreboard *sb,
|
||||
struct origin *porigin = NULL;
|
||||
struct diff_options diff_opts;
|
||||
int i;
|
||||
const char *paths[2];
|
||||
|
||||
diff_setup(&diff_opts);
|
||||
DIFF_OPT_SET(&diff_opts, RECURSIVE);
|
||||
diff_opts.detect_rename = DIFF_DETECT_RENAME;
|
||||
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||
diff_opts.single_follow = origin->path;
|
||||
paths[0] = NULL;
|
||||
diff_tree_setup_paths(paths, &diff_opts);
|
||||
diff_setup_done(&diff_opts);
|
||||
|
||||
if (is_null_sha1(origin->commit->object.sha1))
|
||||
@ -517,7 +514,7 @@ static struct origin *find_rename(struct scoreboard *sb,
|
||||
}
|
||||
}
|
||||
diff_flush(&diff_opts);
|
||||
diff_tree_release_paths(&diff_opts);
|
||||
free_pathspec(&diff_opts.pathspec);
|
||||
return porigin;
|
||||
}
|
||||
|
||||
@ -1065,7 +1062,6 @@ static int find_copy_in_parent(struct scoreboard *sb,
|
||||
int opt)
|
||||
{
|
||||
struct diff_options diff_opts;
|
||||
const char *paths[1];
|
||||
int i, j;
|
||||
int retval;
|
||||
struct blame_list *blame_list;
|
||||
@ -1079,8 +1075,6 @@ static int find_copy_in_parent(struct scoreboard *sb,
|
||||
DIFF_OPT_SET(&diff_opts, RECURSIVE);
|
||||
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||
|
||||
paths[0] = NULL;
|
||||
diff_tree_setup_paths(paths, &diff_opts);
|
||||
diff_setup_done(&diff_opts);
|
||||
|
||||
/* Try "find copies harder" on new path if requested;
|
||||
@ -1163,7 +1157,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
|
||||
}
|
||||
reset_scanned_flag(sb);
|
||||
diff_flush(&diff_opts);
|
||||
diff_tree_release_paths(&diff_opts);
|
||||
free_pathspec(&diff_opts.pathspec);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -64,37 +64,45 @@ static void output_exclude(const char *path, struct exclude *exclude)
|
||||
}
|
||||
|
||||
static int check_ignore(struct dir_struct *dir,
|
||||
const char *prefix, const char **pathspec)
|
||||
const char *prefix, int argc, const char **argv)
|
||||
{
|
||||
const char *path, *full_path;
|
||||
const char *full_path;
|
||||
char *seen;
|
||||
int num_ignored = 0, dtype = DT_UNKNOWN, i;
|
||||
struct exclude *exclude;
|
||||
struct pathspec pathspec;
|
||||
|
||||
if (!pathspec || !*pathspec) {
|
||||
if (!argc) {
|
||||
if (!quiet)
|
||||
fprintf(stderr, "no pathspec given.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check-ignore just needs paths. Magic beyond :/ is really
|
||||
* irrelevant.
|
||||
*/
|
||||
parse_pathspec(&pathspec,
|
||||
PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP,
|
||||
PATHSPEC_SYMLINK_LEADING_PATH |
|
||||
PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE |
|
||||
PATHSPEC_KEEP_ORDER,
|
||||
prefix, argv);
|
||||
|
||||
/*
|
||||
* look for pathspecs matching entries in the index, since these
|
||||
* should not be ignored, in order to be consistent with
|
||||
* 'git status', 'git add' etc.
|
||||
*/
|
||||
seen = find_pathspecs_matching_against_index(pathspec);
|
||||
for (i = 0; pathspec[i]; i++) {
|
||||
path = pathspec[i];
|
||||
full_path = prefix_path(prefix, prefix
|
||||
? strlen(prefix) : 0, path);
|
||||
full_path = check_path_for_gitlink(full_path);
|
||||
die_if_path_beyond_symlink(full_path, prefix);
|
||||
seen = find_pathspecs_matching_against_index(&pathspec);
|
||||
for (i = 0; i < pathspec.nr; i++) {
|
||||
full_path = pathspec.items[i].match;
|
||||
exclude = NULL;
|
||||
if (!seen[i]) {
|
||||
exclude = last_exclude_matching(dir, full_path, &dtype);
|
||||
}
|
||||
if (!quiet && (exclude || show_non_matching))
|
||||
output_exclude(path, exclude);
|
||||
output_exclude(pathspec.items[i].original, exclude);
|
||||
if (exclude)
|
||||
num_ignored++;
|
||||
}
|
||||
@ -120,7 +128,8 @@ static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix)
|
||||
strbuf_swap(&buf, &nbuf);
|
||||
}
|
||||
pathspec[0] = buf.buf;
|
||||
num_ignored += check_ignore(dir, prefix, (const char **)pathspec);
|
||||
num_ignored += check_ignore(dir, prefix,
|
||||
1, (const char **)pathspec);
|
||||
maybe_flush_or_die(stdout, "check-ignore to stdout");
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
@ -166,7 +175,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix)
|
||||
if (stdin_paths) {
|
||||
num_ignored = check_ignore_stdin_paths(&dir, prefix);
|
||||
} else {
|
||||
num_ignored = check_ignore(&dir, prefix, argv);
|
||||
num_ignored = check_ignore(&dir, prefix, argc, argv);
|
||||
maybe_flush_or_die(stdout, "ignore to stdout");
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ struct checkout_opts {
|
||||
|
||||
int branch_exists;
|
||||
const char *prefix;
|
||||
const char **pathspec;
|
||||
struct pathspec pathspec;
|
||||
struct tree *source_tree;
|
||||
};
|
||||
|
||||
@ -83,12 +83,9 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_tree_some(struct tree *tree, const char **pathspec)
|
||||
static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
|
||||
{
|
||||
struct pathspec ps;
|
||||
init_pathspec(&ps, pathspec);
|
||||
read_tree_recursive(tree, "", 0, 0, &ps, update_some, NULL);
|
||||
free_pathspec(&ps);
|
||||
read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL);
|
||||
|
||||
/* update the index with the given tree's info
|
||||
* for all args, expanding wildcards, and exit
|
||||
@ -255,20 +252,18 @@ static int checkout_paths(const struct checkout_opts *opts,
|
||||
|
||||
if (opts->patch_mode)
|
||||
return run_add_interactive(revision, "--patch=checkout",
|
||||
opts->pathspec);
|
||||
&opts->pathspec);
|
||||
|
||||
lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
|
||||
newfd = hold_locked_index(lock_file, 1);
|
||||
if (read_cache_preload(opts->pathspec) < 0)
|
||||
if (read_cache_preload(&opts->pathspec) < 0)
|
||||
return error(_("corrupt index file"));
|
||||
|
||||
if (opts->source_tree)
|
||||
read_tree_some(opts->source_tree, opts->pathspec);
|
||||
read_tree_some(opts->source_tree, &opts->pathspec);
|
||||
|
||||
for (pos = 0; opts->pathspec[pos]; pos++)
|
||||
;
|
||||
ps_matched = xcalloc(1, pos);
|
||||
ps_matched = xcalloc(1, opts->pathspec.nr);
|
||||
|
||||
/*
|
||||
* Make sure all pathspecs participated in locating the paths
|
||||
@ -302,12 +297,12 @@ static int checkout_paths(const struct checkout_opts *opts,
|
||||
* match_pathspec() for _all_ entries when
|
||||
* opts->source_tree != NULL.
|
||||
*/
|
||||
if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce),
|
||||
if (match_pathspec_depth(&opts->pathspec, ce->name, ce_namelen(ce),
|
||||
0, ps_matched))
|
||||
ce->ce_flags |= CE_MATCHED;
|
||||
}
|
||||
|
||||
if (report_path_error(ps_matched, opts->pathspec, opts->prefix)) {
|
||||
if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) {
|
||||
free(ps_matched);
|
||||
return 1;
|
||||
}
|
||||
@ -1000,7 +995,7 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
|
||||
static int checkout_branch(struct checkout_opts *opts,
|
||||
struct branch_info *new)
|
||||
{
|
||||
if (opts->pathspec)
|
||||
if (opts->pathspec.nr)
|
||||
die(_("paths cannot be used with switching branches"));
|
||||
|
||||
if (opts->patch_mode)
|
||||
@ -1151,9 +1146,18 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
if (argc) {
|
||||
opts.pathspec = get_pathspec(prefix, argv);
|
||||
/*
|
||||
* In patch mode (opts.patch_mode != 0), we pass the
|
||||
* pathspec to an external program, git-add--interactive.
|
||||
* Do not accept any kind of magic that that program
|
||||
* cannot handle. Magic mask is pretty safe to be
|
||||
* lifted for new magic when opts.patch_mode == 0.
|
||||
*/
|
||||
parse_pathspec(&opts.pathspec, 0,
|
||||
opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
|
||||
prefix, argv);
|
||||
|
||||
if (!opts.pathspec)
|
||||
if (!opts.pathspec.nr)
|
||||
die(_("invalid path specification"));
|
||||
|
||||
/*
|
||||
@ -1185,7 +1189,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
if (opts.patch_mode || opts.pathspec)
|
||||
if (opts.patch_mode || opts.pathspec.nr)
|
||||
return checkout_paths(&opts, new.name);
|
||||
else
|
||||
return checkout_branch(&opts, &new);
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "quote.h"
|
||||
#include "column.h"
|
||||
#include "color.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
static int force = -1; /* unset */
|
||||
static int interactive;
|
||||
@ -863,13 +864,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
|
||||
struct strbuf abs_path = STRBUF_INIT;
|
||||
struct dir_struct dir;
|
||||
static const char **pathspec;
|
||||
struct pathspec pathspec;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
|
||||
struct exclude_list *el;
|
||||
struct string_list_item *item;
|
||||
const char *qname;
|
||||
char *seen = NULL;
|
||||
struct option options[] = {
|
||||
OPT__QUIET(&quiet, N_("do not print names of files removed")),
|
||||
OPT__DRY_RUN(&dry_run, N_("dry run")),
|
||||
@ -925,12 +925,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
for (i = 0; i < exclude_list.nr; i++)
|
||||
add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1));
|
||||
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
parse_pathspec(&pathspec, 0,
|
||||
PATHSPEC_PREFER_CWD,
|
||||
prefix, argv);
|
||||
|
||||
fill_directory(&dir, pathspec);
|
||||
|
||||
if (pathspec)
|
||||
seen = xmalloc(argc > 0 ? argc : 1);
|
||||
fill_directory(&dir, &pathspec);
|
||||
|
||||
for (i = 0; i < dir.nr; i++) {
|
||||
struct dir_entry *ent = dir.entries[i];
|
||||
@ -961,11 +960,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
if (lstat(ent->name, &st))
|
||||
die_errno("Cannot lstat '%s'", ent->name);
|
||||
|
||||
if (pathspec) {
|
||||
memset(seen, 0, argc > 0 ? argc : 1);
|
||||
matches = match_pathspec(pathspec, ent->name, len,
|
||||
0, seen);
|
||||
}
|
||||
if (pathspec.nr)
|
||||
matches = match_pathspec_depth(&pathspec, ent->name,
|
||||
len, 0, NULL);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (remove_directories || (matches == MATCHED_EXACTLY)) {
|
||||
@ -973,7 +970,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
string_list_append(&del_list, rel);
|
||||
}
|
||||
} else {
|
||||
if (pathspec && !matches)
|
||||
if (pathspec.nr && !matches)
|
||||
continue;
|
||||
rel = relative_path(ent->name, prefix, &buf);
|
||||
string_list_append(&del_list, rel);
|
||||
@ -1019,7 +1016,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
strbuf_reset(&abs_path);
|
||||
}
|
||||
free(seen);
|
||||
|
||||
strbuf_release(&abs_path);
|
||||
strbuf_release(&buf);
|
||||
|
@ -202,17 +202,15 @@ static int commit_index_files(void)
|
||||
* and return the paths that match the given pattern in list.
|
||||
*/
|
||||
static int list_paths(struct string_list *list, const char *with_tree,
|
||||
const char *prefix, const char **pattern)
|
||||
const char *prefix, const struct pathspec *pattern)
|
||||
{
|
||||
int i;
|
||||
char *m;
|
||||
|
||||
if (!pattern)
|
||||
if (!pattern->nr)
|
||||
return 0;
|
||||
|
||||
for (i = 0; pattern[i]; i++)
|
||||
;
|
||||
m = xcalloc(1, i);
|
||||
m = xcalloc(1, pattern->nr);
|
||||
|
||||
if (with_tree) {
|
||||
char *max_prefix = common_prefix(pattern);
|
||||
@ -226,7 +224,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
|
||||
|
||||
if (ce->ce_flags & CE_UPDATE)
|
||||
continue;
|
||||
if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
|
||||
if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m))
|
||||
continue;
|
||||
item = string_list_insert(list, ce->name);
|
||||
if (ce_skip_worktree(ce))
|
||||
@ -298,17 +296,17 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
||||
{
|
||||
int fd;
|
||||
struct string_list partial;
|
||||
const char **pathspec = NULL;
|
||||
struct pathspec pathspec;
|
||||
char *old_index_env = NULL;
|
||||
int refresh_flags = REFRESH_QUIET;
|
||||
|
||||
if (is_status)
|
||||
refresh_flags |= REFRESH_UNMERGED;
|
||||
parse_pathspec(&pathspec, 0,
|
||||
PATHSPEC_PREFER_FULL,
|
||||
prefix, argv);
|
||||
|
||||
if (*argv)
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
|
||||
if (read_cache_preload(pathspec) < 0)
|
||||
if (read_cache_preload(&pathspec) < 0)
|
||||
die(_("index file corrupt"));
|
||||
|
||||
if (interactive) {
|
||||
@ -350,9 +348,9 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
||||
* (A) if all goes well, commit the real index;
|
||||
* (B) on failure, rollback the real index.
|
||||
*/
|
||||
if (all || (also && pathspec && *pathspec)) {
|
||||
if (all || (also && pathspec.nr)) {
|
||||
fd = hold_locked_index(&index_lock, 1);
|
||||
add_files_to_cache(also ? prefix : NULL, pathspec, 0);
|
||||
add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
|
||||
refresh_cache_or_die(refresh_flags);
|
||||
update_main_cache_tree(WRITE_TREE_SILENT);
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
@ -371,7 +369,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
||||
* and create commit from the_index.
|
||||
* We still need to refresh the index here.
|
||||
*/
|
||||
if (!only && (!pathspec || !*pathspec)) {
|
||||
if (!only && !pathspec.nr) {
|
||||
fd = hold_locked_index(&index_lock, 1);
|
||||
refresh_cache_or_die(refresh_flags);
|
||||
if (active_cache_changed) {
|
||||
@ -416,7 +414,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
||||
|
||||
memset(&partial, 0, sizeof(partial));
|
||||
partial.strdup_strings = 1;
|
||||
if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec))
|
||||
if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
|
||||
exit(1);
|
||||
|
||||
discard_cache();
|
||||
@ -1259,11 +1257,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
||||
handle_untracked_files_arg(&s);
|
||||
if (show_ignored_in_status)
|
||||
s.show_ignored_files = 1;
|
||||
if (*argv)
|
||||
s.pathspec = get_pathspec(prefix, argv);
|
||||
parse_pathspec(&s.pathspec, 0,
|
||||
PATHSPEC_PREFER_FULL,
|
||||
prefix, argv);
|
||||
|
||||
read_cache_preload(s.pathspec);
|
||||
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
|
||||
read_cache_preload(&s.pathspec);
|
||||
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
|
||||
|
||||
fd = hold_locked_index(&index_lock, 0);
|
||||
if (0 <= fd)
|
||||
|
@ -61,7 +61,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
|
||||
(rev.diffopt.output_format & DIFF_FORMAT_PATCH))
|
||||
rev.combine_merges = rev.dense_combined_merges = 1;
|
||||
|
||||
if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) {
|
||||
if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
|
||||
usage(diff_cache_usage);
|
||||
if (!cached) {
|
||||
setup_work_tree();
|
||||
if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) {
|
||||
if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ static int builtin_diff_index(struct rev_info *revs,
|
||||
usage(builtin_diff_usage);
|
||||
if (!cached) {
|
||||
setup_work_tree();
|
||||
if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
|
||||
if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
@ -242,7 +242,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
|
||||
revs->combine_merges = revs->dense_combined_merges = 1;
|
||||
|
||||
setup_work_tree();
|
||||
if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
|
||||
if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
@ -367,6 +367,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
}
|
||||
if (rev.prune_data.nr) {
|
||||
/* builtin_diff_b_f() */
|
||||
GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
|
||||
if (!path)
|
||||
path = rev.prune_data.items[0].match;
|
||||
paths += rev.prune_data.nr;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "grep.h"
|
||||
#include "quote.h"
|
||||
#include "dir.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
static char const * const grep_usage[] = {
|
||||
N_("git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]"),
|
||||
@ -521,7 +522,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
|
||||
if (exc_std)
|
||||
setup_standard_excludes(&dir);
|
||||
|
||||
fill_directory(&dir, pathspec->raw);
|
||||
fill_directory(&dir, pathspec);
|
||||
for (i = 0; i < dir.nr; i++) {
|
||||
const char *name = dir.entries[i]->name;
|
||||
int namelen = strlen(name);
|
||||
@ -629,7 +630,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||
const char *show_in_pager = NULL, *default_pager = "dummy";
|
||||
struct grep_opt opt;
|
||||
struct object_array list = OBJECT_ARRAY_INIT;
|
||||
const char **paths = NULL;
|
||||
struct pathspec pathspec;
|
||||
struct string_list path_list = STRING_LIST_INIT_NODUP;
|
||||
int i;
|
||||
@ -856,8 +856,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||
verify_filename(prefix, argv[j], j == i);
|
||||
}
|
||||
|
||||
paths = get_pathspec(prefix, argv + i);
|
||||
init_pathspec(&pathspec, paths);
|
||||
parse_pathspec(&pathspec, 0,
|
||||
PATHSPEC_PREFER_CWD |
|
||||
(opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
|
||||
prefix, argv + i);
|
||||
pathspec.max_depth = opt.max_depth;
|
||||
pathspec.recursive = 1;
|
||||
|
||||
|
@ -503,7 +503,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
|
||||
init_grep_defaults();
|
||||
git_config(git_log_config, NULL);
|
||||
|
||||
init_pathspec(&match_all, NULL);
|
||||
memset(&match_all, 0, sizeof(match_all));
|
||||
init_revisions(&rev, prefix);
|
||||
rev.diff = 1;
|
||||
rev.always_show_header = 1;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "parse-options.h"
|
||||
#include "resolve-undo.h"
|
||||
#include "string-list.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
static int abbrev;
|
||||
static int show_deleted;
|
||||
@ -30,7 +31,7 @@ static int debug_mode;
|
||||
static const char *prefix;
|
||||
static int max_prefix_len;
|
||||
static int prefix_len;
|
||||
static const char **pathspec;
|
||||
static struct pathspec pathspec;
|
||||
static int error_unmatch;
|
||||
static char *ps_matched;
|
||||
static const char *with_tree;
|
||||
@ -63,7 +64,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
|
||||
if (len >= ent->len)
|
||||
die("git ls-files: internal error - directory entry not superset of prefix");
|
||||
|
||||
if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))
|
||||
if (!match_pathspec_depth(&pathspec, ent->name, ent->len, len, ps_matched))
|
||||
return;
|
||||
|
||||
fputs(tag, stdout);
|
||||
@ -138,7 +139,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)
|
||||
if (len >= ce_namelen(ce))
|
||||
die("git ls-files: internal error - cache entry not superset of prefix");
|
||||
|
||||
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched))
|
||||
if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), len, ps_matched))
|
||||
return;
|
||||
|
||||
if (tag && *tag && show_valid_bit &&
|
||||
@ -194,7 +195,7 @@ static void show_ru_info(void)
|
||||
len = strlen(path);
|
||||
if (len < max_prefix_len)
|
||||
continue; /* outside of the prefix */
|
||||
if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
|
||||
if (!match_pathspec_depth(&pathspec, path, len, max_prefix_len, ps_matched))
|
||||
continue; /* uninterested */
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (!ui->mode[i])
|
||||
@ -219,7 +220,7 @@ static void show_files(struct dir_struct *dir)
|
||||
|
||||
/* For cached/deleted files we don't need to even do the readdir */
|
||||
if (show_others || show_killed) {
|
||||
fill_directory(dir, pathspec);
|
||||
fill_directory(dir, &pathspec);
|
||||
if (show_others)
|
||||
show_other_files(dir);
|
||||
if (show_killed)
|
||||
@ -287,21 +288,6 @@ static void prune_cache(const char *prefix)
|
||||
active_nr = last;
|
||||
}
|
||||
|
||||
static void strip_trailing_slash_from_submodules(void)
|
||||
{
|
||||
const char **p;
|
||||
|
||||
for (p = pathspec; *p != NULL; p++) {
|
||||
int len = strlen(*p), pos;
|
||||
|
||||
if (len < 1 || (*p)[len - 1] != '/')
|
||||
continue;
|
||||
pos = cache_name_pos(*p, len - 1);
|
||||
if (pos >= 0 && S_ISGITLINK(active_cache[pos]->ce_mode))
|
||||
*p = xstrndup(*p, len - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the tree specified with --with-tree option
|
||||
* (typically, HEAD) into stage #1 and then
|
||||
@ -333,13 +319,12 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
static const char *(matchbuf[2]);
|
||||
matchbuf[0] = prefix;
|
||||
matchbuf[1] = NULL;
|
||||
init_pathspec(&pathspec, matchbuf);
|
||||
pathspec.items[0].nowildcard_len = pathspec.items[0].len;
|
||||
static const char *(matchbuf[1]);
|
||||
matchbuf[0] = NULL;
|
||||
parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
|
||||
PATHSPEC_PREFER_CWD, prefix, matchbuf);
|
||||
} else
|
||||
init_pathspec(&pathspec, NULL);
|
||||
memset(&pathspec, 0, sizeof(pathspec));
|
||||
if (read_tree(tree, 1, &pathspec))
|
||||
die("unable to read tree entries %s", tree_name);
|
||||
|
||||
@ -364,15 +349,16 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
|
||||
}
|
||||
}
|
||||
|
||||
int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix)
|
||||
int report_path_error(const char *ps_matched,
|
||||
const struct pathspec *pathspec,
|
||||
const char *prefix)
|
||||
{
|
||||
/*
|
||||
* Make sure all pathspec matched; otherwise it is an error.
|
||||
*/
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
const char *name;
|
||||
int num, errors = 0;
|
||||
for (num = 0; pathspec[num]; num++) {
|
||||
for (num = 0; num < pathspec->nr; num++) {
|
||||
int other, found_dup;
|
||||
|
||||
if (ps_matched[num])
|
||||
@ -380,13 +366,16 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char
|
||||
/*
|
||||
* The caller might have fed identical pathspec
|
||||
* twice. Do not barf on such a mistake.
|
||||
* FIXME: parse_pathspec should have eliminated
|
||||
* duplicate pathspec.
|
||||
*/
|
||||
for (found_dup = other = 0;
|
||||
!found_dup && pathspec[other];
|
||||
!found_dup && other < pathspec->nr;
|
||||
other++) {
|
||||
if (other == num || !ps_matched[other])
|
||||
continue;
|
||||
if (!strcmp(pathspec[other], pathspec[num]))
|
||||
if (!strcmp(pathspec->items[other].original,
|
||||
pathspec->items[num].original))
|
||||
/*
|
||||
* Ok, we have a match already.
|
||||
*/
|
||||
@ -395,9 +384,8 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char
|
||||
if (found_dup)
|
||||
continue;
|
||||
|
||||
name = quote_path_relative(pathspec[num], prefix, &sb);
|
||||
error("pathspec '%s' did not match any file(s) known to git.",
|
||||
name);
|
||||
pathspec->items[num].original);
|
||||
errors++;
|
||||
}
|
||||
strbuf_release(&sb);
|
||||
@ -555,23 +543,18 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
|
||||
if (require_work_tree && !is_inside_work_tree())
|
||||
setup_work_tree();
|
||||
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
|
||||
/* be nice with submodule paths ending in a slash */
|
||||
if (pathspec)
|
||||
strip_trailing_slash_from_submodules();
|
||||
parse_pathspec(&pathspec, 0,
|
||||
PATHSPEC_PREFER_CWD |
|
||||
PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
|
||||
prefix, argv);
|
||||
|
||||
/* Find common prefix for all pathspec's */
|
||||
max_prefix = common_prefix(pathspec);
|
||||
max_prefix = common_prefix(&pathspec);
|
||||
max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
|
||||
|
||||
/* Treat unmatching pathspec elements as errors */
|
||||
if (pathspec && error_unmatch) {
|
||||
int num;
|
||||
for (num = 0; pathspec[num]; num++)
|
||||
;
|
||||
ps_matched = xcalloc(1, num);
|
||||
}
|
||||
if (pathspec.nr && error_unmatch)
|
||||
ps_matched = xcalloc(1, pathspec.nr);
|
||||
|
||||
if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given)
|
||||
die("ls-files --ignored needs some exclude pattern");
|
||||
@ -598,7 +581,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
|
||||
|
||||
if (ps_matched) {
|
||||
int bad;
|
||||
bad = report_path_error(ps_matched, pathspec, prefix);
|
||||
bad = report_path_error(ps_matched, &pathspec, prefix);
|
||||
if (bad)
|
||||
fprintf(stderr, "Did you forget to 'git add'?\n");
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "quote.h"
|
||||
#include "builtin.h"
|
||||
#include "parse-options.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
static int line_termination = '\n';
|
||||
#define LS_RECURSIVE 1
|
||||
@ -35,7 +36,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
|
||||
if (ls_options & LS_RECURSIVE)
|
||||
return 1;
|
||||
|
||||
s = pathspec.raw;
|
||||
s = pathspec._raw;
|
||||
if (!s)
|
||||
return 0;
|
||||
|
||||
@ -166,7 +167,15 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
|
||||
if (get_sha1(argv[0], sha1))
|
||||
die("Not a valid object name %s", argv[0]);
|
||||
|
||||
init_pathspec(&pathspec, get_pathspec(prefix, argv + 1));
|
||||
/*
|
||||
* show_recursive() rolls its own matching code and is
|
||||
* generally ignorant of 'struct pathspec'. The magic mask
|
||||
* cannot be lifted until it is converted to use
|
||||
* match_pathspec_depth() or tree_entry_interesting()
|
||||
*/
|
||||
parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE,
|
||||
PATHSPEC_PREFER_CWD,
|
||||
prefix, argv + 1);
|
||||
for (i = 0; i < pathspec.nr; i++)
|
||||
pathspec.items[i].nowildcard_len = pathspec.items[i].len;
|
||||
pathspec.has_wildcard = 0;
|
||||
|
131
builtin/mv.c
131
builtin/mv.c
@ -9,14 +9,16 @@
|
||||
#include "cache-tree.h"
|
||||
#include "string-list.h"
|
||||
#include "parse-options.h"
|
||||
#include "submodule.h"
|
||||
|
||||
static const char * const builtin_mv_usage[] = {
|
||||
N_("git mv [options] <source>... <destination>"),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char **copy_pathspec(const char *prefix, const char **pathspec,
|
||||
int count, int base_name)
|
||||
static const char **internal_copy_pathspec(const char *prefix,
|
||||
const char **pathspec,
|
||||
int count, int base_name)
|
||||
{
|
||||
int i;
|
||||
const char **result = xmalloc((count + 1) * sizeof(const char *));
|
||||
@ -56,7 +58,7 @@ static struct lock_file lock_file;
|
||||
|
||||
int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, newfd;
|
||||
int i, newfd, gitmodules_modified = 0;
|
||||
int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
|
||||
struct option builtin_mv_options[] = {
|
||||
OPT__VERBOSE(&verbose, N_("be verbose")),
|
||||
@ -65,11 +67,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")),
|
||||
OPT_END(),
|
||||
};
|
||||
const char **source, **destination, **dest_path;
|
||||
const char **source, **destination, **dest_path, **submodule_gitfile;
|
||||
enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
|
||||
struct stat st;
|
||||
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
|
||||
|
||||
gitmodules_config();
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
argc = parse_options(argc, argv, prefix, builtin_mv_options,
|
||||
@ -81,17 +84,18 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
if (read_cache() < 0)
|
||||
die(_("index file corrupt"));
|
||||
|
||||
source = copy_pathspec(prefix, argv, argc, 0);
|
||||
source = internal_copy_pathspec(prefix, argv, argc, 0);
|
||||
modes = xcalloc(argc, sizeof(enum update_mode));
|
||||
dest_path = copy_pathspec(prefix, argv + argc, 1, 0);
|
||||
dest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0);
|
||||
submodule_gitfile = xcalloc(argc, sizeof(char *));
|
||||
|
||||
if (dest_path[0][0] == '\0')
|
||||
/* special case: "." was normalized to "" */
|
||||
destination = copy_pathspec(dest_path[0], argv, argc, 1);
|
||||
destination = internal_copy_pathspec(dest_path[0], argv, argc, 1);
|
||||
else if (!lstat(dest_path[0], &st) &&
|
||||
S_ISDIR(st.st_mode)) {
|
||||
dest_path[0] = add_slash(dest_path[0]);
|
||||
destination = copy_pathspec(dest_path[0], argv, argc, 1);
|
||||
destination = internal_copy_pathspec(dest_path[0], argv, argc, 1);
|
||||
} else {
|
||||
if (argc != 1)
|
||||
die("destination '%s' is not a directory", dest_path[0]);
|
||||
@ -117,55 +121,68 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
&& lstat(dst, &st) == 0)
|
||||
bad = _("cannot move directory over file");
|
||||
else if (src_is_dir) {
|
||||
const char *src_w_slash = add_slash(src);
|
||||
int len_w_slash = length + 1;
|
||||
int first, last;
|
||||
int first = cache_name_pos(src, length);
|
||||
if (first >= 0) {
|
||||
struct strbuf submodule_dotgit = STRBUF_INIT;
|
||||
if (!S_ISGITLINK(active_cache[first]->ce_mode))
|
||||
die (_("Huh? Directory %s is in index and no submodule?"), src);
|
||||
if (!is_staging_gitmodules_ok())
|
||||
die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
|
||||
strbuf_addf(&submodule_dotgit, "%s/.git", src);
|
||||
submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf);
|
||||
if (submodule_gitfile[i])
|
||||
submodule_gitfile[i] = xstrdup(submodule_gitfile[i]);
|
||||
strbuf_release(&submodule_dotgit);
|
||||
} else {
|
||||
const char *src_w_slash = add_slash(src);
|
||||
int last, len_w_slash = length + 1;
|
||||
|
||||
modes[i] = WORKING_DIRECTORY;
|
||||
modes[i] = WORKING_DIRECTORY;
|
||||
|
||||
first = cache_name_pos(src_w_slash, len_w_slash);
|
||||
if (first >= 0)
|
||||
die (_("Huh? %.*s is in index?"),
|
||||
len_w_slash, src_w_slash);
|
||||
first = cache_name_pos(src_w_slash, len_w_slash);
|
||||
if (first >= 0)
|
||||
die (_("Huh? %.*s is in index?"),
|
||||
len_w_slash, src_w_slash);
|
||||
|
||||
first = -1 - first;
|
||||
for (last = first; last < active_nr; last++) {
|
||||
const char *path = active_cache[last]->name;
|
||||
if (strncmp(path, src_w_slash, len_w_slash))
|
||||
break;
|
||||
}
|
||||
free((char *)src_w_slash);
|
||||
|
||||
if (last - first < 1)
|
||||
bad = _("source directory is empty");
|
||||
else {
|
||||
int j, dst_len;
|
||||
|
||||
if (last - first > 0) {
|
||||
source = xrealloc(source,
|
||||
(argc + last - first)
|
||||
* sizeof(char *));
|
||||
destination = xrealloc(destination,
|
||||
(argc + last - first)
|
||||
* sizeof(char *));
|
||||
modes = xrealloc(modes,
|
||||
(argc + last - first)
|
||||
* sizeof(enum update_mode));
|
||||
first = -1 - first;
|
||||
for (last = first; last < active_nr; last++) {
|
||||
const char *path = active_cache[last]->name;
|
||||
if (strncmp(path, src_w_slash, len_w_slash))
|
||||
break;
|
||||
}
|
||||
free((char *)src_w_slash);
|
||||
|
||||
dst = add_slash(dst);
|
||||
dst_len = strlen(dst);
|
||||
if (last - first < 1)
|
||||
bad = _("source directory is empty");
|
||||
else {
|
||||
int j, dst_len;
|
||||
|
||||
for (j = 0; j < last - first; j++) {
|
||||
const char *path =
|
||||
active_cache[first + j]->name;
|
||||
source[argc + j] = path;
|
||||
destination[argc + j] =
|
||||
prefix_path(dst, dst_len,
|
||||
path + length + 1);
|
||||
modes[argc + j] = INDEX;
|
||||
if (last - first > 0) {
|
||||
source = xrealloc(source,
|
||||
(argc + last - first)
|
||||
* sizeof(char *));
|
||||
destination = xrealloc(destination,
|
||||
(argc + last - first)
|
||||
* sizeof(char *));
|
||||
modes = xrealloc(modes,
|
||||
(argc + last - first)
|
||||
* sizeof(enum update_mode));
|
||||
}
|
||||
|
||||
dst = add_slash(dst);
|
||||
dst_len = strlen(dst);
|
||||
|
||||
for (j = 0; j < last - first; j++) {
|
||||
const char *path =
|
||||
active_cache[first + j]->name;
|
||||
source[argc + j] = path;
|
||||
destination[argc + j] =
|
||||
prefix_path(dst, dst_len,
|
||||
path + length + 1);
|
||||
modes[argc + j] = INDEX;
|
||||
}
|
||||
argc += last - first;
|
||||
}
|
||||
argc += last - first;
|
||||
}
|
||||
} else if (cache_name_pos(src, length) < 0)
|
||||
bad = _("not under version control");
|
||||
@ -210,9 +227,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
int pos;
|
||||
if (show_only || verbose)
|
||||
printf(_("Renaming %s to %s\n"), src, dst);
|
||||
if (!show_only && mode != INDEX &&
|
||||
rename(src, dst) < 0 && !ignore_errors)
|
||||
die_errno (_("renaming '%s' failed"), src);
|
||||
if (!show_only && mode != INDEX) {
|
||||
if (rename(src, dst) < 0 && !ignore_errors)
|
||||
die_errno (_("renaming '%s' failed"), src);
|
||||
if (submodule_gitfile[i])
|
||||
connect_work_tree_and_git_dir(dst, submodule_gitfile[i]);
|
||||
if (!update_path_in_gitmodules(src, dst))
|
||||
gitmodules_modified = 1;
|
||||
}
|
||||
|
||||
if (mode == WORKING_DIRECTORY)
|
||||
continue;
|
||||
@ -223,6 +245,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
rename_cache_entry_at(pos, dst);
|
||||
}
|
||||
|
||||
if (gitmodules_modified)
|
||||
stage_updated_gitmodules();
|
||||
|
||||
if (active_cache_changed) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(&lock_file))
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "rerere.h"
|
||||
#include "xdiff/xdiff.h"
|
||||
#include "xdiff-interface.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
static const char * const rerere_usage[] = {
|
||||
N_("git rerere [clear | forget path... | status | remaining | diff | gc]"),
|
||||
@ -68,11 +69,12 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
|
||||
return rerere(flags);
|
||||
|
||||
if (!strcmp(argv[0], "forget")) {
|
||||
const char **pathspec;
|
||||
struct pathspec pathspec;
|
||||
if (argc < 2)
|
||||
warning("'git rerere forget' without paths is deprecated");
|
||||
pathspec = get_pathspec(prefix, argv + 1);
|
||||
return rerere_forget(pathspec);
|
||||
parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD,
|
||||
prefix, argv + 1);
|
||||
return rerere_forget(&pathspec);
|
||||
}
|
||||
|
||||
fd = setup_rerere(&merge_rr, flags);
|
||||
|
@ -133,12 +133,13 @@ static void update_index_from_diff(struct diff_queue_struct *q,
|
||||
}
|
||||
}
|
||||
|
||||
static int read_from_tree(const char **pathspec, unsigned char *tree_sha1)
|
||||
static int read_from_tree(const struct pathspec *pathspec,
|
||||
unsigned char *tree_sha1)
|
||||
{
|
||||
struct diff_options opt;
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
diff_tree_setup_paths(pathspec, &opt);
|
||||
copy_pathspec(&opt.pathspec, pathspec);
|
||||
opt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
opt.format_callback = update_index_from_diff;
|
||||
|
||||
@ -147,7 +148,7 @@ static int read_from_tree(const char **pathspec, unsigned char *tree_sha1)
|
||||
return 1;
|
||||
diffcore_std(&opt);
|
||||
diff_flush(&opt);
|
||||
diff_tree_release_paths(&opt);
|
||||
free_pathspec(&opt.pathspec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -174,7 +175,10 @@ static void die_if_unmerged_cache(int reset_type)
|
||||
|
||||
}
|
||||
|
||||
static const char **parse_args(const char **argv, const char *prefix, const char **rev_ret)
|
||||
static void parse_args(struct pathspec *pathspec,
|
||||
const char **argv, const char *prefix,
|
||||
int patch_mode,
|
||||
const char **rev_ret)
|
||||
{
|
||||
const char *rev = "HEAD";
|
||||
unsigned char unused[20];
|
||||
@ -216,7 +220,10 @@ static const char **parse_args(const char **argv, const char *prefix, const char
|
||||
}
|
||||
}
|
||||
*rev_ret = rev;
|
||||
return argv[0] ? get_pathspec(prefix, argv) : NULL;
|
||||
parse_pathspec(pathspec, 0,
|
||||
PATHSPEC_PREFER_FULL |
|
||||
(patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0),
|
||||
prefix, argv);
|
||||
}
|
||||
|
||||
static int update_refs(const char *rev, const unsigned char *sha1)
|
||||
@ -246,7 +253,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
int patch_mode = 0, unborn;
|
||||
const char *rev;
|
||||
unsigned char sha1[20];
|
||||
const char **pathspec = NULL;
|
||||
struct pathspec pathspec;
|
||||
const struct option options[] = {
|
||||
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
|
||||
OPT_SET_INT(0, "mixed", &reset_type,
|
||||
@ -266,13 +273,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
|
||||
argc = parse_options(argc, argv, prefix, options, git_reset_usage,
|
||||
PARSE_OPT_KEEP_DASHDASH);
|
||||
pathspec = parse_args(argv, prefix, &rev);
|
||||
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
|
||||
|
||||
unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", sha1);
|
||||
if (unborn) {
|
||||
/* reset on unborn branch: treat as reset to empty tree */
|
||||
hashcpy(sha1, EMPTY_TREE_SHA1_BIN);
|
||||
} else if (!pathspec) {
|
||||
} else if (!pathspec.nr) {
|
||||
struct commit *commit;
|
||||
if (get_sha1_committish(rev, sha1))
|
||||
die(_("Failed to resolve '%s' as a valid revision."), rev);
|
||||
@ -293,13 +300,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
if (patch_mode) {
|
||||
if (reset_type != NONE)
|
||||
die(_("--patch is incompatible with --{hard,mixed,soft}"));
|
||||
return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", pathspec);
|
||||
return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", &pathspec);
|
||||
}
|
||||
|
||||
/* git reset tree [--] paths... can be used to
|
||||
* load chosen paths from the tree into the index without
|
||||
* affecting the working tree nor HEAD. */
|
||||
if (pathspec) {
|
||||
if (pathspec.nr) {
|
||||
if (reset_type == MIXED)
|
||||
warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead."));
|
||||
else if (reset_type != NONE)
|
||||
@ -326,7 +333,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
|
||||
int newfd = hold_locked_index(lock, 1);
|
||||
if (reset_type == MIXED) {
|
||||
if (read_from_tree(pathspec, sha1))
|
||||
if (read_from_tree(&pathspec, sha1))
|
||||
return 1;
|
||||
} else {
|
||||
int err = reset_index(sha1, reset_type, quiet);
|
||||
@ -347,7 +354,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
die(_("Could not write new index file."));
|
||||
}
|
||||
|
||||
if (!pathspec && !unborn) {
|
||||
if (!pathspec.nr && !unborn) {
|
||||
/* Any resets without paths update HEAD to the head being
|
||||
* switched to, saving the previous head in ORIG_HEAD before. */
|
||||
update_ref_status = update_refs(rev, sha1);
|
||||
@ -355,7 +362,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
if (reset_type == HARD && !update_ref_status && !quiet)
|
||||
print_new_head_line(lookup_commit_reference(sha1));
|
||||
}
|
||||
if (!pathspec)
|
||||
if (!pathspec.nr)
|
||||
remove_branch_state();
|
||||
|
||||
return update_ref_status;
|
||||
|
68
builtin/rm.c
68
builtin/rm.c
@ -11,6 +11,7 @@
|
||||
#include "parse-options.h"
|
||||
#include "string-list.h"
|
||||
#include "submodule.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
static const char * const builtin_rm_usage[] = {
|
||||
N_("git rm [options] [--] <file>..."),
|
||||
@ -277,10 +278,11 @@ static struct option builtin_rm_options[] = {
|
||||
|
||||
int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, newfd, seen_any;
|
||||
const char **pathspec, *match;
|
||||
int i, newfd;
|
||||
struct pathspec pathspec;
|
||||
char *seen;
|
||||
|
||||
gitmodules_config();
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
argc = parse_options(argc, argv, prefix, builtin_rm_options,
|
||||
@ -311,40 +313,45 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
}
|
||||
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL);
|
||||
parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv);
|
||||
refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);
|
||||
|
||||
for (i = 0; pathspec[i] ; i++)
|
||||
/* nothing */;
|
||||
seen = xcalloc(i, 1);
|
||||
seen = xcalloc(pathspec.nr, 1);
|
||||
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
const struct cache_entry *ce = active_cache[i];
|
||||
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
|
||||
if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen))
|
||||
continue;
|
||||
ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
|
||||
list.entry[list.nr].name = ce->name;
|
||||
list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode);
|
||||
list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
|
||||
if (list.entry[list.nr++].is_submodule &&
|
||||
!is_staging_gitmodules_ok())
|
||||
die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
|
||||
}
|
||||
|
||||
|
||||
seen_any = 0;
|
||||
for (i = 0; (match = pathspec[i]) != NULL ; i++) {
|
||||
if (!seen[i]) {
|
||||
if (!ignore_unmatch) {
|
||||
die(_("pathspec '%s' did not match any files"),
|
||||
match);
|
||||
if (pathspec.nr) {
|
||||
const char *original;
|
||||
int seen_any = 0;
|
||||
for (i = 0; i < pathspec.nr; i++) {
|
||||
original = pathspec.items[i].original;
|
||||
if (!seen[i]) {
|
||||
if (!ignore_unmatch) {
|
||||
die(_("pathspec '%s' did not match any files"),
|
||||
original);
|
||||
}
|
||||
}
|
||||
else {
|
||||
seen_any = 1;
|
||||
}
|
||||
if (!recursive && seen[i] == MATCHED_RECURSIVELY)
|
||||
die(_("not removing '%s' recursively without -r"),
|
||||
*original ? original : ".");
|
||||
}
|
||||
else {
|
||||
seen_any = 1;
|
||||
}
|
||||
if (!recursive && seen[i] == MATCHED_RECURSIVELY)
|
||||
die(_("not removing '%s' recursively without -r"),
|
||||
*match ? match : ".");
|
||||
|
||||
if (!seen_any)
|
||||
exit(0);
|
||||
}
|
||||
if (!seen_any)
|
||||
exit(0);
|
||||
|
||||
/*
|
||||
* If not forced, the file, the index and the HEAD (if exists)
|
||||
@ -392,13 +399,15 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
* in the middle)
|
||||
*/
|
||||
if (!index_only) {
|
||||
int removed = 0;
|
||||
int removed = 0, gitmodules_modified = 0;
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
const char *path = list.entry[i].name;
|
||||
if (list.entry[i].is_submodule) {
|
||||
if (is_empty_dir(path)) {
|
||||
if (!rmdir(path)) {
|
||||
removed = 1;
|
||||
if (!remove_path_from_gitmodules(path))
|
||||
gitmodules_modified = 1;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
@ -406,9 +415,14 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
strbuf_addstr(&buf, path);
|
||||
if (!remove_dir_recursively(&buf, 0)) {
|
||||
removed = 1;
|
||||
if (!remove_path_from_gitmodules(path))
|
||||
gitmodules_modified = 1;
|
||||
strbuf_release(&buf);
|
||||
continue;
|
||||
}
|
||||
} else if (!file_exists(path))
|
||||
/* Submodule was removed by user */
|
||||
if (!remove_path_from_gitmodules(path))
|
||||
gitmodules_modified = 1;
|
||||
strbuf_release(&buf);
|
||||
/* Fallthrough and let remove_path() fail. */
|
||||
}
|
||||
@ -420,6 +434,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
if (!removed)
|
||||
die_errno("git rm: '%s'", path);
|
||||
}
|
||||
if (gitmodules_modified)
|
||||
stage_updated_gitmodules();
|
||||
}
|
||||
|
||||
if (active_cache_changed) {
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "refs.h"
|
||||
#include "resolve-undo.h"
|
||||
#include "parse-options.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
/*
|
||||
* Default to not allowing changes to the list of files. The
|
||||
@ -546,10 +547,11 @@ static int do_reupdate(int ac, const char **av,
|
||||
*/
|
||||
int pos;
|
||||
int has_head = 1;
|
||||
const char **paths = get_pathspec(prefix, av + 1);
|
||||
struct pathspec pathspec;
|
||||
|
||||
init_pathspec(&pathspec, paths);
|
||||
parse_pathspec(&pathspec, 0,
|
||||
PATHSPEC_PREFER_CWD,
|
||||
prefix, av + 1);
|
||||
|
||||
if (read_ref("HEAD", head_sha1))
|
||||
/* If there is no HEAD, that means it is an initial
|
||||
|
35
cache.h
35
cache.h
@ -189,6 +189,8 @@ struct cache_entry {
|
||||
#error "CE_EXTENDED_FLAGS out of range"
|
||||
#endif
|
||||
|
||||
struct pathspec;
|
||||
|
||||
/*
|
||||
* Copy the sha1 and stat state of a cache entry from one to
|
||||
* another. But we never change the name, or the hash state!
|
||||
@ -365,6 +367,9 @@ static inline enum object_type object_type(unsigned int mode)
|
||||
#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
|
||||
#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
|
||||
#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
|
||||
#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
|
||||
#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
|
||||
#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
|
||||
|
||||
/*
|
||||
* This environment variable is expected to contain a boolean indicating
|
||||
@ -412,6 +417,7 @@ extern void setup_work_tree(void);
|
||||
extern const char *setup_git_directory_gently(int *);
|
||||
extern const char *setup_git_directory(void);
|
||||
extern char *prefix_path(const char *prefix, int len, const char *path);
|
||||
extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
|
||||
extern const char *prefix_filename(const char *prefix, int len, const char *path);
|
||||
extern int check_filename(const char *prefix, const char *name);
|
||||
extern void verify_filename(const char *prefix,
|
||||
@ -449,7 +455,7 @@ extern void sanitize_stdfds(void);
|
||||
|
||||
/* Initialize and use the cache information */
|
||||
extern int read_index(struct index_state *);
|
||||
extern int read_index_preload(struct index_state *, const char **pathspec);
|
||||
extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
|
||||
extern int read_index_from(struct index_state *, const char *path);
|
||||
extern int is_index_unborn(struct index_state *);
|
||||
extern int read_index_unmerged(struct index_state *);
|
||||
@ -491,28 +497,8 @@ extern void *read_blob_data_from_index(struct index_state *, const char *, unsig
|
||||
extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
|
||||
extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
|
||||
|
||||
#define PATHSPEC_ONESTAR 1 /* the pathspec pattern satisfies GFNM_ONESTAR */
|
||||
|
||||
struct pathspec {
|
||||
const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
|
||||
int nr;
|
||||
unsigned int has_wildcard:1;
|
||||
unsigned int recursive:1;
|
||||
int max_depth;
|
||||
struct pathspec_item {
|
||||
const char *match;
|
||||
int len;
|
||||
int nowildcard_len;
|
||||
int flags;
|
||||
} *items;
|
||||
};
|
||||
|
||||
extern int init_pathspec(struct pathspec *, const char **);
|
||||
extern void free_pathspec(struct pathspec *);
|
||||
extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
|
||||
|
||||
extern int limit_pathspec_to_literal(void);
|
||||
|
||||
#define HASH_WRITE_OBJECT 1
|
||||
#define HASH_FORMAT_CHECK 2
|
||||
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
|
||||
@ -540,7 +526,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
|
||||
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
|
||||
#define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */
|
||||
#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
|
||||
extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
|
||||
extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
|
||||
|
||||
struct lock_file {
|
||||
struct lock_file *next;
|
||||
@ -762,6 +748,7 @@ const char *real_path(const char *path);
|
||||
const char *real_path_if_valid(const char *path);
|
||||
const char *absolute_path(const char *path);
|
||||
const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
|
||||
int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
|
||||
int normalize_path_copy(char *dst, const char *src);
|
||||
int longest_ancestor_length(const char *path, struct string_list *prefixes);
|
||||
char *strip_path_suffix(const char *path, const char *suffix);
|
||||
@ -1243,7 +1230,7 @@ void packet_trace_identity(const char *prog);
|
||||
* return 0 if success, 1 - if addition of a file failed and
|
||||
* ADD_FILES_IGNORE_ERRORS was specified in flags
|
||||
*/
|
||||
int add_files_to_cache(const char *prefix, const char **pathspec, int flags);
|
||||
int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
|
||||
|
||||
/* diff.c */
|
||||
extern int diff_auto_refresh_index;
|
||||
@ -1277,7 +1264,7 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
|
||||
#define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK)
|
||||
|
||||
/* ls-files */
|
||||
int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix);
|
||||
int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix);
|
||||
void overlay_tree_on_cache(const char *tree_name, const char *prefix);
|
||||
|
||||
char *alias_lookup(const char *alias);
|
||||
|
@ -1306,7 +1306,7 @@ void diff_tree_combined(const unsigned char *sha1,
|
||||
int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
|
||||
|
||||
diffopts = *opt;
|
||||
diff_tree_setup_paths(diffopts.pathspec.raw, &diffopts);
|
||||
copy_pathspec(&diffopts.pathspec, &opt->pathspec);
|
||||
diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||
DIFF_OPT_SET(&diffopts, RECURSIVE);
|
||||
DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
|
||||
@ -1378,7 +1378,7 @@ void diff_tree_combined(const unsigned char *sha1,
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
diff_tree_release_paths(&diffopts);
|
||||
free_pathspec(&diffopts.pathspec);
|
||||
}
|
||||
|
||||
void diff_tree_combined_merge(const struct commit *commit, int dense,
|
||||
|
2
commit.h
2
commit.h
@ -208,7 +208,7 @@ int in_merge_bases_many(struct commit *, int, struct commit **);
|
||||
|
||||
extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
|
||||
extern int run_add_interactive(const char *revision, const char *patch_mode,
|
||||
const char **pathspec);
|
||||
const struct pathspec *pathspec);
|
||||
|
||||
static inline int single_parent(struct commit *commit)
|
||||
{
|
||||
|
@ -474,7 +474,6 @@ static int diff_cache(struct rev_info *revs,
|
||||
opts.dst_index = NULL;
|
||||
opts.pathspec = &revs->diffopt.pathspec;
|
||||
opts.pathspec->recursive = 1;
|
||||
opts.pathspec->max_depth = -1;
|
||||
|
||||
init_tree_desc(&t, tree->buffer, tree->size);
|
||||
return unpack_trees(1, &t, &opts);
|
||||
@ -500,7 +499,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
|
||||
struct rev_info revs;
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
init_pathspec(&revs.prune_data, opt->pathspec.raw);
|
||||
copy_pathspec(&revs.prune_data, &opt->pathspec);
|
||||
revs.diffopt = *opt;
|
||||
|
||||
if (diff_cache(&revs, tree_sha1, NULL, 1))
|
||||
|
3
diff.h
3
diff.h
@ -5,6 +5,7 @@
|
||||
#define DIFF_H
|
||||
|
||||
#include "tree-walk.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
struct rev_info;
|
||||
struct diff_options;
|
||||
@ -182,8 +183,6 @@ const char *diff_line_prefix(struct diff_options *);
|
||||
|
||||
extern const char mime_boundary_leader[];
|
||||
|
||||
extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
|
||||
extern void diff_tree_release_paths(struct diff_options *);
|
||||
extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
|
||||
const char *base, struct diff_options *opt);
|
||||
extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
|
||||
|
317
dir.c
317
dir.c
@ -11,6 +11,7 @@
|
||||
#include "dir.h"
|
||||
#include "refs.h"
|
||||
#include "wildmatch.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
struct path_simplify {
|
||||
int len;
|
||||
@ -51,26 +52,32 @@ int fnmatch_icase(const char *pattern, const char *string, int flags)
|
||||
return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));
|
||||
}
|
||||
|
||||
inline int git_fnmatch(const char *pattern, const char *string,
|
||||
int flags, int prefix)
|
||||
inline int git_fnmatch(const struct pathspec_item *item,
|
||||
const char *pattern, const char *string,
|
||||
int prefix)
|
||||
{
|
||||
int fnm_flags = 0;
|
||||
if (flags & GFNM_PATHNAME)
|
||||
fnm_flags |= FNM_PATHNAME;
|
||||
if (prefix > 0) {
|
||||
if (strncmp(pattern, string, prefix))
|
||||
if (ps_strncmp(item, pattern, string, prefix))
|
||||
return FNM_NOMATCH;
|
||||
pattern += prefix;
|
||||
string += prefix;
|
||||
}
|
||||
if (flags & GFNM_ONESTAR) {
|
||||
if (item->flags & PATHSPEC_ONESTAR) {
|
||||
int pattern_len = strlen(++pattern);
|
||||
int string_len = strlen(string);
|
||||
return string_len < pattern_len ||
|
||||
strcmp(pattern,
|
||||
string + string_len - pattern_len);
|
||||
ps_strcmp(item, pattern,
|
||||
string + string_len - pattern_len);
|
||||
}
|
||||
return fnmatch(pattern, string, fnm_flags);
|
||||
if (item->magic & PATHSPEC_GLOB)
|
||||
return wildmatch(pattern, string,
|
||||
WM_PATHNAME |
|
||||
(item->magic & PATHSPEC_ICASE ? WM_CASEFOLD : 0),
|
||||
NULL);
|
||||
else
|
||||
/* wildmatch has not learned no FNM_PATHNAME mode yet */
|
||||
return fnmatch(pattern, string,
|
||||
item->magic & PATHSPEC_ICASE ? FNM_CASEFOLD : 0);
|
||||
}
|
||||
|
||||
static int fnmatch_icase_mem(const char *pattern, int patternlen,
|
||||
@ -102,26 +109,40 @@ static int fnmatch_icase_mem(const char *pattern, int patternlen,
|
||||
return match_status;
|
||||
}
|
||||
|
||||
static size_t common_prefix_len(const char **pathspec)
|
||||
static size_t common_prefix_len(const struct pathspec *pathspec)
|
||||
{
|
||||
const char *n, *first;
|
||||
int n;
|
||||
size_t max = 0;
|
||||
int literal = limit_pathspec_to_literal();
|
||||
|
||||
if (!pathspec)
|
||||
return max;
|
||||
/*
|
||||
* ":(icase)path" is treated as a pathspec full of
|
||||
* wildcard. In other words, only prefix is considered common
|
||||
* prefix. If the pathspec is abc/foo abc/bar, running in
|
||||
* subdir xyz, the common prefix is still xyz, not xuz/abc as
|
||||
* in non-:(icase).
|
||||
*/
|
||||
GUARD_PATHSPEC(pathspec,
|
||||
PATHSPEC_FROMTOP |
|
||||
PATHSPEC_MAXDEPTH |
|
||||
PATHSPEC_LITERAL |
|
||||
PATHSPEC_GLOB |
|
||||
PATHSPEC_ICASE);
|
||||
|
||||
first = *pathspec;
|
||||
while ((n = *pathspec++)) {
|
||||
size_t i, len = 0;
|
||||
for (i = 0; first == n || i < max; i++) {
|
||||
char c = n[i];
|
||||
if (!c || c != first[i] || (!literal && is_glob_special(c)))
|
||||
for (n = 0; n < pathspec->nr; n++) {
|
||||
size_t i = 0, len = 0, item_len;
|
||||
if (pathspec->items[n].magic & PATHSPEC_ICASE)
|
||||
item_len = pathspec->items[n].prefix;
|
||||
else
|
||||
item_len = pathspec->items[n].nowildcard_len;
|
||||
while (i < item_len && (n == 0 || i < max)) {
|
||||
char c = pathspec->items[n].match[i];
|
||||
if (c != pathspec->items[0].match[i])
|
||||
break;
|
||||
if (c == '/')
|
||||
len = i + 1;
|
||||
i++;
|
||||
}
|
||||
if (first == n || len < max) {
|
||||
if (n == 0 || len < max) {
|
||||
max = len;
|
||||
if (!max)
|
||||
break;
|
||||
@ -134,14 +155,14 @@ static size_t common_prefix_len(const char **pathspec)
|
||||
* Returns a copy of the longest leading path common among all
|
||||
* pathspecs.
|
||||
*/
|
||||
char *common_prefix(const char **pathspec)
|
||||
char *common_prefix(const struct pathspec *pathspec)
|
||||
{
|
||||
unsigned long len = common_prefix_len(pathspec);
|
||||
|
||||
return len ? xmemdupz(*pathspec, len) : NULL;
|
||||
return len ? xmemdupz(pathspec->items[0].match, len) : NULL;
|
||||
}
|
||||
|
||||
int fill_directory(struct dir_struct *dir, const char **pathspec)
|
||||
int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
@ -152,7 +173,7 @@ int fill_directory(struct dir_struct *dir, const char **pathspec)
|
||||
len = common_prefix_len(pathspec);
|
||||
|
||||
/* Read the directory and prune it */
|
||||
read_directory(dir, pathspec ? *pathspec : "", len, pathspec);
|
||||
read_directory(dir, pathspec->nr ? pathspec->_raw[0] : "", len, pathspec);
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -171,113 +192,6 @@ int within_depth(const char *name, int namelen,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does 'match' match the given name?
|
||||
* A match is found if
|
||||
*
|
||||
* (1) the 'match' string is leading directory of 'name', or
|
||||
* (2) the 'match' string is a wildcard and matches 'name', or
|
||||
* (3) the 'match' string is exactly the same as 'name'.
|
||||
*
|
||||
* and the return value tells which case it was.
|
||||
*
|
||||
* It returns 0 when there is no match.
|
||||
*/
|
||||
static int match_one(const char *match, const char *name, int namelen)
|
||||
{
|
||||
int matchlen;
|
||||
int literal = limit_pathspec_to_literal();
|
||||
|
||||
/* If the match was just the prefix, we matched */
|
||||
if (!*match)
|
||||
return MATCHED_RECURSIVELY;
|
||||
|
||||
if (ignore_case) {
|
||||
for (;;) {
|
||||
unsigned char c1 = tolower(*match);
|
||||
unsigned char c2 = tolower(*name);
|
||||
if (c1 == '\0' || (!literal && is_glob_special(c1)))
|
||||
break;
|
||||
if (c1 != c2)
|
||||
return 0;
|
||||
match++;
|
||||
name++;
|
||||
namelen--;
|
||||
}
|
||||
} else {
|
||||
for (;;) {
|
||||
unsigned char c1 = *match;
|
||||
unsigned char c2 = *name;
|
||||
if (c1 == '\0' || (!literal && is_glob_special(c1)))
|
||||
break;
|
||||
if (c1 != c2)
|
||||
return 0;
|
||||
match++;
|
||||
name++;
|
||||
namelen--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we don't match the matchstring exactly,
|
||||
* we need to match by fnmatch
|
||||
*/
|
||||
matchlen = strlen(match);
|
||||
if (strncmp_icase(match, name, matchlen)) {
|
||||
if (literal)
|
||||
return 0;
|
||||
return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
|
||||
}
|
||||
|
||||
if (namelen == matchlen)
|
||||
return MATCHED_EXACTLY;
|
||||
if (match[matchlen-1] == '/' || name[matchlen] == '/')
|
||||
return MATCHED_RECURSIVELY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a name and a list of pathspecs, returns the nature of the
|
||||
* closest (i.e. most specific) match of the name to any of the
|
||||
* pathspecs.
|
||||
*
|
||||
* The caller typically calls this multiple times with the same
|
||||
* pathspec and seen[] array but with different name/namelen
|
||||
* (e.g. entries from the index) and is interested in seeing if and
|
||||
* how each pathspec matches all the names it calls this function
|
||||
* with. A mark is left in the seen[] array for each pathspec element
|
||||
* indicating the closest type of match that element achieved, so if
|
||||
* seen[n] remains zero after multiple invocations, that means the nth
|
||||
* pathspec did not match any names, which could indicate that the
|
||||
* user mistyped the nth pathspec.
|
||||
*/
|
||||
int match_pathspec(const char **pathspec, const char *name, int namelen,
|
||||
int prefix, char *seen)
|
||||
{
|
||||
int i, retval = 0;
|
||||
|
||||
if (!pathspec)
|
||||
return 1;
|
||||
|
||||
name += prefix;
|
||||
namelen -= prefix;
|
||||
|
||||
for (i = 0; pathspec[i] != NULL; i++) {
|
||||
int how;
|
||||
const char *match = pathspec[i] + prefix;
|
||||
if (seen && seen[i] == MATCHED_EXACTLY)
|
||||
continue;
|
||||
how = match_one(match, name, namelen);
|
||||
if (how) {
|
||||
if (retval < how)
|
||||
retval = how;
|
||||
if (seen && seen[i] < how)
|
||||
seen[i] = how;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does 'match' match the given name?
|
||||
* A match is found if
|
||||
@ -297,11 +211,44 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
|
||||
const char *match = item->match + prefix;
|
||||
int matchlen = item->len - prefix;
|
||||
|
||||
/*
|
||||
* The normal call pattern is:
|
||||
* 1. prefix = common_prefix_len(ps);
|
||||
* 2. prune something, or fill_directory
|
||||
* 3. match_pathspec_depth()
|
||||
*
|
||||
* 'prefix' at #1 may be shorter than the command's prefix and
|
||||
* it's ok for #2 to match extra files. Those extras will be
|
||||
* trimmed at #3.
|
||||
*
|
||||
* Suppose the pathspec is 'foo' and '../bar' running from
|
||||
* subdir 'xyz'. The common prefix at #1 will be empty, thanks
|
||||
* to "../". We may have xyz/foo _and_ XYZ/foo after #2. The
|
||||
* user does not want XYZ/foo, only the "foo" part should be
|
||||
* case-insensitive. We need to filter out XYZ/foo here. In
|
||||
* other words, we do not trust the caller on comparing the
|
||||
* prefix part when :(icase) is involved. We do exact
|
||||
* comparison ourselves.
|
||||
*
|
||||
* Normally the caller (common_prefix_len() in fact) does
|
||||
* _exact_ matching on name[-prefix+1..-1] and we do not need
|
||||
* to check that part. Be defensive and check it anyway, in
|
||||
* case common_prefix_len is changed, or a new caller is
|
||||
* introduced that does not use common_prefix_len.
|
||||
*
|
||||
* If the penalty turns out too high when prefix is really
|
||||
* long, maybe change it to
|
||||
* strncmp(match, name, item->prefix - prefix)
|
||||
*/
|
||||
if (item->prefix && (item->magic & PATHSPEC_ICASE) &&
|
||||
strncmp(item->match, name - prefix, item->prefix))
|
||||
return 0;
|
||||
|
||||
/* If the match was just the prefix, we matched */
|
||||
if (!*match)
|
||||
return MATCHED_RECURSIVELY;
|
||||
|
||||
if (matchlen <= namelen && !strncmp(match, name, matchlen)) {
|
||||
if (matchlen <= namelen && !ps_strncmp(item, match, name, matchlen)) {
|
||||
if (matchlen == namelen)
|
||||
return MATCHED_EXACTLY;
|
||||
|
||||
@ -310,8 +257,7 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
|
||||
}
|
||||
|
||||
if (item->nowildcard_len < item->len &&
|
||||
!git_fnmatch(match, name,
|
||||
item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0,
|
||||
!git_fnmatch(item, match, name,
|
||||
item->nowildcard_len - prefix))
|
||||
return MATCHED_FNMATCH;
|
||||
|
||||
@ -339,8 +285,17 @@ int match_pathspec_depth(const struct pathspec *ps,
|
||||
{
|
||||
int i, retval = 0;
|
||||
|
||||
GUARD_PATHSPEC(ps,
|
||||
PATHSPEC_FROMTOP |
|
||||
PATHSPEC_MAXDEPTH |
|
||||
PATHSPEC_LITERAL |
|
||||
PATHSPEC_GLOB |
|
||||
PATHSPEC_ICASE);
|
||||
|
||||
if (!ps->nr) {
|
||||
if (!ps->recursive || ps->max_depth == -1)
|
||||
if (!ps->recursive ||
|
||||
!(ps->magic & PATHSPEC_MAXDEPTH) ||
|
||||
ps->max_depth == -1)
|
||||
return MATCHED_RECURSIVELY;
|
||||
|
||||
if (within_depth(name, namelen, 0, ps->max_depth))
|
||||
@ -357,7 +312,9 @@ int match_pathspec_depth(const struct pathspec *ps,
|
||||
if (seen && seen[i] == MATCHED_EXACTLY)
|
||||
continue;
|
||||
how = match_pathspec_item(ps->items+i, prefix, name, namelen);
|
||||
if (ps->recursive && ps->max_depth != -1 &&
|
||||
if (ps->recursive &&
|
||||
(ps->magic & PATHSPEC_MAXDEPTH) &&
|
||||
ps->max_depth != -1 &&
|
||||
how && how != MATCHED_FNMATCH) {
|
||||
int len = ps->items[i].len;
|
||||
if (name[len] == '/')
|
||||
@ -380,7 +337,7 @@ int match_pathspec_depth(const struct pathspec *ps,
|
||||
/*
|
||||
* Return the length of the "simple" part of a path match limiter.
|
||||
*/
|
||||
static int simple_length(const char *match)
|
||||
int simple_length(const char *match)
|
||||
{
|
||||
int len = -1;
|
||||
|
||||
@ -392,7 +349,7 @@ static int simple_length(const char *match)
|
||||
}
|
||||
}
|
||||
|
||||
static int no_wildcard(const char *string)
|
||||
int no_wildcard(const char *string)
|
||||
{
|
||||
return string[simple_length(string)] == '\0';
|
||||
}
|
||||
@ -1381,14 +1338,25 @@ static int treat_leading_path(struct dir_struct *dir,
|
||||
return rc;
|
||||
}
|
||||
|
||||
int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
|
||||
int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
|
||||
{
|
||||
struct path_simplify *simplify;
|
||||
|
||||
/*
|
||||
* Check out create_simplify()
|
||||
*/
|
||||
if (pathspec)
|
||||
GUARD_PATHSPEC(pathspec,
|
||||
PATHSPEC_FROMTOP |
|
||||
PATHSPEC_MAXDEPTH |
|
||||
PATHSPEC_LITERAL |
|
||||
PATHSPEC_GLOB |
|
||||
PATHSPEC_ICASE);
|
||||
|
||||
if (has_symlink_leading_path(path, len))
|
||||
return dir->nr;
|
||||
|
||||
simplify = create_simplify(pathspec);
|
||||
simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
|
||||
if (!len || treat_leading_path(dir, path, len, simplify))
|
||||
read_directory_recursive(dir, path, len, 0, simplify);
|
||||
free_simplify(simplify);
|
||||
@ -1568,71 +1536,6 @@ int remove_path(const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pathspec_item_cmp(const void *a_, const void *b_)
|
||||
{
|
||||
struct pathspec_item *a, *b;
|
||||
|
||||
a = (struct pathspec_item *)a_;
|
||||
b = (struct pathspec_item *)b_;
|
||||
return strcmp(a->match, b->match);
|
||||
}
|
||||
|
||||
int init_pathspec(struct pathspec *pathspec, const char **paths)
|
||||
{
|
||||
const char **p = paths;
|
||||
int i;
|
||||
|
||||
memset(pathspec, 0, sizeof(*pathspec));
|
||||
if (!p)
|
||||
return 0;
|
||||
while (*p)
|
||||
p++;
|
||||
pathspec->raw = paths;
|
||||
pathspec->nr = p - paths;
|
||||
if (!pathspec->nr)
|
||||
return 0;
|
||||
|
||||
pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);
|
||||
for (i = 0; i < pathspec->nr; i++) {
|
||||
struct pathspec_item *item = pathspec->items+i;
|
||||
const char *path = paths[i];
|
||||
|
||||
item->match = path;
|
||||
item->len = strlen(path);
|
||||
item->flags = 0;
|
||||
if (limit_pathspec_to_literal()) {
|
||||
item->nowildcard_len = item->len;
|
||||
} else {
|
||||
item->nowildcard_len = simple_length(path);
|
||||
if (item->nowildcard_len < item->len) {
|
||||
pathspec->has_wildcard = 1;
|
||||
if (path[item->nowildcard_len] == '*' &&
|
||||
no_wildcard(path + item->nowildcard_len + 1))
|
||||
item->flags |= PATHSPEC_ONESTAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qsort(pathspec->items, pathspec->nr,
|
||||
sizeof(struct pathspec_item), pathspec_item_cmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_pathspec(struct pathspec *pathspec)
|
||||
{
|
||||
free(pathspec->items);
|
||||
pathspec->items = NULL;
|
||||
}
|
||||
|
||||
int limit_pathspec_to_literal(void)
|
||||
{
|
||||
static int flag = -1;
|
||||
if (flag < 0)
|
||||
flag = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
|
||||
return flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees memory within dir which was allocated for exclude lists and
|
||||
* the exclude_stack. Does not free dir itself.
|
||||
|
18
dir.h
18
dir.h
@ -128,15 +128,16 @@ struct dir_struct {
|
||||
#define MATCHED_RECURSIVELY 1
|
||||
#define MATCHED_FNMATCH 2
|
||||
#define MATCHED_EXACTLY 3
|
||||
extern char *common_prefix(const char **pathspec);
|
||||
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
|
||||
extern int simple_length(const char *match);
|
||||
extern int no_wildcard(const char *string);
|
||||
extern char *common_prefix(const struct pathspec *pathspec);
|
||||
extern int match_pathspec_depth(const struct pathspec *pathspec,
|
||||
const char *name, int namelen,
|
||||
int prefix, char *seen);
|
||||
extern int within_depth(const char *name, int namelen, int depth, int max_depth);
|
||||
|
||||
extern int fill_directory(struct dir_struct *dir, const char **pathspec);
|
||||
extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);
|
||||
extern int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec);
|
||||
extern int read_directory(struct dir_struct *, const char *path, int len, const struct pathspec *pathspec);
|
||||
|
||||
extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename,
|
||||
int *dtype, struct exclude_list *el);
|
||||
@ -198,10 +199,9 @@ extern int fnmatch_icase(const char *pattern, const char *string, int flags);
|
||||
/*
|
||||
* The prefix part of pattern must not contains wildcards.
|
||||
*/
|
||||
#define GFNM_PATHNAME 1 /* similar to FNM_PATHNAME */
|
||||
#define GFNM_ONESTAR 2 /* there is only _one_ wildcard, a star */
|
||||
|
||||
extern int git_fnmatch(const char *pattern, const char *string,
|
||||
int flags, int prefix);
|
||||
struct pathspec_item;
|
||||
extern int git_fnmatch(const struct pathspec_item *item,
|
||||
const char *pattern, const char *string,
|
||||
int prefix);
|
||||
|
||||
#endif
|
||||
|
12
git.c
12
git.c
@ -147,6 +147,18 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
|
||||
setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else if (!strcmp(cmd, "--glob-pathspecs")) {
|
||||
setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, "1", 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else if (!strcmp(cmd, "--noglob-pathspecs")) {
|
||||
setenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, "1", 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else if (!strcmp(cmd, "--icase-pathspecs")) {
|
||||
setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else if (!strcmp(cmd, "--shallow-file")) {
|
||||
(*argv)++;
|
||||
(*argc)--;
|
||||
|
@ -760,7 +760,7 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
|
||||
r = r->next;
|
||||
}
|
||||
paths[count] = NULL;
|
||||
init_pathspec(&rev->diffopt.pathspec, paths);
|
||||
parse_pathspec(&rev->diffopt.pathspec, 0, 0, "", paths);
|
||||
free(paths);
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ static int get_files_dirs(struct merge_options *o, struct tree *tree)
|
||||
{
|
||||
int n;
|
||||
struct pathspec match_all;
|
||||
init_pathspec(&match_all, NULL);
|
||||
memset(&match_all, 0, sizeof(match_all));
|
||||
if (read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o))
|
||||
return 0;
|
||||
n = o->current_file_set.nr + o->current_directory_set.nr;
|
||||
|
@ -170,7 +170,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
|
||||
sha1_to_hex(mp->remote));
|
||||
}
|
||||
diff_flush(&opt);
|
||||
diff_tree_release_paths(&opt);
|
||||
free_pathspec(&opt.pathspec);
|
||||
|
||||
*num_changes = len;
|
||||
return changes;
|
||||
@ -256,7 +256,7 @@ static void diff_tree_local(struct notes_merge_options *o,
|
||||
sha1_to_hex(mp->local));
|
||||
}
|
||||
diff_flush(&opt);
|
||||
diff_tree_release_paths(&opt);
|
||||
free_pathspec(&opt.pathspec);
|
||||
}
|
||||
|
||||
static void check_notes_merge_worktree(struct notes_merge_options *o)
|
||||
|
15
path.c
15
path.c
@ -543,8 +543,14 @@ const char *relative_path(const char *in, const char *prefix,
|
||||
*
|
||||
* Note that this function is purely textual. It does not follow symlinks,
|
||||
* verify the existence of the path, or make any system calls.
|
||||
*
|
||||
* prefix_len != NULL is for a specific case of prefix_pathspec():
|
||||
* assume that src == dst and src[0..prefix_len-1] is already
|
||||
* normalized, any time "../" eats up to the prefix_len part,
|
||||
* prefix_len is reduced. In the end prefix_len is the remaining
|
||||
* prefix that has not been overridden by user pathspec.
|
||||
*/
|
||||
int normalize_path_copy(char *dst, const char *src)
|
||||
int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
|
||||
{
|
||||
char *dst0;
|
||||
|
||||
@ -619,11 +625,18 @@ int normalize_path_copy(char *dst, const char *src)
|
||||
/* Windows: dst[-1] cannot be backslash anymore */
|
||||
while (dst0 < dst && dst[-1] != '/')
|
||||
dst--;
|
||||
if (prefix_len && *prefix_len > dst - dst0)
|
||||
*prefix_len = dst - dst0;
|
||||
}
|
||||
*dst = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int normalize_path_copy(char *dst, const char *src)
|
||||
{
|
||||
return normalize_path_copy_len(dst, src, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* path = Canonical absolute path
|
||||
* prefixes = string_list containing normalized, absolute paths without
|
||||
|
449
pathspec.c
449
pathspec.c
@ -15,8 +15,8 @@
|
||||
* If seen[] has not already been written to, it may make sense
|
||||
* to use find_pathspecs_matching_against_index() instead.
|
||||
*/
|
||||
void add_pathspec_matches_against_index(const char **pathspec,
|
||||
char *seen, int specs)
|
||||
void add_pathspec_matches_against_index(const struct pathspec *pathspec,
|
||||
char *seen)
|
||||
{
|
||||
int num_unmatched = 0, i;
|
||||
|
||||
@ -26,14 +26,14 @@ void add_pathspec_matches_against_index(const char **pathspec,
|
||||
* mistakenly think that the user gave a pathspec that did not match
|
||||
* anything.
|
||||
*/
|
||||
for (i = 0; i < specs; i++)
|
||||
for (i = 0; i < pathspec->nr; i++)
|
||||
if (!seen[i])
|
||||
num_unmatched++;
|
||||
if (!num_unmatched)
|
||||
return;
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
const struct cache_entry *ce = active_cache[i];
|
||||
match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
|
||||
match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,57 +45,428 @@ void add_pathspec_matches_against_index(const char **pathspec,
|
||||
* nature of the "closest" (i.e. most specific) matches which each of the
|
||||
* given pathspecs achieves against all items in the index.
|
||||
*/
|
||||
char *find_pathspecs_matching_against_index(const char **pathspec)
|
||||
char *find_pathspecs_matching_against_index(const struct pathspec *pathspec)
|
||||
{
|
||||
char *seen;
|
||||
int i;
|
||||
|
||||
for (i = 0; pathspec[i]; i++)
|
||||
; /* just counting */
|
||||
seen = xcalloc(i, 1);
|
||||
add_pathspec_matches_against_index(pathspec, seen, i);
|
||||
char *seen = xcalloc(pathspec->nr, 1);
|
||||
add_pathspec_matches_against_index(pathspec, seen);
|
||||
return seen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the index to see whether path refers to a submodule, or
|
||||
* something inside a submodule. If the former, returns the path with
|
||||
* any trailing slash stripped. If the latter, dies with an error
|
||||
* message.
|
||||
* Magic pathspec
|
||||
*
|
||||
* Possible future magic semantics include stuff like:
|
||||
*
|
||||
* { PATHSPEC_RECURSIVE, '*', "recursive" },
|
||||
* { PATHSPEC_REGEXP, '\0', "regexp" },
|
||||
*
|
||||
*/
|
||||
const char *check_path_for_gitlink(const char *path)
|
||||
|
||||
static struct pathspec_magic {
|
||||
unsigned bit;
|
||||
char mnemonic; /* this cannot be ':'! */
|
||||
const char *name;
|
||||
} pathspec_magic[] = {
|
||||
{ PATHSPEC_FROMTOP, '/', "top" },
|
||||
{ PATHSPEC_LITERAL, 0, "literal" },
|
||||
{ PATHSPEC_GLOB, '\0', "glob" },
|
||||
{ PATHSPEC_ICASE, '\0', "icase" },
|
||||
};
|
||||
|
||||
/*
|
||||
* Take an element of a pathspec and check for magic signatures.
|
||||
* Append the result to the prefix. Return the magic bitmap.
|
||||
*
|
||||
* For now, we only parse the syntax and throw out anything other than
|
||||
* "top" magic.
|
||||
*
|
||||
* NEEDSWORK: This needs to be rewritten when we start migrating
|
||||
* get_pathspec() users to use the "struct pathspec" interface. For
|
||||
* example, a pathspec element may be marked as case-insensitive, but
|
||||
* the prefix part must always match literally, and a single stupid
|
||||
* string cannot express such a case.
|
||||
*/
|
||||
static unsigned prefix_pathspec(struct pathspec_item *item,
|
||||
unsigned *p_short_magic,
|
||||
const char **raw, unsigned flags,
|
||||
const char *prefix, int prefixlen,
|
||||
const char *elt)
|
||||
{
|
||||
int i, path_len = strlen(path);
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
const struct cache_entry *ce = active_cache[i];
|
||||
if (S_ISGITLINK(ce->ce_mode)) {
|
||||
int ce_len = ce_namelen(ce);
|
||||
if (path_len <= ce_len || path[ce_len] != '/' ||
|
||||
memcmp(ce->name, path, ce_len))
|
||||
/* path does not refer to this
|
||||
* submodule or anything inside it */
|
||||
static int literal_global = -1;
|
||||
static int glob_global = -1;
|
||||
static int noglob_global = -1;
|
||||
static int icase_global = -1;
|
||||
unsigned magic = 0, short_magic = 0, global_magic = 0;
|
||||
const char *copyfrom = elt, *long_magic_end = NULL;
|
||||
char *match;
|
||||
int i, pathspec_prefix = -1;
|
||||
|
||||
if (literal_global < 0)
|
||||
literal_global = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
|
||||
if (literal_global)
|
||||
global_magic |= PATHSPEC_LITERAL;
|
||||
|
||||
if (glob_global < 0)
|
||||
glob_global = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT, 0);
|
||||
if (glob_global)
|
||||
global_magic |= PATHSPEC_GLOB;
|
||||
|
||||
if (noglob_global < 0)
|
||||
noglob_global = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, 0);
|
||||
|
||||
if (glob_global && noglob_global)
|
||||
die(_("global 'glob' and 'noglob' pathspec settings are incompatible"));
|
||||
|
||||
|
||||
if (icase_global < 0)
|
||||
icase_global = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT, 0);
|
||||
if (icase_global)
|
||||
global_magic |= PATHSPEC_ICASE;
|
||||
|
||||
if ((global_magic & PATHSPEC_LITERAL) &&
|
||||
(global_magic & ~PATHSPEC_LITERAL))
|
||||
die(_("global 'literal' pathspec setting is incompatible "
|
||||
"with all other global pathspec settings"));
|
||||
|
||||
if (elt[0] != ':' || literal_global) {
|
||||
; /* nothing to do */
|
||||
} else if (elt[1] == '(') {
|
||||
/* longhand */
|
||||
const char *nextat;
|
||||
for (copyfrom = elt + 2;
|
||||
*copyfrom && *copyfrom != ')';
|
||||
copyfrom = nextat) {
|
||||
size_t len = strcspn(copyfrom, ",)");
|
||||
if (copyfrom[len] == ',')
|
||||
nextat = copyfrom + len + 1;
|
||||
else
|
||||
/* handle ')' and '\0' */
|
||||
nextat = copyfrom + len;
|
||||
if (!len)
|
||||
continue;
|
||||
if (path_len == ce_len + 1) {
|
||||
/* path refers to submodule;
|
||||
* strip trailing slash */
|
||||
return xstrndup(ce->name, ce_len);
|
||||
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
|
||||
if (strlen(pathspec_magic[i].name) == len &&
|
||||
!strncmp(pathspec_magic[i].name, copyfrom, len)) {
|
||||
magic |= pathspec_magic[i].bit;
|
||||
break;
|
||||
}
|
||||
if (!prefixcmp(copyfrom, "prefix:")) {
|
||||
char *endptr;
|
||||
pathspec_prefix = strtol(copyfrom + 7,
|
||||
&endptr, 10);
|
||||
if (endptr - copyfrom != len)
|
||||
die(_("invalid parameter for pathspec magic 'prefix'"));
|
||||
/* "i" would be wrong, but it does not matter */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ARRAY_SIZE(pathspec_magic) <= i)
|
||||
die(_("Invalid pathspec magic '%.*s' in '%s'"),
|
||||
(int) len, copyfrom, elt);
|
||||
}
|
||||
if (*copyfrom != ')')
|
||||
die(_("Missing ')' at the end of pathspec magic in '%s'"), elt);
|
||||
long_magic_end = copyfrom;
|
||||
copyfrom++;
|
||||
} else {
|
||||
/* shorthand */
|
||||
for (copyfrom = elt + 1;
|
||||
*copyfrom && *copyfrom != ':';
|
||||
copyfrom++) {
|
||||
char ch = *copyfrom;
|
||||
|
||||
if (!is_pathspec_magic(ch))
|
||||
break;
|
||||
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
|
||||
if (pathspec_magic[i].mnemonic == ch) {
|
||||
short_magic |= pathspec_magic[i].bit;
|
||||
break;
|
||||
}
|
||||
if (ARRAY_SIZE(pathspec_magic) <= i)
|
||||
die(_("Unimplemented pathspec magic '%c' in '%s'"),
|
||||
ch, elt);
|
||||
}
|
||||
if (*copyfrom == ':')
|
||||
copyfrom++;
|
||||
}
|
||||
|
||||
magic |= short_magic;
|
||||
*p_short_magic = short_magic;
|
||||
|
||||
/* --noglob-pathspec adds :(literal) _unless_ :(glob) is specifed */
|
||||
if (noglob_global && !(magic & PATHSPEC_GLOB))
|
||||
global_magic |= PATHSPEC_LITERAL;
|
||||
|
||||
/* --glob-pathspec is overriden by :(literal) */
|
||||
if ((global_magic & PATHSPEC_GLOB) && (magic & PATHSPEC_LITERAL))
|
||||
global_magic &= ~PATHSPEC_GLOB;
|
||||
|
||||
magic |= global_magic;
|
||||
|
||||
if (pathspec_prefix >= 0 &&
|
||||
(prefixlen || (prefix && *prefix)))
|
||||
die("BUG: 'prefix' magic is supposed to be used at worktree's root");
|
||||
|
||||
if ((magic & PATHSPEC_LITERAL) && (magic & PATHSPEC_GLOB))
|
||||
die(_("%s: 'literal' and 'glob' are incompatible"), elt);
|
||||
|
||||
if (pathspec_prefix >= 0) {
|
||||
match = xstrdup(copyfrom);
|
||||
prefixlen = pathspec_prefix;
|
||||
} else if (magic & PATHSPEC_FROMTOP) {
|
||||
match = xstrdup(copyfrom);
|
||||
prefixlen = 0;
|
||||
} else {
|
||||
match = prefix_path_gently(prefix, prefixlen, &prefixlen, copyfrom);
|
||||
if (!match)
|
||||
die(_("%s: '%s' is outside repository"), elt, copyfrom);
|
||||
}
|
||||
*raw = item->match = match;
|
||||
/*
|
||||
* Prefix the pathspec (keep all magic) and assign to
|
||||
* original. Useful for passing to another command.
|
||||
*/
|
||||
if (flags & PATHSPEC_PREFIX_ORIGIN) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
const char *start = elt;
|
||||
if (prefixlen && !literal_global) {
|
||||
/* Preserve the actual prefix length of each pattern */
|
||||
if (long_magic_end) {
|
||||
strbuf_add(&sb, start, long_magic_end - start);
|
||||
strbuf_addf(&sb, ",prefix:%d", prefixlen);
|
||||
start = long_magic_end;
|
||||
} else {
|
||||
die (_("Path '%s' is in submodule '%.*s'"),
|
||||
path, ce_len, ce->name);
|
||||
if (*start == ':')
|
||||
start++;
|
||||
strbuf_addf(&sb, ":(prefix:%d)", prefixlen);
|
||||
}
|
||||
}
|
||||
strbuf_add(&sb, start, copyfrom - start);
|
||||
strbuf_addstr(&sb, match);
|
||||
item->original = strbuf_detach(&sb, NULL);
|
||||
} else
|
||||
item->original = elt;
|
||||
item->len = strlen(item->match);
|
||||
item->prefix = prefixlen;
|
||||
|
||||
if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) &&
|
||||
(item->len >= 1 && item->match[item->len - 1] == '/') &&
|
||||
(i = cache_name_pos(item->match, item->len - 1)) >= 0 &&
|
||||
S_ISGITLINK(active_cache[i]->ce_mode)) {
|
||||
item->len--;
|
||||
match[item->len] = '\0';
|
||||
}
|
||||
return path;
|
||||
|
||||
if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE)
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
int ce_len = ce_namelen(ce);
|
||||
|
||||
if (!S_ISGITLINK(ce->ce_mode))
|
||||
continue;
|
||||
|
||||
if (item->len <= ce_len || match[ce_len] != '/' ||
|
||||
memcmp(ce->name, match, ce_len))
|
||||
continue;
|
||||
if (item->len == ce_len + 1) {
|
||||
/* strip trailing slash */
|
||||
item->len--;
|
||||
match[item->len] = '\0';
|
||||
} else
|
||||
die (_("Pathspec '%s' is in submodule '%.*s'"),
|
||||
elt, ce_len, ce->name);
|
||||
}
|
||||
|
||||
if (magic & PATHSPEC_LITERAL)
|
||||
item->nowildcard_len = item->len;
|
||||
else {
|
||||
item->nowildcard_len = simple_length(item->match);
|
||||
if (item->nowildcard_len < prefixlen)
|
||||
item->nowildcard_len = prefixlen;
|
||||
}
|
||||
item->flags = 0;
|
||||
if (magic & PATHSPEC_GLOB) {
|
||||
/*
|
||||
* FIXME: should we enable ONESTAR in _GLOB for
|
||||
* pattern "* * / * . c"?
|
||||
*/
|
||||
} else {
|
||||
if (item->nowildcard_len < item->len &&
|
||||
item->match[item->nowildcard_len] == '*' &&
|
||||
no_wildcard(item->match + item->nowildcard_len + 1))
|
||||
item->flags |= PATHSPEC_ONESTAR;
|
||||
}
|
||||
|
||||
/* sanity checks, pathspec matchers assume these are sane */
|
||||
assert(item->nowildcard_len <= item->len &&
|
||||
item->prefix <= item->len);
|
||||
return magic;
|
||||
}
|
||||
|
||||
static int pathspec_item_cmp(const void *a_, const void *b_)
|
||||
{
|
||||
struct pathspec_item *a, *b;
|
||||
|
||||
a = (struct pathspec_item *)a_;
|
||||
b = (struct pathspec_item *)b_;
|
||||
return strcmp(a->match, b->match);
|
||||
}
|
||||
|
||||
static void NORETURN unsupported_magic(const char *pattern,
|
||||
unsigned magic,
|
||||
unsigned short_magic)
|
||||
{
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
int i, n;
|
||||
for (n = i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
|
||||
const struct pathspec_magic *m = pathspec_magic + i;
|
||||
if (!(magic & m->bit))
|
||||
continue;
|
||||
if (sb.len)
|
||||
strbuf_addstr(&sb, " ");
|
||||
if (short_magic & m->bit)
|
||||
strbuf_addf(&sb, "'%c'", m->mnemonic);
|
||||
else
|
||||
strbuf_addf(&sb, "'%s'", m->name);
|
||||
n++;
|
||||
}
|
||||
/*
|
||||
* We may want to substitute "this command" with a command
|
||||
* name. E.g. when add--interactive dies when running
|
||||
* "checkout -p"
|
||||
*/
|
||||
die(_("%s: pathspec magic not supported by this command: %s"),
|
||||
pattern, sb.buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dies if the given path refers to a file inside a symlinked
|
||||
* directory in the index.
|
||||
* Given command line arguments and a prefix, convert the input to
|
||||
* pathspec. die() if any magic in magic_mask is used.
|
||||
*/
|
||||
void die_if_path_beyond_symlink(const char *path, const char *prefix)
|
||||
void parse_pathspec(struct pathspec *pathspec,
|
||||
unsigned magic_mask, unsigned flags,
|
||||
const char *prefix, const char **argv)
|
||||
{
|
||||
if (has_symlink_leading_path(path, strlen(path))) {
|
||||
int len = prefix ? strlen(prefix) : 0;
|
||||
die(_("'%s' is beyond a symbolic link"), path + len);
|
||||
struct pathspec_item *item;
|
||||
const char *entry = argv ? *argv : NULL;
|
||||
int i, n, prefixlen;
|
||||
|
||||
memset(pathspec, 0, sizeof(*pathspec));
|
||||
|
||||
if (flags & PATHSPEC_MAXDEPTH_VALID)
|
||||
pathspec->magic |= PATHSPEC_MAXDEPTH;
|
||||
|
||||
/* No arguments, no prefix -> no pathspec */
|
||||
if (!entry && !prefix)
|
||||
return;
|
||||
|
||||
if ((flags & PATHSPEC_PREFER_CWD) &&
|
||||
(flags & PATHSPEC_PREFER_FULL))
|
||||
die("BUG: PATHSPEC_PREFER_CWD and PATHSPEC_PREFER_FULL are incompatible");
|
||||
|
||||
/* No arguments with prefix -> prefix pathspec */
|
||||
if (!entry) {
|
||||
static const char *raw[2];
|
||||
|
||||
if (flags & PATHSPEC_PREFER_FULL)
|
||||
return;
|
||||
|
||||
if (!(flags & PATHSPEC_PREFER_CWD))
|
||||
die("BUG: PATHSPEC_PREFER_CWD requires arguments");
|
||||
|
||||
pathspec->items = item = xmalloc(sizeof(*item));
|
||||
memset(item, 0, sizeof(*item));
|
||||
item->match = prefix;
|
||||
item->original = prefix;
|
||||
item->nowildcard_len = item->len = strlen(prefix);
|
||||
item->prefix = item->len;
|
||||
raw[0] = prefix;
|
||||
raw[1] = NULL;
|
||||
pathspec->nr = 1;
|
||||
pathspec->_raw = raw;
|
||||
return;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
while (argv[n])
|
||||
n++;
|
||||
|
||||
pathspec->nr = n;
|
||||
pathspec->items = item = xmalloc(sizeof(*item) * n);
|
||||
pathspec->_raw = argv;
|
||||
prefixlen = prefix ? strlen(prefix) : 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
unsigned short_magic;
|
||||
entry = argv[i];
|
||||
|
||||
item[i].magic = prefix_pathspec(item + i, &short_magic,
|
||||
argv + i, flags,
|
||||
prefix, prefixlen, entry);
|
||||
if (item[i].magic & magic_mask)
|
||||
unsupported_magic(entry,
|
||||
item[i].magic & magic_mask,
|
||||
short_magic);
|
||||
|
||||
if ((flags & PATHSPEC_SYMLINK_LEADING_PATH) &&
|
||||
has_symlink_leading_path(item[i].match, item[i].len)) {
|
||||
die(_("pathspec '%s' is beyond a symbolic link"), entry);
|
||||
}
|
||||
|
||||
if (item[i].nowildcard_len < item[i].len)
|
||||
pathspec->has_wildcard = 1;
|
||||
pathspec->magic |= item[i].magic;
|
||||
}
|
||||
|
||||
|
||||
if (pathspec->magic & PATHSPEC_MAXDEPTH) {
|
||||
if (flags & PATHSPEC_KEEP_ORDER)
|
||||
die("BUG: PATHSPEC_MAXDEPTH_VALID and PATHSPEC_KEEP_ORDER are incompatible");
|
||||
qsort(pathspec->items, pathspec->nr,
|
||||
sizeof(struct pathspec_item), pathspec_item_cmp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* N.B. get_pathspec() is deprecated in favor of the "struct pathspec"
|
||||
* based interface - see pathspec.c:parse_pathspec().
|
||||
*
|
||||
* Arguments:
|
||||
* - prefix - a path relative to the root of the working tree
|
||||
* - pathspec - a list of paths underneath the prefix path
|
||||
*
|
||||
* Iterates over pathspec, prepending each path with prefix,
|
||||
* and return the resulting list.
|
||||
*
|
||||
* If pathspec is empty, return a singleton list containing prefix.
|
||||
*
|
||||
* If pathspec and prefix are both empty, return an empty list.
|
||||
*
|
||||
* This is typically used by built-in commands such as add.c, in order
|
||||
* to normalize argv arguments provided to the built-in into a list of
|
||||
* paths to process, all relative to the root of the working tree.
|
||||
*/
|
||||
const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||
{
|
||||
struct pathspec ps;
|
||||
parse_pathspec(&ps,
|
||||
PATHSPEC_ALL_MAGIC &
|
||||
~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
|
||||
PATHSPEC_PREFER_CWD,
|
||||
prefix, pathspec);
|
||||
return ps._raw;
|
||||
}
|
||||
|
||||
void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
|
||||
{
|
||||
*dst = *src;
|
||||
dst->items = xmalloc(sizeof(struct pathspec_item) * dst->nr);
|
||||
memcpy(dst->items, src->items,
|
||||
sizeof(struct pathspec_item) * dst->nr);
|
||||
}
|
||||
|
||||
void free_pathspec(struct pathspec *pathspec)
|
||||
{
|
||||
free(pathspec->items);
|
||||
pathspec->items = NULL;
|
||||
}
|
||||
|
88
pathspec.h
88
pathspec.h
@ -1,8 +1,92 @@
|
||||
#ifndef PATHSPEC_H
|
||||
#define PATHSPEC_H
|
||||
|
||||
extern char *find_pathspecs_matching_against_index(const char **pathspec);
|
||||
extern void add_pathspec_matches_against_index(const char **pathspec, char *seen, int specs);
|
||||
/* Pathspec magic */
|
||||
#define PATHSPEC_FROMTOP (1<<0)
|
||||
#define PATHSPEC_MAXDEPTH (1<<1)
|
||||
#define PATHSPEC_LITERAL (1<<2)
|
||||
#define PATHSPEC_GLOB (1<<3)
|
||||
#define PATHSPEC_ICASE (1<<4)
|
||||
#define PATHSPEC_ALL_MAGIC \
|
||||
(PATHSPEC_FROMTOP | \
|
||||
PATHSPEC_MAXDEPTH | \
|
||||
PATHSPEC_LITERAL | \
|
||||
PATHSPEC_GLOB | \
|
||||
PATHSPEC_ICASE)
|
||||
|
||||
#define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */
|
||||
|
||||
struct pathspec {
|
||||
const char **_raw; /* get_pathspec() result, not freed by free_pathspec() */
|
||||
int nr;
|
||||
unsigned int has_wildcard:1;
|
||||
unsigned int recursive:1;
|
||||
unsigned magic;
|
||||
int max_depth;
|
||||
struct pathspec_item {
|
||||
const char *match;
|
||||
const char *original;
|
||||
unsigned magic;
|
||||
int len, prefix;
|
||||
int nowildcard_len;
|
||||
int flags;
|
||||
} *items;
|
||||
};
|
||||
|
||||
#define GUARD_PATHSPEC(ps, mask) \
|
||||
do { \
|
||||
if ((ps)->magic & ~(mask)) \
|
||||
die("BUG:%s:%d: unsupported magic %x", \
|
||||
__FILE__, __LINE__, (ps)->magic & ~(mask)); \
|
||||
} while (0)
|
||||
|
||||
/* parse_pathspec flags */
|
||||
#define PATHSPEC_PREFER_CWD (1<<0) /* No args means match cwd */
|
||||
#define PATHSPEC_PREFER_FULL (1<<1) /* No args means match everything */
|
||||
#define PATHSPEC_MAXDEPTH_VALID (1<<2) /* max_depth field is valid */
|
||||
/* strip the trailing slash if the given path is a gitlink */
|
||||
#define PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP (1<<3)
|
||||
/* die if a symlink is part of the given path's directory */
|
||||
#define PATHSPEC_SYMLINK_LEADING_PATH (1<<4)
|
||||
/*
|
||||
* This is like a combination of ..LEADING_PATH and .._SLASH_CHEAP
|
||||
* (but not the same): it strips the trailing slash if the given path
|
||||
* is a gitlink but also checks and dies if gitlink is part of the
|
||||
* leading path (i.e. the given path goes beyond a submodule). It's
|
||||
* safer than _SLASH_CHEAP and also more expensive.
|
||||
*/
|
||||
#define PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE (1<<5)
|
||||
#define PATHSPEC_PREFIX_ORIGIN (1<<6)
|
||||
#define PATHSPEC_KEEP_ORDER (1<<7)
|
||||
|
||||
extern void parse_pathspec(struct pathspec *pathspec,
|
||||
unsigned magic_mask,
|
||||
unsigned flags,
|
||||
const char *prefix,
|
||||
const char **args);
|
||||
extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
|
||||
extern void free_pathspec(struct pathspec *);
|
||||
|
||||
static inline int ps_strncmp(const struct pathspec_item *item,
|
||||
const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
if (item->magic & PATHSPEC_ICASE)
|
||||
return strncasecmp(s1, s2, n);
|
||||
else
|
||||
return strncmp(s1, s2, n);
|
||||
}
|
||||
|
||||
static inline int ps_strcmp(const struct pathspec_item *item,
|
||||
const char *s1, const char *s2)
|
||||
{
|
||||
if (item->magic & PATHSPEC_ICASE)
|
||||
return strcasecmp(s1, s2);
|
||||
else
|
||||
return strcmp(s1, s2);
|
||||
}
|
||||
|
||||
extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec);
|
||||
extern void add_pathspec_matches_against_index(const struct pathspec *pathspec, char *seen);
|
||||
extern const char *check_path_for_gitlink(const char *path);
|
||||
extern void die_if_path_beyond_symlink(const char *path, const char *prefix);
|
||||
|
||||
|
@ -2,9 +2,11 @@
|
||||
* Copyright (C) 2008 Linus Torvalds
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
#ifdef NO_PTHREADS
|
||||
static void preload_index(struct index_state *index, const char **pathspec)
|
||||
static void preload_index(struct index_state *index,
|
||||
const struct pathspec *pathspec)
|
||||
{
|
||||
; /* nothing */
|
||||
}
|
||||
@ -24,7 +26,7 @@ static void preload_index(struct index_state *index, const char **pathspec)
|
||||
struct thread_data {
|
||||
pthread_t pthread;
|
||||
struct index_state *index;
|
||||
const char **pathspec;
|
||||
struct pathspec pathspec;
|
||||
int offset, nr;
|
||||
};
|
||||
|
||||
@ -35,9 +37,7 @@ static void *preload_thread(void *_data)
|
||||
struct index_state *index = p->index;
|
||||
struct cache_entry **cep = index->cache + p->offset;
|
||||
struct cache_def cache;
|
||||
struct pathspec pathspec;
|
||||
|
||||
init_pathspec(&pathspec, p->pathspec);
|
||||
memset(&cache, 0, sizeof(cache));
|
||||
nr = p->nr;
|
||||
if (nr + p->offset > index->cache_nr)
|
||||
@ -53,7 +53,7 @@ static void *preload_thread(void *_data)
|
||||
continue;
|
||||
if (ce_uptodate(ce))
|
||||
continue;
|
||||
if (!ce_path_match(ce, &pathspec))
|
||||
if (!ce_path_match(ce, &p->pathspec))
|
||||
continue;
|
||||
if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
|
||||
continue;
|
||||
@ -63,11 +63,11 @@ static void *preload_thread(void *_data)
|
||||
continue;
|
||||
ce_mark_uptodate(ce);
|
||||
} while (--nr > 0);
|
||||
free_pathspec(&pathspec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void preload_index(struct index_state *index, const char **pathspec)
|
||||
static void preload_index(struct index_state *index,
|
||||
const struct pathspec *pathspec)
|
||||
{
|
||||
int threads, i, work, offset;
|
||||
struct thread_data data[MAX_PARALLEL];
|
||||
@ -82,10 +82,12 @@ static void preload_index(struct index_state *index, const char **pathspec)
|
||||
threads = MAX_PARALLEL;
|
||||
offset = 0;
|
||||
work = DIV_ROUND_UP(index->cache_nr, threads);
|
||||
memset(&data, 0, sizeof(data));
|
||||
for (i = 0; i < threads; i++) {
|
||||
struct thread_data *p = data+i;
|
||||
p->index = index;
|
||||
p->pathspec = pathspec;
|
||||
if (pathspec)
|
||||
copy_pathspec(&p->pathspec, pathspec);
|
||||
p->offset = offset;
|
||||
p->nr = work;
|
||||
offset += work;
|
||||
@ -100,7 +102,8 @@ static void preload_index(struct index_state *index, const char **pathspec)
|
||||
}
|
||||
#endif
|
||||
|
||||
int read_index_preload(struct index_state *index, const char **pathspec)
|
||||
int read_index_preload(struct index_state *index,
|
||||
const struct pathspec *pathspec)
|
||||
{
|
||||
int retval = read_index(index);
|
||||
|
||||
|
@ -1114,7 +1114,8 @@ static void show_file(const char * fmt, const char * name, int in_porcelain,
|
||||
printf(fmt, name);
|
||||
}
|
||||
|
||||
int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec,
|
||||
int refresh_index(struct index_state *istate, unsigned int flags,
|
||||
const struct pathspec *pathspec,
|
||||
char *seen, const char *header_msg)
|
||||
{
|
||||
int i;
|
||||
@ -1149,7 +1150,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
|
||||
continue;
|
||||
|
||||
if (pathspec &&
|
||||
!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
|
||||
!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen))
|
||||
filtered = 1;
|
||||
|
||||
if (ce_stage(ce)) {
|
||||
|
7
rerere.c
7
rerere.c
@ -6,6 +6,7 @@
|
||||
#include "resolve-undo.h"
|
||||
#include "ll-merge.h"
|
||||
#include "attr.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
#define RESOLVED 0
|
||||
#define PUNTED 1
|
||||
@ -656,7 +657,7 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rerere_forget(const char **pathspec)
|
||||
int rerere_forget(struct pathspec *pathspec)
|
||||
{
|
||||
int i, fd;
|
||||
struct string_list conflict = STRING_LIST_INIT_DUP;
|
||||
@ -671,8 +672,8 @@ int rerere_forget(const char **pathspec)
|
||||
find_conflict(&conflict);
|
||||
for (i = 0; i < conflict.nr; i++) {
|
||||
struct string_list_item *it = &conflict.items[i];
|
||||
if (!match_pathspec(pathspec, it->string, strlen(it->string),
|
||||
0, NULL))
|
||||
if (!match_pathspec_depth(pathspec, it->string, strlen(it->string),
|
||||
0, NULL))
|
||||
continue;
|
||||
rerere_forget_one_path(it->string, &merge_rr);
|
||||
}
|
||||
|
4
rerere.h
4
rerere.h
@ -3,6 +3,8 @@
|
||||
|
||||
#include "string-list.h"
|
||||
|
||||
struct pathspec;
|
||||
|
||||
#define RERERE_AUTOUPDATE 01
|
||||
#define RERERE_NOAUTOUPDATE 02
|
||||
|
||||
@ -16,7 +18,7 @@ extern void *RERERE_RESOLVED;
|
||||
extern int setup_rerere(struct string_list *, int);
|
||||
extern int rerere(int);
|
||||
extern const char *rerere_path(const char *hex, const char *file);
|
||||
extern int rerere_forget(const char **);
|
||||
extern int rerere_forget(struct pathspec *);
|
||||
extern int rerere_remaining(struct string_list *);
|
||||
extern void rerere_clear(struct string_list *);
|
||||
extern void rerere_gc(struct string_list *);
|
||||
|
@ -173,7 +173,7 @@ void unmerge_marked_index(struct index_state *istate)
|
||||
}
|
||||
}
|
||||
|
||||
void unmerge_index(struct index_state *istate, const char **pathspec)
|
||||
void unmerge_index(struct index_state *istate, const struct pathspec *pathspec)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -182,7 +182,7 @@ void unmerge_index(struct index_state *istate, const char **pathspec)
|
||||
|
||||
for (i = 0; i < istate->cache_nr; i++) {
|
||||
const struct cache_entry *ce = istate->cache[i];
|
||||
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL))
|
||||
if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
|
||||
continue;
|
||||
i = unmerge_index_entry_at(istate, i);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ extern void resolve_undo_write(struct strbuf *, struct string_list *);
|
||||
extern struct string_list *resolve_undo_read(const char *, unsigned long);
|
||||
extern void resolve_undo_clear_index(struct index_state *);
|
||||
extern int unmerge_index_entry_at(struct index_state *, int);
|
||||
extern void unmerge_index(struct index_state *, const char **);
|
||||
extern void unmerge_index(struct index_state *, const struct pathspec *);
|
||||
extern void unmerge_marked_index(struct index_state *);
|
||||
|
||||
#endif
|
||||
|
11
revision.c
11
revision.c
@ -1373,7 +1373,7 @@ static void prepare_show_merge(struct rev_info *revs)
|
||||
i++;
|
||||
}
|
||||
free_pathspec(&revs->prune_data);
|
||||
init_pathspec(&revs->prune_data, prune);
|
||||
parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC, 0, "", prune);
|
||||
revs->limited = 1;
|
||||
}
|
||||
|
||||
@ -2121,8 +2121,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
||||
*/
|
||||
ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
|
||||
prune_data.path[prune_data.nr++] = NULL;
|
||||
init_pathspec(&revs->prune_data,
|
||||
get_pathspec(revs->prefix, prune_data.path));
|
||||
parse_pathspec(&revs->prune_data, 0, 0,
|
||||
revs->prefix, prune_data.path);
|
||||
}
|
||||
|
||||
if (revs->def == NULL)
|
||||
@ -2155,12 +2155,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
||||
revs->limited = 1;
|
||||
|
||||
if (revs->prune_data.nr) {
|
||||
diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning);
|
||||
copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
|
||||
/* Can't prune commits with rename following: the paths change.. */
|
||||
if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
|
||||
revs->prune = 1;
|
||||
if (!revs->full_diff)
|
||||
diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt);
|
||||
copy_pathspec(&revs->diffopt.pathspec,
|
||||
&revs->prune_data);
|
||||
}
|
||||
if (revs->combine_merges)
|
||||
revs->ignore_merges = 0;
|
||||
|
173
setup.c
173
setup.c
@ -5,7 +5,19 @@
|
||||
static int inside_git_dir = -1;
|
||||
static int inside_work_tree = -1;
|
||||
|
||||
static char *prefix_path_gently(const char *prefix, int len, const char *path)
|
||||
/*
|
||||
* Normalize "path", prepending the "prefix" for relative paths. If
|
||||
* remaining_prefix is not NULL, return the actual prefix still
|
||||
* remains in the path. For example, prefix = sub1/sub2/ and path is
|
||||
*
|
||||
* foo -> sub1/sub2/foo (full prefix)
|
||||
* ../foo -> sub1/foo (remaining prefix is sub1/)
|
||||
* ../../bar -> bar (no remaining prefix)
|
||||
* ../../sub1/sub2/foo -> sub1/sub2/foo (but no remaining prefix)
|
||||
* `pwd`/../bar -> sub1/bar (no remaining prefix)
|
||||
*/
|
||||
char *prefix_path_gently(const char *prefix, int len,
|
||||
int *remaining_prefix, const char *path)
|
||||
{
|
||||
const char *orig = path;
|
||||
char *sanitized;
|
||||
@ -13,13 +25,17 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path)
|
||||
const char *temp = real_path(path);
|
||||
sanitized = xmalloc(len + strlen(temp) + 1);
|
||||
strcpy(sanitized, temp);
|
||||
if (remaining_prefix)
|
||||
*remaining_prefix = 0;
|
||||
} else {
|
||||
sanitized = xmalloc(len + strlen(path) + 1);
|
||||
if (len)
|
||||
memcpy(sanitized, prefix, len);
|
||||
strcpy(sanitized + len, path);
|
||||
if (remaining_prefix)
|
||||
*remaining_prefix = len;
|
||||
}
|
||||
if (normalize_path_copy(sanitized, sanitized))
|
||||
if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix))
|
||||
goto error_out;
|
||||
if (is_absolute_path(orig)) {
|
||||
size_t root_len, len, total;
|
||||
@ -44,7 +60,7 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path)
|
||||
|
||||
char *prefix_path(const char *prefix, int len, const char *path)
|
||||
{
|
||||
char *r = prefix_path_gently(prefix, len, path);
|
||||
char *r = prefix_path_gently(prefix, len, NULL, path);
|
||||
if (!r)
|
||||
die("'%s' is outside repository", path);
|
||||
return r;
|
||||
@ -53,7 +69,7 @@ char *prefix_path(const char *prefix, int len, const char *path)
|
||||
int path_inside_repo(const char *prefix, const char *path)
|
||||
{
|
||||
int len = prefix ? strlen(prefix) : 0;
|
||||
char *r = prefix_path_gently(prefix, len, path);
|
||||
char *r = prefix_path_gently(prefix, len, NULL, path);
|
||||
if (r) {
|
||||
free(r);
|
||||
return 1;
|
||||
@ -154,155 +170,6 @@ void verify_non_filename(const char *prefix, const char *arg)
|
||||
"'git <command> [<revision>...] -- [<file>...]'", arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Magic pathspec
|
||||
*
|
||||
* NEEDSWORK: These need to be moved to dir.h or even to a new
|
||||
* pathspec.h when we restructure get_pathspec() users to use the
|
||||
* "struct pathspec" interface.
|
||||
*
|
||||
* Possible future magic semantics include stuff like:
|
||||
*
|
||||
* { PATHSPEC_NOGLOB, '!', "noglob" },
|
||||
* { PATHSPEC_ICASE, '\0', "icase" },
|
||||
* { PATHSPEC_RECURSIVE, '*', "recursive" },
|
||||
* { PATHSPEC_REGEXP, '\0', "regexp" },
|
||||
*
|
||||
*/
|
||||
#define PATHSPEC_FROMTOP (1<<0)
|
||||
|
||||
static struct pathspec_magic {
|
||||
unsigned bit;
|
||||
char mnemonic; /* this cannot be ':'! */
|
||||
const char *name;
|
||||
} pathspec_magic[] = {
|
||||
{ PATHSPEC_FROMTOP, '/', "top" },
|
||||
};
|
||||
|
||||
/*
|
||||
* Take an element of a pathspec and check for magic signatures.
|
||||
* Append the result to the prefix.
|
||||
*
|
||||
* For now, we only parse the syntax and throw out anything other than
|
||||
* "top" magic.
|
||||
*
|
||||
* NEEDSWORK: This needs to be rewritten when we start migrating
|
||||
* get_pathspec() users to use the "struct pathspec" interface. For
|
||||
* example, a pathspec element may be marked as case-insensitive, but
|
||||
* the prefix part must always match literally, and a single stupid
|
||||
* string cannot express such a case.
|
||||
*/
|
||||
static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
|
||||
{
|
||||
unsigned magic = 0;
|
||||
const char *copyfrom = elt;
|
||||
int i;
|
||||
|
||||
if (elt[0] != ':') {
|
||||
; /* nothing to do */
|
||||
} else if (elt[1] == '(') {
|
||||
/* longhand */
|
||||
const char *nextat;
|
||||
for (copyfrom = elt + 2;
|
||||
*copyfrom && *copyfrom != ')';
|
||||
copyfrom = nextat) {
|
||||
size_t len = strcspn(copyfrom, ",)");
|
||||
if (copyfrom[len] == ',')
|
||||
nextat = copyfrom + len + 1;
|
||||
else
|
||||
/* handle ')' and '\0' */
|
||||
nextat = copyfrom + len;
|
||||
if (!len)
|
||||
continue;
|
||||
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
|
||||
if (strlen(pathspec_magic[i].name) == len &&
|
||||
!strncmp(pathspec_magic[i].name, copyfrom, len)) {
|
||||
magic |= pathspec_magic[i].bit;
|
||||
break;
|
||||
}
|
||||
if (ARRAY_SIZE(pathspec_magic) <= i)
|
||||
die("Invalid pathspec magic '%.*s' in '%s'",
|
||||
(int) len, copyfrom, elt);
|
||||
}
|
||||
if (*copyfrom != ')')
|
||||
die("Missing ')' at the end of pathspec magic in '%s'", elt);
|
||||
copyfrom++;
|
||||
} else {
|
||||
/* shorthand */
|
||||
for (copyfrom = elt + 1;
|
||||
*copyfrom && *copyfrom != ':';
|
||||
copyfrom++) {
|
||||
char ch = *copyfrom;
|
||||
|
||||
if (!is_pathspec_magic(ch))
|
||||
break;
|
||||
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
|
||||
if (pathspec_magic[i].mnemonic == ch) {
|
||||
magic |= pathspec_magic[i].bit;
|
||||
break;
|
||||
}
|
||||
if (ARRAY_SIZE(pathspec_magic) <= i)
|
||||
die("Unimplemented pathspec magic '%c' in '%s'",
|
||||
ch, elt);
|
||||
}
|
||||
if (*copyfrom == ':')
|
||||
copyfrom++;
|
||||
}
|
||||
|
||||
if (magic & PATHSPEC_FROMTOP)
|
||||
return xstrdup(copyfrom);
|
||||
else
|
||||
return prefix_path(prefix, prefixlen, copyfrom);
|
||||
}
|
||||
|
||||
/*
|
||||
* N.B. get_pathspec() is deprecated in favor of the "struct pathspec"
|
||||
* based interface - see pathspec_magic above.
|
||||
*
|
||||
* Arguments:
|
||||
* - prefix - a path relative to the root of the working tree
|
||||
* - pathspec - a list of paths underneath the prefix path
|
||||
*
|
||||
* Iterates over pathspec, prepending each path with prefix,
|
||||
* and return the resulting list.
|
||||
*
|
||||
* If pathspec is empty, return a singleton list containing prefix.
|
||||
*
|
||||
* If pathspec and prefix are both empty, return an empty list.
|
||||
*
|
||||
* This is typically used by built-in commands such as add.c, in order
|
||||
* to normalize argv arguments provided to the built-in into a list of
|
||||
* paths to process, all relative to the root of the working tree.
|
||||
*/
|
||||
const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||
{
|
||||
const char *entry = *pathspec;
|
||||
const char **src, **dst;
|
||||
int prefixlen;
|
||||
|
||||
if (!prefix && !entry)
|
||||
return NULL;
|
||||
|
||||
if (!entry) {
|
||||
static const char *spec[2];
|
||||
spec[0] = prefix;
|
||||
spec[1] = NULL;
|
||||
return spec;
|
||||
}
|
||||
|
||||
/* Otherwise we have to re-write the entries.. */
|
||||
src = pathspec;
|
||||
dst = pathspec;
|
||||
prefixlen = prefix ? strlen(prefix) : 0;
|
||||
while (*src) {
|
||||
*(dst++) = prefix_pathspec(prefix, prefixlen, *src);
|
||||
src++;
|
||||
}
|
||||
*dst = NULL;
|
||||
if (!*pathspec)
|
||||
return NULL;
|
||||
return pathspec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if it looks like we're at a git directory.
|
||||
|
149
submodule.c
149
submodule.c
@ -10,6 +10,7 @@
|
||||
#include "string-list.h"
|
||||
#include "sha1-array.h"
|
||||
#include "argv-array.h"
|
||||
#include "blob.h"
|
||||
|
||||
static struct string_list config_name_for_path;
|
||||
static struct string_list config_fetch_recurse_submodules_for_name;
|
||||
@ -30,6 +31,118 @@ static struct sha1_array ref_tips_after_fetch;
|
||||
*/
|
||||
static int gitmodules_is_unmerged;
|
||||
|
||||
/*
|
||||
* This flag is set if the .gitmodules file had unstaged modifications on
|
||||
* startup. This must be checked before allowing modifications to the
|
||||
* .gitmodules file with the intention to stage them later, because when
|
||||
* continuing we would stage the modifications the user didn't stage herself
|
||||
* too. That might change in a future version when we learn to stage the
|
||||
* changes we do ourselves without staging any previous modifications.
|
||||
*/
|
||||
static int gitmodules_is_modified;
|
||||
|
||||
|
||||
int is_staging_gitmodules_ok(void)
|
||||
{
|
||||
return !gitmodules_is_modified;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to update the "path" entry in the "submodule.<name>" section of the
|
||||
* .gitmodules file. Return 0 only if a .gitmodules file was found, a section
|
||||
* with the correct path=<oldpath> setting was found and we could update it.
|
||||
*/
|
||||
int update_path_in_gitmodules(const char *oldpath, const char *newpath)
|
||||
{
|
||||
struct strbuf entry = STRBUF_INIT;
|
||||
struct string_list_item *path_option;
|
||||
|
||||
if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
|
||||
return -1;
|
||||
|
||||
if (gitmodules_is_unmerged)
|
||||
die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
|
||||
|
||||
path_option = unsorted_string_list_lookup(&config_name_for_path, oldpath);
|
||||
if (!path_option) {
|
||||
warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
|
||||
return -1;
|
||||
}
|
||||
strbuf_addstr(&entry, "submodule.");
|
||||
strbuf_addstr(&entry, path_option->util);
|
||||
strbuf_addstr(&entry, ".path");
|
||||
if (git_config_set_in_file(".gitmodules", entry.buf, newpath) < 0) {
|
||||
/* Maybe the user already did that, don't error out here */
|
||||
warning(_("Could not update .gitmodules entry %s"), entry.buf);
|
||||
strbuf_release(&entry);
|
||||
return -1;
|
||||
}
|
||||
strbuf_release(&entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to remove the "submodule.<name>" section from .gitmodules where the given
|
||||
* path is configured. Return 0 only if a .gitmodules file was found, a section
|
||||
* with the correct path=<path> setting was found and we could remove it.
|
||||
*/
|
||||
int remove_path_from_gitmodules(const char *path)
|
||||
{
|
||||
struct strbuf sect = STRBUF_INIT;
|
||||
struct string_list_item *path_option;
|
||||
|
||||
if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
|
||||
return -1;
|
||||
|
||||
if (gitmodules_is_unmerged)
|
||||
die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
|
||||
|
||||
path_option = unsorted_string_list_lookup(&config_name_for_path, path);
|
||||
if (!path_option) {
|
||||
warning(_("Could not find section in .gitmodules where path=%s"), path);
|
||||
return -1;
|
||||
}
|
||||
strbuf_addstr(§, "submodule.");
|
||||
strbuf_addstr(§, path_option->util);
|
||||
if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) {
|
||||
/* Maybe the user already did that, don't error out here */
|
||||
warning(_("Could not remove .gitmodules entry for %s"), path);
|
||||
strbuf_release(§);
|
||||
return -1;
|
||||
}
|
||||
strbuf_release(§);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stage_updated_gitmodules(void)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct stat st;
|
||||
int pos;
|
||||
struct cache_entry *ce;
|
||||
int namelen = strlen(".gitmodules");
|
||||
|
||||
pos = cache_name_pos(".gitmodules", namelen);
|
||||
if (pos < 0) {
|
||||
warning(_("could not find .gitmodules in index"));
|
||||
return;
|
||||
}
|
||||
ce = active_cache[pos];
|
||||
ce->ce_flags = namelen;
|
||||
if (strbuf_read_file(&buf, ".gitmodules", 0) < 0)
|
||||
die(_("reading updated .gitmodules failed"));
|
||||
if (lstat(".gitmodules", &st) < 0)
|
||||
die_errno(_("unable to stat updated .gitmodules"));
|
||||
fill_stat_cache_info(ce, &st);
|
||||
ce->ce_mode = ce_mode_from_stat(ce, st.st_mode);
|
||||
if (remove_cache_entry_at(pos) < 0)
|
||||
die(_("unable to remove .gitmodules from index"));
|
||||
if (write_sha1_file(buf.buf, buf.len, blob_type, ce->sha1))
|
||||
die(_("adding updated .gitmodules failed"));
|
||||
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
|
||||
die(_("staging updated .gitmodules failed"));
|
||||
}
|
||||
|
||||
static int add_submodule_odb(const char *path)
|
||||
{
|
||||
struct strbuf objects_directory = STRBUF_INIT;
|
||||
@ -116,6 +229,11 @@ void gitmodules_config(void)
|
||||
!memcmp(ce->name, ".gitmodules", 11))
|
||||
gitmodules_is_unmerged = 1;
|
||||
}
|
||||
} else if (pos < active_nr) {
|
||||
struct stat st;
|
||||
if (lstat(".gitmodules", &st) == 0 &&
|
||||
ce_match_stat(active_cache[pos], &st, 0) & DATA_CHANGED)
|
||||
gitmodules_is_modified = 1;
|
||||
}
|
||||
|
||||
if (!gitmodules_is_unmerged)
|
||||
@ -1010,3 +1128,34 @@ int merge_submodule(unsigned char result[20], const char *path,
|
||||
free(merges.objects);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update gitfile and core.worktree setting to connect work tree and git dir */
|
||||
void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
|
||||
{
|
||||
struct strbuf file_name = STRBUF_INIT;
|
||||
struct strbuf rel_path = STRBUF_INIT;
|
||||
const char *real_work_tree = xstrdup(real_path(work_tree));
|
||||
FILE *fp;
|
||||
|
||||
/* Update gitfile */
|
||||
strbuf_addf(&file_name, "%s/.git", work_tree);
|
||||
fp = fopen(file_name.buf, "w");
|
||||
if (!fp)
|
||||
die(_("Could not create git link %s"), file_name.buf);
|
||||
fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree,
|
||||
&rel_path));
|
||||
fclose(fp);
|
||||
|
||||
/* Update core.worktree setting */
|
||||
strbuf_reset(&file_name);
|
||||
strbuf_addf(&file_name, "%s/config", git_dir);
|
||||
if (git_config_set_in_file(file_name.buf, "core.worktree",
|
||||
relative_path(real_work_tree, git_dir,
|
||||
&rel_path)))
|
||||
die(_("Could not set core.worktree in %s"),
|
||||
file_name.buf);
|
||||
|
||||
strbuf_release(&file_name);
|
||||
strbuf_release(&rel_path);
|
||||
free((void *)real_work_tree);
|
||||
}
|
||||
|
@ -11,6 +11,10 @@ enum {
|
||||
RECURSE_SUBMODULES_ON = 2
|
||||
};
|
||||
|
||||
int is_staging_gitmodules_ok(void);
|
||||
int update_path_in_gitmodules(const char *oldpath, const char *newpath);
|
||||
int remove_path_from_gitmodules(const char *path);
|
||||
void stage_updated_gitmodules(void);
|
||||
void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
|
||||
const char *path);
|
||||
int submodule_config(const char *var, const char *value, void *cb);
|
||||
@ -36,5 +40,6 @@ int merge_submodule(unsigned char result[20], const char *path, const unsigned c
|
||||
int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
|
||||
struct string_list *needs_pushing);
|
||||
int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
|
||||
void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
|
||||
|
||||
#endif
|
||||
|
@ -432,7 +432,7 @@ test_expect_success_multi SYMLINKS 'symlink' ':: a/symlink' '
|
||||
|
||||
test_expect_success_multi SYMLINKS 'beyond a symlink' '' '
|
||||
test_check_ignore "a/symlink/foo" 128 &&
|
||||
test_stderr "fatal: '\''a/symlink/foo'\'' is beyond a symbolic link"
|
||||
test_stderr "fatal: pathspec '\''a/symlink/foo'\'' is beyond a symbolic link"
|
||||
'
|
||||
|
||||
test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
|
||||
@ -440,7 +440,7 @@ test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
|
||||
cd a &&
|
||||
test_check_ignore "symlink/foo" 128
|
||||
) &&
|
||||
test_stderr "fatal: '\''symlink/foo'\'' is beyond a symbolic link"
|
||||
test_stderr "fatal: pathspec '\''symlink/foo'\'' is beyond a symbolic link"
|
||||
'
|
||||
|
||||
############################################################################
|
||||
@ -449,7 +449,7 @@ test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
|
||||
|
||||
test_expect_success_multi 'submodule' '' '
|
||||
test_check_ignore "a/submodule/one" 128 &&
|
||||
test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
|
||||
test_stderr "fatal: Pathspec '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
|
||||
'
|
||||
|
||||
test_expect_success_multi 'submodule from subdirectory' '' '
|
||||
@ -457,7 +457,7 @@ test_expect_success_multi 'submodule from subdirectory' '' '
|
||||
cd a &&
|
||||
test_check_ignore "submodule/one" 128
|
||||
) &&
|
||||
test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
|
||||
test_stderr "fatal: Pathspec '\''submodule/one'\'' is in submodule '\''a/submodule'\''"
|
||||
'
|
||||
|
||||
############################################################################
|
||||
|
@ -263,6 +263,7 @@ test_expect_success 'rm removes subdirectories recursively' '
|
||||
'
|
||||
|
||||
cat >expect <<EOF
|
||||
M .gitmodules
|
||||
D submod
|
||||
EOF
|
||||
|
||||
@ -270,6 +271,15 @@ cat >expect.modified <<EOF
|
||||
M submod
|
||||
EOF
|
||||
|
||||
cat >expect.cached <<EOF
|
||||
D submod
|
||||
EOF
|
||||
|
||||
cat >expect.both_deleted<<EOF
|
||||
D .gitmodules
|
||||
D submod
|
||||
EOF
|
||||
|
||||
test_expect_success 'rm removes empty submodules from work tree' '
|
||||
mkdir submod &&
|
||||
git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) submod &&
|
||||
@ -281,16 +291,20 @@ test_expect_success 'rm removes empty submodules from work tree' '
|
||||
git rm submod &&
|
||||
test ! -e submod &&
|
||||
git status -s -uno --ignore-submodules=none > actual &&
|
||||
test_cmp expect actual
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.url &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.path
|
||||
'
|
||||
|
||||
test_expect_success 'rm removes removed submodule from index' '
|
||||
test_expect_success 'rm removes removed submodule from index and .gitmodules' '
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
rm -rf submod &&
|
||||
git rm submod &&
|
||||
git status -s -uno --ignore-submodules=none > actual &&
|
||||
test_cmp expect actual
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.url &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.path
|
||||
'
|
||||
|
||||
test_expect_success 'rm removes work tree of unmodified submodules' '
|
||||
@ -299,7 +313,9 @@ test_expect_success 'rm removes work tree of unmodified submodules' '
|
||||
git rm submod &&
|
||||
test ! -d submod &&
|
||||
git status -s -uno --ignore-submodules=none > actual &&
|
||||
test_cmp expect actual
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.url &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.path
|
||||
'
|
||||
|
||||
test_expect_success 'rm removes a submodule with a trailing /' '
|
||||
@ -333,6 +349,72 @@ test_expect_success 'rm of a populated submodule with different HEAD fails unles
|
||||
git rm -f submod &&
|
||||
test ! -d submod &&
|
||||
git status -s -uno --ignore-submodules=none > actual &&
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.url &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.path
|
||||
'
|
||||
|
||||
test_expect_success 'rm --cached leaves work tree of populated submodules and .gitmodules alone' '
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
git rm --cached submod &&
|
||||
test -d submod &&
|
||||
test -f submod/.git &&
|
||||
git status -s -uno >actual &&
|
||||
test_cmp expect.cached actual &&
|
||||
git config -f .gitmodules submodule.sub.url &&
|
||||
git config -f .gitmodules submodule.sub.path
|
||||
'
|
||||
|
||||
test_expect_success 'rm --dry-run does not touch the submodule or .gitmodules' '
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
git rm -n submod &&
|
||||
test -f submod/.git &&
|
||||
git diff-index --exit-code HEAD
|
||||
'
|
||||
|
||||
test_expect_success 'rm does not complain when no .gitmodules file is found' '
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
git rm .gitmodules &&
|
||||
git rm submod >actual 2>actual.err &&
|
||||
! test -s actual.err &&
|
||||
! test -d submod &&
|
||||
! test -f submod/.git &&
|
||||
git status -s -uno >actual &&
|
||||
test_cmp expect.both_deleted actual
|
||||
'
|
||||
|
||||
test_expect_success 'rm will error out on a modified .gitmodules file unless staged' '
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
git config -f .gitmodules foo.bar true &&
|
||||
test_must_fail git rm submod >actual 2>actual.err &&
|
||||
test -s actual.err &&
|
||||
test -d submod &&
|
||||
test -f submod/.git &&
|
||||
git diff-files --quiet -- submod &&
|
||||
git add .gitmodules &&
|
||||
git rm submod >actual 2>actual.err &&
|
||||
! test -s actual.err &&
|
||||
! test -d submod &&
|
||||
! test -f submod/.git &&
|
||||
git status -s -uno >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'rm issues a warning when section is not found in .gitmodules' '
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
git config -f .gitmodules --remove-section submodule.sub &&
|
||||
git add .gitmodules &&
|
||||
echo "warning: Could not find section in .gitmodules where path=submod" >expect.err &&
|
||||
git rm submod >actual 2>actual.err &&
|
||||
test_i18ncmp expect.err actual.err &&
|
||||
! test -d submod &&
|
||||
! test -f submod/.git &&
|
||||
git status -s -uno >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
@ -427,7 +509,9 @@ test_expect_success 'rm of a conflicted populated submodule with different HEAD
|
||||
git rm -f submod &&
|
||||
test ! -d submod &&
|
||||
git status -s -uno --ignore-submodules=none > actual &&
|
||||
test_cmp expect actual
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.url &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.path
|
||||
'
|
||||
|
||||
test_expect_success 'rm of a conflicted populated submodule with modifications fails unless forced' '
|
||||
@ -446,7 +530,9 @@ test_expect_success 'rm of a conflicted populated submodule with modifications f
|
||||
git rm -f submod &&
|
||||
test ! -d submod &&
|
||||
git status -s -uno --ignore-submodules=none > actual &&
|
||||
test_cmp expect actual
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.url &&
|
||||
test_must_fail git config -f .gitmodules submodule.sub.path
|
||||
'
|
||||
|
||||
test_expect_success 'rm of a conflicted populated submodule with untracked files fails unless forced' '
|
||||
|
@ -32,6 +32,16 @@ test_expect_success 'star pathspec globs' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'star pathspec globs' '
|
||||
cat >expect <<-\EOF &&
|
||||
bracket
|
||||
star
|
||||
vanilla
|
||||
EOF
|
||||
git log --format=%s -- ":(glob)f*" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'bracket pathspec globs and matches literal brackets' '
|
||||
cat >expect <<-\EOF &&
|
||||
bracket
|
||||
@ -41,28 +51,105 @@ test_expect_success 'bracket pathspec globs and matches literal brackets' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'bracket pathspec globs and matches literal brackets' '
|
||||
cat >expect <<-\EOF &&
|
||||
bracket
|
||||
vanilla
|
||||
EOF
|
||||
git log --format=%s -- ":(glob)f[o][o]" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'no-glob option matches literally (vanilla)' '
|
||||
echo vanilla >expect &&
|
||||
git --literal-pathspecs log --format=%s -- foo >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'no-glob option matches literally (vanilla)' '
|
||||
echo vanilla >expect &&
|
||||
git log --format=%s -- ":(literal)foo" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'no-glob option matches literally (star)' '
|
||||
echo star >expect &&
|
||||
git --literal-pathspecs log --format=%s -- "f*" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'no-glob option matches literally (star)' '
|
||||
echo star >expect &&
|
||||
git log --format=%s -- ":(literal)f*" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'no-glob option matches literally (bracket)' '
|
||||
echo bracket >expect &&
|
||||
git --literal-pathspecs log --format=%s -- "f[o][o]" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'no-glob option matches literally (bracket)' '
|
||||
echo bracket >expect &&
|
||||
git log --format=%s -- ":(literal)f[o][o]" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'no-glob option disables :(literal)' '
|
||||
: >expect &&
|
||||
git --literal-pathspecs log --format=%s -- ":(literal)foo" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'no-glob environment variable works' '
|
||||
echo star >expect &&
|
||||
GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'setup xxx/bar' '
|
||||
mkdir xxx &&
|
||||
test_commit xxx xxx/bar
|
||||
'
|
||||
|
||||
test_expect_success '**/ works with :(glob)' '
|
||||
cat >expect <<-\EOF &&
|
||||
xxx
|
||||
unrelated
|
||||
EOF
|
||||
git log --format=%s -- ":(glob)**/bar" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '**/ does not work with --noglob-pathspecs' '
|
||||
: >expect &&
|
||||
git --noglob-pathspecs log --format=%s -- "**/bar" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '**/ works with :(glob) and --noglob-pathspecs' '
|
||||
cat >expect <<-\EOF &&
|
||||
xxx
|
||||
unrelated
|
||||
EOF
|
||||
git --noglob-pathspecs log --format=%s -- ":(glob)**/bar" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '**/ works with --glob-pathspecs' '
|
||||
cat >expect <<-\EOF &&
|
||||
xxx
|
||||
unrelated
|
||||
EOF
|
||||
git --glob-pathspecs log --format=%s -- "**/bar" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '**/ does not work with :(literal) and --glob-pathspecs' '
|
||||
: >expect &&
|
||||
git --glob-pathspecs log --format=%s -- ":(literal)**/bar" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
103
t/t6131-pathspec-icase.sh
Executable file
103
t/t6131-pathspec-icase.sh
Executable file
@ -0,0 +1,103 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test case insensitive pathspec limiting'
|
||||
. ./test-lib.sh
|
||||
|
||||
if test_have_prereq CASE_INSENSITIVE_FS
|
||||
then
|
||||
skip_all='skipping case sensitive tests - case insensitive file system'
|
||||
test_done
|
||||
fi
|
||||
|
||||
test_expect_success 'create commits with glob characters' '
|
||||
test_commit bar bar &&
|
||||
test_commit bAr bAr &&
|
||||
test_commit BAR BAR &&
|
||||
mkdir foo &&
|
||||
test_commit foo/bar foo/bar &&
|
||||
test_commit foo/bAr foo/bAr &&
|
||||
test_commit foo/BAR foo/BAR &&
|
||||
mkdir fOo &&
|
||||
test_commit fOo/bar fOo/bar &&
|
||||
test_commit fOo/bAr fOo/bAr &&
|
||||
test_commit fOo/BAR fOo/BAR &&
|
||||
mkdir FOO &&
|
||||
test_commit FOO/bar FOO/bar &&
|
||||
test_commit FOO/bAr FOO/bAr &&
|
||||
test_commit FOO/BAR FOO/BAR
|
||||
'
|
||||
|
||||
test_expect_success 'tree_entry_interesting matches bar' '
|
||||
echo bar >expect &&
|
||||
git log --format=%s -- "bar" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'tree_entry_interesting matches :(icase)bar' '
|
||||
cat <<-EOF >expect &&
|
||||
BAR
|
||||
bAr
|
||||
bar
|
||||
EOF
|
||||
git log --format=%s -- ":(icase)bar" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'tree_entry_interesting matches :(icase)bar with prefix' '
|
||||
cat <<-EOF >expect &&
|
||||
fOo/BAR
|
||||
fOo/bAr
|
||||
fOo/bar
|
||||
EOF
|
||||
( cd fOo && git log --format=%s -- ":(icase)bar" ) >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'tree_entry_interesting matches :(icase)bar with empty prefix' '
|
||||
cat <<-EOF >expect &&
|
||||
FOO/BAR
|
||||
FOO/bAr
|
||||
FOO/bar
|
||||
fOo/BAR
|
||||
fOo/bAr
|
||||
fOo/bar
|
||||
foo/BAR
|
||||
foo/bAr
|
||||
foo/bar
|
||||
EOF
|
||||
( cd fOo && git log --format=%s -- ":(icase)../foo/bar" ) >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'match_pathspec_depth matches :(icase)bar' '
|
||||
cat <<-EOF >expect &&
|
||||
BAR
|
||||
bAr
|
||||
bar
|
||||
EOF
|
||||
git ls-files ":(icase)bar" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'match_pathspec_depth matches :(icase)bar with prefix' '
|
||||
cat <<-EOF >expect &&
|
||||
fOo/BAR
|
||||
fOo/bAr
|
||||
fOo/bar
|
||||
EOF
|
||||
( cd fOo && git ls-files --full-name ":(icase)bar" ) >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'match_pathspec_depth matches :(icase)bar with empty prefix' '
|
||||
cat <<-EOF >expect &&
|
||||
bar
|
||||
fOo/BAR
|
||||
fOo/bAr
|
||||
fOo/bar
|
||||
EOF
|
||||
( cd fOo && git ls-files --full-name ":(icase)bar" ../bar ) >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
128
t/t7001-mv.sh
128
t/t7001-mv.sh
@ -259,4 +259,132 @@ test_expect_success SYMLINKS 'check moved symlink' '
|
||||
|
||||
rm -f moved symlink
|
||||
|
||||
test_expect_success 'setup submodule' '
|
||||
git commit -m initial &&
|
||||
git reset --hard &&
|
||||
git submodule add ./. sub &&
|
||||
echo content >file &&
|
||||
git add file &&
|
||||
git commit -m "added sub and file"
|
||||
'
|
||||
|
||||
test_expect_success 'git mv cannot move a submodule in a file' '
|
||||
test_must_fail git mv sub file
|
||||
'
|
||||
|
||||
test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '
|
||||
entry="$(git ls-files --stage sub | cut -f 1)" &&
|
||||
git rm .gitmodules &&
|
||||
(
|
||||
cd sub &&
|
||||
rm -f .git &&
|
||||
cp -a ../.git/modules/sub .git &&
|
||||
GIT_WORK_TREE=. git config --unset core.worktree
|
||||
) &&
|
||||
mkdir mod &&
|
||||
git mv sub mod/sub &&
|
||||
! test -e sub &&
|
||||
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
|
||||
(
|
||||
cd mod/sub &&
|
||||
git status
|
||||
) &&
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet
|
||||
'
|
||||
|
||||
test_expect_success 'git mv moves a submodule with gitfile' '
|
||||
rm -rf mod/sub &&
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
entry="$(git ls-files --stage sub | cut -f 1)" &&
|
||||
(
|
||||
cd mod &&
|
||||
git mv ../sub/ .
|
||||
) &&
|
||||
! test -e sub &&
|
||||
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
|
||||
(
|
||||
cd mod/sub &&
|
||||
git status
|
||||
) &&
|
||||
echo mod/sub >expected &&
|
||||
git config -f .gitmodules submodule.sub.path >actual &&
|
||||
test_cmp expected actual &&
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet
|
||||
'
|
||||
|
||||
test_expect_success 'mv does not complain when no .gitmodules file is found' '
|
||||
rm -rf mod/sub &&
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
git rm .gitmodules &&
|
||||
entry="$(git ls-files --stage sub | cut -f 1)" &&
|
||||
git mv sub mod/sub 2>actual.err &&
|
||||
! test -s actual.err &&
|
||||
! test -e sub &&
|
||||
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
|
||||
(
|
||||
cd mod/sub &&
|
||||
git status
|
||||
) &&
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet
|
||||
'
|
||||
|
||||
test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '
|
||||
rm -rf mod/sub &&
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
git config -f .gitmodules foo.bar true &&
|
||||
entry="$(git ls-files --stage sub | cut -f 1)" &&
|
||||
test_must_fail git mv sub mod/sub 2>actual.err &&
|
||||
test -s actual.err &&
|
||||
test -e sub &&
|
||||
git diff-files --quiet -- sub &&
|
||||
git add .gitmodules &&
|
||||
git mv sub mod/sub 2>actual.err &&
|
||||
! test -s actual.err &&
|
||||
! test -e sub &&
|
||||
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
|
||||
(
|
||||
cd mod/sub &&
|
||||
git status
|
||||
) &&
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet
|
||||
'
|
||||
|
||||
test_expect_success 'mv issues a warning when section is not found in .gitmodules' '
|
||||
rm -rf mod/sub &&
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
git config -f .gitmodules --remove-section submodule.sub &&
|
||||
git add .gitmodules &&
|
||||
entry="$(git ls-files --stage sub | cut -f 1)" &&
|
||||
echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
|
||||
git mv sub mod/sub 2>actual.err &&
|
||||
test_i18ncmp expect.err actual.err &&
|
||||
! test -e sub &&
|
||||
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
|
||||
(
|
||||
cd mod/sub &&
|
||||
git status
|
||||
) &&
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet
|
||||
'
|
||||
|
||||
test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
|
||||
rm -rf mod/sub &&
|
||||
git reset --hard &&
|
||||
git submodule update &&
|
||||
git mv -n sub mod/sub 2>actual.err &&
|
||||
test -f sub/.git &&
|
||||
git diff-index --exit-code HEAD &&
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet -- sub .gitmodules
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -783,13 +783,11 @@ test_expect_success 'submodule add --name allows to replace a submodule with ano
|
||||
test_cmp expect .git
|
||||
) &&
|
||||
echo "repo" >expect &&
|
||||
git config -f .gitmodules submodule.repo.path >actual &&
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git config -f .gitmodules submodule.repo.path &&
|
||||
git config -f .gitmodules submodule.repo_new.path >actual &&
|
||||
test_cmp expect actual&&
|
||||
echo "$submodurl/repo" >expect &&
|
||||
git config -f .gitmodules submodule.repo.url >actual &&
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git config -f .gitmodules submodule.repo.url &&
|
||||
echo "$submodurl/bare.git" >expect &&
|
||||
git config -f .gitmodules submodule.repo_new.url >actual &&
|
||||
test_cmp expect actual &&
|
||||
@ -809,12 +807,8 @@ test_expect_success 'submodule add with an existing name fails unless forced' '
|
||||
git rm repo &&
|
||||
test_must_fail git submodule add -q --name repo_new "$submodurl/repo.git" repo &&
|
||||
test ! -d repo &&
|
||||
echo "repo" >expect &&
|
||||
git config -f .gitmodules submodule.repo_new.path >actual &&
|
||||
test_cmp expect actual&&
|
||||
echo "$submodurl/bare.git" >expect &&
|
||||
git config -f .gitmodules submodule.repo_new.url >actual &&
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git config -f .gitmodules submodule.repo_new.path &&
|
||||
test_must_fail git config -f .gitmodules submodule.repo_new.url &&
|
||||
echo "$submodurl/bare.git" >expect &&
|
||||
git config submodule.repo_new.url >actual &&
|
||||
test_cmp expect actual &&
|
||||
|
@ -253,7 +253,7 @@ test_expect_success 'deleted vs modified submodule' '
|
||||
git checkout -b test6 branch1 &&
|
||||
git submodule update -N &&
|
||||
mv submod submod-movedaside &&
|
||||
git rm submod &&
|
||||
git rm --cached submod &&
|
||||
git commit -m "Submodule deleted from branch" &&
|
||||
git checkout -b test6.a test6 &&
|
||||
test_must_fail git merge master &&
|
||||
@ -322,7 +322,7 @@ test_expect_success 'file vs modified submodule' '
|
||||
git checkout -b test7 branch1 &&
|
||||
git submodule update -N &&
|
||||
mv submod submod-movedaside &&
|
||||
git rm submod &&
|
||||
git rm --cached submod &&
|
||||
echo not a submodule >submod &&
|
||||
git add submod &&
|
||||
git commit -m "Submodule path becomes file" &&
|
||||
@ -453,7 +453,7 @@ test_expect_success 'submodule in subdirectory' '
|
||||
test_expect_success 'directory vs modified submodule' '
|
||||
git checkout -b test11 branch1 &&
|
||||
mv submod submod-movedaside &&
|
||||
git rm submod &&
|
||||
git rm --cached submod &&
|
||||
mkdir submod &&
|
||||
echo not a submodule >submod/file16 &&
|
||||
git add submod/file16 &&
|
||||
|
48
tree-diff.c
48
tree-diff.c
@ -138,7 +138,6 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
|
||||
|
||||
/* Enable recursion indefinitely */
|
||||
opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
|
||||
opt->pathspec.max_depth = -1;
|
||||
|
||||
strbuf_init(&base, PATH_MAX);
|
||||
strbuf_add(&base, base_str, baselen);
|
||||
@ -196,9 +195,27 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
|
||||
struct diff_options diff_opts;
|
||||
struct diff_queue_struct *q = &diff_queued_diff;
|
||||
struct diff_filepair *choice;
|
||||
const char *paths[1];
|
||||
int i;
|
||||
|
||||
/*
|
||||
* follow-rename code is very specific, we need exactly one
|
||||
* path. Magic that matches more than one path is not
|
||||
* supported.
|
||||
*/
|
||||
GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
|
||||
#if 0
|
||||
/*
|
||||
* We should reject wildcards as well. Unfortunately we
|
||||
* haven't got a reliable way to detect that 'foo\*bar' in
|
||||
* fact has no wildcards. nowildcard_len is merely a hint for
|
||||
* optimization. Let it slip for now until wildmatch is taught
|
||||
* about dry-run mode and returns wildcard info.
|
||||
*/
|
||||
if (opt->pathspec.has_wildcard)
|
||||
die("BUG:%s:%d: wildcards are not supported",
|
||||
__FILE__, __LINE__);
|
||||
#endif
|
||||
|
||||
/* Remove the file creation entry from the diff queue, and remember it */
|
||||
choice = q->queue[0];
|
||||
q->nr = 0;
|
||||
@ -207,15 +224,13 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
|
||||
DIFF_OPT_SET(&diff_opts, RECURSIVE);
|
||||
DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
|
||||
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||
diff_opts.single_follow = opt->pathspec.raw[0];
|
||||
diff_opts.single_follow = opt->pathspec.items[0].match;
|
||||
diff_opts.break_opt = opt->break_opt;
|
||||
diff_opts.rename_score = opt->rename_score;
|
||||
paths[0] = NULL;
|
||||
diff_tree_setup_paths(paths, &diff_opts);
|
||||
diff_setup_done(&diff_opts);
|
||||
diff_tree(t1, t2, base, &diff_opts);
|
||||
diffcore_std(&diff_opts);
|
||||
diff_tree_release_paths(&diff_opts);
|
||||
free_pathspec(&diff_opts.pathspec);
|
||||
|
||||
/* Go through the new set of filepairing, and see if we find a more interesting one */
|
||||
opt->found_follow = 0;
|
||||
@ -228,15 +243,18 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
|
||||
* the future!
|
||||
*/
|
||||
if ((p->status == 'R' || p->status == 'C') &&
|
||||
!strcmp(p->two->path, opt->pathspec.raw[0])) {
|
||||
!strcmp(p->two->path, opt->pathspec.items[0].match)) {
|
||||
const char *path[2];
|
||||
|
||||
/* Switch the file-pairs around */
|
||||
q->queue[i] = choice;
|
||||
choice = p;
|
||||
|
||||
/* Update the path we use from now on.. */
|
||||
diff_tree_release_paths(opt);
|
||||
opt->pathspec.raw[0] = xstrdup(p->one->path);
|
||||
diff_tree_setup_paths(opt->pathspec.raw, opt);
|
||||
path[0] = p->one->path;
|
||||
path[1] = NULL;
|
||||
free_pathspec(&opt->pathspec);
|
||||
parse_pathspec(&opt->pathspec, PATHSPEC_ALL_MAGIC, 0, "", path);
|
||||
|
||||
/*
|
||||
* The caller expects us to return a set of vanilla
|
||||
@ -310,13 +328,3 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_
|
||||
free(tree);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void diff_tree_release_paths(struct diff_options *opt)
|
||||
{
|
||||
free_pathspec(&opt->pathspec);
|
||||
}
|
||||
|
||||
void diff_tree_setup_paths(const char **p, struct diff_options *opt)
|
||||
{
|
||||
init_pathspec(&opt->pathspec, p);
|
||||
}
|
||||
|
78
tree-walk.c
78
tree-walk.c
@ -3,6 +3,7 @@
|
||||
#include "unpack-trees.h"
|
||||
#include "dir.h"
|
||||
#include "tree.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
static const char *get_mode(const char *str, unsigned int *modep)
|
||||
{
|
||||
@ -487,13 +488,25 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int match_entry(const struct name_entry *entry, int pathlen,
|
||||
static int match_entry(const struct pathspec_item *item,
|
||||
const struct name_entry *entry, int pathlen,
|
||||
const char *match, int matchlen,
|
||||
enum interesting *never_interesting)
|
||||
{
|
||||
int m = -1; /* signals that we haven't called strncmp() */
|
||||
|
||||
if (*never_interesting != entry_not_interesting) {
|
||||
if (item->magic & PATHSPEC_ICASE)
|
||||
/*
|
||||
* "Never interesting" trick requires exact
|
||||
* matching. We could do something clever with inexact
|
||||
* matching, but it's trickier (and not to forget that
|
||||
* strcasecmp is locale-dependent, at least in
|
||||
* glibc). Just disable it for now. It can't be worse
|
||||
* than the wildcard's codepath of '[Tt][Hi][Is][Ss]'
|
||||
* pattern.
|
||||
*/
|
||||
*never_interesting = entry_not_interesting;
|
||||
else if (*never_interesting != entry_not_interesting) {
|
||||
/*
|
||||
* We have not seen any match that sorts later
|
||||
* than the current path.
|
||||
@ -539,7 +552,7 @@ static int match_entry(const struct name_entry *entry, int pathlen,
|
||||
* we cheated and did not do strncmp(), so we do
|
||||
* that here.
|
||||
*/
|
||||
m = strncmp(match, entry->path, pathlen);
|
||||
m = ps_strncmp(item, match, entry->path, pathlen);
|
||||
|
||||
/*
|
||||
* If common part matched earlier then it is a hit,
|
||||
@ -547,15 +560,39 @@ static int match_entry(const struct name_entry *entry, int pathlen,
|
||||
* leading directory and is shorter than match.
|
||||
*/
|
||||
if (!m)
|
||||
/*
|
||||
* match_entry does not check if the prefix part is
|
||||
* matched case-sensitively. If the entry is a
|
||||
* directory and part of prefix, it'll be rematched
|
||||
* eventually by basecmp with special treatment for
|
||||
* the prefix.
|
||||
*/
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_dir_prefix(const char *base,
|
||||
/* :(icase)-aware string compare */
|
||||
static int basecmp(const struct pathspec_item *item,
|
||||
const char *base, const char *match, int len)
|
||||
{
|
||||
if (item->magic & PATHSPEC_ICASE) {
|
||||
int ret, n = len > item->prefix ? item->prefix : len;
|
||||
ret = strncmp(base, match, n);
|
||||
if (ret)
|
||||
return ret;
|
||||
base += n;
|
||||
match += n;
|
||||
len -= n;
|
||||
}
|
||||
return ps_strncmp(item, base, match, len);
|
||||
}
|
||||
|
||||
static int match_dir_prefix(const struct pathspec_item *item,
|
||||
const char *base,
|
||||
const char *match, int matchlen)
|
||||
{
|
||||
if (strncmp(base, match, matchlen))
|
||||
if (basecmp(item, base, match, matchlen))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -592,7 +629,7 @@ static int match_wildcard_base(const struct pathspec_item *item,
|
||||
*/
|
||||
if (baselen >= matchlen) {
|
||||
*matched = matchlen;
|
||||
return !strncmp(base, match, matchlen);
|
||||
return !basecmp(item, base, match, matchlen);
|
||||
}
|
||||
|
||||
dirlen = matchlen;
|
||||
@ -605,7 +642,7 @@ static int match_wildcard_base(const struct pathspec_item *item,
|
||||
* base ends with '/' so we are sure it really matches
|
||||
* directory
|
||||
*/
|
||||
if (strncmp(base, match, baselen))
|
||||
if (basecmp(item, base, match, baselen))
|
||||
return 0;
|
||||
*matched = baselen;
|
||||
} else
|
||||
@ -634,8 +671,17 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
|
||||
enum interesting never_interesting = ps->has_wildcard ?
|
||||
entry_not_interesting : all_entries_not_interesting;
|
||||
|
||||
GUARD_PATHSPEC(ps,
|
||||
PATHSPEC_FROMTOP |
|
||||
PATHSPEC_MAXDEPTH |
|
||||
PATHSPEC_LITERAL |
|
||||
PATHSPEC_GLOB |
|
||||
PATHSPEC_ICASE);
|
||||
|
||||
if (!ps->nr) {
|
||||
if (!ps->recursive || ps->max_depth == -1)
|
||||
if (!ps->recursive ||
|
||||
!(ps->magic & PATHSPEC_MAXDEPTH) ||
|
||||
ps->max_depth == -1)
|
||||
return all_entries_interesting;
|
||||
return within_depth(base->buf + base_offset, baselen,
|
||||
!!S_ISDIR(entry->mode),
|
||||
@ -653,10 +699,12 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
|
||||
|
||||
if (baselen >= matchlen) {
|
||||
/* If it doesn't match, move along... */
|
||||
if (!match_dir_prefix(base_str, match, matchlen))
|
||||
if (!match_dir_prefix(item, base_str, match, matchlen))
|
||||
goto match_wildcards;
|
||||
|
||||
if (!ps->recursive || ps->max_depth == -1)
|
||||
if (!ps->recursive ||
|
||||
!(ps->magic & PATHSPEC_MAXDEPTH) ||
|
||||
ps->max_depth == -1)
|
||||
return all_entries_interesting;
|
||||
|
||||
return within_depth(base_str + matchlen + 1,
|
||||
@ -667,15 +715,14 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
|
||||
}
|
||||
|
||||
/* Either there must be no base, or the base must match. */
|
||||
if (baselen == 0 || !strncmp(base_str, match, baselen)) {
|
||||
if (match_entry(entry, pathlen,
|
||||
if (baselen == 0 || !basecmp(item, base_str, match, baselen)) {
|
||||
if (match_entry(item, entry, pathlen,
|
||||
match + baselen, matchlen - baselen,
|
||||
&never_interesting))
|
||||
return entry_interesting;
|
||||
|
||||
if (item->nowildcard_len < item->len) {
|
||||
if (!git_fnmatch(match + baselen, entry->path,
|
||||
item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0,
|
||||
if (!git_fnmatch(item, match + baselen, entry->path,
|
||||
item->nowildcard_len - baselen))
|
||||
return entry_interesting;
|
||||
|
||||
@ -716,8 +763,7 @@ match_wildcards:
|
||||
|
||||
strbuf_add(base, entry->path, pathlen);
|
||||
|
||||
if (!git_fnmatch(match, base->buf + base_offset,
|
||||
item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0,
|
||||
if (!git_fnmatch(item, match, base->buf + base_offset,
|
||||
item->nowildcard_len)) {
|
||||
strbuf_setlen(base, base_offset + baselen);
|
||||
return entry_interesting;
|
||||
|
4
tree.c
4
tree.c
@ -47,7 +47,7 @@ static int read_one_entry_quick(const unsigned char *sha1, const char *base, int
|
||||
}
|
||||
|
||||
static int read_tree_1(struct tree *tree, struct strbuf *base,
|
||||
int stage, struct pathspec *pathspec,
|
||||
int stage, const struct pathspec *pathspec,
|
||||
read_tree_fn_t fn, void *context)
|
||||
{
|
||||
struct tree_desc desc;
|
||||
@ -116,7 +116,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
|
||||
|
||||
int read_tree_recursive(struct tree *tree,
|
||||
const char *base, int baselen,
|
||||
int stage, struct pathspec *pathspec,
|
||||
int stage, const struct pathspec *pathspec,
|
||||
read_tree_fn_t fn, void *context)
|
||||
{
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
2
tree.h
2
tree.h
@ -25,7 +25,7 @@ typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const ch
|
||||
|
||||
extern int read_tree_recursive(struct tree *tree,
|
||||
const char *base, int baselen,
|
||||
int stage, struct pathspec *pathspec,
|
||||
int stage, const struct pathspec *pathspec,
|
||||
read_tree_fn_t fn, void *context);
|
||||
|
||||
extern int read_tree(struct tree *tree, int stage, struct pathspec *pathspec);
|
||||
|
16
wt-status.c
16
wt-status.c
@ -1,4 +1,5 @@
|
||||
#include "cache.h"
|
||||
#include "pathspec.h"
|
||||
#include "wt-status.h"
|
||||
#include "object.h"
|
||||
#include "dir.h"
|
||||
@ -438,7 +439,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
|
||||
}
|
||||
rev.diffopt.format_callback = wt_status_collect_changed_cb;
|
||||
rev.diffopt.format_callback_data = s;
|
||||
init_pathspec(&rev.prune_data, s->pathspec);
|
||||
copy_pathspec(&rev.prune_data, &s->pathspec);
|
||||
run_diff_files(&rev, 0);
|
||||
}
|
||||
|
||||
@ -463,22 +464,20 @@ static void wt_status_collect_changes_index(struct wt_status *s)
|
||||
rev.diffopt.detect_rename = 1;
|
||||
rev.diffopt.rename_limit = 200;
|
||||
rev.diffopt.break_opt = 0;
|
||||
init_pathspec(&rev.prune_data, s->pathspec);
|
||||
copy_pathspec(&rev.prune_data, &s->pathspec);
|
||||
run_diff_index(&rev, 1);
|
||||
}
|
||||
|
||||
static void wt_status_collect_changes_initial(struct wt_status *s)
|
||||
{
|
||||
struct pathspec pathspec;
|
||||
int i;
|
||||
|
||||
init_pathspec(&pathspec, s->pathspec);
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct string_list_item *it;
|
||||
struct wt_status_change_data *d;
|
||||
const struct cache_entry *ce = active_cache[i];
|
||||
|
||||
if (!ce_path_match(ce, &pathspec))
|
||||
if (!ce_path_match(ce, &s->pathspec))
|
||||
continue;
|
||||
it = string_list_insert(&s->change, ce->name);
|
||||
d = it->util;
|
||||
@ -493,7 +492,6 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
|
||||
else
|
||||
d->index_status = DIFF_STATUS_ADDED;
|
||||
}
|
||||
free_pathspec(&pathspec);
|
||||
}
|
||||
|
||||
static void wt_status_collect_untracked(struct wt_status *s)
|
||||
@ -516,12 +514,12 @@ static void wt_status_collect_untracked(struct wt_status *s)
|
||||
dir.flags |= DIR_SHOW_IGNORED_TOO;
|
||||
setup_standard_excludes(&dir);
|
||||
|
||||
fill_directory(&dir, s->pathspec);
|
||||
fill_directory(&dir, &s->pathspec);
|
||||
|
||||
for (i = 0; i < dir.nr; i++) {
|
||||
struct dir_entry *ent = dir.entries[i];
|
||||
if (cache_name_is_other(ent->name, ent->len) &&
|
||||
match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
|
||||
match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
|
||||
string_list_insert(&s->untracked, ent->name);
|
||||
free(ent);
|
||||
}
|
||||
@ -529,7 +527,7 @@ static void wt_status_collect_untracked(struct wt_status *s)
|
||||
for (i = 0; i < dir.ignored_nr; i++) {
|
||||
struct dir_entry *ent = dir.ignored[i];
|
||||
if (cache_name_is_other(ent->name, ent->len) &&
|
||||
match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
|
||||
match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
|
||||
string_list_insert(&s->ignored, ent->name);
|
||||
free(ent);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ struct wt_status {
|
||||
int is_initial;
|
||||
char *branch;
|
||||
const char *reference;
|
||||
const char **pathspec;
|
||||
struct pathspec pathspec;
|
||||
int verbose;
|
||||
int amend;
|
||||
enum commit_whence whence;
|
||||
|
Loading…
Reference in New Issue
Block a user