initial commit
diff --git a/bake.go b/bake.go
new file mode 100644
index 0000000..e94a53d
--- /dev/null
+++ b/bake.go
@@ -0,0 +1,75 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Command bake takes a list of file names and writes a Go source file
+// "baked.go" that declares a map of string constants containing the input
+// files.
+//
+// For example, the command
+// 	bake foo.html bar.txt
+// produces a source file that declares the variable bakedFiles
+// that is a map with keys "foo.html" and "bar.txt" that contain the contents
+// of foo.html and bar.txt.
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"unicode/utf8"
+)
+
+func main() {
+	if err := bake(os.Args[1:]); err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+}
+
+func bake(files []string) error {
+	f, err := os.Create("baked.go")
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	w := bufio.NewWriter(f)
+	fmt.Fprintf(w, "%v\n\npackage main\n\n", warning)
+	fmt.Fprintf(w, "var baked = map[string]string{\n")
+	for _, fn := range files {
+		b, err := ioutil.ReadFile(fn)
+		if err != nil {
+			return err
+		}
+		fmt.Fprintf(w, "\t%q: ", fn)
+		if utf8.Valid(b) {
+			fmt.Fprintf(w, "`%s`", sanitize(b))
+		} else {
+			fmt.Fprintf(w, "%q", b)
+		}
+		fmt.Fprintln(w, ",\n")
+	}
+	fmt.Fprintln(w, "}")
+	if err := w.Flush(); err != nil {
+		return err
+	}
+	return f.Close()
+}
+
+// sanitize prepares a valid UTF-8 string as a raw string constant.
+func sanitize(b []byte) []byte {
+	// Replace ` with `+"`"+`
+	b = bytes.Replace(b, []byte("`"), []byte("`+\"`\"+`"), -1)
+
+	// Replace BOM with `+"\xEF\xBB\xBF"+`
+	// (A BOM is valid UTF-8 but not permitted in Go source files.
+	// I wouldn't bother handling this, but for some insane reason
+	// jquery.js has a BOM somewhere in the middle.)
+	return bytes.Replace(b, []byte("\xEF\xBB\xBF"), []byte("`+\"\\xEF\\xBB\\xBF\"+`"), -1)
+}
+
+const warning = "// DO NOT EDIT ** This file was generated with the bake tool ** DO NOT EDIT //"
diff --git a/baked.go b/baked.go
new file mode 100644
index 0000000..243b1a0
--- /dev/null
+++ b/baked.go
@@ -0,0 +1,112 @@
+// DO NOT EDIT ** This file was generated with the bake tool ** DO NOT EDIT //
+
+package main
+
+var baked = map[string]string{
+	"commit-msg.githook": `#!/bin/sh
+# From Gerrit Code Review 2.2.1
+#
+# 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
+
+	if grep -i '^Change-Id:' "$MSG" >/dev/null
+	then
+		return
+	fi
+
+	id=`+"`"+`_gen_ChangeId`+"`"+`
+	perl -e '
+		$MSG = shift;
+		$id = shift;
+		$CHANGE_ID_AFTER = shift;
+
+		undef $/;
+		open(I, $MSG); $_ = <I>; close I;
+		s|^diff --git a/.*||ms;
+		s|^#.*$||mg;
+		exit unless $_;
+
+		@message = split /\n/;
+		$haveFooter = 0;
+		$startFooter = @message;
+		for($line = @message - 1; $line >= 0; $line--) {
+			$_ = $message[$line];
+
+			if (/^[a-zA-Z0-9-]+:/ && !m,^[a-z0-9-]+://,) {
+				$haveFooter++;
+				next;
+			}
+			next if /^[ []/;
+			$startFooter = $line if ($haveFooter && /^\r?$/);
+			last;
+		}
+
+		@footer = @message[$startFooter+1..@message];
+		@message = @message[0..$startFooter];
+		push(@footer, "") unless @footer;
+
+		for ($line = 0; $line < @footer; $line++) {
+			$_ = $footer[$line];
+			next if /^($CHANGE_ID_AFTER):/i;
+			last;
+		}
+		splice(@footer, $line, 0, "Change-Id: I$id");
+
+		$_ = join("\n", @message, @footer);
+		open(O, ">$MSG"); print O; close O;
+	' "$MSG" "$id" "$CHANGE_ID_AFTER"
+}
+_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/commit-msg.githook b/commit-msg.githook
new file mode 100755
index 0000000..985016b
--- /dev/null
+++ b/commit-msg.githook
@@ -0,0 +1,104 @@
+#!/bin/sh
+# From Gerrit Code Review 2.2.1
+#
+# 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
+
+	if grep -i '^Change-Id:' "$MSG" >/dev/null
+	then
+		return
+	fi
+
+	id=`_gen_ChangeId`
+	perl -e '
+		$MSG = shift;
+		$id = shift;
+		$CHANGE_ID_AFTER = shift;
+
+		undef $/;
+		open(I, $MSG); $_ = <I>; close I;
+		s|^diff --git a/.*||ms;
+		s|^#.*$||mg;
+		exit unless $_;
+
+		@message = split /\n/;
+		$haveFooter = 0;
+		$startFooter = @message;
+		for($line = @message - 1; $line >= 0; $line--) {
+			$_ = $message[$line];
+
+			if (/^[a-zA-Z0-9-]+:/ && !m,^[a-z0-9-]+://,) {
+				$haveFooter++;
+				next;
+			}
+			next if /^[ []/;
+			$startFooter = $line if ($haveFooter && /^\r?$/);
+			last;
+		}
+
+		@footer = @message[$startFooter+1..@message];
+		@message = @message[0..$startFooter];
+		push(@footer, "") unless @footer;
+
+		for ($line = 0; $line < @footer; $line++) {
+			$_ = $footer[$line];
+			next if /^($CHANGE_ID_AFTER):/i;
+			last;
+		}
+		splice(@footer, $line, 0, "Change-Id: I$id");
+
+		$_ = join("\n", @message, @footer);
+		open(O, ">$MSG"); print O; close O;
+	' "$MSG" "$id" "$CHANGE_ID_AFTER"
+}
+_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/review.go b/review.go
new file mode 100644
index 0000000..9a3698c
--- /dev/null
+++ b/review.go
@@ -0,0 +1,110 @@
+/*
+Copyright 2014 The Camlistore Authors.
+
+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.
+*/
+
+package main
+
+//go:generate go run bake.go commit-msg.githook
+
+import (
+	"bufio"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+)
+
+var hookFile = filepath.FromSlash(".git/hooks/commit-msg")
+
+func main() {
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, "Usage: go-review\n")
+		os.Exit(2)
+	}
+	flag.Parse()
+	if len(flag.Args()) > 0 {
+		flag.Usage()
+	}
+	goToRepoRoot()
+	checkHook()
+	gitPush()
+}
+
+func goToRepoRoot() {
+	prevDir, err := os.Getwd()
+	if err != nil {
+		dief("could not get current directory: %v\n", err)
+	}
+	for {
+		if _, err := os.Stat(".git"); err == nil {
+			return
+		}
+		if err := os.Chdir(".."); err != nil {
+			dief("could not chdir: %v\n", err)
+		}
+		currentDir, err := os.Getwd()
+		if err != nil {
+			dief("could not get current directory: %v\n", err)
+		}
+		if currentDir == prevDir {
+			dief("Git root not found. Run from within the Git tree please.\n")
+		}
+		prevDir = currentDir
+	}
+}
+
+func checkHook() {
+	_, err := os.Stat(hookFile)
+	if err == nil {
+		return
+	}
+	if !os.IsNotExist(err) {
+		dief("checking for hook file: %v\n", err)
+	}
+	fmt.Printf("Presubmit hook to add Change-Id to commit messages is missing.\nNow automatically creating it at %v.\n\n", hookFile)
+	hookContent := []byte(baked["commit-msg.githook"])
+	if err := ioutil.WriteFile(hookFile, hookContent, 0700); err != nil {
+		dief("writing hook file: %v\n", err)
+	}
+	fmt.Printf("Amending last commit to add Change-Id.\nPlease re-save description without making changes.\n\n")
+	fmt.Printf("Press Enter to continue.\n")
+	if _, _, err := bufio.NewReader(os.Stdin).ReadLine(); err != nil {
+		dief("waiting for user input: %v\n", err)
+	}
+
+	cmd := exec.Command("git", []string{"commit", "--amend"}...)
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Run(); err != nil {
+		dief("amending commit: %v\n", err)
+	}
+}
+
+func gitPush() {
+	cmd := exec.Command("git",
+		[]string{"push", "https://camlistore.googlesource.com/camlistore", "HEAD:refs/for/master"}...)
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Run(); err != nil {
+		dief("Could not git push: %v\n", err)
+	}
+}
+
+func dief(format string, args ...interface{}) {
+	fmt.Fprintf(os.Stderr, format, args...)
+	os.Exit(1)
+}