Make hooks/commit-msg available over HTTP

We have to move the old scproot assets into gerrit-server so they
are commonly available to both the SSH and HTTP daemon packages.

Bug: issue 392
Change-Id: Ie0dc95529f26b14535c2e1041863a441333516b3
Signed-off-by: Shawn O. Pearce <sop@google.com>
Reviewed-by: Nico Sallembien <nsallembien@google.com>
This commit is contained in:
Shawn O. Pearce
2010-01-29 12:48:21 -08:00
parent 733791be38
commit 3e4e804248
15 changed files with 445 additions and 145 deletions

View File

@@ -22,25 +22,19 @@
*/
package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.Version;
import com.google.gerrit.server.tools.ToolsCatalog;
import com.google.gerrit.server.tools.ToolsCatalog.Entry;
import com.google.gerrit.sshd.BaseCommand;
import com.google.inject.Inject;
import org.apache.sshd.server.Environment;
import org.eclipse.jgit.util.RawParseUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
final class ScpCommand extends BaseCommand {
private static final String TYPE_DIR = "D";
@@ -52,7 +46,8 @@ final class ScpCommand extends BaseCommand {
private boolean opt_f;
private String root;
private TreeMap<String, Entry> toc;
@Inject
private ToolsCatalog toc;
private IOException error;
@Override
@@ -101,7 +96,6 @@ final class ScpCommand extends BaseCommand {
throw error;
}
readToc();
if (opt_f) {
if (root.startsWith("/")) {
root = root.substring(1);
@@ -117,10 +111,10 @@ final class ScpCommand extends BaseCommand {
if (ent == null) {
throw new IOException(root + " not found");
} else if (TYPE_FILE.equals(ent.type)) {
} else if (Entry.Type.FILE == ent.getType()) {
readFile(ent);
} else if (TYPE_DIR.equals(ent.type)) {
} else if (Entry.Type.DIR == ent.getType()) {
if (!opt_r) {
throw new IOException(root + " not a regular file");
}
@@ -152,43 +146,6 @@ final class ScpCommand extends BaseCommand {
}
}
private void readToc() throws IOException {
toc = new TreeMap<String, Entry>();
final BufferedReader br =
new BufferedReader(new InputStreamReader(new ByteArrayInputStream(
read("TOC")), "UTF-8"));
String line;
while ((line = br.readLine()) != null) {
if (line.length() > 0 && !line.startsWith("#")) {
final Entry e = new Entry(TYPE_FILE, line);
toc.put(e.path, e);
}
}
final List<Entry> all = new ArrayList<Entry>(toc.values());
for (Entry e : all) {
String path = dirOf(e.path);
while (path != null) {
Entry d = toc.get(path);
if (d == null) {
d = new Entry(TYPE_DIR, 0755, path);
toc.put(d.path, d);
}
d.children.add(e);
path = dirOf(path);
e = d;
}
}
final Entry top = new Entry(TYPE_DIR, 0755, "");
for (Entry e : toc.values()) {
if (dirOf(e.path) == null) {
top.children.add(e);
}
}
toc.put(top.path, top);
}
private String readLine() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (;;) {
@@ -203,62 +160,10 @@ final class ScpCommand extends BaseCommand {
}
}
private static String nameOf(String path) {
final int s = path.lastIndexOf('/');
return s < 0 ? path : path.substring(s + 1);
}
private static String dirOf(String path) {
final int s = path.lastIndexOf('/');
return s < 0 ? null : path.substring(0, s);
}
private static byte[] read(String path) {
final InputStream in =
ScpCommand.class.getClassLoader().getResourceAsStream(
"com/google/gerrit/sshd/scproot/" + path);
if (in == null) {
return null;
}
try {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
final byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf, 0, buf.length)) > 0) {
out.write(buf, 0, n);
}
} finally {
in.close();
}
return out.toByteArray();
} catch (Exception e) {
log.debug("Cannot read " + path, e);
return null;
}
}
private void readFile(final Entry ent) throws IOException {
byte[] data = read(ent.path);
byte[] data = ent.getBytes();
if (data == null) {
throw new FileNotFoundException(ent.path);
}
if (data.length > 3 && data[0] == '#' && data[1] == '!' && data[2] == '/') {
// Embed Gerrit's version number into the top of the script.
//
final String version = Version.getVersion();
final int lf = RawParseUtils.nextLF(data, 0);
if (version != null && lf < data.length) {
final byte[] versionHeader =
("# From Gerrit Code Review " + version + "\n").getBytes("UTF-8");
final ByteArrayOutputStream buf;
buf = new ByteArrayOutputStream(data.length + versionHeader.length);
buf.write(data, 0, lf);
buf.write(versionHeader);
buf.write(data, lf, data.length - lf);
data = buf.toByteArray();
}
throw new FileNotFoundException(ent.getPath());
}
header(ent, data.length);
@@ -273,8 +178,8 @@ final class ScpCommand extends BaseCommand {
header(dir, 0);
readAck();
for (Entry e : dir.children) {
if (TYPE_DIR.equals(e.type)) {
for (Entry e : dir.getChildren()) {
if (Entry.Type.DIR == e.getType()) {
readDir(e);
} else {
readFile(e);
@@ -289,12 +194,19 @@ final class ScpCommand extends BaseCommand {
private void header(final Entry dir, final int len) throws IOException,
UnsupportedEncodingException {
final StringBuilder buf = new StringBuilder();
buf.append(dir.type);
buf.append(dir.mode); // perms
switch(dir.getType()){
case DIR:
buf.append(TYPE_DIR);
break;
case FILE:
buf.append(TYPE_FILE);
break;
}
buf.append("0" + Integer.toOctalString(dir.getMode())); // perms
buf.append(" ");
buf.append(len); // length
buf.append(" ");
buf.append(nameOf(dir.path));
buf.append(dir.getName());
buf.append("\n");
out.write(buf.toString().getBytes("UTF-8"));
out.flush();
@@ -316,29 +228,4 @@ final class ScpCommand extends BaseCommand {
throw new IOException("Received nack: " + readLine());
}
}
private static class Entry {
String type;
String mode;
String path;
List<Entry> children;
Entry(String type, String line) {
this.type = type;
int s = line.indexOf(' ');
mode = line.substring(0, s);
path = line.substring(s + 1);
if (!mode.startsWith("0")) {
mode = "0" + mode;
}
}
Entry(String type, int mode, String path) {
this.type = type;
this.mode = "0" + Integer.toOctalString(mode);
this.path = path;
this.children = new ArrayList<Entry>();
}
}
}

View File

@@ -1,6 +0,0 @@
# Format of this file is:
# mode <SPACE> fullpath
#
755 bin/gerrit-cherry-pick
755 hooks/commit-msg

View File

@@ -1,208 +0,0 @@
#!/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.
#
usage() {
echo >&2 "usage: $0 remote changeid..."
echo >&2 "usage: $0 --continue"
echo >&2 "usage: $0 --skip"
echo >&2 "usage: $0 --abort"
echo >&2 "usage: $0 [--close|--replace] remote"
exit 1
}
die() {
echo >&2 "fatal: $1"
exit 1
}
GIT_DIR=$(git rev-parse --git-dir) || exit
CL="$GIT_DIR/GERRIT_CHANGES"
STATE="$GIT_DIR/rebase-gerrit"
TODO="$STATE/todo"
RESOLVEMSG="
When you have resolved this problem run \"$0 --continue\".
If you would prefer to skip this patch, run \"$0 --skip\".
"
pop_action() {
sed -e 1d <"$TODO" >>"$TODO".new
mv -f "$TODO".new "$TODO"
}
mark_done() {
read commit changeid <"$TODO"
changeid=$(get_changeid "$changeid")
head_after=$(git rev-parse HEAD^0)
head_before=$(cat "$STATE/head_before")
if ! test $head_after = $head_before
then
echo $head_after >"$CL/$changeid"
fi
pop_action
}
do_next() {
while test -s "$TODO"
do
read commit changeid <"$TODO"
git rev-parse HEAD^0 >"$STATE/head_before"
git format-patch \
-k --stdout --full-index --ignore-if-in-upstream \
$commit^..$commit |
git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" || exit
mark_done
done
echo >&2 "Done."
rm -rf "$STATE"
}
git_am_opt=
if test -f "$STATE/git_am_opt"
then
git_am_opt=$(cat "$STATE/git_am_opt")
fi
while test $# != 0
do
case "$1" in
--continue)
test -f "$TODO" || die "No cherry-pick in progress?"
git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" --resolved || exit
mark_done
do_next
exit
;;
--skip)
test -f "$TODO" || die "No cherry-pick in progress?"
git reset --hard HEAD || exit
git am --skip || exit
pop_action
do_next
exit
;;
--abort)
test -f "$TODO" || die "No cherry-pick in progress?"
git reset --hard HEAD
git am --skip
rm -rf "$STATE"
;;
--close|--replace)
shift
test -d "$CL" || die "No changes to close"
test $# = 1 || usage
remote=$1
printf %s "git push $remote" >&2
rs=$(cd "$CL" && for change_id in *; do
test "$change_id" = '*' && die "No changes to close"
c=$(cat "$change_id");
echo "$c:refs/changes/$change_id";
echo ' \' >&2;
printf %s " $c:refs/changes/$change_id" >&2
done)
echo >&2
echo >&2
git push $remote $rs
rc=$?
test $rc = 0 && rm -rf "$CL"
exit $rc
;;
--whitespace=*)
git_am_opt="$git_am_opt $1"
;;
--committer-date-is-author-date|--ignore-date)
git_am_opt="$git_am_opt $1"
;;
-C*)
git_am_opt="$git_am_opt $1"
;;
-s|--signoff)
git_am_opt="$git_am_opt $1"
;;
-*)
usage
;;
*)
break
;;
esac
shift
done
get_changeid() {
case $1 in
*/*) echo ${1%%/*} ;;
*) echo $1 ;;
esac
}
to_ref() {
case $1 in
*/*)
change_id=${1%%/*}
patchset_id=${1##*/}
;;
*)
change_id=$1
patchset_id=1
;;
esac
hash=$(($change_id % 100))
case $hash in
[0-9]) hash="0$hash" ;;
esac
echo "refs/changes/$hash/$change_id/$patchset_id"
}
get_revid() {
grep $(to_ref $1) <"$GIT_DIR/FETCH_HEAD" | cut -f1
}
# Initialize state
#
test $# -lt 2 && usage
remote="$1"
shift
mkdir "$STATE" || die "cherry-pick already in progress"
echo $git_am_opt >"$STATE/git_am_opt"
if ! git fetch $remote $(for id; do to_ref $id; done)
then
rm -rf $STATE
exit 1
fi
(for id
do
if revid=$(get_revid $id)
then
echo "$revid $id"
else
echo >&2 "fatal: $id not found"
exit 1
fi
done) >"$TODO"
mkdir -p "$CL"
echo >&2
do_next

View File

@@ -1,100 +0,0 @@
#!/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
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];
($haveFooter++, next) if /^[a-zA-Z0-9-]+:/;
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