Skip to content

Commit 16d9d71

Browse files
committed
Merge branch 'en/fast-imexport-nested-tags'
Updates to fast-import/export. * en/fast-imexport-nested-tags: fast-export: handle nested tags t9350: add tests for tags of things other than a commit fast-export: allow user to request tags be marked with --mark-tags fast-export: add support for --import-marks-if-exists fast-import: add support for new 'alias' command fast-import: allow tags to be identified by mark labels fast-import: fix handling of deleted tags fast-export: fix exporting a tag and nothing else
2 parents 6d5291b + 941790d commit 16d9d71

File tree

6 files changed

+266
-38
lines changed

6 files changed

+266
-38
lines changed

Documentation/git-fast-export.txt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,20 @@ produced incorrect results if you gave these options.
7575
Before processing any input, load the marks specified in
7676
<file>. The input file must exist, must be readable, and
7777
must use the same format as produced by --export-marks.
78+
79+
--mark-tags::
80+
In addition to labelling blobs and commits with mark ids, also
81+
label tags. This is useful in conjunction with
82+
`--export-marks` and `--import-marks`, and is also useful (and
83+
necessary) for exporting of nested tags. It does not hurt
84+
other cases and would be the default, but many fast-import
85+
frontends are not prepared to accept tags with mark
86+
identifiers.
7887
+
79-
Any commits that have already been marked will not be exported again.
80-
If the backend uses a similar --import-marks file, this allows for
81-
incremental bidirectional exporting of the repository by keeping the
82-
marks the same across runs.
88+
Any commits (or tags) that have already been marked will not be
89+
exported again. If the backend uses a similar --import-marks file,
90+
this allows for incremental bidirectional exporting of the repository
91+
by keeping the marks the same across runs.
8392

8493
--fake-missing-tagger::
8594
Some old repositories have tags without a tagger. The

Documentation/git-fast-import.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,13 @@ and control the current import process. More detailed discussion
337337
`commit` command. This command is optional and is not
338338
needed to perform an import.
339339

340+
`alias`::
341+
Record that a mark refers to a given object without first
342+
creating any new object. Using --import-marks and referring
343+
to missing marks will cause fast-import to fail, so aliases
344+
can provide a way to set otherwise pruned commits to a valid
345+
value (e.g. the nearest non-pruned ancestor).
346+
340347
`checkpoint`::
341348
Forces fast-import to close the current packfile, generate its
342349
unique SHA-1 checksum and index, and start a new packfile.
@@ -774,6 +781,7 @@ lightweight (non-annotated) tags see the `reset` command below.
774781

775782
....
776783
'tag' SP <name> LF
784+
mark?
777785
'from' SP <commit-ish> LF
778786
original-oid?
779787
'tagger' (SP <name>)? SP LT <email> GT SP <when> LF
@@ -913,6 +921,21 @@ a data chunk which does not have an LF as its last byte.
913921
+
914922
The `LF` after `<delim> LF` is optional (it used to be required).
915923

924+
`alias`
925+
~~~~~~~
926+
Record that a mark refers to a given object without first creating any
927+
new object.
928+
929+
....
930+
'alias' LF
931+
mark
932+
'to' SP <commit-ish> LF
933+
LF?
934+
....
935+
936+
For a detailed description of `<commit-ish>` see above under `from`.
937+
938+
916939
`checkpoint`
917940
~~~~~~~~~~~~
918941
Forces fast-import to close the current packfile, start a new one, and to

