From: Junio C Hamano Date: Fri, 4 Jan 2019 21:33:33 +0000 (-0800) Subject: Merge branch 'ab/push-dwim-dst' X-Git-Tag: v2.21.0-rc0~129 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/0a84724bf8ed1240b61e2401aec8a6cab93111b1?ds=inline;hp=-c Merge branch 'ab/push-dwim-dst' "git push $there $src:$dst" rejects when $dst is not a fully qualified refname and not clear what the end user meant. The codepath has been taught to give a clearer error message, and also guess where the push should go by taking the type of the pushed object into account (e.g. a tag object would want to go under refs/tags/). * ab/push-dwim-dst: push doc: document the DWYM behavior pushing to unqualified push: test that doesn't DWYM if is unqualified push: add an advice on unqualified push push: move unqualified refname error into a function push: improve the error shown on unqualified push i18n: remote.c: mark error(...) messages for translation remote.c: add braces in anticipation of a follow-up change --- 0a84724bf8ed1240b61e2401aec8a6cab93111b1 diff --combined remote.c index 70aba02c74,f7477b8eb6..670dd44813 --- a/remote.c +++ b/remote.c @@@ -13,6 -13,7 +13,7 @@@ #include "mergesort.h" #include "argv-array.h" #include "commit-reach.h" + #include "advice.h" enum map_direction { FROM_SRC, FROM_DST }; @@@ -359,7 -360,7 +360,7 @@@ static int handle_config(const char *ke return 0; /* Handle remote..* variables */ if (*name == '/') { - warning("Config remote shorthand cannot begin with '/': %s", + warning(_("config remote shorthand cannot begin with '/': %s"), name); return 0; } @@@ -620,7 -621,7 +621,7 @@@ static void handle_duplicate(struct re * FETCH_HEAD_IGNORE entries always appear at * the end of the list. */ - die(_("Internal error")); + BUG("Internal error"); } } free(ref2->peer_ref); @@@ -680,7 -681,7 +681,7 @@@ static int match_name_with_pattern(cons size_t namelen; int ret; if (!kstar) - die("Key '%s' of pattern had no '*'", key); + die(_("key '%s' of pattern had no '*'"), key); klen = kstar - key; ksuffixlen = strlen(kstar + 1); namelen = strlen(name); @@@ -690,7 -691,7 +691,7 @@@ struct strbuf sb = STRBUF_INIT; const char *vstar = strchr(value, '*'); if (!vstar) - die("Value '%s' of pattern has no '*'", value); + die(_("value '%s' of pattern has no '*'"), value); strbuf_add(&sb, value, vstar - value); strbuf_add(&sb, name + klen, namelen - klen - ksuffixlen); strbuf_addstr(&sb, vstar + 1); @@@ -707,7 -708,7 +708,7 @@@ static void query_refspecs_multiple(str int find_src = !query->src; if (find_src && !query->dst) - error(_("query_refspecs_multiple: need either src or dst")); + BUG("query_refspecs_multiple: need either src or dst"); for (i = 0; i < rs->nr; i++) { struct refspec_item *refspec = &rs->items[i]; @@@ -735,7 -736,7 +736,7 @@@ int query_refspecs(struct refspec *rs, char **result = find_src ? &query->src : &query->dst; if (find_src && !query->dst) - return error(_("query_refspecs: need either src or dst")); + BUG("query_refspecs: need either src or dst"); for (i = 0; i < rs->nr; i++) { struct refspec_item *refspec = &rs->items[i]; @@@ -968,12 -969,13 +969,13 @@@ static char *guess_ref(const char *name if (!r) return NULL; - if (starts_with(r, "refs/heads/")) + if (starts_with(r, "refs/heads/")) { strbuf_addstr(&buf, "refs/heads/"); - else if (starts_with(r, "refs/tags/")) + } else if (starts_with(r, "refs/tags/")) { strbuf_addstr(&buf, "refs/tags/"); - else + } else { return NULL; + } strbuf_addstr(&buf, name); return strbuf_detach(&buf, NULL); @@@ -995,15 -997,71 +997,71 @@@ static int match_explicit_lhs(struct re * way to delete 'other' ref at the remote end. */ if (try_explicit_object_name(rs->src, match) < 0) - return error(_("src refspec %s does not match any."), rs->src); + return error(_("src refspec %s does not match any"), rs->src); if (allocated_match) *allocated_match = 1; return 0; default: - return error(_("src refspec %s matches more than one."), rs->src); + return error(_("src refspec %s matches more than one"), rs->src); } } + static void show_push_unqualified_ref_name_error(const char *dst_value, + const char *matched_src_name) + { + struct object_id oid; + enum object_type type; + + /* + * TRANSLATORS: "matches '%s'%" is the part of "git push + * :" push, and "being pushed ('%s')" is + * the . + */ + error(_("The destination you provided is not a full refname (i.e.,\n" + "starting with \"refs/\"). We tried to guess what you meant by:\n" + "\n" + "- Looking for a ref that matches '%s' on the remote side.\n" + "- Checking if the being pushed ('%s')\n" + " is a ref in \"refs/{heads,tags}/\". If so we add a corresponding\n" + " refs/{heads,tags}/ prefix on the remote side.\n" + "\n" + "Neither worked, so we gave up. You must fully qualify the ref."), + dst_value, matched_src_name); + + if (!advice_push_unqualified_ref_name) + return; + + if (get_oid(matched_src_name, &oid)) + BUG("'%s' is not a valid object, " + "match_explicit_lhs() should catch this!", + matched_src_name); + type = oid_object_info(the_repository, &oid, NULL); + if (type == OBJ_COMMIT) { + advise(_("The part of the refspec is a commit object.\n" + "Did you mean to create a new branch by pushing to\n" + "'%s:refs/heads/%s'?"), + matched_src_name, dst_value); + } else if (type == OBJ_TAG) { + advise(_("The part of the refspec is a tag object.\n" + "Did you mean to create a new tag by pushing to\n" + "'%s:refs/tags/%s'?"), + matched_src_name, dst_value); + } else if (type == OBJ_TREE) { + advise(_("The part of the refspec is a tree object.\n" + "Did you mean to tag a new tree by pushing to\n" + "'%s:refs/tags/%s'?"), + matched_src_name, dst_value); + } else if (type == OBJ_BLOB) { + advise(_("The part of the refspec is a blob object.\n" + "Did you mean to tag a new blob by pushing to\n" + "'%s:refs/tags/%s'?"), + matched_src_name, dst_value); + } else { + BUG("'%s' should be commit/tag/tree/blob, is '%d'", + matched_src_name, type); + } + } + static int match_explicit(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec_item *rs) @@@ -1030,7 -1088,7 +1088,7 @@@ if (!dst_value || ((flag & REF_ISSYMREF) && !starts_with(dst_value, "refs/heads/"))) - die("%s cannot be resolved to branch.", + die(_("%s cannot be resolved to branch"), matched_src->name); } @@@ -1038,32 -1096,30 +1096,29 @@@ case 1: break; case 0: - if (starts_with(dst_value, "refs/")) + if (starts_with(dst_value, "refs/")) { matched_dst = make_linked_ref(dst_value, dst_tail); - else if (is_null_oid(&matched_src->new_oid)) - + } else if (is_null_oid(&matched_src->new_oid)) { error(_("unable to delete '%s': remote ref does not exist"), dst_value); - else if ((dst_guess = guess_ref(dst_value, matched_src))) { + } else if ((dst_guess = guess_ref(dst_value, matched_src))) { matched_dst = make_linked_ref(dst_guess, dst_tail); free(dst_guess); - } else - error(_("unable to push to unqualified destination: %s\n" - "The destination refspec neither matches an " - "existing ref on the remote nor\n" - "begins with refs/, and we are unable to " - "guess a prefix based on the source ref."), - dst_value); + } else { + show_push_unqualified_ref_name_error(dst_value, + matched_src->name); + } break; default: matched_dst = NULL; - error(_("dst refspec %s matches more than one."), + error(_("dst refspec %s matches more than one"), dst_value); break; } if (!matched_dst) return -1; if (matched_dst->peer_ref) - return error(_("dst ref %s receives from more than one src."), + return error(_("dst ref %s receives from more than one src"), matched_dst->name); else { matched_dst->peer_ref = allocated_src ? @@@ -1782,7 -1838,7 +1837,7 @@@ int get_fetch_map(const struct ref *rem ref_map = get_remote_ref(remote_refs, name); } if (!missing_ok && !ref_map) - die("Couldn't find remote ref %s", name); + die(_("couldn't find remote ref %s"), name); if (ref_map) { ref_map->peer_ref = get_local_ref(refspec->dst); if (ref_map->peer_ref && refspec->force) @@@ -1890,7 -1946,7 +1945,7 @@@ int stat_tracking_info(struct branch *b repo_init_revisions(the_repository, &revs, NULL); setup_revisions(argv.argc, argv.argv, &revs, NULL); if (prepare_revision_walk(&revs)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); /* ... and count the commits on each side. */ while (1) { @@@ -2163,8 -2219,7 +2218,8 @@@ static int parse_push_cas_option(struc else if (!colon[1]) oidclr(&entry->expect); else if (get_oid(colon + 1, &entry->expect)) - return error(_("cannot parse expected object name '%s'"), colon + 1); + return error(_("cannot parse expected object name '%s'"), + colon + 1); return 0; }