Merge branch 'en/fast-export'
* en/fast-export: fast-export: Document the fact that git-rev-list arguments are accepted Add new fast-export testcases fast-export: Add a --tag-of-filtered-object option for newly dangling tags fast-export: Do parent rewriting to avoid dropping relevant commits fast-export: Make sure we show actual ref names instead of "(null)" fast-export: Omit tags that tag trees fast-export: Set revs.topo_order before calling setup_revisionsmaint
commit
feab68cd91
|
@ -36,6 +36,17 @@ when encountering a signed tag. With 'strip', the tags will be made
|
||||||
unsigned, with 'verbatim', they will be silently exported
|
unsigned, with 'verbatim', they will be silently exported
|
||||||
and with 'warn', they will be exported, but you will see a warning.
|
and with 'warn', they will be exported, but you will see a warning.
|
||||||
|
|
||||||
|
--tag-of-filtered-object=(abort|drop|rewrite)::
|
||||||
|
Specify how to handle tags whose tagged objectis filtered out.
|
||||||
|
Since revisions and files to export can be limited by path,
|
||||||
|
tagged objects may be filtered completely.
|
||||||
|
+
|
||||||
|
When asking to 'abort' (which is the default), this program will die
|
||||||
|
when encountering such a tag. With 'drop' it will omit such tags from
|
||||||
|
the output. With 'rewrite', if the tagged object is a commit, it will
|
||||||
|
rewrite the tag to tag an ancestor commit (via parent rewriting; see
|
||||||
|
linkgit:git-rev-list[1])
|
||||||
|
|
||||||
-M::
|
-M::
|
||||||
-C::
|
-C::
|
||||||
Perform move and/or copy detection, as described in the
|
Perform move and/or copy detection, as described in the
|
||||||
|
@ -71,6 +82,12 @@ marks the same across runs.
|
||||||
allow that. So fake a tagger to be able to fast-import the
|
allow that. So fake a tagger to be able to fast-import the
|
||||||
output.
|
output.
|
||||||
|
|
||||||
|
[git-rev-list-args...]::
|
||||||
|
A list of arguments, acceptable to 'git-rev-parse' and
|
||||||
|
'git-rev-list', that specifies the specific objects and references
|
||||||
|
to export. For example, `master\~10..master` causes the
|
||||||
|
current master reference to be exported along with all objects
|
||||||
|
added since its 10th ancestor commit.
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
--------
|
--------
|
||||||
|
|
|
@ -23,7 +23,8 @@ static const char *fast_export_usage[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static int progress;
|
static int progress;
|
||||||
static enum { VERBATIM, WARN, STRIP, ABORT } signed_tag_mode = ABORT;
|
static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
|
||||||
|
static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
|
||||||
static int fake_missing_tagger;
|
static int fake_missing_tagger;
|
||||||
|
|
||||||
static int parse_opt_signed_tag_mode(const struct option *opt,
|
static int parse_opt_signed_tag_mode(const struct option *opt,
|
||||||
|
@ -42,6 +43,20 @@ static int parse_opt_signed_tag_mode(const struct option *opt,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_opt_tag_of_filtered_mode(const struct option *opt,
|
||||||
|
const char *arg, int unset)
|
||||||
|
{
|
||||||
|
if (unset || !strcmp(arg, "abort"))
|
||||||
|
tag_of_filtered_mode = ABORT;
|
||||||
|
else if (!strcmp(arg, "drop"))
|
||||||
|
tag_of_filtered_mode = DROP;
|
||||||
|
else if (!strcmp(arg, "rewrite"))
|
||||||
|
tag_of_filtered_mode = REWRITE;
|
||||||
|
else
|
||||||
|
return error("Unknown tag-of-filtered mode: %s", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct decoration idnums;
|
static struct decoration idnums;
|
||||||
static uint32_t last_idnum;
|
static uint32_t last_idnum;
|
||||||
|
|
||||||
|
@ -289,6 +304,23 @@ static void handle_tag(const char *name, struct tag *tag)
|
||||||
char *buf;
|
char *buf;
|
||||||
const char *tagger, *tagger_end, *message;
|
const char *tagger, *tagger_end, *message;
|
||||||
size_t message_size = 0;
|
size_t message_size = 0;
|
||||||
|
struct object *tagged;
|
||||||
|
int tagged_mark;
|
||||||
|
struct commit *p;
|
||||||
|
|
||||||
|
/* Trees have no identifer in fast-export output, thus we have no way
|
||||||
|
* to output tags of trees, tags of tags of trees, etc. Simply omit
|
||||||
|
* such tags.
|
||||||
|
*/
|
||||||
|
tagged = tag->tagged;
|
||||||
|
while (tagged->type == OBJ_TAG) {
|
||||||
|
tagged = ((struct tag *)tagged)->tagged;
|
||||||
|
}
|
||||||
|
if (tagged->type == OBJ_TREE) {
|
||||||
|
warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
|
||||||
|
sha1_to_hex(tag->object.sha1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
buf = read_sha1_file(tag->object.sha1, &type, &size);
|
buf = read_sha1_file(tag->object.sha1, &type, &size);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
|
@ -333,10 +365,45 @@ static void handle_tag(const char *name, struct tag *tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* handle tag->tagged having been filtered out due to paths specified */
|
||||||
|
tagged = tag->tagged;
|
||||||
|
tagged_mark = get_object_mark(tagged);
|
||||||
|
if (!tagged_mark) {
|
||||||
|
switch(tag_of_filtered_mode) {
|
||||||
|
case ABORT:
|
||||||
|
die ("Tag %s tags unexported object; use "
|
||||||
|
"--tag-of-filtered-object=<mode> to handle it.",
|
||||||
|
sha1_to_hex(tag->object.sha1));
|
||||||
|
case DROP:
|
||||||
|
/* Ignore this tag altogether */
|
||||||
|
return;
|
||||||
|
case REWRITE:
|
||||||
|
if (tagged->type != OBJ_COMMIT) {
|
||||||
|
die ("Tag %s tags unexported %s!",
|
||||||
|
sha1_to_hex(tag->object.sha1),
|
||||||
|
typename(tagged->type));
|
||||||
|
}
|
||||||
|
p = (struct commit *)tagged;
|
||||||
|
for (;;) {
|
||||||
|
if (p->parents && p->parents->next)
|
||||||
|
break;
|
||||||
|
if (p->object.flags & UNINTERESTING)
|
||||||
|
break;
|
||||||
|
if (!(p->object.flags & TREESAME))
|
||||||
|
break;
|
||||||
|
if (!p->parents)
|
||||||
|
die ("Can't find replacement commit for tag %s\n",
|
||||||
|
sha1_to_hex(tag->object.sha1));
|
||||||
|
p = p->parents->item;
|
||||||
|
}
|
||||||
|
tagged_mark = get_object_mark(&p->object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!prefixcmp(name, "refs/tags/"))
|
if (!prefixcmp(name, "refs/tags/"))
|
||||||
name += 10;
|
name += 10;
|
||||||
printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
|
printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
|
||||||
name, get_object_mark(tag->tagged),
|
name, tagged_mark,
|
||||||
(int)(tagger_end - tagger), tagger,
|
(int)(tagger_end - tagger), tagger,
|
||||||
tagger == tagger_end ? "" : "\n",
|
tagger == tagger_end ? "" : "\n",
|
||||||
(int)message_size, (int)message_size, message ? message : "");
|
(int)message_size, (int)message_size, message ? message : "");
|
||||||
|
@ -504,6 +571,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||||
OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode",
|
OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode",
|
||||||
"select handling of signed tags",
|
"select handling of signed tags",
|
||||||
parse_opt_signed_tag_mode),
|
parse_opt_signed_tag_mode),
|
||||||
|
OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, "mode",
|
||||||
|
"select handling of tags that tag filtered objects",
|
||||||
|
parse_opt_tag_of_filtered_mode),
|
||||||
OPT_STRING(0, "export-marks", &export_filename, "FILE",
|
OPT_STRING(0, "export-marks", &export_filename, "FILE",
|
||||||
"Dump marks to this file"),
|
"Dump marks to this file"),
|
||||||
OPT_STRING(0, "import-marks", &import_filename, "FILE",
|
OPT_STRING(0, "import-marks", &import_filename, "FILE",
|
||||||
|
@ -520,6 +590,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||||
git_config(git_default_config, NULL);
|
git_config(git_default_config, NULL);
|
||||||
|
|
||||||
init_revisions(&revs, prefix);
|
init_revisions(&revs, prefix);
|
||||||
|
revs.topo_order = 1;
|
||||||
|
revs.show_source = 1;
|
||||||
|
revs.rewrite_parents = 1;
|
||||||
argc = setup_revisions(argc, argv, &revs, NULL);
|
argc = setup_revisions(argc, argv, &revs, NULL);
|
||||||
argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0);
|
argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0);
|
||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
|
@ -530,18 +603,13 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||||
|
|
||||||
get_tags_and_duplicates(&revs.pending, &extra_refs);
|
get_tags_and_duplicates(&revs.pending, &extra_refs);
|
||||||
|
|
||||||
revs.topo_order = 1;
|
|
||||||
if (prepare_revision_walk(&revs))
|
if (prepare_revision_walk(&revs))
|
||||||
die("revision walk setup failed");
|
die("revision walk setup failed");
|
||||||
revs.diffopt.format_callback = show_filemodify;
|
revs.diffopt.format_callback = show_filemodify;
|
||||||
DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
|
DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
|
||||||
while ((commit = get_revision(&revs))) {
|
while ((commit = get_revision(&revs))) {
|
||||||
if (has_unshown_parent(commit)) {
|
if (has_unshown_parent(commit)) {
|
||||||
struct commit_list *parent = commit->parents;
|
|
||||||
add_object_array(&commit->object, NULL, &commits);
|
add_object_array(&commit->object, NULL, &commits);
|
||||||
for (; parent; parent = parent->next)
|
|
||||||
if (!parent->item->util)
|
|
||||||
parent->item->util = commit->util;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
handle_commit(commit, &revs);
|
handle_commit(commit, &revs);
|
||||||
|
|
|
@ -262,6 +262,94 @@ test_expect_success 'cope with tagger-less tags' '
|
||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'setup for limiting exports by PATH' '
|
||||||
|
mkdir limit-by-paths &&
|
||||||
|
cd limit-by-paths &&
|
||||||
|
git init &&
|
||||||
|
echo hi > there &&
|
||||||
|
git add there &&
|
||||||
|
git commit -m "First file" &&
|
||||||
|
echo foo > bar &&
|
||||||
|
git add bar &&
|
||||||
|
git commit -m "Second file" &&
|
||||||
|
git tag -a -m msg mytag &&
|
||||||
|
echo morefoo >> bar &&
|
||||||
|
git add bar &&
|
||||||
|
git commit -m "Change to second file" &&
|
||||||
|
cd ..
|
||||||
|
'
|
||||||
|
|
||||||
|
cat > limit-by-paths/expected << EOF
|
||||||
|
blob
|
||||||
|
mark :1
|
||||||
|
data 3
|
||||||
|
hi
|
||||||
|
|
||||||
|
reset refs/tags/mytag
|
||||||
|
commit refs/tags/mytag
|
||||||
|
mark :2
|
||||||
|
author A U Thor <author@example.com> 1112912713 -0700
|
||||||
|
committer C O Mitter <committer@example.com> 1112912713 -0700
|
||||||
|
data 11
|
||||||
|
First file
|
||||||
|
M 100644 :1 there
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'dropping tag of filtered out object' '
|
||||||
|
cd limit-by-paths &&
|
||||||
|
git fast-export --tag-of-filtered-object=drop mytag -- there > output &&
|
||||||
|
test_cmp output expected &&
|
||||||
|
cd ..
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >> limit-by-paths/expected << EOF
|
||||||
|
tag mytag
|
||||||
|
from :2
|
||||||
|
tagger C O Mitter <committer@example.com> 1112912713 -0700
|
||||||
|
data 4
|
||||||
|
msg
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'rewriting tag of filtered out object' '
|
||||||
|
cd limit-by-paths &&
|
||||||
|
git fast-export --tag-of-filtered-object=rewrite mytag -- there > output &&
|
||||||
|
test_cmp output expected &&
|
||||||
|
cd ..
|
||||||
|
'
|
||||||
|
|
||||||
|
cat > limit-by-paths/expected << EOF
|
||||||
|
blob
|
||||||
|
mark :1
|
||||||
|
data 4
|
||||||
|
foo
|
||||||
|
|
||||||
|
blob
|
||||||
|
mark :2
|
||||||
|
data 3
|
||||||
|
hi
|
||||||
|
|
||||||
|
reset refs/heads/master
|
||||||
|
commit refs/heads/master
|
||||||
|
mark :3
|
||||||
|
author A U Thor <author@example.com> 1112912713 -0700
|
||||||
|
committer C O Mitter <committer@example.com> 1112912713 -0700
|
||||||
|
data 12
|
||||||
|
Second file
|
||||||
|
M 100644 :1 bar
|
||||||
|
M 100644 :2 there
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_failure 'no exact-ref revisions included' '
|
||||||
|
cd limit-by-paths &&
|
||||||
|
git fast-export master~2..master~1 > output &&
|
||||||
|
test_cmp output expected &&
|
||||||
|
cd ..
|
||||||
|
'
|
||||||
|
|
||||||
|
|
||||||
test_expect_success 'set-up a few more tags for tag export tests' '
|
test_expect_success 'set-up a few more tags for tag export tests' '
|
||||||
git checkout -f master &&
|
git checkout -f master &&
|
||||||
HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` &&
|
HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` &&
|
||||||
|
@ -271,8 +359,14 @@ test_expect_success 'set-up a few more tags for tag export tests' '
|
||||||
git tag -a tag-obj_tag-obj -m "tagging a tag" tree_tag-obj
|
git tag -a tag-obj_tag-obj -m "tagging a tag" tree_tag-obj
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'tree_tag' '
|
||||||
|
mkdir result &&
|
||||||
|
(cd result && git init) &&
|
||||||
|
git fast-export tree_tag > fe-stream &&
|
||||||
|
(cd result && git fast-import < ../fe-stream)
|
||||||
|
'
|
||||||
|
|
||||||
# NEEDSWORK: not just check return status, but validate the output
|
# NEEDSWORK: not just check return status, but validate the output
|
||||||
test_expect_success 'tree_tag' 'git fast-export tree_tag'
|
|
||||||
test_expect_success 'tree_tag-obj' 'git fast-export tree_tag-obj'
|
test_expect_success 'tree_tag-obj' 'git fast-export tree_tag-obj'
|
||||||
test_expect_success 'tag-obj_tag' 'git fast-export tag-obj_tag'
|
test_expect_success 'tag-obj_tag' 'git fast-export tag-obj_tag'
|
||||||
test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
|
test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
|
||||||
|
|
Loading…
Reference in New Issue