发表时间
评论 没有

作为一个非专业的程序员,拥有一款舒心的编辑器是很有必要的。

从开始编程序,我大概用过这些编辑器:

  • 用 FrontPage:编辑网页,仅仅是网页;
  • DreamWeaver:编辑网页,编写JSP;
  • Windows 记事本:编写Java程序以及其他需要严格使用Unicode的程序;
  • JCreator:编写Java程序和JSP;
  • JEditor:编写Java程序和JSP;
  • Netbeans:编写Java程序和JSP,但是太大了,当时计算机配置低,没有多使用;
  • Eclipse:编写Java程序和JSP,但是太大了,当时计算机配置低,没有多使用;
  • NotePad++:可以编写各种程序,确实是程序员的“匕首”;
  • Vim:转战Linux系统,开始使用Vim,但是没有大规模长时间使用;
  • Gedit:类似于记事本,但是通过定制之后,类似于NotePad++,无往而不胜啊;
  • Sublime Text:非常好用,但是在Linux上对于汉文的支持很差,而这在很多时候又是避不开的,所以在Linux上弃用;
  • Geany:类似与Gedit,通过安装插件定制之后,也很好用;
  • Atom:Github推出的可定制的21实际的编辑器,好用;

经过这么多选择之后,还是很难说哪个编辑器就是最好的编辑器,只能是根据使用情况,根据个人爱好,你可以选择最适合你的那款编辑器。例如输入一些很简单的东西,记事本、Gedit就可以胜任;Vim可以胜任各种情况,前提是你对她够熟悉,定制够到位;而一些大的编辑器,甚至不能叫做编辑器了,是一种大规模的程序开发软件,例如Eclipse和Netbeans更适合规模化开发。

所以,萝卜白菜,各有所爱,选择最适合你的——才是最好的。

作者
分类

发表时间
评论 没有

前面曾经简单介绍过 ISBN ,关于ISBN的校对,有新码和旧码两种校对方法:

  • 新码的校对方法:取前12位数字,将奇数位乘以1,偶数位乘以3,然后将12个积加到一起除以10得到余数,再用10减去余数,得到的数字即为校验数(第12位);
  • 旧码的校对方法:取前9位数字,依次乘以10,9,8,7,6,5,4,3,2,然后将乘积相加除以11得到余数,再用11减去余数,如果得到10,校验位(第10位)应该是X;如果得到11,校验位应该是0,其他的得到几即为几;

这是Ruby写的校验方法(isbn.rb):

def check_isbn(isbn)
  # delete all - from isbn string
  isbn = isbn.gsub(/-/,"")
  sum = 0
  # new isbn
  if isbn.length == 13
    for i in 0..11
      if isbn[i].to_i.odd?
        sum += isbn[i].to_i * 1
      else
        sum += isbn[i].to_i * 3
      end
    end
    if 10 - sum%10 == isbn[12].to_i
      return true
    else
      return false
    end
  # old isbn
  elsif isbn.length == 10
    for i in 0..8
      sum += isbn[i].to_i * (10 - i)
    end
    if 11 - sum%11 == 10 and isbn[9] == "X"
      return true
    elsif 11 - sum%11 == 11 and isbn[9] == "0"
      return true
    elsif 11 - sum%11 == isbn[9].to_i
      return true
    else
      return false
    end
  else
    return false
  end
end

if check_isbn(ARGV[0])
  puts 'yes'
else
  puts 'wrong'
end

运行 ruby isbn.rb 978-7-2343-266-3 即可得到校对结果。

作者
分类 ,

发表时间
评论 没有

Rails开发中,需要使用远程主机Oracle数据库,本来以为很简单,没想到需要这么多周折:

安装链接适配器程序

需要对应的数据库适配器程序,到GitHub上很容易找到人气很高的 oracle-enhanced ,因为我使用的是Rails 4.2 ,所以,按照要求在Gemfile中加入

gem 'activerecord-oracle_enhanced-adapter', '~> 1.6.0'

安装顺利,然后运行应用程序 rails s 测试,结果出错:

