1#!/bin/sh
   2#
   3# Copyright (c) 2010 Johan Herland
   4#
   5test_description='Test notes merging at various fanout levels'
   7. ./test-lib.sh
   9verify_notes () {
  11        notes_ref="$1"
  12        commit="$2"
  13        if test -f "expect_notes_$notes_ref"
  14        then
  15                git -c core.notesRef="refs/notes/$notes_ref" notes |
  16                        sort >"output_notes_$notes_ref" &&
  17                test_cmp "expect_notes_$notes_ref" "output_notes_$notes_ref" ||
  18                        return 1
  19        fi &&
  20        git -c core.notesRef="refs/notes/$notes_ref" log --format="%H %s%n%N" \
  21                "$commit" >"output_log_$notes_ref" &&
  22        test_cmp "expect_log_$notes_ref" "output_log_$notes_ref"
  23}
  24verify_fanout () {
  26        notes_ref="$1"
  27        # Expect entire notes tree to have a fanout == 1
  28        git rev-parse --quiet --verify "refs/notes/$notes_ref" >/dev/null &&
  29        git ls-tree -r --name-only "refs/notes/$notes_ref" |
  30        while read path
  31        do
  32                case "$path" in
  33                ??/??????????????????????????????????????)
  34                        : true
  35                        ;;
  36                *)
  37                        echo "Invalid path \"$path\"" &&
  38                        return 1
  39                        ;;
  40                esac
  41        done
  42}
  43verify_no_fanout () {
  45        notes_ref="$1"
  46        # Expect entire notes tree to have a fanout == 0
  47        git rev-parse --quiet --verify "refs/notes/$notes_ref" >/dev/null &&
  48        git ls-tree -r --name-only "refs/notes/$notes_ref" |
  49        while read path
  50        do
  51                case "$path" in
  52                ????????????????????????????????????????)
  53                        : true
  54                        ;;
  55                *)
  56                        echo "Invalid path \"$path\"" &&
  57                        return 1
  58                        ;;
  59                esac
  60        done
  61}
  62# Set up a notes merge scenario with different kinds of conflicts
  64test_expect_success 'setup a few initial commits with notes (notes ref: x)' '
  65        git config core.notesRef refs/notes/x &&
  66        for i in 1 2 3 4 5
  67        do
  68                test_commit "commit$i" >/dev/null &&
  69                git notes add -m "notes for commit$i" || return 1
  70        done
  71'
  72commit_sha1=$(git rev-parse commit1^{commit})
  74commit_sha2=$(git rev-parse commit2^{commit})
  75commit_sha3=$(git rev-parse commit3^{commit})
  76commit_sha4=$(git rev-parse commit4^{commit})
  77commit_sha5=$(git rev-parse commit5^{commit})
  78cat <<EOF | sort >expect_notes_x
  80aed91155c7a72c2188e781fdf40e0f3761b299db $commit_sha5
  8199fab268f9d7ee7b011e091a436c78def8eeee69 $commit_sha4
  82953c20ae26c7aa0b428c20693fe38bc687f9d1a9 $commit_sha3
  836358796131b8916eaa2dde6902642942a1cb37e1 $commit_sha2
  84b02d459c32f0e68f2fe0981033bb34f38776ba47 $commit_sha1
  85EOF
  86cat >expect_log_x <<EOF
  88$commit_sha5 commit5
  89notes for commit5
  90$commit_sha4 commit4
  92notes for commit4
  93$commit_sha3 commit3
  95notes for commit3
  96$commit_sha2 commit2
  98notes for commit2
  99$commit_sha1 commit1
 101notes for commit1
 102EOF
 104test_expect_success 'sanity check (x)' '
 106        verify_notes x commit5 &&
 107        verify_no_fanout x
 108'
 109num=300
 111cp expect_log_x expect_log_y
 113test_expect_success 'Add a few hundred commits w/notes to trigger fanout (x -> y)' '
 115        git update-ref refs/notes/y refs/notes/x &&
 116        git config core.notesRef refs/notes/y &&
 117        i=5 &&
 118        while test $i -lt $num
 119        do
 120                i=$(($i + 1)) &&
 121                test_commit "commit$i" >/dev/null &&
 122                git notes add -m "notes for commit$i" || return 1
 123        done &&
 124        test "$(git rev-parse refs/notes/y)" != "$(git rev-parse refs/notes/x)" &&
 125        # Expected number of commits and notes
 126        test $(git rev-list HEAD | wc -l) = $num &&
 127        test $(git notes list | wc -l) = $num &&
 128        # 5 first notes unchanged
 129        verify_notes y commit5
 130'
 131test_expect_success 'notes tree has fanout (y)' 'verify_fanout y'
 133test_expect_success 'No-op merge (already included) (x => y)' '
 135        git update-ref refs/notes/m refs/notes/y &&
 136        git config core.notesRef refs/notes/m &&
 137        git notes merge x &&
 138        test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/y)"
 139'
 140test_expect_success 'Fast-forward merge (y => x)' '
 142        git update-ref refs/notes/m refs/notes/x &&
 143        git notes merge y &&
 144        test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/y)"
 145'
 146cat <<EOF | sort >expect_notes_z
 1489f506ee70e20379d7f78204c77b334f43d77410d $commit_sha3
 14923a47d6ea7d589895faf800752054818e1e7627b $commit_sha2
 150b02d459c32f0e68f2fe0981033bb34f38776ba47 $commit_sha1
 151EOF
 152cat >expect_log_z <<EOF
 154$commit_sha5 commit5
 155$commit_sha4 commit4
 157$commit_sha3 commit3
 159notes for commit3
 160appended notes for commit3
 162$commit_sha2 commit2
 164new notes for commit2
 165$commit_sha1 commit1
 167notes for commit1
 168EOF
 170test_expect_success 'change some of the initial 5 notes (x -> z)' '
 172        git update-ref refs/notes/z refs/notes/x &&
 173        git config core.notesRef refs/notes/z &&
 174        git notes add -f -m "new notes for commit2" commit2 &&
 175        git notes append -m "appended notes for commit3" commit3 &&
 176        git notes remove commit4 &&
 177        git notes remove commit5 &&
 178        verify_notes z commit5
 179'
 180test_expect_success 'notes tree has no fanout (z)' 'verify_no_fanout z'
 182cp expect_log_z expect_log_m
 184test_expect_success 'successful merge without conflicts (y => z)' '
 186        git update-ref refs/notes/m refs/notes/z &&
 187        git config core.notesRef refs/notes/m &&
 188        git notes merge y &&
 189        verify_notes m commit5 &&
 190        # x/y/z unchanged
 191        verify_notes x commit5 &&
 192        verify_notes y commit5 &&
 193        verify_notes z commit5
 194'
 195test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
 197cat >expect_log_w <<EOF
 199$commit_sha5 commit5
 200$commit_sha4 commit4
 202other notes for commit4
 203$commit_sha3 commit3
 205other notes for commit3
 206$commit_sha2 commit2
 208notes for commit2
 209$commit_sha1 commit1
 211other notes for commit1
 212EOF
 214test_expect_success 'introduce conflicting changes (y -> w)' '
 216        git update-ref refs/notes/w refs/notes/y &&
 217        git config core.notesRef refs/notes/w &&
 218        git notes add -f -m "other notes for commit1" commit1 &&
 219        git notes add -f -m "other notes for commit3" commit3 &&
 220        git notes add -f -m "other notes for commit4" commit4 &&
 221        git notes remove commit5 &&
 222        verify_notes w commit5
 223'
 224cat >expect_log_m <<EOF
 226$commit_sha5 commit5
 227$commit_sha4 commit4
 229other notes for commit4
 230$commit_sha3 commit3
 232other notes for commit3
 233$commit_sha2 commit2
 235new notes for commit2
 236$commit_sha1 commit1
 238other notes for commit1
 239EOF
 241test_expect_success 'successful merge using "ours" strategy (z => w)' '
 243        git update-ref refs/notes/m refs/notes/w &&
 244        git config core.notesRef refs/notes/m &&
 245        git notes merge -s ours z &&
 246        verify_notes m commit5 &&
 247        # w/x/y/z unchanged
 248        verify_notes w commit5 &&
 249        verify_notes x commit5 &&
 250        verify_notes y commit5 &&
 251        verify_notes z commit5
 252'
 253test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
 255cat >expect_log_m <<EOF
 257$commit_sha5 commit5
 258$commit_sha4 commit4
 260$commit_sha3 commit3
 262notes for commit3
 263appended notes for commit3
 265$commit_sha2 commit2
 267new notes for commit2
 268$commit_sha1 commit1
 270other notes for commit1
 271EOF
 273test_expect_success 'successful merge using "theirs" strategy (z => w)' '
 275        git update-ref refs/notes/m refs/notes/w &&
 276        git notes merge -s theirs z &&
 277        verify_notes m commit5 &&
 278        # w/x/y/z unchanged
 279        verify_notes w commit5 &&
 280        verify_notes x commit5 &&
 281        verify_notes y commit5 &&
 282        verify_notes z commit5
 283'
 284test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
 286cat >expect_log_m <<EOF
 288$commit_sha5 commit5
 289$commit_sha4 commit4
 291other notes for commit4
 292$commit_sha3 commit3
 294other notes for commit3
 295notes for commit3
 297appended notes for commit3
 299$commit_sha2 commit2
 301new notes for commit2
 302$commit_sha1 commit1
 304other notes for commit1
 305EOF
 307test_expect_success 'successful merge using "union" strategy (z => w)' '
 309        git update-ref refs/notes/m refs/notes/w &&
 310        git notes merge -s union z &&
 311        verify_notes m commit5 &&
 312        # w/x/y/z unchanged
 313        verify_notes w commit5 &&
 314        verify_notes x commit5 &&
 315        verify_notes y commit5 &&
 316        verify_notes z commit5
 317'
 318test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
 320cat >expect_log_m <<EOF
 322$commit_sha5 commit5
 323$commit_sha4 commit4
 325other notes for commit4
 326$commit_sha3 commit3
 328appended notes for commit3
 329notes for commit3
 330other notes for commit3
 331$commit_sha2 commit2
 333new notes for commit2
 334$commit_sha1 commit1
 336other notes for commit1
 337EOF
 339test_expect_success 'successful merge using "cat_sort_uniq" strategy (z => w)' '
 341        git update-ref refs/notes/m refs/notes/w &&
 342        git notes merge -s cat_sort_uniq z &&
 343        verify_notes m commit5 &&
 344        # w/x/y/z unchanged
 345        verify_notes w commit5 &&
 346        verify_notes x commit5 &&
 347        verify_notes y commit5 &&
 348        verify_notes z commit5
 349'
 350test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
 352# We're merging z into w. Here are the conflicts we expect:
 354#
 355# commit | x -> w    | x -> z    | conflict?
 356# -------|-----------|-----------|----------
 357# 1      | changed   | unchanged | no, use w
 358# 2      | unchanged | changed   | no, use z
 359# 3      | changed   | changed   | yes (w, then z in conflict markers)
 360# 4      | changed   | deleted   | yes (w)
 361# 5      | deleted   | deleted   | no, deleted
 362test_expect_success 'fails to merge using "manual" strategy (z => w)' '
 364        git update-ref refs/notes/m refs/notes/w &&
 365        test_must_fail git notes merge z
 366'
 367test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
 369cat <<EOF | sort >expect_conflicts
 371$commit_sha3
 372$commit_sha4
 373EOF
 374cat >expect_conflict_$commit_sha3 <<EOF
 376<<<<<<< refs/notes/m
 377other notes for commit3
 378=======
 379notes for commit3
 380appended notes for commit3
 382>>>>>>> refs/notes/z
 383EOF
 384cat >expect_conflict_$commit_sha4 <<EOF
 386other notes for commit4
 387EOF
 388test_expect_success 'verify conflict entries (with no fanout)' '
 390        ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
 391        test_cmp expect_conflicts output_conflicts &&
 392        ( for f in $(cat expect_conflicts); do
 393                test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
 394                exit 1
 395        done ) &&
 396        # Verify that current notes tree (pre-merge) has not changed (m == w)
 397        test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
 398'
 399cat >expect_log_m <<EOF
 401$commit_sha5 commit5
 402$commit_sha4 commit4
 404other notes for commit4
 405$commit_sha3 commit3
 407other notes for commit3
 408appended notes for commit3
 410$commit_sha2 commit2
 412new notes for commit2
 413$commit_sha1 commit1
 415other notes for commit1
 416EOF
 418test_expect_success 'resolve and finalize merge (z => w)' '
 420        cat >.git/NOTES_MERGE_WORKTREE/$commit_sha3 <<EOF &&
 421other notes for commit3
 422appended notes for commit3
 424EOF
 425        git notes merge --commit &&
 426        verify_notes m commit5 &&
 427        # w/x/y/z unchanged
 428        verify_notes w commit5 &&
 429        verify_notes x commit5 &&
 430        verify_notes y commit5 &&
 431        verify_notes z commit5
 432'
 433test_expect_success 'notes tree still has fanout after merge (m)' 'verify_fanout m'
 435test_done