内容


Git 改变了分布式 Web 开发规则

现代版本控制系统提供对协作的强大支持

Comments

版本控制系统(VCS)为应用和管理项目文件集的更改提供了一个机制,在面向团队的软件、文档和其他在线开发项目中广泛使用。对于开发项目,VCS 和系统备份一样关键,因为它们支持多个用户向同一个文件或项目提交更改,没有任何一个开发人员的更改会意外覆盖另一个开发人员的更改。

即使 Linus Torvalds 没有开发 Linux® 操作系统内核,他也会因为创建了 Git VCS 而出名。像 Linux 这样的复杂项目是对 VCS 的终极系统测试,因此,Git 快速演化为稳定、强大、灵活的系统一点也不奇怪。

Linux 和 UNIX® 系统使用了许多 VCS,从已经绝迹的 Revision Control System (RCS) 和 Concurrent Versions System (CVS) 到更现代的系统,如 Subversion、Mercurial、Bazaar、Arch 和 Darcs。具有讽刺意味的是(尤其在 Linux 领域中),Git 作为一个商业 VCS(BitKeeper)的替代产品开发,BitKeeper 具有独特、印象深刻的功能,并且是一个免费版本。BitKeeper 依然让人印象深刻,但是许可更改最后导致 Torvalds 开始寻找替代产品;在免费软件潮流下,他最后决定编写自己的软件。像 Linux 内核一样,Git 现在由许多开源开发人员贡献产品增强、bug 修复和其他内容。

Git 的吸引力在于它强大的功能,也在于它的成本低廉 —— 免费软件,它很快被许多开源软件项目、研究所和组织所采纳。一旦 “进入” 公司或研究所之后,Git 既可以作为 VCS 又可以作为协作平台,这促使许多传统 “源代码” 之外的项目采用它。正如本文将介绍的,Git 在复杂、分布式 Web 开发项目中非常有用,这些项目对内容和开发都有很多要求,需要不同人员之间一直保持交互。

Git:不仅仅是 VCS

Git 可以促进不同位置(不同 Internet 连接级别)成千上万开发人员之间的分布式开发,没有明显的性能或访问瓶颈。Git 中支持这些基本要求的最重要的方面包括:

  • 使用中央存储库,但为项目中的每一名开发人员提供完整的项目源代码副本。这可以保证所有开发人员都能完成他们的工作,无论他们当前的连接情况如何。
  • 支持快速、可靠地在软件项目中创建不同的工作集(称为分支),可以跨分支共享更改(称为合并)。分支可以轻松支持不同版本的软件包,无论这些版本是永久的还是实验性的。合并一般是源代码控制系统中的关键方面,在面向分支的 VCS 尤其常见。
  • 可以轻松在开发人员子集之间共享分支和代码更改,这些更改无需先签入中央存储库。

这些设计决策及其实现是 Git 成功和流行的关键所在。当然,Git 也满足标准的 VCS 要求,比如不变性和问责制。不变性 表示将更改提交到存储库之后,它们就成为项目历史记录不变的一部分。即使之后可以取消(称为还原),但这些更改和取消这些更改的替换代码也都成为项目历史不变的一部分。问责制 意味着可以轻松确定谁做了什么更改以及何时做了该更改。为什么做出更改仍然无法确定(尽管在提交更改时需要一些注释),但至少我们知道该去问谁。

Git 使用内部索引跟踪文件的状态和存储库中的目录。它还存储能够反映这些文件和目录更改的对象,以简化后续更新。Git 索引和这些对象与本地存储库中的实际文件和目录不同 — 这个模型可以轻松确定本地更改,但尚未提交给本地存储库或远程中央存储库(如果有)的文件和目录。有些 Git 命令可以操作索引,另一些可以操作实际文件和目录内容,如果您用错了命令,可能会发现文件没有被更新。

获取 Git

大部分 Linux 发行版在其包存储库中提供 Git 包。在 Ubuntu、Debian 和使用 .deb 包格式的类似系统中,您需要安装 git-core 包。在基于 RPM 的系统上,比如 Fedora、Red Hat、Centos、SUSE 和 Mandriva,主 Git 包称为 git。基本的 Git 包需要使用 Perl、Perl 库进行加密和错误处理,系统上还将安装补丁实用工具。

