| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='test describe' |
| 4 | |
| 5 | # o---o-----o----o----o-------o----x |
| 6 | # \ D,R e / |
| 7 | # \---o-------------o-' |
| 8 | # \ B / |
| 9 | # `-o----o----o-' |
| 10 | # A c |
| 11 | # |
| 12 | # First parent of a merge commit is on the same line, second parent below. |
| 13 | |
| 14 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| 15 | export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| 16 | |
| 17 | . ./test-lib.sh |
| 18 | |
| 19 | check_describe () { |
| 20 | indir= && |
| 21 | outcome=success && |
| 22 | while test $# != 0 |
| 23 | do |
| 24 | case "$1" in |
| 25 | -C) |
| 26 | indir="$2" |
| 27 | shift |
| 28 | ;; |
| 29 | --expect-failure) |
| 30 | outcome=failure |
| 31 | ;; |
| 32 | *) |
| 33 | break |
| 34 | ;; |
| 35 | esac |
| 36 | shift |
| 37 | done && |
| 38 | indir=${indir:+"$indir"/} && |
| 39 | expect="$1" |
| 40 | shift |
| 41 | describe_opts="$@" |
| 42 | test_expect_${outcome} "describe $describe_opts" ' |
| 43 | git ${indir:+ -C "$indir"} describe $describe_opts >raw && |
| 44 | sed -e "s/-g[0-9a-f]*\$/-gHASH/" <raw >actual && |
| 45 | echo "$expect" >expect && |
| 46 | test_cmp expect actual |
| 47 | ' |
| 48 | } |
| 49 | |
| 50 | test_expect_success setup ' |
| 51 | test_commit initial file one && |
| 52 | test_commit second file two && |
| 53 | test_commit third file three && |
| 54 | test_commit --annotate A file A && |
| 55 | test_commit c file c && |
| 56 | |
| 57 | git reset --hard second && |
| 58 | test_commit --annotate B side B && |
| 59 | |
| 60 | test_tick && |
| 61 | git merge -m Merged c && |
| 62 | merged=$(git rev-parse HEAD) && |
| 63 | |
| 64 | git reset --hard second && |
| 65 | test_commit --no-tag D another D && |
| 66 | |
| 67 | test_tick && |
| 68 | git tag -a -m R R && |
| 69 | |
| 70 | test_commit e another DD && |
| 71 | test_commit --no-tag "yet another" another DDD && |
| 72 | |
| 73 | test_tick && |
| 74 | git merge -m Merged $merged && |
| 75 | |
| 76 | test_commit --no-tag x file |
| 77 | ' |
| 78 | |
| 79 | check_describe A-8-gHASH HEAD |
| 80 | check_describe A-7-gHASH HEAD^ |
| 81 | check_describe R-2-gHASH HEAD^^ |
| 82 | check_describe A-3-gHASH HEAD^^2 |
| 83 | check_describe B HEAD^^2^ |
| 84 | check_describe R-1-gHASH HEAD^^^ |
| 85 | check_describe R-1-gHASH R-1-g$(git rev-parse --short HEAD^^)~1 |
| 86 | |
| 87 | check_describe c-7-gHASH --tags HEAD |
| 88 | check_describe c-6-gHASH --tags HEAD^ |
| 89 | check_describe e-1-gHASH --tags HEAD^^ |
| 90 | check_describe c-2-gHASH --tags HEAD^^2 |
| 91 | check_describe c-2-gHASH --tags c-2-g$(git rev-parse --short HEAD^^2)^0 |
| 92 | check_describe B --tags HEAD^^2^ |
| 93 | check_describe e --tags HEAD^^^ |
| 94 | check_describe e --tags --exact-match HEAD^^^ |
| 95 | |
| 96 | check_describe heads/main --all HEAD |
| 97 | check_describe tags/c-6-gHASH --all HEAD^ |
| 98 | check_describe tags/e --all HEAD^^^ |
| 99 | |
| 100 | check_describe B-0-gHASH --long HEAD^^2^ |
| 101 | check_describe A-3-gHASH --long HEAD^^2 |
| 102 | |
| 103 | check_describe c-7-gHASH --tags |
| 104 | check_describe e-3-gHASH --first-parent --tags |
| 105 | |
| 106 | check_describe c-7-gHASH --tags --no-exact-match HEAD |
| 107 | check_describe e-3-gHASH --first-parent --tags --no-exact-match HEAD |
| 108 | |
| 109 | test_expect_success '--exact-match failure' ' |
| 110 | test_must_fail git describe --exact-match HEAD 2>err |
| 111 | ' |
| 112 | |
| 113 | test_expect_success 'describe --contains defaults to HEAD without commit-ish' ' |
| 114 | echo "A^0" >expect && |
| 115 | git checkout A && |
| 116 | test_when_finished "git checkout -" && |
| 117 | git describe --contains >actual && |
| 118 | test_cmp expect actual |
| 119 | ' |
| 120 | |
| 121 | check_describe tags/A --all A^0 |
| 122 | |
| 123 | test_expect_success 'renaming tag A to Q locally produces a warning' " |
| 124 | git update-ref refs/tags/Q $(git rev-parse refs/tags/A) && |
| 125 | git update-ref -d refs/tags/A && |
| 126 | git describe HEAD 2>err >out && |
| 127 | cat >expected <<-\EOF && |
| 128 | warning: tag 'Q' is externally known as 'A' |
| 129 | EOF |
| 130 | test_cmp expected err && |
| 131 | test_grep -E '^A-8-g[0-9a-f]+$' out |
| 132 | " |
| 133 | |
| 134 | test_expect_success 'misnamed annotated tag forces long output' ' |
| 135 | description=$(git describe --no-long Q^0) && |
| 136 | expr "$description" : "A-0-g[0-9a-f]*$" && |
| 137 | git rev-parse --verify "$description" >actual && |
| 138 | git rev-parse --verify Q^0 >expect && |
| 139 | test_cmp expect actual |
| 140 | ' |
| 141 | |
| 142 | test_expect_success 'abbrev=0 will not break misplaced tag (1)' ' |
| 143 | description=$(git describe --abbrev=0 Q^0) && |
| 144 | expr "$description" : "A-0-g[0-9a-f]*$" |
| 145 | ' |
| 146 | |
| 147 | test_expect_success 'abbrev=0 will not break misplaced tag (2)' ' |
| 148 | description=$(git describe --abbrev=0 c^0) && |
| 149 | expr "$description" : "A-1-g[0-9a-f]*$" |
| 150 | ' |
| 151 | |
| 152 | test_expect_success 'rename tag Q back to A' ' |
| 153 | git update-ref refs/tags/A $(git rev-parse refs/tags/Q) && |
| 154 | git update-ref -d refs/tags/Q |
| 155 | ' |
| 156 | |
| 157 | test_expect_success 'pack tag refs' 'git pack-refs' |
| 158 | check_describe A-8-gHASH HEAD |
| 159 | |
| 160 | test_expect_success 'describe works from outside repo using --git-dir' ' |
| 161 | git clone --bare "$TRASH_DIRECTORY" "$TRASH_DIRECTORY/bare" && |
| 162 | git --git-dir "$TRASH_DIRECTORY/bare" describe >out && |
| 163 | test_grep -E "^A-8-g[0-9a-f]+$" out |
| 164 | ' |
| 165 | |
| 166 | check_describe "A-8-gHASH" --dirty |
| 167 | |
| 168 | test_expect_success 'describe --dirty with --work-tree' ' |
| 169 | ( |
| 170 | cd "$TEST_DIRECTORY" && |
| 171 | git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty >"$TRASH_DIRECTORY/out" |
| 172 | ) && |
| 173 | test_grep -E "^A-8-g[0-9a-f]+$" out |
| 174 | ' |
| 175 | |
| 176 | test_expect_success 'set-up dirty work tree' ' |
| 177 | echo >>file |
| 178 | ' |
| 179 | |
| 180 | test_expect_success 'describe --dirty with --work-tree (dirty)' ' |
| 181 | git describe --dirty >expected && |
| 182 | ( |
| 183 | cd "$TEST_DIRECTORY" && |
| 184 | git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty >"$TRASH_DIRECTORY/out" |
| 185 | ) && |
| 186 | test_grep -E "^A-8-g[0-9a-f]+-dirty$" out && |
| 187 | test_cmp expected out |
| 188 | ' |
| 189 | |
| 190 | test_expect_success 'describe --dirty=.mod with --work-tree (dirty)' ' |
| 191 | git describe --dirty=.mod >expected && |
| 192 | ( |
| 193 | cd "$TEST_DIRECTORY" && |
| 194 | git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty=.mod >"$TRASH_DIRECTORY/out" |
| 195 | ) && |
| 196 | test_grep -E "^A-8-g[0-9a-f]+.mod$" out && |
| 197 | test_cmp expected out |
| 198 | ' |
| 199 | |
| 200 | test_expect_success 'describe --dirty HEAD' ' |
| 201 | test_must_fail git describe --dirty HEAD |
| 202 | ' |
| 203 | |
| 204 | test_expect_success 'set-up matching pattern tests' ' |
| 205 | git tag -a -m test-annotated test-annotated && |
| 206 | echo >>file && |
| 207 | test_tick && |
| 208 | git commit -a -m "one more" && |
| 209 | git tag test1-lightweight && |
| 210 | echo >>file && |
| 211 | test_tick && |
| 212 | git commit -a -m "yet another" && |
| 213 | git tag test2-lightweight && |
| 214 | echo >>file && |
| 215 | test_tick && |
| 216 | git commit -a -m "even more" |
| 217 | |
| 218 | ' |
| 219 | |
| 220 | check_describe "test-annotated-3-gHASH" --match="test-*" |
| 221 | |
| 222 | check_describe "test1-lightweight-2-gHASH" --tags --match="test1-*" |
| 223 | |
| 224 | check_describe "test2-lightweight-1-gHASH" --tags --match="test2-*" |
| 225 | |
| 226 | check_describe "test2-lightweight-0-gHASH" --long --tags --match="test2-*" HEAD^ |
| 227 | |
| 228 | check_describe "test2-lightweight-0-gHASH" --long --tags --match="test1-*" --match="test2-*" HEAD^ |
| 229 | |
| 230 | check_describe "test2-lightweight-0-gHASH" --long --tags --match="test1-*" --no-match --match="test2-*" HEAD^ |
| 231 | |
| 232 | check_describe "test1-lightweight-2-gHASH" --long --tags --match="test1-*" --match="test3-*" HEAD |
| 233 | |
| 234 | check_describe "test1-lightweight-2-gHASH" --long --tags --match="test3-*" --match="test1-*" HEAD |
| 235 | |
| 236 | test_expect_success 'set-up branches' ' |
| 237 | git branch branch_A A && |
| 238 | git branch branch_C c && |
| 239 | git update-ref refs/remotes/origin/remote_branch_A "A^{commit}" && |
| 240 | git update-ref refs/remotes/origin/remote_branch_C "c^{commit}" && |
| 241 | git update-ref refs/original/original_branch_A test-annotated~2 |
| 242 | ' |
| 243 | |
| 244 | check_describe "heads/branch_A-11-gHASH" --all --match="branch_*" --exclude="branch_C" HEAD |
| 245 | |
| 246 | check_describe "remotes/origin/remote_branch_A-11-gHASH" --all --match="origin/remote_branch_*" --exclude="origin/remote_branch_C" HEAD |
| 247 | |
| 248 | check_describe "original/original_branch_A-6-gHASH" --all test-annotated~1 |
| 249 | |
| 250 | test_expect_success '--match does not work for other types' ' |
| 251 | test_must_fail git describe --all --match="*original_branch_*" test-annotated~1 |
| 252 | ' |
| 253 | |
| 254 | test_expect_success '--exclude does not work for other types' ' |
| 255 | R=$(git describe --all --exclude="any_pattern_even_not_matching" test-annotated~1) && |
| 256 | case "$R" in |
| 257 | *original_branch_A*) echo "fail: Found unknown reference $R with --exclude" |
| 258 | false;; |
| 259 | *) echo ok: Found some known type;; |
| 260 | esac |
| 261 | ' |
| 262 | |
| 263 | test_expect_success 'name-rev with exact tags' ' |
| 264 | echo A >expect && |
| 265 | tag_object=$(git rev-parse refs/tags/A) && |
| 266 | git name-rev --tags --name-only $tag_object >actual && |
| 267 | test_cmp expect actual && |
| 268 | |
| 269 | echo "A^0" >expect && |
| 270 | tagged_commit=$(git rev-parse "refs/tags/A^0") && |
| 271 | git name-rev --tags --name-only $tagged_commit >actual && |
| 272 | test_cmp expect actual |
| 273 | ' |
| 274 | |
| 275 | test_expect_success 'name-rev --all' ' |
| 276 | >expect.unsorted && |
| 277 | for rev in $(git rev-list --all) |
| 278 | do |
| 279 | git name-rev $rev >>expect.unsorted || return 1 |
| 280 | done && |
| 281 | sort <expect.unsorted >expect && |
| 282 | git name-rev --all >actual.unsorted && |
| 283 | sort <actual.unsorted >actual && |
| 284 | test_cmp expect actual |
| 285 | ' |
| 286 | |
| 287 | test_expect_success 'name-rev --annotate-stdin' ' |
| 288 | >expect.unsorted && |
| 289 | for rev in $(git rev-list --all) |
| 290 | do |
| 291 | name=$(git name-rev --name-only $rev) && |
| 292 | echo "$rev ($name)" >>expect.unsorted || return 1 |
| 293 | done && |
| 294 | sort <expect.unsorted >expect && |
| 295 | git rev-list --all >list && |
| 296 | git name-rev --annotate-stdin <list >actual.unsorted && |
| 297 | sort <actual.unsorted >actual && |
| 298 | test_cmp expect actual |
| 299 | ' |
| 300 | |
| 301 | test_expect_success 'name-rev --annotate-stdin --name-only' ' |
| 302 | >expect.unsorted && |
| 303 | for rev in $(git rev-list --all) |
| 304 | do |
| 305 | name=$(git name-rev --name-only $rev) && |
| 306 | echo "$name" >>expect.unsorted || return 1 |
| 307 | done && |
| 308 | sort <expect.unsorted >expect && |
| 309 | git name-rev --annotate-stdin --name-only \ |
| 310 | <list >actual.unsorted && |
| 311 | sort <actual.unsorted >actual && |
| 312 | test_cmp expect actual |
| 313 | ' |
| 314 | |
| 315 | test_expect_success 'name-rev --stdin deprecated' ' |
| 316 | git rev-list --all >list && |
| 317 | if ! test_have_prereq WITH_BREAKING_CHANGES |
| 318 | then |
| 319 | git name-rev --stdin <list 2>actual && |
| 320 | test_grep "warning: --stdin is deprecated" actual |
| 321 | else |
| 322 | test_must_fail git name-rev --stdin <list 2>actual && |
| 323 | test_grep "unknown option .stdin." actual |
| 324 | fi |
| 325 | ' |
| 326 | |
| 327 | test_expect_success 'describe --contains with the exact tags' ' |
| 328 | echo "A^0" >expect && |
| 329 | tag_object=$(git rev-parse refs/tags/A) && |
| 330 | git describe --contains $tag_object >actual && |
| 331 | test_cmp expect actual && |
| 332 | |
| 333 | echo "A^0" >expect && |
| 334 | tagged_commit=$(git rev-parse "refs/tags/A^0") && |
| 335 | git describe --contains $tagged_commit >actual && |
| 336 | test_cmp expect actual |
| 337 | ' |
| 338 | |
| 339 | test_expect_success 'describe --contains and --match' ' |
| 340 | echo "A^0" >expect && |
| 341 | tagged_commit=$(git rev-parse "refs/tags/A^0") && |
| 342 | test_must_fail git describe --contains --match="B" $tagged_commit && |
| 343 | git describe --contains --match="B" --match="A" $tagged_commit >actual && |
| 344 | test_cmp expect actual |
| 345 | ' |
| 346 | |
| 347 | test_expect_success 'describe --exclude' ' |
| 348 | echo "c~1" >expect && |
| 349 | tagged_commit=$(git rev-parse "refs/tags/A^0") && |
| 350 | test_must_fail git describe --contains --match="B" $tagged_commit && |
| 351 | git describe --contains --match="?" --exclude="A" $tagged_commit >actual && |
| 352 | test_cmp expect actual |
| 353 | ' |
| 354 | |
| 355 | test_expect_success 'describe --contains and --no-match' ' |
| 356 | echo "A^0" >expect && |
| 357 | tagged_commit=$(git rev-parse "refs/tags/A^0") && |
| 358 | git describe --contains --match="B" --no-match $tagged_commit >actual && |
| 359 | test_cmp expect actual |
| 360 | ' |
| 361 | |
| 362 | test_expect_success 'describe --contains --all --match no matching commit' ' |
| 363 | echo "tags/A^0" >expect && |
| 364 | tagged_commit=$(git rev-parse "refs/tags/A^0") && |
| 365 | test_must_fail git describe --contains --all --match="B" $tagged_commit |
| 366 | ' |
| 367 | |
| 368 | check_describe "tags/A^0" --contains --all --match="A" $(git rev-parse "refs/tags/A^0") |
| 369 | |
| 370 | check_describe "branch_A" --contains --all --match="branch*" $(git rev-parse "refs/tags/A^0") |
| 371 | |
| 372 | check_describe "branch_C~1" --contains --all --match="branch*" --exclude="branch_A" $(git rev-parse "refs/tags/A^0") |
| 373 | |
| 374 | check_describe "branch_A" --contains --all \ |
| 375 | --exclude="A" --exclude="c" --exclude="test*" --exclude="origin/remote_branch_A" \ |
| 376 | $(git rev-parse "refs/tags/A^0") |
| 377 | |
| 378 | check_describe "remotes/origin/remote_branch_A" --contains --all --match="origin/remote*" $(git rev-parse "refs/tags/A^0") |
| 379 | |
| 380 | check_describe "remotes/origin/remote_branch_C~1" --contains --all \ |
| 381 | --match="origin/remote*" --exclude="origin/remote_branch_A" \ |
| 382 | $(git rev-parse "refs/tags/A^0") |
| 383 | |
| 384 | test_expect_success 'setup and absorb a submodule' ' |
| 385 | test_create_repo sub1 && |
| 386 | test_commit -C sub1 initial && |
| 387 | git submodule add ./sub1 && |
| 388 | git submodule absorbgitdirs && |
| 389 | git commit -a -m "add submodule" && |
| 390 | git describe --dirty >expect && |
| 391 | git describe --broken >out && |
| 392 | test_cmp expect out |
| 393 | ' |
| 394 | |
| 395 | test_expect_success 'describe chokes on severely broken submodules' ' |
| 396 | mv .git/modules/sub1/ .git/modules/sub_moved && |
| 397 | test_must_fail git describe --dirty |
| 398 | ' |
| 399 | |
| 400 | test_expect_success 'describe ignoring a broken submodule' ' |
| 401 | git describe --broken >out && |
| 402 | test_grep broken out |
| 403 | ' |
| 404 | |
| 405 | test_expect_success 'describe with --work-tree ignoring a broken submodule' ' |
| 406 | ( |
| 407 | cd "$TEST_DIRECTORY" && |
| 408 | git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --broken >"$TRASH_DIRECTORY/out" |
| 409 | ) && |
| 410 | test_when_finished "mv .git/modules/sub_moved .git/modules/sub1" && |
| 411 | test_grep broken out |
| 412 | ' |
| 413 | |
| 414 | test_expect_success 'describe a blob at a directly tagged commit' ' |
| 415 | echo "make it a unique blob" >file && |
| 416 | git add file && git commit -m "content in file" && |
| 417 | git tag -a -m "latest annotated tag" unique-file && |
| 418 | git describe HEAD:file >actual && |
| 419 | echo "unique-file:file" >expect && |
| 420 | test_cmp expect actual |
| 421 | ' |
| 422 | |
| 423 | test_expect_success 'describe a blob with its first introduction' ' |
| 424 | git commit --allow-empty -m "empty commit" && |
| 425 | git rm file && |
| 426 | git commit -m "delete blob" && |
| 427 | git revert HEAD && |
| 428 | git commit --allow-empty -m "empty commit" && |
| 429 | git describe HEAD:file >actual && |
| 430 | echo "unique-file:file" >expect && |
| 431 | test_cmp expect actual |
| 432 | ' |
| 433 | |
| 434 | test_expect_success 'describe directly tagged blob' ' |
| 435 | git tag test-blob unique-file:file && |
| 436 | git describe test-blob >actual && |
| 437 | echo "unique-file:file" >expect && |
| 438 | # suboptimal: we rather want to see "test-blob" |
| 439 | test_cmp expect actual |
| 440 | ' |
| 441 | |
| 442 | test_expect_success 'describe tag object' ' |
| 443 | git tag test-blob-1 -a -m msg unique-file:file && |
| 444 | test_must_fail git describe test-blob-1 2>actual && |
| 445 | test_grep "fatal: test-blob-1 is neither a commit nor blob" actual |
| 446 | ' |
| 447 | |
| 448 | test_expect_success 'describe an unreachable blob' ' |
| 449 | blob=$(echo not-found-anywhere | git hash-object -w --stdin) && |
| 450 | test_must_fail git describe $blob 2>actual && |
| 451 | test_grep "blob .$blob. not reachable from HEAD" actual |
| 452 | ' |
| 453 | |
| 454 | test_expect_success 'describe blob on an unborn branch' ' |
| 455 | oldbranch=$(git symbolic-ref HEAD) && |
| 456 | test_when_finished "git symbolic-ref HEAD $oldbranch" && |
| 457 | git symbolic-ref HEAD refs/heads/does-not-exist && |
| 458 | test_must_fail git describe test-blob 2>actual && |
| 459 | test_grep "cannot search .* on an unborn branch" actual |
| 460 | ' |
| 461 | |
| 462 | # This test creates a repository state that we generally try to disallow: HEAD |
| 463 | # is pointing to an object that is not a commit. The ref update code forbids |
| 464 | # non-commit writes directly to HEAD or to any branch in refs/heads/. But we |
| 465 | # can use the loophole of pointing HEAD to another non-branch ref (something we |
| 466 | # should forbid, but don't for historical reasons). |
| 467 | # |
| 468 | # Do not take this test as an endorsement of the loophole! If we ever tighten |
| 469 | # it, it is reasonable to just drop this test entirely. |
| 470 | test_expect_success 'describe blob on a non-commit HEAD' ' |
| 471 | oldbranch=$(git symbolic-ref HEAD) && |
| 472 | test_when_finished "git symbolic-ref HEAD $oldbranch" && |
| 473 | git symbolic-ref HEAD refs/tags/test-blob && |
| 474 | test_must_fail git describe test-blob 2>actual && |
| 475 | test_grep "blob .* not reachable from HEAD" actual |
| 476 | ' |
| 477 | |
| 478 | test_expect_success ULIMIT_STACK_SIZE 'name-rev works in a deep repo' ' |
| 479 | i=1 && |
| 480 | while test $i -lt 8000 |
| 481 | do |
| 482 | echo "commit refs/heads/main |
| 483 | committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200 |
| 484 | data <<EOF |
| 485 | commit #$i |
| 486 | EOF" && |
| 487 | if test $i = 1 |
| 488 | then |
| 489 | echo "from refs/heads/main^0" |
| 490 | fi && |
| 491 | i=$(($i + 1)) || return 1 |
| 492 | done | git fast-import && |
| 493 | git checkout main && |
| 494 | git tag far-far-away HEAD^ && |
| 495 | echo "HEAD~4000 tags/far-far-away~3999" >expect && |
| 496 | git name-rev HEAD~4000 >actual && |
| 497 | test_cmp expect actual && |
| 498 | run_with_limited_stack git name-rev HEAD~4000 >actual && |
| 499 | test_cmp expect actual |
| 500 | ' |
| 501 | |
| 502 | test_expect_success ULIMIT_STACK_SIZE 'describe works in a deep repo' ' |
| 503 | git tag -f far-far-away HEAD~7999 && |
| 504 | echo "far-far-away" >expect && |
| 505 | git describe --tags --abbrev=0 HEAD~4000 >actual && |
| 506 | test_cmp expect actual && |
| 507 | run_with_limited_stack git describe --tags --abbrev=0 HEAD~4000 >actual && |
| 508 | test_cmp expect actual |
| 509 | ' |
| 510 | |
| 511 | check_describe tags/A --all A |
| 512 | check_describe tags/c --all c |
| 513 | check_describe heads/branch_A --all --match='branch_*' branch_A |
| 514 | |
| 515 | test_expect_success 'describe complains about tree object' ' |
| 516 | test_must_fail git describe HEAD^{tree} |
| 517 | ' |
| 518 | |
| 519 | test_expect_success 'describe complains about missing object' ' |
| 520 | test_must_fail git describe $ZERO_OID |
| 521 | ' |
| 522 | |
| 523 | test_expect_success 'name-rev a rev shortly after epoch' ' |
| 524 | test_when_finished "git checkout main" && |
| 525 | |
| 526 | git checkout --orphan no-timestamp-underflow && |
| 527 | # Any date closer to epoch than the CUTOFF_DATE_SLOP constant |
| 528 | # in builtin/name-rev.c. |
| 529 | GIT_COMMITTER_DATE="@1234 +0000" \ |
| 530 | git commit -m "committer date shortly after epoch" && |
| 531 | old_commit_oid=$(git rev-parse HEAD) && |
| 532 | |
| 533 | echo "$old_commit_oid no-timestamp-underflow" >expect && |
| 534 | git name-rev $old_commit_oid >actual && |
| 535 | test_cmp expect actual |
| 536 | ' |
| 537 | |
| 538 | # A--------------main |
| 539 | # \ / |
| 540 | # \----------M2 |
| 541 | # \ / |
| 542 | # \---M1-C |
| 543 | # \ / |
| 544 | # B |
| 545 | test_expect_success 'name-rev covers all conditions while looking at parents' ' |
| 546 | git init repo && |
| 547 | ( |
| 548 | cd repo && |
| 549 | |
| 550 | echo A >file && |
| 551 | git add file && |
| 552 | git commit -m A && |
| 553 | A=$(git rev-parse HEAD) && |
| 554 | |
| 555 | git checkout --detach && |
| 556 | echo B >file && |
| 557 | git commit -m B file && |
| 558 | B=$(git rev-parse HEAD) && |
| 559 | |
| 560 | git checkout $A && |
| 561 | git merge --no-ff $B && # M1 |
| 562 | |
| 563 | echo C >file && |
| 564 | git commit -m C file && |
| 565 | |
| 566 | git checkout $A && |
| 567 | git merge --no-ff HEAD@{1} && # M2 |
| 568 | |
| 569 | git checkout main && |
| 570 | git merge --no-ff HEAD@{1} && |
| 571 | |
| 572 | echo "$B main^2^2~1^2" >expect && |
| 573 | git name-rev $B >actual && |
| 574 | |
| 575 | test_cmp expect actual |
| 576 | ) |
| 577 | ' |
| 578 | |
| 579 | # A-B-C-D-E-main |
| 580 | # |
| 581 | # Where C has a non-monotonically increasing commit timestamp w.r.t. other |
| 582 | # commits |
| 583 | test_expect_success 'non-monotonic commit dates setup' ' |
| 584 | UNIX_EPOCH_ZERO="@0 +0000" && |
| 585 | git init non-monotonic && |
| 586 | test_commit -C non-monotonic A && |
| 587 | test_commit -C non-monotonic --no-tag B && |
| 588 | test_commit -C non-monotonic --no-tag --date "$UNIX_EPOCH_ZERO" C && |
| 589 | test_commit -C non-monotonic D && |
| 590 | test_commit -C non-monotonic E |
| 591 | ' |
| 592 | |
| 593 | test_expect_success 'name-rev with commitGraph handles non-monotonic timestamps' ' |
| 594 | test_config -C non-monotonic core.commitGraph true && |
| 595 | ( |
| 596 | cd non-monotonic && |
| 597 | |
| 598 | git commit-graph write --reachable && |
| 599 | |
| 600 | echo "main~3 tags/D~2" >expect && |
| 601 | git name-rev --tags main~3 >actual && |
| 602 | |
| 603 | test_cmp expect actual |
| 604 | ) |
| 605 | ' |
| 606 | |
| 607 | test_expect_success 'name-rev --all works with non-monotonic timestamps' ' |
| 608 | test_config -C non-monotonic core.commitGraph false && |
| 609 | ( |
| 610 | cd non-monotonic && |
| 611 | |
| 612 | rm -rf .git/info/commit-graph* && |
| 613 | |
| 614 | cat >tags <<-\EOF && |
| 615 | tags/E |
| 616 | tags/D |
| 617 | tags/D~1 |
| 618 | tags/D~2 |
| 619 | tags/A |
| 620 | EOF |
| 621 | |
| 622 | git log --pretty=%H >revs && |
| 623 | |
| 624 | paste -d" " revs tags | sort >expect && |
| 625 | |
| 626 | git name-rev --tags --all | sort >actual && |
| 627 | test_cmp expect actual |
| 628 | ) |
| 629 | ' |
| 630 | |
| 631 | test_expect_success 'name-rev --annotate-stdin works with non-monotonic timestamps' ' |
| 632 | test_config -C non-monotonic core.commitGraph false && |
| 633 | ( |
| 634 | cd non-monotonic && |
| 635 | |
| 636 | rm -rf .git/info/commit-graph* && |
| 637 | |
| 638 | cat >expect <<-\EOF && |
| 639 | E |
| 640 | D |
| 641 | D~1 |
| 642 | D~2 |
| 643 | A |
| 644 | EOF |
| 645 | |
| 646 | git log --pretty=%H >revs && |
| 647 | git name-rev --tags --annotate-stdin --name-only <revs >actual && |
| 648 | test_cmp expect actual |
| 649 | ) |
| 650 | ' |
| 651 | |
| 652 | test_expect_success 'name-rev --all works with commitGraph' ' |
| 653 | test_config -C non-monotonic core.commitGraph true && |
| 654 | ( |
| 655 | cd non-monotonic && |
| 656 | |
| 657 | git commit-graph write --reachable && |
| 658 | |
| 659 | cat >tags <<-\EOF && |
| 660 | tags/E |
| 661 | tags/D |
| 662 | tags/D~1 |
| 663 | tags/D~2 |
| 664 | tags/A |
| 665 | EOF |
| 666 | |
| 667 | git log --pretty=%H >revs && |
| 668 | |
| 669 | paste -d" " revs tags | sort >expect && |
| 670 | |
| 671 | git name-rev --tags --all | sort >actual && |
| 672 | test_cmp expect actual |
| 673 | ) |
| 674 | ' |
| 675 | |
| 676 | test_expect_success 'name-rev --annotate-stdin works with commitGraph' ' |
| 677 | test_config -C non-monotonic core.commitGraph true && |
| 678 | ( |
| 679 | cd non-monotonic && |
| 680 | |
| 681 | git commit-graph write --reachable && |
| 682 | |
| 683 | cat >expect <<-\EOF && |
| 684 | E |
| 685 | D |
| 686 | D~1 |
| 687 | D~2 |
| 688 | A |
| 689 | EOF |
| 690 | |
| 691 | git log --pretty=%H >revs && |
| 692 | git name-rev --tags --annotate-stdin --name-only <revs >actual && |
| 693 | test_cmp expect actual |
| 694 | ) |
| 695 | ' |
| 696 | |
| 697 | # B |
| 698 | # o |
| 699 | # H \ |
| 700 | # o-----o---o----x |
| 701 | # A |
| 702 | # |
| 703 | test_expect_success 'setup: describe commits with disjoint bases' ' |
| 704 | git init disjoint1 && |
| 705 | ( |
| 706 | cd disjoint1 && |
| 707 | |
| 708 | echo o >> file && git add file && git commit -m o && |
| 709 | git tag H -a -m H && |
| 710 | echo A >> file && git add file && git commit -m A && |
| 711 | git tag A -a -m A && |
| 712 | echo o >> file && git add file && git commit -m o && |
| 713 | |
| 714 | git checkout --orphan branch && rm file && |
| 715 | echo B > file2 && git add file2 && git commit -m B && |
| 716 | git tag B -a -m B && |
| 717 | git merge --no-ff --allow-unrelated-histories main -m x |
| 718 | ) |
| 719 | ' |
| 720 | |
| 721 | check_describe -C disjoint1 "A-3-gHASH" HEAD |
| 722 | check_describe -C disjoint1 --expect-failure "A-3-gHASH" --candidates=2 HEAD |
| 723 | |
| 724 | # H B |
| 725 | # o---o---o------------. |
| 726 | # \ |
| 727 | # o---o---x |
| 728 | # A |
| 729 | # |
| 730 | test_expect_success 'setup: describe commits with disjoint bases 2' ' |
| 731 | git init disjoint2 && |
| 732 | ( |
| 733 | cd disjoint2 && |
| 734 | |
| 735 | echo A >> file && git add file && GIT_COMMITTER_DATE="2020-01-01 18:00" git commit -m A && |
| 736 | git tag A -a -m A && |
| 737 | echo o >> file && git add file && GIT_COMMITTER_DATE="2020-01-01 18:01" git commit -m o && |
| 738 | |
| 739 | git checkout --orphan branch && |
| 740 | echo o >> file2 && git add file2 && GIT_COMMITTER_DATE="2020-01-01 15:00" git commit -m o && |
| 741 | echo o >> file2 && git add file2 && GIT_COMMITTER_DATE="2020-01-01 15:01" git commit -m o && |
| 742 | git tag H -a -m H && |
| 743 | echo B >> file2 && git add file2 && GIT_COMMITTER_DATE="2020-01-01 15:02" git commit -m B && |
| 744 | git tag B -a -m B && |
| 745 | git merge --no-ff --allow-unrelated-histories main -m x |
| 746 | ) |
| 747 | ' |
| 748 | |
| 749 | check_describe -C disjoint2 "B-3-gHASH" HEAD |
| 750 | check_describe -C disjoint2 --expect-failure "B-3-gHASH" --candidates=2 HEAD |
| 751 | |
| 752 | test_expect_success 'setup misleading taggerdates' ' |
| 753 | GIT_COMMITTER_DATE="2006-12-12 12:31" git tag -a -m "another tag" newer-tag-older-commit unique-file~1 |
| 754 | ' |
| 755 | |
| 756 | check_describe newer-tag-older-commit~1 --contains unique-file~2 |
| 757 | |
| 758 | test_expect_success 'describe --dirty with a file with changed stat' ' |
| 759 | test_when_finished "rm -fr stat-dirty" && |
| 760 | git init stat-dirty && |
| 761 | ( |
| 762 | cd stat-dirty && |
| 763 | |
| 764 | echo A >file && |
| 765 | git add file && |
| 766 | git commit -m A && |
| 767 | git tag A -a -m A && |
| 768 | echo "A" >expect && |
| 769 | |
| 770 | test-tool chmtime -10 file && |
| 771 | git describe --dirty >actual && |
| 772 | test_cmp expect actual |
| 773 | ) |
| 774 | ' |
| 775 | |
| 776 | test_expect_success 'describe --broken --dirty with a file with changed stat' ' |
| 777 | test_when_finished "rm -fr stat-dirty" && |
| 778 | git init stat-dirty && |
| 779 | ( |
| 780 | cd stat-dirty && |
| 781 | |
| 782 | echo A >file && |
| 783 | git add file && |
| 784 | git commit -m A && |
| 785 | git tag A -a -m A && |
| 786 | echo "A" >expect && |
| 787 | |
| 788 | test-tool chmtime -10 file && |
| 789 | git describe --dirty --broken >actual && |
| 790 | test_cmp expect actual |
| 791 | ) |
| 792 | ' |
| 793 | |
| 794 | test_expect_success '--always with no refs falls back to commit hash' ' |
| 795 | git rev-parse HEAD >expect && |
| 796 | git describe --no-abbrev --always --match=no-such-tag >actual && |
| 797 | test_cmp expect actual |
| 798 | ' |
| 799 | |
| 800 | test_expect_success '--exact-match does not show --always fallback' ' |
| 801 | test_must_fail git describe --exact-match --always |
| 802 | ' |
| 803 | |
| 804 | test_expect_success 'avoid being fooled by describe-like filename' ' |
| 805 | test_when_finished rm out && |
| 806 | |
| 807 | git rev-parse --short HEAD >out && |
| 808 | FILENAME=filename-g$(cat out) && |
| 809 | touch $FILENAME && |
| 810 | git add $FILENAME && |
| 811 | git commit -m "Add $FILENAME" && |
| 812 | |
| 813 | git cat-file -t HEAD:$FILENAME >actual && |
| 814 | |
| 815 | echo blob >expect && |
| 816 | test_cmp expect actual |
| 817 | ' |
| 818 | |
| 819 | test_expect_success 'do not be fooled by invalid describe format ' ' |
| 820 | test_when_finished rm out && |
| 821 | |
| 822 | git rev-parse --short HEAD >out && |
| 823 | test_must_fail git cat-file -t "refs/tags/super-invalid/./../...../ ~^:/?*[////\\\\\\&}/busted.lock-42-g"$(cat out) |
| 824 | ' |
| 825 | |
| 826 | test_expect_success 'setup: format-rev' ' |
| 827 | mkdir repo-format && |
| 828 | git -C repo-format init && |
| 829 | test_commit -C repo-format first && |
| 830 | test_commit -C repo-format second && |
| 831 | test_commit -C repo-format third && |
| 832 | test_commit -C repo-format fourth && |
| 833 | test_commit -C repo-format fifth && |
| 834 | test_commit -C repo-format sixth && |
| 835 | test_commit -C repo-format seventh && |
| 836 | test_commit -C repo-format eighth |
| 837 | ' |
| 838 | |
| 839 | test_expect_success 'format-rev --stdin-mode=revs' ' |
| 840 | cat >expect <<-\EOF && |
| 841 | eighth |
| 842 | seventh |
| 843 | fifth |
| 844 | EOF |
| 845 | git -C repo-format format-rev --stdin-mode=revs \ |
| 846 | --format=%s >actual <<-\EOF && |
| 847 | HEAD |
| 848 | HEAD~ |
| 849 | HEAD~3 |
| 850 | EOF |
| 851 | test_cmp expect actual |
| 852 | ' |
| 853 | |
| 854 | test_expect_success 'format-rev --stdin-mode=text from rev-list same as log' ' |
| 855 | git -C repo-format log --format=reference >expect && |
| 856 | test_file_not_empty expect && |
| 857 | git -C repo-format rev-list HEAD >list && |
| 858 | git -C repo-format format-rev --stdin-mode=text \ |
| 859 | --format=reference <list >actual && |
| 860 | test_cmp expect actual |
| 861 | ' |
| 862 | |
| 863 | test_expect_success 'format-rev --stdin-mode=text with running text and tree oid' ' |
| 864 | cmit_oid=$(git -C repo-format rev-parse fifth) && |
| 865 | reference=$(git -C repo-format log -n1 --format=reference fifth) && |
| 866 | tree=$(git -C repo-format rev-parse HEAD^{tree}) && |
| 867 | cat >expect <<-EOF && |
| 868 | We thought we fixed this in ${reference}. |
| 869 | But look at this tree: ${tree}. |
| 870 | EOF |
| 871 | git -C repo-format format-rev --stdin-mode=text --format=reference \ |
| 872 | >actual <<-EOF && |
| 873 | We thought we fixed this in ${cmit_oid}. |
| 874 | But look at this tree: ${tree}. |
| 875 | EOF |
| 876 | test_cmp expect actual |
| 877 | ' |
| 878 | |
| 879 | test_expect_success 'format-rev with %N (note)' ' |
| 880 | test_when_finished "git -C repo-format notes remove" && |
| 881 | git -C repo-format notes add -m"Make a note" && |
| 882 | printf "Make a note\n\n\n" >expect && |
| 883 | git -C repo-format format-rev --stdin-mode=revs \ |
| 884 | --format="tformat:%N" \ |
| 885 | >actual <<-\EOF && |
| 886 | HEAD |
| 887 | HEAD~ |
| 888 | EOF |
| 889 | test_cmp expect actual |
| 890 | ' |
| 891 | |
| 892 | test_expect_success 'format-rev --notes<ref> (custom notes ref)' ' |
| 893 | # One custom notes ref |
| 894 | test_when_finished "git -C repo-format notes remove" && |
| 895 | test_when_finished "git -C repo-format notes --ref=word remove" && |
| 896 | git -C repo-format notes add -m"default" && |
| 897 | git -C repo-format notes --ref=word add -m"custom" && |
| 898 | printf "custom\n\n" >expect && |
| 899 | git -C repo-format format-rev --stdin-mode=revs \ |
| 900 | --format="tformat:%N" \ |
| 901 | --notes=word \ |
| 902 | >actual <<-\EOF && |
| 903 | HEAD |
| 904 | EOF |
| 905 | test_cmp expect actual && |
| 906 | # Glob all |
| 907 | printf "default\ncustom\n\n" >expect && |
| 908 | git -C repo-format format-rev --stdin-mode=revs \ |
| 909 | --format="tformat:%N" \ |
| 910 | --notes=* >actual <<-\EOF && |
| 911 | HEAD |
| 912 | EOF |
| 913 | test_cmp expect actual |
| 914 | ' |
| 915 | |
| 916 | test_expect_success 'format-rev --stdin-mode=revs on annotated tag peels to commit' ' |
| 917 | test_when_finished "git -C repo-format tag -d version" && |
| 918 | git -C repo-format tag -a -m"new version" version && |
| 919 | cat >expect <<-\EOF && |
| 920 | eighth |
| 921 | EOF |
| 922 | git -C repo-format format-rev --stdin-mode=revs \ |
| 923 | --format=%s \ |
| 924 | >actual <<-\EOF && |
| 925 | version |
| 926 | EOF |
| 927 | test_cmp expect actual |
| 928 | ' |
| 929 | |
| 930 | test_expect_success 'format-rev --stdin-mode=revs lookup failures' ' |
| 931 | test_when_finished "git -C repo-format tag -d tag-to-tree" && |
| 932 | invalid_syntax=not-valid && |
| 933 | non_existing_oid=${EMPTY_BLOB} && |
| 934 | tree=$(git -C repo-format rev-parse eighth^{tree}) && |
| 935 | git -C repo-format tag -a -mmessage tag-to-tree "$tree" && |
| 936 | tag_to_tree=$(git -C repo-format rev-parse tag-to-tree) && |
| 937 | cat >expect <<-EOF && |
| 938 | Could not get object name for ${invalid_syntax}. Skipping. |
| 939 | Could not get object for ${non_existing_oid}. Skipping. |
| 940 | Could not get commit for ${tree}. Skipping. |
| 941 | Could not get commit for ${tag_to_tree}. Skipping. |
| 942 | EOF |
| 943 | git -C repo-format format-rev --stdin-mode=revs \ |
| 944 | --format=%s \ |
| 945 | 2>actual >out <<-EOF && |
| 946 | ${invalid_syntax} |
| 947 | ${non_existing_oid} |
| 948 | ${tree} |
| 949 | ${tag_to_tree} |
| 950 | EOF |
| 951 | test_line_count = 0 out && |
| 952 | test_cmp expect actual |
| 953 | ' |
| 954 | |
| 955 | |
| 956 | test_expect_success 'format-rev -z --stdin-mode=text with object name lookup failures' ' |
| 957 | printf "%s\0" "$(git -C repo-format rev-parse HEAD)" >input && |
| 958 | printf "%s\0" "$(git -C repo-format rev-parse HEAD^{tree})" >>input && |
| 959 | printf "%s\0" "$EMPTY_BLOB" >>input && |
| 960 | printf "%s\0" "$(git -C repo-format log --format=%s -1)" >expect && |
| 961 | printf "%s\0" "$(git -C repo-format rev-parse HEAD^{tree})" >>expect && |
| 962 | printf "%s\0" "$EMPTY_BLOB" >>expect && |
| 963 | git -C repo-format format-rev --stdin-mode=text \ |
| 964 | --format=%s -z <input >actual && |
| 965 | test_cmp expect actual |
| 966 | ' |
| 967 | |
| 968 | test_expect_success 'setup: format-rev input and output separators' ' |
| 969 | git -C repo-format rev-list HEAD >input-lf && |
| 970 | git -C repo-format rev-list -z HEAD >input-nul && |
| 971 | git -C repo-format log --format=%s >output-lf && |
| 972 | git -C repo-format log -z --format=%s >output-nul && |
| 973 | echo revs >stdin-modes && |
| 974 | echo text >>stdin-modes |
| 975 | ' |
| 976 | |
| 977 | while read mode |
| 978 | do |
| 979 | test_expect_success "format-rev -z --stdin-mode=$mode" ' |
| 980 | cat output-nul >expect && |
| 981 | git -C repo-format format-rev --stdin-mode="$mode" \ |
| 982 | --format=%s -z <input-nul >actual && |
| 983 | test_cmp expect actual |
| 984 | ' |
| 985 | |
| 986 | test_expect_success "format-rev -z --no-null-input --no-null-output --stdin-mode=$mode" ' |
| 987 | cat output-lf >expect && |
| 988 | git -C repo-format format-rev --stdin-mode="$mode" \ |
| 989 | --format=%s -z --no-null-input --no-null-output \ |
| 990 | <input-lf >actual && |
| 991 | test_cmp expect actual |
| 992 | ' |
| 993 | |
| 994 | test_expect_success "format-rev ---null-input --stdin-mode=$mode" ' |
| 995 | cat output-lf >expect && |
| 996 | git -C repo-format format-rev --stdin-mode="$mode" \ |
| 997 | --format=%s --null-input \ |
| 998 | <input-nul >actual && |
| 999 | test_cmp expect actual |
| 1000 | ' |
| 1001 | |
| 1002 | test_expect_success "format-rev --null-output --stdin-mode=$mode" ' |
| 1003 | cat output-nul >expect && |
| 1004 | git -C repo-format format-rev --stdin-mode="$mode" \ |
| 1005 | --format=%s --null-output \ |
| 1006 | <input-lf >actual && |
| 1007 | test_cmp expect actual |
| 1008 | ' |
| 1009 | |
| 1010 | test_expect_success "format-rev -z --stdin-mode=$mode with multi-line output" ' |
| 1011 | format="%s%n%aI" && |
| 1012 | git -C repo-format log -z --format="$format" \ |
| 1013 | >expect && |
| 1014 | git -C repo-format format-rev --stdin-mode="$mode" \ |
| 1015 | --format="$format" -z <input-nul >actual && |
| 1016 | test_cmp expect actual |
| 1017 | ' |
| 1018 | done <stdin-modes |
| 1019 | |
| 1020 | test_done |