smeagol 0.6.0 → 0.6.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 (37) hide show
  1. checksums.yaml +7 -0
  2. data/.index +82 -0
  3. data/HISTORY.md +14 -0
  4. data/lib/smeagol.rb +2 -1
  5. data/lib/smeagol/app.rb +1 -1
  6. data/lib/smeagol/gollum/file.rb +1 -1
  7. data/lib/smeagol/wiki.rb +1 -0
  8. data/vendor/grit/lib/grit.rb +75 -0
  9. data/vendor/grit/lib/grit/actor.rb +52 -0
  10. data/vendor/grit/lib/grit/blame.rb +70 -0
  11. data/vendor/grit/lib/grit/blob.rb +126 -0
  12. data/vendor/grit/lib/grit/commit.rb +313 -0
  13. data/vendor/grit/lib/grit/commit_stats.rb +128 -0
  14. data/vendor/grit/lib/grit/config.rb +44 -0
  15. data/vendor/grit/lib/grit/diff.rb +79 -0
  16. data/vendor/grit/lib/grit/errors.rb +10 -0
  17. data/vendor/grit/lib/grit/git-ruby.rb +262 -0
  18. data/vendor/grit/lib/grit/git-ruby/commit_db.rb +52 -0
  19. data/vendor/grit/lib/grit/git-ruby/git_object.rb +353 -0
  20. data/vendor/grit/lib/grit/git-ruby/internal/file_window.rb +58 -0
  21. data/vendor/grit/lib/grit/git-ruby/internal/loose.rb +137 -0
  22. data/vendor/grit/lib/grit/git-ruby/internal/pack.rb +397 -0
  23. data/vendor/grit/lib/grit/git-ruby/internal/raw_object.rb +44 -0
  24. data/vendor/grit/lib/grit/git-ruby/repository.rb +775 -0
  25. data/vendor/grit/lib/grit/git.rb +501 -0
  26. data/vendor/grit/lib/grit/index.rb +222 -0
  27. data/vendor/grit/lib/grit/lazy.rb +35 -0
  28. data/vendor/grit/lib/grit/merge.rb +45 -0
  29. data/vendor/grit/lib/grit/ref.rb +78 -0
  30. data/vendor/grit/lib/grit/repo.rb +709 -0
  31. data/vendor/grit/lib/grit/ruby1.9.rb +7 -0
  32. data/vendor/grit/lib/grit/status.rb +153 -0
  33. data/vendor/grit/lib/grit/submodule.rb +88 -0
  34. data/vendor/grit/lib/grit/tag.rb +102 -0
  35. data/vendor/grit/lib/grit/tree.rb +125 -0
  36. metadata +125 -56
  37. data/.ruby +0 -80
