gitgo 0.3.3
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/History +44 -0
- data/License.txt +22 -0
- data/README +45 -0
- data/bin/gitgo +4 -0
- data/lib/gitgo.rb +1 -0
- data/lib/gitgo/app.rb +63 -0
- data/lib/gitgo/controller.rb +89 -0
- data/lib/gitgo/controllers/code.rb +198 -0
- data/lib/gitgo/controllers/issue.rb +76 -0
- data/lib/gitgo/controllers/repo.rb +186 -0
- data/lib/gitgo/controllers/wiki.rb +19 -0
- data/lib/gitgo/document.rb +680 -0
- data/lib/gitgo/document/invalid_document_error.rb +34 -0
- data/lib/gitgo/documents/comment.rb +20 -0
- data/lib/gitgo/documents/issue.rb +56 -0
- data/lib/gitgo/git.rb +941 -0
- data/lib/gitgo/git/tree.rb +315 -0
- data/lib/gitgo/git/utils.rb +59 -0
- data/lib/gitgo/helper.rb +3 -0
- data/lib/gitgo/helper/doc.rb +28 -0
- data/lib/gitgo/helper/form.rb +88 -0
- data/lib/gitgo/helper/format.rb +200 -0
- data/lib/gitgo/helper/html.rb +19 -0
- data/lib/gitgo/helper/utils.rb +85 -0
- data/lib/gitgo/index.rb +421 -0
- data/lib/gitgo/index/idx_file.rb +119 -0
- data/lib/gitgo/index/sha_file.rb +135 -0
- data/lib/gitgo/patches/grit.rb +47 -0
- data/lib/gitgo/repo.rb +626 -0
- data/lib/gitgo/repo/graph.rb +333 -0
- data/lib/gitgo/repo/node.rb +122 -0
- data/lib/gitgo/rest.rb +87 -0
- data/lib/gitgo/server.rb +114 -0
- data/lib/gitgo/version.rb +8 -0
- data/public/css/gitgo.css +24 -0
- data/public/javascript/gitgo.js +148 -0
- data/public/javascript/jquery-1.4.2.min.js +154 -0
- data/views/app/index.erb +4 -0
- data/views/app/timeline.erb +27 -0
- data/views/app/welcome.erb +13 -0
- data/views/code/_comment.erb +10 -0
- data/views/code/_comment_form.erb +14 -0
- data/views/code/_comments.erb +5 -0
- data/views/code/_commit.erb +25 -0
- data/views/code/_grepnav.erb +5 -0
- data/views/code/_treenav.erb +3 -0
- data/views/code/blob.erb +6 -0
- data/views/code/commit_grep.erb +35 -0
- data/views/code/commits.erb +11 -0
- data/views/code/diff.erb +10 -0
- data/views/code/grep.erb +32 -0
- data/views/code/index.erb +17 -0
- data/views/code/obj/blob.erb +4 -0
- data/views/code/obj/commit.erb +25 -0
- data/views/code/obj/tag.erb +25 -0
- data/views/code/obj/tree.erb +9 -0
- data/views/code/tree.erb +9 -0
- data/views/error.erb +19 -0
- data/views/issue/_issue.erb +15 -0
- data/views/issue/_issue_form.erb +39 -0
- data/views/issue/edit.erb +11 -0
- data/views/issue/index.erb +28 -0
- data/views/issue/new.erb +5 -0
- data/views/issue/show.erb +27 -0
- data/views/layout.erb +34 -0
- data/views/not_found.erb +1 -0
- data/views/repo/fsck.erb +29 -0
- data/views/repo/help.textile +5 -0
- data/views/repo/help/faq.textile +19 -0
- data/views/repo/help/howto.textile +31 -0
- data/views/repo/help/trouble.textile +28 -0
- data/views/repo/idx.erb +29 -0
- data/views/repo/index.erb +72 -0
- data/views/repo/status.erb +16 -0
- data/views/wiki/index.erb +3 -0
- metadata +253 -0
@@ -0,0 +1,315 @@
|
|
1
|
+
module Gitgo
|
2
|
+
class Git
|
3
|
+
|
4
|
+
# Tree represents an in-memory working tree for git. Trees are initialized
|
5
|
+
# with a Grit::Tree. In general tree contents are represented as (path,
|
6
|
+
# [:mode,sha]) pairs, but subtrees can be expanded into (path, Tree)
|
7
|
+
# pairs.
|
8
|
+
#
|
9
|
+
# See Git for an example of Tree usage in practice.
|
10
|
+
#
|
11
|
+
# === Efficiency
|
12
|
+
#
|
13
|
+
# Modes are symbolized in the internal [:mode,sha] entries because they
|
14
|
+
# are rarely needed as strings and are typically very redundant.
|
15
|
+
# Symbolizing shas makes less sense because they are frequently used as
|
16
|
+
# strings. However it does make sense to use the same string instance to
|
17
|
+
# represent a sha in multiple places. As a result trees have an internal
|
18
|
+
# string_table that functions like a symbol table, ie it maps the same
|
19
|
+
# string content to a single shared instance. The string table is managed
|
20
|
+
# at the class level through the string_table method.
|
21
|
+
#
|
22
|
+
# Trees only expand as needed. This saves memory and cycles because it is
|
23
|
+
# expensive to read, parse, and maintain the git tree data. In general
|
24
|
+
# trees will stay fairly compact unless certain expensive operations are
|
25
|
+
# performed. These are:
|
26
|
+
#
|
27
|
+
# * each_pair (with expand == true)
|
28
|
+
# * each_tree (with expand == true)
|
29
|
+
# * flatten
|
30
|
+
# * to_hash (with expand == true)
|
31
|
+
#
|
32
|
+
# Avoid these methods if possible, or ensure they are rarely executed.
|
33
|
+
class Tree
|
34
|
+
class << self
|
35
|
+
|
36
|
+
# Returns the string table for shas. Specify clear to reset the
|
37
|
+
# string table.
|
38
|
+
def string_table(clear=false)
|
39
|
+
@string_table.clear if clear
|
40
|
+
@string_table ||= Hash.new {|hash, key| hash[key] = key.freeze }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
include Enumerable
|
44
|
+
|
45
|
+
# The tree mode.
|
46
|
+
attr_reader :mode
|
47
|
+
|
48
|
+
# Initializes a new Tree. The input tree should be a Grit::Tree or nil.
|
49
|
+
def initialize(tree=nil)
|
50
|
+
@index = nil
|
51
|
+
@tree = tree
|
52
|
+
|
53
|
+
if tree
|
54
|
+
self.mode = tree.mode
|
55
|
+
self.sha = tree.id
|
56
|
+
else
|
57
|
+
@mode = nil
|
58
|
+
@sha = nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Sets mode, symbolizing if necessary. Mode may be set to nil in which
|
63
|
+
# case the git.default_tree_mode is adopted when the tree is written.
|
64
|
+
def mode=(mode)
|
65
|
+
@mode = mode ? mode.to_sym : nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# Sets the sha for self. Sha may be set to nil, in which case it will
|
69
|
+
# be calculated when a repo is committed.
|
70
|
+
def sha=(sha)
|
71
|
+
@sha = sha ? string(sha) : nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns the sha representing the contents for self. If check is true,
|
75
|
+
# sha will check that neither self nor any subtree is modified before
|
76
|
+
# returning the sha. If modified, the sha is set to nil to flag a repo
|
77
|
+
# to recalculate the sha on commit.
|
78
|
+
#
|
79
|
+
# Note that check does not validate the sha correctly represents the
|
80
|
+
# contents of self.
|
81
|
+
def sha(check=true)
|
82
|
+
if @sha && check
|
83
|
+
index.each_value do |value|
|
84
|
+
if value.kind_of?(Tree) && value.sha.nil?
|
85
|
+
@sha = nil
|
86
|
+
break
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
@sha
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the keys (ie paths) for all entries in self. Keys are
|
95
|
+
# returned as strings
|
96
|
+
def keys
|
97
|
+
index.keys.collect {|keys| keys.to_s }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the entry for the specified path, either a [:mode,sha] pair
|
101
|
+
# for a blob or a Tree for a subtree.
|
102
|
+
def [](path)
|
103
|
+
case
|
104
|
+
when entry = index[path]
|
105
|
+
entry
|
106
|
+
when tree = index.delete(path.to_sym)
|
107
|
+
index[string(path)] = Tree.new(tree)
|
108
|
+
else
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Sets the entry for the specified path. The entry should be a
|
114
|
+
# [:mode,sha] array, or a Tree. A nil entry indicates removal.
|
115
|
+
def []=(path, entry)
|
116
|
+
# ensure an unexpanded tree is removed
|
117
|
+
index.delete(path.to_sym)
|
118
|
+
|
119
|
+
path = string(path)
|
120
|
+
case entry
|
121
|
+
when Array
|
122
|
+
mode, sha = entry
|
123
|
+
index[path] = [mode.to_sym, string(sha)]
|
124
|
+
when Tree
|
125
|
+
index[path] = entry
|
126
|
+
when nil
|
127
|
+
index.delete(path)
|
128
|
+
else
|
129
|
+
raise "invalid entry: #{entry.inspect}"
|
130
|
+
end
|
131
|
+
|
132
|
+
# add/remove content modifies self so
|
133
|
+
# the sha can and should be invalidated
|
134
|
+
@sha = nil
|
135
|
+
end
|
136
|
+
|
137
|
+
def merge!(another)
|
138
|
+
another.each_pair do |path, entry|
|
139
|
+
self[path] = entry
|
140
|
+
end
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
# Yields each (path, entry) pair to the block, as an array, ordered by
|
145
|
+
# path. Implemented to get access to the enumerable methods.
|
146
|
+
def each
|
147
|
+
each_pair do |key, value|
|
148
|
+
yield [key, value]
|
149
|
+
end
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
# Yields each (path, entry) pair to the block, ordered by path. Entries
|
154
|
+
# can be [:mode,sha] arrays or Trees. If expand is true then subtrees
|
155
|
+
# will be expanded, but strongly consider whether or not expansion is
|
156
|
+
# necessary because it is computationally expensive.
|
157
|
+
def each_pair(expand=false)
|
158
|
+
|
159
|
+
# sorting the keys is important when writing the tree;
|
160
|
+
# unsorted keys cause warnings in git fsck
|
161
|
+
keys = index.keys.sort_by {|key| key.to_s }
|
162
|
+
store = expand ? self : index
|
163
|
+
|
164
|
+
keys.each {|key| yield(key, store[key]) }
|
165
|
+
end
|
166
|
+
|
167
|
+
# Yields the (path, [:mode, sha]) pairs for each blob to the block.
|
168
|
+
def each_blob
|
169
|
+
each_pair do |key, value|
|
170
|
+
next unless value.kind_of?(Array)
|
171
|
+
yield(key, value)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Yields the (path, entry) pairs for each tree to the block. Subtrees
|
176
|
+
# are expanded if specified, in which case all entries will be Trees.
|
177
|
+
# Without expansion, entries may be [:mode,sha] arrays or Trees.
|
178
|
+
def each_tree(expand=false)
|
179
|
+
each_pair(expand) do |key, value|
|
180
|
+
next unless value.kind_of?(Tree)
|
181
|
+
yield(key, value)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns the subtree indicated by the specified segments (an array of
|
186
|
+
# paths), or nil if no such subtree exists. If force is true then
|
187
|
+
# missing subtrees will be created.
|
188
|
+
def subtree(segments, force=false)
|
189
|
+
return self if segments.empty?
|
190
|
+
|
191
|
+
key = segments.shift
|
192
|
+
tree = self[key]
|
193
|
+
|
194
|
+
if !tree.kind_of?(Tree)
|
195
|
+
return nil unless force
|
196
|
+
self[key] = tree = Tree.new(nil)
|
197
|
+
end
|
198
|
+
|
199
|
+
tree.subtree(segments, force)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Flattens all paths under self into a single array.
|
203
|
+
def flatten(prefix=nil, target={})
|
204
|
+
keys.each do |key|
|
205
|
+
next unless entry = self[key]
|
206
|
+
|
207
|
+
key = key.to_s
|
208
|
+
key = File.join(prefix, key) if prefix
|
209
|
+
|
210
|
+
if entry.kind_of?(Tree)
|
211
|
+
entry.flatten(key, target)
|
212
|
+
else
|
213
|
+
target[key] = entry
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
target
|
218
|
+
end
|
219
|
+
|
220
|
+
# Returns self as a hash, expanding if specified.
|
221
|
+
def to_hash(expand=false)
|
222
|
+
hash = {}
|
223
|
+
each_pair(expand) do |key, value|
|
224
|
+
hash[key] = case value
|
225
|
+
when Tree then value.to_hash
|
226
|
+
when Array then value
|
227
|
+
else to_entry(value)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
hash
|
231
|
+
end
|
232
|
+
|
233
|
+
# Returns true if the to_hash results of self and another are equal.
|
234
|
+
def eql?(another)
|
235
|
+
self.to_hash == another.to_hash
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns true if the to_hash results of self and another are equal.
|
239
|
+
def ==(another)
|
240
|
+
self.to_hash == another.to_hash
|
241
|
+
end
|
242
|
+
|
243
|
+
# Writes self to the git instance. All subtrees will likewise be
|
244
|
+
# written. Returns a [mode, sha] entry.
|
245
|
+
#
|
246
|
+
# Tree format:
|
247
|
+
#
|
248
|
+
# mode name\0[packedsha]mode name\0[packedsha]...
|
249
|
+
#
|
250
|
+
# Note there are no newlines separating tree entries.
|
251
|
+
def write_to(git)
|
252
|
+
self.mode ||= git.default_tree_mode
|
253
|
+
self.sha ||= begin
|
254
|
+
lines = []
|
255
|
+
each_pair(false) do |key, entry|
|
256
|
+
mode, sha = case entry
|
257
|
+
when Tree then entry.write_to(git)
|
258
|
+
when Array then entry
|
259
|
+
else [entry.mode, entry.id]
|
260
|
+
end
|
261
|
+
|
262
|
+
# modes should not begin with zeros (although it is not fatal
|
263
|
+
# if they do), otherwise fsck will print warnings like this:
|
264
|
+
#
|
265
|
+
# warning in tree 980127...: contains zero-padded file modes
|
266
|
+
lines << zero_strip("#{mode} #{key}\0#{[sha].pack("H*")}")
|
267
|
+
end
|
268
|
+
|
269
|
+
git.set(:tree, lines.join)
|
270
|
+
end
|
271
|
+
|
272
|
+
[mode, sha]
|
273
|
+
end
|
274
|
+
|
275
|
+
protected
|
276
|
+
|
277
|
+
# returns or initializes the internal working tree (index)
|
278
|
+
def index # :nodoc:
|
279
|
+
@index ||= begin
|
280
|
+
index = {}
|
281
|
+
|
282
|
+
@tree.contents.each do |obj|
|
283
|
+
key = obj.name
|
284
|
+
if obj.respond_to?(:contents)
|
285
|
+
index[key.to_sym] = obj
|
286
|
+
else
|
287
|
+
index[string(key)] = to_entry(obj)
|
288
|
+
end
|
289
|
+
end if @tree
|
290
|
+
@tree = nil
|
291
|
+
|
292
|
+
index
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# helper to lookup the string table entry for key
|
297
|
+
def string(key) # :nodoc:
|
298
|
+
Tree.string_table[key.to_s]
|
299
|
+
end
|
300
|
+
|
301
|
+
# converts obj into a [:mode, sha] entry
|
302
|
+
def to_entry(obj) # :nodoc:
|
303
|
+
[obj.mode.to_sym, string(obj.id)]
|
304
|
+
end
|
305
|
+
|
306
|
+
def zero_strip(str) # :nodoc:
|
307
|
+
return str unless str[0] == ?0
|
308
|
+
|
309
|
+
index = 1
|
310
|
+
index += 1 while str[index] == ?0
|
311
|
+
str[index, str.length - index]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Gitgo
|
2
|
+
class Git
|
3
|
+
|
4
|
+
# A set of utility functions split out for ease of testing.
|
5
|
+
module Utils
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# Executes the block having set the env variables. All ENV variables
|
9
|
+
# that start with GIT_ will be removed regardless of whether they are
|
10
|
+
# specified in env or not.
|
11
|
+
def with_env(env={})
|
12
|
+
overrides = {}
|
13
|
+
begin
|
14
|
+
ENV.keys.each do |key|
|
15
|
+
if key =~ /^GIT_/
|
16
|
+
overrides[key] = ENV.delete(key)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
env.each_pair do |key, value|
|
21
|
+
overrides[key] ||= nil
|
22
|
+
ENV[key] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
yield
|
26
|
+
ensure
|
27
|
+
overrides.each_pair do |key, value|
|
28
|
+
if value
|
29
|
+
ENV[key] = value
|
30
|
+
else
|
31
|
+
ENV.delete(key)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Splits a path along slashes into an array, stripping empty strings
|
38
|
+
# from each end. An array may be provided in place of path; it will be
|
39
|
+
# duplicated before being stripped of nil/empty entries.
|
40
|
+
def split(path)
|
41
|
+
array = path.kind_of?(String) ? path.split("/") : path.dup
|
42
|
+
array.shift if nil_or_empty_string?(array[0])
|
43
|
+
array.pop if nil_or_empty_string?(array[-1])
|
44
|
+
array
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns true if the object is nil, or empty, and assumes the object is
|
48
|
+
# a string (ie that it responds to empty?).
|
49
|
+
def nil_or_empty_string?(obj)
|
50
|
+
obj.nil? || obj.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns true if the object is nil, or responds to empty and is empty.
|
54
|
+
def nil_or_empty?(obj)
|
55
|
+
obj.nil? || (obj.respond_to?(:empty?) && obj.empty?)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/gitgo/helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Gitgo
|
2
|
+
module Helper
|
3
|
+
class Doc
|
4
|
+
|
5
|
+
attr_reader :controller
|
6
|
+
|
7
|
+
def initialize(controller)
|
8
|
+
@controller = controller
|
9
|
+
end
|
10
|
+
|
11
|
+
def url(*paths)
|
12
|
+
controller.url(*paths)
|
13
|
+
end
|
14
|
+
|
15
|
+
def at
|
16
|
+
controller.user_ref
|
17
|
+
end
|
18
|
+
|
19
|
+
def active_shas
|
20
|
+
@active_shas ||= repo.rev_list(at)
|
21
|
+
end
|
22
|
+
|
23
|
+
def active?(sha)
|
24
|
+
at && active_shas.include?(sha)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
module Gitgo
|
4
|
+
module Helper
|
5
|
+
class Form
|
6
|
+
include Rack::Utils
|
7
|
+
|
8
|
+
DEFAULT_STATES = %w{open closed}
|
9
|
+
|
10
|
+
attr_reader :controller
|
11
|
+
|
12
|
+
def initialize(controller)
|
13
|
+
@controller = controller
|
14
|
+
end
|
15
|
+
|
16
|
+
def url(*paths)
|
17
|
+
controller.url(paths)
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
#
|
22
|
+
#
|
23
|
+
|
24
|
+
def value(str)
|
25
|
+
str
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# documents
|
30
|
+
#
|
31
|
+
|
32
|
+
def at(sha)
|
33
|
+
return '(unknown)' unless sha
|
34
|
+
|
35
|
+
refs = refs.select {|ref| ref.commit.sha == sha }
|
36
|
+
refs.collect! {|ref| escape_html ref.name }
|
37
|
+
|
38
|
+
ref_names = refs.empty? ? nil : " (#{refs.join(', ')})"
|
39
|
+
"#{sha_a(sha)}#{ref_names}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def author_value(author)
|
43
|
+
escape_html(author)
|
44
|
+
end
|
45
|
+
|
46
|
+
def title_value(title)
|
47
|
+
escape_html(title)
|
48
|
+
end
|
49
|
+
|
50
|
+
def tags_value(tags)
|
51
|
+
tags ? tags.join(', ') : ''
|
52
|
+
end
|
53
|
+
|
54
|
+
def content_value(content)
|
55
|
+
content
|
56
|
+
end
|
57
|
+
|
58
|
+
def each_tag(tags, *selected) # :yields: value, select_or_check, content
|
59
|
+
tags.sort.each do |tag|
|
60
|
+
yield escape_html(tag), selected.include?(tag), escape_html(tag)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def each_ref(refs, selected_name) # :yields: value, select_or_check, content
|
65
|
+
refs.each do |ref|
|
66
|
+
yield escape_html(ref.commit), selected_name == ref.name, escape_html(ref.name)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def each_ref_name(refs, selected_name) # :yields: value, select_or_check, content
|
71
|
+
found_selected_name = false
|
72
|
+
|
73
|
+
refs.each do |ref|
|
74
|
+
select_or_check = selected_name == ref.name
|
75
|
+
found_selected_name = true if select_or_check
|
76
|
+
|
77
|
+
yield escape_html(ref.name), select_or_check, escape_html(ref.name)
|
78
|
+
end
|
79
|
+
|
80
|
+
if found_selected_name
|
81
|
+
yield("", false, "(none)")
|
82
|
+
else
|
83
|
+
yield(selected_name, true, selected_name.to_s.empty? ? "(none)" : selected_name)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|