fast-export: Add a --tag-of-filtered-object option for newly dangling tags

When providing a list of paths to limit what is exported, the object that
a tag points to can be filtered out entirely.  This new switch allows
the user to specify what should happen to the tag in such a case.  The
default action, 'abort' will exit with an error message.  With 'drop', the
tag will simply be omitted from the output.  With 'rewrite', if the object
tagged was a commit, the tag will be modified to tag an alternate commit.
The alternate commit is determined by treating the original commit as the
"parent" of the tag and then using the parent rewriting algorithm of the
revision traversal machinery (related to the "--parents" option of "git
rev-list")

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Elijah Newren 2009-06-25 22:48:31 -06:00 committed by Junio C Hamano
parent 32164131db
commit 2d8ad46919
2 changed files with 68 additions and 2 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

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;
@ -290,6 +305,8 @@ static void handle_tag(const char *name, struct tag *tag)
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; struct object *tagged;
int tagged_mark;
struct commit *p;
/* Trees have no identifer in fast-export output, thus we have no way /* 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 * to output tags of trees, tags of tags of trees, etc. Simply omit
@ -348,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 : "");
@ -513,6 +565,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",