@@ -0,0 +1,222 @@
1
+ module Grit
2
+
3
+ class Index
4
+ # Public: Gets/Sets the Grit::Repo to which this index belongs.
5
+ attr_accessor :repo
6
+
7
+ # Public: Gets/Sets the Hash tree map that holds the changes to be made
8
+ # in the next commit.
9
+ attr_accessor :tree
10
+
11
+ # Public: Gets/Sets the Grit::Tree object representing the tree upon
12
+ # which the next commit will be based.
13
+ attr_accessor :current_tree
14
+
15
+ # Public: if a tree or commit is written, this stores the size of that object
16
+ attr_reader :last_tree_size
17
+ attr_reader :last_commit_size
18
+
19
+ # Initialize a new Index object.
20
+ #
21
+ # repo - The Grit::Repo to which the index belongs.
22
+ #
23
+ # Returns the newly initialized Grit::Index.
24
+ def initialize(repo)
25
+ self.repo = repo
26
+ self.tree = {}
27
+ self.current_tree = nil
28
+ end
29
+
30
+ # Public: Add a file to the index.
31
+ #
32
+ # path - The String file path including filename (no slash prefix).
33
+ # data - The String binary contents of the file.
34
+ #
35
+ # Returns nothing.
36
+ def add(path, data)
37
+ path = path.split('/')
38
+ filename = path.pop
39
+
40
+ current = self.tree
41
+
42
+ path.each do |dir|
43
+ current[dir] ||= {}
44
+ node = current[dir]
45
+ current = node
46
+ end
47
+
48
+ current[filename] = data
49
+ end
50
+
51
+ # Public: Delete the given file from the index.
52
+ #
53
+ # path - The String file path including filename (no slash prefix).
54
+ #
55
+ # Returns nothing.
56
+ def delete(path)
57
+ add(path, false)
58
+ end
59
+
60
+ # Public: Read the contents of the given Tree into the index to use as a
61
+ # starting point for the index.
62
+ #
63
+ # tree - The String branch/tag/sha of the Git tree object.
64
+ #
65
+ # Returns nothing.
66
+ def read_tree(tree)
67
+ self.current_tree = self.repo.tree(tree)
68
+ end
69
+
70
+ # Public: Commit the contents of the index. This method supports two
71
+ # formats for arguments:
72
+ #
73
+ # message - The String commit message.
74
+ # options - An optional Hash of index options.
75
+ # :parents - Array of String commit SHA1s or Grit::Commit
76
+ # objects to attach this commit to to form a
77
+ # new head (default: nil).
78
+ # :actor - The Grit::Actor details of the user making
79
+ # the commit (default: nil).
80
+ # :last_tree - The String SHA1 of a tree to compare with
81
+ # in order to avoid making empty commits
82
+ # (default: nil).
83
+ # :head - The String branch name to write this head to
84
+ # (default: nil).
85
+ # :committed_date - The Time that the commit was made.
86
+ # (Default: Time.now)
87
+ # :authored_date - The Time that the commit was authored.
88
+ # (Default: committed_date)
89
+ #
90
+ # The legacy argument style looks like:
91
+ #
92
+ # message - The String commit message.
93
+ # parents - Array of String commit SHA1s or Grit::Commit objects to
94
+ # attach this commit to to form a new head (default: nil).
95
+ # actor - The Grit::Actor details of the user making the commit
96
+ # (default: nil).
97
+ # last_tree - The String SHA1 of a tree to compare with in order to avoid
98
+ # making empty commits (default: nil).
99
+ # head - The String branch name to write this head to
100
+ # (default: "master").
101
+ #
102
+ # Returns a String of the SHA1 of the new commit.
103
+ def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
104
+ commit_tree_sha = nil
105
+ if parents.is_a?(Hash)
106
+ commit_tree_sha = parents[:commit_tree_sha]
107
+ actor = parents[:actor]
108
+ committer = parents[:committer]
109
+ author = parents[:author]
110
+ last_tree = parents[:last_tree]
111
+ head = parents[:head]
112
+ committed_date = parents[:committed_date]
113
+ authored_date = parents[:authored_date]
114
+ parents = parents[:parents]
115
+ end
116
+
117
+ committer ||= actor
118
+ author ||= committer
119
+
120
+ if commit_tree_sha
121
+ tree_sha1 = commit_tree_sha
122
+ else
123
+ tree_sha1 = write_tree(self.tree, self.current_tree)
124
+ end
125
+
126
+ # don't write identical commits
127
+ return false if tree_sha1 == last_tree
128
+
129
+ contents = []
130
+ contents << ['tree', tree_sha1].join(' ')
131
+ parents.each do |p|
132
+ contents << ['parent', p].join(' ')
133
+ end if parents
134
+
135
+ committer ||= begin
136
+ config = Config.new(self.repo)
137
+ Actor.new(config['user.name'], config['user.email'])
138
+ end
139
+ author ||= committer
140
+ committed_date ||= Time.now
141
+ authored_date ||= committed_date
142
+
143
+ contents << ['author', author.output(authored_date)].join(' ')
144
+ contents << ['committer', committer.output(committed_date)].join(' ')
145
+ contents << ''
146
+ contents << message
147
+
148
+ contents = contents.join("\n")
149
+ @last_commit_size = contents.size
150
+ commit_sha1 = self.repo.git.put_raw_object(contents, 'commit')
151
+
152
+ self.repo.update_ref(head, commit_sha1) if head
153
+ commit_sha1
154
+ end
155
+
156
+ # Recursively write a tree to the index.
157
+ #
158
+ # tree - The Hash tree map:
159
+ # key - The String directory or filename.
160
+ # val - The Hash submap or the String contents of the file.
161
+ # now_tree - The Grit::Tree representing the a previous tree upon which
162
+ # this tree will be based (default: nil).
163
+ #
164
+ # Returns the String SHA1 String of the tree.
165
+ def write_tree(tree = nil, now_tree = nil)
166
+ tree = self.tree if !tree
167
+ tree_contents = {}
168
+
169
+ # fill in original tree
170
+ now_tree = read_tree(now_tree) if(now_tree && now_tree.is_a?(String))
171
+ now_tree.contents.each do |obj|
172
+ sha = [obj.id].pack("H*")
173
+ k = obj.name
174
+ k += '/' if (obj.class == Grit::Tree)
175
+ tmode = obj.mode.to_i.to_s ## remove zero-padding
176
+ tree_contents[k] = "%s %s\0%s" % [tmode, obj.name, sha]
177
+ end if now_tree
178
+
179
+ # overwrite with new tree contents
180
+ tree.each do |k, v|
181
+ case v
182
+ when Array
183
+ sha, mode = v
184
+ if sha.size == 40 # must be a sha
185
+ sha = [sha].pack("H*")
186
+ mode = mode.to_i.to_s # leading 0s not allowed
187
+ k = k.split('/').last # slashes not allowed
188
+ str = "%s %s\0%s" % [mode, k, sha]
189
+ tree_contents[k] = str
190
+ end
191
+ when String
192
+ sha = write_blob(v)
193
+ sha = [sha].pack("H*")
194
+ str = "%s %s\0%s" % ['100644', k, sha]
195
+ tree_contents[k] = str
196
+ when Hash
197
+ ctree = now_tree/k if now_tree
198
+ sha = write_tree(v, ctree)
199
+ sha = [sha].pack("H*")
200
+ str = "%s %s\0%s" % ['40000', k, sha]
201
+ tree_contents[k + '/'] = str
202
+ when false
203
+ tree_contents.delete(k)
204
+ end
205
+ end
206
+
207
+ tr = tree_contents.sort.map { |k, v| v }.join('')
208
+ @last_tree_size = tr.size
209
+ self.repo.git.put_raw_object(tr, 'tree')
210
+ end
211
+
212
+ # Write a blob to the index.
213
+ #
214
+ # data - The String data to write.
215
+ #
216
+ # Returns the String SHA1 of the new blob.
217
+ def write_blob(data)
218
+ self.repo.git.put_raw_object(data, 'blob')
219
+ end
220
+ end # Index
221
+
222
+ end # Grit
@@ -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
@@ -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,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
@@ -0,0 +1,709 @@
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
+ repo_options = {:is_bare => true}.merge(repo_options)
103
+ self.new(path, repo_options)
104
+ end
105
+
106
+ # Public: Initialize a bare Git repository (create it on the filesystem)
107
+ # or, if the repo already exists, simply return it.
108
+ #
109
+ # path - The String full path to the repo. Traditionally ends with
110
+ # "/<name>.git".
111
+ # git_options - A Hash of additional options to the git init command
112
+ # (default: {}).
113
+ # repo_options - A Hash of additional options to the Grit::Repo.new call
114
+ # (default: {}).
115
+ #
116
+ # Returns the new or existing Grit::Repo.
117
+ def self.init_bare_or_open(path, git_options = {}, repo_options = {})
118
+ git = Git.new(path)
119
+
120
+ unless git.exist?
121
+ git.fs_mkdir(path)
122
+ git.init(git_options)
123
+ end
124
+
125
+ self.new(path, repo_options)
126
+ end
127
+
128
+ # Public: Create a bare fork of this repository.
129
+ #
130
+ # path - The String full path of where to create the new fork.
131
+ # Traditionally ends with "/<name>.git".
132
+ # options - The Hash of additional options to the git clone command.
133
+ # These options will be merged on top of the default Hash:
134
+ # {:bare => true, :shared => true}.
135
+ #
136
+ # Returns the newly forked Grit::Repo.
137
+ def fork_bare(path, options = {})
138
+ default_options = {:bare => true, :shared => true}
139
+ real_options = default_options.merge(options)
140
+ Git.new(path).fs_mkdir('..')
141
+ self.git.clone(real_options, self.path, path)
142
+ Repo.new(path)
143
+ end
144
+
145
+ # Public: Fork a bare git repository from another repo.
146
+ #
147
+ # path - The String full path of the repo from which to fork..
148
+ # Traditionally ends with "/<name>.git".
149
+ # options - The Hash of additional options to the git clone command.
150
+ # These options will be merged on top of the default Hash:
151
+ # {:bare => true, :shared => true}.
152
+ #
153
+ # Returns the newly forked Grit::Repo.
154
+ def fork_bare_from(path, options = {})
155
+ default_options = {:bare => true, :shared => true}
156
+ real_options = default_options.merge(options)
157
+ Git.new(self.path).fs_mkdir('..')
158
+ self.git.clone(real_options, path, self.path)
159
+ Repo.new(self.path)
160
+ end
161
+
162
+ # Public: Return the full Git objects from the given SHAs. Only Commit
163
+ # objects are parsed for now.
164
+ #
165
+ # *shas - Array of String SHAs.
166
+ #
167
+ # Returns an Array of Grit objects (Grit::Commit).
168
+ def batch(*shas)
169
+ shas.flatten!
170
+ text = git.native(:cat_file, {:batch => true, :input => (shas * "\n")})
171
+ parse_batch(text)
172
+ end
173
+
174
+ # Parses `git cat-file --batch` output, returning an array of Grit objects.
175
+ #
176
+ # text - Raw String output.
177
+ #
178
+ # Returns an Array of Grit objects (Grit::Commit).
179
+ def parse_batch(text)
180
+ io = StringIO.new(text)
181
+ objects = []
182
+ while line = io.gets
183
+ sha, type, size = line.split(" ", 3)
184
+ parser = BATCH_PARSERS[type]
185
+ if type == 'missing' || !parser
186
+ io.seek(size.to_i + 1, IO::SEEK_CUR)
187
+ objects << nil
188
+ next
189
+ end
190
+
191
+ object = io.read(size.to_i + 1)
192
+ objects << parser.parse_batch(self, sha, size, object)
193
+ end
194
+ objects
195
+ end
196
+
197
+ # The project's description. Taken verbatim from GIT_REPO/description
198
+ #
199
+ # Returns String
200
+ def description
201
+ self.git.fs_read('description').chomp
202
+ end
203
+
204
+ def blame(file, commit = nil)
205
+ Blame.new(self, file, commit)
206
+ end
207
+
208
+ # An array of Head objects representing the branch heads in
209
+ # this repo
210
+ #
211
+ # Returns Grit::Head[] (baked)
212
+ def heads
213
+ Head.find_all(self)
214
+ end
215
+
216
+ alias_method :branches, :heads
217
+
218
+ def get_head(head_name)
219
+ heads.find { |h| h.name == head_name }
220
+ end
221
+
222
+ def is_head?(head_name)
223
+ get_head(head_name)
224
+ end
225
+
226
+ # Object reprsenting the current repo head.
227
+ #
228
+ # Returns Grit::Head (baked)
229
+ def head
230
+ Head.current(self)
231
+ end
232
+
233
+
234
+ # Commits current index
235
+ #
236
+ # Returns true/false if commit worked
237
+ def commit_index(message)
238
+ self.git.commit({}, '-m', message)
239
+ end
240
+
241
+ # Commits all tracked and modified files
242
+ #
243
+ # Returns true/false if commit worked
244
+ def commit_all(message)
245
+ self.git.commit({}, '-a', '-m', message)
246
+ end
247
+
248
+ # Adds files to the index
249
+ def add(*files)
250
+ self.git.add({}, *files.flatten)
251
+ end
252
+
253
+ # Remove files from the index
254
+ def remove(*files)
255
+ self.git.rm({}, *files.flatten)
256
+ end
257
+
258
+
259
+ def blame_tree(commit, path = nil)
260
+ commit_array = self.git.blame_tree(commit, path)
261
+
262
+ final_array = {}
263
+ commit_array.each do |file, sha|
264
+ final_array[file] = commit(sha)
265
+ end
266
+ final_array
267
+ end
268
+
269
+ def status
270
+ Status.new(self)
271
+ end
272
+
273
+
274
+ # An array of Tag objects that are available in this repo
275
+ #
276
+ # Returns Grit::Tag[] (baked)
277
+ def tags
278
+ Tag.find_all(self)
279
+ end
280
+
281
+ # Finds the most recent annotated tag name that is reachable from a commit.
282
+ #
283
+ # @repo.recent_tag_name('master')
284
+ # # => "v1.0-0-abcdef"
285
+ #
286
+ # committish - optional commit SHA, branch, or tag name.
287
+ # options - optional hash of options to pass to git.
288
+ # Default: {:always => true}
289
+ # :tags => true # use lightweight tags too.
290
+ # :abbrev => Integer # number of hex digits to form the unique
291
+ # name. Defaults to 7.
292
+ # :long => true # always output tag + commit sha
293
+ # # see `git describe` docs for more options.
294
+ #
295
+ # Returns the String tag name, or just the commit if no tag is
296
+ # found. If there have been updates since the tag was made, a
297
+ # suffix is added with the number of commits since the tag, and
298
+ # the abbreviated object name of the most recent commit.
299
+ # Returns nil if the committish value is not found.
300
+ def recent_tag_name(committish = nil, options = {})
301
+ value = git.describe({:always => true}.update(options), committish.to_s).to_s.strip
302
+ value.size.zero? ? nil : value
303
+ end
304
+
305
+ # An array of Remote objects representing the remote branches in
306
+ # this repo
307
+ #
308
+ # Returns Grit::Remote[] (baked)
309
+ def remotes
310
+ Remote.find_all(self)
311
+ end
312
+
313
+ def remote_list
314
+ self.git.list_remotes
315
+ end
316
+
317
+ def remote_add(name, url)
318
+ self.git.remote({}, 'add', name, url)
319
+ end
320
+
321
+ def remote_fetch(name)
322
+ self.git.fetch({}, name)
323
+ end
324
+
325
+ # takes an array of remote names and last pushed dates
326
+ # fetches from all of the remotes where the local fetch
327
+ # date is earlier than the passed date, then records the
328
+ # last fetched date
329
+ #
330
+ # { 'origin' => date,
331
+ # 'peter => date,
332
+ # }
333
+ def remotes_fetch_needed(remotes)
334
+ remotes.each do |remote, date|
335
+ # TODO: check against date
336
+ self.remote_fetch(remote)
337
+ end
338
+ end
339
+
340
+
341
+ # An array of Ref objects representing the refs in
342
+ # this repo
343
+ #
344
+ # Returns Grit::Ref[] (baked)
345
+ def refs
346
+ [ Head.find_all(self), Tag.find_all(self), Remote.find_all(self) ].flatten
347
+ end
348
+
349
+ # returns an array of hashes representing all references
350
+ def refs_list
351
+ refs = self.git.for_each_ref
352
+ refarr = refs.split("\n").map do |line|
353
+ shatype, ref = line.split("\t")
354
+ sha, type = shatype.split(' ')
355
+ [ref, sha, type]
356
+ end
357
+ refarr
358
+ end
359
+
360
+ def delete_ref(ref)
361
+ self.git.native(:update_ref, {:d => true}, ref)
362
+ end
363
+
364
+ def commit_stats(start = 'master', max_count = 10, skip = 0)
365
+ options = {:max_count => max_count,
366
+ :skip => skip}
367
+
368
+ CommitStats.find_all(self, start, options)
369
+ end
370
+
371
+ # An array of Commit objects representing the history of a given ref/commit
372
+ # +start+ is the branch/commit name (default 'master')
373
+ # +max_count+ is the maximum number of commits to return (default 10, use +false+ for all)
374
+ # +skip+ is the number of commits to skip (default 0)
375
+ #
376
+ # Returns Grit::Commit[] (baked)
377
+ def commits(start = 'master', max_count = 10, skip = 0)
378
+ options = {:max_count => max_count,
379
+ :skip => skip}
380
+
381
+ Commit.find_all(self, start, options)
382
+ end
383
+
384
+ # The Commits objects that are reachable via +to+ but not via +from+
385
+ # Commits are returned in chronological order.
386
+ # +from+ is the branch/commit name of the younger item
387
+ # +to+ is the branch/commit name of the older item
388
+ #
389
+ # Returns Grit::Commit[] (baked)
390
+ def commits_between(from, to)
391
+ Commit.find_all(self, "#{from}..#{to}").reverse
392
+ end
393
+
394
+ def fast_forwardable?(to, from)
395
+ mb = self.git.native(:merge_base, {}, [to, from]).strip
396
+ mb == from
397
+ end
398
+
399
+ # The Commits objects that are newer than the specified date.
400
+ # Commits are returned in chronological order.
401
+ # +start+ is the branch/commit name (default 'master')
402
+ # +since+ is a string representing a date/time
403
+ # +extra_options+ is a hash of extra options
404
+ #
405
+ # Returns Grit::Commit[] (baked)
406
+ def commits_since(start = 'master', since = '1970-01-01', extra_options = {})
407
+ options = {:since => since}.merge(extra_options)
408
+
409
+ Commit.find_all(self, start, options)
410
+ end
411
+
412
+ # The number of commits reachable by the given branch/commit
413
+ # +start+ is the branch/commit name (default 'master')
414
+ #
415
+ # Returns Integer
416
+ def commit_count(start = 'master')
417
+ Commit.count(self, start)
418
+ end
419
+
420
+ # The Commit object for the specified id
421
+ # +id+ is the SHA1 identifier of the commit
422
+ #
423
+ # Returns Grit::Commit (baked)
424
+ def commit(id)
425
+ options = {:max_count => 1}
426
+
427
+ Commit.find_all(self, id, options).first
428
+ end
429
+
430
+ # Returns a list of commits that is in +other_repo+ but not in self
431
+ #
432
+ # Returns Grit::Commit[]
433
+ def commit_deltas_from(other_repo, ref = "master", other_ref = "master")
434
+ # TODO: we should be able to figure out the branch point, rather than
435
+ # rev-list'ing the whole thing
436
+ repo_refs = self.git.rev_list({}, ref).strip.split("\n")
437
+ other_repo_refs = other_repo.git.rev_list({}, other_ref).strip.split("\n")
438
+
439
+ (other_repo_refs - repo_refs).map do |refn|
440
+ Commit.find_all(other_repo, refn, {:max_count => 1}).first
441
+ end
442
+ end
443
+
444
+ def objects(refs)
445
+ refs = refs.split(/\s+/) if refs.respond_to?(:to_str)
446
+ self.git.rev_list({:objects => true, :timeout => false}, *refs).
447
+ split("\n").map { |a| a[0, 40] }
448
+ end
449
+
450
+ def commit_objects(refs)
451
+ refs = refs.split(/\s+/) if refs.respond_to?(:to_str)
452
+ self.git.rev_list({:timeout => false}, *refs).split("\n").map { |a| a[0, 40] }
453
+ end
454
+
455
+ def objects_between(ref1, ref2 = nil)
456
+ if ref2
457
+ refs = "#{ref2}..#{ref1}"
458
+ else
459
+ refs = ref1
460
+ end
461
+ self.objects(refs)
462
+ end
463
+
464
+ def diff_objects(commit_sha, parents = true)
465
+ revs = []
466
+ Grit.no_quote = true
467
+ if parents
468
+ # PARENTS:
469
+ revs = self.git.diff_tree({:timeout => false, :r => true, :t => true, :m => true}, commit_sha).
470
+ strip.split("\n").map{ |a| r = a.split(' '); r[3] if r[1] != '160000' }
471
+ else
472
+ # NO PARENTS:
473
+ revs = self.git.native(:ls_tree, {:timeout => false, :r => true, :t => true}, commit_sha).
474
+ split("\n").map{ |a| a.split("\t").first.split(' ')[2] }
475
+ end
476
+ revs << self.commit(commit_sha).tree.id
477
+ Grit.no_quote = false
478
+ return revs.uniq.compact
479
+ end
480
+
481
+ # The Tree object for the given treeish reference
482
+ # +treeish+ is the reference (default 'master')
483
+ # +paths+ is an optional Array of directory paths to restrict the tree (default [])
484
+ #
485
+ # Examples
486
+ # repo.tree('master', ['lib/'])
487
+ #
488
+ # Returns Grit::Tree (baked)
489
+ def tree(treeish = 'master', paths = [])
490
+ Tree.construct(self, treeish, paths)
491
+ end
492
+
493
+ # quick way to get a simple array of hashes of the entries
494
+ # of a single tree or recursive tree listing from a given
495
+ # sha or reference
496
+ # +treeish+ is the reference (default 'master')
497
+ # +options+ is a hash or options - currently only takes :recursive
498
+ #
499
+ # Examples
500
+ # repo.lstree('master', :recursive => true)
501
+ #
502
+ # Returns array of hashes - one per tree entry
503
+ def lstree(treeish = 'master', options = {})
504
+ # check recursive option
505
+ opts = {:timeout => false, :l => true, :t => true}
506
+ if options[:recursive]
507
+ opts[:r] = true
508
+ end
509
+ # mode, type, sha, size, path
510
+ revs = self.git.native(:ls_tree, opts, treeish)
511
+ lines = revs.split("\n")
512
+ revs = lines.map do |a|
513
+ stuff, path = a.split("\t")
514
+ mode, type, sha, size = stuff.split(" ")
515
+ entry = {:mode => mode, :type => type, :sha => sha, :path => path}
516
+ entry[:size] = size.strip.to_i if size.strip != '-'
517
+ entry
518
+ end
519
+ revs
520
+ end
521
+
522
+ def object(sha)
523
+ obj = git.get_git_object(sha)
524
+ raw = Grit::GitRuby::Internal::RawObject.new(obj[:type], obj[:content])
525
+ object = Grit::GitRuby::GitObject.from_raw(raw)
526
+ object.sha = sha
527
+ object
528
+ end
529
+
530
+ # The Blob object for the given id
531
+ # +id+ is the SHA1 id of the blob
532
+ #
533
+ # Returns Grit::Blob (unbaked)
534
+ def blob(id)
535
+ Blob.create(self, :id => id)
536
+ end
537
+
538
+ # The commit log for a treeish
539
+ #
540
+ # Returns Grit::Commit[]
541
+ def log(commit = 'master', path = nil, options = {})
542
+ default_options = {:pretty => "raw"}
543
+ actual_options = default_options.merge(options)
544
+ arg = path ? [commit, '--', path] : [commit]
545
+ commits = self.git.log(actual_options, *arg)
546
+ Commit.list_from_string(self, commits)
547
+ end
548
+
549
+ # The diff from commit +a+ to commit +b+, optionally restricted to the given file(s)
550
+ # +a+ is the base commit
551
+ # +b+ is the other commit
552
+ # +paths+ is an optional list of file paths on which to restrict the diff
553
+ def diff(a, b, *paths)
554
+ diff = self.git.native('diff', {}, a, b, '--', *paths)
555
+
556
+ if diff =~ /diff --git a/
557
+ diff = diff.sub(/.*?(diff --git a)/m, '\1')
558
+ else
559
+ diff = ''
560
+ end
561
+ Diff.list_from_string(self, diff)
562
+ end
563
+
564
+ # The commit diff for the given commit
565
+ # +commit+ is the commit name/id
566
+ #
567
+ # Returns Grit::Diff[]
568
+ def commit_diff(commit)
569
+ Commit.diff(self, commit)
570
+ end
571
+
572
+ # Archive the given treeish
573
+ # +treeish+ is the treeish name/id (default 'master')
574
+ # +prefix+ is the optional prefix
575
+ #
576
+ # Examples
577
+ # repo.archive_tar
578
+ # # => <String containing tar archive>
579
+ #
580
+ # repo.archive_tar('a87ff14')
581
+ # # => <String containing tar archive for commit a87ff14>
582
+ #
583
+ # repo.archive_tar('master', 'myproject/')
584
+ # # => <String containing tar archive and prefixed with 'myproject/'>
585
+ #
586
+ # Returns String (containing tar archive)
587
+ def archive_tar(treeish = 'master', prefix = nil)
588
+ options = {}
589
+ options[:prefix] = prefix if prefix
590
+ self.git.archive(options, treeish)
591
+ end
592
+
593
+ # Archive and gzip the given treeish
594
+ # +treeish+ is the treeish name/id (default 'master')
595
+ # +prefix+ is the optional prefix
596
+ #
597
+ # Examples
598
+ # repo.archive_tar_gz
599
+ # # => <String containing tar.gz archive>
600
+ #
601
+ # repo.archive_tar_gz('a87ff14')
602
+ # # => <String containing tar.gz archive for commit a87ff14>
603
+ #
604
+ # repo.archive_tar_gz('master', 'myproject/')
605
+ # # => <String containing tar.gz archive and prefixed with 'myproject/'>
606
+ #
607
+ # Returns String (containing tar.gz archive)
608
+ def archive_tar_gz(treeish = 'master', prefix = nil)
609
+ options = {}
610
+ options[:prefix] = prefix if prefix
611
+ self.git.archive(options, treeish, "| gzip -n")
612
+ end
613
+
614
+ # Write an archive directly to a file
615
+ # +treeish+ is the treeish name/id (default 'master')
616
+ # +prefix+ is the optional prefix (default nil)
617
+ # +filename+ is the name of the file (default 'archive.tar.gz')
618
+ # +format+ is the optional format (default nil)
619
+ # +pipe+ is the command to run the output through (default 'gzip')
620
+ #
621
+ # Returns nothing
622
+ def archive_to_file(treeish = 'master', prefix = nil, filename = 'archive.tar.gz', format = nil, pipe = "gzip")
623
+ options = {}
624
+ options[:prefix] = prefix if prefix
625
+ options[:format] = format if format
626
+ self.git.archive(options, treeish, "| #{pipe} > #{filename}")
627
+ end
628
+
629
+ # Enable git-daemon serving of this repository by writing the
630
+ # git-daemon-export-ok file to its git directory
631
+ #
632
+ # Returns nothing
633
+ def enable_daemon_serve
634
+ self.git.fs_write(DAEMON_EXPORT_FILE, '')
635
+ end
636
+
637
+ # Disable git-daemon serving of this repository by ensuring there is no
638
+ # git-daemon-export-ok file in its git directory
639
+ #
640
+ # Returns nothing
641
+ def disable_daemon_serve
642
+ self.git.fs_delete(DAEMON_EXPORT_FILE)
643
+ end
644
+
645
+ def gc_auto
646
+ self.git.gc({:auto => true})
647
+ end
648
+
649
+ # The list of alternates for this repo
650
+ #
651
+ # Returns Array[String] (pathnames of alternates)
652
+ def alternates
653
+ alternates_path = "objects/info/alternates"
654
+ self.git.fs_read(alternates_path).strip.split("\n")
655
+ rescue Errno::ENOENT
656
+ []
657
+ end
658
+
659
+ # Sets the alternates
660
+ # +alts+ is the Array of String paths representing the alternates
661
+ #
662
+ # Returns nothing
663
+ def alternates=(alts)
664
+ alts.each do |alt|
665
+ unless File.exist?(alt)
666
+ raise "Could not set alternates. Alternate path #{alt} must exist"
667
+ end
668
+ end
669
+
670
+ if alts.empty?
671
+ self.git.fs_write('objects/info/alternates', '')
672
+ else
673
+ self.git.fs_write('objects/info/alternates', alts.join("\n"))
674
+ end
675
+ end
676
+
677
+ def config
678
+ @config ||= Config.new(self)
679
+ end
680
+
681
+ def index
682
+ Index.new(self)
683
+ end
684
+
685
+ def update_ref(head, commit_sha)
686
+ return nil if !commit_sha || (commit_sha.size != 40)
687
+ self.git.fs_write("refs/heads/#{head}", commit_sha)
688
+ commit_sha
689
+ end
690
+
691
+ # Rename the current repository directory.
692
+ # +name+ is the new name
693
+ #
694
+ # Returns nothing
695
+ def rename(name)
696
+ if @bare
697
+ self.git.fs_move('/', "../#{name}")
698
+ else
699
+ self.git.fs_move('/', "../../#{name}")
700
+ end
701
+ end
702
+
703
+ # Pretty object inspection
704
+ def inspect
705
+ %Q{#<Grit::Repo "#{@path}">}
706
+ end
707
+ end # Repo
708
+
709
+ end # Grit