Class: Amp::Core::Repositories::Git::PackFile

Inherits:
Object
  • Object
show all
Includes:
Support
Defined in:
lib/amp-git/repo_format/packfile.rb

Overview

PackFile

Git uses it’s “gc” command to pack loose objects into PackFiles. This is one file, preferably with an index (though not requiring one), which stores a number of objects in a very simple raw form.

The index is not necessary. It is simply preferable because otherwise you have to uncompress each object in a raw, then calculate the hash of the object, just to find out where each object is and what its hash is.

Defined Under Namespace

Classes: PackFileEntry

Constant Summary collapse

OBJ_COMMIT =
1
OBJ_TREE =
2
OBJ_BLOB =
3
OBJ_TAG =
4
OBJ_OFS_DELTA =
6
OBJ_REF_DELTA =
7
DATA_START_OFFSET =
12
PREFIX_NAME_LOOKUP =
{OBJ_COMMIT => 'commit', OBJ_TREE => 'tree', OBJ_BLOB => 'blob', OBJ_TAG => 'tag'}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, opener) ⇒ PackFile

Initializes a PackFile. Parses the header for some information but that’s about it. It will however determine if there is an index file, and if so, it will load that for fast lookups later. It also verifies the fourcc of the packfile.

Parameters:

  • name (String)

    the name of the packfile. This is relative to the directory it’s in.

  • opener (Support::RootedOpener)

    an opener that should be relative to the .git directory.



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/amp-git/repo_format/packfile.rb', line 197

def initialize(name, opener)
  @name = name
  @opener = opener
  opener.open(name, "r") do |fp|
    # Check signature
    unless fp.read(4) == "PACK"
      raise ArgumentError.new("#{name} is not a packfile.")
    end
    @version = fp.read(4).unpack("N").first
    @size    = fp.read(4).unpack("N").first
    cur = fp.tell
    fp.seek(0, IO::SEEK_END)
    @end_of_data = fp.tell - 20
  end
  possible_index_path = name[0..(name.size - File.extname(name).size - 1)] + ".idx"
  if File.exist? possible_index_path
    # use a persistent file pointer
    fp = File.open(possible_index_path, "r")
    @index = PackFileIndex.parse(fp)
  end
  @offset_cache = {}
end

Instance Attribute Details

#indexObject (readonly)

Returns the value of attribute index.



178
179
180
# File 'lib/amp-git/repo_format/packfile.rb', line 178

def index
  @index
end

#nameObject (readonly)

Returns the value of attribute name.



178
179
180
# File 'lib/amp-git/repo_format/packfile.rb', line 178

def name
  @name
end

#sizeObject (readonly)

Returns the value of attribute size.



178
179
180
# File 'lib/amp-git/repo_format/packfile.rb', line 178

def size
  @size
end

#versionObject (readonly)

Returns the value of attribute version.



178
179
180
# File 'lib/amp-git/repo_format/packfile.rb', line 178

def version
  @version
end

Instance Method Details

#cache_entry(entry) ⇒ Object



224
225
226
# File 'lib/amp-git/repo_format/packfile.rb', line 224

def cache_entry(entry)
  @offset_cache[entry.hash_id] = entry.offset
end

#cached_offset(given_hash) ⇒ Object



220
221
222
# File 'lib/amp-git/repo_format/packfile.rb', line 220

def cached_offset(given_hash)
  @offset_cache[given_hash]
end

#object_for_hash(given_hash) ⇒ RawObject

Gets an object in the Git system with the provided SHA1 hash identifier. If this packfile has an associated index file, that will be used. Otherwise, the packfile can be scanned from the beginning to the end, caching offsets as it goes, enabling easy lookup later. Either way, a RawObject or a subclass of it will be returned, or nil if no matching object is found.

Parameters:

  • given_hash (String)

    the SHA-1 of the desired object

Returns:

  • (RawObject)

    the object with the given ID. Nil if the object is not in the packfile.



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/amp-git/repo_format/packfile.rb', line 238

def object_for_hash(given_hash)
  @opener.open(name, "r") do |fp|
    given_hash.force_encoding("ASCII-8BIT") if given_hash.respond_to?(:force_encoding)
    entry = nil
    if index
      starting_at = index.offset_for_hash(given_hash)
      return PackFileEntry.at(starting_at, fp).to_raw_object
    else
      starting_at = cached_offset(given_hash) || DATA_START_OFFSET
      fp.seek(starting_at, IO::SEEK_SET)
      while fp.tell < @end_of_data
        entry = PackFileEntry.read(fp)
        cache_entry(entry)
        return entry.to_raw_object if entry.hash_id == given_hash
      end
    end
  end
  nil
end