summaryrefslogtreecommitdiff
path: root/dot/local/bin/git-push-clone
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2018-09-11 15:28:58 -0400
committerAndrew Cady <d@jerkface.net>2018-09-11 15:40:55 -0400
commitf864cade7f6dcdb7aef4fbcdb6ac17c008a09e03 (patch)
tree4099fba9365e5498f7b5696682a9fabce0a60d35 /dot/local/bin/git-push-clone
parent886b5f105cae8addb9a0ac5a5302dd4c28c0895c (diff)
git-push-clone
Diffstat (limited to 'dot/local/bin/git-push-clone')
-rwxr-xr-xdot/local/bin/git-push-clone148
1 files changed, 148 insertions, 0 deletions
diff --git a/dot/local/bin/git-push-clone b/dot/local/bin/git-push-clone
new file mode 100755
index 0000000..06d045e
--- /dev/null
+++ b/dot/local/bin/git-push-clone
@@ -0,0 +1,148 @@
1#!/bin/sh
2set -e
3
4usage()
5{
6 cat <<EOF
7Usage:
8 $0 [git-push options] <destination> [refspec]
9EOF
10}
11
12die() { printf "Error: %s\n" "$*" >&2; exit 1; }
13
14loudly() { (set -x; "$@"); }
15
16quietly() { "$@" >/dev/null 2>&1; }
17
18bad_usage()
19{
20 usage >&2
21 exit 1
22}
23
24check_existence()
25{
26 (set +e;
27 quietly rsync "$rsync_dest"
28 case $? in
29 23) return 0;;
30 0) die "Destination already exists: '$rsync_dest'";;
31 *) die "rsync returned $? when checking for destination existence.";;
32 esac) || [ "$NO_ACT" ] || exit
33}
34
35git_init_push_checkout()
36{
37 TEMP_DIR=$(mktemp -d)
38 trap 'rm -rf "$TEMP_DIR"' EXIT
39 git init --bare "$TEMP_DIR"/.git
40 loudly git push "$TEMP_DIR" "$@"
41
42 if [ "$CHECKOUT_BRANCH" -a ! "$BARE" ]; then
43 (cd "$TEMP_DIR";
44 git config --unset core.bare
45 git checkout "$CHECKOUT_BRANCH")
46 fi
47}
48
49go()
50{
51 check_existence
52 git_init_push_checkout "$@"
53}
54
55# What we want to do is push a branch, and then checkout the branch we just
56# pushed. Determining the branch to checkout requires parsing the branch out of
57# the git push args in $@
58
59check_source_ref()
60{
61 local src="$1"
62 git rev-parse --verify --quiet "$src^{commit}" >/dev/null
63}
64
65pop=$(cat <<'EOF'
66i=$#;
67while [ $i -gt 0 ]; do
68 lastarg=$1;
69 shift;
70 i=$((i - 1))
71 if [ $i -eq 0 ]; then
72 break;
73 else
74 set -- "$@" "$lastarg";
75 fi
76done
77EOF
78)
79
80parse_target()
81{
82 case "$1" in
83 ssh://*) rsync_dest=${1#ssh://} ;;
84 *:*) rsync_dest=$1 ;;
85 *) die "Unrecognized git URL: '$1'" ;;
86 esac
87 target=$1
88
89 case "$target" in
90 *[^/].git) BARE=y ;;
91 *) BARE= ;;
92 esac
93}
94
95parse_refspec()
96{
97 local refspec="$1"
98 case "$refspec" in
99 *:*:*) die "expected <local-ref>:<remote-ref> but got too many colons" ;;
100 ?*:?*)
101 check_source_ref "${1%:*}" || die "invalid source ref: $src"
102 CHECKOUT_BRANCH=${1#*:}
103 ;;
104 *) if check_source_ref "$1"; then CHECKOUT_BRANCH=$1; fi;;
105 esac
106}
107
108current_branch() { git status -bs|sed -ne 's/^## //p'; }
109
110REFSPEC_UNNEEDED=
111option_count=0
112for arg in "$@"; do
113 case "$arg" in
114 -h|--help|--usage) usage; exit;;
115 --all|--mirror|--tags) REFSPEC_UNNEEDED=y;;
116 --) option_count=$((option_count+1)); break;;
117 -*) ;;
118 *) break;;
119 esac
120 option_count=$((option_count+1))
121done
122arg_count=$(($# - option_count))
123
124case $arg_count in
125 1)
126 eval "$pop"
127 parse_target "$lastarg"
128 b=$(current_branch)
129 parse_refspec "$b:$b"
130 if ! [ "$REFSPEC_UNNEEDED" ]; then
131 set -- "$@" "$b"
132 fi
133 ;;
134 2)
135 eval "$pop"
136 refspec=$lastarg
137 parse_refspec "$refspec"
138 eval "$pop"
139 parse_target "$lastarg"
140 set -- "$@" "$refspec"
141 ;;
142 *) bad_usage;;
143esac
144
145go "$@"
146
147loudly rsync ${NO_ACT:+-n} -zrlp --info=stats1 "$TEMP_DIR"/"${BARE:+.git/}" "$rsync_dest"/
148