1
+#!/bin/sh
2
+#
3
+# Copyright (c) 2020 Jiang Xin
4
+#
5
+test_description='Test git push porcelain output'
6
+
7
+. ./test-lib.sh
8
+
9
+# Create commits in <repo> and assign each commit's oid to shell variables
10
+# given in the arguments (A, B, and C). E.g.:
11
+#
12
+# create_commits_in <repo> A B C
13
+#
14
+# NOTE: Never calling this function from a subshell since variable
15
+# assignments will disappear when subshell exits.
16
+create_commits_in () {
17
+ repo="$1" &&
18
+ if ! parent=$(git -C "$repo" rev-parse HEAD^{} --)
19
+ then
20
+ parent=
21
+ fi &&
22
+ T=$(git -C "$repo" write-tree) &&
23
+ shift &&
24
+ while test $# -gt 0
25
+ do
26
+ name=$1 &&
27
+ test_tick &&
28
+ if test -z "$parent"
29
+ then
30
+ oid=$(echo $name | git -C "$repo" commit-tree $T)
31
+ else
32
+ oid=$(echo $name | git -C "$repo" commit-tree -p $parent $T)
33
+ fi &&
34
+ eval $name=$oid &&
35
+ parent=$oid &&
36
+ shift ||
37
+ return 1
38
+ done &&
39
+ git -C "$repo" update-ref refs/heads/master $oid
40
+}
41
+
42
+# Format the output of git-push, git-show-ref and other commands to make a
43
+# user-friendly and stable text. We can easily prepare the expect text
44
+# without having to worry about future changes of the commit ID and spaces
45
+# of the output.
46
+make_user_friendly_and_stable_output () {
47
+ sed \
48
+ -e "s/ *\$//" \
49
+ -e "s/ */ /g" \
50
+ -e "s/ / /g" \
51
+ -e "s/$A/<COMMIT-A>/g" \
52
+ -e "s/$B/<COMMIT-B>/g" \
53
+ -e "s/$ZERO_OID/<ZERO-OID>/g" \
54
+ -e "s/$(echo $A | cut -c1-7)[0-9a-f]*/<OID-A>/g" \
55
+ -e "s/$(echo $B | cut -c1-7)[0-9a-f]*/<OID-B>/g" \
56
+ -e "s#To $URL_PREFIX/upstream.git#To <URL/of/upstream.git>#"
57
+}
58
+
59
+setup_upstream_and_workbench () {
60
+ # Upstream after setup : master(B) foo(A) bar(A) baz(A)
61
+ # Workbench after setup : master(A)
62
+ test_expect_success "setup upstream repository and workbench" '
63
+ rm -rf upstream.git workbench &&
64
+ git init --bare upstream.git &&
65
+ git init workbench &&
66
+ create_commits_in workbench A B &&
67
+ (
68
+ cd workbench &&
69
+ # Try to make a stable fixed width for abbreviated commit ID,
70
+ # this fixed-width oid will be replaced with "<OID>".
71
+ git config core.abbrev 7 &&
72
+ git remote add origin ../upstream.git &&
73
+ git update-ref refs/heads/master $A &&
74
+ git push origin \
75
+ $B:refs/heads/master \
76
+ $A:refs/heads/foo \
77
+ $A:refs/heads/bar \
78
+ $A:refs/heads/baz
79
+ ) &&
80
+ git -C "workbench" config advice.pushUpdateRejected false &&
81
+ upstream=upstream.git
82
+ '
83
+}
84
+
85
+run_git_push_porcelain_output_test() {
86
+ case $1 in
87
+ http)
88
+ PROTOCOL="HTTP protocol"
89
+ URL_PREFIX="http://.*"
90
+ ;;
91
+ file)
92
+ PROTOCOL="builtin protocol"
93
+ URL_PREFIX="\.\."
94
+ ;;
95
+ esac
96
+
97
+ # Refs of upstream : master(B) foo(A) bar(A) baz(A)
98
+ # Refs of workbench: master(A) baz(A) next(A)
99
+ # git-push : master(A) NULL (B) baz(A) next(A)
100
+ test_expect_success "porcelain output of successful git-push ($PROTOCOL)" '
101
+ (
102
+ cd workbench &&
103
+ git update-ref refs/heads/master $A &&
104
+ git update-ref refs/heads/baz $A &&
105
+ git update-ref refs/heads/next $A &&
106
+ git push --porcelain --force origin \
107
+ master \
108
+ :refs/heads/foo \
109
+ $B:bar \
110
+ baz \
111
+ next
112
+ ) >out &&
113
+ make_user_friendly_and_stable_output <out >actual &&
114
+ cat >expect <<-EOF &&
115
+ To <URL/of/upstream.git>
116
+ = refs/heads/baz:refs/heads/baz [up to date]
117
+ <COMMIT-B>:refs/heads/bar <OID-A>..<OID-B>
118
+ - :refs/heads/foo [deleted]
119
+ + refs/heads/master:refs/heads/master <OID-B>...<OID-A> (forced update)
120
+ * refs/heads/next:refs/heads/next [new branch]
121
+ Done
122
+ EOF
123
+ test_cmp expect actual &&
124
+
125
+ git -C "$upstream" show-ref >out &&
126
+ make_user_friendly_and_stable_output <out >actual &&
127
+ cat >expect <<-EOF &&
128
+ <COMMIT-B> refs/heads/bar
129
+ <COMMIT-A> refs/heads/baz
130
+ <COMMIT-A> refs/heads/master
131
+ <COMMIT-A> refs/heads/next
132
+ EOF
133
+ test_cmp expect actual
134
+ '
135
+
136
+ # Refs of upstream : master(A) bar(B) baz(A) next(A)
137
+ # Refs of workbench: master(B) bar(A) baz(A) next(A)
138
+ # git-push : master(B) bar(A) NULL next(A)
139
+ test_expect_success "atomic push failed ($PROTOCOL)" '
140
+ (
141
+ cd workbench &&
142
+ git update-ref refs/heads/master $B &&
143
+ git update-ref refs/heads/bar $A &&
144
+ test_must_fail git push --atomic --porcelain origin \
145
+ master \
146
+ bar \
147
+ :baz \
148
+ next
149
+ ) >out &&
150
+ make_user_friendly_and_stable_output <out >actual &&
151
+ cat >expect <<-EOF &&
152
+ To <URL/of/upstream.git>
153
+ ! refs/heads/bar:refs/heads/bar [rejected] (non-fast-forward)
154
+ ! (delete):refs/heads/baz [rejected] (atomic push failed)
155
+ ! refs/heads/master:refs/heads/master [rejected] (atomic push failed)
156
+ ! refs/heads/next:refs/heads/next [rejected] (atomic push failed)
157
+ Done
158
+ EOF
159
+ test_cmp expect actual &&
160
+
161
+ git -C "$upstream" show-ref >out &&
162
+ make_user_friendly_and_stable_output <out >actual &&
163
+ cat >expect <<-EOF &&
164
+ <COMMIT-B> refs/heads/bar
165
+ <COMMIT-A> refs/heads/baz
166
+ <COMMIT-A> refs/heads/master
167
+ <COMMIT-A> refs/heads/next
168
+ EOF
169
+ test_cmp expect actual
170
+ '
171
+
172
+ test_expect_success "prepare pre-receive hook ($PROTOCOL)" '
173
+ write_script "$upstream/hooks/pre-receive" <<-EOF
174
+ exit 1
175
+ EOF
176
+ '
177
+
178
+ # Refs of upstream : master(A) bar(B) baz(A) next(A)
179
+ # Refs of workbench: master(B) bar(A) baz(A) next(A)
180
+ # git-push : master(B) bar(A) NULL next(A)
181
+ test_expect_success "pre-receive hook declined ($PROTOCOL)" '
182
+ (
183
+ cd workbench &&
184
+ git update-ref refs/heads/master $B &&
185
+ git update-ref refs/heads/bar $A &&
186
+ test_must_fail git push --porcelain --force origin \
187
+ master \
188
+ bar \
189
+ :baz \
190
+ next
191
+ ) >out &&
192
+ make_user_friendly_and_stable_output <out >actual &&
193
+ cat >expect <<-EOF &&
194
+ To <URL/of/upstream.git>
195
+ = refs/heads/next:refs/heads/next [up to date]
196
+ ! refs/heads/bar:refs/heads/bar [remote rejected] (pre-receive hook declined)
197
+ ! :refs/heads/baz [remote rejected] (pre-receive hook declined)
198
+ ! refs/heads/master:refs/heads/master [remote rejected] (pre-receive hook declined)
199
+ Done
200
+ EOF
201
+ test_cmp expect actual &&
202
+
203
+ git -C "$upstream" show-ref >out &&
204
+ make_user_friendly_and_stable_output <out >actual &&
205
+ cat >expect <<-EOF &&
206
+ <COMMIT-B> refs/heads/bar
207
+ <COMMIT-A> refs/heads/baz
208
+ <COMMIT-A> refs/heads/master
209
+ <COMMIT-A> refs/heads/next
210
+ EOF
211
+ test_cmp expect actual
212
+ '
213
+
214
+ test_expect_success "remove pre-receive hook ($PROTOCOL)" '
215
+ rm "$upstream/hooks/pre-receive"
216
+ '
217
+
218
+ # Refs of upstream : master(A) bar(B) baz(A) next(A)
219
+ # Refs of workbench: master(B) bar(A) baz(A) next(A)
220
+ # git-push : master(B) bar(A) NULL next(A)
221
+ test_expect_success "non-fastforward push ($PROTOCOL)" '
222
+ (
223
+ cd workbench &&
224
+ test_must_fail git push --porcelain origin \
225
+ master \
226
+ bar \
227
+ :baz \
228
+ next
229
+ ) >out &&
230
+ make_user_friendly_and_stable_output <out >actual &&
231
+ cat >expect <<-EOF &&
232
+ To <URL/of/upstream.git>
233
+ = refs/heads/next:refs/heads/next [up to date]
234
+ - :refs/heads/baz [deleted]
235
+ refs/heads/master:refs/heads/master <OID-A>..<OID-B>
236
+ ! refs/heads/bar:refs/heads/bar [rejected] (non-fast-forward)
237
+ Done
238
+ EOF
239
+ test_cmp expect actual &&
240
+
241
+ git -C "$upstream" show-ref >out &&
242
+ make_user_friendly_and_stable_output <out >actual &&
243
+ cat >expect <<-EOF &&
244
+ <COMMIT-B> refs/heads/bar
245
+ <COMMIT-B> refs/heads/master
246
+ <COMMIT-A> refs/heads/next
247
+ EOF
248
+ test_cmp expect actual
249
+ '
250
+}
251
+
252
+# Initialize the upstream repository and local workbench.
253
+setup_upstream_and_workbench
254
+
255
+# Run git-push porcelain test on builtin protocol
256
+run_git_push_porcelain_output_test file
257
+
258
+ROOT_PATH="$PWD"
259
+. "$TEST_DIRECTORY"/lib-gpg.sh
260
+. "$TEST_DIRECTORY"/lib-httpd.sh
261
+. "$TEST_DIRECTORY"/lib-terminal.sh
262
+start_httpd
263
+
264
+# Re-initialize the upstream repository and local workbench.
265
+setup_upstream_and_workbench
266
+
267
+test_expect_success "setup for http" '
268
+ git -C upstream.git config http.receivepack true &&
269
+ upstream="$HTTPD_DOCUMENT_ROOT_PATH/upstream.git" &&
270
+ mv upstream.git "$upstream" &&
271
+
272
+ git -C workbench remote set-url origin $HTTPD_URL/smart/upstream.git
273
+'
274
+
275
+setup_askpass_helper
276
+
277
+# Run git-push porcelain test on HTTP protocol
278
+run_git_push_porcelain_output_test http
279
+
280
+test_done