Support gitlinks in fast-import.
Currently fast-import/export cannot be used for repositories with submodules. This patch extends the relevant programs to make them correctly process gitlinks. Links can be represented by two forms of the Modify command: M 160000 SHA1 some/path which sets the link target explicitly, or M 160000 :mark some/path where the mark refers to a commit. The latter form can be used by importing tools to build all submodules simultaneously in one physical repository, and then simply fetch them apart. Signed-off-by: Alexander Gavrilov <angavrilov@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Alexander Gavrilov committed
Jul 19, 2008 at 16:21 UTC
03db4525d38119f7778d6e9117f27c47db8466d4
5 files changed
+227
-5
Documentation/git-fast-import.txt
+3
@@ -481,6 +481,9 @@ in octal. Git only supports the following modes:
481
what you want.
482
* `100755` or `755`: A normal, but executable, file.
483
* `120000`: A symlink, the content of the file will be the link target.
484
+* `160000`: A gitlink, SHA-1 of the object refers to a commit in
485
+ another repository. Git links can only be specified by SHA or through
486
+ a commit mark. They are used to implement submodules.
487
488
In both formats `<path>` is the complete path of the file to be added
489
(if not already existing) or modified (if already existing).
builtin-fast-export.c
+15
-4
@@ -136,9 +136,18 @@ static void show_filemodify(struct diff_queue_struct *q,
136
if (is_null_sha1(spec->sha1))
137
printf("D %s\n", spec->path);
138
else {
139
- struct object *object = lookup_object(spec->sha1);
140
- printf("M %06o :%d %s\n", spec->mode,
141
- get_object_mark(object), spec->path);
139
+ /*
140
+ * Links refer to objects in another repositories;
141
+ * output the SHA-1 verbatim.
142
+ */
143
+ if (S_ISGITLINK(spec->mode))
144
+ printf("M %06o %s %s\n", spec->mode,
145
+ sha1_to_hex(spec->sha1), spec->path);
146
+ else {
147
+ struct object *object = lookup_object(spec->sha1);
148
+ printf("M %06o :%d %s\n", spec->mode,
149
+ get_object_mark(object), spec->path);
150
+ }
151
}
152
}
153
}
@@ -196,8 +205,10 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
205
diff_root_tree_sha1(commit->tree->object.sha1,
206
"", &rev->diffopt);
207
208
+ /* Export the referenced blobs, and remember the marks. */
209
for (i = 0; i < diff_queued_diff.nr; i++)
200
- handle_object(diff_queued_diff.queue[i]->two->sha1);
210
+ if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
211
+ handle_object(diff_queued_diff.queue[i]->two->sha1);
212
213
mark_next_object(&commit->object);
214
if (!is_encoding_utf8(encoding))
fast-import.c
+15
-1
@@ -1868,6 +1868,7 @@ static void file_change_m(struct branch *b)
1868
case S_IFREG | 0644:
1869
case S_IFREG | 0755:
1870
case S_IFLNK:
1871
+ case S_IFGITLINK:
1872
case 0644:
1873
case 0755:
1874
/* ok */
@@ -1900,7 +1901,20 @@ static void file_change_m(struct branch *b)
1901
p = uq.buf;
1902
}
1903
1903
- if (inline_data) {
1904
+ if (S_ISGITLINK(mode)) {
1905
+ if (inline_data)
1906
+ die("Git links cannot be specified 'inline': %s",
1907
+ command_buf.buf);
1908
+ else if (oe) {
1909
+ if (oe->type != OBJ_COMMIT)
1910
+ die("Not a commit (actually a %s): %s",
1911
+ typename(oe->type), command_buf.buf);
1912
+ }
1913
+ /*
1914
+ * Accept the sha1 without checking; it expected to be in
1915
+ * another repository.
1916
+ */
1917
+ } else if (inline_data) {
1918
static struct strbuf buf = STRBUF_INIT;
1919
1920
if (p != uq.buf) {
t/t9300-fast-import.sh
+152
@@ -918,4 +918,156 @@ test_expect_success \
918
grep "progress " <input >expect &&
919
test_cmp expect actual'
920
921
+###
922
+### series P (gitlinks)
923
+###
924
+
925
+cat >input <<INPUT_END
926
+blob
927
+mark :1
928
+data 10
929
+test file
930
+
931
+reset refs/heads/sub
932
+commit refs/heads/sub
933
+mark :2
934
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
935
+data 12
936
+sub_initial
937
+M 100644 :1 file
938
+
939
+blob
940
+mark :3
941
+data <<DATAEND
942
+[submodule "sub"]
943
+ path = sub
944
+ url = "`pwd`/sub"
945
+DATAEND
946
+
947
+commit refs/heads/subuse1
948
+mark :4
949
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
950
+data 8
951
+initial
952
+from refs/heads/master
953
+M 100644 :3 .gitmodules
954
+M 160000 :2 sub
955
+
956
+blob
957
+mark :5
958
+data 20
959
+test file
960
+more data
961
+
962
+commit refs/heads/sub
963
+mark :6
964
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
965
+data 11
966
+sub_second
967
+from :2
968
+M 100644 :5 file
969
+
970
+commit refs/heads/subuse1
971
+mark :7
972
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
973
+data 7
974
+second
975
+from :4
976
+M 160000 :6 sub
977
+
978
+INPUT_END
979
+
980
+test_expect_success \
981
+ 'P: supermodule & submodule mix' \
982
+ 'git-fast-import <input &&
983
+ git checkout subuse1 &&
984
+ rm -rf sub && mkdir sub && cd sub &&
985
+ git init &&
986
+ git fetch .. refs/heads/sub:refs/heads/master &&
987
+ git checkout master &&
988
+ cd .. &&
989
+ git submodule init &&
990
+ git submodule update'
991
+
992
+SUBLAST=$(git-rev-parse --verify sub)
993
+SUBPREV=$(git-rev-parse --verify sub^)
994
+
995
+cat >input <<INPUT_END
996
+blob
997
+mark :1
998
+data <<DATAEND
999
+[submodule "sub"]
1000
+ path = sub
1001
+ url = "`pwd`/sub"
1002
+DATAEND
1003
+
1004
+commit refs/heads/subuse2
1005
+mark :2
1006
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1007
+data 8
1008
+initial
1009
+from refs/heads/master
1010
+M 100644 :1 .gitmodules
1011
+M 160000 $SUBPREV sub
1012
+
1013
+commit refs/heads/subuse2
1014
+mark :3
1015
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1016
+data 7
1017
+second
1018
+from :2
1019
+M 160000 $SUBLAST sub
1020
+
1021
+INPUT_END
1022
+
1023
+test_expect_success \
1024
+ 'P: verbatim SHA gitlinks' \
1025
+ 'git branch -D sub &&
1026
+ git gc && git prune &&
1027
+ git-fast-import <input &&
1028
+ test $(git-rev-parse --verify subuse2) = $(git-rev-parse --verify subuse1)'
1029
+
1030
+test_tick
1031
+cat >input <<INPUT_END
1032
+commit refs/heads/subuse3
1033
+mark :1
1034
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1035
+data <<COMMIT
1036
+corrupt
1037
+COMMIT
1038
+
1039
+from refs/heads/subuse2
1040
+M 160000 inline sub
1041
+data <<DATA
1042
+$SUBPREV
1043
+DATA
1044
+
1045
+INPUT_END
1046
+
1047
+test_expect_success 'P: fail on inline gitlink' '
1048
+ ! git-fast-import <input'
1049
+
1050
+test_tick
1051
+cat >input <<INPUT_END
1052
+blob
1053
+mark :1
1054
+data <<DATA
1055
+$SUBPREV
1056
+DATA
1057
+
1058
+commit refs/heads/subuse3
1059
+mark :2
1060
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
1061
+data <<COMMIT
1062
+corrupt
1063
+COMMIT
1064
+
1065
+from refs/heads/subuse2
1066
+M 160000 :1 sub
1067
+
1068
+INPUT_END
1069
+
1070
+test_expect_success 'P: fail on blob mark in gitlink' '
1071
+ ! git-fast-import <input'
1072
+
1073
test_done
t/t9301-fast-export.sh
+42
@@ -143,4 +143,46 @@ test_expect_success 'signed-tags=strip' '
143
144
'
145
146
+test_expect_success 'setup submodule' '
147
+
148
+ git checkout -f master &&
149
+ mkdir sub &&
150
+ cd sub &&
151
+ git init &&
152
+ echo test file > file &&
153
+ git add file &&
154
+ git commit -m sub_initial &&
155
+ cd .. &&
156
+ git submodule add "`pwd`/sub" sub &&
157
+ git commit -m initial &&
158
+ test_tick &&
159
+ cd sub &&
160
+ echo more data >> file &&
161
+ git add file &&
162
+ git commit -m sub_second &&
163
+ cd .. &&
164
+ git add sub &&
165
+ git commit -m second
166
+
167
+'
168
+
169
+test_expect_success 'submodule fast-export | fast-import' '
170
+
171
+ SUBENT1=$(git ls-tree master^ sub) &&
172
+ SUBENT2=$(git ls-tree master sub) &&
173
+ rm -rf new &&
174
+ mkdir new &&
175
+ git --git-dir=new/.git init &&
176
+ git fast-export --signed-tags=strip --all |
177
+ (cd new &&
178
+ git fast-import &&
179
+ test "$SUBENT1" = "$(git ls-tree refs/heads/master^ sub)" &&
180
+ test "$SUBENT2" = "$(git ls-tree refs/heads/master sub)" &&
181
+ git checkout master &&
182
+ git submodule init &&
183
+ git submodule update &&
184
+ cmp sub/file ../sub/file)
185
+
186
+'
187
+
188
test_done