Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'OTWO-415'

  • Loading branch information...
commit 98133f186fdaa38177f3eb2262a307a26e91caef 2 parents 7528067 + 50572a9
@robinluckey authored
View
1  .gitignore
@@ -1,3 +1,4 @@
*.swp
*.pyc
pkg/
+*.cache
View
1  lib/scm.rb
@@ -16,6 +16,7 @@ module Scm
require 'lib/scm/adapters/svn_chain_adapter'
require 'lib/scm/adapters/git_adapter'
require 'lib/scm/adapters/hg_adapter'
+require 'lib/scm/adapters/hglib_adapter'
require 'lib/scm/adapters/bzr_adapter'
require 'lib/scm/adapters/bzrlib_adapter'
require 'lib/scm/adapters/factory'
View
14 lib/scm/adapters/hglib/cat_file.rb
@@ -0,0 +1,14 @@
+module Scm::Adapters
+ class HglibAdapter < HgAdapter
+
+ def cat_file(commit, diff)
+ hg_client.cat_file(commit.token, diff.path)
+ end
+
+ def cat_file_parent(commit, diff)
+ tokens = parent_tokens(commit)
+ hg_client.cat_file(tokens.first, diff.path) if tokens.first
+ end
+
+ end
+end
View
60 lib/scm/adapters/hglib/client.rb
@@ -0,0 +1,60 @@
+require 'rubygems'
+require 'open4'
+
+class HglibClient
+ def initialize(repository_url)
+ @repository_url = repository_url
+ @py_script = File.dirname(__FILE__) + '/server.py'
+ end
+
+ def start
+ @pid, @stdin, @stdout, @stderr = Open4::popen4 "python #{@py_script}"
+ open_repository
+ end
+
+ def open_repository
+ send_command("REPO_OPEN\t#{@repository_url}")
+ end
+
+ def cat_file(revision, file)
+ begin
+ send_command("CAT_FILE\t#{revision}\t#{file}")
+ rescue RuntimeError => e
+ if e.message =~ /not found in manifest/
+ return nil # File does not exist.
+ else
+ raise
+ end
+ end
+ end
+
+ def parent_tokens(revision)
+ send_command("PARENT_TOKENS\t#{revision}").split("\t")
+ end
+
+ def send_command(cmd)
+ # send the command
+ @stdin.puts cmd
+ @stdin.flush
+
+ # get status on stderr, first letter indicates state,
+ # remaing value indicates length of the file content
+ status = @stderr.read(10)
+ flag = status[0,1]
+ size = status[1,9].to_i
+ if flag == 'F'
+ return nil
+ elsif flag == 'E'
+ error = @stdout.read(size)
+ raise RuntimeError.new("Exception in server process\n#{error}")
+ end
+
+ # read content from stdout
+ return @stdout.read(size)
+ end
+
+ def shutdown
+ send_command("QUIT")
+ Process.waitpid(@pid, Process::WNOHANG)
+ end
+end
View
9 lib/scm/adapters/hglib/head.rb
@@ -0,0 +1,9 @@
+module Scm::Adapters
+ class HglibAdapter < HgAdapter
+
+ def parent_tokens(commit)
+ hg_client.parent_tokens(commit.token)
+ end
+
+ end
+end
View
84 lib/scm/adapters/hglib/server.py
@@ -0,0 +1,84 @@
+import sys
+import time
+import traceback
+
+from mercurial import ui, hg
+
+class HglibPipeServer:
+ def __init__(self, repository_url):
+ self.ui = ui.ui()
+ self.repository = hg.repository(self.ui, repository_url)
+
+ def get_file_content(self, filename, revision):
+ c = self.repository.changectx(revision)
+ fc = c[filename]
+ contents = fc.data()
+ return contents
+
+ def get_parent_tokens(self, revision):
+ c = self.repository.changectx(revision)
+ parents = [p.hex() for p in c.parents() if p.hex() != '0000000000000000000000000000000000000000']
+ return parents
+
+class Command:
+ def __init__(self, line):
+ self.args = line.rstrip().split('\t')
+
+ def get_action(self):
+ return self.args[0]
+
+ def get_arg(self, num):
+ return self.args[num]
+
+def send_status(code, data_len):
+ sys.stderr.write('%s%09d' % (code, data_len))
+ sys.stderr.flush()
+
+def send_success(data_len=0):
+ send_status('T', data_len)
+
+def send_failure(data_len=0):
+ send_status('F', data_len)
+
+def send_error(data_len=0):
+ send_status('E', data_len)
+
+def send_data(result):
+ sys.stdout.write(result)
+ sys.stdout.flush()
+
+def command_loop():
+ while True:
+ s = sys.stdin.readline()
+ cmd = Command(s)
+ if s == '' or cmd.get_action() == 'QUIT':
+ sys.exit(0)
+ elif cmd.get_action() == 'REPO_OPEN':
+ commander = HglibPipeServer(cmd.get_arg(1))
+ send_success()
+ elif cmd.get_action() == 'CAT_FILE':
+ try:
+ content = commander.get_file_content(cmd.get_arg(2), cmd.get_arg(1))
+ send_success(len(content))
+ send_data(content)
+ except Exception:
+ send_failure() # Assume file not found
+ elif cmd.get_action() == 'PARENT_TOKENS':
+ tokens = commander.get_parent_tokens(cmd.get_arg(1))
+ tokens = '\t'.join(tokens)
+ send_success(len(tokens))
+ send_data(tokens)
+ else:
+ error = "Invalid Command - %s" % cmd.get_action()
+ send_error(len(error))
+ send_data(error)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ try:
+ command_loop()
+ except Exception:
+ exc_trace = traceback.format_exc()
+ send_error(len(exc_trace))
+ send_data(exc_trace)
+ sys.exit(1)
View
25 lib/scm/adapters/hglib_adapter.rb
@@ -0,0 +1,25 @@
+require 'rubygems'
+require 'lib/scm/adapters/hglib/client'
+
+module Scm::Adapters
+ class HglibAdapter < HgAdapter
+
+ def setup
+ hg_client = HglibClient.new(url)
+ hg_client.start
+ hg_client
+ end
+
+ def hg_client
+ @hg_client ||= setup
+ end
+
+ def cleanup
+ @hg_client && @hg_client.shutdown
+ end
+
+ end
+end
+
+require 'lib/scm/adapters/hglib/cat_file'
+require 'lib/scm/adapters/hglib/head'
View
4 test/test_helper.rb
@@ -93,6 +93,10 @@ def with_hg_repository(name)
with_repository(Scm::Adapters::HgAdapter, name) { |hg| yield hg }
end
+ def with_hglib_repository(name)
+ with_repository(Scm::Adapters::HglibAdapter, name) { |hg| yield hg }
+ end
+
def with_bzr_repository(name)
with_repository(Scm::Adapters::BzrAdapter, name) { |bzr| yield bzr }
end
View
47 test/unit/hglib_cat_file_test.rb
@@ -0,0 +1,47 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class HglibCatFileTest < Scm::Test
+
+ def test_cat_file
+ with_hglib_repository('hg') do |hg|
+expected = <<-EXPECTED
+/* Hello, World! */
+
+/*
+ * This file is not covered by any license, especially not
+ * the GNU General Public License (GPL). Have fun!
+ */
+
+#include <stdio.h>
+main()
+{
+ printf("Hello, World!\\n");
+}
+EXPECTED
+
+ # The file was deleted in revision 468336c6671c. Check that it does not exist now, but existed in parent.
+ assert_equal nil, hg.cat_file(Scm::Commit.new(:token => '75532c1e1f1d'), Scm::Diff.new(:path => 'helloworld.c'))
+ assert_equal expected, hg.cat_file_parent(Scm::Commit.new(:token => '75532c1e1f1d'), Scm::Diff.new(:path => 'helloworld.c'))
+ assert_equal expected, hg.cat_file(Scm::Commit.new(:token => '468336c6671c'), Scm::Diff.new(:path => 'helloworld.c'))
+ end
+ end
+
+ # Ensure that we escape bash-significant characters like ' and & when they appear in the filename
+ def test_funny_file_name_chars
+ Scm::ScratchDir.new do |dir|
+ # Make a file with a problematic filename
+ funny_name = '#|file_name` $(&\'")#'
+ File.open(File.join(dir, funny_name), 'w') { |f| f.write "contents" }
+
+ # Add it to an hg repository
+ `cd #{dir} && hg init && hg add * && hg commit -m test`
+
+ # Confirm that we can read the file back
+ hg = HglibAdapter.new(:url => dir).normalize
+ assert_equal "contents", hg.cat_file(hg.head, Scm::Diff.new(:path => funny_name))
+ end
+ end
+
+ end
+end
View
18 test/unit/hglib_head_test.rb
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class HgHeadTest < Scm::Test
+
+ def test_head_and_parents
+ with_hglib_repository('hg') do |hg|
+ assert_equal '75532c1e1f1d', hg.head_token
+ assert_equal '75532c1e1f1de55c2271f6fd29d98efbe35397c4', hg.head.token
+ assert hg.head.diffs.any? # diffs should be populated
+
+ assert_equal '468336c6671cbc58237a259d1b7326866afc2817', hg.parents(hg.head).first.token
+ assert hg.parents(hg.head).first.diffs.any?
+ end
+ end
+
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.