gitgo 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
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