commit-graph: when incompatible with graphs, indicate why
[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" &&
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/next:refs/heads/next    [up to date]
154                 !    refs/heads/bar:refs/heads/bar    [rejected] (non-fast-forward)
155                 !    (delete):refs/heads/baz    [rejected] (atomic push failed)
156                 !    refs/heads/master:refs/heads/master    [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         test_expect_success "prepare pre-receive hook ($PROTOCOL)" '
172                 write_script "$upstream/hooks/pre-receive" <<-EOF
173                 exit 1
174                 EOF
175         '
176
177         # Refs of upstream : master(A)  bar(B)  baz(A)  next(A)
178         # Refs of workbench: master(B)  bar(A)  baz(A)  next(A)
179         # git-push         : master(B)  bar(A)  NULL    next(A)
180         test_expect_success "pre-receive hook declined ($PROTOCOL)" '
181                 (
182                         cd workbench &&
183                         git update-ref refs/heads/master $B &&
184                         git update-ref refs/heads/bar $A &&
185                         test_must_fail git push --porcelain --force origin \
186                                 master \
187                                 bar \
188                                 :baz \
189                                 next
190                 ) >out &&
191                 make_user_friendly_and_stable_output <out >actual &&
192                 cat >expect <<-EOF &&
193                 To <URL/of/upstream.git>
194                 =    refs/heads/next:refs/heads/next    [up to date]
195                 !    refs/heads/bar:refs/heads/bar    [remote rejected] (pre-receive hook declined)
196                 !    :refs/heads/baz    [remote rejected] (pre-receive hook declined)
197                 !    refs/heads/master:refs/heads/master    [remote rejected] (pre-receive hook declined)
198                 Done
199                 EOF
200                 test_cmp expect actual &&
201
202                 git -C "$upstream" show-ref >out &&
203                 make_user_friendly_and_stable_output <out >actual &&
204                 cat >expect <<-EOF &&
205                 <COMMIT-B> refs/heads/bar
206                 <COMMIT-A> refs/heads/baz
207                 <COMMIT-A> refs/heads/master
208                 <COMMIT-A> refs/heads/next
209                 EOF
210                 test_cmp expect actual
211         '
212
213         test_expect_success "remove pre-receive hook ($PROTOCOL)" '
214                 rm "$upstream/hooks/pre-receive"
215         '
216
217         # Refs of upstream : master(A)  bar(B)  baz(A)  next(A)
218         # Refs of workbench: master(B)  bar(A)  baz(A)  next(A)
219         # git-push         : master(B)  bar(A)  NULL    next(A)
220         test_expect_success "non-fastforward push ($PROTOCOL)" '
221                 (
222                         cd workbench &&
223                         test_must_fail git push --porcelain origin \
224                                 master \
225                                 bar \
226                                 :baz \
227                                 next
228                 ) >out &&
229                 make_user_friendly_and_stable_output <out >actual &&
230                 cat >expect <<-EOF &&
231                 To <URL/of/upstream.git>
232                 =    refs/heads/next:refs/heads/next    [up to date]
233                 -    :refs/heads/baz    [deleted]
234                      refs/heads/master:refs/heads/master    <OID-A>..<OID-B>
235                 !    refs/heads/bar:refs/heads/bar    [rejected] (non-fast-forward)
236                 Done
237                 EOF
238                 test_cmp expect actual &&
239
240                 git -C "$upstream" show-ref >out &&
241                 make_user_friendly_and_stable_output <out >actual &&
242                 cat >expect <<-EOF &&
243                 <COMMIT-B> refs/heads/bar
244                 <COMMIT-B> refs/heads/master
245                 <COMMIT-A> refs/heads/next
246                 EOF
247                 test_cmp expect actual
248         '
249 }
250
251 # Initialize the upstream repository and local workbench.
252 setup_upstream_and_workbench
253
254 # Run git-push porcelain test on builtin protocol
255 run_git_push_porcelain_output_test file
256
257 ROOT_PATH="$PWD"
258 . "$TEST_DIRECTORY"/lib-gpg.sh
259 . "$TEST_DIRECTORY"/lib-httpd.sh
260 . "$TEST_DIRECTORY"/lib-terminal.sh
261 start_httpd
262
263 # Re-initialize the upstream repository and local workbench.
264 setup_upstream_and_workbench
265
266 test_expect_success "setup for http" '
267         git -C upstream.git config http.receivepack true &&
268         upstream="$HTTPD_DOCUMENT_ROOT_PATH/upstream.git" &&
269         mv upstream.git "$upstream" &&
270
271         git -C workbench remote set-url origin $HTTPD_URL/smart/upstream.git
272 '
273
274 setup_askpass_helper
275
276 # Run git-push porcelain test on HTTP protocol
277 run_git_push_porcelain_output_test http
278
279 test_done