Class: Refinement::Changeset
- Inherits:
-
Object
- Object
- Refinement::Changeset
- Defined in:
- lib/refinement/changeset.rb,
lib/refinement/changeset/file_modification.rb
Overview
Represents a set of changes in a repository between a prior revision and the current state
Defined Under Namespace
Classes: FileModification, GitError
Instance Attribute Summary collapse
-
#description ⇒ String
readonly
A desciption of the changeset.
-
#repository ⇒ Pathname
readonly
The path to the repository.
Class Method Summary collapse
-
.from_git(repository:, base_revision:) ⇒ Changeset
The changes in the given git repository between the given revision and HEAD.
-
.parse_raw_diff(diff, repository:, base_revision:) ⇒ Array<FileModification>
Parses the raw diff into FileModification objects.
Instance Method Summary collapse
-
#find_modification_for_glob(absolute_glob:) ⇒ FileModification, Nil
The modification for the given absolute glob, or ‘nil` if no files matching the glob were modified.
-
#find_modification_for_path(absolute_path:) ⇒ FileModification, Nil
The changeset for the given absolute path, or ‘nil` if the given path is un-modified.
-
#find_modification_for_yaml_keypath(absolute_path:, keypath:) ⇒ FileModification, Nil
A modification and yaml diff for the keypath at the given absolute path, or ‘nil` if the value at the given keypath is un-modified.
-
#initialize(repository:, modifications:, description: nil) ⇒ Changeset
constructor
A new instance of Changeset.
Constructor Details
#initialize(repository:, modifications:, description: nil) ⇒ Changeset
Returns a new instance of Changeset.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/refinement/changeset.rb', line 26 def initialize(repository:, modifications:, description: nil) @repository = repository @modifications = self.class.add_directories(modifications).uniq.freeze @description = description @modified_paths = {} @modifications .each { |mod| @modified_paths[mod.path] = mod } .each { |mod| @modified_paths[mod.prior_path] ||= mod if mod.prior_path } @modified_paths.freeze @modified_absolute_paths = {} @modified_paths .each { |path, mod| @modified_absolute_paths[path.(repository).freeze] = mod } @modified_absolute_paths.freeze end |
Instance Attribute Details
#description ⇒ String (readonly)
Returns a desciption of the changeset.
22 23 24 |
# File 'lib/refinement/changeset.rb', line 22 def description @description end |
#repository ⇒ Pathname (readonly)
Returns the path to the repository.
14 15 16 |
# File 'lib/refinement/changeset.rb', line 14 def repository @repository end |
Class Method Details
.from_git(repository:, base_revision:) ⇒ Changeset
Returns the changes in the given git repository between the given revision and HEAD.
143 144 145 146 147 148 149 150 151 152 |
# File 'lib/refinement/changeset.rb', line 143 def self.from_git(repository:, base_revision:) raise ArgumentError, "must be given a Pathname for repository, got #{repository.inspect}" unless repository.is_a?(Pathname) raise ArgumentError, "must be given a String for base_revision, got #{base_revision.inspect}" unless base_revision.is_a?(String) merge_base = git!('merge-base', base_revision, 'HEAD', chdir: repository).strip diff = git!('diff', '--raw', '-z', merge_base, chdir: repository) modifications = parse_raw_diff(diff, repository: repository, base_revision: merge_base).freeze new(repository: repository, modifications: modifications, description: "since #{base_revision}") end |
.parse_raw_diff(diff, repository:, base_revision:) ⇒ Array<FileModification>
Parses the raw diff into FileModification objects
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 |
# File 'lib/refinement/changeset.rb', line 174 def self.parse_raw_diff(diff, repository:, base_revision:) # since we're null separating the chunks (to avoid dealing with path escaping) we have to reconstruct # the chunks into individual diff entries. entries always start with a colon so we can use that to signal if # we're on a new entry parsed_lines = diff.split("\0").each_with_object([]) do |chunk, lines| lines << [] if chunk.start_with?(':') lines.last << chunk end parsed_lines.map do |split_line| # change chunk (letter + optional similarity percentage) will always be the last part of first line chunk change_chunk = split_line[0].split(/\s/).last new_path = Pathname(split_line[2]).freeze if split_line[2] old_path = Pathname(split_line[1]).freeze prior_path = old_path if new_path # new path if one exists, else existing path. new path only exists for rename and copy changed_path = new_path || old_path change_character = change_chunk[0] # returns 0 when a similarity percentage isn't specified by git. _similarity = change_chunk[1..3].to_i FileModification.new( path: changed_path, type: CHANGE_CHARACTERS[change_character], prior_path: prior_path, contents_reader: -> { repository.join(changed_path).read }, prior_contents_reader: lambda { git!('show', "#{base_revision}:#{prior_path || changed_path}", chdir: repository) } ) end end |
Instance Method Details
#find_modification_for_glob(absolute_glob:) ⇒ FileModification, Nil
Will only return a single (arbitrary) matching modification, even if there are multiple modifications that match the glob
Returns the modification for the given absolute glob, or ‘nil` if no files matching the glob were modified.
117 118 119 120 121 122 123 124 125 |
# File 'lib/refinement/changeset.rb', line 117 def find_modification_for_glob(absolute_glob:) absolute_globs = dir_glob_equivalent_patterns(absolute_glob) _path, modification = modified_absolute_paths.find do |absolute_path, _modification| absolute_globs.any? do |glob| File.fnmatch?(glob, absolute_path, File::FNM_CASEFOLD | File::FNM_PATHNAME) end end modification end |
#find_modification_for_path(absolute_path:) ⇒ FileModification, Nil
Returns the changeset for the given absolute path, or ‘nil` if the given path is un-modified.
65 66 67 |
# File 'lib/refinement/changeset.rb', line 65 def find_modification_for_path(absolute_path:) modified_absolute_paths[absolute_path] end |
#find_modification_for_yaml_keypath(absolute_path:, keypath:) ⇒ FileModification, Nil
Returns a modification and yaml diff for the keypath at the given absolute path, or ‘nil` if the value at the given keypath is un-modified.
131 132 133 134 135 136 137 138 |
# File 'lib/refinement/changeset.rb', line 131 def find_modification_for_yaml_keypath(absolute_path:, keypath:) return unless (file_modification = find_modification_for_path(absolute_path: absolute_path)) diff = file_modification.yaml_diff(keypath) return unless diff [file_modification, diff] end |