Class: SvnTransform
- Inherits:
-
Object
- Object
- SvnTransform
- Defined in:
- lib/svn-transform.rb,
lib/svn-transform/dir.rb,
lib/svn-transform/file.rb,
lib/svn-transform/session.rb,
lib/svn-transform/transform/noop.rb,
lib/svn-transform/transform/newline.rb,
lib/svn-transform/transform/extension.rb,
lib/svn-transform/transform/props_to_yaml.rb
Defined Under Namespace
Modules: Transform Classes: Dir, File, Session
Constant Summary collapse
- VERSION =
'0.2.0'
- PRINT_INFO =
false
- COMPARE_DIR =
'/tmp/svn-transform/compare'
- COMPARE_OLD_DIR =
File.join(COMPARE_DIR, 'old')
- COMPARE_NEW_DIR =
File.join(COMPARE_DIR, 'new')
Class Method Summary collapse
-
.co_compare(old_repo, new_repo, min_rev, max_rev) ⇒ Object
Compare checkouts.
-
.co_compare_rev(rev) ⇒ Object
Called by .co_compare, this checks out and compares the repositories at a signle revision.
-
.compare(old_dir, new_dir) ⇒ Object
Use diff to compare two repositories (on local file system) Where reasonable, this (or something like it) should be run to verify expected results.
Instance Method Summary collapse
-
#changesets ⇒ Object
Process the existing changesets and generate a SvnFixture::Revision for each.
-
#convert ⇒ Object
Run the conversion.
-
#dir_transform(klass = nil, *args, &block) ⇒ Object
Add a transform to be run on directories.
-
#file_transform(klass = nil, *args, &block) ⇒ Object
Add a transform to be run on files.
-
#initialize(in_repo_uri, out_repo_name = nil, options = {}) ⇒ SvnTransform
constructor
Setup and SvnTransform with in (existing) repository URI, a name for the out (transformed) repository, and options.
Constructor Details
#initialize(in_repo_uri, out_repo_name = nil, options = {}) ⇒ SvnTransform
Setup and SvnTransform with in (existing) repository URI, a name for the out (transformed) repository, and options.
Parameters
- in_repo_uri<String>
-
URI of existing repository(e.g. file:///home/jm81/repo, svn://localhost/repo)
- out_repo_name<String>
-
Name only of out repository (e.g. “out”). See options for specifying full path (on local filesystem only)
Options
- :username<String>
-
Username for in (existing) repository
- :password<String>
-
Password for in (existing) repository
- :out_repos_path<String>
-
Full path for out repository (defaults to “#:base_path/repo_#out_repo_name”)
- :out_wc_path<String>
-
Full path for out working copy (used by SvnFixture; defaults to “#:base_path/wc_#out_repo_name”)
122 123 124 125 126 127 128 129 130 131 |
# File 'lib/svn-transform.rb', line 122 def initialize(in_repo_uri, out_repo_name = nil, = {}) @in_username = [:username] @in_password = [:password] @in_repo_uri = in_repo_uri @out_repo_name = out_repo_name @out_repos_path = [:out_repos_path] @out_wc_path = [:out_wc_path] @file_transforms = [] @dir_transforms = [] end |
Class Method Details
.co_compare(old_repo, new_repo, min_rev, max_rev) ⇒ Object
Compare checkouts. This takes much longer than .compare but can give a more accurate picture, because it is not affected by the changes in svn:entry properties (well, it actually just ignores the related files), the chance that entries are just in different order within the db file, or differences between the directory structure of the repo’s db folders.
It also has the advantage that the repositories can be remote.
Note that this method will destroy existing files in the COMPARE_DIR
Parameters
- old_repo<String>
-
URI of original (in) repository.
- new_repo<String>
-
URI of generated (out) repository.
- min_rev<Integer>
-
Starting revision for comparison
- max_rev<Integer>
-
Ending revision for comparison
64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/svn-transform.rb', line 64 def co_compare(old_repo, new_repo, min_rev, max_rev) FileUtils.rm_rf(COMPARE_OLD_DIR) FileUtils.rm_rf(COMPARE_NEW_DIR) rev = min_rev `svn co -r#{rev} "#{old_repo}" "#{COMPARE_OLD_DIR}"` `svn co -r#{rev} "#{new_repo}" "#{COMPARE_NEW_DIR}"` while rev <= max_rev co_compare_rev(rev) rev += 1 end end |
.co_compare_rev(rev) ⇒ Object
Called by .co_compare, this checks out and compares the repositories at a signle revision. It prints out any differences.
Parameters
- rev<Integer>
-
The revision to compare.
Returns
- True, False
-
Whether the revisions are identical
86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/svn-transform.rb', line 86 def co_compare_rev(rev) print "#{rev} " if PRINT_INFO `svn update -r#{rev} "#{COMPARE_OLD_DIR}"` `svn update -r#{rev} "#{COMPARE_NEW_DIR}"` ret = `diff --brief --exclude=entries -r "#{COMPARE_OLD_DIR}" "#{COMPARE_NEW_DIR}"` if ret.empty? return true else puts "\nREVISION #{rev}" puts ret puts ("-" * 70) return false end end |
.compare(old_dir, new_dir) ⇒ Object
Use diff to compare two repositories (on local file system) Where reasonable, this (or something like it) should be run to verify expected results. I recommend trying a direct copy first to ensure your original repo doesn’t have any featurs that SvnPropsToYaml won’t understand.
www.coderetard.com/2009/02/17/compare-directories-and-file-content-in-linux-without-dircmp/
Gotchas
svn:entry fields aren’t directly copied, but seem to match. repo uuid is different, but not relevant, so that file is ignored. other differences may also exist if the in_repo is an older format.
Parameters
- old_dir<String>
-
FS Path to original (in) repository.
- new_dir<String>
-
FS Path to generated (out) repository.
Note that these are filesystem paths, not Subversion URI’s
Returns
- True, False
-
Whether the directories are the same (except db/uuid file) If False, puts the result of running the diff command.
35 36 37 38 39 40 41 42 43 |
# File 'lib/svn-transform.rb', line 35 def compare(old_dir, new_dir) ret = `diff --brief --exclude=uuid -r "#{old_dir}" "#{new_dir}"` if ret.empty? return true else puts ret return false end end |
Instance Method Details
#changesets ⇒ Object
Process the existing changesets and generate a SvnFixture::Revision for each.
TODO This is a massive mess. It works, at least for my purposes. But it is a mess. Ideally, it should be multiple methods. Part of this is due to how I set up the SvnFixture::Revision class, which accepts a block at initialize that is process only when its #commit method is called.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/svn-transform.rb', line 202 def changesets args = ['', 1, @in_repo.latest_revnum, 0, true, nil] path_renames = {} @in_repo.log(*args) do |changes, rev_num, , date, msg| print "#{rev_num} " if PRINT_INFO # Sort so that files are processed first (for benefit of PropsToYaml), # and deletes are last changes = changes.sort { |a,b| sort_for(a, rev_num) <=> sort_for(b, rev_num) } # Get revision properties rev_props = @ctx.revprop_list(@in_repo_uri, rev_num)[0] # Create Revision, including all revprops. Note that svn:author and # svn:date are revprops. SvnFixture::Revision allows these without the # svn: prefix (as Symbol), but revprops are written last, and so this # should be completely accurate. in_repo = @in_repo out_wc_path = @out_repo.wc_path svn_transform = self @out_repo.revision(rev_num, msg, rev_props) do print "#{rev_num} " if SvnTransform::PRINT_INFO # Now go through all the changes. Setup directorie structure for each # node. This is easier to understand, in my opinion. changes.each do |full_path, change| full_path = Pathname.new(full_path.sub(/\A\//, '')) # Descend to parent directory parent_dir = self full_path.dirname.descend do |path| unless path.basename == '.' parent_dir = parent_dir.dir(path.basename.to_s) end end # TODO Replaces copy_from_path = nil if change.copyfrom_path short_from_path = path_renames[change.copyfrom_path] || change.copyfrom_path copy_from_path = ::File.join(out_wc_path, short_from_path) end if change.action == 'D' del_path = path_renames['/' + full_path.to_s] || full_path.to_s @ctx.delete(::File.join(out_wc_path, del_path)) elsif in_repo.stat(full_path.to_s, rev_num).file? data = in_repo.file(full_path.to_s, rev_num) transform_file = ::SvnTransform::File.new(full_path, data, rev_num, rev_props) original_path = transform_file.path svn_transform.__send__(:process_file_transforms, transform_file) @ctx.cp(copy_from_path, ::File.join(out_wc_path, transform_file.path.to_s)) if copy_from_path unless transform_file.skip? parent_dir.file(transform_file.basename) do body(transform_file.body) props(transform_file.properties) end # For benefit of copies if original_path != transform_file.path path_renames['/' + original_path] = '/' + transform_file.path.to_s end end else # directory # Paths don't change for directories, but use this for consistency @ctx.cp(copy_from_path, ::File.join(out_wc_path, full_path.to_s)) if copy_from_path parent_dir.dir(full_path.basename.to_s) do data = in_repo.dir(full_path.to_s, rev_num) transform_dir = ::SvnTransform::Dir.new(full_path, data, rev_num, rev_props, in_repo, self) svn_transform.__send__(:process_dir_transforms, transform_dir) props(transform_dir.properties) end end end end end end |
#convert ⇒ Object
Run the conversion. This method sets up the connection to the existing repo and the SvnFixture that will generate the final transformed repo, then calls changesets
to do the actual work. Finally, commit the SvnFixture (out repo) and update its rev 0 date to match the in repo
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/svn-transform.rb', line 178 def convert in_repo_session = Session.new(@in_repo_uri, @in_username, @in_password) @in_repo = in_repo_session.session @ctx = in_repo_session.context @out_repo = SvnFixture.repo(@out_repo_name, @out_repos_path, @out_wc_path) # Process changesets and commit puts "\nReading existing log..." if PRINT_INFO changesets puts "\nCommitting to new..." if PRINT_INFO @out_repo.commit # Update rev 0 date r0_date = @ctx.revprop_list(@in_repo_uri, 0)[0]['svn:date'] @out_repo.repos.fs.set_prop('svn:date', SvnFixture.svn_time(r0_date), 0) end |
#dir_transform(klass = nil, *args, &block) ⇒ Object
Add a transform to be run on directories. See file_transform
164 165 166 167 168 169 170 171 172 |
# File 'lib/svn-transform.rb', line 164 def dir_transform(klass = nil, *args, &block) if klass @dir_transforms << [klass, args] elsif block_given? @dir_transforms << block else raise(ArgumentError, "Class or Block required") end end |
#file_transform(klass = nil, *args, &block) ⇒ Object
Add a transform to be run on files. This can either be a class or a block (see Parameters). Each file at revision is given as an SvnTransform::File to each transform, which can alter the basename, body and/or properties of the file prior to its being committed to the new Repository.
Parameters
- klass<Class>
-
A class whose #initialize method accepts a SvnTransform::File as the first argument and which responds to #run.
- args<Array>
-
Additional arguments to pass to klass#initialize
- block<Proc>
-
A block that accepts one argument (a SvnTransform::File). If a klass is also given, the block is ignored
Returns
- Array
-
The current @file_transforms Array
Raises
- ArgumentError
-
Neither a Class nor a block was given.
153 154 155 156 157 158 159 160 161 |
# File 'lib/svn-transform.rb', line 153 def file_transform(klass = nil, *args, &block) if klass @file_transforms << [klass, args] elsif block_given? @file_transforms << block else raise(ArgumentError, "Class or Block required") end end |