Skip to content

Commit 3a72345

Browse files
authored
Merge pull request libgit2#5581 from libgit2/ethomson/mainbranch
Respect `init.defaultBranch` setting
2 parents a94fedc + 0ff70f4 commit 3a72345

File tree

7 files changed

+181
-46
lines changed

7 files changed

+181
-46
lines changed

src/clone.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,31 @@ static int update_head_to_new_branch(
137137
return error;
138138
}
139139

140+
static int update_head_to_default(git_repository *repo)
141+
{
142+
git_buf initialbranch = GIT_BUF_INIT;
143+
const char *branch_name;
144+
int error = 0;
145+
146+
if ((error = git_repository_initialbranch(&initialbranch, repo)) < 0)
147+
goto done;
148+
149+
if (git__prefixcmp(initialbranch.ptr, GIT_REFS_HEADS_DIR) != 0) {
150+
git_error_set(GIT_ERROR_INVALID, "invalid initial branch '%s'", initialbranch.ptr);
151+
error = -1;
152+
goto done;
153+
}
154+
155+
branch_name = initialbranch.ptr + strlen(GIT_REFS_HEADS_DIR);
156+
157+
error = setup_tracking_config(repo, branch_name, GIT_REMOTE_ORIGIN,
158+
initialbranch.ptr);
159+
160+
done:
161+
git_buf_dispose(&initialbranch);
162+
return error;
163+
}
164+
140165
static int update_head_to_remote(
141166
git_repository *repo,
142167
git_remote *remote,
@@ -147,16 +172,15 @@ static int update_head_to_remote(
147172
git_refspec *refspec;
148173
const git_remote_head *remote_head, **refs;
149174
const git_oid *remote_head_id;
150-
git_buf remote_master_name = GIT_BUF_INIT;
175+
git_buf remote_branch_name = GIT_BUF_INIT;
151176
git_buf branch = GIT_BUF_INIT;
152177

153178
if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
154179
return error;
155180

156181
/* We cloned an empty repository or one with an unborn HEAD */
157182
if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE))
158-
return setup_tracking_config(
159-
repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
183+
return update_head_to_default(repo);
160184

161185
/* We know we have HEAD, let's see where it points */
162186
remote_head = refs[0];
@@ -179,9 +203,9 @@ static int update_head_to_remote(
179203
goto cleanup;
180204
}
181205

182-
/* Determine the remote tracking reference name from the local master */
206+
/* Determine the remote tracking ref name from the local branch */
183207
if ((error = git_refspec_transform(
184-
&remote_master_name,
208+
&remote_branch_name,
185209
refspec,
186210
git_buf_cstr(&branch))) < 0)
187211
goto cleanup;
@@ -193,7 +217,7 @@ static int update_head_to_remote(
193217
reflog_message);
194218

195219
cleanup:
196-
git_buf_dispose(&remote_master_name);
220+
git_buf_dispose(&remote_branch_name);
197221
git_buf_dispose(&branch);
198222

199223
return error;

src/refs.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ extern bool git_reference__enable_symbolic_ref_target_validation;
4545
#define GIT_REBASE_APPLY_DIR "rebase-apply/"
4646
#define GIT_REBASE_APPLY_REBASING_FILE GIT_REBASE_APPLY_DIR "rebasing"
4747
#define GIT_REBASE_APPLY_APPLYING_FILE GIT_REBASE_APPLY_DIR "applying"
48-
#define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master"
4948

5049
#define GIT_SEQUENCER_DIR "sequencer/"
5150
#define GIT_SEQUENCER_HEAD_FILE GIT_SEQUENCER_DIR "head"

src/remote.c

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2375,29 +2375,36 @@ int git_remote_default_branch(git_buf *out, git_remote *remote)
23752375
const git_remote_head *guess = NULL;
23762376
const git_oid *head_id;
23772377
size_t heads_len, i;
2378+
git_buf local_default = GIT_BUF_INIT;
23782379
int error;
23792380

23802381
assert(out);
23812382

23822383
if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
2383-
return error;
2384-
2385-
if (heads_len == 0)
2386-
return GIT_ENOTFOUND;
2384+
goto done;
23872385

2388-
if (strcmp(heads[0]->name, GIT_HEAD_FILE))
2389-
return GIT_ENOTFOUND;
2386+
if (heads_len == 0 || strcmp(heads[0]->name, GIT_HEAD_FILE)) {
2387+
error = GIT_ENOTFOUND;
2388+
goto done;
2389+
}
23902390

23912391
git_buf_sanitize(out);
2392+
23922393
/* the first one must be HEAD so if that has the symref info, we're done */
2393-
if (heads[0]->symref_target)
2394-
return git_buf_puts(out, heads[0]->symref_target);
2394+
if (heads[0]->symref_target) {
2395+
error = git_buf_puts(out, heads[0]->symref_target);
2396+
goto done;
2397+
}
23952398

23962399
/*
23972400
* If there's no symref information, we have to look over them
2398-
* and guess. We return the first match unless the master
2399-
* branch is a candidate. Then we return the master branch.
2401+
* and guess. We return the first match unless the default
2402+
* branch is a candidate. Then we return the default branch.
24002403
*/
2404+
2405+
if ((error = git_repository_initialbranch(&local_default, remote->repo)) < 0)
2406+
goto done;
2407+
24012408
head_id = &heads[0]->oid;
24022409

24032410
for (i = 1; i < heads_len; i++) {
@@ -2412,16 +2419,22 @@ int git_remote_default_branch(git_buf *out, git_remote *remote)
24122419
continue;
24132420
}
24142421

2415-
if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) {
2422+
if (!git__strcmp(local_default.ptr, heads[i]->name)) {
24162423
guess = heads[i];
24172424
break;
24182425
}
24192426
}
24202427

