mojombo-grit 0.9.4 → 1.1.1

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 (47) hide show
  1. data/API.txt +101 -0
  2. data/History.txt +38 -2
  3. data/README.md +210 -0
  4. data/VERSION.yml +4 -0
  5. data/lib/grit.rb +13 -4
  6. data/lib/grit/blame.rb +61 -0
  7. data/lib/grit/blob.rb +11 -2
  8. data/lib/grit/commit.rb +44 -31
  9. data/lib/grit/commit_stats.rb +26 -2
  10. data/lib/grit/diff.rb +6 -6
  11. data/lib/grit/git-ruby.rb +4 -2
  12. data/lib/grit/git-ruby/file_index.rb +10 -3
  13. data/lib/grit/git-ruby/git_object.rb +9 -3
  14. data/lib/grit/git-ruby/internal/{mmap.rb → file_window.rb} +2 -2
  15. data/lib/grit/git-ruby/internal/loose.rb +6 -6
  16. data/lib/grit/git-ruby/internal/pack.rb +19 -19
  17. data/lib/grit/git-ruby/object.rb +8 -2
  18. data/lib/grit/git-ruby/repository.rb +11 -6
  19. data/lib/grit/git.rb +23 -10
  20. data/lib/grit/index.rb +12 -11
  21. data/lib/grit/merge.rb +45 -0
  22. data/lib/grit/ref.rb +20 -16
  23. data/lib/grit/repo.rb +59 -9
  24. data/lib/grit/ruby1.9.rb +7 -0
  25. data/lib/grit/submodule.rb +5 -1
  26. data/lib/grit/tag.rb +61 -66
  27. data/lib/grit/tree.rb +20 -1
  28. metadata +29 -47
  29. data/Manifest.txt +0 -71
  30. data/README.txt +0 -213
  31. data/Rakefile +0 -29
  32. data/grit.gemspec +0 -62
  33. data/lib/grit/head.rb +0 -83
  34. data/test/test_actor.rb +0 -35
  35. data/test/test_blob.rb +0 -79
  36. data/test/test_commit.rb +0 -190
  37. data/test/test_config.rb +0 -58
  38. data/test/test_diff.rb +0 -18
  39. data/test/test_git.rb +0 -64
  40. data/test/test_grit.rb +0 -32
  41. data/test/test_head.rb +0 -47
  42. data/test/test_real.rb +0 -19
  43. data/test/test_reality.rb +0 -17
  44. data/test/test_remote.rb +0 -14
  45. data/test/test_repo.rb +0 -277
  46. data/test/test_tag.rb +0 -25
  47. data/test/test_tree.rb +0 -96
@@ -11,7 +11,7 @@
11
11
 
12
12
  require 'zlib'
13
13
  require 'grit/git-ruby/internal/raw_object'
14
- require 'grit/git-ruby/internal/mmap'
14
+ require 'grit/git-ruby/internal/file_window'
15
15
 
16
16
  PACK_SIGNATURE = "PACK"
17
17
  PACK_IDX_SIGNATURE = "\377tOc"
@@ -48,9 +48,9 @@ module Grit
48
48
  def with_idx(index_file = nil)
49
49
  if !index_file
50
50
  index_file = @name
51
- idxfile = File.open(@name[0...-4]+'idx')
51
+ idxfile = File.open(@name[0...-4]+'idx', 'rb')
52
52
  else
53
- idxfile = File.open(index_file)
53
+ idxfile = File.open(index_file, 'rb')
54
54
  end
55
55
 
56
56
  # read header
@@ -66,14 +66,14 @@ module Grit
66
66
  @version = 1
67
67
  end
68
68
 
69
- idx = Mmap.new(idxfile, @version)
69
+ idx = FileWindow.new(idxfile, @version)
70
70
  yield idx
71
71
  idx.unmap
72
72
  idxfile.close
73
73
  end
74
74
 
75
75
  def with_packfile
76
- packfile = File.open(@name)
76
+ packfile = File.open(@name, 'rb')
77
77
  yield packfile
78
78
  packfile.close
79
79
  end
@@ -189,7 +189,7 @@ module Grit
189
189
  end
190
190
 
191
191
  def find_object_in_index(idx, sha1)
192
- slot = sha1[0]
192
+ slot = sha1.getord(0)
193
193
  return nil if !slot
194
194
  first, last = @offsets[slot,2]
195
195
  while first < last
@@ -248,13 +248,13 @@ module Grit
248
248
  obj_offset = offset
249
249
  packfile.seek(offset)
250
250
 
251
- c = packfile.read(1)[0]
251
+ c = packfile.read(1).getord(0)
252
252
  size = c & 0xf
253
253
  type = (c >> 4) & 7
254
254
  shift = 4
255
255
  offset += 1
256
256
  while c & 0x80 != 0
257
- c = packfile.read(1)[0]
257
+ c = packfile.read(1).getord(0)
258
258
  size |= ((c & 0x7f) << shift)
259
259
  shift += 7
260
260
  offset += 1
@@ -281,10 +281,10 @@ module Grit
281
281
 
282
282
  if type == OBJ_OFS_DELTA
283
283
  i = 0
284
- c = data[i]
284
+ c = data.getord(i)
285
285
  base_offset = c & 0x7f
286
286
  while c & 0x80 != 0
287
- c = data[i += 1]
287
+ c = data.getord(i += 1)
288
288
  base_offset += 1
289
289
  base_offset <<= 7
290
290
  base_offset |= c & 0x7f
@@ -335,18 +335,18 @@ module Grit
335
335
  dest_size, pos = patch_delta_header_size(delta, pos)
336
336
  dest = ""
337
337
  while pos < delta.size
338
- c = delta[pos]
338
+ c = delta.getord(pos)
339
339
  pos += 1
340
340
  if c & 0x80 != 0
341
341
  pos -= 1
342
342
  cp_off = cp_size = 0
343
- cp_off = delta[pos += 1] if c & 0x01 != 0
344
- cp_off |= delta[pos += 1] << 8 if c & 0x02 != 0
345
- cp_off |= delta[pos += 1] << 16 if c & 0x04 != 0
346
- cp_off |= delta[pos += 1] << 24 if c & 0x08 != 0
347
- cp_size = delta[pos += 1] if c & 0x10 != 0
348
- cp_size |= delta[pos += 1] << 8 if c & 0x20 != 0
349
- cp_size |= delta[pos += 1] << 16 if c & 0x40 != 0
343
+ cp_off = delta.getord(pos += 1) if c & 0x01 != 0
344
+ cp_off |= delta.getord(pos += 1) << 8 if c & 0x02 != 0
345
+ cp_off |= delta.getord(pos += 1) << 16 if c & 0x04 != 0
346
+ cp_off |= delta.getord(pos += 1) << 24 if c & 0x08 != 0
347
+ cp_size = delta.getord(pos += 1) if c & 0x10 != 0
348
+ cp_size |= delta.getord(pos += 1) << 8 if c & 0x20 != 0
349
+ cp_size |= delta.getord(pos += 1) << 16 if c & 0x40 != 0
350
350
  cp_size = 0x10000 if cp_size == 0
351
351
  pos += 1
352
352
  dest += base[cp_off,cp_size]
@@ -365,7 +365,7 @@ module Grit
365
365
  size = 0
366
366
  shift = 0
367
367
  begin
368
- c = delta[pos]
368
+ c = delta.getord(pos)
369
369
  if c == nil
370
370
  raise PackFormatError, 'invalid delta header'
371
371
  end
@@ -163,8 +163,14 @@ module Grit
163
163
 
164
164
  def self.read_bytes_until(io, char)
165
165
  string = ''
166
- while ((next_char = io.getc.chr) != char) && !io.eof
167
- string += next_char
166
+ if RUBY_VERSION > '1.9'
167
+ while ((next_char = io.getc) != char) && !io.eof
168
+ string += next_char
169
+ end
170
+ else
171
+ while ((next_char = io.getc.chr) != char) && !io.eof
172
+ string += next_char
173
+ end
168
174
  end
169
175
  string
170
176
  end
@@ -185,16 +185,15 @@ module Grit
185
185
  def get_raw_tree(sha)
186
186
  o = get_raw_object_by_sha1(sha)
187
187
  if o.type == :commit
188
- tree = cat_file(get_object_by_sha1(sha).tree)
188
+ cat_file(get_object_by_sha1(sha).tree)
189
189
  elsif o.type == :tag
190
190
  commit_sha = get_object_by_sha1(sha).object
191
- tree = cat_file(get_object_by_sha1(commit_sha).tree)
192
- else
193
- tree = cat_file(sha)
191
+ cat_file(get_object_by_sha1(commit_sha).tree)
192
+ elsif o.type == :tree
193
+ cat_file(sha)
194
194
  end
195
- return tree
196
195
  end
197
-
196
+
198
197
  # return array of tree entries
199
198
  ## TODO : refactor this to remove the fugly
200
199
  def ls_tree_path(sha, path, append = nil)
@@ -664,6 +663,7 @@ module Grit
664
663
  private
665
664
 
666
665
  def initloose
666
+ @loaded = []
667
667
  @loose = []
668
668
  load_loose(git_path('objects'))
669
669
  load_alternate_loose(git_path('objects'))
@@ -675,6 +675,7 @@ module Grit
675
675
  alt = File.join(path, 'info/alternates')
676
676
  if File.exists?(alt)
677
677
  File.readlines(alt).each do |line|
678
+ next if @loaded.include?(line.chomp)
678
679
  if line[0, 2] == '..'
679
680
  line = File.expand_path(File.join(@git_dir, line))
680
681
  end
@@ -685,12 +686,14 @@ module Grit
685
686
  end
686
687
 
687
688
  def load_loose(path)
689
+ @loaded << path
688
690
  return if !File.exists?(path)
689
691
  @loose << Grit::GitRuby::Internal::LooseStorage.new(path)
690
692
  end
691
693
 
692
694
  def initpacks
693
695
  close
696
+ @loaded_packs = []
694
697
  @packs = []
695
698
  load_packs(git_path("objects/pack"))
696
699
  load_alternate_packs(git_path('objects'))
@@ -705,6 +708,7 @@ module Grit
705
708
  line = File.expand_path(File.join(@git_dir, line))
706
709
  end
707
710
  full_pack = File.join(line.chomp, 'pack')
711
+ next if @loaded_packs.include?(full_pack)
708
712
  load_packs(full_pack)
709
713
  load_alternate_packs(File.join(line.chomp))
710
714
  end
@@ -712,6 +716,7 @@ module Grit
712
716
  end
713
717
 
714
718
  def load_packs(path)
719
+ @loaded_packs << path
715
720
  return if !File.exists?(path)
716
721
  Dir.open(path) do |dir|
717
722
  dir.each do |entry|
@@ -15,11 +15,19 @@ module Grit
15
15
  include GitRuby
16
16
 
17
17
  class << self
18
- attr_accessor :git_binary, :git_timeout
18
+ attr_accessor :git_binary, :git_timeout, :git_max_size
19
19
  end
20
20
 
21
- self.git_binary = "/usr/bin/env git"
22
- self.git_timeout = 10
21
+ self.git_binary = "/usr/bin/env git"
22
+ self.git_timeout = 10
23
+ self.git_max_size = 5242880 # 5.megabytes
24
+
25
+ def self.with_timeout(timeout = 10.seconds)
26
+ old_timeout = Grit::Git.git_timeout
27
+ Grit::Git.git_timeout = timeout
28
+ yield
29
+ Grit::Git.git_timeout = old_timeout
30
+ end
23
31
 
24
32
  attr_accessor :git_dir, :bytes_read
25
33
 
@@ -28,6 +36,11 @@ module Grit
28
36
  self.bytes_read = 0
29
37
  end
30
38
 
39
+ def shell_escape(str)
40
+ str.to_s.gsub("'", "\\\\'").gsub(";", '\\;')
41
+ end
42
+ alias_method :e, :shell_escape
43
+
31
44
  # Run the given git command with the specified arguments and return
32
45
  # the result as a String
33
46
  # +cmd+ is the command
@@ -43,13 +56,13 @@ module Grit
43
56
  end
44
57
 
45
58
  def run(prefix, cmd, postfix, options, args)
46
- timeout = options.delete(:timeout)
59
+ timeout = options.delete(:timeout) rescue nil
47
60
  timeout = true if timeout.nil?
48
61
 
49
62
  opt_args = transform_options(options)
50
- ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{a}'" }
51
-
52
- call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{postfix}"
63
+ ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{e(a)}'" }
64
+
65
+ call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
53
66
  Grit.log(call) if Grit.debug
54
67
  response, err = timeout ? sh(call) : wild_sh(call)
55
68
  Grit.log(response) if Grit.debug
