--- a/rakelib/hglib.rb Thu Nov 24 10:40:01 2016 +0000
+++ b/rakelib/hglib.rb Thu Nov 24 20:22:25 2016 +0000
@@ -38,11 +38,11 @@
require 'logger'
$LOGGER = Logger.new(STDOUT)
- if (VERBOSE != nil) then
- $LOGGER.level = Logger::DEBUG
- else
- $LOGGER.level = Logger::INFO
- end
+ if (VERBOSE != nil) then
+ $LOGGER.level = Logger::DEBUG
+ else
+ $LOGGER.level = Logger::INFO
+ end
else
require 'syslog/logger'
$LOGGER = Syslog::Logger.new($0)
@@ -130,13 +130,13 @@
files = Dir.glob('/etc/mercurial/hgrc.d/*.rc') +
[ '/etc/mercurial/hgrc' ,
hgrc() ]
- if Gem.win_platform? then
- hg_exe = which("hg")
- hgrc_d = File.join(File.dirname(hg_exe), "hgrc.d")
- if File.directory? (hgrc_d) then
- files += Dir.glob("#{hgrc_d}\\*.rc".gsub('\\', '/'))
- end
- end
+ if Gem.win_platform? then
+ hg_exe = which("hg")
+ hgrc_d = File.join(File.dirname(hg_exe), "hgrc.d")
+ if File.directory? (hgrc_d) then
+ files += Dir.glob("#{hgrc_d}\\*.rc".gsub('\\', '/'))
+ end
+ end
@@config = IniFile.new()
files.each do | file |
if File.exist?(file)
@@ -149,12 +149,111 @@
end
def self.hgrc()
- return File.expand_path('~/.hgrc')
+ return File.expand_path('~/.hgrc')
end
class Repository
+ @@HOSTS_ON_LAN = {}
+
attr_accessor :path, :config
+
+ private
+ # Return --ssh config string for use with passed remote url or nil
+ # if no special --ssh config is needed.
+ #
+ # Rationale:
+ #
+ # On Windows, most of users tend to use TortoiseHG and hg.exe comming with
+ # it. THG makes hg.exe to use (shipped) plink.exe which is bad for performance
+ # since it uses 16k channel input buffer (!) leading to a pretty slow transfers
+ # (a lot of iowaits...)
+ # OpenSSH OTOH has 2MB input buffer which is good though on Windows bit
+ # oversized as Windows TCP window size is fixed to 65k for all connections with
+ # RTT less than 1ms. Still, 65k better then 16k.
+ # As a workaround, look if MSYS2's OpenSSH client is installed and if so, use that
+ # one - but only if `ui.ssh` config option has the default value.
+ # Ugly, isn't it?
+ def self.sshconf(uri_string)
+ uri = URI(uri_string)
+ ssh = nil
+ if (uri.scheme == 'ssh')
+ ssh_in_path = which('ssh') ? true : false
+ if Gem.win_platform? then
+ # Running on Windows
+ #
+ # Mercurial uses `ssh` by default, so to use `plink.exe`, `ui.ssh`
+ # config option has to be explicitly set.
+ #
+ # It it's set to `plink.exe`, check whether MSYS's `ssh.exe` is available
+ # and if so, change it to `ssh.exe`...
+ ssh_configured = HG::config['ui']['ssh']
+ if /^.*[pP]link.exe"?\s*(-ssh)?\s*(-2)?$/ =~ ssh_configured then
+ if ssh_in_path then
+ ssh = 'ssh'
+ else
+ if (File.exist? "c:\\msys64\\usr\\bin\\ssh.exe") then
+ ssh = "\"c:\\msys64\\usr\\bin\\ssh.exe\""
+ end
+ end
+ # A downside of messing with ssh configuration is that OpenSSH client does not know how
+ # to connect to pageant. So issue a warning...
+ if ssh then
+ $LOGGER.info("Passing --ssh \"#{ssh}\" option to 'hg' command to reduce CPU load on LAN tranfers")
+ if not ENV['SSH_AUTH_SOCK'] then
+ $LOGGER.warn("Transfet may fail since MSYS2 `ssh.exe` dont know how to talk to pageant. ")
+ $LOGGER.warn("Consider using ssh-agent or ssh-pageant (if you want to use PuTTY's pageant)")
+ end
+ end
+ # Turn off SSH compression - data transferred by Mercurial are either
+ # already compressed or --uncompressed was given to reduce CPU load
+ # in which case passing -C would reduce it further.
+ if ssh and HG::Repository::host_on_lan?(uri.host) then
+ ssh += " -C"
+ end
+ end
+ else
+ # Turn off SSH compression - data transferred by Mercurial are either
+ # already compressed or --uncompressed was given to reduce CPU load
+ # in which case passing -C would reduce it further.
+ if ssh_in_path and HG::Repository::host_on_lan?(uri.host) then
+ ssh = "ssh -C"
+ end
+ end
+ end
+ return ssh
+ end
+
+ # Same as class method, but allows for remote aliases
+ def sshconf(remote)
+ return HG::Repository::sshconf(self.paths[remote] || remote)
+ end
+
+ # Given a hostname, return true if the host is on local LAN, false
+ # otherwise.
+ #
+ # Results are cached to reduce resolver queries (maybe not worth it,
+ # system may cache answers anyways)
+ #
+ # The detection is rather simplistic - it only checks if host's address
+ # is from private IP range - and only for IPv4.
+ # This may cause problems when using over VPN that assigns private address
+ # (the usuall case). In that case this code will treat is a local
+ # which may result in transfer of uncompressed data over WAN. Not nice,
+ # This should be fixed, somehow.
+ def self.host_on_lan?(hostname)
+ if not @@HOSTS_ON_LAN.has_key? hostname then
+ require 'resolv'
+ addr = Resolv.getaddress(hostname)
+ # Really poor detection of LAN, but since this is an
+ # optimization, getting this wrong does not hurt.
+ local = (addr.start_with? '192.168.') or (addr.start_with? '10.10.')
+ @@HOSTS_ON_LAN[hostname] = local
+ end
+ return @@HOSTS_ON_LAN[hostname]
+ end
+
+ public
# Clone a repository from given `uri` to given `directory`.
# Returns an `HG::Repository` instance representing the repository
# clone.
@@ -162,60 +261,14 @@
# empty. Use this when you're going to issue `update(rev)` shortly after.
#
def self.clone(uri, directory, noupdate: false)
- uri_obj = URI(uri)
+ uri_obj = URI(uri)
host = uri_obj.host
- scheme = uri_obj.scheme
+ scheme = uri_obj.scheme
# When cloning over LAN, use --uncompressed option
# as it tends to be faster if bandwidth is good (1GB norm
# these days) amd saves some CPU cycles.
- local = false
- if host
- require 'resolv'
- addr = Resolv.getaddress(host)
- # Really poor detection of LAN, but since this is an
- # optimization, getting this wrong does not hurt.
- local = (addr.start_with? '192.168.') or (addr.start_with? '10.10.')
- end
- # On Windows, most of users tend to use TortoiseHG and hg.exe comming with
- # it. THG makes hg.exe to use (shipped) plink.exe which is bad for performance
- # since it uses 16k channel input buffer (!) leading to a pretty slow transfers
- # (a lot of iowaits...)
- # OpenSSH OTOH has 2MB input buffer which is good though on Windows bit
- # oversized as Windows TCP window size is fixed to 65k for all connections with
- # RTT less than 1ms. Still, 65k better then 16k.
- # As a workaround, look if MSYS2's OpenSSH client is installed and if so, use that
- # one - but only if `ui.ssh` config option has the default value.
- # Ugly, isn't it?
- ssh = nil
- puts "1 #{HG::config['ui'].has_key?('ssh')}"
- if (scheme == 'ssh') and (Gem.win_platform?) and (HG::config['ui'].has_key?('ssh')) then
- # For THG, `ui.ssh` is configured as:
- #
- # "C:\Program Files\TortoiseHg\lib\TortoisePlink.exe" -ssh -2
- #
- # Be more relaxed and conver all "standard" plink.exe usages...
- if File.exist? "c:\\msys64\\usr\\bin\\ssh.exe" then
- # Rename. only if user did not override the setting!
- if /^.*[pP]link.exe"?\s*(-ssh)?\s*(-2)?$/ =~ HG::config['ui']['ssh'] then
- ssh = "\"c:\\msys64\\usr\\bin\\ssh.exe\""
- end
- # Since we're messing wth ssh config anyway, add -C if we're cloning "over LAN"
- # to save some CPU cycles. Same reasosing as for --uncompressed above.
- if local then
- ssh += " -C"
- end
- end
- end
-
- # A downside of messing with ssh configuration is that OpenSSH client does not know how
- # to connect to pageant. So issue a warning...
- if ssh then
- $LOGGER.warn("Passing --ssh \"#{ssh}\" option to 'hg clone' for better performance.")
- if not ENV['SSH_AUTH_SOCK'] then
- $LOGGER.warn("Clone may fail since MSYS2 `ssh.exe` dont know how to talk to pageant. ")
- $LOGGER.warn("Consider using ssh-pageant")
- end
- end
+ local = HG::Repository::host_on_lan?(URI(uri).host)
+ ssh = HG::Repository::sshconf(uri)
if noupdate then
HG::hg("clone", uri, directory, ssh: ssh, uncompressed: local, noupdate: true)
else
@@ -342,7 +395,7 @@
public
def incoming(remote = 'default', user: nil, pass: nil, rev: nil)
- hg("incoming", remote, config: authconf(remote, user, pass), rev: rev) do | status, stdout|
+ hg("incoming", remote, ssh: sshconf(remote), config: authconf(remote, user, pass), rev: rev) do | status, stdout|
case status.exitstatus
when 0
STDOUT.print stdout
@@ -355,7 +408,7 @@
end
def pull(remote = 'default', user: nil, pass: nil, rev: nil, bookmarks: nil)
- hg("pull", remote, config: authconf(remote, user, pass), rev: rev, bookmark: bookmarks) do | status, stdout|
+ hg("pull", remote, ssh: sshconf(remote), config: authconf(remote, user, pass), rev: rev, bookmark: bookmarks) do | status, stdout|
STDOUT.print stdout
case status.exitstatus
when 0
@@ -369,7 +422,7 @@
end
def outgoing(remote = 'default', user: nil, pass: nil, rev: nil)
- hg("outgoing", remote, config: authconf(remote, user, pass), rev: rev) do | status, stdout|
+ hg("outgoing", remote, ssh: sshconf(remote), config: authconf(remote, user, pass), rev: rev) do | status, stdout|
case status.exitstatus
when 0
STDOUT.print stdout
@@ -382,7 +435,7 @@
end
def push(remote = 'default', user: nil, pass: nil, rev: nil, bookmarks: [])
- hg("push", remote, config: authconf(remote, user, pass), rev: rev, bookmark: bookmarks) do | status, stdout|
+ hg("push", remote, ssh: sshconf(remote), config: authconf(remote, user, pass), rev: rev, bookmark: bookmarks) do | status, stdout|
case status.exitstatus
when 0
STDOUT.print stdout
@@ -452,8 +505,8 @@
end
def has_revision?(rev)
- revs = log(rev)
- return revs.size > 0
+ revs = log(rev)
+ return revs.size > 0
end
# Lookup a repository in given `directory`. If found,