class Au::Commit
Attributes
created_at[R]
doc_diff_ids[R]
id[R]
message[R]
parent_id_1[R]
parent_id_2[R]
Public Class Methods
create(staged_file_paths, commit_message, parent_commit_id = nil)
click to toggle source
# File lib/au/models/commit.rb, line 46 def self.create(staged_file_paths, commit_message, parent_commit_id = nil) # only support one parent # file_paths: changed / added / removed files_paths parent_commit = find(parent_commit_id) new_doc_diff_ids = {} # Check if this commit exists somewhere in the repository already if Commit.list.any? Commit.list.each do |commit| same = true other_commit = find(commit) if other_commit.doc_diff_ids.length == staged_file_paths.length Document.find(staged_file_paths).each do |doc| # pass in the diff id for this doc from parent commit, if present same = ((doc.diff(other_commit ? other_commit.doc_diff_ids[doc.path] : nil) == '') ? same : false) end raise ('Error: Duplicate commit with ' + commit) if same end end end Document.find(staged_file_paths).each do |doc| # pass in the diff id for this doc from parent commit, if present diff_id = doc.create_diff(parent_commit ? parent_commit.doc_diff_ids[doc.path] : nil) new_doc_diff_ids[doc.path] = diff_id ? diff_id : -1 end if parent_commit docs_from_parent_commit = Document.find(parent_commit.doc_diff_ids.keys) docs_from_parent_commit.each do |doc| new_doc_diff_ids[doc.path] ||= parent_commit.doc_diff_ids[doc.path] end end new_commit_id = build_commit_id db.transaction do db[new_commit_id] = { message: commit_message, parent_id_1: parent_commit_id, doc_diff_ids: new_doc_diff_ids.delete_if{ |key, val| val == -1 }, created_at: Time.now.to_f } end new_commit_id end
create_commit_resolve_conflict(staged_file_paths, parent_commit_id_1, parent_commit_id_2)
click to toggle source
# File lib/au/models/commit.rb, line 158 def self.create_commit_resolve_conflict(staged_file_paths, parent_commit_id_1, parent_commit_id_2) parent_commit_1 = find(parent_commit_id_1) new_doc_diff_ids = {} Document.find(staged_file_paths).each do |doc| # pass in the diff id for this doc from parent commit, if present diff_id = doc.create_diff(parent_commit_1 ? parent_commit_1.doc_diff_ids[doc.path] : nil) new_doc_diff_ids[doc.path] = diff_id ? diff_id : -1 end if parent_commit_1 docs_from_parent_commit_1 = Document.find(parent_commit_1.doc_diff_ids.keys) docs_from_parent_commit_1.each do |doc| new_doc_diff_ids[doc.path] ||= parent_commit_1.doc_diff_ids[doc.path] end end new_commit_id = build_commit_id db.transaction do db[new_commit_id] = { message: "Resolved conflicts for #{parent_commit_id_1} and #{parent_commit_id_2}", parent_id_1: parent_commit_id_1, parent_id_2: parent_commit_id_2, doc_diff_ids: new_doc_diff_ids.delete_if{ |key, val| val == -1 }, created_at: Time.now.to_f } end new_commit_id end
find(commit_id)
click to toggle source
find a commit which has been created
# File lib/au/models/commit.rb, line 29 def self.find(commit_id) return unless commit_id # used for initial commit db.transaction(true) do init_attrs = %i(message doc_diff_ids parent_id_1 parent_id_2 created_at) new(commit_id, *db[commit_id].values_at(*init_attrs)) end end
find_remote(commit_id, repo_path)
click to toggle source
find a remote commit which has been created
# File lib/au/models/commit.rb, line 38 def self.find_remote(commit_id, repo_path) return unless commit_id # used for initial commit db(repo_path).transaction(true) do init_attrs = %i(message doc_diff_ids parent_id_1 parent_id_2 created_at) new(commit_id, *db(repo_path)[commit_id].values_at(*init_attrs)) end end
list()
click to toggle source
# File lib/au/models/commit.rb, line 22 def self.list db.transaction(true) do db.roots end end
merge(parent_commit_id_1, parent_commit_id_2)
click to toggle source
# File lib/au/models/commit.rb, line 92 def self.merge(parent_commit_id_1, parent_commit_id_2) # If commit 2 is commit 1's ancestor, don't merge if gen_ancestor_set(parent_commit_id_1, Set.new).include?(parent_commit_id_2) puts 'Stop merging. Commit to be merged is ancestor of current commit' return nil end # Load commit objects parent1_commit = find(parent_commit_id_1) parent2_commit = find(parent_commit_id_2) ancestor_commit_id = lowest_common_ancestor(parent_commit_id_1, parent_commit_id_2) ancestor_commit = find(ancestor_commit_id) # Initialize local variables new_doc_diff_ids = {} conflict_file_paths = [] # Go through files in commit 1 parent1_commit.doc_diff_ids.each do |doc_path, diff1_id| if parent2_commit.doc_diff_ids[doc_path].nil? # If this file only exists in commit 1, add it to the file table new_doc_diff_ids[doc_path] = diff1_id next end doc = Document.find(doc_path) begin # If this file also exists in commit 2, run diff3 to merge files and check for conflict. doc.diff_3(ancestor_diff_id: ancestor_commit.doc_diff_ids[doc_path], other_diff_id: parent2_commit.doc_diff_ids[doc_path]) new_doc_diff_ids[doc_path] = doc.create_diff(diff1_id) if conflict_file_paths.empty? rescue Document::HasMergeConflict conflict_file_paths << doc.path end end unless conflict_file_paths.empty? # add a file to indicate user is resolving conflicts File.open(Repository.resolve_conflict_fname, 'w') do |f| f.truncate(0) f.write(parent_commit_id_1.to_s + ',' + parent_commit_id_2.to_s) end puts "Merge failed due to conflicts. Fix conflicts in files: \n#{conflict_file_paths.join('\n')}" return nil end # Check if there is any file in commit 2 and not in commit 1, # if so, add them to file table parent2_commit.doc_diff_ids.each do |doc_path, diff2_id| new_doc_diff_ids[doc_path] = diff2_id unless parent1_commit.doc_diff_ids.key?(doc_path) end # Generate commit id and Write commit new_commit_id = build_commit_id db.transaction do db[new_commit_id] = { message: "Merged #{parent_commit_id_1} and #{parent_commit_id_2}", parent_id_1: parent_commit_id_1, parent_id_2: parent_commit_id_2, doc_diff_ids: new_doc_diff_ids, created_at: Time.now.to_f } end new_commit_id end
new(id, message, doc_diff_ids, parent_id_1 = nil, parent_id_2 = nil, created_at = 0)
click to toggle source
# File lib/au/models/commit.rb, line 13 def initialize(id, message, doc_diff_ids, parent_id_1 = nil, parent_id_2 = nil, created_at = 0) @id = id @message = message @doc_diff_ids = doc_diff_ids @parent_id_1 = parent_id_1 @parent_id_2 = parent_id_2 @created_at = created_at end
Private Class Methods
build_commit_id()
click to toggle source
# File lib/au/models/commit.rb, line 241 def self.build_commit_id Digest::MD5.hexdigest(Time.now.to_f.to_s) end
db(repo_path = Repository.path)
click to toggle source
# File lib/au/models/commit.rb, line 235 def self.db(repo_path = Repository.path) # in this pstore file, each key is a commit id @db ||= {} @db[repo_path] ||= PStore.new(File.join(repo_path, 'commits.pstore')) end
gen_ancestor_set(commit_id, commit_id_set, skip_first=false)
click to toggle source
# File lib/au/models/commit.rb, line 245 def self.gen_ancestor_set(commit_id, commit_id_set, skip_first=false) commit_id_set.add(commit_id) unless skip_first commit = find(commit_id) if commit.parent_id_1 commit_id_set = self.gen_ancestor_set(commit.parent_id_1, commit_id_set) end if commit.parent_id_2 commit_id_set = self.gen_ancestor_set(commit.parent_id_2, commit_id_set) end commit_id_set end
lowest_common_ancestor(parent_commit_1, parent_commit_2)
click to toggle source
# File lib/au/models/commit.rb, line 258 def self.lowest_common_ancestor(parent_commit_1, parent_commit_2) # keys are commit id, values are Array of number of hops all_ancestors_of_parent_1 = self.gen_ancestor_set(parent_commit_1, Set.new, skip_first=true) all_ancestors_of_parent_2 = self.gen_ancestor_set(parent_commit_2, Set.new, skip_first=true) common_ancestors = all_ancestors_of_parent_1 & all_ancestors_of_parent_2 raise 'no common ancestors' if common_ancestors.size.zero? while common_ancestors.size != 1 picked_ancestor = common_ancestors.first ancestors_to_be_removed_set = gen_ancestor_set(picked_ancestor, Set.new, skip_first=true) common_ancestors -= ancestors_to_be_removed_set end common_ancestors.first end
Public Instance Methods
cat(file_path)
click to toggle source
returns a tempfile with the content of the document at the recorded diff of commit.
# File lib/au/models/commit.rb, line 200 def cat(file_path) diff_id = doc_diff_ids[file_path] return unless diff_id Document.find(file_path).content_from(diff_id) end
checkout(current_commit_id)
click to toggle source
# File lib/au/models/commit.rb, line 188 def checkout(current_commit_id) current_commit = Commit.find(current_commit_id) @doc_diff_ids.each{ |doc_path, diff_id| Document.find(doc_path).checkout(diff_id) } current_commit.doc_diff_ids.each_key do |doc_path| abs_doc_path = File.join(Repository.root, doc_path) abs_dir_path = File.dirname(abs_doc_path) File.delete(abs_doc_path) if not @doc_diff_ids.include?(doc_path) and File.exist?(abs_doc_path) Dir.delete(abs_dir_path) if Dir.empty?(abs_dir_path) end end
has_parent_1?()
click to toggle source
# File lib/au/models/commit.rb, line 218 def has_parent_1? !!parent_id_1 end
log()
click to toggle source
Returns an array of ids, starting from self.id and through all left parents (parent 1).
# File lib/au/models/commit.rb, line 208 def log commits = [self] this_commit = self while this_commit.has_parent_1? commits << this_commit.parent_1 this_commit = this_commit.parent_1 end commits end
parent_1()
click to toggle source
# File lib/au/models/commit.rb, line 222 def parent_1 return nil unless parent_id_1 @parent ||= self.class.find(parent_id_1) end
tracked_docs_md5()
click to toggle source
# File lib/au/models/commit.rb, line 227 def tracked_docs_md5 Document.find(doc_diff_ids.keys).each_with_object({}) do |doc, accum| accum[doc.path] = doc.md5_at(doc_diff_ids[doc.path]) end end