Class: RIM::GitSession

Inherits:
Object
  • Object
show all
Defined in:
lib/rim/git.rb

Defined Under Namespace

Classes: ChangedFile, Status

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(logger, execute_dir, arg = {}) ⇒ GitSession

Returns a new instance of GitSession.



21
22
23
24
25
26
27
28
# File 'lib/rim/git.rb', line 21

def initialize(logger, execute_dir, arg = {})
  @execute_dir = execute_dir
  if arg.is_a?(Hash)
    @work_dir = arg.has_key?(:work_dir) ? arg[:work_dir] : ""
    @git_dir = arg.has_key?(:git_dir) ? arg[:git_dir] : ""
  end
  @logger = logger
end

Instance Attribute Details

#execute_dirObject (readonly)

Returns the value of attribute execute_dir.



19
20
21
# File 'lib/rim/git.rb', line 19

def execute_dir
  @execute_dir
end

Class Method Details

.logger=(logger) ⇒ Object



30
31
32
# File 'lib/rim/git.rb', line 30

def self.logger=(logger)
  @logger = logger
end

.next_invocation_idObject



39
40
41
42
# File 'lib/rim/git.rb', line 39

def self.next_invocation_id
  @invocation_id ||= 0
  @invocation_id += 1
end

.open(execute_dir, options = {}) ⇒ Object



34
35
36
37
# File 'lib/rim/git.rb', line 34

def self.open(execute_dir, options = {})
  log = @logger || Logger.new($stdout)
  self.new(log, execute_dir, options)
end

Instance Method Details

#all_reachable_non_remote_revs(rev) ⇒ Object

all commits reachable from rev which are not ancestors of remote branches



161
162
163
164
# File 'lib/rim/git.rb', line 161

def all_reachable_non_remote_revs(rev)
  out = execute "git rev-list #{rev} --not --remotes --"
  out.split("\n")
end

#changed_files(rev, rev_from = nil) ⇒ Object

returns a list of all files which changed in commit rev together with the kind of the change (:modified, :deleted, :added)

if from_rev is given, lists changes between +from_rev and rev with one argument only, no changes will be returned for merge commits use the two argument variant for merge commits and decide for one parent



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/rim/git.rb', line 232

def changed_files(rev, rev_from=nil)
  out = execute "git diff-tree -r --no-commit-id #{rev_from} #{rev}"
  out.split("\n").collect do |l|
    cols = l.split
    path = cols[5]
    kind = case cols[4]
      when "M"
        :modified
      when "A"
        :added
      when "D"
        :deleted
      else
        nil
      end
    ChangedFile.new(path, kind)
  end
end

#current_branchObject

returns the current branch



92
93
94
95
96
97
98
99
100
# File 'lib/rim/git.rb', line 92

def current_branch
  out = execute "git branch"
  out.split("\n").each do |l| 
    if l =~ /^\*\s+(\S+)/
      return $1
    end
  end
  nil
end

#current_branch_nameObject



219
220
221
222
# File 'lib/rim/git.rb', line 219

def current_branch_name
  out = execute "git rev-parse --abbrev-ref HEAD"
  out.strip
end

#execute(cmd) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/rim/git.rb', line 268

def execute(cmd)
  raise "git command has to start with 'git'" unless cmd.start_with? "git "
  cmd.slice!("git ")
  # remove any newlines as they will cause the command line to end prematurely
  cmd.gsub!("\n", "")
  options = ((!@execute_dir || @execute_dir == ".") ? "" : " -C #{@execute_dir}") \
      + (@work_dir.empty? ? "" : " --work-tree=#{File.expand_path(@work_dir)}") \
      + (@git_dir.empty? ? "" : " --git-dir=#{File.expand_path(@git_dir)}") 
  cmd = "git#{options} #{cmd} 2>&1"

  out = `#{cmd}`
  # make sure we don't run into any encoding misinterpretation issues
  out.force_encoding("binary")

  exitstatus = $?.exitstatus

  invid = self.class.next_invocation_id.to_s.ljust(4)
  @logger.debug "git##{invid} \"#{cmd}\" => #{exitstatus}" 

  out.split(/\r?\n/).each do |ol|
    @logger.debug "git##{invid} out : #{ol}"
  end

  exception = exitstatus != 0 ? GitException.new(cmd, exitstatus, out) : nil
  
  if block_given?
    yield out, exception  
  elsif exception
    raise exception
  end

  out
end

#export_rev(rev, dir, paths = []) ⇒ Object

export file contents of rev to dir if paths is given and non-empty, checks out only those parts of the filesystem tree does not remove any files from dir which existed before



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/rim/git.rb', line 169

