From d5970d6274308b01153d3ae7edbe45a49409af99 Mon Sep 17 00:00:00 2001 From: Dawa Ometto Date: Sun, 22 Mar 2020 17:40:13 +0100 Subject: [PATCH] Tag compatibility. Resolves #1487 (#1492) * Add --global-tag-lookup * Add bin/gollum-migrate-tags * Add migration tests (not on Travis) --- HISTORY.md | 2 +- README.md | 3 +- bin/gollum | 123 ++++----- bin/gollum-migrate-tags | 248 ++++++++++++++++++ gollum.gemspec | 2 +- test/examples/lotr_migration.git/HEAD | 1 + test/examples/lotr_migration.git/config | 8 + test/examples/lotr_migration.git/description | 1 + .../hooks/applypatch-msg.sample | 15 ++ .../hooks/commit-msg.sample | 24 ++ .../hooks/fsmonitor-watchman.sample | 114 ++++++++ .../hooks/post-update.sample | 8 + .../hooks/pre-applypatch.sample | 14 + .../hooks/pre-commit.sample | 49 ++++ .../lotr_migration.git/hooks/pre-push.sample | 53 ++++ .../hooks/pre-rebase.sample | 169 ++++++++++++ .../hooks/pre-receive.sample | 24 ++ .../hooks/prepare-commit-msg.sample | 42 +++ .../lotr_migration.git/hooks/update.sample | 128 +++++++++ test/examples/lotr_migration.git/info/exclude | 6 + ...d9bfbc1e908b433669f482566a9377bd8c58cf.idx | Bin 0 -> 1968 bytes ...9bfbc1e908b433669f482566a9377bd8c58cf.pack | Bin 0 -> 13683 bytes test/examples/lotr_migration.git/packed-refs | 2 + test/examples/lotr_migration.git/shallow | 1 + test/test_migrate.rb | 52 ++++ 25 files changed, 1026 insertions(+), 63 deletions(-) create mode 100755 bin/gollum-migrate-tags create mode 100644 test/examples/lotr_migration.git/HEAD create mode 100644 test/examples/lotr_migration.git/config create mode 100644 test/examples/lotr_migration.git/description create mode 100755 test/examples/lotr_migration.git/hooks/applypatch-msg.sample create mode 100755 test/examples/lotr_migration.git/hooks/commit-msg.sample create mode 100755 test/examples/lotr_migration.git/hooks/fsmonitor-watchman.sample create mode 100755 test/examples/lotr_migration.git/hooks/post-update.sample create mode 100755 test/examples/lotr_migration.git/hooks/pre-applypatch.sample create mode 100755 test/examples/lotr_migration.git/hooks/pre-commit.sample create mode 100755 test/examples/lotr_migration.git/hooks/pre-push.sample create mode 100755 test/examples/lotr_migration.git/hooks/pre-rebase.sample create mode 100755 test/examples/lotr_migration.git/hooks/pre-receive.sample create mode 100755 test/examples/lotr_migration.git/hooks/prepare-commit-msg.sample create mode 100755 test/examples/lotr_migration.git/hooks/update.sample create mode 100644 test/examples/lotr_migration.git/info/exclude create mode 100644 test/examples/lotr_migration.git/objects/pack/pack-a2d9bfbc1e908b433669f482566a9377bd8c58cf.idx create mode 100644 test/examples/lotr_migration.git/objects/pack/pack-a2d9bfbc1e908b433669f482566a9377bd8c58cf.pack create mode 100644 test/examples/lotr_migration.git/packed-refs create mode 100644 test/examples/lotr_migration.git/shallow create mode 100644 test/test_migrate.rb diff --git a/HISTORY.md b/HISTORY.md index 483758be..ac65657d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,7 +2,7 @@ This is a major new release that substantially cleans up and improves the codebase. It also introduces many new features, bugfixes, and removing major limitations. See [here](https://github.com/gollum/gollum/wiki/5.0-release-notes) for a list of changes. -**Note**: due to changes in the way in which Gollum handles filenames and some other changes, you may have to change some links in your wiki when migrating from gollum 4.x. See the [release notes](https://github.com/gollum/gollum/wiki/5.0-release-notes#migrating-your-wiki) for more details. +**Note**: due to changes to the way in which Gollum handles filenames, you may have to change some links in your wiki when migrating from gollum 4.x. See the [release notes](https://github.com/gollum/gollum/wiki/5.0-release-notes#migrating-your-wiki) for more details. You may be able to use the `bin/gollum-migrate-tags` script to accomplish this. Many thanks to all the users who have provided feedback, and everyone who has chipped in in the development process! diff --git a/README.md b/README.md index 1f43d09d..0b500fb9 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,6 @@ Gollum comes with the following command line options: | --assets | [PATH] | Set the path to look for static assets. | | --css | none | Tell Gollum to inject custom CSS into each page. Uses `custom.css` from repository root.3,5 | | --js | none | Tell Gollum to inject custom JS into each page. Uses `custom.js` from repository root.3,5 | -| --emoji | none | Parse and interpret emoji tags (e.g. `:heart:`) except when the leading colon is backslashed (e.g. `\:heart:`). | | --no-edit | none | Disable the feature of editing pages. | | --follow-renames, --no-follow-renames | none | Follow pages across renames in the History view. Default: true. | --allow-uploads | [MODE] | Enable file uploads. If set to `dir`, Gollum will store all uploads in the `/uploads/` directory in repository root. If set to `page`, Gollum will store each upload at the currently edited page.4 | @@ -123,6 +122,8 @@ Gollum comes with the following command line options: | --mathjax-config | [FILE] | Specify path to a custom MathJax configuration. If not specified, uses the `mathjax.config.js` file from repository root. | | --template-dir | [PATH] | Specify custom mustache template directory. | | --template-page | none | Use _Template in root as a template for new pages. Must be committed. | +| --emoji | none | Parse and interpret emoji tags (e.g. `:heart:`) except when the leading colon is backslashed (e.g. `\:heart:`). | +| --global-tag-lookup | none | Match an internal link to 'Foo' with the first page found with that filename, anywhere in the repository. Provides compatibility with Gollum 4.x. | | --help | none | Display the list of options on the command line. | | --version | none | Display the current version of Gollum. | | --versions | none | Display the current version of Gollum and auxiliary gems. | diff --git a/bin/gollum b/bin/gollum index a99b5556..473c9954 100755 --- a/bin/gollum +++ b/bin/gollum @@ -20,9 +20,9 @@ wiki_options = { opts = OptionParser.new do |opts| # define program name (although this defaults to the name of the file, just in case...) - opts.program_name = "gollum" + opts.program_name = 'gollum' - # set basic info for the "--help" command (options will be appended automatically from the below definitions) + # set basic info for the '--help' command (options will be appended automatically from the below definitions) opts.banner = ' Gollum is a multi-format Wiki Engine/API/Frontend. @@ -40,32 +40,32 @@ opts = OptionParser.new do |opts| OPTIONS' # define gollum options - opts.separator "" - opts.separator " Major:" + opts.separator '' + opts.separator ' Major:' - opts.on("-h", "--host [HOST]", "Specify the hostname or IP address to listen on. Default: '0.0.0.0'.") do |host| + opts.on('-h', '--host [HOST]', 'Specify the hostname or IP address to listen on. Default: \'0.0.0.0\'.') do |host| options[:bind] = host end - opts.on("-p", "--port [PORT]", "Specify the port to bind Gollum with. Default: '4567'.") do |port| + opts.on('-p', '--port [PORT]', 'Specify the port to bind Gollum with. Default: \'4567\'.') do |port| begin - # don't use "port.to_i" here... it doesn't raise errors which might result in a nice confusion later on + # don't use 'port.to_i' here... it doesn't raise errors which might result in a nice confusion later on options[:port] = Integer(port) rescue ArgumentError - puts "Error: '#{port}' is not a valid port number." + puts 'Error: '#{port}' is not a valid port number.' exit 1 end end - opts.on("-c", "--config [FILE]", "Specify path to the Gollum's configuration file.") do |file| + opts.on('-c', '--config [FILE]', 'Specify path to the Gollum\'s configuration file.') do |file| options[:config] = file end - opts.on("-r", "--ref [REF]", "Specify the branch to serve. Default: 'master'.") do |ref| + opts.on('-r', '--ref [REF]', 'Specify the branch to serve. Default: \'master\'.') do |ref| wiki_options[:ref] = ref end - opts.on("-a", "--adapter [ADAPTER]", "Launch Gollum using a specific git adapter. Default: 'rugged'.") do |adapter| + opts.on('-a', '--adapter [ADAPTER]', 'Launch Gollum using a specific git adapter. Default: \'rugged\'.') do |adapter| Gollum::GIT_ADAPTER = adapter end - opts.on("-b", "--base-path [PATH]", "Specify the leading portion of all Gollum URLs (path info). Default: '/'.", - "Example: setting this to '/wiki' will make the wiki accessible under 'http://localhost:4567/wiki/'.") do |base_path| + opts.on('-b', '--base-path [PATH]', 'Specify the leading portion of all Gollum URLs (path info). Default: \'/\'.', + 'Example: setting this to \'/wiki\' will make the wiki accessible under \'http://localhost:4567/wiki/\'.') do |base_path| # first trim a leading slash, if any base_path.sub!(/^\/+/, '') @@ -87,94 +87,97 @@ MSG # and finally, let others enjoy our hard work: wiki_options[:base_path] = base_path unless base_path.empty? end - opts.on("--page-file-dir [PATH]", "Specify the subdirectory for all pages. Default: repository root.", - "Example: setting this to 'pages' will make Gollum serve only pages at '/pages/*'.") do |path| + opts.on('--page-file-dir [PATH]', 'Specify the subdirectory for all pages. Default: repository root.', + 'Example: setting this to \'pages\' will make Gollum serve only pages at \'/pages/*\'.') do |path| wiki_options[:page_file_dir] = path end - opts.on("--static", "Use static assets. Defaults to false in development/test, defaults to true in production/staging.") do + opts.on('--static', 'Use static assets. Defaults to false in development/test, defaults to true in production/staging.') do wiki_options[:static] = true end - opts.on("--no-static", "Do not use static assets (even when in production/staging).") do + opts.on('--no-static', 'Do not use static assets (even when in production/staging).') do wiki_options[:static] = false end - opts.on("--assets [PATH]", "Set the path to look for static assets. Only used if --static is set to true, or environment is production/staging. Default: ./public/assets") do |path| + opts.on('--assets [PATH]', 'Set the path to look for static assets. Only used if --static is set to true, or environment is production/staging. Default: ./public/assets') do |path| wiki_options[:static_assets_path] = path end - opts.on("--css", "Inject custom CSS into each page. The '/custom.css' file is used (must be committed).") do + opts.on('--css', 'Inject custom CSS into each page. The \'/custom.css\' file is used (must be committed).') do wiki_options[:css] = true end - opts.on("--js", "Inject custom JavaScript into each page. The '/custom.js' file is used (must be committed).") do + opts.on('--js', 'Inject custom JavaScript into each page. The \'/custom.js\' file is used (must be committed).') do wiki_options[:js] = true end - opts.on("--emoji", "Parse and interpret emoji tags (e.g. :heart:) except when the leading colon is backslashed (e.g. \\:heart:).") do - wiki_options[:emoji] = true - end - opts.on("--no-edit", "Disable the feature of editing pages.") do + opts.on('--no-edit', 'Disable the feature of editing pages.') do wiki_options[:allow_editing] = false end - opts.on("--follow-renames", "Follow pages across renames in the History view. Default: true.") do + opts.on('--follow-renames', 'Follow pages across renames in the History view. Default: true.') do wiki_options[:follow_renames] = true end - opts.on("--no-follow-renames", "Do not follow pages across renames in the History view.") do + opts.on('--no-follow-renames', 'Do not follow pages across renames in the History view.') do wiki_options[:follow_renames] = false end - opts.on("--allow-uploads [MODE]", [:dir, :page], "Enable file uploads.", - "If set to 'dir', Gollum will store all uploads in the '/uploads/' directory.", - "If set to 'page', Gollum will store uploads per page in '/uploads/[pagename]'.") do |mode| + opts.on('--allow-uploads [MODE]', [:dir, :page], 'Enable file uploads.', + 'If set to \'dir\', Gollum will store all uploads in the \'/uploads/\' directory.', + 'If set to \'page\', Gollum will store uploads per page in \'/uploads/[pagename]\'.') do |mode| wiki_options[:allow_uploads] = true wiki_options[:per_page_uploads] = true if mode == :page end - opts.on("--mathjax", "Enable MathJax (renders mathematical equations).", - "By default, uses the 'TeX-AMS-MML_HTMLorMML' config with the 'autoload-all' extension.") do + opts.on('--mathjax', 'Enable MathJax (renders mathematical equations).', + 'By default, uses the \'TeX-AMS-MML_HTMLorMML\' config with the \'autoload-all\' extension.') do wiki_options[:mathjax] = true wiki_options[:mathjax_config] = 'mathjax.config.js' end - opts.on("--critic-markup", "Enable support for annotations using CriticMarkup.") do + opts.on('--critic-markup', 'Enable support for annotations using CriticMarkup.') do wiki_options[:critic_markup] = true end - opts.on("--irb", "Launch Gollum in 'console mode', with a predefined API.") do + opts.on('--irb', 'Launch Gollum in \'console mode\', with a predefined API.') do options[:irb] = true end - opts.separator "" - opts.separator " Minor:" + opts.separator '' + opts.separator ' Minor:' - opts.on("--h1-title", "Use the first '

' as page title.") do + opts.on('--h1-title', 'Use the first \'

\' as page title.') do wiki_options[:h1_title] = true end - opts.on("--no-display-metadata", "Do not render metadata tables in pages.") do + opts.on('--no-display-metadata', 'Do not render metadata tables in pages.') do wiki_options[:display_metadata] = false end - opts.on("--user-icons [MODE]", [:gravatar, :identicon], "Use specific user-icons for history view.", - "Can be set to 'gravatar' or 'identicon'. Default: standard avatar.") do |mode| + opts.on('--user-icons [MODE]', [:gravatar, :identicon], 'Use specific user-icons for history view.', + 'Can be set to \'gravatar\' or \'identicon\'. Default: standard avatar.') do |mode| wiki_options[:user_icons] = mode.to_s end - opts.on("--template-dir [PATH]", "Specify custom mustache template directory.") do |path| + opts.on('--template-dir [PATH]', 'Specify custom mustache template directory.') do |path| wiki_options[:template_dir] = path end - opts.on("--template-page", "Use _Template in root as a template for new pages.") do + opts.on('--template-page', 'Use _Template in root as a template for new pages.') do wiki_options[:template_page] = true end - opts.separator "" - opts.separator " Common:" + opts.on('--global-tag-lookup', 'Match an internal link to \'Foo\' with the first page found with that filename, anywhere in the repository. Provides compatibility with Gollum 4.x.') do + wiki_options[:global_tag_lookup] = true + end + opts.on('--emoji', 'Parse and interpret emoji tags (e.g. :heart:) except when the leading colon is backslashed (e.g. \\:heart:).') do + wiki_options[:emoji] = true + end + opts.separator '' + opts.separator ' Common:' - opts.on("--help", "Display this message.") do + opts.on('--help', 'Display this message.') do puts opts exit 0 end - opts.on("--version", "Display the current version of Gollum.") do - puts "Gollum " + Gollum::VERSION + opts.on('--version', 'Display the current version of Gollum.') do + puts 'Gollum ' + Gollum::VERSION end - opts.on("--versions", "Display the current version of Gollum and auxiliary gems.") do + opts.on('--versions', 'Display the current version of Gollum and auxiliary gems.') do require 'gollum-lib' - puts "Gollum " + Gollum::VERSION - puts "Running on: #{RUBY_PLATFORM} with Ruby version #{RUBY_VERSION}" - puts "Using:" + puts 'Gollum ' + Gollum::VERSION + puts 'Running on: #{RUBY_PLATFORM} with Ruby version #{RUBY_VERSION}' + puts 'Using:' loaded_gemspecs = Gem.loaded_specs gollum_gems = ['gollum-lib', 'gollum-rjgit_adapter', 'rjgit', 'gollum-rugged_adapter', 'rugged'] puts Gem.loaded_specs.select{|name, spec| gollum_gems.include?(name)}.map {|name, spec| "#{name} #{spec.version}"} puts "Markdown rendering gem: #{GitHub::Markup::Markdown.implementation_name}" - puts "Other renderering gems:" + puts 'Other renderering gems:' renderer_gems = ['RedCloth', 'org-ruby', 'creole', 'asciidoctor', 'wikicloth'] renderer_gems.each do |renderer| begin @@ -188,15 +191,15 @@ MSG end - opts.separator "" + opts.separator '' end # Read command line options into `options` hash begin opts.parse! rescue OptionParser::InvalidOption - puts "gollum: #{$!.message}" - puts "gollum: try 'gollum --help' for more information" + puts 'gollum: #{$!.message}' + puts 'gollum: try \'gollum --help\' for more information' exit end @@ -236,21 +239,21 @@ if options[:irb] end puts - puts "Loaded Gollum wiki at:" + puts 'Loaded Gollum wiki at:' puts "#{File.expand_path(gollum_path).inspect}" puts - puts "Example API calls:" + puts 'Example API calls:' puts %( page = wiki.page('page-name')) puts %( # => ) puts puts %( page.raw_data) - puts %( # => "# My wiki page") + puts %( # => '# My wiki page') puts puts %( page.formatted_data) - puts %( # => "

