Class: QB::Repo::Git

Inherits:
NRSER::Meta::Props::Base
  • Object
show all
Defined in:
lib/qb/repo/git.rb

Overview

Encapsulate information about a Git repository and expose useful operations as instance methods.

The main entry point is Git.from_path, which creates a

Defined Under Namespace

Classes: User

Constant Summary collapse

GITHUB_SSH_URL_RE =
/^git@github\.com\:(?<owner>.*)\/(?<name>.*)\.git$/
GITHUB_HTTPS_URL_RE =
/^https:\/\/github\.com\/(?<owner>.*)\/(?<name>.*)\.git$/

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.from_path(path, use_github_api: false) ⇒ QB::Repo::Git?

Instantiate a Package::Git resource for whatever Git repo path is in, or return nil if path is not in a Git repo.

Parameters:

  • path (String, Pathname)

    A path that may be in the Git repo.

  • use_github_api: (Boolean) (defaults to: false)

    When true will will contact the GitHub API for information to populate the #github property for repos that have a GitHub origin URL.

    Otherwise we will just assume GitHub repos are private since it's the safe guess, resulting in a #github value of {private: true}.

Returns:

  • (QB::Repo::Git)

    If path is in a Git repo.

  • (nil)

    If path is not in a Git repo.

Raises:

  • (QB::FSStateError)
    • If we can't find any existing directory to look in based on input_path.


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/qb/repo/git.rb', line 147

def self.from_path path, use_github_api: false
  raw_input_path = path
  
  # Create a Pathname from the input
  input_path = Pathname.new raw_input_path
  
  # input_path may point to a file, or something that doesn't even exist.
  # We want to ascend until we find an existing directory that we can cd into.
  closest_dir = input_path.ascend.find {|p| p.directory?}
  
  # Make sure we found something
  if closest_dir.nil?
    raise QB::FSStateError,
          "Unable to find any existing directory from path " +
          "#{ raw_input_path.inspect }"
  end
  
  # Change into the directory to make shell life easier
  Dir.chdir closest_dir do
    root_result = Cmds.capture "git rev-parse --show-toplevel"
    
    unless root_result.ok?
      return nil
    end
    
    root = Pathname.new root_result.out.chomp
    
    user = User.new **NRSER.map_values(User.props.keys) {|key, _|
      begin
        Cmds.chomp! "git config user.#{ key }"
      rescue
      end
    }
    
    is_clean = Cmds.chomp!('git status --porcelain 2>/dev/null').empty?
    
    rev_parse = Cmds.capture 'git rev-parse HEAD'
    
    head = if rev_parse.ok?
      rev_parse.out.chomp
    end
    
    branch_result = Cmds.capture 'git branch --no-color 2> /dev/null'
    
    branch = if branch_result.ok?
      if line = branch_result.out.lines.find {|line| line.start_with? '*'}
        if m = line.match(/\*\s+(\S+)/)
          m[1]
        end
      end
    end
    
    origin = begin
      Cmds.chomp! "git remote get-url origin"
    rescue
    end
    
    owner = nil
    name = nil
    github = nil
    
    if origin && match = (
          GITHUB_SSH_URL_RE.match(origin) ||
          GITHUB_HTTPS_URL_RE.match(origin)
        )
      
      owner = match['owner']
      name = match['name']
      
      if use_github_api
        github = OpenStruct.new
        github.api_url = "https://api.github.com/repos/#{ owner }/#{ name }"
        
        response = Net::HTTP.get_response URI(github.api_url)
        
        if response.is_a? Net::HTTPSuccess
          # parse response body and add everything to github result
          parsed = JSON.parse response.body
          parsed.each {|k, v| github[k] = v}
        else
          # assume it's private if we failed to find it
          github.private = true
        end
        
        github = github.to_h
      end
      
    end
    
    new(
      raw_input_path: raw_input_path,
      input_path: input_path,
      root: root,
      user: user,
      is_clean: is_clean,
      head: head,
      branch: branch,
      origin: origin,
      owner: owner,
      name: name,
      github: github,
    )
    
  end # chdir
  
end

Instance Method Details

#clean?Boolean

Returns false if the repo has any uncommitted changes or untracked files.

Returns:

  • (Boolean)

    false if the repo has any uncommitted changes or untracked files.



328
329
330
# File 'lib/qb/repo/git.rb', line 328

def clean?
  is_clean
end

#from_path!(path, use_github_api: false) ⇒ QB::Repo::Git

Instantiate a Package::Git resource for whatever Git repo path is in, raising an error if it's not in one.

Parameters:

  • path (String, Pathname)

    A path that is in the Git repo.

  • use_github_api: (defaults to: false)

    see #from_path

Returns:

Raises:

  • (QB::FSStateError)
    • If we can't find any existing directory to look in based on input_path.

    • If the directory we do find to look in does not seems to be part of a Git repo.



272
273
274
275
276
277
278
279
280
# File 'lib/qb/repo/git.rb', line 272

def from_path! path, use_github_api: false
  from_path( path, use_github_api: use_github_api ).tap { |git|
    if git.nil?
      raise QB::FSStateError,
            "Path #{ path.inspect } does not appear to be in a " +
            "Git repo."
    end
  }
end

#full_nameObject

Instance Methods



309
310
311
# File 'lib/qb/repo/git.rb', line 309

def full_name
  "#{ owner }/#{ name }" if owner && name
end

#github?Boolean

Returns:

  • (Boolean)


317
318
319
# File 'lib/qb/repo/git.rb', line 317

def github?
  !github.nil?
end

#head_shortObject



313
314
315
# File 'lib/qb/repo/git.rb', line 313

def head_short
  head[0...7] if head
end