Skip to content

Commit 98133f1

Browse files
author
Robin Luckey
committed
Merge branch 'OTWO-415'
2 parents 7528067 + 50572a9 commit 98133f1

File tree

10 files changed

+263
-0
lines changed

10 files changed

+263
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*.swp
22
*.pyc
33
pkg/
4+
*.cache

lib/scm.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module Scm
1616
require 'lib/scm/adapters/svn_chain_adapter'
1717
require 'lib/scm/adapters/git_adapter'
1818
require 'lib/scm/adapters/hg_adapter'
19+
require 'lib/scm/adapters/hglib_adapter'
1920
require 'lib/scm/adapters/bzr_adapter'
2021
require 'lib/scm/adapters/bzrlib_adapter'
2122
require 'lib/scm/adapters/factory'

lib/scm/adapters/hglib/cat_file.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module Scm::Adapters
2+
class HglibAdapter < HgAdapter
3+
4+
def cat_file(commit, diff)
5+
hg_client.cat_file(commit.token, diff.path)
6+
end
7+
8+
def cat_file_parent(commit, diff)
9+
tokens = parent_tokens(commit)
10+
hg_client.cat_file(tokens.first, diff.path) if tokens.first
11+
end
12+
13+
end
14+
end

lib/scm/adapters/hglib/client.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
require 'rubygems'
2+
require 'open4'
3+
4+
class HglibClient
5+
def initialize(repository_url)
6+
@repository_url = repository_url
7+
@py_script = File.dirname(__FILE__) + '/server.py'
8+
end
9+
10+
def start
11+
@pid, @stdin, @stdout, @stderr = Open4::popen4 "python #{@py_script}"
12+
open_repository
13+
end
14+
15+
def open_repository
16+
send_command("REPO_OPEN\t#{@repository_url}")
17+
end
18+
19+
def cat_file(revision, file)
20+
begin
21+
send_command("CAT_FILE\t#{revision}\t#{file}")
22+
rescue RuntimeError => e
23+
if e.message =~ /not found in manifest/
24+
return nil # File does not exist.
25+
else
26+
raise
27+
end
28+
end
29+
end
30+
31+
def parent_tokens(revision)
32+
send_command("PARENT_TOKENS\t#{revision}").split("\t")
33+
end
34+
35+
def send_command(cmd)
36+
# send the command
37+
@stdin.puts cmd
38+
@stdin.flush
39+
40+
# get status on stderr, first letter indicates state,
41+
# remaing value indicates length of the file content
42+
status = @stderr.read(10)
43+
flag = status[0,1]
44+
size = status[1,9].to_i
45+
if flag == 'F'
46+
return nil
47+
elsif flag == 'E'
48+
error = @stdout.read(size)
49+
raise RuntimeError.new("Exception in server process\n#{error}")
50+
end
51+
52+
# read content from stdout
53+
return @stdout.read(size)
54+
end
55+
56+
def shutdown
57+
send_command("QUIT")
58+
Process.waitpid(@pid, Process::WNOHANG)
59+
end
60+
end

lib/scm/adapters/hglib/head.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module Scm::Adapters
2+
class HglibAdapter < HgAdapter
3+
4+
def parent_tokens(commit)
5+
hg_client.parent_tokens(commit.token)
6+
end
7+
8+
end
9+
end

lib/scm/adapters/hglib/server.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import sys
2+
import time
3+
import traceback
4+
5+
from mercurial import ui, hg
6+
7+
class HglibPipeServer:
8+
def __init__(self, repository_url):
9+
self.ui = ui.ui()
10+
self.repository = hg.repository(self.ui, repository_url)
11+
12+
def get_file_content(self, filename, revision):
13+
c = self.repository.changectx(revision)
14+
fc = c[filename]
15+
contents = fc.data()
16+
return contents
17+
18+
def get_parent_tokens(self, revision):
19+
c = self.repository.changectx(revision)
20+
parents = [p.hex() for p in c.parents() if p.hex() != '0000000000000000000000000000000000000000']
21+
return parents
22+
23+
class Command:
24+
def __init__(self, line):
25+
self.args = line.rstrip().split('\t')
26+
27+
def get_action(self):
28+
return self.args[0]
29+
30+
def get_arg(self, num):
31+
return self.args[num]
32+
33+
def send_status(code, data_len):
34+
sys.stderr.write('%s%09d' % (code, data_len))
35+
sys.stderr.flush()
36+
37+
def send_success(data_len=0):
38+
send_status('T', data_len)
39+
40+
def send_failure(data_len=0):
41+
send_status('F', data_len)
42+
43+
def send_error(data_len=0):
44+
send_status('E', data_len)
45+
46+
def send_data(result):
47+
sys.stdout.write(result)
48+
sys.stdout.flush()
49+
50+
def command_loop():
51+
while True:
52+
s = sys.stdin.readline()
53+
cmd = Command(s)
54+
if s == '' or cmd.get_action() == 'QUIT':
55+
sys.exit(0)
56+
elif cmd.get_action() == 'REPO_OPEN':
57+
commander = HglibPipeServer(cmd.get_arg(1))
58+
send_success()
59+
elif cmd.get_action() == 'CAT_FILE':
60+
try:
61+
content = commander.get_file_content(cmd.get_arg(2), cmd.get_arg(1))
62+
send_success(len(content))
63+
send_data(content)
64+
except Exception:
65+
send_failure() # Assume file not found
66+
elif cmd.get_action() == 'PARENT_TOKENS':
67+
tokens = commander.get_parent_tokens(cmd.get_arg(1))
68+
tokens = '\t'.join(tokens)
69+
send_success(len(tokens))
70+
send_data(tokens)
71+
else:
72+
error = "Invalid Command - %s" % cmd.get_action()
73+
send_error(len(error))
74+
send_data(error)
75+
sys.exit(1)
76+
77+
if __name__ == "__main__":
78+
try:
79+
command_loop()
80+
except Exception:
81+
exc_trace = traceback.format_exc()
82+
send_error(len(exc_trace))
83+
send_data(exc_trace)
84+
sys.exit(1)

