1. 09 12月, 2017 1 次提交
  2. 22 11月, 2017 1 次提交
    • J
      pack-objects: add list-objects filtering · 9535ce73
      Jeff Hostetler 提交于
      Teach pack-objects to use the filtering provided by the
      traverse_commit_list_filtered() interface to omit unwanted
      objects from the resulting packfile.
      
      Filtering requires the use of the "--stdout" option.
      
      Add t5317 test.
      
      In the future, we will introduce a "partial clone" mechanism
      wherein an object in a repo, obtained from a remote, may
      reference a missing object that can be dynamically fetched from
      that remote once needed.  This "partial clone" mechanism will
      have a way, sometimes slow, of determining if a missing link
      is one of the links expected to be produced by this mechanism.
      
      This patch introduces handling of missing objects to help
      debugging and development of the "partial clone" mechanism,
      and once the mechanism is implemented, for a power user to
      perform operations that are missing-object aware without
      incurring the cost of checking if a missing link is expected.
      Signed-off-by: NJeff Hostetler <jeffhost@microsoft.com>
      Reviewed-by: NJonathan Tan <jonathantanmy@google.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      9535ce73
  3. 10 10月, 2017 1 次提交
  4. 22 9月, 2017 1 次提交
  5. 14 9月, 2017 1 次提交
  6. 24 8月, 2017 2 次提交
    • J
      pack: move open_pack_index(), parse_pack_index() · 0317f455
      Jonathan Tan 提交于
      alloc_packed_git() in packfile.c is duplicated from sha1_file.c. In a
      subsequent commit, alloc_packed_git() will be removed from sha1_file.c.
      Signed-off-by: NJonathan Tan <jonathantanmy@google.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      0317f455
    • M
      pack-objects: take lock before accessing `remaining` · 0c2ad00b
      Martin Ågren 提交于
      When checking the conditional of "while (me->remaining)", we did not
      hold the lock. Calling find_deltas would still be safe, since it checks
      "remaining" (after taking the lock) and is able to handle all values. In
      fact, this could (currently) not trigger any bug: a bug could happen if
      `remaining` transitioning from zero to non-zero races with the evaluation
      of the while-condition, but these are always separated by the
      data_ready-mechanism.
      
      Make sure we have the lock when we read `remaining`. This does mean we
      release it just so that find_deltas can take it immediately again. We
      could tweak the contract so that the lock should be taken before calling
      find_deltas, but let's defer that until someone can actually show that
      "unlock+lock" has a measurable negative impact.
      Signed-off-by: NMartin Ågren <martin.agren@gmail.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      0c2ad00b
  7. 21 7月, 2017 1 次提交
    • R
      pack-objects: remove unnecessary NULL check · c7b07805
      René Scharfe 提交于
      If done_pbase_paths is NULL then done_pbase_paths_num must be zero and
      done_pbase_path_pos() returns -1 without accessing the array, so the
      check is not necessary.
      
      If the invariant was violated then the check would make sure we keep
      on going and allocate the necessary amount of memory in the next
      ALLOC_GROW call.  That sounds nice, but all array entries except for
      one would contain garbage data.
      
      If the invariant was violated without the check we'd get a segfault in
      done_pbase_path_pos(), i.e. an observable crash, alerting us of the
      presence of a bug.
      
      Currently there is no such bug: Only the functions check_pbase_path()
      and cleanup_preferred_base() change pointer and counter, and both make
      sure to keep them in sync.  Get rid of the check anyway to allow us to
      see if later changes introduce such a defect, and to simplify the code.
      
      Detected by Coverity Scan.
      Signed-off-by: NRene Scharfe <l.s.r@web.de>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      c7b07805
  8. 18 7月, 2017 1 次提交
    • R
      use MOVE_ARRAY · f331ab9d
      René Scharfe 提交于
      Simplify the code for moving members inside of an array and make it more
      robust by using the helper macro MOVE_ARRAY.  It calculates the size
      based on the specified number of elements for us and supports NULL
      pointers when that number is zero.  Raw memmove(3) calls with NULL can
      cause the compiler to (over-eagerly) optimize out later NULL checks.
      
      This patch was generated with contrib/coccinelle/array.cocci and spatch
      (Coccinelle).
      Signed-off-by: NRene Scharfe <l.s.r@web.de>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      f331ab9d
  9. 17 6月, 2017 1 次提交
  10. 16 6月, 2017 1 次提交
  11. 26 5月, 2017 1 次提交
  12. 09 5月, 2017 1 次提交
    • J
      pack-objects: disable pack reuse for object-selection options · 9df4a607
      Jeff King 提交于
      If certain options like --honor-pack-keep, --local, or
      --incremental are used with pack-objects, then we need to
      feed each potential object to want_object_in_pack() to see
      if it should be filtered out. But when the bitmap
      reuse_packfile optimization is in effect, we do not call
      that function at all, and in fact skip adding the objects to
      the to_pack list entirely.  This means we have a bug: for
      certain requests we will silently ignore those options and
      include objects in that pack that should not be there.
      
      The problem has been present since the inception of the
      pack-reuse code in 6b8fda2d (pack-objects: use bitmaps when
      packing objects, 2013-12-21), but it was unlikely to come up
      in practice.  These options are generally used for on-disk
      packing, not transfer packs (which go to stdout), but we've
      never allowed pack reuse for non-stdout packs (until
      645c432d, we did not even use bitmaps, which the reuse
      optimization relies on; after that, we explicitly turned it
      off when not packing to stdout).
      
      We can fix this by just disabling the reuse_packfile
      optimization when the options are in use. In theory we could
      teach the pack-reuse code to satisfy these checks, but it's
      not worth the complexity. The purpose of the optimization is
      to keep the amount of per-object work we do to a minimum.
      But these options inherently require us to search for other
      copies of each object, drowning out any benefit of the
      pack-reuse optimization. But note that the optimizations
      from 56dfeb62 (pack-objects: compute local/ignore_pack_keep
      early, 2016-07-29) happen before pack-reuse, meaning that
      specifying "--honor-pack-keep" in a repository with no .keep
      files can still follow the fast path.
      
      There are tests in t5310 that check these options with
      bitmaps and --stdout, but they didn't catch the bug, and
      it's hard to adapt them to do so.
      
      One problem is that they don't use --delta-base-offset;
      without that option, we always disable the reuse
      optimization entirely. It would be fine to add it in (it
      actually makes the test more realistic), but that still
      isn't quite enough.
      
      The other problem is that the reuse code is very picky; it
      only kicks in when it can reuse most of a pack, starting
      from the first byte. So we'd have to start from a fully
      repacked and bitmapped state to trigger it. But the tests
      for these options use a much more subtle state; they want to
      be sure that the want_object_in_pack() code is allowing some
      objects but not others. Doing a full repack runs counter to
      that.
      
      So this patch adds new tests at the end of the script which
      create the fully-packed state and make sure that each option
      is not fooled by reusable pack.
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      9df4a607
  13. 08 5月, 2017 3 次提交
  14. 27 4月, 2017 1 次提交
    • J
      timestamp_t: a new data type for timestamps · dddbad72
      Johannes Schindelin 提交于
      Git's source code assumes that unsigned long is at least as precise as
      time_t. Which is incorrect, and causes a lot of problems, in particular
      where unsigned long is only 32-bit (notably on Windows, even in 64-bit
      versions).
      
      So let's just use a more appropriate data type instead. In preparation
      for this, we introduce the new `timestamp_t` data type.
      
      By necessity, this is a very, very large patch, as it has to replace all
      timestamps' data type in one go.
      
      As we will use a data type that is not necessarily identical to `time_t`,
      we need to be very careful to use `time_t` whenever we interact with the
      system functions, and `timestamp_t` everywhere else.
      Signed-off-by: NJohannes Schindelin <johannes.schindelin@gmx.de>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      dddbad72
  15. 31 3月, 2017 4 次提交
  16. 25 3月, 2017 2 次提交
    • J
      pack.h: define largest possible encoded object size · 2c5e2865
      Jeff King 提交于
      Several callers use fixed buffers for storing the pack
      object header, and they've picked 10 as a magic number. This
      is reasonable, since it handles objects up to 2^67. But
      let's give them a constant so it's clear that the number
      isn't pulled out of thin air.
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      2c5e2865
    • J
      encode_in_pack_object_header: respect output buffer length · 7202a6fa
      Jeff King 提交于
      The encode_in_pack_object_header() writes a variable-length
      header to an output buffer, but it doesn't actually know
      long the buffer is. At first glance, this looks like it
      might be possible to overflow.
      
      In practice, this is probably impossible. The smallest
      buffer we use is 10 bytes, which would hold the header for
      an object up to 2^67 bytes. Obviously we're not likely to
      see such an object, but we might worry that an object could
      lie about its size (causing us to overflow before we realize
      it does not actually have that many bytes). But the argument
      is passed as a uintmax_t. Even on systems that have __int128
      available, uintmax_t is typically restricted to 64-bit by
      the ABI.
      
      So it's unlikely that a system exists where this could be
      exploited. Still, it's easy enough to use a normal out/len
      pair and make sure we don't write too far. That protects the
      hypothetical 128-bit system, makes it harder for callers to
      accidentally specify a too-small buffer, and makes the
      resulting code easier to audit.
      
      Note that the one caller in fast-import tried to catch such
      a case, but did so _after_ the call (at which point we'd
      have already overflowed!). This check can now go away.
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      7202a6fa
  17. 23 2月, 2017 1 次提交
  18. 02 2月, 2017 2 次提交
  19. 28 1月, 2017 2 次提交
    • J
      pack-objects: convert recursion to iteration in break_delta_chain() · 42b766d7
      Jeff King 提交于
      The break_delta_chain() function is recursive over the depth
      of a given delta chain, which can lead to possibly running
      out of stack space. Normally delta depth is quite small, but
      if there _is_ a pathological case, this is where we would
      find and fix it, so we should be more careful.
      
      We can do it without recursion at all, but there's a little
      bit of cleverness needed to do so. It's easiest to explain
      by covering the less-clever strategies first.
      
      The obvious thing to try is just keeping our own stack on
      the heap. Whenever we would recurse, push the new entry onto
      the stack and loop instead. But this gets tricky; when we
      see an ACTIVE entry, we need to care if we just pushed it
      (in which case it's a cycle) or if we just popped it (in
      which case we dealt with its bases, and no we need to clear
      the ACTIVE flag and compute its depth).
      
      You can hack around that in various ways, like keeping a
      "just pushed" flag, but the logic gets muddled. However, we
      can observe that we do all of our pushes first, and then all
      of our pops afterwards. In other words, we can do this in
      two passes. First dig down to the base, stopping when we see
      a cycle, and pushing each item onto our stack.  Then pop the
      stack elements, clearing the ACTIVE flag and computing the
      depth for each.
      
      This works, and is reasonably elegant. However, why do we
      need the stack for the second pass? We can just walk the
      delta pointers again. There's one complication. Popping the
      stack went over our list in reverse, so we could compute the
      depth of each entry by incrementing the depth of its base,
      which we will have just computed.  To go forward in the
      second pass, we have to compute the total depth on the way
      down, and then assign it as we go.
      
      This patch implements this final strategy, because it not
      only keeps the memory off the stack, but it eliminates it
      entirely. Credit for the cleverness in that approach goes to
      Michael Haggerty; bugs are mine.
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      42b766d7
    • J
      pack-objects: enforce --depth limit in reused deltas · 7dbabbbe
      Jeff King 提交于
      Since 898b14ce (pack-objects: rework check_delta_limit usage,
      2007-04-16), we check the delta depth limit only when
      figuring out whether we should make a new delta. We don't
      consider it at all when reusing deltas, which means that
      packing once with --depth=250, and then again with
      --depth=50, the second pack may still contain chains larger
      than 50.
      
      This is generally considered a feature, as the results of
      earlier high-depth repacks are carried forward, used for
      serving fetches, etc. However, since we started using
      cross-pack deltas in c9af708b (pack-objects: use mru list
      when iterating over packs, 2016-08-11), we are no longer
      bounded by the length of an existing delta chain in a single
      pack.
      
      Here's one particular pathological case: a sequence of N
      packs, each with 2 objects, the base of which is stored as a
      delta in a previous pack. If we chain all the deltas
      together, we have a cycle of length N. We break the cycle,
      but the tip delta is still at depth N-1.
      
      This is less unlikely than it might sound. See the included
      test for a reconstruction based on real-world actions.  I
      ran into such a case in the wild, where a client was rapidly
      sending packs, and we had accumulated 10,000 before doing a
      server-side repack.  The pack that "git repack" tried to
      generate had a very deep chain, which caused pack-objects to
      run out of stack space in the recursive write_one().
      
      This patch bounds the length of delta chains in the output
      pack based on --depth, regardless of whether they are caused
      by cross-pack deltas or existed in the input packs. This
      fixes the problem, but does have two possible downsides:
      
        1. High-depth aggressive repacks followed by "normal"
           repacks will throw away the high-depth chains.
      
           In the long run this is probably OK; investigation
           showed that high-depth repacks aren't actually
           beneficial, and we dropped the aggressive depth default
           to match the normal case in 07e7dbf0 (gc: default
           aggressive depth to 50, 2016-08-11).
      
        2. If you really do want to store high-depth deltas on
           disk, they may be discarded and new delta computed when
           serving a fetch, unless you set pack.depth to match
           your high-depth size.
      
      The implementation uses the existing search for delta
      cycles.  That lets us compute the depth of any node based on
      the depth of its base, because we know the base is DFS_DONE
      by the time we look at it (modulo any cycles in the graph,
      but we know there cannot be any because we break them as we
      see them).
      
      There is some subtlety worth mentioning, though. We record
      the depth of each object as we compute it. It might seem
      like we could save the per-object storage space by just
      keeping track of the depth of our traversal (i.e., have
      break_delta_chains() report how deep it went). But we may
      visit an object through multiple delta paths, and on
      subsequent paths we want to know its depth immediately,
      without having to walk back down to its final base (doing so
      would make our graph walk quadratic rather than linear).
      
      Likewise, one could try to record the depth not from the
      base, but from our starting point (i.e., start
      recursion_depth at 0, and pass "recursion_depth + 1" to each
      invocation of break_delta_chains()). And then when
      recursion_depth gets too big, we know that we must cut the
      delta chain.  But that technique is wrong if we do not visit
      the nodes in topological order. In a chain A->B->C, it
      if we visit "C", then "B", then "A", we will never recurse
      deeper than 1 link (because we see at each node that we have
      already visited it).
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      7dbabbbe
  20. 16 11月, 2016 1 次提交
    • J
      compression: unify pack.compression configuration parsing · 8de7eeb5
      Junio C Hamano 提交于
      There are three codepaths that use a variable whose name is
      pack_compression_level to affect how objects and deltas sent to a
      packfile is compressed.  Unlike zlib_compression_level that controls
      the loose object compression, however, this variable was static to
      each of these codepaths.  Two of them read the pack.compression
      configuration variable, using core.compression as the default, and
      one of them also allowed overriding it from the command line.
      
      The other codepath in bulk-checkin did not pay any attention to the
      configuration.
      
      Unify the configuration parsing to git_default_config(), where we
      implement the parsing of core.loosecompression and core.compression
      and make the former override the latter, by moving code to parse
      pack.compression and also allow core.compression to give default to
      this variable.
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      8de7eeb5
  21. 26 10月, 2016 1 次提交
  22. 30 9月, 2016 1 次提交
  23. 13 9月, 2016 2 次提交
    • K
      pack-objects: use reachability bitmap index when generating non-stdout pack · 645c432d
      Kirill Smelkov 提交于
      Starting from 6b8fda2d (pack-objects: use bitmaps when packing objects)
      if a repository has bitmap index, pack-objects can nicely speedup
      "Counting objects" graph traversal phase. That however was done only for
      case when resultant pack is sent to stdout, not written into a file.
      
      The reason here is for on-disk repack by default we want:
      
      - to produce good pack (with bitmap index not-yet-packed objects are
        emitted to pack in suboptimal order).
      
      - to use more robust pack-generation codepath (avoiding possible
        bugs in bitmap code and possible bitmap index corruption).
      
      Jeff King further explains:
      
          The reason for this split is that pack-objects tries to determine how
          "careful" it should be based on whether we are packing to disk or to
          stdout. Packing to disk implies "git repack", and that we will likely
          delete the old packs after finishing. We want to be more careful (so
          as not to carry forward a corruption, and to generate a more optimal
          pack), and we presumably run less frequently and can afford extra CPU.
          Whereas packing to stdout implies serving a remote via "git fetch" or
          "git push". This happens more frequently (e.g., a server handling many
          fetching clients), and we assume the receiving end takes more
          responsibility for verifying the data.
      
          But this isn't always the case. One might want to generate on-disk
          packfiles for a specialized object transfer. Just using "--stdout" and
          writing to a file is not optimal, as it will not generate the matching
          pack index.
      
          So it would be useful to have some way of overriding this heuristic:
          to tell pack-objects that even though it should generate on-disk
          files, it is still OK to use the reachability bitmaps to do the
          traversal.
      
      So we can teach pack-objects to use bitmap index for initial object
      counting phase when generating resultant pack file too:
      
      - if we take care to not let it be activated under git-repack:
      
        See above about repack robustness and not forward-carrying corruption.
      
      - if we know bitmap index generation is not enabled for resultant pack:
      
        The current code has singleton bitmap_git, so it cannot work
        simultaneously with two bitmap indices.
      
        We also want to avoid (at least with current implementation)
        generating bitmaps off of bitmaps. The reason here is: when generating
        a pack, not-yet-packed objects will be emitted into pack in
        suboptimal order and added to tail of the bitmap as "extended entries".
        When the resultant pack + some new objects in associated repository
        are in turn used to generate another pack with bitmap, the situation
        repeats: new objects are again not emitted optimally and just added to
        bitmap tail - not in recency order.
      
        So the pack badness can grow over time when at each step we have
        bitmapped pack + some other objects. That's why we want to avoid
        generating bitmaps off of bitmaps, not to let pack badness grow.
      
      - if we keep pack reuse enabled still only for "send-to-stdout" case:
      
        Because pack-to-file needs to generate index for destination pack, and
        currently on pack reuse raw entries are directly written out to the
        destination pack by write_reused_pack(), bypassing needed for pack index
        generation bookkeeping done by regular codepath in write_one() and
        friends.
      
        ( In the future we might teach pack-reuse code about cases when index
          also needs to be generated for resultant pack and remove
          pack-reuse-only-for-stdout limitation )
      
      This way for pack-objects -> file we get nice speedup:
      
          erp5.git[1] (~230MB) extracted from ~ 5GB lab.nexedi.com backup
          repository managed by git-backup[2] via
      
          time echo 0186ac99 | git pack-objects --revs erp5pack
      
      before:  37.2s
      after:   26.2s
      
      And for `git repack -adb` packed git.git
      
          time echo 5c589a73 | git pack-objects --revs gitpack
      
      before:   7.1s
      after:    3.6s
      
      i.e. it can be 30% - 50% speedup for pack extraction.
      
      git-backup extracts many packs on repositories restoration. That was my
      initial motivation for the patch.
      
      [1] https://lab.nexedi.com/nexedi/erp5
      [2] https://lab.nexedi.com/kirr/git-backup
      
      NOTE
      
      Jeff also suggests that pack.useBitmaps was probably a mistake to
      introduce originally. This way we are not adding another config point,
      but instead just always default to-file pack-objects not to use bitmap
      index: Tools which need to generate on-disk packs with using bitmap, can
      pass --use-bitmap-index explicitly. And git-repack does never pass
      --use-bitmap-index, so this way we can be sure regular on-disk repacking
      remains robust.
      
      NOTE2
      
      `git pack-objects --stdout >file.pack` + `git index-pack file.pack` is much slower
      than `git pack-objects file.pack`. Extracting erp5.git pack from
      lab.nexedi.com backup repository:
      
          $ time echo 0186ac99 | git pack-objects --stdout --revs >erp5pack-stdout.pack
      
          real    0m22.309s
          user    0m21.148s
          sys     0m0.932s
      
          $ time git index-pack erp5pack-stdout.pack
      
          real    0m50.873s   <-- more than 2 times slower than time to generate pack itself!
          user    0m49.300s
          sys     0m1.360s
      
      So the time for
      
          `pack-object --stdout >file.pack` + `index-pack file.pack`  is  72s,
      
      while
      
          `pack-objects file.pack` which does both pack and index     is  27s.
      
      And even
      
          `pack-objects --no-use-bitmap-index file.pack`              is  37s.
      
      Jeff explains:
      
          The packfile does not carry the sha1 of the objects. A receiving
          index-pack has to compute them itself, including inflating and applying
          all of the deltas.
      
      that's why for `git-backup restore` we want to teach `git pack-objects
      file.pack` to use bitmaps instead of using `git pack-objects --stdout
      >file.pack` + `git index-pack file.pack`.
      
      NOTE3
      
      The speedup is now tracked via t/perf/p5310-pack-bitmaps.sh
      
          Test                                    56dfeb62          this tree
          --------------------------------------------------------------------------------
          5310.2: repack to disk                  8.98(8.05+0.29)   9.05(8.08+0.33) +0.8%
          5310.3: simulated clone                 2.02(2.27+0.09)   2.01(2.25+0.08) -0.5%
          5310.4: simulated fetch                 0.81(1.07+0.02)   0.81(1.05+0.04) +0.0%
          5310.5: pack to file                    7.58(7.04+0.28)   7.60(7.04+0.30) +0.3%
          5310.6: pack to file (bitmap)           7.55(7.02+0.28)   3.25(2.82+0.18) -57.0%
          5310.8: clone (partial bitmap)          1.83(2.26+0.12)   1.82(2.22+0.14) -0.5%
          5310.9: pack to file (partial bitmap)   6.86(6.58+0.30)   2.87(2.74+0.20) -58.2%
      
      More context:
      
          http://marc.info/?t=146792101400001&r=1&w=2
          http://public-inbox.org/git/20160707190917.20011-1-kirr@nexedi.com/T/#t
      
      Cc: Vicent Marti <tanoku@gmail.com>
      Helped-by: NJeff King <peff@peff.net>
      Signed-off-by: NKirill Smelkov <kirr@nexedi.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      645c432d
    • K
      pack-objects: respect --local/--honor-pack-keep/--incremental when bitmap is in use · 702d1b95
      Kirill Smelkov 提交于
      Since 6b8fda2d (pack-objects: use bitmaps when packing objects) there
      are two codepaths in pack-objects: with & without using bitmap
      reachability index.
      
      However add_object_entry_from_bitmap(), despite its non-bitmapped
      counterpart add_object_entry(), in no way does check for whether --local
      or --honor-pack-keep or --incremental should be respected. In
      non-bitmapped codepath this is handled in want_object_in_pack(), but
      bitmapped codepath has simply no such checking at all.
      
      The bitmapped codepath however was allowing to pass in all those options
      and with bitmap indices still being used under such conditions -
      potentially giving wrong output (e.g. including objects from non-local or
      .keep'ed pack).
      
      We can easily fix this by noting the following: when an object comes to
      add_object_entry_from_bitmap() it can come for two reasons:
      
          1. entries coming from main pack covered by bitmap index, and
          2. object coming from, possibly alternate, loose or other packs.
      
      "2" can be already handled by want_object_in_pack() and to cover
      "1" we can teach want_object_in_pack() to expect that *found_pack can be
      non-NULL, meaning calling client already found object's pack entry.
      
      In want_object_in_pack() we care to start the checks from already found
      pack, if we have one, this way determining the answer right away
      in case neither --local nor --honour-pack-keep are active. In
      particular, as p5310-pack-bitmaps.sh shows (3 consecutive runs), we do
      not do harm to served-with-bitmap clones performance-wise:
      
          Test                      56dfeb62          this tree
          -----------------------------------------------------------------
          5310.2: repack to disk    9.08(8.20+0.25)   9.09(8.14+0.32) +0.1%
          5310.3: simulated clone   1.92(2.12+0.08)   1.93(2.12+0.09) +0.5%
          5310.4: simulated fetch   0.82(1.07+0.04)   0.82(1.06+0.04) +0.0%
          5310.6: partial bitmap    1.96(2.42+0.13)   1.95(2.40+0.15) -0.5%
      
          Test                      56dfeb62          this tree
          -----------------------------------------------------------------
          5310.2: repack to disk    9.11(8.16+0.32)   9.11(8.19+0.28) +0.0%
          5310.3: simulated clone   1.93(2.14+0.07)   1.92(2.11+0.10) -0.5%
          5310.4: simulated fetch   0.82(1.06+0.04)   0.82(1.04+0.05) +0.0%
          5310.6: partial bitmap    1.95(2.38+0.16)   1.94(2.39+0.14) -0.5%
      
          Test                      56dfeb62          this tree
          -----------------------------------------------------------------
          5310.2: repack to disk    9.13(8.17+0.31)   9.07(8.13+0.28) -0.7%
          5310.3: simulated clone   1.92(2.13+0.07)   1.91(2.12+0.06) -0.5%
          5310.4: simulated fetch   0.82(1.08+0.03)   0.82(1.08+0.03) +0.0%
          5310.6: partial bitmap    1.96(2.43+0.14)   1.96(2.42+0.14) +0.0%
      
      with delta timings showing they are all within noise from run to run.
      
      In the general case we do not want to call find_pack_entry_one() more than
      once, because it is expensive. This patch splits the loop in
      want_object_in_pack() into two parts: finding the object and seeing if it
      impacts our choice to include it in the pack. We may call the inexpensive
      want_found_object() twice, but we will never call find_pack_entry_one() if we
      do not need to.
      
      I appreciate help and discussing this change with Junio C Hamano and
      Jeff King.
      Signed-off-by: NKirill Smelkov <kirr@nexedi.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      702d1b95
  24. 08 9月, 2016 1 次提交
    • J
      pack-objects: walk tag chains for --include-tag · b773ddea
      Jeff King 提交于
      When pack-objects is given --include-tag, it peels each tag
      ref down to a non-tag object, and if that non-tag object is
      going to be packed, we include the tag, too. But what
      happens if we have a chain of tags (e.g., tag "A" points to
      tag "B", which points to commit "C")?
      
      We'll peel down to "C" and realize that we want to include
      tag "A", but we do not ever consider tag "B", leading to a
      broken pack (assuming "B" was not otherwise selected).
      Instead, we have to walk the whole chain, adding any tags we
      find to the pack.
      
      Interestingly, it doesn't seem possible to trigger this
      problem with "git fetch", but you can with "git clone
      --single-branch". The reason is that we generate the correct
      pack when the client explicitly asks for "A" (because we do
      a real reachability analysis there), and "fetch" is more
      willing to do so. There are basically two cases:
      
        1. If "C" is already a ref tip, then the client can deduce
           that it needs "A" itself (via find_non_local_tags), and
           will ask for it explicitly rather than relying on the
           include-tag capability. Everything works.
      
        2. If "C" is not already a ref tip, then we hope for
           include-tag to send us the correct tag. But it doesn't;
           it generates a broken pack. However, the next step is
           to do a follow-up run of find_non_local_tags(),
           followed by fetch_refs() to backfill any tags we
           learned about.
      
           In the normal case, fetch_refs() calls quickfetch(),
           which does a connectivity check and sees we have no
           new objects to fetch. We just write the refs.
      
           But for the broken-pack case, the connectivity check
           fails, and quickfetch will follow-up with the remote,
           asking explicitly for each of the ref tips. This picks
           up the missing object in a new pack.
      
      For a regular "git clone", we are similarly OK, because we
      explicitly request all of the tag refs, and get a correct
      pack. But with "--single-branch", we kick in tag
      auto-following via "include-tag", but do _not_ do a
      follow-up backfill. We just take whatever the server sent us
      via include-tag and write out tag refs for any tag objects
      we were sent. So prior to c6807a40 (clone: open a shortcut
      for connectivity check, 2013-05-26), we actually claimed the
      clone was a success, but the result was silently
      corrupted!  Since c6807a40, index-pack's connectivity
      check catches this case, and we correctly complain.
      
      The included test directly checks that pack-objects does not
      generate a broken pack, but also confirms that "clone
      --single-branch" does not hit the bug.
      
      Note that tag chains introduce another interesting question:
      if we are packing the tag "B" but not the commit "C", should
      "A" be included?
      
      Both before and after this patch, we do not include "A",
      because the initial peel_ref() check only knows about the
      bottom-most level, "C". To realize that "B" is involved at
      all, we would have to switch to an incremental peel, in
      which we examine each tagged object, asking if it is being
      packed (and including the outer tag if so).
      
      But that runs contrary to the optimizations in peel_ref(),
      which avoid accessing the objects at all, in favor of using
      the value we pull from packed-refs. It's OK to walk the
      whole chain once we know we're going to include the tag (we
      have to access it anyway, so the effort is proportional to
      the pack we're generating). But for the initial selection,
      we have to look at every ref. If we're only packing a few
      objects, we'd still have to parse every single referenced
      tag object just to confirm that it isn't part of a tag
      chain.
      
      This could be addressed if packed-refs stored the complete
      tag chain for each peeled ref (in most cases, this would be
      the same cost as now, as each "chain" is only a single
      link). But given the size of that project, it's out of scope
      for this fix (and probably nobody cares enough anyway, as
      it's such an obscure situation). This commit limits itself
      to just avoiding the creation of a broken pack.
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      b773ddea
  25. 12 8月, 2016 2 次提交
    • J
      pack-objects: use mru list when iterating over packs · c9af708b
      Jeff King 提交于
      In the original implementation of want_object_in_pack(), we
      always looked for the object in every pack, so the order did
      not matter for performance.
      
      As of the last few patches, however, we can now often break
      out of the loop early after finding the first instance, and
      avoid looking in the other packs at all. In this case, pack
      order can make a big difference, because we'd like to find
      the objects by looking at as few packs as possible.
      
      This patch switches us to the same packed_git_mru list that
      is now used by normal object lookups.
      
      Here are timings for p5303 on linux.git:
      
      Test                      HEAD^                HEAD
      ------------------------------------------------------------------------
      5303.3: rev-list (1)      31.31(31.07+0.23)    31.28(31.00+0.27) -0.1%
      5303.4: repack (1)        40.35(38.84+2.60)    40.53(39.31+2.32) +0.4%
      5303.6: rev-list (50)     31.37(31.15+0.21)    31.41(31.16+0.24) +0.1%
      5303.7: repack (50)       58.25(68.54+2.03)    47.28(57.66+1.89) -18.8%
      5303.9: rev-list (1000)   31.91(31.57+0.33)    31.93(31.64+0.28) +0.1%
      5303.10: repack (1000)    304.80(376.00+3.92)  87.21(159.54+2.84) -71.4%
      
      The rev-list numbers are unchanged, which makes sense (they
      are not exercising this code at all). The 50- and 1000-pack
      repack cases show considerable improvement.
      
      The single-pack repack case doesn't, of course; there's
      nothing to improve. In fact, it gives us a baseline for how
      fast we could possibly go. You can see that though rev-list
      can approach the single-pack case even with 1000 packs,
      repack doesn't. The reason is simple: the loop we are
      optimizing is only part of what the repack is doing. After
      the "counting" phase, we do delta compression, which is much
      more expensive when there are multiple packs, because we
      have fewer deltas we can reuse (you can also see that these
      numbers come from a multicore machine; the CPU times are
      much higher than the wall-clock times due to the delta
      phase).
      
      So the good news is that in cases with many packs, we used
      to be dominated by the "counting" phase, and now we are
      dominated by the delta compression (which is faster, and
      which we have already parallelized).
      
      Here are similar numbers for git.git:
      
      Test                      HEAD^               HEAD
      ---------------------------------------------------------------------
      5303.3: rev-list (1)      1.55(1.51+0.02)     1.54(1.53+0.00) -0.6%
      5303.4: repack (1)        1.82(1.80+0.08)     1.82(1.78+0.09) +0.0%
      5303.6: rev-list (50)     1.58(1.57+0.00)     1.58(1.56+0.01) +0.0%
      5303.7: repack (50)       2.50(3.12+0.07)     2.31(2.95+0.06) -7.6%
      5303.9: rev-list (1000)   2.22(2.20+0.02)     2.23(2.19+0.03) +0.5%
      5303.10: repack (1000)    10.47(16.78+0.22)   7.50(13.76+0.22) -28.4%
      
      Not as impressive in terms of percentage, but still
      measurable wins.  If you look at the wall-clock time
      improvements in the 1000-pack case, you can see that linux
      improved by roughly 10x as many seconds as git. That's
      because it has roughly 10x as many objects, and we'd expect
      this improvement to scale linearly with the number of
      objects (since the number of packs is kept constant). It's
      just that the "counting" phase is a smaller percentage of
      the total time spent for a git.git repack, and hence the
      percentage win is smaller.
      
      The implementation itself is a straightforward use of the
      MRU code. We only bother marking a pack as used when we know
      that we are able to break early out of the loop, for two
      reasons:
      
        1. If we can't break out early, it does no good; we have
           to visit each pack anyway, so we might as well avoid
           even the minor overhead of managing the cache order.
      
        2. The mru_mark() function reorders the list, which would
           screw up our traversal. So it is only safe to mark when
           we are about to break out of the loop. We could record
           the found pack and mark it after the loop finishes, of
           course, but that's more complicated and it doesn't buy
           us anything due to (1).
      
      Note that this reordering does have a potential impact on
      the final pack, as we store only a single "found" pack for
      each object, even if it is present in multiple packs. In
      principle, any copy is acceptable, as they all refer to the
      same content. But in practice, they may differ in whether
      they are stored as deltas, against which base, etc. This may
      have an impact on delta reuse, and even the delta search
      (since we skip pairs that were already in the same pack).
      
      It's not clear whether this change of order would hurt or
      even help average cases, though. The most likely reason to
      have duplicate objects is from the completion of thin packs
      (e.g., you have some objects in a "base" pack, then receive
      several pushes; the packs you receive may be thin on the
      wire, with deltas that refer to bases outside the pack, but
      we complete them with duplicate base objects when indexing
      them).
      
      In such a case the current code would always find the thin
      duplicates (because we currently walk the packs in reverse
      chronological order). Whereas with this patch, some of those
      duplicates would be found in the base pack instead.
      
      In my tests repacking a real-world case of linux.git with
      3600 thin-pack pushes (on top of a large "base" pack), the
      resulting pack was about 0.04% larger with this patch. On
      the other hand, because we were more likely to hit the base
      pack, there were more opportunities for delta reuse, and we
      had 50,000 fewer objects to examine in the delta search.
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      c9af708b
    • J
      pack-objects: break delta cycles before delta-search phase · 4cf2143e
      Jeff King 提交于
      We do not allow cycles in the delta graph of a pack (i.e., A
      is a delta of B which is a delta of A) for the obvious
      reason that you cannot actually access any of the objects in
      such a case.
      
      There's a last-ditch attempt to notice cycles during the
      write phase, during which we issue a warning to the user and
      write one of the objects out in full. However, this is
      "last-ditch" for two reasons:
      
        1. By this time, it's too late to find another delta for
           the object, so the resulting pack is larger than it
           otherwise could be.
      
        2. The warning is there because this is something that
           _shouldn't_ ever happen. If it does, then either:
      
             a. a pack we are reusing deltas from had its own
                cycle
      
             b. we are reusing deltas from multiple packs, and
                we found a cycle among them (i.e., A is a delta of
                B in one pack, but B is a delta of A in another,
                and we choose to use both deltas).
      
             c. there is a bug in the delta-search code
      
           So this code serves as a final check that none of these
           things has happened, warns the user, and prevents us
           from writing a bogus pack.
      
      Right now, (2b) should never happen because of the static
      ordering of packs in want_object_in_pack(). If two objects
      have a delta relationship, then they must be in the same
      pack, and therefore we will find them from that same pack.
      
      However, a future patch would like to change that static
      ordering, which will make (2b) a common occurrence. In
      preparation, we should be able to handle those kinds of
      cycles better. This patch does by introducing a
      cycle-breaking step during the get_object_details() phase,
      when we are deciding which deltas can be reused. That gives
      us the chance to feed the objects into the delta search as
      if the cycle did not exist.
      
      We'll leave the detection and warning in the write_object()
      phase in place, as it still serves as a check for case (2c).
      
      This does mean we will stop warning for (2a). That case is
      caused by bogus input packs, and we ideally would warn the
      user about it.  However, since those cycles show up after
      picking reusable deltas, they look the same as (2b) to us;
      our new code will break the cycles early and the last-ditch
      check will never see them.
      
      We could do analysis on any cycles that we find to
      distinguish the two cases (i.e., it is a bogus pack if and
      only if every delta in the cycle is in the same pack), but
      we don't need to. If there is a cycle inside a pack, we'll
      run into problems not only reusing the delta, but accessing
      the object data at all. So when we try to dig up the actual
      size of the object, we'll hit that same cycle and kick in
      our usual complain-and-try-another-source code.
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      4cf2143e
  26. 30 7月, 2016 2 次提交
    • J
      pack-objects: compute local/ignore_pack_keep early · 56dfeb62
      Jeff King 提交于
      In want_object_in_pack(), we can exit early from our loop if
      neither "local" nor "ignore_pack_keep" are set. If they are,
      however, we must examine each pack to see if it has the
      object and is non-local or has a ".keep".
      
      It's quite common for there to be no non-local or .keep
      packs at all, in which case we know ahead of time that
      looking further will be pointless. We can pre-compute this
      by simply iterating over the list of packs ahead of time,
      and dropping the flags if there are no packs that could
      match.
      
      Another similar strategy would be to modify the loop in
      want_object_in_pack() to notice that we have already found
      the object once, and that we are looping only to check for
      "local" and "keep" attributes. If a pack has neither of
      those, we can skip the call to find_pack_entry_one(), which
      is the expensive part of the loop.
      
      This has two advantages:
      
        - it isn't all-or-nothing; we still get some improvement
          when there's a small number of kept or non-local packs,
          and a large number of non-kept local packs
      
        - it eliminates any possible race where we add new
          non-local or kept packs after our initial scan. In
          practice, I don't think this race matters; we already
          cache the packed_git information, so somebody who adds a
          new pack or .keep file after we've started will not be
          noticed at all, unless we happen to need to call
          reprepare_packed_git() because a lookup fails.
      
          In other words, we're already racy, and the race is not
          a big deal (losing the race means we might include an
          object in the pack that would not otherwise be, which is
          an acceptable outcome).
      
      However, it also has a disadvantage: we still loop over the
      rest of the packs for each object to check their flags. This
      is much less expensive than doing the object lookup, but
      still not free. So if we wanted to implement that strategy
      to cover the non-all-or-nothing cases, we could do so in
      addition to this one (so you get the most speedup in the
      all-or-nothing case, and the best we can do in the other
      cases). But given that the all-or-nothing case is likely the
      most common, it is probably not worth the trouble, and we
      can revisit this later if evidence points otherwise.
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      56dfeb62
    • J
      pack-objects: break out of want_object loop early · cd379967
      Jeff King 提交于
      When pack-objects collects the list of objects to pack
      (either from stdin, or via its internal rev-list), it
      filters each one through want_object_in_pack().
      
      This function loops through each existing packfile, looking
      for the object. When we find it, we mark the pack/offset
      combo for later use. However, we can't just return "yes, we
      want it" at that point. If --honor-pack-keep is in effect,
      we must keep looking to find it in _all_ packs, to make sure
      none of them has a .keep. Likewise, if --local is in effect,
      we must make sure it is not present in any non-local pack.
      
      As a result, the sum effort of these calls is effectively
      O(nr_objects * nr_packs). In an ordinary repository, we have
      only a handful of packs, and this doesn't make a big
      difference. But in pathological cases, it can slow the
      counting phase to a crawl.
      
      This patch notices the case that we have neither "--local"
      nor "--honor-pack-keep" in effect and breaks out of the loop
      early, after finding the first instance. Note that our worst
      case is still "objects * packs" (i.e., we might find each
      object in the last pack we look in), but in practice we will
      often break out early. On an "average" repo, my git.git with
      8 packs, this shows a modest 2% (a few dozen milliseconds)
      improvement in the counting-objects phase of "git
      pack-objects --all <foo" (hackily instrumented by sticking
      exit(0) right after list_objects).
      
      But in a much more pathological case, it makes a bigger
      difference. I ran the same command on a real-world example
      with ~9 million objects across 1300 packs. The counting time
      dropped from 413s to 45s, an improvement of about 89%.
      
      Note that this patch won't do anything by itself for a
      normal "git gc", as it uses both --honor-pack-keep and
      --local.
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      cd379967
  27. 14 7月, 2016 1 次提交
    • N
      pack-objects: do not truncate result in-pack object size on 32-bit systems · af92a645
      Nguyễn Thái Ngọc Duy 提交于
      A typical diff will not show what's going on and you need to see full
      functions. The core code is like this, at the end of of write_one()
      
      	e->idx.offset = *offset;
      	size = write_object(f, e, *offset);
      	if (!size) {
      		e->idx.offset = recursing;
      		return WRITE_ONE_BREAK;
      	}
      	written_list[nr_written++] = &e->idx;
      
      	/* make sure off_t is sufficiently large not to wrap */
      	if (signed_add_overflows(*offset, size))
      		die("pack too large for current definition of off_t");
      	*offset += size;
      
      Here we can see that the in-pack object size is returned by
      write_object (or indirectly by write_reuse_object). And it's used to
      calculate object offsets, which end up in the pack index file,
      generated at the end.
      
      If "size" overflows (on 32-bit sytems, unsigned long is 32-bit while
      off_t can be 64-bit), we got wrong offsets and produce incorrect .idx
      file, which may make it look like the .pack file is corrupted.
      Signed-off-by: NNguyễn Thái Ngọc Duy <pclouds@gmail.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      af92a645
  28. 13 7月, 2016 1 次提交