]> git.ipfire.org Git - thirdparty/git.git/blobdiff - Reintegrate
What's cooking (2024/05 #07)
[thirdparty/git.git] / Reintegrate
index 6a86314006cb6d3552ead63dd74274ee0ce07c59..63340a9db583a70015683d40c1fe8dc15b9ed26e 100755 (executable)
@@ -1,10 +1,16 @@
 #!/bin/sh
 
-accept_rerere=t generate=no update= diff=
+accept_rerere="--rerere-autoupdate"
+generate=no
+exec=:
+update= diff= edit= stop_at_cut= skip_cocci= force_cocci= no_cocci=
 while case "$#,$1" in 0,*) break;; *,-*) ;; esac
 do
        case "$1" in
        -n)     accept_rerere= ;;
+       -e)     edit=t ;;
+       -c)     stop_at_cut=1 ;;
+       -c?*)   stop_at_cut=${1#-c} ;;
        -d)     update=${2?"diff with what?"}
                diff=yes
                generate=yes
@@ -12,39 +18,258 @@ do
        -u)     update=${2?"update what?"}
                generate=yes
                shift ;;
+       -x)     exec=${2?exec}; shift ;;
+       -x?*)   exec=${1#-x} ;;
+       -ss)    skip_cocci=t ;;
+       -fs)    force_cocci=t ;;
+       -ns)    no_cocci=t ;;
        *)      generate=yes
                break ;;
        esac
        shift
 done
 
+annotate_merge () {
+       test -f Meta/whats-cooking.txt || return 0
+
+       # NEEDSWORK: unify with cook::wildo_match
+       perl -e '
+               sub wildo_match {
+                       s/^\s*//;
+                       if (/^Will (?:\S+ ){0,2}(fast-track|hold|keep|merge|drop|discard|cook|kick|defer|eject|be re-?rolled|wait)[,. ]/ ||
+                           /^Not urgent/ || /^Not ready/ || /^Waiting for / ||
+                           /^Can wait in / || /^Still / || /^Stuck / || /^On hold/ || /^Breaks / ||
+                           /^Needs? / || /^Expecting / || /^May want to / || /^Under review/) {
+                               return 1;
+                       }
+                       return 0;
+               }
+
+               sub read_message {
+                       my ($fh, $branch) = @_;
+                       my ($in_section, $in_desc);
+                       my @msg = ();
+                       while (<$fh>) {
+                               chomp;
+                               if (/^\* \Q$branch\E /) {
+                                       $in_section = 1;
+                                       next;
+                               }
+                               last if (/^[-*\[]/ && $in_section);
+                               next unless $in_section;
+                               s/^\s+//;
+                               if (/^$/) {
+                                       $in_desc = 1;
+                               }
+                               next unless ($in_section && $in_desc);
+                               next if (/Originally merged to '\''next'\'' on ([-0-9]+)/);
+                               next if (/^source: /);
+                               last if (wildo_match($_));
+                               push @msg, "$_\n";
+                       }
+                       return ($in_section, @msg);
+               }
+
+               my ($branch) = $ARGV[0];
+               my ($fh, $in_section, @msg);
+               if (open $fh, "<", "Meta/whats-cooking.txt") {
+                       ($in_section, @msg) = read_message($fh, $branch);
+               }
+               if (!@msg) {
+                       open my $revs, "-|",
+                               qw(git -C Meta rev-list -32 HEAD -- whats-cooking.txt);
+                       while (my $rev = <$revs>) {
+                               chomp($rev);
+                               open $fh, "-|",
+                               qw(git -C Meta cat-file blob), "$rev:whats-cooking.txt";
+                               ($in_section, @msg) = read_message($fh, $branch);
+                               last if (@msg);
+                       }
+               }
+               if (@msg) {
+                       open(my $fh, "-|", qw(git cat-file commit HEAD));
+                       my @original = (<$fh>);
+                       close $fh;
+                       my @final;
+                       $in_section = 0;
+                       for (@original) {
+                               if (!$in_section) {
+                                       $in_section = 1 if (/^$/);
+                                       next;
+                               }
+                               if (/^Conflicts:$/ && $in_section == 2) {
+                                       $in_section = 3;
+                               }
+
+                               if ($in_section == 3) {
+                                       $_ = "# $_";
+                               }
+                               push @final, $_;
+                               if (/^$/ && $in_section == 1) {
+                                       push @final, @msg;
+                                       push @final, "\n";
+                                       $in_section = 2;
+                               }
+                       }
+                       open($fh, "|-", qw(git commit --amend -F -));
+                       print $fh @final;
+                       close $fh;
+               }
+       ' "$1"
+}
+
+cocci_mark="treewide: apply cocci patch"
+
 case "$generate" in
 no)
        accept_rerere () {
-               if test -z "$accept_rerere"
-               then
-                       return 1
-               fi
-               if git diff |
-                  grep -e "^.+" -e "^+." |
-                  grep -e "^..<<<<<<<" -e "^..=======" -e "^..>>>>>>>" >/dev/null
+               git ls-files -u -z |
+               perl -0 -e '
+                       my %path_stage = ();
+                       my @to_remove = ();
+                       while (<>) {
+                               my ($mode, $sha1, $stage, $path) =
+                                       /^([0-7]+) ([0-9a-f]+) ([0-3])  (.*)$/;
+                               $path_stage{$path} ||= 0;
+                               $path_stage{$path} |= (1 << ($stage - 1));
+                       }
+
+                       while (my ($path, $bits) = each %path_stage) {
+                               if ($bits == 3 || $bits == 5) {
+                                       push @to_remove, $path;
+                               }
+                       }
+                       if (@to_remove) {
+                               system(qw(git rm -f), @to_remove);
+                       }
+               '
+
+               if ! git write-tree 2>/dev/null >/dev/null
                then
+                       git rerere remaining
                        return 1
                else
-                       EDITOR=: git commit -a --no-verify
+                       GIT_EDITOR=: git commit --no-verify
                        echo "Accepted previous resolution"
                        return 0
                fi
        }
 
