Module: Git::Multi

Defined in:
lib/git/multi.rb,
lib/git/multi/config.rb,
lib/git/multi/report.rb,
lib/git/multi/gemspec.rb,
lib/git/multi/version.rb,
lib/git/multi/commands.rb

Defined Under Namespace

Modules: Commands, Nike, Report Classes: Error

Constant Summary collapse

HOME =
Dir.home
DEFAULT_WORKAREA =
File.join(HOME, 'Workarea')
WORKAREA =
global_option('git.multi.workarea', DEFAULT_WORKAREA)
DEFAULT_TOKEN =

same as Octokit

env_var('OCTOKIT_ACCESS_TOKEN')
TOKEN =
global_option('github.token', DEFAULT_TOKEN)
GIT_MULTI_DIR =
File.join(HOME, '.git', 'multi')
GITHUB_CACHE =
File.join(GIT_MULTI_DIR, 'repositories.byte')
USERS =
global_list('git.multi.users')
ORGANIZATIONS =
global_list('git.multi.organizations')
SUPERPROJECTS =
global_list('git.multi.superprojects')
MULTI_REPOS =
(USERS + ORGANIZATIONS + SUPERPROJECTS)
EXCLUDED_REPOS =
global_options('git.multi.exclude')
MAN_PAGE =
File.expand_path('../../man/git-multi.1', __dir__)
HTML_PAGE =
File.expand_path('../../man/git-multi.html', __dir__)
PIM =
<<~"PIM".freeze

  The only required setting for \033[1mgit multi\33[0m is:

  \tgit config --global --add \033[1mgithub.token\033[0m <your_github_oauth_token>

  The following settings for \033[1mgit multi\33[0m are
  optional, and take 0, 1 or more values:

  \tgit config --global --add \033[1mgit.multi.users\033[0m <list_of_github_users>
  \tgit config --global --add \033[1mgit.multi.organizations\033[0m <list_of_github_orgs>

  Unless your top-level workarea is `${HOME}/Workarea` you should also set:

  \tgit config --global --add \033[1mgit.multi.workarea\033[0m <your_root_workarea>

  You can specify a list of user or organization repositories to ignore:

  \tgit config --global --add \033[1mgit.multi.exclude\033[0m <org_name>/<repo_name>
  \tgit config --global --add \033[1mgit.multi.exclude\033[0m <user_name>/<repo_name>

  (can be used multiple times to exclude multiple user or organization repositories)

  Thanks for using \033[1mgit multi\033[0m ... the ultimate multi-repo utility for git!

PIM
NAME =
'git-multi'.freeze
VERSION =
'8.0.1'.freeze

Class Method Summary collapse

Class Method Details

.all_repositories_for(multi_repo = nil) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/git/multi.rb', line 224

def all_repositories_for(multi_repo = nil)
  case (owner = superproject = full_names = multi_repo)
  when nil
    repositories # all of them
  when Array
    repositories.find_all { |repository|
      full_names.include?(repository.full_name)
    }
  when *USERS, *ORGANIZATIONS
    repositories.find_all { |repository|
      repository.owner. == owner
    }
  when *SUPERPROJECTS
    all_repositories_for(full_names_for(superproject))
  else
    raise ArgumentError, multi_repo
  end
end

.archived_repositories_for(multi_repo = nil) ⇒ Object

lists of repositories with a given state



251
252
253
# File 'lib/git/multi.rb', line 251

def archived_repositories_for(multi_repo = nil)
  all_repositories_for(multi_repo).find_all(&:archived)
end

.cloned_repositories_for(multi_repo = nil) ⇒ Object



305
306
307
308
309
# File 'lib/git/multi.rb', line 305

def cloned_repositories_for(multi_repo = nil)
  repositories_for(multi_repo).find_all { |repo|
    File.directory? repo.local_path
  }
end

.env_var(name, default = nil) ⇒ Object



24
25
26
# File 'lib/git/multi/config.rb', line 24

def env_var(name, default = nil)
  ENV.fetch(name, default)
end

.excess_repositories_for(multi_repo = nil) ⇒ Object

derived lists of repositories



271
272
273
274
275
276
# File 'lib/git/multi.rb', line 271

def excess_repositories_for(multi_repo = nil)
  repository_full_names = repositories_for(multi_repo).map(&:full_name)
  local_repositories_for(multi_repo).reject { |repo|
    repository_full_names.include? repo.full_name
  }
end

.excluded_repositories_for(multi_repo = nil) ⇒ Object



263
264
265
# File 'lib/git/multi.rb', line 263

def excluded_repositories_for(multi_repo = nil)
  all_repositories_for(multi_repo).find_all(&:excluded)
end

.forked_repositories_for(multi_repo = nil) ⇒ Object



255
256
257
# File 'lib/git/multi.rb', line 255

def forked_repositories_for(multi_repo = nil)
  all_repositories_for(multi_repo).find_all(&:fork)
end

.full_names_for(superproject) ⇒ Object

lists of repos for a given multi-repo



220
221
222
# File 'lib/git/multi.rb', line 220

def full_names_for(superproject)
  global_options("superproject.#{superproject}.repo")
end

.github_repositoriesObject



106
107
108
109
110
111
# File 'lib/git/multi.rb', line 106

def github_repositories
  @github_repositories ||= (
    USERS.map { |user| @github_user_repositories[user] } +
    ORGANIZATIONS.map { |org| @github_org_repositories[org] }
  ).flatten
end

.github_repositories_for(owner = nil) ⇒ Object



113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/git/multi.rb', line 113

def github_repositories_for(owner = nil)
  case owner
  when nil
    github_repositories # all of them
  when *USERS
    @github_user_repositories[owner]
  when *ORGANIZATIONS
    @github_org_repositories[owner]
  else
    raise "Unknown owner: #{owner}"
  end
end

.global_list(name, default = nil) ⇒ Object



20
21
22
# File 'lib/git/multi/config.rb', line 20

def global_list(name, default = nil)
  global_option(name, default).split(',').map(&:strip).map(&:freeze)
end

.global_option(name, default = nil) ⇒ Object



15
16
17
18
# File 'lib/git/multi/config.rb', line 15

def global_option(name, default = nil)
  value = `git config --global --get #{name}`.chomp.freeze
  value.empty? && default ? default : value
end

.global_options(name) ⇒ Object



10
11
12
13
# File 'lib/git/multi/config.rb', line 10

def global_options(name)
  list = `git config --global --get-all #{name}`
  list.split($RS).map(&:strip).map(&:freeze)
end

.local_option(path, name, default = nil) ⇒ Object



5
6
7
8
# File 'lib/git/multi/config.rb', line 5

def local_option(path, name, default = nil)
  value = `git -C #{path} config --local --get #{name}`.chomp.freeze
  value.empty? && default ? default : value
end

.local_repositoriesObject



74
75
76
77
78
79
# File 'lib/git/multi.rb', line 74

def local_repositories
  @local_repositories ||= (
    USERS.map { |user| @local_user_repositories[user] } +
    ORGANIZATIONS.map { |org| @local_org_repositories[org] }
  ).flatten
end

.local_repositories_for(owner = nil) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/git/multi.rb', line 81

def local_repositories_for(owner = nil)
  case owner
  when nil
    local_repositories # all of them
  when *USERS
    @local_user_repositories[owner]
  when *ORGANIZATIONS
    @local_org_repositories[owner]
  else
    raise "Unknown owner: #{owner}"
  end
end

.missing_repositories_for(multi_repo = nil) ⇒ Object



299
300
301
302
303
# File 'lib/git/multi.rb', line 299

def missing_repositories_for(multi_repo = nil)
  repositories_for(multi_repo).find_all { |repo|
    !File.directory? repo.local_path
  }
end

.private_repositories_for(multi_repo = nil) ⇒ Object



259
260
261
# File 'lib/git/multi.rb', line 259

def private_repositories_for(multi_repo = nil)
  all_repositories_for(multi_repo).find_all(&:private)
end

.refresh_repositoriesObject

manage the local repository cache



130
131
132
133
134
135
136
137
# File 'lib/git/multi.rb', line 130

def refresh_repositories
  # ensure `~/.git/multi` directory exists
  FileUtils.mkdir_p(GIT_MULTI_DIR)

  File.open(GITHUB_CACHE, 'wb') do |file|
    Marshal.dump(github_repositories, file)
  end
end

.repositoriesObject



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
# File 'lib/git/multi.rb', line 188

def repositories
  if File.size?(GITHUB_CACHE)
    # rubocop:disable Security/MarshalLoad
    @repositories ||= Marshal.load(File.read(GITHUB_CACHE)).tap do |repos|
      repos.each_with_index do |repo, index|
        # ensure 'repo' has handle on an Octokit client
        repo.client = Git::Hub.send(:client)
        # adorn 'repo', which is a Sawyer::Resource
        repo.parent_dir = Pathname.new(File.join(WORKAREA, repo.owner.))
        repo.local_path = Pathname.new(File.join(WORKAREA, repo.full_name))
        repo.fractional_index = "#{index + 1}/#{repos.count}"
        # git multi will "hard ignore" all excluded repos
        repo.excluded = EXCLUDED_REPOS.include?(repo.full_name)
        # fix 'repo' => https://github.com/octokit/octokit.rb/issues/727
        repo.compliant_ssh_url = "ssh://#{repo.ssh_url.split(':', 2).join('/')}"
        # remove optional '.git' suffix from '[email protected]:pvdb/git-multi.git'
        repo.abbreviated_ssh_url = repo.ssh_url.chomp('.git')
        # extend 'repo' with 'just do it' capabilities
        repo.extend Nike
      end
    end
    # rubocop:enable Security/MarshalLoad
  else
    refresh_repositories
    repositories # retry
  end
end

.repositories_for(multi_repo = nil) ⇒ Object



243
244
245
# File 'lib/git/multi.rb', line 243

def repositories_for(multi_repo = nil)
  all_repositories_for(multi_repo).delete_if(&:excluded)
end

.spurious_repositories_for(multi_repo = nil) ⇒ Object



285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/git/multi.rb', line 285

def spurious_repositories_for(multi_repo = nil)
  cloned_repositories_for(multi_repo).find_all { |repo|
    origin_url = local_option(repo.local_path, 'remote.origin.url')

    ![
      repo.clone_url,
      repo.ssh_url,
      repo.compliant_ssh_url,
      repo.abbreviated_ssh_url,
      repo.git_url,
    ].include? origin_url
  }
end

.stale_repositories_for(multi_repo = nil) ⇒ Object



278
279
280
281
282
283
# File 'lib/git/multi.rb', line 278

def stale_repositories_for(multi_repo = nil)
  repository_full_names = github_repositories_for(multi_repo).map(&:full_name)
  repositories_for(multi_repo).reject { |repo|
    repository_full_names.include? repo.full_name
  }
end

.valid?(multi_repo) ⇒ Boolean

multi-repo support

Returns:

  • (Boolean)


58
59
60
# File 'lib/git/multi.rb', line 58

def valid?(multi_repo)
  MULTI_REPOS.include? multi_repo
end

.versionObject



6
7
8
# File 'lib/git/multi/version.rb', line 6

def self.version
  "#{NAME} v#{VERSION}"
end