bin/rails:6: warning: already initialized constant APP_PATH
/path_to_my_app/bin/rails:6: warning: previous definition of APP_PATH was here
Usage: rails COMMAND [ARGS]

The most common rails commands are:
 generate    Generate new code (short-cut alias: "g")
 console     Start the Rails console (short-cut alias: "c")
 server      Start the Rails server (short-cut alias: "s")
 dbconsole   Start a console for the database specified in config/database.yml
             (short-cut alias: "db")
 new         Create a new Rails application. "rails new my_app" creates a
             new application called MyApp in "./my_app"

In addition to those, there are:
 destroy      Undo code generated with "generate" (short-cut alias: "d")
 plugin new   Generates skeleton for developing a Rails plugin
 runner       Run a piece of code in the application environment (short-cut alias: "r")

All commands can be run with -h (or --help) for more information.

很奇怪的错误信息,经过网络搜索后,发现 这篇文章 ,说是需要运行 rake rails:update:bin ,运行后提示:

rake aborted!
LoadError: ERROR: 'cannot load such file -- oci8'. ActiveRecord oracle_enhanced adapter could not load ruby-oci8 library. You may need install ruby-oci8 gem.
/home/hfl/library/imulib/config/application.rb:7:in `<top (required)>'
/home/hfl/library/imulib/Rakefile:4:in `<top (required)>'
LoadError: cannot load such file -- oci8
/home/hfl/library/imulib/config/application.rb:7:in `<top (required)>'
/home/hfl/library/imulib/Rakefile:4:in `<top (required)>'
(See full trace by running task with --trace)

需要安装 ruby-oci8

安装 ruby-oci8

gem install ruby-oci8

出错:

Fetching: ruby-oci8-2.2.1.gem (100%)
Building native extensions.  This could take a while...
ERROR:  Error installing ruby-oci8:
	ERROR: Failed to build gem native extension.

checking for load library path... 
  LD_LIBRARY_PATH is not set.
  checking ld.so.conf... no
checking for cc... ok
checking for gcc... yes
checking for LP64... yes
checking for sys/types.h... yes
checking for ruby header... ok
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
	--with-opt-dir
	--without-opt-dir
	--with-opt-include
	--without-opt-include=${opt-dir}/include
	--with-opt-lib
	--without-opt-lib=${opt-dir}/lib
	--with-make-prog
	--without-make-prog
	--srcdir=.
	--curdir
	--with-instant-client
	--without-instant-client
---------------------------------------------------
Error Message:
  Set the environment variable ORACLE_HOME if Oracle Full Client.
  Append the path of Oracle client libraries to LD_LIBRARY_PATH if Oracle Instant Client.

提示需要设置 ORACLE_HOMELD_LIBRARY_PATH ,这是什么东西呢?经查,原来需要在使用Oracle的Rails应用服务器上安装客户端 Oracle Instant Client,到 这里 下载(下载还需要注册登录一下)。

下载完毕,安装见说明:

1. Download the desired Instant Client ZIP files. All installations require the Basic or Basic Lite package. 

2. Unzip the packages into a single directory such as "/opt/oracle/instantclient_12_1" that is accessible to your application.

3. Create the appropriate libclntsh.so and libocci.so links for the version of Instant Client. For example:

cd /opt/oracle/instantclient_12_1
ln -s libclntsh.so.12.1 libclntsh.so
ln -s libocci.so.12.1 libocci.so

4. Set the environment variable LD_LIBRARY_PATH to the directory created in Step 2, for example:

export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_1:$LD_LIBRARY_PATH

Alternatively, add this path to an ldconfig configuration file if there is no other Oracle software that will be impacted.

5. To use supplied binaries such as SQL*Plus, update your PATH environment variable, for example:

export PATH=/opt/oracle/instantclient_12_1:$PATH

6. Start your application.

安装完毕,再次安装ruby-oci8,又报错了:

LoadError: You need to install libaio.so.1. Run 'apt-get install libaio1'.

需要安装 libaio1

sudo apt-get install libaio1

然后在安装 ruby-oci8 ,成功!

运行 rails s 还是出开始运行应用服务器时出现的错误,运行 rake rails:update:bin ,提示

