diff --git a/fast-import.c b/fast-import.c
index 3a0aaad..8643e23 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1552,6 +1552,31 @@ static int tree_content_set(
return 1;
}
+static int tree_content_clear(
+ struct tree_entry *root,
+ struct tree_entry *backup_leaf)
+{
+ if (backup_leaf)
+ memcpy(backup_leaf, root, sizeof(*backup_leaf));
+ else if (root->tree)
+ release_tree_content_recursive(root->tree);
+ root->tree = NULL;
+ hashclr(root->versions[1].sha1);
+ return 1;
+}
+
+static int is_empty_tree(struct tree_entry *root)
+{
+ int n;
+ if (!root->tree)
+ load_tree(root);
+ for (n = 0; n < root->tree->entry_count; n++) {
+ if (root->tree->entries[n]->versions[1].mode)
+ return 0;
+ }
+ return 1;
+}
+
static int tree_content_remove(
struct tree_entry *root,
const char *p,@@ -1587,11 +1612,9 @@ static int tree_content_remove(
if (!e->tree)
load_tree(e);
if (tree_content_remove(e, slash1 + 1, backup_leaf)) {
- for (n = 0; n < e->tree->entry_count; n++) {
- if (e->tree->entries[n]->versions[1].mode) {
- hashclr(root->versions[1].sha1);
- return 1;
- }
+ if (!is_empty_tree(e)) {
+ hashclr(root->versions[1].sha1);
+ return 1;
}
backup_leaf = NULL;
goto del_entry;@@ -1613,6 +1636,19 @@ del_entry:
return 1;
}
+static int tree_content_copy_root(
+ struct tree_entry *root,
+ struct tree_entry *leaf
+)
+{
+ memcpy(leaf, root, sizeof(*leaf));
+ if (root->tree && is_null_sha1(root->versions[1].sha1))
+ leaf->tree = dup_tree_content(root->tree);
+ else
+ leaf->tree = NULL;
+ return 1;
+}
+
static int tree_content_get(
struct tree_entry *root,
const char *p,@@ -2329,10 +2365,17 @@ static void file_change_cr(struct branch *b, int rename)
}
memset(&leaf, 0, sizeof(leaf));
- if (rename)
- tree_content_remove(&b->branch_tree, s, &leaf);
- else
- tree_content_get(&b->branch_tree, s, &leaf);
+ if (rename) {
+ if (!*s)
+ tree_content_clear(&b->branch_tree, &leaf);
+ else
+ tree_content_remove(&b->branch_tree, s, &leaf);
+ } else {
+ if (!*s)
+ tree_content_copy_root(&b->branch_tree, &leaf);
+ else
+ tree_content_get(&b->branch_tree, s, &leaf);
+ }
if (!leaf.versions[1].mode)
die("Path %s not in branch", s);
if (!*d) { /* C "path/to/subdir" "" */@@ -2342,6 +2385,14 @@ static void file_change_cr(struct branch *b, int rename)
leaf.tree);
return;
}
+ /*
+ * Git does not track empty, non-toplevel directories.
+ * At this point destination is non-toplevel, so if source is
+ * toplevel and empty, leave it as is all empty.
+ */
+ if (!*s && is_empty_tree(&leaf)) {
+ return;
+ }
tree_content_set(&b->branch_tree, d,
leaf.versions[1].sha1,
leaf.versions[1].mode,@@ -2969,7 +3020,11 @@ static void parse_ls(struct branch *b)
die("Garbage after path in: %s", command_buf.buf);
p = uq.buf;
}
- tree_content_get(root, p, &leaf);
+ if (!*p) {
+ if (!is_empty_tree(root))
+ tree_content_copy_root(root, &leaf);
+ } else
+ tree_content_get(root, p, &leaf);
/*
* A directory in preparation would have a sha1 of zero
* until it is saved. Save, for simplicity.diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index f256475..0e46cf0 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -978,6 +978,64 @@ test_expect_success PIPE 'N: empty directory reads as missing' '
'
test_expect_success \
+ 'N: ls root directory' \
+ 'cat >expect <<-\EOF &&
+ missing
+ 040000
+ EOF
+ cat >input <<-EOF &&
+ commit refs/heads/ls-root
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ On empty tree: ls add file ls.
+ COMMIT
+ ls ""
+ M 644 inline file1
+ data 0
+ ls ""
+ EOF
+ git fast-import --quiet <input >tmp &&
+ cat tmp | cut -d " " -f 1 >actual &&
+ test_cmp expect actual'
+
+test_expect_success \
+ 'N: move root directory' \
+ 'echo "root/a/b" >expect &&
+ cat >input <<-EOF &&
+ commit refs/heads/move-root
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ Add stuff and move it.
+ COMMIT
+ M 644 inline a/b
+ data 0
+ R "" root
+ EOF
+ git fast-import <input &&
+ git ls-tree -r --name-only refs/heads/move-root >actual &&
+ test_cmp expect actual'
+
+test_expect_success \
+ 'N: copy root directory' \
+ 'cat <<-\EOF >expect &&
+ a/b
+ a/root/a/b
+ EOF
+ cat >input <<-EOF &&
+ commit refs/heads/copy-root
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ Add stuff and move it.
+ COMMIT
+ M 644 inline a/b
+ data 0
+ C "" a/root
+ EOF
+ git fast-import <input &&
+ git ls-tree -r --name-only refs/heads/copy-root >actual &&
+ test_cmp expect actual'
+
+test_expect_success \
'N: copy root directory by tree hash' \
'cat >expect <<-\EOF &&
:100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D file3/newf
--
1.7.3.4