Skip to content

[JENKINS-73240] git-plugin falls back to hard-coded master branch #3840

@jenkins-infra-bot

Description

@jenkins-infra-bot

Originally raised in Gitter: https://matrix.to/#/!ouJVNKRtaWHFflDvBW:gitter.im/$ric5V0euwF0jSREIWwLmZVRK_-h1ZleJDy7BpPcJfwY?via=gitter.im&via=matrix.org&via=minds.com

 

Cheers, got a bit of a problem with the git-plugin, and wondering if my expectations are against some grand design or just... coincidence/circumstance?

Namely: when in command line I go git clone SOME_URL I get the default branch as configured in the SCM platform provider (gitlab, github, etc.) for that repository (master for some, main for others, trunk or staging on yet others).

In Jenkins, if I use a checkout step with an empty or absent list of branches, I expect to get such a default branch whichever way that particular project defines one, however it stumbles on checkout of repos whose default is not master (noted on a project that lacks one altogether). I think I've tracked this down to https://github.com/jenkinsci/git-plugin/blob/master/src/main/java/hudson/plugins/git/GitSCM.java#L214-L220 or possibly https://github.com/jenkinsci/git-plugin/blob/master/src/main/java/jenkins/plugins/git/GitStep.java#L53

Relevant part of the job's log looks like this: 

    Line  7:  > git --version # timeout=10
    Line 11:  > git --version # 'git version 2.39.3'
    Line 23:  > git fetch --tags --force --progress -- git@​gitlab.local:tests/domain-it.git +refs/heads/*:refs/remotes/origin/* # timeout=40
    Line 27:  > git config remote.origin.url git@​gitlab.local:tests/domain-it.git # timeout=10
    Line 31:  > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
    Line 35:  > git rev-parse refs/remotes/origin/master^{commit} # timeout=10
    Line 39:  > git rev-parse origin/master^{commit} # timeout=10

Executing git in workdir /home/jenkins-worker/jenkins/workspace/release-candidate-test/domain-it
Couldn't find any revision to build. Verify the repository and branch configuration for this job.

The latter message is certainly from this plugin:

throw new AbortException("Couldn't find any revision to build. Verify the repository and branch configuration for this job.");

The repository does appear in the filesystem, but not the workspace. It claims to be checked out into a "master" branch that has no commits yet; the remotes/origin/main is known but not used: 

[jenkins-worker@​jenkins domain-it]$ ls -la
total 4
drwxrwxr-x.  3 jenkins-worker jenkins-worker   18 May 29 09:49 .
drwxrwxr-x. 17 jenkins-worker jenkins-worker 4096 May 29 09:49 ..
drwxrwxr-x.  8 jenkins-worker jenkins-worker  149 May 29 15:16 .git

[jenkins-worker@​jenkins domain-it]$ cat .git/HEAD
ref: refs/heads/master

[jenkins-worker@​jenkins domain-it]$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

[jenkins-worker@​jenkins domain-it]$ git log
fatal: your current branch 'master' does not have any commits yet

[jenkins-worker@​jenkins domain-it]$ git log main
fatal: ambiguous argument 'main': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git  [...] -- [...]'

[jenkins-worker@​jenkins domain-it]$ git branch -a
  remotes/origin/main

 

So, the question is if requiring a specific source specifier like "master" in Jenkins jobs is by grand design of some development theory, or just happens to be so and can be changed (e.g. if it is reasonable to trust the SCM server that it knows better; maybe only fall back to trying "master" and possibly other names if an anonymous clone is not successful).

Originally in the discussion I was not sure what the git query for that would be, seeing how the plugin does first fetch the index and then locally checks out something. Later investigation was fruitful:

While there are many queries working with a local index of already-fetched information (and the bit we need is not even fetched originally), modern git (by some accounts, like 2.8.0+ from 3 years ago) allows remote queries for this even when there is no local repository yet.

 

As suggested in https://stackoverflow.com/questions/64904364/how-can-i-ask-git-for-the-name-of-a-repositorys-default-branch git ls-remote --symref ... can report what the remote repo thinks of its HEAD value (as an un-expanded symbolic reference if it is one); the example below uses a named origin from an initialized repo, but an URL can also be used: 

[jenkins-worker@​jenkins domain-it]$ git ls-remote --symref origin HEAD
ref: refs/heads/main    HEAD
b8185f414758ae5b57796b58342d2d4bd29a39c2HEAD 

 

For an initialized repository some more operations are feasible:

  • we can auto-set the remote HEAD tracking as a named branch (and change it later), which allows us to directly rely more on git facilities, at least since "some version", and code less of git in the plugin: 
    [jenkins-worker@​jenkins domain-it]$ git branch -a
      remotes/origin/main
    
    [jenkins-worker@​jenkins domain-it]$ git remote set-head origin --auto
    origin/HEAD set to main
    
    [jenkins-worker@​jenkins domain-it]$ git branch -a
      remotes/origin/HEAD -> origin/main
      remotes/origin/main
    

     

  • Also there is git remote show that can be parsed (to find that main branch), at least as a possible fallback: 
    [jenkins-worker@​jenkins domain-it]$ git branch -a
      remotes/origin/main
    
    [jenkins-worker@​jenkins domain-it]$ git remote show origin
    * remote origin
      Fetch URL: git@​gitlab.local:tests/domain-it.git
      Push  URL: git@​gitlab.local:tests/domain-it.git
      HEAD branch: main
      Remote branch:
        main tracked

     

For comparison, here's the info available (and not) in the local repository prepared by Jenkins with the current plugin: a remotename/HEAD at least specifies the commit on whatever the default branch is at; probably known branches can be interpolated so if there is one (exactly?) at that commit, that is the one to check out. Not sure what to do with such heuristic if several branches happen to be at that same commit.

The expected tip of main branch is the same commit as resolved remotely above; there is no locally known branch (or a commit on one): 

[jenkins-worker@​jenkins domain-it]$ cat .git/refs/remotes/origin/main
b8185f414758ae5b57796b58342d2d4bd29a39c2

[jenkins-worker@​jenkins domain-it]$ cat .git/refs/heads/^C
### Nothing here, in locally known refs/heads

Trying various query syntaxes:

[jenkins-worker@​jenkins domain-it]$ git ls-remote origin HEAD
b8185f414758ae5b57796b58342d2d4bd29a39c2HEAD

[jenkins-worker@​jenkins domain-it]$ git ls-remote origin -- HEAD
b8185f414758ae5b57796b58342d2d4bd29a39c2HEAD

[jenkins-worker@​jenkins domain-it]$ git ls-remote origin
b8185f414758ae5b57796b58342d2d4bd29a39c2HEAD
b8185f414758ae5b57796b58342d2d4bd29a39c2refs/heads/main
9a518ecf007a10893b67f5529f84c1fc4647d338refs/tags/RELEASE-domain-it-1.0.0
205ca7c24f42b6fceba2b7b8fa5db35f25642741refs/tags/RELEASE-domain-it-1.0.0^{}
...

Note that we can not just look in the local repo index, as it lacks the needed object after the git init + git fetch perpetrated by the Jenkins git-plugin (log in original post): 

[jenkins-worker@​jenkins domain-it]$ git symbolic-ref refs/remotes/origin/HEAD
fatal: ref refs/remotes/origin/HEAD is not a symbolic ref

FWIW, added such logic (to try detecting the branch name for a remote/HEAD) in a local JSL which calls the checkout step and it seems to work reasonably.

 
So... I've surprisingly learned a lot today. There are technical ways to use the Git server's current (even updatable!) definition of a default branch, instead of hard-coding master as the first and only fall-back.

Are there any reasons (that I'd be unaware of) to forbid such a change in the plugin?


Originally reported by jimklimov, imported from: git-plugin falls back to hard-coded `master` branch
  • status: Open
  • priority: Minor
  • component(s): git-plugin
  • resolution: Unresolved
  • votes: 0
  • watchers: 2
  • imported: 2025-12-02
Raw content of original issue

Originally raised in Gitter: https://matrix.to/#/!ouJVNKRtaWHFflDvBW:gitter.im/$ric5V0euwF0jSREIWwLmZVRK_-h1ZleJDy7BpPcJfwY?via=gitter.im&via=matrix.org&via=minds.com

 

Cheers, got a bit of a problem with the git-plugin, and wondering if my expectations are against some grand design or just... coincidence/circumstance?

Namely: when in command line I go git clone SOME_URL I get the default branch as configured in the SCM platform provider (gitlab, github, etc.) for that repository (master for some, main for others, trunk or staging on yet others).

In Jenkins, if I use a checkout step with an empty or absent list of branches, I expect to get such a default branch whichever way that particular project defines one, however it stumbles on checkout of repos whose default is not master (noted on a project that lacks one altogether). I think I've tracked this down to https://github.com/jenkinsci/git-plugin/blob/master/src/main/java/hudson/plugins/git/GitSCM.java#L214-L220 or possibly https://github.com/jenkinsci/git-plugin/blob/master/src/main/java/jenkins/plugins/git/GitStep.java#L53

Relevant part of the job's log looks like this: 

    Line  7:  > git --version # timeout=10
    Line 11:  > git --version # 'git version 2.39.3'
    Line 23:  > git fetch --tags --force --progress -- [email protected]:tests/domain-it.git +refs/heads/*:refs/remotes/origin/* # timeout=40
    Line 27:  > git config remote.origin.url [email protected]:tests/domain-it.git # timeout=10
    Line 31:  > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
    Line 35:  > git rev-parse refs/remotes/origin/master^{commit} # timeout=10
    Line 39:  > git rev-parse origin/master^{commit} # timeout=10

Executing git in workdir /home/jenkins-worker/jenkins/workspace/release-candidate-test/domain-it
Couldn't find any revision to build. Verify the repository and branch configuration for this job.

The latter message is certainly from this plugin:

throw new AbortException("Couldn't find any revision to build. Verify the repository and branch configuration for this job.");

The repository does appear in the filesystem, but not the workspace. It claims to be checked out into a "master" branch that has no commits yet; the remotes/origin/main is known but not used: 

[jenkins-worker@jenkins domain-it]$ ls -la
total 4
drwxrwxr-x.  3 jenkins-worker jenkins-worker   18 May 29 09:49 .
drwxrwxr-x. 17 jenkins-worker jenkins-worker 4096 May 29 09:49 ..
drwxrwxr-x.  8 jenkins-worker jenkins-worker  149 May 29 15:16 .git

[jenkins-worker@jenkins domain-it]$ cat .git/HEAD
ref: refs/heads/master

[jenkins-worker@jenkins domain-it]$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

[jenkins-worker@jenkins domain-it]$ git log
fatal: your current branch 'master' does not have any commits yet

[jenkins-worker@jenkins domain-it]$ git log main
fatal: ambiguous argument 'main': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

[jenkins-worker@jenkins domain-it]$ git branch -a
  remotes/origin/main

 

So, the question is if requiring a specific source specifier like "master" in Jenkins jobs is by grand design of some development theory, or just happens to be so and can be changed (e.g. if it is reasonable to trust the SCM server that it knows better; maybe only fall back to trying "master" and possibly other names if an anonymous clone is not successful).

Originally in the discussion I was not sure what the git query for that would be, seeing how the plugin does first fetch the index and then locally checks out something. Later investigation was fruitful:

While there are many queries working with a local index of already-fetched information (and the bit we need is not even fetched originally), modern git (by some accounts, like 2.8.0+ from 3 years ago) allows remote queries for this even when there is no local repository yet.

 

As suggested in https://stackoverflow.com/questions/64904364/how-can-i-ask-git-for-the-name-of-a-repositorys-default-branch git ls-remote --symref ... can report what the remote repo thinks of its HEAD value (as an un-expanded symbolic reference if it is one); the example below uses a named origin from an initialized repo, but an URL can also be used: 

[jenkins-worker@jenkins domain-it]$ git ls-remote --symref origin HEAD
ref: refs/heads/main    HEAD
b8185f414758ae5b57796b58342d2d4bd29a39c2        HEAD 

 

For an initialized repository some more operations are feasible:

  • we can auto-set the remote HEAD tracking as a named branch (and change it later), which allows us to directly rely more on git facilities, at least since "some version", and code less of git in the plugin: 
    [jenkins-worker@jenkins domain-it]$ git branch -a
      remotes/origin/main
    

    [jenkins-worker@jenkins domain-it]$ git remote set-head origin --auto
    origin/HEAD set to main

    [jenkins-worker@jenkins domain-it]$ git branch -a
    remotes/origin/HEAD -> origin/main
    remotes/origin/main

     

  • Also there is git remote show that can be parsed (to find that main branch), at least as a possible fallback: 
    [jenkins-worker@jenkins domain-it]$ git branch -a
      remotes/origin/main
    

    [jenkins-worker@jenkins domain-it]$ git remote show origin

 

For comparison, here's the info available (and not) in the local repository prepared by Jenkins with the current plugin: a remotename/HEAD at least specifies the commit on whatever the default branch is at; probably known branches can be interpolated so if there is one (exactly?) at that commit, that is the one to check out. Not sure what to do with such heuristic if several branches happen to be at that same commit.

The expected tip of main branch is the same commit as resolved remotely above; there is no locally known branch (or a commit on one): 

[jenkins-worker@jenkins domain-it]$ cat .git/refs/remotes/origin/main
b8185f414758ae5b57796b58342d2d4bd29a39c2

[jenkins-worker@jenkins domain-it]$ cat .git/refs/heads/^C

Nothing here, in locally known refs/heads

Trying various query syntaxes:

[jenkins-worker@jenkins domain-it]$ git ls-remote origin HEAD
b8185f414758ae5b57796b58342d2d4bd29a39c2        HEAD

[jenkins-worker@jenkins domain-it]$ git ls-remote origin -- HEAD
b8185f414758ae5b57796b58342d2d4bd29a39c2 HEAD

[jenkins-worker@jenkins domain-it]$ git ls-remote origin
b8185f414758ae5b57796b58342d2d4bd29a39c2 HEAD
b8185f414758ae5b57796b58342d2d4bd29a39c2 refs/heads/main
9a518ecf007a10893b67f5529f84c1fc4647d338 refs/tags/RELEASE-domain-it-1.0.0
205ca7c24f42b6fceba2b7b8fa5db35f25642741 refs/tags/RELEASE-domain-it-1.0.0^{}
...

Note that we can not just look in the local repo index, as it lacks the needed object after the git init + git fetch perpetrated by the Jenkins git-plugin (log in original post): 

[jenkins-worker@jenkins domain-it]$ git symbolic-ref refs/remotes/origin/HEAD
fatal: ref refs/remotes/origin/HEAD is not a symbolic ref

FWIW, added such logic (to try detecting the branch name for a remote/HEAD) in a local JSL which calls the checkout step and it seems to work reasonably.

  So... I've surprisingly learned a lot today. There are technical ways to use the Git server's current (even updatable!) definition of a default branch, instead of hard-coding master as the first and only fall-back.

Are there any reasons (that I'd be unaware of) to forbid such a change in the plugin?

environment
Jenkins LTS 2.452.1<br/>
git-plugin 5.2.2

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions