t / t5314-pack-cycle-detection.shon commit built-in rebase --autostash: leave the current branch alone if possible (176f5d9)
   1#!/bin/sh
   2
   3test_description='test handling of inter-pack delta cycles during repack
   4
   5The goal here is to create a situation where we have two blobs, A and B, with A
   6as a delta against B in one pack, and vice versa in the other. Then if we can
   7persuade a full repack to find A from one pack and B from the other, that will
   8give us a cycle when we attempt to reuse those deltas.
   9
  10The trick is in the "persuade" step, as it depends on the internals of how
  11pack-objects picks which pack to reuse the deltas from. But we can assume
  12that it does so in one of two general strategies:
  13
  14 1. Using a static ordering of packs. In this case, no inter-pack cycles can
  15    happen. Any objects with a delta relationship must be present in the same
  16    pack (i.e., no "--thin" packs on disk), so we will find all related objects
  17    from that pack. So assuming there are no cycles within a single pack (and
  18    we avoid generating them via pack-objects or importing them via
  19    index-pack), then our result will have no cycles.
  20
  21    So this case should pass the tests no matter how we arrange things.
  22
  23 2. Picking the next pack to examine based on locality (i.e., where we found
  24    something else recently).
  25
  26    In this case, we want to make sure that we find the delta versions of A and
  27    B and not their base versions. We can do this by putting two blobs in each
  28    pack. The first is a "dummy" blob that can only be found in the pack in
  29    question.  And then the second is the actual delta we want to find.
  30
  31    The two blobs must be present in the same tree, not present in other trees,
  32    and the dummy pathname must sort before the delta path.
  33
  34The setup below focuses on case 2. We have two commits HEAD and HEAD^, each
  35which has two files: "dummy" and "file". Then we can make two packs which
  36contain:
  37
  38  [pack one]
  39  HEAD:dummy
  40  HEAD:file  (as delta against HEAD^:file)
  41  HEAD^:file (as base)
  42
  43  [pack two]
  44  HEAD^:dummy
  45  HEAD^:file (as delta against HEAD:file)
  46  HEAD:file  (as base)
  47
  48Then no matter which order we start looking at the packs in, we know that we
  49will always find a delta for "file", because its lookup will always come
  50immediately after the lookup for "dummy".
  51'
  52. ./test-lib.sh
  53
  54
  55
  56# Create a pack containing the the tree $1 and blob $1:file, with
  57# the latter stored as a delta against $2:file.
  58#
  59# We convince pack-objects to make the delta in the direction of our choosing
  60# by marking $2 as a preferred-base edge. That results in $1:file as a thin
  61# delta, and index-pack completes it by adding $2:file as a base.
  62#
  63# Note that the two variants of "file" must be similar enough to convince git
  64# to create the delta.
  65make_pack () {
  66        {
  67                printf '%s\n' "-$(git rev-parse $2)"
  68                printf '%s dummy\n' "$(git rev-parse $1:dummy)"
  69                printf '%s file\n' "$(git rev-parse $1:file)"
  70        } |
  71        git pack-objects --stdout |
  72        git index-pack --stdin --fix-thin
  73}
  74
  75test_expect_success 'setup' '
  76        test-tool genrandom base 4096 >base &&
  77        for i in one two
  78        do
  79                # we want shared content here to encourage deltas...
  80                cp base file &&
  81                echo $i >>file &&
  82
  83                # ...whereas dummy should be short, because we do not want
  84                # deltas that would create duplicates when we --fix-thin
  85                echo $i >dummy &&
  86
  87                git add file dummy &&
  88                test_tick &&
  89                git commit -m $i ||
  90                return 1
  91        done &&
  92
  93        make_pack HEAD^ HEAD &&
  94        make_pack HEAD HEAD^
  95'
  96
  97test_expect_success 'repack' '
  98        # We first want to check that we do not have any internal errors,
  99        # and also that we do not hit the last-ditch cycle-breaking code
 100        # in write_object(), which will issue a warning to stderr.
 101        >expect &&
 102        git repack -ad 2>stderr &&
 103        test_cmp expect stderr &&
 104
 105        # And then double-check that the resulting pack is usable (i.e.,
 106        # we did not fail to notice any cycles). We know we are accessing
 107        # the objects via the new pack here, because "repack -d" will have
 108        # removed the others.
 109        git cat-file blob HEAD:file >/dev/null &&
 110        git cat-file blob HEAD^:file >/dev/null
 111'
 112
 113test_done