Class: GitReview::Local

Inherits:
Object
  • Object
show all
Includes:
Internals
Defined in:
lib/git-review/local.rb

Overview

The local repository is where the git-review command is being called by default. It is (supposedly) able to handle systems other than Github. TODO: remove Github-dependency

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeLocal

Returns a new instance of Local.



18
19
20
21
22
23
24
25
26
# File 'lib/git-review/local.rb', line 18

def initialize
  # find root git directory if currently in subdirectory
  if git_call('rev-parse --show-toplevel').strip.empty?
    raise ::GitReview::InvalidGitRepositoryError
  else
    add_pull_refspec
    load_config
  end
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



10
11
12
# File 'lib/git-review/local.rb', line 10

def config
  @config
end

Class Method Details

.instanceObject

acts like a singleton class but it’s actually not use ::GitReview::Local.instance everywhere except in tests



14
15
16
# File 'lib/git-review/local.rb', line 14

def self.instance
  @instance ||= new
end

Instance Method Details

#add_pull_refspecObject

add remote.origin.fetch to check out pull request locally see https://help.github.com/articles/checking-out-pull-requests-locally



198
199
200
201
202
# File 'lib/git-review/local.rb', line 198

def add_pull_refspec
  refspec = '+refs/pull/*/head:refs/remotes/origin/pr/*'
  fetch_config = "config --local --add remote.origin.fetch #{refspec}"
  git_call(fetch_config, false) unless config_list.include?(refspec)
end

#all_branchesArray<String>

Returns all existing branches.

Returns:

  • (Array<String>)

    all existing branches



29
30
31
# File 'lib/git-review/local.rb', line 29

def all_branches
  git_call('branch -a').split("\n").collect { |s| s.strip }
end

#branch_exists?(location, branch_name) ⇒ Boolean

Returns whether a branch exists in a specified location.

