Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-69481

Multibranch Git changelog shows all history when using several checkout / git steps

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Minor Minor
    • git-plugin
    • None

      When using Pipeline Multibranch with a Pipeline script that checkout the branch scm and also a different branch of the same repo later on, the next build changelog contains all commit history (bounded to a maximum).

      Here is a sample scenario:

      • Use a Jenkinsfile like the following (you may use the declarative default checkout or not):
      // With declarative default checkout
      pipeline {
          
          agent any
        
          stages {
              stage('Other Checkout') {
                steps {
                  checkout([$class: 'GitSCM', 
                    branches: [[name: '*/otherBranch']], 
                    userRemoteConfigs: [[credentialsId: 'myCreds', name: 'origin', refspec: '+refs/heads/otherBranch:refs/remotes/origin/otherBranch', url: env.GIT_URL]]
                  ])
                }
              }
          }
      }
      
      // With explicit checkout scm
      // pipeline {
          
      //     agent any
        
      //     options {
      //       skipDefaultCheckout()
      //     }
        
      //     stages {
      //         stage('Checkout') {
      //           steps {
      //             checkout scm
      //           }
      //         }
      //         stage('Other Checkout') {
      //           steps {
      
      //             // Must be the same repository
      //             checkout([$class: 'GitSCM', 
      //               branches: [[name: '*/otherBranch']], 
      //               userRemoteConfigs: [[credentialsId: 'myCreds', name: 'origin', refspec: '+refs/heads/otherBranch:refs/remotes/origin/otherBranch', url: 'https://github.example.com/sameOwner/sameOwner.git']]
      //             ])
                  
      //             // Same problem if using git()
      //             // git branch: 'otherBranch', credentialsId: 'myCreds', url: 'https://github.example.com/sameOwner/sameOwner.git'
      //           }
      //         }
      //     }
      // }
      
      • Create a Multibranch for this
      • The first build shows no changelog (expected)
      • The next builds (whether there is a change or not) shows the entire build history in the change log

      Evidence

      While collecting some evidence, I noticed that the git whatchanged command that is launched to calculate the changelog of the main branch (the one from checkout scm) refers to a commit of the other branch that is checked out.

      Looking through the code, I narrowed this down to the SpecificRevisionBuildChooser that just returns the last recorded git build data when asked for the previous build revision of a branch.

      The GitSCM#computeChangeLog kind of assume that the revision returned by the chooser is valid if found in the repo.

      In the scenario shown above, the last git build data is from a different branch. While it exists in the repo apparently, it is from a different branch. In the console output we see something like this where 4e1243bd22c66e76c2ba9eddc1f91394e57f9f83 is from the branch of the checkout scm and d185a06ad42d21210864bb1abddf60289dfc59de is from the checkout of the otherBranch from the last build. Not even in the history of branch of interest...:

      > git checkout -f 4e1243bd22c66e76c2ba9eddc1f91394e57f9f83 # timeout=10
       > git branch -a -v --no-abbrev # timeout=10
       > git checkout -b master 4e1243bd22c66e76c2ba9eddc1f91394e57f9f83 # timeout=10
      Commit message: "[test] update"
       > git rev-list --no-walk d185a06ad42d21210864bb1abddf60289dfc59de # timeout=10
      

      Workaround

      A workaround might be to not rely on git / checkout step for the checkout of other branches. For example:

      def withGitCredentials(String credentialsId, Closure body) {
        withCredentials([usernamePassword(credentialsId: credentialsId, passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
            // Must be inside a git directory otherwise this would fail
            sh 'git config --local credential.helper \'!f() { [ -z \"$GIT_ASKPASS\" ] && printf \"username=$GIT_USERNAME\npassword=$GIT_PASSWORD\"; }; f\''
            try {
              body()
            } finally {
              sh "git config --local --unset credential.helper"
            }
        }
      }
      
      pipeline {
          
          agent any
        
          stages {
              stage('Other Checkout') {
                steps {
                  withGitCredentials('myCreds') {
                    sh '''
                      git fetch --force --progress -- $GIT_URL +refs/heads/otherBranch:refs/remotes/origin/otherBranch
                      git checkout otherBranch
                    '''
                  }
                }
              }
          }
      }
      

            Unassigned Unassigned
            allan_burdajewicz Allan BURDAJEWICZ
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: