Fix switching to a branch with D/F when current branch has file D.

This loosens the over-eager verify_absent() check that gets
upset to find directory D in the current working tree when
switching to a branch that has a file there.  The check needs to
make sure that we do not lose precious working tree files as a
result of removing directory D and replacing it with the file
from the other branch, which is a tad expensive but this is a
less common case.

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2007-03-15 23:25:22 -07:00
parent b8ba1535bd
commit c81935348b

View File

@ -465,6 +465,64 @@ static void invalidate_ce_path(struct cache_entry *ce)
cache_tree_invalidate_path(active_cache_tree, ce->name);
}
static int verify_clean_subdirectory(const char *path, const char *action,
struct unpack_trees_options *o)
{
/*
* we are about to extract "path"; we would not want to lose
* anything in the existing directory there.
*/
int namelen;
int pos, i;
struct dir_struct d;
char *pathbuf;
int cnt = 0;
/*
* First let's make sure we do not have a local modification
* in that directory.
*/
namelen = strlen(path);
pos = cache_name_pos(path, namelen);
if (0 <= pos)
return cnt; /* we have it as nondirectory */
pos = -pos - 1;
for (i = pos; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
int len = ce_namelen(ce);
if (len < namelen ||
strncmp(path, ce->name, namelen) ||
ce->name[namelen] != '/')
break;
/*
* ce->name is an entry in the subdirectory.
*/
if (!ce_stage(ce)) {
verify_uptodate(ce, o);
ce->ce_mode = 0;
}
cnt++;
}
/*
* Then we need to make sure that we do not lose a locally
* present file that is not ignored.
*/
pathbuf = xmalloc(namelen + 2);
memcpy(pathbuf, path, namelen);
strcpy(pathbuf+namelen, "/");
memset(&d, 0, sizeof(d));
if (o->dir)
d.exclude_per_dir = o->dir->exclude_per_dir;
i = read_directory(&d, path, pathbuf, namelen+1, NULL);
if (i)
die("Updating '%s' would lose untracked files in it",
path);
free(pathbuf);
return cnt;
}
/*
* We do not want to remove or overwrite a working tree file that
* is not tracked, unless it is ignored.
@ -476,9 +534,62 @@ static void verify_absent(const char *path, const char *action,
if (o->index_only || o->reset || !o->update)
return;
if (!lstat(path, &st) && !(o->dir && excluded(o->dir, path)))
if (!lstat(path, &st)) {
int cnt;
if (o->dir && excluded(o->dir, path))
/*
* path is explicitly excluded, so it is Ok to
* overwrite it.
*/
return;
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
* it.
*/
cnt = verify_clean_subdirectory(path, action, o);
/*
* If this removed entries from the index,
* what that means is:
*
* (1) the caller unpack_trees_rec() saw path/foo
* in the index, and it has not removed it because
* it thinks it is handling 'path' as blob with
* D/F conflict;
* (2) we will return "ok, we placed a merged entry
* in the index" which would cause o->pos to be
* incremented by one;
* (3) however, original o->pos now has 'path/foo'
* marked with "to be removed".
*
* We need to increment it by the number of
* deleted entries here.
*/
o->pos += cnt;
return;
}
/*
* The previous round may already have decided to
* delete this path, which is in a subdirectory that
* is being replaced with a blob.
*/
cnt = cache_name_pos(path, strlen(path));
if (0 <= cnt) {
struct cache_entry *ce = active_cache[cnt];
if (!ce_stage(ce) && !ce->ce_mode)
return;
}
die("Untracked working tree file '%s' "
"would be %s by merge.", path, action);
}
}
static int merged_entry(struct cache_entry *merge, struct cache_entry *old,