Module: Amp::Graphs::CopyCalculator
- Defined in:
- lib/amp/graphs/copies.rb
Overview
CopyCalculator
This module manages finding what files have been copied between two changesets, using a base, ancestor changeset. Closely related to merging. We need this class because Mercurial, by default, allows us to copy files and move files, and be smart enough to follow these copies throughout the version history. Other VCS’s just treat the moved file as a brand-new file. Thus, when we update from one changeset to another, we need to follow these copies.
Class Method Summary collapse
-
.find_copies(repo, changeset_local, changeset_remote, changeset_ancestor, check_dirs = false) ⇒ [Hash, Hash]
Calculates the copies between 2 changesets, using a pre-calculated common ancestor node.
Class Method Details
.find_copies(repo, changeset_local, changeset_remote, changeset_ancestor, check_dirs = false) ⇒ [Hash, Hash]
Add tracking of directory renames!
Calculates the copies between 2 changesets, using a pre-calculated common ancestor node. This is used during updates as part of Mercurial’s ability to track renames without git-style guessing. Unfortunately it does require some amount of calculation. This method returns two hashes in an array: [renames, divergent]. “Renames” are moves from one file to another, and “divergent” are two moves of the same file, only with different end-names. See @example for how divergent works.
if the local changeset renamed “foo” to “bar”, and the remote changeset renamed “foo” to “baz”, then the “divergent” hash would be:
{"foo" => ["bar", "baz"]}
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 |
# File 'lib/amp/graphs/copies.rb', line 42 def self.find_copies(repo, changeset_local, changeset_remote, changeset_ancestor, check_dirs=false) # are we udpating from an empty directory? quite easy. if changeset_local.nil? || changeset_remote.nil? || changeset_remote == changeset_local return {}, {} end # avoid silly behavior for parent -> working directory if changeset_remote.node == nil && c1.node == repo.dirstate.parents.first return repo.dirstate.copies, {} end limit = find_limit(repo, changeset_local.revision, changeset_remote.revision) man_local = changeset_local.manifest man_remote = changeset_remote.manifest man_ancestor = changeset_ancestor.manifest # gets the versioned_file for a given file and node ID easy_file_lookup = proc do |file, node| if node.size == 20 return repo.versioned_file(file, :file_id => node) end cs = (changeset_local.revision == nil) ? changeset_local : changeset_remote return cs.get_file(file) end ctx = easy_file_lookup copy = {} full_copy = {} diverge = {} # check for copies from manifest1 to manifest2 check_copies = proc do |file, man1, man2| vf1 = easy_file_lookup[file, man1[file]] find_old_names(vf1, limit) do |old_name| full_copy[file] = old_name # remember for dir rename detection if man2[old_name] # original file in other manifest? # if the original file is unchanged on the other branch, # no merge needed if man2[old_name] != man_ancestor[old_name] vf2 = easy_file_lookup[old_file, man2[old_file]] vfa = vf1.ancestor(vf2) # related and name changed on only one side? if vfa && (vfa.path == file || vfa.path == vf2.path) && (vf1 == vfa || vf2 == vfa) copy[file] = old_file end end elsif man_ancestor[old_file] (diverge[old_file] ||= []) << file end end end UI.debug(" searching for copies back to rev #{limit}") unmatched_1 = double_intersection(man_local, man_remote, man_ancestor) unmatched_2 = double_intersection(man_remote, man_local, man_ancestor) UI.debug(" unmatched files in local:\n #{unmatched_1.join("\n")}") if unmatched_1.any? UI.debug(" unmatched files in other:\n #{unmatched_2.join("\n")}") if unmatched_2.any? unmatched_1.each {|file| check_copies[file, man_local, man_remote] } unmatched_2.each {|file| check_copies[file, man_remote, man_local] } diverge_2 = {} diverge.each do |old_file, file_list| if file_list.size == 1 diverge.delete old_file else file_list.each {|file| diverge_2[file] = true} end end if !(full_copy.any?) || !check_dirs return copy, diverge end # CHECK FOR DIRECTORY RENAMES # TODO TODO TODO end |