Class: RailsWayback::Git
- Inherits:
-
Object
- Object
- RailsWayback::Git
- Defined in:
- lib/rails_wayback/git.rb
Overview
Thin wrapper around shell git for the host application repository.
We shell out to git instead of pulling in a dependency to keep the gem light. All commands are scoped to the host app root and return plain Ruby data structures.
NOTE: Maybe we should need to use a git library instead of shelling out to git. Only if this functionality is not enough for the gem on the future.
Defined Under Namespace
Classes: Commit, ExecutableNotFoundError, GitError, Reference
Constant Summary collapse
- REFERENCE_PREFIXES =
{ "refs/heads/" => :branch, "refs/remotes/" => :remote, "refs/tags/" => :tag }.freeze
- REFERENCE_KIND_ORDER =
{ branch: 0, remote: 1, tag: 2 }.freeze
Instance Attribute Summary collapse
-
#root ⇒ Object
readonly
Returns the value of attribute root.
Instance Method Summary collapse
- #branches ⇒ Object
-
#branches_containing(sha) ⇒ Object
Returns the local branch names that contain
shain their history. - #checkout_ref(ref, into:) ⇒ Object
- #commits(branch, limit: RailsWayback.configuration.max_commits) ⇒ Object
- #current_branch ⇒ Object
- #current_commit ⇒ Object
-
#diff_paths(ref, paths: []) ⇒ Object
Returns the list of files that differ between
refand the current working tree (unstaged changes included). -
#initialize(root: nil) ⇒ Git
constructor
A new instance of Git.
-
#reference(selector, patterns: RailsWayback.configuration.trusted_ref_patterns) ⇒ Object
Resolves either a canonical full ref name (preferred) or a legacy short branch name, but only from the configured trusted discovery set.
-
#references(patterns: RailsWayback.configuration.trusted_ref_patterns) ⇒ Object
Lists refs that are already present in the local repository and match the same trust patterns used to authorize historical rendering.
- #refs_containing(sha) ⇒ Object
- #repository? ⇒ Boolean
-
#resolve_branch_for(sha) ⇒ Object
Same as
branches_containing, but picks the most contextual one for display in the bar when the developer did NOT provide an explicit branch alongside the sha: * if the current branch contains the sha, prefer it (keeps you in your own context — the sha is on your history line), * otherwise pick the first non-current local branch that contains it (typical case of traveling AWAY from your branch), * returns nil if no local branch contains the sha (detached ref). - #resolve_ref(ref) ⇒ Object
- #show(ref, path) ⇒ Object
- #tree?(ref, path) ⇒ Boolean
Constructor Details
#initialize(root: nil) ⇒ Git
Returns a new instance of Git.
28 29 30 |
# File 'lib/rails_wayback/git.rb', line 28 def initialize(root: nil) @root = Pathname.new(root || RailsWayback.configuration.app_root_path) end |
Instance Attribute Details
#root ⇒ Object (readonly)
Returns the value of attribute root.
188 189 190 |
# File 'lib/rails_wayback/git.rb', line 188 def root @root end |
Instance Method Details
#branches ⇒ Object
46 47 48 49 |
# File 'lib/rails_wayback/git.rb', line 46 def branches output = run("for-each-ref", "--format=%(refname:short)", "refs/heads/") output.split("\n").map(&:strip).reject(&:empty?) end |
#branches_containing(sha) ⇒ Object
Returns the local branch names that contain sha in their history.
Used by the bar to reflect the branch you are RENDERING with when
you travel, instead of the branch that is checked out on disk
(which never moves — the gem never touches your working tree).
127 128 129 130 131 132 |
# File 'lib/rails_wayback/git.rb', line 127 def branches_containing(sha) output = run("branch", "--contains", sha, "--format=%(refname:short)") output.split("\n").map(&:strip).reject(&:empty?) rescue GitError [] end |
#checkout_ref(ref, into:) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/rails_wayback/git.rb', line 175 def checkout_ref(ref, into:) target = Pathname.new(into) FileUtils.mkdir_p(target) # `git --work-tree` + `checkout` writes the ref's contents into a # detached directory without touching the developer's HEAD or index. env = { "GIT_INDEX_FILE" => target.join(".rails_wayback_index").to_s } run_with_env(env, "--work-tree=#{target}", "read-tree", ref) run_with_env(env, "--work-tree=#{target}", "checkout-index", "--all", "--force") target ensure FileUtils.rm_f(target.join(".rails_wayback_index")) if target end |
#commits(branch, limit: RailsWayback.configuration.max_commits) ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/rails_wayback/git.rb', line 89 def commits(branch, limit: RailsWayback.configuration.max_commits) output = run( "log", branch, "--max-count=#{limit.to_i}", "--pretty=format:%H%x1f%h%x1f%s%x1f%an%x1f%ad", "--date=iso-strict" ) output.split("\n").reject(&:empty?).map do |line| sha, short, subject, , date = line.split("\x1f", 5) Commit.new(sha: sha, short_sha: short, subject: subject, author: , date: date) end end |
#current_branch ⇒ Object
38 39 40 |
# File 'lib/rails_wayback/git.rb', line 38 def current_branch run("rev-parse", "--abbrev-ref", "HEAD").strip end |
#current_commit ⇒ Object
42 43 44 |
# File 'lib/rails_wayback/git.rb', line 42 def current_commit run("rev-parse", "HEAD").strip end |
#diff_paths(ref, paths: []) ⇒ Object
Returns the list of files that differ between ref and the current
working tree (unstaged changes included). Optionally scoped to a set
of pathspecs so we only report files inside the paths the gem
actually swaps for rendering. Never raises: on git failure we log
and return an empty list so a missing/broken ref never breaks the
bar.
163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/rails_wayback/git.rb', line 163 def diff_paths(ref, paths: []) args = ["diff", "--name-only", ref] args += ["--", *paths] unless paths.empty? output = run(*args) output.split("\n").map(&:strip).reject(&:empty?) rescue GitError => e if defined?(Rails) && Rails.respond_to?(:logger) Rails.logger.warn("[rails-wayback] diff_paths(#{ref.inspect}) failed: #{e.}") end [] end |
#reference(selector, patterns: RailsWayback.configuration.trusted_ref_patterns) ⇒ Object
Resolves either a canonical full ref name (preferred) or a legacy short branch name, but only from the configured trusted discovery set.
75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/rails_wayback/git.rb', line 75 def reference(selector, patterns: RailsWayback.configuration.trusted_ref_patterns) available = references(patterns: patterns) input = selector.to_s exact = available.find { |candidate| candidate.full_name == input } return exact if exact compatible = available.find do |candidate| candidate.name == input || candidate.label == input end return compatible if compatible raise GitError, "Git ref #{input.inspect} is not trusted or is unavailable locally" end |
#references(patterns: RailsWayback.configuration.trusted_ref_patterns) ⇒ Object
Lists refs that are already present in the local repository and match the same trust patterns used to authorize historical rendering. This never fetches or mutates Git state.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/rails_wayback/git.rb', line 54 def references(patterns: RailsWayback.configuration.trusted_ref_patterns) output = run( "for-each-ref", "--format=%(refname)%09%(symref)", "refs/heads/", "refs/remotes/", "refs/tags/" ) discovered = output.split("\n").filter_map do |line| full_name, symbolic_target = line.split("\t", 2) next unless symbolic_target.to_s.empty? next unless trusted_reference?(full_name, patterns) build_reference(full_name) end discovered.sort_by { |reference| [REFERENCE_KIND_ORDER.fetch(reference.kind), reference.label] } end |
#refs_containing(sha) ⇒ Object
134 135 136 137 |
# File 'lib/rails_wayback/git.rb', line 134 def refs_containing(sha) output = run("for-each-ref", "--contains=#{sha}", "--format=%(refname)") output.split("\n").map(&:strip).reject(&:empty?) end |
#repository? ⇒ Boolean
32 33 34 35 36 |
# File 'lib/rails_wayback/git.rb', line 32 def repository? run("rev-parse", "--is-inside-work-tree").strip == "true" rescue GitError false end |
#resolve_branch_for(sha) ⇒ Object
Same as branches_containing, but picks the most contextual one
for display in the bar when the developer did NOT provide an
explicit branch alongside the sha:
- if the current branch contains the sha, prefer it (keeps you in your own context — the sha is on your history line),
- otherwise pick the first non-current local branch that contains it (typical case of traveling AWAY from your branch),
- returns nil if no local branch contains the sha (detached ref).
147 148 149 150 151 152 153 154 155 |
# File 'lib/rails_wayback/git.rb', line 147 def resolve_branch_for(sha) candidates = branches_containing(sha) return nil if candidates.empty? here = current_branch return here if candidates.include?(here) candidates.first end |
#resolve_ref(ref) ⇒ Object
103 104 105 106 107 108 109 |
# File 'lib/rails_wayback/git.rb', line 103 def resolve_ref(ref) run("rev-parse", "--verify", "#{ref}^{commit}").strip rescue ExecutableNotFoundError raise rescue GitError raise RefNotFoundError, "Unknown git ref: #{ref.inspect}" end |
#show(ref, path) ⇒ Object
111 112 113 |
# File 'lib/rails_wayback/git.rb', line 111 def show(ref, path) run("show", "#{ref}:#{path}") end |
#tree?(ref, path) ⇒ Boolean
115 116 117 118 119 120 121 |
# File 'lib/rails_wayback/git.rb', line 115 def tree?(ref, path) run("cat-file", "-t", "#{ref}:#{path}").strip == "tree" rescue ExecutableNotFoundError raise rescue GitError false end |