Class: QB::Repo::Git

Inherits:
QB::Repo show all
Defined in:
lib/qb/repo/git.rb

Direct Known Subclasses

GitHub

Defined Under Namespace

Classes: GitHub, User

Instance Attribute Summary

Attributes inherited from QB::Repo

#name, #ref_path, #root_path

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from QB::Repo

from_path!

Methods inherited from Util::Resource

#initialize

Constructor Details

This class inherits a constructor from QB::Util::Resource

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.


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
# File 'lib/qb/repo/git.rb', line 92

def self.from_path path, use_github_api: false
  ref_path = path
  
  # Create a Pathname from the input
  input_path = Pathname.new ref_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 " +
          "#{ ref_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_path = Pathname.new root_result.out.chomp
    
    user = User.new **User.props.keys.assoc_to { |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 && QB::Repo::Git::GitHub.url?( origin )
      
      repo_id = QB::Repo::Git::GitHub.parse_url origin
      
      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
      
      return QB::Repo::Git::GitHub.new(
        ref_path: ref_path,
        input_path: input_path,
        root_path: root_path,
        user: user,
        is_clean: is_clean,
        head: head,
        branch: branch,
        origin: origin,
        repo_id: repo_id,
        owner: repo_id.owner,
        name: repo_id.name,
        github: github,
      )
      
    end
    
    new(
      ref_path: ref_path,
      input_path: input_path,
      root_path: root_path,
      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?(ignore: nil) ⇒ 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.



288
289
290
291
292
293
294
295
296
297
# File 'lib/qb/repo/git.rb', line 288

def clean? ignore: nil
  if ignore
    ignore = [*ignore]
    status.reject { |path, mode|
      ignore.any? { |pattern| File.fnmatch? pattern, path }
    }.empty?
  else
    status.empty?
  end
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.



228
229
230
231
232
233
234
235
236
# File 'lib/qb/repo/git.rb', line 228

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



242
243
244
# File 'lib/qb/repo/git.rb', line 242

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

#github?false

Always returns false, where QB::Repo::Git::GitHub#github? always returns true.

Use from_path to construct instances so you end up with the right class.

Returns:

  • (false)


259
260
261
262
# File 'lib/qb/repo/git.rb', line 259

def github?
  # origin && QB::Repo::Git::GitHub.url?( origin )
  false
end

#head_shortObject



246
247
248
# File 'lib/qb/repo/git.rb', line 246

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

#libObject



265
266
267
268
269
# File 'lib/qb/repo/git.rb', line 265

def lib
  lazy_var :@lib do
    ::Git.open root_path
  end
end

#statusObject

Reading Repo State



275
276
277
278
279
280
281
282
# File 'lib/qb/repo/git.rb', line 275

def status
  Cmds.new( 'git status --porcelain', chdir: root_path ).
    out!.lines.map( &:chomp ).map { |line|
      m = /\A\s*(?<mode>\S+)\s*(?<path>.*)\z/.match line
      
      [m['path'], m['mode']]
    }.to_h
end

#tagsObject



300
301
302
# File 'lib/qb/repo/git.rb', line 300

def tags
  lib.tags.map &:name
end