git-commit-msg revision 11887
12155SN/A#!/bin/sh 22155SN/A# From Gerrit Code Review 2.13.5-2617-gba50ae91fd 32155SN/A# 42155SN/A# Part of Gerrit Code Review (https://www.gerritcodereview.com/) 52155SN/A# 62155SN/A# Copyright (C) 2009 The Android Open Source Project 72155SN/A# 82155SN/A# Licensed under the Apache License, Version 2.0 (the "License"); 92155SN/A# you may not use this file except in compliance with the License. 102155SN/A# You may obtain a copy of the License at 112155SN/A# 122155SN/A# http://www.apache.org/licenses/LICENSE-2.0 132155SN/A# 142155SN/A# Unless required by applicable law or agreed to in writing, software 152155SN/A# distributed under the License is distributed on an "AS IS" BASIS, 162155SN/A# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 172155SN/A# See the License for the specific language governing permissions and 182155SN/A# limitations under the License. 192155SN/A# 202155SN/A 212155SN/Aunset GREP_OPTIONS 222155SN/A 232155SN/ACHANGE_ID_AFTER="Bug|Depends-On|Issue|Test|Feature|Fixes|Fixed" 242155SN/AMSG="$1" 252155SN/A 262155SN/A# Check for, and add if missing, a unique Change-Id 272155SN/A# 282665Ssaidi@eecs.umich.eduadd_ChangeId() { 292665Ssaidi@eecs.umich.edu clean_message=`sed -e ' 302155SN/A /^diff --git .*/{ 314202Sbinkertn@umich.edu s/// 322155SN/A q 332178SN/A } 342178SN/A /^Signed-off-by:/d 352178SN/A /^#/d 362178SN/A ' "$MSG" | git stripspace` 372178SN/A if test -z "$clean_message" 382178SN/A then 392178SN/A return 402178SN/A fi 412178SN/A 422178SN/A # Do not add Change-Id to temp commits 432178SN/A if echo "$clean_message" | head -1 | grep -q '^\(fixup\|squash\)!' 442178SN/A then 452155SN/A return 462178SN/A fi 472155SN/A 482155SN/A if test "false" = "`git config --bool --get gerrit.createChangeId`" 492178SN/A then 502155SN/A return 512155SN/A fi 522623SN/A 533918Ssaidi@eecs.umich.edu # Does Change-Id: already exist? if so, exit (no change). 542623SN/A if grep -i '^Change-Id:' "$MSG" >/dev/null 552623SN/A then 563918Ssaidi@eecs.umich.edu return 572155SN/A fi 582155SN/A 592292SN/A id=`_gen_ChangeId` 603918Ssaidi@eecs.umich.edu T="$MSG.tmp.$$" 612292SN/A AWK=awk 622292SN/A if [ -x /usr/xpg4/bin/awk ]; then 632292SN/A # Solaris AWK is just too broken 643918Ssaidi@eecs.umich.edu AWK=/usr/xpg4/bin/awk 652292SN/A fi 662292SN/A 672766Sktlim@umich.edu # Get core.commentChar from git config or use default symbol 682766Sktlim@umich.edu commentChar=`git config --get core.commentChar` 692766Sktlim@umich.edu commentChar=${commentChar:-#} 702921Sktlim@umich.edu 712921Sktlim@umich.edu # How this works: 722766Sktlim@umich.edu # - parse the commit message as (textLine+ blankLine*)* 732766Sktlim@umich.edu # - assume textLine+ to be a footer until proven otherwise 742766Sktlim@umich.edu # - exception: the first block is not footer (as it is the title) 754762Snate@binkert.org # - read textLine+ into a variable 762155SN/A # - then count blankLines 772155SN/A # - once the next textLine appears, print textLine+ blankLine* as these 782155SN/A # aren't footer 792155SN/A # - in END, the last textLine+ block is available for footer parsing 802155SN/A $AWK ' 812155SN/A BEGIN { 822766Sktlim@umich.edu # while we start with the assumption that textLine+ 832155SN/A # is a footer, the first block is not. 842623SN/A isFooter = 0 852155SN/A footerComment = 0 862155SN/A blankLines = 0 872155SN/A } 882155SN/A 892178SN/A # Skip lines starting with commentChar without any spaces before it. 902178SN/A /^'"$commentChar"'/ { next } 912178SN/A 922766Sktlim@umich.edu # Skip the line starting with the diff command and everything after it, 932178SN/A # up to the end of the file, assuming it is only patch data. 942178SN/A # If more than one line before the diff was empty, strip all but one. 952178SN/A /^diff --git / { 962178SN/A blankLines = 0 972766Sktlim@umich.edu while (getline) { } 982766Sktlim@umich.edu next 992766Sktlim@umich.edu } 1002788Sktlim@umich.edu 1012178SN/A # Count blank lines outside footer comments 1022733Sktlim@umich.edu /^$/ && (footerComment == 0) { 1032733Sktlim@umich.edu blankLines++ 1042817Sksewell@umich.edu next 1052733Sktlim@umich.edu } 1064486Sbinkertn@umich.edu 1074486Sbinkertn@umich.edu # Catch footer comment 1084776Sgblack@eecs.umich.edu /^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) { 1094776Sgblack@eecs.umich.edu footerComment = 1 1104486Sbinkertn@umich.edu } 1114202Sbinkertn@umich.edu 1124202Sbinkertn@umich.edu /]$/ && (footerComment == 1) { 1134202Sbinkertn@umich.edu footerComment = 2 1144202Sbinkertn@umich.edu } 1154202Sbinkertn@umich.edu 1164776Sgblack@eecs.umich.edu # We have a non-blank line after blank lines. Handle this. 1174202Sbinkertn@umich.edu (blankLines > 0) { 1184202Sbinkertn@umich.edu print lines 1194202Sbinkertn@umich.edu for (i = 0; i < blankLines; i++) { 1204202Sbinkertn@umich.edu print "" 1215217Ssaidi@eecs.umich.edu } 1224202Sbinkertn@umich.edu 1232155SN/A lines = "" 1244202Sbinkertn@umich.edu blankLines = 0 1254486Sbinkertn@umich.edu isFooter = 1 1264486Sbinkertn@umich.edu footerComment = 0 1274202Sbinkertn@umich.edu } 1284202Sbinkertn@umich.edu 1292821Sktlim@umich.edu # Detect that the current block is not the footer 1304776Sgblack@eecs.umich.edu (footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) { 1314776Sgblack@eecs.umich.edu isFooter = 0 1324776Sgblack@eecs.umich.edu } 1334776Sgblack@eecs.umich.edu 1344776Sgblack@eecs.umich.edu { 1354776Sgblack@eecs.umich.edu # We need this information about the current last comment line 1364776Sgblack@eecs.umich.edu if (footerComment == 2) { 1374776Sgblack@eecs.umich.edu footerComment = 0 1382766Sktlim@umich.edu } 1394202Sbinkertn@umich.edu if (lines != "") { 1405192Ssaidi@eecs.umich.edu lines = lines "\n"; 1412733Sktlim@umich.edu } 1422733Sktlim@umich.edu lines = lines $0 1432733Sktlim@umich.edu } 1442733Sktlim@umich.edu 1452733Sktlim@umich.edu # Footer handling: 1462874Sktlim@umich.edu # If the last block is considered a footer, splice in the Change-Id at the 1472874Sktlim@umich.edu # right place. 1482874Sktlim@umich.edu # Look for the right place to inject Change-Id by considering 1494202Sbinkertn@umich.edu # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first, 1502733Sktlim@umich.edu # then Change-Id, then everything else (eg. Signed-off-by:). 1515400Ssaidi@eecs.umich.edu # 1525400Ssaidi@eecs.umich.edu # Otherwise just print the last block, a new line and the Change-Id as a 1535398Ssaidi@eecs.umich.edu # block of its own. 1545398Ssaidi@eecs.umich.edu END { 1555192Ssaidi@eecs.umich.edu unprinted = 1 1565192Ssaidi@eecs.umich.edu if (isFooter == 0) { 1575192Ssaidi@eecs.umich.edu print lines "\n" 1585217Ssaidi@eecs.umich.edu lines = "" 1595192Ssaidi@eecs.umich.edu } 1605192Ssaidi@eecs.umich.edu changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):" 1615192Ssaidi@eecs.umich.edu numlines = split(lines, footer, "\n") 1625192Ssaidi@eecs.umich.edu for (line = 1; line <= numlines; line++) { 1635192Ssaidi@eecs.umich.edu if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) { 1645192Ssaidi@eecs.umich.edu unprinted = 0 1655192Ssaidi@eecs.umich.edu print "Change-Id: I'"$id"'" 1665192Ssaidi@eecs.umich.edu } 1675192Ssaidi@eecs.umich.edu print footer[line] 1685192Ssaidi@eecs.umich.edu } 1695192Ssaidi@eecs.umich.edu if (unprinted) { 1705192Ssaidi@eecs.umich.edu print "Change-Id: I'"$id"'" 1715192Ssaidi@eecs.umich.edu } 1725192Ssaidi@eecs.umich.edu }' "$MSG" > "$T" && mv "$T" "$MSG" || rm -f "$T" 1735192Ssaidi@eecs.umich.edu} 1745192Ssaidi@eecs.umich.edu_gen_ChangeIdInput() { 1755192Ssaidi@eecs.umich.edu echo "tree `git write-tree`" 1765192Ssaidi@eecs.umich.edu if parent=`git rev-parse "HEAD^0" 2>/dev/null` 1775192Ssaidi@eecs.umich.edu then 1785192Ssaidi@eecs.umich.edu echo "parent $parent" 179 fi 180 echo "author `git var GIT_AUTHOR_IDENT`" 181 echo "committer `git var GIT_COMMITTER_IDENT`" 182 echo 183 printf '%s' "$clean_message" 184} 185_gen_ChangeId() { 186 _gen_ChangeIdInput | 187 git hash-object -t commit --stdin 188} 189 190 191add_ChangeId 192