Merge branch 'fc/remote-helper-refmap'
Allow remote-helper/fast-import based transport to rename the refs while transferring the history. * fc/remote-helper-refmap: transport-helper: remove unnecessary strbuf resets transport-helper: add support to delete branches fast-export: add support to delete refs fast-import: add support to delete refs transport-helper: add support to push symbolic refs transport-helper: add support for old:new refspec fast-export: add new --refspec option fast-export: improve argument parsing
This commit is contained in:
commit
c9fc3a6ac5
@ -105,6 +105,10 @@ marks the same across runs.
|
||||
in the commit (as opposed to just listing the files which are
|
||||
different from the commit's first parent).
|
||||
|
||||
--refspec::
|
||||
Apply the specified refspec to each ref exported. Multiple of them can
|
||||
be specified.
|
||||
|
||||
[<git-rev-list-args>...]::
|
||||
A list of arguments, acceptable to 'git rev-parse' and
|
||||
'git rev-list', that specifies the specific objects and references
|
||||
|
@ -483,6 +483,9 @@ Marks must be declared (via `mark`) before they can be used.
|
||||
* Any valid Git SHA-1 expression that resolves to a commit. See
|
||||
``SPECIFYING REVISIONS'' in linkgit:gitrevisions[7] for details.
|
||||
|
||||
* The special null SHA-1 (40 zeros) specifies that the branch is to be
|
||||
removed.
|
||||
|
||||
The special case of restarting an incremental import from the
|
||||
current branch value should be written as:
|
||||
----
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "utf8.h"
|
||||
#include "parse-options.h"
|
||||
#include "quote.h"
|
||||
#include "remote.h"
|
||||
|
||||
static const char *fast_export_usage[] = {
|
||||
N_("git fast-export [rev-list-opts]"),
|
||||
@ -31,6 +32,8 @@ static int use_done_feature;
|
||||
static int no_data;
|
||||
static int full_tree;
|
||||
static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
|
||||
static struct refspec *refspecs;
|
||||
static int refspecs_nr;
|
||||
|
||||
static int parse_opt_signed_tag_mode(const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
@ -525,6 +528,15 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
|
||||
if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
|
||||
continue;
|
||||
|
||||
if (refspecs) {
|
||||
char *private;
|
||||
private = apply_refspecs(refspecs, refspecs_nr, full_name);
|
||||
if (private) {
|
||||
free(full_name);
|
||||
full_name = private;
|
||||
}
|
||||
}
|
||||
|
||||
commit = get_commit(e, full_name);
|
||||
if (!commit) {
|
||||
warning("%s: Unexpected object of type %s, skipping.",
|
||||
@ -661,6 +673,19 @@ static void import_marks(char *input_file)
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void handle_deletes(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < refspecs_nr; i++) {
|
||||
struct refspec *refspec = &refspecs[i];
|
||||
if (*refspec->src)
|
||||
continue;
|
||||
|
||||
printf("reset %s\nfrom %s\n\n",
|
||||
refspec->dst, sha1_to_hex(null_sha1));
|
||||
}
|
||||
}
|
||||
|
||||
int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct rev_info revs;
|
||||
@ -668,6 +693,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||
struct commit *commit;
|
||||
char *export_filename = NULL, *import_filename = NULL;
|
||||
uint32_t lastimportid;
|
||||
struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
|
||||
struct option options[] = {
|
||||
OPT_INTEGER(0, "progress", &progress,
|
||||
N_("show progress after <n> objects")),
|
||||
@ -688,6 +714,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||
OPT_BOOL(0, "use-done-feature", &use_done_feature,
|
||||
N_("Use the done feature to terminate the stream")),
|
||||
OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")),
|
||||
OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"),
|
||||
N_("Apply refspec to exported refs")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@ -701,11 +729,27 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||
revs.topo_order = 1;
|
||||
revs.show_source = 1;
|
||||
revs.rewrite_parents = 1;
|
||||
argc = parse_options(argc, argv, prefix, options, fast_export_usage,
|
||||
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
|
||||
argc = setup_revisions(argc, argv, &revs, NULL);
|
||||
argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0);
|
||||
if (argc > 1)
|
||||
usage_with_options (fast_export_usage, options);
|
||||
|
||||
if (refspecs_list.nr) {
|
||||
const char **refspecs_str;
|
||||
int i;
|
||||
|
||||
refspecs_str = xmalloc(sizeof(*refspecs_str) * refspecs_list.nr);
|
||||
for (i = 0; i < refspecs_list.nr; i++)
|
||||
refspecs_str[i] = refspecs_list.items[i].string;
|
||||
|
||||
refspecs_nr = refspecs_list.nr;
|
||||
refspecs = parse_fetch_refspec(refspecs_nr, refspecs_str);
|
||||
|
||||
string_list_clear(&refspecs_list, 1);
|
||||
free(refspecs_str);
|
||||
}
|
||||
|
||||
if (use_done_feature)
|
||||
printf("feature done\n");
|
||||
|
||||
@ -733,6 +777,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
handle_tags_and_duplicates();
|
||||
handle_deletes();
|
||||
|
||||
if (export_filename && lastimportid != last_idnum)
|
||||
export_marks(export_filename);
|
||||
@ -740,5 +785,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||
if (use_done_feature)
|
||||
printf("done\n");
|
||||
|
||||
free_refspec(refspecs_nr, refspecs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -248,6 +248,7 @@ struct branch {
|
||||
uintmax_t last_commit;
|
||||
uintmax_t num_notes;
|
||||
unsigned active : 1;
|
||||
unsigned delete : 1;
|
||||
unsigned pack_id : PACK_ID_BITS;
|
||||
unsigned char sha1[20];
|
||||
};
|
||||
@ -1681,10 +1682,13 @@ static int update_branch(struct branch *b)
|
||||
struct ref_lock *lock;
|
||||
unsigned char old_sha1[20];
|
||||
|
||||
if (is_null_sha1(b->sha1))
|
||||
return 0;
|
||||
if (read_ref(b->name, old_sha1))
|
||||
hashclr(old_sha1);
|
||||
if (is_null_sha1(b->sha1)) {
|
||||
if (b->delete)
|
||||
delete_ref(b->name, old_sha1, 0);
|
||||
return 0;
|
||||
}
|
||||
lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
|
||||
if (!lock)
|
||||
return error("Unable to lock %s", b->name);
|
||||
@ -2611,8 +2615,11 @@ static int parse_from(struct branch *b)
|
||||
free(buf);
|
||||
} else
|
||||
parse_from_existing(b);
|
||||
} else if (!get_sha1(from, b->sha1))
|
||||
} else if (!get_sha1(from, b->sha1)) {
|
||||
parse_from_existing(b);
|
||||
if (is_null_sha1(b->sha1))
|
||||
b->delete = 1;
|
||||
}
|
||||
else
|
||||
die("Invalid ref name or SHA1 expression: %s", from);
|
||||
|
||||
|
@ -87,13 +87,29 @@ test_expect_success 'push new branch by name' '
|
||||
compare_refs local HEAD server refs/heads/new-name
|
||||
'
|
||||
|
||||
test_expect_failure 'push new branch with old:new refspec' '
|
||||
test_expect_success 'push new branch with old:new refspec' '
|
||||
(cd local &&
|
||||
git push origin new-name:new-refspec
|
||||
) &&
|
||||
compare_refs local HEAD server refs/heads/new-refspec
|
||||
'
|
||||
|
||||
test_expect_success 'push new branch with HEAD:new refspec' '
|
||||
(cd local &&
|
||||
git checkout new-name
|
||||
git push origin HEAD:new-refspec-2
|
||||
) &&
|
||||
compare_refs local HEAD server refs/heads/new-refspec-2
|
||||
'
|
||||
|
||||
test_expect_success 'push delete branch' '
|
||||
(cd local &&
|
||||
git push origin :new-name
|
||||
) &&
|
||||
test_must_fail git --git-dir="server/.git" \
|
||||
rev-parse --verify refs/heads/new-name
|
||||
'
|
||||
|
||||
test_expect_success 'forced push' '
|
||||
(cd local &&
|
||||
git checkout -b force-test &&
|
||||
|
@ -2999,4 +2999,22 @@ test_expect_success 'T: ls root tree' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'T: delete branch' '
|
||||
git branch to-delete &&
|
||||
git fast-import <<-EOF &&
|
||||
reset refs/heads/to-delete
|
||||
from 0000000000000000000000000000000000000000
|
||||
EOF
|
||||
test_must_fail git rev-parse --verify refs/heads/to-delete
|
||||
'
|
||||
|
||||
test_expect_success 'T: empty reset doesnt delete branch' '
|
||||
git branch not-to-delete &&
|
||||
git fast-import <<-EOF &&
|
||||
reset refs/heads/not-to-delete
|
||||
EOF
|
||||
git show-ref &&
|
||||
git rev-parse --verify refs/heads/not-to-delete
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -504,4 +504,22 @@ test_expect_success 'refs are updated even if no commits need to be exported' '
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'use refspec' '
|
||||
git fast-export --refspec refs/heads/master:refs/heads/foobar master | \
|
||||
grep "^commit " | sort | uniq > actual &&
|
||||
echo "commit refs/heads/foobar" > expected &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'delete refspec' '
|
||||
git branch to-delete &&
|
||||
git fast-export --refspec :refs/heads/to-delete to-delete ^to-delete > actual &&
|
||||
cat > expected <<-EOF &&
|
||||
reset refs/heads/to-delete
|
||||
from 0000000000000000000000000000000000000000
|
||||
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -850,7 +850,7 @@ static int push_refs_with_export(struct transport *transport,
|
||||
struct ref *ref;
|
||||
struct child_process *helper, exporter;
|
||||
struct helper_data *data = transport->data;
|
||||
struct string_list revlist_args = STRING_LIST_INIT_NODUP;
|
||||
struct string_list revlist_args = STRING_LIST_INIT_DUP;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
if (!data->refspecs)
|
||||
@ -870,15 +870,10 @@ static int push_refs_with_export(struct transport *transport,
|
||||
|
||||
write_constant(helper->in, "export\n");
|
||||
|
||||
strbuf_reset(&buf);
|
||||
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
char *private;
|
||||
unsigned char sha1[20];
|
||||
|
||||
if (ref->deletion)
|
||||
die("remote-helpers do not support ref deletion");
|
||||
|
||||
private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
|
||||
if (private && !get_sha1(private, sha1)) {
|
||||
strbuf_addf(&buf, "^%s", private);
|
||||
@ -888,15 +883,34 @@ static int push_refs_with_export(struct transport *transport,
|
||||
free(private);
|
||||
|
||||
if (ref->peer_ref) {
|
||||
if (strcmp(ref->peer_ref->name, ref->name))
|
||||
die("remote-helpers do not support old:new syntax");
|
||||
string_list_append(&revlist_args, ref->peer_ref->name);
|
||||
if (strcmp(ref->name, ref->peer_ref->name)) {
|
||||
if (!ref->deletion) {
|
||||
const char *name;
|
||||
int flag;
|
||||
|
||||
/* Follow symbolic refs (mainly for HEAD). */
|
||||
name = resolve_ref_unsafe(ref->peer_ref->name, sha1, 1, &flag);
|
||||
if (!name || !(flag & REF_ISSYMREF))
|
||||
name = ref->peer_ref->name;
|
||||
|
||||
strbuf_addf(&buf, "%s:%s", name, ref->name);
|
||||
} else
|
||||
strbuf_addf(&buf, ":%s", ref->name);
|
||||
|
||||
string_list_append(&revlist_args, "--refspec");
|
||||
string_list_append(&revlist_args, buf.buf);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
if (!ref->deletion)
|
||||
string_list_append(&revlist_args, ref->peer_ref->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (get_exporter(transport, &exporter, &revlist_args))
|
||||
die("Couldn't run fast-export");
|
||||
|
||||
string_list_clear(&revlist_args, 1);
|
||||
|
||||
if (finish_command(&exporter))
|
||||
die("Error while running fast-export");
|
||||
if (push_update_refs_status(data, remote_refs, flags))
|
||||
|
Loading…
Reference in New Issue
Block a user