Class: Ninny::Git

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/ninny/git.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

NO_BRANCH =
'(no branch)'
DEFAULT_DIRTY_MESSAGE =
'Your Git index is not clean. Commit, stash, or otherwise clean' \
' up the index before continuing.'
DIRTY_CONFIRM_MESSAGE =
'Your Git index is not clean. Do you want to continue?'
DEPLOYABLE_PREFIX =

branch prefixes

'deployable'
STAGING_PREFIX =
'staging'
QAREADY_PREFIX =
'qaready'
CheckoutFailed =

Exceptions

Class.new(StandardError)
NotOnBranch =
Class.new(StandardError)
NoBranchOfType =
Class.new(StandardError)
DirtyIndex =
Class.new(StandardError)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeGit

Returns a new instance of Git.



21
22
23
# File 'lib/ninny/git.rb', line 21

def initialize
  @git = ::Git.open(Dir.pwd)
end

Instance Attribute Details

#gitObject (readonly)

Returns the value of attribute git.



19
20
21
# File 'lib/ninny/git.rb', line 19

def git
  @git
end

Instance Method Details

#alert_dirty_index(message) ⇒ Object

Public: Display the message and show the git status

Raises:



163
164
165
166
167
168
169
# File 'lib/ninny/git.rb', line 163

def alert_dirty_index(message)
  prompt.say ' '
  prompt.say message
  prompt.say ' '
  prompt.say command('status')
  raise DirtyIndex
end

#branches_for(prefix) ⇒ Object

Public: List of branches starting with the given string

prefix - String to match branch names against

Returns an Array of Branches containing the branch name



129
130
131
132
133
# File 'lib/ninny/git.rb', line 129

def branches_for(prefix)
  remote_branches.select do |branch|
    branch.name =~ /^#{prefix}/
  end
end

#check_out(branch, do_after_pull = true) ⇒ Object

Public: Check out the given branch name

branch_name - The name of the branch to check out do_after_pull - Should a pull be done after checkout?

Raises:



67
68
69
70
71
72
# File 'lib/ninny/git.rb', line 67

def check_out(branch, do_after_pull = true)
  `git fetch --prune &> /dev/null`
  git.checkout(branch)
  pull if do_after_pull
  raise CheckoutFailed, "Failed to check out '#{branch}'" unless current_branch.name == branch.name
end

#clean?Boolean

Public: Whether the Git index is clean (has no uncommited changes)

Returns a Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/ninny/git.rb', line 148

def clean?
  command('status', '--short').empty?
end

#command(*args) ⇒ Object



25
26
27
# File 'lib/ninny/git.rb', line 25

def command(*args)
  git.lib.send(:command, *args)
end

#current_branchObject



29
30
31
# File 'lib/ninny/git.rb', line 29

def current_branch
  git.branch(current_branch_name)
end

#current_branch_nameObject

Raises:



33
34
35
36
37
# File 'lib/ninny/git.rb', line 33

def current_branch_name
  raise NotOnBranch, 'Not currently checked out to a particular branch' if git.current_branch == NO_BRANCH

  git.current_branch
end

#delete_branch(branch_name) ⇒ Object

Public: Delete the given branch

branch_name - The name of the branch to delete



106
107
108
109
110
111
112
113
114
# File 'lib/ninny/git.rb', line 106

def delete_branch(branch_name)
  branch = branch_name.is_a?(::Git::Branch) ? branch_name : git.branch(branch_name)
  git.push('origin', ":#{branch}")
  branch.delete
rescue ::Git::GitExecuteError => e
  if e.message.include?(':error: branch') && e.message.include?(' not found.')
    puts 'Could not delete local branch, but the remote branch was deleted.'
  end
end

#if_clean(message = DEFAULT_DIRTY_MESSAGE) ⇒ Object

Public: Perform the block if the Git index is clean



153
154
155
156
157
158
159
160
# File 'lib/ninny/git.rb', line 153

def if_clean(message = DEFAULT_DIRTY_MESSAGE)
  if clean? || prompt.yes?(DIRTY_CONFIRM_MESSAGE)
    yield
  else
    alert_dirty_index message
    exit 1
  end
end

#latest_branch_for(prefix) ⇒ Object

Public: Most recent branch starting with the given string

prefix - String to match branch names against

Returns an Array of Branches containing the branch name



140
141
142
143
# File 'lib/ninny/git.rb', line 140

def latest_branch_for(prefix)
  # I don't really see why the first part would break, and the second would work, but you never know
  branches_for(prefix).last || Ninny.git.branches_for(prefix).last || raise(NoBranchOfType, "No #{prefix} branch")
end

#merge(branch_name) ⇒ Object



39
40
41
42
43
44
45
46
47
# File 'lib/ninny/git.rb', line 39

def merge(branch_name)
  if_clean do
    `git fetch --prune &> /dev/null`
    command('merge', '--no-ff', "origin/#{branch_name}")
    raise MergeFailed unless clean?

    push
  end
end

#new_branch(new_branch_name, source_branch_name) ⇒ Object

Public: Create a new branch from the given source

new_branch_name - The name of the branch to create source_branch_name - The name of the branch to branch from



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/ninny/git.rb', line 86

def new_branch(new_branch_name, source_branch_name)
  `git fetch --prune &> /dev/null`
  remote_branches = command('branch', '--remote')

  if remote_branches.include?("origin/#{new_branch_name}")
    ask_to_recreate_branch(new_branch_name, source_branch_name)
  else
    create_branch(new_branch_name, source_branch_name)
  end
rescue ::Git::GitExecuteError => e
  if e.message.include?(':fatal: A branch named') && e.message.include?(' already exists')
    puts "The local branch #{new_branch_name} already exists." \
         ' Please delete it manually and then run this command again.'
    exit 1
  end
end

#prompt(**options) ⇒ Object



171
172
173
174
# File 'lib/ninny/git.rb', line 171

def prompt(**options)
  require 'tty-prompt'
  TTY::Prompt.new(*options)
end

#pullObject

Public: Pull the latest changes for the checked-out branch



57
58
59
60
61
# File 'lib/ninny/git.rb', line 57

def pull
  if_clean do
    command('pull')
  end
end

#pushObject

Public: Push the current branch to GitHub



50
51
52
53
54
# File 'lib/ninny/git.rb', line 50

def push
  if_clean do
    git.push('origin', current_branch_name)
  end
end

#remote_branchesObject

Public: The list of branches on GitHub

Returns an Array of Strings containing the branch names



119
120
121
122
# File 'lib/ninny/git.rb', line 119

def remote_branches
  `git fetch --prune &> /dev/null`
  git.branches.remote.map { |branch| git.branch(branch.name) }.sort_by(&:name)
end

#track_current_branch(do_after_pull = true) ⇒ Object

Public: Track remote branch matching current branch

do_after_pull - Should a pull be done after tracking?



77
78
79
80
# File 'lib/ninny/git.rb', line 77

def track_current_branch(do_after_pull = true)
  command('branch', '-u', "origin/#{current_branch_name}")
  pull if do_after_pull
end