Merge branch 'jt/fast-export-copy-modify-fix' into maint
"git fast-export" with -M/-C option issued "copy" instruction on a path that is simultaneously modified, which was incorrect. * jt/fast-export-copy-modify-fix: fast-export: do not copy from modified file
This commit is contained in:
commit
120ce97f9d
@ -344,6 +344,7 @@ static void show_filemodify(struct diff_queue_struct *q,
|
|||||||
struct diff_options *options, void *data)
|
struct diff_options *options, void *data)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
struct string_list *changed = data;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle files below a directory first, in case they are all deleted
|
* Handle files below a directory first, in case they are all deleted
|
||||||
@ -359,20 +360,31 @@ static void show_filemodify(struct diff_queue_struct *q,
|
|||||||
case DIFF_STATUS_DELETED:
|
case DIFF_STATUS_DELETED:
|
||||||
printf("D ");
|
printf("D ");
|
||||||
print_path(spec->path);
|
print_path(spec->path);
|
||||||
|
string_list_insert(changed, spec->path);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DIFF_STATUS_COPIED:
|
case DIFF_STATUS_COPIED:
|
||||||
case DIFF_STATUS_RENAMED:
|
case DIFF_STATUS_RENAMED:
|
||||||
printf("%c ", q->queue[i]->status);
|
/*
|
||||||
print_path(ospec->path);
|
* If a change in the file corresponding to ospec->path
|
||||||
putchar(' ');
|
* has been observed, we cannot trust its contents
|
||||||
print_path(spec->path);
|
* because the diff is calculated based on the prior
|
||||||
putchar('\n');
|
* contents, not the current contents. So, declare a
|
||||||
|
* copy or rename only if there was no change observed.
|
||||||
|
*/
|
||||||
|
if (!string_list_has_string(changed, ospec->path)) {
|
||||||
|
printf("%c ", q->queue[i]->status);
|
||||||
|
print_path(ospec->path);
|
||||||
|
putchar(' ');
|
||||||
|
print_path(spec->path);
|
||||||
|
string_list_insert(changed, spec->path);
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
if (!oidcmp(&ospec->oid, &spec->oid) &&
|
if (!oidcmp(&ospec->oid, &spec->oid) &&
|
||||||
ospec->mode == spec->mode)
|
ospec->mode == spec->mode)
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
|
|
||||||
case DIFF_STATUS_TYPE_CHANGED:
|
case DIFF_STATUS_TYPE_CHANGED:
|
||||||
@ -393,6 +405,7 @@ static void show_filemodify(struct diff_queue_struct *q,
|
|||||||
get_object_mark(object));
|
get_object_mark(object));
|
||||||
}
|
}
|
||||||
print_path(spec->path);
|
print_path(spec->path);
|
||||||
|
string_list_insert(changed, spec->path);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -528,7 +541,8 @@ static void anonymize_ident_line(const char **beg, const char **end)
|
|||||||
*end = out->buf + out->len;
|
*end = out->buf + out->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_commit(struct commit *commit, struct rev_info *rev)
|
static void handle_commit(struct commit *commit, struct rev_info *rev,
|
||||||
|
struct string_list *paths_of_changed_objects)
|
||||||
{
|
{
|
||||||
int saved_output_format = rev->diffopt.output_format;
|
int saved_output_format = rev->diffopt.output_format;
|
||||||
const char *commit_buffer;
|
const char *commit_buffer;
|
||||||
@ -615,6 +629,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
|
|||||||
if (full_tree)
|
if (full_tree)
|
||||||
printf("deleteall\n");
|
printf("deleteall\n");
|
||||||
log_tree_diff_flush(rev);
|
log_tree_diff_flush(rev);
|
||||||
|
string_list_clear(paths_of_changed_objects, 0);
|
||||||
rev->diffopt.output_format = saved_output_format;
|
rev->diffopt.output_format = saved_output_format;
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -630,14 +645,15 @@ static void *anonymize_tag(const void *old, size_t *len)
|
|||||||
return strbuf_detach(&out, len);
|
return strbuf_detach(&out, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_tail(struct object_array *commits, struct rev_info *revs)
|
static void handle_tail(struct object_array *commits, struct rev_info *revs,
|
||||||
|
struct string_list *paths_of_changed_objects)
|
||||||
{
|
{
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
while (commits->nr) {
|
while (commits->nr) {
|
||||||
commit = (struct commit *)commits->objects[commits->nr - 1].item;
|
commit = (struct commit *)commits->objects[commits->nr - 1].item;
|
||||||
if (has_unshown_parent(commit))
|
if (has_unshown_parent(commit))
|
||||||
return;
|
return;
|
||||||
handle_commit(commit, revs);
|
handle_commit(commit, revs, paths_of_changed_objects);
|
||||||
commits->nr--;
|
commits->nr--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -977,6 +993,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
|||||||
char *export_filename = NULL, *import_filename = NULL;
|
char *export_filename = NULL, *import_filename = NULL;
|
||||||
uint32_t lastimportid;
|
uint32_t lastimportid;
|
||||||
struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
|
struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
|
||||||
|
struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
|
||||||
struct option options[] = {
|
struct option options[] = {
|
||||||
OPT_INTEGER(0, "progress", &progress,
|
OPT_INTEGER(0, "progress", &progress,
|
||||||
N_("show progress after <n> objects")),
|
N_("show progress after <n> objects")),
|
||||||
@ -1049,14 +1066,15 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
|||||||
if (prepare_revision_walk(&revs))
|
if (prepare_revision_walk(&revs))
|
||||||
die("revision walk setup failed");
|
die("revision walk setup failed");
|
||||||
revs.diffopt.format_callback = show_filemodify;
|
revs.diffopt.format_callback = show_filemodify;
|
||||||
|
revs.diffopt.format_callback_data = &paths_of_changed_objects;
|
||||||
DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
|
DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
|
||||||
while ((commit = get_revision(&revs))) {
|
while ((commit = get_revision(&revs))) {
|
||||||
if (has_unshown_parent(commit)) {
|
if (has_unshown_parent(commit)) {
|
||||||
add_object_array(&commit->object, NULL, &commits);
|
add_object_array(&commit->object, NULL, &commits);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
handle_commit(commit, &revs);
|
handle_commit(commit, &revs, &paths_of_changed_objects);
|
||||||
handle_tail(&commits, &revs);
|
handle_tail(&commits, &revs, &paths_of_changed_objects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ test_expect_success 'fast-export -C -C | fast-import' '
|
|||||||
mkdir new &&
|
mkdir new &&
|
||||||
git --git-dir=new/.git init &&
|
git --git-dir=new/.git init &&
|
||||||
git fast-export -C -C --signed-tags=strip --all > output &&
|
git fast-export -C -C --signed-tags=strip --all > output &&
|
||||||
grep "^C file6 file7\$" output &&
|
grep "^C file2 file4\$" output &&
|
||||||
cat output |
|
cat output |
|
||||||
(cd new &&
|
(cd new &&
|
||||||
git fast-import &&
|
git fast-import &&
|
||||||
@ -522,4 +522,22 @@ test_expect_success 'delete refspec' '
|
|||||||
test_cmp expected actual
|
test_cmp expected actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'when using -C, do not declare copy when source of copy is also modified' '
|
||||||
|
test_create_repo src &&
|
||||||
|
echo a_line >src/file.txt &&
|
||||||
|
git -C src add file.txt &&
|
||||||
|
git -C src commit -m 1st_commit &&
|
||||||
|
|
||||||
|
cp src/file.txt src/file2.txt &&
|
||||||
|
echo another_line >>src/file.txt &&
|
||||||
|
git -C src add file.txt file2.txt &&
|
||||||
|
git -C src commit -m 2nd_commit &&
|
||||||
|
|
||||||
|
test_create_repo dst &&
|
||||||
|
git -C src fast-export --all -C | git -C dst fast-import &&
|
||||||
|
git -C src show >expected &&
|
||||||
|
git -C dst show >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user