Warning: NLS_LANG is not set. fallback to US7ASCII.
       exist  bin
   identical  bin/bundle
    conflict  bin/rails
Overwrite /home_to_app/bin/rails? (enter "h" for help) [Ynaqdh] Y
       force  bin/rails
    conflict  bin/rake
Overwrite /home_to_app/bin/rake? (enter "h" for help) [Ynaqdh] Y
       force  bin/rake
   identical  bin/setup

两次Y之后,可以运行应用服务器了。

作者
分类

发表时间
评论 没有

翻译自: REXML: Processing XML in Ruby

REXMLRuby程序员 的XML处理器,一般该模块是 Ruby 内建模块,已经包含于发行包之中。该模块使用Ruby编写、运行迅速。通常使用两种方式解析:树式(Tree)解析和流式(Stream)解析。本文将展示如何使用REXML解析XML的基本用法。我们也将介绍在 irb 中基于REXML浏览XML文档。

我们将使用一个 DocBook 的目录文件作为示例XML文档。你将看到如何通过树形解析访问元素和树形,并且创建和插入元素。We’ll also look into the peculiarities of text nodes and entity processing。最后,我们将基于流解析如何解析XML文档。这个就是我们的DocBook文件:

例1: bibliography.xml 文件

bibliography.xml

开始树式(Tree)解析

我们使用树解析,其实这就是类似于DOM、但是更直观的的解析方法。这就是我们的第一段代码了:

显示一个XML文件内容(code1.rb)

require 'rexml/document'
include REXML
file = File.new("bibliography.xml")
doc = Document.new(file)
puts doc

require 声明用来加载 REXML 库。然后我们就包含(include)了REXML的命名空间,这样做就是为了不使用类似于 REXML::Document 这样的引用了。我们打开文件名为 bibliography.xml 的文件然后解析XML源码,然后我们就会得到一个 Document 对象。然后我们就把这个文档现实到屏幕上。当你执行命令 ruby code1.rb 的时候,bibliograpy.xml 的内容就会显示。

可能没有看到XML文件内容却得到了这样的信息:

