git-whistles 0.4.4 → 0.5.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.
- data/bin/git-ff-all-branches +190 -0
- data/git-whistles.gemspec +1 -0
- data/lib/git-whistles/version.rb +1 -1
- metadata +21 -3
@@ -0,0 +1,190 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Fast-forward all local branches to their remote counterparts.
|
4
|
+
#
|
5
|
+
# Inspired from http://stackoverflow.com/questions/5147537/how-do-i-fast-forward-other-tracking-branches-in-git
|
6
|
+
#
|
7
|
+
require 'ostruct'
|
8
|
+
require 'optparse'
|
9
|
+
require 'logger'
|
10
|
+
require 'term/ansicolor'
|
11
|
+
|
12
|
+
############################################################################
|
13
|
+
|
14
|
+
|
15
|
+
class App
|
16
|
+
|
17
|
+
DEFAULTS = {
|
18
|
+
:fetch => false,
|
19
|
+
:dry_run => false,
|
20
|
+
:remote => 'origin',
|
21
|
+
:loglevel => Logger::WARN
|
22
|
+
}
|
23
|
+
|
24
|
+
class Logger < ::Logger
|
25
|
+
Colors = {
|
26
|
+
'DEBUG' => :reset,
|
27
|
+
'INFO' => :green,
|
28
|
+
'WARN' => :yellow,
|
29
|
+
'ERROR' => :red,
|
30
|
+
'FATAL' => :red,
|
31
|
+
'UNKNOWN' => :red
|
32
|
+
}
|
33
|
+
|
34
|
+
def initialize(*args)
|
35
|
+
super
|
36
|
+
self.formatter = self.method(:custom_formatter)
|
37
|
+
end
|
38
|
+
|
39
|
+
def custom_formatter(severity, time, progname, msg)
|
40
|
+
Term::ANSIColor.send(Colors[severity], "#{msg}\n")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def initialize
|
46
|
+
@options = OpenStruct.new(DEFAULTS)
|
47
|
+
@local_branches = {}
|
48
|
+
@remote_branches = {}
|
49
|
+
@current_branch = nil
|
50
|
+
@log = Logger.new($stderr)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def main(args)
|
55
|
+
parse_args!(args)
|
56
|
+
log.level = options.loglevel
|
57
|
+
|
58
|
+
if options.fetch
|
59
|
+
run "git fetch"
|
60
|
+
end
|
61
|
+
|
62
|
+
load_refs
|
63
|
+
local_branches.each_pair do |branch_name, _|
|
64
|
+
process_branch(branch_name)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
attr :options
|
73
|
+
attr :local_branches
|
74
|
+
attr :remote_branches
|
75
|
+
attr :current_branch
|
76
|
+
attr :log
|
77
|
+
|
78
|
+
|
79
|
+
def process_branch(branch_name)
|
80
|
+
unless remote_branches[branch_name]
|
81
|
+
log.debug("skipping #{branch_name} (not on remote)")
|
82
|
+
return
|
83
|
+
end
|
84
|
+
|
85
|
+
old_head = local_branches[branch_name]
|
86
|
+
new_head = remote_branches[branch_name]
|
87
|
+
if old_head == new_head
|
88
|
+
log.debug("skipping #{branch_name} (up-to-date)")
|
89
|
+
return
|
90
|
+
end
|
91
|
+
|
92
|
+
if branch_name == current_branch
|
93
|
+
if run('git status --porcelain').strip.empty?
|
94
|
+
flag = (options.loglevel > Logger::INFO) ? '-q' : ''
|
95
|
+
run("git merge --ff-only #{flag} #{new_head}") unless options.dry_run
|
96
|
+
else
|
97
|
+
log.warn('not merging current branch as it has local changes')
|
98
|
+
return
|
99
|
+
end
|
100
|
+
else
|
101
|
+
merge_base = run("git merge-base #{old_head} #{new_head}").strip
|
102
|
+
if merge_base != old_head
|
103
|
+
log.warn("cannot fast-forward #{branch_name}")
|
104
|
+
return
|
105
|
+
end
|
106
|
+
run("git update-ref refs/heads/#{branch_name} #{new_head} #{old_head}")
|
107
|
+
end
|
108
|
+
|
109
|
+
log.info("#{branch_name}: #{short_sha old_head} -> #{short_sha new_head}")
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def option_parser
|
114
|
+
@option_parser ||= OptionParser.new do |op|
|
115
|
+
op.banner = "Usage: git ff-all-branches [options]"
|
116
|
+
|
117
|
+
op.on("-v", "--verbose", "Run verbosely (add twice for more verbosity)") do |v|
|
118
|
+
options.loglevel -= 1
|
119
|
+
end
|
120
|
+
|
121
|
+
op.on("-q", "--quiet", "Run silently") do |v|
|
122
|
+
options.loglevel = Logger::UNKNOWN
|
123
|
+
end
|
124
|
+
|
125
|
+
op.on("-r", "--remote REMOTE", "Set the remote [origin]") do |remote|
|
126
|
+
options.remote = remote
|
127
|
+
end
|
128
|
+
|
129
|
+
op.on("-f", "--fetch", "Run git fetch beforehand") do |v|
|
130
|
+
options.fetch = v
|
131
|
+
end
|
132
|
+
|
133
|
+
op.on("-p", "--dry-run", "Don't actually do anything") do |v|
|
134
|
+
options.dry_run = v
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
def parse_args!(args)
|
141
|
+
begin
|
142
|
+
option_parser.parse!(args)
|
143
|
+
rescue OptionParser::InvalidOption => error
|
144
|
+
die error.message, :usage => true
|
145
|
+
end
|
146
|
+
|
147
|
+
if ARGV.any?
|
148
|
+
die "this command does not take any argument besides flags", :usage => true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
def run(command)
|
154
|
+
result = %x(#{command})
|
155
|
+
return result if $? == 0
|
156
|
+
die "command '#{command}' failed"
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
def die(message, options = {})
|
161
|
+
puts Term::ANSIColor.red(message)
|
162
|
+
if options[:usage]
|
163
|
+
puts
|
164
|
+
puts option_parser.help
|
165
|
+
end
|
166
|
+
exit 1
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
def load_refs
|
171
|
+
@current_branch = run("git symbolic-ref HEAD").strip.gsub(%r(^refs/heads/), '')
|
172
|
+
|
173
|
+
run('git show-ref').strip.split(/\n+/).each do |line|
|
174
|
+
line =~ %r(([a-f0-9]{40}) refs/(remotes/#{options.remote}|heads)/(.*)) or next
|
175
|
+
if $2 == 'heads'
|
176
|
+
@local_branches[$3] = $1
|
177
|
+
else
|
178
|
+
@remote_branches[$3] = $1
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def short_sha(sha)
|
184
|
+
sha[0..7]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
############################################################################
|
189
|
+
|
190
|
+
App.new.main(ARGV)
|
data/git-whistles.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.add_development_dependency "bundler", ">= 1.0.0"
|
18
18
|
gem.add_development_dependency "rake"
|
19
19
|
gem.add_development_dependency "pry"
|
20
|
+
gem.add_development_dependency "pry-nav"
|
20
21
|
|
21
22
|
gem.add_dependency "pivotal-tracker", "~> 0.5.6"
|
22
23
|
gem.add_dependency "term-ansicolor"
|
data/lib/git-whistles/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git-whistles
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -59,6 +59,22 @@ dependencies:
|
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: pry-nav
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
62
78
|
- !ruby/object:Gem::Dependency
|
63
79
|
name: pivotal-tracker
|
64
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,6 +112,7 @@ email:
|
|
96
112
|
- julien.letessier@gmail.com
|
97
113
|
executables:
|
98
114
|
- git-chop
|
115
|
+
- git-ff-all-branches
|
99
116
|
- git-list-branches
|
100
117
|
- git-merge-po
|
101
118
|
- git-outstanding-features
|
@@ -110,6 +127,7 @@ files:
|
|
110
127
|
- README.md
|
111
128
|
- Rakefile
|
112
129
|
- bin/git-chop
|
130
|
+
- bin/git-ff-all-branches
|
113
131
|
- bin/git-list-branches
|
114
132
|
- bin/git-merge-po
|
115
133
|
- bin/git-outstanding-features
|
@@ -138,7 +156,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
156
|
version: '0'
|
139
157
|
segments:
|
140
158
|
- 0
|
141
|
-
hash:
|
159
|
+
hash: 1186482442266727224
|
142
160
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
161
|
none: false
|
144
162
|
requirements:
|