2421-
if (!guess)
2422-
return GIT_ENOTFOUND;
2428+
if (!guess) {
2429+
error = GIT_ENOTFOUND;
2430+
goto done;
2431+
}
2432+
2433+
error = git_buf_puts(out, guess->name);
24232434

2424-
return git_buf_puts(out, guess->name);
2435+
done:
2436+
git_buf_dispose(&local_default);
2437+
return error;
24252438
}
24262439

24272440
int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)

src/repository.c

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ static int check_extensions(git_config *config, int version);
7070

7171
#define GIT_FILE_CONTENT_PREFIX "gitdir:"
7272

73-
#define GIT_BRANCH_MASTER "master"
73+
#define GIT_BRANCH_DEFAULT "master"
7474

7575
#define GIT_REPO_VERSION 0
7676
#define GIT_REPO_MAX_VERSION 1
@@ -1408,9 +1408,6 @@ int git_repository_create_head(const char *git_dir, const char *ref_name)
14081408
(error = git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE)) < 0)
14091409
goto out;
14101410

1411-
if (!ref_name)
1412-
ref_name = GIT_BRANCH_MASTER;
1413-
14141411
if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0)
14151412
fmt = "ref: %s\n";
14161413
else
@@ -2061,6 +2058,43 @@ static int repo_init_directories(
20612058
return error;
20622059
}
20632060

