1
+#!/bin/sh
2
+
3
+test_description='migration of ref storage backends'
4
+
5
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
7
+
8
+TEST_PASSES_SANITIZE_LEAK=true
9
+. ./test-lib.sh
10
+
11
+test_migration () {
12
+ git -C "$1" for-each-ref --include-root-refs \
13
+ --format='%(refname) %(objectname) %(symref)' >expect &&
14
+ git -C "$1" refs migrate --ref-format="$2" &&
15
+ git -C "$1" for-each-ref --include-root-refs \
16
+ --format='%(refname) %(objectname) %(symref)' >actual &&
17
+ test_cmp expect actual &&
18
+
19
+ git -C "$1" rev-parse --show-ref-format >actual &&
20
+ echo "$2" >expect &&
21
+ test_cmp expect actual
22
+}
23
+
24
+test_expect_success 'setup' '
25
+ rm -rf .git &&
26
+ # The migration does not yet support reflogs.
27
+ git config --global core.logAllRefUpdates false
28
+'
29
+
30
+test_expect_success "superfluous arguments" '
31
+ test_when_finished "rm -rf repo" &&
32
+ git init repo &&
33
+ test_must_fail git -C repo refs migrate foo 2>err &&
34
+ cat >expect <<-EOF &&
35
+ usage: too many arguments
36
+ EOF
37
+ test_cmp expect err
38
+'
39
+
40
+test_expect_success "missing ref storage format" '
41
+ test_when_finished "rm -rf repo" &&
42
+ git init repo &&
43
+ test_must_fail git -C repo refs migrate 2>err &&
44
+ cat >expect <<-EOF &&
45
+ usage: missing --ref-format=<format>
46
+ EOF
47
+ test_cmp expect err
48
+'
49
+
50
+test_expect_success "unknown ref storage format" '
51
+ test_when_finished "rm -rf repo" &&
52
+ git init repo &&
53
+ test_must_fail git -C repo refs migrate \
54
+ --ref-format=unknown 2>err &&
55
+ cat >expect <<-EOF &&
56
+ error: unknown ref storage format ${SQ}unknown${SQ}
57
+ EOF
58
+ test_cmp expect err
59
+'
60
+
61
+ref_formats="files reftable"
62
+for from_format in $ref_formats
63
+do
64
+ for to_format in $ref_formats
65
+ do
66
+ if test "$from_format" = "$to_format"
67
+ then
68
+ continue
69
+ fi
70
+
71
+ test_expect_success "$from_format: migration to same format fails" '
72
+ test_when_finished "rm -rf repo" &&
73
+ git init --ref-format=$from_format repo &&
74
+ test_must_fail git -C repo refs migrate \
75
+ --ref-format=$from_format 2>err &&
76
+ cat >expect <<-EOF &&
77
+ error: repository already uses ${SQ}$from_format${SQ} format
78
+ EOF
79
+ test_cmp expect err
80
+ '
81
+
82
+ test_expect_success "$from_format -> $to_format: migration with reflog fails" '
83
+ test_when_finished "rm -rf repo" &&
84
+ git init --ref-format=$from_format repo &&
85
+ test_config -C repo core.logAllRefUpdates true &&
86
+ test_commit -C repo logged &&
87
+ test_must_fail git -C repo refs migrate \
88
+ --ref-format=$to_format 2>err &&
89
+ cat >expect <<-EOF &&
90
+ error: migrating reflogs is not supported yet
91
+ EOF
92
+ test_cmp expect err
93
+ '
94
+
95
+ test_expect_success "$from_format -> $to_format: migration with worktree fails" '
96
+ test_when_finished "rm -rf repo" &&
97
+ git init --ref-format=$from_format repo &&
98
+ git -C repo worktree add wt &&
99
+ test_must_fail git -C repo refs migrate \
100
+ --ref-format=$to_format 2>err &&
101
+ cat >expect <<-EOF &&
102
+ error: migrating repositories with worktrees is not supported yet
103
+ EOF
104
+ test_cmp expect err
105
+ '
106
+
107
+ test_expect_success "$from_format -> $to_format: unborn HEAD" '
108
+ test_when_finished "rm -rf repo" &&
109
+ git init --ref-format=$from_format repo &&
110
+ test_migration repo "$to_format"
111
+ '
112
+
113
+ test_expect_success "$from_format -> $to_format: single ref" '
114
+ test_when_finished "rm -rf repo" &&
115
+ git init --ref-format=$from_format repo &&
116
+ test_commit -C repo initial &&
117
+ test_migration repo "$to_format"
118
+ '
119
+
120
+ test_expect_success "$from_format -> $to_format: bare repository" '
121
+ test_when_finished "rm -rf repo repo.git" &&
122
+ git init --ref-format=$from_format repo &&
123
+ test_commit -C repo initial &&
124
+ git clone --ref-format=$from_format --mirror repo repo.git &&
125
+ test_migration repo.git "$to_format"
126
+ '
127
+
128
+ test_expect_success "$from_format -> $to_format: dangling symref" '
129
+ test_when_finished "rm -rf repo" &&
130
+ git init --ref-format=$from_format repo &&
131
+ test_commit -C repo initial &&
132
+ git -C repo symbolic-ref BROKEN_HEAD refs/heads/nonexistent &&
133
+ test_migration repo "$to_format" &&
134
+ echo refs/heads/nonexistent >expect &&
135
+ git -C repo symbolic-ref BROKEN_HEAD >actual &&
136
+ test_cmp expect actual
137
+ '
138
+
139
+ test_expect_success "$from_format -> $to_format: broken ref" '
140
+ test_when_finished "rm -rf repo" &&
141
+ git init --ref-format=$from_format repo &&
142
+ test_commit -C repo initial &&
143
+ test-tool -C repo ref-store main update-ref "" refs/heads/broken \
144
+ "$(test_oid 001)" "$ZERO_OID" REF_SKIP_CREATE_REFLOG,REF_SKIP_OID_VERIFICATION &&
145
+ test_migration repo "$to_format" &&
146
+ test_oid 001 >expect &&
147
+ git -C repo rev-parse refs/heads/broken >actual &&
148
+ test_cmp expect actual
149
+ '
150
+
151
+ test_expect_success "$from_format -> $to_format: pseudo-refs" '
152
+ test_when_finished "rm -rf repo" &&
153
+ git init --ref-format=$from_format repo &&
154
+ test_commit -C repo initial &&
155
+ git -C repo update-ref FOO_HEAD HEAD &&
156
+ test_migration repo "$to_format"
157
+ '
158
+
159
+ test_expect_success "$from_format -> $to_format: special refs are left alone" '
160
+ test_when_finished "rm -rf repo" &&
161
+ git init --ref-format=$from_format repo &&
162
+ test_commit -C repo initial &&
163
+ git -C repo rev-parse HEAD >repo/.git/MERGE_HEAD &&
164
+ git -C repo rev-parse MERGE_HEAD &&
165
+ test_migration repo "$to_format" &&
166
+ test_path_is_file repo/.git/MERGE_HEAD
167
+ '
168
+
169
+ test_expect_success "$from_format -> $to_format: a bunch of refs" '
170
+ test_when_finished "rm -rf repo" &&
171
+ git init --ref-format=$from_format repo &&
172
+
173
+ test_commit -C repo initial &&
174
+ cat >input <<-EOF &&
175
+ create FOO_HEAD HEAD
176
+ create refs/heads/branch-1 HEAD
177
+ create refs/heads/branch-2 HEAD
178
+ create refs/heads/branch-3 HEAD
179
+ create refs/heads/branch-4 HEAD
180
+ create refs/tags/tag-1 HEAD
181
+ create refs/tags/tag-2 HEAD
182
+ EOF
183
+ git -C repo update-ref --stdin <input &&
184
+ test_migration repo "$to_format"
185
+ '
186
+
187
+ test_expect_success "$from_format -> $to_format: dry-run migration does not modify repository" '
188
+ test_when_finished "rm -rf repo" &&
189
+ git init --ref-format=$from_format repo &&
190
+ test_commit -C repo initial &&
191
+ git -C repo refs migrate --dry-run \
192
+ --ref-format=$to_format >output &&
193
+ grep "Finished dry-run migration of refs" output &&
194
+ test_path_is_dir repo/.git/ref_migration.* &&
195
+ echo $from_format >expect &&
196
+ git -C repo rev-parse --show-ref-format >actual &&
197
+ test_cmp expect actual
198
+ '
199
+ done
200
+done
201
+
202
+test_expect_success 'migrating from files format deletes backend files' '
203
+ test_when_finished "rm -rf repo" &&
204
+ git init --ref-format=files repo &&
205
+ test_commit -C repo first &&
206
+ git -C repo pack-refs --all &&
207
+ test_commit -C repo second &&
208
+ git -C repo update-ref ORIG_HEAD HEAD &&
209
+ git -C repo rev-parse HEAD >repo/.git/FETCH_HEAD &&
210
+
211
+ test_path_is_file repo/.git/HEAD &&
212
+ test_path_is_file repo/.git/ORIG_HEAD &&
213
+ test_path_is_file repo/.git/refs/heads/main &&
214
+ test_path_is_file repo/.git/packed-refs &&
215
+
216
+ test_migration repo reftable &&
217
+
218
+ echo "ref: refs/heads/.invalid" >expect &&
219
+ test_cmp expect repo/.git/HEAD &&
220
+ echo "this repository uses the reftable format" >expect &&
221
+ test_cmp expect repo/.git/refs/heads &&
222
+ test_path_is_file repo/.git/FETCH_HEAD &&
223
+ test_path_is_missing repo/.git/ORIG_HEAD &&
224
+ test_path_is_missing repo/.git/refs/heads/main &&
225
+ test_path_is_missing repo/.git/logs &&
226
+ test_path_is_missing repo/.git/packed-refs
227
+'
228
+
229
+test_expect_success 'migrating from reftable format deletes backend files' '
230
+ test_when_finished "rm -rf repo" &&
231
+ git init --ref-format=reftable repo &&
232
+ test_commit -C repo first &&
233
+
234
+ test_path_is_dir repo/.git/reftable &&
235
+ test_migration repo files &&
236
+
237
+ test_path_is_missing repo/.git/reftable &&
238
+ echo "ref: refs/heads/main" >expect &&
239
+ test_cmp expect repo/.git/HEAD &&
240
+ test_path_is_file repo/.git/refs/heads/main
241
+'
242
+
243
+test_done