robinluckey-grit 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,140 @@
1
+ module Grit
2
+
3
+ class Git
4
+ class GitTimeout < RuntimeError
5
+ attr_reader :command, :bytes_read
6
+
7
+ def initialize(command = nil, bytes_read = nil)
8
+ @command = command
9
+ @bytes_read = bytes_read
10
+ end
11
+ end
12
+
13
+ undef_method :clone
14
+
15
+ include GitRuby
16
+
17
+ class << self
18
+ attr_accessor :git_binary, :git_timeout, :git_max_size
19
+ end
20
+
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
31
+
32
+ attr_accessor :git_dir, :bytes_read
33
+
34
+ def initialize(git_dir)
35
+ self.git_dir = git_dir
36
+ self.bytes_read = 0
37
+ end
38
+
39
+ def shell_escape(str)
40
+ str.to_s.gsub("'", "\\\\'").gsub(";", '\\;')
41
+ end
42
+ alias_method :e, :shell_escape
43
+
44
+ # Run the given git command with the specified arguments and return
45
+ # the result as a String
46
+ # +cmd+ is the command
47
+ # +options+ is a hash of Ruby style options
48
+ # +args+ is the list of arguments (to be joined by spaces)
49
+ #
50
+ # Examples
51
+ # git.rev_list({:max_count => 10, :header => true}, "master")
52
+ #
53
+ # Returns String
54
+ def method_missing(cmd, options = {}, *args)
55
+ run('', cmd, '', options, args)
56
+ end
57
+
58
+ def run(prefix, cmd, postfix, options, args)
59
+ timeout = options.delete(:timeout) rescue nil
60
+ timeout = true if timeout.nil?
61
+
62
+ opt_args = transform_options(options)
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)}"
66
+ Grit.log(call) if Grit.debug
67
+ response, err = timeout ? sh(call) : wild_sh(call)
68
+ Grit.log(response) if Grit.debug
69
+ Grit.log(err) if Grit.debug
70
+ response
71
+ end
72
+
73
+ def sh(command)
74
+ ret, err = '', ''
75
+ Open3.popen3(command) do |_, stdout, stderr|
76
+ Timeout.timeout(self.class.git_timeout) do
77
+ while tmp = stdout.read(1024)
78
+ ret += tmp
79
+ if (@bytes_read += tmp.size) > self.class.git_max_size
80
+ bytes = @bytes_read
81
+ @bytes_read = 0
82
+ raise GitTimeout.new(command, bytes)
83
+ end
84
+ end
85
+ end
86
+
87
+ while tmp = stderr.read(1024)
88
+ err += tmp
89
+ end
90
+ end
91
+ [ret, err]
92
+ rescue Timeout::Error, Grit::Git::GitTimeout
93
+ bytes = @bytes_read
94
+ @bytes_read = 0
95
+ raise GitTimeout.new(command, bytes)
96
+ end
97
+
98
+ def wild_sh(command)
99
+ ret, err = '', ''
100
+ Open3.popen3(command) do |_, stdout, stderr|
101
+ while tmp = stdout.read(1024)
102
+ ret += tmp
103
+ end
104
+
105
+ while tmp = stderr.read(1024)
106
+ err += tmp
107
+ end
108
+ end
109
+ [ret, err]
110
+ end
111
+
112
+ # Transform Ruby style options into git command line options
113
+ # +options+ is a hash of Ruby style options
114
+ #
115
+ # Returns String[]
116
+ # e.g. ["--max-count=10", "--header"]
117
+ def transform_options(options)
118
+ args = []
119
+ options.keys.each do |opt|
120
+ if opt.to_s.size == 1
121
+ if options[opt] == true
122
+ args << "-#{opt}"
123
+ else
124
+ val = options.delete(opt)
125
+ args << "-#{opt.to_s} '#{e(val)}'"
126
+ end
127
+ else
128
+ if options[opt] == true
129
+ args << "--#{opt.to_s.gsub(/_/, '-')}"
130
+ else
131
+ val = options.delete(opt)
132
+ args << "--#{opt.to_s.gsub(/_/, '-')}='#{e(val)}'"
133
+ end
134
+ end
135
+ end
136
+ args
137
+ end
138
+ end # Git
139
+
140
+ end # Grit
@@ -0,0 +1,122 @@
1
+ module Grit
2
+
3
+ class Index
4
+ attr_accessor :repo, :tree, :current_tree
5
+
6
+ def initialize(repo)
7
+ self.repo = repo
8
+ self.tree = {}
9
+ self.current_tree = nil
10
+ end
11
+
12
+ # Add a file to the index
13
+ # +path+ is the path (including filename)
14
+ # +data+ is the binary contents of the file
15
+ #
16
+ # Returns nothing
17
+ def add(file_path, data)
18
+ path = file_path.split('/')
19
+ filename = path.pop
20
+
21
+ current = self.tree
22
+
23
+ path.each do |dir|
24
+ current[dir] ||= {}
25
+ node = current[dir]
26
+ current = node
27
+ end
28
+
29
+ current[filename] = data
30
+ end
31
+
32
+ # Sets the current tree
33
+ # +tree+ the branch/tag/sha... to use - a string
34
+ #
35
+ # Returns index (self)
36
+ def read_tree(tree)
37
+ self.current_tree = self.repo.tree(tree)
38
+ end
39
+
40
+ # Commit the contents of the index
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]
46
+ #
47
+ # Returns a String of the SHA1 of the commit
48
+ def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
49
+ tree_sha1 = write_tree(self.tree, self.current_tree)
50
+ return false if tree_sha1 == last_tree # don't write identical commits
51
+
52
+ contents = []
53
+ contents << ['tree', tree_sha1].join(' ')
54
+ parents.each do |p|
55
+ contents << ['parent', p].join(' ') if p
56
+ end if parents
57
+
58
+ if actor
59
+ name = actor.name
60
+ email = actor.email
61
+ else
62
+ config = Config.new(self.repo)
63
+ name = config['user.name']
64
+ email = config['user.email']
65
+ end
66
+
67
+ author_string = "#{name} <#{email}> #{Time.now.to_i} -0700" # !! TODO : gotta fix this
68
+ contents << ['author', author_string].join(' ')
69
+ contents << ['committer', author_string].join(' ')
70
+ contents << ''
71
+ contents << message
72
+
73
+ commit_sha1 = self.repo.git.ruby_git.put_raw_object(contents.join("\n"), 'commit')
74
+
75
+ self.repo.update_ref(head, commit_sha1)
76
+ end
77
+
78
+ # Recursively write a tree to the index
79
+ # +tree+ is the tree
80
+ #
81
+ # Returns the SHA1 String of the tree
82
+ def write_tree(tree, now_tree = nil)
83
+ tree_contents = {}
84
+
85
+ # fill in original tree
86
+ now_tree.contents.each do |obj|
87
+ sha = [obj.id].pack("H*")
88
+ k = obj.name
89
+ k += '/' if (obj.class == Grit::Tree)
90
+ tree_contents[k] = "%s %s\0%s" % [obj.mode.to_s, obj.name, sha]
91
+ end if now_tree
92
+
93
+ # overwrite with new tree contents
94
+ tree.each do |k, v|
95
+ case v
96
+ when String
97
+ sha = write_blob(v)
98
+ sha = [sha].pack("H*")
99
+ str = "%s %s\0%s" % ['100644', k, sha]
100
+ tree_contents[k] = str
101
+ when Hash
102
+ ctree = now_tree/k if now_tree
103
+ sha = write_tree(v, ctree)
104
+ sha = [sha].pack("H*")
105
+ str = "%s %s\0%s" % ['040000', k, sha]
106
+ tree_contents[k + '/'] = str
107
+ end
108
+ end
109
+ tr = tree_contents.sort.map { |k, v| v }.join('')
110
+ self.repo.git.ruby_git.put_raw_object(tr, 'tree')
111
+ end
112
+
113
+ # Write the blob to the index
114
+ # +data+ is the data to write
115
+ #
116
+ # Returns the SHA1 String of the blob
117
+ def write_blob(data)
118
+ self.repo.git.ruby_git.put_raw_object(data, 'blob')
119
+ end
120
+ end # Index
121
+
122
+ end # Grit
@@ -0,0 +1,33 @@
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 lazy_reader(*args)
20
+ args.each do |arg|
21
+ ivar = "@#{arg}"
22
+ define_method(arg) do
23
+ if instance_variable_defined?(ivar)
24
+ val = instance_variable_get(ivar)
25
+ return val if val
26
+ end
27
+ instance_variable_set(ivar, (@lazy_source ||= lazy_source).send(arg))
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Object.extend Lazy unless Object.ancestors.include? Lazy
@@ -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,99 @@
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 = []
14
+ already = {}
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
+
28
+ if File.file?('packed-refs')
29
+ File.readlines('packed-refs').each do |line|
30
+ if m = /^(\w{40}) (.*?)$/.match(line)
31
+ next if !Regexp.new('^' + prefix).match(m[2])
32
+ name = m[2].sub("#{prefix}/", '')
33
+ commit = Commit.create(repo, :id => m[1])
34
+ if !already[name]
35
+ refs << self.new(name, commit)
36
+ already[name] = true
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ refs
44
+ end
45
+
46
+ protected
47
+
48
+ def prefix
49
+ "refs/#{name.to_s.gsub(/^.*::/, '').downcase}s"
50
+ end
51
+
52
+ end
53
+
54
+ attr_reader :name
55
+ attr_reader :commit
56
+
57
+ # Instantiate a new Head
58
+ # +name+ is the name of the head
59
+ # +commit+ is the Commit that the head points to
60
+ #
61
+ # Returns Grit::Head (baked)
62
+ def initialize(name, commit)
63
+ @name = name
64
+ @commit = commit
65
+ end
66
+
67
+ # Pretty object inspection
68
+ def inspect
69
+ %Q{#<#{self.class.name} "#{@name}">}
70
+ end
71
+ end # Ref
72
+
73
+ # A Head is a named reference to a Commit. Every Head instance contains a name
74
+ # and a Commit object.
75
+ #
76
+ # r = Grit::Repo.new("/path/to/repo")
77
+ # h = r.heads.first
78
+ # h.name # => "master"
79
+ # h.commit # => #<Grit::Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
80
+ # h.commit.id # => "1c09f116cbc2cb4100fb6935bb162daa4723f455"
81
+ class Head < Ref
82
+
83
+ # Get the HEAD revision of the repo.
84
+ # +repo+ is the Repo
85
+ # +options+ is a Hash of options
86
+ #
87
+ # Returns Grit::Head (baked)
88
+ def self.current(repo, options = {})
89
+ head = File.open(File.join(repo.path, 'HEAD')).read.chomp
90
+ if /ref: refs\/heads\/(.*)/.match(head)
91
+ self.new($1, repo.git.rev_parse(options, 'HEAD'))
92
+ end
93
+ end
94
+
95
+ end # Head
96
+
97
+ class Remote < Ref; end
98
+
99
+ end # Grit
@@ -0,0 +1,437 @@
1
+ module Grit
2
+
3
+ class Repo
4
+ DAEMON_EXPORT_FILE = 'git-daemon-export-ok'
5
+
6
+ # The path of the git repo as a String
7
+ attr_accessor :path
8
+ attr_accessor :working_dir
9
+ attr_reader :bare
10
+
11
+ # The git command line interface object
12
+ attr_accessor :git
13
+
14
+ # Create a new Repo instance
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
17
+ #
18
+ # Examples
19
+ # g = Repo.new("/Users/tom/dev/grit")
20
+ # g = Repo.new("/Users/tom/public/grit.git")
21
+ #
22
+ # Returns Grit::Repo
23
+ def initialize(path, options = {})
24
+ epath = File.expand_path(path)
25
+
26
+ if File.exist?(File.join(epath, '.git'))
27
+ self.working_dir = epath
28
+ self.path = File.join(epath, '.git')
29
+ @bare = false
30
+ elsif File.exist?(epath) && (epath =~ /\.git$/ || options[:is_bare])
31
+ self.path = epath
32
+ @bare = true
33
+ elsif File.exist?(epath)
34
+ raise InvalidGitRepositoryError.new(epath)
35
+ else
36
+ raise NoSuchPathError.new(epath)
37
+ end
38
+
39
+ self.git = Git.new(self.path)
40
+ end
41
+
42
+ # Does nothing yet...
43
+ def self.init(path)
44
+ # !! TODO !!
45
+ # create directory
46
+ # generate initial git directory
47
+ # create new Grit::Repo on that dir, return it
48
+ end
49
+
50
+ # The project's description. Taken verbatim from GIT_REPO/description
51
+ #
52
+ # Returns String
53
+ def description
54
+ File.open(File.join(self.path, 'description')).read.chomp
55
+ end
56
+
57
+ def blame(file, commit = nil)
58
+ Blame.new(self, file, commit)
59
+ end
60
+
61
+
62
+ # An array of Head objects representing the branch heads in
63
+ # this repo
64
+ #
65
+ # Returns Grit::Head[] (baked)
66
+ def heads
67
+ Head.find_all(self)
68
+ end
69
+
70
+ alias_method :branches, :heads
71
+
72
+ def get_head(head_name)
73
+ heads.find { |h| h.name == head_name }
74
+ end
75
+
76
+ def is_head?(head_name)
77
+ get_head(head_name)
78
+ end
79
+
80
+ # Object reprsenting the current repo head.
81
+ #
82
+ # Returns Grit::Head (baked)
83
+ def head
84
+ Head.current(self)
85
+ end
86
+
87
+
88
+ # Commits current index
89
+ #
90
+ # Returns true/false if commit worked
91
+ def commit_index(message)
92
+ self.git.commit({}, '-m', message)
93
+ end
94
+
95
+ # Commits all tracked and modified files
96
+ #
97
+ # Returns true/false if commit worked
98
+ def commit_all(message)
99
+ self.git.commit({}, '-a', '-m', message)
100
+ end
101
+
102
+ # Adds files to the index
103
+ def add(*files)
104
+ self.git.add({}, *files.flatten)
105
+ end
106
+
107
+ # Remove files from the index
108
+ def remove(*files)
109
+ self.git.rm({}, *files.flatten)
110
+ end
111
+
112
+
113
+ def blame_tree(commit, path = nil)
114
+ commit_array = self.git.blame_tree(commit, path)
115
+
116
+ final_array = {}
117
+ commit_array.each do |file, sha|
118
+ final_array[file] = commit(sha)
119
+ end
120
+ final_array
121
+ end
122
+
123
+ def status
124
+ Status.new(self)
125
+ end
126
+
127
+
128
+ # An array of Tag objects that are available in this repo
129
+ #
130
+ # Returns Grit::Tag[] (baked)
131
+ def tags
132
+ Tag.find_all(self)
133
+ end
134
+
135
+ # An array of Remote objects representing the remote branches in
136
+ # this repo
137
+ #
138
+ # Returns Grit::Remote[] (baked)
139
+ def remotes
140
+ Remote.find_all(self)
141
+ end
142
+
143
+ # An array of Ref objects representing the refs in
144
+ # this repo
145
+ #
146
+ # Returns Grit::Ref[] (baked)
147
+ def refs
148
+ [ Head.find_all(self), Tag.find_all(self), Remote.find_all(self) ].flatten
149
+ end
150
+
151
+ def commit_stats(start = 'master', max_count = 10, skip = 0)
152
+ options = {:max_count => max_count,
153
+ :skip => skip}
154
+
155
+ CommitStats.find_all(self, start, options)
156
+ end
157
+
158
+ # An array of Commit objects representing the history of a given ref/commit
159
+ # +start+ is the branch/commit name (default 'master')
160
+ # +max_count+ is the maximum number of commits to return (default 10, use +false+ for all)
161
+ # +skip+ is the number of commits to skip (default 0)
162
+ #
163
+ # Returns Grit::Commit[] (baked)
164
+ def commits(start = 'master', max_count = 10, skip = 0)
165
+ options = {:max_count => max_count,
166
+ :skip => skip}
167
+
168
+ Commit.find_all(self, start, options)
169
+ end
170
+
171
+ # The Commits objects that are reachable via +to+ but not via +from+
172
+ # Commits are returned in chronological order.
173
+ # +from+ is the branch/commit name of the younger item
174
+ # +to+ is the branch/commit name of the older item
175
+ #
176
+ # Returns Grit::Commit[] (baked)
177
+ def commits_between(from, to)
178
+ Commit.find_all(self, "#{from}..#{to}").reverse
179
+ end
180
+
181
+ # The Commits objects that are newer than the specified date.
182
+ # Commits are returned in chronological order.
183
+ # +start+ is the branch/commit name (default 'master')
184
+ # +since+ is a string represeting a date/time
185
+ # +extra_options+ is a hash of extra options
186
+ #
187
+ # Returns Grit::Commit[] (baked)
188
+ def commits_since(start = 'master', since = '1970-01-01', extra_options = {})
189
+ options = {:since => since}.merge(extra_options)
190
+
191
+ Commit.find_all(self, start, options)
192
+ end
193
+
194
+ # The number of commits reachable by the given branch/commit
195
+ # +start+ is the branch/commit name (default 'master')
196
+ #
197
+ # Returns Integer
198
+ def commit_count(start = 'master')
199
+ Commit.count(self, start)
200
+ end
201
+
202
+ # The Commit object for the specified id
203
+ # +id+ is the SHA1 identifier of the commit
204
+ #
205
+ # Returns Grit::Commit (baked)
206
+ def commit(id)
207
+ options = {:max_count => 1}
208
+
209
+ Commit.find_all(self, id, options).first
210
+ end
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
+
226
+ # The Tree object for the given treeish reference
227
+ # +treeish+ is the reference (default 'master')
228
+ # +paths+ is an optional Array of directory paths to restrict the tree (deafult [])
229
+ #
230
+ # Examples
231
+ # repo.tree('master', ['lib/'])
232
+ #
233
+ # Returns Grit::Tree (baked)
234
+ def tree(treeish = 'master', paths = [])
235
+ Tree.construct(self, treeish, paths)
236
+ end
237
+
238
+ # The Blob object for the given id
239
+ # +id+ is the SHA1 id of the blob
240
+ #
241
+ # Returns Grit::Blob (unbaked)
242
+ def blob(id)
243
+ Blob.create(self, :id => id)
244
+ end
245
+
246
+ # The commit log for a treeish
247
+ #
248
+ # Returns Grit::Commit[]
249
+ def log(commit = 'master', path = nil, options = {})
250
+ default_options = {:pretty => "raw"}
251
+ actual_options = default_options.merge(options)
252
+ arg = path ? [commit, '--', path] : [commit]
253
+ commits = self.git.log(actual_options, *arg)
254
+ Commit.list_from_string(self, commits)
255
+ end
256
+
257
+ # The diff from commit +a+ to commit +b+, optionally restricted to the given file(s)
258
+ # +a+ is the base commit
259
+ # +b+ is the other commit
260
+ # +paths+ is an optional list of file paths on which to restrict the diff
261
+ def diff(a, b, *paths)
262
+ self.git.diff({}, a, b, '--', *paths)
263
+ end
264
+
265
+ # The commit diff for the given commit
266
+ # +commit+ is the commit name/id
267
+ #
268
+ # Returns Grit::Diff[]
269
+ def commit_diff(commit)
270
+ Commit.diff(self, commit)
271
+ end
272
+
273
+ # Initialize a bare git repository at the given path
274
+ # +path+ is the full path to the repo (traditionally ends with /<name>.git)
275
+ # +options+ is any additional options to the git init command
276
+ #
277
+ # Examples
278
+ # Grit::Repo.init_bare('/var/git/myrepo.git')
279
+ #
280
+ # Returns Grit::Repo (the newly created repo)
281
+ def self.init_bare(path, git_options = {}, repo_options = {})
282
+ git = Git.new(path)
283
+ git.init(git_options)
284
+ self.new(path, repo_options)
285
+ end
286
+
287
+ # Fork a bare git repository from this repo
288
+ # +path+ is the full path of the new repo (traditionally ends with /<name>.git)
289
+ # +options+ is any additional options to the git clone command (:bare and :shared are true by default)
290
+ #
291
+ # Returns Grit::Repo (the newly forked repo)
292
+ def fork_bare(path, options = {})
293
+ default_options = {:bare => true, :shared => true}
294
+ real_options = default_options.merge(options)
295
+ self.git.clone(real_options, self.path, path)
296
+ Repo.new(path)
297
+ end
298
+
299
+ # Archive the given treeish
300
+ # +treeish+ is the treeish name/id (default 'master')
301
+ # +prefix+ is the optional prefix
302
+ #
303
+ # Examples
304
+ # repo.archive_tar
305
+ # # => <String containing tar archive>
306
+ #
307
+ # repo.archive_tar('a87ff14')
308
+ # # => <String containing tar archive for commit a87ff14>
309
+ #
310
+ # repo.archive_tar('master', 'myproject/')
311
+ # # => <String containing tar archive and prefixed with 'myproject/'>
312
+ #
313
+ # Returns String (containing tar archive)
314
+ def archive_tar(treeish = 'master', prefix = nil)
315
+ options = {}
316
+ options[:prefix] = prefix if prefix
317
+ self.git.archive(options, treeish)
318
+ end
319
+
320
+ # Archive and gzip the given treeish
321
+ # +treeish+ is the treeish name/id (default 'master')
322
+ # +prefix+ is the optional prefix
323
+ #
324
+ # Examples
325
+ # repo.archive_tar_gz
326
+ # # => <String containing tar.gz archive>
327
+ #
328
+ # repo.archive_tar_gz('a87ff14')
329
+ # # => <String containing tar.gz archive for commit a87ff14>
330
+ #
331
+ # repo.archive_tar_gz('master', 'myproject/')
332
+ # # => <String containing tar.gz archive and prefixed with 'myproject/'>
333
+ #
334
+ # Returns String (containing tar.gz archive)
335
+ def archive_tar_gz(treeish = 'master', prefix = nil)
336
+ options = {}
337
+ options[:prefix] = prefix if prefix
338
+ self.git.archive(options, treeish, "| gzip")
339
+ end
340
+
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")
350
+ options = {}
351
+ options[:prefix] = prefix if prefix
352
+ options[:format] = format if format
353
+ self.git.archive(options, treeish, "| #{pipe} > #{filename}")
354
+ end
355
+
356
+ # Enable git-daemon serving of this repository by writing the
357
+ # git-daemon-export-ok file to its git directory
358
+ #
359
+ # Returns nothing
360
+ def enable_daemon_serve
361
+ FileUtils.touch(File.join(self.path, DAEMON_EXPORT_FILE))
362
+ end
363
+
364
+ # Disable git-daemon serving of this repository by ensuring there is no
365
+ # git-daemon-export-ok file in its git directory
366
+ #
367
+ # Returns nothing
368
+ def disable_daemon_serve
369
+ FileUtils.rm_f(File.join(self.path, DAEMON_EXPORT_FILE))
370
+ end
371
+
372
+ def gc_auto
373
+ self.git.gc({:auto => true})
374
+ end
375
+
376
+ # The list of alternates for this repo
377
+ #
378
+ # Returns Array[String] (pathnames of alternates)
379
+ def alternates
380
+ alternates_path = File.join(self.path, *%w{objects info alternates})
381
+
382
+ if File.exist?(alternates_path)
383
+ File.read(alternates_path).strip.split("\n")
384
+ else
385
+ []
386
+ end
387
+ end
388
+
389
+ # Sets the alternates
390
+ # +alts+ is the Array of String paths representing the alternates
391
+ #
392
+ # Returns nothing
393
+ def alternates=(alts)
394
+ alts.each do |alt|
395
+ unless File.exist?(alt)
396
+ raise "Could not set alternates. Alternate path #{alt} must exist"
397
+ end
398
+ end
399
+
400
+ if alts.empty?
401
+ File.open(File.join(self.path, *%w{objects info alternates}), 'w') do |f|
402
+ f.write ''
403
+ end
404
+ else
405
+ File.open(File.join(self.path, *%w{objects info alternates}), 'w') do |f|
406
+ f.write alts.join("\n")
407
+ end
408
+ end
409
+ end
410
+
411
+ def config
412
+ @config ||= Config.new(self)
413
+ end
414
+
415
+ def index
416
+ Index.new(self)
417
+ end
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
+
431
+ # Pretty object inspection
432
+ def inspect
433
+ %Q{#<Grit::Repo "#{@path}">}
434
+ end
435
+ end # Repo
436
+
437
+ end # Grit