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>
Jonathan Nieder committed
Jun 30, 2010 at 22:18 UTC
334fba656b50c92345586970bc6b100a449e1fc5
3 files changed
+75
-11
Documentation/git-fast-import.txt
+6
-2
@@ -482,9 +482,11 @@ External data format::
482
'M' SP <mode> SP <dataref> SP <path> LF
483
....
484
+
485
-Here `<dataref>` can be either a mark reference (`:<idnum>`)
485
+Here usually `<dataref>` must be either a mark reference (`:<idnum>`)
486
set by a prior `blob` command, or a full 40-byte SHA-1 of an
487
-existing Git blob object.
487
+existing Git blob object. If `<mode>` is `040000`` then
488
+`<dataref>` must be the full 40-byte SHA-1 of an existing
489
+Git tree object or a mark reference set with `--import-marks`.
490
491
Inline data format::
492
The data content for the file has not been supplied yet.
@@ -509,6 +511,8 @@ in octal. Git only supports the following modes:
511
* `160000`: A gitlink, SHA-1 of the object refers to a commit in
512
another repository. Git links can only be specified by SHA or through
513
a commit mark. They are used to implement submodules.
514
+* `040000`: A subdirectory. Subdirectories can only be specified by
515
+ SHA or through a tree mark set with `--import-marks`.
516
517
In both formats `<path>` is the complete path of the file to be added
518
(if not already existing) or modified (if already existing).
fast-import.c
+15
-9
@@ -2131,6 +2131,7 @@ static void file_change_m(struct branch *b)
2131
case S_IFREG | 0644:
2132
case S_IFREG | 0755:
2133
case S_IFLNK:
2134
+ case S_IFDIR:
2135
case S_IFGITLINK:
2136
/* ok */
2137
break;
@@ -2176,23 +2177,28 @@ static void file_change_m(struct branch *b)
2177
* another repository.
2178
*/
2179
} else if (inline_data) {
2180
+ if (S_ISDIR(mode))
2181
+ die("Directories cannot be specified 'inline': %s",
2182
+ command_buf.buf);
2183
if (p != uq.buf) {
2184
strbuf_addstr(&uq, p);
2185
p = uq.buf;
2186
}
2187
read_next_command();
2188
parse_and_store_blob(&last_blob, sha1, 0);
2185
- } else if (oe) {
2186
- if (oe->type != OBJ_BLOB)
2187
- die("Not a blob (actually a %s): %s",
2188
- typename(oe->type), command_buf.buf);
2189
} else {
2190
- enum object_type type = sha1_object_info(sha1, NULL);
2190
+ enum object_type expected = S_ISDIR(mode) ?
2191
+ OBJ_TREE: OBJ_BLOB;
2192
+ enum object_type type = oe ? oe->type :
2193
+ sha1_object_info(sha1, NULL);
2194
if (type < 0)
2192
- die("Blob not found: %s", command_buf.buf);
2193
- if (type != OBJ_BLOB)
2194
- die("Not a blob (actually a %s): %s",
2195
- typename(type), command_buf.buf);
2195
+ die("%s not found: %s",
2196
+ S_ISDIR(mode) ? "Tree" : "Blob",
2197
+ command_buf.buf);
2198
+ if (type != expected)
2199
+ die("Not a %s (actually a %s): %s",
2200
+ typename(expected), typename(type),
2201
+ command_buf.buf);
2202
}
2203
2204
tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
t/t9300-fast-import.sh
+54
@@ -796,6 +796,60 @@ test_expect_success \
796
'git fast-import <input &&
797
test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
798
799
+test_expect_success \
800
+ 'N: copy directory by id' \
801
+ 'cat >expect <<-\EOF &&
802
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
803
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
804
+ EOF
805
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
806
+ cat >input <<-INPUT_END &&
807
+ commit refs/heads/N4
808
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
809
+ data <<COMMIT
810
+ copy by tree hash
811
+ COMMIT
812
+
813
+ from refs/heads/branch^0
814
+ M 040000 $subdir file3
815
+ INPUT_END
816
+ git fast-import <input &&
817
+ git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
818
+ compare_diff_raw expect actual'
819
+
820
+test_expect_success \
821
+ 'N: modify copied tree' \
822
+ 'cat >expect <<-\EOF &&
823
+ :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5
824
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
825
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
826
+ EOF
827
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
828
+ cat >input <<-INPUT_END &&
829
+ commit refs/heads/N5
830
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
831
+ data <<COMMIT
832
+ copy by tree hash
833
+ COMMIT
834
+
835
+ from refs/heads/branch^0
836
+ M 040000 $subdir file3
837
+
838
+ commit refs/heads/N5
839
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
840
+ data <<COMMIT
841
+ modify directory copy
842
+ COMMIT
843
+
844
+ M 644 inline file3/file5
845
+ data <<EOF
846
+ $file5_data
847
+ EOF
848
+ INPUT_END
849
+ git fast-import <input &&
850
+ git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
851
+ compare_diff_raw expect actual'
852
+
853
###
854
### series O
855
###