Skip to content

API: Creating a Project with a full ref as Default Branch Omits Initialization

Summary

When creating a GitLab project while calling the Gitlab projects API, specifying the default_branch as a full ref (e.g., refs/heads/main) and setting initialize_with_readme to true results in the project being created without an initialized README and without setting the specified default branch.

This behavior was observed downstream in the Gitlab Terraform Provider:

terraform-provider-gitlab#6309

Steps to reproduce

  • Create a new project using for example Curl to call the projects API
    • Set default_branch to a full ref i.e. refs/heads/main
    • Set initialize_with_readme to true

We can see the following behavior:

  • Default branch has been changed to main
  • Created project is not initialized

Example Project

N/A

What is the current bug behavior?

The project is created unitialized, and the default branch is not set as specified.

What is the expected correct behavior?

Provide a 400 Bad Request when a full ref is provided as a default branch instead of ignoring the provided default branch name.

Relevant logs and/or screenshots

Call Projects API creating a new project with default branch refs/heads/master and initialize_with_readme set to true.

curl --request POST \
     --header "PRIVATE-TOKEN: <PAT>" \
     --header "Content-Type: application/json" \
     --data '{
       "name": "terraform-project",
       "path": "terraform-project",
       "namespace_id": 2666,
       "default_branch": "refs/heads/master",
       "initialize_with_readme": true,
       "visibility": "private",
       
       "merge_requests_enabled": true,
       "forking_access_level": "disabled",
       "lfs_enabled": false,
       "packages_enabled": false,
       "container_registry_access_level": "disabled",
       "analytics_access_level": "enabled",
       "security_and_compliance_access_level": "enabled",
       "wiki_enabled": false,
       "snippets_enabled": false,
       "issues_enabled": false,
       "issues_access_level": "disabled",
       "releases_access_level": "enabled",
       "environments_access_level": "enabled",
       "feature_flags_access_level": "disabled",
       "infrastructure_access_level": "private",
       "monitor_access_level": "disabled",
       
       "merge_pipelines_enabled": false,
       "resolve_outdated_diff_discussions": false,
       "printing_merge_request_link_enabled": true,
       "remove_source_branch_after_merge": true,
       "squash_option": "default_off",
       
       "only_allow_merge_if_pipeline_succeeds": true,
       "only_allow_merge_if_all_discussions_are_resolved": true,
       
       "builds_access_level": "enabled",
       "public_jobs": false
     }' \
     "http://<URL>/api/v4/projects" | jq

If we check the results from the API call, we notice the following:

