Git fork

submodule--helper: improve logic for fallback remote name

The repo_get_default_remote() function in submodule--helper currently
tries to figure out the proper remote name to use for a submodule based
on a few factors.

First, it tries to find the remote for the currently checked out branch.
This works if the submodule is configured to checkout to a branch
instead of a detached HEAD state.

In the detached HEAD state, the code calls back to using "origin", on
the assumption that this is the default remote name. Some users may
change this, such as by setting clone.defaultRemoteName, or by changing
the remote name manually within the submodule repository.

As a first step to improving this situation, refactor to reuse the logic
from remotes_remote_for_branch(). This function uses the remote from the
branch if it has one. If it doesn't then it checks to see if there is
exactly one remote. It uses this remote first before attempting to fall
back to "origin".

To allow using this helper function, introduce a repo_default_remote()
helper to remote.c which takes a repository structure. This helper will
load the remote configuration and get the "HEAD" branch. Then it will
call remotes_remote_for_branch to find the default remote.

Replace calls of repo_get_default_remote() with the calls to this new
function. To maintain consistency with the existing callers, continue
copying the returned string with xstrdup.

This isn't a perfect solution for users who change remote names, but it
should help in cases where the remote name is changed but users haven't
added any additional remotes.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Jacob Keller and committed by
Junio C Hamano
e759275c f8542961

+57 -46
+5 -41
builtin/submodule--helper.c
··· 41 41 typedef void (*each_submodule_fn)(const struct cache_entry *list_item, 42 42 void *cb_data); 43 43 44 - static int repo_get_default_remote(struct repository *repo, char **default_remote) 45 - { 46 - char *dest = NULL; 47 - struct strbuf sb = STRBUF_INIT; 48 - struct ref_store *store = get_main_ref_store(repo); 49 - const char *refname = refs_resolve_ref_unsafe(store, "HEAD", 0, NULL, 50 - NULL); 51 - 52 - if (!refname) 53 - return die_message(_("No such ref: %s"), "HEAD"); 54 - 55 - /* detached HEAD */ 56 - if (!strcmp(refname, "HEAD")) { 57 - *default_remote = xstrdup("origin"); 58 - return 0; 59 - } 60 - 61 - if (!skip_prefix(refname, "refs/heads/", &refname)) 62 - return die_message(_("Expecting a full ref name, got %s"), 63 - refname); 64 - 65 - strbuf_addf(&sb, "branch.%s.remote", refname); 66 - if (repo_config_get_string(repo, sb.buf, &dest)) 67 - *default_remote = xstrdup("origin"); 68 - else 69 - *default_remote = dest; 70 - 71 - strbuf_release(&sb); 72 - return 0; 73 - } 74 - 75 44 static int get_default_remote_submodule(const char *module_path, char **default_remote) 76 45 { 77 46 struct repository subrepo; 78 - int ret; 79 47 80 48 if (repo_submodule_init(&subrepo, the_repository, module_path, 81 49 null_oid(the_hash_algo)) < 0) 82 50 return die_message(_("could not get a repository handle for submodule '%s'"), 83 51 module_path); 84 - ret = repo_get_default_remote(&subrepo, default_remote); 52 + 53 + *default_remote = xstrdup(repo_default_remote(&subrepo)); 54 + 85 55 repo_clear(&subrepo); 86 56 87 - return ret; 57 + return 0; 88 58 } 89 59 90 60 static char *get_default_remote(void) 91 61 { 92 - char *default_remote; 93 - int code = repo_get_default_remote(the_repository, &default_remote); 94 - 95 - if (code) 96 - exit(code); 97 - 98 - return default_remote; 62 + return xstrdup(repo_default_remote(the_repository)); 99 63 } 100 64 101 65 static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet)
+20 -5
remote.c
··· 1772 1772 } 1773 1773 } 1774 1774 1775 - struct branch *branch_get(const char *name) 1775 + static struct branch *repo_branch_get(struct repository *repo, const char *name) 1776 1776 { 1777 1777 struct branch *ret; 1778 1778 1779 - read_config(the_repository, 0); 1779 + read_config(repo, 0); 1780 1780 if (!name || !*name || !strcmp(name, "HEAD")) 1781 - ret = the_repository->remote_state->current_branch; 1781 + ret = repo->remote_state->current_branch; 1782 1782 else 1783 - ret = make_branch(the_repository->remote_state, name, 1783 + ret = make_branch(repo->remote_state, name, 1784 1784 strlen(name)); 1785 - set_merge(the_repository, ret); 1785 + set_merge(repo, ret); 1786 1786 return ret; 1787 + } 1788 + 1789 + struct branch *branch_get(const char *name) 1790 + { 1791 + return repo_branch_get(the_repository, name); 1792 + } 1793 + 1794 + const char *repo_default_remote(struct repository *repo) 1795 + { 1796 + struct branch *branch; 1797 + 1798 + read_config(repo, 0); 1799 + branch = repo_branch_get(repo, "HEAD"); 1800 + 1801 + return remotes_remote_for_branch(repo->remote_state, branch, NULL); 1787 1802 } 1788 1803 1789 1804 int branch_has_merge_config(struct branch *branch)
+3
remote.h
··· 9 9 10 10 struct option; 11 11 struct transport_ls_refs_options; 12 + struct repository; 12 13 13 14 /** 14 15 * The API gives access to the configuration related to remotes. It handles ··· 337 338 const char *remote_for_branch(struct branch *branch, int *explicit); 338 339 const char *pushremote_for_branch(struct branch *branch, int *explicit); 339 340 char *remote_ref_for_branch(struct branch *branch, int for_push); 341 + 342 + const char *repo_default_remote(struct repository *repo); 340 343 341 344 /* returns true if the given branch has merge configuration given. */ 342 345 int branch_has_merge_config(struct branch *branch);
+29
t/t7406-submodule-update.sh
··· 1134 1134 git clone --recurse-submodules top top-clean 1135 1135 ' 1136 1136 1137 + test_expect_success 'submodule update with renamed remote' ' 1138 + test_when_finished "rm -fr top-cloned" && 1139 + cp -r top-clean top-cloned && 1140 + 1141 + # Create a commit in each repo, starting with bottom 1142 + test_commit -C bottom rename_commit && 1143 + # Create middle commit 1144 + git -C middle/bottom fetch && 1145 + git -C middle/bottom checkout -f FETCH_HEAD && 1146 + git -C middle add bottom && 1147 + git -C middle commit -m "rename_commit" && 1148 + # Create top commit 1149 + git -C top/middle fetch && 1150 + git -C top/middle checkout -f FETCH_HEAD && 1151 + git -C top add middle && 1152 + git -C top commit -m "rename_commit" && 1153 + 1154 + # rename the submodule remote 1155 + git -C top-cloned/middle remote rename origin upstream && 1156 + 1157 + # Make the update of "middle" a no-op, otherwise we error out 1158 + # because of its unmerged state 1159 + test_config -C top-cloned submodule.middle.update !true && 1160 + git -C top-cloned submodule update --recursive 2>actual.err && 1161 + cat >expect.err <<-\EOF && 1162 + EOF 1163 + test_cmp expect.err actual.err 1164 + ' 1165 + 1137 1166 test_expect_success 'submodule update should skip unmerged submodules' ' 1138 1167 test_when_finished "rm -fr top-cloned" && 1139 1168 cp -r top-clean top-cloned &&