1#!/bin/sh
   2#
   3# Copyright (c) 2009 Johan Herland
   4#
   5test_description='test git fast-import of notes objects'
   7. ./test-lib.sh
   8test_tick
  11cat >input <<INPUT_END
  12commit refs/heads/master
  13committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
  14data <<COMMIT
  15first commit
  16COMMIT
  17M 644 inline foo
  19data <<EOF
  20file foo in first commit
  21EOF
  22M 755 inline bar
  24data <<EOF
  25file bar in first commit
  26EOF
  27M 644 inline baz/xyzzy
  29data <<EOF
  30file baz/xyzzy in first commit
  31EOF
  32commit refs/heads/master
  34committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
  35data <<COMMIT
  36second commit
  37COMMIT
  38M 644 inline foo
  40data <<EOF
  41file foo in second commit
  42EOF
  43M 755 inline baz/xyzzy
  45data <<EOF
  46file baz/xyzzy in second commit
  47EOF
  48commit refs/heads/master
  50committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
  51data <<COMMIT
  52third commit
  53COMMIT
  54M 644 inline foo
  56data <<EOF
  57file foo in third commit
  58EOF
  59commit refs/heads/master
  61committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
  62data <<COMMIT
  63fourth commit
  64COMMIT
  65M 755 inline bar
  67data <<EOF
  68file bar in fourth commit
  69EOF
  70INPUT_END
  72test_expect_success 'set up master branch' '
  74        git fast-import <input &&
  76        git whatchanged master
  77'
  78commit4=$(git rev-parse refs/heads/master)
  80commit3=$(git rev-parse "$commit4^")
  81commit2=$(git rev-parse "$commit4~2")
  82commit1=$(git rev-parse "$commit4~3")
  83test_tick
  85cat >input <<INPUT_END
  86commit refs/notes/test
  87committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
  88data <<COMMIT
  89first notes commit
  90COMMIT
  91M 644 inline $commit1
  93data <<EOF
  94first note for first commit
  95EOF
  96M 755 inline $commit2
  98data <<EOF
  99first note for second commit
 100EOF
 101INPUT_END
 103cat >expect <<EXPECT_END
 105    fourth commit
 106    third commit
 107    second commit
 108    first note for second commit
 109    first commit
 110    first note for first commit
 111EXPECT_END
 112test_expect_success 'add notes with simple M command' '
 114        git fast-import <input &&
 116        GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
 117        test_cmp expect actual
 118'
 120test_tick
 122cat >input <<INPUT_END
 123feature notes
 124commit refs/notes/test
 125committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 126data <<COMMIT
 127second notes commit
 128COMMIT
 129from refs/notes/test^0
 131N inline $commit3
 132data <<EOF
 133first note for third commit
 134EOF
 135N inline $commit4
 137data <<EOF
 138first note for fourth commit
 139EOF
 140INPUT_END
 142cat >expect <<EXPECT_END
 144    fourth commit
 145    first note for fourth commit
 146    third commit
 147    first note for third commit
 148    second commit
 149    first note for second commit
 150    first commit
 151    first note for first commit
 152EXPECT_END
 153test_expect_success 'add notes with simple N command' '
 155        git fast-import <input &&
 157        GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
 158        test_cmp expect actual
 159'
 161test_tick
 163cat >input <<INPUT_END
 164commit refs/notes/test
 165committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 166data <<COMMIT
 167third notes commit
 168COMMIT
 169from refs/notes/test^0
 171N inline $commit1
 172data <<EOF
 173second note for first commit
 174EOF
 175N inline $commit2
 177data <<EOF
 178second note for second commit
 179EOF
 180N inline $commit3
 182data <<EOF
 183second note for third commit
 184EOF
 185N inline $commit4
 187data <<EOF
 188second note for fourth commit
 189EOF
 190INPUT_END
 192cat >expect <<EXPECT_END
 194    fourth commit
 195    second note for fourth commit
 196    third commit
 197    second note for third commit
 198    second commit
 199    second note for second commit
 200    first commit
 201    second note for first commit
 202EXPECT_END
 203test_expect_success 'update existing notes with N command' '
 205        git fast-import <input &&
 207        GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
 208        test_cmp expect actual
 209'
 211test_tick
 213cat >input <<INPUT_END
 214commit refs/notes/test
 215committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 216data <<COMMIT
 217fourth notes commit
 218COMMIT
 219from refs/notes/test^0
 221M 644 inline $(echo "$commit3" | sed "s|^..|&/|")
 222data <<EOF
 223prefix of note for third commit
 224EOF
 225M 644 inline $(echo "$commit4" | sed "s|^..|&/|")
 227data <<EOF
 228prefix of note for fourth commit
 229EOF
 230M 644 inline $(echo "$commit4" | sed "s|^\(..\)\(..\)|\1/\2/|")
 232data <<EOF
 233pre-prefix of note for fourth commit
 234EOF
 235N inline $commit1
 237data <<EOF
 238third note for first commit
 239EOF
 240N inline $commit2
 242data <<EOF
 243third note for second commit
 244EOF
 245N inline $commit3
 247data <<EOF
 248third note for third commit
 249EOF
 250N inline $commit4
 252data <<EOF
 253third note for fourth commit
 254EOF
 255INPUT_END
 258whitespace="    "
 260cat >expect <<EXPECT_END
 262    fourth commit
 263    pre-prefix of note for fourth commit
 264$whitespace
 265    prefix of note for fourth commit
 266$whitespace
 267    third note for fourth commit
 268    third commit
 269    prefix of note for third commit
 270$whitespace
 271    third note for third commit
 272    second commit
 273    third note for second commit
 274    first commit
 275    third note for first commit
 276EXPECT_END
 277test_expect_success 'add concatentation notes with M command' '
 279        git fast-import <input &&
 281        GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
 282        test_cmp expect actual
 283'
 285test_tick
 287cat >input <<INPUT_END
 288commit refs/notes/test
 289committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 290data <<COMMIT
 291fifth notes commit
 292COMMIT
 293from refs/notes/test^0
 295deleteall
 296INPUT_END
 298cat >expect <<EXPECT_END
 300    fourth commit
 301    third commit
 302    second commit
 303    first commit
 304EXPECT_END
 305test_expect_success 'verify that deleteall also removes notes' '
 307        git fast-import <input &&
 309        GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
 310        test_cmp expect actual
 311'
 313test_tick
 315cat >input <<INPUT_END
 316commit refs/notes/test
 317committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 318data <<COMMIT
 319sixth notes commit
 320COMMIT
 321from refs/notes/test^0
 323M 644 inline $commit1
 324data <<EOF
 325third note for first commit
 326EOF
 327M 644 inline $commit3
 329data <<EOF
 330third note for third commit
 331EOF
 332N inline $commit1
 334data <<EOF
 335fourth note for first commit
 336EOF
 337N inline $commit3
 339data <<EOF
 340fourth note for third commit
 341EOF
 342INPUT_END
 344cat >expect <<EXPECT_END
 346    fourth commit
 347    third commit
 348    fourth note for third commit
 349    second commit
 350    first commit
 351    fourth note for first commit
 352EXPECT_END
 353test_expect_success 'verify that later N commands override earlier M commands' '
 355        git fast-import <input &&
 357        GIT_NOTES_REF=refs/notes/test git log | grep "^    " > actual &&
 358        test_cmp expect actual
 359'
 361# Write fast-import commands to create the given number of commits
 363fast_import_commits () {
 364        my_ref=$1
 365        my_num_commits=$2
 366        my_append_to_file=$3
 367        my_i=0
 368        while test $my_i -lt $my_num_commits
 369        do
 370                my_i=$(($my_i + 1))
 371                test_tick
 372                cat >>"$my_append_to_file" <<INPUT_END
 373commit $my_ref
 374mark :$my_i
 375committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 376data <<COMMIT
 377commit #$my_i
 378COMMIT
 379M 644 inline file
 381data <<EOF
 382file contents in commit #$my_i
 383EOF
 384INPUT_END
 386        done
 387}
 388# Write fast-import commands to create the given number of notes annotating
 390# the commits created by fast_import_commits()
 391fast_import_notes () {
 392        my_notes_ref=$1
 393        my_num_commits=$2
 394        my_append_to_file=$3
 395        my_note_append=$4
 396        test_tick
 397        cat >>"$my_append_to_file" <<INPUT_END
 398commit $my_notes_ref
 399committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 400data <<COMMIT
 401committing $my_num_commits notes
 402COMMIT
 403INPUT_END
 405        my_i=0
 407        while test $my_i -lt $my_num_commits
 408        do
 409                my_i=$(($my_i + 1))
 410                cat >>"$my_append_to_file" <<INPUT_END
 411N inline :$my_i
 412data <<EOF
 413note for commit #$my_i$my_note_append
 414EOF
 415INPUT_END
 417        done
 418}
 419rm input expect
 422num_commits=400
 423# Create lots of commits
 424fast_import_commits "refs/heads/many_commits" $num_commits input
 425# Create one note per above commit
 426fast_import_notes "refs/notes/many_notes" $num_commits input
 427# Add a couple of non-notes as well
 428test_tick
 429cat >>input <<INPUT_END
 430commit refs/notes/many_notes
 431committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 432data <<COMMIT
 433committing some non-notes to the notes tree
 434COMMIT
 435M 755 inline foobar/non-note.txt
 437data <<EOF
 438This is not a note, but rather a regular file residing in a notes tree
 439EOF
 440M 644 inline deadbeef
 442data <<EOF
 443Non-note file
 444EOF
 445M 644 inline de/adbeef
 447data <<EOF
 448Another non-note file
 449EOF
 450INPUT_END
 452# Finally create the expected output from all these notes and commits
 453i=$num_commits
 454while test $i -gt 0
 455do
 456        cat >>expect <<EXPECT_END
 457    commit #$i
 458    note for commit #$i
 459EXPECT_END
 460        i=$(($i - 1))
 461done
 462test_expect_success 'add lots of commits and notes' '
 464        git fast-import <input &&
 466        GIT_NOTES_REF=refs/notes/many_notes git log refs/heads/many_commits |
 467            grep "^    " > actual &&
 468        test_cmp expect actual
 469'
 471test_expect_success 'verify that lots of notes trigger a fanout scheme' '
 473        # None of the entries in the top-level notes tree should be a full SHA1
 475        git ls-tree --name-only refs/notes/many_notes |
 476        while read path
 477        do
 478                if test $(expr length "$path") -ge 40
 479                then
 480                        return 1
 481                fi
 482        done
 483'
 485# Create another notes tree from the one above
 487SP=" "
 488cat >>input <<INPUT_END
 489commit refs/heads/other_commits
 490committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 491data <<COMMIT
 492commit #$(($num_commit + 1))
 493COMMIT
 494from refs/heads/many_commits
 496M 644 inline file
 497data <<EOF
 498file contents in commit #$(($num_commit + 1))
 499EOF
 500commit refs/notes/other_notes
 502committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 503data <<COMMIT
 504committing one more note on a tree imported from a previous notes tree
 505COMMIT
 506M 040000 $(git log --no-walk --format=%T refs/notes/many_notes)$SP
 508N inline :$(($num_commit + 1))
 509data <<EOF
 510note for commit #$(($num_commit + 1))
 511EOF
 512INPUT_END
 513test_expect_success 'verify that importing a notes tree respects the fanout scheme' '
 515        git fast-import <input &&
 516        # None of the entries in the top-level notes tree should be a full SHA1
 518        git ls-tree --name-only refs/notes/other_notes |
 519        while read path
 520        do
 521                if test $(expr length "$path") -ge 40
 522                then
 523                        return 1
 524                fi
 525        done
 526'
 527cat >>expect_non-note1 << EOF
 529This is not a note, but rather a regular file residing in a notes tree
 530EOF
 531cat >>expect_non-note2 << EOF
 533Non-note file
 534EOF
 535cat >>expect_non-note3 << EOF
 537Another non-note file
 538EOF
 539test_expect_success 'verify that non-notes are untouched by a fanout change' '
 541        git cat-file -p refs/notes/many_notes:foobar/non-note.txt > actual &&
 543        test_cmp expect_non-note1 actual &&
 544        git cat-file -p refs/notes/many_notes:deadbeef > actual &&
 545        test_cmp expect_non-note2 actual &&
 546        git cat-file -p refs/notes/many_notes:de/adbeef > actual &&
 547        test_cmp expect_non-note3 actual
 548'
 550# Change the notes for the three top commits
 552test_tick
 553cat >input <<INPUT_END
 554commit refs/notes/many_notes
 555committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 556data <<COMMIT
 557changing notes for the top three commits
 558COMMIT
 559from refs/notes/many_notes^0
 560INPUT_END
 561rm expect
 563i=$num_commits
 564j=0
 565while test $j -lt 3
 566do
 567        cat >>input <<INPUT_END
 568N inline refs/heads/many_commits~$j
 569data <<EOF
 570changed note for commit #$i
 571EOF
 572INPUT_END
 573        cat >>expect <<EXPECT_END
 574    commit #$i
 575    changed note for commit #$i
 576EXPECT_END
 577        i=$(($i - 1))
 578        j=$(($j + 1))
 579done
 580test_expect_success 'change a few existing notes' '
 582        git fast-import <input &&
 584        GIT_NOTES_REF=refs/notes/many_notes git log -n3 refs/heads/many_commits |
 585            grep "^    " > actual &&
 586        test_cmp expect actual
 587'
 589test_expect_success 'verify that changing notes respect existing fanout' '
 591        # None of the entries in the top-level notes tree should be a full SHA1
 593        git ls-tree --name-only refs/notes/many_notes |
 594        while read path
 595        do
 596                if test $(expr length "$path") -ge 40
 597                then
 598                        return 1
 599                fi
 600        done
 601'
 603remaining_notes=10
 605test_tick
 606cat >input <<INPUT_END
 607commit refs/notes/many_notes
 608committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 609data <<COMMIT
 610removing all notes but $remaining_notes
 611COMMIT
 612from refs/notes/many_notes^0
 613INPUT_END
 614i=$(($num_commits - $remaining_notes))
 616for sha1 in $(git rev-list -n $i refs/heads/many_commits)
 617do
 618        cat >>input <<INPUT_END
 619N 0000000000000000000000000000000000000000 $sha1
 620INPUT_END
 621done
 622i=$num_commits
 624rm expect
 625while test $i -gt 0
 626do
 627        cat >>expect <<EXPECT_END
 628    commit #$i
 629EXPECT_END
 630        if test $i -le $remaining_notes
 631        then
 632                cat >>expect <<EXPECT_END
 633    note for commit #$i
 634EXPECT_END
 635        fi
 636        i=$(($i - 1))
 637done
 638test_expect_success 'remove lots of notes' '
 640        git fast-import <input &&
 642        GIT_NOTES_REF=refs/notes/many_notes git log refs/heads/many_commits |
 643            grep "^    " > actual &&
 644        test_cmp expect actual
 645'
 647test_expect_success 'verify that removing notes trigger fanout consolidation' '
 649        # All entries in the top-level notes tree should be a full SHA1
 651        git ls-tree --name-only -r refs/notes/many_notes |
 652        while read path
 653        do
 654                # Explicitly ignore the non-note paths
 655                test "$path" = "foobar/non-note.txt" && continue
 656                test "$path" = "deadbeef" && continue
 657                test "$path" = "de/adbeef" && continue
 658                if test $(expr length "$path") -ne 40
 660                then
 661                        return 1
 662                fi
 663        done
 664'
 666test_expect_success 'verify that non-notes are untouched by a fanout change' '
 668        git cat-file -p refs/notes/many_notes:foobar/non-note.txt > actual &&
 670        test_cmp expect_non-note1 actual &&
 671        git cat-file -p refs/notes/many_notes:deadbeef > actual &&
 672        test_cmp expect_non-note2 actual &&
 673        git cat-file -p refs/notes/many_notes:de/adbeef > actual &&
 674        test_cmp expect_non-note3 actual
 675'
 677rm input expect
 680num_notes_refs=10
 681num_commits=16
 682some_commits=8
 683# Create commits
 684fast_import_commits "refs/heads/more_commits" $num_commits input
 685# Create one note per above commit per notes ref
 686i=0
 687while test $i -lt $num_notes_refs
 688do
 689        i=$(($i + 1))
 690        fast_import_notes "refs/notes/more_notes_$i" $num_commits input
 691done
 692# Trigger branch reloading in git-fast-import by repeating the note creation
 693i=0
 694while test $i -lt $num_notes_refs
 695do
 696        i=$(($i + 1))
 697        fast_import_notes "refs/notes/more_notes_$i" $some_commits input " (2)"
 698done
 699# Finally create the expected output from the notes in refs/notes/more_notes_1
 700i=$num_commits
 701while test $i -gt 0
 702do
 703        note_data="note for commit #$i"
 704        if test $i -le $some_commits
 705        then
 706                note_data="$note_data (2)"
 707        fi
 708        cat >>expect <<EXPECT_END
 709    commit #$i
 710    $note_data
 711EXPECT_END
 712        i=$(($i - 1))
 713done
 714test_expect_success "add notes to $num_commits commits in each of $num_notes_refs refs" '
 716        git fast-import --active-branches=5 <input &&
 718        GIT_NOTES_REF=refs/notes/more_notes_1 git log refs/heads/more_commits |
 719            grep "^    " > actual &&
 720        test_cmp expect actual
 721'
 723test_done