1*91f16700Schasinglulu#!/bin/sh 2*91f16700Schasinglulu# From Gerrit Code Review 2.14.20 3*91f16700Schasinglulu# 4*91f16700Schasinglulu# Part of Gerrit Code Review (https://www.gerritcodereview.com/) 5*91f16700Schasinglulu# 6*91f16700Schasinglulu# Copyright (C) 2009 The Android Open Source Project 7*91f16700Schasinglulu# 8*91f16700Schasinglulu# Licensed under the Apache License, Version 2.0 (the "License"); 9*91f16700Schasinglulu# you may not use this file except in compliance with the License. 10*91f16700Schasinglulu# You may obtain a copy of the License at 11*91f16700Schasinglulu# 12*91f16700Schasinglulu# http://www.apache.org/licenses/LICENSE-2.0 13*91f16700Schasinglulu# 14*91f16700Schasinglulu# Unless required by applicable law or agreed to in writing, software 15*91f16700Schasinglulu# distributed under the License is distributed on an "AS IS" BASIS, 16*91f16700Schasinglulu# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17*91f16700Schasinglulu# See the License for the specific language governing permissions and 18*91f16700Schasinglulu# limitations under the License. 19*91f16700Schasinglulu# 20*91f16700Schasinglulu 21*91f16700Schasingluluunset GREP_OPTIONS 22*91f16700Schasinglulu 23*91f16700SchasingluluCHANGE_ID_AFTER="Bug|Depends-On|Issue|Test|Feature|Fixes|Fixed" 24*91f16700SchasingluluMSG="$1" 25*91f16700Schasinglulu 26*91f16700Schasinglulu# Check for, and add if missing, a unique Change-Id 27*91f16700Schasinglulu# 28*91f16700Schasingluluadd_ChangeId() { 29*91f16700Schasinglulu clean_message=`sed -e ' 30*91f16700Schasinglulu /^diff --git .*/{ 31*91f16700Schasinglulu s/// 32*91f16700Schasinglulu q 33*91f16700Schasinglulu } 34*91f16700Schasinglulu /^Signed-off-by:/d 35*91f16700Schasinglulu /^#/d 36*91f16700Schasinglulu ' "$MSG" | git stripspace` 37*91f16700Schasinglulu if test -z "$clean_message" 38*91f16700Schasinglulu then 39*91f16700Schasinglulu return 40*91f16700Schasinglulu fi 41*91f16700Schasinglulu 42*91f16700Schasinglulu # Do not add Change-Id to temp commits 43*91f16700Schasinglulu if echo "$clean_message" | head -1 | grep -q '^\(fixup\|squash\)!' 44*91f16700Schasinglulu then 45*91f16700Schasinglulu return 46*91f16700Schasinglulu fi 47*91f16700Schasinglulu 48*91f16700Schasinglulu if test "false" = "`git config --bool --get gerrit.createChangeId`" 49*91f16700Schasinglulu then 50*91f16700Schasinglulu return 51*91f16700Schasinglulu fi 52*91f16700Schasinglulu 53*91f16700Schasinglulu # Does Change-Id: already exist? if so, exit (no change). 54*91f16700Schasinglulu if grep -i '^Change-Id:' "$MSG" >/dev/null 55*91f16700Schasinglulu then 56*91f16700Schasinglulu return 57*91f16700Schasinglulu fi 58*91f16700Schasinglulu 59*91f16700Schasinglulu id=`_gen_ChangeId` 60*91f16700Schasinglulu T="$MSG.tmp.$$" 61*91f16700Schasinglulu AWK=awk 62*91f16700Schasinglulu if [ -x /usr/xpg4/bin/awk ]; then 63*91f16700Schasinglulu # Solaris AWK is just too broken 64*91f16700Schasinglulu AWK=/usr/xpg4/bin/awk 65*91f16700Schasinglulu fi 66*91f16700Schasinglulu 67*91f16700Schasinglulu # Get core.commentChar from git config or use default symbol 68*91f16700Schasinglulu commentChar=`git config --get core.commentChar` 69*91f16700Schasinglulu commentChar=${commentChar:-#} 70*91f16700Schasinglulu 71*91f16700Schasinglulu # How this works: 72*91f16700Schasinglulu # - parse the commit message as (textLine+ blankLine*)* 73*91f16700Schasinglulu # - assume textLine+ to be a footer until proven otherwise 74*91f16700Schasinglulu # - exception: the first block is not footer (as it is the title) 75*91f16700Schasinglulu # - read textLine+ into a variable 76*91f16700Schasinglulu # - then count blankLines 77*91f16700Schasinglulu # - once the next textLine appears, print textLine+ blankLine* as these 78*91f16700Schasinglulu # aren't footer 79*91f16700Schasinglulu # - in END, the last textLine+ block is available for footer parsing 80*91f16700Schasinglulu $AWK ' 81*91f16700Schasinglulu BEGIN { 82*91f16700Schasinglulu if (match(ENVIRON["OS"], "Windows")) { 83*91f16700Schasinglulu RS="\r?\n" # Required on recent Cygwin 84*91f16700Schasinglulu } 85*91f16700Schasinglulu # while we start with the assumption that textLine+ 86*91f16700Schasinglulu # is a footer, the first block is not. 87*91f16700Schasinglulu isFooter = 0 88*91f16700Schasinglulu footerComment = 0 89*91f16700Schasinglulu blankLines = 0 90*91f16700Schasinglulu } 91*91f16700Schasinglulu 92*91f16700Schasinglulu # Skip lines starting with commentChar without any spaces before it. 93*91f16700Schasinglulu /^'"$commentChar"'/ { next } 94*91f16700Schasinglulu 95*91f16700Schasinglulu # Skip the line starting with the diff command and everything after it, 96*91f16700Schasinglulu # up to the end of the file, assuming it is only patch data. 97*91f16700Schasinglulu # If more than one line before the diff was empty, strip all but one. 98*91f16700Schasinglulu /^diff --git / { 99*91f16700Schasinglulu blankLines = 0 100*91f16700Schasinglulu while (getline) { } 101*91f16700Schasinglulu next 102*91f16700Schasinglulu } 103*91f16700Schasinglulu 104*91f16700Schasinglulu # Count blank lines outside footer comments 105*91f16700Schasinglulu /^$/ && (footerComment == 0) { 106*91f16700Schasinglulu blankLines++ 107*91f16700Schasinglulu next 108*91f16700Schasinglulu } 109*91f16700Schasinglulu 110*91f16700Schasinglulu # Catch footer comment 111*91f16700Schasinglulu /^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) { 112*91f16700Schasinglulu footerComment = 1 113*91f16700Schasinglulu } 114*91f16700Schasinglulu 115*91f16700Schasinglulu /]$/ && (footerComment == 1) { 116*91f16700Schasinglulu footerComment = 2 117*91f16700Schasinglulu } 118*91f16700Schasinglulu 119*91f16700Schasinglulu # We have a non-blank line after blank lines. Handle this. 120*91f16700Schasinglulu (blankLines > 0) { 121*91f16700Schasinglulu print lines 122*91f16700Schasinglulu for (i = 0; i < blankLines; i++) { 123*91f16700Schasinglulu print "" 124*91f16700Schasinglulu } 125*91f16700Schasinglulu 126*91f16700Schasinglulu lines = "" 127*91f16700Schasinglulu blankLines = 0 128*91f16700Schasinglulu isFooter = 1 129*91f16700Schasinglulu footerComment = 0 130*91f16700Schasinglulu } 131*91f16700Schasinglulu 132*91f16700Schasinglulu # Detect that the current block is not the footer 133*91f16700Schasinglulu (footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) { 134*91f16700Schasinglulu isFooter = 0 135*91f16700Schasinglulu } 136*91f16700Schasinglulu 137*91f16700Schasinglulu { 138*91f16700Schasinglulu # We need this information about the current last comment line 139*91f16700Schasinglulu if (footerComment == 2) { 140*91f16700Schasinglulu footerComment = 0 141*91f16700Schasinglulu } 142*91f16700Schasinglulu if (lines != "") { 143*91f16700Schasinglulu lines = lines "\n"; 144*91f16700Schasinglulu } 145*91f16700Schasinglulu lines = lines $0 146*91f16700Schasinglulu } 147*91f16700Schasinglulu 148*91f16700Schasinglulu # Footer handling: 149*91f16700Schasinglulu # If the last block is considered a footer, splice in the Change-Id at the 150*91f16700Schasinglulu # right place. 151*91f16700Schasinglulu # Look for the right place to inject Change-Id by considering 152*91f16700Schasinglulu # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first, 153*91f16700Schasinglulu # then Change-Id, then everything else (eg. Signed-off-by:). 154*91f16700Schasinglulu # 155*91f16700Schasinglulu # Otherwise just print the last block, a new line and the Change-Id as a 156*91f16700Schasinglulu # block of its own. 157*91f16700Schasinglulu END { 158*91f16700Schasinglulu unprinted = 1 159*91f16700Schasinglulu if (isFooter == 0) { 160*91f16700Schasinglulu print lines "\n" 161*91f16700Schasinglulu lines = "" 162*91f16700Schasinglulu } 163*91f16700Schasinglulu changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):" 164*91f16700Schasinglulu numlines = split(lines, footer, "\n") 165*91f16700Schasinglulu for (line = 1; line <= numlines; line++) { 166*91f16700Schasinglulu if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) { 167*91f16700Schasinglulu unprinted = 0 168*91f16700Schasinglulu print "Change-Id: I'"$id"'" 169*91f16700Schasinglulu } 170*91f16700Schasinglulu print footer[line] 171*91f16700Schasinglulu } 172*91f16700Schasinglulu if (unprinted) { 173*91f16700Schasinglulu print "Change-Id: I'"$id"'" 174*91f16700Schasinglulu } 175*91f16700Schasinglulu }' "$MSG" > "$T" && mv "$T" "$MSG" || rm -f "$T" 176*91f16700Schasinglulu} 177*91f16700Schasinglulu_gen_ChangeIdInput() { 178*91f16700Schasinglulu echo "tree `git write-tree`" 179*91f16700Schasinglulu if parent=`git rev-parse "HEAD^0" 2>/dev/null` 180*91f16700Schasinglulu then 181*91f16700Schasinglulu echo "parent $parent" 182*91f16700Schasinglulu fi 183*91f16700Schasinglulu echo "author `git var GIT_AUTHOR_IDENT`" 184*91f16700Schasinglulu echo "committer `git var GIT_COMMITTER_IDENT`" 185*91f16700Schasinglulu echo 186*91f16700Schasinglulu printf '%s' "$clean_message" 187*91f16700Schasinglulu} 188*91f16700Schasinglulu_gen_ChangeId() { 189*91f16700Schasinglulu _gen_ChangeIdInput | 190*91f16700Schasinglulu git hash-object -t commit --stdin 191*91f16700Schasinglulu} 192*91f16700Schasinglulu 193*91f16700Schasinglulu 194*91f16700Schasingluluadd_ChangeId 195