简体   繁体   中英

Force branch to be rebased before it is merged and pushed

I would want to add a hook on my Gitlab server to prevent pushing merged branches on master if they was not rebased before.

For example : A---B---C---D ← master \\ E---F---G ← new-feature

I want the user to rebase his feature before merging/pushing. A---B---C---D-------------H ← master \\ / E'---F'---G'

I don't want this to be pushed A---B---C---D---H ← master \\ / E---F---G

This, is a good beginning but I don't find mean to only refuse not empty merge commits : Force Feature Branch to be Rebased Before it is Merged or Pushed

It is definitely possible, but you need to write some code. You must also decide what precisely defines a "good" commit-graph update. Your example says that a request to go from this:

o--o--o--*   <-- master

to this:

o--o--o--*---o   <-- master
    \       /
     o--o--o

is to be rejected, while this:

o--o--o--*---------o   <-- master
          \       /
           o--o--o

is to be accepted. But what about this third alternative:

o--o--o--*------o-----o   <-- master
          \    /     /
           o--o--o--o

This adds two merges rather than just one; but no new commit's merge has any parent that is an ancestor of the prior value of master .

And, what about this?

o--o--o   <-- master

(Here the push has removed the commit that used to be the tip of master .)

If the second push, that adds two merges but none of them reach back to any earlier commits, is not to be accepted, and the last push is also not to be accepted, part of your task is pretty easy: you want to allow at most one merge, perhaps restricting it to exactly two parents with one of its two parents—perhaps this must even be "the first parent"—being the prior value of master (the commit marked * ). The rest of your task is probably to allow no merges at all, as long as the proposed new master is not an ancestor of the old master (no commits are to be removed).

If the second (two-merge) push is to be accepted, the coding will be trickier. Note that if it's not to be accepted, someone can still push such a merge, they just have to do it in multiple steps (one push per merge).

The usual reason to force rebasing is a pathological hatred of merge commits because the person setting the policy doesn't see their value. It's not clear why you'd want to take the disadvantages of rebase (odds are the intermediate commits will not have been tested) but still have the merge; this seems to me like the least valuable merge commit possible. But if you must...

You imply that the merge you want to accept would be "empty"; that's not exactly true. It applies changes to its first parent (though not to its 2nd parent, since it would be a fast-forward if allowed to be).

What I think you're really saying is that you would accept a merge if the first parent is reachable (via parent pointers) from the second parent. So you could take the output of

git rev-list --merges $oldrev..$newrev

and feed each resulting commit ID as the commit-ID arguments in

git merge-base --is-ancestor commit-ID^ commit-ID^2

rejecting if the merge-base command ever returns non-zero.

(Technically I guess you might also want to make sure the commit didn't have 3 or more parents.)

That still allows something like this

     (origin/master)
            |
x -- x -- x -------------------- M <--(master)
           \                    /
            x -- x -------x -- x
                  \      /
                   x -- x

If avoiding that is a rule, it's significantly harder; you basically would want every merge to be reachable via first-parent pointers from the head commit. (But you can't just say that merges should be reachable from the head commit's first parent, because then you'd still allow

     (origin/master)
            |
x -- x -- x -------------------- M -- x<--(master)
           \                    /
            x -- x -------x -- x
                  \      /
                   x -- x

which is the same thing.) So you could maybe, as a "first step" before you start looking for merges, do a

git rev-list --first-parent $oldrev..$newrev

and hang onto a list of all the commit ID values that returns, so as you find each merge you can confirm that it's in that list.

If this all sounds like no fun at all, I couldn't agree more; which is why I'm not going to the trouble of trying to assemble a working script from this advice, and why I recommend you either allow merges or don't instead of trying to take such an unusual middle ground.

If you are still looking for this. gitlab is the only git server that implements this. they called it semi-linear history. look at the 2nd option gitlab配置

This will ENFORCE this kind of history (look at the right) seemlessly inside your merge request: 在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM