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_revisions
maint
Junio C Hamano 2009-07-26 11:23:52 -07:00
commit feab68cd91
3 changed files with 187 additions and 8 deletions

View File

@ -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
-------- --------

View File

@ -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);

View File

@ -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'