-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathgit-private-push
executable file
·190 lines (152 loc) · 5.43 KB
/
git-private-push
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/bin/bash
# KISS!
# - Want multiple privates!
# - Want to make it easy to private-push all configured SRC/DST pairs (only when needed!)
# Option parsing:
# TODO: my option parsing sucks!
# recognize -iID, -i=ID ?
# recognize -fiID ?
# Configuration?
# TODO: when SRC is given, shall DST be found in push configs?
# TODO: add branch.BRANCH.private.ID.dest(?)
# TODO: Add option to override default no-force setting. (per ID only!)
# UX:-)
# TODO: Add git-private-set [--remote REMOTE] [--path PATH] [--tsfmt TSFMT] [--id ID] - easy!
# TODO: Add git-private-add-push [--id ID] SRC [DST] - easy!
# ???
# TODO: do not push branch if no changes were made from last push to same DST :-/ - bit more tricky! Keep in config? :-/ Does not seem the best option - it is not configuration, it is property of remote repo!
# TODO: Add private tags? (Use them to annotate branches?)
# TODO: Is there a short option to fetch remote defined by URL?
# TODO: Is there an easy way to fetch all remotes for all IDs?
DEFAULT_ID='junk'
DEFAULT_TSFMT='%Y/%m/%d/%H/'
DEFAULT_FMT=''
usage() {
echo "Usage: git-private-push [-i|--id ID] [-f|--force] [-F|--no-force] [SRC [DST]]"
}
helpme() {
cat <<END
git-private-push - push to a "hidden" remote branch.
$(usage)
ARGUMENTS
SRC - source ref (like 'remote/branch').
DST - final path element (like 'remote/branch'). When empty SRC is used.
OPTIONS
--id ID - identifier allowing more private configs: junk, wip, archive each
with own repo, tsfmt, path and default push pairs.
Default is taken from 'private.id' or '$DEFAULT_ID'.
CONFIGURATION
private.id - default ID. Default is '$DEFAULT_ID'.
private.fmt - default path format. Strings '\$TS', '\$USER', '\$EMAIL'
and '\SDST' are replaced with corresponding values.
These strings are replaced in '\$TS' but not in any other
of the values.
Default: '\$TS\$DST'.
private.tsfmt - default timestamp format for all IDs.[1]
Default is '$DEFAULT_TSFMT'.
private.remote - default remote for all IDs. Default is '\$ID'.
private.\$ID.fmt - path format.
private.\$ID.remote - remote used for private brnaches. 'private.remote' is
used when undefined. This can be a remote name or URL.
private.\$ID.path - path on the remote.[1] Default is '\$ID/'.
private.\$ID.tsfmt - timestamp format.[1] 'private.tsfmt' is used when
undefined.
private.\$ID.force - force push if the remote ref already exists. Default is
empty.
private.\$ID.push - define SRC [DST] used when no arguments are given on
command line. Multiple of these can be defined as:
git config private.\$ID.push SRC [DST]
[1]: Use a trailing slash in 'path' and 'tsfmt' to prevent joining with next
path element.
NOTES
Remember: These branches are hidden and offer no privacy at all!
This operates as if following command was given on command line:
git push <REMOTE> <SRC>:refs/<PATH>\$(date <TIMESTAMP>)<DST>
Junk is not fetched by default, and even when fetched, it not displayed by
tools unless '--all' switch is used.
To fetch them add following config option:
git config --add remote.\$REMOTE.fetch "+refs/\$ID/*:refs/\$ID/\$REMOTE"
Also gitk displays these refs in a nice grayish color :-)
END
}
private_push() {
local src=$1 dst=${2:-"$1"} ts="$(date "$PRIVATE_TSFMT")"
[[ -n $src ]] || { echo "ERROR: SRC not given!">&2; usage>&2; return 1; }
if [[ -n $PRIVATE_FMT ]]; then
local formatted="$PRIVATE_FMT"
local user="${USER:-"$(id -un)"}"
local email="$(git config --get "user.email")"
if [[ -z $email ]]; then
email="$user"
fi
formatted="${formatted//\$TS/$ts}"
formatted="${formatted//\$USER/${user//\$/}}"
formatted="${formatted//\$EMAIL/${email//\$/}}"
formatted="${formatted//\$DST/$dst}"
else
local formatted="$ts$dst"
fi
git push $GIT_PUSH_OPTS $PRIVATE_REMOTE "$src:refs/${PRIVATE_PATH}${formatted}"
}
private_conf() { git config --get "private.$PRIVATE_ID.$1" || git config --get "private.$1"; }
FORCE="" # Never force by default!
while [[ -n "$*" ]]; do
case "$1" in
--help|-h)
helpme
exit 0
;;
-f|--force)
FORCE=1
;;
-F|--no-force)
FORCE=0
;;
--id=)
PRIVATE_ID=${1#*=}
;;
--id|-i)
shift
PRIVATE_ID=$1
;;
--)
shift
break
;;
-*)
echo "Unknown option '$1'">&2
usage >&2
exit 1
;;
*)
break
;;
esac
shift
done
_id=$(git config --get "private.id")
PRIVATE_ID="${PRIVATE_ID:-"${_id:-"$DEFAULT_ID"}"}"
PRIVATE_REMOTE="$(private_conf remote)"
PRIVATE_REMOTE="${PRIVATE_REMOTE:-"$PRIVATE_ID"}"
PRIVATE_PATH="$(private_conf path)"
PRIVATE_PATH="${PRIVATE_PATH:-"$PRIVATE_ID/"}"
PRIVATE_TSFMT="$(private_conf tsfmt)"
PRIVATE_TSFMT="${PRIVATE_TSFMT:-"$DEFAULT_TSFMT"}"
[[ "$PRIVATE_TSFMT" == +* ]] || PRIVATE_TSFMT="+$PRIVATE_TSFMT"
PRIVATE_FMT="$(private_conf fmt)"
PRIVATE_FMT="${PRIVATE_FMT:-"$DEFAULT_FMT"}"
GIT_PUSH_OPTS=""
if [[ -n "$FORCE" ]]; then
[[ "$FORCE" != 0 ]] && GIT_PUSH_OPTS="$GIT_PUSH_OPTS -f"
else
[[ -n "$(private_conf force)" ]] && GIT_PUSH_OPTS="$GIT_PUSH_OPTS -f"
fi
if [[ -n $* ]]; then
private_push "$@"
else
answ=0
git config --get-all private.$PRIVATE_ID.push | while read; do
private_push $REPLY || answ=1
done
exit $answ
fi