def export_rev(rev, dir, paths=[])
  paths = paths.dup
  loop do
    path_args = ""
    # max command line length on Windows XP and higher is 8191
    # consider the following extra characters which will be added:
    # up to 3 paths in execute, 1 path for tar, max path length 260 = 1040
    # plus some "glue" characters, plus the last path item with 260 max;
    # use 6000 to be on the safe side
    while !paths.empty? && path_args.size < 6000
      path_args << " "
      path_args << paths.shift
    end
    execute "git archive --format tar #{rev} #{path_args} | tar -x -C #{dir}"
    break if paths.empty?
  end
end

#git_versionObject

3 most significant numbers of git version of nil if it can’t be determined



252
253
254
255
256
257
258
259
# File 'lib/rim/git.rb', line 252

def git_version
  out = execute("git --version")
  if out =~ /^git version (\d+\.\d+\.\d+)/
    $1
  else
    nil
  end
end

#has_branch?(branch) ⇒ Boolean

check whether branch exists

Returns:

  • (Boolean)


103
104
105
106
107
# File 'lib/rim/git.rb', line 103

def has_branch?(branch)
  execute("git show-ref refs/heads/#{branch}") do |b, e|
    return !e
  end
end

#has_remote_branch?(branch) ⇒ Boolean

check whether remote branch exists

Returns:

  • (Boolean)


110
111
112
113
114
115
116
# File 'lib/rim/git.rb', line 110

def has_remote_branch?(branch)
  out = execute("git ls-remote --heads")
  out.split("\n").each do |l|
    return true if l.split(/\s+/)[1] == "refs/heads/#{branch}"
  end
  false
end

#has_valid_remote_repository?Boolean

check whether remote repository is valid

Returns:

  • (Boolean)


119
120
121
122
123
# File 'lib/rim/git.rb', line 119

def has_valid_remote_repository?()
  execute("git ls-remote") do |b, e|
    return !e
  end
end

#is_ancestor?(ancestor, child) ⇒ Boolean

checks whether the first (ancestor) revision is is ancestor of the second (child) revision

Returns:

  • (Boolean)


126
127
128
129
130
# File 'lib/rim/git.rb', line 126

def is_ancestor?(ancestor, child)
  execute("git merge-base --is-ancestor #{ancestor} #{child}") do |b, e|
    return !e
  end
end

#parent_revs(rev) ⇒ Object

returns the parent commits of rev as SHA-1s returns an empty array if there are no parents (e.g. orphan or initial)



134
135
136
137
# File 'lib/rim/git.rb', line 134

def parent_revs(rev)
  out = execute "git rev-list -n 1 --parents #{rev} --"
  out.strip.split[1..-1]
end

#remote_branch_revsObject

returns the SHA-1 representations of the heads of all remote branches



149
150
151
152
153
154
155
156
157
158
# File 'lib/rim/git.rb', line 149

def remote_branch_revs
  out = execute "git show-ref"
  out.split("\n").collect { |l|
    if l =~ /refs\/remotes\//
      l.split[0]
    else
      nil
    end
  }.compact
end

#rev_sha1(rev) ⇒ Object

returns the SHA-1 representation of rev



140
141
142
143
144
145
146
# File 'lib/rim/git.rb', line 140

def rev_sha1(rev)
  sha1 = nil
  execute "git rev-list -n 1 #{rev} --" do |out, e|
    sha1 = out.strip if !e
  end
  sha1
end

#status(dir = nil) ⇒ Object



261
262
263
264
265
266
# File 'lib/rim/git.rb', line 261

def status(dir = nil)
  # -s            short format
  # --ignored     show ignored
  out = execute "git status -s --ignored #{dir}"
  parse_status(out)
end

#uncommited_changes?Boolean

Returns:

  • (Boolean)


214
215
216
217
# File 'lib/rim/git.rb', line 214

def uncommited_changes?
  # either no status lines are all of them due to ignored items
  !status.lines.all?{|l| l.ignored?}
end

#within_exported_rev(rev, paths = []) ⇒ Object

checks out rev to a temporary directory and yields this directory to the given block if paths is given and non-empty, checks out only those parts of the filesystem tree returns the value returned by the block



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/rim/git.rb', line 190

def within_exported_rev(rev, paths=[])
  Dir.mktmpdir("rim") do |d|
    c = File.join(d, "content")
    FileUtils.mkdir(c)
    export_rev(rev, c, paths)
    # return contents of yielded block
    # mktmpdir returns value return by our block
    yield c
    FileUtils.rm_rf(c)
    # retry to delete if it hasn't been deleted yet
    # this could be due to Windows keeping the files locked for some time
    # this is especially a problem if the machine is at its limits
    retries = 600
    while File.exist?(c) && retries > 0
      sleep(0.1)
      FileUtils.rm_rf(c)
      retries -= 1
    end
    if File.exist?(c)
      @logger.warn "could not delete temp dir: #{c}"
    end
  end
end