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
maint
Junio C Hamano 2014-06-16 12:18:15 -07:00
commit c9fc3a6ac5
8 changed files with 141 additions and 14 deletions

View File

@ -105,6 +105,10 @@ marks the same across runs.
in the commit (as opposed to just listing the files which are in the commit (as opposed to just listing the files which are
different from the commit's first parent). 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>...]:: [<git-rev-list-args>...]::
A list of arguments, acceptable to 'git rev-parse' and A list of arguments, acceptable to 'git rev-parse' and
'git rev-list', that specifies the specific objects and references 'git rev-list', that specifies the specific objects and references

View File

@ -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 * Any valid Git SHA-1 expression that resolves to a commit. See
``SPECIFYING REVISIONS'' in linkgit:gitrevisions[7] for details. ``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 The special case of restarting an incremental import from the
current branch value should be written as: current branch value should be written as:
---- ----

View File

@ -17,6 +17,7 @@
#include "utf8.h" #include "utf8.h"
#include "parse-options.h" #include "parse-options.h"
#include "quote.h" #include "quote.h"
#include "remote.h"


static const char *fast_export_usage[] = { static const char *fast_export_usage[] = {
N_("git fast-export [rev-list-opts]"), N_("git fast-export [rev-list-opts]"),
@ -31,6 +32,8 @@ static int use_done_feature;
static int no_data; static int no_data;
static int full_tree; static int full_tree;
static struct string_list extra_refs = STRING_LIST_INIT_NODUP; 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, static int parse_opt_signed_tag_mode(const struct option *opt,
const char *arg, int unset) 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) if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
continue; 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); commit = get_commit(e, full_name);
if (!commit) { if (!commit) {
warning("%s: Unexpected object of type %s, skipping.", warning("%s: Unexpected object of type %s, skipping.",
@ -661,6 +673,19 @@ static void import_marks(char *input_file)
fclose(f); 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) int cmd_fast_export(int argc, const char **argv, const char *prefix)
{ {
struct rev_info revs; struct rev_info revs;
@ -668,6 +693,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
struct commit *commit; struct commit *commit;
char *export_filename = NULL, *import_filename = NULL; char *export_filename = NULL, *import_filename = NULL;
uint32_t lastimportid; uint32_t lastimportid;
struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
struct option options[] = { struct option options[] = {
OPT_INTEGER(0, "progress", &progress, OPT_INTEGER(0, "progress", &progress,
N_("show progress after <n> objects")), 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, OPT_BOOL(0, "use-done-feature", &use_done_feature,
N_("Use the done feature to terminate the stream")), N_("Use the done feature to terminate the stream")),
OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")), 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() OPT_END()
}; };


@ -701,11 +729,27 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
revs.topo_order = 1; revs.topo_order = 1;
revs.show_source = 1; revs.show_source = 1;
revs.rewrite_parents = 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 = setup_revisions(argc, argv, &revs, NULL);
argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0);
if (argc > 1) if (argc > 1)
usage_with_options (fast_export_usage, options); 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) if (use_done_feature)
printf("feature done\n"); 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_tags_and_duplicates();
handle_deletes();


if (export_filename && lastimportid != last_idnum) if (export_filename && lastimportid != last_idnum)
export_marks(export_filename); export_marks(export_filename);
@ -740,5 +785,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
if (use_done_feature) if (use_done_feature)
printf("done\n"); printf("done\n");


free_refspec(refspecs_nr, refspecs);

return 0; return 0;
} }

View File

@ -248,6 +248,7 @@ struct branch {
uintmax_t last_commit; uintmax_t last_commit;
uintmax_t num_notes; uintmax_t num_notes;
unsigned active : 1; unsigned active : 1;
unsigned delete : 1;
unsigned pack_id : PACK_ID_BITS; unsigned pack_id : PACK_ID_BITS;
unsigned char sha1[20]; unsigned char sha1[20];
}; };
@ -1681,10 +1682,13 @@ static int update_branch(struct branch *b)
struct ref_lock *lock; struct ref_lock *lock;
unsigned char old_sha1[20]; unsigned char old_sha1[20];


if (is_null_sha1(b->sha1))
return 0;
if (read_ref(b->name, old_sha1)) if (read_ref(b->name, old_sha1))
hashclr(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); lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
if (!lock) if (!lock)
return error("Unable to lock %s", b->name); return error("Unable to lock %s", b->name);
@ -2611,8 +2615,11 @@ static int parse_from(struct branch *b)
free(buf); free(buf);
} else } else
parse_from_existing(b); parse_from_existing(b);
} else if (!get_sha1(from, b->sha1)) } else if (!get_sha1(from, b->sha1)) {
parse_from_existing(b); parse_from_existing(b);
if (is_null_sha1(b->sha1))
b->delete = 1;
}
else else
die("Invalid ref name or SHA1 expression: %s", from); die("Invalid ref name or SHA1 expression: %s", from);



View File

@ -87,13 +87,29 @@ test_expect_success 'push new branch by name' '
compare_refs local HEAD server refs/heads/new-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 && (cd local &&
git push origin new-name:new-refspec git push origin new-name:new-refspec
) && ) &&
compare_refs local HEAD server refs/heads/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' ' test_expect_success 'forced push' '
(cd local && (cd local &&
git checkout -b force-test && git checkout -b force-test &&

View File

@ -2999,4 +2999,22 @@ test_expect_success 'T: ls root tree' '
test_cmp expect actual 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 test_done

View File

@ -504,4 +504,22 @@ test_expect_success 'refs are updated even if no commits need to be exported' '
test_cmp expected actual 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 test_done

View File

@ -850,7 +850,7 @@ static int push_refs_with_export(struct transport *transport,
struct ref *ref; struct ref *ref;
struct child_process *helper, exporter; struct child_process *helper, exporter;
struct helper_data *data = transport->data; 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; struct strbuf buf = STRBUF_INIT;


if (!data->refspecs) if (!data->refspecs)
@ -870,15 +870,10 @@ static int push_refs_with_export(struct transport *transport,


write_constant(helper->in, "export\n"); write_constant(helper->in, "export\n");


strbuf_reset(&buf);

for (ref = remote_refs; ref; ref = ref->next) { for (ref = remote_refs; ref; ref = ref->next) {
char *private; char *private;
unsigned char sha1[20]; 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); private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
if (private && !get_sha1(private, sha1)) { if (private && !get_sha1(private, sha1)) {
strbuf_addf(&buf, "^%s", private); strbuf_addf(&buf, "^%s", private);
@ -888,15 +883,34 @@ static int push_refs_with_export(struct transport *transport,
free(private); free(private);


if (ref->peer_ref) { if (ref->peer_ref) {
if (strcmp(ref->peer_ref->name, ref->name)) if (strcmp(ref->name, ref->peer_ref->name)) {
die("remote-helpers do not support old:new syntax"); if (!ref->deletion) {
string_list_append(&revlist_args, ref->peer_ref->name); 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)) if (get_exporter(transport, &exporter, &revlist_args))
die("Couldn't run fast-export"); die("Couldn't run fast-export");


string_list_clear(&revlist_args, 1);

if (finish_command(&exporter)) if (finish_command(&exporter))
die("Error while running fast-export"); die("Error while running fast-export");
if (push_update_refs_status(data, remote_refs, flags)) if (push_update_refs_status(data, remote_refs, flags))