1#!/bin/sh
   2#
   3# Copyright (C) 2005 Rene Scharfe
   4#
   5test_description='git archive and git get-tar-commit-id test
   7This test covers the topics of file contents, commit date handling and
   9commit id embedding:
  10  The contents of the repository is compared to the extracted tar
  12  archive.  The repository contains simple text files, symlinks and a
  13  binary file (/bin/sh).  Only paths shorter than 99 characters are
  14  used.
  15  git archive applies the commit date to every file in the archive it
  17  creates.  The test sets the commit date to a specific value and checks
  18  if the tar archive contains that value.
  19  When giving git archive a commit id (in contrast to a tree id) it
  21  embeds this commit id into the tar archive as a comment.  The test
  22  checks the ability of git get-tar-commit-id to figure it out from the
  23  tar file.
  24'
  26. ./test-lib.sh
  28SUBSTFORMAT=%H%n
  30test_lazy_prereq TAR_NEEDS_PAX_FALLBACK '
  32        (
  33                mkdir pax &&
  34                cd pax &&
  35                "$TAR" xf "$TEST_DIRECTORY"/t5000/pax.tar &&
  36                test -f PaxHeaders.1791/file
  37        )
  38'
  39test_lazy_prereq GZIP 'gzip --version'
  41get_pax_header() {
  43        file=$1
  44        header=$2=
  45        while read len rest
  47        do
  48                if test "$len" = $(echo "$len $rest" | wc -c)
  49                then
  50                        case "$rest" in
  51                        $header*)
  52                                echo "${rest#$header}"
  53                                ;;
  54                        esac
  55                fi
  56        done <"$file"
  57}
  58check_tar() {
  60        tarfile=$1.tar
  61        listfile=$1.lst
  62        dir=$1
  63        dir_with_prefix=$dir/$2
  64        test_expect_success ' extract tar archive' '
  66                (mkdir $dir && cd $dir && "$TAR" xf -) <$tarfile
  67        '
  68        test_expect_success TAR_NEEDS_PAX_FALLBACK ' interpret pax headers' '
  70                (
  71                        cd $dir &&
  72                        for header in *.paxheader
  73                        do
  74                                data=${header%.paxheader}.data &&
  75                                if test -h $data || test -e $data
  76                                then
  77                                        path=$(get_pax_header $header path) &&
  78                                        if test -n "$path"
  79                                        then
  80                                                mv "$data" "$path"
  81                                        fi
  82                                fi
  83                        done
  84                )
  85        '
  86        test_expect_success ' validate filenames' '
  88                (cd ${dir_with_prefix}a && find .) | sort >$listfile &&
  89                test_cmp a.lst $listfile
  90        '
  91        test_expect_success ' validate file contents' '
  93                diff -r a ${dir_with_prefix}a
  94        '
  95}
  96test_expect_success \
  98    'populate workdir' \
  99    'mkdir a &&
 100     echo simple textfile >a/a &&
 101     ten=0123456789 && hundred=$ten$ten$ten$ten$ten$ten$ten$ten$ten$ten &&
 102     echo long filename >a/four$hundred &&
 103     mkdir a/bin &&
 104     test-genrandom "frotz" 500000 >a/bin/sh &&
 105     printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
 106     printf "A not substituted O" >a/substfile2 &&
 107     if test_have_prereq SYMLINKS; then
 108        ln -s a a/l1
 109     else
 110        printf %s a > a/l1
 111     fi &&
 112     (p=long_path_to_a_file && cd a &&
 113      for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
 114      echo text >file_with_long_path) &&
 115     (cd a && find .) | sort >a.lst'
 116test_expect_success \
 118    'add ignored file' \
 119    'echo ignore me >a/ignored &&
 120     echo ignored export-ignore >.git/info/attributes'
 121test_expect_success 'add files to repository' '
 123        git add a &&
 124        GIT_COMMITTER_DATE="2005-05-27 22:00" git commit -m initial
 125'
 126test_expect_success 'setup export-subst' '
 128        echo "substfile?" export-subst >>.git/info/attributes &&
 129        git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
 130                >a/substfile1
 131'
 132test_expect_success \
 134    'create bare clone' \
 135    'git clone --bare . bare.git &&
 136     cp .git/info/attributes bare.git/info/attributes'
 137test_expect_success \
 139    'remove ignored file' \
 140    'rm a/ignored'
 141test_expect_success \
 143    'git archive' \
 144    'git archive HEAD >b.tar'
 145check_tar b
 147test_expect_success 'git archive --prefix=prefix/' '
 149        git archive --prefix=prefix/ HEAD >with_prefix.tar
 150'
 151check_tar with_prefix prefix/
 153test_expect_success 'git-archive --prefix=olde-' '
 155        git archive --prefix=olde- HEAD >with_olde-prefix.tar
 156'
 157check_tar with_olde-prefix olde-
 159test_expect_success 'git archive on large files' '
 161    test_config core.bigfilethreshold 1 &&
 162    git archive HEAD >b3.tar &&
 163    test_cmp_bin b.tar b3.tar
 164'
 165test_expect_success \
 167    'git archive in a bare repo' \
 168    '(cd bare.git && git archive HEAD) >b3.tar'
 169test_expect_success \
 171    'git archive vs. the same in a bare repo' \
 172    'test_cmp_bin b.tar b3.tar'
 173test_expect_success 'git archive with --output' \
 175    'git archive --output=b4.tar HEAD &&
 176    test_cmp_bin b.tar b4.tar'
 177test_expect_success 'git archive --remote' \
 179    'git archive --remote=. HEAD >b5.tar &&
 180    test_cmp_bin b.tar b5.tar'
 181test_expect_success 'git archive --remote with configured remote' '
 183        git config remote.foo.url . &&
 184        (
 185                cd a &&
 186                git archive --remote=foo --output=../b5-nick.tar HEAD
 187        ) &&
 188        test_cmp_bin b.tar b5-nick.tar
 189'
 190test_expect_success \
 192    'validate file modification time' \
 193    'mkdir extract &&
 194     "$TAR" xf b.tar -C extract a/a &&
 195     test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime &&
 196     echo "1117231200" >expected.mtime &&
 197     test_cmp expected.mtime b.mtime'
 198test_expect_success \
 200    'git get-tar-commit-id' \
 201    'git get-tar-commit-id <b.tar >b.commitid &&
 202     test_cmp .git/$(git symbolic-ref HEAD) b.commitid'
 203test_expect_success 'git archive with --output, override inferred format' '
 205        git archive --format=tar --output=d4.zip HEAD &&
 206        test_cmp_bin b.tar d4.zip
 207'
 208test_expect_success 'git archive --list outside of a git repo' '
 210        nongit git archive --list
 211'
 212test_expect_success 'git archive --remote outside of a git repo' '
 214        git archive HEAD >expect.tar &&
 215        nongit git archive --remote="$PWD" HEAD >actual.tar &&
 216        test_cmp_bin expect.tar actual.tar
 217'
 218test_expect_success 'clients cannot access unreachable commits' '
 220        test_commit unreachable &&
 221        sha1=$(git rev-parse HEAD) &&
 222        git reset --hard HEAD^ &&
 223        git archive $sha1 >remote.tar &&
 224        test_must_fail git archive --remote=. $sha1 >remote.tar
 225'
 226test_expect_success 'upload-archive can allow unreachable commits' '
 228        test_commit unreachable1 &&
 229        sha1=$(git rev-parse HEAD) &&
 230        git reset --hard HEAD^ &&
 231        git archive $sha1 >remote.tar &&
 232        test_config uploadarchive.allowUnreachable true &&
 233        git archive --remote=. $sha1 >remote.tar
 234'
 235test_expect_success 'setup tar filters' '
 237        git config tar.tar.foo.command "tr ab ba" &&
 238        git config tar.bar.command "tr ab ba" &&
 239        git config tar.bar.remote true &&
 240        git config tar.invalid baz
 241'
 242test_expect_success 'archive --list mentions user filter' '
 244        git archive --list >output &&
 245        grep "^tar\.foo\$" output &&
 246        grep "^bar\$" output
 247'
 248test_expect_success 'archive --list shows only enabled remote filters' '
 250        git archive --list --remote=. >output &&
 251        ! grep "^tar\.foo\$" output &&
 252        grep "^bar\$" output
 253'
 254test_expect_success 'invoke tar filter by format' '
 256        git archive --format=tar.foo HEAD >config.tar.foo &&
 257        tr ab ba <config.tar.foo >config.tar &&
 258        test_cmp_bin b.tar config.tar &&
 259        git archive --format=bar HEAD >config.bar &&
 260        tr ab ba <config.bar >config.tar &&
 261        test_cmp_bin b.tar config.tar
 262'
 263test_expect_success 'invoke tar filter by extension' '
 265        git archive -o config-implicit.tar.foo HEAD &&
 266        test_cmp_bin config.tar.foo config-implicit.tar.foo &&
 267        git archive -o config-implicit.bar HEAD &&
 268        test_cmp_bin config.tar.foo config-implicit.bar
 269'
 270test_expect_success 'default output format remains tar' '
 272        git archive -o config-implicit.baz HEAD &&
 273        test_cmp_bin b.tar config-implicit.baz
 274'
 275test_expect_success 'extension matching requires dot' '
 277        git archive -o config-implicittar.foo HEAD &&
 278        test_cmp_bin b.tar config-implicittar.foo
 279'
 280test_expect_success 'only enabled filters are available remotely' '
 282        test_must_fail git archive --remote=. --format=tar.foo HEAD \
 283                >remote.tar.foo &&
 284        git archive --remote=. --format=bar >remote.bar HEAD &&
 285        test_cmp_bin remote.bar config.bar
 286'
 287test_expect_success GZIP 'git archive --format=tgz' '
 289        git archive --format=tgz HEAD >j.tgz
 290'
 291test_expect_success GZIP 'git archive --format=tar.gz' '
 293        git archive --format=tar.gz HEAD >j1.tar.gz &&
 294        test_cmp_bin j.tgz j1.tar.gz
 295'
 296test_expect_success GZIP 'infer tgz from .tgz filename' '
 298        git archive --output=j2.tgz HEAD &&
 299        test_cmp_bin j.tgz j2.tgz
 300'
 301test_expect_success GZIP 'infer tgz from .tar.gz filename' '
 303        git archive --output=j3.tar.gz HEAD &&
 304        test_cmp_bin j.tgz j3.tar.gz
 305'
 306test_expect_success GZIP 'extract tgz file' '
 308        gzip -d -c <j.tgz >j.tar &&
 309        test_cmp_bin b.tar j.tar
 310'
 311test_expect_success GZIP 'remote tar.gz is allowed by default' '
 313        git archive --remote=. --format=tar.gz HEAD >remote.tar.gz &&
 314        test_cmp_bin j.tgz remote.tar.gz
 315'
 316test_expect_success GZIP 'remote tar.gz can be disabled' '
 318        git config tar.tar.gz.remote false &&
 319        test_must_fail git archive --remote=. --format=tar.gz HEAD \
 320                >remote.tar.gz
 321'
 322test_expect_success 'archive and :(glob)' '
 324        git archive -v HEAD -- ":(glob)**/sh" >/dev/null 2>actual &&
 325        cat >expect <<EOF &&
 326a/
 327a/bin/
 328a/bin/sh
 329EOF
 330        test_cmp expect actual
 331'
 332test_expect_success 'catch non-matching pathspec' '
 334        test_must_fail git archive -v HEAD -- "*.abc" >/dev/null
 335'
 336# Pull the size and date of each entry in a tarfile using the system tar.
 338#
 339# We'll pull out only the year from the date; that avoids any question of
 340# timezones impacting the result (as long as we keep our test times away from a
 341# year boundary; our reference times are all in August).
 342#
 343# The output of tar_info is expected to be "<size> <year>", both in decimal. It
 344# ignores the return value of tar. We have to do this, because some of our test
 345# input is only partial (the real data is 64GB in some cases).
 346tar_info () {
 347        "$TAR" tvf "$1" |
 348        awk '{
 349                split($4, date, "-")
 350                print $3 " " date[1]
 351        }'
 352}
 353# See if our system tar can handle a tar file with huge sizes and dates far in
 355# the future, and that we can actually parse its output.
 356#
 357# The reference file was generated by GNU tar, and the magic time and size are
 358# both octal 01000000000001, which overflows normal ustar fields.
 359test_lazy_prereq TAR_HUGE '
 360        echo "68719476737 4147" >expect &&
 361        tar_info "$TEST_DIRECTORY"/t5000/huge-and-future.tar >actual &&
 362        test_cmp expect actual
 363'
 364test_expect_success LONG_IS_64BIT 'set up repository with huge blob' '
 366        obj_d=19 &&
 367        obj_f=f9c8273ec45a8938e6999cb59b3ff66739902a &&
 368        obj=${obj_d}${obj_f} &&
 369        mkdir -p .git/objects/$obj_d &&
 370        cp "$TEST_DIRECTORY"/t5000/$obj .git/objects/$obj_d/$obj_f &&
 371        rm -f .git/index &&
 372        git update-index --add --cacheinfo 100644,$obj,huge &&
 373        git commit -m huge
 374'
 375# We expect git to die with SIGPIPE here (otherwise we
 377# would generate the whole 64GB).
 378test_expect_success LONG_IS_64BIT 'generate tar with huge size' '
 379        {
 380                git archive HEAD
 381                echo $? >exit-code
 382        } | test_copy_bytes 4096 >huge.tar &&
 383        echo 141 >expect &&
 384        test_cmp expect exit-code
 385'
 386test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' '
 388        echo 68719476737 >expect &&
 389        tar_info huge.tar | cut -d" " -f1 >actual &&
 390        test_cmp expect actual
 391'
 392test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
 394        rm -f .git/index &&
 395        echo content >file &&
 396        git add file &&
 397        GIT_COMMITTER_DATE="@68719476737 +0000" \
 398                git commit -m "tempori parendum"
 399'
 400test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 402        git archive HEAD >future.tar
 403'
 404test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
 406        echo 4147 >expect &&
 407        tar_info future.tar | cut -d" " -f2 >actual &&
 408        test_cmp expect actual
 409'
 410test_done