2061+
static int repo_init_head(const char *repo_dir, const char *given)
2062+
{
2063+
git_config *cfg = NULL;
2064+
git_buf head_path = GIT_BUF_INIT, cfg_branch = GIT_BUF_INIT;
2065+
const char *initial_head = NULL;
2066+
int error;
2067+
2068+
if ((error = git_buf_joinpath(&head_path, repo_dir, GIT_HEAD_FILE)) < 0)
2069+
goto out;
2070+
2071+
/*
2072+
* A template may have set a HEAD; use that unless it's been
2073+
* overridden by the caller's given initial head setting.
2074+
*/
2075+
if (git_path_exists(head_path.ptr) && !given)
2076+
goto out;
2077+
2078+
if (given) {
2079+
initial_head = given;
2080+
} else if ((error = git_config_open_default(&cfg)) >= 0 &&
2081+
(error = git_config_get_string_buf(&cfg_branch, cfg, "init.defaultbranch")) >= 0) {
2082+
initial_head = cfg_branch.ptr;
2083+
}
2084+
2085+
if (!initial_head)
2086+
initial_head = GIT_BRANCH_DEFAULT;
2087+
2088+
error = git_repository_create_head(repo_dir, initial_head);
2089+
2090+
out:
2091+
git_config_free(cfg);
2092+
git_buf_dispose(&head_path);
2093+
git_buf_dispose(&cfg_branch);
2094+
2095+
return error;
2096+
}
2097+
20642098
static int repo_init_create_origin(git_repository *repo, const char *url)
20652099
{
20662100
int error;
@@ -2091,7 +2125,7 @@ int git_repository_init_ext(
20912125
git_repository_init_options *opts)
20922126
{
20932127
git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT,
2094-
common_path = GIT_BUF_INIT, head_path = GIT_BUF_INIT;
2128+
common_path = GIT_BUF_INIT;
20952129
const char *wd;
20962130
bool is_valid;
20972131
int error;
@@ -2125,16 +2159,7 @@ int git_repository_init_ext(
21252159
} else {
21262160
if ((error = repo_init_structure(repo_path.ptr, wd, opts)) < 0 ||
21272161
(error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode)) < 0 ||
2128-
(error = git_buf_joinpath(&head_path, repo_path.ptr, GIT_HEAD_FILE)) < 0)
2129-
goto out;
2130-
2131-
/*
2132-
* Only set the new HEAD if the file does not exist already via
2133-
* a template or if the caller has explicitly supplied an
2134-
* initial HEAD value.
2135-
*/
2136-
if ((!git_path_exists(head_path.ptr) || opts->initial_head) &&
2137-
(error = git_repository_create_head(repo_path.ptr, opts->initial_head)) < 0)
2162+
(error = repo_init_head(repo_path.ptr, opts->initial_head)) < 0)
21382163
goto out;
21392164
}
21402165

@@ -2146,7 +2171,6 @@ int git_repository_init_ext(
21462171
goto out;
21472172

21482173
out:
2149-
git_buf_dispose(&head_path);
21502174
git_buf_dispose(&common_path);
21512175
git_buf_dispose(&repo_path);
21522176
git_buf_dispose(&wd_path);
@@ -2330,23 +2354,59 @@ static int repo_contains_no_reference(git_repository *repo)
23302354
return error;
23312355
}
23322356

