It's unlikely, but warn the user to update first before telling them they need to...
[git-central.git] / server / update-prefer-rebase
1 #!/bin/sh
2 #
3 # Watches for merges that had only local merges (e.g. should have been rebases).
4 #
5 # If they are introducing non-merge commits /and/ merge commits, it could
6 # look like one of two ways. This way:
7 #
8 # A -- B             <-- origin/topic
9 #  \    \
10 #   c -- d           <-- topic
11 #
12 # They basically had an un-shared local dev branch (probably by making a
13 # merge) and instead should have done a rebase. Also, if they did:
14 #
15 # A -- B             <-- origin/topic
16 #  \    \
17 #   c -- d -- e      <-- topic
18 #
19 # We should try and catch them--where the merge happened previously to
20 # them doing more work in the newrev commit.
21 #
22 # But if it looks like:
23 #
24 # A -- B             <-- origin/foo
25 #  \    \
26 #   C    \           <-- origin/topic
27 #    \    \
28 #     d -- e         <-- topic
29 #
30 # Then they had a pre-shared branch that cannot be rebased and so they
31 # were correct in doing a merge to tie "old" and "oldrev" together.
32 #
33 # Also, we obviously have to be okay with:
34 #
35 # A -- B            <-- origin/topic
36 #       \
37 #        c -- d     <-- topic
38
39 . $(dirname $0)/functions
40
41 refname="$1"
42 oldrev="$2"
43 newrev="$3"
44
45 if expr "$oldrev" : '0*$' >/dev/null ; then
46         exit 0
47 fi
48
49 # Read backwards: all commits from old..new, unless they are already referenced by a branch
50 git rev-parse --not --branches | git rev-list --stdin $oldrev..$newrev | while read commit ; do
51         number_of_parents=$(git rev-list -n 1 --parents $commit | sed 's/ /\n/g' | grep -v $commit | wc -l)
52         if [[ $number_of_parents > 1 ]] ; then
53                 # Hack to not interfer with the update-stable "--no-ff" method
54                 if [[ "$refname" == "refs/heads/stable" && "$commit" == "$newrev" ]] ; then
55                         exit 0
56                 fi
57
58                 # Find the original branch point (B)
59                 parents=$(git rev-list -n 1 --parents $commit | sed 's/ /\n/g' | grep -v $commit)
60
61                 # For each baserev
62                 git merge-base --all $parents | while read baserev ; do
63                         # For each parent
64                         git rev-list -n 1 --parents $commit | sed 's/ /\n/g' | grep -v $commit | while read parent ; do
65                                 all_commits=$(git rev-list --first-parent $baserev..$parent | wc -l)
66                                 new_commits=$(git rev-parse --not --branches | git rev-list --stdin $baserev..$parent | wc -l)
67                                 if [ $all_commits -eq $new_commits ] ; then
68                                         # echo "parent=$parent" # echo "all_commits=$all_commits" # echo "new_commits=$new_commits"
69                                         display_error_message "It looks like you should rebase instead of merging $commit"
70                                         exit 1
71                                 fi
72                         done
73                         if [ $? -ne 0 ] ; then
74                                 exit 1
75                         fi
76                 done
77                 if [ $? -ne 0 ] ; then
78                         exit 1
79                 fi
80         fi
81 done
82