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.
Files changed (77) hide show
  1. data/COPYING +21 -0
  2. data/README.md +124 -0
  3. data/Rakefile +35 -0
  4. data/bash_completion.sh +33 -0
  5. data/bin/feature +191 -0
  6. data/bin/hotfix +142 -0
  7. data/lib/git.rb +170 -0
  8. data/lib/github.rb +190 -0
  9. data/lib/helpers.rb +137 -0
  10. data/man/feature-clean.1 +28 -0
  11. data/man/feature-clean.1.html +115 -0
  12. data/man/feature-clean.1.markdown +53 -0
  13. data/man/feature-clean.1.ronn +26 -0
  14. data/man/feature-finish.1 +19 -0
  15. data/man/feature-finish.1.html +107 -0
  16. data/man/feature-finish.1.markdown +47 -0
  17. data/man/feature-finish.1.ronn +21 -0
  18. data/man/feature-github-test.1 +19 -0
  19. data/man/feature-github-test.1.html +105 -0
  20. data/man/feature-github-test.1.markdown +45 -0
  21. data/man/feature-github-test.1.ronn +19 -0
  22. data/man/feature-list.1 +19 -0
  23. data/man/feature-list.1.html +106 -0
  24. data/man/feature-list.1.markdown +46 -0
  25. data/man/feature-list.1.ronn +20 -0
  26. data/man/feature-merge.1 +22 -0
  27. data/man/feature-merge.1.html +110 -0
  28. data/man/feature-merge.1.markdown +50 -0
  29. data/man/feature-merge.1.ronn +24 -0
  30. data/man/feature-start.1 +19 -0
  31. data/man/feature-start.1.html +105 -0
  32. data/man/feature-start.1.markdown +45 -0
  33. data/man/feature-start.1.ronn +19 -0
  34. data/man/feature-stashes.1 +28 -0
  35. data/man/feature-stashes.1.html +117 -0
  36. data/man/feature-stashes.1.markdown +55 -0
  37. data/man/feature-stashes.1.ronn +28 -0
  38. data/man/feature-status.1 +22 -0
  39. data/man/feature-status.1.html +108 -0
  40. data/man/feature-status.1.markdown +48 -0
  41. data/man/feature-status.1.ronn +22 -0
  42. data/man/feature-switch.1 +25 -0
  43. data/man/feature-switch.1.html +113 -0
  44. data/man/feature-switch.1.markdown +51 -0
  45. data/man/feature-switch.1.ronn +24 -0
  46. data/man/feature.1 +61 -0
  47. data/man/feature.1.html +123 -0
  48. data/man/feature.1.markdown +70 -0
  49. data/man/feature.1.ronn +43 -0
  50. data/man/feature.html +140 -0
  51. data/man/hotfix-finish.1 +19 -0
  52. data/man/hotfix-finish.1.html +107 -0
  53. data/man/hotfix-finish.1.markdown +47 -0
  54. data/man/hotfix-finish.1.ronn +21 -0
  55. data/man/hotfix-list.1 +19 -0
  56. data/man/hotfix-list.1.html +106 -0
  57. data/man/hotfix-list.1.markdown +46 -0
  58. data/man/hotfix-list.1.ronn +20 -0
  59. data/man/hotfix-merge.1 +22 -0
  60. data/man/hotfix-merge.1.html +110 -0
  61. data/man/hotfix-merge.1.markdown +50 -0
  62. data/man/hotfix-merge.1.ronn +24 -0
  63. data/man/hotfix-start.1 +19 -0
  64. data/man/hotfix-start.1.html +105 -0
  65. data/man/hotfix-start.1.markdown +45 -0
  66. data/man/hotfix-start.1.ronn +19 -0
  67. data/man/hotfix-switch.1 +19 -0
  68. data/man/hotfix-switch.1.html +105 -0
  69. data/man/hotfix-switch.1.markdown +45 -0
  70. data/man/hotfix-switch.1.ronn +19 -0
  71. data/man/hotfix.1 +41 -0
  72. data/man/hotfix.1.html +118 -0
  73. data/man/hotfix.1.markdown +60 -0
  74. data/man/hotfix.1.ronn +33 -0
  75. data/man/index.html +7 -0
  76. data/man/index.txt +21 -0
  77. 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
@@ -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/