Class: Geet::Utils::GitClient
- Inherits:
-
Object
- Object
- Geet::Utils::GitClient
- Includes:
- Helpers::OsHelper
- Defined in:
- lib/geet/utils/git_client.rb
Overview
Represents the git program interface; used for performing git operations.
Constant Summary collapse
- ORIGIN_NAME =
'origin'
- UPSTREAM_NAME =
'upstream'
- REMOTE_URL_REGEX =
Simplified, but good enough, pattern.
Relevant matches:
1: protocol + suffix 2: domain 3: domain<>path separator 4: path (repo, project) 5: suffix
%r{ \A (https://|git@) (.+?) ([/:]) (.+/.+?) (\.git)? \Z }x
- UPSTREAM_BRANCH_REGEX =
%r{\A[^/]+/([^/]+)\Z}
- MAIN_BRANCH_CONFIG_ENTRY =
'custom.development-branch'
- CLEAN_TREE_MESSAGE_REGEX =
/^nothing to commit, working tree clean$/
Instance Method Summary collapse
- #add_remote(name, url) ⇒ Object
-
#checkout(branch) ⇒ Object
OPERATION APIS.
-
#cherry(limit) ⇒ Object
Return the commit shas between HEAD and ‘limit`, excluding the already applied commits (which start with `-`).
- #current_branch ⇒ Object
-
#delete_branch(branch) ⇒ Object
Unforced deletion.
-
#fetch ⇒ Object
Performs pruning.
-
#initialize(location: nil) ⇒ GitClient
constructor
A new instance of GitClient.
- #main_branch ⇒ Object
- #owner ⇒ Object
-
#path(upstream: false) ⇒ Object
Example: ‘donaldduck/geet`.
- #provider_domain ⇒ Object
-
#push(upstream_branch: nil) ⇒ Object
upstream_branch: create an upstream branch.
- #rebase ⇒ Object
-
#remote(name: nil) ⇒ Object
Returns the URL of the remote with the given name.
-
#remote_components(name: nil) ⇒ Object
Return the components of the remote, according to REMOTE_URL_REGEX; doesn’t include the full match.
-
#remote_defined?(name) ⇒ Boolean
Doesn’t sanity check for the remote url format; this action is for querying purposes, any any action that needs to work with the remote, uses #remote.
-
#show_description(object) ⇒ Object
Show the description (“<subject>nn<body>”) for the given git object.
-
#upstream_branch ⇒ Object
Not to be confused with ‘upstream` repository!.
-
#upstream_branch_gone? ⇒ Boolean
TODO: May be merged with :upstream_branch, although it would require designing how a gone remote branch is expressed.
- #working_tree_clean? ⇒ Boolean
Methods included from Helpers::OsHelper
#execute_command, #open_file_with_default_application
Constructor Details
#initialize(location: nil) ⇒ GitClient
Returns a new instance of GitClient.
43 44 45 |
# File 'lib/geet/utils/git_client.rb', line 43 def initialize(location: nil) @location = location end |
Instance Method Details
#add_remote(name, url) ⇒ Object
233 234 235 |
# File 'lib/geet/utils/git_client.rb', line 233 def add_remote(name, url) execute_git_command("remote add #{name.shellescape} #{url}") end |
#checkout(branch) ⇒ Object
OPERATION APIS
205 206 207 |
# File 'lib/geet/utils/git_client.rb', line 205 def checkout(branch) execute_git_command("checkout #{branch.shellescape}") end |
#cherry(limit) ⇒ Object
Return the commit shas between HEAD and ‘limit`, excluding the already applied commits (which start with `-`)
54 55 56 57 58 |
# File 'lib/geet/utils/git_client.rb', line 54 def cherry(limit) raw_commits = execute_git_command("cherry #{limit.shellescape}") raw_commits.split("\n").grep(/^\+/).map { |line| line[3..-1] } end |
#current_branch ⇒ Object
60 61 62 63 64 65 66 |
# File 'lib/geet/utils/git_client.rb', line 60 def current_branch branch = execute_git_command("rev-parse --abbrev-ref HEAD") raise "Couldn't find current branch" if branch == 'HEAD' branch end |
#delete_branch(branch) ⇒ Object
Unforced deletion.
211 212 213 |
# File 'lib/geet/utils/git_client.rb', line 211 def delete_branch(branch) execute_git_command("branch --delete #{branch.shellescape}") end |
#fetch ⇒ Object
Performs pruning.
229 230 231 |
# File 'lib/geet/utils/git_client.rb', line 229 def fetch execute_git_command("fetch --prune") end |
#main_branch ⇒ Object
110 111 112 113 114 115 116 117 118 119 |
# File 'lib/geet/utils/git_client.rb', line 110 def main_branch branch_name = execute_git_command("config --get #{MAIN_BRANCH_CONFIG_ENTRY}", allow_error: true) if branch_name.empty? full_branch_name = execute_git_command("rev-parse --abbrev-ref #{ORIGIN_NAME}/HEAD") full_branch_name.split('/').last else branch_name end end |
#owner ⇒ Object
156 157 158 |
# File 'lib/geet/utils/git_client.rb', line 156 def owner path.split('/')[0] end |
#path(upstream: false) ⇒ Object
Example: ‘donaldduck/geet`
150 151 152 153 154 |
# File 'lib/geet/utils/git_client.rb', line 150 def path(upstream: false) remote_name_option = upstream ? {name: UPSTREAM_NAME} : {} remote(**remote_name_option)[REMOTE_URL_REGEX, 4] end |
#provider_domain ⇒ Object
160 161 162 163 164 165 166 167 168 |
# File 'lib/geet/utils/git_client.rb', line 160 def provider_domain # We assume that it's not possible to have origin and upstream on different providers. domain = remote()[REMOTE_URL_REGEX, 2] raise "Can't identify domain in the provider domain string: #{domain}" if domain !~ /\w+\.\w+/ domain end |
#push(upstream_branch: nil) ⇒ Object
upstream_branch: create an upstream branch.
221 222 223 224 225 |
# File 'lib/geet/utils/git_client.rb', line 221 def push(upstream_branch: nil) upstream_branch_option = "-u #{ORIGIN_NAME} #{upstream_branch.shellescape}" if upstream_branch execute_git_command("push #{upstream_branch_option}") end |
#rebase ⇒ Object
215 216 217 |
# File 'lib/geet/utils/git_client.rb', line 215 def rebase execute_git_command("rebase") end |
#remote(name: nil) ⇒ Object
Returns the URL of the remote with the given name. Sanity checks are performed.
The result is in the format ‘[email protected]:donaldduck/geet.git`
options
:name: remote name; if unspecified, the default remote is used.
178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/geet/utils/git_client.rb', line 178 def remote(name: nil) remote_url = execute_git_command("ls-remote --get-url #{name}") if !remote_defined?(name) raise "Remote #{name.inspect} not found!" elsif remote_url !~ REMOTE_URL_REGEX raise "Unexpected remote reference format: #{remote_url.inspect}" end remote_url end |
#remote_components(name: nil) ⇒ Object
Return the components of the remote, according to REMOTE_URL_REGEX; doesn’t include the full match.
144 145 146 |
# File 'lib/geet/utils/git_client.rb', line 144 def remote_components(name: nil) remote.match(REMOTE_URL_REGEX)[1..] end |
#remote_defined?(name) ⇒ Boolean
Doesn’t sanity check for the remote url format; this action is for querying purposes, any any action that needs to work with the remote, uses #remote.
193 194 195 196 197 198 199 |
# File 'lib/geet/utils/git_client.rb', line 193 def remote_defined?(name) remote_url = execute_git_command("ls-remote --get-url #{name}") # If the remote is not defined, `git ls-remote` will return the passed value. # remote_url != name end |
#show_description(object) ⇒ Object
Show the description (“<subject>nn<body>”) for the given git object.
133 134 135 |
# File 'lib/geet/utils/git_client.rb', line 133 def show_description(object) execute_git_command("show --quiet --format='%s\n\n%b' #{object.shellescape}") end |
#upstream_branch ⇒ Object
Not to be confused with ‘upstream` repository!
This API doesn’t reveal if the remote branch is gone.
return: nil, if the upstream branch is not configured.
74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/geet/utils/git_client.rb', line 74 def upstream_branch head_symbolic_ref = execute_git_command("symbolic-ref -q HEAD") raw_upstream_branch = execute_git_command("for-each-ref --format='%(upstream:short)' #{head_symbolic_ref.shellescape}").strip if raw_upstream_branch != '' raw_upstream_branch[UPSTREAM_BRANCH_REGEX, 1] || raise("Unexpected upstream format: #{raw_upstream_branch}") else nil end end |
#upstream_branch_gone? ⇒ Boolean
TODO: May be merged with :upstream_branch, although it would require designing how a gone remote branch is expressed.
Sample command output:
## add_milestone_closing...origin/add_milestone_closing [gone]
M spec/integration/merge_pr_spec.rb
94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/geet/utils/git_client.rb', line 94 def upstream_branch_gone? git_command = "status -b --porcelain" status_output = execute_git_command(git_command) # Simplified branch naming pattern. The exact one (see https://stackoverflow.com/a/3651867) # is not worth implementing. # if status_output =~ %r(^## .+\.\.\..+?( \[gone\])?$) !!$LAST_MATCH_INFO[1] else raise "Unexpected git command #{git_command.inspect} output: #{status_output}" end end |
#working_tree_clean? ⇒ Boolean
121 122 123 124 125 |
# File 'lib/geet/utils/git_client.rb', line 121 def working_tree_clean? = execute_git_command("status") !!( =~ CLEAN_TREE_MESSAGE_REGEX) end |