Skip to content

Commit 45792b6

Browse files
committed
Merge branch 'di/fast-import-deltified-tree'
* di/fast-import-deltified-tree: fast-import: prevent producing bad delta fast-import: add a test for tree delta base corruption
2 parents 0b98954 + 8fb3ad7 commit 45792b6

File tree

2 files changed

+71
-5
lines changed

2 files changed

+71
-5
lines changed

fast-import.c

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ Format of STDIN stream:
170170
#define DEPTH_BITS 13
171171
#define MAX_DEPTH ((1<<DEPTH_BITS)-1)
172172

173+
/*
174+
* We abuse the setuid bit on directories to mean "do not delta".
175+
*/
176+
#define NO_DELTA S_ISUID
177+
173178
struct object_entry {
174179
struct pack_idx_entry idx;
175180
struct object_entry *next;
@@ -1416,8 +1421,9 @@ static void mktree(struct tree_content *t, int v, struct strbuf *b)
14161421
struct tree_entry *e = t->entries[i];
14171422
if (!e->versions[v].mode)
14181423
continue;
1419-
strbuf_addf(b, "%o %s%c", (unsigned int)e->versions[v].mode,
1420-
e->name->str_dat, '\0');
1424+
strbuf_addf(b, "%o %s%c",
1425+
(unsigned int)(e->versions[v].mode & ~NO_DELTA),
1426+
e->name->str_dat, '\0');
14211427
strbuf_add(b, e->versions[v].sha1, 20);
14221428
}
14231429
}
@@ -1427,7 +1433,7 @@ static void store_tree(struct tree_entry *root)
14271433
struct tree_content *t = root->tree;
14281434
unsigned int i, j, del;
14291435
struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 };
1430-
struct object_entry *le;
1436+
struct object_entry *le = NULL;
14311437

14321438
if (!is_null_sha1(root->versions[1].sha1))
14331439
return;
@@ -1437,7 +1443,8 @@ static void store_tree(struct tree_entry *root)
14371443
store_tree(t->entries[i]);
14381444
}
14391445

1440-
le = find_object(root->versions[0].sha1);
1446+
if (!(root->versions[0].mode & NO_DELTA))
1447+
le = find_object(root->versions[0].sha1);
14411448
if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) {
14421449
mktree(t, 0, &old_tree);
14431450
lo.data = old_tree;
@@ -1471,6 +1478,7 @@ static void tree_content_replace(
14711478
{
14721479
if (!S_ISDIR(mode))
14731480
die("Root cannot be a non-directory");
1481+
hashclr(root->versions[0].sha1);
14741482
hashcpy(root->versions[1].sha1, sha1);
14751483
if (root->tree)
14761484
release_tree_content_recursive(root->tree);
@@ -1515,6 +1523,23 @@ static int tree_content_set(
15151523
if (e->tree)
15161524
release_tree_content_recursive(e->tree);
15171525
e->tree = subtree;
1526+
1527+
/*
1528+
* We need to leave e->versions[0].sha1 alone
1529+
* to avoid modifying the preimage tree used
1530+
* when writing out the parent directory.
1531+
* But after replacing the subdir with a
1532+
* completely different one, it's not a good
1533+
* delta base any more, and besides, we've
1534+
* thrown away the tree entries needed to
1535+
* make a delta against it.
1536+
*
1537+
* So let's just explicitly disable deltas
1538+
* for the subtree.
1539+
*/
1540+
if (S_ISDIR(e->versions[0].mode))
1541+
e->versions[0].mode |= NO_DELTA;
1542+
15181543
hashclr(root->versions[1].sha1);
15191544
return 1;
15201545
}
@@ -2938,7 +2963,7 @@ static void print_ls(int mode, const unsigned char *sha1, const char *path)
29382963
/* mode SP type SP object_name TAB path LF */
29392964
strbuf_reset(&line);
29402965
strbuf_addf(&line, "%06o %s %s\t",
2941-
mode, type, sha1_to_hex(sha1));
2966+
mode & ~NO_DELTA, type, sha1_to_hex(sha1));
29422967
quote_c_style(path, &line, NULL, 0);
29432968
strbuf_addch(&line, '\n');
29442969
}

t/t9300-fast-import.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,47 @@ test_expect_success \
833833
git diff-tree --abbrev --raw L^ L >output &&
834834
test_cmp expect output'
835835

836+
cat >input <<INPUT_END
837+
blob
838+
mark :1
839+
data <<EOF
840+
the data
841+
EOF
842+
843+
commit refs/heads/L2
844+
committer C O Mitter <committer@example.com> 1112912473 -0700
845+
data <<COMMIT
846+
init L2
847+
COMMIT
848+
M 644 :1 a/b/c
849+
M 644 :1 a/b/d
850+
M 644 :1 a/e/f
851+
852+
commit refs/heads/L2
853+
committer C O Mitter <committer@example.com> 1112912473 -0700
854+
data <<COMMIT
855+
update L2
856+
COMMIT
857+
C a g
858+
C a/e g/b
859+
M 644 :1 g/b/h
860+
INPUT_END
861+
862+
cat <<EOF >expect
863+
g/b/f
864+
g/b/h
865+
EOF
866+
867+
test_expect_success \
868+
'L: nested tree copy does not corrupt deltas' \
869+
'git fast-import <input &&
870+
git ls-tree L2 g/b/ >tmp &&
871+
cat tmp | cut -f 2 >actual &&
872+
test_cmp expect actual &&
873+
git fsck `git rev-parse L2`'
874+
875+
git update-ref -d refs/heads/L2
876+
836877
###
837878
### series M
838879
###

0 commit comments

Comments
 (0)