Class: Amp::Repositories::HTTPRepository

Inherits:
Repository show all
Includes:
Amp::RevlogSupport::Node
Defined in:
lib/amp/repository/repositories/http_repository.rb

Overview

This is the class for connecting to an HTTP-based repository.

The protocol’s pretty simple - just ?cmd=“command”, and any other args you need. Should be pretty easy.

Direct Known Subclasses

HTTPSRepository

Constant Summary collapse

DEFAULT_HEADERS =
{"User-agent" => "Amp-#{Amp::VERSION}",
"Accept" => "Application/Mercurial-0.1"}

Constants included from Amp::RevlogSupport::Node

Amp::RevlogSupport::Node::NULL_ID, Amp::RevlogSupport::Node::NULL_REV

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Amp::RevlogSupport::Node

#short

Methods inherited from Repository

#add_path, #can_copy?, #capable?, #require_capability

Constructor Details

#initialize(path = "", create = false, config = nil) ⇒ HTTPRepository

Standard initializer for a repository. However, “create” is a no-op.

Parameters:

  • path (String) (defaults to: "")

    the URL for the repository.

  • create (Boolean) (defaults to: false)

    this is useless since we can’t create remote repos

  • config (Amp::AmpConfig) (defaults to: nil)

    the configuration for Amp right now.

Raises:

  • (InvalidArgumentError)


48
49
50
51
52
# File 'lib/amp/repository/repositories/http_repository.rb', line 48

def initialize(path="", create=false, config=nil)
  @url, @config = URI.parse(path), config
  @auth_mode = :none
  raise InvalidArgumentError.new("Invalid URL for an HTTP repo!") if @url.nil?
end

Instance Attribute Details

#secureObject

Should the repository connect via SSL?



33
34
35
# File 'lib/amp/repository/repositories/http_repository.rb', line 33

def secure
  @secure
end

#urlObject (readonly)

The URL we connect to for this repository



29
30
31
# File 'lib/amp/repository/repositories/http_repository.rb', line 29

def url
  @url
end

Instance Method Details

#between(pairs) ⇒ Array<Array<String>>

For each provided pair of nodes, return the nodes between the pair.

add lstrip to split_newlines to fix but not cure bug

Parameters:

  • an (Array<Array<String>>)

    array of node pairs, so an array of an array of strings. The first node is the head, the second node is the root of the pair.

Returns:

  • (Array<Array<String>>)

    for each pair, we return 1 array, which contains the node IDs of every node between the pair.



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/amp/repository/repositories/http_repository.rb', line 228

def between(pairs)
  batch = 8
  ret   = []
  
  (0..(pairs.size)).step(batch) do |i|
    n = pairs[i..(i+batch-1)].map {|p| p.map {|k| k.hexlify }.join("-") }.join(" ")
    d, code = *do_read("between", :pairs => n)
    
    raise RepoError.new("unexpected code: #{code}") unless code == 200
    
    ret += d.lstrip.split_newlines.map {|l| (l && l.split(" ").map{|i| i.unhexlify }) || []}
  end
  Amp::UI.debug "between returns: #{ret.inspect}"
  ret
end

#branches(nodes) ⇒ Array<Array<String>>

Gets the node IDs of all the branch roots in the repository. Uses the supplied nodes to use to search for branches.

Parameters:

  • nodes (Array<String>)

    the nodes to use as heads to search for branches. The search starts at each supplied node (or the tip, if left empty), and goes to that tree’s root, and returns the relevant information for the branch.

Returns:

  • (Array<Array<String>>)

    An array of arrays of strings. Each array has 4 components: [head, root, parent1, parent2].



120
121
122
123
124
125
126
# File 'lib/amp/repository/repositories/http_repository.rb', line 120

def branches(nodes)
  n = nodes.map {|n| n.hexlify }.join(" ")
  data = do_read("branches", :nodes => n).first
  data.split("\n").map do |b|
    b.split(" ").map {|b| b.unhexlify}
  end
end

#changegroup(nodes, kind) ⇒ StringIO

TODO:

figure out what the kind parameter is for

Asks the server to bundle up the given nodes into a changegroup, and returns it uncompressed. This is for pulls.

Parameters:

  • nodes (Array<String>)

    the nodes to package into the changegroup

  • kind (NilClass)

    (UNUSED)

Returns:

  • (StringIO)

    the uncompressed changegroup as a stream



136
137
138
139
140
141
142
143
144
# File 'lib/amp/repository/repositories/http_repository.rb', line 136

def changegroup(nodes, kind)
  n = nodes.map{|i| i.hexlify }.join ' '
  f = do_read('changegroup', n.empty? ? {} : {:roots => n}).first

  s = StringIO.new "",(ruby_19? ? "w+:ASCII-8BIT" : "w+")
  s.write Zlib::Inflate.inflate(f)
  s.pos = 0
  s
end

#changegroup_subset(bases, heads, source) ⇒ StringIO

Asks the server to bundle up all the necessary nodes between the lists bases and heads. It is returned as a stream that reads it in a decompressed fashion. This is for pulls.