如果您的 Linux 系统需要最新最棒的 Git 版本,Git 网站还提供了预打包 .deb 和 RPM 包,以及最新 Git 源代码(如果需要构建自己的版本)的下载链接。Git 站点还提供了预编译版的 Git for Mac® OS X、本机 Windows®、Windows 系统上的 Cygwin、Sun/Oracle Solaris® 系统的下载链接。目前,IBM® AIX® 和 Hewlett-Packard® HP-UX 系统管理员必须通过它们平台的源代码构建 Git。有关获取和构建平台 Git 的信息,请参见 参考资料

主 Git 包包含 git 可执行文件和一些辅助 Git 应用程序。可以猜想,还有大量其他与 Git 相关的包可供使用。一些常用的 Git 相关包包括:

  • git-cola:用于处理 Git 存储库中文件和目录的 GUI
  • git-doc:在本地安装 Git 用户手册、教程和文档
  • git-gui:浏览和处理 Git 存储库的 GUI;使用 gitk
  • git-web:基于 Web 的图形化 Git 存储库接口
  • gitk:一个浏览和处理 Git 存储库的简单 GUI
  • qgit:基于 Qt 的图形化应用程序,用户查看和浏览 Git 存储库

git-gui、git-web、gitkqgit 包提供类似的功能,git-web 是基于 Web 的,所有其他包都是在本地运行的。刚开始接触 Git 时,这些包都非常有用,而 git-web 包可能是分布式环境中的最佳选择。

如果您对 Git 感兴趣,但是又已经使用了其他 VCS,那么以下包非常有用:

  • git-cvs:该包提供 Git 与 CVS 存储库之间的交互操作,使您能将 CVS 存储库和更改历史导入 Git,在 Git 中进行处理,将更改重新合并到 CVS 存储库,并导入 CVS 存储库的更新。
  • git-svn:该包提供 Git 和 Subversion 存储库之间的交互操作,使您能将 Subversion 存储库和更改历史导入 Git,在 Git 中进行处理,将更改重新合并到 Subversion 存储库,并导入 Subversion 存储库的更新。

通用 Git 命令

Git 就是我们常说的命令套件 平台,这意味着您使用的主命令是 git,命令行的第一个参数表示您想运行的特定 Git 命令。

任何时候,只要运行不带任何参数的 git 就可以看到通用 Git 命令清单。以下列出部分该清单:

  • add。向 Git 索引添加一个新文件。您必须提交该文件,以向 Git 存储库添加其内容。
  • branch。使您能列出签出(check out)的分支,确定当前处理的分支,创建新分支,销毁已经创建或签出的本地分支副本。该命令不能切换到其他分支:使用 Git 的 checkout 命令实现。
  • checkout。签出指定的分支或文件/目录。如果签出一个分支,则该分支将成为工作分支。如果指定一个特定文件或目录,则该文件或目录将更新以匹配当前签入工作分支目录的版本。您还可以使用该命令基于指定现有分支的可选跟踪更改新建一个分支。
  • commit。记录 Git 索引中的文件和目录更改。指定想提交更改的文件和目录时,您可以使用 -a 选项将所有挂起更改添加到 Git 跟踪的文件,也可以使用 --interactive 选项选择希望一起提交的文件和目录更改。(如果处理的是几个涉及大量文件的不同任务,但又想一起提交某些更改,那么后者将非常有用。提交是对本地存储库执行的操作;如果使用远程中央存储库,那么必须使用 Git 的 push 命令将本地更改传输到远程存储库。)
  • diff。显示本地文件与其他提交之间,或者两次不同提交的文件之间的不同之处。使用该命令时常常指定文件名,显示指定文件与提交到当前分支存储库的文件版本之间的不同之处。
  • fetch。从另一个存储检索索引更新,指出已经创建的新标记,并提供有关已经提交到该存储库但尚未在本地显示的文件或目录更改的信息。然后,可以使用 git log 命令检测可用更改。要在 fetch 之后实际检索相关文件更改,您可以使用 git pullgit rebase 命令。
  • grep。查询或显示当前分支中文件模式的匹配。该命令支持大部分您所喜爱的 GNU grep 选项。
  • log。展示当前分支或当前分支中指定文件的提交日志信息。
  • merge。将一个分支的更改合并到另一个。该命令提供一些选项,用于确定是否自动提交合并的更改,这使您能在实际接受这些更改之前了解合并的影响。
  • mv。重命名 Git 当前跟踪的文件、目录或符号链接。
  • pull。从另一个存储库检索索引更新,并将其合并到当前分支的文件或目录。
  • push。使用本地索引和对象更改信息更新远程存储库。
  • rebase。更新当前分支以匹配远程分支,修改尚未传输到远程分支的本地提交,使其适用于远程分支的当前状态。这是一个强大而危险的命令,因为它会根据需要重写命令以便合并。如果对远程存储库的更改频率和范围不大,可以使用 git pull 命令替代。
  • rm。移除 Git 当前跟踪的文件、目录或符号链接。
  • stash。临时将当前更改推入堆栈,并将当前签出返回到原始状态。git stash save 在本地堆栈中保存当前更改,git stash apply 检索并重新应用它们。如果您想 fetch 远程更改或者要在不永久提交进行中的更改的情况下执行 rebase,那么这将非常有用。
  • status。展示当前分支的状态,指出尚未提交的更改、不会跟踪的文件等等。

