fast-import: allow ls or filecopy of the root tree

Commit 178e1de (fast-import: don't allow 'ls' of path with empty components, 2012-03-09) restricted paths which: . contain an empty directory component (e.g. foo//bar is invalid), . end with a directory separator (e.g. foo/ is invalid), . start with a directory separator (e.g. /foo is invalid). However, the implementation also caught the empty path, which should represent the root tree. Relax this restriction so that the empty path is explicitly allowed and refers to the root tree. Reported-by: Dave Abrahams <dave@boostpro.com> Signed-off-by: John Keeping <john@keeping.me.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>

John Keeping committed Jun 23, 2013 at 15:58 UTC e0eb6b9720db743993d9374e366cb6b7664f0a6d
2 files changed +24 -15
fast-import.c
+22 -13
@@ -1629,7 +1629,8 @@ del_entry:
1629 static int tree_content_get(
1630 struct tree_entry *root,
1631 const char *p,
1632 - struct tree_entry *leaf)
1632 + struct tree_entry *leaf,
1633 + int allow_root)
1634 {
1635 struct tree_content *t;
1636 const char *slash1;
@@ -1641,31 +1642,39 @@ static int tree_content_get(
1642 n = slash1 - p;
1643 else
1644 n = strlen(p);
1644 - if (!n)
1645 + if (!n && !allow_root)
1646 die("Empty path component found in input");
1647
1648 if (!root->tree)
1649 load_tree(root);
1650 +
1651 + if (!n) {
1652 + e = root;
1653 + goto found_entry;
1654 + }
1655 +
1656 t = root->tree;
1657 for (i = 0; i < t->entry_count; i++) {
1658 e = t->entries[i];
1659 if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
1653 - if (!slash1) {
1654 - memcpy(leaf, e, sizeof(*leaf));
1655 - if (e->tree && is_null_sha1(e->versions[1].sha1))
1656 - leaf->tree = dup_tree_content(e->tree);
1657 - else
1658 - leaf->tree = NULL;
1659 - return 1;
1660 - }
1660 + if (!slash1)
1661 + goto found_entry;
1662 if (!S_ISDIR(e->versions[1].mode))
1663 return 0;
1664 if (!e->tree)
1665 load_tree(e);
1665 - return tree_content_get(e, slash1 + 1, leaf);
1666 + return tree_content_get(e, slash1 + 1, leaf, 0);
1667 }
1668 }
1669 return 0;
1670 +
1671 +found_entry:
1672 + memcpy(leaf, e, sizeof(*leaf));
1673 + if (e->tree && is_null_sha1(e->versions[1].sha1))
1674 + leaf->tree = dup_tree_content(e->tree);
1675 + else
1676 + leaf->tree = NULL;
1677 + return 1;
1678 }
1679
1680 static int update_branch(struct branch *b)
@@ -2415,7 +2424,7 @@ static void file_change_cr(struct branch *b, int rename)
2424 if (rename)
2425 tree_content_remove(&b->branch_tree, s, &leaf);
2426 else
2418 - tree_content_get(&b->branch_tree, s, &leaf);
2427 + tree_content_get(&b->branch_tree, s, &leaf, 1);
2428 if (!leaf.versions[1].mode)
2429 die("Path %s not in branch", s);
2430 if (!*d) { /* C "path/to/subdir" "" */
@@ -3067,7 +3076,7 @@ static void parse_ls(struct branch *b)
3076 die("Garbage after path in: %s", command_buf.buf);
3077 p = uq.buf;
3078 }
3070 - tree_content_get(root, p, &leaf);
3079 + tree_content_get(root, p, &leaf, 1);
3080 /*
3081 * A directory in preparation would have a sha1 of zero
3082 * until it is saved. Save, for simplicity.
t/t9300-fast-import.sh
+2 -2
@@ -1253,7 +1253,7 @@ test_expect_success \
1253 git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
1254 compare_diff_raw expect actual'
1255
1256 -test_expect_failure \
1256 +test_expect_success \
1257 'N: copy root by path' \
1258 'cat >expect <<-\EOF &&
1259 :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf oldroot/file2/newf
@@ -2988,7 +2988,7 @@ test_expect_success 'S: ls with garbage after sha1 must fail' '
2988 ###
2989 # Setup is carried over from series S.
2990
2991 -test_expect_failure 'T: ls root tree' '
2991 +test_expect_success 'T: ls root tree' '
2992 sed -e "s/Z\$//" >expect <<-EOF &&
2993 040000 tree $(git rev-parse S^{tree}) Z
2994 EOF