Parameters:

  • location (Symbol)

    location of the branch, ‘:remote` or `:local`

  • branch_name (String)

    name of the branch

Returns:

  • (Boolean)

    whether a branch exists in a specified location



97
98
99
100
101
# File 'lib/git-review/local.rb', line 97

def branch_exists?(location, branch_name)
  return false unless [:remote, :local].include?(location)
  prefix = location == :remote ? 'remotes/origin/' : ''
  all_branches.include?(prefix + branch_name)
end

#clean_allObject

clean all obsolete branches



64
65
66
67
68
69
# File 'lib/git-review/local.rb', line 64

def clean_all
  (review_branches - protected_branches).each do |branch_name|
    # only clean up obsolete branches.
    delete_branch(branch_name) unless unmerged_commits?(branch_name, false)
  end
end

#clean_single(number, force = false) ⇒ Object

clean a single request’s obsolete branch



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/git-review/local.rb', line 47

def clean_single(number, force=false)
  request = github.pull_request(source_repo, number)
  if request && request.state == 'closed'
    # ensure there are no unmerged commits or '--force' flag has been set
    branch_name = request.head.ref
    if unmerged_commits?(branch_name) && !force
      puts "Won't delete branches that contain unmerged commits."
      puts "Use '--force' to override."
    else
      delete_branch(branch_name)
    end
  end
rescue Octokit::NotFound
  false
end

#config_listObject



217
218
219
# File 'lib/git-review/local.rb', line 217

def config_list
  git_call('config --list', false)
end

#delete_branch(branch_name) ⇒ Object

delete local and remote branches that match a given name

Parameters:

  • branch_name (String)

    name of the branch to delete



73
74
75
76
# File 'lib/git-review/local.rb', line 73

def delete_branch(branch_name)
  delete_local_branch(branch_name)
  delete_remote_branch(branch_name)
end

#delete_local_branch(branch_name) ⇒ Object

delete local branch if it exists.

Parameters:

  • branch_name (String)

    name of the branch to delete



80
81
82
83
84
# File 'lib/git-review/local.rb', line 80

def delete_local_branch(branch_name)
  if branch_exists?(:local, branch_name)
    git_call("branch -D #{branch_name}", true)
  end
end

#delete_remote_branch(branch_name) ⇒ Object

delete remote branch if it exists.

Parameters:

  • branch_name (String)

    name of the branch to delete



88
89
90
91
92
# File 'lib/git-review/local.rb', line 88

def delete_remote_branch(branch_name)
  if branch_exists?(:remote, branch_name)
    git_call("push origin :#{branch_name}", true)
  end
end

#githubObject



221
222
223
# File 'lib/git-review/local.rb', line 221

def github
  @github ||= ::GitReview::Github.instance
end

#headString

Returns the head string used for pull requests.

Returns:

  • (String)

    the head string used for pull requests



183
184
185
186
# File 'lib/git-review/local.rb', line 183

def head
  # in the form of 'user:branch'
  "#{github.github.}:#{source_branch}"
end

#load_configObject



204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/git-review/local.rb', line 204

def load_config
  @config = {}
  config_list.split("\n").each do |line|
    key, value = line.split(/=/, 2)
    if @config[key] && @config[key] != value
      @config[key] = [@config[key]].flatten << value
    else
      @config[key] = value
    end
  end
  @config
end

#merged?(sha) ⇒ Boolean

Returns whether a specified commit has already been merged.

Returns:

  • (Boolean)

    whether a specified commit has already been merged.



141
142
143
# File 'lib/git-review/local.rb', line 141

def merged?(sha)
  not git_call("rev-list #{sha} ^HEAD 2>&1").split("\n").size > 0
end

#on_feature_branch?Boolean

Returns whether already on a feature branch.

Returns:

  • (Boolean)

    whether already on a feature branch



189
190
191
192
193
194
# File 'lib/git-review/local.rb', line 189

def on_feature_branch?
  # If current and target are the same, we are not on a feature branch.
  # If they are different, but we are on master, we should still to switch
  # to a separate branch (since master makes for a poor feature branch).
  source_branch != target_branch && source_branch != 'master'
end

#protected_branchesArray<String>

Returns all open requests’ branches shouldn’t be deleted.

Returns:

  • (Array<String>)

    all open requests’ branches shouldn’t be deleted



34
35
36
# File 'lib/git-review/local.rb', line 34

def protected_branches
  github.current_requests.collect { |r| r.head.ref }
end

#review_branchesArray<String>

Returns all review branches with ‘review_’ prefix.

Returns:

  • (Array<String>)

    all review branches with ‘review_’ prefix



39
40
41
42
43
44
# File 'lib/git-review/local.rb', line 39

def review_branches
  all_branches.collect { |branch|
    # only use uniq branch names (no matter if local or remote)
    branch.split('/').last if branch.include?('review_')
  }.compact.uniq
end

#sourceString

Returns combine source repo and branch.

Returns:

  • (String)

    combine source repo and branch



156
157
158
# File 'lib/git-review/local.rb', line 156

def source
  "#{source_repo}/#{source_branch}"
end

#source_branchString

Returns the current source branch.

Returns:

  • (String)

    the current source branch



151
152
153
# File 'lib/git-review/local.rb', line 151

def source_branch
  git_call('branch').chomp.match(/\*(.*)/)[0][2..-1]
end

#source_repoString

Returns the source repo.

Returns:

  • (String)

    the source repo



146
147
148
# File 'lib/git-review/local.rb', line 146

def source_repo
  github.source_repo
end

#targetString

Returns combine target repo and branch.

Returns:

  • (String)

    combine target repo and branch



178
179
180
# File 'lib/git-review/local.rb', line 178

def target
  "#{target_repo}/#{target_branch}"
end

#target_branchString

Returns the name of the target branch.

Returns:

  • (String)

    the name of the target branch



161
162
163
164
# File 'lib/git-review/local.rb', line 161

def target_branch
  # TODO: Manually override this and set arbitrary branches
  ENV['TARGET_BRANCH'] || 'master'
end

#target_repo(upstream = false) ⇒ String

if to send a pull request to upstream repo, get the parent as target

Returns:

  • (String)

    the name of the target repo



168
169
170
171
172
173
174
175
# File 'lib/git-review/local.rb', line 168

def target_repo(upstream=false)
  # TODO: Manually override this and set arbitrary repositories
  if upstream
    github.repository(source_repo).parent.full_name
  else
    source_repo
  end
end

#uncommitted_changes?Boolean

Returns whether there are local changes not committed.

Returns:

  • (Boolean)

    whether there are local changes not committed



104
105
106
# File 'lib/git-review/local.rb', line 104

def uncommitted_changes?
  !git_call('diff HEAD').empty?
end

#unmerged_commits?(branch_name, verbose = true) ⇒ Boolean

Returns whether there are unmerged commits on the local or remote branch.

Parameters:

  • branch_name (String)

    name of the branch

  • verbose (Boolean) (defaults to: true)

    if verbose output

Returns:

  • (Boolean)

    whether there are unmerged commits on the local or remote branch.



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
# File 'lib/git-review/local.rb', line 112

def unmerged_commits?(branch_name, verbose=true)
  locations = []
  locations << '' if branch_exists?(:local, branch_name)
  locations << 'origin/' if branch_exists?(:remote, branch_name)
  locations = locations.repeated_permutation(2).to_a
  if locations.empty?
    puts 'Nothing to do. All cleaned up already.' if verbose
    return false
  end
  # compare remote and local branch with remote and local master
  responses = locations.collect { |loc|
    git_call "cherry #{loc.first}#{target_branch} #{loc.last}#{branch_name}"
  }
  # select commits (= non empty, not just an error message and not only
  #   duplicate commits staring with '-').
  unmerged_commits = responses.reject { |response|
    response.empty? or response.include?('fatal: Unknown commit') or
        response.split("\n").reject { |x| x.index('-') == 0 }.empty?
  }
  # if the array ain't empty, we got unmerged commits
  if unmerged_commits.empty?
    false
  else
    puts "Unmerged commits on branch '#{branch_name}'."
    true
  end
end