Output
{
  "id": 1372,
  "description": null,
  "name": "terraform-project",
  "name_with_namespace": "token-test / terraform-project",
  "path": "terraform-project",
  "path_with_namespace": "token-test/terraform-project",
  "created_at": "2025-01-31T21:30:48.441Z",
  "default_branch": "main",
  "tag_list": [],
  "topics": [],
  "ssh_url_to_repo": "ssh://git@<URL>:2222/token-test/terraform-project.git",
  "http_url_to_repo": "http://<URL>/token-test/terraform-project.git",
  "web_url": "http://<URL>/token-test/terraform-project",
  "readme_url": null,
  "forks_count": 0,
  "avatar_url": null,
  "star_count": 0,
  "last_activity_at": "2025-01-31T21:30:48.413Z",
  "namespace": {
    "id": 2666,
    "name": "token-test",
    "path": "token-test",
    "kind": "group",
    "full_path": "token-test",
    "parent_id": null,
    "avatar_url": null,
    "web_url": "http://<URL>/groups/token-test"
  },
  "repository_storage": "default",
  "_links": {
    "self": "http://<URL>/api/v4/projects/1372",
    "merge_requests": "http://<URL>/api/v4/projects/1372/merge_requests",
    "repo_branches": "http://<URL>/api/v4/projects/1372/repository/branches",
    "labels": "http://<URL>/api/v4/projects/1372/labels",
    "events": "http://<URL>/api/v4/projects/1372/events",
    "members": "http://<URL>/api/v4/projects/1372/members",
    "cluster_agents": "http://<URL>/api/v4/projects/1372/cluster_agents"
  },
  "packages_enabled": false,
  "empty_repo": true,
  "archived": false,
  "visibility": "private",
  "resolve_outdated_diff_discussions": false,
  "container_expiration_policy": {
    "cadence": "1d",
    "enabled": false,
    "keep_n": 10,
    "older_than": "90d",
    "name_regex": ".*",
    "name_regex_keep": null,
    "next_run_at": "2025-02-01T21:30:48.447Z"
  },
  "repository_object_format": "sha1",
  "issues_enabled": false,
  "merge_requests_enabled": true,
  "wiki_enabled": false,
  "jobs_enabled": true,
  "snippets_enabled": false,
  "container_registry_enabled": false,
  "service_desk_enabled": false,
  "can_create_merge_request_in": true,
  "issues_access_level": "disabled",
  "repository_access_level": "enabled",
  "merge_requests_access_level": "enabled",
  "forking_access_level": "disabled",
  "wiki_access_level": "disabled",
  "builds_access_level": "enabled",
  "snippets_access_level": "disabled",
  "pages_access_level": "private",
  "analytics_access_level": "enabled",
  "container_registry_access_level": "disabled",
  "security_and_compliance_access_level": "enabled",
  "releases_access_level": "enabled",
  "environments_access_level": "enabled",
  "feature_flags_access_level": "disabled",
  "infrastructure_access_level": "private",
  "monitor_access_level": "disabled",
  "model_experiments_access_level": "enabled",
  "model_registry_access_level": "enabled",
  "emails_disabled": false,
  "emails_enabled": true,
  "shared_runners_enabled": true,
  "lfs_enabled": false,
  "creator_id": 1,
  "import_url": null,
  "import_type": null,
  "import_status": "none",
  "import_error": null,
  "description_html": "",
  "updated_at": "2025-01-31T21:30:48.441Z",
  "ci_default_git_depth": 20,
  "ci_delete_pipelines_in_seconds": null,
  "ci_forward_deployment_enabled": true,
  "ci_forward_deployment_rollback_allowed": true,
  "ci_job_token_scope_enabled": false,
  "ci_separated_caches": true,
  "ci_allow_fork_pipelines_to_run_in_parent_project": true,
  "ci_id_token_sub_claim_components": [
    "project_path",
    "ref_type",
    "ref"
  ],
  "build_git_strategy": "fetch",
  "keep_latest_artifact": true,
  "restrict_user_defined_variables": true,
  "ci_pipeline_variables_minimum_override_role": "developer",
  "runners_token": "GR1348941pznzhhsweqKuXBmiXZH8",
  "runner_token_expiration_interval": null,
  "group_runners_enabled": true,
  "auto_cancel_pending_pipelines": "enabled",
  "build_timeout": 3600,
  "auto_devops_enabled": true,
  "auto_devops_deploy_strategy": "continuous",
  "ci_push_repository_for_job_token_allowed": false,
  "ci_config_path": null,
  "public_jobs": false,
  "shared_with_groups": [],
  "only_allow_merge_if_pipeline_succeeds": true,
  "allow_merge_on_skipped_pipeline": null,
  "request_access_enabled": true,
  "only_allow_merge_if_all_discussions_are_resolved": true,
  "remove_source_branch_after_merge": true,
  "printing_merge_request_link_enabled": true,
  "merge_method": "merge",
  "squash_option": "default_off",
  "enforce_auth_checks_on_uploads": true,
  "suggestion_commit_message": null,
  "merge_commit_template": null,
  "squash_commit_template": null,
  "issue_branch_template": null,
  "warn_about_potentially_unwanted_characters": true,
  "autoclose_referenced_issues": true,
  "requirements_enabled": false,
  "requirements_access_level": "enabled",
  "security_and_compliance_enabled": true,
  "compliance_frameworks": []
}

As seen in the output, the default_branch has automatically been set to main also notice that the readme_url is set to null .

Output of checks

Results of GitLab environment info

N/A

Results of GitLab application Check

N/A

Possible fixes

Investigate whether the GitLab Projects API is intentionally designed to ignore default_branch values that include a full ref. If so, the API should return a 400 Bad Request error when such values are provided, rather than silently ignoring them. Alternatively, the API could be updated to handle default_branch values that include a full ref appropriately

Edited by 🤖 GitLab Bot 🤖