David Hendricks has uploaded a new change for review. ( https://review.coreboot.org/19206 )
Change subject: Convert flashrom to git ......................................................................
Convert flashrom to git
- Drop support for Subversion in the getrevision script and Makefile. - Add .gitignore and .gitattributes file (the latter to limit exports). - Restore modification dates of the exported files from the SCM. - Stop exporting SCM log dumps to CHANGELOG. This makes no sense. - Remove djgpp-dos target (it is not different to other x-compilations). - Do not export the pre-"compiled" manpage. It can be generated like anything else from the code dump when we export the respective variable. The latter is added with this change. - Add some initial client-side git hooks * When committing check for obvious stuff you never want anyway: - white space errors * When pushing to the upstream repository check mandatory rules: - existing signoffs and acks in all new commits - no deletions or creation of branches - do not rewrite history of the precious branches, even if forced - Change version string of flashrom as follows. Previously, we included the last stable version according to a hard- coded string in the Makefile and appended the subversion revision number.
With this patch the version string includes the last reachable git tag, number of commits since said tag in the upstream branches (if any), the name of said upstream branch, number of commits since that branch (if any), and the shortened git hash. In case there are uncommitted changes a "-dirty" indicator is also added. The case of unknown versions is explicitly handled in getrevision instead of simply appending "-unknown" to a hardcoded release number.
The version information is either taken from an existing git remote pointing to an upstream repository (or a known mirror), or if that is not available - with the user's consent - a shadow copy is fetched from the upstream repo that is updated on every build (usually takes less than a second).
In the following some examples of the version string changes are shown. Basically we print the distance to the last known upstream tag, which comprises any upstream commits since that tag as well as local commits on top of that. Additionally we handle upstream's stable and staging branches specially.
Old output: flashrom v0.9.7-r1716 on Linux 3.8.0-6-generic (x86_64)
New output variants:
Build of the 0.9.99 release without any changes: flashrom v0.9.99-e4f6643 on Linux 3.13.0-76-generic (x86_64)
5 commits since last tag in upstream's stable branch: flashrom v0.9.99-5-stable-e4f6643-dirty on Linux 3.13.0-76-generic (x86_64)
3 commits since last tag in upstream's staging branch and 4 local commits on top of that: flashrom v0.9.99-3-staging-4-e4f6643 on Linux 3.13.0-76-generic (x86_64)
3 commits since last tag in upstream's staging branch and 4 local commits on top of that, and some local uncommitted changes too: flashrom v0.9.99-3-staging-4-e4f6643-dirty on Linux 3.13.0-76-generic (x86_64)
3 commits since the last tag in an unrelated upstream branch (e.g., a stable release *branch* such as 0.9.99.x) or local branch: flashrom v0.9.99-3-e4f6643 on Linux 3.13.0-76-generic (x86_64)
No tags reachable from current commit (generic git fallback): flashrom d95935a version on Linux 3.13.0-76-generic (x86_64)
Not in a repository: flashrom unknown version on Linux 3.13.0-76-generic (x86_64)
NOTE: This patch is adapted from Stefan Tauner's original commit ( f5dd7ce1), but uses coreboot's commit-msg hook which includes support for generating and appending Change-Id which is now mandatory when pushing to the review server.
Change-Id: I64eef21982cac0a0a7419bcd2c8a936672ae9cb2 Signed-off-by: Stefan Tauner stefan.tauner@alumni.tuwien.ac.at Acked-by: Stefan Tauner stefan.tauner@alumni.tuwien.ac.at Signed-off-by: David Hendricks dhendricks@fb.com --- A .gitattributes A .gitignore M Makefile M flashrom.c M util/getrevision.sh A util/git-hooks/applypatch-msg A util/git-hooks/commit-msg A util/git-hooks/install.sh A util/git-hooks/pre-applypatch A util/git-hooks/pre-commit A util/git-hooks/pre-push A util/git-hooks/wrapper.sh 12 files changed, 544 insertions(+), 124 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/06/19206/1
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..10f8962 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +.gitattributes export-ignore +.gitignore export-ignore +/util/getrevision.sh export-ignore +/util/git-hooks export-ignore +/util/git-hooks/** export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19a4365 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.d +*.o +/.features +/.dependencies +/.libdeps +/build_details.txt +/flashrom +/flashrom-* +/flashrom.exe +/flashrom.8 + +/util/ich_descriptors_tool/ich_descriptors_tool +/util/ich_descriptors_tool/ich_descriptors_tool.exe +/util/ich_descriptors_tool/.dep +/util/ich_descriptors_tool/.obj diff --git a/Makefile b/Makefile index 4ebde1e..8ddd6f3 100644 --- a/Makefile +++ b/Makefile @@ -526,23 +526,27 @@
CLI_OBJS = cli_classic.o cli_output.o cli_common.o print.o
-# Set the flashrom version string from the highest revision number of the checked out flashrom files. +# Set the flashrom version string from the repository metadata (cf. util/getrevision.sh). # Note to packagers: Any tree exported with "make export" or "make tarball" -# will not require subversion. The downloadable snapshots are already exported. -SVNVERSION := $(shell ./util/getrevision.sh -u 2>/dev/null ) +# will not require git. The downloadable snapshots are already exported. +VERSION := $(shell ./util/getrevision.sh --revision) +# VERSION equals "offline" if online access is required but the respective git config variable is not set yet. +ifeq ($(VERSION),offline) + $(error Aborting) +endif +SCMDEF := -D'FLASHROM_VERSION="$(VERSION)"'
-RELEASE := 0.9.9 -VERSION := $(RELEASE)-$(SVNVERSION) -RELEASENAME ?= $(VERSION) +# No spaces in release names unless set explicitly +RELEASENAME ?= $(shell echo "$(VERSION)" | sed -e 's/ /_/')
-SVNDEF := -D'FLASHROM_VERSION="$(VERSION)"' - -# Inform user if there is no meaningful version string. If there is version information from a VCS print +# Inform user about the version string used. If there is no version information from a VCS then print # something anyway because $(info...) will print a line break in any case which would look suspicious. # The && between the echos is a workaround for old versions of GNU make that issue the error "unterminated # variable reference" if a semicolon is used instead. -$(info $(shell ./util/getrevision.sh -c 2>/dev/null || echo "Files don't seem to be under version control." && \ - echo "Replacing all version templates with $(VERSION)." )) +# Also, if a VCS is found then try to install hooks. +$(info $(shell ./util/getrevision.sh -c 2>/dev/null && ./util/git-hooks/install.sh || \ + echo "Files don't seem to be under version control." && \ + echo "Replacing all version templates with $(VERSION)."))
############################################################################### # Default settings of CONFIG_* variables. @@ -1024,7 +1028,7 @@ TAROPTIONS = $(shell LC_ALL=C tar --version|grep -q GNU && echo "--owner=root --group=root")
%.o: %.c .features - $(CC) -MMD $(CFLAGS) $(CPPFLAGS) $(FLASHROM_CFLAGS) $(FEATURE_CFLAGS) $(SVNDEF) -o $@ -c $< + $(CC) -MMD $(CFLAGS) $(CPPFLAGS) $(FLASHROM_CFLAGS) $(FEATURE_CFLAGS) $(SCMDEF) -o $@ -c $<
# Make sure to add all names of generated binaries here. # This includes all frontends and libflashrom. @@ -1347,9 +1351,10 @@ $(PROGRAM).8.html: $(PROGRAM).8 @groff -mandoc -Thtml $< >$@
+MAN_DATE := $(shell ./util/getrevision.sh -d $(PROGRAM).8.tmpl 2>/dev/null) $(PROGRAM).8: $(PROGRAM).8.tmpl @# Add the man page change date and version to the man page - @sed -e 's#.TH FLASHROM 8 ".*".*#.TH FLASHROM 8 "$(shell ./util/getrevision.sh -d $(PROGRAM).8.tmpl 2>/dev/null)" "$(VERSION)"#' <$< >$@ + @sed -e 's#.TH FLASHROM 8 .*#.TH FLASHROM 8 "$(MAN_DATE)" "$(VERSION)" "$(MAN_DATE)"#' <$< >$@
install: $(PROGRAM)$(EXEC_SUFFIX) $(PROGRAM).8 mkdir -p $(DESTDIR)$(PREFIX)/sbin @@ -1357,25 +1362,38 @@ $(INSTALL) -m 0755 $(PROGRAM)$(EXEC_SUFFIX) $(DESTDIR)$(PREFIX)/sbin $(INSTALL) -m 0644 $(PROGRAM).8 $(DESTDIR)$(MANDIR)/man8
-export: $(PROGRAM).8 - @rm -rf $(EXPORTDIR)/flashrom-$(RELEASENAME) - @svn export -r BASE . $(EXPORTDIR)/flashrom-$(RELEASENAME) - @sed "s/^SVNVERSION.*/SVNVERSION := $(SVNVERSION)/" Makefile >$(EXPORTDIR)/flashrom-$(RELEASENAME)/Makefile - @cp $(PROGRAM).8 "$(EXPORTDIR)/flashrom-$(RELEASENAME)/$(PROGRAM).8" - @svn log >$(EXPORTDIR)/flashrom-$(RELEASENAME)/ChangeLog - @echo Exported $(EXPORTDIR)/flashrom-$(RELEASENAME)/ +_export: $(PROGRAM).8 + @rm -rf "$(EXPORTDIR)/flashrom-$(RELEASENAME)" + @mkdir -p "$(EXPORTDIR)/flashrom-$(RELEASENAME)" + @git archive HEAD | tar -x -C "$(EXPORTDIR)/flashrom-$(RELEASENAME)" + @sed -e 's/^VERSION :=.*/VERSION := $(VERSION)/' \ + -e 's/^MAN_DATE :=.*/MAN_DATE := $(MAN_DATE)/' \ + -e 's#./util/getrevision.sh -c#false#' \ + Makefile >"$(EXPORTDIR)/flashrom-$(RELEASENAME)/Makefile" +# Restore modification date of all tracked files not marked 'export-ignore' in .gitattributes. +# sed is required to filter out file names having the attribute set. + @git ls-tree -r -z -t --full-name --name-only HEAD | \ + git check-attr -z --stdin export-ignore | \ + sed -zne 'x;n;n;s/^set$$//;t;x;p' | \ + xargs -0 sh -c 'for f; do \ + touch -d $$(git log --pretty=format:%cI -1 HEAD -- "$$f") \ + "$(EXPORTDIR)/flashrom-$(RELEASENAME)/$$f"; \ + done'
-tarball: export - @tar cjf $(EXPORTDIR)/flashrom-$(RELEASENAME).tar.bz2 -C $(EXPORTDIR)/ $(TAROPTIONS) flashrom-$(RELEASENAME)/ - @rm -rf $(EXPORTDIR)/flashrom-$(RELEASENAME) - @echo Created $(EXPORTDIR)/flashrom-$(RELEASENAME).tar.bz2 +export: _export + @echo "Exported $(EXPORTDIR)/flashrom-$(RELEASENAME)/"
-djgpp-dos: clean - make CC=i586-pc-msdosdjgpp-gcc STRIP=i586-pc-msdosdjgpp-strip +tarball: _export + @tar -cz --format=ustar -f $(EXPORTDIR)/flashrom-$(RELEASENAME).tar.gz -C $(EXPORTDIR)/ \ + $(TAROPTIONS) flashrom-$(RELEASENAME)/ +# Delete the exported directory again because it is most likely what's expected by the user. + @rm -rf $(EXPORTDIR)/flashrom-$(RELEASENAME) + @echo Created $(EXPORTDIR)/flashrom-$(RELEASENAME).tar.gz + libpayload: clean make CC="CC=i386-elf-gcc lpgcc" AR=i386-elf-ar RANLIB=i386-elf-ranlib
-.PHONY: all install clean distclean compiler hwlibs features export tarball djgpp-dos featuresavailable libpayload +.PHONY: all install clean distclean compiler hwlibs features _export export tarball featuresavailable libpayload
# Disable implicit suffixes and built-in rules (for performance and profit) .SUFFIXES: diff --git a/flashrom.c b/flashrom.c index 25e53f2..1a43303 100644 --- a/flashrom.c +++ b/flashrom.c @@ -1801,7 +1801,7 @@
void print_version(void) { - msg_ginfo("flashrom v%s", flashrom_version); + msg_ginfo("flashrom %s", flashrom_version); print_sysinfo(); msg_ginfo("\n"); } diff --git a/util/getrevision.sh b/util/getrevision.sh index 1012058..03d0b1e 100755 --- a/util/getrevision.sh +++ b/util/getrevision.sh @@ -5,7 +5,7 @@ # Copyright (C) 2005 coresystems GmbH stepan@coresystems.de # Copyright (C) 2009,2010 Carl-Daniel Hailfinger # Copyright (C) 2010 Chromium OS Authors -# Copyright (C) 2013 Stefan Tauner +# Copyright (C) 2013-2016 Stefan Tauner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -30,23 +30,65 @@ # nor local times or dates export TZ=UTC0
-# Helper functions -# First argument is the path to inspect (usually optional; w/o it the whole repository will be considered) -svn_has_local_changes() { - svn status "$1" | egrep '^ *[ADMR] *' >/dev/null +# List of important upstream branches... +upstream_branches="stable staging" +upstream_url="https://flashrom.org/git/flashrom.git" +upstream_patterns="github.com.flashrom/flashrom(.git)?|flashrom.org.git/flashrom(.git)?" + +upcache_prefix="refs/flashrom_org/" +# Generate upcache_refs +for b in $upstream_branches ; do + upcache_refs="$upcache_refs ${upcache_prefix}$b" +done + +# We need to update our upstream information sometimes so that we can create detailed version information. +# To that end the code below fetches parts of the upstream repository via https. +# This takes about one second or less under normal circumstances. +# +# It can be called manually, but is usually called via +# - the Makefile (implicitly via revision()) when there is no upstream information in any existing remote +forced_update() { + local rev_remote_refs + for ref in $upcache_refs ; do + rev_remote_refs="$rev_remote_refs +${ref##*/}:$ref" + done + git fetch -q "$upstream_url" --tags $rev_remote_refs && echo "Success." }
+update() { + offline=$(git config flashrom.offline-builds 2>/dev/null) + if [ -z "$offline" ]; then + echo "To produce useful version information the build process needs access to the commit +history from an upstream repository. If no git remote is pointing to one we +can store the necessary information out of sight and update it on every build. +To enable this functionality and fetch the upstream commits from $upstream_url +please execute 'git config flashrom.offline-builds false' or add one of the +upstream repositories as git remote to rely on that information. +However, if you want to work completely offline and generate possibly meaningless +version strings then disable it with 'git config flashrom.offline-builds true' +You can force updating the local commit cache with '$0 --forced-update'">&2 + return 1 + elif [ "x$offline" = "xfalse" ]; then + echo "Fetching commit history from upstream repository $upstream_url +To disable any network activity execute 'git config flashrom.offline-builds true'.">&2 + forced_update >/dev/null + else + echo "Fetching commit history from upstream is disabled - version strings might be misleading. +To ensure proper version strings and allow network access run 'git config flashrom.offline-builds false'.">&2 + fi + return 0 +} + +# Helper functions +# First argument is the path to inspect (usually optional; w/o it the whole repository will be considered) git_has_local_changes() { git update-index -q --refresh >/dev/null ! git diff-index --quiet HEAD -- "$1" }
git_last_commit() { + # git rev-parse --short HEAD would suffice if repository as a whole is of interest (no $1) git log --pretty=format:"%h" -1 -- "$1" -} - -svn_is_file_tracked() { - svn info "$1" >/dev/null 2>&1 }
git_is_file_tracked() { @@ -54,7 +96,7 @@ }
is_file_tracked() { - svn_is_file_tracked "$1" || git_is_file_tracked "$1" + git_is_file_tracked "$1" }
# Tries to find a remote source for the changes committed locally. @@ -89,13 +131,7 @@ scm_url() { local url=
- # for a primitive VCS like subversion finding the URL is easy: there is only one upstream host - if svn_is_file_tracked "$1" ; then - url="$(svn info "$1" 2>/dev/null | - grep URL: | - sed 's/.*URL:[[:blank:]]*//;s/://.*@/:///' | - grep ^.)" - elif git_is_file_tracked "$1" ; then + if git_is_file_tracked "$1" ; then url="$(git_url "$1")" else return ${EXIT_FAILURE} @@ -116,29 +152,7 @@ # freebsd date [-jnu] [-d dst] [-r seconds] [-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format] [...] # dragonflybsd date [-jnu] [-d dst] [-r seconds] [-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format] [...] # openbsd date [-aju] [-d dst] [-r seconds] [+format] [[[[[[cc]yy]mm]dd]HH]MM[.SS]] [...] - if svn_is_file_tracked "$2" ; then - if svn_has_local_changes "$2"; then - t=$(date -u "$1") - else - # No local changes, get date of the last log record. Subversion provides that in - # ISO 8601 format when using the --xml switch. The sed call extracts that ignoring any - # fractional parts started by a comma or a dot. - local last_commit_date="$(svn info --xml "$2"| \ - sed -n -e 's/<date>([^,.]*)([.,].*)*Z</date>/\1Z/p')" - - case $(uname) in - # Most BSD dates do not support parsing date values from user input with -d but all of - # them support parsing the syntax with [[[[[[cc]yy]mm]dd]HH]MM[.ss]]. We have to - # transform the ISO8601 date first though. - NetBSD|OpenBSD|DragonFly|FreeBSD) - last_commit_date="$(echo ${last_commit_date} | \ - sed -n -e 's/(....)-(..)-(..)T(..):(..):(..)Z/\1\2\3\4\5.\6/p')" - t=$(date -u -j "${last_commit_date}" "$1" 2>/dev/null);; - *) - t=$(date -u -d "${last_commit_date}" "$1" 2>/dev/null);; - esac - fi - elif git_is_file_tracked "$2" ; then + if git_is_file_tracked "$2" ; then # are there local changes? if git_has_local_changes "$2" ; then t=$(date -u "${1}") @@ -163,55 +177,102 @@ echo "${t}" }
-# Retrieve local SCM revision info. This is useful if we're working in a different SCM than upstream and/or -# have local changes. -local_revision() { - local r= +tag() { + local t=
- if svn_is_file_tracked "$1" ; then - r=$(svn_has_local_changes "$1" && echo "dirty") - elif git_is_file_tracked "$1" ; then - r=$(git_last_commit "$1") - - local svn_base=$(git log --grep=git-svn-id -1 --format='%h') - if [ "$svn_base" != "" ] ; then - local diff_to_svn=$(git rev-list --count ${svn_base}..${r}) - if [ "$diff_to_svn" -gt 0 ] ; then - r="$r-$diff_to_svn" - fi - fi - - if git_has_local_changes "$1" ; then - r="$r-dirty" - fi - else - return ${EXIT_FAILURE} + if git_is_file_tracked "$1" ; then + local sha=$(git_last_commit "$1") + t=$(git describe --abbrev=0 "$sha") fi - - echo "${r}" + if [ -z "$t" ]; then + t="unknown" # default to unknown + fi + echo "${t}" }
-# Get the upstream flashrom revision stored in SVN metadata. -upstream_revision() { - local r= +find_upremote() { + # Try to find upstream's remote name + for remote in $(git remote) ; do + local url=$(git ls-remote --get-url $remote) + if echo "$url" | grep -q -E "$upstream_patterns" ; then + echo "$remote" + return + fi + done +}
- if svn_is_file_tracked "$1" ; then - r=$(svn info "$1" 2>/dev/null | \ - grep "Last Changed Rev:" | \ - sed -e "s/^Last Changed Rev: *//" -e "s/([0-9]*).*/r\1/" | \ - grep "r[0-9]") - elif git_is_file_tracked "$1" ; then - # If this is a "native" git-svn clone we could use git svn log: - # git svn log --oneline -1 | sed 's/^r//;s/[[:blank:]].*//' or even git svn find-rev - # but it is easier to just grep for the git-svn-id unconditionally - r=$(git log --grep=git-svn-id -1 -- "$1" | \ - grep git-svn-id | \ - sed 's/.*@/r/;s/[[:blank:]].*//') +revision() { + local sha=$(git_last_commit "$1" 2>/dev/null) + # No git no fun + if [ -z "$sha" ]; then + echo "unknown" + return fi
- if [ -z "$r" ]; then - r="unknown" # default to unknown + local r="$sha" + if git_has_local_changes "$1" ; then + r="$r-dirty" fi + + # sha + possibly dirty info is not exactly verbose, therefore the code below tries to use tags and + # branches from the upstream repos to derive a more previse version string. + # To that end we try to use the existing remotes first. + # If the upstream repos (and its mirrors) are not available as remotes, use a shadow copy instead. + + local up_refs + local up_remote=$(find_upremote) + if [ -n "$up_remote" ]; then + for b in $upstream_branches ; do + up_refs="$up_refs ${up_remote}/${b}" + done + else + update || { echo "offline" ; return ; } + up_refs=$upcache_refs + fi + + # Find nearest commit contained in this branch that is also in any of the up_refs, i.e. the branch point + # of the current branch. This might be the latest commit if it's in any of the upstream branches. + local merge_point=$(git merge-base ${sha} ${up_refs}) + local upstream_branch + if [ -z "$merge_point" ]; then + echo "$sha" + return + fi + + # If the current commit is reachable from any remote branch, append the branch name and its + # distance to the nearest earlier tag to that tag name itself (tag-distance-branch). + # If multiple branches are reachable then we use the newest one (by commit date). + # If none is reachable we use the nearest tag and ? for distances and remote branch name. + + local cnt_upstream_branch2sha=$(git rev-list --count "${merge_point}..${sha}" 2>/dev/null) + + local lasttag=$(git describe --abbrev=0 "$merge_point" 2>/dev/null) + if [ -z "$lasttag" ]; then + echo "Could not find tag reachable from merge point!">&2 + echo "$sha" + return + fi + + local cnt_tag2upstream_branch + for ref in $up_refs ; do + if git merge-base --is-ancestor ${merge_point} ${ref}; then + upstream_branch=${ref##*/} # remove everything till last / + cnt_tag2upstream_branch=$(git rev-list --count "${lasttag}..${merge_point}" 2>/dev/null) + break + fi + done + + if [ "$cnt_upstream_branch2sha" -gt 0 ]; then + r="$cnt_upstream_branch2sha-$r" + fi + if [ "$cnt_tag2upstream_branch" -gt 0 ]; then + if [ -n "$upstream_branch" ]; then + r="$upstream_branch-$r" + fi + r="$cnt_tag2upstream_branch-$r" + fi + r="$lasttag-$r" + echo "${r}" }
@@ -228,16 +289,20 @@ this message -c or --check test if path is under version control at all - -l or --local - local revision information including an indicator for uncommitted changes - -u or --upstream - upstream revision - -U or --url + -T or --tag + returns the name of the last release/tag + -r or --revision + return unique revision information including the last tag and an indicator for uncommitted changes + -u or --url URL associated with the latest commit -d or --date date of most recent modification -t or --timestamp timestamp of most recent modification + -U or --update + update local shadow copy of upstream commits if need be and offline builds are not enforced + --forced-update + force updating the local shadow copy of upstream commits " return } @@ -260,15 +325,15 @@ -h|--help) action=show_help; shift;; - -l|--local) + -T|--tag) check_action $1 - action=local_revision + action=tag shift;; - -u|--upstream) + -r|--revision) check_action $1 - action=upstream_revision + action=revision shift;; - -U|--url) + -u|--url) check_action $1 action=scm_url shift;; @@ -280,9 +345,17 @@ check_action $1 action="timestamp +%Y-%m-%dT%H:%M:%SZ" # There is only one valid time format! ISO 8601 shift;; + -U|--update) + check_action $1 + action=update + shift;; + --forced-update) + check_action $1 + action=forced_update + shift;; -c|--check) - check_action=$1 - action="is_tracked" + check_action $1 + action=is_tracked shift;; -*) show_help; @@ -305,9 +378,6 @@ # default to current directory (usually equals the whole repository) if [ -z "$query_path" ] ; then query_path=. - fi - if ! is_file_tracked "$query_path" ; then - echo "Warning: Path "${query_path}" is not under version control.">&2 fi if [ -z "$action" ] ; then show_help diff --git a/util/git-hooks/applypatch-msg b/util/git-hooks/applypatch-msg new file mode 100755 index 0000000..32ff6c7 --- /dev/null +++ b/util/git-hooks/applypatch-msg @@ -0,0 +1,15 @@ +#!/bin/sh +# +# A hook script to check the commit log message taken by +# applypatch from an e-mail message (via git-am). +# We simply do the same as for other commit messages +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# + +. git-sh-setup +test -x "$GIT_DIR/hooks/commit-msg" && + exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} +: diff --git a/util/git-hooks/commit-msg b/util/git-hooks/commit-msg new file mode 100755 index 0000000..82f0581 --- /dev/null +++ b/util/git-hooks/commit-msg @@ -0,0 +1,173 @@ +#!/bin/sh +# +# Part of Gerrit Code Review (http://code.google.com/p/gerrit/) +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +CHANGE_ID_AFTER="Bug|Issue" +MSG="$1" + +# Check for, and add if missing, a unique Change-Id +# +add_ChangeId() { + clean_message=`sed -e ' + /^diff --git a/.*/{ + s/// + q + } + /^Signed-off-by:/d + /^#/d + ' "$MSG" | git stripspace` + if test -z "$clean_message" + then + return + fi + + # Does Change-Id: already exist? if so, exit (no change). + if grep -i '^Change-Id: I[0-9a-f]{40}$' "$MSG" >/dev/null + then + return + fi + + id=`_gen_ChangeId` + T="$MSG.tmp.$$" + AWK=awk + if [ -x /usr/xpg4/bin/awk ]; then + # Solaris AWK is just too broken + AWK=/usr/xpg4/bin/awk + fi + + # How this works: + # - parse the commit message as (textLine+ blankLine*)* + # - assume textLine+ to be a footer until proven otherwise + # - exception: the first block is not footer (as it is the title) + # - read textLine+ into a variable + # - then count blankLines + # - once the next textLine appears, print textLine+ blankLine* as these + # aren't footer + # - in END, the last textLine+ block is available for footer parsing + $AWK ' + BEGIN { + # while we start with the assumption that textLine+ + # is a footer, the first block is not. + isFooter = 0 + footerComment = 0 + blankLines = 0 + } + + # Skip lines starting with "#" without any spaces before it. + /^#/ { next } + + # Skip the line starting with the diff command and everything after it, + # up to the end of the file, assuming it is only patch data. + # If more than one line before the diff was empty, strip all but one. + /^diff --git a/ { + blankLines = 0 + while (getline) { } + next + } + + # Count blank lines outside footer comments + /^$/ && (footerComment == 0) { + blankLines++ + next + } + + # Catch footer comment + /^[[a-zA-Z0-9-]+:/ && (isFooter == 1) { + footerComment = 1 + } + + /]$/ && (footerComment == 1) { + footerComment = 2 + } + + # We have a non-blank line after blank lines. Handle this. + (blankLines > 0) { + print lines + for (i = 0; i < blankLines; i++) { + print "" + } + + lines = "" + blankLines = 0 + isFooter = 1 + footerComment = 0 + } + + # Detect that the current block is not the footer + (footerComment == 0) && (!/^[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:///) { + isFooter = 0 + } + + { + # We need this information about the current last comment line + if (footerComment == 2) { + footerComment = 0 + } + if (lines != "") { + lines = lines "\n"; + } + lines = lines $0 + } + + # Footer handling: + # If the last block is considered a footer, splice in the Change-Id at the + # right place. + # Look for the right place to inject Change-Id by considering + # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first, + # then Change-Id, then everything else (eg. Signed-off-by:). + # + # Otherwise just print the last block, a new line and the Change-Id as a + # block of its own. + END { + unprinted = 1 + if (isFooter == 0) { + print lines "\n" + lines = "" + } + changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):" + numlines = split(lines, footer, "\n") + for (line = 1; line <= numlines; line++) { + if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) { + unprinted = 0 + print "Change-Id: I'"$id"'" + } + print footer[line] + } + if (unprinted) { + print "Change-Id: I'"$id"'" + } + }' "$MSG" > $T && mv $T "$MSG" || rm -f $T +} +_gen_ChangeIdInput() { + echo "tree `git write-tree`" + if parent=`git rev-parse "HEAD^0" 2>/dev/null` + then + echo "parent $parent" + fi + echo "author `git var GIT_AUTHOR_IDENT`" + echo "committer `git var GIT_COMMITTER_IDENT`" + echo + printf '%s' "$clean_message" +} +_gen_ChangeId() { + _gen_ChangeIdInput | + git hash-object -t commit --stdin +} + + +add_ChangeId diff --git a/util/git-hooks/install.sh b/util/git-hooks/install.sh new file mode 100755 index 0000000..e216af4 --- /dev/null +++ b/util/git-hooks/install.sh @@ -0,0 +1,19 @@ +#!/bin/sh -e + +root=$(git rev-parse --show-cdup 2>/dev/null) || \ + { echo "Not under git control. Cannot install git hooks." >&2 ; exit 0 ; } + +dst="${root}"$(git rev-parse --git-path hooks/) +src=util/git-hooks/ # relative to root +hooks=$(cd "${root}${src}" && git ls-files -c | grep -Ev 'install.sh|wrapper.sh') + +for h in $hooks; do + # Test if hook is not already installed, i.e. doesn't point at the wrapper + if [ ! "${dst}$h" -ef "${root}${src}wrapper.sh" ]; then + # preserve custom hooks if any + if [ -e "${dst}$h" ]; then + mv "${dst}$h" "${dst}$h.local" + fi + ln -s "../../${src}wrapper.sh" "${dst}$h" + fi +done diff --git a/util/git-hooks/pre-applypatch b/util/git-hooks/pre-applypatch new file mode 100755 index 0000000..2ed28f7 --- /dev/null +++ b/util/git-hooks/pre-applypatch @@ -0,0 +1,12 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +: diff --git a/util/git-hooks/pre-commit b/util/git-hooks/pre-commit new file mode 100755 index 0000000..dbccb9e --- /dev/null +++ b/util/git-hooks/pre-commit @@ -0,0 +1,9 @@ +#!/bin/sh +# +# A hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. + +# Check for whitespace errors +git diff-index --check --cached HEAD -- || exit 1 diff --git a/util/git-hooks/pre-push b/util/git-hooks/pre-push new file mode 100755 index 0000000..b7e34ee --- /dev/null +++ b/util/git-hooks/pre-push @@ -0,0 +1,74 @@ +#!/bin/sh + +# A hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# <local ref> <local sha1> <remote ref> <remote sha1> + +remote="$1" +url="$2" + +zero=0000000000000000000000000000000000000000 + +upstream_pattern="github.com.flashrom/flashrom(.git)?|flashrom.org.git/flashrom(.git)?" +precious_branches=( stable staging ) + +# Only care about the upstream repository +if echo "$url" | grep -q -v -E "$upstream_pattern" ; then + exit 0 +fi + +while read local_ref local_sha remote_ref remote_sha ; do + if [ "$remote_ref" != "refs/heads/staging" -a "$remote_ref" != "refs/heads/stable" ]; then + echo "Feature branches not allowed ($remote_ref)." >&2 + exit 1 + fi + + if [ "$local_sha" = $zero ]; then + echo "Deletion of branches is prohibited." >&2 + exit 1 + else + if [ "$remote_sha" = "$zero" ]; then + echo "No new branches allowed." >&2 + exit 1 + else + range="$remote_sha..$local_sha" + fi + + # Check for Signed-off-by and Acked-by + commit=$(git rev-list -n 1 --all-match --invert-grep -E \ + --grep '^Signed-off-by: .+ <.+@.+..+>$' --grep '^Acked-by: .+ <.+@.+..+>$' "$range") + if [ -n "$commit" ]; then + echo "No Signoff or Ack found in commit $local_sha in $local_ref, not pushing." >&2 + exit 1 + fi + + # Make _really_ sure we do not rewrite precious history + for lbranch in "${precious_branches[@]}"; do + if [ "$remote_ref" = "refs/heads/$lbranch" ]; then + nonreachable=$(git rev-list $remote_sha ^$local_sha) + if [ -n "$nonreachable" ]; then + echo "Only fast-forward pushes are allowed on $lbranch." >&2 + echo "$nonreachable is not included in $remote_sha while pusing to $remote_ref" >&2 + exit 1 + fi + fi + done + + # FIXME: check commit log format (subject without full stop at the end etc). + # FIXME: do buildbot checks if authorized? + fi +done + +exit 0 diff --git a/util/git-hooks/wrapper.sh b/util/git-hooks/wrapper.sh new file mode 100755 index 0000000..3fc3e05 --- /dev/null +++ b/util/git-hooks/wrapper.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +if [ -x $0.local ]; then + $0.local "$@" || exit $? +fi + +hook=$(git rev-parse --show-toplevel)"/util/git-hooks/"$(basename $0) +if [ -x $hook ]; then + $hook "$@" || exit $? +fi