Skip to content

Add configuration option to set the default branch name for new repos #653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed

Add configuration option to set the default branch name for new repos #653

wants to merge 1 commit into from

Conversation

UncannyBingo
Copy link

No documentation has been added, nor tests written, no any other checks done to see what this could break.

Thanks for taking the time to contribute to Git! Please be advised that the
Git community does not use github.com for their contributions. Instead, we use
a mailing list (git@vger.kernel.org) for code submissions, code reviews, and
bug reports. Nevertheless, you can use GitGitGadget (https://gitgitgadget.github.io/)
to conveniently send your Pull Requests commits to our mailing list.

Please read the "guidelines for contributing" linked above!

@gitgitgadget
Copy link

gitgitgadget bot commented Jun 9, 2020

Welcome to GitGitGadget

Hi @DEGoodmanWilson, and welcome to GitGitGadget, the GitHub App to send patch series to the Git mailing list from GitHub Pull Requests.

Please make sure that your Pull Request has a good description, as it will be used as cover letter.

Also, it is a good idea to review the commit messages one last time, as the Git project expects them in a quite specific form:

  • the lines should not exceed 76 columns,
  • the first line should be like a header and typically start with a prefix like "tests:" or "commit:", and
  • the commit messages' body should be describing the "why?" of the change.
  • Finally, the commit messages should end in a Signed-off-by: line matching the commits' author.

It is in general a good idea to await the automated test ("Checks") in this Pull Request before contributing the patches, e.g. to avoid trivial issues such as unportable code.

Contributing the patches

Before you can contribute the patches, your GitHub username needs to be added to the list of permitted users. Any already-permitted user can do that, by adding a comment to your PR of the form /allow. A good way to find other contributors is to locate recent pull requests where someone has been /allowed:

Both the person who commented /allow and the PR author are able to /allow you.

An alternative is the channel #git-devel on the FreeNode IRC network:

<newcontributor> I've just created my first PR, could someone please /allow me? https://github.com/gitgitgadget/git/pull/12345
<veteran> newcontributor: it is done
<newcontributor> thanks!

Once on the list of permitted usernames, you can contribute the patches to the Git mailing list by adding a PR comment /submit.

If you want to see what email(s) would be sent for a /submit request, add a PR comment /preview to have the email(s) sent to you. You must have a public GitHub email address for this.

After you submit, GitGitGadget will respond with another comment that contains the link to the cover letter mail in the Git mailing list archive. Please make sure to monitor the discussion in that thread and to address comments and suggestions (while the comments and suggestions will be mirrored into the PR by GitGitGadget, you will still want to reply via mail).

If you do not want to subscribe to the Git mailing list just to be able to respond to a mail, you can download the mbox from the Git mailing list archive (click the (raw) link), then import it into your mail program. If you use GMail, you can do this via:

curl -g --user "<EMailAddress>:<Password>" \
    --url "imaps://imap.gmail.com/INBOX" -T /path/to/raw.txt

To iterate on your change, i.e. send a revised patch or patch series, you will first want to (force-)push to the same branch. You probably also want to modify your Pull Request description (or title). It is a good idea to summarize the revision by adding something like this to the cover letter (read: by editing the first comment on the PR, i.e. the PR description):

Changes since v1:
- Fixed a typo in the commit message (found by ...)
- Added a code comment to ... as suggested by ...
...

To send a new iteration, just add another PR comment with the contents: /submit.

Need help?

New contributors who want advice are encouraged to join git-mentoring@googlegroups.com, where volunteers who regularly contribute to Git are willing to answer newbie questions, give advice, or otherwise provide mentoring to interested contributors. You must join in order to post or view messages, but anyone can join.

You may also be able to find help in real time in the developer IRC channel, #git-devel on Freenode. Remember that IRC does not support offline messaging, so if you send someone a private message and log out, they cannot respond to you. The scrollback of #git-devel is archived, though.

@gitgitgadget
Copy link

gitgitgadget bot commented Jun 9, 2020

There are issues in commit d2a68b0c1752fc0ff01cbca044f50679438d1330:
First line of commit message is too long (> 76 columns): Add configuration option to set the default branch name for new repositories.
Commit not signed off

@dscho
Copy link
Member

dscho commented Jun 9, 2020

/allow

@gitgitgadget
Copy link

gitgitgadget bot commented Jun 9, 2020

User DEGoodmanWilson is now allowed to use GitGitGadget.

WARNING: DEGoodmanWilson has no public email address set on GitHub

@UncannyBingo
Copy link
Author

You should have output be of type (const char *)* (I think those parens are right, the const applies in strange ways).

I thought that would be the case. I couldn’t quite get the syntax for it right. I’ll try with the parens.

@derrickstolee
Copy link

No documentation has been added, nor tests written, no any other checks done to see what this could break.

You will want to do these things before submitting upstream, or at minimum update your title with "[RFC]" and fill your PR description with questions you'd like to see from the community.

@UncannyBingo
Copy link
Author

WARNING: DEGoodmanWilson has no public email address set on GitHub

(This is…well, it shouldn’t be true. Just checked my settings to verify. A bug in the bot?)

Copy link
Member

@dscho dscho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice!

I left a couple of suggestions how to improve the patch. The commit message will also need a bit of work, maybe this?

init: allow overriding the default branch name for new repositories

There is a growing number of projects trying to avoid the non-inclusive
name `master` in their repositories. Currently, the only way to do that
automatically is by copying all of Git's template directory, then
hard-coding the desired default branch name in the `HEAD` file, and then
configuring `init.templateDir`.

To make this process much less cumbersome, let's introduce support for
`init.defaultBranchName`. That way, users won't need to keep their
copied template files up to date, and won't interfere with default hooks
installed by their administrators.

While at it, also look at the environment variable
`GIT_TEST_DEFAULT_BRANCH_NAME`, in preparation for adjusting Git's test
suite to a more inclusive default branch name.

Also, feel free to add the footer:

Reviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>

followed by your Signed-off-by: line.

Further, Documentation/config/init.txt will need to describe the new config setting.

Lastly, this will need new regression tests. The best place for it is likely t/t0001-init.sh. Ideally, I would like to see a new test_expect_success that verifies via test_config and test_must_fail that an invalid branch name is rejected and then verifies that a correct branch name is respected (by git -C <directory> symbolic-ref HEAD >actual and then using test_cmp with a pre-generated expect file), followed by another new test_expect_success that verifies that the environment variable can override the config setting.

Oh, and we will probably want to get clarity what happens when running git clone on a repository that has no commits yet. If it respects that init.defaultBranchName setting automatically, we will want to mention that in the commit message. Otherwise, we will want to make Git respect it ;-)

