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