From c7fe97189e5af1b9783b3145f064b4597c252dac Mon Sep 17 00:00:00 2001 From: Dawa Ometto Date: Sat, 11 Jan 2025 20:10:44 +0100 Subject: [PATCH 1/3] Add TreeEntry class --- lib/gollum-lib.rb | 2 ++ lib/gollum-lib/tree_entry.rb | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 lib/gollum-lib/tree_entry.rb diff --git a/lib/gollum-lib.rb b/lib/gollum-lib.rb index f4d8ea40..1c4d7f1a 100644 --- a/lib/gollum-lib.rb +++ b/lib/gollum-lib.rb @@ -23,6 +23,7 @@ module Gollum; end require File.expand_path('../gollum-lib/committer', __FILE__) require File.expand_path('../gollum-lib/pagination', __FILE__) require File.expand_path('../gollum-lib/blob_entry', __FILE__) +require File.expand_path('../gollum-lib/tree_entry', __FILE__) require File.expand_path('../gollum-lib/wiki', __FILE__) require File.expand_path('../gollum-lib/redirects', __FILE__) require File.expand_path('../gollum-lib/file', __FILE__) @@ -31,6 +32,7 @@ module Gollum; end require File.expand_path('../gollum-lib/markup', __FILE__) require File.expand_path('../gollum-lib/markups', __FILE__) require File.expand_path('../gollum-lib/sanitization', __FILE__) + require File.expand_path('../gollum-lib/filter', __FILE__) module Gollum diff --git a/lib/gollum-lib/tree_entry.rb b/lib/gollum-lib/tree_entry.rb new file mode 100644 index 00000000..c2fc17d0 --- /dev/null +++ b/lib/gollum-lib/tree_entry.rb @@ -0,0 +1,16 @@ +# ~*~ encoding: utf-8 ~*~ +module Gollum + # Represents a Tree (directory) that can be contained in another Tree. + # We do not really care about any information other than the Tree's path and name: + # If we want to know a Tree `foo`'s'contents, this can be achieved by e.g. calling wiki.path_list(foo.path) + class TreeEntry + attr_reader :sha + attr_reader :path + + def initialize(sha, path) + @sha = sha + @path = path + @name = ::File.basename(@path) + end + end +end \ No newline at end of file From 2f8fc3233cbe14a26a1b29e57ffd81e78bbe9935 Mon Sep 17 00:00:00 2001 From: Dawa Ometto Date: Sat, 11 Jan 2025 20:14:30 +0100 Subject: [PATCH 2/3] Implement GitAccess#tree! with support for path and non-recursive --- lib/gollum-lib/git_access.rb | 23 ++++++++++-------- lib/gollum-lib/wiki.rb | 47 ++++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/lib/gollum-lib/git_access.rb b/lib/gollum-lib/git_access.rb index 2b010c07..d144b527 100644 --- a/lib/gollum-lib/git_access.rb +++ b/lib/gollum-lib/git_access.rb @@ -48,9 +48,9 @@ def ref_to_sha(ref) # ref - A String Git reference or Git SHA to a commit. # # Returns an Array of BlobEntry instances. - def tree(ref) + def tree(ref, path = '/', recursive = true) if (sha = ref_to_sha(ref)) - get_cache(:tree, sha) { tree!(sha) } + get_cache(:tree, "#{sha}#{path}") { tree!(sha, path, recursive) } else [] end @@ -156,16 +156,19 @@ def ref_to_sha!(ref) # sha - String commit SHA. # # Returns an Array of BlobEntry instances. - def tree!(sha) - tree = @repo.lstree(sha, { :recursive => true }) - items = [] - tree.each do |entry| - if entry[:type] == 'blob' - next if @page_file_dir && !entry[:path].start_with?("#{@page_file_dir}/") - items << BlobEntry.new(entry[:sha], entry[:path], entry[:size], entry[:mode]) + def tree!(sha, path = '/', recursive = true) + tree = @repo.lstree(sha, (Pathname.new(@page_file_dir.to_s) + path).to_s, recursive: recursive ) + tree.reduce([]) do |accumulator, entry| + if !@page_file_dir || entry[:path].start_with?("#{@page_file_dir}/") # guard against directory traversal + accumulator << case entry[:type] + when 'blob' + BlobEntry.new(entry[:sha], entry[:path], entry[:size], entry[:mode]) + when 'tree' + TreeEntry.new(entry[:sha], entry[:path]) + end end + accumulator end - items end # Reads the content from the Git db at the given SHA. diff --git a/lib/gollum-lib/wiki.rb b/lib/gollum-lib/wiki.rb index 920399fb..ba32be59 100644 --- a/lib/gollum-lib/wiki.rb +++ b/lib/gollum-lib/wiki.rb @@ -488,7 +488,7 @@ def files(treeish = nil) # Returns a Fixnum def size(ref = nil) tree_map_for(ref || @ref).inject(0) do |num, entry| - num + (::Gollum::Page.valid_page_name?(entry.name) ? 1 : 0) + num + (entry.is_a?(BlobEntry) && ::Gollum::Page.valid_page_name?(entry.name) ? 1 : 0) end rescue Gollum::Git::NoSuchShaFound 0 @@ -637,7 +637,7 @@ def page_file_name(name, format) format.nil? ? name : "#{name}.#{::Gollum::Page.format_to_ext(format)}" end - # Fill an array with a list of pages and files in the wiki. + # Fill an array with a list of all pages and files in the wiki. # # ref - A String ref that is either a commit SHA or references one. # @@ -645,16 +645,39 @@ def page_file_name(name, format) def tree_list(ref = @ref, pages=true, files=true) if (sha = @access.ref_to_sha(ref)) commit = @access.commit(sha) - tree_map_for(sha).inject([]) do |list, entry| + wrap_tree_entries(tree_map_for(sha), commit, pages, files, false) + else + [] + end + end + + # Fill an array with a list of pages, files, and directories at a specific path in the wiki. + # + # path - A String path in the wiki. + # ref - A String ref that is either a commit SHA or references one. + # + # Returns a flat Array of Gollum::Page and Gollum::File instances. + def path_list(path, ref = @ref) + if (sha = @access.ref_to_sha(ref)) + commit = @access.commit(sha) + wrap_tree_entries(tree_map_for(sha, false, path, false), commit, true, true, true) + else + [] + end + end + + def wrap_tree_entries(entries, commit, pages, files, trees) + entries.inject([]) do |list, entry| + if entry.is_a?(TreeEntry) + list << entry if trees + else if ::Gollum::Page.valid_page_name?(entry.name) list << entry.page(self, commit) if pages elsif files && !entry.name.start_with?('_') && !::Gollum::Page.protected_files.include?(entry.name) list << entry.file(self, commit) end - list end - else - [] + list end end @@ -690,17 +713,21 @@ def commit_for(ref) # # ref - A String ref that is either a commit SHA or references one. # ignore_page_file_dir - Boolean, if true, searches all files within the git repo, regardless of dir/subdir + # path - A String path to a directory that should be listed. + # recursive - Boolean. Whether to recursively list all contents or not. # # Returns an Array of BlobEntry instances. - def tree_map_for(ref, ignore_page_file_dir = false) + def tree_map_for(ref, ignore_page_file_dir = false, path = '/', recursive = true) if ignore_page_file_dir && !@page_file_dir.nil? @root_access ||= GitAccess.new(path, nil, @repo_is_bare) @root_access.tree(ref) else - @access.tree(ref) + begin + @access.tree(ref, path, recursive) + rescue Gollum::Git::NoSuchShaFound + [] + end end - rescue Gollum::Git::NoSuchShaFound - [] end def inspect From 1f355803624d0dccc0b8d5ac3f1fbe97ff6a5b5b Mon Sep 17 00:00:00 2001 From: Dawa Ometto Date: Sat, 11 Jan 2025 20:17:58 +0100 Subject: [PATCH 3/3] Add rudimentary test to assist with debugging --- test/test_wiki.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test_wiki.rb b/test/test_wiki.rb index bb6b8512..6b7a5df6 100644 --- a/test/test_wiki.rb +++ b/test/test_wiki.rb @@ -48,6 +48,11 @@ assert_equal commits, @wiki.log(:page_num => 2).map(&:id) end + test "list specific directory in the wiki" do + puts @wiki.path_list('Mordor').inspect + assert false + end + test "list files and pages" do contents = @wiki.tree_list pages = contents.select {|x| x.is_a?(::Gollum::Page)}