@@ -63,7 +76,7 @@ module Grit
63
76
  Timeout.timeout(self.class.git_timeout) do
64
77
  while tmp = stdout.read(1024)
65
78
  ret += tmp
66
- if (@bytes_read += tmp.size) > 5242880 # 5.megabytes
79
+ if (@bytes_read += tmp.size) > self.class.git_max_size
67
80
  bytes = @bytes_read
68
81
  @bytes_read = 0
69
82
  raise GitTimeout.new(command, bytes)
@@ -109,14 +122,14 @@ module Grit
109
122
  args << "-#{opt}"
110
123
  else
111
124
  val = options.delete(opt)
112
- args << "-#{opt.to_s} '#{val}'"
125
+ args << "-#{opt.to_s} '#{e(val)}'"
113
126
  end
114
127
  else
115
128
  if options[opt] == true
116
129
  args << "--#{opt.to_s.gsub(/_/, '-')}"
117
130
  else
118
131
  val = options.delete(opt)
119
- args << "--#{opt.to_s.gsub(/_/, '-')}='#{val}'"
132
+ args << "--#{opt.to_s.gsub(/_/, '-')}='#{e(val)}'"
120
133
  end
121
134
  end
122
135
  end
@@ -29,12 +29,20 @@ module Grit
29
29
  current[filename] = data
30
30
  end
31
31
 
32
+ # Sets the current tree
33
+ # +tree+ the branch/tag/sha... to use - a string
34
+ #
35
+ # Returns index (self)
32
36
  def read_tree(tree)
33
37
  self.current_tree = self.repo.tree(tree)
34
38
  end
35
39
 
36
40
  # Commit the contents of the index
37
- # +message+ is the commit message
41
+ # +message+ is the commit message [nil]
42
+ # +parents+ is one or more commits to attach this commit to to form a new head [nil]
43
+ # +actor+ is the details of the user making the commit [nil]
44
+ # +last_tree+ is a tree to compare with - to avoid making empty commits [nil]
45
+ # +head+ is the branch to write this head to [master]
38
46
  #
39
47
  # Returns a String of the SHA1 of the commit
40
48
  def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
@@ -64,14 +72,7 @@ module Grit
64
72
 
65
73
  commit_sha1 = self.repo.git.ruby_git.put_raw_object(contents.join("\n"), 'commit')
66
74
 
67
- # self.repo.git.update_ref({}, 'HEAD', commit_sha1)
68
- ref_heads = File.join(self.repo.path, 'refs', 'heads')
69
- FileUtils.mkdir_p(ref_heads)
70
- File.open(File.join(ref_heads, head), 'w') do |f|
71
- f.write(commit_sha1)
72
- end if commit_sha1
73
-
74
- commit_sha1
75
+ self.repo.update_ref(head, commit_sha1)
75
76
  end
76
77
 
77
78
  # Recursively write a tree to the index
@@ -92,12 +93,12 @@ module Grit
92
93
  # overwrite with new tree contents
93
94
  tree.each do |k, v|
94
95
  case v
95
- when String:
96
+ when String
96
97
  sha = write_blob(v)
97
98
  sha = [sha].pack("H*")
98
99
  str = "%s %s\0%s" % ['100644', k, sha]
99
100
  tree_contents[k] = str
100
- when Hash:
101
+ when Hash
101
102
  ctree = now_tree/k if now_tree
102
103
  sha = write_tree(v, ctree)
103
104
  sha = [sha].pack("H*")
