module Gitrb class Tree < GitObject include Enumerable attr_accessor :mode, :repository # Initialize a tree def initialize(options = {}) super(options) @children = {} @mode = options[:mode] || 040000 parse(options[:data]) if options[:data] @modified = true if !id end def type :tree end # Set new repository (modified flag is reset) def id=(id) @modified = false super end # Has this tree been modified? def modified? @modified || @children.values.any? { |entry| entry.type == :tree && entry.modified? } end def dump @children.to_a.sort {|a,b| a.first <=> b.first }.map do |name, child| child.save if !(Reference === child) || child.resolved? "#{child.mode.to_s(8)} #{name}\0#{repository.set_encoding [child.id].pack("H*")}" end.join end # Save this tree back to the git repository. # # Returns the object id of the tree. def save repository.put(self) if modified? id end # Does this key exist in the children? def exists?(name) self[name] != nil end # Read an entry on specified path. def [](path) path = normalize_path(path) return self if path.empty? entry = @children[path.first] if path.size == 1 entry elsif entry raise 'Not a tree' if entry.type != :tree entry[path[1..-1]] end end # Write an entry on specified path. def []=(path, entry) raise ArgumentError if !entry path = normalize_path(path) if path.empty? raise 'Empty path' elsif path.size == 1 raise 'No blob or tree' if entry.type != :tree && entry.type != :blob entry.repository = repository @modified = true @children[path.first] = entry else tree = @children[path.first] if !tree tree = @children[path.first] = Tree.new(:repository => repository) @modified = true end raise 'Not a tree' if tree.type != :tree tree[path[1..-1]] = entry end end # Delete an entry on specified path. def delete(path) path = normalize_path(path) if path.empty? raise 'Empty path' elsif path.size == 1 child = @children.delete(path.first) @modified = true if child child else tree = @children[path.first] raise 'Not a tree' if tree.type != :tree tree.delete(path[1..-1]) end end # Move a entry def move(path, dest) self[dest] = delete(path) end # Iterate over all children def each(&block) @children.sort.each do |name, child| yield(name, child) end end def names @children.keys.sort end def values map { |name, child| child } end alias children values private def normalize_path(path) return path if Array === path path = path.to_s (path[0, 1] == '/' ? path[1..-1] : path).split('/') end # Read the contents of a raw git object. def parse(data) @children.clear data = StringIO.new(data) while !data.eof? mode = Util.read_bytes_until(data, ' ').to_i(8) name = repository.set_encoding Util.read_bytes_until(data, "\0") id = repository.set_encoding data.read(20).unpack("H*").first @children[name] = Reference.new(:repository => repository, :id => id, :mode => mode) end end end end