builtin/fast-export.c

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ static int no_data;
4040
static int full_tree;
4141
static int reference_excluded_commits;
4242
static int show_original_ids;
43+
static int mark_tags;
4344
static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
4445
static struct string_list tag_refs = STRING_LIST_INIT_NODUP;
4546
static struct refspec refspecs = REFSPEC_INIT_FETCH;
@@ -842,25 +843,40 @@ static void handle_tag(const char *name, struct tag *tag)
842843
free(buf);
843844
return;
844845
case REWRITE:
845-
if (tagged->type != OBJ_COMMIT) {
846-
die("tag %s tags unexported %s!",
847-
oid_to_hex(&tag->object.oid),
848-
type_name(tagged->type));
849-
}
850-
p = rewrite_commit((struct commit *)tagged);
851-
if (!p) {
852-
printf("reset %s\nfrom %s\n\n",
853-
name, oid_to_hex(&null_oid));
854-
free(buf);
855-
return;
846+
if (tagged->type == OBJ_TAG && !mark_tags) {
847+
die(_("Error: Cannot export nested tags unless --mark-tags is specified."));
848+
} else if (tagged->type == OBJ_COMMIT) {
849+
p = rewrite_commit((struct commit *)tagged);
850+
if (!p) {
851+
printf("reset %s\nfrom %s\n\n",
852+
name, oid_to_hex(&null_oid));
853+
free(buf);
854+
return;
855+
}
856+
tagged_mark = get_object_mark(&p->object);
857+
} else {
858+
/* tagged->type is either OBJ_BLOB or OBJ_TAG */
859+
tagged_mark = get_object_mark(tagged);
856860
}
857-
tagged_mark = get_object_mark(&p->object);
858861
}
859862
}
860863

864+
if (tagged->type == OBJ_TAG) {
865+
printf("reset %s\nfrom %s\n\n",
866+
name, oid_to_hex(&null_oid));
867+
}
861868
if (starts_with(name, "refs/tags/"))
862869
name += 10;
863-
printf("tag %s\nfrom :%d\n", name, tagged_mark);
870+
printf("tag %s\n", name);
871+
if (mark_tags) {
872+
mark_next_object(&tag->object);
873+
printf("mark :%"PRIu32"\n", last_idnum);
874+
}
875+
if (tagged_mark)
876+
printf("from :%d\n", tagged_mark);
877+
else
878+
printf("from %s\n", oid_to_hex(&tagged->oid));
879+
864880
if (show_original_ids)
865881
printf("original-oid %s\n", oid_to_hex(&tag->object.oid));
866882
printf("%.*s%sdata %d\n%.*s\n",
@@ -1047,11 +1063,16 @@ static void export_marks(char *file)
10471063
error("Unable to write marks file %s.", file);
10481064
}
10491065

1050-
static void import_marks(char *input_file)
1066+
static void import_marks(char *input_file, int check_exists)
10511067
{
10521068
char line[512];
1053-
FILE *f = xfopen(input_file, "r");
1069+
FILE *f;
1070+
struct stat sb;
1071+
1072+
if (check_exists && stat(input_file, &sb))
1073+
return;
10541074

1075+
f = xfopen(input_file, "r");
10551076
while (fgets(line, sizeof(line), f)) {
10561077
uint32_t mark;
10571078
char *line_end, *mark_end;
@@ -1115,7 +1136,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
11151136
struct rev_info revs;
11161137
struct object_array commits = OBJECT_ARRAY_INIT;
11171138
struct commit *commit;
1118-
char *export_filename = NULL, *import_filename = NULL;
1139+
char *export_filename = NULL,
1140+
*import_filename = NULL,
1141+
*import_filename_if_exists = NULL;
11191142
uint32_t lastimportid;
11201143
struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
11211144
struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
@@ -1135,6 +1158,10 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
11351158
N_("Dump marks to this file")),
11361159
OPT_STRING(0, "import-marks", &import_filename, N_("file"),
11371160
N_("Import marks from this file")),
1161+
OPT_STRING(0, "import-marks-if-exists",
1162+
&import_filename_if_exists,
1163+
N_("file"),
1164+
N_("Import marks from this file if it exists")),
11381165
OPT_BOOL(0, "fake-missing-tagger", &fake_missing_tagger,
11391166
N_("Fake a tagger when tags lack one")),
11401167
OPT_BOOL(0, "full-tree", &full_tree,
@@ -1149,6 +1176,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
11491176
&reference_excluded_commits, N_("Reference parents which are not in fast-export stream by object id")),
11501177
OPT_BOOL(0, "show-original-ids", &show_original_ids,
11511178
N_("Show original object ids of blobs/commits")),
1179+
OPT_BOOL(0, "mark-tags", &mark_tags,
1180+
N_("Label tags with mark ids")),
11521181

11531182
OPT_END()
11541183
};
@@ -1182,8 +1211,12 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
11821211
if (use_done_feature)
11831212
printf("feature done\n");
11841213