* Create the default symlink from ".git/HEAD" to the "master"
* branch, if it does not exist yet.
* Create the default symlink from ".git/HEAD" to the default
* branch name, if it does not exist yet.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good!

*/
path = git_path_buf(&buf, "HEAD");
reinit = (!access(path, R_OK)
|| readlink(path, junk, sizeof(junk)-1) != -1);
if (!reinit) {
if (create_symref("HEAD", "refs/heads/master", NULL) < 0)
char *default_branch_name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest an empty line after the declaration, for readability.

*/
path = git_path_buf(&buf, "HEAD");
reinit = (!access(path, R_OK)
|| readlink(path, junk, sizeof(junk)-1) != -1);
if (!reinit) {
if (create_symref("HEAD", "refs/heads/master", NULL) < 0)
char *default_branch_name;
git_config_get_default_branch_name(&default_branch_name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can just call git_config_get_string("init.defaultbranchname", &default_branch_name) directly?

Also, for testing, it would be good to let the environment variable GIT_TEST_DEFAULT_BRANCH_NAME override this, i.e. something like this:

const char *branch_name = getenv("GIT_TEST_DEFAULT_BRANCH_NAME");

if (!branch_name && 
    git_config_get_string_const("init.defaultbranchname", &branch_name) < 0)
        return error("could not get `init.defaultBranchName`");
if (!branch_name)
        branch_name = "master";

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite follow what you're suggesting here (the return statement is confusing me…). Are you suggesting replacing the method git_config_get_default_branch_name() with the sample code? In git_config_get_default_branch_name(), I am checking the env, but only after checking the config, which seems to be the pattern for config vars. It appears you are also suggesting reversing the priority here?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I like the helper method to avoid extra complexity here.

The fact that git_config_get_string_const() returns a negative value means something went horribly wrong while parsing config, so we should probably halt here. However, it would be better to signal an error higher up.

In this case, I don't think the extra care here is merited. Perhaps we should just do the following?

const char *branch_name;

if ((branch_name = getenv("GIT_TEST_DEFAULT_BRANCH_NAME") &&
    *branch_name)
        return branch_name;

if (git_config_get_string_const("init.defaultbranchname", &branch_name))
        return branch_name;

return "master";

(nit: replace my spaces with tabs)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a really good suggestion.

char *default_branch_name;
git_config_get_default_branch_name(&default_branch_name);
/* prepend "refs/heads/" to the branch name */
struct strbuf sb = STRBUF_INIT, sb_sanitized = STRBUF_INIT;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note that Git is pretty strict about the "no declaration after a statement" rule, i.e. you would have to declare this variable at the beginning of the scope.

/* prepend "refs/heads/" to the branch name */
struct strbuf sb = STRBUF_INIT, sb_sanitized = STRBUF_INIT;
strbuf_addstr(&sb, "refs/heads/");
strbuf_addstr(&sb, default_branch_name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quicker way to do this would be char *prefixed = xstrfmt("refs/heads/%s", branch_name); (but you'll need to declare the variable separately, at the beginning of the scope).

Later on, this needs to be free()d. You can do it like this:

char *prefixed = NULL;

if (!branch_name)
        branch_name = "refs/heads/master";
else
        branch_name = prefixed = xstrfmt("refs/heads/%s", branch_name);

[... use `branch_name`...]

free(prefixed);

This works because free(NULL); is a no-op.

struct strbuf sb = STRBUF_INIT, sb_sanitized = STRBUF_INIT;
strbuf_addstr(&sb, "refs/heads/");
strbuf_addstr(&sb, default_branch_name);
sanitize_refname_component(sb.buf, &sb_sanitized);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to be stricter and to validate the name instead, i.e. call check_refname_format(branch_name, 0). If it is misconfigured, do not try to fix it, but tell the user about it and error out instead.

config.h Outdated
@@ -598,6 +598,8 @@ struct key_value_info {
enum config_scope scope;
};

int git_config_get_default_branch_name(char **output);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, we do not introduce such helpers. Maybe it would be a better practice, but we will want this patch to be as unproblematic as possible. To that end, I would like it to be as conforming with current coding style in Git as possible.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making it static inside builtin/init.c should be sufficient.

@dscho
Copy link
Member

dscho commented Jun 9, 2020

FWIW the build failure is due to the declaration of those two strbufs after a statement.

@gitgitgadget
Copy link

gitgitgadget bot commented Jun 9, 2020

There are issues in commit d2a68b0c1752fc0ff01cbca044f50679438d1330:
First line of commit message is too long (> 76 columns): Add configuration option to set the default branch name for new repositories.
Commit not signed off

@gitgitgadget
Copy link

gitgitgadget bot commented Jun 9, 2020

There is an issue in commit a5d486346cca3ba5665dc1da9b93290f3f7ce0a6:
Commit checks stopped - the message is too short

@UncannyBingo
Copy link
Author

This is great stuff @dscho thank you! I'll look at your comments soon, in the next day or two.

@dscho
Copy link
Member

dscho commented Jun 9, 2020

(This is…well, it shouldn’t be true. Just checked my settings to verify. A bug in the bot?)

@DEGoodmanWilson I don't see any public email on the left side of your profile. That's essentially what GitGitGadget is looking for.

@gitgitgadget
Copy link

gitgitgadget bot commented Jun 9, 2020

There are issues in commit 6e94bc62eb911d5b5c0db67dcb77241929145be4:
First line of commit message is too long (> 76 columns): Add configuration option to set the default branch name for new repositories.
Commit not signed off

@gitgitgadget
Copy link

gitgitgadget bot commented Jun 9, 2020

There are issues in commit 61d9f9ecb16f17c07e33859f3b7a3ac08b55eda4:
First line of commit message is too long (> 76 columns): Add configuration option to set the default branch name for new repositories.
Commit not signed off

@UncannyBingo
Copy link
Author

@dscho I've made most of the suggested changes, however, please see the discussion here: #653 (comment)

Next up is tests.

@gitgitgadget
Copy link

gitgitgadget bot commented Jun 9, 2020

There are issues in commit 3451b264b9ee1eb7b36c3af03d8f5e873b15c497:
First line of commit message is too long (> 76 columns): Add configuration option to set the default branch name for new repositories.
Commit not signed off

…itories.

No documentation has been added, nor tests written, no any other checks done to see what this could break.
@gitgitgadget
Copy link

gitgitgadget bot commented Jun 9, 2020

There are issues in commit c23823f:
First line of commit message is too long (> 76 columns): Add configuration option to set the default branch name for new repositories.
Commit not signed off

@UncannyBingo
Copy link
Author

I've got some testing in place; unsure if they're written robustly or not, would love some 👀 on them!

Next up: Docs, and cleaning up the commit message.

Comment on lines +268 to +288
const char *branch_name;
char *prefixed;

/* get the default branch name from config, or failing that, env */
if (git_config_get_string_const("init.defaultbranchname", &branch_name))
branch_name = getenv("GIT_DEFAULT_BRANCH_NAME");

if (!branch_name) {
branch_name = "master";
}

/* prepend "refs/heads/" to the branch name */
prefixed = xstrfmt("refs/heads/%s", branch_name);
if(check_refname_format(prefixed, 0)) {
die(_("Invalid value for default branch name %s"), branch_name);
}

if (create_symref("HEAD", prefixed, NULL) < 0)
exit(1);

free(prefixed);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you took @dscho's plan to consume the config directly here. Good.

I do think this method is complicated enough, so extracting your implementation might be good. There are also a few style issues with extra curly braces.

I made a fixup! commit for you at derrickstolee@a890fcb

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this. It is an abstraction at a different level: not just retrieving the config setting, but handling the default, the environment and the prefixing, too. I like it!

@@ -464,4 +464,20 @@ test_expect_success MINGW 'redirect std handles' '
grep "Needed a single revision" output.txt
'

test_expect_success 'custom default branch name from config' '
git config --global init.defaultbranchname nmb &&

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: tab here.

Comment on lines +474 to +477
test_expect_success 'custom default branch name from env' '
GIT_DEFAULT_BRANCH_NAME=nmb git init custom-env &&
grep nmb custom-env/.git/HEAD
'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that your logic currently has the config option overriding the environment variable. I believe that is the wrong direction, but you should make the behavior explicit in a test. (I make this suggestion because I have hit that exact problem before.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the common pattern in Git is that if there are environment variables and config settings for the same feature, the environment variable overrides the config setting.


/* get the default branch name from config, or failing that, env */
if (git_config_get_string_const("init.defaultbranchname", &branch_name))
branch_name = getenv("GIT_DEFAULT_BRANCH_NAME");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to use the GIT_TEST_ prefix here, otherwise the value will simply be unset by our test suite (meaning: we could not adjust the test suite incrementally).

Please use GIT_TEST_DEFAULT_BRANCH_NAME instead.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Very easy to miss.

@dscho
Copy link
Member

dscho commented Jun 10, 2020

A good point was raised on the Git mailing list: we also need to handle fmt-merge-msg, which special-cases the default branch name:

git/fmt-merge-msg.c

Lines 454 to 457 in b3d7a52

if (!strcmp("master", current_branch))
strbuf_addch(out, '\n');
else
strbuf_addf(out, " into %s\n", current_branch);

@dscho
Copy link
Member

dscho commented Jun 10, 2020

Oh, and we will probably want to get clarity what happens when running git clone on a repository that has no commits yet. If it respects that init.defaultBranchName setting automatically, we will want to mention that in the commit message. Otherwise, we will want to make Git respect it ;-)

Right, and here is the location in the code that will need this to be adjusted:

git/builtin/clone.c

Lines 1267 to 1268 in b3d7a52

install_branch_config(0, "master", option_origin,
"refs/heads/master");

Loosely related, I found these location that also need to be adjusted:

return "master";

static const char *remote_ref = "refs/heads/master";

strbuf_addf(&private_ref_sb, "refs/svn/%s/master", remote->name);

svndump_read((argc > 1) ? argv[1] : NULL, "refs/heads/master",

git/remote.c

Lines 277 to 287 in b3d7a52

/*
* The branches file would have URL and optionally
* #branch specified. The "master" (or specified) branch is
* fetched and stored in the local branch matching the
* remote name.
*/
frag = strchr(buf.buf, '#');
if (frag)
*(frag++) = '\0';
else
frag = "master";

git/remote.c

Lines 2100 to 2105 in b3d7a52

/* If refs/heads/master could be right, it is. */
if (!all) {
r = find_ref_by_name(refs, "refs/heads/master");
if (r && oideq(&r->old_oid, &head->old_oid))
return copy_ref(r);
}

git/send-pack.c

Line 409 in b3d7a52

"Perhaps you should specify a branch such as 'master'.\n");

"Perhaps you should specify a branch such as 'master'.\n"));

In light of this, I fear that my initial preference to inline the logic in builtin/init-db.c was misguided, and @derrickstolee's suggestion to keep it in a separate helper function was more than just smart: this function should probably live in refs.c and be declared in refs.h so that all of the above code locations can simply call char *default_branch = git_default_branch_name();, use that value, and then free(default_branch);. The helper should then probably also offer a flag short_name (and only prefix the result with refs/heads/ when that flag is 0).

@DEGoodmanWilson I feel a bit sorry for dumping so much work on you, but you know where to find me (Gitter): I will be more than eager to assist in any way I can.

@UncannyBingo
Copy link
Author

In light of this, I fear that my initial preference to inline the logic in builtin/init-db.c was misguided, and @derrickstolee's suggestion to keep it in a separate helper function was more than just smart: this function should probably live in refs.c and be declared in refs.h so that all of the above code locations can simply call char *default_branch = git_default_branch_name();, use that value, and then free(default_branch);. The helper should then probably also offer a flag short_name (and only prefix the result with refs/heads/ when that flag is 0).

Yes, I felt that this functionality might be needed in multiple places, which was why I had originally chosen config.c. I'll look at how to get this into refs.c in a way that makes sense.

@DEGoodmanWilson I feel a bit sorry for dumping so much work on you, but you know where to find me (Gitter): I will be more than eager to assist in any way I can.

Hardly; no need to apologize. The feedback has been very helpful in understanding the git codebase, which I'd never looked at before this week! We'll get this figured out and move on to bigger picture things soon. It might be a few days until I can get around to handling the latest round of comments, just FYI, but I won't let this effort lose momentum.

@dscho
Copy link
Member

dscho commented Jun 10, 2020

I'll look at how to get this into refs.c in a way that makes sense.

Oh, please do look no further as git/git@master...fd17a67 (I already did that for you...)

@dscho
Copy link
Member

dscho commented Jun 10, 2020

Oh, please do look no further as git/git@master...fd17a67 (I already did that for you...)

And I'll allow myself to move a bit further, in the interest of pushing this PR forward (I really want to see this on the Git mailing list very soon....).

@dscho dscho mentioned this pull request Jun 10, 2020
19 tasks
@dscho
Copy link
Member

dscho commented Jun 10, 2020

And I'll allow myself to move a bit furthe

Phew. That was a chunk of work. I opened a PR: #655

Now, my PR is not intended to override this here PR, but to augment it. I essentially wanted to make sure that we know that the path forward is clear because the test suite will pass with this. And so it does, at least locally.

What this means is that in git/git@master...f442831, we have all the code changes necessary to make the new config option work!

🎉

What is missing, still, is the documentation changes (Documentation/config/init.txt and t/README, the latter is the correct place to document and describe the GIT_TEST_* variable).

We also have to decide whether this is an init. variable, or a core. variable (because it now affects also git fmt-merge-msg, git clone, etc).

@derrickstolee what do you think?

(For the record, I will now be offline for a few hours, so if you want to tackle the git rebase --autosquash and the documentation, that would be awesome!)

@derrickstolee
Copy link

And I'll allow myself to move a bit furthe

Phew. That was a chunk of work. I opened a PR: #655

Now, my PR is not intended to override this here PR, but to augment it. I essentially wanted to make sure that we know that the path forward is clear because the test suite will pass with this. And so it does, at least locally.

What this means is that in git/git@master...f442831, we have all the code changes necessary to make the new config option work!

🎉

What is missing, still, is the documentation changes (Documentation/config/init.txt and t/README, the latter is the correct place to document and describe the GIT_TEST_* variable).

We also have to decide whether this is an init. variable, or a core. variable (because it now affects also git fmt-merge-msg, git clone, etc).

@derrickstolee what do you think?

I think this is a great start! I'm excited for @dscho to submit his RFC for the full transition plan shortly after this patch is sent to the list. I just want to make sure this commit is as solid as possible. There is enough resistance to the change that I want to make sure there are no serious technical reasons to prevent it from being an acceptable change.

(For the record, I will now be offline for a few hours, so if you want to tackle the git rebase --autosquash and the documentation, that would be awesome!)

I am available to assist today. @DEGoodmanWilson: Let me know if you want to do any pairing on the documentation or just want to point me any updates so we can green-light the change!

@dscho
Copy link
Member

dscho commented Jun 10, 2020

Okay, I'm back and will now tackle the documentation side.

And I think I really prefer core.defaultBranchName, now that I thought about it for a while.

@derrickstolee
Copy link

And I think I really prefer core.defaultBranchName, now that I thought about it for a while.

Now that this affects more than just git init, I agree!

@dscho
Copy link
Member

dscho commented Jun 10, 2020

@DEGoodmanWilson thank you so much for all your help! I submitted this patch, together with my modifications and additions, on the Git mailing list: https://lore.kernel.org/git/pull.656.git.1591823971.gitgitgadget@gmail.com.

@dscho
Copy link
Member

dscho commented Jun 23, 2020

@DEGoodmanWilson can we close this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants