Hi,
I have forked a repository from github and imported it into our Bitbucket instance.
I edited one file and pushed to our Bitbucket repository.
I'm now trying to automate a rebase against the GitHub repository but have no knowledge at all on Groovy nor Bitbucket API. After hours of research, I have made this Custom Schedule Job inline script which does not work of course:
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.bitbucket.repository.Repository
import com.atlassian.bitbucket.repository.RepositoryService
import com.atlassian.bitbucket.repository.RefService
import com.atlassian.bitbucket.repository.Branch
import com.atlassian.bitbucket.user.ApplicationUser
import com.atlassian.bitbucket.user.UserService
import com.atlassian.bitbucket.scm.git.command.GitRebaseCommandParameters
import com.atlassian.bitbucket.scm.git.command.GitExtendedCommandFactory
def repositoryService = ComponentLocator.getComponent(RepositoryService.class)
def refService = ComponentLocator.getComponent(RefService.class)
def cmdFactory = ComponentLocator.getComponent(GitExtendedCommandFactory.class)
def userService = ComponentLocator.getComponent(UserService.class)
Repository rep = repositoryService.getBySlug("RUST", "crates.io-mirror")
Branch master = refService.getDefaultBranch(rep)
ApplicationUser user = userService.findUserByEmail("the_committer@my_company.com")
def paramsBuilder = new GitRebaseCommandParameters.Builder(master, "https://github.com/rust-lang/crates.io-index.git")
paramsBuilder.dryRun(true).committer(user)
def rebase = cmdFactory.rebase(rep, paramsBuilder.build())
rebase.call()
This gives me the following error:
2021-03-18 15:27:06,990 ERROR [c.o.s.j.AbstractCustomScheduledJob]: *************************************************************************************
2021-03-18 15:27:06,995 ERROR [c.o.s.j.AbstractCustomScheduledJob]: Script job: 'Update crates.io mirror' failed
com.atlassian.bitbucket.validation.ArgumentValidationException: The specified upstream, 'https://github.com/rust-lang/crates.io-index.git', is not valid. The upstream must be specified as a full 40-character SHA-1.
at com.atlassian.stash.internal.scm.git.DefaultGitExtendedCommandFactory.rebase(DefaultGitExtendedCommandFactory.java:236)
at com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26)
at com.sun.proxy.$Proxy438.rebase(Unknown Source)
at com.atlassian.bitbucket.scm.git.command.GitExtendedCommandFactory$rebase.call(Unknown Source)
at Script16.run(Script16.groovy:23)
at com.atlassian.stash.internal.scm.git.DefaultGitExtendedCommandFactory.rebase(DefaultGitExtendedCommandFactory.java:236)
at com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26)
at com.sun.proxy.$Proxy438.rebase(Unknown Source)
at com.atlassian.bitbucket.scm.git.command.GitExtendedCommandFactory$rebase.call(Unknown Source)
at Script16.run(Script16.groovy:23)
So the upstream parameters is a SHA-1 string, so I guess I can't refer to a repository outside Bitbucket this way.
How can I do this rebase against a public GitHub repository?
I am afraid it is not possible to rebase a branch on a different repository.
My suggestion is, instead of importing a fork and commiting new work on it, to have separate branches to use for mirroring and development.
I have managed to achieve what you want to do using Scriptrunner's 'Mirror GitHub repositories', then fetching any changes into a buffer/proxy branch where I could rebase on.
In total, my bitbucket server had 2 repositories:
This is the script that worked for me:
import com.atlassian.bitbucket.i18n.I18nService
import com.atlassian.bitbucket.repository.RepositoryBranchesRequest
import com.atlassian.bitbucket.scm.CommandOutputHandler
import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory
import com.atlassian.bitbucket.scm.git.command.fetch.GitFetchTagMode
import com.atlassian.bitbucket.util.Page
import com.atlassian.bitbucket.util.PageProvider
import com.atlassian.bitbucket.util.PageRequest
import com.atlassian.bitbucket.util.PagedIterable
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.bitbucket.repository.Repository
import com.atlassian.bitbucket.repository.RepositoryService
import com.atlassian.bitbucket.repository.RefService
import com.atlassian.bitbucket.repository.Branch
import com.atlassian.bitbucket.user.ApplicationUser
import com.atlassian.bitbucket.user.UserService
import com.atlassian.bitbucket.scm.git.command.GitRebaseCommandParameters
import com.atlassian.bitbucket.scm.git.command.GitExtendedCommandFactory
import com.atlassian.stash.internal.scm.git.command.updateref.UpdateRefCommandExitHandler
import com.atlassian.utils.process.ProcessException
import com.atlassian.utils.process.Watchdog
import com.onresolve.scriptrunner.runner.ScriptRunnerImpl
def repositoryService = ComponentLocator.getComponent(RepositoryService)
def refService = ComponentLocator.getComponent(RefService)
def userService = ComponentLocator.getComponent(UserService)
def extendedCommandFactory = ComponentLocator.getComponent(GitExtendedCommandFactory)
def i18nService = ComponentLocator.getComponent(I18nService)
// A bitbucket project that contains both repositories (mirror and dev).
String PROJECT_KEY = 'MIR'
// the repo that contains the branch to be rebased.
String TARGET_REPO_NAME = 'develop'
// The branch that you want to use for development and rebase occasionally.
String REBASED_BRANCH_NAME = 'main'
// We cannot rebase from a different repository. Will use a buffer or proxy branch, in the same repo. We will fetch any new commits from the mirror here, and rebase on this one
// This branch should be read only for other users, as we should not push other commits here
String BUFFER_BRANCH_NAME = 'mirror'
// Another repo, set to mirror a github repo
String MIRROR_REPO_NAME = 'test'
// The name of the branch in the mirror repo that we originally want to rebase on
String MIRROR_BRANCH_NAME = 'main'
/**
Overview of branches and actions
SERVER | REPO | BRANCH
-------------------------------
GitHub | MIRROR_REPO_NAME | MIRROR_BRANCH_NAME
↓ Scriptrunner will be mirroring this into bitbucket (Use 'Mirror GitHub Repositories' to set it up)
BitBucket | MIRROR_REPO_NAME | MIRROR_BRANCH_NAME
↓ This script will fetch any new changes from the mirror, into the repo that we want to rebase
BitBucket | TARGET_REPO_NAME | BUFFER_BRANCH_NAME
↓ This script will rebase REBASED_BRANCH_NAME on BUFFER_BRANCH_NAME
BitBucket | TARGET_REPO_NAME | REBASED_BRANCH_NAME
*/
class FetchOutputHandler implements CommandOutputHandler<Void> {
@Override
Void getOutput() { return null }
@Override
void process(InputStream output) throws ProcessException {}
@Override
void complete() throws ProcessException {}
@Override
void setWatchdog(Watchdog watchdog) {}
}
Branch getBranch(RefService refService, Repository repository, String branchName) {
def repositoryBranchesRequest = new RepositoryBranchesRequest.Builder(repository).build()
new PagedIterable<Branch>(new PageProvider<Branch>() {
@Override
Page<Branch> get(PageRequest pageRequest) {
refService.getBranches(repositoryBranchesRequest, pageRequest) as Page<Branch>
}
}, 1000).iterator().find { Branch it -> it.displayId == branchName }
}
Repository targetRep = repositoryService.getBySlug(PROJECT_KEY, TARGET_REPO_NAME)
assert targetRep : 'target repository not found'
Repository mirrorRep = repositoryService.getBySlug(PROJECT_KEY, MIRROR_REPO_NAME)
assert mirrorRep : "source repository not found"
Branch targetBranch = getBranch(refService, targetRep, REBASED_BRANCH_NAME)
//if you want to rebase the default branch, use the next line instead, for better performance
//Branch targetBranch = get refService.getDefaultBranch(targetRep)
log.debug("Will rebase $targetBranch.displayId ($targetBranch.id) in $targetRep.project.key / $targetRep.slug ")
Branch bufferBranch = getBranch(refService, targetRep, BUFFER_BRANCH_NAME)
assert bufferBranch : 'buffer branch not found'
log.debug("on to buffer branch $bufferBranch.displayId ($bufferBranch.id) in $targetRep.project.key / $targetRep.slug")
Branch mirrorBranch = getBranch(refService, mirrorRep, MIRROR_BRANCH_NAME)
assert mirrorBranch : 'source branch not found'
log.debug("Mirror branch is $mirrorBranch.displayId ($mirrorBranch.id) in $mirrorRep.project.key / $mirrorRep.slug")
def latestCommit = targetBranch.latestCommit
log.debug("Latest commit in target branch is $latestCommit")
def latestMirrorCommit = mirrorBranch.latestCommit
log.debug("Latest commit in mirror branch is $latestMirrorCommit")
ApplicationUser user = userService.findUserByNameOrEmail("admin")
assert user : "user not found"
ApplicationUser committer = userService.findUserByNameOrEmail("admin")
GitCommandBuilderFactory gitCommandBuilderFactory = ScriptRunnerImpl.getOsgiService(GitCommandBuilderFactory)
// Fetching any new commits from the MIRROR_BRANCH
def fetchCommand = gitCommandBuilderFactory.builder(targetRep).fetch().repository(mirrorRep)
.refspec(mirrorBranch.id)
.tags(GitFetchTagMode.NO_TAGS)
.build(new FetchOutputHandler())
fetchCommand.setTimeout(60)
fetchCommand.call()
// Updating refs to complete pulling the changes into BUFFER_BRANCH
def updateRefCommand = gitCommandBuilderFactory.builder(targetRep).updateRef()
.set(bufferBranch.id, mirrorBranch.latestCommit)
.author(user) //for reflogs
.exitHandler(new UpdateRefCommandExitHandler(i18nService, targetRep))
.oldValue(bufferBranch.latestCommit)
.build(new FetchOutputHandler())
//optionally set a timeout in seconds
updateRefCommand.setTimeout(60)
updateRefCommand.call()
// Rebasing REBASED_BRANCH on to BUFFER_BRANCH
def rebaseCommand = extendedCommandFactory.rebase(targetRep,
new GitRebaseCommandParameters.Builder(targetBranch, latestMirrorCommit)
.committer(committer)
.commitRequired(true)
.upstreamRepository(targetRep)
.build())
//optionally set a timeout in seconds
rebaseCommand.setTimeout(60)
rebaseCommand.call()
I hope this helps. I appreciate this setup might be more complicated than expected, but this was the best way I could find around the problems and limitations that I mentioned above.
Please let us know if you have any further questions or issues.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.