1214+
if (import_filename && import_filename_if_exists)
1215+
die(_("Cannot pass both --import-marks and --import-marks-if-exists"));
11851216
if (import_filename)
1186-
import_marks(import_filename);
1217+
import_marks(import_filename, 0);
1218+
else if (import_filename_if_exists)
1219+
import_marks(import_filename_if_exists, 1);
11871220
lastimportid = last_idnum;
11881221

11891222
if (import_filename && revs.prune_data.nr)

fast-import.c

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2489,27 +2489,23 @@ static void parse_from_existing(struct branch *b)
24892489
}
24902490
}
24912491

2492-
static int parse_from(struct branch *b)
2492+
static int parse_objectish(struct branch *b, const char *objectish)
24932493
{
2494-
const char *from;
24952494
struct branch *s;
24962495
struct object_id oid;
24972496

2498-
if (!skip_prefix(command_buf.buf, "from ", &from))
2499-
return 0;
2500-
25012497
oidcpy(&oid, &b->branch_tree.versions[1].oid);
25022498

2503-
s = lookup_branch(from);
2499+
s = lookup_branch(objectish);
25042500
if (b == s)
25052501
die("Can't create a branch from itself: %s", b->name);
25062502
else if (s) {
25072503
struct object_id *t = &s->branch_tree.versions[1].oid;
25082504
oidcpy(&b->oid, &s->oid);
25092505
oidcpy(&b->branch_tree.versions[0].oid, t);
25102506
oidcpy(&b->branch_tree.versions[1].oid, t);
2511-
} else if (*from == ':') {
2512-
uintmax_t idnum = parse_mark_ref_eol(from);
2507+
} else if (*objectish == ':') {
2508+
uintmax_t idnum = parse_mark_ref_eol(objectish);
25132509
struct object_entry *oe = find_mark(idnum);
25142510
if (oe->type != OBJ_COMMIT)
25152511
die("Mark :%" PRIuMAX " not a commit", idnum);
@@ -2523,13 +2519,13 @@ static int parse_from(struct branch *b)
25232519
} else
25242520
parse_from_existing(b);
25252521
}
2526-
} else if (!get_oid(from, &b->oid)) {
2522+
} else if (!get_oid(objectish, &b->oid)) {
25272523
parse_from_existing(b);
25282524
if (is_null_oid(&b->oid))
25292525
b->delete = 1;
25302526
}
25312527
else
2532-
die("Invalid ref name or SHA1 expression: %s", from);
2528+
die("Invalid ref name or SHA1 expression: %s", objectish);
25332529

25342530
if (b->branch_tree.tree && !oideq(&oid, &b->branch_tree.versions[1].oid)) {
25352531
release_tree_content_recursive(b->branch_tree.tree);
@@ -2540,6 +2536,26 @@ static int parse_from(struct branch *b)
25402536
return 1;
25412537
}
25422538

