Search code examples
gitjenkinscontinuous-integration

Why does Jenkins allow specifying multiple git branches?


Multiple times now I have stumbled over why does Jenkins support multiple "branches to build", considering that the help text does not recommend it, what would be a valid use-case?

multiple branches in jenkins git scm configuration

Edit: I am referring to why there is the "Add branch" button in a single Job, not Multi-Branch. multiple branches in jenkins git scm configuration


Solution

  • I cannot explain what a use-case would be, but from reading the source code there seems to be an "advanced use-case" in the git plugin which selects a single branch* (ref) and schedules a new build for the other ones.

    It must be noted that scheduling new builds is limited to jobs whose implementation inherits from AbstractProject, like Free-Style Jobs. Note well that Pipeline Jobs are not AbstractProjects!

    * There are "build choosers" available which order or limit the matched branches/refs.

    Selection code excerpt:

        /**
         * If the configuration is such that we are tracking just one branch of one repository
         * return that branch specifier (in the form of something like "origin/master" or a SHA1-hash
         *
         * Otherwise return {@code null}.
         */
        @CheckForNull
        private String getSingleBranch(EnvVars env) {
            // if we have multiple branches skip to advanced usecase
            if (getBranches().size() != 1) {
                return null;
            }
    
            // [...]
        }
    
        // [...]
    
        /**
         * Determines the commit to be built in this round [...]
         */
        private @NonNull Build determineRevisionToBuild(/* ... */) {
    
            // [...]
        
            if (candidates.isEmpty() ) {
                final String singleBranch = environment.expand( getSingleBranch(environment) );
    
    
                candidates = getBuildChooser().getCandidateRevisions(
                        false, singleBranch, git, listener, buildData, context);
            }
    
    
            if (candidates.isEmpty()) {
                // getBuildCandidates should make the last item the last build, so a re-build
                // will build the last built thing.
                throw new AbortException("Couldn't find any revision to build. Verify the repository and branch configuration for this job.");
            }
    
    
            Revision marked = candidates.iterator().next();
    

    (source)

    scheduling new build:

            if (candidates.size() > 1) {
                log.println("Multiple candidate revisions");
                if (checkForMultipleRevisions) {
                    Job<?, ?> job = build.getParent();
                    if (job instanceof AbstractProject) {
                        AbstractProject project = (AbstractProject) job;
                        if (!project.isDisabled()) {
                            log.println("Scheduling another build to catch up with " + project.getFullDisplayName());
                            if (!project.scheduleBuild(0, new SCMTrigger.SCMTriggerCause("This build was triggered by build "
                                    + build.getNumber() + " because more than one build candidate was found."))) {
                                log.println("WARNING: multiple candidate revisions, but unable to schedule build of " + project.getFullDisplayName());
                            }
                        }
                    }
                }
            }
    

    (source)