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.
Files changed (76) hide show
  1. data/History +44 -0
  2. data/License.txt +22 -0
  3. data/README +45 -0
  4. data/bin/gitgo +4 -0
  5. data/lib/gitgo.rb +1 -0
  6. data/lib/gitgo/app.rb +63 -0
  7. data/lib/gitgo/controller.rb +89 -0
  8. data/lib/gitgo/controllers/code.rb +198 -0
  9. data/lib/gitgo/controllers/issue.rb +76 -0
  10. data/lib/gitgo/controllers/repo.rb +186 -0
  11. data/lib/gitgo/controllers/wiki.rb +19 -0
  12. data/lib/gitgo/document.rb +680 -0
  13. data/lib/gitgo/document/invalid_document_error.rb +34 -0
  14. data/lib/gitgo/documents/comment.rb +20 -0
  15. data/lib/gitgo/documents/issue.rb +56 -0
  16. data/lib/gitgo/git.rb +941 -0
  17. data/lib/gitgo/git/tree.rb +315 -0
  18. data/lib/gitgo/git/utils.rb +59 -0
  19. data/lib/gitgo/helper.rb +3 -0
  20. data/lib/gitgo/helper/doc.rb +28 -0
  21. data/lib/gitgo/helper/form.rb +88 -0
  22. data/lib/gitgo/helper/format.rb +200 -0
  23. data/lib/gitgo/helper/html.rb +19 -0
  24. data/lib/gitgo/helper/utils.rb +85 -0
  25. data/lib/gitgo/index.rb +421 -0
  26. data/lib/gitgo/index/idx_file.rb +119 -0
  27. data/lib/gitgo/index/sha_file.rb +135 -0
  28. data/lib/gitgo/patches/grit.rb +47 -0
  29. data/lib/gitgo/repo.rb +626 -0
  30. data/lib/gitgo/repo/graph.rb +333 -0
  31. data/lib/gitgo/repo/node.rb +122 -0
  32. data/lib/gitgo/rest.rb +87 -0
  33. data/lib/gitgo/server.rb +114 -0
  34. data/lib/gitgo/version.rb +8 -0
  35. data/public/css/gitgo.css +24 -0
  36. data/public/javascript/gitgo.js +148 -0
  37. data/public/javascript/jquery-1.4.2.min.js +154 -0
  38. data/views/app/index.erb +4 -0
  39. data/views/app/timeline.erb +27 -0
  40. data/views/app/welcome.erb +13 -0
  41. data/views/code/_comment.erb +10 -0
  42. data/views/code/_comment_form.erb +14 -0
  43. data/views/code/_comments.erb +5 -0
  44. data/views/code/_commit.erb +25 -0
  45. data/views/code/_grepnav.erb +5 -0
  46. data/views/code/_treenav.erb +3 -0
  47. data/views/code/blob.erb +6 -0
  48. data/views/code/commit_grep.erb +35 -0
  49. data/views/code/commits.erb +11 -0
  50. data/views/code/diff.erb +10 -0
  51. data/views/code/grep.erb +32 -0
  52. data/views/code/index.erb +17 -0
  53. data/views/code/obj/blob.erb +4 -0
  54. data/views/code/obj/commit.erb +25 -0
  55. data/views/code/obj/tag.erb +25 -0
  56. data/views/code/obj/tree.erb +9 -0
  57. data/views/code/tree.erb +9 -0
  58. data/views/error.erb +19 -0
  59. data/views/issue/_issue.erb +15 -0
  60. data/views/issue/_issue_form.erb +39 -0
  61. data/views/issue/edit.erb +11 -0
  62. data/views/issue/index.erb +28 -0
  63. data/views/issue/new.erb +5 -0
  64. data/views/issue/show.erb +27 -0
  65. data/views/layout.erb +34 -0
  66. data/views/not_found.erb +1 -0
  67. data/views/repo/fsck.erb +29 -0
  68. data/views/repo/help.textile +5 -0
  69. data/views/repo/help/faq.textile +19 -0
  70. data/views/repo/help/howto.textile +31 -0
  71. data/views/repo/help/trouble.textile +28 -0
  72. data/views/repo/idx.erb +29 -0
  73. data/views/repo/index.erb +72 -0
  74. data/views/repo/status.erb +16 -0
  75. data/views/wiki/index.erb +3 -0
  76. 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
@@ -0,0 +1,3 @@
1
+ require 'gitgo/helper/format'
2
+ require 'gitgo/helper/form'
3
+ require 'gitgo/helper/html'
@@ -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