Module: Fastlane::Helper::GitHelper

Defined in:
lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb

Overview

Helper methods to execute git-related operations

Constant Summary collapse

DEFAULT_GIT_BRANCH =

Fallback default branch of the client repository.

'trunk'.freeze

Class Method Summary collapse

Class Method Details

.branch_exists?(branch_name) ⇒ Bool

Checks if a branch exists locally.

Parameters:

  • branch_name (String)

    The name of the branch to check for

Returns:

  • (Bool)

    True if the branch exists in the local working copy, false otherwise.



236
237
238
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 236

def self.branch_exists?(branch_name)
  !Action.sh('git', 'branch', '--list', branch_name).empty?
end

.branch_exists_on_remote?(branch_name:, remote_name: 'origin') ⇒ Boolean

Checks if a branch exists on the repository’s remote.

Parameters:

  • branch_name (String)

    the name of the branch to check.

  • remote_name (String) (defaults to: 'origin')

    the name of the remote repository (default is ‘origin’).

Returns:

  • (Boolean)

    true if the branch exists on remote, false otherwise.



247
248
249
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 247

def self.branch_exists_on_remote?(branch_name:, remote_name: 'origin')
  !Action.sh('git', 'ls-remote', '--heads', remote_name, branch_name).empty?
end

.checkout_and_pull(branch) ⇒ Bool

Switch to the given branch and pull its latest commits.

Parameters:

  • branch (String, Hash)

    Name of the branch to pull. If you provide a Hash with a single key=>value pair, it will build the branch name as ‘“#key/#value”`, i.e. `checkout_and_pull(release: version)` is equivalent to `checkout_and_pull(“release/#version”)`.

Returns:

  • (Bool)

    True if it succeeded switching and pulling, false if there was an error during the switch or pull.



61
62
63
64
65
66
67
68
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 61

def self.checkout_and_pull(branch)
  branch = branch.first.join('/') if branch.is_a?(Hash)
  Action.sh('git', 'checkout', branch)
  Action.sh('git', 'pull')
  true
rescue StandardError
  false
end

.commit(message:, files: nil) ⇒ Bool

‘git add` the specified files (if any provided) then commit them using the provided message.

Parameters:

  • message (String)

    The commit message to use

  • files (String|Array<String>) (defaults to: nil)

    A file or array of files to git-add before creating the commit. Use ‘nil` or `[]` if you already added the files in a separate step and don’t wan’t this method to add any new file before commit. Also accepts the special symbol ‘:all` to add all the files (`git commit -a -m …`).

Returns:

  • (Bool)

    True if commit was successful, false if there was an issue (most likely being “nothing to commit”).



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 98

def self.commit(message:, files: nil)
  files = [files] if files.is_a?(String)
  args = []
  if files == :all
    args = ['-a']
  elsif !files.nil? && !files.empty?
    Action.sh('git', 'add', *files)
  end
  begin
    Action.sh('git', 'commit', *args, '-m', message)
    true
  rescue StandardError
    false
  end
end

.create_branch(branch_name, from: nil) ⇒ Object

Create a new branch named ‘branch_name`, cutting it from branch/commit/tag `from`

If the branch with that name already exists, it will instead switch to it and pull new commits.

Parameters:

  • branch_name (String)

    The full name of the new branch to create, e.g “release/1.2”

  • from (String?) (defaults to: nil)

    The branch or tag from which to cut the branch from. If ‘nil`, will cut the new branch from the current commit. Otherwise, will checkout that commit/branch/tag before cutting the branch.



78
79
80
81
82
83
84
85
86
87
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 78

def self.create_branch(branch_name, from: nil)
  if branch_exists?(branch_name)
    UI.message("Branch #{branch_name} already exists. Skipping creation.")
    Action.sh('git', 'checkout', branch_name)
    Action.sh('git', 'pull', 'origin', branch_name)
  else
    Action.sh('git', 'checkout', from) unless from.nil?
    Action.sh('git', 'checkout', '-b', branch_name)
  end
end

.create_tag(version, push: true) ⇒ Object

Creates a tag for the given version, and optionally push it to the remote.

Parameters:

  • version (String)

    The name of the tag to push, e.g. “1.2”

  • push (Bool) (defaults to: true)

    If true (the default), the tag will also be pushed to ‘origin`



136
137
138
139
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 136

def self.create_tag(version, push: true)
  Action.sh('git', 'tag', version)
  Action.sh('git', 'push', 'origin', version) if push
end

.current_git_branchString

