[RFC/PATCH] fast-import: treat filemodify with empty tree as delete
From: Jonathan Nieder <hidden>
Date: 2016-06-15 22:50:14
Subsystem:
the rest · Maintainer:
Linus Torvalds
Jonathan Nieder wrote:
Maybe fast-import should make the check itself, to avoid writing trees with empty directories that would be hard to re-create with "git write-tree".
Like this, maybe?
-- 8< --
Subject: fast-import: treat filemodify with empty tree as delete
Traditionally, git trees do not contain entries for empty
subdirectories. Generally speaking, subtrees are not created or
destroyed explicitly; instead, they automatically appear when needed
to hold regular files, symlinks, and submodules.
v1.7.3-rc0~75^2 (Teach fast-import to import subtrees named by tree
id, 2010-06-30) changed that, by allowing an empty subtree to be
included in a fast-import stream explicitly:
M 040000 4b825dc642cb6eb9a060e54bf8d69288fbee4904 subdir
That was unintentional. Better and more closely analogous to "git
read-tree --prefix" to treat such an input line as a request to delete
("to empty") subdir.
Noticed-by: David Barr [off-list ref]
Signed-off-by: Jonathan Nieder <redacted>
---
Tests use the "ls" command from vcs-svn-pu. The actual change
would apply cleanly to master or maint, though.
fast-import.c | 10 ++++
t/t9300-fast-import.sh | 107 ++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 109 insertions(+), 8 deletions(-)
diff --git a/fast-import.c b/fast-import.c
index e62f34d..c774893 100644
--- a/fast-import.c
+++ b/fast-import.c@@ -2196,6 +2196,16 @@ static void file_change_m(struct branch *b) p = uq.buf; } + /* + * Git does not track empty, non-toplevel directories. + */ + if (S_ISDIR(mode) && + !memcmp(sha1, (unsigned char *) EMPTY_TREE_SHA1_BIN, 20) && + *p) { + tree_content_remove(&b->branch_tree, p, NULL); + return; + } + if (S_ISGITLINK(mode)) { if (inline_data) die("Git links cannot be specified 'inline': %s",
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index b0e3bda..c17f704 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh@@ -25,6 +25,14 @@ echo "$@"' >empty +test_expect_success 'setup: have pipes?' ' + rm -f frob && + if mkfifo frob + then + test_set_prereq PIPE + fi +' + ### ### series A ###
@@ -881,6 +889,97 @@ test_expect_success \ git diff-tree -C --find-copies-harder -r N4^ N4 >actual && compare_diff_raw expect actual' +test_expect_success PIPE 'N: read and copy directory' ' + cat >expect <<-\EOF + :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf + :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf + EOF + git update-ref -d refs/heads/N4 && + rm -f backflow && + mkfifo backflow && + ( + exec <backflow && + cat <<-EOF && + commit refs/heads/N4 + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + copy by tree hash, part 2 + COMMIT + + from refs/heads/branch^0 + ls "file2" + EOF + read mode type tree filename && + echo "M 040000 $tree file3" + ) | + git fast-import --cat-blob-fd=3 3>backflow && + git diff-tree -C --find-copies-harder -r N4^ N4 >actual && + compare_diff_raw expect actual +' + +test_expect_success PIPE 'N: read and copy "empty" directory' ' + cat <<-\EOF >expect && + OBJNAME + :000000 100644 OBJNAME OBJNAME A greeting + OBJNAME + :100644 000000 OBJNAME OBJNAME D unrelated + OBJNAME + :000000 100644 OBJNAME OBJNAME A unrelated + EOF + git update-ref -d refs/heads/copy-empty && + rm -f backflow && + mkfifo backflow && + ( + exec <backflow && + cat <<-EOF && + commit refs/heads/copy-empty + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + copy "empty" (missing) directory + COMMIT + + M 100644 inline src/greeting + data <<BLOB + hello + BLOB + C src/greeting dst1/non-greeting + C src/greeting unrelated + # leave behind "empty" src directory + D src/greeting + ls "src" + EOF + read mode type tree filename && + sed -e "s/X\$//" <<-EOF + M $mode $tree dst1 + M $mode $tree dst2 + + commit refs/heads/copy-empty + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + copy empty directory to root + COMMIT + + M $mode $tree X + + commit refs/heads/copy-empty + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + add another file + COMMIT + + M 100644 inline greeting + data <<BLOB + hello + BLOB + EOF + ) | + git fast-import --cat-blob-fd=3 3>backflow && + git rev-list copy-empty | + git diff-tree -r --root --stdin | + sed "s/$_x40/OBJNAME/g" >actual && + test_cmp expect actual +' + test_expect_success \ 'N: copy root directory by tree hash' \ 'cat >expect <<-\EOF &&
@@ -1773,14 +1872,6 @@ test_expect_success 'R: print two blobs to stdout' ' test_cmp expect actual ' -test_expect_success 'setup: have pipes?' ' - rm -f frob && - if mkfifo frob - then - test_set_prereq PIPE - fi -' - test_expect_success PIPE 'R: copy using cat-file' ' expect_id=$(git hash-object big) && expect_len=$(wc -c <big) &&
--
1.7.2.4