2539+
static int parse_from(struct branch *b)
2540+
{
2541+
const char *from;
2542+
2543+
if (!skip_prefix(command_buf.buf, "from ", &from))
2544+
return 0;
2545+
2546+
return parse_objectish(b, from);
2547+
}
2548+
2549+
static int parse_objectish_with_prefix(struct branch *b, const char *prefix)
2550+
{
2551+
const char *base;
2552+
2553+
if (!skip_prefix(command_buf.buf, prefix, &base))
2554+
return 0;
2555+
2556+
return parse_objectish(b, base);
2557+
}
2558+
25432559
static struct hash_list *parse_merge(unsigned int *count)
25442560
{
25452561
struct hash_list *list = NULL, **tail = &list, *n;
@@ -2714,6 +2730,7 @@ static void parse_new_tag(const char *arg)
27142730
first_tag = t;
27152731
last_tag = t;
27162732
read_next_command();
2733+
parse_mark();
27172734

27182735
/* from ... */
27192736
if (!skip_prefix(command_buf.buf, "from ", &from))
@@ -2770,7 +2787,7 @@ static void parse_new_tag(const char *arg)
27702787
strbuf_addbuf(&new_data, &msg);
27712788
free(tagger);
27722789

2773-
if (store_object(OBJ_TAG, &new_data, NULL, &t->oid, 0))
2790+
if (store_object(OBJ_TAG, &new_data, NULL, &t->oid, next_mark))
27742791
t->pack_id = MAX_PACK_ID;
27752792
else
27762793
t->pack_id = pack_id;
@@ -2779,6 +2796,7 @@ static void parse_new_tag(const char *arg)
27792796
static void parse_reset_branch(const char *arg)
27802797
{
27812798
struct branch *b;
2799+
const char *tag_name;
27822800

27832801
b = lookup_branch(arg);
27842802
if (b) {
@@ -2794,6 +2812,32 @@ static void parse_reset_branch(const char *arg)
27942812
b = new_branch(arg);
27952813
read_next_command();
27962814
parse_from(b);
2815+
if (b->delete && skip_prefix(b->name, "refs/tags/", &tag_name)) {
2816+
/*
2817+
* Elsewhere, we call dump_branches() before dump_tags(),
2818+
* and dump_branches() will handle ref deletions first, so
2819+
* in order to make sure the deletion actually takes effect,
2820+
* we need to remove the tag from our list of tags to update.
2821+
*
2822+
* NEEDSWORK: replace list of tags with hashmap for faster
2823+
* deletion?
2824+
*/
2825+
struct tag *t, *prev = NULL;
2826+
for (t = first_tag; t; t = t->next_tag) {
2827+
if (!strcmp(t->name, tag_name))
2828+
break;
2829+
prev = t;
2830+
}
2831+
if (t) {
2832+
if (prev)
2833+
prev->next_tag = t->next_tag;
2834+
else
2835+
first_tag = t->next_tag;
2836+
if (!t->next_tag)
2837+
last_tag = prev;
2838+
/* There is no mem_pool_free(t) function to call. */
2839+
}
2840+
}
27972841
if (command_buf.len > 0)
27982842
unread_command_buf = 1;
27992843
}
@@ -3060,6 +3104,28 @@ static void parse_progress(void)
30603104
skip_optional_lf();
30613105
}
30623106

3107+
static void parse_alias(void)
3108+
{
3109+
struct object_entry *e;
3110+
struct branch b;
3111+
3112+
skip_optional_lf();
3113+
read_next_command();
3114+
3115+
/* mark ... */
3116+
parse_mark();
3117+
if (!next_mark)
3118+
die(_("Expected 'mark' command, got %s"), command_buf.buf);
3119+
3120+
/* to ... */
3121+
memset(&b, 0, sizeof(b));
3122+
if (!parse_objectish_with_prefix(&b, "to "))
3123+
die(_("Expected 'to' command, got %s"), command_buf.buf);
3124+
e = find_object(&b.oid);
3125+
assert(e);
3126+
insert_mark(next_mark, e);
3127+
}
3128+
30633129
static char* make_fast_import_path(const char *path)
30643130
{
30653131
if (!relative_marks_paths || is_absolute_path(path))
@@ -3187,6 +3253,8 @@ static int parse_one_feature(const char *feature, int from_stream)
31873253
option_import_marks(arg, from_stream, 1);
31883254
} else if (skip_prefix(feature, "export-marks=", &arg)) {
31893255
option_export_marks(arg);
3256+
} else if (!strcmp(feature, "alias")) {
3257+
; /* Don't die - this feature is supported */
31903258
} else if (!strcmp(feature, "get-mark")) {
31913259
; /* Don't die - this feature is supported */
31923260
} else if (!strcmp(feature, "cat-blob")) {
@@ -3343,6 +3411,8 @@ int cmd_main(int argc, const char **argv)
33433411
parse_checkpoint();
33443412
else if (!strcmp("done", command_buf.buf))
33453413
break;
3414+
else if (!strcmp("alias", command_buf.buf))
3415+
parse_alias();
33463416
else if (starts_with(command_buf.buf, "progress "))
33473417
parse_progress();
33483418
else if (skip_prefix(command_buf.buf, "feature ", &v))

0 commit comments

Comments
 (0)