| #!/bin/sh |
| |
| 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 |
| shift ;; |
| -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 () { |
| 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 |
| 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 "$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" |
| |
| 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" |
| then |
| : |
| 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" && |
| git diff --stat HEAD && |
| GIT_EDITOR=: git commit --amend -a || leave $? |
| fi |
| ;; |
| pick" "*) |
| echo >&2 "* $eh" |
| |
| mark_cut |
| |
| git cherry-pick "$branch" || leave $? ;; |
| *) echo >&2 "Eh? $branch $eh"; leave $? ;; |
| esac |
| |
| eval "$exec" || leave $? |
| done |
| leave $? |
| ;; |
| esac |
| |
| if test -n "$update" && test $# = 0 |
| then |
| set x $(sed -n -e '2s/^# //p' <"$update") && |
| shift |
| fi |
| |
| # Generation (or updating) |
| |
| x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' |
| x40="$x40$x40$x40$x40$x40$x40$x40$x40" |
| LF=' |
| ' |
| |
| show_merge () { |
| case "$msg" in |
| "Merge branch '"*"'"*) |
| branch=$(expr "$msg" : "Merge branch '\(.*\)'") |
| merge_hier=heads/ |
| ;; |
| "Merge remote branch '"*"'"*) |
| branch=$(expr "$msg" : "Merge remote branch '\(.*\)'") |
| merge_hier= |
| ;; |
| *) |
| echo 2>&1 "Huh?: $msg" |
| return |
| ;; |
| esac && |
| tip=$(git rev-parse --verify "refs/$merge_hier$branch" 2>/dev/null) && |
| merged=$(git name-rev --refs="refs/$merge_hier$branch" "$other" 2>/dev/null) && |
| merged=$(expr "$merged" : "$x40 \(.*\)") && |
| test "$merged" != undefined || { |
| other=$(git log -1 --pretty='format:%s' $other) && |
| merged="$branch :rebased? $other" |
| } |
| } |
| |
| show_pick () { |
| case "$msg" in |
| "### "* | "###") |
| merged="$msg$LF" |
| ;; |
| *) |
| merged="$(git rev-parse --verify "$commit") pick $msg" |
| ;; |
| esac |
| |
| } |
| |
| generate () { |
| PROGRAM=$1 |
| 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 --no-decorate --pretty=oneline --first-parent "$1" | |
| { |
| series= |
| while read commit msg |
| do |
| 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 |
| |
| if test -z "$series" |
| then |
| series="$merged" |
| else |
| series="$merged$LF$series" |
| fi |
| done |
| echo "$series" |
| } |
| echo EOF |
| } |
| |
| if test -z "$update" |
| then |
| generate "$0" "$@" |
| elif test -z "$diff" |
| then |
| generate "$0" "$@" | diff -w -u "$update" - |
| if test $? = 0 |
| then |
| echo >&2 "No changes." |
| else |
| echo >&2 -n "Update [y/N]? " |
| read yesno |
| case "$yesno" in |
| [Yy]*) |
| generate "$0" "$@" | |
| sed -e 's/ :rebased?.*//' >"$update" ;; |
| *) |
| echo >&2 "No update then." ;; |
| esac |
| fi |
| else |
| generate "$0" "$@" | diff -w -u "$update" - |
| fi |