lib/rosette/core/commands/git/diff_command.rb
# encoding: UTF-8
module Rosette
module Core
module Commands
# Detects phrase changes between two git refs or commit ids. Identifies phrases
# that have been added, removed, or changed. The refs used in the comparison
# are referred to as a "head" and a "diff point". In Rosette/git parlance, a "head"
# generally means a git ref that is currently being modified, i.e. a branch. A
# "diff point" is some common ref to compare "head" to, often the repository's
# "master" branch.
#
# Perhaps an easier way to visualize these concepts is through a git command-line
# example. Imagine you're developing a feature to add widget support to your app.
# You start work by switching to a new git branch (i.e. +git+ +checkout+ +-b+ +add_widgets+).
# After adding a few files, changing some others, and deleting old code, you want
# to see a complete set of all your changes. To do this, you run +git+ +diff+. At
# this point, your "head" is your branch "add_widgets" and your "diff point" is
# "master" (or whichever branch you were on when you ran +git+ +checkout+). You
# could also have run +git+ +diff+ +master+ or +git+ +diff+ +master+ +HEAD+ to
# get the same result. You can think of {DiffCommand} like a +git+ +diff+ command
# of the form +git+ +diff+ +<diff+ +point>+ +<head>+.
#
# @see Rosette::Core::DiffFinder
#
# @example
# cmd = DiffCommand.new(configuration)
# .set_repo_name('my_repo')
# .set_head_ref('my_branch')
# .set_diff_point_ref('master')
# .set_paths(['config/locales/en.yml'])
#
# cmd.execute
# # {
# # added: [
# # { key: 'Foo', ... },
# # ],
# # removed: [
# # { key: 'I got deleted', ... }
# # ],
# # modified: [
# # { key: 'New value', old_key: 'Old value', ... }
# # ]
# # }
#
# @!attribute [r] head_commit_str
# @return [String] the raw head ref or commit id as set via {#set_head_ref}
# or {#set_head_commit_id}.
#
# @!attribute [r] diff_point_commit_str
# @return [String] the raw diff point ref or commit id as set via
# {#set_diff_point_ref} or {#set_diff_point_commit_id}.
#
# @!attribute [r] paths
# @return [Array] the list of paths to include in the diff. The diff will not
# contain phrases that were added/removed/modified in files that are not
# contained within this list.
class DiffCommand < DiffBaseCommand
attr_reader :head_commit_str, :diff_point_commit_str, :paths
include WithRepoName
validate :head_commit_str, type: :commit
validate :diff_point_commit_str, type: :commit
# Set the head commit id. Calling this method after {#set_head_ref} will
# overwrite the head ref value. In other words, it's generally a good idea
# to only call one of {#set_head_commit_id} or {#set_head_ref} but not both.
#
# @param [String] head_commit_id The head commit id.
# @return [self]
def set_head_commit_id(head_commit_id)
@head_commit_str = head_commit_id
self
end
# Set the head ref (i.e. a branch name). Calling this method after
# {#set_head_commit_id} will overwrite the head commit id value. In other
# words, it's generally a good idea to only call one of {#set_head_commit_id}
# or {#set_head_ref} but not both.
#
# @param [String] head_ref The head ref.
# @return [self]
def set_head_ref(head_ref)
@head_commit_str = head_ref
self
end
# Set the diff point commit id. Calling this method after {#set_head_ref}
# will overwrite the head ref value. In other words, it's generally a good
# idea to only call one of {#set_head_commit_id} or {#set_head_ref} but
# not both.
#
# @param [String] diff_point_commit_id The diff point commit id.
# @return [self]
def set_diff_point_commit_id(diff_point_commit_id)
@diff_point_commit_str = diff_point_commit_id
self
end
# Set the diff point ref (i.e. master). Calling this method after
# {#set_diff_point_commit_id} will overwrite the diff point commit id value.
# In other words, it's generally a good idea to only call one of
# {#set_diff_point_commit_id} or {#set_diff_point_ref} but not both.
#
# @param [String] diff_point_ref The diff point ref.
# @return [self]
def set_diff_point_ref(diff_point_ref)
@diff_point_commit_str = diff_point_ref
self
end
# Set the list of paths to consider when computing the diff. Any paths not
# in this list will not appear in the diff.
#
# @param [Array] paths The list of paths.
# @return [self]
def set_paths(paths)
@paths = paths
self
end
# Resolves the given head git ref or commit id and returns the corresponding
# commit id. If {#set_head_ref} was used to set a git ref (i.e. branch name),
# this method looks up and returns the corresponding commit id. If
# {#set_head_commit_id} was used to set a commit id, then that commit id is
# validated and returned.
#
# @return [String] The commit id set via either {#set_head_ref} or
# {#set_head_commit_id}.
#
# @raise [Java::OrgEclipseJgitErrors::MissingObjectException, Java::JavaLang::IllegalArgumentException]
# If either the commit id doesn't exist or the ref can't be found.
def head_commit_id
@head_commit_id ||= get_repo(repo_name)
.repo.get_rev_commit(head_commit_str)
.getId
.name
end
# Resolves the given diff point git ref or commit id and returns the corresponding
# commit id. If {#set_diff_point_ref} was used to set a git ref (i.e. branch name),
# this method looks up and returns the corresponding commit id. If
# {#set_diff_point_commit_id} was used to set a commit id, then that commit id is
# validated and returned.
#
# @return [String] The commit id set via either {#set_diff_point_ref} or
# {#set_diff_point_commit_id}.
#
# @raise [Java::OrgEclipseJgitErrors::MissingObjectException, Java::JavaLang::IllegalArgumentException]
# If either the commit id doesn't exist or the ref can't be found.
def diff_point_commit_id
@diff_point_commit_id ||= get_repo(repo_name)
.repo.get_rev_commit(diff_point_commit_str)
.getId
.name
end
# Computes the diff.
# @return [Hash] a hash of differences, grouped by added/removed/modified keys. Each
# value is an array of phrases. For added and removed phrases, the phrase hashes
# will contain normal phrase attributes. For changed phrases, the phrase hashes
# will contain normal phrase attributes plus a special +old_key+ attribute that
# contains the previous key of the phrase. See the example above for a visual
# representation of the diff hash.
def execute
diff_between(head_commit_id, Array(parent_commit_id))
end
protected
def diff_point_exists?
commit_exists?(repo_name, diff_point_commit_id)
end
def parent_commit_id
parent_commit_id = if strict? || diff_point_exists?
diff_point_commit_id
else
get_closest_processed_parent(diff_point_commit_id)
end
end
end
end
end
end