+       mark_cut () {
+               test -n "$stop_at_cut" && return
+
+               count_since_last_cut=$(( $count_since_last_cut + 1 ))
+               test -z "$prev_cut" && return
+               git commit --allow-empty -m "$prev_cut"
+               prev_cut=
+       }
+
+       detach () {
+               if original_branch=$(git symbolic-ref HEAD 2>/dev/null)
+               then
+                       original_branch=${original_branch#refs/heads/}
+                       git checkout --quiet --detach
+                       into="--into $original_branch"
+               else
+                       original_branch=
+                       into=
+               fi
+
+       }
+
+       leave () {
+               if test -n "$original_branch" && ! git symbolic-ref HEAD 2>/dev/null
+               then
+                       git checkout --quiet -B "$original_branch"
+               fi
+               if test -n "$1"
+               then
+                       exit "$1"
+               fi
+       }
+
+       detach
+       cut_seen=0 prev_cut= count_since_last_cut=0 cocci_count=0
+
        while read branch eh
        do
-               case "$eh" in
-               "")
-                       save=$(git rev-parse --verify HEAD)
+               case "$branch" in '###') cut_seen=$(( $cut_seen + 1 )) ;; esac
+               if test -n "$stop_at_cut" && test $stop_at_cut -le $cut_seen
+               then
+                       continue ;# slurp the remainder and skip
+               fi
 
