threads / discuss / 16597

pre-rebase safety hook

Subject: pre-rebase safety hook

## tl;dr

3 messages between Dec 4, 2008 and Dec 4, 2008.

replies: 2people: 2as markdown or json

Tim Harper· Dec 4, 2008, 17:58 UTC · lore

Is anyone aware of a pre-rebase hook script that will prevent (or at least warn) you from letting a rebase rewrite a commit that has been pushed or merged into any branch except it's own?

I've activated the pre-rebase.sample, and it does seem to give me any warnings at all:

Here's the terminal output demonstrating what I mean:
http://pastie.org/331082
Thanks!
Tim
Junio C Hamano· Dec 4, 2008, 19:35 UTC · re: Tim Harper · lore

Re: pre-rebase safety hook

"Tim Harper" <timcharper@gmail.com> writes:
Show 10 quoted lines
> Is anyone aware of a pre-rebase hook script that will prevent (or at
> least warn) you from letting a rebase rewrite a commit that has been
> pushed or merged into any branch except it's own?
>
> I've activated the pre-rebase.sample, and it does seem to give me any
> warnings at all:
>
> Here's the terminal output demonstrating what I mean:
>
> http://pastie.org/331082
The example script starts like this:
    #!/bin/sh
    # ...
    # This sample shows how to prevent topic branches that are already
    # merged to 'next' branch from getting rebased, because allowing it
    # would result in rebasing already published history.
As it says, it explicitly checks against 'next'.

The sample was written way before "branch --with" feature was added, and I suspect what it does can be expressed more concisely in modern git.

If you want to prevent a branch whose tip commit is on more than one branches from being rebased, I think something like this would suffice.

    #!/bin/sh
    LF='
    '
    in_branches=$(git branch -a --with "${2-HEAD}")
    case "$in_branches" in
    *"$LF"*)
	: this commit is on more than two branches
        exit 1
        ;;
    esac
    exit 0
But I didn't test it.
Junio C Hamano· Dec 4, 2008, 22:29 UTC · re: Junio C Hamano · lore

Re: pre-rebase safety hook

Junio C Hamano <gitster@pobox.com> writes:
Show 16 quoted lines
> If you want to prevent a branch whose tip commit is on more than one
> branches from being rebased, I think something like this would suffice.
>
>     #!/bin/sh
>     LF='
>     '
>     in_branches=$(git branch -a --with "${2-HEAD}")
>     case "$in_branches" in
>     *"$LF"*)
> 	: this commit is on more than two branches
>         exit 1
>         ;;
>     esac
>     exit 0
>
> But I didn't test it.

Actually, the above cannot possibly be right. To decide whether to allow rebasing of a branch or not, you need to also give it from which commit the rebase will rewrite.

For example, suppose you have a branch "topic", that was forked from "master" and built two commits, then another branch "side" was forked from that, and you have three more commits on "topic" since then:

               o "side"
              /  
         A---B---C---D---E "topic"
        /
    ---o---o---o---o "master"

Now, can I allow you to rebase "topic"? It depends. These should be allowed:

	git rebase B "topic"
	git rebase C "topic"
	git rebase D "topic"

but rebasing "topic" on top of "master", or anything that changes the fact that "topic" contains commits A and B, should be prohibited, because it will interfere with "side". For example,

	git rebase A "topic"
would make this history:
           B---o "side"
          /
         A---B'--C'--D'--E' "topic"
        /
    ---o---o---o---o "master"
where B' and B are different commits.

So you need to check all the commits that will be affected by the rebase to see if any of them is on a branch other than the one that is being rebased. The set of commits that needs to be checked are:

        git rev-list "$1..${2-HEAD}"

so a naive implementation that is based on brnach --with would probably look like:

	#!/bin/sh
	: allow rebasing a detached HEAD
	git symbolic-ref -q HEAD || exit 0
        LF='
        '
        git rev-list "$1..${2-HEAD}" |
        while read commit
        do
        	case "$(git branch -a --with $commit)" in
                *"$LF"*)
                	: this is on two or more branches
                        exit 1
                        ;;
		esac
	done

← back to recent threads