Viewing files at commit 5717cc9fc7edCommit detailsView latest

Browse files
suites/merge/vendor/gitoxide/tree-cartesian-baseline.sh 346 lines · 9.6 KB · bash
1
#!/usr/bin/env bash
2
set -eu -o pipefail
3
4
# This is a complete Cartesian product within a deliberately finite model:
5
#
6
# * one merge base;
7
# * one regular file whose identity can be modified, deleted, or renamed;
8
# * one occupied rename destination;
9
# * two initially free destinations;
10
# * at most one atomic operation per side;
11
# * exact rename detection and the built-in text merge driver.
12
#
13
# Modes, symlinks, submodules, copies, attributes, recursive merge bases, and
14
# multiple simultaneous operations are separate dimensions. Including them here
15
# would obscure the state-machine coverage with a mostly redundant cross product.
16
#
17
# The atomic states are:
18
#
19
# * keep, modify, or delete the source;
20
# * rename it unchanged to either free destination;
21
# * rename it with a content change to `free-a`;
22
# * add an unrelated file at either free destination;
23
# * modify or delete the occupied destination;
24
# * replace the occupied destination with the source, unchanged or modified;
25
# * replace the source file with a directory and child;
26
# * rename the source away and then create a directory and child at its old path.
27
#
28
# `free-b` exists to distinguish same-destination from different-destination
29
# interactions. Repeating the modified-rename state for `free-b` would only
30
# duplicate `free-a` cases under a path-name substitution.
31
operations=(
32
  keep
33
  modify-source
34
  delete-source
35
  rename-free-a
36
  rename-free-b
37
  rename-modify-free-a
38
  add-free-a
39
  add-free-b
40
  modify-occupied
41
  delete-occupied
42
  replace-occupied
43
  replace-modify-occupied
44
  file-to-dir
45
  rename-and-file-to-dir
46
)
47
48
function tick () {
49
  if test -z "${tick+set}"
50
  then
51
    tick=1112911993
52
  else
53
    tick=$((tick + 60))
54
  fi
55
  GIT_COMMITTER_DATE="$tick -0700"
56
  GIT_AUTHOR_DATE="$tick -0700"
57
  export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
58
}
59
60
function apply_operation () {
61
  local side=${1:?side}
62
  local operation=${2:?operation}
63
  local payload="payload-${side}-${operation}"
64
65
  case "$operation" in
66
  keep)
67
    ;;
68
  modify-source)
69
    echo "$payload" >>source
70
    ;;
71
  delete-source)
72
    git rm source
73
    ;;
74
  rename-free-a)
75
    git mv source free-a
76
    ;;
77
  rename-free-b)
78
    git mv source free-b
79
    ;;
80
  rename-modify-free-a)
81
    echo "$payload" >>source
82
    git mv source free-a
83
    ;;
84
  add-free-a)
85
    echo "$payload" >free-a
86
    ;;
87
  add-free-b)
88
    echo "$payload" >free-b
89
    ;;
90
  modify-occupied)
91
    echo "$payload" >>occupied
92
    ;;
93
  delete-occupied)
94
    git rm occupied
95
    ;;
96
  replace-occupied)
97
    git rm occupied
98
    git mv source occupied
99
    ;;
100
  replace-modify-occupied)
101
    git rm occupied
102
    echo "$payload" >>source
103
    git mv source occupied
104
    ;;
105
  file-to-dir)
106
    git rm source
107
    mkdir source
108
    echo "$payload" >source/child
109
    ;;
110
  rename-and-file-to-dir)
111
    git mv source moved
112
    mkdir source
113
    echo "$payload" >source/child
114
    ;;
115
  *)
116
    echo "unknown operation: $operation" >&2
117
    return 1
118
    ;;
119
  esac
120
}
121
122
git init --object-format=sha1 matrix
123
(cd matrix
124
  git config user.name "Cartesian Merge Baseline"
125
  git config user.email "[email protected]"
126
127
  printf '%s\n' source source source source source >source
128
  printf '%s\n' occupied occupied occupied occupied occupied >occupied
129
  git add .
130
  tick
131
  git commit -m "base"
132
  git branch base
133
  git rev-parse HEAD^{tree} >.git/base.tree
134
135
  # Make a side-specific commit for every state. Content-changing states include
136
  # a unique payload so the Rust test can detect silently discarded changes.
137
  for side in A B
138
  do
139
    for operation in "${operations[@]}"
140
    do
141
      git checkout --detach --force base
142
      apply_operation "$side" "$operation"
143
      git add -A
144
      tick
145
      git commit --allow-empty -m "$side: $operation"
146
      git branch "$side-$operation"
147
    done
148
  done
149
150
  : >cases.tsv
151
  for ((left = 0; left < ${#operations[@]}; left++))
152
  do
153
    for ((right = left; right < ${#operations[@]}; right++))
154
    do
155
      left_operation=${operations[$left]}
156
      right_operation=${operations[$right]}
157
      case_name="${left_operation}--${right_operation}"
158
      forward_file="${case_name}-A-B.merge-info"
159
      reverse_file="${case_name}-B-A.merge-info"
160
161
      if git merge-tree -z --write-tree \
162
        "A-$left_operation" "B-$right_operation" >".git/$forward_file"
163
      then
164
        forward_status=clean
165
      else
166
        forward_status=conflicted
167
      fi
168
      if git merge-tree -z --write-tree \
169
        "B-$right_operation" "A-$left_operation" >".git/$reverse_file"
170
      then
171
        reverse_status=clean
172
      else
173
        reverse_status=conflicted
174
      fi
175
176
      printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
177
        "$left_operation" \
178
        "$right_operation" \
179
        "$(git rev-parse "A-$left_operation")" \
180
        "$(git rev-parse "B-$right_operation")" \
181
        "$forward_file" \
182
        "$forward_status" \
183
        "$reverse_file" \
184
        "$reverse_status" >>cases.tsv
185
    done
186
  done
187
)
188
189
function apply_submodule_operation () {
190
  local side=${1:?side}
191
  local operation=${2:?operation}
192
  local payload="payload-${side}-${operation}"
193
  local changed_source changed_occupied added
194
  if test "$side" = A
195
  then
196
    changed_source=$source_a
197
    changed_occupied=$occupied_a
198
    added=$added_a
199
  else
200
    changed_source=$source_b
201
    changed_occupied=$occupied_b
202
    added=$added_b
203
  fi
204
205
  case "$operation" in
206
  keep)
207
    ;;
208
  modify-source)
209
    git update-index --add --cacheinfo "160000,$changed_source,source"
210
    ;;
211
  delete-source)
212
    git update-index --force-remove source
213
    ;;
214
  rename-free-a)
215
    git update-index --force-remove source
216
    git update-index --add --cacheinfo "160000,$source_base,free-a"
217
    ;;
218
  rename-free-b)
219
    git update-index --force-remove source
220
    git update-index --add --cacheinfo "160000,$source_base,free-b"
221
    ;;
222
  rename-modify-free-a)
223
    git update-index --force-remove source
224
    git update-index --add --cacheinfo "160000,$changed_source,free-a"
225
    ;;
226
  add-free-a)
227
    git update-index --add --cacheinfo "160000,$added,free-a"
228
    ;;
229
  add-free-b)
230
    git update-index --add --cacheinfo "160000,$added,free-b"
231
    ;;
232
  modify-occupied)
233
    git update-index --add --cacheinfo "160000,$changed_occupied,occupied"
234
    ;;
235
  delete-occupied)
236
    git update-index --force-remove occupied
237
    ;;
238
  replace-occupied)
239
    git update-index --force-remove source
240
    git update-index --add --cacheinfo "160000,$source_base,occupied"
241
    ;;
242
  replace-modify-occupied)
243
    git update-index --force-remove source
244
    git update-index --add --cacheinfo "160000,$changed_source,occupied"
245
    ;;
246
  file-to-dir)
247
    git update-index --force-remove source
248
    mkdir -p source
249
    echo "$payload" >source/child
250
    git add source/child
251
    ;;
252
  rename-and-file-to-dir)
253
    git update-index --force-remove source
254
    git update-index --add --cacheinfo "160000,$source_base,moved"
255
    mkdir -p source
256
    echo "$payload" >source/child
257
    git add source/child
258
    ;;
259
  *)
260
    echo "unknown submodule operation: $operation" >&2
261
    return 1
262
    ;;
263
  esac
264
}
265
266
# Repeat the same state machine with gitlinks. Commit objects live in this repository merely to provide deterministic,
267
# valid gitlink IDs; the tree merger still treats their ancestry as a caller-side submodule concern.
268
git init --object-format=sha1 matrix-submodule
269
(cd matrix-submodule
270
  git config user.name "Cartesian Submodule Baseline"
271
  git config user.email "[email protected]"
272
273
  empty_tree=$(git mktree </dev/null)
274
  tick
275
  source_base=$(printf 'submodule source base\n' | git commit-tree "$empty_tree")
276
  tick
277
  occupied_base=$(printf 'submodule occupied base\n' | git commit-tree "$empty_tree")
278
  tick
279
  source_a=$(printf 'submodule source A\n' | git commit-tree "$empty_tree" -p "$source_base")
280
  tick
281
  source_b=$(printf 'submodule source B\n' | git commit-tree "$empty_tree" -p "$source_base")
282
  tick
283
  occupied_a=$(printf 'submodule occupied A\n' | git commit-tree "$empty_tree" -p "$occupied_base")
284
  tick
285
  occupied_b=$(printf 'submodule occupied B\n' | git commit-tree "$empty_tree" -p "$occupied_base")
286
  tick
287
  added_a=$(printf 'submodule addition A\n' | git commit-tree "$empty_tree")
288
  tick
289
  added_b=$(printf 'submodule addition B\n' | git commit-tree "$empty_tree")
290
291
  git update-index --add --cacheinfo "160000,$source_base,source"
292
  git update-index --add --cacheinfo "160000,$occupied_base,occupied"
293
  tick
294
  git commit -m "gitlink base"
295
  git branch base
296
297
  for side in A B
298
  do
299
    for operation in "${operations[@]}"
300
    do
301
      git checkout --detach --force base
302
      apply_submodule_operation "$side" "$operation"
303
      tick
304
      git commit --allow-empty -m "$side: $operation"
305
      git branch "$side-$operation"
306
    done
307
  done
308
309
  : >cases.tsv
310
  for ((left = 0; left < ${#operations[@]}; left++))
311
  do
312
    for ((right = left; right < ${#operations[@]}; right++))
313
    do
314
      left_operation=${operations[$left]}
315
      right_operation=${operations[$right]}
316
      case_name="${left_operation}--${right_operation}"
317
      forward_file="${case_name}-A-B.merge-info"
318
      reverse_file="${case_name}-B-A.merge-info"
319
320
      if git merge-tree -z --write-tree \
321
        "A-$left_operation" "B-$right_operation" >".git/$forward_file"
322
      then
323
        forward_status=clean
324
      else
325
        forward_status=conflicted
326
      fi
327
      if git merge-tree -z --write-tree \
328
        "B-$right_operation" "A-$left_operation" >".git/$reverse_file"
329
      then
330
        reverse_status=clean
331
      else
332
        reverse_status=conflicted
333
      fi
334
335
      printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
336
        "$left_operation" \
337
        "$right_operation" \
338
        "$(git rev-parse "A-$left_operation")" \
339
        "$(git rev-parse "B-$right_operation")" \
340
        "$forward_file" \
341
        "$forward_status" \
342
        "$reverse_file" \
343
        "$reverse_status" >>cases.tsv
344
    done
345
  done
346
)