summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2009-06-13 19:49:04 -0700
committerShawn O. Pearce <sop@google.com>2009-06-13 19:49:04 -0700
commit1a47d583d9e21170449a8611da82c62f95a3ca3a (patch)
treebffcec2fa1573df1a8ee7215fded1efca42a3779
parentd443dc3d6e41f16420e65af5995536e5f4dcbcca (diff)
Create gerrit-cherry-pick for client usage
End users can now obtain the gerrit-cherry-pick script by fetching it with scp: scp -P 29418 review.source.android.com:bin/gerrit-cherry-pick ~/bin Arguments passed to the script represent changes or patch sets like repo download would accept. The script executes git fetch to ensure the objects are available locally, then passes the changes through git am to apply them to the current branch. Common git am workflow is supported, including adding a new Signed-off-by line for the caller, and fixing broken whitespace. Later the cherry-picked changes can be automatically closed out by uploading them back as replacement patch sets. Signed-off-by: Shawn O. Pearce <sop@google.com>
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/scproot/TOC1
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/scproot/bin/gerrit-cherry-pick206
2 files changed, 207 insertions, 0 deletions
diff --git a/src/main/java/com/google/gerrit/server/ssh/scproot/TOC b/src/main/java/com/google/gerrit/server/ssh/scproot/TOC
index e2c5dfac80..9200ff61a8 100644
--- a/src/main/java/com/google/gerrit/server/ssh/scproot/TOC
+++ b/src/main/java/com/google/gerrit/server/ssh/scproot/TOC
@@ -1,3 +1,4 @@
# Format of this file is:
# mode <SPACE> fullpath
#
+755 bin/gerrit-cherry-pick
diff --git a/src/main/java/com/google/gerrit/server/ssh/scproot/bin/gerrit-cherry-pick b/src/main/java/com/google/gerrit/server/ssh/scproot/bin/gerrit-cherry-pick
new file mode 100644
index 0000000000..ceddff8a01
--- /dev/null
+++ b/src/main/java/com/google/gerrit/server/ssh/scproot/bin/gerrit-cherry-pick
@@ -0,0 +1,206 @@
+#!/bin/sh
+#
+# 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