mirror of
https://github.com/git/git.git
synced 2026-03-14 10:53:25 +01:00
Merge branch 'jc/rm' into next
* jc/rm: git-add: add ignored files when asked explicitly. read_directory: show_both option. git-rm: Documentation t3600: update the test for updated git rm git-rm: update to saner semantics match_pathspec() -- return how well the spec matched git-add --interactive: add documentation
This commit is contained in:
@@ -7,7 +7,7 @@ git-add - Add file contents to the changeset to be committed next
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-add' [-n] [-v] [--] <file>...
|
||||
'git-add' [-n] [-v] [--interactive] [--] <file>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -25,8 +25,9 @@ the commit.
|
||||
The 'git status' command can be used to obtain a summary of what is included
|
||||
for the next commit.
|
||||
|
||||
This command only adds non-ignored files, to add ignored files use
|
||||
"git update-index --add".
|
||||
This command can be used to add ignored files, but they have to be
|
||||
explicitly and exactly specified from the command line. File globbing
|
||||
and recursive behaviour do not add ignored files.
|
||||
|
||||
Please see gitlink:git-commit[1] for alternative ways to add content to a
|
||||
commit.
|
||||
@@ -35,7 +36,11 @@ commit.
|
||||
OPTIONS
|
||||
-------
|
||||
<file>...::
|
||||
Files to add content from.
|
||||
Files to add content from. Fileglobs (e.g. `*.c`) can
|
||||
be given to add all matching files. Also a
|
||||
leading directory name (e.g. `dir` to add `dir/file1`
|
||||
and `dir/file2`) can be given to add all files in the
|
||||
directory, recursively.
|
||||
|
||||
-n::
|
||||
Don't actually add the file(s), just show if they exist.
|
||||
@@ -43,6 +48,10 @@ OPTIONS
|
||||
-v::
|
||||
Be verbose.
|
||||
|
||||
\--interactive::
|
||||
Add modified contents in the working tree interactively to
|
||||
the index.
|
||||
|
||||
\--::
|
||||
This option can be used to separate command-line options from
|
||||
the list of files, (useful when filenames might be mistaken
|
||||
@@ -67,6 +76,119 @@ git-add git-*.sh::
|
||||
(i.e. you are listing the files explicitly), it does not
|
||||
consider `subdir/git-foo.sh`.
|
||||
|
||||
Interactive mode
|
||||
----------------
|
||||
When the command enters the interactive mode, it shows the
|
||||
output of the 'status' subcommand, and then goes into ints
|
||||
interactive command loop.
|
||||
|
||||
The command loop shows the list of subcommands available, and
|
||||
gives a prompt "What now> ". In general, when the prompt ends
|
||||
with a single '>', you can pick only one of the choices given
|
||||
and type return, like this:
|
||||
|
||||
------------
|
||||
*** Commands ***
|
||||
1: status 2: update 3: revert 4: add untracked
|
||||
5: patch 6: diff 7: quit 8: help
|
||||
What now> 1
|
||||
------------
|
||||
|
||||
You also could say "s" or "sta" or "status" above as long as the
|
||||
choice is unique.
|
||||
|
||||
The main command loop has 6 subcommands (plus help and quit).
|
||||
|
||||
status::
|
||||
|
||||
This shows the change between HEAD and index (i.e. what will be
|
||||
committed if you say "git commit"), and between index and
|
||||
working tree files (i.e. what you could stage further before
|
||||
"git commit" using "git-add") for each path. A sample output
|
||||
looks like this:
|
||||
+
|
||||
------------
|
||||
staged unstaged path
|
||||
1: binary nothing foo.png
|
||||
2: +403/-35 +1/-1 git-add--interactive.perl
|
||||
------------
|
||||
+
|
||||
It shows that foo.png has differences from HEAD (but that is
|
||||
binary so line count cannot be shown) and there is no
|
||||
difference between indexed copy and the working tree
|
||||
version (if the working tree version were also different,
|
||||
'binary' would have been shown in place of 'nothing'). The
|
||||
other file, git-add--interactive.perl, has 403 lines added
|
||||
and 35 lines deleted if you commit what is in the index, but
|
||||
working tree file has further modifications (one addition and
|
||||
one deletion).
|
||||
|
||||
update::
|
||||
|
||||
This shows the status information and gives prompt
|
||||
"Update>>". When the prompt ends with double '>>', you can
|
||||
make more than one selection, concatenated with whitespace or
|
||||
comma. Also you can say ranges. E.g. "2-5 7,9" to choose
|
||||
2,3,4,5,7,9 from the list. You can say '*' to choose
|
||||
everything.
|
||||
+
|
||||
What you chose are then highlighted with '*',
|
||||
like this:
|
||||
+
|
||||
------------
|
||||
staged unstaged path
|
||||
1: binary nothing foo.png
|
||||
* 2: +403/-35 +1/-1 git-add--interactive.perl
|
||||
------------
|
||||
+
|
||||
To remove selection, prefix the input with `-`
|
||||
like this:
|
||||
+
|
||||
------------
|
||||
Update>> -2
|
||||
------------
|
||||
+
|
||||
After making the selection, answer with an empty line to stage the
|
||||
contents of working tree files for selected paths in the index.
|
||||
|
||||
revert::
|
||||
|
||||
This has a very similar UI to 'update', and the staged
|
||||
information for selected paths are reverted to that of the
|
||||
HEAD version. Reverting new paths makes them untracked.
|
||||
|
||||
add untracked::
|
||||
|
||||
This has a very similar UI to 'update' and
|
||||
'revert', and lets you add untracked paths to the index.
|
||||
|
||||
patch::
|
||||
|
||||
This lets you choose one path out of 'status' like selection.
|
||||
After choosing the path, it presents diff between the index
|
||||
and the working tree file and asks you if you want to stage
|
||||
the change of each hunk. You can say:
|
||||
|
||||
y - add the change from that hunk to index
|
||||
n - do not add the change from that hunk to index
|
||||
a - add the change from that hunk and all the rest to index
|
||||
d - do not the change from that hunk nor any of the rest to index
|
||||
j - do not decide on this hunk now, and view the next
|
||||
undecided hunk
|
||||
J - do not decide on this hunk now, and view the next hunk
|
||||
k - do not decide on this hunk now, and view the previous
|
||||
undecided hunk
|
||||
K - do not decide on this hunk now, and view the previous hunk
|
||||
+
|
||||
After deciding the fate for all hunks, if there is any hunk
|
||||
that was chosen, the index is updated with the selected hunks.
|
||||
|
||||
diff::
|
||||
|
||||
This lets you review what will be committed (i.e. between
|
||||
HEAD and index).
|
||||
|
||||
|
||||
See Also
|
||||
--------
|
||||
gitlink:git-status[1]
|
||||
|
||||
@@ -7,51 +7,54 @@ git-rm - Remove files from the working tree and from the index
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-rm' [-f] [-n] [-v] [--] <file>...
|
||||
'git-rm' [-f] [-n] [-r] [--cached] [--] <file>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
A convenience wrapper for git-update-index --remove. For those coming
|
||||
from cvs, git-rm provides an operation similar to "cvs rm" or "cvs
|
||||
remove".
|
||||
Remove files from the working tree and from the index. The
|
||||
files have to be identical to the tip of the branch, and no
|
||||
updates to its contents must have been placed in the staging
|
||||
area (aka index).
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
<file>...::
|
||||
Files to remove from the index and optionally, from the
|
||||
working tree as well.
|
||||
Files to remove. Fileglobs (e.g. `*.c`) can be given to
|
||||
remove all matching files. Also a leading directory name
|
||||
(e.g. `dir` to add `dir/file1` and `dir/file2`) can be
|
||||
given to remove all files in the directory, recursively,
|
||||
but this requires `-r` option to be given for safety.
|
||||
|
||||
-f::
|
||||
Remove files from the working tree as well as from the index.
|
||||
Override the up-to-date check.
|
||||
|
||||
-n::
|
||||
Don't actually remove the file(s), just show if they exist in
|
||||
the index.
|
||||
|
||||
-v::
|
||||
Be verbose.
|
||||
-r::
|
||||
Allow recursive removal when a leading directory name is
|
||||
given.
|
||||
|
||||
\--::
|
||||
This option can be used to separate command-line options from
|
||||
the list of files, (useful when filenames might be mistaken
|
||||
for command-line options).
|
||||
|
||||
\--cached::
|
||||
This option can be used to tell the command to remove
|
||||
the paths only from the index, leaving working tree
|
||||
files.
|
||||
|
||||
|
||||
DISCUSSION
|
||||
----------
|
||||
|
||||
The list of <file> given to the command is fed to `git-ls-files`
|
||||
command to list files that are registered in the index and
|
||||
are not ignored/excluded by `$GIT_DIR/info/exclude` file or
|
||||
`.gitignore` file in each directory. This means two things:
|
||||
|
||||
. You can put the name of a directory on the command line, and the
|
||||
command will remove all files in it and its subdirectories (the
|
||||
directories themselves are never removed from the working tree);
|
||||
|
||||
. Giving the name of a file that is not in the index does not
|
||||
remove that file.
|
||||
The list of <file> given to the command can be exact pathnames,
|
||||
file glob patterns, or leading directory name. The command
|
||||
removes only the paths that is known to git. Giving the name of
|
||||
a file that you have not told git about does not remove that file.
|
||||
|
||||
|
||||
EXAMPLES
|
||||
@@ -69,10 +72,10 @@ subdirectories of `Documentation/` directory.
|
||||
git-rm -f git-*.sh::
|
||||
|
||||
Remove all git-*.sh scripts that are in the index. The files
|
||||
are removed from the index, and (because of the -f option),
|
||||
from the working tree as well. Because this example lets the
|
||||
shell expand the asterisk (i.e. you are listing the files
|
||||
explicitly), it does not remove `subdir/git-foo.sh`.
|
||||
are removed from the index, and from the working
|
||||
tree. Because this example lets the shell expand the
|
||||
asterisk (i.e. you are listing the files explicitly), it
|
||||
does not remove `subdir/git-foo.sh`.
|
||||
|
||||
See Also
|
||||
--------
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "cache-tree.h"
|
||||
|
||||
static const char builtin_add_usage[] =
|
||||
"git-add [-n] [-v] <filepattern>...";
|
||||
"git-add [-n] [-v] [--interactive] [--] <filepattern>...";
|
||||
|
||||
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
|
||||
{
|
||||
@@ -26,7 +26,14 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
|
||||
i = dir->nr;
|
||||
while (--i >= 0) {
|
||||
struct dir_entry *entry = *src++;
|
||||
if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) {
|
||||
int how = match_pathspec(pathspec, entry->name, entry->len,
|
||||
prefix, seen);
|
||||
/*
|
||||
* ignored entries can be added with exact match,
|
||||
* but not with glob nor recursive.
|
||||
*/
|
||||
if (!how ||
|
||||
(entry->ignored_entry && how != MATCHED_EXACTLY)) {
|
||||
free(entry);
|
||||
continue;
|
||||
}
|
||||
@@ -55,6 +62,8 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec)
|
||||
|
||||
/* Set up the default git porcelain excludes */
|
||||
memset(dir, 0, sizeof(*dir));
|
||||
if (pathspec)
|
||||
dir->show_both = 1;
|
||||
dir->exclude_per_dir = ".gitignore";
|
||||
path = git_path("info/exclude");
|
||||
if (!access(path, R_OK))
|
||||
|
||||
123
builtin-rm.c
123
builtin-rm.c
@@ -7,9 +7,10 @@
|
||||
#include "builtin.h"
|
||||
#include "dir.h"
|
||||
#include "cache-tree.h"
|
||||
#include "tree-walk.h"
|
||||
|
||||
static const char builtin_rm_usage[] =
|
||||
"git-rm [-n] [-v] [-f] <filepattern>...";
|
||||
"git-rm [-n] [-f] [--cached] <filepattern>...";
|
||||
|
||||
static struct {
|
||||
int nr, alloc;
|
||||
@@ -41,12 +42,75 @@ static int remove_file(const char *name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_local_mod(unsigned char *head)
|
||||
{
|
||||
/* items in list are already sorted in the cache order,
|
||||
* so we could do this a lot more efficiently by using
|
||||
* tree_desc based traversal if we wanted to, but I am
|
||||
* lazy, and who cares if removal of files is a tad
|
||||
* slower than the theoretical maximum speed?
|
||||
*/
|
||||
int i, no_head;
|
||||
int errs = 0;
|
||||
|
||||
no_head = is_null_sha1(head);
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
struct stat st;
|
||||
int pos;
|
||||
struct cache_entry *ce;
|
||||
const char *name = list.name[i];
|
||||
unsigned char sha1[20];
|
||||
unsigned mode;
|
||||
|
||||
pos = cache_name_pos(name, strlen(name));
|
||||
if (pos < 0)
|
||||
continue; /* removing unmerged entry */
|
||||
ce = active_cache[pos];
|
||||
|
||||
if (lstat(ce->name, &st) < 0) {
|
||||
if (errno != ENOENT)
|
||||
fprintf(stderr, "warning: '%s': %s",
|
||||
ce->name, strerror(errno));
|
||||
/* It already vanished from the working tree */
|
||||
continue;
|
||||
}
|
||||
else if (S_ISDIR(st.st_mode)) {
|
||||
/* if a file was removed and it is now a
|
||||
* directory, that is the same as ENOENT as
|
||||
* far as git is concerned; we do not track
|
||||
* directories.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
if (ce_match_stat(ce, &st, 0))
|
||||
errs = error("'%s' has local modifications "
|
||||
"(hint: try -f)", ce->name);
|
||||
if (no_head)
|
||||
continue;
|
||||
/*
|
||||
* It is Ok to remove a newly added path, as long as
|
||||
* it is cache-clean.
|
||||
*/
|
||||
if (get_tree_entry(head, name, sha1, &mode))
|
||||
continue;
|
||||
/*
|
||||
* Otherwise make sure the version from the HEAD
|
||||
* matches the index.
|
||||
*/
|
||||
if (ce->ce_mode != create_ce_mode(mode) ||
|
||||
hashcmp(ce->sha1, sha1))
|
||||
errs = error("'%s' has changes staged in the index "
|
||||
"(hint: try -f)", name);
|
||||
}
|
||||
return errs;
|
||||
}
|
||||
|
||||
static struct lock_file lock_file;
|
||||
|
||||
int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, newfd;
|
||||
int verbose = 0, show_only = 0, force = 0;
|
||||
int show_only = 0, force = 0, index_only = 0, recursive = 0;
|
||||
const char **pathspec;
|
||||
char *seen;
|
||||
|
||||
@@ -62,23 +126,20 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (*arg != '-')
|
||||
break;
|
||||
if (!strcmp(arg, "--")) {
|
||||
else if (!strcmp(arg, "--")) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(arg, "-n")) {
|
||||
else if (!strcmp(arg, "-n"))
|
||||
show_only = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-v")) {
|
||||
verbose = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-f")) {
|
||||
else if (!strcmp(arg, "--cached"))
|
||||
index_only = 1;
|
||||
else if (!strcmp(arg, "-f"))
|
||||
force = 1;
|
||||
continue;
|
||||
}
|
||||
usage(builtin_rm_usage);
|
||||
else if (!strcmp(arg, "-r"))
|
||||
recursive = 1;
|
||||
else
|
||||
usage(builtin_rm_usage);
|
||||
}
|
||||
if (argc <= i)
|
||||
usage(builtin_rm_usage);
|
||||
@@ -99,14 +160,36 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
if (pathspec) {
|
||||
const char *match;
|
||||
for (i = 0; (match = pathspec[i]) != NULL ; i++) {
|
||||
if (*match && !seen[i])
|
||||
die("pathspec '%s' did not match any files", match);
|
||||
if (!seen[i])
|
||||
die("pathspec '%s' did not match any files",
|
||||
match);
|
||||
if (!recursive && seen[i] == MATCHED_RECURSIVELY)
|
||||
die("not removing '%s' recursively without -r",
|
||||
*match ? match : ".");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If not forced, the file, the index and the HEAD (if exists)
|
||||
* must match; but the file can already been removed, since
|
||||
* this sequence is a natural "novice" way:
|
||||
*
|
||||
* rm F; git fm F
|
||||
*
|
||||
* Further, if HEAD commit exists, "diff-index --cached" must
|
||||
* report no changes unless forced.
|
||||
*/
|
||||
if (!force) {
|
||||
unsigned char sha1[20];
|
||||
if (get_sha1("HEAD", sha1))
|
||||
hashclr(sha1);
|
||||
if (check_local_mod(sha1))
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* First remove the names from the index: we won't commit
|
||||
* the index unless all of them succeed
|
||||
* the index unless all of them succeed.
|
||||
*/
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
const char *path = list.name[i];
|
||||
@@ -121,14 +204,14 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Then, if we used "-f", remove the filenames from the
|
||||
* workspace. If we fail to remove the first one, we
|
||||
* Then, unless we used "--cache", remove the filenames from
|
||||
* the workspace. If we fail to remove the first one, we
|
||||
* abort the "git rm" (but once we've successfully removed
|
||||
* any file at all, we'll go ahead and commit to it all:
|
||||
* by then we've already committed ourselves and can't fail
|
||||
* in the middle)
|
||||
*/
|
||||
if (force) {
|
||||
if (!index_only) {
|
||||
int removed = 0;
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
const char *path = list.name[i];
|
||||
|
||||
70
dir.c
70
dir.c
@@ -40,6 +40,18 @@ int common_prefix(const char **pathspec)
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does 'match' matches 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;
|
||||
@@ -47,27 +59,30 @@ static int match_one(const char *match, const char *name, int namelen)
|
||||
/* If the match was just the prefix, we matched */
|
||||
matchlen = strlen(match);
|
||||
if (!matchlen)
|
||||
return 1;
|
||||
return MATCHED_RECURSIVELY;
|
||||
|
||||
/*
|
||||
* If we don't match the matchstring exactly,
|
||||
* we need to match by fnmatch
|
||||
*/
|
||||
if (strncmp(match, name, matchlen))
|
||||
return !fnmatch(match, name, 0);
|
||||
return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
|
||||
|
||||
/*
|
||||
* If we did match the string exactly, we still
|
||||
* need to make sure that it happened on a path
|
||||
* component boundary (ie either the last character
|
||||
* of the match was '/', or the next character of
|
||||
* the name was '/' or the terminating NUL.
|
||||
*/
|
||||
return match[matchlen-1] == '/' ||
|
||||
name[matchlen] == '/' ||
|
||||
!name[matchlen];
|
||||
if (!name[matchlen])
|
||||
return MATCHED_EXACTLY;
|
||||
if (match[matchlen-1] == '/' || name[matchlen] == '/')
|
||||
return MATCHED_RECURSIVELY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a name and a list of pathspecs, see if the name matches
|
||||
* any of the pathspecs. The caller is also interested in seeing
|
||||
* all pathspec matches some names it calls this function with
|
||||
* (otherwise the user could have mistyped the unmatched pathspec),
|
||||
* and a mark is left in seen[] array for pathspec element that
|
||||
* actually matched anything.
|
||||
*/
|
||||
int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
|
||||
{
|
||||
int retval;
|
||||
@@ -77,12 +92,16 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, int pre
|
||||
namelen -= prefix;
|
||||
|
||||
for (retval = 0; (match = *pathspec++) != NULL; seen++) {
|
||||
if (retval & *seen)
|
||||
int how;
|
||||
if (retval && *seen == MATCHED_EXACTLY)
|
||||
continue;
|
||||
match += prefix;
|
||||
if (match_one(match, name, namelen)) {
|
||||
retval = 1;
|
||||
*seen = 1;
|
||||
how = match_one(match, name, namelen);
|
||||
if (how) {
|
||||
if (retval < how)
|
||||
retval = how;
|
||||
if (*seen < how)
|
||||
*seen = how;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
@@ -241,7 +260,8 @@ int excluded(struct dir_struct *dir, const char *pathname)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_name(struct dir_struct *dir, const char *pathname, int len)
|
||||
static void add_name(struct dir_struct *dir, const char *pathname, int len,
|
||||
int ignored_entry)
|
||||
{
|
||||
struct dir_entry *ent;
|
||||
|
||||
@@ -254,6 +274,7 @@ static void add_name(struct dir_struct *dir, const char *pathname, int len)
|
||||
dir->entries = xrealloc(dir->entries, alloc*sizeof(ent));
|
||||
}
|
||||
ent = xmalloc(sizeof(*ent) + len + 1);
|
||||
ent->ignored_entry = ignored_entry;
|
||||
ent->len = len;
|
||||
memcpy(ent->name, pathname, len);
|
||||
ent->name[len] = 0;
|
||||
@@ -295,6 +316,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
|
||||
|
||||
while ((de = readdir(fdir)) != NULL) {
|
||||
int len;
|
||||
int ignored_entry;
|
||||
|
||||
if ((de->d_name[0] == '.') &&
|
||||
(de->d_name[1] == 0 ||
|
||||
@@ -303,11 +325,12 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
|
||||
continue;
|
||||
len = strlen(de->d_name);
|
||||
memcpy(fullname + baselen, de->d_name, len+1);
|
||||
if (excluded(dir, fullname) != dir->show_ignored) {
|
||||
if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ignored_entry = excluded(dir, fullname);
|
||||
|
||||
if (!dir->show_both &&
|
||||
(ignored_entry != dir->show_ignored) &&
|
||||
(!dir->show_ignored || DTYPE(de) != DT_DIR))
|
||||
continue;
|
||||
|
||||
switch (DTYPE(de)) {
|
||||
struct stat st;
|
||||
@@ -345,7 +368,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
|
||||
if (check_only)
|
||||
goto exit_early;
|
||||
else
|
||||
add_name(dir, fullname, baselen + len);
|
||||
add_name(dir, fullname, baselen + len,
|
||||
ignored_entry);
|
||||
}
|
||||
exit_early:
|
||||
closedir(fdir);
|
||||
|
||||
10
dir.h
10
dir.h
@@ -13,7 +13,8 @@
|
||||
|
||||
|
||||
struct dir_entry {
|
||||
int len;
|
||||
unsigned ignored_entry : 1;
|
||||
unsigned int len : 15;
|
||||
char name[FLEX_ARRAY]; /* more */
|
||||
};
|
||||
|
||||
@@ -29,7 +30,8 @@ struct exclude_list {
|
||||
|
||||
struct dir_struct {
|
||||
int nr, alloc;
|
||||
unsigned int show_ignored:1,
|
||||
unsigned int show_both: 1,
|
||||
show_ignored:1,
|
||||
show_other_directories:1,
|
||||
hide_empty_directories:1;
|
||||
struct dir_entry **entries;
|
||||
@@ -40,6 +42,10 @@ struct dir_struct {
|
||||
};
|
||||
|
||||
extern int common_prefix(const char **pathspec);
|
||||
|
||||
#define MATCHED_RECURSIVELY 1
|
||||
#define MATCHED_FNMATCH 2
|
||||
#define MATCHED_EXACTLY 3
|
||||
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
|
||||
|
||||
extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);
|
||||
|
||||
@@ -43,19 +43,19 @@ test_expect_success \
|
||||
|
||||
test_expect_success \
|
||||
'Test that git-rm foo succeeds' \
|
||||
'git-rm foo'
|
||||
'git-rm --cached foo'
|
||||
|
||||
test_expect_success \
|
||||
'Post-check that foo exists but is not in index after git-rm foo' \
|
||||
'[ -f foo ] && ! git-ls-files --error-unmatch foo'
|
||||
|
||||
test_expect_success \
|
||||
'Pre-check that bar exists and is in index before "git-rm -f bar"' \
|
||||
'Pre-check that bar exists and is in index before "git-rm bar"' \
|
||||
'[ -f bar ] && git-ls-files --error-unmatch bar'
|
||||
|
||||
test_expect_success \
|
||||
'Test that "git-rm -f bar" succeeds' \
|
||||
'git-rm -f bar'
|
||||
'Test that "git-rm bar" succeeds' \
|
||||
'git-rm bar'
|
||||
|
||||
test_expect_success \
|
||||
'Post-check that bar does not exist and is not in index after "git-rm -f bar"' \
|
||||
@@ -84,4 +84,74 @@ test_expect_success \
|
||||
'When the rm in "git-rm -f" fails, it should not remove the file from the index' \
|
||||
'git-ls-files --error-unmatch baz'
|
||||
|
||||
# Now, failure cases.
|
||||
test_expect_success 'Re-add foo and baz' '
|
||||
git add foo baz &&
|
||||
git ls-files --error-unmatch foo baz
|
||||
'
|
||||
|
||||
test_expect_success 'Modify foo -- rm should refuse' '
|
||||
echo >>foo &&
|
||||
! git rm foo baz &&
|
||||
test -f foo &&
|
||||
test -f baz &&
|
||||
git ls-files --error-unmatch foo baz
|
||||
'
|
||||
|
||||
test_expect_success 'Modified foo -- rm -f should work' '
|
||||
git rm -f foo baz &&
|
||||
test ! -f foo &&
|
||||
test ! -f baz &&
|
||||
! git ls-files --error-unmatch foo &&
|
||||
! git ls-files --error-unmatch bar
|
||||
'
|
||||
|
||||
test_expect_success 'Re-add foo and baz for HEAD tests' '
|
||||
echo frotz >foo &&
|
||||
git checkout HEAD -- baz &&
|
||||
git add foo baz &&
|
||||
git ls-files --error-unmatch foo baz
|
||||
'
|
||||
|
||||
test_expect_success 'foo is different in index from HEAD -- rm should refuse' '
|
||||
! git rm foo baz &&
|
||||
test -f foo &&
|
||||
test -f baz &&
|
||||
git ls-files --error-unmatch foo baz
|
||||
'
|
||||
|
||||
test_expect_success 'but with -f it should work.' '
|
||||
git rm -f foo baz &&
|
||||
test ! -f foo &&
|
||||
test ! -f baz &&
|
||||
! git ls-files --error-unmatch foo
|
||||
! git ls-files --error-unmatch baz
|
||||
'
|
||||
|
||||
test_expect_success 'Recursive test setup' '
|
||||
mkdir -p frotz &&
|
||||
echo qfwfq >frotz/nitfol &&
|
||||
git add frotz &&
|
||||
git commit -m "subdir test"
|
||||
'
|
||||
|
||||
test_expect_success 'Recursive without -r fails' '
|
||||
! git rm frotz &&
|
||||
test -d frotz &&
|
||||
test -f frotz/nitfol
|
||||
'
|
||||
|
||||
test_expect_success 'Recursive with -r but dirty' '
|
||||
echo qfwfq >>frotz/nitfol
|
||||
! git rm -r frotz &&
|
||||
test -d frotz &&
|
||||
test -f frotz/nitfol
|
||||
'
|
||||
|
||||
test_expect_success 'Recursive with -r -f' '
|
||||
git rm -f -r frotz &&
|
||||
! test -f frotz/nitfol &&
|
||||
! test -d frotz
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
Reference in New Issue
Block a user