Skip to content

Rebase a branch in a Git repository

Replays a branch on top of another one and pushes the rewritten branch back into the repository Clarive hosts. Like Merge a branch in a Git repository, the work happens in a throwaway clone on the Clarive server, and the clone is deleted whether the rebase worked or not.

Reach for it when you want a branch to carry the latest upstream work without a merge commit in the way. The cost is that the branch is rewritten: every commit gets a new id, and the push that saves the result is a forced one.

before upstream branch rebase after upstream branch, new commit ids the branch is then pushed with force: whatever the hosted repository held for that branch is overwritten, not rejected

Fields

Repository

The repositories to work on. More than one entry is allowed, and each is cloned, rebased and pushed in turn. The picker offers variables as well as repository resources, and a variable that resolves to several ids separated by commas counts as several repositories.

Every entry costs a full clone on every run.

Blank stops the op with a missing-repository error.

Branch

The branch that gets rewritten, the source side. Its commits are the ones replayed, and it is this branch that is pushed back at the end. Expands ${var} against the stash.

Leave it blank and the op stops with a missing-value error whose wording talks about a "from" value rather than about Branch. It is this field it means.

Upstream Branch

The branch the commits are replayed on top of. Expands ${var}. It is read only, never written, so this side of the pair comes out of the op unchanged.

Blank stops the op with a missing-upstream error.

Options

Extra options handed to the rebase, as one string. Expands ${var}. The string is split on whitespace, so --rebase-merges --keep-empty arrives as two options. There is no shell and no quoting, so an option whose value contains a space cannot be expressed here. The upstream branch is always appended after whatever you type.

Leave it blank for a plain rebase, which is what most rules want.

Interactive options do not work. There is no terminal and no editor behind this op, so anything that would open one fails or hangs. If you use options that can stall, put a value in Timeout on the op's Options tab. It arms a clock before the op starts and aborts it when the time runs out. Trap timeout is no help here: it only counts down once an error has already been trapped, so a rebase that is still waiting never reaches it.

The forced push

This is the part that catches people. The rewritten branch is pushed with force, so the hosted repository takes the new history whatever it was holding before.

A push that would be rejected elsewhere goes through here. If somebody pushed to that branch, or another rule wrote to it, between the moment this op cloned and the moment it pushed, those commits are dropped from the branch without an error and without a log line. Nothing in the rule reports it.

Anyone with the branch checked out has to reset to the new history. Commit ids change even for commits the rebase did not have to modify. A tag or a topic that references an old id keeps pointing at a commit no branch reaches any more.

Run this op only on branches your rules own, and never on a branch people share, such as an integration or production branch.

Failure, ordering and rollback

Repositories are handled in picker order. A conflict during the replay stops the op on that repository with the error text and Git's output attached. The clone is discarded, so that repository keeps its original branch, while repositories earlier in the list keep the rebases they already received.

Conflicts cannot be resolved here, since there is nobody to resolve them. Any conflict is a failure.

The op returns nothing, so a Return Key on it holds an empty value. A branch already sitting on top of its upstream is rebased to the same result and pushed again, which is a success with no visible change.

Both directions are enabled on a new op, so a rebase left at its defaults runs again during a rollback pass and force-pushes a second time. It does not put the old history back. Untick Run Rollback, and take a tag on the branch beforehand if you want the old commits to stay reachable.

Combining with other ops

Tag the branch before the rebase and you have a way back, since the tag keeps the pre-rebase commits alive even though no branch points at them. Drop the tag afterwards with Delete a reference in a Git repository once you are satisfied.

Rebase then merge is the usual pair for a linear history: bring the topic branch up to date here, then run Merge a branch in a Git repository with No Fast-Forward? unchecked so the target moves forward without a merge commit.

Guard the op with IF var condition THEN so it only runs for the branches you intend, and wrap it in TRY with CATCH to turn a conflict into a message on the topic rather than a stopped job. For a repository set known only at run time, use FOREACH CI.

Examples

Bring a topic's feature branch up to date with the integration branch before it is reviewed.

Repository        ${repository}
Branch            feature/${topic_id}
Upstream Branch   develop
Options

Rebase across a list of repositories held in a variable, keeping merge commits in the replayed range.

Repository        ${project_repos}
Branch            ${branch_name}
Upstream Branch   ${production_branch}
Options           --rebase-merges