Merge branch 'ew/apply'

* ew/apply:
  Fix t4114 on cygwin
  apply: handle type-changing patch correctly.
  apply: split out removal and creation into different phases.
  apply: check D/F conflicts more carefully.
  typechange tests for git apply (currently failing)
This commit is contained in:
Junio C Hamano 2006-07-25 12:50:23 -07:00
commit a8861ea81b
2 changed files with 163 additions and 14 deletions

View File

@ -1664,13 +1664,14 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
return 0; return 0;
} }
static int check_patch(struct patch *patch) static int check_patch(struct patch *patch, struct patch *prev_patch)
{ {
struct stat st; struct stat st;
const char *old_name = patch->old_name; const char *old_name = patch->old_name;
const char *new_name = patch->new_name; const char *new_name = patch->new_name;
const char *name = old_name ? old_name : new_name; const char *name = old_name ? old_name : new_name;
struct cache_entry *ce = NULL; struct cache_entry *ce = NULL;
int ok_if_exists;
if (old_name) { if (old_name) {
int changed = 0; int changed = 0;
@ -1728,13 +1729,33 @@ static int check_patch(struct patch *patch)
old_name, st_mode, patch->old_mode); old_name, st_mode, patch->old_mode);
} }
if (new_name && prev_patch && prev_patch->is_delete &&
!strcmp(prev_patch->old_name, new_name))
/* A type-change diff is always split into a patch to
* delete old, immediately followed by a patch to
* create new (see diff.c::run_diff()); in such a case
* it is Ok that the entry to be deleted by the
* previous patch is still in the working tree and in
* the index.
*/
ok_if_exists = 1;
else
ok_if_exists = 0;
if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) { if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0) if (check_index &&
cache_name_pos(new_name, strlen(new_name)) >= 0 &&
!ok_if_exists)
return error("%s: already exists in index", new_name); return error("%s: already exists in index", new_name);
if (!cached) { if (!cached) {
if (!lstat(new_name, &st)) struct stat nst;
return error("%s: already exists in working directory", new_name); if (!lstat(new_name, &nst)) {
if (errno != ENOENT) if (S_ISDIR(nst.st_mode) || ok_if_exists)
; /* ok */
else
return error("%s: already exists in working directory", new_name);
}
else if ((errno != ENOENT) && (errno != ENOTDIR))
return error("%s: %s", new_name, strerror(errno)); return error("%s: %s", new_name, strerror(errno));
} }
if (!patch->new_mode) { if (!patch->new_mode) {
@ -1762,10 +1783,13 @@ static int check_patch(struct patch *patch)
static int check_patch_list(struct patch *patch) static int check_patch_list(struct patch *patch)
{ {
struct patch *prev_patch = NULL;
int error = 0; int error = 0;
for (;patch ; patch = patch->next) for (prev_patch = NULL; patch ; patch = patch->next) {
error |= check_patch(patch); error |= check_patch(patch, prev_patch);
prev_patch = patch;
}
return error; return error;
} }
@ -2010,6 +2034,16 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
return; return;
} }
if (errno == EEXIST || errno == EACCES) {
/* We may be trying to create a file where a directory
* used to be.
*/
struct stat st;
errno = 0;
if (!lstat(path, &st) && S_ISDIR(st.st_mode) && !rmdir(path))
errno = EEXIST;
}
if (errno == EEXIST) { if (errno == EEXIST) {
unsigned int nr = getpid(); unsigned int nr = getpid();
@ -2044,32 +2078,42 @@ static void create_file(struct patch *patch)
cache_tree_invalidate_path(active_cache_tree, path); cache_tree_invalidate_path(active_cache_tree, path);
} }
static void write_out_one_result(struct patch *patch) /* phase zero is to remove, phase one is to create */
static void write_out_one_result(struct patch *patch, int phase)
{ {
if (patch->is_delete > 0) { if (patch->is_delete > 0) {
remove_file(patch); if (phase == 0)
remove_file(patch);
return; return;
} }
if (patch->is_new > 0 || patch->is_copy) { if (patch->is_new > 0 || patch->is_copy) {
create_file(patch); if (phase == 1)
create_file(patch);
return; return;
} }
/* /*
* Rename or modification boils down to the same * Rename or modification boils down to the same
* thing: remove the old, write the new * thing: remove the old, write the new
*/ */
remove_file(patch); if (phase == 0)
remove_file(patch);
if (phase == 1)
create_file(patch); create_file(patch);
} }
static void write_out_results(struct patch *list, int skipped_patch) static void write_out_results(struct patch *list, int skipped_patch)
{ {
int phase;
if (!list && !skipped_patch) if (!list && !skipped_patch)
die("No changes"); die("No changes");
while (list) { for (phase = 0; phase < 2; phase++) {
write_out_one_result(list); struct patch *l = list;
list = list->next; while (l) {
write_out_one_result(l, phase);
l = l->next;
}
} }
} }

