Class: Gitgo::Git::Tree
- Inherits:
-
Object
- Object
- Gitgo::Git::Tree
- Includes:
- Enumerable
- Defined in:
- lib/gitgo/git/tree.rb
Overview
Tree represents an in-memory working tree for git. Trees are initialized with a Grit::Tree. In general tree contents are represented as (path, [:mode,sha]) pairs, but subtrees can be expanded into (path, Tree) pairs.
See Git for an example of Tree usage in practice.
Efficiency
Modes are symbolized in the internal [:mode,sha] entries because they are rarely needed as strings and are typically very redundant. Symbolizing shas makes less sense because they are frequently used as strings. However it does make sense to use the same string instance to represent a sha in multiple places. As a result trees have an internal string_table that functions like a symbol table, ie it maps the same string content to a single shared instance. The string table is managed at the class level through the string_table method.
Trees only expand as needed. This saves memory and cycles because it is expensive to read, parse, and maintain the git tree data. In general trees will stay fairly compact unless certain expensive operations are performed. These are:
-
each_pair (with expand == true)
-
each_tree (with expand == true)
-
flatten
-
to_hash (with expand == true)
Avoid these methods if possible, or ensure they are rarely executed.
Instance Attribute Summary collapse
-
#mode ⇒ Object
The tree mode.
Class Method Summary collapse
-
.string_table(clear = false) ⇒ Object
Returns the string table for shas.
Instance Method Summary collapse
-
#==(another) ⇒ Object
Returns true if the to_hash results of self and another are equal.
-
#[](path) ⇒ Object
Returns the entry for the specified path, either a [:mode,sha] pair for a blob or a Tree for a subtree.
-
#[]=(path, entry) ⇒ Object
Sets the entry for the specified path.
-
#each ⇒ Object
Yields each (path, entry) pair to the block, as an array, ordered by path.
-
#each_blob ⇒ Object
Yields the (path, [:mode, sha]) pairs for each blob to the block.
-
#each_pair(expand = false) ⇒ Object
Yields each (path, entry) pair to the block, ordered by path.
-
#each_tree(expand = false) ⇒ Object
Yields the (path, entry) pairs for each tree to the block.
-
#eql?(another) ⇒ Boolean
Returns true if the to_hash results of self and another are equal.
-
#flatten(prefix = nil, target = {}) ⇒ Object
Flattens all paths under self into a single array.
-
#initialize(tree = nil) ⇒ Tree
constructor
Initializes a new Tree.
-
#keys ⇒ Object
Returns the keys (ie paths) for all entries in self.
- #merge!(another) ⇒ Object
-
#sha(check = true) ⇒ Object
Returns the sha representing the contents for self.
-
#sha=(sha) ⇒ Object
Sets the sha for self.
-
#subtree(segments, force = false) ⇒ Object
Returns the subtree indicated by the specified segments (an array of paths), or nil if no such subtree exists.
-
#to_hash(expand = false) ⇒ Object
Returns self as a hash, expanding if specified.
-
#write_to(git) ⇒ Object
Writes self to the git instance.
Constructor Details
#initialize(tree = nil) ⇒ Tree
Initializes a new Tree. The input tree should be a Grit::Tree or nil.
49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/gitgo/git/tree.rb', line 49 def initialize(tree=nil) @index = nil @tree = tree if tree self.mode = tree.mode self.sha = tree.id else @mode = nil @sha = nil end end |
Instance Attribute Details
#mode ⇒ Object
The tree mode.
46 47 48 |
# File 'lib/gitgo/git/tree.rb', line 46 def mode @mode end |
Class Method Details
.string_table(clear = false) ⇒ Object
Returns the string table for shas. Specify clear to reset the string table.
38 39 40 41 |
# File 'lib/gitgo/git/tree.rb', line 38 def string_table(clear=false) @string_table.clear if clear @string_table ||= Hash.new {|hash, key| hash[key] = key.freeze } end |
Instance Method Details
#==(another) ⇒ Object
Returns true if the to_hash results of self and another are equal.
239 240 241 |
# File 'lib/gitgo/git/tree.rb', line 239 def ==(another) self.to_hash == another.to_hash end |
#[](path) ⇒ Object
Returns the entry for the specified path, either a [:mode,sha] pair for a blob or a Tree for a subtree.
102 103 104 105 106 107 108 109 110 111 |
# File 'lib/gitgo/git/tree.rb', line 102 def [](path) case when entry = index[path] entry when tree = index.delete(path.to_sym) index[string(path)] = Tree.new(tree) else nil end end |
#[]=(path, entry) ⇒ Object
Sets the entry for the specified path. The entry should be a
- :mode,sha
-
array, or a Tree. A nil entry indicates removal.
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/gitgo/git/tree.rb', line 115 def []=(path, entry) # ensure an unexpanded tree is removed index.delete(path.to_sym) path = string(path) case entry when Array mode, sha = entry index[path] = [mode.to_sym, string(sha)] when Tree index[path] = entry when nil index.delete(path) else raise "invalid entry: #{entry.inspect}" end # add/remove content modifies self so # the sha can and should be invalidated @sha = nil end |
#each ⇒ Object
Yields each (path, entry) pair to the block, as an array, ordered by path. Implemented to get access to the enumerable methods.
146 147 148 149 150 151 |
# File 'lib/gitgo/git/tree.rb', line 146 def each each_pair do |key, value| yield [key, value] end self end |
#each_blob ⇒ Object
Yields the (path, [:mode, sha]) pairs for each blob to the block.
168 169 170 171 172 173 |
# File 'lib/gitgo/git/tree.rb', line 168 def each_blob each_pair do |key, value| next unless value.kind_of?(Array) yield(key, value) end end |
#each_pair(expand = false) ⇒ Object
Yields each (path, entry) pair to the block, ordered by path. Entries can be [:mode,sha] arrays or Trees. If expand is true then subtrees will be expanded, but strongly consider whether or not expansion is necessary because it is computationally expensive.
157 158 159 160 161 162 163 164 165 |
# File 'lib/gitgo/git/tree.rb', line 157 def each_pair(=false) # sorting the keys is important when writing the tree; # unsorted keys cause warnings in git fsck keys = index.keys.sort_by {|key| key.to_s } store = ? self : index keys.each {|key| yield(key, store[key]) } end |
#each_tree(expand = false) ⇒ Object
Yields the (path, entry) pairs for each tree to the block. Subtrees are expanded if specified, in which case all entries will be Trees. Without expansion, entries may be [:mode,sha] arrays or Trees.
178 179 180 181 182 183 |
# File 'lib/gitgo/git/tree.rb', line 178 def each_tree(=false) each_pair() do |key, value| next unless value.kind_of?(Tree) yield(key, value) end end |
#eql?(another) ⇒ Boolean
Returns true if the to_hash results of self and another are equal.
234 235 236 |
# File 'lib/gitgo/git/tree.rb', line 234 def eql?(another) self.to_hash == another.to_hash end |
#flatten(prefix = nil, target = {}) ⇒ Object
Flattens all paths under self into a single array.
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/gitgo/git/tree.rb', line 203 def flatten(prefix=nil, target={}) keys.each do |key| next unless entry = self[key] key = key.to_s key = File.join(prefix, key) if prefix if entry.kind_of?(Tree) entry.flatten(key, target) else target[key] = entry end end target end |
#keys ⇒ Object
Returns the keys (ie paths) for all entries in self. Keys are returned as strings
96 97 98 |
# File 'lib/gitgo/git/tree.rb', line 96 def keys index.keys.collect {|keys| keys.to_s } end |
#merge!(another) ⇒ Object
137 138 139 140 141 142 |
# File 'lib/gitgo/git/tree.rb', line 137 def merge!(another) another.each_pair do |path, entry| self[path] = entry end self end |
#sha(check = true) ⇒ Object
Returns the sha representing the contents for self. If check is true, sha will check that neither self nor any subtree is modified before returning the sha. If modified, the sha is set to nil to flag a repo to recalculate the sha on commit.
Note that check does not validate the sha correctly represents the contents of self.
81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/gitgo/git/tree.rb', line 81 def sha(check=true) if @sha && check index.each_value do |value| if value.kind_of?(Tree) && value.sha.nil? @sha = nil break end end end @sha end |
#sha=(sha) ⇒ Object
Sets the sha for self. Sha may be set to nil, in which case it will be calculated when a repo is committed.
70 71 72 |
# File 'lib/gitgo/git/tree.rb', line 70 def sha=(sha) @sha = sha ? string(sha) : nil end |
#subtree(segments, force = false) ⇒ Object
Returns the subtree indicated by the specified segments (an array of paths), or nil if no such subtree exists. If force is true then missing subtrees will be created.
188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/gitgo/git/tree.rb', line 188 def subtree(segments, force=false) return self if segments.empty? key = segments.shift tree = self[key] if !tree.kind_of?(Tree) return nil unless force self[key] = tree = Tree.new(nil) end tree.subtree(segments, force) end |
#to_hash(expand = false) ⇒ Object
Returns self as a hash, expanding if specified.
221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/gitgo/git/tree.rb', line 221 def to_hash(=false) hash = {} each_pair() do |key, value| hash[key] = case value when Tree then value.to_hash when Array then value else to_entry(value) end end hash end |
#write_to(git) ⇒ Object
Writes self to the git instance. All subtrees will likewise be written. Returns a [mode, sha] entry.
Tree format:
mode name\0[packedsha]mode name\0[packedsha]...
Note there are no newlines separating tree entries.
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/gitgo/git/tree.rb', line 251 def write_to(git) self.mode ||= git.default_tree_mode self.sha ||= begin lines = [] each_pair(false) do |key, entry| mode, sha = case entry when Tree then entry.write_to(git) when Array then entry else [entry.mode, entry.id] end # modes should not begin with zeros (although it is not fatal # if they do), otherwise fsck will print warnings like this: # # warning in tree 980127...: contains zero-padded file modes lines << zero_strip("#{mode} #{key}\0#{[sha].pack("H*")}") end git.set(:tree, lines.join) end [mode, sha] end |