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 <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
parent
f73b2aba05
commit
b8f50e5b60
|
|
@ -337,6 +337,13 @@ and control the current import process. More detailed discussion
|
||||||
`commit` command. This command is optional and is not
|
`commit` command. This command is optional and is not
|
||||||
needed to perform an import.
|
needed to perform an import.
|
||||||
|
|
||||||
|
`alias`::
|
||||||
|
Record that a mark refers to a given object without first
|
||||||
|
creating any new object. Using --import-marks and referring
|
||||||
|
to missing marks will cause fast-import to fail, so aliases
|
||||||
|
can provide a way to set otherwise pruned commits to a valid
|
||||||
|
value (e.g. the nearest non-pruned ancestor).
|
||||||
|
|
||||||
`checkpoint`::
|
`checkpoint`::
|
||||||
Forces fast-import to close the current packfile, generate its
|
Forces fast-import to close the current packfile, generate its
|
||||||
unique SHA-1 checksum and index, and start a new packfile.
|
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.
|
||||||
+
|
+
|
||||||
The `LF` after `<delim> LF` is optional (it used to be required).
|
The `LF` after `<delim> LF` is optional (it used to be required).
|
||||||
|
|
||||||
|
`alias`
|
||||||
|
~~~~~~~
|
||||||
|
Record that a mark refers to a given object without first creating any
|
||||||
|
new object.
|
||||||
|
|
||||||
|
....
|
||||||
|
'alias' LF
|
||||||
|
mark
|
||||||
|
'to' SP <commit-ish> LF
|
||||||
|
LF?
|
||||||
|
....
|
||||||
|
|
||||||
|
For a detailed description of `<commit-ish>` see above under `from`.
|
||||||
|
|
||||||
|
|
||||||
`checkpoint`
|
`checkpoint`
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
Forces fast-import to close the current packfile, start a new one, and to
|
Forces fast-import to close the current packfile, start a new one, and to
|
||||||
|
|
|
||||||
|
|
@ -2491,18 +2491,14 @@ static void parse_from_existing(struct branch *b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_from(struct branch *b)
|
static int parse_objectish(struct branch *b, const char *objectish)
|
||||||
{
|
{
|
||||||
const char *from;
|
|
||||||
struct branch *s;
|
struct branch *s;
|
||||||
struct object_id oid;
|
struct object_id oid;
|
||||||
|
|
||||||
if (!skip_prefix(command_buf.buf, "from ", &from))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
oidcpy(&oid, &b->branch_tree.versions[1].oid);
|
oidcpy(&oid, &b->branch_tree.versions[1].oid);
|
||||||
|
|
||||||
s = lookup_branch(from);
|
s = lookup_branch(objectish);
|
||||||
if (b == s)
|
if (b == s)
|
||||||
die("Can't create a branch from itself: %s", b->name);
|
die("Can't create a branch from itself: %s", b->name);
|
||||||
else if (s) {
|
else if (s) {
|
||||||
|
|
@ -2510,8 +2506,8 @@ static int parse_from(struct branch *b)
|
||||||
oidcpy(&b->oid, &s->oid);
|
oidcpy(&b->oid, &s->oid);
|
||||||
oidcpy(&b->branch_tree.versions[0].oid, t);
|
oidcpy(&b->branch_tree.versions[0].oid, t);
|
||||||
oidcpy(&b->branch_tree.versions[1].oid, t);
|
oidcpy(&b->branch_tree.versions[1].oid, t);
|
||||||
} else if (*from == ':') {
|
} else if (*objectish == ':') {
|
||||||
uintmax_t idnum = parse_mark_ref_eol(from);
|
uintmax_t idnum = parse_mark_ref_eol(objectish);
|
||||||
struct object_entry *oe = find_mark(idnum);
|
struct object_entry *oe = find_mark(idnum);
|
||||||
if (oe->type != OBJ_COMMIT)
|
if (oe->type != OBJ_COMMIT)
|
||||||
die("Mark :%" PRIuMAX " not a commit", idnum);
|
die("Mark :%" PRIuMAX " not a commit", idnum);
|
||||||
|
|
@ -2525,13 +2521,13 @@ static int parse_from(struct branch *b)
|
||||||
} else
|
} else
|
||||||
parse_from_existing(b);
|
parse_from_existing(b);
|
||||||
}
|
}
|
||||||
} else if (!get_oid(from, &b->oid)) {
|
} else if (!get_oid(objectish, &b->oid)) {
|
||||||
parse_from_existing(b);
|
parse_from_existing(b);
|
||||||
if (is_null_oid(&b->oid))
|
if (is_null_oid(&b->oid))
|
||||||
b->delete = 1;
|
b->delete = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
die("Invalid ref name or SHA1 expression: %s", from);
|
die("Invalid ref name or SHA1 expression: %s", objectish);
|
||||||
|
|
||||||
if (b->branch_tree.tree && !oideq(&oid, &b->branch_tree.versions[1].oid)) {
|
if (b->branch_tree.tree && !oideq(&oid, &b->branch_tree.versions[1].oid)) {
|
||||||
release_tree_content_recursive(b->branch_tree.tree);
|
release_tree_content_recursive(b->branch_tree.tree);
|
||||||
|
|
@ -2542,6 +2538,26 @@ static int parse_from(struct branch *b)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_from(struct branch *b)
|
||||||
|
{
|
||||||
|
const char *from;
|
||||||
|
|
||||||
|
if (!skip_prefix(command_buf.buf, "from ", &from))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return parse_objectish(b, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_objectish_with_prefix(struct branch *b, const char *prefix)
|
||||||
|
{
|
||||||
|
const char *base;
|
||||||
|
|
||||||
|
if (!skip_prefix(command_buf.buf, prefix, &base))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return parse_objectish(b, base);
|
||||||
|
}
|
||||||
|
|
||||||
static struct hash_list *parse_merge(unsigned int *count)
|
static struct hash_list *parse_merge(unsigned int *count)
|
||||||
{
|
{
|
||||||
struct hash_list *list = NULL, **tail = &list, *n;
|
struct hash_list *list = NULL, **tail = &list, *n;
|
||||||
|
|
@ -3087,6 +3103,28 @@ static void parse_progress(void)
|
||||||
skip_optional_lf();
|
skip_optional_lf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_alias(void)
|
||||||
|
{
|
||||||
|
struct object_entry *e;
|
||||||
|
struct branch b;
|
||||||
|
|
||||||
|
skip_optional_lf();
|
||||||
|
read_next_command();
|
||||||
|
|
||||||
|
/* mark ... */
|
||||||
|
parse_mark();
|
||||||
|
if (!next_mark)
|
||||||
|
die(_("Expected 'mark' command, got %s"), command_buf.buf);
|
||||||
|
|
||||||
|
/* to ... */
|
||||||
|
memset(&b, 0, sizeof(b));
|
||||||
|
if (!parse_objectish_with_prefix(&b, "to "))
|
||||||
|
die(_("Expected 'to' command, got %s"), command_buf.buf);
|
||||||
|
e = find_object(&b.oid);
|
||||||
|
assert(e);
|
||||||
|
insert_mark(next_mark, e);
|
||||||
|
}
|
||||||
|
|
||||||
static char* make_fast_import_path(const char *path)
|
static char* make_fast_import_path(const char *path)
|
||||||
{
|
{
|
||||||
if (!relative_marks_paths || is_absolute_path(path))
|
if (!relative_marks_paths || is_absolute_path(path))
|
||||||
|
|
@ -3214,6 +3252,8 @@ static int parse_one_feature(const char *feature, int from_stream)
|
||||||
option_import_marks(arg, from_stream, 1);
|
option_import_marks(arg, from_stream, 1);
|
||||||
} else if (skip_prefix(feature, "export-marks=", &arg)) {
|
} else if (skip_prefix(feature, "export-marks=", &arg)) {
|
||||||
option_export_marks(arg);
|
option_export_marks(arg);
|
||||||
|
} else if (!strcmp(feature, "alias")) {
|
||||||
|
; /* Don't die - this feature is supported */
|
||||||
} else if (!strcmp(feature, "get-mark")) {
|
} else if (!strcmp(feature, "get-mark")) {
|
||||||
; /* Don't die - this feature is supported */
|
; /* Don't die - this feature is supported */
|
||||||
} else if (!strcmp(feature, "cat-blob")) {
|
} else if (!strcmp(feature, "cat-blob")) {
|
||||||
|
|
@ -3370,6 +3410,8 @@ int cmd_main(int argc, const char **argv)
|
||||||
parse_checkpoint();
|
parse_checkpoint();
|
||||||
else if (!strcmp("done", command_buf.buf))
|
else if (!strcmp("done", command_buf.buf))
|
||||||
break;
|
break;
|
||||||
|
else if (!strcmp("alias", command_buf.buf))
|
||||||
|
parse_alias();
|
||||||
else if (starts_with(command_buf.buf, "progress "))
|
else if (starts_with(command_buf.buf, "progress "))
|
||||||
parse_progress();
|
parse_progress();
|
||||||
else if (skip_prefix(command_buf.buf, "feature ", &v))
|
else if (skip_prefix(command_buf.buf, "feature ", &v))
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,10 @@ test_expect_success 'A: create pack from stdin' '
|
||||||
Tag of tag of our lovely commit
|
Tag of tag of our lovely commit
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
alias
|
||||||
|
mark :8
|
||||||
|
to :5
|
||||||
|
|
||||||
INPUT_END
|
INPUT_END
|
||||||
git fast-import --export-marks=marks.out <input &&
|
git fast-import --export-marks=marks.out <input &&
|
||||||
git whatchanged master
|
git whatchanged master
|
||||||
|
|
@ -195,6 +199,7 @@ test_expect_success 'A: verify marks output' '
|
||||||
:5 $(git rev-parse --verify master^0)
|
:5 $(git rev-parse --verify master^0)
|
||||||
:6 $(git cat-file tag nested | grep object | cut -d" " -f 2)
|
:6 $(git cat-file tag nested | grep object | cut -d" " -f 2)
|
||||||
:7 $(git rev-parse --verify nested)
|
:7 $(git rev-parse --verify nested)
|
||||||
|
:8 $(git rev-parse --verify master^0)
|
||||||
EOF
|
EOF
|
||||||
test_cmp expect marks.out
|
test_cmp expect marks.out
|
||||||
'
|
'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue