add RebaseCommitOnBranch param to UserFFBranchRequest
Goal
Squash commit without merge commit is a long desired feature. This MR tries to support it with minimal efforts on both Gitaly and Rails sides.
This MR is part of JiHu Gitlab milestone 16.2 and mofification on Rails is also planned.
Method
I propose to add a new, optional boolean field rebase_commit_on_branch
to the GRPC request of UserFFBranch
, when setting to true, accomplishing the wish of the caller, that the commit referred to by field "commit_id" is going to be rebased on the commit referred to by "branch" before the fast-forward happens.
// UserFFBranchRequest contains parameters for the UserFFBranch RPC.
message UserFFBranchRequest {
// repository is the repository for which to perform the fast-forward merge.
Repository repository = 1 [(target_repository)=true];
// user is the user which to perform the fast-forward merge as. This is used
// for authorization checks.
User user = 2;
// commit_id is the commit ID to update the branch to.
string commit_id = 3;
// branch is the name of the branch that shall be update. This must be the
// branch name only and not a fully qualified reference, e.g. "master"
// instead of "refs/heads/master".
bytes branch = 4;
// expected_old_oid is the object ID which branch is expected to point to.
// This is used as a safety guard to avoid races when branch has been
// updated meanwhile to point to a different object ID.
//
// If unset, the target branch will be overwritten regardless of its current
// state. If set, it must either contain a valid, full object ID or the
// zero object ID in case the branch should be created. Otherwise, this RPC
// will return an error.
string expected_old_oid = 5;
// If true, the commit referred to by "commit_id" is rebased on "branch" before
// the fast-forward happens. The fast-forward fails if there is a rebase failure.
bool rebase_commit_on_branch = 6;
// timestamp is the optional timestamp to use for the rebased
// commit's committer date when rebase_commit_on_branch is true.
// If it's not set, the current time will be used.
google.protobuf.Timestamp timestamp = 7;
}
In this way, Rails can implement "squash merge without merge commit" with only 2 RPC calls:
-
UserSquash
: to squash all commits in the feature branch into one, denoted assquashed commit
; -
UserFFBranch
: rebasesquashed commit
on the target branch(usually, main or master) via our newly added fieldrebase_commit_on_branch
and fast-forward the pointer of the target branch to the rebased commit.
Benefits
UserSquash
, UserRebaseConfirmable
and UserFFBranch
Compare with: Rails calling UserRebaseConfirmable
works with branches instead of commit so that Rails has to create a temporary branch on squashed commit
and cleans it up after UserFFBranch
, which results in 5 RPC calls at least and hard-to-clean-up senario when one of the RPC encounters error.
Future consideration: implementing "rebase merge without merge commit"
rebase merge without merge commit is also a desired feature potentially worth implementing in the future and the updated UserFFBranch
in this MR already support it at Gitaly side.
Demo
Demo repo: git-merge.zip
The branches and commits of the demo repo look like:
gitGraph
commit id: "root"
commit id: "m-1"
branch feature
commit id: "f-1"
commit id: "f-2"
commit id: "f-3"
checkout main
commit id: "m-2"
commit id: "m-3"
I wrote a Go example, calling Gitaly mocking the way of Rails: https://jihulab.com/nanmu42/call-gitaly/-/blob/43e24e4e963768eeea1263de56890701a5ecf0c0/SquashMergeWithoutMergeCommit/main.go
After squash merge without merge commit, the commits on main branch looks like: