schleyfox-grit 2.3.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.
data/lib/grit/lazy.rb ADDED
@@ -0,0 +1,35 @@
1
+ ##
2
+ # Allows attributes to be declared as lazy, meaning that they won't be
3
+ # computed until they are asked for.
4
+ #
5
+ # Works by delegating each lazy_reader to a cached lazy_source method.
6
+ #
7
+ # class Person
8
+ # lazy_reader :eyes
9
+ #
10
+ # def lazy_source
11
+ # OpenStruct.new(:eyes => 2)
12
+ # end
13
+ # end
14
+ #
15
+ # >> Person.new.eyes
16
+ # => 2
17
+ #
18
+ module Lazy
19
+ def self.extended(klass)
20
+ klass.send(:attr_writer, :lazy_source)
21
+ end
22
+
23
+ def lazy_reader(*args)
24
+ args.each do |arg|
25
+ ivar = "@#{arg}"
26
+ define_method(arg) do
27
+ if instance_variable_defined?(ivar)
28
+ val = instance_variable_get(ivar)
29
+ return val if val
30
+ end
31
+ instance_variable_set(ivar, (@lazy_source ||= lazy_source).send(arg))
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/grit/merge.rb ADDED
@@ -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
@@ -0,0 +1,46 @@
1
+ module Open3
2
+ extend self
3
+
4
+ def popen3(*cmd)
5
+ pw = IO::pipe # pipe[0] for read, pipe[1] for write
6
+ pr = IO::pipe
7
+ pe = IO::pipe
8
+
9
+ pid = fork{
10
+ # child
11
+ fork{
12
+ # grandchild
13
+ pw[1].close
14
+ STDIN.reopen(pw[0])
15
+ pw[0].close
16
+
17
+ pr[0].close
18
+ STDOUT.reopen(pr[1])
19
+ pr[1].close
20
+
21
+ pe[0].close
22
+ STDERR.reopen(pe[1])
23
+ pe[1].close
24
+
25
+ exec(*cmd)
26
+ }
27
+ exit!(0)
28
+ }
29
+
30
+ pw[0].close
31
+ pr[1].close
32
+ pe[1].close
33
+ Process.waitpid(pid)
34
+ pi = [pw[1], pr[0], pe[0]]
35
+ pw[1].sync = true
36
+ if defined? yield
37
+ begin
38
+ return yield(*pi)
39
+ ensure
40
+ Process.detach(pid) if pid
41
+ pi.each { |p| p.close unless p.closed? }
42
+ end
43
+ end
44
+ pi
45
+ end
46
+ end
data/lib/grit/ref.rb ADDED
@@ -0,0 +1,78 @@
1
+ module Grit
2
+
3
+ class Ref
4
+
5
+ class << self
6
+
7
+ # Find all Refs
8
+ # +repo+ is the Repo
9
+ # +options+ is a Hash of options
10
+ #
11
+ # Returns Grit::Ref[] (baked)
12
+ def find_all(repo, options = {})
13
+ refs = repo.git.refs(options, prefix)
14
+ refs.split("\n").map do |ref|
15
+ name, id = *ref.split(' ')
16
+ commit = Commit.create(repo, :id => id)
17
+ self.new(name, commit)
18
+ end
19
+ end
20
+
21
+ protected
22
+
23
+ def prefix
24
+ "refs/#{name.to_s.gsub(/^.*::/, '').downcase}s"
25
+ end
26
+
27
+ end
28
+
29
+ attr_reader :name
30
+ attr_reader :commit
31
+
32
+ # Instantiate a new Head
33
+ # +name+ is the name of the head
34
+ # +commit+ is the Commit that the head points to
35
+ #
36
+ # Returns Grit::Head (baked)
37
+ def initialize(name, commit)
38
+ @name = name
39
+ @commit = commit
40
+ end
41
+
42
+ # Pretty object inspection
43
+ def inspect
44
+ %Q{#<#{self.class.name} "#{@name}">}
45
+ end
46
+ end # Ref
47
+
48
+ # A Head is a named reference to a Commit. Every Head instance contains a name
49
+ # and a Commit object.
50
+ #
51
+ # r = Grit::Repo.new("/path/to/repo")
52
+ # h = r.heads.first
53
+ # h.name # => "master"
54
+ # h.commit # => #<Grit::Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
55
+ # h.commit.id # => "1c09f116cbc2cb4100fb6935bb162daa4723f455"
56
+ class Head < Ref
57
+
58
+ # Get the HEAD revision of the repo.
59
+ # +repo+ is the Repo
60
+ # +options+ is a Hash of options
61
+ #
62
+ # Returns Grit::Head (baked)
63
+ def self.current(repo, options = {})
64
+ head = repo.git.fs_read('HEAD').chomp
65
+ if /ref: refs\/heads\/(.*)/.match(head)
66
+ id = repo.git.rev_parse(options, 'HEAD')
67
+ commit = Commit.create(repo, :id => id)
68
+ self.new($1, commit)
69
+ end
70
+ end
71
+
72
+ end # Head
73
+
74
+ class Remote < Ref; end
75
+
76
+ class Note < Ref; end
77
+
78
+ end # Grit
data/lib/grit/repo.rb ADDED
@@ -0,0 +1,657 @@
1
+ module Grit
2
+
3
+ class Repo
4
+ DAEMON_EXPORT_FILE = 'git-daemon-export-ok'
5
+ BATCH_PARSERS = {
6
+ 'commit' => ::Grit::Commit
7
+ }
8
+
9
+ # Public: The String path of the Git repo.
10
+ attr_accessor :path
11
+
12
+ # Public: The String path to the working directory of the repo, or nil if
13
+ # there is no working directory.
14
+ attr_accessor :working_dir
15
+
16
+ # Public: The Boolean of whether or not the repo is bare.
17
+ attr_reader :bare
18
+
19
+ # Public: The Grit::Git command line interface object.
20
+ attr_accessor :git
21
+
22
+ # Public: Create a new Repo instance.
23
+ #
24
+ # path - The String path to either the root git directory or the bare
25
+ # git repo. Bare repos are expected to end with ".git".
26
+ # options - A Hash of options (default: {}):
27
+ # :is_bare - Boolean whether to consider the repo as bare even
28
+ # if the repo name does not end with ".git".
29
+ #
30
+ # Examples
31
+ #
32
+ # r = Repo.new("/Users/tom/dev/normal")
33
+ # r = Repo.new("/Users/tom/public/bare.git")
34
+ # r = Repo.new("/Users/tom/public/bare", {:is_bare => true})
35
+ #
36
+ # Returns a newly initialized Grit::Repo.
37
+ # Raises Grit::InvalidGitRepositoryError if the path exists but is not
38
+ # a Git repository.
39
+ # Raises Grit::NoSuchPathError if the path does not exist.
40
+ def initialize(path, options = {})
41
+ epath = File.expand_path(path)
42
+
43
+ if File.exist?(File.join(epath, '.git'))
44
+ self.working_dir = epath
45
+ self.path = File.join(epath, '.git')
46
+ @bare = false
47
+ elsif File.exist?(epath) && (epath =~ /\.git$/ || options[:is_bare])
48
+ self.path = epath
49
+ @bare = true
50
+ elsif File.exist?(epath)
51
+ raise InvalidGitRepositoryError.new(epath)
52
+ else
53
+ raise NoSuchPathError.new(epath)
54
+ end
55
+
56
+ self.git = Git.new(self.path)
57
+ end
58
+
59
+ # Public: Initialize a git repository (create it on the filesystem). By
60
+ # default, the newly created repository will contain a working directory.
61
+ # If you would like to create a bare repo, use Grit::Repo.init_bare.
62
+ #
63
+ # path - The String full path to the repo. Traditionally ends with
64
+ # "/<name>.git".
65
+ # git_options - A Hash of additional options to the git init command
66
+ # (default: {}).
67
+ # repo_options - A Hash of additional options to the Grit::Repo.new call
68
+ # (default: {}).
69
+ #
70
+ # Examples
71
+ #
72
+ # Grit::Repo.init('/var/git/myrepo.git')
73
+ #
74
+ # Returns the newly created Grit::Repo.
75
+ def self.init(path, git_options = {}, repo_options = {})
76
+ git_options = {:base => false}.merge(git_options)
77
+ git = Git.new(path)
78
+ git.fs_mkdir('..')
79
+ git.init(git_options, path)
80
+ self.new(path, repo_options)
81
+ end
82
+
83
+ # Public: Initialize a bare git repository (create it on the filesystem).
84
+ #
85
+ # path - The String full path to the repo. Traditionally ends with
86
+ # "/<name>.git".
87
+ # git_options - A Hash of additional options to the git init command
88
+ # (default: {}).
89
+ # repo_options - A Hash of additional options to the Grit::Repo.new call
90
+ # (default: {}).
91
+ #
92
+ # Examples
93
+ #
94
+ # Grit::Repo.init_bare('/var/git/myrepo.git')
95
+ #
96
+ # Returns the newly created Grit::Repo.
97
+ def self.init_bare(path, git_options = {}, repo_options = {})
98
+ git_options = {:bare => true}.merge(git_options)
99
+ git = Git.new(path)
100
+ git.fs_mkdir('..')
101
+ git.init(git_options)
102
+ self.new(path, repo_options)
103
+ end
104
+
105
+ # Public: Initialize a bare Git repository (create it on the filesystem)
106
+ # or, if the repo already exists, simply return it.
107
+ #
108
+ # path - The String full path to the repo. Traditionally ends with
109
+ # "/<name>.git".
110
+ # git_options - A Hash of additional options to the git init command
111
+ # (default: {}).
112
+ # repo_options - A Hash of additional options to the Grit::Repo.new call
113
+ # (default: {}).
114
+ #
115
+ # Returns the new or existing Grit::Repo.
116
+ def self.init_bare_or_open(path, git_options = {}, repo_options = {})
117
+ git = Git.new(path)
118
+
119
+ unless git.exist?
120
+ git.fs_mkdir(path)
121
+ git.init(git_options)
122
+ end
123
+
124
+ self.new(path, repo_options)
125
+ end
126
+
127
+ # Public: Create a bare fork of this repository.
128
+ #
129
+ # path - The String full path of where to create the new fork.
130
+ # Traditionally ends with "/<name>.git".
131
+ # options - The Hash of additional options to the git clone command.
132
+ # These options will be merged on top of the default Hash:
133
+ # {:bare => true, :shared => true}.
134
+ #
135
+ # Returns the newly forked Grit::Repo.
136
+ def fork_bare(path, options = {})
137
+ default_options = {:bare => true, :shared => true}
138
+ real_options = default_options.merge(options)
139
+ Git.new(path).fs_mkdir('..')
140
+ self.git.clone(real_options, self.path, path)
141
+ Repo.new(path)
142
+ end
143
+
144
+ # Public: Fork a bare git repository from another repo.
145
+ #
146
+ # path - The String full path of the repo from which to fork..
147
+ # Traditionally ends with "/<name>.git".
148
+ # options - The Hash of additional options to the git clone command.
149
+ # These options will be merged on top of the default Hash:
150
+ # {:bare => true, :shared => true}.
151
+ #
152
+ # Returns the newly forked Grit::Repo.
153
+ def fork_bare_from(path, options = {})
154
+ default_options = {:bare => true, :shared => true}
155
+ real_options = default_options.merge(options)
156
+ Git.new(self.path).fs_mkdir('..')
157
+ self.git.clone(real_options, path, self.path)
158
+ Repo.new(self.path)
159
+ end
160
+
161
+ # Public: Return the full Git objects from the given SHAs. Only Commit
162
+ # objects are parsed for now.
163
+ #
164
+ # *shas - Array of String SHAs.
165
+ #
166
+ # Returns an Array of Grit objects (Grit::Commit).
167
+ def batch(*shas)
168
+ shas.flatten!
169
+ text = git.native(:cat_file, {:batch => true}) do |stdin|
170
+ stdin.write(shas * "\n")
171
+ stdin.close
172
+ end
173
+
174
+ parse_batch(text)
175
+ end
176
+
177
+ # Parses `git cat-file --batch` output, returning an array of Grit objects.
178
+ #
179
+ # text - Raw String output.
180
+ #
181
+ # Returns an Array of Grit objects (Grit::Commit).
182
+ def parse_batch(text)
183
+ io = StringIO.new(text)
184
+ objects = []
185
+ while line = io.gets
186
+ sha, type, size = line.split(" ", 3)
187
+ parser = BATCH_PARSERS[type]
188
+ if type == 'missing' || !parser
189
+ objects << nil
190
+ next
191
+ end
192
+
193
+ object = io.read(size.to_i + 1)
194
+ objects << parser.parse_batch(self, sha, size, object)
195
+ end
196
+ objects
197
+ end
198
+
199
+ # The project's description. Taken verbatim from GIT_REPO/description
200
+ #
201
+ # Returns String
202
+ def description
203
+ self.git.fs_read('description').chomp
204
+ end
205
+
206
+ def blame(file, commit = nil)
207
+ Blame.new(self, file, commit)
208
+ end
209
+
210
+ # An array of Head objects representing the branch heads in
211
+ # this repo
212
+ #
213
+ # Returns Grit::Head[] (baked)
214
+ def heads
215
+ Head.find_all(self)
216
+ end
217
+
218
+ alias_method :branches, :heads
219
+
220
+ def get_head(head_name)
221
+ heads.find { |h| h.name == head_name }
222
+ end
223
+
224
+ def is_head?(head_name)
225
+ get_head(head_name)
226
+ end
227
+
228
+ # Object reprsenting the current repo head.
229
+ #
230
+ # Returns Grit::Head (baked)
231
+ def head
232
+ Head.current(self)
233
+ end
234
+
235
+
236
+ # Commits current index
237
+ #
238
+ # Returns true/false if commit worked
239
+ def commit_index(message)
240
+ self.git.commit({}, '-m', message)
241
+ end
242
+
243
+ # Commits all tracked and modified files
244
+ #
245
+ # Returns true/false if commit worked
246
+ def commit_all(message)
247
+ self.git.commit({}, '-a', '-m', message)
248
+ end
249
+
250
+ # Adds files to the index
251
+ def add(*files)
252
+ self.git.add({}, *files.flatten)
253
+ end
254
+
255
+ # Remove files from the index
256
+ def remove(*files)
257
+ self.git.rm({}, *files.flatten)
258
+ end
259
+
260
+
261
+ def blame_tree(commit, path = nil)
262
+ commit_array = self.git.blame_tree(commit, path)
263
+
264
+ final_array = {}
265
+ commit_array.each do |file, sha|
266
+ final_array[file] = commit(sha)
267
+ end
268
+ final_array
269
+ end
270
+
271
+ def status
272
+ Status.new(self)
273
+ end
274
+
275
+
276
+ # An array of Tag objects that are available in this repo
277
+ #
278
+ # Returns Grit::Tag[] (baked)
279
+ def tags
280
+ Tag.find_all(self)
281
+ end
282
+
283
+ # Finds the most recent annotated tag name that is reachable from a commit.
284
+ #
285
+ # @repo.recent_tag_name('master')
286
+ # # => "v1.0-0-abcdef"
287
+ #
288
+ # committish - optional commit SHA, branch, or tag name.
289
+ # options - optional hash of options to pass to git.
290
+ # Default: {:always => true}
291
+ # :tags => true # use lightweight tags too.
292
+ # :abbrev => Integer # number of hex digits to form the unique
293
+ # name. Defaults to 7.
294
+ # :long => true # always output tag + commit sha
295
+ # # see `git describe` docs for more options.
296
+ #
297
+ # Returns the String tag name, or just the commit if no tag is
298
+ # found. If there have been updates since the tag was made, a
299
+ # suffix is added with the number of commits since the tag, and
300
+ # the abbreviated object name of the most recent commit.
301
+ # Returns nil if the committish value is not found.
302
+ def recent_tag_name(committish = nil, options = {})
303
+ value = git.describe({:always => true}.update(options), committish.to_s).to_s.strip
304
+ value.size.zero? ? nil : value
305
+ end
306
+
307
+ # An array of Remote objects representing the remote branches in
308
+ # this repo
309
+ #
310
+ # Returns Grit::Remote[] (baked)
311
+ def remotes
312
+ Remote.find_all(self)
313
+ end
314
+
315
+ def remote_list
316
+ self.git.list_remotes
317
+ end
318
+
319
+ def remote_add(name, url)
320
+ self.git.remote({}, 'add', name, url)
321
+ end
322
+
323
+ def remote_fetch(name)
324
+ self.git.fetch({}, name)
325
+ end
326
+
327
+ # takes an array of remote names and last pushed dates
328
+ # fetches from all of the remotes where the local fetch
329
+ # date is earlier than the passed date, then records the
330
+ # last fetched date
331
+ #
332
+ # { 'origin' => date,
333
+ # 'peter => date,
334
+ # }
335
+ def remotes_fetch_needed(remotes)
336
+ remotes.each do |remote, date|
337
+ # TODO: check against date
338
+ self.remote_fetch(remote)
339
+ end
340
+ end
341
+
342
+
343
+ # An array of Ref objects representing the refs in
344
+ # this repo
345
+ #
346
+ # Returns Grit::Ref[] (baked)
347
+ def refs
348
+ [ Head.find_all(self), Tag.find_all(self), Remote.find_all(self) ].flatten
349
+ end
350
+
351
+ def commit_stats(start = 'master', max_count = 10, skip = 0)
352
+ options = {:max_count => max_count,
353
+ :skip => skip}
354
+
355
+ CommitStats.find_all(self, start, options)
356
+ end
357
+
358
+ # An array of Commit objects representing the history of a given ref/commit
359
+ # +start+ is the branch/commit name (default 'master')
360
+ # +max_count+ is the maximum number of commits to return (default 10, use +false+ for all)
361
+ # +skip+ is the number of commits to skip (default 0)
362
+ #
363
+ # Returns Grit::Commit[] (baked)
364
+ def commits(start = 'master', max_count = 10, skip = 0)
365
+ options = {:max_count => max_count,
366
+ :skip => skip}
367
+
368
+ Commit.find_all(self, start, options)
369
+ end
370
+
371
+ # The Commits objects that are reachable via +to+ but not via +from+
372
+ # Commits are returned in chronological order.
373
+ # +from+ is the branch/commit name of the younger item
374
+ # +to+ is the branch/commit name of the older item
375
+ #
376
+ # Returns Grit::Commit[] (baked)
377
+ def commits_between(from, to)
378
+ Commit.find_all(self, "#{from}..#{to}").reverse
379
+ end
380
+
381
+ # The Commits objects that are newer than the specified date.
382
+ # Commits are returned in chronological order.
383
+ # +start+ is the branch/commit name (default 'master')
384
+ # +since+ is a string represeting a date/time
385
+ # +extra_options+ is a hash of extra options
386
+ #
387
+ # Returns Grit::Commit[] (baked)
388
+ def commits_since(start = 'master', since = '1970-01-01', extra_options = {})
389
+ options = {:since => since}.merge(extra_options)
390
+
391
+ Commit.find_all(self, start, options)
392
+ end
393
+
394
+ # The number of commits reachable by the given branch/commit
395
+ # +start+ is the branch/commit name (default 'master')
396
+ #
397
+ # Returns Integer
398
+ def commit_count(start = 'master')
399
+ Commit.count(self, start)
400
+ end
401
+
402
+ # The Commit object for the specified id
403
+ # +id+ is the SHA1 identifier of the commit
404
+ #
405
+ # Returns Grit::Commit (baked)
406
+ def commit(id)
407
+ options = {:max_count => 1}
408
+
409
+ Commit.find_all(self, id, options).first
410
+ end
411
+
412
+ # Returns a list of commits that is in +other_repo+ but not in self
413
+ #
414
+ # Returns Grit::Commit[]
415
+ def commit_deltas_from(other_repo, ref = "master", other_ref = "master")
416
+ # TODO: we should be able to figure out the branch point, rather than
417
+ # rev-list'ing the whole thing
418
+ repo_refs = self.git.rev_list({}, ref).strip.split("\n")
419
+ other_repo_refs = other_repo.git.rev_list({}, other_ref).strip.split("\n")
420
+
421
+ (other_repo_refs - repo_refs).map do |ref|
422
+ Commit.find_all(other_repo, ref, {:max_count => 1}).first
423
+ end
424
+ end
425
+
426
+ def objects(refs)
427
+ Grit.no_quote = true
428
+ obj = self.git.rev_list({:objects => true, :timeout => false}, refs).split("\n").map { |a| a[0, 40] }
429
+ Grit.no_quote = false
430
+ obj
431
+ end
432
+
433
+ def commit_objects(refs)
434
+ Grit.no_quote = true
435
+ obj = self.git.rev_list({:timeout => false}, refs).split("\n").map { |a| a[0, 40] }
436
+ Grit.no_quote = false
437
+ obj
438
+ end
439
+
440
+ def objects_between(ref1, ref2 = nil)
441
+ if ref2
442
+ refs = "#{ref2}..#{ref1}"
443
+ else
444
+ refs = ref1
445
+ end
446
+ self.objects(refs)
447
+ end
448
+
449
+ def diff_objects(commit_sha, parents = true)
450
+ revs = []
451
+ Grit.no_quote = true
452
+ if parents
453
+ # PARENTS:
454
+ cmd = "-r -t -m #{commit_sha}"
455
+ revs = self.git.diff_tree({:timeout => false}, cmd).strip.split("\n").map{ |a| r = a.split(' '); r[3] if r[1] != '160000' }
456
+ else
457
+ # NO PARENTS:
458
+ cmd = "-r -t #{commit_sha}"
459
+ revs = self.git.method_missing('ls-tree', {:timeout => false}, "-r -t #{commit_sha}").split("\n").map{ |a| a.split("\t").first.split(' ')[2] }
460
+ end
461
+ revs << self.commit(commit_sha).tree.id
462
+ Grit.no_quote = false
463
+ return revs.uniq.compact
464
+ end
465
+
466
+ # The Tree object for the given treeish reference
467
+ # +treeish+ is the reference (default 'master')
468
+ # +paths+ is an optional Array of directory paths to restrict the tree (deafult [])
469
+ #
470
+ # Examples
471
+ # repo.tree('master', ['lib/'])
472
+ #
473
+ # Returns Grit::Tree (baked)
474
+ def tree(treeish = 'master', paths = [])
475
+ Tree.construct(self, treeish, paths)
476
+ end
477
+
478
+ # The Blob object for the given id
479
+ # +id+ is the SHA1 id of the blob
480
+ #
481
+ # Returns Grit::Blob (unbaked)
482
+ def blob(id)
483
+ Blob.create(self, :id => id)
484
+ end
485
+
486
+ # The commit log for a treeish
487
+ #
488
+ # Returns Grit::Commit[]
489
+ def log(commit = 'master', path = nil, options = {})
490
+ default_options = {:pretty => "raw"}
491
+ actual_options = default_options.merge(options)
492
+ arg = path ? [commit, '--', path] : [commit]
493
+ commits = self.git.log(actual_options, *arg)
494
+ Commit.list_from_string(self, commits)
495
+ end
496
+
497
+ # The diff from commit +a+ to commit +b+, optionally restricted to the given file(s)
498
+ # +a+ is the base commit
499
+ # +b+ is the other commit
500
+ # +paths+ is an optional list of file paths on which to restrict the diff
501
+ def diff(a, b, *paths)
502
+ diff = self.git.native('diff', {:full_index => true}, a, b, '--', *paths)
503
+
504
+ if diff =~ /diff --git a/
505
+ diff = diff.sub(/.+?(diff --git a)/m, '\1')
506
+ else
507
+ diff = ''
508
+ end
509
+ Diff.list_from_string(self, diff)
510
+ end
511
+
512
+ # The commit diff for the given commit
513
+ # +commit+ is the commit name/id
514
+ #
515
+ # Returns Grit::Diff[]
516
+ def commit_diff(commit)
517
+ Commit.diff(self, commit)
518
+ end
519
+
520
+ # Archive the given treeish
521
+ # +treeish+ is the treeish name/id (default 'master')
522
+ # +prefix+ is the optional prefix
523
+ #
524
+ # Examples
525
+ # repo.archive_tar
526
+ # # => <String containing tar archive>
527
+ #
528
+ # repo.archive_tar('a87ff14')
529
+ # # => <String containing tar archive for commit a87ff14>
530
+ #
531
+ # repo.archive_tar('master', 'myproject/')
532
+ # # => <String containing tar archive and prefixed with 'myproject/'>
533
+ #
534
+ # Returns String (containing tar archive)
535
+ def archive_tar(treeish = 'master', prefix = nil)
536
+ options = {}
537
+ options[:prefix] = prefix if prefix
538
+ self.git.archive(options, treeish)
539
+ end
540
+
541
+ # Archive and gzip the given treeish
542
+ # +treeish+ is the treeish name/id (default 'master')
543
+ # +prefix+ is the optional prefix
544
+ #
545
+ # Examples
546
+ # repo.archive_tar_gz
547
+ # # => <String containing tar.gz archive>
548
+ #
549
+ # repo.archive_tar_gz('a87ff14')
550
+ # # => <String containing tar.gz archive for commit a87ff14>
551
+ #
552
+ # repo.archive_tar_gz('master', 'myproject/')
553
+ # # => <String containing tar.gz archive and prefixed with 'myproject/'>
554
+ #
555
+ # Returns String (containing tar.gz archive)
556
+ def archive_tar_gz(treeish = 'master', prefix = nil)
557
+ options = {}
558
+ options[:prefix] = prefix if prefix
559
+ self.git.archive(options, treeish, "| gzip -n")
560
+ end
561
+
562
+ # Write an archive directly to a file
563
+ # +treeish+ is the treeish name/id (default 'master')
564
+ # +prefix+ is the optional prefix (default nil)
565
+ # +filename+ is the name of the file (default 'archive.tar.gz')
566
+ # +format+ is the optional format (default nil)
567
+ # +pipe+ is the command to run the output through (default 'gzip')
568
+ #
569
+ # Returns nothing
570
+ def archive_to_file(treeish = 'master', prefix = nil, filename = 'archive.tar.gz', format = nil, pipe = "gzip")
571
+ options = {}
572
+ options[:prefix] = prefix if prefix
573
+ options[:format] = format if format
574
+ self.git.archive(options, treeish, "| #{pipe} > #{filename}")
575
+ end
576
+
577
+ # Enable git-daemon serving of this repository by writing the
578
+ # git-daemon-export-ok file to its git directory
579
+ #
580
+ # Returns nothing
581
+ def enable_daemon_serve
582
+ self.git.fs_write(DAEMON_EXPORT_FILE, '')
583
+ end
584
+
585
+ # Disable git-daemon serving of this repository by ensuring there is no
586
+ # git-daemon-export-ok file in its git directory
587
+ #
588
+ # Returns nothing
589
+ def disable_daemon_serve
590
+ self.git.fs_delete(DAEMON_EXPORT_FILE)
591
+ end
592
+
593
+ def gc_auto
594
+ self.git.gc({:auto => true})
595
+ end
596
+
597
+ # The list of alternates for this repo
598
+ #
599
+ # Returns Array[String] (pathnames of alternates)
600
+ def alternates
601
+ alternates_path = "objects/info/alternates"
602
+ self.git.fs_read(alternates_path).strip.split("\n")
603
+ rescue Errno::ENOENT
604
+ []
605
+ end
606
+
607
+ # Sets the alternates
608
+ # +alts+ is the Array of String paths representing the alternates
609
+ #
610
+ # Returns nothing
611
+ def alternates=(alts)
612
+ alts.each do |alt|
613
+ unless File.exist?(alt)
614
+ raise "Could not set alternates. Alternate path #{alt} must exist"
615
+ end
616
+ end
617
+
618
+ if alts.empty?
619
+ self.git.fs_write('objects/info/alternates', '')
620
+ else
621
+ self.git.fs_write('objects/info/alternates', alts.join("\n"))
622
+ end
623
+ end
624
+
625
+ def config
626
+ @config ||= Config.new(self)
627
+ end
628
+
629
+ def index
630
+ Index.new(self)
631
+ end
632
+
633
+ def update_ref(head, commit_sha)
634
+ return nil if !commit_sha || (commit_sha.size != 40)
635
+ self.git.fs_write("refs/heads/#{head}", commit_sha)
636
+ commit_sha
637
+ end
638
+
639
+ # Rename the current repository directory.
640
+ # +name+ is the new name
641
+ #
642
+ # Returns nothing
643
+ def rename(name)
644
+ if @bare
645
+ self.git.fs_move('/', "../#{name}")
646
+ else
647
+ self.git.fs_move('/', "../../#{name}")
648
+ end
649
+ end
650
+
651
+ # Pretty object inspection
652
+ def inspect
653
+ %Q{#<Grit::Repo "#{@path}">}
654
+ end
655
+ end # Repo
656
+
657
+ end # Grit