t / t5318-commit-graph.shon commit Merge branch 'tb/void-check-attr' into maint (761868b)
   1#!/bin/sh
   2
   3test_description='commit graph'
   4. ./test-lib.sh
   5
   6test_expect_success 'setup full repo' '
   7        mkdir full &&
   8        cd "$TRASH_DIRECTORY/full" &&
   9        git init &&
  10        git config core.commitGraph true &&
  11        objdir=".git/objects"
  12'
  13
  14test_expect_success 'verify graph with no graph file' '
  15        cd "$TRASH_DIRECTORY/full" &&
  16        git commit-graph verify
  17'
  18
  19test_expect_success 'write graph with no packs' '
  20        cd "$TRASH_DIRECTORY/full" &&
  21        git commit-graph write --object-dir . &&
  22        test_path_is_file info/commit-graph
  23'
  24
  25test_expect_success 'create commits and repack' '
  26        cd "$TRASH_DIRECTORY/full" &&
  27        for i in $(test_seq 3)
  28        do
  29                test_commit $i &&
  30                git branch commits/$i
  31        done &&
  32        git repack
  33'
  34
  35graph_git_two_modes() {
  36        git -c core.commitGraph=true $1 >output
  37        git -c core.commitGraph=false $1 >expect
  38        test_cmp output expect
  39}
  40
  41graph_git_behavior() {
  42        MSG=$1
  43        DIR=$2
  44        BRANCH=$3
  45        COMPARE=$4
  46        test_expect_success "check normal git operations: $MSG" '
  47                cd "$TRASH_DIRECTORY/$DIR" &&
  48                graph_git_two_modes "log --oneline $BRANCH" &&
  49                graph_git_two_modes "log --topo-order $BRANCH" &&
  50                graph_git_two_modes "log --graph $COMPARE..$BRANCH" &&
  51                graph_git_two_modes "branch -vv" &&
  52                graph_git_two_modes "merge-base -a $BRANCH $COMPARE"
  53        '
  54}
  55
  56graph_git_behavior 'no graph' full commits/3 commits/1
  57
  58graph_read_expect() {
  59        OPTIONAL=""
  60        NUM_CHUNKS=3
  61        if test ! -z $2
  62        then
  63                OPTIONAL=" $2"
  64                NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
  65        fi
  66        cat >expect <<- EOF
  67        header: 43475048 1 1 $NUM_CHUNKS 0
  68        num_commits: $1
  69        chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL
  70        EOF
  71        git commit-graph read >output &&
  72        test_cmp expect output
  73}
  74
  75test_expect_success 'write graph' '
  76        cd "$TRASH_DIRECTORY/full" &&
  77        graph1=$(git commit-graph write) &&
  78        test_path_is_file $objdir/info/commit-graph &&
  79        graph_read_expect "3"
  80'
  81
  82graph_git_behavior 'graph exists' full commits/3 commits/1
  83
  84test_expect_success 'Add more commits' '
  85        cd "$TRASH_DIRECTORY/full" &&
  86        git reset --hard commits/1 &&
  87        for i in $(test_seq 4 5)
  88        do
  89                test_commit $i &&
  90                git branch commits/$i
  91        done &&
  92        git reset --hard commits/2 &&
  93        for i in $(test_seq 6 7)
  94        do
  95                test_commit $i &&
  96                git branch commits/$i
  97        done &&
  98        git reset --hard commits/2 &&
  99        git merge commits/4 &&
 100        git branch merge/1 &&
 101        git reset --hard commits/4 &&
 102        git merge commits/6 &&
 103        git branch merge/2 &&
 104        git reset --hard commits/3 &&
 105        git merge commits/5 commits/7 &&
 106        git branch merge/3 &&
 107        git repack
 108'
 109
 110# Current graph structure:
 111#
 112#   __M3___
 113#  /   |   \
 114# 3 M1 5 M2 7
 115# |/  \|/  \|
 116# 2    4    6
 117# |___/____/
 118# 1
 119
 120test_expect_success 'write graph with merges' '
 121        cd "$TRASH_DIRECTORY/full" &&
 122        git commit-graph write &&
 123        test_path_is_file $objdir/info/commit-graph &&
 124        graph_read_expect "10" "large_edges"
 125'
 126
 127graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
 128graph_git_behavior 'merge 1 vs 3' full merge/1 merge/3
 129graph_git_behavior 'merge 2 vs 3' full merge/2 merge/3
 130
 131test_expect_success 'Add one more commit' '
 132        cd "$TRASH_DIRECTORY/full" &&
 133        test_commit 8 &&
 134        git branch commits/8 &&
 135        ls $objdir/pack | grep idx >existing-idx &&
 136        git repack &&
 137        ls $objdir/pack| grep idx | grep -v -f existing-idx >new-idx
 138'
 139
 140# Current graph structure:
 141#
 142#      8
 143#      |
 144#   __M3___
 145#  /   |   \
 146# 3 M1 5 M2 7
 147# |/  \|/  \|
 148# 2    4    6
 149# |___/____/
 150# 1
 151
 152graph_git_behavior 'mixed mode, commit 8 vs merge 1' full commits/8 merge/1
 153graph_git_behavior 'mixed mode, commit 8 vs merge 2' full commits/8 merge/2
 154
 155test_expect_success 'write graph with new commit' '
 156        cd "$TRASH_DIRECTORY/full" &&
 157        git commit-graph write &&
 158        test_path_is_file $objdir/info/commit-graph &&
 159        graph_read_expect "11" "large_edges"
 160'
 161
 162graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
 163graph_git_behavior 'full graph, commit 8 vs merge 2' full commits/8 merge/2
 164
 165test_expect_success 'write graph with nothing new' '
 166        cd "$TRASH_DIRECTORY/full" &&
 167        git commit-graph write &&
 168        test_path_is_file $objdir/info/commit-graph &&
 169        graph_read_expect "11" "large_edges"
 170'
 171
 172graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
 173graph_git_behavior 'cleared graph, commit 8 vs merge 2' full commits/8 merge/2
 174
 175test_expect_success 'build graph from latest pack with closure' '
 176        cd "$TRASH_DIRECTORY/full" &&
 177        cat new-idx | git commit-graph write --stdin-packs &&
 178        test_path_is_file $objdir/info/commit-graph &&
 179        graph_read_expect "9" "large_edges"
 180'
 181
 182graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
 183graph_git_behavior 'graph from pack, commit 8 vs merge 2' full commits/8 merge/2
 184
 185test_expect_success 'build graph from commits with closure' '
 186        cd "$TRASH_DIRECTORY/full" &&
 187        git tag -a -m "merge" tag/merge merge/2 &&
 188        git rev-parse tag/merge >commits-in &&
 189        git rev-parse merge/1 >>commits-in &&
 190        cat commits-in | git commit-graph write --stdin-commits &&
 191        test_path_is_file $objdir/info/commit-graph &&
 192        graph_read_expect "6"
 193'
 194
 195graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
 196graph_git_behavior 'graph from commits, commit 8 vs merge 2' full commits/8 merge/2
 197
 198test_expect_success 'build graph from commits with append' '
 199        cd "$TRASH_DIRECTORY/full" &&
 200        git rev-parse merge/3 | git commit-graph write --stdin-commits --append &&
 201        test_path_is_file $objdir/info/commit-graph &&
 202        graph_read_expect "10" "large_edges"
 203'
 204
 205graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
 206graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
 207
 208test_expect_success 'build graph using --reachable' '
 209        cd "$TRASH_DIRECTORY/full" &&
 210        git commit-graph write --reachable &&
 211        test_path_is_file $objdir/info/commit-graph &&
 212        graph_read_expect "11" "large_edges"
 213'
 214
 215graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
 216graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
 217
 218test_expect_success 'setup bare repo' '
 219        cd "$TRASH_DIRECTORY" &&
 220        git clone --bare --no-local full bare &&
 221        cd bare &&
 222        git config core.commitGraph true &&
 223        baredir="./objects"
 224'
 225
 226graph_git_behavior 'bare repo, commit 8 vs merge 1' bare commits/8 merge/1
 227graph_git_behavior 'bare repo, commit 8 vs merge 2' bare commits/8 merge/2
 228
 229test_expect_success 'write graph in bare repo' '
 230        cd "$TRASH_DIRECTORY/bare" &&
 231        git commit-graph write &&
 232        test_path_is_file $baredir/info/commit-graph &&
 233        graph_read_expect "11" "large_edges"
 234'
 235
 236graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
 237graph_git_behavior 'bare repo with graph, commit 8 vs merge 2' bare commits/8 merge/2
 238
 239test_expect_success 'perform fast-forward merge in full repo' '
 240        cd "$TRASH_DIRECTORY/full" &&
 241        git checkout -b merge-5-to-8 commits/5 &&
 242        git merge commits/8 &&
 243        git show-ref -s merge-5-to-8 >output &&
 244        git show-ref -s commits/8 >expect &&
 245        test_cmp expect output
 246'
 247
 248test_expect_success 'check that gc computes commit-graph' '
 249        cd "$TRASH_DIRECTORY/full" &&
 250        git commit --allow-empty -m "blank" &&
 251        git commit-graph write --reachable &&
 252        cp $objdir/info/commit-graph commit-graph-before-gc &&
 253        git reset --hard HEAD~1 &&
 254        git config gc.writeCommitGraph true &&
 255        git gc &&
 256        cp $objdir/info/commit-graph commit-graph-after-gc &&
 257        ! test_cmp_bin commit-graph-before-gc commit-graph-after-gc &&
 258        git commit-graph write --reachable &&
 259        test_cmp_bin commit-graph-after-gc $objdir/info/commit-graph
 260'
 261
 262test_expect_success 'replace-objects invalidates commit-graph' '
 263        cd "$TRASH_DIRECTORY" &&
 264        test_when_finished rm -rf replace &&
 265        git clone full replace &&
 266        (
 267                cd replace &&
 268                git commit-graph write --reachable &&
 269                test_path_is_file .git/objects/info/commit-graph &&
 270                git replace HEAD~1 HEAD~2 &&
 271                git -c core.commitGraph=false log >expect &&
 272                git -c core.commitGraph=true log >actual &&
 273                test_cmp expect actual &&
 274                git commit-graph write --reachable &&
 275                git -c core.commitGraph=false --no-replace-objects log >expect &&
 276                git -c core.commitGraph=true --no-replace-objects log >actual &&
 277                test_cmp expect actual &&
 278                rm -rf .git/objects/info/commit-graph &&
 279                git commit-graph write --reachable &&
 280                test_path_is_file .git/objects/info/commit-graph
 281        )
 282'
 283
 284test_expect_success 'commit grafts invalidate commit-graph' '
 285        cd "$TRASH_DIRECTORY" &&
 286        test_when_finished rm -rf graft &&
 287        git clone full graft &&
 288        (
 289                cd graft &&
 290                git commit-graph write --reachable &&
 291                test_path_is_file .git/objects/info/commit-graph &&
 292                H1=$(git rev-parse --verify HEAD~1) &&
 293                H3=$(git rev-parse --verify HEAD~3) &&
 294                echo "$H1 $H3" >.git/info/grafts &&
 295                git -c core.commitGraph=false log >expect &&
 296                git -c core.commitGraph=true log >actual &&
 297                test_cmp expect actual &&
 298                git commit-graph write --reachable &&
 299                git -c core.commitGraph=false --no-replace-objects log >expect &&
 300                git -c core.commitGraph=true --no-replace-objects log >actual &&
 301                test_cmp expect actual &&
 302                rm -rf .git/objects/info/commit-graph &&
 303                git commit-graph write --reachable &&
 304                test_path_is_missing .git/objects/info/commit-graph
 305        )
 306'
 307
 308test_expect_success 'replace-objects invalidates commit-graph' '
 309        cd "$TRASH_DIRECTORY" &&
 310        test_when_finished rm -rf shallow &&
 311        git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow &&
 312        (
 313                cd shallow &&
 314                git commit-graph write --reachable &&
 315                test_path_is_missing .git/objects/info/commit-graph &&
 316                git fetch origin --unshallow &&
 317                git commit-graph write --reachable &&
 318                test_path_is_file .git/objects/info/commit-graph
 319        )
 320'
 321
 322# the verify tests below expect the commit-graph to contain
 323# exactly the commits reachable from the commits/8 branch.
 324# If the file changes the set of commits in the list, then the
 325# offsets into the binary file will result in different edits
 326# and the tests will likely break.
 327
 328test_expect_success 'git commit-graph verify' '
 329        cd "$TRASH_DIRECTORY/full" &&
 330        git rev-parse commits/8 | git commit-graph write --stdin-commits &&
 331        git commit-graph verify >output
 332'
 333
 334NUM_COMMITS=9
 335NUM_OCTOPUS_EDGES=2
 336HASH_LEN=20
 337GRAPH_BYTE_VERSION=4
 338GRAPH_BYTE_HASH=5
 339GRAPH_BYTE_CHUNK_COUNT=6
 340GRAPH_CHUNK_LOOKUP_OFFSET=8
 341GRAPH_CHUNK_LOOKUP_WIDTH=12
 342GRAPH_CHUNK_LOOKUP_ROWS=5
 343GRAPH_BYTE_OID_FANOUT_ID=$GRAPH_CHUNK_LOOKUP_OFFSET
 344GRAPH_BYTE_OID_LOOKUP_ID=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
 345                            1 * $GRAPH_CHUNK_LOOKUP_WIDTH))
 346GRAPH_BYTE_COMMIT_DATA_ID=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
 347                             2 * $GRAPH_CHUNK_LOOKUP_WIDTH))
 348GRAPH_FANOUT_OFFSET=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
 349                       $GRAPH_CHUNK_LOOKUP_WIDTH * $GRAPH_CHUNK_LOOKUP_ROWS))
 350GRAPH_BYTE_FANOUT1=$(($GRAPH_FANOUT_OFFSET + 4 * 4))
 351GRAPH_BYTE_FANOUT2=$(($GRAPH_FANOUT_OFFSET + 4 * 255))
 352GRAPH_OID_LOOKUP_OFFSET=$(($GRAPH_FANOUT_OFFSET + 4 * 256))
 353GRAPH_BYTE_OID_LOOKUP_ORDER=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 8))
 354GRAPH_BYTE_OID_LOOKUP_MISSING=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 4 + 10))
 355GRAPH_COMMIT_DATA_OFFSET=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * $NUM_COMMITS))
 356GRAPH_BYTE_COMMIT_TREE=$GRAPH_COMMIT_DATA_OFFSET
 357GRAPH_BYTE_COMMIT_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN))
 358GRAPH_BYTE_COMMIT_EXTRA_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 4))
 359GRAPH_BYTE_COMMIT_WRONG_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 3))
 360GRAPH_BYTE_COMMIT_GENERATION=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 11))
 361GRAPH_BYTE_COMMIT_DATE=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 12))
 362GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16))
 363GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \
 364                             $GRAPH_COMMIT_DATA_WIDTH * $NUM_COMMITS))
 365GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
 366GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
 367
 368# usage: corrupt_graph_and_verify <position> <data> <string>
 369# Manipulates the commit-graph file at the position
 370# by inserting the data, then runs 'git commit-graph verify'
 371# and places the output in the file 'err'. Test 'err' for
 372# the given string.
 373corrupt_graph_and_verify() {
 374        pos=$1
 375        data="${2:-\0}"
 376        grepstr=$3
 377        cd "$TRASH_DIRECTORY/full" &&
 378        test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
 379        cp $objdir/info/commit-graph commit-graph-backup &&
 380        printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
 381        test_must_fail git commit-graph verify 2>test_err &&
 382        grep -v "^+" test_err >err
 383        test_i18ngrep "$grepstr" err
 384}
 385
 386test_expect_success 'detect bad signature' '
 387        corrupt_graph_and_verify 0 "\0" \
 388                "graph signature"
 389'
 390
 391test_expect_success 'detect bad version' '
 392        corrupt_graph_and_verify $GRAPH_BYTE_VERSION "\02" \
 393                "graph version"
 394'
 395
 396test_expect_success 'detect bad hash version' '
 397        corrupt_graph_and_verify $GRAPH_BYTE_HASH "\02" \
 398                "hash version"
 399'
 400
 401test_expect_success 'detect low chunk count' '
 402        corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\02" \
 403                "missing the .* chunk"
 404'
 405
 406test_expect_success 'detect missing OID fanout chunk' '
 407        corrupt_graph_and_verify $GRAPH_BYTE_OID_FANOUT_ID "\0" \
 408                "missing the OID Fanout chunk"
 409'
 410
 411test_expect_success 'detect missing OID lookup chunk' '
 412        corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ID "\0" \
 413                "missing the OID Lookup chunk"
 414'
 415
 416test_expect_success 'detect missing commit data chunk' '
 417        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATA_ID "\0" \
 418                "missing the Commit Data chunk"
 419'
 420
 421test_expect_success 'detect incorrect fanout' '
 422        corrupt_graph_and_verify $GRAPH_BYTE_FANOUT1 "\01" \
 423                "fanout value"
 424'
 425
 426test_expect_success 'detect incorrect fanout final value' '
 427        corrupt_graph_and_verify $GRAPH_BYTE_FANOUT2 "\01" \
 428                "fanout value"
 429'
 430
 431test_expect_success 'detect incorrect OID order' '
 432        corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ORDER "\01" \
 433                "incorrect OID order"
 434'
 435
 436test_expect_success 'detect OID not in object database' '
 437        corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_MISSING "\01" \
 438                "from object database"
 439'
 440
 441test_expect_success 'detect incorrect tree OID' '
 442        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_TREE "\01" \
 443                "root tree OID for commit"
 444'
 445
 446test_expect_success 'detect incorrect parent int-id' '
 447        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_PARENT "\01" \
 448                "invalid parent"
 449'
 450
 451test_expect_success 'detect extra parent int-id' '
 452        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_EXTRA_PARENT "\00" \
 453                "is too long"
 454'
 455
 456test_expect_success 'detect wrong parent' '
 457        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_WRONG_PARENT "\01" \
 458                "commit-graph parent for"
 459'
 460
 461test_expect_success 'detect incorrect generation number' '
 462        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\070" \
 463                "generation for commit"
 464'
 465
 466test_expect_success 'detect incorrect generation number' '
 467        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\01" \
 468                "non-zero generation number"
 469'
 470
 471test_expect_success 'detect incorrect commit date' '
 472        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATE "\01" \
 473                "commit date"
 474'
 475
 476test_expect_success 'detect incorrect parent for octopus merge' '
 477        corrupt_graph_and_verify $GRAPH_BYTE_OCTOPUS "\01" \
 478                "invalid parent"
 479'
 480
 481test_expect_success 'detect invalid checksum hash' '
 482        corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
 483                "incorrect checksum"
 484'
 485
 486test_expect_success 'git fsck (checks commit-graph)' '
 487        cd "$TRASH_DIRECTORY/full" &&
 488        git fsck &&
 489        corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
 490                "incorrect checksum" &&
 491        test_must_fail git fsck
 492'
 493
 494test_expect_success 'setup non-the_repository tests' '
 495        rm -rf repo &&
 496        git init repo &&
 497        test_commit -C repo one &&
 498        test_commit -C repo two &&
 499        git -C repo config core.commitGraph true &&
 500        git -C repo rev-parse two | \
 501                git -C repo commit-graph write --stdin-commits
 502'
 503
 504test_expect_success 'parse_commit_in_graph works for non-the_repository' '
 505        test-tool repository parse_commit_in_graph \
 506                repo/.git repo "$(git -C repo rev-parse two)" >actual &&
 507        {
 508                git -C repo log --pretty=format:"%ct " -1 &&
 509                git -C repo rev-parse one
 510        } >expect &&
 511        test_cmp expect actual &&
 512
 513        test-tool repository parse_commit_in_graph \
 514                repo/.git repo "$(git -C repo rev-parse one)" >actual &&
 515        git -C repo log --pretty="%ct" -1 one >expect &&
 516        test_cmp expect actual
 517'
 518
 519test_expect_success 'get_commit_tree_in_graph works for non-the_repository' '
 520        test-tool repository get_commit_tree_in_graph \
 521                repo/.git repo "$(git -C repo rev-parse two)" >actual &&
 522        git -C repo rev-parse two^{tree} >expect &&
 523        test_cmp expect actual &&
 524
 525        test-tool repository get_commit_tree_in_graph \
 526                repo/.git repo "$(git -C repo rev-parse one)" >actual &&
 527        git -C repo rev-parse one^{tree} >expect &&
 528        test_cmp expect actual
 529'
 530
 531test_done