Skip to content

Commit 120ce97

Browse files
committed
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
2 parents 5253ad1 + b3e8ca8 commit 120ce97

File tree

2 files changed

+51
-15
lines changed

2 files changed

+51
-15
lines changed

builtin/fast-export.c

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ static void show_filemodify(struct diff_queue_struct *q,
344344
struct diff_options *options, void *data)
345345
{
346346
int i;
347+
struct string_list *changed = data;
347348

348349
/*
349350
* 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,
359360
case DIFF_STATUS_DELETED:
360361
printf("D ");
361362
print_path(spec->path);
363+
string_list_insert(changed, spec->path);
362364
putchar('\n');
363365
break;
364366

365367
case DIFF_STATUS_COPIED:
366368
case DIFF_STATUS_RENAMED:
367-
printf("%c ", q->queue[i]->status);
368-
print_path(ospec->path);
369-
putchar(' ');
370-
print_path(spec->path);
371-
putchar('\n');
372-
373-
if (!oidcmp(&ospec->oid, &spec->oid) &&
374-
ospec->mode == spec->mode)
375-
break;
369+
/*
370+
* If a change in the file corresponding to ospec->path
371+
* has been observed, we cannot trust its contents
372+
* because the diff is calculated based on the prior
373+
* contents, not the current contents. So, declare a
374+
* copy or rename only if there was no change observed.
375+
*/
376+
if (!string_list_has_string(changed, ospec->path)) {
377+
printf("%c ", q->queue[i]->status);
378+
print_path(ospec->path);
379+
putchar(' ');
380+
print_path(spec->path);
381+
string_list_insert(changed, spec->path);
382+
putchar('\n');
383+
384+
if (!oidcmp(&ospec->oid, &spec->oid) &&
385+
ospec->mode == spec->mode)
386+
break;
387+
}
376388
/* fallthrough */
377389

378390
case DIFF_STATUS_TYPE_CHANGED:
@@ -393,6 +405,7 @@ static void show_filemodify(struct diff_queue_struct *q,
393405
get_object_mark(object));
394406
}
395407
print_path(spec->path);
408+
string_list_insert(changed, spec->path);
396409
putchar('\n');
397410
break;
398411

@@ -528,7 +541,8 @@ static void anonymize_ident_line(const char **beg, const char **end)
528541
*end = out->buf + out->len;
529542
}
530543

531-
static void handle_commit(struct commit *commit, struct rev_info *rev)
544+
static void handle_commit(struct commit *commit, struct rev_info *rev,
545+
struct string_list *paths_of_changed_objects)
532546
{
533547
int saved_output_format = rev->diffopt.output_format;
534548
const char *commit_buffer;
@@ -615,6 +629,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
615629
if (full_tree)
616630
printf("deleteall\n");
617631
log_tree_diff_flush(rev);
632+
string_list_clear(paths_of_changed_objects, 0);
618633
rev->diffopt.output_format = saved_output_format;
619634

620635
printf("\n");
@@ -630,14 +645,15 @@ static void *anonymize_tag(const void *old, size_t *len)
630645
return strbuf_detach(&out, len);
631646
}
632647

633-
static void handle_tail(struct object_array *commits, struct rev_info *revs)
648+
static void handle_tail(struct object_array *commits, struct rev_info *revs,
649+
struct string_list *paths_of_changed_objects)
634650
{
635651
struct commit *commit;
636652
while (commits->nr) {
637653
commit = (struct commit *)commits->objects[commits->nr - 1].item;
638654
if (has_unshown_parent(commit))
639655
return;
640-
handle_commit(commit, revs);
656+
handle_commit(commit, revs, paths_of_changed_objects);
641657
commits->nr--;
642658
}
643659
}
@@ -977,6 +993,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
977993
char *export_filename = NULL, *import_filename = NULL;
978994
uint32_t lastimportid;
979995
struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
996+
struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
980997
struct option options[] = {
981998
OPT_INTEGER(0, "progress", &progress,
982999
N_("show progress after <n> objects")),
@@ -1049,14 +1066,15 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
10491066
if (prepare_revision_walk(&revs))
10501067
die("revision walk setup failed");
10511068
revs.diffopt.format_callback = show_filemodify;
1069+
revs.diffopt.format_callback_data = &paths_of_changed_objects;
10521070
DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
10531071
while ((commit = get_revision(&revs))) {
10541072
if (has_unshown_parent(commit)) {
10551073
add_object_array(&commit->object, NULL, &commits);
10561074
}
10571075
else {
1058-
handle_commit(commit, &revs);
1059-
handle_tail(&commits, &revs);
1076+
handle_commit(commit, &revs, &paths_of_changed_objects);
1077+
handle_tail(&commits, &revs, &paths_of_changed_objects);
10601078
}
10611079
}
10621080

t/t9350-fast-export.sh

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ test_expect_success 'fast-export -C -C | fast-import' '
234234
mkdir new &&
235235
git --git-dir=new/.git init &&
236236
git fast-export -C -C --signed-tags=strip --all > output &&
237-
grep "^C file6 file7\$" output &&
237+
grep "^C file2 file4\$" output &&
238238
cat output |
239239
(cd new &&
240240
git fast-import &&
@@ -522,4 +522,22 @@ test_expect_success 'delete refspec' '
522522
test_cmp expected actual
523523
'
524524

525+
test_expect_success 'when using -C, do not declare copy when source of copy is also modified' '
526+
test_create_repo src &&
527+
echo a_line >src/file.txt &&
528+
git -C src add file.txt &&
529+
git -C src commit -m 1st_commit &&
530+
531+
cp src/file.txt src/file2.txt &&
532+
echo another_line >>src/file.txt &&
533+
git -C src add file.txt file2.txt &&
534+
git -C src commit -m 2nd_commit &&
535+
536+
test_create_repo dst &&
537+
git -C src fast-export --all -C | git -C dst fast-import &&
538+
git -C src show >expected &&
539+
git -C dst show >actual &&
540+
test_cmp expected actual
541+
'
542+
525543
test_done

0 commit comments

Comments
 (0)