Class: Git::Trac::Repository

Inherits:
Object
  • Object
show all
Defined in:
lib/git/trac/pager.rb,
lib/git/trac/repository.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dir = nil, options = {}) ⇒ Repository

Returns a new instance of Repository.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/git/trac/repository.rb', line 10

def initialize(dir = nil, options = {})
  options, dir = dir, nil if dir.kind_of?(Hash)
  dir ||= ENV["GIT_DIR"]
  dir ||= Dir.getwd
  Dir.chdir(dir) do
    @git_dir = Open3.popen3("git","rev-parse","--git-dir") do |i,o,e|
      e.read.empty? ? o.read.chomp("\n") : nil
    end
    raise Git::Trac::Error, "Not a git repository" unless @git_dir
    @git_dir = File.join(".",@git_dir) if @git_dir[0] == ?~
    @git_dir = File.expand_path(@git_dir)
  end
  @options = options
  reload
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object



66
67
68
69
70
71
72
# File 'lib/git/trac/repository.rb', line 66

def method_missing(method,*args,&block)
  if method.to_s =~ /^git_/
    exec(method.to_s.tr('_-','-_'),*args, &block)
  else
    super(method,*args,&block)
  end
end

Instance Attribute Details

#git_dirObject (readonly)

Returns the value of attribute git_dir.



8
9
10
# File 'lib/git/trac/repository.rb', line 8

def git_dir
  @git_dir
end

#optionsObject (readonly)

Returns the value of attribute options.



8
9
10
# File 'lib/git/trac/repository.rb', line 8

def options
  @options
end

Instance Method Details

#agentObject



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/git/trac/repository.rb', line 147

def agent
  unless defined?(@agent)
    unless url
      raise Git::Trac::Error, "missing trac url: consider `git config trac.url http://tracurl`"
    end
    require 'mechanize'
    @agent = WWW::Mechanize.new {|a| a.log = nil}
    url = "#{url()}/login"
    if options[:password]

      begin
        page = @agent.get(url)
      rescue WWW::Mechanize::ResponseCodeError => e
        # 401 Forbidden, let's try HTTP authentication
        raise e unless e.response_code == "401"
        @agent.basic_auth(options[:username],options[:password])
        begin
          page = @agent.get(url)
          return @agent
        rescue WWW::Mechanize::ResponseCodeError => e
          raise e unless e.response_code == "401"
          raise Git::Trac::Error, "invalid username or password"
        end
      end

      # Form authentication
      agent.redirect_ok = false
      form = page.forms.last
      form.fields.name("user").value = options[:username]
      form.fields.name("password").value = options[:password]
      form.action = url # Mechanize gets this wrong
      begin
        page = @agent.submit(form)
        unless page.respond_to?(:code) && page.code == "303"
          raise Git::Trac::Error, "invalid username or password"
        end
      ensure
        agent.redirect_ok = true
      end
    end

  end
  @agent
end

#cleanup_branches(options, *revs) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/git/trac/repository.rb', line 201

def cleanup_branches(options, *revs)
  return if revs.empty?
  diff_hashes = revs.map {|r| diff_hash(r)}.compact if options[:rebased]
  current = current_checkout
  each_ref("refs/heads") do |object, ref|
    if current != File.basename(ref)
      if revs.include?(object)
        exec("git","branch","-D",File.basename(ref))
      elsif diff_hashes && diff_hashes.include?(dh = diff_hash(ref))
        exec("git","branch","-D",File.basename(ref))
      end
    end
  end
end

#config(arg1 = nil, arg2 = nil) ⇒ Object

Parse the output of git-config and return it as a hash.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/git/trac/repository.rb', line 114

def config(arg1 = nil, arg2 = nil)
  unless @config
    @config = {}
    exec("git","config","-l") do |line|
      key, value = line.chomp.split("=",2)
      superkey, subkey = key.match(/(.*)\.(.*)/).to_a[1,2]
      @config[superkey] ||= {}
      @config[superkey][subkey.tr('-_','_-').to_sym] = value
      # hash[key] ||= []
      # hash[key] << value
      # @config[key] = value
    end
  end
  if arg2
    (@config[arg1.to_s]||{})[arg2.to_sym]
  elsif arg1
    @config[arg1]
  else
    @config
  end
end

#current_checkoutObject



95
96
97
98
99
100
101
102
# File 'lib/git/trac/repository.rb', line 95

def current_checkout
  contents = File.read("#{git_dir}/HEAD").chomp
  if contents =~ %r{^ref: refs/heads/(.*)}
    $1
  else
    contents
  end
end

#diff_hash(head) ⇒ Object



216
217
218
219
220
221
222
223
224
# File 'lib/git/trac/repository.rb', line 216

def diff_hash(head)
  require 'digest/sha1'
  guess = guess_upstream(head)
  return unless guess
  diff = exec("git","diff","#{guess}...#{head}")
  diff.gsub!(/^(?:index |@@ -).*\n/,'')
  diff.gsub!(/[[:space:]]/,'')
  Digest::SHA1.digest(diff)
end

#each_ref(*args) ⇒ Object



88
89
90
91
92
93
# File 'lib/git/trac/repository.rb', line 88

def each_ref(*args)
  pattern = args.empty? ? "refs" : args.join("/")
  exec("git","for-each-ref", "--format=%(objectname) %(refname)", pattern) do |line|
    yield *line.chomp.split(" ",2)
  end
end

#exec(*args, &block) ⇒ Object

Invokes a command. If the command writes to stderr, said output is raised as an exception. Otherwise, read from stdout and return it as a string. If a block is given, each line is yielded to it.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/git/trac/repository.rb', line 50

