module Git::Repository::Diffing::Private
Private helpers local to {Git::Repository::Diffing}
@api private
Public Instance Methods
Source
# File lib/git/repository/diffing.rb, line 657 def build_diff_files_entry(path, parts) { mode_index: parts[1], mode_repo: parts[0].to_s[1, 7], path: path, sha_repo: parts[2], sha_index: parts[3], type: parts[4] } end
Builds a single file-info hash for {#parse_diff_files_output}
@param path [String] the file path
@param parts [Array<String>] the whitespace-split fields from the info
portion of the diff-files line: `[mode_src, mode_dest, sha_src, sha_dest, type]`
@return [Hash] entry hash with keys ‘:mode_index`, `:mode_repo`, `:path`,
`:sha_repo`, `:sha_index`, `:type`
Source
# File lib/git/repository/diffing.rb, line 734 def build_numstat_files(file_stats) file_stats.to_h { |s| [s[:filename], s.slice(:insertions, :deletions)] } end
Builds the ‘:files` sub-hash for {#parse_numstat_output}
@param file_stats [Array<Hash>] per-file stats from {#parse_numstat_line}
@return [Hash{String => Hash}] per-file insertion and deletion counts
Source
# File lib/git/repository/diffing.rb, line 721 def build_numstat_totals(file_stats) insertions = file_stats.sum { |s| s[:insertions] } deletions = file_stats.sum { |s| s[:deletions] } { insertions: insertions, deletions: deletions, lines: insertions + deletions, files: file_stats.size } end
Builds the ‘:total` sub-hash for {#parse_numstat_output}
@param file_stats [Array<Hash>] per-file stats from {#parse_numstat_line}
@return [Hash] aggregate totals
`{ insertions: Integer, deletions: Integer, lines: Integer, files: Integer }`
Source
# File lib/git/repository/diffing.rb, line 558 def call_diff_command(execution_context, from, to, pathspecs) Git::Commands::Diff.new(execution_context).call( *[from, to].compact, raw: true, numstat: true, shortstat: true, src_prefix: 'a/', dst_prefix: 'b/', path: pathspecs ) end
Runs git-diff with ‘–raw` format options and returns the result
@param execution_context [Git::ExecutionContext] the execution context
used to run git commands
@param from [String] first ref
@param to [String, nil] second ref
@param pathspecs [Array<String>, nil] path limiters
@return [Git::CommandLine::Result] the result of calling ‘git diff`
Source
# File lib/git/repository/diffing.rb, line 679 def extract_name_status_from_raw(output) output.split("\n").each_with_object({}) do |line, memo| next unless line.start_with?(':') parts = line[1..].split(/\s+/, 5) status_and_paths = parts[4].split("\t") status = status_and_paths[0] path = status_and_paths.length > 2 ? status_and_paths[2] : status_and_paths[1] memo[unescape_quoted_path(path)] = status end end
Extracts name-status data from ‘–raw` diff output lines
Raw lines have the format:
:old_mode new_mode old_sha new_sha status\tpath
or for renames/copies:
:old_mode new_mode old_sha new_sha Rxx\told_path\tnew_path
@param output [String] raw diff output
@return [Hash{String => String}] mapping of file paths to status tokens
Source
# File lib/git/repository/diffing.rb, line 744 def extract_numstat_lines(output) output.split("\n").reject { |l| l.empty? || l.match?(/^\s*\d+\s+files?\s+changed/) } end
Filters raw numstat+shortstat output to only the numstat lines
@param output [String] combined command output
@return [Array<String>] only the numstat lines (no empties, no shortstat line)
Source
# File lib/git/repository/diffing.rb, line 540 def extract_patch_text(output) match = output.match(/^diff --git /m) match ? output[match.begin(0)..] : output end
Extracts only the patch text from combined diff command output
When {Git::Commands::Diff} is called with ‘patch: true, numstat: true, shortstat: true`, the stdout contains numstat lines, a shortstat summary line, and then the unified patch text starting at `“diff –git ”`. This method strips the leading numstat/shortstat lines and returns only the patch portion.
@param output [String] combined command output
@return [String] only the patch text (may be empty when there are no
changes)
Source
# File lib/git/repository/diffing.rb, line 578 def normalize_pathspecs(pathspecs, arg_name) return nil unless pathspecs normalized = Array(pathspecs) validate_pathspec_types(normalized, arg_name) normalized = normalized.map(&:to_s).reject(&:empty?) return nil if normalized.empty? normalized end
Normalizes path specifications for Git commands
@param pathspecs [String, Pathname, Array<String, Pathname>, nil]
the path(s) to normalize
@param arg_name [String] the argument name used in error messages
@return [Array<String>, nil] the normalized paths, or ‘nil` if none are valid
@raise [ArgumentError] if any path is not a ‘String` or `Pathname`
Source
# File lib/git/repository/diffing.rb, line 640 def parse_diff_files_line(line, tab_pos) path = unescape_quoted_path(line[(tab_pos + 1)..]) parts = line[0, tab_pos].split [path, build_diff_files_entry(path, parts)] end
Parses a single raw ‘git diff-files` output line into a path/entry pair
@param line [String] a single non-empty line containing a tab character
@param tab_pos [Integer] the index of the first tab in the line
@return [Array(String, Hash)] two-element array of ‘[path, entry_hash]`
Source
# File lib/git/repository/diffing.rb, line 620 def parse_diff_files_output(stdout) stdout.split("\n").each_with_object({}) do |line, memo| next if line.empty? tab_pos = line.index("\t") next unless tab_pos path, entry = parse_diff_files_line(line, tab_pos) memo[path] = entry end end
Parses raw ‘git diff-files` output into a file-keyed hash
Each output line has the format:
`:old_mode new_mode old_sha new_sha status\tpath`
The leading colon on ‘old_mode` is stripped when building the `:mode_repo` value.
@param stdout [String] raw stdout from {Git::Commands::DiffFiles#call}
@return [Hash{String => Hash}] a hash keyed by file path where each
value has keys `:mode_index`, `:mode_repo`, `:path`, `:sha_repo`, `:sha_index`, and `:type`
Source
# File lib/git/repository/diffing.rb, line 757 def parse_numstat_line(line) insertions_s, deletions_s, filename = line.split("\t", 3) { filename: unescape_quoted_path(filename), insertions: insertions_s.to_i, deletions: deletions_s.to_i } end
Parses a single ‘–numstat` line into a stats hash
Numstat lines have the format ‘<insertions>t<deletions>t<path>`. Quoted paths (containing non-ASCII or special characters) are unescaped.
@param line [String] a single numstat output line
@return [Hash] ‘{ filename: String, insertions: Integer, deletions: Integer }`
Source
# File lib/git/repository/diffing.rb, line 708 def parse_numstat_output(output) file_stats = extract_numstat_lines(output).map { |line| parse_numstat_line(line) } { total: build_numstat_totals(file_stats), files: build_numstat_files(file_stats) } end
Parses combined ‘–numstat –shortstat` output into an insertions/deletions hash
Strips the trailing shortstat summary line and empty lines, parses the remaining numstat lines, and returns a structured hash with per-file stats and aggregated totals.
@param output [String] raw stdout from ‘git diff –numstat –shortstat`
@return [Hash] per-file insertion and deletion counts plus aggregate totals
```
{
total: { insertions: Integer, deletions: Integer, lines: Integer, files: Integer },
files: { "path/to/file" => { insertions: Integer, deletions: Integer } }
}
```
Source
# File lib/git/repository/diffing.rb, line 515 def resolve_path_limiter(opts) if opts.key?(:path_limiter) opts[:path_limiter] elsif opts.key?(:path) Git::Deprecation.warn( 'Git::Repository#diff_path_status :path option is deprecated and will be removed in v6.0.0. ' \ 'Use :path_limiter instead.' ) opts[:path] end end
Resolves the effective path limiter from the options hash
When ‘:path_limiter` is present it is used directly and no warning is emitted. When only `:path` is present a deprecation warning is emitted and its value is used. Returns `nil` when neither key is present.
@param opts [Hash] the options hash from {#diff_path_status}
@option opts [String, Pathname, Array<String, Pathname>, nil] :path_limiter (nil)
primary path limiter value used as-is when present
@option opts [String, Pathname, Array<String, Pathname>, nil] :path (nil)
**deprecated** — fallback path limiter when `:path_limiter` is not provided
@return [String, Pathname, Array<String, Pathname>, nil]
the effective path limiter
Source
# File lib/git/repository/diffing.rb, line 773 def unescape_quoted_path(path) if path.start_with?('"') && path.end_with?('"') Git::EscapedPath.new(path[1..-2]).unescape else path end end
Unescapes a git-quoted path (e.g. ‘“quoted_file_\342\230\240”`)
Git quotes paths that contain non-ASCII or special characters by wrapping them in double-quotes and octal-escaping each byte. This method strips the surrounding quotes and delegates unescaping to {Git::EscapedPath}.
@param path [String] the path as it appears in git output
@return [String] the unescaped path
Source
# File lib/git/repository/diffing.rb, line 600 def validate_pathspec_types(pathspecs, arg_name) return if pathspecs.all? { |p| p.is_a?(String) || p.is_a?(Pathname) } raise ArgumentError, "Invalid #{arg_name}: must be a String, Pathname, or Array of Strings/Pathnames" end
Raises an error if any element of ‘pathspecs` is not a `String` or `Pathname`
@param pathspecs [Array] the path elements to validate
@param arg_name [String] the argument name used in error messages
@return [void]
@raise [ArgumentError] if any element is not a ‘String` or `Pathname`