• J
    for_each_ref: load all loose refs before packed refs · 98eeb09e
    Jeff King 提交于
    If we are iterating through the refs using for_each_ref (or
    any of its sister functions), we can get into a race
    condition with a simultaneous "pack-refs --prune" that looks
    like this:
    
      0. We have a large number of loose refs, and a few packed
         refs. refs/heads/z/foo is loose, with no matching entry
         in the packed-refs file.
    
      1. Process A starts iterating through the refs. It loads
         the packed-refs file from disk, then starts lazily
         traversing through the loose ref directories.
    
      2. Process B, running "pack-refs --prune", writes out the
         new packed-refs file. It then deletes the newly packed
         refs, including refs/heads/z/foo.
    
      3. Meanwhile, process A has finally gotten to
         refs/heads/z (it traverses alphabetically). It
         descends, but finds nothing there.  It checks its
         cached view of the packed-refs file, but it does not
         mention anything in "refs/heads/z/" at all (it predates
         the new file written by B in step 2).
    
    The traversal completes successfully without mentioning
    refs/heads/z/foo at all (the name, of course, isn't
    important; but the more refs you have and the farther down
    the alphabetical list a ref is, the more likely it is to hit
    the race). If refs/heads/z/foo did exist in the packed refs
    file at state 0, we would see an entry for it, but it would
    show whatever sha1 the ref had the last time it was packed
    (which could be an arbitrarily long time ago).
    
    This can be especially dangerous when process A is "git
    prune", as it means our set of reachable tips will be
    incomplete, and we may erroneously prune objects reachable
    from that tip (the same thing can happen if "repack -ad" is
    used, as it simply drops unreachable objects that are
    packed).
    
    This patch solves it by loading all of the loose refs for
    our traversal into our in-memory cache, and then refreshing
    the packed-refs cache. Because a pack-refs writer will
    always put the new packed-refs file into place before
    starting the prune, we know that any loose refs we fail to
    see will either truly be missing, or will have already been
    put in the packed-refs file by the time we refresh.
    Signed-off-by: NMichael Haggerty <mhagger@alum.mit.edu>
    Signed-off-by: NJeff King <peff@peff.net>
    Signed-off-by: NJunio C Hamano <gitster@pobox.com>
    98eeb09e
refs.c 88.2 KB