From aae9eb188332f3c34a2dff90892c21c415bf74e2 Mon Sep 17 00:00:00 2001 From: rick Date: Wed, 19 Jan 2011 05:11:11 -0800 Subject: [PATCH 01/13] refactor wiki commit logic into Gollum::Committer class --- lib/gollum.rb | 1 + lib/gollum/committer.rb | 216 +++++++++++++++++++++++++ lib/gollum/wiki.rb | 347 +++++++++++++--------------------------- test/test_committer.rb | 27 ++++ test/test_wiki.rb | 17 -- 5 files changed, 357 insertions(+), 251 deletions(-) create mode 100644 lib/gollum/committer.rb create mode 100644 test/test_committer.rb diff --git a/lib/gollum.rb b/lib/gollum.rb index 6970d0be..2320b281 100644 --- a/lib/gollum.rb +++ b/lib/gollum.rb @@ -12,6 +12,7 @@ require 'gollum/ruby1.8' # internal require 'gollum/git_access' +require 'gollum/committer' require 'gollum/pagination' require 'gollum/blob_entry' require 'gollum/wiki' diff --git a/lib/gollum/committer.rb b/lib/gollum/committer.rb new file mode 100644 index 00000000..9038f2ff --- /dev/null +++ b/lib/gollum/committer.rb @@ -0,0 +1,216 @@ +module Gollum + # Responsible for handling the commit process for a Wiki. It sets up the + # Git index, provides methods for modifying the tree, and stores callbacks + # to be fired after the commit has been made. This is specifically + # designed to handle multiple updated pages in a single commit. + class Committer + # Gets the instance of the Gollum::Wiki that is being updated. + attr_reader :wiki + + # Gets a Hash of commit options. + attr_reader :options + + # Initializes the Committer. + # + # wiki - The Gollum::Wiki instance that is being updated. + # options - The commit Hash details: + # :message - The String commit message. + # :name - The String author full name. + # :email - The String email address. + # :parent - Optional Grit::Commit parent to this update. + # :tree - Optional String SHA of the tree to create the + # index from. + # :committer - Optional Gollum::Committer instance. If provided, + # assume that this operation is part of batch of + # updates and the commit happens later. + # + # Returns the Committer instance. + def initialize(wiki, options = {}) + @wiki = wiki + @options = options + @callbacks = [] + end + + # Public: References the Git index for this commit. + # + # Returns a Grit::Index. + def index + @index ||= begin + idx = @wiki.repo.index + if tree = options[:tree] + idx.read_tree(tree) + elsif parent = parents.first + idx.read_tree(parent.tree.id) + end + idx + end + end + + # Public: The committer for this commit. + # + # Returns a Grit::Actor. + def actor + @actor ||= begin + @options[:name] = @wiki.default_committer_name if @options[:name].to_s.empty? + @options[:email] = @wiki.default_committer_email if @options[:email].to_s.empty? + Grit::Actor.new(@options[:name], @options[:email]) + end + end + + # Public: The parent commits to this pending commit. + # + # Returns an array of Grit::Commit instances. + def parents + @parents ||= begin + arr = [@options[:parent] || @wiki.repo.commit('master')] + arr.flatten! + arr.compact! + arr + end + end + + # Adds a page to the given Index. + # + # dir - The String subdirectory of the Gollum::Page without any + # prefix or suffix slashes (e.g. "foo/bar"). + # name - The String Gollum::Page name. + # format - The Symbol Gollum::Page format. + # data - The String wiki data to store in the tree map. + # allow_same_ext - A Boolean determining if the tree map allows the same + # filename with the same extension. + # + # Raises Gollum::DuplicatePageError if a matching filename already exists. + # This way, pages are not inadvertently overwritten. + # + # Returns nothing (modifies the Index in place). + def add_to_index(dir, name, format, data, allow_same_ext = false) + path = @wiki.page_file_name(name, format) + + dir = '/' if dir.strip.empty? + + fullpath = ::File.join(*[@wiki.page_file_dir, dir, path].compact) + fullpath = fullpath[1..-1] if fullpath =~ /^\// + + if index.current_tree && tree = index.current_tree / dir + downpath = path.downcase.sub(/\.\w+$/, '') + + tree.blobs.each do |blob| + next if page_path_scheduled_for_deletion?(index.tree, fullpath) + file = blob.name.downcase.sub(/\.\w+$/, '') + file_ext = ::File.extname(blob.name).sub(/^\./, '') + if downpath == file && !(allow_same_ext && file_ext == ext) + raise DuplicatePageError.new(dir, blob.name, path) + end + end + end + + index.add(fullpath, @wiki.normalize(data)) + end + + # Update the given file in the repository's working directory if there + # is a working directory present. + # + # dir - The String directory in which the file lives. + # name - The String name of the page (may be in human format). + # format - The Symbol format of the page. + # + # Returns nothing. + def update_working_dir(dir, name, format) + unless @wiki.repo.bare + if @wiki.page_file_dir + dir = dir.size.zero? ? @wiki.page_file_dir : File.join(dir, @wiki.page_file_dir) + end + + path = + if dir == '' + @wiki.page_file_name(name, format) + else + ::File.join(dir, @wiki.page_file_name(name, format)) + end + + Dir.chdir(::File.join(@wiki.repo.path, '..')) do + if file_path_scheduled_for_deletion?(index.tree, path) + @wiki.repo.git.rm({'f' => true}, '--', path) + else + @wiki.repo.git.checkout({}, 'HEAD', '--', path) + end + end + end + end + + # Writes the commit to Git and runs the after_commit callbacks. + # + # Returns the String SHA1 of the new commit. + def commit + sha1 = index.commit(@options[:message], parents, actor) + @callbacks.each do |cb| + cb.call(self) + end + sha1 + end + + # Adds a callback to be fired after a commit. + # + # block - A block that expects this Committer instance as the argument. + # + # Returns nothing. + def after_commit(&block) + @callbacks << block + end + + # Determine if a given page (regardless of format) is scheduled to be + # deleted in the next commit for the given Index. + # + # map - The Hash map: + # key - The String directory or filename. + # val - The Hash submap or the String contents of the file. + # path - The String path of the page file. This may include the format + # extension in which case it will be ignored. + # + # Returns the Boolean response. + def page_path_scheduled_for_deletion?(map, path) + parts = path.split('/') + if parts.size == 1 + deletions = map.keys.select { |k| !map[k] } + downfile = parts.first.downcase.sub(/\.\w+$/, '') + deletions.any? { |d| d.downcase.sub(/\.\w+$/, '') == downfile } + else + part = parts.shift + if rest = map[part] + page_path_scheduled_for_deletion?(rest, parts.join('/')) + else + false + end + end + end + + # Determine if a given file is scheduled to be deleted in the next commit + # for the given Index. + # + # map - The Hash map: + # key - The String directory or filename. + # val - The Hash submap or the String contents of the file. + # path - The String path of the file including extension. + # + # Returns the Boolean response. + def file_path_scheduled_for_deletion?(map, path) + parts = path.split('/') + if parts.size == 1 + deletions = map.keys.select { |k| !map[k] } + deletions.any? { |d| d == parts.first } + else + part = parts.shift + if rest = map[part] + file_path_scheduled_for_deletion?(rest, parts.join('/')) + else + false + end + end + end + + # Proxies methods t + def method_missing(name, *args) + index.send(name, *args) + end + end +end \ No newline at end of file diff --git a/lib/gollum/wiki.rb b/lib/gollum/wiki.rb index 76107586..211bbe66 100644 --- a/lib/gollum/wiki.rb +++ b/lib/gollum/wiki.rb @@ -181,22 +181,36 @@ module Gollum # format - The Symbol format of the page. # data - The new String contents of the page. # commit - The commit Hash details: - # :message - The String commit message. - # :name - The String author full name. - # :email - The String email address. + # :message - The String commit message. + # :name - The String author full name. + # :email - The String email address. + # :parent - Optional Grit::Commit parent to this update. + # :tree - Optional String SHA of the tree to create the + # index from. + # :committer - Optional Gollum::Committer instance. If provided, + # assume that this operation is part of batch of + # updates and the commit happens later. # - # Returns the String SHA1 of the newly written version. + # Returns the String SHA1 of the newly written version, or the + # Gollum::Committer instance if this is part of a batch update. def write_page(name, format, data, commit = {}) - index = nil - sha1 = commit_index(commit) do |idx| - index = idx - add_to_index(index, '', name, format, data) + multi_commit = false + + committer = if obj = commit[:committer] + multi_commit = true + obj + else + Committer.new(self, commit) end - @access.refresh - update_working_dir(index, '', name, format) + committer.add_to_index('', name, format, data) - sha1 + committer.after_commit do |index| + @access.refresh + index.update_working_dir('', name, format) + end + + multi_commit ? committer : committer.commit end # Public: Update an existing page with new content. The location of the @@ -209,57 +223,85 @@ module Gollum # format - The Symbol format of the page. # data - The new String contents of the page. # commit - The commit Hash details: - # :message - The String commit message. - # :name - The String author full name. - # :email - The String email address. + # :message - The String commit message. + # :name - The String author full name. + # :email - The String email address. + # :parent - Optional Grit::Commit parent to this update. + # :tree - Optional String SHA of the tree to create the + # index from. + # :committer - Optional Gollum::Committer instance. If provided, + # assume that this operation is part of batch of + # updates and the commit happens later. # - # Returns the String SHA1 of the newly written version. + # Returns the String SHA1 of the newly written version, or the + # Gollum::Committer instance if this is part of a batch update. def update_page(page, name, format, data, commit = {}) name ||= page.name format ||= page.format dir = ::File.dirname(page.path) dir = '' if dir == '.' - index = nil - sha1 = commit_index(commit) do |idx| - index = idx - if page.name == name && page.format == format - index.add(page.path, normalize(data)) - else - index.delete(page.path) - add_to_index(index, dir, name, format, data, :allow_same_ext) - end + multi_commit = false + + committer = if obj = commit[:committer] + multi_commit = true + obj + else + Committer.new(self, commit) end - @access.refresh - update_working_dir(index, dir, page.name, page.format) - update_working_dir(index, dir, name, format) + if page.name == name && page.format == format + committer.add(page.path, normalize(data)) + else + committer.delete(page.path) + committer.add_to_index(dir, name, format, data, :allow_same_ext) + end - sha1 + committer.after_commit do |index| + @access.refresh + index.update_working_dir(dir, page.name, page.format) + index.update_working_dir(dir, name, format) + end + + multi_commit ? committer : committer.commit end # Public: Delete a page. # # page - The Gollum::Page to delete. # commit - The commit Hash details: - # :message - The String commit message. - # :name - The String author full name. - # :email - The String email address. + # :message - The String commit message. + # :name - The String author full name. + # :email - The String email address. + # :parent - Optional Grit::Commit parent to this update. + # :tree - Optional String SHA of the tree to create the + # index from. + # :committer - Optional Gollum::Committer instance. If provided, + # assume that this operation is part of batch of + # updates and the commit happens later. # - # Returns the String SHA1 of the newly written version. + # Returns the String SHA1 of the newly written version, or the + # Gollum::Committer instance if this is part of a batch update. def delete_page(page, commit) - index = nil - sha1 = commit_index(commit) do |idx| - index = idx - index.delete(page.path) + multi_commit = false + + committer = if obj = commit[:committer] + multi_commit = true + obj + else + Committer.new(self, commit) + end + + committer.delete(page.path) + + committer.after_commit do |index| + dir = ::File.dirname(page.path) + dir = '' if dir == '.' + + @access.refresh + index.update_working_dir(dir, page.name, page.format) end - dir = ::File.dirname(page.path) - dir = '' if dir == '.' - - @access.refresh - update_working_dir(index, dir, page.name, page.format) - - sha1 + multi_commit ? committer : committer.commit end # Public: Applies a reverse diff for a given page. If only 1 SHA is given, @@ -274,6 +316,7 @@ module Gollum # :message - The String commit message. # :name - The String author full name. # :email - The String email address. + # :parent - Optional Grit::Commit parent to this update. # # Returns a String SHA1 of the new commit, or nil if the reverse diff does # not apply. @@ -283,41 +326,39 @@ module Gollum sha2 = nil end - pcommit = @repo.commit('master') - patch = full_reverse_diff_for(page, sha1, sha2) - commit[:parent] = [pcommit] - commit[:tree] = @repo.git.apply_patch(pcommit.sha, patch) - return false unless commit[:tree] + patch = full_reverse_diff_for(page, sha1, sha2) + committer = Committer.new(self, commit) + parent = committer.parents[0] + committer.options[:tree] = @repo.git.apply_patch(parent.sha, patch) + return false unless committer.options[:tree] + committer.after_commit do |index| + @access.refresh - index = nil - sha1 = commit_index(commit) { |i| index = i } - @access.refresh - - files = [] - if page - files << [page.path, page.name, page.format] - else - # Grit::Diff can't parse reverse diffs.... yet - lines = patch.split("\n") - while line = lines.shift - if line =~ %r{^diff --git b/.+? a/(.+)$} - path = $1 - ext = ::File.extname(path) - name = ::File.basename(path, ext) - if format = ::Gollum::Page.format_for(ext) - files << [path, name, format] + files = [] + if page + files << [page.path, page.name, page.format] + else + # Grit::Diff can't parse reverse diffs.... yet + patch.each_line do |line| + if line =~ %r{^diff --git b/.+? a/(.+)$} + path = $1 + ext = ::File.extname(path) + name = ::File.basename(path, ext) + if format = ::Gollum::Page.format_for(ext) + files << [path, name, format] + end end end end + + files.each do |(path, name, format)| + dir = ::File.dirname(path) + dir = '' if dir == '.' + index.update_working_dir(dir, name, format) + end end - files.each do |(path, name, format)| - dir = ::File.dirname(path) - dir = '' if dir == '.' - update_working_dir(index, dir, name, format) - end - - sha1 + committer.commit end # Public: Applies a reverse diff to the repo. If only 1 SHA is given, @@ -464,38 +505,6 @@ module Gollum @page_class.cname(name) + '.' + ext end - # Update the given file in the repository's working directory if there - # is a working directory present. - # - # index - The Grit::Index with which to sync. - # dir - The String directory in which the file lives. - # name - The String name of the page (may be in human format). - # format - The Symbol format of the page. - # - # Returns nothing. - def update_working_dir(index, dir, name, format) - unless @repo.bare - if @page_file_dir - dir = dir.size.zero? ? @page_file_dir : File.join(dir, @page_file_dir) - end - - path = - if dir == '' - page_file_name(name, format) - else - ::File.join(dir, page_file_name(name, format)) - end - - Dir.chdir(::File.join(@repo.path, '..')) do - if file_path_scheduled_for_deletion?(index.tree, path) - @repo.git.rm({'f' => true}, '--', path) - else - @repo.git.checkout({}, 'HEAD', '--', path) - end - end - end - end - # Fill an array with a list of pages. # # ref - A String ref that is either a commit SHA or references one. @@ -510,122 +519,6 @@ module Gollum end end - # Determine if a given file is scheduled to be deleted in the next commit - # for the given Index. - # - # map - The Hash map: - # key - The String directory or filename. - # val - The Hash submap or the String contents of the file. - # path - The String path of the file including extension. - # - # Returns the Boolean response. - def file_path_scheduled_for_deletion?(map, path) - parts = path.split('/') - if parts.size == 1 - deletions = map.keys.select { |k| !map[k] } - deletions.any? { |d| d == parts.first } - else - part = parts.shift - if rest = map[part] - file_path_scheduled_for_deletion?(rest, parts.join('/')) - else - false - end - end - end - - # Determine if a given page (regardless of format) is scheduled to be - # deleted in the next commit for the given Index. - # - # map - The Hash map: - # key - The String directory or filename. - # val - The Hash submap or the String contents of the file. - # path - The String path of the page file. This may include the format - # extension in which case it will be ignored. - # - # Returns the Boolean response. - def page_path_scheduled_for_deletion?(map, path) - parts = path.split('/') - if parts.size == 1 - deletions = map.keys.select { |k| !map[k] } - downfile = parts.first.downcase.sub(/\.\w+$/, '') - deletions.any? { |d| d.downcase.sub(/\.\w+$/, '') == downfile } - else - part = parts.shift - if rest = map[part] - page_path_scheduled_for_deletion?(rest, parts.join('/')) - else - false - end - end - end - - # Adds a page to the given Index. - # - # index - The Grit::Index to which the page will be added. - # dir - The String subdirectory of the Gollum::Page without any - # prefix or suffix slashes (e.g. "foo/bar"). - # name - The String Gollum::Page name. - # format - The Symbol Gollum::Page format. - # data - The String wiki data to store in the tree map. - # allow_same_ext - A Boolean determining if the tree map allows the same - # filename with the same extension. - # - # Raises Gollum::DuplicatePageError if a matching filename already exists. - # This way, pages are not inadvertently overwritten. - # - # Returns nothing (modifies the Index in place). - def add_to_index(index, dir, name, format, data, allow_same_ext = false) - path = page_file_name(name, format) - - dir = '/' if dir.strip.empty? - - fullpath = ::File.join(*[@page_file_dir, dir, path].compact) - fullpath = fullpath[1..-1] if fullpath =~ /^\// - - if index.current_tree && tree = index.current_tree / dir - downpath = path.downcase.sub(/\.\w+$/, '') - - tree.blobs.each do |blob| - next if page_path_scheduled_for_deletion?(index.tree, fullpath) - file = blob.name.downcase.sub(/\.\w+$/, '') - file_ext = ::File.extname(blob.name).sub(/^\./, '') - if downpath == file && !(allow_same_ext && file_ext == ext) - raise DuplicatePageError.new(dir, blob.name, path) - end - end - end - - index.add(fullpath, normalize(data)) - end - - # Commits to the repo. This is a common method used by Gollum for - # creating, updating, and deleting pages. There are typically three steps: - # building an index with the current tree, yielding the index for - # modification, and then writing the commit. - # - # options - Hash of option - # - # Returns the String SHA of the new Commit. - def commit_index(options = {}) - normalize_commit(options) - parents = [options[:parent] || @repo.commit('master')] - parents.flatten! - parents.compact! - index = self.repo.index - if tree = options[:tree] - index.read_tree(tree) - elsif parent = parents[0] - index.read_tree(parent.tree.id) - end - yield index if block_given? - - options[:name] = default_committer_name if options[:name].to_s.empty? - options[:email] = default_committer_email if options[:email].to_s.empty? - actor = Grit::Actor.new(options[:name], options[:email]) - index.commit(options[:message], parents, actor) - end - # Creates a reverse diff for the given SHAs on the given Gollum::Page. # # page - The Gollum::Page to scope the patch to, or a String Path. @@ -654,20 +547,6 @@ module Gollum full_reverse_diff_for(nil, sha1, sha2) end - # Ensures a commit hash has all the required fields for a commit. - # - # commit - The commit Hash details: - # :message - The String commit message. - # :name - The String author full name. - # :email - The String email address. - # - # Returns the commit Hash - def normalize_commit(commit = {}) - commit[:name] = default_committer_name if commit[:name].to_s.empty? - commit[:email] = default_committer_email if commit[:email].to_s.empty? - commit - end - # Gets the default name for commits. # # Returns the String name. diff --git a/test/test_committer.rb b/test/test_committer.rb new file mode 100644 index 00000000..f87a05a4 --- /dev/null +++ b/test/test_committer.rb @@ -0,0 +1,27 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "helper")) + +context "Wiki" do + setup do + @wiki = Gollum::Wiki.new(testpath("examples/lotr.git")) + end + + test "normalizes commit hash" do + commit = {:message => 'abc'} + name = @wiki.repo.config['user.name'] + email = @wiki.repo.config['user.email'] + committer = Gollum::Committer.new(@wiki, commit) + assert_equal name, committer.actor.name + assert_equal email, committer.actor.email + + commit[:name] = 'bob' + commit[:email] = '' + committer = Gollum::Committer.new(@wiki, commit) + assert_equal 'bob', committer.actor.name + assert_equal email, committer.actor.email + + commit[:email] = 'foo@bar.com' + committer = Gollum::Committer.new(@wiki, commit) + assert_equal 'bob', committer.actor.name + assert_equal 'foo@bar.com', committer.actor.email + end +end \ No newline at end of file diff --git a/test/test_wiki.rb b/test/test_wiki.rb index 1f862dd0..4ac9aa4e 100644 --- a/test/test_wiki.rb +++ b/test/test_wiki.rb @@ -44,23 +44,6 @@ context "Wiki" do assert_equal 4, @wiki.size end - test "normalizes commit hash" do - commit = {:message => 'abc'} - name = @wiki.repo.config['user.name'] - email = @wiki.repo.config['user.email'] - assert_equal({:message => 'abc', :name => name, :email => email}, - @wiki.normalize_commit(commit.dup)) - - commit[:name] = 'bob' - commit[:email] = '' - assert_equal({:message => 'abc', :name => 'bob', :email => email}, - @wiki.normalize_commit(commit.dup)) - - commit[:email] = 'foo@bar.com' - assert_equal({:message => 'abc', :name => 'bob', :email => 'foo@bar.com'}, - @wiki.normalize_commit(commit.dup)) - end - test "text_data" do wiki = Gollum::Wiki.new(testpath("examples/yubiwa.git")) if String.instance_methods.include?(:encoding) From 2b1510af111ed3b2b8b053ee1462a805b6652d97 Mon Sep 17 00:00:00 2001 From: rick Date: Wed, 19 Jan 2011 05:20:58 -0800 Subject: [PATCH 02/13] edit a page, sidebar, and footer all in one commit --- lib/gollum/frontend/app.rb | 23 ++++++++++++++--------- test/test_app.rb | 11 ++++++----- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/gollum/frontend/app.rb b/lib/gollum/frontend/app.rb index 355cf826..6ee46f60 100644 --- a/lib/gollum/frontend/app.rb +++ b/lib/gollum/frontend/app.rb @@ -57,12 +57,15 @@ module Precious post '/edit/*' do wiki = Gollum::Wiki.new(settings.gollum_path, settings.wiki_options) page = wiki.page(params[:splat].first) - name = params[:rename] || page.name - msg = commit_message - update_wiki_page(wiki, page, params[:content], msg, name, + name = params[:rename] + committer = Gollum::Committer.new(wiki, commit_message) + commit = {:committer => committer} + + update_wiki_page(wiki, page, params[:content], commit, name, params[:format]) - update_wiki_page(wiki, page.footer, params[:footer], msg) if params[:footer] - update_wiki_page(wiki, page.sidebar, params[:sidebar], msg) if params[:sidebar] + update_wiki_page(wiki, page.footer, params[:footer], commit) if params[:footer] + update_wiki_page(wiki, page.sidebar, params[:sidebar], commit) if params[:sidebar] + committer.commit redirect "/#{CGI.escape(Gollum::Page.cname(name))}" end @@ -187,10 +190,12 @@ module Precious end def update_wiki_page(wiki, page, content, commit_message, name = nil, format = nil) - return if !page || !content || page.raw_data == content - name ||= page.name - format = (format || page.format).to_sym - wiki.update_page(page, name, format, content, commit_message) + return if !page || + ((!content || page.raw_data == content) && page.format == format) + name ||= page.name + format = (format || page.format).to_sym + content ||= page.raw_data + wiki.update_page(page, name, format, content.to_s, commit_message) end def commit_message diff --git a/test/test_app.rb b/test/test_app.rb index 96aa3f19..4380615b 100644 --- a/test/test_app.rb +++ b/test/test_app.rb @@ -30,13 +30,13 @@ context "Frontend" do end test "edits page footer and sidebar" do - page_1 = @wiki.page('A') - foot_1 = page_1.footer - side_1 = page_1.sidebar + commits = @wiki.repo.commits('master').size + page_1 = @wiki.page('A') + foot_1 = page_1.footer + side_1 = page_1.sidebar post "/edit/A", - :footer => 'footer', :sidebar => 'sidebar', - :format => page_1.format, :message => 'def' + :footer => 'footer', :sidebar => 'sidebar', :message => 'def' follow_redirect! assert last_response.ok? @@ -53,6 +53,7 @@ context "Frontend" do assert_equal 'sidebar', side_2.raw_data assert_equal 'def', side_2.version.message assert_not_equal side_1.version.sha, side_2.version.sha + assert_equal commits+1, @wiki.repo.commits('master').size end test "renames page" do From 8ddb78158887c61ccb18bcdc6522d7bb83af4ebe Mon Sep 17 00:00:00 2001 From: rick Date: Wed, 19 Jan 2011 05:51:16 -0800 Subject: [PATCH 03/13] yield the created SHA1 in the after_commit callback --- lib/gollum/committer.rb | 5 +++-- lib/gollum/wiki.rb | 8 ++++---- test/test_committer.rb | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/gollum/committer.rb b/lib/gollum/committer.rb index 9038f2ff..a8b73673 100644 --- a/lib/gollum/committer.rb +++ b/lib/gollum/committer.rb @@ -144,14 +144,15 @@ module Gollum def commit sha1 = index.commit(@options[:message], parents, actor) @callbacks.each do |cb| - cb.call(self) + cb.call(self, sha1) end sha1 end # Adds a callback to be fired after a commit. # - # block - A block that expects this Committer instance as the argument. + # block - A block that expects this Committer instance and the created + # commit's SHA1 as the arguments. # # Returns nothing. def after_commit(&block) diff --git a/lib/gollum/wiki.rb b/lib/gollum/wiki.rb index 211bbe66..dd362572 100644 --- a/lib/gollum/wiki.rb +++ b/lib/gollum/wiki.rb @@ -205,7 +205,7 @@ module Gollum committer.add_to_index('', name, format, data) - committer.after_commit do |index| + committer.after_commit do |index, sha| @access.refresh index.update_working_dir('', name, format) end @@ -256,7 +256,7 @@ module Gollum committer.add_to_index(dir, name, format, data, :allow_same_ext) end - committer.after_commit do |index| + committer.after_commit do |index, sha| @access.refresh index.update_working_dir(dir, page.name, page.format) index.update_working_dir(dir, name, format) @@ -293,7 +293,7 @@ module Gollum committer.delete(page.path) - committer.after_commit do |index| + committer.after_commit do |index, sha| dir = ::File.dirname(page.path) dir = '' if dir == '.' @@ -331,7 +331,7 @@ module Gollum parent = committer.parents[0] committer.options[:tree] = @repo.git.apply_patch(parent.sha, patch) return false unless committer.options[:tree] - committer.after_commit do |index| + committer.after_commit do |index, sha| @access.refresh files = [] diff --git a/test/test_committer.rb b/test/test_committer.rb index f87a05a4..68157844 100644 --- a/test/test_committer.rb +++ b/test/test_committer.rb @@ -24,4 +24,27 @@ context "Wiki" do assert_equal 'bob', committer.actor.name assert_equal 'foo@bar.com', committer.actor.email end + + test "yield after_commit callback" do + @path = cloned_testpath('examples/lotr.git') + yielded = nil + begin + wiki = Gollum::Wiki.new(@path) + committer = Gollum::Committer.new(wiki) + committer.after_commit do |index, sha1| + yielded = sha1 + assert_equal committer, index + end + + res = wiki.write_page("Gollum", :markdown, "# Gollum", + :committer => committer) + + assert_equal committer, res + + sha1 = committer.commit + assert_equal sha1, yielded + ensure + FileUtils.rm_rf(@path) + end + end end \ No newline at end of file From a78964ae30d49928e848560e718963c2fbdc1f43 Mon Sep 17 00:00:00 2001 From: rick Date: Thu, 20 Jan 2011 14:03:27 -0800 Subject: [PATCH 04/13] upgrade to sanitize 2.0 --- gollum.gemspec | 4 ++-- lib/gollum/page.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gollum.gemspec b/gollum.gemspec index 22764d55..fab41920 100644 --- a/gollum.gemspec +++ b/gollum.gemspec @@ -23,12 +23,12 @@ Gem::Specification.new do |s| s.rdoc_options = ["--charset=UTF-8"] s.extra_rdoc_files = %w[README.md LICENSE] - s.add_dependency('grit', "~> 2.4.0") + s.add_dependency('grit', "~> 2.4.1") s.add_dependency('github-markup', [">= 0.4.0", "< 1.0.0"]) s.add_dependency('albino', "~> 1.2.3") s.add_dependency('sinatra', "~> 1.0") s.add_dependency('mustache', [">= 0.11.2", "< 1.0.0"]) - s.add_dependency('sanitize', "~> 1.1") + s.add_dependency('sanitize', "~> 2.0.0") s.add_dependency('nokogiri', "~> 1.4") s.add_development_dependency('RedCloth') diff --git a/lib/gollum/page.rb b/lib/gollum/page.rb index 61d04484..487700e2 100644 --- a/lib/gollum/page.rb +++ b/lib/gollum/page.rb @@ -136,7 +136,7 @@ module Gollum Sanitize.clean(header.to_html) else Sanitize.clean(name) - end + end.strip end # Public: The path of the page within the repo. From feef486f63b0b69423500ed6a6949c2babfe74e1 Mon Sep 17 00:00:00 2001 From: rick Date: Thu, 20 Jan 2011 14:37:35 -0800 Subject: [PATCH 05/13] add wikicloth as a dev gem dep --- gollum.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/gollum.gemspec b/gollum.gemspec index fab41920..bcea6b4f 100644 --- a/gollum.gemspec +++ b/gollum.gemspec @@ -37,6 +37,7 @@ Gem::Specification.new do |s| s.add_development_dependency('rdiscount') s.add_development_dependency('shoulda') s.add_development_dependency('rack-test') + s.add_development_dependency('wikicloth') # = MANIFEST = s.files = %w[ From f708dc7002ba8c1569eeef521ff595b157765e87 Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Thu, 20 Jan 2011 15:06:21 -0800 Subject: [PATCH 06/13] fix bug due to the way Array#to_s works in 1.9 --- lib/gollum/git_access.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index 808e4f2e..21444f68 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -221,7 +221,7 @@ module Gollum # Returns an Array of BlobEntry instances. def parse_tree_line(line) mode, type, sha, size, *name = line.split(/\s+/) - BlobEntry.new(sha, name.to_s, size.to_i) + BlobEntry.new(sha, name.join(' '), size.to_i) end # Decode octal sequences (\NNN) in tree path names. From 732e8a936191ca4dafa48218fa30ba5186f7e77a Mon Sep 17 00:00:00 2001 From: rick Date: Thu, 27 Jan 2011 16:34:29 -0800 Subject: [PATCH 07/13] fix bug where relative images in previews did not render --- lib/gollum/page.rb | 4 ++-- lib/gollum/wiki.rb | 8 ++++---- test/test_markup.rb | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/gollum/page.rb b/lib/gollum/page.rb index 487700e2..be6cc10e 100644 --- a/lib/gollum/page.rb +++ b/lib/gollum/page.rb @@ -333,9 +333,9 @@ module Gollum # path - The String directory path of the page file. # # Returns the populated Gollum::Page. - def populate(blob, path) + def populate(blob, path=nil) @blob = blob - @path = (path + '/' + blob.name)[1..-1] + @path = "#{path}/#{blob.name}"[1..-1] self end diff --git a/lib/gollum/wiki.rb b/lib/gollum/wiki.rb index dd362572..e8a65246 100644 --- a/lib/gollum/wiki.rb +++ b/lib/gollum/wiki.rb @@ -168,10 +168,10 @@ module Gollum def preview_page(name, data, format) page = @page_class.new(self) ext = @page_class.format_to_ext(format.to_sym) - path = @page_class.cname(name) + '.' + ext - blob = OpenStruct.new(:name => path, :data => data) - page.populate(blob, path) - page.version = @access.commit('HEAD') + name = @page_class.cname(name) + '.' + ext + blob = OpenStruct.new(:name => name, :data => data) + page.populate(blob) + page.version = @access.commit('master') page end diff --git a/test/test_markup.rb b/test/test_markup.rb index 4f759ca3..6c8db408 100644 --- a/test/test_markup.rb +++ b/test/test_markup.rb @@ -238,6 +238,27 @@ context "Markup" do assert_equal %{

