diff -r 86c9590fb015 app/controllers/wiki_controller.rb --- a/app/controllers/wiki_controller.rb Sun Jun 28 14:12:27 2009 +0200 +++ b/app/controllers/wiki_controller.rb Tue Jun 30 20:21:43 2009 +0200 @@ -89,7 +89,13 @@ end rescue ActiveRecord::StaleObjectError # Optimistic locking exception - flash[:error] = l(:notice_locking_conflict) + # Try to merge + if @content.merge + flash[:notice] = l(:notice_merged) + redirect_to :action => 'index', :id => @project, :page => @page.title + else + flash[:error] = l(:notice_locking_conflict) + end end # rename a page diff -r 86c9590fb015 app/models/wiki_content.rb --- a/app/models/wiki_content.rb Sun Jun 28 14:12:27 2009 +0200 +++ b/app/models/wiki_content.rb Tue Jun 30 20:21:43 2009 +0200 @@ -92,4 +92,24 @@ :conditions => ["wiki_content_id = ? AND version < ?", wiki_content_id, version]) end end + + def merge + transaction do + parent_version = versions.find_by_version(version) + last_version = versions.last + begin + conflict, merged_text = Merger.merge(text, parent_version.text, last_version.text, + l(:label_merge_my), + l(:label_merge_old), + l(:label_merge_other, + :author_name => last_version.author.name)) + self.text = merged_text + self.version = last_version.version + return (!conflict and save) + rescue Merger::DiffBinaryNotAvailable + return false + end + end + end + end diff -r 86c9590fb015 config/locales/en.yml --- a/config/locales/en.yml Sun Jun 28 14:12:27 2009 +0200 +++ b/config/locales/en.yml Tue Jun 30 20:21:43 2009 +0200 @@ -121,6 +121,7 @@ notice_successful_connection: Successful connection. notice_file_not_found: The page you were trying to access doesn't exist or has been removed. notice_locking_conflict: Data has been updated by another user. + notice_merged: "Another user edited this page in the meantime. An automatic merge occured." notice_not_authorized: You are not authorized to access this page. notice_email_sent: "An email was sent to {{value}}" notice_email_error: "An error occurred while sending mail ({{value}})" @@ -678,6 +679,9 @@ label_date_from_to: From {{start}} to {{end}} label_wiki_content_added: Wiki page added label_wiki_content_updated: Wiki page updated + label_merge_my: "My version" + label_merge_old: "Previous version" + label_merge_other: "{{author_name}}'s version" button_login: Login button_submit: Submit diff -r 86c9590fb015 config/locales/fr.yml --- a/config/locales/fr.yml Sun Jun 28 14:12:27 2009 +0200 +++ b/config/locales/fr.yml Tue Jun 30 20:21:43 2009 +0200 @@ -153,6 +153,7 @@ notice_successful_connection: Connection réussie. notice_file_not_found: "La page à laquelle vous souhaitez accéder n'existe pas ou a été supprimée." notice_locking_conflict: Les données ont été mises à jour par un autre utilisateur. Mise à jour impossible. + notice_merged: "Un autre utilisateur a modifié la page en même temps que vous. Un assemblage automatique a donc eu lieu." notice_not_authorized: "Vous n'êtes pas autorisés à accéder à cette page." notice_email_sent: "Un email a été envoyé à {{value}}" notice_email_error: "Erreur lors de l'envoi de l'email ({{value}})" @@ -708,6 +709,9 @@ label_date_from_to: Du {{start}} au {{end}} label_wiki_content_added: Page wiki ajoutée label_wiki_content_updated: Page wiki mise à jour + label_merge_my: "Ma version" + label_merge_old: "Version précédente" + label_merge_other: "Version de {{author_name}}" button_login: Connexion button_submit: Soumettre diff -r 86c9590fb015 lib/merger.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/merger.rb Tue Jun 30 20:21:43 2009 +0200 @@ -0,0 +1,63 @@ +module Merger + + class DiffBinaryNotAvailable < Exception + end + + + class << self + private + + def shell_quote(str) + if RUBY_PLATFORM =~ /mswin/ + '"' + str.gsub(/"/, '\\"') + '"' + else + "'" + str.gsub(/'/, "'\"'\"'") + "'" + end + end + + def diff3_command(*opts) + command = format("diff3 %s", + opts.join(" ")) + output = nil + IO.popen(command) do |io| + output = io.read + end + raise "diff3 exited with an error" if $?.exitstatus > 1 + return output + end + + def tempfile(content, &block) + Tempfile.open("redmine_wiki_merge") do |t_file| + t_file << content + t_file.open + yield t_file + end + end + end + + begin + diff3_command('-v').blank? + def self.merge(mine, old, other, mine_label, old_label, other_label) + tempfile(mine) do |my_file| + tempfile(old) do |old_file| + tempfile(other) do |other_file| + conflict = diff3_command("-a", "-x", shell_quote(my_file.path), + shell_quote(old_file.path), + shell_quote(other_file.path)) + return !conflict.blank?, diff3_command("-a", "-m", "-E", + "-L #{shell_quote(mine_label)}", + "-L #{shell_quote(old_label)}", + "-L #{shell_quote(other_label)}", + shell_quote(my_file.path), + shell_quote(old_file.path), + shell_quote(other_file.path)) + end + end + end + end + rescue + def self.merge(*args) + raise "diff3 binary not found." + end + end +end diff -r 86c9590fb015 test/unit/merger_test.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/unit/merger_test.rb Tue Jun 30 20:21:43 2009 +0200 @@ -0,0 +1,10 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class MergerTest < Test::Unit::TestCase + + def test_simple_merge + assert_equal [false, "aa\n\nbb\n"], Merger.merge("a\n\nbb\n", "a\n\nb\n", "aa\n\nb\n", + "my", "old", "yours") + end + +end