4 进阶篇
4.1 团队协同开发
4.1.1 账号权限管理
要求:团队里的每个人都对仓库有写权限,又不能给每个人在服务器上建立账户. 那么提供 SSH 连接就是唯一的选择了。
方法 1: 是给每个人建立一个账户,直截了当但过于繁琐。反复的运行 adduser 并且给所有人设定临时密码可不是好玩的。
方法 2: 是在主机上建立一个 git 账户,让每个需要写权限的人发送一个 SSH 公钥,然后将其加入 git 账户的 ~/.ssh /authorized_keys 文件。这样一来,所有人都将通过 git 账户访问主机。这丝毫不会影响提交的数据——访问主机用的身份不会影响 commit 的记录。
方法 3: 是让 SSH 服务器通过某个 LDAP 服务,或者其他已经设定好的集中授权机制,来进行授权。只要每个人都能获得主机的 shell 访问权,任何可用的 SSH 授权机制都能达到相同效果。
方法 2 的详细步骤
- 产生公钥
ssh-keygen -C "你的 email 地址" -t rsa
该命令将生成一对非对称的公\私密钥,默认它们被存储在:
- XP/2003 用户:
c:\Documents and Settings\登陆名\.ssh
- Vista 用户:
c:\Users\登陆名\.ssh
- linux:
~/.ssh
- 在 linux 服务器上将公钥加到 git 用户的 authorized_keys 文件中。
类似工具 :使用 Gitosis 的多用户访问
在 gitosis 中,有一个叫 authorized_keys 的文件,里面包括了所有授权可以访问仓库的用户的公钥(public key), 这样每个用户就可以直接使用'git'用户来推送(push) 和拉(pull) 代码。
译者注 1: github.com 就是采用这种方式来配置私有(仓库) 访问。
译者注 2: Gitosis 配置(中文)
4.1.2 开源协同开发
示例: git@github.com :looly/elasticsearch-definitive-guide-cn.git
开始我对 Pull Request 流程不熟悉,后来参考了 @numbbbbb 的《The Swift Programming Language》协作流程,在此感谢。
- 首先 fork 我的项目
- 把 fork 过去的项目也就是你的项目 clone 到你的本地
- 运行
git remote add looly [git@github.com](mailto:git@github.com):looly/elasticsearch-definitive-guide-cn.git
把我的库添加为远端库 - 运行
git pull looly master
拉取并合并到本地 - 翻译内容
- commit 后 push 到自己的库(git push origin master)
- 登录 Github 在你首页可以看到一个 pull request 按钮,点击它,填写一些说明信息,然后提交即可。
1-3 是初始化操作,执行一次即可。在翻译前必须执行第 4 步同步我的库(这样避免冲突),然后执行 5-7 既可。
4.2 分支 branch 和 tag 管理
版本号:x.x.x=主版本号.次版本号.发布序列。主版本号只用在重要架构级或功能大升级。
表格 branch 和 tag 比较表
说明 | 主要命令 | |
---|---|---|
tag 标签 | 组成为 vx.x.x,完整的版本号,用来标识阶段性的发布版本(不再编辑的分支), 相当于 release-x.x.x git push --tags #推送全部 taggit push origin [xxx] #推送单个 tag 到远程git push origin -d [xxx] #删除远程指定 tag | git tag # 查看 git tag -a [xxx] # 打 tag git tag -d [xxx] #删除本地指定 tag |
branch 分支 | 组成为 x.x,其中含义两个固定分支 master 和 devel。 如果项目不是太复杂,devel 分支将取代所有版本分支。 # 查看远程分支,查看本地不用 -a $ git branch -a $ git push origin [xxx] # 推送分支到远程$ git push origin :[xxx] # 删除远程分支,或者将:改为--delete | # 创建分支git checkout --orphan [空分支] git checkout -b [to] [from] git branch -d [xxx] #删除分支git checkout [xxx] #切换分支git merge [to] #合并分支git branch -m [old] [new] # 重命名分支 |
备注:命令参数如-d/-D 大小写不敏感。tag 和 branch 推送远程的操作命令类似。
图 2 git 客户端里的 revision graph
说明 :最下面 e35a6b9 是仓库第一个 commit 的 SHA-1 值,黄色字体 v1.0.0/v1.1.0
是 tag,蓝色字体 master/develop
是远程分支,红色字体 distributed 是本地当前分支。带有 origin 前缀的都是远程分支。理论上 tag 要一直保持在 master
分支里。
4.2.1 分支管理策略
分支名称 | 使用场合 | 注意事项 |
---|---|---|
主分支 master | 所有提供给用户使用的正式版本,都在这个主分支上发布。用来分布重大版本。 | |
开发分支 develop | 日常开发。 包括创建分支、切换分支和合并分支。 | gitcheckout−bdevelopmastergit checkout -b develop mastergitcheckout−bdevelopmaster git checkout master $ git merge --no-ff develop |
临时性分支 | 用于应对一些特定目的的版本开发。如 * 功能(feature)分支~ feature-* * 预发布(release)分支~ release-* * 修补 bug(fixbug)分支~ fixbug-* | feature/release 从 develop 分支分出,fixbug 从 master 分支分出。 临时性分支合并到 develop 分支后可删除。 release 和 fixbug 分支合并到 master 后可打 tag。 |
备注:tag 需从 master 打,标识为 vx.x.x,如 v1.2.0
1. 主分支 Master
首先,代码库应该有一个、且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。
2. 开发分支 Develop
主分支只用来分布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做 Develop。这个分支可以用来生成代码的最新隔夜版本 (nightly)。如果想正式对外发布,就在 Master 分支上,对 Develop 分支进行 合并(merge)。(备注:被 master 合并分布后,也可以反向合并 master 或从其它分支获取修改,进行下一步的修改)
# 创建分支:Git 创建 Develop 分支的命令
$ git checkout -b develop master
# 切换分支:将 Develop 分支发布到 Master 分支的命令
$ git checkout master
# 对 Develop 分支进行合并,缺省合并是快进式合并~即将 master 直接指向 develop
# -no-ff 参数会执行正常合并,在 Master 分支上生成一个新节点。推荐使用-no-ff,可以保持演进的清晰(被合并分支 develop 会多一个指向 master 某 commit 的节点)。
# 若 develop 的修改未提交到远程,就合并到本地 master,那么合并后 develop 远程分支并不会出现修改部分,这样会造成 develop 与 master 并不同步,在进行 develop 开发前建议先同步远程 master(会增加很多来自 master 的 commit log)。develop 开发前根据需要从不同的远程分支更新内容,develop 本身的日志基本无意义。
$ git merge --no-ff develop
3. 临时性分支
临时性分支,用于应对一些特定目的的版本开发,开发合并后可以删除此分支。临时性分支主要有三种:
- 功能(feature)分支,为了开发某种特定功能,从 develop 分支分出。feature 分支开发完需合并到 develop。
- 预发布(release)分支,develop 合并到 master 之前提供的测试分支,从 develop 分支分出。
- 修补 bug(fixbug)分支,从 Master 分支上面分出来的。relaese/fixub 分支开发完需合并进 Master 和 Develop 分支,合并到 master 后可以打 tag。
示例 1:修补 bug 分支 fixbug-x 流程(从 master 创建,合并到 maste 打 tag,再合并到 devel 删除分支)。
# 创建分支 fixbug-x
$ git checkout -b fixbug-x master
# 合并到 master 分支,并 tag
$ git checkout master
$ git merge --no-ff master
$ git tag -a v1.2.2
# 合并到 devel 分支,并删除分支
$ git checkout devel
$ git merge --no-ff devel
$ git branch -d bugfix-x
4.2.2 分支管理实例
1) 本地管理分支
# create branch, branch_name default: local
$ git branch [branch_name]
# switch to branch
$ git checkout [branch_name]
# merge branch,将 branch_name 合并到当前分支
$ git merge [branch_name]
# delete branch
$ git branch –D [branch_name]
2) 分支协同开发
a) 首先在本地产生 branch, 工作过后,将本地 branch 合并到本地 master.
b) git pull 取其它成员的工作树到本地,如果有修改,将自动合并到本地 master
c) git push 将本地 master 分支更新到远程服务器。
4.3 子模块 submodule
Git 子模块(submodule)是一个强大的功能,用于在一个 Git 仓库中包含另一个 Git 仓库。它允许你将一个项目作为另一个项目的子目录进行管理。子模块常用于将依赖库或第三方代码集成到你的项目中,而无需将它们的代码直接合并到主项目中。
核心概念
子模块 :
- 子模块是一个 Git 仓库,它被嵌套在另一个 Git 仓库的子目录中。主仓库跟踪子模块的特定版本,而不是子模块的最新版本。
引用 :
- 主仓库存储子模块的 URL 和特定的提交 ID。每当子模块更新时,主仓库需要更新引用以跟踪子模块的新版本。
隔离性 :
- 子模块与主仓库的内容独立管理,允许你单独更新和管理子模块中的代码。
常用命令
添加子模块 :
git submodule add <repository> [<path>]
:- 将指定的 Git 仓库作为子模块添加到主仓库中。如果未指定
<path>
,则使用仓库名作为路径。
- 将指定的 Git 仓库作为子模块添加到主仓库中。如果未指定
git submodule add https://github.com/example/libfoo.git libfoo
初始化子模块 :
git submodule init
:- 初始化
.gitmodules
文件中列出的子模块。这会在本地工作目录中创建子模块目录,但不会自动更新内容。
- 初始化
git submodule init
更新子模块 :
git submodule update
:- 将子模块的内容更新到主仓库中指定的提交 ID。这个命令会检出子模块的正确版本。
git submodule update
克隆包含子模块的仓库 :
git clone --recurse-submodules <repository>
:- 克隆主仓库及其子模块。如果子模块不在克隆过程中自动初始化和更新,可以使用
git submodule update --init --recursive
进行更新。
- 克隆主仓库及其子模块。如果子模块不在克隆过程中自动初始化和更新,可以使用
git clone --recurse-submodules https://github.com/example/myrepo.git
查看子模块状态 :
git submodule status
:- 查看子模块的当前状态,包括当前提交 ID 和是否有任何未提交的更改。
git submodule status
更新子模块到最新提交 :
cd <submodule-path>
git pull origin <branch>
- 回到主仓库:
git add <submodule-path>
git commit -m "Update submodule to latest commit"
cd libfoo git pull origin main cd .. git add libfoo git commit -m "Update libfoo submodule to latest commit"
删除子模块 :
- 从主仓库中删除子模块的引用,并从
.gitmodules
文件中移除。
git submodule deinit <submodule-path> git rm <submodule-path> rm -rf .git/modules/<submodule-path>
- 从主仓库中删除子模块的引用,并从
示例
假设你有一个主项目,并且你想要将一个开源库作为子模块集成:
添加子模块 :
git submodule add https://github.com/someuser/somelibrary.git lib/somelibrary
这会将
somelibrary
仓库作为子模块添加到lib/somelibrary
目录。初始化和更新子模块 :
git submodule update --init --recursive
这会检出子模块的代码,并确保它处于主仓库指定的提交状态。
提交子模块更改 :
git add lib/somelibrary git commit -m "Add somelibrary as a submodule"
总结
Git 子模块提供了一种有效的方式来管理项目中的外部依赖。通过将其他 Git 仓库嵌套在主仓库中,子模块可以帮助保持代码的组织性和模块化。尽管子模块增加了一些管理复杂性,但它们是处理共享和复用代码的一种强大工具。使用子模块 时,请确保定期更新和维护,以确保依赖项始终保持在所需的版本。
4.4 filter-branch 全局修改分支
filter-branch 可以全局修改分支的历史记录。
1) 开源前修改用户信息
do_commit_filter()
{ # '=' seems 完全相同,通配符*不起作用
git filter-branch -f --commit-filter '
if [ "${GIT_AUTHOR_NAME}" = denny ];
then
GIT_COMMITTER_NAME="Denny Wu";
GIT_AUTHOR_NAME="Denny Wu";
GIT_COMMITTER_EMAIL="wuqifu@gmail.com";
GIT_AUTHOR_EMAIL="wuqifu@gmail.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD
}
do_env_filter()
{
git filter-branch -f --env-filter '
case "${GIT_AUTHOR_NAME} ${GIT_AUTHOR_EMAIL}" in
*enny*)
export GIT_AUTHOR_NAME="Denny Wu"
export GIT_AUTHOR_EMAIL="wuqifu@gmail.com"
;;
esac
case "${GIT_COMMITTER_NAME} ${GIT_COMMITTER_EMAIL}" in
*enny*)
export GIT_COMMITTER_NAME="Denny Wu"
export GIT_COMMITTER_EMAIL="wuqifu@gmail.com"
;;
esac
'
}
2)删除历史纪录中某个文件
# remove unnecessay file or directory, 如 passwords.txt
git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
4.5 小技巧
# git 通过 commit_id 找到开发分支。
$ git branch -r –contains [commit_id]
# git 通过代码 找到代码当前分支的 commit_id。
git log -S "代码关键字"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论