lib/scm/adapters/hglib_adapter.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require 'rubygems'
2+
require 'lib/scm/adapters/hglib/client'
3+
4+
module Scm::Adapters
5+
class HglibAdapter < HgAdapter
6+
7+
def setup
8+
hg_client = HglibClient.new(url)
9+
hg_client.start
10+
hg_client
11+
end
12+
13+
def hg_client
14+
@hg_client ||= setup
15+
end
16+
17+
def cleanup
18+
@hg_client && @hg_client.shutdown
19+
end
20+
21+
end
22+
end
23+
24+
require 'lib/scm/adapters/hglib/cat_file'
25+
require 'lib/scm/adapters/hglib/head'

test/test_helper.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ def with_hg_repository(name)
9393
with_repository(Scm::Adapters::HgAdapter, name) { |hg| yield hg }
9494
end
9595

96+
def with_hglib_repository(name)
97+
with_repository(Scm::Adapters::HglibAdapter, name) { |hg| yield hg }
98+
end
99+
96100
def with_bzr_repository(name)
97101
with_repository(Scm::Adapters::BzrAdapter, name) { |bzr| yield bzr }
98102
end

test/unit/hglib_cat_file_test.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
require File.dirname(__FILE__) + '/../test_helper'
2+
3+
module Scm::Adapters
4+
class HglibCatFileTest < Scm::Test
5+
6+
def test_cat_file
7+
with_hglib_repository('hg') do |hg|
8+
expected = <<-EXPECTED
9+
/* Hello, World! */
10+
11+
/*
12+
* This file is not covered by any license, especially not
13+
* the GNU General Public License (GPL). Have fun!
14+
*/
15+
16+
#include <stdio.h>
17+
main()
18+
{
19+
printf("Hello, World!\\n");
20+
}
21+
EXPECTED
22+
23+
# The file was deleted in revision 468336c6671c. Check that it does not exist now, but existed in parent.
24+
assert_equal nil, hg.cat_file(Scm::Commit.new(:token => '75532c1e1f1d'), Scm::Diff.new(:path => 'helloworld.c'))
25+
assert_equal expected, hg.cat_file_parent(Scm::Commit.new(:token => '75532c1e1f1d'), Scm::Diff.new(:path => 'helloworld.c'))
26+
assert_equal expected, hg.cat_file(Scm::Commit.new(:token => '468336c6671c'), Scm::Diff.new(:path => 'helloworld.c'))
27+
end
28+
end
29+
30+
# Ensure that we escape bash-significant characters like ' and & when they appear in the filename
31+
def test_funny_file_name_chars
32+
Scm::ScratchDir.new do |dir|
33+
# Make a file with a problematic filename
34+
funny_name = '#|file_name` $(&\'")#'
35+
File.open(File.join(dir, funny_name), 'w') { |f| f.write "contents" }
36+
37+
# Add it to an hg repository
38+
`cd #{dir} && hg init && hg add * && hg commit -m test`
39+
40+
# Confirm that we can read the file back
41+
hg = HglibAdapter.new(:url => dir).normalize
42+
assert_equal "contents", hg.cat_file(hg.head, Scm::Diff.new(:path => funny_name))
43+
end
44+
end
45+
46+
end
47+
end

test/unit/hglib_head_test.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
require File.dirname(__FILE__) + '/../test_helper'
2+
3+
module Scm::Adapters
4+
class HgHeadTest < Scm::Test
5+
6+
def test_head_and_parents
7+
with_hglib_repository('hg') do |hg|
8+
assert_equal '75532c1e1f1d', hg.head_token
9+
assert_equal '75532c1e1f1de55c2271f6fd29d98efbe35397c4', hg.head.token
10+
assert hg.head.diffs.any? # diffs should be populated
11+
12+
assert_equal '468336c6671cbc58237a259d1b7326866afc2817', hg.parents(hg.head).first.token
13+
assert hg.parents(hg.head).first.diffs.any?
14+
end
15+
end
16+
17+
end
18+
end

0 commit comments

Comments
 (0)