a a b

}, output end + test "image with absolute path on a preview" do + @wiki = Gollum::Wiki.new(@path, :base_path => '/wiki') + index = @wiki.repo.index + index.add("alpha.jpg", "hi") + index.commit("Add alpha.jpg") + + page = @wiki.preview_page("Test", "a [[/alpha.jpg]] b", :markdown) + assert_equal %{

a b

}, page.formatted_data + end + + test "image with relative path on a preview" do + @wiki = Gollum::Wiki.new(@path, :base_path => '/wiki') + index = @wiki.repo.index + index.add("alpha.jpg", "hi") + index.add("greek/alpha.jpg", "hi") + index.commit("Add alpha.jpg") + + page = @wiki.preview_page("Test", "a [[alpha.jpg]] [[greek/alpha.jpg]] b", :markdown) + assert_equal %{

a b

}, page.formatted_data + end + test "image with alt" do content = "a [[alpha.jpg|alt=Alpha Dog]] b" output = %{

a Alpha Dog b

} From 46d70e0492c884f9677875175a0e468c0e315510 Mon Sep 17 00:00:00 2001 From: Puneeth Chaganti Date: Thu, 3 Feb 2011 03:22:35 +0530 Subject: [PATCH 08/13] Add support for Org-mode to the new editor --- .../javascript/gollum-editor/langs/org.js | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js diff --git a/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js b/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js new file mode 100644 index 00000000..2982a120 --- /dev/null +++ b/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js @@ -0,0 +1,169 @@ +/** + * Org-mode Language Definition +**/ +(function() { + +var OrgMode = { + + 'function-bold' : { + search: /([^\n]+)([\n\s]*)/g, + replace: "*$1*$2" + }, + + 'function-italic' : { + search: /([^\n]+)([\n\s]*)/g, + replace: "/$1/$2" + }, + + 'function-code' : { + search: /(^[\n]+)([\n\s]*)/g, + replace: "=$1=$2" + }, + + 'function-ul' : { + search: /(.+)([\n]?)/g, + replace: "* $1$2" + }, + + /* This works, just like it works for Markdown */ + 'function-ol' : { + search: /(.+)([\n]?)/g, + replace: "1. $1$2" + }, + + 'function-blockquote' : { + search: /(.+)([\n]?)/g, + replace: "#+BEGIN_QUOTE\n$1$2\n#+END_QUOTE\n" + }, + + 'function-h1' : { + search: /(.+)([\n]?)/g, + replace: "* $1$2" + }, + + 'function-h2' : { + search: /(.+)([\n]?)/g, + replace: "** $1$2" + }, + + 'function-h3' : { + search: /(.+)([\n]?)/g, + replace: "*** $1$2" + }, + + 'function-link' : { + exec: function( txt, selText, $field ) { + var results = null; + $.GollumEditor.Dialog.init({ + title: 'Insert Link', + fields: [ + { + id: 'text', + name: 'Link Text', + type: 'text' + }, + { + id: 'href', + name: 'URL', + type: 'text' + } + ], + OK: function( res ) { + var rep = ''; + if ( res['text'] && res['href'] ) { + rep = '[[' + res['href'] + '][' + + res['text'] + ']]'; + } + else if ( res['href'] ) { + rep = '[[' + res['href'] + ']]'; + } + $.GollumEditor.replaceSelection( rep ); + } + }); + } + }, + + 'function-image' : { + exec: function( txt, selText, $field ) { + var results = null; + $.GollumEditor.Dialog.init({ + title: 'Insert Image', + fields: [ + { + id: 'url', + name: 'Image URL', + type: 'text' + }, + ], + OK: function( res ) { + var rep = ''; + if ( res['url'] ) { + rep = '[[' + res['url'] + ']]'; + } + $.GollumEditor.replaceSelection( rep ); + } + }); + } + } + +}; + +var OrgModeHelp = [ + + { + menuName: 'Block Elements', + content: [ + { + menuName: 'Paragraphs & Breaks', + data: '

To create a paragraph, simply create a block of text that is not separated by one or more blank lines. Blocks of text separated by one or more blank lines will be parsed as paragraphs.

' + }, + { + menuName: 'Headers', + data: '

Simply prefix your header text with the number of * characters to specify heading depth. For example: * Header 1, ** Header 2 and *** Header 3 will be progressively smaller headers.

' + }, + { + menuName: 'Blockquotes', + data: '

To create a blockquote, simple embed the text between #+BEGIN_QUOTE and #+END_QUOTE. An example quote block is displayed below:
#+BEGIN_QUOTE
This is my quote block. Quote something nice here, otherwise there is no point in quoting.
#+END_QUOTE

' + }, + { + menuName: 'Lists', + data: '

Markdown supports both ordered and unordered lists. To create an ordered list, simply prefix each line with a number (any number will do — this is why the editor only uses one number.) To create an unordered list, you can prefix each line with + or -.

' + }, + { + menuName: 'Code Blocks', + data: '

Code Blocks are similar to blockquote, except that #+BEGIN_EXAMPLE and #+END_EXAMPLE are used.

' + }, + ] + }, + + { + menuName: 'Span Elements', + content: [ + { + menuName: 'Links', + data: '

To create links to external pages, you need to enclose the URI in double square brackets. (i.e., [[http://github.com/]] will automatically be parsed to http://github.com/)If you want to add text, to be displayed to the user, you write the URI and the text next to each other, both enclosed in square brackets and both of them together enclosed in another pair of square brackets. For example, if you want your link to display the text “GitHub”, you write [[http://github.com][GitHub]].

' + }, + + { + menuName: 'Emphasis', + data: '

Forward slashes (/) are treated as emphasis and are wrapped with an <i> tag. Asterisks (*) are treated as bold using the <b> tag.

' + }, + + { + menuName: 'Code', + data: '

To create inline spans of code, simply wrap the code in equal signs (=). Markdown will turn =myFunction= into myFunction.

' + }, + + { + menuName: 'Images', + data: '

Orgmode image syntax is exactly same as the syntax that you would use for a URI to link to itself. The image URI is enclosed in double square brackets. org-ruby doesn't yet support alt-text for images.

' + } + ] + }, +]; + + +jQuery.GollumEditor.defineLanguage('org', OrgMode); +jQuery.GollumEditor.defineHelp('org', OrgModeHelp); + +})(); From 2648e75328da60b45cb2b41fcbee037bb0dc3584 Mon Sep 17 00:00:00 2001 From: Puneeth Chaganti Date: Thu, 3 Feb 2011 05:11:13 +0530 Subject: [PATCH 09/13] Remove references to Markdown --- .../frontend/public/javascript/gollum-editor/langs/org.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js b/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js index 2982a120..736214ea 100644 --- a/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js +++ b/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js @@ -127,7 +127,7 @@ var OrgModeHelp = [ }, { menuName: 'Lists', - data: '

Markdown supports both ordered and unordered lists. To create an ordered list, simply prefix each line with a number (any number will do — this is why the editor only uses one number.) To create an unordered list, you can prefix each line with + or -.

' + data: '

Orgmode supports both ordered and unordered lists. To create an ordered list, simply prefix each line with a number (any number will do — this is why the editor only uses one number.) To create an unordered list, you can prefix each line with + or -.

' }, { menuName: 'Code Blocks', @@ -151,7 +151,7 @@ var OrgModeHelp = [ { menuName: 'Code', - data: '

To create inline spans of code, simply wrap the code in equal signs (=). Markdown will turn =myFunction= into myFunction.

' + data: '

To create inline spans of code, simply wrap the code in equal signs (=). Orgmode will turn =myFunction= into myFunction.

' }, { From c088cc0617a50c39def43169987a6bfb012bc1a9 Mon Sep 17 00:00:00 2001 From: Puneeth Chaganti Date: Thu, 3 Feb 2011 16:32:43 +0530 Subject: [PATCH 10/13] Add help for the use of tables in org-mode --- .../frontend/public/javascript/gollum-editor/langs/org.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js b/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js index 736214ea..8e7a9cb6 100644 --- a/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js +++ b/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js @@ -133,6 +133,10 @@ var OrgModeHelp = [ menuName: 'Code Blocks', data: '

Code Blocks are similar to blockquote, except that #+BEGIN_EXAMPLE and #+END_EXAMPLE are used.

' }, + { + menuName: 'Tables', + data: '

Orgmode supports simple tables (tables with equal number of cells in each row). To create a simple table, just separate the contents of each cell with a | character. For example,

|one|two|three|
|four|five|six|


will appear as a table with two rows and three columns. Additionally,

|one|two|three|
|---+---+-----|
|four|five|six|


will also appear as a table, but the first row will be interpreted as a header row and the <th> tag will be used to render it.

' + }, ] }, From fb240fa79e0ade7021f380af834cf5572dfd935b Mon Sep 17 00:00:00 2001 From: eston Date: Thu, 3 Feb 2011 15:09:27 -0800 Subject: [PATCH 11/13] Fix Org-mode mismatched quotes; clean up syntax --- .../frontend/public/javascript/gollum-editor/langs/org.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js b/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js index 8e7a9cb6..24d5aa21 100644 --- a/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js +++ b/lib/gollum/frontend/public/javascript/gollum-editor/langs/org.js @@ -127,7 +127,7 @@ var OrgModeHelp = [ }, { menuName: 'Lists', - data: '

Orgmode supports both ordered and unordered lists. To create an ordered list, simply prefix each line with a number (any number will do — this is why the editor only uses one number.) To create an unordered list, you can prefix each line with + or -.

' + data: '

Org-mode supports both ordered and unordered lists. To create an ordered list, simply prefix each line with a number (any number will do — this is why the editor only uses one number.) To create an unordered list, you can prefix each line with + or -.

' }, { menuName: 'Code Blocks', @@ -135,7 +135,7 @@ var OrgModeHelp = [ }, { menuName: 'Tables', - data: '

Orgmode supports simple tables (tables with equal number of cells in each row). To create a simple table, just separate the contents of each cell with a | character. For example,

|one|two|three|
|four|five|six|


will appear as a table with two rows and three columns. Additionally,

|one|two|three|
|---+---+-----|
|four|five|six|


will also appear as a table, but the first row will be interpreted as a header row and the <th> tag will be used to render it.

' + data: '

Org-mode supports simple tables (tables with equal number of cells in each row). To create a simple table, just separate the contents of each cell with a | character. For example,

|one|two|three|
|four|five|six|


will appear as a table with two rows and three columns. Additionally,

|one|two|three|
|---+---+-----|
|four|five|six|


will also appear as a table, but the first row will be interpreted as a header row and the <th> tag will be used to render it.

' }, ] }, @@ -160,7 +160,7 @@ var OrgModeHelp = [ { menuName: 'Images', - data: '

Orgmode image syntax is exactly same as the syntax that you would use for a URI to link to itself. The image URI is enclosed in double square brackets. org-ruby doesn't yet support alt-text for images.

' + data: "

Org-mode image syntax is exactly same as the syntax that you would use for a URI to link to itself. The image URI is enclosed in double square brackets. Alt text on images is not currently supported by Gollum's Org-mode parser.

" } ] }, From db3d534ee53b952040c0d3d490a8ab11f2cabd9c Mon Sep 17 00:00:00 2001 From: Caleb Spare Date: Wed, 9 Feb 2011 13:06:00 -0800 Subject: [PATCH 12/13] [Frontend] Fixed bug that gave a bad redirect on page edit. --- lib/gollum/frontend/app.rb | 2 +- test/test_app.rb | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/gollum/frontend/app.rb b/lib/gollum/frontend/app.rb index 6ee46f60..4dac5b4f 100644 --- a/lib/gollum/frontend/app.rb +++ b/lib/gollum/frontend/app.rb @@ -57,7 +57,7 @@ module Precious post '/edit/*' do wiki = Gollum::Wiki.new(settings.gollum_path, settings.wiki_options) page = wiki.page(params[:splat].first) - name = params[:rename] + name = params[:rename] || params[:page] committer = Gollum::Committer.new(wiki, commit_message) commit = {:committer => committer} diff --git a/test/test_app.rb b/test/test_app.rb index 4380615b..b68c5da5 100644 --- a/test/test_app.rb +++ b/test/test_app.rb @@ -36,8 +36,9 @@ context "Frontend" do side_1 = page_1.sidebar post "/edit/A", - :footer => 'footer', :sidebar => 'sidebar', :message => 'def' + :footer => 'footer', :page => "A", :sidebar => 'sidebar', :message => 'def' follow_redirect! + assert_equal "/A", last_request.fullpath assert last_response.ok? @wiki.clear_cache @@ -62,6 +63,7 @@ context "Frontend" do :rename => "C", :format => page_1.format, :message => 'def' follow_redirect! + assert_equal "/C", last_request.fullpath assert last_response.ok? @wiki.clear_cache @@ -139,4 +141,4 @@ context "Frontend" do def app Precious::App end -end \ No newline at end of file +end From d76a40b1a82c56944ab575e6bb698195f405bf1e Mon Sep 17 00:00:00 2001 From: Caleb Spare Date: Wed, 9 Feb 2011 16:33:51 -0800 Subject: [PATCH 13/13] [Frontend] Small fix (no change in functionality) based on technoweenie's CR. --- lib/gollum/frontend/app.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gollum/frontend/app.rb b/lib/gollum/frontend/app.rb index 4dac5b4f..49687403 100644 --- a/lib/gollum/frontend/app.rb +++ b/lib/gollum/frontend/app.rb @@ -57,7 +57,7 @@ module Precious post '/edit/*' do wiki = Gollum::Wiki.new(settings.gollum_path, settings.wiki_options) page = wiki.page(params[:splat].first) - name = params[:rename] || params[:page] + name = params[:rename] || page.name committer = Gollum::Committer.new(wiki, commit_message) commit = {:committer => committer}