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:
@@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# Format of this file is:
|
||||
# mode <SPACE> fullpath
|
||||
#
|
||||
755 bin/gerrit-cherry-pick
|
||||
|
||||
755 hooks/commit-msg
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user