@@ -0,0 +1,45 @@
1
+ module Grit
2
+
3
+ class Merge
4
+
5
+ STATUS_BOTH = 'both'
6
+ STATUS_OURS = 'ours'
7
+ STATUS_THEIRS = 'theirs'
8
+
9
+ attr_reader :conflicts, :text, :sections
10
+
11
+ def initialize(str)
12
+ status = STATUS_BOTH
13
+
14
+ section = 1
15
+ @conflicts = 0
16
+ @text = {}
17
+
18
+ lines = str.split("\n")
19
+ lines.each do |line|
20
+ if /^<<<<<<< (.*?)/.match(line)
21
+ status = STATUS_OURS
22
+ @conflicts += 1
23
+ section += 1
24
+ elsif line == '======='
25
+ status = STATUS_THEIRS
26
+ elsif /^>>>>>>> (.*?)/.match(line)
27
+ status = STATUS_BOTH
28
+ section += 1
29
+ else
30
+ @text[section] ||= {}
31
+ @text[section][status] ||= []
32
+ @text[section][status] << line
33
+ end
34
+ end
35
+ @text = @text.values
36
+ @sections = @text.size
37
+ end
38
+
39
+ # Pretty object inspection
40
+ def inspect
41
+ %Q{#<Grit::Merge}
42
+ end
43
+ end # Merge
44
+
45
+ end # Grit
@@ -9,31 +9,37 @@ module Grit
9
9
  # +options+ is a Hash of options
10
10
  #
11
11
  # Returns Grit::Ref[] (baked)
12
- def find_all(repo, options = {})
12
+ def find_all(repo, options = {})
13
13
  refs = []
14
-
14
+ already = {}
15
15
  Dir.chdir(repo.path) do
16
+ files = Dir.glob(prefix + '/**/*')
17
+ files.each do |ref|
18
+ next if !File.file?(ref)
19
+ id = File.read(ref).chomp
20
+ name = ref.sub("#{prefix}/", '')
21
+ commit = Commit.create(repo, :id => id)
22
+ if !already[name]
23
+ refs << self.new(name, commit)
24
+ already[name] = true
25
+ end
26
+ end
27
+
16
28
  if File.file?('packed-refs')
17
29
  File.readlines('packed-refs').each do |line|
18
30
  if m = /^(\w{40}) (.*?)$/.match(line)
19
31
  next if !Regexp.new('^' + prefix).match(m[2])
20
32
  name = m[2].sub("#{prefix}/", '')
21
33
  commit = Commit.create(repo, :id => m[1])
22
- refs << self.new(name, commit)
34
+ if !already[name]
35
+ refs << self.new(name, commit)
36
+ already[name] = true
37
+ end
23
38
  end
24
39
  end
25
40
  end
26
-
27
- files = Dir.glob(prefix + '/**/*')
28
- files.each do |ref|
29
- next if !File.file?(ref)
30
- id = File.read(ref).chomp
31
- name = ref.sub("#{prefix}/", '')
32
- commit = Commit.create(repo, :id => id)
33
- refs << self.new(name, commit)
34
- end
35
41
  end
36
-
42
+
37
43
  refs
38
44
  end
39
45
 
@@ -88,8 +94,6 @@ module Grit
88
94
 
89
95
  end # Head
90
96
 
91
- class Tag < Ref ; end
92
-
93
97
  class Remote < Ref; end
94
-
98
+
95
99
  end # Grit
@@ -13,6 +13,7 @@ module Grit
13
13
 
14
14
  # Create a new Repo instance
15
15
  # +path+ is the path to either the root git directory or the bare git repo
16
+ # +options+ :is_bare force to load a bare repo
16
17
  #
17
18
  # Examples
18
19
  # g = Repo.new("/Users/tom/dev/grit")
@@ -37,7 +38,8 @@ module Grit
37
38
 
38
39
  self.git = Git.new(self.path)
39
40
  end
40
-
41
+
42
+ # Does nothing yet...
41
43
  def self.init(path)
42
44
  # !! TODO !!
43
45
  # create directory
@@ -51,6 +53,11 @@ module Grit
51
53
  def description
52
54
  File.open(File.join(self.path, 'description')).read.chomp
53
55
  end
56
+
57
+ def blame(file, commit = nil)
58
+ Blame.new(self, file, commit)
59
+ end
60
+
54
61
 
55
62
  # An array of Head objects representing the branch heads in
56
63
  # this repo
@@ -62,10 +69,14 @@ module Grit
62
69
 
63
70
  alias_method :branches, :heads
64
71
 
65
- def is_head?(head_name)
72
+ def get_head(head_name)
66
73
  heads.find { |h| h.name == head_name }
67
74
  end
68
75
 
76
+ def is_head?(head_name)
77
+ get_head(head_name)
78
+ end
79
+
69
80
  # Object reprsenting the current repo head.
70
81
  #
71
82
  # Returns Grit::Head (baked)
@@ -93,7 +104,7 @@ module Grit
93
104
  self.git.add({}, *files.flatten)
94
105
  end
95
106
 
96
- # Adds files to the index
107
+ # Remove files from the index
97
108
  def remove(*files)
98
109
  self.git.rm({}, *files.flatten)
99
110
  end
@@ -198,6 +209,20 @@ module Grit
198
209
  Commit.find_all(self, id, options).first
199
210
  end
200
211
 
212
+ # Returns a list of commits that is in +other_repo+ but not in self
213
+ #
214
+ # Returns Grit::Commit[]
215
+ def commit_deltas_from(other_repo, ref = "master", other_ref = "master")
216
+ # TODO: we should be able to figure out the branch point, rather than
217
+ # rev-list'ing the whole thing
218
+ repo_refs = self.git.rev_list({}, ref).strip.split("\n")
219
+ other_repo_refs = other_repo.git.rev_list({}, other_ref).strip.split("\n")
220
+
221
+ (other_repo_refs - repo_refs).map do |ref|
222
+ Commit.find_all(other_repo, ref, {:max_count => 1}).first
223
+ end
224
+ end
225
+
201
226
  # The Tree object for the given treeish reference
202
227
  # +treeish+ is the reference (default 'master')
203
228
  # +paths+ is an optional Array of directory paths to restrict the tree (deafult [])
@@ -261,7 +286,7 @@ module Grit
261
286
 
262
287
  # Fork a bare git repository from this repo
263
288
  # +path+ is the full path of the new repo (traditionally ends with /<name>.git)
264
- # +options+ is any additional options to the git clone command
289
+ # +options+ is any additional options to the git clone command (:bare and :shared are true by default)
265
290
  #
266
291
  # Returns Grit::Repo (the newly forked repo)
267
292
  def fork_bare(path, options = {})
@@ -313,14 +338,21 @@ module Grit
313
338
  self.git.archive(options, treeish, "| gzip")
314
339
  end
315
340
 
316
- # run archive directly to a file
317
- def archive_to_file(treeish = 'master', prefix = nil, filename = 'archive.tar.gz')
341
+ # Write an archive directly to a file
342
+ # +treeish+ is the treeish name/id (default 'master')
343
+ # +prefix+ is the optional prefix (default nil)
344
+ # +filename+ is the name of the file (default 'archive.tar.gz')
345
+ # +format+ is the optional format (default nil)
346
+ # +pipe+ is the command to run the output through (default 'gzip')
347
+ #
348
+ # Returns nothing
349
+ def archive_to_file(treeish = 'master', prefix = nil, filename = 'archive.tar.gz', format = nil, pipe = "gzip")
318
350
  options = {}
319
351
  options[:prefix] = prefix if prefix
320
- self.git.archive(options, treeish, "| gzip > #{filename}")
352
+ options[:format] = format if format
353
+ self.git.archive(options, treeish, "| #{pipe} > #{filename}")
321
354
  end
322
355
 
323
-
324
356
  # Enable git-daemon serving of this repository by writing the
325
357
  # git-daemon-export-ok file to its git directory
326
358
  #
@@ -337,6 +369,10 @@ module Grit
337
369
  FileUtils.rm_f(File.join(self.path, DAEMON_EXPORT_FILE))
338
370
  end
339
371
 
372
+ def gc_auto
373
+ self.git.gc({:auto => true})
374
+ end
375
+
340
376
  # The list of alternates for this repo
341
377
  #
342
378
  # Returns Array[String] (pathnames of alternates)
@@ -362,7 +398,9 @@ module Grit
362
398
  end
363
399
 
364
400
  if alts.empty?
365
- File.delete(File.join(self.path, *%w{objects info alternates}))
401
+ File.open(File.join(self.path, *%w{objects info alternates}), 'w') do |f|
402
+ f.write ''
403
+ end
366
404
  else
367
405
  File.open(File.join(self.path, *%w{objects info alternates}), 'w') do |f|
368
406
  f.write alts.join("\n")
@@ -378,6 +416,18 @@ module Grit
378
416
  Index.new(self)
379
417
  end
380
418
 
419
+ def update_ref(head, commit_sha)
420
+ return nil if !commit_sha || (commit_sha.size != 40)
421
+
422
+ ref_heads = File.join(self.path, 'refs', 'heads')
423
+ FileUtils.mkdir_p(ref_heads)
424
+ File.open(File.join(ref_heads, head), 'w') do |f|
425
+ f.write(commit_sha)
426
+ end
427
+ commit_sha
428
+
429
+ end
430
+
381
431
  # Pretty object inspection
382
432
  def inspect
383
433
  %Q{#<Grit::Repo "#{@path}">}