def exec(*args,&block)
  popen3(*args) do |i,o,e|
    err = e.read
    if err.empty?
      if block_given?
        o.each_line(&block)
        nil
      else
        o.read
      end
    else
      raise Git::Trac::ExecutionError, err.chomp
    end
  end
end

#generated_commitsObject

A hash of the form => ticket_number



193
194
195
196
197
198
199
# File 'lib/git/trac/repository.rb', line 193

def generated_commits
  hash = {}
  each_ref("refs/remotes/trac") do |object, ref|
    hash[object] = File.basename(File.dirname(ref)).to_i
  end
  hash
end

#get_response(uri) ⇒ Object



141
142
143
144
145
# File 'lib/git/trac/repository.rb', line 141

def get_response(uri)
  require 'net/http'
  require 'uri'
  Net::HTTP.get_response(URI.parse(uri))
end

#guess_current_ticket_numberObject

See if the current commit or an ancestor was previously tagged as a trac ticket. Also check git config branch.branchname.tracticket



228
229
230
231
232
233
234
235
236
237
238
# File 'lib/git/trac/repository.rb', line 228

def guess_current_ticket_number
  if number = config("branch.#{current_checkout}", :tracticket)
    return number.to_i
  else
    hash = generated_commits
    exec("git","rev-list", "--max-count=25", "HEAD") do |line|
      number = hash[line.chomp] and return number
    end
    nil
  end
end

#guess_upstream(head = "HEAD") ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/git/trac/repository.rb', line 240

def guess_upstream(head = "HEAD")
  svn = config("svn-remote.svn")
  return unless (svn||{})[:url]
  branch = popen3("git","log","--no-color",head) do |i,o,e|
    o.each_line do |line|
      next unless line.gsub!(/^\s*git-svn-id:\s*/,'')
      url, uuid = line.chomp.split(" ")
      url.sub!(/^#{Regexp.escape(svn[:url])}\/*/,'')
      branch, revision = url.split("@")
      break branch
    end
  end
  if branch
    trunk = svn[:trunk] || "trunk:refs/remotes/trunk"
    branches = svn[:branches] || "branches/*:refs/remotes/*"
    tags = svn[:tags] || "tags/*:refs/remotes/tags/*"
    [trunk, branches, tags].each do |pair|
      remote, local = pair.split(":",2)
      next if local.to_s.empty?
      before, after = remote.split("*",2)
      if branch =~ /^#{Regexp.escape(before)}(.*)#{Regexp.escape(after.to_s)}/
        return local.sub(/\*/,$1)
      end
    end
  end
  nil
end

#in_work_tree(&block) ⇒ Object

Call the block while chdired to the working tree.



36
37
38
# File 'lib/git/trac/repository.rb', line 36

def in_work_tree(&block)
  Dir.chdir(File.dirname(@git_dir),&block)
end

#inspectObject



31
32
33
# File 'lib/git/trac/repository.rb', line 31

def inspect
  "#<#{self.class.inspect} #{git_dir} #{url}>"
end

#pager(body, diff = false) ⇒ Object



5
6
7
8
9
10
11
12
# File 'lib/git/trac/pager.rb', line 5

def pager(body, diff = false)
  pager = Pager.new(ENV["GIT_PAGER"] || config("core","pager"))
  if pager.command
    diff &&= !%w(false no 0).include?(config("color","pager").to_s.downcase)
  end
  diff &&= %w(true yes 1 always auto).include?(config("color","diff").to_s.downcase)
  pager.page(body, diff)
end

#popen3(*args, &block) ⇒ Object

Open3.popen3 in the current working tree.



41
42
43
44
45
# File 'lib/git/trac/repository.rb', line 41

def popen3(*args,&block)
  args.flatten!
  cmd = args.shift
  in_work_tree { Open3.popen3(cmd,*args,&block) }
end

#reloadObject

:nodoc:



26
27
28
29
# File 'lib/git/trac/repository.rb', line 26

def reload #:nodoc:
  @config = nil
  @options = (config("trac")||{}).merge(config("trac-remote.trac")||{}).merge(@options)
end

#rev_parse(rev) ⇒ Object

Call git-rev-parse on a revision, returning nil if nothing is found.



75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/git/trac/repository.rb', line 75

def rev_parse(rev)
  if rev.kind_of?(String)
    exec("git","rev-parse","--verify",rev).chomp
  elsif rev.kind_of?(Array)
    hash = {}
    return hash if rev.empty?
    exec("git","rev-parse",*rev) do |rev|
      hash[name] = rev.chomp if rev.chomp =~ /^[0-9a-f]{40}$/
    end
    hash
  end
end

#ticket(number) ⇒ Object

Fetch a ticket object by number.



269
270
271
# File 'lib/git/trac/repository.rb', line 269

def ticket(number)
  Ticket.new(self, number)
end

#urlObject

The trac url associated with the repository.



137
138
139
# File 'lib/git/trac/repository.rb', line 137

def url
  options[:url] || config("svn-remote.svn",:url).to_s[%r{(http://.*)/svn},1]
end

#with_index(file) ⇒ Object



104
105
106
107
108
109
110
111
# File 'lib/git/trac/repository.rb', line 104

def with_index(file)
  old_index = ENV["GIT_INDEX_FILE"]
  ENV["GIT_INDEX_FILE"] = File.join(git_dir,file)
  yield file
ensure
  ENV["GIT_INDEX_FILE"] = old_index
  File.unlink(File.join(git_dir,file)) rescue nil
end

#working_ticketsObject

Return ticket objects for each ticket referenced in the repository.



274
275
276
# File 'lib/git/trac/repository.rb', line 274

def working_tickets
  Dir.entries("#{git_dir}/refs/remotes/trac").map {|number| ticket(number.to_i) unless number.to_i.zero? }.compact
end