git_helpers 0.1.0 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,347 +1,116 @@
1
1
  #!/usr/bin/env ruby
2
- # Inspired by https://github.com/olivierverdier/zsh-git-prompt
3
- # [commit: 350be32093d0585f395413253536d891c247f538,
4
- # last commit checked: 0a6c8b610e799040b612db8888945f502a2ddd9d (2016-02-14))
5
- #Inspired by the contrib git script
6
2
 
7
- require "open3"
8
- require "pathname"
9
- require "shellwords"
10
- require "optparse"
11
- require "simplecolor"
12
- #require "dr/git" #TODO merge the two implems
13
- SimpleColor.mix_in_string
3
+ require "git_helpers"
4
+ require 'optparse'
14
5
 
15
- module GitStatus
16
- module Run
17
- extend(self)
18
- #if we get interrupted once, we don't want to launch any more commands
19
- @interrupted=false
20
- def runstatus(*args)
21
- if !@interrupted
22
- begin
23
- if Open3.respond_to?(:capture3) then
24
- out, error, status=Open3.capture3(*args)
25
- return out, status.success?
26
- else
27
- out = `#{args} 2>/dev/null`
28
- status=$?
29
- return out, status.success?
30
- end
31
- rescue Interrupt #interruption
32
- @interrupted=true
33
- return "", false
34
- end
35
- else
36
- return "", false
37
- end
38
- end
39
- def run(*args)
40
- msg,_=runstatus(*args)
41
- return msg
42
- end
43
- end
44
-
45
- class Git
46
- include GitStatus::Run
47
- attr_reader :msg
48
-
49
- def git?
50
- if @git.nil?
51
- _,@git=runstatus "git rev-parse"
52
- end
53
- return @git
54
- end
55
- def getgitdir
56
- return Pathname.new((run "git rev-parse --git-dir").chomp)
57
- end
58
- def ingitdir?
59
- return (run "git rev-parse --is-inside-git-dir") == "true\n"
60
- end
61
- def worktree?
62
- return (run "git rev-parse --is-inside-work-tree") == "true\n"
63
- end
64
- def bare?
65
- return (run "git rev-parse --is-bare-repository") == "true\n"
66
- end
67
-
68
- def cd_and_exec(*args)
69
- if @path.nil? then
70
- yield(*args)
71
- else
72
- if File.directory?(@path)
73
- Dir.chdir(@path) do
74
- yield(*args)
75
- end
76
- else
77
- warn "#{@path} is not a directory"
78
- end
79
- end
80
- end
81
-
82
- def initialize(path=nil)
83
- #a nil path means we want information on the current directory
84
- if !path.nil?
85
- @path=Pathname.new(path).expand_path
86
- end
87
- cd_and_exec {git?}
88
- end
89
-
90
- def get_msg
91
- #gitst="git status --porcelain --branch"
92
- #too many git are too old to mix --porcelain with --branch
93
- gitm=git="git"
94
- gitm="#{git} -c color.ui=always" if $opts[:color]
95
- gitm="#{gitm} status --short --branch"
96
- @msg=run(gitm)
97
- end
98
-
99
- def describe_detached_head
100
- case $opts[:describe]
101
- when "sha1"
102
- describe=(run "git rev-parse --short HEAD").chomp
103
- when "describe"
104
- describe=(run "git describe HEAD").chomp
105
- when "contains"
106
- describe=(run "git describe --contains HEAD").chomp
107
- when "branch"
108
- describe=(run "git describe --contains --all HEAD").chomp
109
- when "match"
110
- describe=(run "git describe --tags --exact-match HEAD").chomp
111
- when "all" #try --contains all, then --all
112
- describe=(run "git describe --contains --all HEAD").chomp
113
- describe=(run "git describe --all HEAD").chomp if describe.nil? or describe.empty?
114
- when "magic"
115
- describe1=(run "git describe --contains --all HEAD").chomp
116
- describe2=(run "git describe --all HEAD").chomp
117
- describe= describe1.length < describe2.length ? describe1 : describe2
118
- describe=describe1 if describe2.empty?
119
- describe=describe2 if describe1.empty?
120
- else
121
- describe=(run($opts[:describe])).chomp
122
- end
123
- if describe.empty?
124
- describe=(run "git rev-parse --short HEAD").chomp
125
- end
126
- @branch=":#{describe}"
127
- end
128
-
129
- def parse_head(head)
130
- @ahead=@behind=0
131
- if (head =~ /## Initial commit on (\S*)/) then
132
- @branch=$1
133
- if @branch =~ /(\S*)\.\.\./
134
- @branch=$1
135
- end
136
- @branch+="…"
137
- elsif (head =~ /## (\S*) \(no branch\)/) then
138
- describe_detached_head
139
- elsif (head =~ /## (\S*)(.*)/) then
140
- branchs=$1
141
- rest=$2
142
- if (branchs =~ /(\S*)\.\.\.(\S*)/) then
143
- @branch=$1
144
- remote=$2
145
- else
146
- @branch=branchs
147
- end
148
- if (rest =~ /.*\[ahead\s+(\d*)(.*)/) then
149
- @ahead=$1.to_i
150
- rest=$2
151
- end
152
- if (rest =~ /.*behind\s+(\d*)\]/) then
153
- @behind=$1.to_i
154
- end
155
- end
156
- end
157
-
158
- def parse_msg
159
- msg=@msg
160
- #find the branch name, and if we are behind/ahead of upstream
161
- lines=msg.uncolor.lines.to_a
162
- return if lines.empty?
163
- head=lines.shift
164
- parse_head(head)
165
-
166
- #get status of files
167
- @changed=@staged=@untracked=@conflicts=0
168
- lines.each do |line|
169
- index = line[0];
170
- workdir = line[1];
171
- #puts "index: #{index}, workdir: #{workdir}"
172
- if index=~/[DRAMTC]/ then
173
- @staged+=1
174
- end
175
- if workdir=~ /[DMT]/ then
176
- @changed+=1
177
- end
178
- if workdir=='?' || index=='?' then
179
- @untracked+=1
180
- end
181
- if workdir=='U' || index=='U' then
182
- @conflicts+=1
183
- end
184
- end
185
- @clean=true
186
- @clean=false if @staged != 0 || @changed !=0 ||
187
- @untracked !=0 || @conflicts !=0
188
- end
189
-
190
- def get_status
191
- if worktree?
192
- get_msg
193
- parse_msg
194
- if $opts[:sequencer] and !@msg.empty?
195
- @sequencer=""
196
- gitdir=getgitdir
197
- if (gitdir+"rebase-merge").directory?
198
- if (gitdir+"rebase-merge/interactive").file?
199
- @sequencer<<" rb-i " #REBASE-i
200
- else
201
- @sequencer<<" rb-m " #REBASE-m
202
- end
203
- @sequencer<<(gitdir+"rebase-merge/head-name").read.chomp.sub(/^refs\/heads\//,"")
204
- end
205
- if (gitdir+"rebase-apply").directory?
206
- if (gitdir+"rebase-apply/rebasing").file?
207
- @sequencer<<" rb" #RB
208
- elsif (gitdir+"rebase-apply/applying").file?
209
- @sequencer<<" am" #AM
210
- else
211
- @sequencer<<" am/rb" #AM/REBASE
212
- end
213
- end
214
- if (gitdir+"MERGE_HEAD").file?
215
- @sequencer<<" mg" #MERGING
216
- end
217
- if (gitdir+"CHERRY_PICK_HEAD").file?
218
- @sequencer<<" ch" #CHERRY-PICKING
219
- end
220
- if (gitdir+"BISECT_LOG").file?
221
- @sequencer<<" bi" #BISECTING
222
- end
223
- _,stashstatus=runstatus "git rev-parse --verify refs/stash"
224
- if stashstatus
225
- stashs=run "git rev-list -g refs/stash"
226
- @sequencer<<" $#{stashs.lines.to_a.length}" #Stash
227
- end
228
- end
229
- return !@msg.empty?
230
- else
231
- if $opts[:sequencer]
232
- if ingitdir?
233
- if bare?
234
- @branch="|bare|"
235
- else
236
- @branch="|.git|"
237
- end
238
- end
239
- end
240
- end
241
- return false
242
- end
243
-
244
- def status
245
- cd_and_exec { get_status } if git?
246
- end
247
-
248
- def prompt
249
- if status
250
- return "(" <<
251
- @branch.color(:magenta,:bold) <<
252
- (@ahead==0 ? "" : "↑"<<@ahead.to_s ) <<
253
- (@behind==0 ? "" : "↓"<<@behind.to_s ) <<
254
- "|" <<
255
- (@staged==0 ? "" : ("●"+@staged.to_s).color(:red) ) <<
256
- (@conflicts==0 ? "" : ("✖"+@conflicts.to_s).color(:red) ) <<
257
- (@changed==0 ? "" : ("✚"+@changed.to_s).color(:blue) ) <<
258
- (@untracked==0 ? "" : "…" ) <<
259
- (@clean ? "✔".color(:green,:bold) : "" ) <<
260
- (@sequencer.empty? ? "" : @sequencer.color(:yellow) ) <<
261
- ")"
262
- else
263
- return "(" << @branch.color(:magenta,:bold) << ")" if @branch
264
- end
265
- end
266
-
267
- def porcelain
268
- if git?
269
- return "#{@branch}\n#{@ahead}\n#{@behind}\n#{@staged}\n#{@conflicts}\n#{@changed}\n#{@untracked}\n#{@clean?1:0}\n#{@sequencer}\n"
270
- else
271
- return ""
272
- end
273
- end
274
- end
275
- end
276
-
277
- $opts={:color => true, :indent => nil, :sequencer => true, :describe => "magic"}
6
+ opts={:color => true}
278
7
  optparse = OptionParser.new do |opt|
279
8
  opt.banner= "#{File.basename($0)} [options] git_dirs"
280
9
  opt.on("-p", "--[no-]prompt", "To be used in shell prompt", "This ensure that color ansi sequence are escaped so that they are not counted as text by the shell") do |v|
281
- $opts[:prompt]=v
282
- end
283
- opt.on("--[no-]porcelain", "Don't format the status but output it in a machine convenient format") do |v|
284
- $opts[:porcelain]=v
10
+ opts[:prompt]=v
11
+ opts[:max_length]=40 if v
285
12
  end
286
- opt.on("-s", "--[no-]status", "List file", "Print the output of git status additionally of what this program parse") do |v|
287
- $opts[:status]=v
13
+ opt.on("-s", "--[no-]status[=options]", "List file", "Print the output of git status additionally of what this program parse") do |v|
14
+ opts[:status]=v
288
15
  end
289
16
  opt.on("-c", "--[no-]color", "Color output", "on by default") do |v|
290
- $opts[:color]=v
17
+ opts[:color]=v
291
18
  end
292
19
  opt.on("--[no-]sequencer", "Show sequencer data (and also look for bare directory)", "on by default") do |v|
293
- $opts[:sequencer]=v
20
+ opts[:sequencer]=v
294
21
  end
295
22
  opt.on("--indent spaces", Integer, "Indent to use if showing git status", "2 by default, 0 for empty ARGV") do |v|
296
- $opts[:indent]=v
23
+ opts[:indent]=v
24
+ end
25
+ opt.on("--describe sha1/describe/contains/branch/match/all/magic", "How to describe a detached HEAD", "'branch-fb' by default") do |v|
26
+ opts[:detached_name]=v
27
+ end
28
+ opt.on("--[no-]ignored[=full]", "-i", "Show ignored files") do |v|
29
+ opts[:ignored]=v
30
+ end
31
+ opt.on("--[no-]untracked[=full]", "-u", "Show untracked files") do |v|
32
+ opts[:untracked]=v
33
+ end
34
+ opt.on("--[no-]branch", "Get branch infos (true by default)") do |v|
35
+ opts[:branch]=v
36
+ end
37
+ opt.on("--[no-]files", "Get files infos (true by default)") do |v|
38
+ opts[:files]=v
297
39
  end
298
- opt.on("--describe sha1/describe/contains/branch/match/all/magic", "How to describe a detached HEAD", "'magic' by default") do |v|
299
- $opts[:describe]=v
40
+ opt.on("--use=branch_name", "Show a different branch than HEAD") do |v|
41
+ opts[:use]=v
42
+ end
43
+ opt.on("--[no-]raw", "Show raw status infos") do |v|
44
+ opts[:raw]=v
300
45
  end
301
46
  opt.on("--sm", "Recurse on each submodules") do |v|
302
- $opts[:submodules]=v
47
+ opts[:submodules]=v
48
+ end
49
+ opt.on("--max-length=length", "Maximum status length", Integer) do |v|
50
+ opts[:max_length]=v
51
+ end
52
+ opt.on("--[no-]debug", "Debug git calls") do |v|
53
+ opts[:debug]=v
303
54
  end
304
55
  end
305
56
  optparse.parse!
306
57
 
307
- if !$opts[:color]
58
+ if !opts[:color]
308
59
  SimpleColor.enabled=false
309
60
  end
61
+ if opts[:debug]
62
+ SH.debug
63
+ end
310
64
 
311
65
  def prettify_dir(dir)
312
- return dir.sub(/^#{ENV['HOME']}/,"~")
66
+ return '' if dir.nil?
67
+ return (dir.sub(/^#{ENV['HOME']}/,"~"))+": "
313
68
  end
314
69
 
315
- def gs_output(dir)
316
- g=GitStatus::Git.new(dir)
317
- puts "#{prettify_dir(dir)+": " if dir}#{g.prompt}"
318
- if $opts[:status] and g.git?
319
- g.msg.lines.each do |line|
320
- print " "*$opts[:indent] + line
70
+ def gs_output(dir=".", **opts)
71
+ g=GitHelpers::GitDir.new(dir || ".")
72
+ status={}
73
+ arg=opts.delete(:use)
74
+ args= arg.nil? ? [] : [arg]
75
+ if opts[:raw]
76
+ puts "#{prettify_dir(dir)}#{g.status(*args,**opts)}"
77
+ else
78
+ puts "#{prettify_dir(dir)}#{g.format_status(*args,**opts) {|s| status=s}}"
79
+ end
80
+ if opts[:status] and g.worktree?
81
+ g.with_dir do
82
+ options=opts[:status]
83
+ if options.is_a?(String)
84
+ options=options.split(',')
85
+ else
86
+ options=[]
87
+ end
88
+ out=SH.run_simple("git #{opts[:color] ? "-c color.ui=always" : ""} status --short #{(status[:status_options]+options).shelljoin}")
89
+ out.each_line.each do |line|
90
+ print " "*(opts[:indent]||0) + line
91
+ end
321
92
  end
322
93
  end
323
94
  end
324
95
 
325
- if $opts[:porcelain]
326
- puts GitStatus::Git.new.porcelain
327
- elsif $opts[:prompt]
96
+ if opts[:prompt]
328
97
  SimpleColor.enabled=:shell
329
- prompt=GitStatus::Git.new.prompt
98
+ prompt=GitHelpers.create.format_status(**opts)
330
99
  puts prompt if prompt #in ruby1.8, puts nil output nil...
331
100
  else
332
101
  args=ARGV
333
102
  if args.empty?
334
- $opts[:indent]=0 unless $opts[:indent]
103
+ opts[:indent]=0 unless opts[:indent]
335
104
  args=[nil]
336
105
  else
337
- $opts[:indent]=2 unless $opts[:indent]
106
+ opts[:indent]=2 unless opts[:indent]
338
107
  end
339
108
  args.each do |dir|
340
- gs_output(dir)
341
- if $opts[:submodules]
342
- Dir.chdir(dir||".") do
343
- %x/git submodule status/.each_line.map { |l| l.split[1] }.each do |dir|
344
- gs_output(dir)
109
+ gs_output(dir,**opts)
110
+ if opts[:submodules]
111
+ g.with_dir do
112
+ %x/git submodule status/.each_line.map { |l| l.split[1] }.each do |sdir|
113
+ gs_output(sdir, **opts)
345
114
  end
346
115
  end
347
116
  end
@@ -1,344 +1,38 @@
1
1
  require 'git_helpers/version'
2
- require 'git_helpers/extra_helpers'
2
+ require 'simplecolor/mixin'
3
+ require 'shell_helpers'
3
4
  require 'dr/base/bool'
4
- require 'pathname'
5
+ require 'git_helpers/git_dir'
6
+ require 'git_helpers/branch'
5
7
 
8
+ #git functions helper
9
+ #small library wrapping git; use rugged for more interesting things
6
10
  module GitHelpers
7
- #git functions helper
8
-
9
- #small library wrapping git; use rugged for more interesting things
10
- class GitDir
11
- attr_accessor :dir
12
- def initialize(dir=".")
13
- @dir=Pathname.new(dir.to_s).realpath
14
- end
15
-
16
- def to_s
17
- @dir.to_s
18
- end
19
-
20
-
21
- #we could also use 'git -C #{@dir}' for each git invocation
22
- def with_dir
23
- Dir.chdir(@dir) { yield }
24
- end
25
-
26
- def all_files
27
- with_dir do
28
- %x/git ls-files -z/.split("\0")
29
- end
30
- end
31
-
32
- #are we in a git folder?
33
- def git?(quiet: false)
34
- launch="git rev-parse"
35
- launch=launch + " 2>/dev/null" if quiet
36
- with_dir do
37
- system launch
38
- return Bool.to_bool($?)
39
- end
40
- end
41
-
42
- #are we in .git/?
43
- def gitdir?
44
- with_dir do
45
- return Bool.to_bool(%x/git rev-parse --is-inside-git-dir/)
46
- end
47
- end
48
- #are we in the worktree?
49
- def worktree?
50
- with_dir do
51
- return Bool.to_bool(%x/git rev-parse --is-inside-work-tree/)
52
- end
53
- end
54
- #are we in a bare repo?
55
- def bare?
56
- with_dir do
57
- return Bool.to_bool(%x/git rev-parse --is-bare-repository/)
58
- end
59
- end
60
-
61
- #return the absolute path of the toplevel
62
- def toplevel
63
- with_dir do
64
- return Pathname.new(%x/git rev-parse --show-toplevel/.chomp)
65
- end
66
- end
67
- #relative path from toplevel to @dir
68
- def prefix
69
- with_dir do
70
- return Pathname.new(%x/git rev-parse --show-prefix/.chomp)
71
- end
72
- end
73
- #return the relative path from @dir to the toplevel
74
- def relative_toplevel
75
- with_dir do
76
- return Pathname.new(%x/git rev-parse --show-cdup/.chomp)
77
- end
78
- end
79
- #get path to .git directory (can be relative or absolute)
80
- def gitdir
81
- with_dir do
82
- return Pathname.new(%x/git rev-parse --git-dir/.chomp)
83
- end
84
- end
85
-
86
- def with_toplevel(&b)
87
- with_dir do
88
- dir=relative_toplevel
89
- if !dir.to_s.empty?
90
- Dir.chdir(dir,&b)
91
- else
92
- warn "No toplevel found, executing inside dir #{@dir}"
93
- with_dir(&b)
94
- end
95
- end
96
- end
97
-
98
- #return a list of submodules
99
- def submodules
100
- with_dir do
101
- return %x/git submodule status/.each_line.map { |l| l.split[1] }
102
- end
103
- end
104
-
105
- def get_config(*args)
106
- with_dir do
107
- return %x/git config #{args.shelljoin}/.chomp
108
- end
109
- end
110
-
111
- def current_branch(always: true)
112
- with_dir do
113
- branchname= %x/git symbolic-ref -q --short HEAD/.chomp!
114
- branchname||= %x/git rev-parse --verify HEAD/.chomp! if always
115
- return branch(branchname)
116
- end
117
- end
118
-
119
- def head
120
- return branch('HEAD')
121
- end
122
-
123
- #return all branches that have an upstream
124
- #if branches=:all look through all branches
125
- def all_upstream_branches(branches)
126
- #TODO
127
- upstreams=%x!git for-each-ref --format='%(upstream:short)' refs/heads/branch/!
128
- end
129
-
130
- def get_topic_branches(*branches, complete: :local)
131
- if branches.length >= 2
132
- return branch(branches[0]), branch(branches[1])
133
- elsif branches.length == 1
134
- b=branch(branches[0])
135
- if complete == :local
136
- return current_branch, b
137
- elsif complete == :remote
138
- return b, b.upstream
139
- else
140
- fail "complete keyword should be :local or :remote"
141
- end
142
- else
143
- c=current_branch
144
- return c, c.upstream
145
- end
146
- end
147
-
148
- def branch(branch="HEAD")
149
- GitBranch.new(branch, dir: @self)
150
- end
151
-
152
- def branch_infos(*branches, local: false, remote: false, tags: false)
153
- query=branches.map {|b| name_branch(b, method: 'full_name')}
154
- query << 'refs/heads' if local
155
- query << 'refs/remotes' if remote
156
- query << 'refs/tags' if tags
157
- r={}
158
- format=%w(refname refname:short objecttype objectsize objectname upstream upstream:short upstream:track upstream:remotename upstream:remoteref push push:short push:remotename push:remoteref HEAD symref)
159
- out=SH::Run.run_simple("git for-each-ref --format '#{format.map {|f| "%(#{f})"}.join(',')}, ' #{query.shelljoin}", chomp: :lines)
160
- out.each do |l|
161
- infos=l.split(',')
162
- full_name=infos[0]
163
- r[full_name]=Hash[format.zip(infos)]
164
- type=if full_name.start_with?("refs/heads/")
165
- :local
166
- elsif full_name.start_with?("refs/remotes/")
167
- :remote
168
- elsif full_name.start_with?("refs/tags/")
169
- :tags
170
- end
171
- name = case type
172
- when :local
173
- full_name.delete_prefix("refs/heads/")
174
- when :remote
175
- full_name.delete_prefix("refs/remotes/")
176
- when :tags
177
- full_name.delete_prefix("refs/tags/")
178
- end
179
- r[full_name][:type]=type
180
- r[full_name][:name]=name
181
- end
182
- r
183
- end
184
-
185
- def name_branch(branch,*args)
186
- self.branch(branch).name(*args)
187
- end
188
- end
11
+ DefaultLogOptions=["-M", "-C", "--no-color"].shelljoin
12
+ # we only call git to get status updates, we never modify the git dir
13
+ # so locks are not required, pass that information through the env
14
+ # variabole:
15
+ ENV['GIT_OPTIONAL_LOCKS']="0"
16
+ # another solution would be to invoke git via git --no-optional-locks
17
+ # each time. For now the env variable is easier to use.
18
+ # Note that the only optional lock is for git status currently.
19
+ # There is the following trade-off: If git-status will not take locks, it
20
+ # cannot update the index to save refresh information and reuse the next
21
+ # time. So do we want to use this?
189
22
 
190
23
  extend self
191
24
  add_instance_methods = lambda do |klass|
192
25
  klass.instance_methods(false).each do |m|
193
- define_method(m) do |*args,&b|
194
- GitDir.new.public_send(m,*args,&b)
26
+ define_method(m) do |*args,**kws,&b|
27
+ GitDir.new.public_send(m,*args,**kws,&b)
195
28
  end
196
29
  end
197
30
  end
198
- add_instance_methods.call(GitDir)
199
- add_instance_methods.call(GitStats)
200
- add_instance_methods.call(GitExtraInfos)
201
-
202
- class GitBranch
203
- attr_accessor :gitdir
204
- attr_accessor :branch
205
- attr_writer :infos
206
-
207
- def initialize(branch="HEAD", dir: ".")
208
- @gitdir=dir.is_a?(GitDir) ? dir : GitDir.new(dir)
209
- @branch=branch
210
- end
211
-
212
- def new_branch(name)
213
- self.class.new(name, @gitdir)
214
- end
215
-
216
- def to_s
217
- @branch.to_s
218
- end
219
-
220
- def nil?
221
- @branch.nil?
222
- end
223
-
224
- def shellescape
225
- @branch.shellescape
226
- end
227
-
228
- def infos
229
- return @infos if @infos
230
- infos=branch_infos
231
- type=infos[:type]
232
- if type == :local
233
- rebase=gitdir.get_config("branch.#{name}.rebase")
234
- rebase = false if rebase.empty?
235
- rebase = true if rebase == "true"
236
- infos[:rebase]=rebase
237
- end
238
- @infos=infos
239
- end
240
-
241
- def name(method: "name", always: true)
242
- @gitdir.with_dir do
243
- case method
244
- when "sha1"
245
- describe=%x"git rev-parse --short #{@branch.shellescape}".chomp!
246
- when "describe"
247
- describe=%x"git describe #{@branch.shellescape}".chomp!
248
- when "contains"
249
- describe=%x"git describe --contains #{@branch.shellescape}".chomp!
250
- when "match"
251
- describe=%x"git describe --tags --exact-match #{@branch.shellescape}".chomp!
252
- when "topic"
253
- describe=%x"git describe --all #{@branch.shellescape}".chomp!
254
- when "branch"
255
- describe=%x"git describe --contains --all #{@branch.shellescape}".chomp!
256
- when "topic-fb" #try --all, then --contains all
257
- describe=%x"git describe --all #{@branch.shellescape}".chomp!
258
- describe=%x"git describe --contains --all #{@branch.shellescape}".chomp! if describe.nil? or describe.empty?
259
- when "branch-fb" #try --contains all, then --all
260
- describe=%x"git describe --contains --all #{@branch.shellescape}".chomp!
261
- describe=%x"git describe --all #{@branch.shellescape}".chomp! if describe.nil? or describe.empty?
262
- when "magic"
263
- describe1=%x"git describe --contains --all #{@branch.shellescape}".chomp!
264
- describe2=%x"git describe --all #{@branch.shellescape}".chomp!
265
- describe= describe1.length < describe2.length ? describe1 : describe2
266
- describe=describe1 if describe2.empty?
267
- describe=describe2 if describe1.empty?
268
- when "name"
269
- describe=%x"git rev-parse --abbrev-ref --symbolic-full-name #{@branch.shellescape}".chomp!
270
- when "full_name"
271
- describe=%x"git rev-parse --symbolic-full-name #{@branch.shellescape}".chomp!
272
- when "symbolic"
273
- describe=%x"git rev-parse --symbolic #{@branch.shellescape}".chomp!
274
- else
275
- describe=%x/#{method}/.chomp! unless method.nil? or method.empty?
276
- end
277
- if (describe.nil? or describe.empty?) and always
278
- describe=%x/git rev-parse --short #{@branch.shellescape}/.chomp!
279
- end
280
- return describe
281
- end
282
- end
283
-
284
- def rebase?
285
- @gitdir.with_dir do
286
- rb=%x/git config --bool branch.#{@branch.shellescape}.rebase/.chomp!
287
- rb||=%x/git config --bool pull.rebase/.chomp!
288
- return rb=="true"
289
- end
290
- end
291
-
292
- def remote
293
- @gitdir.with_dir do
294
- rm=%x/git config --get branch.#{@branch.shellescape}.remote/.chomp!
295
- rm||="origin"
296
- return rm
297
- end
298
- end
299
-
300
- def push_remote
301
- @gitdir.with_dir do
302
- rm= %x/git config --get branch.#{@branch.shellescape}.pushRemote/.chomp! ||
303
- %x/git config --get remote.pushDefault/.chomp! ||
304
- remote
305
- return rm
306
- end
307
- end
308
-
309
- def upstream
310
- @gitdir.with_dir do
311
- up=%x/git rev-parse --abbrev-ref #{@branch.shellescape}@{u}/.chomp!
312
- return new_branch(up)
313
- end
314
- end
315
-
316
- def push
317
- @gitdir.with_dir do
318
- pu=%x/git rev-parse --abbrev-ref #{@branch.shellescape}@{push}/.chomp!
319
- return new_branch(pu)
320
- end
321
- end
322
-
323
- def hash
324
- @hash||=`git rev-parse #{@branch.shellescape}`.chomp!
325
- end
326
-
327
- def ==(other)
328
- @branch == other.branch && @gitdir=other.gitdir
329
- end
330
-
331
- #return upstream + push if push !=upstream
332
- def related
333
- up=upstream
334
- pu=push
335
- pu=new_branch(nil) if up==pu
336
- return up, pu
337
- end
338
-
339
- def branch_infos
340
- @gitdir.branch_infos(@branch).values.first
341
- end
31
+ GitDir.ancestors.each do |mod|
32
+ add_instance_methods.call(mod) if mod.to_s =~ /^GitHelpers::/
342
33
  end
343
34
 
35
+ def self.create(dir='.')
36
+ GitDir.new(dir)
37
+ end
344
38
  end