зеркало из https://github.com/microsoft/git.git
Merge branch 'en/fast-export-import'
Small fixes and features for fast-export and fast-import, mostly on the fast-export side. * en/fast-export-import: fast-export: add a --show-original-ids option to show original names fast-import: remove unmaintained duplicate documentation fast-export: add --reference-excluded-parents option fast-export: ensure we export requested refs fast-export: when using paths, avoid corrupt stream with non-existent mark fast-export: move commit rewriting logic into a function for reuse fast-export: avoid dying when filtering by paths and old tags exist fast-export: use value from correct enum git-fast-export.txt: clarify misleading documentation about rev-list args git-fast-import.txt: fix documentation for --quiet option fast-export: convert sha1 to oid
This commit is contained in:
Коммит
4d59753227
|
@ -110,6 +110,25 @@ marks the same across runs.
|
|||
the shape of the history and stored tree. See the section on
|
||||
`ANONYMIZING` below.
|
||||
|
||||
--reference-excluded-parents::
|
||||
By default, running a command such as `git fast-export
|
||||
master~5..master` will not include the commit master{tilde}5
|
||||
and will make master{tilde}4 no longer have master{tilde}5 as
|
||||
a parent (though both the old master{tilde}4 and new
|
||||
master{tilde}4 will have all the same files). Use
|
||||
--reference-excluded-parents to instead have the the stream
|
||||
refer to commits in the excluded range of history by their
|
||||
sha1sum. Note that the resulting stream can only be used by a
|
||||
repository which already contains the necessary parent
|
||||
commits.
|
||||
|
||||
--show-original-ids::
|
||||
Add an extra directive to the output for commits and blobs,
|
||||
`original-oid <SHA1SUM>`. While such directives will likely be
|
||||
ignored by importers such as git-fast-import, it may be useful
|
||||
for intermediary filters (e.g. for rewriting commit messages
|
||||
which refer to older commits, or for stripping blobs by id).
|
||||
|
||||
--refspec::
|
||||
Apply the specified refspec to each ref exported. Multiple of them can
|
||||
be specified.
|
||||
|
@ -119,7 +138,9 @@ marks the same across runs.
|
|||
'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.
|
||||
added since its 10th ancestor commit and (unless the
|
||||
--reference-excluded-parents option is specified) all files
|
||||
common to master{tilde}9 and master{tilde}10.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
|
|
@ -40,9 +40,10 @@ OPTIONS
|
|||
not contain the old commit).
|
||||
|
||||
--quiet::
|
||||
Disable all non-fatal output, making fast-import silent when it
|
||||
is successful. This option disables the output shown by
|
||||
--stats.
|
||||
Disable the output shown by --stats, making fast-import usually
|
||||
be silent when it is successful. However, if the import stream
|
||||
has directives intended to show user output (e.g. `progress`
|
||||
directives), the corresponding messages will still be shown.
|
||||
|
||||
--stats::
|
||||
Display some basic statistics about the objects fast-import has
|
||||
|
@ -384,6 +385,7 @@ change to the project.
|
|||
....
|
||||
'commit' SP <ref> LF
|
||||
mark?
|
||||
original-oid?
|
||||
('author' (SP <name>)? SP LT <email> GT SP <when> LF)?
|
||||
'committer' (SP <name>)? SP LT <email> GT SP <when> LF
|
||||
data
|
||||
|
@ -740,6 +742,19 @@ New marks are created automatically. Existing marks can be moved
|
|||
to another object simply by reusing the same `<idnum>` in another
|
||||
`mark` command.
|
||||
|
||||
`original-oid`
|
||||
~~~~~~~~~~~~~~
|
||||
Provides the name of the object in the original source control system.
|
||||
fast-import will simply ignore this directive, but filter processes
|
||||
which operate on and modify the stream before feeding to fast-import
|
||||
may have uses for this information
|
||||
|
||||
....
|
||||
'original-oid' SP <object-identifier> LF
|
||||
....
|
||||
|
||||
where `<object-identifer>` is any string not containing LF.
|
||||
|
||||
`tag`
|
||||
~~~~~
|
||||
Creates an annotated tag referring to a specific commit. To create
|
||||
|
@ -748,6 +763,7 @@ lightweight (non-annotated) tags see the `reset` command below.
|
|||
....
|
||||
'tag' SP <name> LF
|
||||
'from' SP <commit-ish> LF
|
||||
original-oid?
|
||||
'tagger' (SP <name>)? SP LT <email> GT SP <when> LF
|
||||
data
|
||||
....
|
||||
|
@ -822,6 +838,7 @@ assigned mark.
|
|||
....
|
||||
'blob' LF
|
||||
mark?
|
||||
original-oid?
|
||||
data
|
||||
....
|
||||
|
||||
|
|
|
@ -31,13 +31,16 @@ static const char *fast_export_usage[] = {
|
|||
};
|
||||
|
||||
static int progress;
|
||||
static enum { ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = ABORT;
|
||||
static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ERROR;
|
||||
static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
|
||||
static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
|
||||
static int fake_missing_tagger;
|
||||
static int use_done_feature;
|
||||
static int no_data;
|
||||
static int full_tree;
|
||||
static int reference_excluded_commits;
|
||||
static int show_original_ids;
|
||||
static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
|
||||
static struct string_list tag_refs = STRING_LIST_INIT_NODUP;
|
||||
static struct refspec refspecs = REFSPEC_INIT_FETCH;
|
||||
static int anonymize;
|
||||
static struct revision_sources revision_sources;
|
||||
|
@ -46,7 +49,7 @@ static int parse_opt_signed_tag_mode(const struct option *opt,
|
|||
const char *arg, int unset)
|
||||
{
|
||||
if (unset || !strcmp(arg, "abort"))
|
||||
signed_tag_mode = ABORT;
|
||||
signed_tag_mode = SIGNED_TAG_ABORT;
|
||||
else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
|
||||
signed_tag_mode = VERBATIM;
|
||||
else if (!strcmp(arg, "warn"))
|
||||
|
@ -64,7 +67,7 @@ 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 = ERROR;
|
||||
tag_of_filtered_mode = TAG_FILTERING_ABORT;
|
||||
else if (!strcmp(arg, "drop"))
|
||||
tag_of_filtered_mode = DROP;
|
||||
else if (!strcmp(arg, "rewrite"))
|
||||
|
@ -187,6 +190,22 @@ static int get_object_mark(struct object *object)
|
|||
return ptr_to_mark(decoration);
|
||||
}
|
||||
|
||||
static struct commit *rewrite_commit(struct commit *p)
|
||||
{
|
||||
for (;;) {
|
||||
if (p->parents && p->parents->next)
|
||||
break;
|
||||
if (p->object.flags & UNINTERESTING)
|
||||
break;
|
||||
if (!(p->object.flags & TREESAME))
|
||||
break;
|
||||
if (!p->parents)
|
||||
return NULL;
|
||||
p = p->parents->item;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void show_progress(void)
|
||||
{
|
||||
static int counter = 0;
|
||||
|
@ -243,7 +262,7 @@ static void export_blob(const struct object_id *oid)
|
|||
if (!buf)
|
||||
die("could not read blob %s", oid_to_hex(oid));
|
||||
if (check_object_signature(oid, buf, size, type_name(type)) < 0)
|
||||
die("sha1 mismatch in blob %s", oid_to_hex(oid));
|
||||
die("oid mismatch in blob %s", oid_to_hex(oid));
|
||||
object = parse_object_buffer(the_repository, oid, type,
|
||||
size, buf, &eaten);
|
||||
}
|
||||
|
@ -253,7 +272,10 @@ static void export_blob(const struct object_id *oid)
|
|||
|
||||
mark_next_object(object);
|
||||
|
||||
printf("blob\nmark :%"PRIu32"\ndata %"PRIuMAX"\n", last_idnum, (uintmax_t)size);
|
||||
printf("blob\nmark :%"PRIu32"\n", last_idnum);
|
||||
if (show_original_ids)
|
||||
printf("original-oid %s\n", oid_to_hex(oid));
|
||||
printf("data %"PRIuMAX"\n", (uintmax_t)size);
|
||||
if (size && fwrite(buf, size, 1, stdout) != 1)
|
||||
die_errno("could not write blob '%s'", oid_to_hex(oid));
|
||||
printf("\n");
|
||||
|
@ -330,17 +352,18 @@ static void print_path(const char *path)
|
|||
|
||||
static void *generate_fake_oid(const void *old, size_t *len)
|
||||
{
|
||||
static uint32_t counter = 1; /* avoid null sha1 */
|
||||
unsigned char *out = xcalloc(GIT_SHA1_RAWSZ, 1);
|
||||
put_be32(out + GIT_SHA1_RAWSZ - 4, counter++);
|
||||
static uint32_t counter = 1; /* avoid null oid */
|
||||
const unsigned hashsz = the_hash_algo->rawsz;
|
||||
unsigned char *out = xcalloc(hashsz, 1);
|
||||
put_be32(out + hashsz - 4, counter++);
|
||||
return out;
|
||||
}
|
||||
|
||||
static const unsigned char *anonymize_sha1(const struct object_id *oid)
|
||||
static const struct object_id *anonymize_oid(const struct object_id *oid)
|
||||
{
|
||||
static struct hashmap sha1s;
|
||||
size_t len = GIT_SHA1_RAWSZ;
|
||||
return anonymize_mem(&sha1s, generate_fake_oid, oid, &len);
|
||||
static struct hashmap objs;
|
||||
size_t len = the_hash_algo->rawsz;
|
||||
return anonymize_mem(&objs, generate_fake_oid, oid, &len);
|
||||
}
|
||||
|
||||
static void show_filemodify(struct diff_queue_struct *q,
|
||||
|
@ -399,9 +422,9 @@ static void show_filemodify(struct diff_queue_struct *q,
|
|||
*/
|
||||
if (no_data || S_ISGITLINK(spec->mode))
|
||||
printf("M %06o %s ", spec->mode,
|
||||
sha1_to_hex(anonymize ?
|
||||
anonymize_sha1(&spec->oid) :
|
||||
spec->oid.hash));
|
||||
oid_to_hex(anonymize ?
|
||||
anonymize_oid(&spec->oid) :
|
||||
&spec->oid));
|
||||
else {
|
||||
struct object *object = lookup_object(the_repository,
|
||||
spec->oid.hash);
|
||||
|
@ -579,7 +602,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
|
|||
message += 2;
|
||||
|
||||
if (commit->parents &&
|
||||
get_object_mark(&commit->parents->item->object) != 0 &&
|
||||
(get_object_mark(&commit->parents->item->object) != 0 ||
|
||||
reference_excluded_commits) &&
|
||||
!full_tree) {
|
||||
parse_commit_or_die(commit->parents->item);
|
||||
diff_tree_oid(get_commit_tree_oid(commit->parents->item),
|
||||
|
@ -595,6 +619,13 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
|
|||
export_blob(&diff_queued_diff.queue[i]->two->oid);
|
||||
|
||||
refname = *revision_sources_at(&revision_sources, commit);
|
||||
/*
|
||||
* FIXME: string_list_remove() below for each ref is overall
|
||||
* O(N^2). Compared to a history walk and diffing trees, this is
|
||||
* just lost in the noise in practice. However, theoretically a
|
||||
* repo may have enough refs for this to become slow.
|
||||
*/
|
||||
string_list_remove(&extra_refs, refname, 0);
|
||||
if (anonymize) {
|
||||
refname = anonymize_refname(refname);
|
||||
anonymize_ident_line(&committer, &committer_end);
|
||||
|
@ -608,8 +639,10 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
|
|||
reencoded = reencode_string(message, "UTF-8", encoding);
|
||||
if (!commit->parents)
|
||||
printf("reset %s\n", refname);
|
||||
printf("commit %s\nmark :%"PRIu32"\n%.*s\n%.*s\ndata %u\n%s",
|
||||
refname, last_idnum,
|
||||
printf("commit %s\nmark :%"PRIu32"\n", refname, last_idnum);
|
||||
if (show_original_ids)
|
||||
printf("original-oid %s\n", oid_to_hex(&commit->object.oid));
|
||||
printf("%.*s\n%.*s\ndata %u\n%s",
|
||||
(int)(author_end - author), author,
|
||||
(int)(committer_end - committer), committer,
|
||||
(unsigned)(reencoded
|
||||
|
@ -620,13 +653,21 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
|
|||
unuse_commit_buffer(commit, commit_buffer);
|
||||
|
||||
for (i = 0, p = commit->parents; p; p = p->next) {
|
||||
int mark = get_object_mark(&p->item->object);
|
||||
if (!mark)
|
||||
struct object *obj = &p->item->object;
|
||||
int mark = get_object_mark(obj);
|
||||
|
||||
if (!mark && !reference_excluded_commits)
|
||||
continue;
|
||||
if (i == 0)
|
||||
printf("from :%d\n", mark);
|
||||
printf("from ");
|
||||
else
|
||||
printf("merge :%d\n", mark);
|
||||
printf("merge ");
|
||||
if (mark)
|
||||
printf(":%d\n", mark);
|
||||
else
|
||||
printf("%s\n", oid_to_hex(anonymize ?
|
||||
anonymize_oid(&obj->oid) :
|
||||
&obj->oid));
|
||||
i++;
|
||||
}
|
||||
|
||||
|
@ -727,7 +768,7 @@ static void handle_tag(const char *name, struct tag *tag)
|
|||
"\n-----BEGIN PGP SIGNATURE-----\n");
|
||||
if (signature)
|
||||
switch(signed_tag_mode) {
|
||||
case ABORT:
|
||||
case SIGNED_TAG_ABORT:
|
||||
die("encountered signed tag %s; use "
|
||||
"--signed-tags=<mode> to handle it",
|
||||
oid_to_hex(&tag->object.oid));
|
||||
|
@ -752,7 +793,7 @@ static void handle_tag(const char *name, struct tag *tag)
|
|||
tagged_mark = get_object_mark(tagged);
|
||||
if (!tagged_mark) {
|
||||
switch(tag_of_filtered_mode) {
|
||||
case ABORT:
|
||||
case TAG_FILTERING_ABORT:
|
||||
die("tag %s tags unexported object; use "
|
||||
"--tag-of-filtered-object=<mode> to handle it",
|
||||
oid_to_hex(&tag->object.oid));
|
||||
|
@ -766,18 +807,12 @@ static void handle_tag(const char *name, struct tag *tag)
|
|||
oid_to_hex(&tag->object.oid),
|
||||
type_name(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",
|
||||
oid_to_hex(&tag->object.oid));
|
||||
p = p->parents->item;
|
||||
p = rewrite_commit((struct commit *)tagged);
|
||||
if (!p) {
|
||||
printf("reset %s\nfrom %s\n\n",
|
||||
name, oid_to_hex(&null_oid));
|
||||
free(buf);
|
||||
return;
|
||||
}
|
||||
tagged_mark = get_object_mark(&p->object);
|
||||
}
|
||||
|
@ -785,8 +820,10 @@ static void handle_tag(const char *name, struct tag *tag)
|
|||
|
||||
if (starts_with(name, "refs/tags/"))
|
||||
name += 10;
|
||||
printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
|
||||
name, tagged_mark,
|
||||
printf("tag %s\nfrom :%d\n", name, tagged_mark);
|
||||
if (show_original_ids)
|
||||
printf("original-oid %s\n", oid_to_hex(&tag->object.oid));
|
||||
printf("%.*s%sdata %d\n%.*s\n",
|
||||
(int)(tagger_end - tagger), tagger,
|
||||
tagger == tagger_end ? "" : "\n",
|
||||
(int)message_size, (int)message_size, message ? message : "");
|
||||
|
@ -804,7 +841,7 @@ static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
|
|||
/* handle nested tags */
|
||||
while (tag && tag->object.type == OBJ_TAG) {
|
||||
parse_object(the_repository, &tag->object.oid);
|
||||
string_list_append(&extra_refs, full_name)->util = tag;
|
||||
string_list_append(&tag_refs, full_name)->util = tag;
|
||||
tag = (struct tag *)tag->tagged;
|
||||
}
|
||||
if (!tag)
|
||||
|
@ -863,25 +900,30 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
|
|||
}
|
||||
|
||||
/*
|
||||
* This ref will not be updated through a commit, lets make
|
||||
* sure it gets properly updated eventually.
|
||||
* Make sure this ref gets properly updated eventually, whether
|
||||
* through a commit or manually at the end.
|
||||
*/
|
||||
if (*revision_sources_at(&revision_sources, commit) ||
|
||||
commit->object.flags & SHOWN)
|
||||
if (e->item->type != OBJ_TAG)
|
||||
string_list_append(&extra_refs, full_name)->util = commit;
|
||||
|
||||
if (!*revision_sources_at(&revision_sources, commit))
|
||||
*revision_sources_at(&revision_sources, commit) = full_name;
|
||||
}
|
||||
|
||||
string_list_sort(&extra_refs);
|
||||
string_list_remove_duplicates(&extra_refs, 0);
|
||||
}
|
||||
|
||||
static void handle_tags_and_duplicates(void)
|
||||
static void handle_tags_and_duplicates(struct string_list *extras)
|
||||
{
|
||||
struct commit *commit;
|
||||
int i;
|
||||
|
||||
for (i = extra_refs.nr - 1; i >= 0; i--) {
|
||||
const char *name = extra_refs.items[i].string;
|
||||
struct object *object = extra_refs.items[i].util;
|
||||
for (i = extras->nr - 1; i >= 0; i--) {
|
||||
const char *name = extras->items[i].string;
|
||||
struct object *object = extras->items[i].util;
|
||||
int mark;
|
||||
|
||||
switch (object->type) {
|
||||
case OBJ_TAG:
|
||||
handle_tag(name, (struct tag *)object);
|
||||
|
@ -890,9 +932,45 @@ static void handle_tags_and_duplicates(void)
|
|||
if (anonymize)
|
||||
name = anonymize_refname(name);
|
||||
/* create refs pointing to already seen commits */
|
||||
commit = (struct commit *)object;
|
||||
printf("reset %s\nfrom :%d\n\n", name,
|
||||
get_object_mark(&commit->object));
|
||||
commit = rewrite_commit((struct commit *)object);
|
||||
if (!commit) {
|
||||
/*
|
||||
* Neither this object nor any of its
|
||||
* ancestors touch any relevant paths, so
|
||||
* it has been filtered to nothing. Delete
|
||||
* it.
|
||||
*/
|
||||
printf("reset %s\nfrom %s\n\n",
|
||||
name, oid_to_hex(&null_oid));
|
||||
continue;
|
||||
}
|
||||
|
||||
mark = get_object_mark(&commit->object);
|
||||
if (!mark) {
|
||||
/*
|
||||
* Getting here means we have a commit which
|
||||
* was excluded by a negative refspec (e.g.
|
||||
* fast-export ^master master). If we are
|
||||
* referencing excluded commits, set the ref
|
||||
* to the exact commit. Otherwise, the user
|
||||
* wants the branch exported but every commit
|
||||
* in its history to be deleted, which basically
|
||||
* just means deletion of the ref.
|
||||
*/
|
||||
if (!reference_excluded_commits) {
|
||||
/* delete the ref */
|
||||
printf("reset %s\nfrom %s\n\n",
|
||||
name, oid_to_hex(&null_oid));
|
||||
continue;
|
||||
}
|
||||
/* set ref to commit using oid, not mark */
|
||||
printf("reset %s\nfrom %s\n\n", name,
|
||||
oid_to_hex(&commit->object.oid));
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("reset %s\nfrom :%d\n\n", name, mark
|
||||
);
|
||||
show_progress();
|
||||
break;
|
||||
}
|
||||
|
@ -988,7 +1066,7 @@ static void handle_deletes(void)
|
|||
continue;
|
||||
|
||||
printf("reset %s\nfrom %s\n\n",
|
||||
refspec->dst, sha1_to_hex(null_sha1));
|
||||
refspec->dst, oid_to_hex(&null_oid));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1024,6 +1102,11 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
|||
OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"),
|
||||
N_("Apply refspec to exported refs")),
|
||||
OPT_BOOL(0, "anonymize", &anonymize, N_("anonymize output")),
|
||||
OPT_BOOL(0, "reference-excluded-parents",
|
||||
&reference_excluded_commits, N_("Reference parents which are not in fast-export stream by object id")),
|
||||
OPT_BOOL(0, "show-original-ids", &show_original_ids,
|
||||
N_("Show original object ids of blobs/commits")),
|
||||
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
@ -1080,7 +1163,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
}
|
||||
|
||||
handle_tags_and_duplicates();
|
||||
handle_tags_and_duplicates(&extra_refs);
|
||||
handle_tags_and_duplicates(&tag_refs);
|
||||
handle_deletes();
|
||||
|
||||
if (export_filename && lastimportid != last_idnum)
|
||||
|
|
166
fast-import.c
166
fast-import.c
|
@ -1,157 +1,3 @@
|
|||
/*
|
||||
(See Documentation/git-fast-import.txt for maintained documentation.)
|
||||
Format of STDIN stream:
|
||||
|
||||
stream ::= cmd*;
|
||||
|
||||
cmd ::= new_blob
|
||||
| new_commit
|
||||
| new_tag
|
||||
| reset_branch
|
||||
| checkpoint
|
||||
| progress
|
||||
;
|
||||
|
||||
new_blob ::= 'blob' lf
|
||||
mark?
|
||||
file_content;
|
||||
file_content ::= data;
|
||||
|
||||
new_commit ::= 'commit' sp ref_str lf
|
||||
mark?
|
||||
('author' (sp name)? sp '<' email '>' sp when lf)?
|
||||
'committer' (sp name)? sp '<' email '>' sp when lf
|
||||
commit_msg
|
||||
('from' sp commit-ish lf)?
|
||||
('merge' sp commit-ish lf)*
|
||||
(file_change | ls)*
|
||||
lf?;
|
||||
commit_msg ::= data;
|
||||
|
||||
ls ::= 'ls' sp '"' quoted(path) '"' lf;
|
||||
|
||||
file_change ::= file_clr
|
||||
| file_del
|
||||
| file_rnm
|
||||
| file_cpy
|
||||
| file_obm
|
||||
| file_inm;
|
||||
file_clr ::= 'deleteall' lf;
|
||||
file_del ::= 'D' sp path_str lf;
|
||||
file_rnm ::= 'R' sp path_str sp path_str lf;
|
||||
file_cpy ::= 'C' sp path_str sp path_str lf;
|
||||
file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
|
||||
file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
|
||||
data;
|
||||
note_obm ::= 'N' sp (hexsha1 | idnum) sp commit-ish lf;
|
||||
note_inm ::= 'N' sp 'inline' sp commit-ish lf
|
||||
data;
|
||||
|
||||
new_tag ::= 'tag' sp tag_str lf
|
||||
'from' sp commit-ish lf
|
||||
('tagger' (sp name)? sp '<' email '>' sp when lf)?
|
||||
tag_msg;
|
||||
tag_msg ::= data;
|
||||
|
||||
reset_branch ::= 'reset' sp ref_str lf
|
||||
('from' sp commit-ish lf)?
|
||||
lf?;
|
||||
|
||||
checkpoint ::= 'checkpoint' lf
|
||||
lf?;
|
||||
|
||||
progress ::= 'progress' sp not_lf* lf
|
||||
lf?;
|
||||
|
||||
# note: the first idnum in a stream should be 1 and subsequent
|
||||
# idnums should not have gaps between values as this will cause
|
||||
# the stream parser to reserve space for the gapped values. An
|
||||
# idnum can be updated in the future to a new object by issuing
|
||||
# a new mark directive with the old idnum.
|
||||
#
|
||||
mark ::= 'mark' sp idnum lf;
|
||||
data ::= (delimited_data | exact_data)
|
||||
lf?;
|
||||
|
||||
# note: delim may be any string but must not contain lf.
|
||||
# data_line may contain any data but must not be exactly
|
||||
# delim.
|
||||
delimited_data ::= 'data' sp '<<' delim lf
|
||||
(data_line lf)*
|
||||
delim lf;
|
||||
|
||||
# note: declen indicates the length of binary_data in bytes.
|
||||
# declen does not include the lf preceding the binary data.
|
||||
#
|
||||
exact_data ::= 'data' sp declen lf
|
||||
binary_data;
|
||||
|
||||
# note: quoted strings are C-style quoting supporting \c for
|
||||
# common escapes of 'c' (e..g \n, \t, \\, \") or \nnn where nnn
|
||||
# is the signed byte value in octal. Note that the only
|
||||
# characters which must actually be escaped to protect the
|
||||
# stream formatting is: \, " and LF. Otherwise these values
|
||||
# are UTF8.
|
||||
#
|
||||
commit-ish ::= (ref_str | hexsha1 | sha1exp_str | idnum);
|
||||
ref_str ::= ref;
|
||||
sha1exp_str ::= sha1exp;
|
||||
tag_str ::= tag;
|
||||
path_str ::= path | '"' quoted(path) '"' ;
|
||||
mode ::= '100644' | '644'
|
||||
| '100755' | '755'
|
||||
| '120000'
|
||||
;
|
||||
|
||||
declen ::= # unsigned 32 bit value, ascii base10 notation;
|
||||
bigint ::= # unsigned integer value, ascii base10 notation;
|
||||
binary_data ::= # file content, not interpreted;
|
||||
|
||||
when ::= raw_when | rfc2822_when;
|
||||
raw_when ::= ts sp tz;
|
||||
rfc2822_when ::= # Valid RFC 2822 date and time;
|
||||
|
||||
sp ::= # ASCII space character;
|
||||
lf ::= # ASCII newline (LF) character;
|
||||
|
||||
# note: a colon (':') must precede the numerical value assigned to
|
||||
# an idnum. This is to distinguish it from a ref or tag name as
|
||||
# GIT does not permit ':' in ref or tag strings.
|
||||
#
|
||||
idnum ::= ':' bigint;
|
||||
path ::= # GIT style file path, e.g. "a/b/c";
|
||||
ref ::= # GIT ref name, e.g. "refs/heads/MOZ_GECKO_EXPERIMENT";
|
||||
tag ::= # GIT tag name, e.g. "FIREFOX_1_5";
|
||||
sha1exp ::= # Any valid GIT SHA1 expression;
|
||||
hexsha1 ::= # SHA1 in hexadecimal format;
|
||||
|
||||
# note: name and email are UTF8 strings, however name must not
|
||||
# contain '<' or lf and email must not contain any of the
|
||||
# following: '<', '>', lf.
|
||||
#
|
||||
name ::= # valid GIT author/committer name;
|
||||
email ::= # valid GIT author/committer email;
|
||||
ts ::= # time since the epoch in seconds, ascii base10 notation;
|
||||
tz ::= # GIT style timezone;
|
||||
|
||||
# note: comments, get-mark, ls-tree, and cat-blob requests may
|
||||
# appear anywhere in the input, except within a data command. Any
|
||||
# form of the data command always escapes the related input from
|
||||
# comment processing.
|
||||
#
|
||||
# In case it is not clear, the '#' that starts the comment
|
||||
# must be the first character on that line (an lf
|
||||
# preceded it).
|
||||
#
|
||||
|
||||
get_mark ::= 'get-mark' sp idnum lf;
|
||||
cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
|
||||
ls_tree ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;
|
||||
|
||||
comment ::= '#' not_lf* lf;
|
||||
not_lf ::= # Any byte that is not ASCII newline (LF);
|
||||
*/
|
||||
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "repository.h"
|
||||
|
@ -1968,6 +1814,13 @@ static void parse_mark(void)
|
|||
next_mark = 0;
|
||||
}
|
||||
|
||||
static void parse_original_identifier(void)
|
||||
{
|
||||
const char *v;
|
||||
if (skip_prefix(command_buf.buf, "original-oid ", &v))
|
||||
read_next_command();
|
||||
}
|
||||
|
||||
static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
|
||||
{
|
||||
const char *data;
|
||||
|
@ -2110,6 +1963,7 @@ static void parse_new_blob(void)
|
|||
{
|
||||
read_next_command();
|
||||
parse_mark();
|
||||
parse_original_identifier();
|
||||
parse_and_store_blob(&last_blob, NULL, next_mark);
|
||||
}
|
||||
|
||||
|
@ -2733,6 +2587,7 @@ static void parse_new_commit(const char *arg)
|
|||
|
||||
read_next_command();
|
||||
parse_mark();
|
||||
parse_original_identifier();
|
||||
if (skip_prefix(command_buf.buf, "author ", &v)) {
|
||||
author = parse_ident(v);
|
||||
read_next_command();
|
||||
|
@ -2865,6 +2720,9 @@ static void parse_new_tag(const char *arg)
|
|||
die("Invalid ref name or SHA1 expression: %s", from);
|
||||
read_next_command();
|
||||
|
||||
/* original-oid ... */
|
||||
parse_original_identifier();
|
||||
|
||||
/* tagger ... */
|
||||
if (skip_prefix(command_buf.buf, "tagger ", &v)) {
|
||||
tagger = parse_ident(v);
|
||||
|
|
|
@ -66,6 +66,34 @@ test_expect_success 'fast-export master~2..master' '
|
|||
|
||||
'
|
||||
|
||||
test_expect_success 'fast-export --reference-excluded-parents master~2..master' '
|
||||
|
||||
git fast-export --reference-excluded-parents master~2..master >actual &&
|
||||
grep commit.refs/heads/master actual >commit-count &&
|
||||
test_line_count = 2 commit-count &&
|
||||
sed "s/master/rewrite/" actual |
|
||||
(cd new &&
|
||||
git fast-import &&
|
||||
test $MASTER = $(git rev-parse --verify refs/heads/rewrite))
|
||||
'
|
||||
|
||||
test_expect_success 'fast-export --show-original-ids' '
|
||||
|
||||
git fast-export --show-original-ids master >output &&
|
||||
grep ^original-oid output| sed -e s/^original-oid.// | sort >actual &&
|
||||
git rev-list --objects master muss >objects-and-names &&
|
||||
awk "{print \$1}" objects-and-names | sort >commits-trees-blobs &&
|
||||
comm -23 actual commits-trees-blobs >unfound &&
|
||||
test_must_be_empty unfound
|
||||
'
|
||||
|
||||
test_expect_success 'fast-export --show-original-ids | git fast-import' '
|
||||
|
||||
git fast-export --show-original-ids master muss | git fast-import --quiet &&
|
||||
test $MASTER = $(git rev-parse --verify refs/heads/master) &&
|
||||
test $MUSS = $(git rev-parse --verify refs/tags/muss)
|
||||
'
|
||||
|
||||
test_expect_success 'iso-8859-1' '
|
||||
|
||||
git config i18n.commitencoding ISO8859-1 &&
|
||||
|
@ -325,6 +353,22 @@ test_expect_success 'rewriting tag of filtered out object' '
|
|||
)
|
||||
'
|
||||
|
||||
test_expect_success 'rewrite tag predating pathspecs to nothing' '
|
||||
test_create_repo rewrite_tag_predating_pathspecs &&
|
||||
(
|
||||
cd rewrite_tag_predating_pathspecs &&
|
||||
|
||||
test_commit initial &&
|
||||
|
||||
git tag -a -m "Some old tag" v0.0.0.0.0.0.1 &&
|
||||
|
||||
test_commit bar &&
|
||||
|
||||
git fast-export --tag-of-filtered-object=rewrite --all -- bar.t >output &&
|
||||
grep from.$ZERO_OID output
|
||||
)
|
||||
'
|
||||
|
||||
cat > limit-by-paths/expected << EOF
|
||||
blob
|
||||
mark :1
|
||||
|
@ -366,6 +410,26 @@ test_expect_success 'path limiting with import-marks does not lose unmodified fi
|
|||
grep file0 actual
|
||||
'
|
||||
|
||||
test_expect_success 'avoid corrupt stream with non-existent mark' '
|
||||
test_create_repo avoid_non_existent_mark &&
|
||||
(
|
||||
cd avoid_non_existent_mark &&
|
||||
|
||||
test_commit important-path &&
|
||||
|
||||
test_commit ignored &&
|
||||
|
||||
git branch A &&
|
||||
git branch B &&
|
||||
|
||||
echo foo >>important-path.t &&
|
||||
git add important-path.t &&
|
||||
test_commit more changes &&
|
||||
|
||||
git fast-export --all -- important-path.t | git fast-import --force
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'full-tree re-shows unmodified files' '
|
||||
git checkout -f simple &&
|
||||
git fast-export --full-tree simple >actual &&
|
||||
|
@ -508,10 +572,20 @@ test_expect_success 'use refspec' '
|
|||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'delete refspec' '
|
||||
test_expect_success 'delete ref because entire history excluded' '
|
||||
git branch to-delete &&
|
||||
git fast-export --refspec :refs/heads/to-delete to-delete ^to-delete > actual &&
|
||||
cat > expected <<-EOF &&
|
||||
git fast-export to-delete ^to-delete >actual &&
|
||||
cat >expected <<-EOF &&
|
||||
reset refs/heads/to-delete
|
||||
from 0000000000000000000000000000000000000000
|
||||
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'delete refspec' '
|
||||
git fast-export --refspec :refs/heads/to-delete >actual &&
|
||||
cat >expected <<-EOF &&
|
||||
reset refs/heads/to-delete
|
||||
from 0000000000000000000000000000000000000000
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче