Merge branch 'cb/leading-path-removal'
* cb/leading-path-removal: use persistent memory for rejected paths do not overwrite files in leading path lstat_cache: optionally return match_len add function check_ok_to_remove() t7607: add leading-path tests t7607: use test-lib functions and check MERGE_HEAD Conflicts: t/t7607-merge-overwrite.sh
This commit is contained in:
commit
208247adb9
2
cache.h
2
cache.h
@ -859,7 +859,7 @@ struct cache_def {
|
|||||||
|
|
||||||
extern int has_symlink_leading_path(const char *name, int len);
|
extern int has_symlink_leading_path(const char *name, int len);
|
||||||
extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
|
extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
|
||||||
extern int has_symlink_or_noent_leading_path(const char *name, int len);
|
extern int check_leading_path(const char *name, int len);
|
||||||
extern int has_dirs_only_path(const char *name, int len, int prefix_len);
|
extern int has_dirs_only_path(const char *name, int len, int prefix_len);
|
||||||
extern void schedule_dir_for_removal(const char *name, int len);
|
extern void schedule_dir_for_removal(const char *name, int len);
|
||||||
extern void remove_scheduled_dirs(void);
|
extern void remove_scheduled_dirs(void);
|
||||||
|
64
symlinks.c
64
symlinks.c
@ -64,11 +64,13 @@ static inline void reset_lstat_cache(struct cache_def *cache)
|
|||||||
* of the prefix, where the cache should use the stat() function
|
* of the prefix, where the cache should use the stat() function
|
||||||
* instead of the lstat() function to test each path component.
|
* instead of the lstat() function to test each path component.
|
||||||
*/
|
*/
|
||||||
static int lstat_cache(struct cache_def *cache, const char *name, int len,
|
static int lstat_cache_matchlen(struct cache_def *cache,
|
||||||
int track_flags, int prefix_len_stat_func)
|
const char *name, int len,
|
||||||
|
int *ret_flags, int track_flags,
|
||||||
|
int prefix_len_stat_func)
|
||||||
{
|
{
|
||||||
int match_len, last_slash, last_slash_dir, previous_slash;
|
int match_len, last_slash, last_slash_dir, previous_slash;
|
||||||
int match_flags, ret_flags, save_flags, max_len, ret;
|
int save_flags, max_len, ret;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (cache->track_flags != track_flags ||
|
if (cache->track_flags != track_flags ||
|
||||||
@ -90,13 +92,13 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
|
|||||||
match_len = last_slash =
|
match_len = last_slash =
|
||||||
longest_path_match(name, len, cache->path, cache->len,
|
longest_path_match(name, len, cache->path, cache->len,
|
||||||
&previous_slash);
|
&previous_slash);
|
||||||
match_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
|
*ret_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
|
||||||
|
|
||||||
if (!(track_flags & FL_FULLPATH) && match_len == len)
|
if (!(track_flags & FL_FULLPATH) && match_len == len)
|
||||||
match_len = last_slash = previous_slash;
|
match_len = last_slash = previous_slash;
|
||||||
|
|
||||||
if (match_flags && match_len == cache->len)
|
if (*ret_flags && match_len == cache->len)
|
||||||
return match_flags;
|
return match_len;
|
||||||
/*
|
/*
|
||||||
* If we now have match_len > 0, we would know that
|
* If we now have match_len > 0, we would know that
|
||||||
* the matched part will always be a directory.
|
* the matched part will always be a directory.
|
||||||
@ -105,16 +107,16 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
|
|||||||
* a substring of the cache on a path component basis,
|
* a substring of the cache on a path component basis,
|
||||||
* we can return immediately.
|
* we can return immediately.
|
||||||
*/
|
*/
|
||||||
match_flags = track_flags & FL_DIR;
|
*ret_flags = track_flags & FL_DIR;
|
||||||
if (match_flags && len == match_len)
|
if (*ret_flags && len == match_len)
|
||||||
return match_flags;
|
return match_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Okay, no match from the cache so far, so now we have to
|
* Okay, no match from the cache so far, so now we have to
|
||||||
* check the rest of the path components.
|
* check the rest of the path components.
|
||||||
*/
|
*/
|
||||||
ret_flags = FL_DIR;
|
*ret_flags = FL_DIR;
|
||||||
last_slash_dir = last_slash;
|
last_slash_dir = last_slash;
|
||||||
max_len = len < PATH_MAX ? len : PATH_MAX;
|
max_len = len < PATH_MAX ? len : PATH_MAX;
|
||||||
while (match_len < max_len) {
|
while (match_len < max_len) {
|
||||||
@ -133,16 +135,16 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
|
|||||||
ret = lstat(cache->path, &st);
|
ret = lstat(cache->path, &st);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ret_flags = FL_LSTATERR;
|
*ret_flags = FL_LSTATERR;
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
ret_flags |= FL_NOENT;
|
*ret_flags |= FL_NOENT;
|
||||||
} else if (S_ISDIR(st.st_mode)) {
|
} else if (S_ISDIR(st.st_mode)) {
|
||||||
last_slash_dir = last_slash;
|
last_slash_dir = last_slash;
|
||||||
continue;
|
continue;
|
||||||
} else if (S_ISLNK(st.st_mode)) {
|
} else if (S_ISLNK(st.st_mode)) {
|
||||||
ret_flags = FL_SYMLINK;
|
*ret_flags = FL_SYMLINK;
|
||||||
} else {
|
} else {
|
||||||
ret_flags = FL_ERR;
|
*ret_flags = FL_ERR;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -152,7 +154,7 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
|
|||||||
* path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached
|
* path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached
|
||||||
* for the moment!
|
* for the moment!
|
||||||
*/
|
*/
|
||||||
save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
|
save_flags = *ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
|
||||||
if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
|
if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
|
||||||
cache->path[last_slash] = '\0';
|
cache->path[last_slash] = '\0';
|
||||||
cache->len = last_slash;
|
cache->len = last_slash;
|
||||||
@ -176,7 +178,16 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
|
|||||||
} else {
|
} else {
|
||||||
reset_lstat_cache(cache);
|
reset_lstat_cache(cache);
|
||||||
}
|
}
|
||||||
return ret_flags;
|
return match_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lstat_cache(struct cache_def *cache, const char *name, int len,
|
||||||
|
int track_flags, int prefix_len_stat_func)
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
(void)lstat_cache_matchlen(cache, name, len, &flags, track_flags,
|
||||||
|
prefix_len_stat_func);
|
||||||
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define USE_ONLY_LSTAT 0
|
#define USE_ONLY_LSTAT 0
|
||||||
@ -198,15 +209,26 @@ int has_symlink_leading_path(const char *name, int len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return non-zero if path 'name' has a leading symlink component or
|
* Return zero if path 'name' has a leading symlink component or
|
||||||
* if some leading path component does not exists.
|
* if some leading path component does not exists.
|
||||||
|
*
|
||||||
|
* Return -1 if leading path exists and is a directory.
|
||||||
|
*
|
||||||
|
* Return path length if leading path exists and is neither a
|
||||||
|
* directory nor a symlink.
|
||||||
*/
|
*/
|
||||||
int has_symlink_or_noent_leading_path(const char *name, int len)
|
int check_leading_path(const char *name, int len)
|
||||||
{
|
{
|
||||||
struct cache_def *cache = &default_cache; /* FIXME */
|
struct cache_def *cache = &default_cache; /* FIXME */
|
||||||
return lstat_cache(cache, name, len,
|
int flags;
|
||||||
FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
|
int match_len = lstat_cache_matchlen(cache, name, len, &flags,
|
||||||
(FL_SYMLINK|FL_NOENT);
|
FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT);
|
||||||
|
if (flags & (FL_SYMLINK|FL_NOENT))
|
||||||
|
return 0;
|
||||||
|
else if (flags & FL_DIR)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return match_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -7,48 +7,54 @@ Do not overwrite changes.'
|
|||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
test_expect_success 'setup' '
|
test_expect_success 'setup' '
|
||||||
echo c0 > c0.c &&
|
test_commit c0 c0.c &&
|
||||||
git add c0.c &&
|
test_commit c1 c1.c &&
|
||||||
git commit -m c0 &&
|
test_commit c1a c1.c "c1 a" &&
|
||||||
git tag c0 &&
|
|
||||||
echo c1 > c1.c &&
|
|
||||||
git add c1.c &&
|
|
||||||
git commit -m c1 &&
|
|
||||||
git tag c1 &&
|
|
||||||
git reset --hard c0 &&
|
git reset --hard c0 &&
|
||||||
echo c2 > c2.c &&
|
test_commit c2 c2.c &&
|
||||||
git add c2.c &&
|
git reset --hard c0 &&
|
||||||
git commit -m c2 &&
|
mkdir sub &&
|
||||||
git tag c2 &&
|
echo "sub/f" > sub/f &&
|
||||||
git reset --hard c1 &&
|
mkdir sub2 &&
|
||||||
echo "c1 a" > c1.c &&
|
echo "sub2/f" > sub2/f &&
|
||||||
git add c1.c &&
|
git add sub/f sub2/f &&
|
||||||
git commit -m "c1 a" &&
|
git commit -m sub &&
|
||||||
git tag c1a &&
|
git tag sub &&
|
||||||
echo "VERY IMPORTANT CHANGES" > important
|
echo "VERY IMPORTANT CHANGES" > important
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'will not overwrite untracked file' '
|
test_expect_success 'will not overwrite untracked file' '
|
||||||
git reset --hard c1 &&
|
git reset --hard c1 &&
|
||||||
cat important > c2.c &&
|
cp important c2.c &&
|
||||||
test_must_fail git merge c2 &&
|
test_must_fail git merge c2 &&
|
||||||
|
test_path_is_missing .git/MERGE_HEAD &&
|
||||||
test_cmp important c2.c
|
test_cmp important c2.c
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'will overwrite tracked file' '
|
||||||
|
git reset --hard c1 &&
|
||||||
|
cp important c2.c &&
|
||||||
|
git add c2.c &&
|
||||||
|
git commit -m important &&
|
||||||
|
git checkout c2
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'will not overwrite new file' '
|
test_expect_success 'will not overwrite new file' '
|
||||||
git reset --hard c1 &&
|
git reset --hard c1 &&
|
||||||
cat important > c2.c &&
|
cp important c2.c &&
|
||||||
git add c2.c &&
|
git add c2.c &&
|
||||||
test_must_fail git merge c2 &&
|
test_must_fail git merge c2 &&
|
||||||
|
test_path_is_missing .git/MERGE_HEAD &&
|
||||||
test_cmp important c2.c
|
test_cmp important c2.c
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'will not overwrite staged changes' '
|
test_expect_success 'will not overwrite staged changes' '
|
||||||
git reset --hard c1 &&
|
git reset --hard c1 &&
|
||||||
cat important > c2.c &&
|
cp important c2.c &&
|
||||||
git add c2.c &&
|
git add c2.c &&
|
||||||
rm c2.c &&
|
rm c2.c &&
|
||||||
test_must_fail git merge c2 &&
|
test_must_fail git merge c2 &&
|
||||||
|
test_path_is_missing .git/MERGE_HEAD &&
|
||||||
git checkout c2.c &&
|
git checkout c2.c &&
|
||||||
test_cmp important c2.c
|
test_cmp important c2.c
|
||||||
'
|
'
|
||||||
@ -57,7 +63,7 @@ test_expect_success 'will not overwrite removed file' '
|
|||||||
git reset --hard c1 &&
|
git reset --hard c1 &&
|
||||||
git rm c1.c &&
|
git rm c1.c &&
|
||||||
git commit -m "rm c1.c" &&
|
git commit -m "rm c1.c" &&
|
||||||
cat important > c1.c &&
|
cp important c1.c &&
|
||||||
test_must_fail git merge c1a &&
|
test_must_fail git merge c1a &&
|
||||||
test_cmp important c1.c
|
test_cmp important c1.c
|
||||||
'
|
'
|
||||||
@ -66,9 +72,10 @@ test_expect_success 'will not overwrite re-added file' '
|
|||||||
git reset --hard c1 &&
|
git reset --hard c1 &&
|
||||||
git rm c1.c &&
|
git rm c1.c &&
|
||||||
git commit -m "rm c1.c" &&
|
git commit -m "rm c1.c" &&
|
||||||
cat important > c1.c &&
|
cp important c1.c &&
|
||||||
git add c1.c &&
|
git add c1.c &&
|
||||||
test_must_fail git merge c1a &&
|
test_must_fail git merge c1a &&
|
||||||
|
test_path_is_missing .git/MERGE_HEAD &&
|
||||||
test_cmp important c1.c
|
test_cmp important c1.c
|
||||||
'
|
'
|
||||||
|
|
||||||
@ -76,14 +83,63 @@ test_expect_success 'will not overwrite removed file with staged changes' '
|
|||||||
git reset --hard c1 &&
|
git reset --hard c1 &&
|
||||||
git rm c1.c &&
|
git rm c1.c &&
|
||||||
git commit -m "rm c1.c" &&
|
git commit -m "rm c1.c" &&
|
||||||
cat important > c1.c &&
|
cp important c1.c &&
|
||||||
git add c1.c &&
|
git add c1.c &&
|
||||||
rm c1.c &&
|
rm c1.c &&
|
||||||
test_must_fail git merge c1a &&
|
test_must_fail git merge c1a &&
|
||||||
|
test_path_is_missing .git/MERGE_HEAD &&
|
||||||
git checkout c1.c &&
|
git checkout c1.c &&
|
||||||
test_cmp important c1.c
|
test_cmp important c1.c
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'will not overwrite untracked subtree' '
|
||||||
|
git reset --hard c0 &&
|
||||||
|
rm -rf sub &&
|
||||||
|
mkdir -p sub/f &&
|
||||||
|
cp important sub/f/important &&
|
||||||
|
test_must_fail git merge sub &&
|
||||||
|
test_path_is_missing .git/MERGE_HEAD &&
|
||||||
|
test_cmp important sub/f/important
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expect <<\EOF
|
||||||
|
error: The following untracked working tree files would be overwritten by merge:
|
||||||
|
sub
|
||||||
|
sub2
|
||||||
|
Please move or remove them before you can merge.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'will not overwrite untracked file in leading path' '
|
||||||
|
git reset --hard c0 &&
|
||||||
|
rm -rf sub &&
|
||||||
|
cp important sub &&
|
||||||
|
cp important sub2 &&
|
||||||
|
test_must_fail git merge sub 2>out &&
|
||||||
|
test_cmp out expect &&
|
||||||
|
test_path_is_missing .git/MERGE_HEAD &&
|
||||||
|
test_cmp important sub &&
|
||||||
|
test_cmp important sub2 &&
|
||||||
|
rm -f sub sub2
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_failure SYMLINKS 'will not overwrite untracked symlink in leading path' '
|
||||||
|
git reset --hard c0 &&
|
||||||
|
rm -rf sub &&
|
||||||
|
mkdir sub2 &&
|
||||||
|
ln -s sub2 sub &&
|
||||||
|
test_must_fail git merge sub &&
|
||||||
|
test_path_is_missing .git/MERGE_HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'will not be confused by symlink in leading path' '
|
||||||
|
git reset --hard c0 &&
|
||||||
|
rm -rf sub &&
|
||||||
|
ln -s sub2 sub &&
|
||||||
|
git add sub &&
|
||||||
|
git commit -m ln &&
|
||||||
|
git checkout sub
|
||||||
|
'
|
||||||
|
|
||||||
cat >expect <<\EOF
|
cat >expect <<\EOF
|
||||||
error: Untracked working tree file 'c0.c' would be overwritten by merge.
|
error: Untracked working tree file 'c0.c' would be overwritten by merge.
|
||||||
fatal: read-tree failed
|
fatal: read-tree failed
|
||||||
|
@ -27,10 +27,10 @@ test_expect_success 'setup' '
|
|||||||
|
|
||||||
cat >expect <<\EOF
|
cat >expect <<\EOF
|
||||||
error: The following untracked working tree files would be overwritten by merge:
|
error: The following untracked working tree files would be overwritten by merge:
|
||||||
two
|
|
||||||
three
|
|
||||||
four
|
|
||||||
five
|
five
|
||||||
|
four
|
||||||
|
three
|
||||||
|
two
|
||||||
Please move or remove them before you can merge.
|
Please move or remove them before you can merge.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@ -49,9 +49,9 @@ test_expect_success 'untracked files overwritten by merge (fast and non-fast for
|
|||||||
|
|
||||||
cat >expect <<\EOF
|
cat >expect <<\EOF
|
||||||
error: Your local changes to the following files would be overwritten by merge:
|
error: Your local changes to the following files would be overwritten by merge:
|
||||||
two
|
|
||||||
three
|
|
||||||
four
|
four
|
||||||
|
three
|
||||||
|
two
|
||||||
Please, commit your changes or stash them before you can merge.
|
Please, commit your changes or stash them before you can merge.
|
||||||
error: The following untracked working tree files would be overwritten by merge:
|
error: The following untracked working tree files would be overwritten by merge:
|
||||||
five
|
five
|
||||||
@ -68,8 +68,8 @@ test_expect_success 'untracked files or local changes ovewritten by merge' '
|
|||||||
|
|
||||||
cat >expect <<\EOF
|
cat >expect <<\EOF
|
||||||
error: Your local changes to the following files would be overwritten by checkout:
|
error: Your local changes to the following files would be overwritten by checkout:
|
||||||
rep/two
|
|
||||||
rep/one
|
rep/one
|
||||||
|
rep/two
|
||||||
Please, commit your changes or stash them before you can switch branches.
|
Please, commit your changes or stash them before you can switch branches.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@ -89,8 +89,8 @@ test_expect_success 'cannot switch branches because of local changes' '
|
|||||||
|
|
||||||
cat >expect <<\EOF
|
cat >expect <<\EOF
|
||||||
error: Your local changes to the following files would be overwritten by checkout:
|
error: Your local changes to the following files would be overwritten by checkout:
|
||||||
rep/two
|
|
||||||
rep/one
|
rep/one
|
||||||
|
rep/two
|
||||||
Please, commit your changes or stash them before you can switch branches.
|
Please, commit your changes or stash them before you can switch branches.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@ -102,8 +102,8 @@ test_expect_success 'not uptodate file porcelain checkout error' '
|
|||||||
|
|
||||||
cat >expect <<\EOF
|
cat >expect <<\EOF
|
||||||
error: Updating the following directories would lose untracked files in it:
|
error: Updating the following directories would lose untracked files in it:
|
||||||
rep2
|
|
||||||
rep
|
rep
|
||||||
|
rep2
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
155
unpack-trees.c
155
unpack-trees.c
@ -53,6 +53,7 @@ const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
|
|||||||
void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
|
void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
|
||||||
const char *cmd)
|
const char *cmd)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
const char **msgs = opts->msgs;
|
const char **msgs = opts->msgs;
|
||||||
const char *msg;
|
const char *msg;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
@ -96,6 +97,9 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
|
|||||||
"The following Working tree files would be removed by sparse checkout update:\n%s";
|
"The following Working tree files would be removed by sparse checkout update:\n%s";
|
||||||
|
|
||||||
opts->show_all_errors = 1;
|
opts->show_all_errors = 1;
|
||||||
|
/* rejected paths may not have a static buffer */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(opts->unpack_rejects); i++)
|
||||||
|
opts->unpack_rejects[i].strdup_strings = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
|
static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
|
||||||
@ -124,7 +128,6 @@ static int add_rejected_path(struct unpack_trees_options *o,
|
|||||||
enum unpack_trees_error_types e,
|
enum unpack_trees_error_types e,
|
||||||
const char *path)
|
const char *path)
|
||||||
{
|
{
|
||||||
struct rejected_paths_list *newentry;
|
|
||||||
if (!o->show_all_errors)
|
if (!o->show_all_errors)
|
||||||
return error(ERRORMSG(o, e), path);
|
return error(ERRORMSG(o, e), path);
|
||||||
|
|
||||||
@ -132,45 +135,28 @@ static int add_rejected_path(struct unpack_trees_options *o,
|
|||||||
* Otherwise, insert in a list for future display by
|
* Otherwise, insert in a list for future display by
|
||||||
* display_error_msgs()
|
* display_error_msgs()
|
||||||
*/
|
*/
|
||||||
newentry = xmalloc(sizeof(struct rejected_paths_list));
|
string_list_append(&o->unpack_rejects[e], path);
|
||||||
newentry->path = (char *)path;
|
|
||||||
newentry->next = o->unpack_rejects[e];
|
|
||||||
o->unpack_rejects[e] = newentry;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* free all the structures allocated for the error <e>
|
|
||||||
*/
|
|
||||||
static void free_rejected_paths(struct unpack_trees_options *o,
|
|
||||||
enum unpack_trees_error_types e)
|
|
||||||
{
|
|
||||||
while (o->unpack_rejects[e]) {
|
|
||||||
struct rejected_paths_list *del = o->unpack_rejects[e];
|
|
||||||
o->unpack_rejects[e] = o->unpack_rejects[e]->next;
|
|
||||||
free(del);
|
|
||||||
}
|
|
||||||
free(o->unpack_rejects[e]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* display all the error messages stored in a nice way
|
* display all the error messages stored in a nice way
|
||||||
*/
|
*/
|
||||||
static void display_error_msgs(struct unpack_trees_options *o)
|
static void display_error_msgs(struct unpack_trees_options *o)
|
||||||
{
|
{
|
||||||
int e;
|
int e, i;
|
||||||
int something_displayed = 0;
|
int something_displayed = 0;
|
||||||
for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {
|
for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {
|
||||||
if (o->unpack_rejects[e]) {
|
struct string_list *rejects = &o->unpack_rejects[e];
|
||||||
struct rejected_paths_list *rp;
|
if (rejects->nr > 0) {
|
||||||
struct strbuf path = STRBUF_INIT;
|
struct strbuf path = STRBUF_INIT;
|
||||||
something_displayed = 1;
|
something_displayed = 1;
|
||||||
for (rp = o->unpack_rejects[e]; rp; rp = rp->next)
|
for (i = 0; i < rejects->nr; i++)
|
||||||
strbuf_addf(&path, "\t%s\n", rp->path);
|
strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
|
||||||
error(ERRORMSG(o, e), path.buf);
|
error(ERRORMSG(o, e), path.buf);
|
||||||
strbuf_release(&path);
|
strbuf_release(&path);
|
||||||
free_rejected_paths(o, e);
|
|
||||||
}
|
}
|
||||||
|
string_list_clear(rejects, 0);
|
||||||
}
|
}
|
||||||
if (something_displayed)
|
if (something_displayed)
|
||||||
printf("Aborting\n");
|
printf("Aborting\n");
|
||||||
@ -182,7 +168,7 @@ static void display_error_msgs(struct unpack_trees_options *o)
|
|||||||
*/
|
*/
|
||||||
static void unlink_entry(struct cache_entry *ce)
|
static void unlink_entry(struct cache_entry *ce)
|
||||||
{
|
{
|
||||||
if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
|
if (!check_leading_path(ce->name, ce_namelen(ce)))
|
||||||
return;
|
return;
|
||||||
if (remove_or_warn(ce->ce_mode, ce->name))
|
if (remove_or_warn(ce->ce_mode, ce->name))
|
||||||
return;
|
return;
|
||||||
@ -1127,14 +1113,65 @@ static int verify_clean_subdirectory(struct cache_entry *ce,
|
|||||||
* See if we can find a case-insensitive match in the index that also
|
* See if we can find a case-insensitive match in the index that also
|
||||||
* matches the stat information, and assume it's that other file!
|
* matches the stat information, and assume it's that other file!
|
||||||
*/
|
*/
|
||||||
static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst, struct stat *st)
|
static int icase_exists(struct unpack_trees_options *o, const char *name, int len, struct stat *st)
|
||||||
{
|
{
|
||||||
struct cache_entry *src;
|
struct cache_entry *src;
|
||||||
|
|
||||||
src = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1);
|
src = index_name_exists(o->src_index, name, len, 1);
|
||||||
return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
|
return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int check_ok_to_remove(const char *name, int len, int dtype,
|
||||||
|
struct cache_entry *ce, struct stat *st,
|
||||||
|
enum unpack_trees_error_types error_type,
|
||||||
|
struct unpack_trees_options *o)
|
||||||
|
{
|
||||||
|
struct cache_entry *result;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It may be that the 'lstat()' succeeded even though
|
||||||
|
* target 'ce' was absent, because there is an old
|
||||||
|
* entry that is different only in case..
|
||||||
|
*
|
||||||
|
* Ignore that lstat() if it matches.
|
||||||
|
*/
|
||||||
|
if (ignore_case && icase_exists(o, name, len, st))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (o->dir && excluded(o->dir, name, &dtype))
|
||||||
|
/*
|
||||||
|
* ce->name is explicitly excluded, so it is Ok to
|
||||||
|
* overwrite it.
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
if (S_ISDIR(st->st_mode)) {
|
||||||
|
/*
|
||||||
|
* We are checking out path "foo" and
|
||||||
|
* found "foo/." in the working tree.
|
||||||
|
* This is tricky -- if we have modified
|
||||||
|
* files that are in "foo/" we would lose
|
||||||
|
* them.
|
||||||
|
*/
|
||||||
|
if (verify_clean_subdirectory(ce, error_type, o) < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The previous round may already have decided to
|
||||||
|
* delete this path, which is in a subdirectory that
|
||||||
|
* is being replaced with a blob.
|
||||||
|
*/
|
||||||
|
result = index_name_exists(&o->result, name, len, 0);
|
||||||
|
if (result) {
|
||||||
|
if (result->ce_flags & CE_REMOVE)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return o->gently ? -1 :
|
||||||
|
add_rejected_path(o, error_type, name);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We do not want to remove or overwrite a working tree file that
|
* We do not want to remove or overwrite a working tree file that
|
||||||
* is not tracked, unless it is ignored.
|
* is not tracked, unless it is ignored.
|
||||||
@ -1143,63 +1180,31 @@ static int verify_absent_1(struct cache_entry *ce,
|
|||||||
enum unpack_trees_error_types error_type,
|
enum unpack_trees_error_types error_type,
|
||||||
struct unpack_trees_options *o)
|
struct unpack_trees_options *o)
|
||||||
{
|
{
|
||||||
|
int len;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (o->index_only || o->reset || !o->update)
|
if (o->index_only || o->reset || !o->update)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
|
len = check_leading_path(ce->name, ce_namelen(ce));
|
||||||
|
if (!len)
|
||||||
return 0;
|
return 0;
|
||||||
|
else if (len > 0) {
|
||||||
|
char path[PATH_MAX + 1];
|
||||||
|
memcpy(path, ce->name, len);
|
||||||
|
path[len] = 0;
|
||||||
|
lstat(path, &st);
|
||||||
|
|
||||||
if (!lstat(ce->name, &st)) {
|
return check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st,
|
||||||
int dtype = ce_to_dtype(ce);
|
error_type, o);
|
||||||
struct cache_entry *result;
|
} else if (!lstat(ce->name, &st))
|
||||||
|
return check_ok_to_remove(ce->name, ce_namelen(ce),
|
||||||
|
ce_to_dtype(ce), ce, &st,
|
||||||
|
error_type, o);
|
||||||
|
|
||||||
/*
|
|
||||||
* It may be that the 'lstat()' succeeded even though
|
|
||||||
* target 'ce' was absent, because there is an old
|
|
||||||
* entry that is different only in case..
|
|
||||||
*
|
|
||||||
* Ignore that lstat() if it matches.
|
|
||||||
*/
|
|
||||||
if (ignore_case && icase_exists(o, ce, &st))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (o->dir && excluded(o->dir, ce->name, &dtype))
|
|
||||||
/*
|
|
||||||
* ce->name is explicitly excluded, so it is Ok to
|
|
||||||
* overwrite it.
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
if (S_ISDIR(st.st_mode)) {
|
|
||||||
/*
|
|
||||||
* We are checking out path "foo" and
|
|
||||||
* found "foo/." in the working tree.
|
|
||||||
* This is tricky -- if we have modified
|
|
||||||
* files that are in "foo/" we would lose
|
|
||||||
* them.
|
|
||||||
*/
|
|
||||||
if (verify_clean_subdirectory(ce, error_type, o) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The previous round may already have decided to
|
|
||||||
* delete this path, which is in a subdirectory that
|
|
||||||
* is being replaced with a blob.
|
|
||||||
*/
|
|
||||||
result = index_name_exists(&o->result, ce->name, ce_namelen(ce), 0);
|
|
||||||
if (result) {
|
|
||||||
if (result->ce_flags & CE_REMOVE)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return o->gently ? -1 :
|
|
||||||
add_rejected_path(o, error_type, ce->name);
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int verify_absent(struct cache_entry *ce,
|
static int verify_absent(struct cache_entry *ce,
|
||||||
enum unpack_trees_error_types error_type,
|
enum unpack_trees_error_types error_type,
|
||||||
struct unpack_trees_options *o)
|
struct unpack_trees_options *o)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef UNPACK_TREES_H
|
#ifndef UNPACK_TREES_H
|
||||||
#define UNPACK_TREES_H
|
#define UNPACK_TREES_H
|
||||||
|
|
||||||
|
#include "string-list.h"
|
||||||
|
|
||||||
#define MAX_UNPACK_TREES 8
|
#define MAX_UNPACK_TREES 8
|
||||||
|
|
||||||
struct unpack_trees_options;
|
struct unpack_trees_options;
|
||||||
@ -29,11 +31,6 @@ enum unpack_trees_error_types {
|
|||||||
void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
|
void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
|
||||||
const char *cmd);
|
const char *cmd);
|
||||||
|
|
||||||
struct rejected_paths_list {
|
|
||||||
char *path;
|
|
||||||
struct rejected_paths_list *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct unpack_trees_options {
|
struct unpack_trees_options {
|
||||||
unsigned int reset,
|
unsigned int reset,
|
||||||
merge,
|
merge,
|
||||||
@ -59,7 +56,7 @@ struct unpack_trees_options {
|
|||||||
* Store error messages in an array, each case
|
* Store error messages in an array, each case
|
||||||
* corresponding to a error message type
|
* corresponding to a error message type
|
||||||
*/
|
*/
|
||||||
struct rejected_paths_list *unpack_rejects[NB_UNPACK_TREES_ERROR_TYPES];
|
struct string_list unpack_rejects[NB_UNPACK_TREES_ERROR_TYPES];
|
||||||
|
|
||||||
int head_idx;
|
int head_idx;
|
||||||
int merge_size;
|
int merge_size;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user