所有 Git 命令都接受 --help 选项,这是一个常用的选项,可以获取任何命令的详细信息,查看每个命令接受的命令行选项清单等等。您还可以执行命令 git help command 获取有关任何 Git 命令的帮助。

有关 Git 命令的完整清单,请执行 man git 命令查看 Git 的在线参考信息。

设置一个新的 Git 项目

要对不受任何修订控件控制的现有项目使用 Git,请执行以下步骤:

  1. 更改为包含项目源代码的目录:
    $ cd www.move2linux.com
    $ ls
    greenbar_wide.gif  images  index.html  legacy.html  services.html
  2. 使用 git init 命令在当前目录中创建一个空的存储库:
    $ git init
    Initialized empty Git repository in .git/
  3. 还可以选择使用 git status 命令查看新项目的状态。

    该命令列出当前目录中未跟踪的所有文件和目录,表示 Git 了解它们的存在但是没有指示跟踪该文件:

    $ git status
    # On branch master
    #
    # Initial commit
    #
    # Untracked files:
    #   (use "git add <file>..." to include in what will be committed)
    #
    #       greenbar_wide.gif
    #       images/
    #       index.html
    #       legacy.html
    #       services.html
    nothing added to commit but untracked files present...
  4. 将项目中的文件和目录添加到新的 Git 存储库。

    您可以明确列出它们或者使用句点(.)作为 “当前目录内容” 的传统快捷方式:

    $ git add .
  5. 再次执行 git status 命令验证当前目录及其子目录中所有文件都已经添加到新项目中:
      $ git status
      # On branch master
      #
      # Initial commit
      #
      # Changes to be committed:
      #   (use "git rm --cached <file>..." to unstage)
      #
      #       new file: greenbar_wide.gif
      #       new file: images/digits/b/0.gif
      #       new file: images/digits/b/1.gif
      #       new file: images/digits/b/4.gif
      #       new file: images/digits/b/5.gif
      #       new file: images/digits/b/6.gif
      #       new file: images/digits/b/7.gif
      #       new file: images/digits/b/8.gif
      #       new file: images/digits/b/9.gif
      #       new file: index.html
      #       new file: legacy.html
      #       new file: services.html
      #
  6. 执行 git commit 命令签入初始文件。

    除非使用 -m "commit message" 选项在命令行上指定提交消息,否则该命令将启动默认编辑器,您必须在其中输入一个与提交关联的注释。保存命令和现有编辑器之后,Git 将签入与更改关联的文件,并显示有关提交和关联文件的信息。

    $ git commit 
    Created initial commit dfbd6cc: Initial checkin
    12 files changed, 285 insertions(+), 0 deletions(-)
    ...

现在,您可以使用上述命令在 Git 中处理项目文件了。

更改现有 Git 项目

如果可以更改其他人已经创建的 Git 项目,事情会变得更加简单。您可以使用 git clone 命令创建自己的 Git 项目工作副本:

$ git clone /home/jake/src/maps

该示例在当前工作目录中创建一个 Git 地图项目副本。您可以将目录更改为地图项目副本,并使用之前讨论的命令在 Git 中开始处理该项目中的文件。

复制 Git 项目

复制位于另一个机器上的 Git 项目非常简单。Git 默认支持 Secure shell (SSH) 和 HTTP 协议,如果远程系统上运行着 Git daemon,还可以使用特别有效的 git 协议导出感兴趣的项目。Git 默认使用 SSH,因此用于通过 SSH 复制目录的语法与您的预期完全一致:

$ git clone remote-machine:/home/jake/src/maps
$ git clone ssh://remote-machine/home/jake/src/maps

注意:通过 SSH 复制 Git 项目要求您授权对远程系统的访问,这是最好使用 git 协议的又一原因,尽管它要求您运行 Git daemon。

使用 Git 进行分布式 Web 开发

