-
Bug
-
Resolution: Unresolved
-
Minor
-
None
-
Jenkins LTS 2.452.1
git-plugin 5.2.2
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: https://github.com/jenkinsci/git-plugin/blob/87afc1cbf39df74c7b4378dc0320bc8f94b1465a/src/main/java/hudson/plugins/git/GitSCM.java#L1155
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 * 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 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?