git-scripts 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.
- data/COPYING +21 -0
- data/README.md +124 -0
- data/Rakefile +35 -0
- data/bash_completion.sh +33 -0
- data/bin/feature +191 -0
- data/bin/hotfix +142 -0
- data/lib/git.rb +170 -0
- data/lib/github.rb +190 -0
- data/lib/helpers.rb +137 -0
- data/man/feature-clean.1 +28 -0
- data/man/feature-clean.1.html +115 -0
- data/man/feature-clean.1.markdown +53 -0
- data/man/feature-clean.1.ronn +26 -0
- data/man/feature-finish.1 +19 -0
- data/man/feature-finish.1.html +107 -0
- data/man/feature-finish.1.markdown +47 -0
- data/man/feature-finish.1.ronn +21 -0
- data/man/feature-github-test.1 +19 -0
- data/man/feature-github-test.1.html +105 -0
- data/man/feature-github-test.1.markdown +45 -0
- data/man/feature-github-test.1.ronn +19 -0
- data/man/feature-list.1 +19 -0
- data/man/feature-list.1.html +106 -0
- data/man/feature-list.1.markdown +46 -0
- data/man/feature-list.1.ronn +20 -0
- data/man/feature-merge.1 +22 -0
- data/man/feature-merge.1.html +110 -0
- data/man/feature-merge.1.markdown +50 -0
- data/man/feature-merge.1.ronn +24 -0
- data/man/feature-start.1 +19 -0
- data/man/feature-start.1.html +105 -0
- data/man/feature-start.1.markdown +45 -0
- data/man/feature-start.1.ronn +19 -0
- data/man/feature-stashes.1 +28 -0
- data/man/feature-stashes.1.html +117 -0
- data/man/feature-stashes.1.markdown +55 -0
- data/man/feature-stashes.1.ronn +28 -0
- data/man/feature-status.1 +22 -0
- data/man/feature-status.1.html +108 -0
- data/man/feature-status.1.markdown +48 -0
- data/man/feature-status.1.ronn +22 -0
- data/man/feature-switch.1 +25 -0
- data/man/feature-switch.1.html +113 -0
- data/man/feature-switch.1.markdown +51 -0
- data/man/feature-switch.1.ronn +24 -0
- data/man/feature.1 +61 -0
- data/man/feature.1.html +123 -0
- data/man/feature.1.markdown +70 -0
- data/man/feature.1.ronn +43 -0
- data/man/feature.html +140 -0
- data/man/hotfix-finish.1 +19 -0
- data/man/hotfix-finish.1.html +107 -0
- data/man/hotfix-finish.1.markdown +47 -0
- data/man/hotfix-finish.1.ronn +21 -0
- data/man/hotfix-list.1 +19 -0
- data/man/hotfix-list.1.html +106 -0
- data/man/hotfix-list.1.markdown +46 -0
- data/man/hotfix-list.1.ronn +20 -0
- data/man/hotfix-merge.1 +22 -0
- data/man/hotfix-merge.1.html +110 -0
- data/man/hotfix-merge.1.markdown +50 -0
- data/man/hotfix-merge.1.ronn +24 -0
- data/man/hotfix-start.1 +19 -0
- data/man/hotfix-start.1.html +105 -0
- data/man/hotfix-start.1.markdown +45 -0
- data/man/hotfix-start.1.ronn +19 -0
- data/man/hotfix-switch.1 +19 -0
- data/man/hotfix-switch.1.html +105 -0
- data/man/hotfix-switch.1.markdown +45 -0
- data/man/hotfix-switch.1.ronn +19 -0
- data/man/hotfix.1 +41 -0
- data/man/hotfix.1.html +118 -0
- data/man/hotfix.1.markdown +60 -0
- data/man/hotfix.1.ronn +33 -0
- data/man/index.html +7 -0
- data/man/index.txt +21 -0
- metadata +177 -0
data/lib/git.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
module Git
|
2
|
+
def self.has_uncommitted_changes()
|
3
|
+
clean = system("git diff --quiet 2>/dev/null >&2")
|
4
|
+
return !clean
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.development_branch
|
8
|
+
dev_branch = `git config feature.development-branch`.strip
|
9
|
+
if !dev_branch || $? != 0
|
10
|
+
$stderr.puts "No development branch specified"
|
11
|
+
$stderr.puts " set it with: git config feature.development-branch master"
|
12
|
+
exit 1;
|
13
|
+
end
|
14
|
+
dev_branch
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns an array of branches that aren't merged into the specifeid branch
|
18
|
+
def self.branches_not_merged_into(branch)
|
19
|
+
self::all_branches - self::merged_branches(branch)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns an array of unmerged hotfix branches
|
23
|
+
def self.hotfix_branches(type)
|
24
|
+
branches = if type == :unmerged
|
25
|
+
self.branches_not_merged_into('stable')
|
26
|
+
elsif type == :merged
|
27
|
+
self.merged_branches('stable')
|
28
|
+
end
|
29
|
+
|
30
|
+
branches.select {|branch| branch.include?('hotfix-') }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns an array of unmerged feature branches
|
34
|
+
def self.feature_branches(type)
|
35
|
+
branches = if type == :unmerged
|
36
|
+
self.branches_not_merged_into('master')
|
37
|
+
elsif type == :merged
|
38
|
+
self.merged_branches('master')
|
39
|
+
end
|
40
|
+
|
41
|
+
branches.reject {|branch| branch.include?('hotfix-') }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns an array of all branch names that have have been merged into the
|
45
|
+
# specified branch
|
46
|
+
def self.merged_branches(into_branch='master')
|
47
|
+
`git branch --merged #{into_branch} -a`.
|
48
|
+
split("\n").
|
49
|
+
map {|branch| branch.gsub('*','').strip.sub('remotes/','')}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns an array of all local branch names
|
53
|
+
def self.all_branches()
|
54
|
+
`git for-each-ref --sort=-committerdate --format='%(refname)' refs/heads refs/remotes`.
|
55
|
+
split("\n").
|
56
|
+
map {|branch| branch.sub(/refs\/\w+\//, '') }.uniq
|
57
|
+
end
|
58
|
+
|
59
|
+
# returns the name of th currently checked out brnach, or nil if detached.
|
60
|
+
def self.current_branch()
|
61
|
+
ref = `git symbolic-ref -q HEAD`.strip
|
62
|
+
ref.split('/').last
|
63
|
+
end
|
64
|
+
|
65
|
+
# returns the SHA1 hash that the specified branch or symbol points to
|
66
|
+
def self.branch_hash(branch)
|
67
|
+
`git rev-parse --verify --quiet "#{branch}" 2>/dev/null`.strip
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return formatted string containing:
|
71
|
+
# commit_hash Authoe Name (relative date)
|
72
|
+
# for the specifeid branch or commit
|
73
|
+
def self.branch_info(branch)
|
74
|
+
# branch info format: hash author (relative date)
|
75
|
+
format = "%h %an %Cgreen(%ar)%Creset"
|
76
|
+
branch_info = `git show -s --pretty="#{format}" #{branch}`.strip
|
77
|
+
simple_branch = branch.sub('origin/', '')
|
78
|
+
sprintf "%-30s %s", simple_branch, branch_info
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.run_safe(command)
|
82
|
+
puts "> #{command}"
|
83
|
+
result = system(command)
|
84
|
+
raise "Git command failed, aborting." if (!result)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.show_stashes_saved_on(branch = nil)
|
88
|
+
self.stashes.each do |stash|
|
89
|
+
if !branch || stash[:branch] == branch
|
90
|
+
puts "=" * 40
|
91
|
+
puts highlight(
|
92
|
+
"There is a stash saved from #{branch} #{stash[:date]}")
|
93
|
+
puts wrap_text(stash[:subject])
|
94
|
+
puts "see it with >\n git stash show -p " + stash[:ref]
|
95
|
+
puts "apply it with >\n git stash apply " + stash[:ref]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.show_branch_list(options = {})
|
101
|
+
puts "\nCurrent Branch:"
|
102
|
+
puts "--" * 30
|
103
|
+
current = Git::current_branch
|
104
|
+
print HIGHLIGHT
|
105
|
+
if current
|
106
|
+
print Git::branch_info(current)
|
107
|
+
else
|
108
|
+
print "(not on any branch!)"
|
109
|
+
end
|
110
|
+
puts HIGHLIGHT_OFF
|
111
|
+
|
112
|
+
options.each do |branch_type, branches|
|
113
|
+
puts "\nAvailable #{branch_type} branches:"
|
114
|
+
puts "--" * 30
|
115
|
+
if branches && !branches.empty?
|
116
|
+
shown_branches = {}
|
117
|
+
branches.each do |branch|
|
118
|
+
simple_branch = branch.sub('origin/', '')
|
119
|
+
next if shown_branches.has_key?(simple_branch)
|
120
|
+
puts Git::branch_info(branch)
|
121
|
+
shown_branches[simple_branch] = true
|
122
|
+
end
|
123
|
+
else
|
124
|
+
puts "(none)"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.stashes
|
130
|
+
# Do we even have a stash?
|
131
|
+
if ! File.exist? '.git/refs/stash'
|
132
|
+
return []
|
133
|
+
end
|
134
|
+
|
135
|
+
# format = "relative date|stash ref|commit message"
|
136
|
+
`git log --format="%ar|%gd|%s" -g "refs/stash"`.lines.map do |line|
|
137
|
+
fields = line.split '|', 3
|
138
|
+
# All stashes have commit messages like "WIP on branch_name: ..."
|
139
|
+
branch = line[/\S+:/]
|
140
|
+
{
|
141
|
+
:date => fields[0],
|
142
|
+
:ref => fields[1],
|
143
|
+
:branch => branch && branch.chop,
|
144
|
+
:subject =>fields[2]
|
145
|
+
}
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.switch_branch(branch)
|
150
|
+
self.run_safe("git checkout \"#{branch}\"")
|
151
|
+
self.submodules_update
|
152
|
+
self.run_safe("git clean -ffd") if ARGV.include?('--clean')
|
153
|
+
|
154
|
+
self.show_stashes_saved_on(branch)
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.submodules_update
|
158
|
+
# capture only the path, not the newline
|
159
|
+
basedir = `git rev-parse --show-toplevel`.split("\n").first
|
160
|
+
|
161
|
+
Git::run_safe("cd #{basedir} && git submodule --quiet update --init --rebase --recursive")
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Returns the commit message from the given commit hash or branch name
|
166
|
+
#
|
167
|
+
def self.commit_message(ref)
|
168
|
+
`git log -1 --format="%B" #{ref}`.strip
|
169
|
+
end
|
170
|
+
end
|
data/lib/github.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
require 'octokit'
|
4
|
+
require 'shellwords'
|
5
|
+
require 'readline'
|
6
|
+
require 'highline/import'
|
7
|
+
|
8
|
+
module Github
|
9
|
+
##
|
10
|
+
# Get a global git config property
|
11
|
+
##
|
12
|
+
def self.config(property)
|
13
|
+
`git config --global #{property.to_s.shellescape}`.strip
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Get a local (to the repo) git config property
|
18
|
+
##
|
19
|
+
def self.local_config(property)
|
20
|
+
`git config #{property.to_s.shellescape}`.strip
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Get an instance of the Octokit API class
|
25
|
+
#
|
26
|
+
# Authorization info is the structure from here:
|
27
|
+
# http://developer.github.com/v3/oauth/#create-a-new-authorization
|
28
|
+
#
|
29
|
+
# something like this:
|
30
|
+
# {
|
31
|
+
# :scopes => ['repo'],
|
32
|
+
# :note => "git-scripts command line interface",
|
33
|
+
# :note_url => "https://github.com/ifixit/git-scripts"
|
34
|
+
# }
|
35
|
+
##
|
36
|
+
def self.api(authorization_info = {})
|
37
|
+
# Defaults
|
38
|
+
authorization_info = {
|
39
|
+
:scopes => ['repo'],
|
40
|
+
:note => "ifixit git-scripts command line interface",
|
41
|
+
:note_url => "https://github.com/ifixit/git-scripts"
|
42
|
+
}.merge(authorization_info)
|
43
|
+
OctokitWrapper.new(self::get_authentication(authorization_info))
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.get_authentication(authorization_info)
|
47
|
+
username = self::config("github.user")
|
48
|
+
token = self::config("github.token")
|
49
|
+
if !username.empty? && !token.empty?
|
50
|
+
return {:login => username, :oauth_token => token}
|
51
|
+
else
|
52
|
+
return self::request_authorization(authorization_info)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Returns a hash containing username and github oauth token
|
58
|
+
#
|
59
|
+
# Prompts the user for credentials if the token isn't stored in git config
|
60
|
+
##
|
61
|
+
def self.request_authorization(authorization_info)
|
62
|
+
puts "Authorizing..."
|
63
|
+
|
64
|
+
username ||= Readline.readline("github username: ", true)
|
65
|
+
password = ask("github password: ") { |q| q.echo = false }
|
66
|
+
|
67
|
+
octokit = OctokitWrapper.new(:login => username, :password => password)
|
68
|
+
|
69
|
+
auth = octokit.authorizations.find {|auth|
|
70
|
+
note = auth['note']
|
71
|
+
note && note.include?(authorization_info[:note])
|
72
|
+
}
|
73
|
+
|
74
|
+
auth = auth || octokit.create_authorization(authorization_info)
|
75
|
+
|
76
|
+
success =
|
77
|
+
system("git config --global github.user #{username}") &&
|
78
|
+
system("git config --global github.token #{auth[:token]}")
|
79
|
+
|
80
|
+
if !success
|
81
|
+
puts "Couldn't set git config"
|
82
|
+
exit
|
83
|
+
end
|
84
|
+
|
85
|
+
return {:login => username, :oauth_token => auth[:token]}
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Returns the github repo identifier in the form that the API likes:
|
90
|
+
# "someuser/theirrepo"
|
91
|
+
#
|
92
|
+
# Requires the "origin" remote to be set to a github url
|
93
|
+
##
|
94
|
+
def self.get_github_repo()
|
95
|
+
url = self::local_config("remote.origin.url")
|
96
|
+
m = /github\.com.(.*?)\/(.*)/.match(url)
|
97
|
+
if m
|
98
|
+
return [m[1], m[2].sub(/\.git\Z/, "")].join("/")
|
99
|
+
else
|
100
|
+
raise "remote.origin.url in git config but be a github url"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Prompts the user (using $EDITOR) to provide a title and body
|
106
|
+
# for this pull-request
|
107
|
+
#
|
108
|
+
# Returns a hash containing a :title and :body
|
109
|
+
##
|
110
|
+
def self.get_pull_request_description(branch_name = nil)
|
111
|
+
require 'tempfile'
|
112
|
+
|
113
|
+
if branch_name
|
114
|
+
initial_message = Git::commit_message(branch_name).gsub("\r","")
|
115
|
+
else
|
116
|
+
initial_message = <<-MESSAGE
|
117
|
+
Title of pull-request
|
118
|
+
# Second line is ignored (do no edit)
|
119
|
+
Body of pull-request
|
120
|
+
MESSAGE
|
121
|
+
end
|
122
|
+
|
123
|
+
msg = Tempfile.new('pull-message')
|
124
|
+
msg.write(initial_message)
|
125
|
+
msg.close
|
126
|
+
|
127
|
+
# -c blah only works for vim
|
128
|
+
if (ENV['EDITOR'].include?('vim'))
|
129
|
+
opts = "-c \":set filetype=gitcommit\""
|
130
|
+
else
|
131
|
+
opts = ""
|
132
|
+
end
|
133
|
+
|
134
|
+
system("$EDITOR #{opts} #{msg.path.shellescape}")
|
135
|
+
full_message = File.open(msg.path, "r").read
|
136
|
+
lines = full_message.split("\n")
|
137
|
+
lines = lines.reject {|line| line =~ /^\s*#/ }
|
138
|
+
title = lines.shift
|
139
|
+
body = lines.join("\n").strip
|
140
|
+
|
141
|
+
if title.empty? || body.empty?
|
142
|
+
puts "You must provide a title and a body:\n"
|
143
|
+
puts title
|
144
|
+
puts
|
145
|
+
puts body
|
146
|
+
exit 1
|
147
|
+
end
|
148
|
+
|
149
|
+
return {
|
150
|
+
:title => title,
|
151
|
+
:body => body
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.get_pull_request_description_from_api(branch_name, into_branch)
|
156
|
+
octokit = Github::api
|
157
|
+
# Should succeed if authentication is setup.
|
158
|
+
pulls = octokit.pulls(Github::get_github_repo)
|
159
|
+
pull = pulls.find {|pull| branch_name == pull[:head][:ref] }
|
160
|
+
|
161
|
+
if pull
|
162
|
+
return <<-MSG
|
163
|
+
Merge #{branch_name} (##{pull[:number]}) into #{into_branch}
|
164
|
+
|
165
|
+
#{pull[:title].gsub("\r", '')}
|
166
|
+
|
167
|
+
#{pull[:body].gsub("\r", '')}
|
168
|
+
MSG
|
169
|
+
else
|
170
|
+
return "Merge #{branch_name} into #{into_branch}"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class OctokitWrapper
|
176
|
+
def initialize(*args)
|
177
|
+
@client = Octokit::Client.new(*args)
|
178
|
+
end
|
179
|
+
|
180
|
+
def method_missing(meth,*args)
|
181
|
+
begin
|
182
|
+
return @client.send(meth,*args)
|
183
|
+
rescue Octokit::Error => e
|
184
|
+
$stderr.puts "=" * 80
|
185
|
+
$stderr.puts "Github API Error"
|
186
|
+
$stderr.puts e
|
187
|
+
exit(1)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
data/lib/helpers.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
HIGHLIGHT="\033[31m"
|
2
|
+
HIGHLIGHT_OFF="\033[0m"
|
3
|
+
|
4
|
+
def fail_on_local_changes
|
5
|
+
if Git::has_uncommitted_changes
|
6
|
+
die "Cannot perform this action with a dirty working tree, " +
|
7
|
+
"please stash your changes with 'git stash save \"Some message\"'."
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def display_feature_help(command = nil, message = nil)
|
12
|
+
display_help(
|
13
|
+
:script_name => "Git Feature Branch Helper",
|
14
|
+
:commands => {
|
15
|
+
:list => "feature list",
|
16
|
+
:start => "feature start name-of-feature",
|
17
|
+
:switch => "feature switch name-of-feature [--clean]",
|
18
|
+
:finish => "feature finish name-of-feature",
|
19
|
+
:merge => "feature merge [name-of-feature]",
|
20
|
+
:pull => "feature pull",
|
21
|
+
:status => "feature status",
|
22
|
+
:stashes => "feature stashes [-v]",
|
23
|
+
:clean => "feature clean [--all]",
|
24
|
+
:'github-test' => "feature github-test"
|
25
|
+
},
|
26
|
+
:command_name => 'feature',
|
27
|
+
:command => command,
|
28
|
+
:message => message
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def display_hotfix_help(command = nil, message = nil)
|
33
|
+
display_help(
|
34
|
+
:script_name => "Git Hotfix Helper",
|
35
|
+
:commands => {
|
36
|
+
:list => "hotfix list",
|
37
|
+
:start => "hotfix start name-of-hotfix",
|
38
|
+
:switch => "hotfix switch name-of-hotfix",
|
39
|
+
:finish => "hotfix finish [name-of-hotfix]",
|
40
|
+
:merge => "hotfix merge [name-of-hotfix]"
|
41
|
+
},
|
42
|
+
:command_name => 'hotfix',
|
43
|
+
:command => command,
|
44
|
+
:message => message
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def display_help(args)
|
49
|
+
command = args[:command]
|
50
|
+
message = args[:message]
|
51
|
+
script_name = args[:script_name]
|
52
|
+
|
53
|
+
highlighted_commands = args[:commands].map do |name, desc|
|
54
|
+
help_line = " #{name.to_s.ljust(8)} #{desc}"
|
55
|
+
|
56
|
+
if name == command
|
57
|
+
HIGHLIGHT + help_line + HIGHLIGHT_OFF
|
58
|
+
else
|
59
|
+
help_line
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if message
|
64
|
+
puts HIGHLIGHT + "Error: " + HIGHLIGHT_OFF + message
|
65
|
+
puts
|
66
|
+
puts
|
67
|
+
end
|
68
|
+
|
69
|
+
die <<HELP
|
70
|
+
#{script_name}
|
71
|
+
|
72
|
+
usage:
|
73
|
+
#{highlighted_commands.join("\n")}
|
74
|
+
|
75
|
+
arguments:
|
76
|
+
name-of-#{args[:command_name]}: letters, numbers, and dashes
|
77
|
+
|
78
|
+
Look at the source to discover what each of these commands does.
|
79
|
+
|
80
|
+
HELP
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# prints out an error and the approprite help if there is not exactly one
|
85
|
+
# commandline argument
|
86
|
+
def require_argument(program, command = nil, min = 2, max = 2)
|
87
|
+
help = lambda do |msg|
|
88
|
+
if program == :hotfix
|
89
|
+
display_hotfix_help(command, msg)
|
90
|
+
else
|
91
|
+
display_feature_help(command, msg)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
if (ARGV.length > max)
|
96
|
+
help.call "Too many arguments. This command accepts only one argument."
|
97
|
+
end
|
98
|
+
|
99
|
+
if (ARGV.length < min)
|
100
|
+
help.call "Missing argument. This command requires exactly one argument."
|
101
|
+
end
|
102
|
+
|
103
|
+
if (ARGV.last !~ /^[a-zA-z0-9-]+$/)
|
104
|
+
help.call "Invalid branch name: '#{ARGV.last}'"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Repeatedly prints out a y/n question until a y or n is input
|
110
|
+
def confirm(question)
|
111
|
+
loop do
|
112
|
+
print(question)
|
113
|
+
print(" (y/n):")
|
114
|
+
STDOUT.flush
|
115
|
+
s = STDIN.gets
|
116
|
+
exit if s == nil
|
117
|
+
s.chomp!
|
118
|
+
|
119
|
+
return true if s == 'y'
|
120
|
+
return false if s == 'n'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def die(message = nil)
|
125
|
+
puts wrap_text(message) if message
|
126
|
+
exit 1
|
127
|
+
end
|
128
|
+
|
129
|
+
def highlight(str)
|
130
|
+
return HIGHLIGHT + str + HIGHLIGHT_OFF;
|
131
|
+
end
|
132
|
+
|
133
|
+
def wrap_text(txt, col = 80)
|
134
|
+
txt.gsub(
|
135
|
+
/(.{1,#{col}})(?: +|$)\n?|(.{#{col}})/,
|
136
|
+
"\\1\\3\n")
|
137
|
+
end
|
data/man/feature-clean.1
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
.\" generated with Ronn/v0.7.3
|
2
|
+
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
3
|
+
.
|
4
|
+
.TH "FEATURE\-CLEAN" "1" "February 2013" "iFixit" ""
|
5
|
+
.
|
6
|
+
.SH "NAME"
|
7
|
+
\fBfeature\-clean\fR \- Remove untracked files and submodules\.
|
8
|
+
.
|
9
|
+
.SH "SYNOPSIS"
|
10
|
+
\fBfeature clean\fR [\-\-all]
|
11
|
+
.
|
12
|
+
.SH "DESCRIPTION"
|
13
|
+
Remove untracked files and submodules\.
|
14
|
+
.
|
15
|
+
.P
|
16
|
+
Compatible with hotfix branches\.
|
17
|
+
.
|
18
|
+
.SH "OPTIONS"
|
19
|
+
.
|
20
|
+
.TP
|
21
|
+
\fB\-\-clean\fR
|
22
|
+
Remove all untracked \.gitignored files as well\.
|
23
|
+
.
|
24
|
+
.SH "COPYRIGHT"
|
25
|
+
Copyright (c) 2012\-2013 iFixit\.
|
26
|
+
.
|
27
|
+
.SH "SEE ALSO"
|
28
|
+
feature(1)
|
@@ -0,0 +1,115 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv='content-type' value='text/html;charset=utf8'>
|
5
|
+
<meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
|
6
|
+
<title>feature-clean(1) - Remove untracked files and submodules.</title>
|
7
|
+
<style type='text/css' media='all'>
|
8
|
+
/* style: man */
|
9
|
+
body#manpage {margin:0}
|
10
|
+
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
|
11
|
+
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
|
12
|
+
.mp h2 {margin:10px 0 0 0}
|
13
|
+
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
|
14
|
+
.mp h3 {margin:0 0 0 4ex}
|
15
|
+
.mp dt {margin:0;clear:left}
|
16
|
+
.mp dt.flush {float:left;width:8ex}
|
17
|
+
.mp dd {margin:0 0 0 9ex}
|
18
|
+
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
|
19
|
+
.mp pre {margin-bottom:20px}
|
20
|
+
.mp pre+h2,.mp pre+h3 {margin-top:22px}
|
21
|
+
.mp h2+pre,.mp h3+pre {margin-top:5px}
|
22
|
+
.mp img {display:block;margin:auto}
|
23
|
+
.mp h1.man-title {display:none}
|
24
|
+
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
|
25
|
+
.mp h2 {font-size:16px;line-height:1.25}
|
26
|
+
.mp h1 {font-size:20px;line-height:2}
|
27
|
+
.mp {text-align:justify;background:#fff}
|
28
|
+
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
|
29
|
+
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
|
30
|
+
.mp u {text-decoration:underline}
|
31
|
+
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
|
32
|
+
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
|
33
|
+
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
|
34
|
+
.mp b.man-ref {font-weight:normal;color:#434241}
|
35
|
+
.mp pre {padding:0 4ex}
|
36
|
+
.mp pre code {font-weight:normal;color:#434241}
|
37
|
+
.mp h2+pre,h3+pre {padding-left:0}
|
38
|
+
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
|
39
|
+
ol.man-decor {width:100%}
|
40
|
+
ol.man-decor li.tl {text-align:left}
|
41
|
+
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
|
42
|
+
ol.man-decor li.tr {text-align:right;float:right}
|
43
|
+
</style>
|
44
|
+
<style type='text/css' media='all'>
|
45
|
+
/* style: toc */
|
46
|
+
.man-navigation {display:block !important;position:fixed;top:0;left:113ex;height:100%;width:100%;padding:48px 0 0 0;border-left:1px solid #dbdbdb;background:#eee}
|
47
|
+
.man-navigation a,.man-navigation a:hover,.man-navigation a:link,.man-navigation a:visited {display:block;margin:0;padding:5px 2px 5px 30px;color:#999;text-decoration:none}
|
48
|
+
.man-navigation a:hover {color:#111;text-decoration:underline}
|
49
|
+
</style>
|
50
|
+
</head>
|
51
|
+
<!--
|
52
|
+
The following styles are deprecated and will be removed at some point:
|
53
|
+
div#man, div#man ol.man, div#man ol.head, div#man ol.man.
|
54
|
+
|
55
|
+
The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
|
56
|
+
.man-navigation should be used instead.
|
57
|
+
-->
|
58
|
+
<body id='manpage'>
|
59
|
+
<div class='mp' id='man'>
|
60
|
+
|
61
|
+
<div class='man-navigation' style='display:none'>
|
62
|
+
<a href="#NAME">NAME</a>
|
63
|
+
<a href="#SYNOPSIS">SYNOPSIS</a>
|
64
|
+
<a href="#DESCRIPTION">DESCRIPTION</a>
|
65
|
+
<a href="#OPTIONS">OPTIONS</a>
|
66
|
+
<a href="#COPYRIGHT">COPYRIGHT</a>
|
67
|
+
<a href="#SEE-ALSO">SEE ALSO</a>
|
68
|
+
</div>
|
69
|
+
|
70
|
+
<ol class='man-decor man-head man head'>
|
71
|
+
<li class='tl'>feature-clean(1)</li>
|
72
|
+
<li class='tc'></li>
|
73
|
+
<li class='tr'>feature-clean(1)</li>
|
74
|
+
</ol>
|
75
|
+
|
76
|
+
<h2 id="NAME">NAME</h2>
|
77
|
+
<p class="man-name">
|
78
|
+
<code>feature-clean</code> - <span class="man-whatis">Remove untracked files and submodules.</span>
|
79
|
+
</p>
|
80
|
+
|
81
|
+
<h2 id="SYNOPSIS">SYNOPSIS</h2>
|
82
|
+
|
83
|
+
<p><code>feature clean</code> [--all]</p>
|
84
|
+
|
85
|
+
<h2 id="DESCRIPTION">DESCRIPTION</h2>
|
86
|
+
|
87
|
+
<p>Remove untracked files and submodules.</p>
|
88
|
+
|
89
|
+
<p>Compatible with hotfix branches.</p>
|
90
|
+
|
91
|
+
<h2 id="OPTIONS">OPTIONS</h2>
|
92
|
+
|
93
|
+
<dl>
|
94
|
+
<dt> <code>--clean</code></dt><dd> Remove all untracked .gitignored files as well.</dd>
|
95
|
+
</dl>
|
96
|
+
|
97
|
+
|
98
|
+
<h2 id="COPYRIGHT">COPYRIGHT</h2>
|
99
|
+
|
100
|
+
<p>Copyright (c) 2012-2013 iFixit.</p>
|
101
|
+
|
102
|
+
<h2 id="SEE-ALSO">SEE ALSO</h2>
|
103
|
+
|
104
|
+
<p><a class="man-ref" href="feature.1.html">feature<span class="s">(1)</span></a></p>
|
105
|
+
|
106
|
+
|
107
|
+
<ol class='man-decor man-foot man foot'>
|
108
|
+
<li class='tl'>iFixit</li>
|
109
|
+
<li class='tc'>February 2013</li>
|
110
|
+
<li class='tr'>feature-clean(1)</li>
|
111
|
+
</ol>
|
112
|
+
|
113
|
+
</div>
|
114
|
+
</body>
|
115
|
+
</html>
|
@@ -0,0 +1,53 @@
|
|
1
|
+
feature-clean(1) - Remove untracked files and submodules.
|
2
|
+
=========================================================
|
3
|
+
|
4
|
+
## SYNOPSIS
|
5
|
+
|
6
|
+
`feature clean` [--all]
|
7
|
+
|
8
|
+
## DESCRIPTION
|
9
|
+
|
10
|
+
Remove untracked files and submodules.
|
11
|
+
|
12
|
+
Compatible with hotfix branches.
|
13
|
+
|
14
|
+
## OPTIONS
|
15
|
+
|
16
|
+
* `--clean`:
|
17
|
+
Remove all untracked .gitignored files as well.
|
18
|
+
|
19
|
+
## COPYRIGHT
|
20
|
+
|
21
|
+
Copyright (c) 2012-2013 iFixit.
|
22
|
+
|
23
|
+
## SEE ALSO
|
24
|
+
|
25
|
+
feature(1)
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
[SYNOPSIS]: #SYNOPSIS "SYNOPSIS"
|
30
|
+
[DESCRIPTION]: #DESCRIPTION "DESCRIPTION"
|
31
|
+
[OPTIONS]: #OPTIONS "OPTIONS"
|
32
|
+
[COPYRIGHT]: #COPYRIGHT "COPYRIGHT"
|
33
|
+
[SEE ALSO]: #SEE-ALSO "SEE ALSO"
|
34
|
+
|
35
|
+
|
36
|
+
[feature(1)]: feature.1.html
|
37
|
+
[feature-list(1)]: feature-list.1.html
|
38
|
+
[feature-start(1)]: feature-start.1.html
|
39
|
+
[feature-switch(1)]: feature-switch.1.html
|
40
|
+
[feature-finish(1)]: feature-finish.1.html
|
41
|
+
[feature-merge(1)]: feature-merge.1.html
|
42
|
+
[feature-pull(1)]: feature-pull.1.html
|
43
|
+
[feature-status(1)]: feature-status.1.html
|
44
|
+
[feature-stashes(1)]: feature-stashes.1.html
|
45
|
+
[feature-clean(1)]: feature-clean.1.html
|
46
|
+
[feature-github-test(1)]: feature-github-test.1.html
|
47
|
+
[hotfix(1)]: hotfix.1.html
|
48
|
+
[hotfix-list(1)]: hotfix-list.1.html
|
49
|
+
[hotfix-start(1)]: hotfix-start.1.html
|
50
|
+
[hotfix-switch(1)]: hotfix-switch.1.html
|
51
|
+
[hotfix-finish(1)]: hotfix-finish.1.html
|
52
|
+
[hotfix-merge(1)]: hotfix-merge.1.html
|
53
|
+
[git(1)]: https://www.kernel.org/pub/software/scm/git/docs/
|