存储库(如上文创建的存储库)包含 Git 项目中所有文件的工作副本,以及 Git 跟踪更改、分支、标记等内容时需要的所有文件。默认情况下,推入包含项目文件特定工作副本的 Git 存储库仅更新该项目的索引,而不是项目中的实际文件。这是因为如果您同时还在更新的文件上工作,更新文件本身将造成合并冲突。

要创建可以推入的 Git 项目,您需要创建一个所谓的 存储库 — 即不包含文件工作副本,仅包含 Git 索引、反映该索引更新的对象,以及 Git 要求的其他文件。因为裸存储库不包含文件的工作副本,因此没人能真正在其中工作,它仅用作在其所包含项目上工作的所有开发人员的收集点。

执行以下步骤可在Web 服务器上创建 一个包含网站内容且您和其他开发人员可以执行推入的 Git 存储库。该过程还将使用包含新 Git 存储库签出版本的目录以及文件推入共享 Git 存储库时更新的目录替换现有的 Web 内容目录。有许多方法可以做到这一点:为了该示例的简单易懂,您的网站上仅涉及 HTML 内容。处理网站的其他部分时可以使用相同的原则。

  1. 在 Web 服务器上使用 SSH,并更改为包含 Web 内容的目录。

    如果 Git 没有跟踪您的 Web 内容,则使用 上一节 中描述的过程在此设置 Git 存储库。例如:

    $ ssh somehost
    $ cd /var/www/html
    $ git init
    $ git add .
    $ git commit -m "Initial commit"
  2. 更改到上一级目录,通过复制刚刚为 Web 内容创建的项目创建一个裸 Git 存储库:
    $ cd ..
    $ git clone --bare html html.git

    使用 .git 扩展名创建裸存储库是一个好习惯,这样您就可以使用 gitweb 等工具查看,这些工具要求该扩展名。

  3. 重新命名现有的 Web 目录,并通过复制裸存储库使用相同的名称新建一个 Git 项目:
    $ mv html html.OLD && git clone html.git html

    新项目目录包含对应于 Web 服务器内容的 Git 项目中所有文件的签出版本。

  4. 在裸存储库的 hooks 子目录中编辑(或创建)一个更新后处理(post-update)脚本,将更改推入包含 Web 内容的签出文件的新项目目录。

    该脚本将在裸存储库跟踪的任何文件更新后执行一次。确保该脚本可以执行:

    $ pushd html.git/hooks
    $ emacs post-update
    $ chmod 755 post-update

    更新后处理脚本应该类似 清单 1

    清单 1. 更新后处理脚本
    #!/bin/bash
    #
    WEB_DIR="/var/www/html"
    export GIT_DIR="$WEB_DIR/.git"
    pushd $WEB_DIR > /dev/null
    git pull
    popd > /dev/null

    注意,该脚本必须使用 /bin/bash 作为解释器才能使用该 shell 的 pushdpopd 内置命令。如果已经存在更新后处理文件,您可以在验证解释器之后将脚本的其余部分添加到该文件。您必须确保现有更新后处理脚本中的现有命令前面没有 exec 命令,它会阻止执行文件中的后续行。

现在,您或任何开发人员可以在 Web 服务器上复制裸存储库并开始处理您的网站。您和任何其他开发人员都可以一起处理网站的文件,可以执行任何以下任务:

  • 在网站签出中处理文件并直接推入共享的中央存储库。
  • 在网站签出中处理文件并将您签出中的提交更改推入合作方的签出。在将文件推入共享中央存储库(及网站)之前您可以协作处理这些文件。

尽管 Git 比大部分 VCS 要快很多,但如果您要处理的是一个大型的复杂网站且用户很多,在网站目录中直接工作是不现实的。访客可能会接受到某些文件的更新,而其他文件却尚未更新。如果尚未收到 CSS 更新,而使用它们的更新页面已经被加载,那么很可能出现问题。通常可以使用两种方法避免这个问题:一是在 Web 服务器中使用符号链接之类的解决方案以实际指向您的内容目录,并在希望激活更新内容时切换它们;二是修改 Web 服务器配置文件以指向其他目录,并在希望激活新内容时重启 Web 服务器。

结束语

Git 是一个强大、灵活的 VCS,提供了许多协作功能,因为它的目标用户就是分布式开发人员。Git 以全新而独特的方式支持协作开发,包括网站的共享开发,基于 Web 的应用程序等等,完全值得花些时间理解它的内部机制,并学习常用的 Git 命令子集。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=435167
ArticleTitle=Git 改变了分布式 Web 开发规则
publish-date=10152009