* subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
[svn.git] / subversion / bindings / swig / ruby / svn / synchronizer.rb
blob5a53a1a43c002418fce694c708b4f1cd3184a57f
1 # Experimental
3 require "cgi"
4 require "svn/ra"
6 module Svn
7   class Synchronizer
8     class Error < StandardError
9     end
11     class NotInitialized < Error
12       attr_reader :destination_url
13       def initialize(destination_url)
14         @destination_url = destination_url
15         super("#{@destination_url} is not initialized yet.")
16       end
17     end
19     class ConsistencyError < Error
20       attr_reader :destination_url, :destination_latest_revision, :next_revision
21       def initialize(destination_url, destination_latest_revision, next_revision)
22         @destination_url = destination_url
23         @destination_latest_revision = destination_latest_revision
24         @next_revision = next_revision
25         super("the latest revision (#{@destination_latest_revision}) of " +
26               "the destination repository (#{@destination_url}) " +
27               "should be #{@next_revision - 1}.")
28       end
29     end
31     module Property
32       Svn::Ext::Core.constants.each do |name|
33         case name.to_s
34         when /\ASVNSYNC_PROP_/
35           const_set($POSTMATCH, Svn::Ext::Core.const_get(name))
36         end
37       end
38     end
40     def initialize(destination_url, callbacks=nil)
41       @destination = Ra::Session.open(destination_url, nil, callbacks)
42       if @destination.repos_root != destination_url
43         raise ArgumentError,
44               "URL '#{destination_url}' is not repository root " +
45               "(#{@destination.repos_root})."
46       end
47     end
49     def source=(source)
50       @destination[Property::FROM_URL, 0] = source.repos_root
51       @destination[Property::FROM_UUID, 0] = source.uuid
52       @destination[Property::LAST_MERGED_REV, 0] = "0"
54       sync_property_re = /\A#{Property::PREFIX}/
55       source.properties(0).each do |key, value|
56         next if sync_property_re =~ key
57         @destination[key, 0] = value
58       end
59     end
61     def sync(callbacks=nil)
62       last_merged_rev = @destination[Property::LAST_MERGED_REV, 0]
63       raise NotInitialized.new(@destination.repos_root) if last_merged_rev.nil?
65       last_merged_rev = Integer(last_merged_rev)
66       source = Ra::Session.open(@destination[Property::FROM_URL, 0], nil,
67                                 callbacks)
68       (last_merged_rev + 1).upto(source.latest_revision) do |revision|
69         unless @destination.latest_revision + 1 == revision
70           raise ConsistencyError.new(@destination.repos_root,
71                                      @destination.latest_revision,
72                                      revision)
73         end
74         editor = sync_editor(source, revision)
75         source.replay(revision, 0, editor)
76         editor.close_edit
77         sync_svn_revision_properties(source, revision)
78         @destination[Property::LAST_MERGED_REV, 0] = revision.to_s
79       end
80     end
82     private
83     def sync_editor(source, base_revision)
84       properties = source.properties(base_revision)
85       commit_editor = @destination.commit_editor2(properties)
86       Editor.new(commit_editor, @destination.repos_root, base_revision)
87     end
89     def sync_svn_revision_properties(source, revision)
90       [Core::PROP_REVISION_AUTHOR, Core::PROP_REVISION_DATE].each do |key|
91         @destination[key, revision] = source[key, revision]
92       end
93     end
95     class Editor < Delta::WrapEditor
96       def initialize(wrapped_editor, dest_url, base_revision)
97         super(wrapped_editor)
98         @dest_url = dest_url
99         @base_revision = base_revision
100         @opened_root = false
101       end
103       def open_root(base_revision)
104         @opened_root = true
105         super
106       end
108       def add_directory(path, parent_baton, copy_from_path, copy_from_rev)
109         if copy_from_path
110           copy_from_path = "#{@dest_url}#{CGI.escape(copy_from_path)}"
111         end
112         super(path, parent_baton, copy_from_path, copy_from_rev)
113       end
115       def add_file(path, parent_baton, copy_from_path, copy_from_rev)
116         if copy_from_path
117           copy_from_path = "#{@dest_url}#{CGI.escape(copy_from_path)}"
118         end
119         super(path, parent_baton, copy_from_path, copy_from_rev)
120       end
122       def close_edit
123         unless @opened_root
124           baton = @wrapped_editor.open_root(@base_revision)
125           @wrapped_editor.close_directory(baton)
126         end
127         super
128       end
129     end
130   end