Parameters:

  • bases (Array<String>)

    the base nodes of the subset we’re requesting. Should be an array (or any Enumerable) of node ids.

  • heads (Array<String>)

    the heads of the subset we’re requesting. These nodes will be retrieved as well. Should be an array of node IDs.

  • source (NilClass)

    i have no idea (UNUSED)

Returns:

  • (StringIO)

    the uncompressed changegroup subset as a stream.



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/amp/repository/repositories/http_repository.rb', line 157

def changegroup_subset(bases, heads, source)
  #require_capability 'changegroupsubset', 'look up remote changes'
  base_list = bases.map {|n| n.hexlify }.join ' '
  head_list = heads.map {|n| n.hexlify }.join ' '
#        p base_list, head_list
  f, code = *do_read("changegroupsubset", :bases => base_list, :heads => head_list)
  
  s = StringIO.new "",(ruby_19? ? "w+:ASCII-8BIT" : "w+")
  s.write Zlib::Inflate.inflate(f)
  s.rewind
  s
end

#get_capabilitiesHash

Loads the capabilities from the server when necessary. (Lazy loading)

or
   { capability => "capability;settings;"}

Returns:

  • (Hash)

    the capabilities of the server, in the form: { capability => true }



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/amp/repository/repositories/http_repository.rb', line 61

def get_capabilities
  return @capabilities if @capabilities
  begin
    @capabilities = {}
    do_read("capabilities").first.split.each do |k| 
      if k.include? "="
        key, value = k.split("=", 2)
        @capabilities[key] = value
      else
        @capabilities[k] = true
      end
    end
  rescue
    @capabilities = []
  end
  @capabilities
end

#headsArray<String>

Gets all the heads of the repository. Returned in binary form.

Returns:

  • (Array<String>)

    the full, binary node_ids of all the heads on the remote server.



105
106
107
108
# File 'lib/amp/repository/repositories/http_repository.rb', line 105

def heads
  data = do_read("heads").first
  data.chomp.split(" ").map {|h| h.unhexlify }
end

#local?Boolean

Returns whether the repository is local or not. Which it isn’t. Because we’re connecting over HTTP.

Returns:

  • (Boolean)

    false. Because the repo isn’t local.



40
# File 'lib/amp/repository/repositories/http_repository.rb', line 40

def local?; false; end

#lockObject

Unsupported - raises an error.

Raises:



81
# File 'lib/amp/repository/repositories/http_repository.rb', line 81

def lock; raise RepoError.new("You can't lock an HTTP repo."); end

#lookup(key) ⇒ String

Looks up a node with the given key. The key could be a node ID (full or partial), an index number (though this is slightly risky as it might match a node ID partially), “tip”, and so on. See LocalRepository#[].

Parameters:

  • key (String)

    the key to look up - could be node ID, revision index, and so on.

Returns:

  • (String)

    the full node ID of the requested node on the remote server

Raises:



91
92
93
94
95
96
97
98
# File 'lib/amp/repository/repositories/http_repository.rb', line 91

def lookup(key)
  require_capability("lookup", "Look up Remote Revision")
  data = do_read("lookup", :key => key).first
  code, data = data.chomp.split(" ", 2)
  
  return data.unhexlify if code.to_i > 0
  raise RepoError.new("Unknown Revision #{data}")
end

#stream_outObject



216
217
218
# File 'lib/amp/repository/repositories/http_repository.rb', line 216

def stream_out
  do_cmd 'stream_out'
end

#unbundle(cg, heads, source) ⇒ Fixnum

Sends a bundled up changegroup to the server, who will add it to its repository. Uses the bundle format.

Parameters:

  • cg (StringIO)

    the changegroup to push as a stream.

  • heads (Array<String>)

    the heads of the changegroup being sent

  • source (NilClass)

    no idea UNUSED

Returns:

  • (Fixnum)

    the response code from the server (1 indicates success)



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/amp/repository/repositories/http_repository.rb', line 178

def unbundle(cg, heads, source)
  # have to stream bundle to a temp file because we do not have
  # http 1.1 chunked transfer
  
  type = ''
  types = capable? 'unbundle'
  
  # servers older than d1b16a746db6 will send 'unbundle' as a boolean
  # capability
  # this will be a list of allowed bundle compression types
  types = types.split ',' rescue ['']
  
  # pick a compression format
  types.each do |x|
    (type = x and break) if RevlogSupport::ChangeGroup::BUNDLE_HEADERS.include? x
  end
  
  # compress and create the bundle
  data = RevlogSupport::ChangeGroup.write_bundle cg, type
  
  # send the data
  resp = do_read 'unbundle', :data => data.string,
                             :headers => {'Content-Type' => 'application/octet-stream'},
                             :heads => heads.map{|h| h.hexlify }.join(' ')
  # parse output
  resp_code, output = resp.first.split "\n"
  
  # make sure the reponse was in an expected format (i.e. with a response code)
  unless resp_code.to_i.to_s == resp_code
    raise abort("push failed (unexpected response): #{resp}")
  end
  
  # output any text from the server
  UI::say output
  # return 1 for success, 0 for failure
  resp_code.to_i
end