From 0149c6cb83f0f88954d8e1beb7531d0705f5a2c4 Mon Sep 17 00:00:00 2001 From: Frantisek Hrbata Date: Wed, 18 May 2022 17:39:54 +0200 Subject: [PATCH] initial update command Signed-off-by: Frantisek Hrbata --- Makefile | 4 +- src/common.c | 27 +++- src/common.h | 5 + src/create.c | 19 +-- src/gimrr.c | 7 +- src/gimrr.h | 3 + src/gimrr.mak | 3 +- src/upload.c | 414 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 465 insertions(+), 17 deletions(-) create mode 100644 src/upload.c diff --git a/Makefile b/Makefile index 08321a5..cf1650d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ all: gimrr$(X) +CFLAGS = -g -O0 -Wall + GIT_DIR = git GIT_URL = https://github.com/git/git.git GIT_LNK = $(GIT_DIR)/$(GIMRR_GIT_DIR) @@ -25,7 +27,7 @@ $(GIT_LNK): $(GIT_DIR)/Makefile gimrr$(X): FORCE | $(GIT_LNK) - $(MAKE) -C $(GIT_DIR) $(GIMRR_GIT_DIR)/$@ + $(MAKE) -C $(GIT_DIR) $(GIMRR_GIT_DIR)/$@ CFLAGS="$(CFLAGS)" clean: | $(GIT_LNK) $(RM) $(GIMRR_SRC_DIR)/gimrr$(X) cscope.out diff --git a/src/common.c b/src/common.c index 941302d..21ddd7d 100644 --- a/src/common.c +++ b/src/common.c @@ -139,4 +139,29 @@ const char *lookup_mrid_by_name(const char *str) return id; } -#undef BASE58_SIZE +const char *get_remote_user(const char *remote) { + struct strbuf key = STRBUF_INIT; + char *user; + + strbuf_addf(&key, "remote.%s.mruser", remote); + if (git_config_get_string(key.buf, &user) && + git_config_get_string("user.mruser", &user)) + user = NULL; + strbuf_release(&key); + return user; +} + +const char *find_nth_component(const char *s, int nth, int *len) +{ + while (*s && nth) + if (*s++ == '/') + --nth; + if (len && *s) { + const char *end; + for (end = s; *end && *end != '/'; end++) + ; + *len = end - s; + } + + return nth ? NULL : s; +} diff --git a/src/common.h b/src/common.h index 578f395..5d7e6b3 100644 --- a/src/common.h +++ b/src/common.h @@ -4,5 +4,10 @@ #include "gimrr.h" const char *lookup_mrid_by_name(const char *name); +const char *get_remote_user(const char *remote); +const char *find_nth_component(const char *s, int nth, int *len); + +#define local_ref_suffix(s) find_nth_component((s), 4, NULL) +#define remote_ref_suffix(s) find_nth_component((s), 2, NULL) #endif diff --git a/src/create.c b/src/create.c index 56f851b..8d20710 100644 --- a/src/create.c +++ b/src/create.c @@ -1,5 +1,10 @@ #include "gimrr.h" +static const char * const create_usage[] = { + N_("gimrr create []"), + NULL +}; + struct create_ctx { struct commit_list *commits; struct commit_list *deps; @@ -15,11 +20,6 @@ struct create_ctx { struct strbuf msg; }; -static const char * const create_usage[] = { - N_("git mr create []"), - NULL -}; - /* * Base58 output buffer size requires to be approximately 1.4 greater. * log58(256^x) / log256(256^x) = 1.36 @@ -499,7 +499,6 @@ static void init_create_ctx(int argc, const char **argv, const char *prefix, struct create_ctx *ctx) { const char *remote = NULL, *start = NULL, *end = NULL, *branch = NULL; - struct strbuf buf = STRBUF_INIT; struct option options[] = { OPT_STRING('r', "remote", &remote, N_("remote"), N_("remote")), OPT_STRING('s', "start", &start, N_("start"), N_("start")), @@ -513,6 +512,7 @@ static void init_create_ctx(int argc, const char **argv, const char *prefix, OPT_END() }; + argc = parse_options(argc, argv, prefix, options, create_usage, PARSE_OPT_STOP_AT_NON_OPTION); @@ -546,14 +546,9 @@ static void init_create_ctx(int argc, const char **argv, const char *prefix, usage_with_options(create_usage, options); } - strbuf_addf(&buf, "remote.%s.mruser", ctx->remote); - - if (git_config_get_string(buf.buf, (char **)&ctx->user) && - git_config_get_string("user.mruser", (char **)&ctx->user)) + if (!(ctx->user = get_remote_user(ctx->remote))) die(_("no mr user defined")); - strbuf_release(&buf); - init_mr_commits(ctx); } diff --git a/src/gimrr.c b/src/gimrr.c index 1e6fc15..6aef9bb 100644 --- a/src/gimrr.c +++ b/src/gimrr.c @@ -2,8 +2,9 @@ #include "gimrr.h" static const char * const gimrr_usage[] = { - N_("gimrr mr list"), - N_("gimrr mr create [ []]"), + N_("gimrr list"), + N_("gimrr create [ []]"), + N_("gimrr upload [...]"), NULL }; @@ -23,6 +24,8 @@ int cmd_main(int argc, const char *argv[]) return cmd_list(argc, argv, prefix); else if (!strcmp(argv[0], "create")) return cmd_create(argc, argv, prefix); + else if (!strcmp(argv[0], "upload")) + return cmd_upload(argc, argv, prefix); else error(_("unknown subcommand: %s"), argv[0]); diff --git a/src/gimrr.h b/src/gimrr.h index 082d09b..ae88d99 100644 --- a/src/gimrr.h +++ b/src/gimrr.h @@ -11,8 +11,11 @@ #include "parse-options.h" #include "common.h" #include "strvec.h" +#include "transport.h" +#include "transport-internal.h" int cmd_create(int argc, const char **argv, const char *prefix); int cmd_list(int argc, const char **argv, const char *prefix); +int cmd_push(int argc, const char **argv, const char *prefix); #endif diff --git a/src/gimrr.mak b/src/gimrr.mak index 3805ca1..7252e0f 100644 --- a/src/gimrr.mak +++ b/src/gimrr.mak @@ -1,8 +1,9 @@ GIMRR_GIT_DIR = contrib/gimrr GIMRR_OBJECTS += $(GIMRR_GIT_DIR)/gimrr.o +GIMRR_OBJECTS += $(GIMRR_GIT_DIR)/common.o GIMRR_OBJECTS += $(GIMRR_GIT_DIR)/create.o GIMRR_OBJECTS += $(GIMRR_GIT_DIR)/list.o -GIMRR_OBJECTS += $(GIMRR_GIT_DIR)/common.o +GIMRR_OBJECTS += $(GIMRR_GIT_DIR)/upload.o OBJECTS += $(GIMRR_OBJECTS) $(GIMRR_GIT_DIR)/gimrr$X: $(GIMRR_OBJECTS) GIT-LDFLAGS $(GITLIBS) diff --git a/src/upload.c b/src/upload.c new file mode 100644 index 0000000..ec2b339 --- /dev/null +++ b/src/upload.c @@ -0,0 +1,414 @@ +#include "gimrr.h" + +#define HAS_LOCAL_REFS 1 + +#define UPLOAD_HEAD (1<<1) +#define UPLOAD_TAGS (1<<2) +#define UPLOAD_MBOX (1<<3) +#define UPLOAD_ALL (UPLOAD_HEAD | UPLOAD_TAGS | UPLOAD_MBOX) + +static const char * const upload_usage[] = { + N_("gimrr upload [...]"), + NULL +}; + +/* local mr refs for one remote */ +struct local_refs { + struct remote *remote; + struct ref *ref; +}; + +static struct string_list mrids = STRING_LIST_INIT_DUP; +static struct local_refs *local_refs; +static size_t local_refs_nr; +static size_t local_refs_alloc; +static int upload_flags; +static int verbosity; +static int verbose; +static int quiet; +static int progress = -1; + +static int get_mr_refs(const char *refname, const struct object_id *oid, + int flag, void *cb_data) +{ + struct ref ***ref_tail = cb_data; + struct ref *ref; + const char *last; + int type = 0; + + last = strrchr(refname, '/'); + last++; + if (!strcmp("head", last)) + type = UPLOAD_HEAD; + else if (!strcmp("tags", last)) + type = UPLOAD_TAGS; + else if (!strcmp("mbox", last)) + type = UPLOAD_MBOX; + + if (!type) { + warning(_("unknown mr reference '%s'"), refname); + return 0; + } + + if (!(type & upload_flags)) + return 0; + + ref = alloc_ref(refname); + oidcpy(&ref->new_oid, oid); + **ref_tail = ref; + *ref_tail = &ref->next; + + return 0; +} + +static int get_local_refs(struct remote *remote, void *cb_data) +{ + struct string_list_item *item; + struct strbuf ref_prefix = STRBUF_INIT; + struct local_refs *local_ref; + struct ref **ref_tail; + + if (!remote_is_configured(remote, 1)) + return 0; + + ALLOC_GROW(local_refs, local_refs_nr + 1, local_refs_alloc); + local_ref = &local_refs[local_refs_nr++]; + local_ref->remote = remote; + local_ref->ref = NULL; + ref_tail = &local_ref->ref; + + for_each_string_list_item(item, &mrids) { + struct ref **ref_tail_orig = ref_tail; + strbuf_reset(&ref_prefix); + strbuf_addf(&ref_prefix, "refs/mrs/local/%s/%s/", + remote->name, item->string); + for_each_fullref_in(ref_prefix.buf, get_mr_refs, &ref_tail); + /* no new refs added for remote */ + if (ref_tail_orig == ref_tail) + continue; + if (item->util) + warning(_("mr '%s' in different remotes"), item->string); + item->util = (void *)HAS_LOCAL_REFS; + } + + strbuf_release(&ref_prefix); + + return 0; +} + +static void set_remote_prefixes(struct ref *local_refs, struct strvec *names) +{ + struct ref *ref = local_refs; + + while (ref) { + strvec_pushf(names, "refs/mrs/%s", local_ref_suffix(ref->name)); + ref = ref->next; + } +} + +static struct ref *get_remote_peer(struct ref *src, const char *local_suffix, + struct ref *dst) +{ + const char *remote_suffix; + + while (dst) { + remote_suffix = remote_ref_suffix(dst->name); + if (!strcmp(local_suffix, remote_suffix)) + return dst; + dst = dst->next; + } + + return NULL; +} + +static void set_remote_peer(struct ref *src, struct ref *dst, + struct ref ***dst_tail) +{ + struct strbuf dst_refname = STRBUF_INIT; + const char *local_suffix = local_ref_suffix(src->name); + struct ref *peer = get_remote_peer(src, local_suffix, dst); + + if (peer) { + peer->peer_ref = copy_ref(src); + return; + } + + strbuf_addf(&dst_refname, "refs/mrs/%s", local_suffix); + peer = alloc_ref(dst_refname.buf); + strbuf_release(&dst_refname); + **dst_tail = peer; + *dst_tail = &peer->next; + peer->peer_ref = copy_ref(src); +} + +static void set_remote_ref_status(struct ref *ref) +{ + if (!ref->peer_ref) + return; + + oidcpy(&ref->new_oid, &ref->peer_ref->new_oid); + + if (is_null_oid(&ref->old_oid)) + return; + + /* + if (ends_with(ref->name, "head")) + ref->status = REF_STATUS_REJECT_ALREADY_EXISTS; + */ + if (!has_object_file(&ref->old_oid)) + ref->status = REF_STATUS_REJECT_FETCH_FIRST; + else if (!lookup_commit_reference_gently(the_repository, &ref->old_oid, 1)) + ref->status = REF_STATUS_REJECT_NEEDS_FORCE; + else if (!lookup_commit_reference_gently(the_repository, &ref->new_oid, 1)) + ref->status = REF_STATUS_REJECT_NEEDS_FORCE; + else if (!ref_newer(&ref->new_oid, &ref->old_oid)) + ref->status = REF_STATUS_REJECT_NONFASTFORWARD; + else if (oideq(&ref->old_oid, &ref->new_oid)) + ref->status = REF_STATUS_UPTODATE; +} +static void set_remote_peers(struct ref *dst, struct ref *src) +{ + struct ref **dst_tail = &dst; + struct ref *ref; + + for (ref = dst; ref; ref = ref->next) + dst_tail = &ref->next; + + for (ref = src; ref; ref = ref->next) + set_remote_peer(ref, dst, &dst_tail); + + for (ref = dst; ref; ref = ref->next) + set_remote_ref_status(ref); +} + +static const char *get_ref_status_msg(struct ref *r) +{ + switch(r->status) { + case REF_STATUS_NONE: + return "[no match]"; + case REF_STATUS_REJECT_NODELETE: + return "remote does not support deleting refs"; + case REF_STATUS_UPTODATE: + return "up to date"; + case REF_STATUS_REJECT_NONFASTFORWARD: + return "non-fast-forward"; + case REF_STATUS_REJECT_ALREADY_EXISTS: + return "already exists"; + case REF_STATUS_REJECT_FETCH_FIRST: + return "fetch first"; + case REF_STATUS_REJECT_NEEDS_FORCE: + return "needs force"; + case REF_STATUS_REJECT_STALE: + return "stale info"; + case REF_STATUS_REJECT_REMOTE_UPDATED: + return "remote ref updated since checkout"; + case REF_STATUS_REJECT_SHALLOW: + return "new shallow roots not allowed"; + case REF_STATUS_REMOTE_REJECT: + return "remote rejected"; + case REF_STATUS_EXPECTING_REPORT: + return "remote failed to report status"; + case REF_STATUS_ATOMIC_PUSH_FAILED: + return "atomic push failed"; + case REF_STATUS_OK: + return "ok"; + } + + return "unknown"; +} + +static void print_ref_status(struct ref *r, const char *flag, const char *msg, + const char *err_msg) +{ + int type_len, id_len, ns_len; + const char *type, *id, *ns; + + type = find_nth_component(r->name, 4, &type_len); + id = find_nth_component(r->name, 2, &id_len); + ns = find_nth_component(r->name, 1, &ns_len); + ns--; + + if (err_msg) + fprintf(stderr, " %s [%s %.*s %.*s] %.*s (%s)\n", flag, msg, ns_len, + ns, type_len, type, id_len, id, err_msg); + else + fprintf(stderr, " %s [%s %.*s %.*s] %.*s\n", flag, msg, ns_len, + ns, type_len, type, id_len, id); +} +static void print_upload_status(struct ref *r, int err) +{ + int s; + + if (!err && quiet) + return; + + for (; r; r = r->next) { + s = r->status; + + if (s == REF_STATUS_UPTODATE) { + if (verbose) + print_ref_status(r, "=", "up to date", NULL); + + } else if (s == REF_STATUS_OK) { + if (is_null_oid(&r->old_oid)) + print_ref_status(r, "*", "new", NULL); + else + print_ref_status(r, ">", "updated", NULL); + + } else if (s != REF_STATUS_NONE) + print_ref_status(r, "!", "rejected", get_ref_status_msg(r)); + } +} + +static void update_tracking_refs(struct ref *refs) +{ + struct ref *r; + int type_len, remote_len, id_len, ns_len; + const char *type, *remote, *id, *ns; + struct strbuf buf = STRBUF_INIT; + + for (r = refs; r; r = r->next) { + if (r->status != REF_STATUS_OK && + r->status != REF_STATUS_UPTODATE) + continue; + + type = find_nth_component(r->name, 4, &type_len); + remote = find_nth_component(r->peer_ref->name, 3, &remote_len); + id = find_nth_component(r->name, 2, &id_len); + ns = find_nth_component(r->name, 1, &ns_len); + ns_len--; + + if (verbose) { + fprintf(stderr, "updating remote tracking ref for " + "%.*s %.*s '%.*s'\n", + ns_len, ns, type_len, type, id_len, + id); + } + strbuf_reset(&buf); + strbuf_addf(&buf, "refs/mrs/remote/%s", remote); + update_ref("update by gimrr", buf.buf, &r->new_oid, NULL, 0, 0); + + if (verbose) + fprintf(stderr, "deleting local ref for " + "%.*s %.*s '%.*s'\n", + ns_len, ns, type_len, type, id_len, + id); + delete_ref(NULL, r->peer_ref->name, NULL, 0); + } + + strbuf_release(&buf); +} + +static int upload_local_refs_url(struct remote *remote, const char *url, + struct ref *local_refs) +{ + struct transport *t = transport_get(remote, url); + struct transport_ls_refs_options opts = TRANSPORT_LS_REFS_OPTIONS_INIT; + char *anon_url = transport_anonymize_url(url); + struct ref *remote_refs; + int err = 0; + + if (verbose > 0) + fprintf(stderr, _("uploading to remote '%s' with url '%s'\n"), + remote->name, anon_url); + + if (!t->vtable->push_refs) + goto err; + + transport_set_verbosity(t, verbosity, progress); + set_remote_prefixes(local_refs, &opts.ref_prefixes); + remote_refs = t->vtable->get_refs_list(t, 1, &opts); + transport_ls_refs_options_release(&opts); + set_remote_peers(remote_refs, local_refs); + err = t->vtable->push_refs(t, remote_refs, 0); + print_upload_status(remote_refs, err); + update_tracking_refs(remote_refs); + err += transport_disconnect(t); + if (err) + goto err; + + if (!quiet) + fprintf(stderr, "everything already uploaded for " + "remote '%s' with url '%s'\n", + remote->name, anon_url); + + free(anon_url); + return 0; +err: + error(_("failed to upload some refs to remote '%s' " + "with url '%s'"), remote->name, anon_url); + free(anon_url); + return 1; +} + +static int upload_local_refs(struct remote *remote, struct ref *local_refs) +{ + const char **url = remote->pushurl_nr ? remote->pushurl : remote->url; + int url_nr = remote->pushurl_nr ? remote->pushurl_nr : remote->url_nr; + int i, rv = 0; + + for (i = 0; i < url_nr; ++i) + rv += upload_local_refs_url(remote, url[i], local_refs); + + return !!rv; +} + +static int for_each_cmdl_remote(struct string_list *remotes, each_remote_fn fn, + void *priv) +{ + struct string_list_item *item; + struct remote *remote; + int result; + + for_each_string_list_item(item, remotes) { + remote = remote_get(item->string); + if (!remote_is_configured(remote, 1)) + die(_("no such remote: '%s'"), item->string); + if ((result = fn(remote, priv))) + break; + } + return result; +} + +int cmd_upload(int argc, const char **argv, const char *prefix) +{ + struct string_list remotes = STRING_LIST_INIT_NODUP; + int i, rv = 0; + + struct option options[] = { + OPT__VERBOSITY(&verbosity), + OPT_BIT('h' , "head", &upload_flags, N_("upload only heads"), UPLOAD_HEAD), + OPT_BIT('t' , "tags", &upload_flags, N_("upload only tags"), UPLOAD_TAGS), + OPT_BIT('m' , "mbox", &upload_flags, N_("upload only mboxes"), UPLOAD_MBOX), + OPT_STRING_LIST('r', "remote", &remotes, N_("remote"), N_("upload only to ")), + OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, upload_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + verbose = (verbosity > 0); + quiet = (verbosity < 0); + + if (!upload_flags) + upload_flags = UPLOAD_ALL; + + for (i = 0; i < argc; ++i) { + const char *mrid = lookup_mrid_by_name(argv[i]); + if (!mrid) + die(_("no mr found for '%s'"), argv[i]); + string_list_insert(&mrids, mrid); + } + + if (remotes.nr) + for_each_cmdl_remote(&remotes, get_local_refs, NULL); + else + for_each_remote(get_local_refs, NULL); + + for (i = 0; i < local_refs_nr; ++i) { + rv += upload_local_refs(local_refs[i].remote, local_refs[i].ref); + } + + return rv; +} -- 1.7.10.4