Skip to content

Commit b8f50e5

Browse files
newrengitster
authored andcommitted
fast-import: add support for new 'alias' command
fast-export and fast-import have nice --import-marks flags which allow for incremental migrations. However, if there is a mark in fast-export's file of marks without a corresponding mark in the one for fast-import, then we run the risk that fast-export tries to send new objects relative to the mark it knows which fast-import does not, causing fast-import to fail. This arises in practice when there is a filter of some sort running between the fast-export and fast-import processes which prunes some commits programmatically. Provide such a filter with the ability to alias pruned commits to their most recent non-pruned ancestor. Signed-off-by: Elijah Newren <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f73b2ab commit b8f50e5

File tree

3 files changed

+79
-10
lines changed

3 files changed

+79
-10
lines changed

Documentation/git-fast-import.txt

Lines changed: 22 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.
@@ -914,6 +921,21 @@ a data chunk which does not have an LF as its last byte.
914921
+
915922
The `LF` after `<delim> LF` is optional (it used to be required).
916923

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+
917939
`checkpoint`
918940
~~~~~~~~~~~~
919941
Forces fast-import to close the current packfile, start a new one, and to

fast-import.c

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,27 +2491,23 @@ static void parse_from_existing(struct branch *b)
24912491
}
24922492
}
24932493

2494-
static int parse_from(struct branch *b)
2494+
static int parse_objectish(struct branch *b, const char *objectish)
24952495
{
2496-
const char *from;
24972496
struct branch *s;
24982497
struct object_id oid;
24992498

2500-
if (!skip_prefix(command_buf.buf, "from ", &from))
2501-
return 0;
2502-
25032499
oidcpy(&oid, &b->branch_tree.versions[1].oid);
25042500

2505-
s = lookup_branch(from);
2501+
s = lookup_branch(objectish);
25062502
if (b == s)
25072503
die("Can't create a branch from itself: %s", b->name);
25082504
else if (s) {
25092505
struct object_id *t = &s->branch_tree.versions[1].oid;
25102506
oidcpy(&b->oid, &s->oid);
25112507
oidcpy(&b->branch_tree.versions[0].oid, t);
25122508
oidcpy(&b->branch_tree.versions[1].oid, t);
2513-
} else if (*from == ':') {
2514-
uintmax_t idnum = parse_mark_ref_eol(from);
2509+
} else if (*objectish == ':') {
2510+
uintmax_t idnum = parse_mark_ref_eol(objectish);
25152511
struct object_entry *oe = find_mark(idnum);
25162512
if (oe->type != OBJ_COMMIT)
25172513
die("Mark :%" PRIuMAX " not a commit", idnum);
@@ -2525,13 +2521,13 @@ static int parse_from(struct branch *b)
25252521
} else
25262522
parse_from_existing(b);
25272523
}
2528-
} else if (!get_oid(from, &b->oid)) {
2524+
} else if (!get_oid(objectish, &b->oid)) {
25292525
parse_from_existing(b);
25302526
if (is_null_oid(&b->oid))
25312527
b->delete = 1;
25322528
}
25332529
else
2534-
die("Invalid ref name or SHA1 expression: %s", from);
2530+
die("Invalid ref name or SHA1 expression: %s", objectish);
25352531

25362532
if (b->branch_tree.tree && !oideq(&oid, &b->branch_tree.versions[1].oid)) {
25372533
release_tree_content_recursive(b->branch_tree.tree);
@@ -2542,6 +2538,26 @@ static int parse_from(struct branch *b)
25422538
return 1;
25432539
}
25442540

2541+
static int parse_from(struct branch *b)
2542+
{
2543+
const char *from;
2544+
2545+
if (!skip_prefix(command_buf.buf, "from ", &from))
2546+
return 0;
2547+
2548+
return parse_objectish(b, from);
2549+
}
2550+
2551+
static int parse_objectish_with_prefix(struct branch *b, const char *prefix)
2552+
{
2553+
const char *base;
2554+
2555+
if (!skip_prefix(command_buf.buf, prefix, &base))
2556+
return 0;
2557+
2558+
return parse_objectish(b, base);
2559+
}
2560+
25452561
static struct hash_list *parse_merge(unsigned int *count)
25462562
{
25472563
struct hash_list *list = NULL, **tail = &list, *n;
@@ -3087,6 +3103,28 @@ static void parse_progress(void)
30873103
skip_optional_lf();
30883104
}
30893105

3106+
static void parse_alias(void)
3107+
{
3108+
struct object_entry *e;
3109+
struct branch b;
3110+
3111+
skip_optional_lf();
3112+
read_next_command();
3113+
3114+
/* mark ... */
3115+
parse_mark();
3116+
if (!next_mark)
3117+
die(_("Expected 'mark' command, got %s"), command_buf.buf);
3118+
3119+
/* to ... */
3120+
memset(&b, 0, sizeof(b));
3121+
if (!parse_objectish_with_prefix(&b, "to "))
3122+
die(_("Expected 'to' command, got %s"), command_buf.buf);
3123+
e = find_object(&b.oid);
3124+
assert(e);
3125+
insert_mark(next_mark, e);
3126+
}
3127+
30903128
static char* make_fast_import_path(const char *path)
30913129
{
30923130
if (!relative_marks_paths || is_absolute_path(path))
@@ -3214,6 +3252,8 @@ static int parse_one_feature(const char *feature, int from_stream)
32143252
option_import_marks(arg, from_stream, 1);
32153253
} else if (skip_prefix(feature, "export-marks=", &arg)) {
32163254
option_export_marks(arg);
3255+
} else if (!strcmp(feature, "alias")) {
3256+
; /* Don't die - this feature is supported */
32173257
} else if (!strcmp(feature, "get-mark")) {
32183258
; /* Don't die - this feature is supported */
32193259
} else if (!strcmp(feature, "cat-blob")) {
@@ -3370,6 +3410,8 @@ int cmd_main(int argc, const char **argv)
33703410
parse_checkpoint();
33713411
else if (!strcmp("done", command_buf.buf))
33723412
break;
3413+
else if (!strcmp("alias", command_buf.buf))
3414+
parse_alias();
33733415
else if (starts_with(command_buf.buf, "progress "))
33743416
parse_progress();
33753417
else if (skip_prefix(command_buf.buf, "feature ", &v))

t/t9300-fast-import.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ test_expect_success 'A: create pack from stdin' '
111111
Tag of tag of our lovely commit
112112
EOF
113113
114+
alias
115+
mark :8
116+
to :5
117+
114118
INPUT_END
115119
git fast-import --export-marks=marks.out <input &&
116120
git whatchanged master
@@ -195,6 +199,7 @@ test_expect_success 'A: verify marks output' '
195199
:5 $(git rev-parse --verify master^0)
196200
:6 $(git cat-file tag nested | grep object | cut -d" " -f 2)
197201
:7 $(git rev-parse --verify nested)
202+
:8 $(git rev-parse --verify master^0)
198203
EOF
199204
test_cmp expect marks.out
200205
'

0 commit comments

Comments
 (0)