2357+
int git_repository_initialbranch(git_buf *out, git_repository *repo)
2358+
{
2359+
git_config *config;
2360+
git_config_entry *entry = NULL;
2361+
const char *branch;
2362+
int error;
2363+
2364+
if ((error = git_repository_config__weakptr(&config, repo)) < 0)
2365+
return error;
2366+
2367+
if ((error = git_config_get_entry(&entry, config, "init.defaultbranch")) == 0) {
2368+
branch = entry->value;
2369+
}
2370+
else if (error == GIT_ENOTFOUND) {
2371+
branch = GIT_BRANCH_DEFAULT;
2372+
}
2373+
else {
2374+
goto done;
2375+
}
2376+
2377+
if ((error = git_buf_puts(out, GIT_REFS_HEADS_DIR)) < 0 ||
2378+
(error = git_buf_puts(out, branch)) < 0)
2379+
goto done;
2380+
2381+
if (!git_reference_is_valid_name(out->ptr)) {
2382+
git_error_set(GIT_ERROR_INVALID, "the value of init.defaultBranch is not a valid reference name");
2383+
error = -1;
2384+
}
2385+
2386+
done:
2387+
git_config_entry_free(entry);
2388+
return error;
2389+
}
2390+
23332391
int git_repository_is_empty(git_repository *repo)
23342392
{
23352393
git_reference *head = NULL;
2336-
int is_empty = 0;
2394+
git_buf initialbranch = GIT_BUF_INIT;
2395+
int result = 0;
23372396

2338-
if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
2339-
return -1;
2397+
if ((result = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0 ||
2398+
(result = git_repository_initialbranch(&initialbranch, repo)) < 0)
2399+
goto done;
23402400

2341-
if (git_reference_type(head) == GIT_REFERENCE_SYMBOLIC)
2342-
is_empty =
2343-
(strcmp(git_reference_symbolic_target(head),
2344-
GIT_REFS_HEADS_DIR "master") == 0) &&
2345-
repo_contains_no_reference(repo);
2401+
result = (git_reference_type(head) == GIT_REFERENCE_SYMBOLIC &&
2402+
strcmp(git_reference_symbolic_target(head), initialbranch.ptr) == 0 &&
2403+
repo_contains_no_reference(repo));
23462404

2405+
done:
23472406
git_reference_free(head);
2407+
git_buf_dispose(&initialbranch);
23482408

2349-
return is_empty;
2409+
return result;
23502410
}
23512411

23522412
static const char *resolved_parent_path(const git_repository *repo, git_repository_item_t item, git_repository_item_t fallback)

src/repository.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,10 @@ extern size_t git_repository__reserved_names_posix_len;
232232
bool git_repository__reserved_names(
233233
git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs);
234234

235+
/*
236+
* The default branch for the repository; the `init.defaultBranch`
237+
* configuration option, if set, or `master` if it is not.
238+
*/
239+
int git_repository_initialbranch(git_buf *out, git_repository *repo);
240+
235241
#endif

tests/clone/empty.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "git2/clone.h"
44
#include "repository.h"
5+
#include "repo/repo_helpers.h"
56

67
static git_clone_options g_options;
78
static git_repository *g_repo;
@@ -22,6 +23,7 @@ void test_clone_empty__initialize(void)
2223

2324
void test_clone_empty__cleanup(void)
2425
{
26+
cl_fixture_cleanup("tmp_global_path");
2527
cl_git_sandbox_cleanup();
2628
}
2729

@@ -66,6 +68,21 @@ void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
6668
expected_tracked_branch_name));
6769
}
6870

71+
void test_clone_empty__respects_initialbranch_config(void)
72+
{
73+
git_buf buf = GIT_BUF_INIT;
74+
75+
create_tmp_global_config("tmp_global_path", "init.defaultbranch", "my_default_branch");
76+
77+
cl_set_cleanup(&cleanup_repository, "./empty");
78+
79+
g_options.bare = true;
80+
cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
81+
cl_git_pass(git_branch_upstream_name(&buf, g_repo_cloned, "refs/heads/my_default_branch"));
82+
cl_assert_equal_s("refs/remotes/origin/my_default_branch", buf.ptr);
83+
git_buf_dispose(&buf);
84+
}
85+
6986
void test_clone_empty__can_clone_an_empty_local_repo(void)
7087
{
7188
cl_set_cleanup(&cleanup_repository, "./empty");

tests/repo/init.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,3 +665,19 @@ void test_repo_init__unwriteable_directory(void)
665665
clar__skip();
666666
#endif
667667
}
668+
669+
void test_repo_init__defaultbranch_config(void)
670+
{
671+
git_reference *head;
672+
673+
cl_set_cleanup(&cleanup_repository, "repo");
674+
675+
create_tmp_global_config("tmp_global_path", "init.defaultbranch", "my_default_branch");
676+
677+
cl_git_pass(git_repository_init(&_repo, "repo", 0));
678+
cl_git_pass(git_reference_lookup(&head, _repo, "HEAD"));
679+
680+
cl_assert_equal_s("refs/heads/my_default_branch", git_reference_symbolic_target(head));
681+
682+
git_reference_free(head);
683+
}

0 commit comments

Comments
 (0)