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'
 485cat >>expect_non-note1 << EOF
 487This is not a note, but rather a regular file residing in a notes tree
 488EOF
 489cat >>expect_non-note2 << EOF
 491Non-note file
 492EOF
 493cat >>expect_non-note3 << EOF
 495Another non-note file
 496EOF
 497test_expect_success 'verify that non-notes are untouched by a fanout change' '
 499        git cat-file -p refs/notes/many_notes:foobar/non-note.txt > actual &&
 501        test_cmp expect_non-note1 actual &&
 502        git cat-file -p refs/notes/many_notes:deadbeef > actual &&
 503        test_cmp expect_non-note2 actual &&
 504        git cat-file -p refs/notes/many_notes:de/adbeef > actual &&
 505        test_cmp expect_non-note3 actual
 506'
 508# Change the notes for the three top commits
 510test_tick
 511cat >input <<INPUT_END
 512commit refs/notes/many_notes
 513committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 514data <<COMMIT
 515changing notes for the top three commits
 516COMMIT
 517from refs/notes/many_notes^0
 518INPUT_END
 519rm expect
 521i=$num_commits
 522j=0
 523while test $j -lt 3
 524do
 525        cat >>input <<INPUT_END
 526N inline refs/heads/many_commits~$j
 527data <<EOF
 528changed note for commit #$i
 529EOF
 530INPUT_END
 531        cat >>expect <<EXPECT_END
 532    commit #$i
 533    changed note for commit #$i
 534EXPECT_END
 535        i=$(($i - 1))
 536        j=$(($j + 1))
 537done
 538test_expect_success 'change a few existing notes' '
 540        git fast-import <input &&
 542        GIT_NOTES_REF=refs/notes/many_notes git log -n3 refs/heads/many_commits |
 543            grep "^    " > actual &&
 544        test_cmp expect actual
 545'
 547test_expect_success 'verify that changing notes respect existing fanout' '
 549        # None of the entries in the top-level notes tree should be a full SHA1
 551        git ls-tree --name-only refs/notes/many_notes |
 552        while read path
 553        do
 554                if test $(expr length "$path") -ge 40
 555                then
 556                        return 1
 557                fi
 558        done
 559'
 561remaining_notes=10
 563test_tick
 564cat >input <<INPUT_END
 565commit refs/notes/many_notes
 566committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 567data <<COMMIT
 568removing all notes but $remaining_notes
 569COMMIT
 570from refs/notes/many_notes^0
 571INPUT_END
 572i=$(($num_commits - $remaining_notes))
 574for sha1 in $(git rev-list -n $i refs/heads/many_commits)
 575do
 576        cat >>input <<INPUT_END
 577N 0000000000000000000000000000000000000000 $sha1
 578INPUT_END
 579done
 580i=$num_commits
 582rm expect
 583while test $i -gt 0
 584do
 585        cat >>expect <<EXPECT_END
 586    commit #$i
 587EXPECT_END
 588        if test $i -le $remaining_notes
 589        then
 590                cat >>expect <<EXPECT_END
 591    note for commit #$i
 592EXPECT_END
 593        fi
 594        i=$(($i - 1))
 595done
 596test_expect_success 'remove lots of notes' '
 598        git fast-import <input &&
 600        GIT_NOTES_REF=refs/notes/many_notes git log refs/heads/many_commits |
 601            grep "^    " > actual &&
 602        test_cmp expect actual
 603'
 605test_expect_success 'verify that removing notes trigger fanout consolidation' '
 607        # All entries in the top-level notes tree should be a full SHA1
 609        git ls-tree --name-only -r refs/notes/many_notes |
 610        while read path
 611        do
 612                # Explicitly ignore the non-note paths
 613                test "$path" = "foobar/non-note.txt" && continue
 614                test "$path" = "deadbeef" && continue
 615                test "$path" = "de/adbeef" && continue
 616                if test $(expr length "$path") -ne 40
 618                then
 619                        return 1
 620                fi
 621        done
 622'
 624test_expect_success 'verify that non-notes are untouched by a fanout change' '
 626        git cat-file -p refs/notes/many_notes:foobar/non-note.txt > actual &&
 628        test_cmp expect_non-note1 actual &&
 629        git cat-file -p refs/notes/many_notes:deadbeef > actual &&
 630        test_cmp expect_non-note2 actual &&
 631        git cat-file -p refs/notes/many_notes:de/adbeef > actual &&
 632        test_cmp expect_non-note3 actual
 633'
 635rm input expect
 638num_notes_refs=10
 639num_commits=16
 640some_commits=8
 641# Create commits
 642fast_import_commits "refs/heads/more_commits" $num_commits input
 643# Create one note per above commit per notes ref
 644i=0
 645while test $i -lt $num_notes_refs
 646do
 647        i=$(($i + 1))
 648        fast_import_notes "refs/notes/more_notes_$i" $num_commits input
 649done
 650# Trigger branch reloading in git-fast-import by repeating the note creation
 651i=0
 652while test $i -lt $num_notes_refs
 653do
 654        i=$(($i + 1))
 655        fast_import_notes "refs/notes/more_notes_$i" $some_commits input " (2)"
 656done
 657# Finally create the expected output from the notes in refs/notes/more_notes_1
 658i=$num_commits
 659while test $i -gt 0
 660do
 661        note_data="note for commit #$i"
 662        if test $i -le $some_commits
 663        then
 664                note_data="$note_data (2)"
 665        fi
 666        cat >>expect <<EXPECT_END
 667    commit #$i
 668    $note_data
 669EXPECT_END
 670        i=$(($i - 1))
 671done
 672test_expect_success "add notes to $num_commits commits in each of $num_notes_refs refs" '
 674        git fast-import --active-branches=5 <input &&
 676        GIT_NOTES_REF=refs/notes/more_notes_1 git log refs/heads/more_commits |
 677            grep "^    " > actual &&
 678        test_cmp expect actual
 679'
 681test_done