My wiki page

") + puts %( # => '

My wiki page

') puts - puts "Full API documentation at:" - puts "https://github.com/gollum/gollum-lib" + puts 'Full API documentation at:' + puts 'https://github.com/gollum/gollum-lib' puts IRB.start_session(binding) rescue Gollum::InvalidGitRepositoryError, Gollum::NoSuchPathError diff --git a/bin/gollum-migrate-tags b/bin/gollum-migrate-tags new file mode 100755 index 00000000..241d9001 --- /dev/null +++ b/bin/gollum-migrate-tags @@ -0,0 +1,248 @@ +#!/usr/bin/env ruby +#coding:utf-8 +require 'optparse' +require 'pathname' +require 'rubygems' + +REPO = ARGV[0] || Dir.pwd + +wiki_options = {} +options = {} + +opts = OptionParser.new do |opts| +opts.banner = <= 5.x to enable 4.x-style global tags. + +See https://github.com/gollum/gollum/wiki/5.0-release-notes#filename-handling for more information. +Usage of this script comes without any warranty. + +Note: you can also use this script with the --dry-run option to find broken tags in your wiki. + +Usage: gollum-migrate-tags /path/to/repo + +NB: without the --write flag, this will be a 'dry run' that doesn't actually make any changes, but outputs the changes that would be made. + +You can use the --page-file-dir and --config options as you would normally with gollum. + +Requires a non-bare repository. Recommended usage: + +1. Clone your wiki's repository to create a backup. +2. Run this script on your cloned repo. +3. If all looks sane, run the script with the --write option. This will overwrite files in your working directory, but not commit the changes, so you have time to review them. +4. Do a 'git diff' to inspect the changes. +5. Commit the changes if all looks sane, and push/pull them back into your original repo. + +Options: +EOF + opts.on('-c', '--config [FILE]', 'Specify path to the Gollum\'s configuration file.') do |file| + options[:config] = file + end + + opts.on('--page-file-dir [PATH]', 'Specify the subdirectory for all pages. Default: repository root.') do |path| + wiki_options[:page_file_dir] = path + end + + opts.on('--prefer-relative-links', 'When specified, will try to replace broken links with relative links (\'[[Foo/Bar]]\' instead of \'[[/Subdir/Foo/Bar]]\') where possible.') do + PREFER_RELATIVE = true + end + + opts.on('--run-silent', 'Don\'t output anything.') do + PREFER_RELATIVE = true + end + + opts.on('--write', 'No dry run: actually perform the substitutions.') do + NO_DRY_RUN = true + end +end + +# Read command line options into `options` hash +begin + opts.parse! +rescue OptionParser::InvalidOption + puts "gollum-migrate-tags: #{$!.message}" + puts "gollum-migrate-tags: try 'gollum-migrate-tags --help' for more information" + exit +end + +require 'gollum-lib' + +if cfg = options[:config] + # If the path begins with a '/' it will be considered an absolute path, + # otherwise it will be relative to the CWD + cfg = File.join(Dir.getwd, cfg) unless cfg.slice(0) == File::SEPARATOR + require cfg +end + +class Gollum::Filter::CodeMigrator < Gollum::Filter::Code + def extract(data) + case @markup.format + when :asciidoc + data.gsub!(/^(\[source,([^\r\n]*)\]\n)?----\n(.+?)\n----$/m) do + cache_codeblock($~.to_s) + end + when :org + org_headers = %r{([ \t]*#\+HEADER[S]?:[^\r\n]*\n)*} + org_name = %r{([ \t]*#\+NAME:[^\r\n]*\n)?} + org_lang = %r{[ ]*([^\n \r]*)[ ]*[^\r\n]*} + org_begin = %r{([ \t]*)#\+BEGIN_SRC#{org_lang}\r?\n} + org_end = %r{\r?\n[ \t]*#\+END_SRC[ \t\r]*} + data.gsub!(/^#{org_headers}#{org_name}#{org_begin}(.+?)#{org_end}$/mi) do + cache_codeblock($~.to_s) + end + when :markdown + data.gsub!(/^([ ]{0,3})(~~~+) ?([^\r\n]+)?\r?\n(.+?)\r?\n[ ]{0,3}(~~~+)[ \t\r]*$/m) do + m_indent = Regexp.last_match[1] + m_start = Regexp.last_match[2] # ~~~ + m_lang = Regexp.last_match[3] + m_code = Regexp.last_match[4] + m_end = Regexp.last_match[5] # ~~~ + # The closing code fence must be at least as long as the opening fence + next '' if m_end.length < m_start.length + lang = m_lang ? m_lang.strip.split.first : nil + cache_codeblock($~.to_s) + end + end + + data.gsub!(/^([ ]{0,3})``` ?([^\r\n]+)?\r?\n(.+?)\r?\n[ ]{0,3}```[ \t]*\r?$/m) do + cache_codeblock($~.to_s) + end + data + end + + def process(data) + return data if data.nil? || data.size.zero? || @map.size.zero? + @map.each do |id, block| ## Just put the code blocks back in verbatim + data.gsub!(id, block) + end + data + end + + def cache_codeblock(block) + id = "#{open_pattern}#{Digest::SHA1.hexdigest(block)}#{close_pattern}" + @map[id] = block + id + end +end + +class ::Gollum::Filter::TagMigrator < Gollum::Filter::Tags + def process_tag(tag) + link_part, extra = parse_tag_parts(tag) + orig_tag = %{[[#{tag}]]} + return orig_tag if link_part.nil? + + img_args = extra ? [extra, link_part] : [link_part] + mime = MIME::Types.type_for(::File.extname(img_args.first.to_s)).first + + # For any kind of tag other than an internal link: just return the tag. + if tag =~ /^_TOC_/ || link_part =~ /^_$/ || link_part =~ /^#{INCLUDE_TAG}/ || (mime && mime.content_type =~ /^image/) || process_external_link_tag(link_part, extra) || process_file_link_tag(link_part, extra) + return orig_tag + end + + # It's an internal Page link tag. Try to find the file/page it links to + link = link_part + page = find_page_from_path(link) + anchor = nil + + if page.nil? # No match yet, now try finding the page with anchor removed + if pos = link.rindex('#') + anchor = link[pos..-1] + link = link[0...pos] + end + + if link.empty? && anchor # Internal anchor link, don't search for the page but return the original tag + return orig_tag + end + + page = find_page_from_path(link) + end + + if page + # Great, the link is not broken. Return the original tag. + return orig_tag + else + possibles = find_linked(link) + if possibles.empty? + log(:info, "Found no candidates for broken link: #{orig_tag}") + return orig_tag + else + if possibles.size > 1 + log(:empty) + log(:warn, "Found multiple possibilities for the link '#{orig_tag}':") + possibles.map! {|p| Pathname.new(p)} + possibles.sort! + possibles.each{|p| log(:none, "* #{p}")} + log(:warn,"Picking #{possibles.first}") + log(:empty) + end + pick = possibles.first + return tag_for_pick(pick, orig_tag, extra, anchor, @markup.page.path) + end + + end + end + + private + + def tag_for_pick(pick, orig_tag, extra, anchor, linking_page_path) + pick = if defined?(PREFER_RELATIVE) + overlapping_path = Pathname.new(linking_page_path).dirname.to_s + relative_path = pick.to_s.match(/^\/#{overlapping_path}\/(.+)/) + relative_path ? relative_path[1] : pick + else + pick + end + new_tag = extra.nil? ? %{[[#{pick}]]} : %{[[#{extra}|#{pick}#{anchor}]]} + log(:info, "#{@markup.page.path}: Changing #{orig_tag} -> #{new_tag}") + return defined?(DRY_RUN) ? orig_tag : new_tag + end +end + +class ::Gollum::Filter::PlainTextMigrator < Gollum::Filter::PlainText + def extract(data) + data + end +end + +filter_chain = [:PlainTextMigrator, :CodeMigrator, :TagMigrator] + +wiki = ::Gollum::Wiki.new(REPO, wiki_options.merge({:filter_chain => filter_chain})) +TREE = wiki.tree_list(wiki.ref, true, false).map {|page| ::File.join('/', page.path)} + +def find_linked(link) + # If the link has no explicit file extension, test against the link + the '.' character. + # This is to avoid that 'Samwi' matches 'Samwise.md' + # If it has an explicit file extension ('Samwi.md'), just test against that. + test_path = ::File.extname(link).empty? ? /#{link}\..+/ : link + # Select pages from the wiki whose path =~ 'Foo/Bar/Samwi.*' + # Match case-insenstively to mimic 4.x behavior! + TREE.select {|path| path =~ /^\/(.*\/)?#{test_path}/i} +end + +def log(kind, msg = nil) + unless defined?(RUN_SILENT) + if kind == :none + puts msg + elsif kind == :empty + puts + else + puts "[#{kind.to_s.upcase}] #{msg}" + end + end +end + +wiki.pages.each do |page| + log(:info,"Page #{page.path}") + new_data = page.formatted_data + if defined?(NO_DRY_RUN) + path = ::File.join([wiki.path, wiki.page_file_dir, page.path].compact) + f = File.new(path, 'w') + f.write(new_data) + f.close + end + log(:none, "====") +end \ No newline at end of file diff --git a/gollum.gemspec b/gollum.gemspec index 7b26862c..c66ae883 100644 --- a/gollum.gemspec +++ b/gollum.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.require_paths = %w[lib] - s.executables = ['gollum'] + s.executables = ['gollum', 'gollum-migrate-tags'] s.rdoc_options = ['--charset=UTF-8'] s.extra_rdoc_files = %w[README.md LICENSE] diff --git a/test/examples/lotr_migration.git/HEAD b/test/examples/lotr_migration.git/HEAD new file mode 100644 index 00000000..cb089cd8 --- /dev/null +++ b/test/examples/lotr_migration.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/test/examples/lotr_migration.git/config b/test/examples/lotr_migration.git/config new file mode 100644 index 00000000..e3a55b0d --- /dev/null +++ b/test/examples/lotr_migration.git/config @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true + ignorecase = true + precomposeunicode = true +[remote "origin"] + url = file:///tmp/lotr diff --git a/test/examples/lotr_migration.git/description b/test/examples/lotr_migration.git/description new file mode 100644 index 00000000..498b267a --- /dev/null +++ b/test/examples/lotr_migration.git/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/test/examples/lotr_migration.git/hooks/applypatch-msg.sample b/test/examples/lotr_migration.git/hooks/applypatch-msg.sample new file mode 100755 index 00000000..a5d7b84a --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} +: diff --git a/test/examples/lotr_migration.git/hooks/commit-msg.sample b/test/examples/lotr_migration.git/hooks/commit-msg.sample new file mode 100755 index 00000000..b58d1184 --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/test/examples/lotr_migration.git/hooks/fsmonitor-watchman.sample b/test/examples/lotr_migration.git/hooks/fsmonitor-watchman.sample new file mode 100755 index 00000000..e673bb39 --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/fsmonitor-watchman.sample @@ -0,0 +1,114 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to speed up detecting +# new and modified files. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-watchman" and set +# 'git config core.fsmonitor .git/hooks/query-watchman' +# +my ($version, $time) = @ARGV; + +# Check the hook interface version + +if ($version == 1) { + # convert nanoseconds to seconds + $time = int $time / 1000000000; +} else { + die "Unsupported query-fsmonitor hook version '$version'.\n" . + "Falling back to scanning...\n"; +} + +my $git_work_tree; +if ($^O =~ 'msys' || $^O =~ 'cygwin') { + $git_work_tree = Win32::GetCwd(); + $git_work_tree =~ tr/\\/\//; +} else { + require Cwd; + $git_work_tree = Cwd::cwd(); +} + +my $retry = 1; + +launch_watchman(); + +sub launch_watchman { + + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') + or die "open2() failed: $!\n" . + "Falling back to scanning...\n"; + + # In the query expression below we're asking for names of files that + # changed since $time but were not transient (ie created after + # $time but no longer exist). + # + # To accomplish this, we're using the "since" generator to use the + # recency index to select candidate nodes and "fields" to limit the + # output to file names only. Then we're using the "expression" term to + # further constrain the results. + # + # The category of transient files that we want to ignore will have a + # creation clock (cclock) newer than $time_t value and will also not + # currently exist. + + my $query = <<" END"; + ["query", "$git_work_tree", { + "since": $time, + "fields": ["name"], + "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]] + }] + END + + print CHLD_IN $query; + close CHLD_IN; + my $response = do {local $/; }; + + die "Watchman: command returned no output.\n" . + "Falling back to scanning...\n" if $response eq ""; + die "Watchman: command returned invalid output: $response\n" . + "Falling back to scanning...\n" unless $response =~ /^\{/; + + my $json_pkg; + eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; + } or do { + require JSON::PP; + $json_pkg = "JSON::PP"; + }; + + my $o = $json_pkg->new->utf8->decode($response); + + if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) { + print STDERR "Adding '$git_work_tree' to watchman's watch list.\n"; + $retry--; + qx/watchman watch "$git_work_tree"/; + die "Failed to make watchman watch '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + + # Watchman will always return all files on the first query so + # return the fast "everything is dirty" flag to git and do the + # Watchman query just to get it over with now so we won't pay + # the cost in git to look up each individual file. + print "/\0"; + eval { launch_watchman() }; + exit 0; + } + + die "Watchman: $o->{error}.\n" . + "Falling back to scanning...\n" if $o->{error}; + + binmode STDOUT, ":utf8"; + local $, = "\0"; + print @{$o->{files}}; +} diff --git a/test/examples/lotr_migration.git/hooks/post-update.sample b/test/examples/lotr_migration.git/hooks/post-update.sample new file mode 100755 index 00000000..ec17ec19 --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/test/examples/lotr_migration.git/hooks/pre-applypatch.sample b/test/examples/lotr_migration.git/hooks/pre-applypatch.sample new file mode 100755 index 00000000..4142082b --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +precommit="$(git rev-parse --git-path hooks/pre-commit)" +test -x "$precommit" && exec "$precommit" ${1+"$@"} +: diff --git a/test/examples/lotr_migration.git/hooks/pre-commit.sample b/test/examples/lotr_migration.git/hooks/pre-commit.sample new file mode 100755 index 00000000..6a756416 --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/pre-commit.sample @@ -0,0 +1,49 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=$(git hash-object -t tree /dev/null) +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against -- diff --git a/test/examples/lotr_migration.git/hooks/pre-push.sample b/test/examples/lotr_migration.git/hooks/pre-push.sample new file mode 100755 index 00000000..6187dbf4 --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/pre-push.sample @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +z40=0000000000000000000000000000000000000000 + +while read local_ref local_sha remote_ref remote_sha +do + if [ "$local_sha" = $z40 ] + then + # Handle delete + : + else + if [ "$remote_sha" = $z40 ] + then + # New branch, examine all commits + range="$local_sha" + else + # Update to existing branch, examine new commits + range="$remote_sha..$local_sha" + fi + + # Check for WIP commit + commit=`git rev-list -n 1 --grep '^WIP' "$range"` + if [ -n "$commit" ] + then + echo >&2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/test/examples/lotr_migration.git/hooks/pre-rebase.sample b/test/examples/lotr_migration.git/hooks/pre-rebase.sample new file mode 100755 index 00000000..6cbef5c3 --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up to date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +<<\DOC_END + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". + +DOC_END diff --git a/test/examples/lotr_migration.git/hooks/pre-receive.sample b/test/examples/lotr_migration.git/hooks/pre-receive.sample new file mode 100755 index 00000000..a1fd29ec --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/pre-receive.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to make use of push options. +# The example simply echoes all push options that start with 'echoback=' +# and rejects all pushes when the "reject" push option is used. +# +# To enable this hook, rename this file to "pre-receive". + +if test -n "$GIT_PUSH_OPTION_COUNT" +then + i=0 + while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" + do + eval "value=\$GIT_PUSH_OPTION_$i" + case "$value" in + echoback=*) + echo "echo from the pre-receive-hook: ${value#*=}" >&2 + ;; + reject) + exit 1 + esac + i=$((i + 1)) + done +fi diff --git a/test/examples/lotr_migration.git/hooks/prepare-commit-msg.sample b/test/examples/lotr_migration.git/hooks/prepare-commit-msg.sample new file mode 100755 index 00000000..10fa14c5 --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/prepare-commit-msg.sample @@ -0,0 +1,42 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first one removes the +# "# Please enter the commit message..." help message. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +COMMIT_MSG_FILE=$1 +COMMIT_SOURCE=$2 +SHA1=$3 + +/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" + +# case "$COMMIT_SOURCE,$SHA1" in +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; +# *) ;; +# esac + +# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" +# if test -z "$COMMIT_SOURCE" +# then +# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" +# fi diff --git a/test/examples/lotr_migration.git/hooks/update.sample b/test/examples/lotr_migration.git/hooks/update.sample new file mode 100755 index 00000000..80ba9413 --- /dev/null +++ b/test/examples/lotr_migration.git/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to block unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/test/examples/lotr_migration.git/info/exclude b/test/examples/lotr_migration.git/info/exclude new file mode 100644 index 00000000..a5196d1b --- /dev/null +++ b/test/examples/lotr_migration.git/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/test/examples/lotr_migration.git/objects/pack/pack-a2d9bfbc1e908b433669f482566a9377bd8c58cf.idx b/test/examples/lotr_migration.git/objects/pack/pack-a2d9bfbc1e908b433669f482566a9377bd8c58cf.idx new file mode 100644 index 0000000000000000000000000000000000000000..fd00e0d7c8198bea76c2c57b946f9e0655a78aa3 GIT binary patch literal 1968 zcmexg;-AdGz`z8=j6`CPS{P;qvhiUSpggTGE6{8ZW+Q;vf%4>HE}&j+A~6q89knno z&@5sxA5bj}^8?ue1h61b9)^X0Y+7Jppt&$C0%W6OQJ@&HSPZBZ8H)q?AS{6eO9I6P zBbEZXi`rNkXeJ>n15|^I<$!#7ELefzQ}x=WIg1o@Zn;1Q#9*x^F-Ig6#@Fk`J-ez>O9u!t$&ylU+1+ff(PJBl*MJ{frYIwz(FX%$l@Hf=`**^~n>T+0SoY`I+Oovw5G{b{qDNJ9GAI&)MDZ zr+P!U>u1S#LZ7wCQ@rha5|eiz;XSTVvJJo#(OoPgq!JN$Wfgxu<>W zQe!>N7Zy4zA7oLU;;{5s)<&)Lx9lry9Q=zcA5D_cOfn1Q-1KcK@3Iwn`3w8ri|H+{7Fo4T{>9v|ABjF(je)mJep=pn`zvG3E5~_S%dD0!VBX;E zUv!?I;nJ^J8&~?dohaJd@4xPgM}<94L2KwAuItQR+jtCJgZ$6_kGrUFFk{26{-Pk z!hWtolkIzZS54Tx$o2HXyxTW;r$3k`-M{+P=PPUi%8~k|oID+q9;w|h^*r-(OW>PM zU{$h(fkD3>Se-aBFqljSR%@{g48}Vd7z|^8xCbcK4OFv`fq@HD-vP^cW?(*I)dBK> zy!POn~)|@d}{6%|P}I1_o13pq>XnGkk&S y&H=^Z7Tw&xM{Yv5vsvbsrm(EZ<$HS~&NmmWmyFmYS*QQxvSHzh-oHK7>-qs)EU+m6 literal 0 HcmV?d00001 diff --git a/test/examples/lotr_migration.git/objects/pack/pack-a2d9bfbc1e908b433669f482566a9377bd8c58cf.pack b/test/examples/lotr_migration.git/objects/pack/pack-a2d9bfbc1e908b433669f482566a9377bd8c58cf.pack new file mode 100644 index 0000000000000000000000000000000000000000..6ce7a97901fb33c922a2dbf3bd2dbd6af2f31600 GIT binary patch literal 13683 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 5.x tag migrator' do + include Rack::Test::Methods + + setup do + @path = cloned_testpath("examples/lotr_migration.git") + end + + test 'repair broken links' do + PREFER_RELATIVE = true + RUN_SILENT = true + NO_DRY_RUN = true + script_path = File.expand_path(File.join(File.dirname(__FILE__), '../', 'bin', 'gollum-migrate-tags')) + Dir.chdir(@path) do + load script_path + end + f = File.new(::File.join(@path, 'Subdir/Foo.md'), 'r') + assert_equal result, f.read + end + + teardown do + FileUtils.rm_rf(@path) + end + end + +end \ No newline at end of file