Unverified Commit 3a72345b by Edward Thomson Committed by GitHub

Merge pull request #5581 from libgit2/ethomson/mainbranch

Respect `init.defaultBranch` setting
parents a94fedc1 0ff70f4a
...@@ -137,6 +137,31 @@ static int update_head_to_new_branch( ...@@ -137,6 +137,31 @@ static int update_head_to_new_branch(
return error; return error;
} }
static int update_head_to_default(git_repository *repo)
{
git_buf initialbranch = GIT_BUF_INIT;
const char *branch_name;
int error = 0;
if ((error = git_repository_initialbranch(&initialbranch, repo)) < 0)
goto done;
if (git__prefixcmp(initialbranch.ptr, GIT_REFS_HEADS_DIR) != 0) {
git_error_set(GIT_ERROR_INVALID, "invalid initial branch '%s'", initialbranch.ptr);
error = -1;
goto done;
}
branch_name = initialbranch.ptr + strlen(GIT_REFS_HEADS_DIR);
error = setup_tracking_config(repo, branch_name, GIT_REMOTE_ORIGIN,
initialbranch.ptr);
done:
git_buf_dispose(&initialbranch);
return error;
}
static int update_head_to_remote( static int update_head_to_remote(
git_repository *repo, git_repository *repo,
git_remote *remote, git_remote *remote,
...@@ -147,7 +172,7 @@ static int update_head_to_remote( ...@@ -147,7 +172,7 @@ static int update_head_to_remote(
git_refspec *refspec; git_refspec *refspec;
const git_remote_head *remote_head, **refs; const git_remote_head *remote_head, **refs;
const git_oid *remote_head_id; const git_oid *remote_head_id;
git_buf remote_master_name = GIT_BUF_INIT; git_buf remote_branch_name = GIT_BUF_INIT;
git_buf branch = GIT_BUF_INIT; git_buf branch = GIT_BUF_INIT;
if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0) if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
...@@ -155,8 +180,7 @@ static int update_head_to_remote( ...@@ -155,8 +180,7 @@ static int update_head_to_remote(
/* We cloned an empty repository or one with an unborn HEAD */ /* We cloned an empty repository or one with an unborn HEAD */
if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE)) if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE))
return setup_tracking_config( return update_head_to_default(repo);
repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
/* We know we have HEAD, let's see where it points */ /* We know we have HEAD, let's see where it points */
remote_head = refs[0]; remote_head = refs[0];
...@@ -179,9 +203,9 @@ static int update_head_to_remote( ...@@ -179,9 +203,9 @@ static int update_head_to_remote(
goto cleanup; goto cleanup;
} }
/* Determine the remote tracking reference name from the local master */ /* Determine the remote tracking ref name from the local branch */
if ((error = git_refspec_transform( if ((error = git_refspec_transform(
&remote_master_name, &remote_branch_name,
refspec, refspec,
git_buf_cstr(&branch))) < 0) git_buf_cstr(&branch))) < 0)
goto cleanup; goto cleanup;
...@@ -193,7 +217,7 @@ static int update_head_to_remote( ...@@ -193,7 +217,7 @@ static int update_head_to_remote(
reflog_message); reflog_message);
cleanup: cleanup:
git_buf_dispose(&remote_master_name); git_buf_dispose(&remote_branch_name);
git_buf_dispose(&branch); git_buf_dispose(&branch);
return error; return error;
......
...@@ -45,7 +45,6 @@ extern bool git_reference__enable_symbolic_ref_target_validation; ...@@ -45,7 +45,6 @@ extern bool git_reference__enable_symbolic_ref_target_validation;
#define GIT_REBASE_APPLY_DIR "rebase-apply/" #define GIT_REBASE_APPLY_DIR "rebase-apply/"
#define GIT_REBASE_APPLY_REBASING_FILE GIT_REBASE_APPLY_DIR "rebasing" #define GIT_REBASE_APPLY_REBASING_FILE GIT_REBASE_APPLY_DIR "rebasing"
#define GIT_REBASE_APPLY_APPLYING_FILE GIT_REBASE_APPLY_DIR "applying" #define GIT_REBASE_APPLY_APPLYING_FILE GIT_REBASE_APPLY_DIR "applying"
#define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master"
#define GIT_SEQUENCER_DIR "sequencer/" #define GIT_SEQUENCER_DIR "sequencer/"
#define GIT_SEQUENCER_HEAD_FILE GIT_SEQUENCER_DIR "head" #define GIT_SEQUENCER_HEAD_FILE GIT_SEQUENCER_DIR "head"
......
...@@ -2375,29 +2375,36 @@ int git_remote_default_branch(git_buf *out, git_remote *remote) ...@@ -2375,29 +2375,36 @@ int git_remote_default_branch(git_buf *out, git_remote *remote)
const git_remote_head *guess = NULL; const git_remote_head *guess = NULL;
const git_oid *head_id; const git_oid *head_id;
size_t heads_len, i; size_t heads_len, i;
git_buf local_default = GIT_BUF_INIT;
int error; int error;
assert(out); assert(out);
if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0) if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
return error; goto done;
if (heads_len == 0)
return GIT_ENOTFOUND;
if (strcmp(heads[0]->name, GIT_HEAD_FILE)) if (heads_len == 0 || strcmp(heads[0]->name, GIT_HEAD_FILE)) {
return GIT_ENOTFOUND; error = GIT_ENOTFOUND;
goto done;
}
git_buf_sanitize(out); git_buf_sanitize(out);
/* the first one must be HEAD so if that has the symref info, we're done */ /* the first one must be HEAD so if that has the symref info, we're done */
if (heads[0]->symref_target) if (heads[0]->symref_target) {
return git_buf_puts(out, heads[0]->symref_target); error = git_buf_puts(out, heads[0]->symref_target);
goto done;
}
/* /*
* If there's no symref information, we have to look over them * If there's no symref information, we have to look over them
* and guess. We return the first match unless the master * and guess. We return the first match unless the default
* branch is a candidate. Then we return the master branch. * branch is a candidate. Then we return the default branch.
*/ */
if ((error = git_repository_initialbranch(&local_default, remote->repo)) < 0)
goto done;
head_id = &heads[0]->oid; head_id = &heads[0]->oid;
for (i = 1; i < heads_len; i++) { for (i = 1; i < heads_len; i++) {
...@@ -2412,16 +2419,22 @@ int git_remote_default_branch(git_buf *out, git_remote *remote) ...@@ -2412,16 +2419,22 @@ int git_remote_default_branch(git_buf *out, git_remote *remote)
continue; continue;
} }
if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) { if (!git__strcmp(local_default.ptr, heads[i]->name)) {
guess = heads[i]; guess = heads[i];
break; break;
} }
} }
if (!guess) if (!guess) {
return GIT_ENOTFOUND; error = GIT_ENOTFOUND;
goto done;
}
error = git_buf_puts(out, guess->name);
return git_buf_puts(out, guess->name); done:
git_buf_dispose(&local_default);
return error;
} }
int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts) int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
......
...@@ -70,7 +70,7 @@ static int check_extensions(git_config *config, int version); ...@@ -70,7 +70,7 @@ static int check_extensions(git_config *config, int version);
#define GIT_FILE_CONTENT_PREFIX "gitdir:" #define GIT_FILE_CONTENT_PREFIX "gitdir:"
#define GIT_BRANCH_MASTER "master" #define GIT_BRANCH_DEFAULT "master"
#define GIT_REPO_VERSION 0 #define GIT_REPO_VERSION 0
#define GIT_REPO_MAX_VERSION 1 #define GIT_REPO_MAX_VERSION 1
...@@ -1408,9 +1408,6 @@ int git_repository_create_head(const char *git_dir, const char *ref_name) ...@@ -1408,9 +1408,6 @@ int git_repository_create_head(const char *git_dir, const char *ref_name)
(error = git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE)) < 0) (error = git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE)) < 0)
goto out; goto out;
if (!ref_name)
ref_name = GIT_BRANCH_MASTER;
if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0) if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0)
fmt = "ref: %s\n"; fmt = "ref: %s\n";
else else
...@@ -2061,6 +2058,43 @@ static int repo_init_directories( ...@@ -2061,6 +2058,43 @@ static int repo_init_directories(
return error; return error;
} }
static int repo_init_head(const char *repo_dir, const char *given)
{
git_config *cfg = NULL;
git_buf head_path = GIT_BUF_INIT, cfg_branch = GIT_BUF_INIT;
const char *initial_head = NULL;
int error;
if ((error = git_buf_joinpath(&head_path, repo_dir, GIT_HEAD_FILE)) < 0)
goto out;
/*
* A template may have set a HEAD; use that unless it's been
* overridden by the caller's given initial head setting.
*/
if (git_path_exists(head_path.ptr) && !given)
goto out;
if (given) {
initial_head = given;
} else if ((error = git_config_open_default(&cfg)) >= 0 &&
(error = git_config_get_string_buf(&cfg_branch, cfg, "init.defaultbranch")) >= 0) {
initial_head = cfg_branch.ptr;
}
if (!initial_head)
initial_head = GIT_BRANCH_DEFAULT;
error = git_repository_create_head(repo_dir, initial_head);
out:
git_config_free(cfg);
git_buf_dispose(&head_path);
git_buf_dispose(&cfg_branch);
return error;
}
static int repo_init_create_origin(git_repository *repo, const char *url) static int repo_init_create_origin(git_repository *repo, const char *url)
{ {
int error; int error;
...@@ -2091,7 +2125,7 @@ int git_repository_init_ext( ...@@ -2091,7 +2125,7 @@ int git_repository_init_ext(
git_repository_init_options *opts) git_repository_init_options *opts)
{ {
git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT, git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT,
common_path = GIT_BUF_INIT, head_path = GIT_BUF_INIT; common_path = GIT_BUF_INIT;
const char *wd; const char *wd;
bool is_valid; bool is_valid;
int error; int error;
...@@ -2125,16 +2159,7 @@ int git_repository_init_ext( ...@@ -2125,16 +2159,7 @@ int git_repository_init_ext(
} else { } else {
if ((error = repo_init_structure(repo_path.ptr, wd, opts)) < 0 || if ((error = repo_init_structure(repo_path.ptr, wd, opts)) < 0 ||
(error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode)) < 0 || (error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode)) < 0 ||
(error = git_buf_joinpath(&head_path, repo_path.ptr, GIT_HEAD_FILE)) < 0) (error = repo_init_head(repo_path.ptr, opts->initial_head)) < 0)
goto out;
/*
* Only set the new HEAD if the file does not exist already via
* a template or if the caller has explicitly supplied an
* initial HEAD value.
*/
if ((!git_path_exists(head_path.ptr) || opts->initial_head) &&
(error = git_repository_create_head(repo_path.ptr, opts->initial_head)) < 0)
goto out; goto out;
} }
...@@ -2146,7 +2171,6 @@ int git_repository_init_ext( ...@@ -2146,7 +2171,6 @@ int git_repository_init_ext(
goto out; goto out;
out: out:
git_buf_dispose(&head_path);
git_buf_dispose(&common_path); git_buf_dispose(&common_path);
git_buf_dispose(&repo_path); git_buf_dispose(&repo_path);
git_buf_dispose(&wd_path); git_buf_dispose(&wd_path);
...@@ -2330,23 +2354,59 @@ static int repo_contains_no_reference(git_repository *repo) ...@@ -2330,23 +2354,59 @@ static int repo_contains_no_reference(git_repository *repo)
return error; return error;
} }
int git_repository_initialbranch(git_buf *out, git_repository *repo)
{
git_config *config;
git_config_entry *entry = NULL;
const char *branch;
int error;
if ((error = git_repository_config__weakptr(&config, repo)) < 0)
return error;
if ((error = git_config_get_entry(&entry, config, "init.defaultbranch")) == 0) {
branch = entry->value;
}
else if (error == GIT_ENOTFOUND) {
branch = GIT_BRANCH_DEFAULT;
}
else {
goto done;
}
if ((error = git_buf_puts(out, GIT_REFS_HEADS_DIR)) < 0 ||
(error = git_buf_puts(out, branch)) < 0)
goto done;
if (!git_reference_is_valid_name(out->ptr)) {
git_error_set(GIT_ERROR_INVALID, "the value of init.defaultBranch is not a valid reference name");
error = -1;
}
done:
git_config_entry_free(entry);
return error;
}
int git_repository_is_empty(git_repository *repo) int git_repository_is_empty(git_repository *repo)
{ {
git_reference *head = NULL; git_reference *head = NULL;
int is_empty = 0; git_buf initialbranch = GIT_BUF_INIT;
int result = 0;
if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) if ((result = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0 ||
return -1; (result = git_repository_initialbranch(&initialbranch, repo)) < 0)
goto done;
if (git_reference_type(head) == GIT_REFERENCE_SYMBOLIC) result = (git_reference_type(head) == GIT_REFERENCE_SYMBOLIC &&
is_empty = strcmp(git_reference_symbolic_target(head), initialbranch.ptr) == 0 &&
(strcmp(git_reference_symbolic_target(head), repo_contains_no_reference(repo));
GIT_REFS_HEADS_DIR "master") == 0) &&
repo_contains_no_reference(repo);
done:
git_reference_free(head); git_reference_free(head);
git_buf_dispose(&initialbranch);
return is_empty; return result;
} }
static const char *resolved_parent_path(const git_repository *repo, git_repository_item_t item, git_repository_item_t fallback) static const char *resolved_parent_path(const git_repository *repo, git_repository_item_t item, git_repository_item_t fallback)
......
...@@ -232,4 +232,10 @@ extern size_t git_repository__reserved_names_posix_len; ...@@ -232,4 +232,10 @@ extern size_t git_repository__reserved_names_posix_len;
bool git_repository__reserved_names( bool git_repository__reserved_names(
git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs); git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs);
/*
* The default branch for the repository; the `init.defaultBranch`
* configuration option, if set, or `master` if it is not.
*/
int git_repository_initialbranch(git_buf *out, git_repository *repo);
#endif #endif
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include "git2/clone.h" #include "git2/clone.h"
#include "repository.h" #include "repository.h"
#include "repo/repo_helpers.h"
static git_clone_options g_options; static git_clone_options g_options;
static git_repository *g_repo; static git_repository *g_repo;
...@@ -22,6 +23,7 @@ void test_clone_empty__initialize(void) ...@@ -22,6 +23,7 @@ void test_clone_empty__initialize(void)
void test_clone_empty__cleanup(void) void test_clone_empty__cleanup(void)
{ {
cl_fixture_cleanup("tmp_global_path");
cl_git_sandbox_cleanup(); cl_git_sandbox_cleanup();
} }
...@@ -66,6 +68,21 @@ void test_clone_empty__can_clone_an_empty_local_repo_barely(void) ...@@ -66,6 +68,21 @@ void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
expected_tracked_branch_name)); expected_tracked_branch_name));
} }
void test_clone_empty__respects_initialbranch_config(void)
{
git_buf buf = GIT_BUF_INIT;
create_tmp_global_config("tmp_global_path", "init.defaultbranch", "my_default_branch");
cl_set_cleanup(&cleanup_repository, "./empty");
g_options.bare = true;
cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
cl_git_pass(git_branch_upstream_name(&buf, g_repo_cloned, "refs/heads/my_default_branch"));
cl_assert_equal_s("refs/remotes/origin/my_default_branch", buf.ptr);
git_buf_dispose(&buf);
}
void test_clone_empty__can_clone_an_empty_local_repo(void) void test_clone_empty__can_clone_an_empty_local_repo(void)
{ {
cl_set_cleanup(&cleanup_repository, "./empty"); cl_set_cleanup(&cleanup_repository, "./empty");
......
...@@ -665,3 +665,19 @@ void test_repo_init__unwriteable_directory(void) ...@@ -665,3 +665,19 @@ void test_repo_init__unwriteable_directory(void)
clar__skip(); clar__skip();
#endif #endif
} }
void test_repo_init__defaultbranch_config(void)
{
git_reference *head;
cl_set_cleanup(&cleanup_repository, "repo");
create_tmp_global_config("tmp_global_path", "init.defaultbranch", "my_default_branch");
cl_git_pass(git_repository_init(&_repo, "repo", 0));
cl_git_pass(git_reference_lookup(&head, _repo, "HEAD"));
cl_assert_equal_s("refs/heads/my_default_branch", git_reference_symbolic_target(head));
git_reference_free(head);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment