Git fork
1#!/bin/sh
2
3test_description='migration of ref storage backends'
4
5GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
7
8. ./test-lib.sh
9
10print_all_reflog_entries () {
11 repo=$1 &&
12 test-tool -C "$repo" ref-store main for-each-reflog >reflogs &&
13 while read reflog
14 do
15 echo "REFLOG: $reflog" &&
16 test-tool -C "$repo" ref-store main for-each-reflog-ent "$reflog" ||
17 return 1
18 done <reflogs
19}
20
21# Migrate the provided repository from one format to the other and
22# verify that the references and logs are migrated over correctly.
23# Usage: test_migration <repo> <format> [<skip_reflog_verify> [<options...>]]
24# <repo> is the relative path to the repo to be migrated.
25# <format> is the ref format to be migrated to.
26# <skip_reflog_verify> (default: false) whether to skip reflog verification.
27# <options...> are other options be passed directly to 'git refs migrate'.
28test_migration () {
29 repo=$1 &&
30 format=$2 &&
31 shift 2 &&
32 skip_reflog_verify=false &&
33 if test $# -ge 1
34 then
35 skip_reflog_verify=$1
36 shift
37 fi &&
38 git -C "$repo" for-each-ref --include-root-refs \
39 --format='%(refname) %(objectname) %(symref)' >expect &&
40 if ! $skip_reflog_verify
41 then
42 print_all_reflog_entries "$repo" >expect_logs
43 fi &&
44
45 git -C "$repo" refs migrate --ref-format="$format" "$@" &&
46
47 git -C "$repo" for-each-ref --include-root-refs \
48 --format='%(refname) %(objectname) %(symref)' >actual &&
49 test_cmp expect actual &&
50 if ! $skip_reflog_verify
51 then
52 print_all_reflog_entries "$repo" >actual_logs &&
53 test_cmp expect_logs actual_logs
54 fi &&
55
56 git -C "$repo" rev-parse --show-ref-format >actual &&
57 echo "$format" >expect &&
58 test_cmp expect actual
59}
60
61test_expect_success 'setup' '
62 rm -rf .git
63'
64
65test_expect_success "superfluous arguments" '
66 test_when_finished "rm -rf repo" &&
67 git init repo &&
68 test_must_fail git -C repo refs migrate foo 2>err &&
69 cat >expect <<-EOF &&
70 usage: too many arguments
71 EOF
72 test_cmp expect err
73'
74
75test_expect_success "missing ref storage format" '
76 test_when_finished "rm -rf repo" &&
77 git init repo &&
78 test_must_fail git -C repo refs migrate 2>err &&
79 cat >expect <<-EOF &&
80 usage: missing --ref-format=<format>
81 EOF
82 test_cmp expect err
83'
84
85test_expect_success "unknown ref storage format" '
86 test_when_finished "rm -rf repo" &&
87 git init repo &&
88 test_must_fail git -C repo refs migrate \
89 --ref-format=unknown 2>err &&
90 cat >expect <<-EOF &&
91 error: unknown ref storage format ${SQ}unknown${SQ}
92 EOF
93 test_cmp expect err
94'
95
96ref_formats="files reftable"
97for from_format in $ref_formats
98do
99 for to_format in $ref_formats
100 do
101 if test "$from_format" = "$to_format"
102 then
103 continue
104 fi
105
106 test_expect_success "$from_format: migration to same format fails" '
107 test_when_finished "rm -rf repo" &&
108 git init --ref-format=$from_format repo &&
109 test_must_fail git -C repo refs migrate \
110 --ref-format=$from_format 2>err &&
111 cat >expect <<-EOF &&
112 error: repository already uses ${SQ}$from_format${SQ} format
113 EOF
114 test_cmp expect err
115 '
116
117 test_expect_success "$from_format -> $to_format: migration with worktree fails" '
118 test_when_finished "rm -rf repo" &&
119 git init --ref-format=$from_format repo &&
120 git -C repo worktree add wt &&
121 test_must_fail git -C repo refs migrate \
122 --ref-format=$to_format 2>err &&
123 cat >expect <<-EOF &&
124 error: migrating repositories with worktrees is not supported yet
125 EOF
126 test_cmp expect err
127 '
128
129 test_expect_success "$from_format -> $to_format: unborn HEAD" '
130 test_when_finished "rm -rf repo" &&
131 git init --ref-format=$from_format repo &&
132 test_migration repo "$to_format"
133 '
134
135 test_expect_success "$from_format -> $to_format: single ref" '
136 test_when_finished "rm -rf repo" &&
137 git init --ref-format=$from_format repo &&
138 test_commit -C repo initial &&
139 test_migration repo "$to_format"
140 '
141
142 test_expect_success "$from_format -> $to_format: bare repository" '
143 test_when_finished "rm -rf repo repo.git" &&
144 git init --ref-format=$from_format repo &&
145 test_commit -C repo initial &&
146 git clone --ref-format=$from_format --mirror repo repo.git &&
147 test_migration repo.git "$to_format"
148 '
149
150 test_expect_success "$from_format -> $to_format: dangling symref" '
151 test_when_finished "rm -rf repo" &&
152 git init --ref-format=$from_format repo &&
153 test_commit -C repo initial &&
154 git -C repo symbolic-ref BROKEN_HEAD refs/heads/nonexistent &&
155 test_migration repo "$to_format" &&
156 echo refs/heads/nonexistent >expect &&
157 git -C repo symbolic-ref BROKEN_HEAD >actual &&
158 test_cmp expect actual
159 '
160
161 test_expect_success "$from_format -> $to_format: broken ref" '
162 test_when_finished "rm -rf repo" &&
163 git init --ref-format=$from_format repo &&
164 test_commit -C repo initial &&
165 test-tool -C repo ref-store main update-ref "" refs/heads/broken \
166 "$(test_oid 001)" "$ZERO_OID" REF_SKIP_CREATE_REFLOG,REF_SKIP_OID_VERIFICATION &&
167 test_migration repo "$to_format" true &&
168 test_oid 001 >expect &&
169 git -C repo rev-parse refs/heads/broken >actual &&
170 test_cmp expect actual
171 '
172
173 test_expect_success "$from_format -> $to_format: pseudo-refs" '
174 test_when_finished "rm -rf repo" &&
175 git init --ref-format=$from_format repo &&
176 test_commit -C repo initial &&
177 git -C repo update-ref FOO_HEAD HEAD &&
178 test_migration repo "$to_format"
179 '
180
181 test_expect_success "$from_format -> $to_format: special refs are left alone" '
182 test_when_finished "rm -rf repo" &&
183 git init --ref-format=$from_format repo &&
184 test_commit -C repo initial &&
185 git -C repo rev-parse HEAD >repo/.git/MERGE_HEAD &&
186 git -C repo rev-parse MERGE_HEAD &&
187 test_migration repo "$to_format" &&
188 test_path_is_file repo/.git/MERGE_HEAD
189 '
190
191 test_expect_success "$from_format -> $to_format: a bunch of refs" '
192 test_when_finished "rm -rf repo" &&
193 git init --ref-format=$from_format repo &&
194
195 test_commit -C repo initial &&
196 cat >input <<-EOF &&
197 create FOO_HEAD HEAD
198 create refs/heads/branch-1 HEAD
199 create refs/heads/branch-2 HEAD
200 create refs/heads/branch-3 HEAD
201 create refs/heads/branch-4 HEAD
202 create refs/tags/tag-1 HEAD
203 create refs/tags/tag-2 HEAD
204 EOF
205 git -C repo update-ref --stdin <input &&
206 test_migration repo "$to_format"
207 '
208
209 test_expect_success "$from_format -> $to_format: dry-run migration does not modify repository" '
210 test_when_finished "rm -rf repo" &&
211 git init --ref-format=$from_format repo &&
212 test_commit -C repo initial &&
213 git -C repo refs migrate --dry-run \
214 --ref-format=$to_format >output &&
215 grep "Finished dry-run migration of refs" output &&
216 test_path_is_dir repo/.git/ref_migration.* &&
217 echo $from_format >expect &&
218 git -C repo rev-parse --show-ref-format >actual &&
219 test_cmp expect actual
220 '
221
222 test_expect_success "$from_format -> $to_format: reflogs of symrefs with target deleted" '
223 test_when_finished "rm -rf repo" &&
224 git init --ref-format=$from_format repo &&
225 test_commit -C repo initial &&
226 git -C repo branch branch-1 HEAD &&
227 git -C repo symbolic-ref refs/heads/symref refs/heads/branch-1 &&
228 cat >input <<-EOF &&
229 delete refs/heads/branch-1
230 EOF
231 git -C repo update-ref --stdin <input &&
232 test_migration repo "$to_format"
233 '
234
235 test_expect_success "$from_format -> $to_format: reflogs order is retained" '
236 test_when_finished "rm -rf repo" &&
237 git init --ref-format=$from_format repo &&
238 test_commit --date "100005000 +0700" --no-tag -C repo initial &&
239 test_commit --date "100003000 +0700" --no-tag -C repo second &&
240 test_migration repo "$to_format"
241 '
242
243 test_expect_success "$from_format -> $to_format: stash is retained" '
244 test_when_finished "rm -rf repo" &&
245 git init --ref-format=$from_format repo &&
246 (
247 cd repo &&
248 test_commit initial A &&
249 echo foo >A &&
250 git stash push &&
251 echo bar >A &&
252 git stash push &&
253 git stash list >expect.reflog &&
254 test_migration . "$to_format" &&
255 git stash list >actual.reflog &&
256 test_cmp expect.reflog actual.reflog
257 )
258 '
259
260 test_expect_success "$from_format -> $to_format: skip reflog with --skip-reflog" '
261 test_when_finished "rm -rf repo" &&
262 git init --ref-format=$from_format repo &&
263 test_commit -C repo initial &&
264 # we see that the repository contains reflogs.
265 git -C repo reflog --all >reflogs &&
266 test_line_count = 2 reflogs &&
267 test_migration repo "$to_format" true --no-reflog &&
268 # there should be no reflogs post migration.
269 git -C repo reflog --all >reflogs &&
270 test_must_be_empty reflogs
271 '
272 done
273done
274
275test_expect_success 'multiple reftable blocks with multiple entries' '
276 test_when_finished "rm -rf repo" &&
277 git init --ref-format=files repo &&
278 test_commit -C repo first &&
279 printf "create refs/heads/ref-%d HEAD\n" $(test_seq 5000) >stdin &&
280 git -C repo update-ref --stdin <stdin &&
281 test_commit -C repo second &&
282 printf "update refs/heads/ref-%d HEAD\n" $(test_seq 3000) >stdin &&
283 git -C repo update-ref --stdin <stdin &&
284 test_migration repo reftable true
285'
286
287test_expect_success 'migrating from files format deletes backend files' '
288 test_when_finished "rm -rf repo" &&
289 git init --ref-format=files repo &&
290 test_commit -C repo first &&
291 git -C repo pack-refs --all &&
292 test_commit -C repo second &&
293 git -C repo update-ref ORIG_HEAD HEAD &&
294 git -C repo rev-parse HEAD >repo/.git/FETCH_HEAD &&
295
296 test_path_is_file repo/.git/HEAD &&
297 test_path_is_file repo/.git/ORIG_HEAD &&
298 test_path_is_file repo/.git/refs/heads/main &&
299 test_path_is_file repo/.git/packed-refs &&
300
301 test_migration repo reftable &&
302
303 echo "ref: refs/heads/.invalid" >expect &&
304 test_cmp expect repo/.git/HEAD &&
305 echo "this repository uses the reftable format" >expect &&
306 test_cmp expect repo/.git/refs/heads &&
307 test_path_is_file repo/.git/FETCH_HEAD &&
308 test_path_is_missing repo/.git/ORIG_HEAD &&
309 test_path_is_missing repo/.git/refs/heads/main &&
310 test_path_is_missing repo/.git/logs &&
311 test_path_is_missing repo/.git/packed-refs
312'
313
314test_expect_success 'migrating from reftable format deletes backend files' '
315 test_when_finished "rm -rf repo" &&
316 git init --ref-format=reftable repo &&
317 test_commit -C repo first &&
318
319 test_path_is_dir repo/.git/reftable &&
320 test_migration repo files &&
321
322 test_path_is_missing repo/.git/reftable &&
323 echo "ref: refs/heads/main" >expect &&
324 test_cmp expect repo/.git/HEAD &&
325 test_path_is_file repo/.git/packed-refs
326'
327
328test_done