====== Git版本管理系统 ====== Git是一个分布式的版本管理系统,相比较传统的CVS、Subversion等集中式版本控制系统,有诸多不同。本文以一个小型的示例来讲述Git的简单用法。 ===== 环境 ===== ==== 硬件环境 ==== 这里用三台机器来搭建一个Git的测试环境,git-server作为Git服务器,git-client1与git-client2作为开发者机器,采用集中式协同模型: ^ 机器 ^ IP地址 ^ |git-server.example.org |192.168.176.91/24 | |git-client1.example.org |192.168.176.92/24 | |git-client2.example.org |192.168.176.93/24 | ==== git-server配置 ==== Git服务器的设置不是本文的重点,这里使用最简单的配置。 建立一个git用户负责Git的管理: useradd -m -s /bin/bash git 建立Git仓库目录: mkdir -p /git/repos chown git:git /git/repos ==== git-client配置 ==== 在git-client机器上为开发者创建帐户,git-client1上建立用户user1,git-client2上建立用户user2,如: useradd -m -s /bin/bash user1 user1和user2都通过ssh共用git-server上的git帐户,这是最简单的ssh使用方式。 为了方便,user1和user2都将自己的公钥放到git-server上,就不用每次输入密码了: ssh-keygen sh-copy-id git@git-server ===== 使用示例 ===== ==== 初始化版本库 ==== 在git-server上创建一个裸版本库: git init --bare /git/repos/test.git ==== user1克隆版本库 ==== user1克隆版本库test.git,由于此时test.git还没有任何内容,所以得到一个空的版本库: git clone git@git-server:/git/repos/test.git 设置基本的用户名和邮箱信息: git config user.name user1 git config user.email user1@example.org ==== user1新建文件 ==== user1新建一个文件test.txt,后续都用这个文件做例子: echo "git test file." > test.txt 完毕提交: git add test.txt git commit -m "Initial." 推送到服务器: git push origin master ==== user2克隆版本库 ==== user2也来克隆版本库,由于之前user1已经推送了内容,此时版本库就不再是空的了: git clone git@git-server:/git/repos/test.git 设置基本信息: git config user.name user2 git config user.email user2@example.org ==== user2建立develop分支 ==== 当前只有一个master主分支,一般开发应该建立专门的开发分支,master主要用来做正式版本的发布。 开发者都可以建立分支,这里假设刚好是user2来做: git branch develop 切换到develop分支: git checkout develop 更新文件内容,以模拟在develop分支上开始开发工作: echo "begin develop..." >> test.txt git add test.txt git commit -m "begin develop." 将develop分支推送到服务器上: git push origin develop ==== user1获得develop分支 ==== 先更新一下: git pull 对user1而言,服务器上的develop分支是一个远程分支,不能直接使用,需要基于该远程分支建立一个本地跟踪分支: git branch develop origin/develop 现在就可切换到develop分支了: git checkout develop ==== user1引入bug1 ==== 假设user1在开发过程中,不小心引入了一个bug,该bug将来可能会被发现。 用test.txt模拟: echo "bug1 by user1" >> test.txt git add test.txt git commit -m "bug1 by user1." 将当前开发成果推送到服务器: git push ==== user2引入bug2 ==== user2虽然首先创建了develop分支,并将其推送到服务器上,当两者并没有建立跟踪关系。下面的命令用来建立跟踪: git branch --set-upstream develop origin/develop 获得user1的更新: git pull user2也不小心引入了bug2: echo "bug2 by user2" >> test.txt git add test.txt git commit -m "bug2 by user2." 推送: git push ==== 完成第一个版本的开发 ==== user1又开发了一些代码,第一个版本开发结束了,后续开始测试: git pull echo "version 1.0 end." >> test.txt git add test.txt git commit -m "version 1.0 completed." git push user2获得最新代码,也进行测试: git pull 有些大的开发项目,会专门建立一个预发布分支,在该分支上进行正式发布前的测试,而不影响develop分支的继续开发。这里省略了,直接在develop上测试。 ==== 将develop分支合并到master分支 ==== 测试完毕,准备发布正式的版本了。 首先将develop分支上的内容合并到master分支上,假设是user1来完成: git checkout master git merge develop 完毕推送: git push ==== 建立第一个正式版本的tag ==== 假设同样由user1来建立: git tag -a v1.0 -m "Version 1.0" 推送tag: git push origin v1.0 至此,就可以基于tag v1.0发布软件的第一个正式版本了。 user2也可切换到master分支,查看结果: git checkout master git pull 当前test.txt的内容: git test file. begin develop... bug1 by user1. bug2 by user2. version 1.0 end. ==== 开始新一轮的开发 ==== 发布第一个正式版本后,开发组马不停蹄的开始了新一轮的开发工作。 user1首先上手: git checkout develop echo "begin next develop..." >> test.txt git add test.txt git commit -m "begin next develop." git push ==== 特性分支 ==== 在开发过程中,有两个新功能(特性)暂不能确定是否一定要在下一个正式版本中提供。故开发组决定这两个功能不在develop主线上开发,而是在develop上建立专门的特性分支,由user1负责特性1开发,user2负责特性2开发,后面再决定合并事宜。 user1在本地建立特性分支,开始特性1的开发: git branch user1/feature1 git checkout user1/feature1 第一版发布后,user2休息了一段时间,现在也回来了,开始特性2的开发: git checkout develop git pull git branch user2/feature2 git checkout user2/feature2 ==== user1完成特性1开发 ==== user1很顺利的开发完了特性1: echo "feature1 by user1." >> test.txt git add test.txt git commit -m "feature1 by user1." 经研究,开发组决定将特性1合并进develop分支,于是user1进行合并: git checkout develop git merge user1/feature1 git push ==== user2修复bug2 ==== user2在user2/feature2分支上干得正欢的时候,用户反馈,在正式版中发现了bug2,由于该部分代码当初是user2写的,于是交给user2来处理。 user2暂时放下手头的工作,在最新的正式版tag(v1.0)上建立一个bug修补分支: git branch v1.0_fix1 v1.0 git checkout v1.0_fix1 修改bug: echo "bug2 fixed by user2." >> test.txt git add test.txt git commit -m "bug2 fixed by user2." 改完的代码合并到master中: git checkout master git merge v1.0_fix1 git push origin master 为最新的正式版新建一个tag(v1.0.1): git tag -a v1.0.1 -m "Version 1.0 bugfix1" git push origin v1.0.1 将v1.0.1的产品提交给用户。 该bug在develop分支上也存在,所以也要合并进develop中: git checkout develop git pull git merge v1.0_fix1 git push 切换回user2/feature2分支继续: git checkout user2/feature2 ==== user1修复bug1 ==== 不久,bug1也被发现了,这次由user1来处理。在最新tag(v1.0.1)上建立分支: git pull git branch v1.0_fix2 v1.0.1 git checkout v1.0_fix2 修改bug: echo "bug1 fixed by user1." >> test.txt git add test.txt git commit -m "bug1 fixed by user1." 合并进master,建立新的tag(v1.0.2): git checkout master git pull git merge v1.0_fix2 git push git tag -a v1.0.2 -m "Version 1.0 bugfix2" git push origin v1.0.2 将v1.0.2的产品提交给用户。 同样要合并进develop中: git checkout develop git merge v1.0_fix2 git push ==== user2完成特性2开发 ==== user2也完成了特性分支的开发: echo "feature2 by user2" >> test.txt git add test.txt git commit -m "feature2 by user2." 开发组决定,特性2也合并进develop中: git checkout develop git pull git merge user2/feature2 git push ==== 后续 ==== 目前看来一切都有条不紊的进行着。 除了偶尔修补bug,项目组正热火朝天的开发着新产品,新的一版马上就要到来了! 当前master分支中的test.txt内容: git test file. begin develop... bug1 by user1. bug2 by user2. version 1.0 end. bug2 fixed by user2. bug1 fixed by user1. 当前develop分支中的test.txt内容: git test file. begin develop... bug1 by user1. bug2 by user2. version 1.0 end. begin next develop... feature1 by user1. bug2 fixed by user2. bug1 fixed by user1. feature2 by user2. ===== 总结 ===== 以上我们用一个测试文件,模拟了一般软件的开发流程,从中可以体会Git的灵活性。如果你决定使用Git做版本管理,值得多花点时间学习它。 {{tag>Git}}