Returns the current git branch, or “HEAD” if it’s not checked out to any branch Can NOT be replaced using the environment variables such as ‘GIT_BRANCH` or `BUILDKITE_BRANCH`

‘fastlane` already has a helper action for this called `git_branch`, however it’s modified by CI environment variables. We need to check which branch we are actually on and not the initial branch a CI build is started from, so we are using the ‘git_branch_name_using_HEAD` helper instead.

See docs.fastlane.tools/actions/git_branch/#git_branch

Returns:

  • (String)

    The current git branch, or “HEAD” if it’s not checked out to any branch



225
226
227
228
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 225

def self.current_git_branch
  # We can't use `other_action.git_branch`, because it is modified by environment variables in Buildkite.
  Fastlane::Actions.git_branch_name_using_HEAD
end

.delete_local_branch_if_exists!(branch_name) ⇒ Boolean

Delete a local branch if it exists.

Parameters:

  • branch_name (String)

    The name of the local branch to delete.

Returns:

  • (Boolean)

    true if the branch was deleted, false if not (e.g. no such local branch existed in the first place)



256
257
258
259
260
261
262
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 256

def self.delete_local_branch_if_exists!(branch_name)
  git_repo = Git.open(Dir.pwd)
  return false unless git_repo.is_local_branch?(branch_name)

  git_repo.branch(branch_name).delete
  true
end

.delete_remote_branch_if_exists!(branch_name, remote_name: 'origin') ⇒ Boolean

Delete a remote branch if it exists.

Parameters:

  • branch_name (String)

    The name of the remote branch to delete.

  • remote_name (String) (defaults to: 'origin')

    The name of the remote to delete the branch from. Defaults to ‘origin’

Returns:

  • (Boolean)

    true if the branch was deleted, false if not (e.g. no such local branch existed in the first place)



270
271
272
273
274
275
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 270

def self.delete_remote_branch_if_exists!(branch_name, remote_name: 'origin')
  git_repo = Git.open(Dir.pwd)
  return false unless git_repo.branches.any? { |b| b.remote&.name == remote_name && b.name == branch_name }

  git_repo.push(remote_name, branch_name, delete: true)
end

.delete_tags(tags_to_delete, delete_on_remote: false) ⇒ Object

Delete the mentioned local tags in the local working copy, and optionally delete them on the remote too.

Parameters:

  • tags_to_delete (Array<String>)

    The list of tags to delete

  • delete_on_remote (Bool) (defaults to: false)

    If true, will also delete the tag from the remote. Otherwise, it will only be deleted locally.



165
166
167
168
169
170
171
172
173
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 165

def self.delete_tags(tags_to_delete, delete_on_remote: false)
  g = Git.open(Dir.pwd)
  local_tag_names = g.tags.map(&:name)

  Array(tags_to_delete).each do |tag|
    g.delete_tag(tag) if local_tag_names.include?(tag)
    g.push('origin', ":refs/tags/#{tag}") if delete_on_remote
  end
end

.fetch_all_tagsObject

Fetch all the tags from the remote.



177
178
179
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 177

def self.fetch_all_tags
  Action.sh('git', 'fetch', '--tags')
end

.find_merge_base(ref1, ref2) ⇒ String

Note:

If a reference (e.g. branch name) can’t be found locally, it will try with the same ref prefixed with ‘origin/`

Use ‘git merge-base` to find as good a common ancestors as possible for a merge

Parameters:

  • ref1 (String)

    The first git reference (sha1, ref name…)to find the common ancestor of

  • ref2 (String)

    The second git reference (sha1, ref name…)to find the common ancestor of

Returns:

  • (String)

    The merge-base aka common ancestor for the 2 commits provided



188
189
190
191
192
193
194
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 188

def self.find_merge_base(ref1, ref2)
  git_repo = Git.open(Dir.pwd)
  # Resolve to shas, mostly so that we can support cases with and without `origin/` explicit prefix on branch names
  ref1_sha, ref2_sha = [ref1, ref2].map { |ref| get_commit_sha(ref: ref, prepend_origin_if_needed: true) }

  git_repo.merge_base(ref1_sha, ref2_sha)&.first&.sha
end

.first_existing_ancestor_of(path:) ⇒ Pathname

Travels back the hierarchy of the given path until it finds an existing ancestor, or it reaches the root of the file system.

Parameters:

  • path (String)

    The path to inspect

Returns:

  • (Pathname)

    The first existing ancestor, or ‘path` itself if it exists



37
38
39
40
41
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 37

def self.first_existing_ancestor_of(path:)
  p = Pathname(path).expand_path
  p = p.parent until p.exist? || p.root?
  p
