Merge branch 'ar/add-unreadable'
* ar/add-unreadable: Add a config option to ignore errors for git-add Add a test for git-add --ignore-errors Add --ignore-errors to git-add to allow it to skip files with read errors Extend interface of add_files_to_cache to allow ignore indexing errors Make the exit code of add_file_to_index actually useful
This commit is contained in:
commit
9d880582ee
@ -9,7 +9,7 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-add' [-n] [-v] [-f] [--interactive | -i] [--patch | -p] [-u] [--refresh]
|
||||
[--] <filepattern>...
|
||||
[--ignore-errors] [--] <filepattern>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -83,6 +83,11 @@ OPTIONS
|
||||
Don't add the file(s), but only refresh their stat()
|
||||
information in the index.
|
||||
|
||||
\--ignore-errors::
|
||||
If some files could not be added because of errors indexing
|
||||
them, do not abort the operation, but continue adding the
|
||||
others. The command shall still exit with non-zero status.
|
||||
|
||||
\--::
|
||||
This option can be used to separate command-line options from
|
||||
the list of files, (useful when filenames might be mistaken
|
||||
|
@ -79,12 +79,18 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
|
||||
prune_directory(dir, pathspec, baselen);
|
||||
}
|
||||
|
||||
struct update_callback_data
|
||||
{
|
||||
int flags;
|
||||
int add_errors;
|
||||
};
|
||||
|
||||
static void update_callback(struct diff_queue_struct *q,
|
||||
struct diff_options *opt, void *cbdata)
|
||||
{
|
||||
int i, verbose;
|
||||
int i;
|
||||
struct update_callback_data *data = cbdata;
|
||||
|
||||
verbose = *((int *)cbdata);
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
const char *path = p->one->path;
|
||||
@ -94,27 +100,35 @@ static void update_callback(struct diff_queue_struct *q,
|
||||
case DIFF_STATUS_UNMERGED:
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
case DIFF_STATUS_TYPE_CHANGED:
|
||||
add_file_to_cache(path, verbose);
|
||||
if (add_file_to_cache(path, data->flags & ADD_FILES_VERBOSE)) {
|
||||
if (!(data->flags & ADD_FILES_IGNORE_ERRORS))
|
||||
die("updating files failed");
|
||||
data->add_errors++;
|
||||
}
|
||||
break;
|
||||
case DIFF_STATUS_DELETED:
|
||||
remove_file_from_cache(path);
|
||||
if (verbose)
|
||||
if (data->flags & ADD_FILES_VERBOSE)
|
||||
printf("remove '%s'\n", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_files_to_cache(int verbose, const char *prefix, const char **pathspec)
|
||||
int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
|
||||
{
|
||||
struct update_callback_data data;
|
||||
struct rev_info rev;
|
||||
init_revisions(&rev, prefix);
|
||||
setup_revisions(0, NULL, &rev, NULL);
|
||||
rev.prune_data = pathspec;
|
||||
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
rev.diffopt.format_callback = update_callback;
|
||||
rev.diffopt.format_callback_data = &verbose;
|
||||
data.flags = flags;
|
||||
data.add_errors = 0;
|
||||
rev.diffopt.format_callback_data = &data;
|
||||
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
|
||||
return !!data.add_errors;
|
||||
}
|
||||
|
||||
static void refresh(int verbose, const char **pathspec)
|
||||
@ -177,6 +191,7 @@ static const char ignore_error[] =
|
||||
"The following paths are ignored by one of your .gitignore files:\n";
|
||||
|
||||
static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
|
||||
static int ignore_add_errors;
|
||||
|
||||
static struct option builtin_add_options[] = {
|
||||
OPT__DRY_RUN(&show_only),
|
||||
@ -187,11 +202,22 @@ static struct option builtin_add_options[] = {
|
||||
OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"),
|
||||
OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"),
|
||||
OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
|
||||
OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
static int add_config(const char *var, const char *value)
|
||||
{
|
||||
if (!strcasecmp(var, "add.ignore-errors")) {
|
||||
ignore_add_errors = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int exit_status = 0;
|
||||
int i, newfd;
|
||||
const char **pathspec;
|
||||
struct dir_struct dir;
|
||||
@ -203,16 +229,23 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
if (add_interactive)
|
||||
exit(interactive_add(argc, argv, prefix));
|
||||
|
||||
git_config(git_default_config);
|
||||
git_config(add_config);
|
||||
|
||||
newfd = hold_locked_index(&lock_file, 1);
|
||||
|
||||
if (take_worktree_changes) {
|
||||
int flags = 0;
|
||||
const char **pathspec;
|
||||
if (read_cache() < 0)
|
||||
die("index file corrupt");
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
add_files_to_cache(verbose, prefix, pathspec);
|
||||
|
||||
if (verbose)
|
||||
flags |= ADD_FILES_VERBOSE;
|
||||
if (ignore_add_errors)
|
||||
flags |= ADD_FILES_IGNORE_ERRORS;
|
||||
|
||||
exit_status = add_files_to_cache(prefix, pathspec, flags);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -254,7 +287,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
for (i = 0; i < dir.nr; i++)
|
||||
add_file_to_cache(dir.entries[i]->name, verbose);
|
||||
if (add_file_to_cache(dir.entries[i]->name, verbose)) {
|
||||
if (!ignore_add_errors)
|
||||
die("adding files failed");
|
||||
exit_status = 1;
|
||||
}
|
||||
|
||||
finish:
|
||||
if (active_cache_changed) {
|
||||
@ -263,5 +300,5 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
die("Unable to write new index file");
|
||||
}
|
||||
|
||||
return 0;
|
||||
return exit_status;
|
||||
}
|
||||
|
@ -282,7 +282,7 @@ static int merge_working_tree(struct checkout_opts *opts,
|
||||
* entries in the index.
|
||||
*/
|
||||
|
||||
add_files_to_cache(0, NULL, NULL);
|
||||
add_files_to_cache(NULL, NULL, 0);
|
||||
work = write_tree_from_memory();
|
||||
|
||||
ret = reset_to_new(new->commit->tree, opts->quiet);
|
||||
|
@ -179,9 +179,10 @@ static void add_remove_files(struct path_list *list)
|
||||
struct stat st;
|
||||
struct path_list_item *p = &(list->items[i]);
|
||||
|
||||
if (!lstat(p->path, &st))
|
||||
add_to_cache(p->path, &st, 0);
|
||||
else
|
||||
if (!lstat(p->path, &st)) {
|
||||
if (add_to_cache(p->path, &st, 0))
|
||||
die("updating files failed");
|
||||
} else
|
||||
remove_file_from_cache(p->path);
|
||||
}
|
||||
}
|
||||
@ -246,7 +247,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
|
||||
*/
|
||||
if (all || (also && pathspec && *pathspec)) {
|
||||
int fd = hold_locked_index(&index_lock, 1);
|
||||
add_files_to_cache(0, also ? prefix : NULL, pathspec);
|
||||
add_files_to_cache(also ? prefix : NULL, pathspec, 0);
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
close_lock_file(&index_lock))
|
||||
|
@ -256,7 +256,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
|
||||
for (i = 0; i < added.nr; i++) {
|
||||
const char *path = added.items[i].path;
|
||||
add_file_to_cache(path, verbose);
|
||||
if (add_file_to_cache(path, verbose))
|
||||
die("updating index entries failed");
|
||||
}
|
||||
|
||||
for (i = 0; i < deleted.nr; i++)
|
||||
|
8
cache.h
8
cache.h
@ -782,7 +782,13 @@ extern int convert_to_git(const char *path, const char *src, size_t len,
|
||||
extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
|
||||
|
||||
/* add */
|
||||
void add_files_to_cache(int verbose, const char *prefix, const char **pathspec);
|
||||
#define ADD_FILES_VERBOSE 01
|
||||
#define ADD_FILES_IGNORE_ERRORS 02
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/* diff.c */
|
||||
extern int diff_auto_refresh_index;
|
||||
|
@ -470,7 +470,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
|
||||
unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
|
||||
|
||||
if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
|
||||
die("%s: can only add regular files, symbolic links or git-directories", path);
|
||||
return error("%s: can only add regular files, symbolic links or git-directories", path);
|
||||
|
||||
namelen = strlen(path);
|
||||
if (S_ISDIR(st_mode)) {
|
||||
@ -505,12 +505,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
|
||||
return 0;
|
||||
}
|
||||
if (index_path(ce->sha1, path, st, 1))
|
||||
die("unable to index file %s", path);
|
||||
return error("unable to index file %s", path);
|
||||
if (ignore_case && alias && different_name(ce, alias))
|
||||
ce = create_alias_ce(ce, alias);
|
||||
ce->ce_flags |= CE_ADDED;
|
||||
if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
|
||||
die("unable to add %s to index",path);
|
||||
return error("unable to add %s to index",path);
|
||||
if (verbose)
|
||||
printf("add '%s'\n", path);
|
||||
return 0;
|
||||
|
@ -179,4 +179,47 @@ test_expect_success 'git add --refresh' '
|
||||
test -z "`git diff-index HEAD -- foo`"
|
||||
'
|
||||
|
||||
test_expect_success 'git add should fail atomically upon an unreadable file' '
|
||||
git reset --hard &&
|
||||
date >foo1 &&
|
||||
date >foo2 &&
|
||||
chmod 0 foo2 &&
|
||||
test_must_fail git add --verbose . &&
|
||||
! ( git ls-files foo1 | grep foo1 )
|
||||
'
|
||||
|
||||
rm -f foo2
|
||||
|
||||
test_expect_success 'git add --ignore-errors' '
|
||||
git reset --hard &&
|
||||
date >foo1 &&
|
||||
date >foo2 &&
|
||||
chmod 0 foo2 &&
|
||||
test_must_fail git add --verbose --ignore-errors . &&
|
||||
git ls-files foo1 | grep foo1
|
||||
'
|
||||
|
||||
rm -f foo2
|
||||
|
||||
test_expect_success 'git add (add.ignore-errors)' '
|
||||
git config add.ignore-errors 1 &&
|
||||
git reset --hard &&
|
||||
date >foo1 &&
|
||||
date >foo2 &&
|
||||
chmod 0 foo2 &&
|
||||
test_must_fail git add --verbose . &&
|
||||
git ls-files foo1 | grep foo1
|
||||
'
|
||||
rm -f foo2
|
||||
|
||||
test_expect_success 'git add (add.ignore-errors = false)' '
|
||||
git config add.ignore-errors 0 &&
|
||||
git reset --hard &&
|
||||
date >foo1 &&
|
||||
date >foo2 &&
|
||||
chmod 0 foo2 &&
|
||||
test_must_fail git add --verbose . &&
|
||||
! ( git ls-files foo1 | grep foo1 )
|
||||
'
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user