diff options
author | Andrew Cady <d@jerkface.net> | 2018-09-11 15:28:58 -0400 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2018-09-11 15:40:55 -0400 |
commit | f864cade7f6dcdb7aef4fbcdb6ac17c008a09e03 (patch) | |
tree | 4099fba9365e5498f7b5696682a9fabce0a60d35 /dot/local/bin/git-push-clone | |
parent | 886b5f105cae8addb9a0ac5a5302dd4c28c0895c (diff) |
git-push-clone
Diffstat (limited to 'dot/local/bin/git-push-clone')
-rwxr-xr-x | dot/local/bin/git-push-clone | 148 |
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 | ||
2 | set -e | ||
3 | |||
4 | usage() | ||
5 | { | ||
6 | cat <<EOF | ||
7 | Usage: | ||
8 | $0 [git-push options] <destination> [refspec] | ||
9 | EOF | ||
10 | } | ||
11 | |||
12 | die() { printf "Error: %s\n" "$*" >&2; exit 1; } | ||
13 | |||
14 | loudly() { (set -x; "$@"); } | ||
15 | |||
16 | quietly() { "$@" >/dev/null 2>&1; } | ||
17 | |||
18 | bad_usage() | ||
19 | { | ||
20 | usage >&2 | ||
21 | exit 1 | ||
22 | } | ||
23 | |||
24 | check_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 | |||
35 | git_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 | |||
49 | go() | ||
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 | |||
59 | check_source_ref() | ||
60 | { | ||
61 | local src="$1" | ||
62 | git rev-parse --verify --quiet "$src^{commit}" >/dev/null | ||
63 | } | ||
64 | |||
65 | pop=$(cat <<'EOF' | ||
66 | i=$#; | ||
67 | while [ $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 | ||
76 | done | ||
77 | EOF | ||
78 | ) | ||
79 | |||
80 | parse_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 | |||
95 | parse_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 | |||
108 | current_branch() { git status -bs|sed -ne 's/^## //p'; } | ||
109 | |||
110 | REFSPEC_UNNEEDED= | ||
111 | option_count=0 | ||
112 | for 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)) | ||
121 | done | ||
122 | arg_count=$(($# - option_count)) | ||
123 | |||
124 | case $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;; | ||
143 | esac | ||
144 | |||
145 | go "$@" | ||
146 | |||
147 | loudly rsync ${NO_ACT:+-n} -zrlp --info=stats1 "$TEMP_DIR"/"${BARE:+.git/}" "$rsync_dest"/ | ||
148 | |||