Class: Amp::Repositories::BundleRepository
- Inherits:
-
LocalRepository
- Object
- Repository
- LocalRepository
- Amp::Repositories::BundleRepository
- Defined in:
- lib/amp/repository/repositories/bundle_repository.rb
Overview
BundleRepository
This class represents a read-only repository that combines both local repository data with a bundle file. The bundle file contains un-merged-in changesets - this is useful for, say, previewing the results of a pull action.
A bundle is stored in the following manner:
- Changelog entries
- Manifest entries
- Modified File entry #1
- Modified File entry #2
- ...
- Modified file entry #N
Constant Summary
Constants included from Amp::RevlogSupport::Node
Amp::RevlogSupport::Node::NULL_ID, Amp::RevlogSupport::Node::NULL_REV
Constants included from TagManager
TagManager::TAG_FORBIDDEN_LETTERS
Instance Attribute Summary
Attributes inherited from LocalRepository
#branch_manager, #config, #hg, #hg_opener, #root, #root_pathname, #store, #store_opener
Instance Method Summary collapse
-
#can_copy? ⇒ Boolean
We can’t copy files.
-
#changelog ⇒ BundleChangeLog
Gets the changelog of the repository.
-
#close ⇒ Object
Closes the repository - in this case, it closes the bundle_file.
-
#file(filename) ⇒ FileLog
Gets the file-log for the given path, so we can look at an individual file’s history, for example.
-
#file_start ⇒ Integer
Returns the position in the bundle file where the file log changesets are located.
-
#get_cwd ⇒ Object
Gets the current working directory.
-
#initialize(path = "", config = nil, bundle_name = "") ⇒ BundleRepository
constructor
A new instance of BundleRepository.
-
#manifest ⇒ BundleChangeLog
Gets the manifest of the repository.
-
#manifest_start ⇒ Integer
Returns the position in the bundle file where the manifest changesets are located.
-
#url ⇒ String
Gets the URL for this repository - unused, I believe.
Methods inherited from LocalRepository
#[], #add, #add_changegroup, #add_file, #annotate, #between, #branches, #changed?, #changegroup, #changegroup_info, #changegroup_subset, #clone, #commit, #commit_changeset, #commit_file, #common_nodes, #copy, #cwd, #dirstate, #dirstate_copy, #each, #find_incoming_roots, #find_outgoing_roots, #forget, #get_changegroup, #heads, #init, #inspect, #invalidate!, #join, #living_parents, #local?, #lock_store, #lock_working, #lock_working_and_store, #lookup, #make_lock, #merge_state, #open, #parents, #path_to, #pristine?, #pull, #push, #push_add_changegroup, #push_unbundle, #relative_join, #remove, #revert, #run_hook, #size, #status, #store_join, #stream_in, #undelete, #versioned_file, #walk, #working_file, #working_join, #working_read, #working_write
Methods included from Verification
Methods included from Updatable
Methods included from Amp::RevlogSupport::Node
Methods included from TagManager
#apply_tag, #invalidate_tag_cache!, #tag_list, #tag_type, #tags, #tags_for_node
Methods included from BranchManager
#branch_heads, #branch_tags, #save_branch_cache, #save_branches
Methods inherited from Repository
#add_path, #capable?, #get_capabilities, #local?, #require_capability
Constructor Details
#initialize(path = "", config = nil, bundle_name = "") ⇒ BundleRepository
Returns a new instance of BundleRepository.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/amp/repository/repositories/bundle_repository.rb', line 19 def initialize(path="", config=nil, bundle_name="") @temp_parent = nil # Figure out what to do here - if there's no current local repository, that # takes some special work. begin super(path, false, config) # don't create, just look for a repository rescue # Ok, no local repository. Let's make one really quickly. @temp_parent = File.join(Dir.tmpdir, File.amp_make_tmpname("bundlerepo")) File.mkdir(@temp_parent) tmprepo = LocalRepository.new(@temp_parent, true, config) # true -> create super(@temp_parent, false, config) # and proceed as scheduled! end # Set up our URL variable, if anyone asks us what it is if path @url = "bundle:#{path}+#{bundle_name}" else @url = "bundle:#{bundle_name}" end @temp_file = nil @bundle_file = File.open(bundle_name, "r") @bundle_file.seek(0, IO::SEEK_END) Amp::UI.debug "Bundle File Size: #{@bundle_file.tell}" @bundle_file.seek(0, IO::SEEK_SET) # OK, now for the fun part - check the header to see if we're compressed. header = @bundle_file.read(6) # And switch based on that header if !header.start_with?("HG") # Not even an HG file. FML. Bail raise abort("#{bundle_name}: not a Mercurial bundle file") elsif not header.start_with?("HG10") # Not a version we understand, bail raise abort("#{bundle_name}: unknown bundle version") elsif header == "HG10BZ" || header == "HG10GZ" # Compressed! We'll have to save to a new file, because this could get messy. temp_file = Tempfile.new("hg-bundle-hg10un", @root) @temp_file_path = temp_file.path # Are we BZip, or GZip? case header when "HG10BZ" # fuck BZip. Seriously. headerio = StringIO.new "BZ", (ruby_19? ? "w+:ASCII-8BIT" : "w+") input = Amp::Support::MultiIO.new(headerio, @bundle_file) decomp = BZ2::Reader.new(input) when "HG10GZ" # Gzip is much nicer. decomp = Zlib::GzipReader.new(@bundle_file) end # We're writing this in an uncompressed fashion, of course. @temp_file.write("HG10UN") # While we can uncompressed.... while !r.eof? do # Write the uncompressed data to our new file! @temp_file.write decomp.read(4096) end # and close 'er up @temp_file.close # Close the compressed bundle file @bundle_file.close # And re-open the uncompressed bundle file! @bundle_file = File.open(@temp_file_path, "r") # Skip the header. @bundle_file.seek(6) elsif header == "HG10UN" # uncompressed, do nothing else # We have no idae what's going on raise abort("#{bundle_name}: unknown bundle compression type") end # This hash stores pairs of {filename => position_in_bundle_file_of_this_file} @bundle_files_positions = {} end |
Instance Method Details
#can_copy? ⇒ Boolean
We can’t copy files. Read-only.
208 |
# File 'lib/amp/repository/repositories/bundle_repository.rb', line 208 def can_copy?; false; end |
#changelog ⇒ BundleChangeLog
Gets the changelog of the repository. This is different from LocalRepository#changelog in that it uses a BundleChangeLog. Also, since the manifest is stored in the bundle directly after the changelog, by checking our position in the bundle file, we can save where the bundle_file is stored.
105 106 107 108 109 |
# File 'lib/amp/repository/repositories/bundle_repository.rb', line 105 def changelog @changelog ||= Bundles::BundleChangeLog.new(@store.opener, @bundle_file) @manifest_start ||= @bundle_file.tell @changelog end |
#close ⇒ Object
Closes the repository - in this case, it closes the bundle_file. Analogous to closing an SSHRepository’s socket.
203 204 205 |
# File 'lib/amp/repository/repositories/bundle_repository.rb', line 203 def close @bundle_file.close end |
#file(filename) ⇒ FileLog
Gets the file-log for the given path, so we can look at an individual file’s history, for example. However, we need to be cognizant of files that traverse the local repository’s history as well as the bundle file.
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/amp/repository/repositories/bundle_repository.rb', line 159 def file(filename) # Load the file-log positions now - we didn't do this in the constructor for a reason # (if they don't ask for them, don't load them!) if @bundle_files_positions.empty? # Jump to the file position @bundle_file.seek file_start while true # get a changegroup chunk - it'll be the filename chunk = RevlogSupport::ChangeGroup.get_chunk @bundle_file # no filename? bail break if chunk.nil? || chunk.empty? # Now that we've read the filename, we're at the start of the changelogs for that # file. So let's save this position for later. @bundle_files_positions[chunk] = @bundle_file.tell # Then read chunks until we get to the next file! RevlogSupport::ChangeGroup.each_chunk(@bundle_file) {|c|} end end # Remove leading slash filename = filename.shift("/") # Does this file cross local history as well as the bundle? if @bundle_files_positions[filename] # If so, we'll need to make a BundleFileLog. Meh. @bundle_file.seek @bundle_files_positions[filename] Bundles::BundleFileLog.new @store.opener, filename, @bundle_file, proc {|n| changelog.rev(n) } else # Nope? Make a normal FileLog! FileLog.new(@store.opener, filename) end end |
#file_start ⇒ Integer
Returns the position in the bundle file where the file log changesets are located. This involves loading the changelog and the manifest first - see #manifest.
148 149 150 |
# File 'lib/amp/repository/repositories/bundle_repository.rb', line 148 def file_start manifest && @file_start end |
#get_cwd ⇒ Object
Gets the current working directory. Not sure why we need this.
210 |
# File 'lib/amp/repository/repositories/bundle_repository.rb', line 210 def get_cwd; Dir.pwd; end |
#manifest ⇒ BundleChangeLog
Gets the manifest of the repository. This is different from LocalRepository#manifest in that it uses a BundleManifest. The file logs are stored in the bundle directly after the manifest, so once we load the manifest, we save where the file logs start when we are done loading the manifest.
This has the side-effect of loading the changelog, if it hasn’t been loaded already -# this is necessary because the manifest changesets are stored after the changelog changesets, and we must fully load the changelog changesets to know where to look for the manifest changesets.
Don’t look at me, I didn’t design the file format.
124 125 126 127 128 129 130 |
# File 'lib/amp/repository/repositories/bundle_repository.rb', line 124 def manifest return @manifest if @manifest @bundle_file.seek manifest_start @manifest ||= Bundles::BundleManifest.new @store.opener, @bundle_file, proc {|n| changelog.rev(n) } @file_start ||= @bundle_file.tell @manifest end |
#manifest_start ⇒ Integer
Returns the position in the bundle file where the manifest changesets are located. This involves loading the changelog first - see #manifest
138 139 140 |
# File 'lib/amp/repository/repositories/bundle_repository.rb', line 138 def manifest_start changelog && @manifest_start end |
#url ⇒ String
Gets the URL for this repository - unused, I believe.
198 |
# File 'lib/amp/repository/repositories/bundle_repository.rb', line 198 def url; @url; end |