+               case "$branch" in
+               '###')
+                       if test "$count_since_last_cut" = 0
+                       then
+                               prev_cut=
+                       else
+                               echo >&2 "$branch $eh"
+                               prev_cut="$branch $eh"
+                               count_since_last_cut=0
+                       fi
+                       continue ;;
+               '#cocci')
+                       if test -n "$no_cocci"
+                       then
+                               continue
+                       elif test 0 = "$cocci_count" && test -z "$force_cocci"
+                       then
+                               continue
+                       fi
+
+                       if test -n "$skip_cocci" && test -n "$eh"
+                       then
+                               git cherry-pick --no-commit "$eh"
+                       else
+                               rm -f contrib/coccinelle/*.patch
+                               Meta/Make -j8 coccicheck
+                               if grep coccicheck-pending Makefile >/dev/null
+                               then
+                                       Meta/Make -j8 coccicheck-pending
+                               fi
+                               cat contrib/coccinelle/*.patch >cocci.patch
+                               if ! test -s cocci.patch
+                               then
+                                       leave 0
+                               fi
+                               git apply --index -3 cocci.patch || leave $?
+                               rm cocci.patch
+                               git diff --quiet HEAD && continue
+                       fi
+                       git commit -m "$cocci_mark" || leave $?
+
+                       mark_cut
+                       continue
+                       ;;
+               '#'* | '')
+                       continue ;;
+               esac
+
+               case "$eh" in
+               "" | "#"* | [0-9][0-9]-[0-9][0-9]*)
                        echo >&2 "* $branch"
-                       git merge "$branch" || accept_rerere || exit
+
+                       save=$(git rev-parse --verify HEAD) &&
+                       tip=$(git rev-parse --verify "$branch^0") &&
+                       mb=$(git merge-base "$tip" "$save") ||
+                       leave $?
+
+                       test "$mb" = "$tip" && continue
+
+                       mark_cut
+                       cocci_count=$(( $cocci_count + 1 ))
+
+                       rebuild=$(git config "branch.$branch.rebuild" || :)
+
+                       GIT_EDITOR=: git merge --no-ff $into $rebuild $accept_rerere --edit "$branch" ||
+                       accept_rerere ||
+                       leave $?
+
+                       annotate_merge "$branch" || leave $?
+                       test -z "$edit" ||
+                       git commit --amend || leave $?
 
                        this=$(git rev-parse --verify HEAD)
                        if test "$this" = "$save"
@@ -52,17 +277,25 @@ no)
                                :
                        elif git show-ref -q --verify "refs/merge-fix/$branch"
                        then
+                               echo >&2 "Fixing up the merge"
                                git cherry-pick --no-commit "refs/merge-fix/$branch" &&
-                               EDITOR=: git commit --amend -a
+                               git diff --stat HEAD &&
+                               GIT_EDITOR=: git commit --amend -a || leave $?
                        fi
                        ;;
                pick" "*)
                        echo >&2 "* $eh"
-                       git cherry-pick "$branch" || exit ;;
-               *) echo >&2 "Eh? $branch $eh"; exit ;;
+
+                       mark_cut
+
+                       git cherry-pick "$branch" || leave $? ;;
+               *) echo >&2 "Eh? $branch $eh"; leave $? ;;
                esac
+
+               eval "$exec" || leave $?
        done
-       exit
+       leave $?
+       ;;
 esac
 
 if test -n "$update" && test $# = 0
@@ -80,17 +313,17 @@ LF='
 
 show_merge () {
        case "$msg" in
-       "Merge branch '"*"'"*" into "*)
+       "Merge branch '"*"'"*)
                branch=$(expr "$msg" : "Merge branch '\(.*\)'")
                merge_hier=heads/
                ;;
-       "Merge remote branch '"*"'"*" into "*)
+       "Merge remote branch '"*"'"*)
                branch=$(expr "$msg" : "Merge remote branch '\(.*\)'")
                merge_hier=
                ;;
        *)
                echo 2>&1 "Huh?: $msg"
-               exit 1
+               return
                ;;
        esac &&
        tip=$(git rev-parse --verify "refs/$merge_hier$branch" 2>/dev/null) &&
@@ -103,7 +336,15 @@ show_merge () {
 }
 
 show_pick () {
-       merged="$(git rev-parse --verify "$commit") pick $msg" 
+       case "$msg" in
+       "### "* | "###")
+               merged="$msg$LF"
+               ;;
+       *)
+               merged="$(git rev-parse --verify "$commit") pick $msg"
+               ;;
+       esac
+
 }
 
 generate () {
@@ -111,8 +352,12 @@ generate () {
        shift
        echo '#!/bin/sh'
        echo "# $1"
+       echo 'case "$#,$1" in'
+       echo '1,-u|1,-d)'
+       echo "  exec $PROGRAM" '"$1" "$0"'
+       echo 'esac'
        echo "$PROGRAM" '"$@" <<\EOF'
-       git log --pretty=oneline --first-parent "$1" |
+       git log --no-decorate --pretty=oneline --first-parent "$1" |
        {
                series=
                while read commit msg
@@ -120,6 +365,9 @@ generate () {
                        if other=$(git rev-parse -q --verify "$commit^2")
                        then
                                show_merge
+                       elif test "$msg" = "$cocci_mark"
+                       then
+                               merged="#cocci "$(git rev-parse "$commit^0")
                        else
                                show_pick
                        fi
@@ -141,12 +389,12 @@ then
        generate "$0" "$@"
 elif test -z "$diff"
 then
-       generate "$0" "$@" | diff -u "$update" -
+       generate "$0" "$@" | diff -w -u "$update" -
        if test $? = 0
        then
                echo >&2 "No changes."
        else
-               echo >&2 -n "Update [Y/n]? "
+               echo >&2 -n "Update [y/N]? "
                read yesno
                case "$yesno" in
                [Yy]*)
@@ -157,5 +405,5 @@ then
                esac
        fi
 else
-       generate "$0" "$@" | diff -u "$update" -
+       generate "$0" "$@" | diff -w -u "$update" -
 fi