example1.rb:1:in `require': No such file to load 
  -- rexml/document (LoadError)
        from example1.rb:1

如果是这样,你安装的Ruby中可能没有REXML模块,因为有些软件管理系统(如Debian中的APT)是将各种库作为单独的软件安装的。如果是这样,那么就安装 REXML 然后再试。

Document.new 方法生成一个基于文档(Document)或者字符串(String)对象为参数的IO流。参数定义了我们想要读取的XML文档来源。在我们的第一个示例中,我们使用了一个流(IO)对象,是继承自IO类的File对象。另一个IO类的子类是Socket类,也可以使用 Document.new 通过网络来获得一个XML文档。

如果Document构建器获得一个Document作为参数,那么所有的元素节点都会被复制到Document对象之中。如果构建器获得一个String参数,那么该字符串中会包含一个XML文档。例如:

示例3:显示一个XML“嵌入文档”(code2.rb)

require 'rexml/document'
include REXML
string = <<EOF
<?xml version="1.0" encoding="ISO-8859-15"?>
<!DOCTYPE bibliography PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
    "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<bibliography>
    <biblioentry id="FHIW13C-1234">
      <author>
        <firstname>Godfrey</firstname>
        <surname>Vesey</surname>
      </author>
      <title>Personal Identity: A Philosophical Analysis</title>
      <publisher>
        <publishername>Cornell University Press</publishername>
      </publisher>
      <pubdate>1977</pubdate>
   </biblioentry>
</bibliography>
EOF
doc = Document.new(string)
puts doc

我们使用“嵌入文档”字符串(所有位于 <<EOFEOF 中间的字符,包含换行以及字符串等等)。

访问元素和属性

从现在开始,我们将使用 irb (交互式Ruby调试器),就像我们前面使用 REXML 库一样。在 irb 提示符下,我们加载 bibliography.xml 文件到一个文档对象。然后我们就可以以交互方式执行命令访问文档的元素和属性了。

koan$ irb
irb(main):001:0> require 'rexml/document'
=> true
irb(main):002:0> include REXML
=> Object
irb(main):003:0> doc = Document.new(File.new("bibliography.xml"))
=> <UNDEFINED> ... </>

现在你可以很容易地浏览文档了。接下来我们在 irb 中看一看XML文档:

irb(main):004:0> root = doc.root
=> <bibliography id='personal_identity'> ... </>
irb(main):005:0> root.attributes['id']
=> "personal identity"
irb(main):006:0> puts root.elements[1].elements["author"]
<author>
  <firstname>Godfrey</firstname>
  <surname>Vesey</surname>
</author>
irb(main):007:0> puts root.elements["biblioentry[1]/author"]
<author>
  <firstname>Godfrey</firstname>
  <surname>Vesey</surname>
</author>
irb(main):008:0> puts root.elements["biblioentry[@id='FHIW13C-1260']"]
<biblioentry id='FHIW13C-1260'>
      <author>
        <firstname>Sydney</firstname>
        <surname>Shoemaker</surname>
      </author>
      <author>
        <firstname>Richard</firstname>
        <surname>Swinburne</surname>
      </author>
      <title>Personal Identity</title>
      <publisher>
        <publishername>Basil Blackwell</publishername>
      </publisher>
      <pubdate>1984</pubdate>
    </biblioentry>
=> nil
irb(main):009:0> root.each_element('//author') {|author| puts author}
<author>
  <firstname>Godfrey</firstname>
  <surname>Vesey</surname>
</author>
<author>
  <firstname>René</firstname>
  <surname>Marres</surname>
</author>
<author>
  <firstname>James</firstname>
  <surname>Baillie</surname>
</author>
<author>
  <firstname>Brian</firstname>
  <surname>Garrett</surname>
</author>
<author>
  <firstname>John</firstname>
  <surname>Perry</surname>
</author>
<author>
  <firstname>Geoffrey</firstname>
  <surname>Madell</surname>
</author>
<author>
  <firstname>Sydney</firstname>
  <surname>Shoemaker</surname>
</author>
<author>
  <firstname>Richard</firstname>
  <surname>Swinburne</surname>
</author>
<author>
  <firstname>Jonathan</firstname>
  <surname>Glover</surname>
</author>
<author>
  <firstname>Harold</firstname>
  <othername>W.</othername>
  <surname>Noonan</surname>
</author>
=> [<author> ... </>, <author> ... 
  </>, <author> ... </>, <author> ... 
  </>, <author> ... </>, <author> ... 
  </>, <author> ... </>, <author> ... 
  </>, <author> ... </>, <author> ... </>]

首先我们使用 root 访问文档根元素。文档根元素这里是 bibliography 元素。每个元素对象都有一个属性族(Attributes)对象叫做 attributes ,该对象以“键-值”对的形式保存所有属性值。所以使用 root.attributes['id'] 就会得到root元素的以 id 为键的属性值。同样,每个元素对象都有一个元素族对象叫做 elements ,使用 each[ ] 方法可以访问个子元素。 [ ] 方法获得一个索引或者 XPath 表达式作为参数,返回符合参数的子元素。 XPath 表达式像一个过滤器,来匹配哪一个元素符合条件。注意: root.elements[1] 指第一个元素,因为XPath的索引值是从1而不是0开始的。实际上, root.elements[1] 等同于 root.elements[*[1]] ,而 *[1] 就是匹配第一个子元素的表达式。 Elements 类的 each 方法循环遍历所有的子元素,也可以通过一些XPath表达式来过滤某些元素。在 each 循环中代码块将会执行。另外, Element.each_elementElement.elements.each 的简化形式。

创建和插入元素和属性

现在我们创建一个小的bibliography文档,包含一个 biblioentry 。如下:

irb(main):010:0> doc2 = Document.new
=> <UNDEFINED/>
irb(main):011:0> doc2.add_element("bibliography", 
                    {"id" => "philosophy"})
=> <bibliography id='philosophy'/>
irb(main):012:0> doc2.root.add_element("biblioentry")
=> <biblioentry/>
irb(main):013:0> biblioentry = doc2.root.elements[1]
=> <biblioentry/>
irb(main):014:0> author = Element.new("author")
=> <author/>
irb(main):015:0> author.add_element("firstname")
=> <firstname/>
irb(main):016:0> author.elements["firstname"].text = "Bertrand"
=> "Bertrand"
irb(main):017:0> author.add_element("surname")
=> <surname/>
irb(main):018:0> author.elements["surname"].text = "Russell"
=> "Russell"
irb(main):019:0> biblioentry.elements << author
=> <author> ... </>
irb(main):020:0> title = Element.new("title")
=> <title/>
irb(main):021:0> title.text = "The Problems of Philosophy"
=> "The Problems of Philosophy"
irb(main):022:0> biblioentry.elements << title
=> <title> ... </>
irb(main):023:0> biblioentry.elements << Element.new("pubdate")
=> <pubdate/>
irb(main):024:0> biblioentry.elements["pubdate"].text = "1912"
=> "1912"
irb(main):025:0> biblioentry.add_attribute("id", "ISBN0-19-285423-2")
=> "ISBN0-19-285423-2"
irb(main):026:0> puts doc2
<bibliography id='philosophy'>
  <biblioentry id='ISBN0-19-285423-2'>
    <author>
      <firstname>Bertrand</firstname>
      <surname>Russell</surname>
    </author>
    <title>The Problems of Philosophy</title>
    <pubdate>1912</pubdate>
  </biblioentry>
</bibliography>
=> nil

我们上面创建了一个空的文档,然后添加了一个元素,这是元素就是根元素。 add_element 方法使用元素名字作为参数,可选参数使用“键-值”对来构建属性。所以使用该方法可以添加一个子元素到文档或者设定元素的属性。

也可以构建一个新的元素,就像我们构建的 author 元素,然后添加它到另一个元素:如果 add_element 方法获得一个 Element 元素对象,该对象将被添加到父元素。同 add_element 方法具有相同作用的还有@Element.elments@ 的 << 方法。另个方法都会返回添加的元素。

另外,使用方法 add_attribute 可以给已经存在的元素添加一个属性。第一个参数就是属性名字,第二个参数是属性值。方法返回添加的属性。一个元素的文本值使用 Element.text 或者 add_text 都很容易就可以改变。

如果你想插入一个元素到指定的位置,你可以使用方法 insert_beforeinsert_after

irb(main):027:0> publisher = Element.new("publisher")
=> <publisher/>
irb(main):028:0> publishername = Element.new("publishername")
=> <publishername/>
irb(main):029:0> publishername.add_text("Oxford University Press")
=> <publishername> ... </>
irb(main):030:0> publisher << publishername
=> <publishername> ... </>
irb(main):031:0> doc2.root.insert_before("//pubdate", publisher)
=> <bibliography id='philosophy'> ... </>
irb(main):032:0> puts doc2
<bibliography id='philosophy'>
  <biblioentry id='ISBN0-19-285423-2'>
    <author>
      <firstname>Bertrand</firstname>
      <surname>Russell</surname>
    </author>
    <title>The Problems of Philosophy</title>
    <publisher>
      <publishername>Oxford University Press</publishername>
    </publisher>
    <pubdate>1912</pubdate>
  </biblioentry>
</bibliography>
=> nil

删除元素和属性

add_elmentadd_attribute 方法都有他们的逆方法。示例如下:

irb(main):033:0> doc2.root.delete_attribute('id')
=> <bibliography> ... </>
irb(main):034:0> puts doc2
<bibliography>
  <biblioentry id='ISBN0-19-285423-2'>
    <author>
      <firstname>Bertrand</firstname>
      <surname>Russell</surname>
    </author>
    <title>The Problems of Philosophy</title>
    <publisher>
      <publishername>Oxford University Press</publishername>
    </publisher>
    <pubdate>1912</pubdate>
  </biblioentry>
</bibliography>
=> nil

delete_attribute 方法返回被删除的属性。

delete_element 方法以元素对象、字符串或者索引值作为参数:

irb(main):034:0> doc2.delete_element("//publisher")
=> <publisher> ... </>
irb(main):035:0> puts doc2
<bibliography>
  <biblioentry id='ISBN0-19-285423-2'>
    <author>
      <firstname>Bertrand</firstname>
      <surname>Russell</surname>
    </author>
    <title>The Problems of Philosophy</title>
    <pubdate>1912</pubdate>
  </biblioentry>
</bibliography>
=> nil
irb(main):036:0> doc2.root.delete_element(1)
=> <biblioentry id='ISBN0-19-285423-2'> ... </>
irb(main):037:0> puts doc2
<bibliography/>
=> nil

第一个 delete_element 方法调用一个XPath表达式定位欲删除元素,第二次使用了索引值1,意味着文档根元素内的元素将被删除。 delete_element 方法返回被删除的元素。

文本节点和实体处理

在前面的示例中,我们已经使用过文本节点。这一部分我们将展示更多文本处理示例。尤其是如何使用REXML处理实体?REXML是一种非验证解析器,所以不需要扩展外部实体。所以外部的实体不会被他们的值取代,但是内部实体可以:当REXML解析一个XML文档的时候,它会处理DTD和创建一个内部实体和他们的值的表格。当这些实体中的一个出现在文档中的时候,REXML会用他的值取代它。如下:

irb(main):038:0> doc3 = Document.new('<!DOCTYPE testentity [
irb(main):039:1' <!ENTITY entity "test">]>
irb(main):040:1' <testentity>&entity; the entity</testentity>')
=> <UNDEFINED> ... </>
irb(main):041:0> puts doc3
<!DOCTYPE testentity [
<!ENTITY entity "test">]>
<testentity>&entity; the entity</testentity>
=> nil
irb(main):042:0> doc3.root.text
=> "test the entity"

可以看到,当打印XML文档的时候,已经使用了争取的实体。当你访问文本的时候,实体 &entiry; 已经替换为 “text”。

然而,REXML使用了实体的延迟求值算法。所以,下面问题就来了:

irb(main):043:0> doc3.root.text = "test the &entity;"
=> "test the &entity;"
irb(main):044:0> puts doc3
<!DOCTYPE testentity [
<!ENTITY entity "test">
]>
<testentity>&entity; the &entity;</testentity>
=> nil
irb(main):045:0> doc3.root.text                      
=> "test the test"

如你所见,文本“test the &entity;”变成了“&entity; the &entity;”。如果你改变entity的值,结果会更加超出你所期望:文本中更多的地方将被替换。如果你的程序确认被这个问题折磨,你可以在 Text 或者 Element 上设置 :raw 标志,设置在 Document 上也可以,那么文本中的实体将不会被处理,如果需要做相关处理就需要手动处理了。例如:

irb(main):046:0> doc3 = Document.new('<!DOCTYPE testentity [
irb(main):047:1' <!ENTITY entity "test">]>
irb(main):048:1' <testentity>test the &entity;</testentity>', 
                 {:raw => :all})
=> <UNDEFINED> ... </>
irb(main):049:0> puts doc3
<!DOCTYPE testentity [
<!ENTITY entity "test">
]>
<testentity>test the &entity;</testentity>
=> nil
irb(main):050:0> doc3.root.text
=> "test the test"

实体“ &, <, >, “, ”和“‘”会自动处理。如果你在文本节点或者属性中使用这些字符的时候,REXML会自动将他们转化为相应的实体转义表达式,例如 &amp; 会转变为 & 。

流式(Stream)解析

流式解析速度优于树式解析。如果看重速度,那么优先选择流式解析。可是,在流式解析模式下,是不提供XPath的。你不得不构建一个监视器类,REXML要处理每个事件(例如start_tag, end_tag等等),事件发生时监视器发出提醒。示例程序如下:

示例4:流式解析 (code3.rb)

require 'rexml/document'
require 'rexml/streamlistener'
include REXML

class Listener
  include StreamListener
  def tag_start(name, attributes)
    puts "Start #{name}"
  end
  def tag_end(name)
    puts "End #{name}"
  end
end

listener = Listener.new
parser = Parsers::StreamParser.new(File.new("bibliography2.xml"), listener)
parser.parse

文件bibliography2.xml如下:

示例5:bibliography2.xml文件

bibliography2.xml

运行 code2.rb 得到如下结果:

koan$ ruby code3.rb 
Start bibliography
Start biblioentry
Start author
Start firstname
End firstname
Start surname
End surname
End author
Start title
End title
Start publisher
Start publishername
End publishername
End publisher
Start pubdate
End pubdate
End biblioentry
End bibliography

结论

Ruby and XML make a great team(译者按:这句话感觉很莫名奇妙,就不翻译了。另外这个链接也很诡异,网站只剩一个页面了!)。REXML的XML处理器可以很直观的去创建、访问和修改XML文档。如果使用 irb Ruby互动调试器,我们也可很容易的去浏览一个XML文档。

相关链接

作者
分类

发表时间
评论 没有

Git是近年来很好用,当然也很火的一款分布式版本控制系统。基于Git有很多的软件,也有一些代码托管平台,例如 Github 等。

但是,有些时候,我们会写一些不适合公开的代码,而我们又习惯使用Git的时候,就需要搭建起自用的Git服务器。

首先,如果你使用Git,不论是服务器还是客户端,当然都需要安装Git软件了。我们这里在Ubuntu服务器上安装:

sudo apt-get install git

过去,是需要安装git-core的,现在不用了,直接使用git。安装完毕,软件已经就绪,开始搭建环境。

然后,我们创建一个使用Git的用户。为该用户创建收集公钥的通行证目录

sudo adduser git
su git
cd
mkdir .ssh && chmod 700 .ssh
touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys

接着,我们需要为系统用户 git 的 authorized_keys 文件添加一些开发者 SSH 公钥。 将这些公钥加入系统用户 git 的 .ssh 目录下 authorized_keys 文件的末尾。

好,环境已经创建,接下来,我们要创建我们要使用的Git仓库,可以借助带 —bare 选项的 git init 命令来做到这一点,该命令在初始化仓库时不会创建工作目录:

$ cd /opt/git
$ mkdir project.git
$ cd project.git
$ git init --bare
Initialized empty Git repository in /opt/git/project.git/

接着,John、Josie 或者 Jessica 中的任意一人可以将他们项目的最初版本推送到这个仓库中,他只需将此仓库设置为项目的远程仓库并向其推送分支。 请注意,每添加一个新项目,都需要有人登录服务器取得 shell,并创建一个裸仓库。 我们假定这个设置了 git 用户和 Git 仓库的服务器使用 gitserver 作为主机名。 同时,假设该服务器运行在内网,并且你已在 DNS 配置中将 gitserver 指向此服务器。那么我们可以运行如下命令(假定 myproject 是已有项目且其中已包含文件):

# on John's computer
$ cd myproject
$ git init
$ git add .
$ git commit -m 'initial commit'
$ git remote add origin git@gitserver:/opt/git/project.git
$ git push origin master

此时,其他开发者可以克隆此仓库,并推回各自的改动,步骤很简单:

$ git clone git@gitserver:/opt/git/project.git
$ cd project
$ vim README
$ git commit -am 'fix for the README file'
$ git push origin master

至此,Git服务器已经搭建起来,但是还有缺陷。目前所有(获得授权的)开发者用户都能以系统用户 git 的身份登录服务器从而获得一个普通 shell。 如果你想对此加以限制,则需要修改 passwd 文件中(git 用户所对应)的 shell 值。

借助一个名为 git-shell 的受限 shell 工具,你可以方便地将用户 git 的活动限制在与 Git 相关的范围内。该工具随 Git 软件包一同提供。 如果将 git-shell 设置为用户 git 的登录 shell(login shell),那么用户 git 便不能获得此服务器的普通 shell 访问权限。 若要使用 git-shell,需要用它替换掉 bash 或 csh,使其成为系统用户的登录 shell。 通过编辑/etc/passwd文件完成。找到类似下面的一行:

git:x:1001:1001:,,,:/home/git:/bin/bash

改成

git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell

这样,git用户只能使用git了。

作者
分类

← 较早的 较新的 →