Merge branch 'jk/fast-import-empty-ls'
* jk/fast-import-empty-ls: fast-import: allow moving the root tree fast-import: allow ls or filecopy of the root tree fast-import: set valid mode on root tree in "ls" t9300: document fast-import empty path issuesmaint
						commit
						7e39472020
					
				|  | @ -1568,7 +1568,8 @@ static int tree_content_set( | ||||||
| static int tree_content_remove( | static int tree_content_remove( | ||||||
| 	struct tree_entry *root, | 	struct tree_entry *root, | ||||||
| 	const char *p, | 	const char *p, | ||||||
| 	struct tree_entry *backup_leaf) | 	struct tree_entry *backup_leaf, | ||||||
|  | 	int allow_root) | ||||||
| { | { | ||||||
| 	struct tree_content *t; | 	struct tree_content *t; | ||||||
| 	const char *slash1; | 	const char *slash1; | ||||||
|  | @ -1583,6 +1584,12 @@ static int tree_content_remove( | ||||||
|  |  | ||||||
| 	if (!root->tree) | 	if (!root->tree) | ||||||
| 		load_tree(root); | 		load_tree(root); | ||||||
|  |  | ||||||
|  | 	if (!*p && allow_root) { | ||||||
|  | 		e = root; | ||||||
|  | 		goto del_entry; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	t = root->tree; | 	t = root->tree; | ||||||
| 	for (i = 0; i < t->entry_count; i++) { | 	for (i = 0; i < t->entry_count; i++) { | ||||||
| 		e = t->entries[i]; | 		e = t->entries[i]; | ||||||
|  | @ -1599,7 +1606,7 @@ static int tree_content_remove( | ||||||
| 				goto del_entry; | 				goto del_entry; | ||||||
| 			if (!e->tree) | 			if (!e->tree) | ||||||
| 				load_tree(e); | 				load_tree(e); | ||||||
| 			if (tree_content_remove(e, slash1 + 1, backup_leaf)) { | 			if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) { | ||||||
| 				for (n = 0; n < e->tree->entry_count; n++) { | 				for (n = 0; n < e->tree->entry_count; n++) { | ||||||
| 					if (e->tree->entries[n]->versions[1].mode) { | 					if (e->tree->entries[n]->versions[1].mode) { | ||||||
| 						hashclr(root->versions[1].sha1); | 						hashclr(root->versions[1].sha1); | ||||||
|  | @ -1629,7 +1636,8 @@ del_entry: | ||||||
| static int tree_content_get( | static int tree_content_get( | ||||||
| 	struct tree_entry *root, | 	struct tree_entry *root, | ||||||
| 	const char *p, | 	const char *p, | ||||||
| 	struct tree_entry *leaf) | 	struct tree_entry *leaf, | ||||||
|  | 	int allow_root) | ||||||
| { | { | ||||||
| 	struct tree_content *t; | 	struct tree_content *t; | ||||||
| 	const char *slash1; | 	const char *slash1; | ||||||
|  | @ -1641,31 +1649,39 @@ static int tree_content_get( | ||||||
| 		n = slash1 - p; | 		n = slash1 - p; | ||||||
| 	else | 	else | ||||||
| 		n = strlen(p); | 		n = strlen(p); | ||||||
| 	if (!n) | 	if (!n && !allow_root) | ||||||
| 		die("Empty path component found in input"); | 		die("Empty path component found in input"); | ||||||
|  |  | ||||||
| 	if (!root->tree) | 	if (!root->tree) | ||||||
| 		load_tree(root); | 		load_tree(root); | ||||||
|  |  | ||||||
|  | 	if (!n) { | ||||||
|  | 		e = root; | ||||||
|  | 		goto found_entry; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	t = root->tree; | 	t = root->tree; | ||||||
| 	for (i = 0; i < t->entry_count; i++) { | 	for (i = 0; i < t->entry_count; i++) { | ||||||
| 		e = t->entries[i]; | 		e = t->entries[i]; | ||||||
| 		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) { | 		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) { | ||||||
| 			if (!slash1) { | 			if (!slash1) | ||||||
| 				memcpy(leaf, e, sizeof(*leaf)); | 				goto found_entry; | ||||||
| 				if (e->tree && is_null_sha1(e->versions[1].sha1)) |  | ||||||
| 					leaf->tree = dup_tree_content(e->tree); |  | ||||||
| 				else |  | ||||||
| 					leaf->tree = NULL; |  | ||||||
| 				return 1; |  | ||||||
| 			} |  | ||||||
| 			if (!S_ISDIR(e->versions[1].mode)) | 			if (!S_ISDIR(e->versions[1].mode)) | ||||||
| 				return 0; | 				return 0; | ||||||
| 			if (!e->tree) | 			if (!e->tree) | ||||||
| 				load_tree(e); | 				load_tree(e); | ||||||
| 			return tree_content_get(e, slash1 + 1, leaf); | 			return tree_content_get(e, slash1 + 1, leaf, 0); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return 0; | 	return 0; | ||||||
|  |  | ||||||
|  | found_entry: | ||||||
|  | 	memcpy(leaf, e, sizeof(*leaf)); | ||||||
|  | 	if (e->tree && is_null_sha1(e->versions[1].sha1)) | ||||||
|  | 		leaf->tree = dup_tree_content(e->tree); | ||||||
|  | 	else | ||||||
|  | 		leaf->tree = NULL; | ||||||
|  | 	return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int update_branch(struct branch *b) | static int update_branch(struct branch *b) | ||||||
|  | @ -2179,7 +2195,7 @@ static uintmax_t do_change_note_fanout( | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			/* Rename fullpath to realpath */ | 			/* Rename fullpath to realpath */ | ||||||
| 			if (!tree_content_remove(orig_root, fullpath, &leaf)) | 			if (!tree_content_remove(orig_root, fullpath, &leaf, 0)) | ||||||
| 				die("Failed to remove path %s", fullpath); | 				die("Failed to remove path %s", fullpath); | ||||||
| 			tree_content_set(orig_root, realpath, | 			tree_content_set(orig_root, realpath, | ||||||
| 				leaf.versions[1].sha1, | 				leaf.versions[1].sha1, | ||||||
|  | @ -2314,7 +2330,7 @@ static void file_change_m(struct branch *b) | ||||||
|  |  | ||||||
| 	/* Git does not track empty, non-toplevel directories. */ | 	/* Git does not track empty, non-toplevel directories. */ | ||||||
| 	if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) { | 	if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) { | ||||||
| 		tree_content_remove(&b->branch_tree, p, NULL); | 		tree_content_remove(&b->branch_tree, p, NULL, 0); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -2375,7 +2391,7 @@ static void file_change_d(struct branch *b) | ||||||
| 			die("Garbage after path in: %s", command_buf.buf); | 			die("Garbage after path in: %s", command_buf.buf); | ||||||
| 		p = uq.buf; | 		p = uq.buf; | ||||||
| 	} | 	} | ||||||
| 	tree_content_remove(&b->branch_tree, p, NULL); | 	tree_content_remove(&b->branch_tree, p, NULL, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void file_change_cr(struct branch *b, int rename) | static void file_change_cr(struct branch *b, int rename) | ||||||
|  | @ -2413,9 +2429,9 @@ static void file_change_cr(struct branch *b, int rename) | ||||||
|  |  | ||||||
| 	memset(&leaf, 0, sizeof(leaf)); | 	memset(&leaf, 0, sizeof(leaf)); | ||||||
| 	if (rename) | 	if (rename) | ||||||
| 		tree_content_remove(&b->branch_tree, s, &leaf); | 		tree_content_remove(&b->branch_tree, s, &leaf, 1); | ||||||
| 	else | 	else | ||||||
| 		tree_content_get(&b->branch_tree, s, &leaf); | 		tree_content_get(&b->branch_tree, s, &leaf, 1); | ||||||
| 	if (!leaf.versions[1].mode) | 	if (!leaf.versions[1].mode) | ||||||
| 		die("Path %s not in branch", s); | 		die("Path %s not in branch", s); | ||||||
| 	if (!*d) {	/* C "path/to/subdir" "" */ | 	if (!*d) {	/* C "path/to/subdir" "" */ | ||||||
|  | @ -2521,7 +2537,7 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path); | 	construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path); | ||||||
| 	if (tree_content_remove(&b->branch_tree, path, NULL)) | 	if (tree_content_remove(&b->branch_tree, path, NULL, 0)) | ||||||
| 		b->num_notes--; | 		b->num_notes--; | ||||||
|  |  | ||||||
| 	if (is_null_sha1(sha1)) | 	if (is_null_sha1(sha1)) | ||||||
|  | @ -3051,6 +3067,8 @@ static void parse_ls(struct branch *b) | ||||||
| 		struct object_entry *e = parse_treeish_dataref(&p); | 		struct object_entry *e = parse_treeish_dataref(&p); | ||||||
| 		root = new_tree_entry(); | 		root = new_tree_entry(); | ||||||
| 		hashcpy(root->versions[1].sha1, e->idx.sha1); | 		hashcpy(root->versions[1].sha1, e->idx.sha1); | ||||||
|  | 		if (!is_null_sha1(root->versions[1].sha1)) | ||||||
|  | 			root->versions[1].mode = S_IFDIR; | ||||||
| 		load_tree(root); | 		load_tree(root); | ||||||
| 		if (*p++ != ' ') | 		if (*p++ != ' ') | ||||||
| 			die("Missing space after tree-ish: %s", command_buf.buf); | 			die("Missing space after tree-ish: %s", command_buf.buf); | ||||||
|  | @ -3065,7 +3083,7 @@ static void parse_ls(struct branch *b) | ||||||
| 			die("Garbage after path in: %s", command_buf.buf); | 			die("Garbage after path in: %s", command_buf.buf); | ||||||
| 		p = uq.buf; | 		p = uq.buf; | ||||||
| 	} | 	} | ||||||
| 	tree_content_get(root, p, &leaf); | 	tree_content_get(root, p, &leaf, 1); | ||||||
| 	/* | 	/* | ||||||
| 	 * A directory in preparation would have a sha1 of zero | 	 * A directory in preparation would have a sha1 of zero | ||||||
| 	 * until it is saved.  Save, for simplicity. | 	 * until it is saved.  Save, for simplicity. | ||||||
|  |  | ||||||
|  | @ -1031,6 +1031,32 @@ test_expect_success \ | ||||||
| 	 git diff-tree -M -r M3^ M3 >actual && | 	 git diff-tree -M -r M3^ M3 >actual && | ||||||
| 	 compare_diff_raw expect actual' | 	 compare_diff_raw expect actual' | ||||||
|  |  | ||||||
|  | cat >input <<INPUT_END | ||||||
|  | commit refs/heads/M4 | ||||||
|  | committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE | ||||||
|  | data <<COMMIT | ||||||
|  | rename root | ||||||
|  | COMMIT | ||||||
|  |  | ||||||
|  | from refs/heads/M2^0 | ||||||
|  | R "" sub | ||||||
|  |  | ||||||
|  | INPUT_END | ||||||
|  |  | ||||||
|  | cat >expect <<EOF | ||||||
|  | :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100	file2/oldf	sub/file2/oldf | ||||||
|  | :100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 R100	file4	sub/file4 | ||||||
|  | :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100	i/am/new/to/you	sub/i/am/new/to/you | ||||||
|  | :100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 R100	newdir/exec.sh	sub/newdir/exec.sh | ||||||
|  | :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100	newdir/interesting	sub/newdir/interesting | ||||||
|  | EOF | ||||||
|  | test_expect_success \ | ||||||
|  | 	'M: rename root to subdirectory' \ | ||||||
|  | 	'git fast-import <input && | ||||||
|  | 	 git diff-tree -M -r M4^ M4 >actual && | ||||||
|  | 	 cat actual && | ||||||
|  | 	 compare_diff_raw expect actual' | ||||||
|  |  | ||||||
| ### | ### | ||||||
| ### series N | ### series N | ||||||
| ### | ### | ||||||
|  | @ -1227,6 +1253,29 @@ test_expect_success \ | ||||||
| 	 git diff-tree -C --find-copies-harder -r N4 N6 >actual && | 	 git diff-tree -C --find-copies-harder -r N4 N6 >actual && | ||||||
| 	 compare_diff_raw expect actual' | 	 compare_diff_raw expect actual' | ||||||
|  |  | ||||||
|  | test_expect_success \ | ||||||
|  | 	'N: copy root by path' \ | ||||||
|  | 	'cat >expect <<-\EOF && | ||||||
|  | 	:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100	file2/newf	oldroot/file2/newf | ||||||
|  | 	:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100	file2/oldf	oldroot/file2/oldf | ||||||
|  | 	:100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 C100	file4	oldroot/file4 | ||||||
|  | 	:100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 C100	newdir/exec.sh	oldroot/newdir/exec.sh | ||||||
|  | 	:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100	newdir/interesting	oldroot/newdir/interesting | ||||||
|  | 	EOF | ||||||
|  | 	 cat >input <<-INPUT_END && | ||||||
|  | 	commit refs/heads/N-copy-root-path | ||||||
|  | 	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE | ||||||
|  | 	data <<COMMIT | ||||||
|  | 	copy root directory by (empty) path | ||||||
|  | 	COMMIT | ||||||
|  |  | ||||||
|  | 	from refs/heads/branch^0 | ||||||
|  | 	C "" oldroot | ||||||
|  | 	INPUT_END | ||||||
|  | 	 git fast-import <input && | ||||||
|  | 	 git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual && | ||||||
|  | 	 compare_diff_raw expect actual' | ||||||
|  |  | ||||||
| test_expect_success \ | test_expect_success \ | ||||||
| 	'N: delete directory by copying' \ | 	'N: delete directory by copying' \ | ||||||
| 	'cat >expect <<-\EOF && | 	'cat >expect <<-\EOF && | ||||||
|  | @ -2934,4 +2983,20 @@ test_expect_success 'S: ls with garbage after sha1 must fail' ' | ||||||
| 	test_i18ngrep "space after tree-ish" err | 	test_i18ngrep "space after tree-ish" err | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | ### | ||||||
|  | ### series T (ls) | ||||||
|  | ### | ||||||
|  | # Setup is carried over from series S. | ||||||
|  |  | ||||||
|  | test_expect_success 'T: ls root tree' ' | ||||||
|  | 	sed -e "s/Z\$//" >expect <<-EOF && | ||||||
|  | 	040000 tree $(git rev-parse S^{tree})	Z | ||||||
|  | 	EOF | ||||||
|  | 	sha1=$(git rev-parse --verify S) && | ||||||
|  | 	git fast-import --import-marks=marks <<-EOF >actual && | ||||||
|  | 	ls $sha1 "" | ||||||
|  | 	EOF | ||||||
|  | 	test_cmp expect actual | ||||||
|  | ' | ||||||
|  |  | ||||||
| test_done | test_done | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano