1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
|
module Grit
class Index
# Public: Gets/Sets the Grit::Repo to which this index belongs.
attr_accessor :repo
# Public: Gets/Sets the Hash tree map that holds the changes to be made
# in the next commit.
attr_accessor :tree
# Public: Gets/Sets the Grit::Tree object representing the tree upon
# which the next commit will be based.
attr_accessor :current_tree
# Public: if a tree or commit is written, this stores the size of that object
attr_reader :last_tree_size
attr_reader :last_commit_size
# Initialize a new Index object.
#
# repo - The Grit::Repo to which the index belongs.
#
# Returns the newly initialized Grit::Index.
def initialize(repo)
self.repo = repo
self.tree = {}
self.current_tree = nil
end
# Public: Add a file to the index.
#
# path - The String file path including filename (no slash prefix).
# data - The String binary contents of the file.
#
# Returns nothing.
def add(path, data)
path = path.split('/')
filename = path.pop
current = self.tree
path.each do |dir|
current[dir] ||= {}
node = current[dir]
current = node
end
current[filename] = data
end
# Public: Delete the given file from the index.
#
# path - The String file path including filename (no slash prefix).
#
# Returns nothing.
def delete(path)
add(path, false)
end
# Public: Read the contents of the given Tree into the index to use as a
# starting point for the index.
#
# tree - The String branch/tag/sha of the Git tree object.
#
# Returns nothing.
def read_tree(tree)
self.current_tree = self.repo.tree(tree)
end
# Public: Commit the contents of the index. This method supports two
# formats for arguments:
#
# message - The String commit message.
# options - An optional Hash of index options.
# :parents - Array of String commit SHA1s or Grit::Commit
# objects to attach this commit to to form a
# new head (default: nil).
# :actor - The Grit::Actor details of the user making
# the commit (default: nil).
# :last_tree - The String SHA1 of a tree to compare with
# in order to avoid making empty commits
# (default: nil).
# :head - The String branch name to write this head to
# (default: nil).
# :committed_date - The Time that the commit was made.
# (Default: Time.now)
# :authored_date - The Time that the commit was authored.
# (Default: committed_date)
#
# The legacy argument style looks like:
#
# message - The String commit message.
# parents - Array of String commit SHA1s or Grit::Commit objects to
# attach this commit to to form a new head (default: nil).
# actor - The Grit::Actor details of the user making the commit
# (default: nil).
# last_tree - The String SHA1 of a tree to compare with in order to avoid
# making empty commits (default: nil).
# head - The String branch name to write this head to
# (default: "master").
#
# Returns a String of the SHA1 of the new commit.
def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
commit_tree_sha = nil
if parents.is_a?(Hash)
commit_tree_sha = parents[:commit_tree_sha]
actor = parents[:actor]
committer = parents[:committer]
author = parents[:author]
last_tree = parents[:last_tree]
head = parents[:head]
committed_date = parents[:committed_date]
authored_date = parents[:authored_date]
parents = parents[:parents]
end
committer ||= actor
author ||= committer
if commit_tree_sha
tree_sha1 = commit_tree_sha
else
tree_sha1 = write_tree(self.tree, self.current_tree)
end
# don't write identical commits
return false if tree_sha1 == last_tree
contents = []
contents << ['tree', tree_sha1].join(' ')
parents.each do |p|
contents << ['parent', p].join(' ')
end if parents
committer ||= begin
config = Config.new(self.repo)
Actor.new(config['user.name'], config['user.email'])
end
author ||= committer
committed_date ||= Time.now
authored_date ||= committed_date
contents << ['author', author.output(authored_date)].join(' ')
contents << ['committer', committer.output(committed_date)].join(' ')
contents << ''
contents << message
contents = contents.join("\n")
@last_commit_size = contents.size
commit_sha1 = self.repo.git.put_raw_object(contents, 'commit')
self.repo.update_ref(head, commit_sha1) if head
commit_sha1
end
# Recursively write a tree to the index.
#
# tree - The Hash tree map:
# key - The String directory or filename.
# val - The Hash submap or the String contents of the file.
# now_tree - The Grit::Tree representing the a previous tree upon which
# this tree will be based (default: nil).
#
# Returns the String SHA1 String of the tree.
def write_tree(tree = nil, now_tree = nil)
tree = self.tree if !tree
tree_contents = {}
# fill in original tree
now_tree = read_tree(now_tree) if(now_tree && now_tree.is_a?(String))
now_tree.contents.each do |obj|
sha = [obj.id].pack("H*")
k = obj.name
k += '/' if (obj.class == Grit::Tree)
tmode = obj.mode.to_i.to_s ## remove zero-padding
tree_contents[k] = "%s %s\0%s" % [tmode, obj.name, sha]
end if now_tree
# overwrite with new tree contents
tree.each do |k, v|
case v
when Array
sha, mode = v
if sha.size == 40 # must be a sha
sha = [sha].pack("H*")
mode = mode.to_i.to_s # leading 0s not allowed
k = k.split('/').last # slashes not allowed
str = "%s %s\0%s" % [mode, k, sha]
tree_contents[k] = str
end
when String
sha = write_blob(v)
sha = [sha].pack("H*")
str = "%s %s\0%s" % ['100644', k, sha]
tree_contents[k] = str
when Hash
ctree = now_tree/k if now_tree
sha = write_tree(v, ctree)
sha = [sha].pack("H*")
str = "%s %s\0%s" % ['40000', k, sha]
tree_contents[k + '/'] = str
when false
tree_contents.delete(k)
end
end
tr = tree_contents.sort.map { |k, v| v }.join('')
@last_tree_size = tr.size
self.repo.git.put_raw_object(tr, 'tree')
end
# Write a blob to the index.
#
# data - The String data to write.
#
# Returns the String SHA1 of the new blob.
def write_blob(data)
self.repo.git.put_raw_object(data, 'blob')
end
end # Index
end # Grit
|