git_tools 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/README.md +4 -0
- data/Rakefile +27 -0
- data/git_tools.gemspec +20 -0
- data/lib/git_tools/branches/cleaner.rb +237 -0
- data/lib/git_tools/extensions/string.rb +8 -0
- data/lib/git_tools/extensions/time.rb +50 -0
- data/lib/git_tools/hooks/pre-commit/01_check_keywords_and_conflicts.rb +33 -0
- data/lib/git_tools/hooks/prepare-commit-msg/01_capture_issue_tracker_number_from_branch_name.rb +51 -0
- data/lib/git_tools/hooks.rb +79 -0
- data/lib/git_tools.rb +8 -0
- data/lib/tasks/git_tools/branches.rake +30 -0
- data/lib/tasks/git_tools/hooks.rake +21 -0
- metadata +57 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d0e458481a11751c30773053821dc103dcb20066
|
4
|
+
data.tar.gz: ad109373d0c651babb912fa2a97ff8f247ad9ad8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b2c2ef68608acf414c38b9acf868c87afa209743c34fc98b87aec3c35a948a398233948132f7957ac2bd272c3362e98f872a85a7b6cab8e2841062c101a89a7b
|
7
|
+
data.tar.gz: 0e4c13416a9498544dcd3e10f8711cc00b8953743ed8da487df4c4a05ffba1bb797d0d8730b6882c86d8263a091a8df03b6821cead0bce96dcfdaed99cbc31db
|
data/.gitignore
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
namespace :gem do
|
4
|
+
desc 'Build the gem'
|
5
|
+
task :build do
|
6
|
+
`mkdir -p pkg`
|
7
|
+
`gem build *.gemspec`
|
8
|
+
`mv *.gem pkg/`
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'Publish the gem'
|
12
|
+
task :publish do
|
13
|
+
gem = `ls pkg | sort | tail -n 1`
|
14
|
+
exec("gem push pkg/#{gem}")
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Install the gem locally'
|
18
|
+
task :install do
|
19
|
+
gem = `ls pkg`.split.sort
|
20
|
+
`gem install pkg/#{gem.last}`
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Remove generated files'
|
25
|
+
task :clean do
|
26
|
+
`rm -rf pkg`
|
27
|
+
end
|
data/git_tools.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "git_tools"
|
6
|
+
s.version = '0.1.0'
|
7
|
+
s.authors = ["Birkir A. Barkarson"]
|
8
|
+
s.email = ["birkirb@stoicviking.net"]
|
9
|
+
s.licenses = ['MIT']
|
10
|
+
s.homepage = "https://github.com/birkirb/git_tools"
|
11
|
+
s.summary = %q{Collection of various handy git commands and tasks.}
|
12
|
+
s.description = %q{Git tools for installing hooks, cleaning up branches and more.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "git_tools"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'git_tools/extensions/time'
|
2
|
+
|
3
|
+
module GitTools
|
4
|
+
module Branches
|
5
|
+
|
6
|
+
KEEP_LIST = %w(master develop development testing stable release production)
|
7
|
+
KEEP_BRANCH_FILENAME = File.join(CUSTOM_DIR, 'keep_branches')
|
8
|
+
|
9
|
+
def self.git_keep_branches_from_file
|
10
|
+
if File.exists?(KEEP_BRANCH_FILENAME)
|
11
|
+
File.readlines(KEEP_BRANCH_FILENAME).each { |line| line.strip! }
|
12
|
+
else
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.default_keep_list
|
18
|
+
KEEP_LIST + git_keep_branches_from_file
|
19
|
+
end
|
20
|
+
|
21
|
+
class Cleaner
|
22
|
+
MASTER_BRANCH = 'master'
|
23
|
+
DEFAULT_REMOTE = 'origin'
|
24
|
+
AGE_THRESHOLD_IN_MONTHS_FOR_DELETING_REMOTE_BRANCHES_IN_MASTER = 1 * Time::SECONDS_IN_MONTH
|
25
|
+
AGE_THRESHOLD_IN_MONTHS_FOR_DELETING_ANY_UNMERGED_BRANCHES = 6 * Time::SECONDS_IN_MONTH
|
26
|
+
|
27
|
+
attr_reader :master_branch, :remote, :protected_branches
|
28
|
+
|
29
|
+
def self.with_local(master_branch, protected_branches = nil)
|
30
|
+
self.new(nil, master_branch, protected_branches)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.with_origin(protected_branches = nil)
|
34
|
+
self.new(DEFAULT_REMOTE, nil, protected_branches)
|
35
|
+
end
|
36
|
+
|
37
|
+
public
|
38
|
+
|
39
|
+
def initialize(remote = nil, master_branch = nil, protected_branches = nil)
|
40
|
+
@remote = remote
|
41
|
+
@protected_branches = protected_branches || Branches.default_keep_list
|
42
|
+
@master_branch = master_branch || get_remote_head || MASTER_BRANCH
|
43
|
+
@branches = branches
|
44
|
+
|
45
|
+
if @master_branch.nil?
|
46
|
+
raise "Master branch was not set or determined."
|
47
|
+
else
|
48
|
+
puts "Master branch is #{@master_branch}" if $VERBOSE
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def local?
|
53
|
+
@remote.nil?
|
54
|
+
end
|
55
|
+
|
56
|
+
def run!
|
57
|
+
(@branches - protected_branches - [master_branch] ).each do |branch|
|
58
|
+
branch = Branch.new(branch, remote)
|
59
|
+
containing_branches = contained_branches(branch.normalized_name) - [branch.name]
|
60
|
+
|
61
|
+
if protected_branch?(branch.name)
|
62
|
+
puts "#{label_for_remote} branch [#{branch}] is on keep list ( #{kbs.join(" , ")} )" if $VERBOSE
|
63
|
+
elsif in_master_branch?(containing_branches)
|
64
|
+
if delete_branch_merged_to_master_without_confirmation?(branch.age)
|
65
|
+
message = "Removing #{label_for_remote.downcase} branch [#{branch}] since it is in #{master_branch}. [#{branch.age.relative}]"
|
66
|
+
branch.remove!(message)
|
67
|
+
else
|
68
|
+
message = "#{label_for_remote} branch [#{branch}] is in #{master_branch} and could be deleted [#{branch.age.relative}]"
|
69
|
+
branch.confirm_remove(message, "Delete #{branch}?")
|
70
|
+
end
|
71
|
+
elsif delete_unmerged_branch?(branch.age)
|
72
|
+
branch_list = containing_branches.empty? ? '' : "Branch has been merged into:\n #{containing_branches.join("\n ")}"
|
73
|
+
message = "#{label_for_remote} branch [#{branch}] is not on #{master_branch}, but old [#{branch.age.relative}]. #{branch_list}"
|
74
|
+
branch.confirm_remove(message, "Delete old unmerged branch: #{branch} ?")
|
75
|
+
else
|
76
|
+
puts "Ignoring unmerged #{label_for_remote.downcase} branch [#{branch}]" if $VERBOSE
|
77
|
+
end
|
78
|
+
|
79
|
+
if defined?($signal_handler)
|
80
|
+
break if $signal_handler.interrupted?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
git_remote_prune
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def git_remote_prune
|
90
|
+
`git remote prune #{remote}` unless local?
|
91
|
+
end
|
92
|
+
|
93
|
+
def label_for_remote
|
94
|
+
local? ? "Local" : "Remote"
|
95
|
+
end
|
96
|
+
|
97
|
+
def branches
|
98
|
+
clean_branches_result(`git branch #{git_argument_for_remote}`)
|
99
|
+
end
|
100
|
+
|
101
|
+
def git_argument_for_remote
|
102
|
+
local? ? '' : ' -r'
|
103
|
+
end
|
104
|
+
|
105
|
+
def get_remote_head
|
106
|
+
(`git branch -r | grep #{remote}/HEAD`).sub(/#{remote}\/HEAD -> #{remote}\//, '').strip
|
107
|
+
end
|
108
|
+
|
109
|
+
def clean_branches_result(branches)
|
110
|
+
bs = branches.to_s.split(/\n/).map { |b| b.strip.sub(/^\s*\*\s*/, '') }
|
111
|
+
|
112
|
+
if local?
|
113
|
+
bs
|
114
|
+
else
|
115
|
+
# technically split out remote branch name (may not be origin) and return as list
|
116
|
+
bs.delete_if { |b| b =~ /HEAD/ }
|
117
|
+
bs.find_all { |b| /^#{remote}\// =~ b }.map { |b| b.sub(/^#{remote}\//, '') }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def contained_branches(branch)
|
122
|
+
# git's --contains param seems to cause this stderr out:
|
123
|
+
# error: branch 'origin/HEAD' does not point at a commit
|
124
|
+
# piping that to stderr and cleaning out.
|
125
|
+
clean_branches_result(`git branch #{git_argument_for_remote} --contains #{branch} 2>&1`)
|
126
|
+
end
|
127
|
+
|
128
|
+
def in_master_branch?(branch)
|
129
|
+
branch.include?(master_branch)
|
130
|
+
end
|
131
|
+
|
132
|
+
def protected_branch?(branch)
|
133
|
+
protected_branches.include?(branch)
|
134
|
+
end
|
135
|
+
|
136
|
+
def delete_branch_merged_to_master_without_confirmation?(time)
|
137
|
+
if local?
|
138
|
+
true
|
139
|
+
else
|
140
|
+
(Time.now - time) > AGE_THRESHOLD_IN_MONTHS_FOR_DELETING_REMOTE_BRANCHES_IN_MASTER
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def delete_unmerged_branch?(time)
|
145
|
+
(Time.now - time) > AGE_THRESHOLD_IN_MONTHS_FOR_DELETING_ANY_UNMERGED_BRANCHES
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
class Branch
|
151
|
+
|
152
|
+
DATE_REGEXP = /^Date:\s+(.*)$/
|
153
|
+
|
154
|
+
def self.age(branch)
|
155
|
+
time = DATE_REGEXP.match(`git log --shortstat --date=iso -n 1 #{branch}`)
|
156
|
+
Time.parse(time[1])
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.executor
|
160
|
+
ActionExecutor.new
|
161
|
+
end
|
162
|
+
|
163
|
+
public
|
164
|
+
|
165
|
+
attr_reader :name, :remote, :age
|
166
|
+
|
167
|
+
def initialize(name, remote)
|
168
|
+
@name = name
|
169
|
+
@remote = remote
|
170
|
+
@age = self.class.age(normalized_name)
|
171
|
+
end
|
172
|
+
|
173
|
+
def normalized_name
|
174
|
+
local? ? name : "#{remote}/#{name}"
|
175
|
+
end
|
176
|
+
|
177
|
+
def remove!(message)
|
178
|
+
self.class.executor.execute(remove_branch_action, message)
|
179
|
+
end
|
180
|
+
|
181
|
+
def confirm_remove(message, prompt)
|
182
|
+
self.class.executor.execute(remove_branch_action, message, prompt)
|
183
|
+
end
|
184
|
+
|
185
|
+
def to_s
|
186
|
+
name
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
def local?
|
192
|
+
remote.nil?
|
193
|
+
end
|
194
|
+
|
195
|
+
def remove_branch_action
|
196
|
+
local? ? "git branch -d #{name}" : "git push #{remote} :#{name}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class ActionExecutor
|
201
|
+
|
202
|
+
@@test_mode = true
|
203
|
+
@@skip_prompted = false
|
204
|
+
|
205
|
+
def self.test_mode=(value)
|
206
|
+
@@test_mode = (value == true)
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.skip_prompted=(value)
|
210
|
+
@@skip_prompted = (value == true)
|
211
|
+
end
|
212
|
+
|
213
|
+
def execute(command, action_message, confirmation_prompt = nil)
|
214
|
+
if @@test_mode
|
215
|
+
$stderr.puts("#{action_message} -> #{command}")
|
216
|
+
else
|
217
|
+
if confirmation_prompt
|
218
|
+
if @@skip_prompted
|
219
|
+
puts "#{action_message} -> skipping prompts" if $VERBOSE
|
220
|
+
else
|
221
|
+
puts action_message
|
222
|
+
puts "#{confirmation_prompt} [y/N]"
|
223
|
+
case $stdin.gets.chomp
|
224
|
+
when 'y'
|
225
|
+
`#{command}`
|
226
|
+
end
|
227
|
+
end
|
228
|
+
else
|
229
|
+
puts action_message
|
230
|
+
`#{command}`
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'git_tools/extensions/string'
|
3
|
+
|
4
|
+
class Time
|
5
|
+
|
6
|
+
DAYS_IN_YEAR = 365.2424
|
7
|
+
SECONDS_IN_MINUTE = 60
|
8
|
+
SECONDS_IN_HOUR = 3_600
|
9
|
+
SECONDS_IN_DAY = 86_400
|
10
|
+
SECONDS_IN_WEEK = 604_800
|
11
|
+
SECONDS_IN_YEAR = 31_556_736 # AVERAGE
|
12
|
+
SECONDS_IN_MONTH = SECONDS_IN_YEAR/12 # AVERAGE
|
13
|
+
|
14
|
+
HUMAN_TIMES = [
|
15
|
+
[SECONDS_IN_MINUTE, 120, "minutes"],
|
16
|
+
[SECONDS_IN_HOUR , 72, "hours"],
|
17
|
+
[SECONDS_IN_DAY , 21, "days"],
|
18
|
+
[SECONDS_IN_WEEK , 12, "weeks"],
|
19
|
+
[SECONDS_IN_MONTH , 12, "months"]
|
20
|
+
#[SECONDS_IN_YEAR , 10, "years"]
|
21
|
+
]
|
22
|
+
|
23
|
+
def relative(t0 = Time.now)
|
24
|
+
dt = self - t0
|
25
|
+
|
26
|
+
if dt < 0
|
27
|
+
tense = 'ago'
|
28
|
+
dt = dt.abs - 1
|
29
|
+
else
|
30
|
+
tense = 'from now'
|
31
|
+
end
|
32
|
+
|
33
|
+
if dt < SECONDS_IN_MINUTE
|
34
|
+
return 'now'.t
|
35
|
+
else
|
36
|
+
HUMAN_TIMES.each do |time|
|
37
|
+
seconds = time[0]
|
38
|
+
limit = time[1]
|
39
|
+
time_unit = time[2]
|
40
|
+
|
41
|
+
if dt < seconds * limit
|
42
|
+
return "{time} #{time_unit} #{tense}".multi_gsub!(:time => (dt/seconds).ceil.to_i)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
# Above the higest limit
|
46
|
+
"over a year #{tense}".t
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/env/ruby
|
2
|
+
|
3
|
+
FAILURE = false
|
4
|
+
|
5
|
+
diff_lines = `git diff`.split("\n")
|
6
|
+
added_lines = diff_lines.select { |line| line =~ /^\+/ }
|
7
|
+
|
8
|
+
conflict_markers = ['<<<<<<<', '=======', '>>>>>>>']
|
9
|
+
debug_keywords = ['puts', 'debugger']
|
10
|
+
|
11
|
+
conflict_lines = added_lines.select do |line|
|
12
|
+
conflict_markers.any? { |marker| line =~ /#{marker}/ }
|
13
|
+
end
|
14
|
+
debug_lines = added_lines.select do |line|
|
15
|
+
debug_keywords.any? { |keyword| line =~ /\b#{keyword}\b/ }
|
16
|
+
end
|
17
|
+
|
18
|
+
$stdin.readlines.each { |l| puts l }
|
19
|
+
|
20
|
+
if conflict_lines.any?
|
21
|
+
puts "You might be committing changes containing conflict markers, this indicates an unfinished merge"
|
22
|
+
puts "The lines are the following:"
|
23
|
+
conflict_lines.each { |line| puts line }
|
24
|
+
end
|
25
|
+
if debug_lines.any?
|
26
|
+
puts "You might be committing changes containing debug keywords"
|
27
|
+
puts "The lines are the following:"
|
28
|
+
debug_lines.each { |line| puts line }
|
29
|
+
end
|
30
|
+
if conflict_lines.any? || debug_lines.any?
|
31
|
+
puts "If you are sure you want to make this commit, commit again with the --no-verify flag"
|
32
|
+
exit(FAILURE)
|
33
|
+
end
|
data/lib/git_tools/hooks/prepare-commit-msg/01_capture_issue_tracker_number_from_branch_name.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/env/ruby
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
message_file = ARGV[0]
|
5
|
+
commit_source = ARGV[1]
|
6
|
+
# sha1 = ARGV[2]
|
7
|
+
|
8
|
+
exit if ['merge', 'commit', 'message'].include? commit_source
|
9
|
+
|
10
|
+
working_dir = File.expand_path(File.join(File.dirname(message_file), '..'))
|
11
|
+
template_file = File.join(working_dir, '.git_tools', 'templates', 'prepare_commit_msg')
|
12
|
+
contents = File.read(message_file)
|
13
|
+
|
14
|
+
has_merge_message = contents.match(/^Merge branch/m) # Commit with resolved merge conflicts don't have a commit source.
|
15
|
+
exit if has_merge_message
|
16
|
+
ticket_no_from_branch_name = contents.scan(/On branch .+#(\d{1,5})/)
|
17
|
+
|
18
|
+
if ticket_no_from_branch_name.empty?
|
19
|
+
references = nil
|
20
|
+
default_line = <<-MESSAGE
|
21
|
+
#
|
22
|
+
# PLEASE ADD THE ISSUE TRACKER NUMBER TO THE BRANCH NAME.
|
23
|
+
# Replace these lines with any applicable issue tracker references.
|
24
|
+
MESSAGE
|
25
|
+
else
|
26
|
+
references = ticket_no_from_branch_name.join(', #')
|
27
|
+
default_line = "Issue tracker: ##{references}"
|
28
|
+
end
|
29
|
+
|
30
|
+
if File.exists?(template_file)
|
31
|
+
message_template = ERB.new(File.read(template_file), 0, '>').result
|
32
|
+
else
|
33
|
+
message_template = <<-MESSAGE
|
34
|
+
|
35
|
+
# [Tell us *why* are you committing on the line above.]
|
36
|
+
|
37
|
+
# [Details below this line.]
|
38
|
+
#{default_line}
|
39
|
+
#
|
40
|
+
# Check out the following links on how to write proper git messges:
|
41
|
+
#
|
42
|
+
# http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
43
|
+
# http://robots.thoughtbot.com/post/48933156625/5-useful-tips-for-a-better-commit-message
|
44
|
+
#
|
45
|
+
MESSAGE
|
46
|
+
end
|
47
|
+
|
48
|
+
File.open(message_file, 'w') do |file|
|
49
|
+
file.write(message_template.chomp)
|
50
|
+
file.write(contents)
|
51
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module GitTools
|
2
|
+
module Hooks
|
3
|
+
GIT_HOOK_INSTALL_LINE_BEGIN = "# BEGIN Ruby git-hooks\n"
|
4
|
+
GIT_HOOK_INSTALL_LINE_END = "# END Ruby git-hooks\n"
|
5
|
+
GIT_HOOK_DIR = File.join('.git', 'hooks')
|
6
|
+
GIT_TOOLS_CUSTOM_HOOKS_DIR = File.join(CUSTOM_DIR, 'hooks')
|
7
|
+
GIT_TOOLS_INCLUDED_HOOKS_DIR = File.join(File.dirname(__FILE__), 'hooks')
|
8
|
+
|
9
|
+
def self.with_git_hook_files
|
10
|
+
if File.exist?(GIT_HOOK_DIR)
|
11
|
+
default_ruby_hooks.merge(custom_ruby_hooks).each do |dir, files|
|
12
|
+
files.each do |file|
|
13
|
+
git_hook = File.join(GIT_HOOK_DIR, file)
|
14
|
+
yield(dir, file, git_hook)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
else
|
18
|
+
puts "Git hook directory not found."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.default_ruby_hooks
|
23
|
+
{GIT_TOOLS_INCLUDED_HOOKS_DIR => (Dir.entries(GIT_TOOLS_INCLUDED_HOOKS_DIR) - ['.', '..'])}
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.custom_ruby_hooks
|
27
|
+
if Dir.exists?(GIT_TOOLS_CUSTOM_HOOKS_DIR)
|
28
|
+
{GIT_TOOLS_CUSTOM_HOOKS_DIR => (Dir.entries(GIT_TOOLS_CUSTOM_HOOKS_DIR) - ['.', '..'])}
|
29
|
+
else
|
30
|
+
{}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.clear_git_hooks
|
35
|
+
with_git_hook_files do |dir, ruby_hook, git_hook|
|
36
|
+
if File.exists?(git_hook)
|
37
|
+
hook_content = File.read(git_hook)
|
38
|
+
if hook_content.match(/#{GIT_HOOK_INSTALL_LINE_BEGIN}/)
|
39
|
+
puts "Clearing Ruby #{ruby_hook} git-hooks."
|
40
|
+
hook_content.gsub!(/#{GIT_HOOK_INSTALL_LINE_BEGIN}.*#{GIT_HOOK_INSTALL_LINE_END}/m, '')
|
41
|
+
File.open(git_hook, 'w+') do |file|
|
42
|
+
file.write(hook_content)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.install_git_hooks
|
50
|
+
with_git_hook_files do |ruby_hook_dir, ruby_hook_file, git_hook|
|
51
|
+
if File.exists?(git_hook)
|
52
|
+
hook_content = File.read(git_hook)
|
53
|
+
else
|
54
|
+
hook_content = "#!/bin/sh\n\n"
|
55
|
+
end
|
56
|
+
|
57
|
+
if hook_content.match(/#{GIT_HOOK_INSTALL_LINE_BEGIN}/)
|
58
|
+
next
|
59
|
+
else
|
60
|
+
puts "Installing Ruby #{ruby_hook_file} git-hooks."
|
61
|
+
hook_commands = ''
|
62
|
+
hook_files = File.join(ruby_hook_dir, ruby_hook_file)
|
63
|
+
puts "Hook file: #{hook_files}" if $VERBOSE
|
64
|
+
Dir.foreach(hook_files) do |file_path|
|
65
|
+
if file_path.match(/\.rb$/)
|
66
|
+
hook_commands += "if [ $? -eq 0 ]; then ruby #{File.join(ruby_hook_dir, ruby_hook_file, file_path)} \"$@\"; else exit 1; fi\n"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
hook_content += "#{GIT_HOOK_INSTALL_LINE_BEGIN}\n#{hook_commands}\n#{GIT_HOOK_INSTALL_LINE_END}"
|
70
|
+
File.open(git_hook, 'w+') do |file|
|
71
|
+
file.write(hook_content)
|
72
|
+
end
|
73
|
+
FileUtils.chmod(0744, git_hook)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/lib/git_tools.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'git_tools/branches/cleaner'
|
2
|
+
|
3
|
+
namespace :git do
|
4
|
+
namespace :branches do
|
5
|
+
# puts $VERBOSE, $-V, $DEBUG, $-W
|
6
|
+
# VERBOSE and DEBUG mode don't seem to be triggered in rake tasks even with RUBYOPTS.
|
7
|
+
# Something turning it off?
|
8
|
+
|
9
|
+
GitTools::Branches::ActionExecutor.test_mode = $DEBUG
|
10
|
+
GitTools::Branches::ActionExecutor.skip_prompted = true
|
11
|
+
|
12
|
+
desc 'Clean up all branches'
|
13
|
+
task(:clean => ['clean:local', 'clean:remote']) do
|
14
|
+
end
|
15
|
+
|
16
|
+
namespace :clean do
|
17
|
+
desc 'Clean up local branches'
|
18
|
+
task :local do
|
19
|
+
GitTools::Branches::Cleaner.with_local('stable').run!
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Clean up remote branches'
|
23
|
+
task :remote do
|
24
|
+
# TODO: Handle non origin
|
25
|
+
GitTools::Branches::Cleaner.with_origin.run!
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'git_tools/hooks'
|
2
|
+
|
3
|
+
namespace :git do
|
4
|
+
namespace :hooks do
|
5
|
+
desc "Clear any installed git-hooks"
|
6
|
+
task :clear do
|
7
|
+
GitTools::Hooks.clear_git_hooks
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Install all git-hooks"
|
11
|
+
task :install do
|
12
|
+
GitTools::Hooks.install_git_hooks
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Re-install all git-hooks"
|
16
|
+
task :reinstall do
|
17
|
+
GitTools::Hooks.clear_git_hooks
|
18
|
+
GitTools::Hooks.install_git_hooks
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: git_tools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Birkir A. Barkarson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-14 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Git tools for installing hooks, cleaning up branches and more.
|
14
|
+
email:
|
15
|
+
- birkirb@stoicviking.net
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- .gitignore
|
21
|
+
- README.md
|
22
|
+
- Rakefile
|
23
|
+
- git_tools.gemspec
|
24
|
+
- lib/git_tools.rb
|
25
|
+
- lib/git_tools/branches/cleaner.rb
|
26
|
+
- lib/git_tools/extensions/string.rb
|
27
|
+
- lib/git_tools/extensions/time.rb
|
28
|
+
- lib/git_tools/hooks.rb
|
29
|
+
- lib/git_tools/hooks/pre-commit/01_check_keywords_and_conflicts.rb
|
30
|
+
- lib/git_tools/hooks/prepare-commit-msg/01_capture_issue_tracker_number_from_branch_name.rb
|
31
|
+
- lib/tasks/git_tools/branches.rake
|
32
|
+
- lib/tasks/git_tools/hooks.rake
|
33
|
+
homepage: https://github.com/birkirb/git_tools
|
34
|
+
licenses:
|
35
|
+
- MIT
|
36
|
+
metadata: {}
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
requirements: []
|
52
|
+
rubyforge_project: git_tools
|
53
|
+
rubygems_version: 2.0.6
|
54
|
+
signing_key:
|
55
|
+
specification_version: 4
|
56
|
+
summary: Collection of various handy git commands and tasks.
|
57
|
+
test_files: []
|