Teach fast-import to import subtrees named by tree id

To simulate the svn cp command, it would be very useful to be
replace an arbitrary file in the current revision by an
arbitrary directory from a previous one.  Modify the filemodify
command to allow that:

 M 040000 <tree id> pathname

This would be most useful in combination with a facility to
print the commit ids for new revisions as they are written.

Cc: Shawn O. Pearce <spearce@spearce.org>
Cc: Sverre Rabbelier <srabbelier@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jonathan Nieder 2010-06-30 22:18:19 -05:00 committed by Junio C Hamano
parent 45e9a825ed
commit 334fba656b
3 changed files with 75 additions and 11 deletions

View File

@ -482,9 +482,11 @@ External data format::
'M' SP <mode> SP <dataref> SP <path> LF
....
+
Here `<dataref>` can be either a mark reference (`:<idnum>`)
Here usually `<dataref>` must be either a mark reference (`:<idnum>`)
set by a prior `blob` command, or a full 40-byte SHA-1 of an
existing Git blob object.
existing Git blob object. If `<mode>` is `040000`` then
`<dataref>` must be the full 40-byte SHA-1 of an existing
Git tree object or a mark reference set with `--import-marks`.
Inline data format::
The data content for the file has not been supplied yet.
@ -509,6 +511,8 @@ in octal. Git only supports the following modes:
* `160000`: A gitlink, SHA-1 of the object refers to a commit in
another repository. Git links can only be specified by SHA or through
a commit mark. They are used to implement submodules.
* `040000`: A subdirectory. Subdirectories can only be specified by
SHA or through a tree mark set with `--import-marks`.
In both formats `<path>` is the complete path of the file to be added
(if not already existing) or modified (if already existing).

View File

@ -2131,6 +2131,7 @@ static void file_change_m(struct branch *b)
case S_IFREG | 0644:
case S_IFREG | 0755:
case S_IFLNK:
case S_IFDIR:
case S_IFGITLINK:
/* ok */
break;
@ -2176,23 +2177,28 @@ static void file_change_m(struct branch *b)
* another repository.
*/
} else if (inline_data) {
if (S_ISDIR(mode))
die("Directories cannot be specified 'inline': %s",
command_buf.buf);
if (p != uq.buf) {
strbuf_addstr(&uq, p);
p = uq.buf;
}
read_next_command();
parse_and_store_blob(&last_blob, sha1, 0);
} else if (oe) {
if (oe->type != OBJ_BLOB)
die("Not a blob (actually a %s): %s",
typename(oe->type), command_buf.buf);
} else {
enum object_type type = sha1_object_info(sha1, NULL);
enum object_type expected = S_ISDIR(mode) ?
OBJ_TREE: OBJ_BLOB;
enum object_type type = oe ? oe->type :
sha1_object_info(sha1, NULL);
if (type < 0)
die("Blob not found: %s", command_buf.buf);
if (type != OBJ_BLOB)
die("Not a blob (actually a %s): %s",
typename(type), command_buf.buf);
die("%s not found: %s",
S_ISDIR(mode) ? "Tree" : "Blob",
command_buf.buf);
if (type != expected)
die("Not a %s (actually a %s): %s",
typename(expected), typename(type),
command_buf.buf);
}
tree_content_set(&b->branch_tree, p, sha1, mode, NULL);

View File

@ -796,6 +796,60 @@ test_expect_success \
'git fast-import <input &&
test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
test_expect_success \
'N: copy directory by id' \
'cat >expect <<-\EOF &&
:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
EOF
subdir=$(git rev-parse refs/heads/branch^0:file2) &&
cat >input <<-INPUT_END &&
commit refs/heads/N4
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
copy by tree hash
COMMIT
from refs/heads/branch^0
M 040000 $subdir file3
INPUT_END
git fast-import <input &&
git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
compare_diff_raw expect actual'
test_expect_success \
'N: modify copied tree' \
'cat >expect <<-\EOF &&
:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5
:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
EOF
subdir=$(git rev-parse refs/heads/branch^0:file2) &&
cat >input <<-INPUT_END &&
commit refs/heads/N5
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
copy by tree hash
COMMIT
from refs/heads/branch^0
M 040000 $subdir file3
commit refs/heads/N5
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
modify directory copy
COMMIT
M 644 inline file3/file5
data <<EOF
$file5_data
EOF
INPUT_END
git fast-import <input &&
git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
compare_diff_raw expect actual'
###
### series O
###