105
t/t4114-apply-typechange.sh Executable file
View File

@ -0,0 +1,105 @@
#!/bin/sh
#
# Copyright (c) 2006 Eric Wong
#
test_description='git-apply should not get confused with type changes.
'
. ./test-lib.sh
test_expect_success 'setup repository and commits' '
echo "hello world" > foo &&
echo "hi planet" > bar &&
git update-index --add foo bar &&
git commit -m initial &&
git branch initial &&
rm -f foo &&
ln -s bar foo &&
git update-index foo &&
git commit -m "foo symlinked to bar" &&
git branch foo-symlinked-to-bar &&
rm -f foo &&
echo "how far is the sun?" > foo &&
git update-index foo &&
git commit -m "foo back to file" &&
git branch foo-back-to-file &&
rm -f foo &&
git update-index --remove foo &&
mkdir foo &&
echo "if only I knew" > foo/baz &&
git update-index --add foo/baz &&
git commit -m "foo becomes a directory" &&
git branch "foo-becomes-a-directory" &&
echo "hello world" > foo/baz &&
git update-index foo/baz &&
git commit -m "foo/baz is the original foo" &&
git branch foo-baz-renamed-from-foo
'
test_expect_success 'file renamed from foo to foo/baz' '
git checkout -f initial &&
git diff-tree -M -p HEAD foo-baz-renamed-from-foo > patch &&
git apply --index < patch
'
test_debug 'cat patch'
test_expect_success 'file renamed from foo/baz to foo' '
git checkout -f foo-baz-renamed-from-foo &&
git diff-tree -M -p HEAD initial > patch &&
git apply --index < patch
'
test_debug 'cat patch'
test_expect_success 'directory becomes file' '
git checkout -f foo-becomes-a-directory &&
git diff-tree -p HEAD initial > patch &&
git apply --index < patch
'
test_debug 'cat patch'
test_expect_success 'file becomes directory' '
git checkout -f initial &&
git diff-tree -p HEAD foo-becomes-a-directory > patch &&
git apply --index < patch
'
test_debug 'cat patch'
test_expect_success 'file becomes symlink' '
git checkout -f initial &&
git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
git apply --index < patch
'
test_debug 'cat patch'
test_expect_success 'symlink becomes file' '
git checkout -f foo-symlinked-to-bar &&
git diff-tree -p HEAD foo-back-to-file > patch &&
git apply --index < patch
'
test_debug 'cat patch'
test_expect_success 'symlink becomes directory' '
git checkout -f foo-symlinked-to-bar &&
git diff-tree -p HEAD foo-becomes-a-directory > patch &&
git apply --index < patch
'
test_debug 'cat patch'
test_expect_success 'directory becomes symlink' '
git checkout -f foo-becomes-a-directory &&
git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
git apply --index < patch
'
test_debug 'cat patch'
test_done