end

.get_commit_sha(ref: 'HEAD', prepend_origin_if_needed: false) ⇒ String

Get the SHA of a given git ref. Typically useful to get the SHA of the current HEAD commit.

Parameters:

  • ref (String) (defaults to: 'HEAD')

    The git ref (commit, branch name, ‘HEAD’, …) to resolve as a SHA

  • prepend_origin_if_needed (Boolean) (defaults to: false)

    If true, will retry the rev-parse by prefixing ‘origin/` to the ref it it failed without it

Returns:

  • (String)

    The commit SHA of the ref



122
123
124
125
126
127
128
129
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 122

def self.get_commit_sha(ref: 'HEAD', prepend_origin_if_needed: false)
  repo = Git.open(Dir.pwd)
  repo.revparse(ref)
rescue Git::FailedError
  raise unless prepend_origin_if_needed

  repo.revparse("origin/#{ref}")
end

.has_git_lfs?Bool

Check if the current directory has git-lfs enabled

Returns:

  • (Bool)

    True if the current directory is a git working copy and has git-lfs enabled.



47
48
49
50
51
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 47

def self.has_git_lfs?
  return false unless is_git_repo?

  !`git config --get-regex lfs`.empty?
end

.is_git_repo?(path: Dir.pwd) ⇒ Bool

Checks if the given path, or current directory if no path is given, is inside a Git repository

Parameters:

  • path (String) (defaults to: Dir.pwd)

    An optional path where to check if a Git repo exists.

Returns:

  • (Bool)

    True if the current directory is the root of a git repo (i.e. a local working copy) or a subdirectory of one.



17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 17

def self.is_git_repo?(path: Dir.pwd)
  # If the path doesn't exist, find its first ancestor.
  path = first_existing_ancestor_of(path: path)
  # Get the path's directory, so we can look in it for the Git folder
  dir = path.directory? ? path : path.dirname

  # Recursively look for the Git folder until it's found or we read the the file system root
  dir = dir.parent until Dir.entries(dir).include?('.git') || dir.root?

  # If we reached the root, we haven't found a repo.
  # (Technically, there could be a repo in the root of the system, but that's a usecase that we don't need to support at this time)
  dir.root? == false
end

.is_ignored?(path:) ⇒ Bool

Checks whether a given path is ignored by Git, relying on Git’s ‘check-ignore` under the hood.

Parameters:

  • path (String)

    The path to check against ‘.gitignore`

Returns:

  • (Bool)

    True if the given path is ignored or outside a Git repository, false otherwise.



282
283
284
285
286
287
288
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 282

def self.is_ignored?(path:)
  return true unless is_git_repo?(path: path)

  Actions.sh('git', 'check-ignore', path) do |status, _, _|
    status.success?
  end
end

.list_local_tags(matching: '*') ⇒ Array<String>

List all the tags in the local working copy, optionally filtering the list using a pattern

Parameters:

  • matching (String) (defaults to: '*')

    The pattern of the tag(s) to match and filter on; use “*” for wildcards. For example, ‘“1.2.*”` will match every tag starting with `“1.2.”`. Defaults to ’*‘ which lists all tags.

Returns:

  • (Array<String>)

    The list of local tags matching the pattern



156
157
158
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 156

def self.list_local_tags(matching: '*')
  Action.sh('git', 'tag', '--list', matching).split("\n")
end

.list_tags_on_current_commitArray<String>

Returns the list of tags that are pointing to the current commit (HEAD)

Returns:

  • (Array<String>)

    List of tags associated with the HEAD commit



145
146
147
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 145

def self.list_tags_on_current_commit
  Action.sh('git', 'tag', '--points-at', 'HEAD').split("\n")
end

.point_to_same_commit?(ref1, ref2) ⇒ Boolean

Checks if two git references point to the same commit.

Parameters:

  • ref1 (String)

    the first git reference to check.

  • ref2 (String)

    the second git reference to check.

Returns:

  • (Boolean)

    true if the two references point to the same commit, false otherwise.



203
204
205
206
207
208
209
210
211
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb', line 203

def self.point_to_same_commit?(ref1, ref2)
  begin
    ref1_sha = get_commit_sha(ref: ref1, prepend_origin_if_needed: true)
    ref2_sha = get_commit_sha(ref: ref2, prepend_origin_if_needed: true)
  rescue StandardError => e
    UI.user_error! "Error fetching commits for #{ref1} and/or #{ref2}: #{e.message}"
  end
  ref1_sha == ref2_sha
end