Gradle-release-plugin 更新被拒绝,因为你当前分支的提示落后了

Gradle-release-plugin Updates were rejected because the tip of your current branch is behind

在我的工作场所,我们使用多个开发人员共享的月度发布分支。

Gradle 版本为 2.14.1

我们使用 Jenkins 手动触发代码构建和发布(任务)(有效 运行 - gradle clean compileJava release)

整个过程大约需要30-40分钟,主要是编译,生成工件,运行Junit测试,上传工件到Artifactory。

终于到了打标签推送版本号的步骤了: preTagCommit,它尝试更新 gradle.properties 并增加版本号并提交和推送。

此时,如果在过去的 30-40 分钟内分支上没有任何提交(因为构建是手动触发的),则发布成功。

在整个过程之间甚至有一个提交的那一刻它失败并出现错误: 任务“:preTagCommit”执行失败。

*
error: failed to push some refs to 'xxx.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
*

我尝试了几种 hack 并搜索了文档,但还没有找到任何合适的解决方案。

我的发布步骤是这样的:

***
    release {
        project.setProperty("gradle.release.useAutomaticVersion", "true");
        newVersionCommitMessage = "Re-snapshoted project to version "
        preTagCommitMessage = "Preparing version for release "
        tagCommitMessage = "Tagging Release "
        tagPrefix = "calypso"
        requireBranch = ""
        // Sometimes the plugin trips over its own feet with modifying the gradle.properties
        // then complaining it has changed.
        failOnCommitNeeded = false
        pushToCurrentBranch = true
    }
***

抱歉,如果之前有人问过这个问题,我找到的所有解决方案都是 git 的一般方法,而不是来自使用 Gradle-Release-Plugin 和 git 的人。

如有任何回复,我们将不胜感激。

我相信插件的 GitHub page 中提到了您要查找的标志:

Eg. To ignore upstream changes, change 'failOnUpdateNeeded' to false:

release {
  failOnUpdateNeeded = false
}

编辑

由于某种原因,上面的标志似乎不起作用。然而,还有另一种方法可以实现这一点,那就是在 release 扩展的 git 选项中添加一个强制推送选项(说起来多嘴)。

release {
  git {
    pushOptions = ['--force']
  }
}

这基本上会强制覆盖分支(请参阅下面的问题分析),除非托管存储库的服务器已调整为拒绝此类强制推送。在那种情况下,您实际上没有可以通过此处提供帮助的选项。


问题分析

正如我在下面的最后一条评论中所说,失败的部分是扩展 运行 执行 preTagCommit 任务,然后尝试将此提交推送到上游分支(master 例如)。可以看到这个推送命令对here.

当然,如果其他人已经将提交推送到该分支,那么这当然会失败。

如果插件作者让我们能够说

,那么解决这个问题的更简单方法就是

不要将 preTag 提交推送到上游

将 preTagCommits 推送到名为 foo

的分支

不幸的是,他们没有给我们这个选项(至少从我目前收集到的信息来看)。所以我想出了一些 hacky 解决方案来规避这个问题。

Solutions/Hacks

指定 pushToBranchPrefix 创建标记分支

这是另一个可以传递给 git 对象的 option当前分支:

例如:

afterReleaseBuild {
  doFirst {
    project.exec {
        executable = 'git'
        args 'checkout', '-b', 'v1.x.x@master'
    }
  }
}

v1.x.x 替换为与当前版本的标签匹配的内容。

这样做的目的是,每当插件尝试 push/commit 时,它都会 push/commit 到我们创建的这个分支,并最终从这个分支创建标签。

我认为这可能是最好的选择

禁用 preTagCommit 任务

由于 preTagCommit 任务失败了,我们可以禁用该任务,它不会再 运行:

tasks.named('preTagCommit') {
    enabled = false
}

自己决定是否可以接受。

动态推送行为

因为我看过源代码,所以我可以 see 它推送的原因是它看到 git.pushToRemote 不为空。知道了这一点,我们就可以动态地控制这种行为。

tasks.named('preTagCommit') {
  def pushToRemote = null
  doFirst {
    project.extensions.configure(net.researchgate.release.ReleaseExtension) {
      pushToRemote = it.git.pushToRemote
      it.git.pushToRemote = null
    }
  }

  doLast {
    project.extensions.configure(net.researchgate.release.ReleaseExtension) {
      it.git.pushToRemote = pushToRemote
    }
  }
}

这样,在 preTagCommit 中,它不会推送任何内容,但会成功执行其他所有操作。

针对我的情况,最简单的解决方法是在 build.gradle:

中简单地修改我的发布插件组件
***
release {
....
 pushToRemote = ''
....
}
***

这确保发布插件不会推送!

控制权来自 gradle 版本后,我跟进了:

Git拉原点

Git push --tags origin

这很好地同步了我的提交,也遵循了先拉后推的规则!

尽管这是一个 hacky 解决方案,@Clijsters 建议我使用特殊发布分支来解决这个问题。