diff options
author | Junio C Hamano <gitster@pobox.com> | 2014-06-16 12:18:15 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2014-06-16 12:18:15 -0700 |
commit | c9fc3a6ac5011ce03e8132973687d1242807dc6c (patch) | |
tree | 9d8a40b578307a5be129f5385ad28fea3d8e8bad | |
parent | Merge branch 'nd/daemonize-gc' (diff) | |
parent | transport-helper: remove unnecessary strbuf resets (diff) | |
download | tgif-c9fc3a6ac5011ce03e8132973687d1242807dc6c.tar.xz |
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
-rw-r--r-- | Documentation/git-fast-export.txt | 4 | ||||
-rw-r--r-- | Documentation/git-fast-import.txt | 3 | ||||
-rw-r--r-- | builtin/fast-export.c | 49 | ||||
-rw-r--r-- | fast-import.c | 13 | ||||
-rwxr-xr-x | t/t5801-remote-helpers.sh | 18 | ||||
-rwxr-xr-x | t/t9300-fast-import.sh | 18 | ||||
-rwxr-xr-x | t/t9350-fast-export.sh | 18 | ||||
-rw-r--r-- | transport-helper.c | 32 |
8 files changed, 141 insertions, 14 deletions
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt index 85f1f30fdf..221506b04b 100644 --- a/Documentation/git-fast-export.txt +++ b/Documentation/git-fast-export.txt @@ -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 diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index 3ffa2fa2c8..377eeaa36d 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -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: ---- diff --git a/builtin/fast-export.c b/builtin/fast-export.c index b8d8a3aaf9..ef4481615f 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -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; } diff --git a/fast-import.c b/fast-import.c index fb4738d373..6707a66471 100644 --- a/fast-import.c +++ b/fast-import.c @@ -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); diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh index a00a660763..2419407546 100755 --- a/t/t5801-remote-helpers.sh +++ b/t/t5801-remote-helpers.sh @@ -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 && diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 27263dfb80..5fc9ef262a 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -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 diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh index 2312dec8f0..66c8b0a371 100755 --- a/t/t9350-fast-export.sh +++ b/t/t9350-fast-export.sh @@ -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 diff --git a/transport-helper.c b/transport-helper.c index 4cfb8f3a4a..84c616f180 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -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)) |