git_tools 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|