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>
This commit is contained in:
Alexander Gavrilov 2008-07-19 16:21:24 +04:00 committed by Junio C Hamano
parent 4a3fedd597
commit 03db4525d3
5 changed files with 227 additions and 5 deletions

View file

@ -481,6 +481,9 @@ in octal. Git only supports the following modes:
what you want.
* `100755` or `755`: A normal, but executable, file.
* `120000`: A symlink, the content of the file will be the link target.
* `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.
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

@ -136,9 +136,18 @@ static void show_filemodify(struct diff_queue_struct *q,
if (is_null_sha1(spec->sha1))
printf("D %s\n", spec->path);
else {
struct object *object = lookup_object(spec->sha1);
printf("M %06o :%d %s\n", spec->mode,
get_object_mark(object), spec->path);
/*
* Links refer to objects in another repositories;
* output the SHA-1 verbatim.
*/
if (S_ISGITLINK(spec->mode))
printf("M %06o %s %s\n", spec->mode,
sha1_to_hex(spec->sha1), spec->path);
else {
struct object *object = lookup_object(spec->sha1);
printf("M %06o :%d %s\n", spec->mode,
get_object_mark(object), spec->path);
}
}
}
}
@ -196,8 +205,10 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
diff_root_tree_sha1(commit->tree->object.sha1,
"", &rev->diffopt);
/* Export the referenced blobs, and remember the marks. */
for (i = 0; i < diff_queued_diff.nr; i++)
handle_object(diff_queued_diff.queue[i]->two->sha1);
if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
handle_object(diff_queued_diff.queue[i]->two->sha1);
mark_next_object(&commit->object);
if (!is_encoding_utf8(encoding))

View file

@ -1868,6 +1868,7 @@ static void file_change_m(struct branch *b)
case S_IFREG | 0644:
case S_IFREG | 0755:
case S_IFLNK:
case S_IFGITLINK:
case 0644:
case 0755:
/* ok */
@ -1900,7 +1901,20 @@ static void file_change_m(struct branch *b)
p = uq.buf;
}
if (inline_data) {
if (S_ISGITLINK(mode)) {
if (inline_data)
die("Git links cannot be specified 'inline': %s",
command_buf.buf);
else if (oe) {
if (oe->type != OBJ_COMMIT)
die("Not a commit (actually a %s): %s",
typename(oe->type), command_buf.buf);
}
/*
* Accept the sha1 without checking; it expected to be in
* another repository.
*/
} else if (inline_data) {
static struct strbuf buf = STRBUF_INIT;
if (p != uq.buf) {

View file

@ -918,4 +918,156 @@ test_expect_success \
grep "progress " <input >expect &&
test_cmp expect actual'
###
### series P (gitlinks)
###
cat >input <<INPUT_END
blob
mark :1
data 10
test file
reset refs/heads/sub
commit refs/heads/sub
mark :2
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data 12
sub_initial
M 100644 :1 file
blob
mark :3
data <<DATAEND
[submodule "sub"]
path = sub
url = "`pwd`/sub"
DATAEND
commit refs/heads/subuse1
mark :4
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data 8
initial
from refs/heads/master
M 100644 :3 .gitmodules
M 160000 :2 sub
blob
mark :5
data 20
test file
more data
commit refs/heads/sub
mark :6
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data 11
sub_second
from :2
M 100644 :5 file
commit refs/heads/subuse1
mark :7
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data 7
second
from :4
M 160000 :6 sub
INPUT_END
test_expect_success \
'P: supermodule & submodule mix' \
'git-fast-import <input &&
git checkout subuse1 &&
rm -rf sub && mkdir sub && cd sub &&
git init &&
git fetch .. refs/heads/sub:refs/heads/master &&
git checkout master &&
cd .. &&
git submodule init &&
git submodule update'
SUBLAST=$(git-rev-parse --verify sub)
SUBPREV=$(git-rev-parse --verify sub^)
cat >input <<INPUT_END
blob
mark :1
data <<DATAEND
[submodule "sub"]
path = sub
url = "`pwd`/sub"
DATAEND
commit refs/heads/subuse2
mark :2
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data 8
initial
from refs/heads/master
M 100644 :1 .gitmodules
M 160000 $SUBPREV sub
commit refs/heads/subuse2
mark :3
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data 7
second
from :2
M 160000 $SUBLAST sub
INPUT_END
test_expect_success \
'P: verbatim SHA gitlinks' \
'git branch -D sub &&
git gc && git prune &&
git-fast-import <input &&
test $(git-rev-parse --verify subuse2) = $(git-rev-parse --verify subuse1)'
test_tick
cat >input <<INPUT_END
commit refs/heads/subuse3
mark :1
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
corrupt
COMMIT
from refs/heads/subuse2
M 160000 inline sub
data <<DATA
$SUBPREV
DATA
INPUT_END
test_expect_success 'P: fail on inline gitlink' '
! git-fast-import <input'
test_tick
cat >input <<INPUT_END
blob
mark :1
data <<DATA
$SUBPREV
DATA
commit refs/heads/subuse3
mark :2
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
corrupt
COMMIT
from refs/heads/subuse2
M 160000 :1 sub
INPUT_END
test_expect_success 'P: fail on blob mark in gitlink' '
! git-fast-import <input'
test_done

View file

@ -143,4 +143,46 @@ test_expect_success 'signed-tags=strip' '
'
test_expect_success 'setup submodule' '
git checkout -f master &&
mkdir sub &&
cd sub &&
git init &&
echo test file > file &&
git add file &&
git commit -m sub_initial &&
cd .. &&
git submodule add "`pwd`/sub" sub &&
git commit -m initial &&
test_tick &&
cd sub &&
echo more data >> file &&
git add file &&
git commit -m sub_second &&
cd .. &&
git add sub &&
git commit -m second
'
test_expect_success 'submodule fast-export | fast-import' '
SUBENT1=$(git ls-tree master^ sub) &&
SUBENT2=$(git ls-tree master sub) &&
rm -rf new &&
mkdir new &&
git --git-dir=new/.git init &&
git fast-export --signed-tags=strip --all |
(cd new &&
git fast-import &&
test "$SUBENT1" = "$(git ls-tree refs/heads/master^ sub)" &&
test "$SUBENT2" = "$(git ls-tree refs/heads/master sub)" &&
git checkout master &&
git submodule init &&
git submodule update &&
cmp sub/file ../sub/file)
'
test_done