[版本控制器Git] Git 远程仓库 | 多人协作 | 企业级 Git Flow 开发模型

一、远程仓库

1.1 分布式与主从之分

Git 是分布式版本控制系统,同一个 Git 仓库可以分布到不同机器上。最早只有一台机器上有原始版本库,此后别的机器可以"克隆"它,而且每台机器上的版本库其实都一样,并没有主次之分。

其实一台电脑也能克隆多个版本库(不同目录),但是实际情况是找一台电脑充当"服务器"角色,每天 24 小时开机:每个人从它克隆一份到本机,把自己的提交推送上去,也从它拉取别人的提交。

1.2 自建服务器与托管平台

可以选择:

  1. 自己搭一台跑 Git 的服务器托管代码;
  2. 或者使用线上托管平台:GitHub 是国外网站、速度较慢,国内课堂与企业更常用码云(Gitee)

二、新建与克隆远程仓库

2.1 新建仓库

新建远程项目仓库时填写基本信息(仓库名 / 路径 / 是否开源),创建成功后还可以再把仓库设置为开源或私有。从创建好的远程仓库里能看到,之前本地学过的分支同样存在于远程仓库并被管理起来------刚创建的仓库有且只有一个默认的 master 分支。

2.2 两种传输协议

克隆用 git clone,后面跟远端仓库链接,链接从仓库页的"克隆/下载"里获取。SSH 和 HTTPS 是 Git 最常用的两种数据传输协议。

使用 HTTPS 方式没有前置要求,直接就能克隆,代价是每次都要输账号密码:

bash 复制代码
$ git clone https://gitee.com/hyb91/git_teaching.git
Cloning into 'git_teaching'...
Username for 'https://gitee.com': hyb91
Password for 'https://hyb91@gitee.com':
remote: Enumerating objects: 4, done.
Unpacking objects: 100% (4/4), 1.80 KiB | 1.80 MiB/s, done.
$ ls git_teaching/
README.en.md  README.md

SSH 协议使用公钥加密和公钥登录机制,更安全,但需要先把公钥放到服务器上由 Git 服务器管理。没配公钥就直接克隆,服务器会拒绝:

bash 复制代码
$ git clone git@gitee.com:hyb91/git_teaching.git
git@gitee.com: Permission denied (publickey).
fatal: Could not read from remote repository.

2.3 生成并添加公钥

  • 第一步:创建 SSH Key。先看用户主目录下有没有 .ssh 目录,里面有没有 id_rsa 和 id_rsa.pub,有就直接跳到下一步;没有就生成:
bash 复制代码
# 注意要输入自己的邮箱,然后一路回车使用默认值
$ ssh-keygen -t rsa -C "123123@qq.com"
Generating public/private rsa key pair.
Your identification has been saved in /home/hyb/.ssh/id_rsa
Your public key has been saved in /home/hyb/.ssh/id_rsa.pub
$ ls -a .ssh/
.  ..  id_rsa  id_rsa.pub  known_hosts

这两个就是 SSH Key 的密钥对:id_rsa 是私钥,不能泄露出去;id_rsa.pub 是公钥,可以放心告诉任何人。

  • 第二步:点开平台的"SSH 公钥"选项,把 id_rsa.pub 内容贴进去,确认后输入账号密码认证即可:

    再次克隆就成功了:

多人协作时,GitHub/Gitee 允许添加多个公钥,把每个人电脑上的 Key 都加上,就能在每台电脑上往远端提交推送。

2.4 origin 与 git remote

克隆之后,Git 会自动把本地 master 分支和远程 master 分支对应起来,并且远程仓库的默认名称是 origin:

bash 复制代码
$ git remote
origin
$ git remote -v
origin  git@gitee.com:hyb91/git_teaching.git (fetch)
origin  git@gitee.com:hyb91/git_teaching.git (push)

上面的输出显示了可以抓取和推送的 origin 地址;如果没有推送权限,就看不到 push 的地址。

三、推送与拉取

3.1 push

本地克隆成功后就可以往仓库里提交内容,内容先只进本地仓库,要推到远端就需要用 git push------它把本地分支版本上传到远程并合并,格式如下:

bash 复制代码
git push <远程主机名> <本地分支名>:<远程分支名>
# 如果本地分支名与远程分支名相同,可以省略冒号:
git push <远程主机名> <本地分支名>

把本地 master 推到 origin 主机的 master:

bash 复制代码
$ git push origin master
To gitee.com:hyb91/git_teaching.git
   c6ce3f0..7ce3183  master -> master

推送成功。使用 SSH 协议时不必每次输密码,方便很多;用 HTTPS 则每次推送都必须输入口令。

3.2 身份要一致

如果之前设置过全局的 user.name 和 user.email,这两项配置需要和 Gitee 上配置的用户名、邮箱一致,否则会出错;从来没设置过的,第一次提交时会报错,需要重新配置。

3.3 pull:拉取并合并

在 Gitee 上直接在线修改 README.md 后,远程仓库就领先本地一个版本。想让本地保持最新,需要拉取远端代码并合并:

bash 复制代码
git pull <远程主机名> <远程分支名>:<本地分支名>
# 如果远程分支是与当前分支合并,则冒号后面的部分可以省略
git pull <远程主机名> <远程分支名>

实际执行结果:

bash 复制代码
$ git pull origin master
 * branch            master     -> FETCH_HEAD
   7ce3183..60e6b0a  master     -> origin/master
Updating 7ce3183..60e6b0a
Fast-forward
 README.md | 2 ++
 1 file changed, 2 insertions(+)

push 是"上传并合并",pull 是"取回并合并",本地与远端一致时 pull 的结果就是 Fast-forward 快进策略。

四、三个实用的仓库级配置

4.1 .gitignore:忽略不该提交的文件

日常开发中有些文件不想或不应该提交到远端,比如保存了数据库密码的配置文件。在 Git 工作区根目录下创建一个 .gitignore 文件,把要忽略的文件名填进去,Git 就会自动忽略。

gitee 在创建仓库时就可以帮我们生成,但需要主动勾选;当时没选,事后在工作区补建一个也行。例如想忽略所有以 .so 和 .ini 结尾的文件:

检验 .gitignore 是否生效的标准就是 git status 是否显示 working tree clean。可以看到相应后缀的文件被忽略:

三个细节:

  • 添加一个被忽略的文件,用 git add -f [filename] 强制添加:

  • 查询规则命中: git check-ignore -v a.so,(eg输出: .gitignore 第 3 行 *.so 忽略了该文件

  • 规则 .* 会把 .gitignore 自己也排除掉,加一条例外规则 !.gitignore 即可(把指定文件排除在规则外的写法: !+文件名

    可以看到文件被追踪了:

4.2 alias:给命令起外号

Git 支持简化命令:

bash 复制代码
$ git config --global alias.st status      # git st 等价于 git status
$ git config --global alias.last 'log -1'  # git last 显示最近一次提交

--global 是全局参数,对本机所有 Git 仓库生效;不加则只对当前仓库起作用。

配置别名:

4.3 tag标签

tag 可以简单理解为对某次 commit 的标识,相当于起了个别名。项目发布版本时,针对最后一次 commit 打一个 v1.0 这样的标签来标识里程碑,意义在于:commit id 是一串难以记住的十六进制数字,标签则一定起得容易记住且有含义,需要回退到某个重要版本时能快速定位。

bash 复制代码
$ git tag v1.0                 # 默认打在最新提交上
$ git tag                      # 查看所有标签
$ git tag v0.9 c6ce3f0         # 在指定 commit 上打标签
$ git tag -a v1.0 -m "说明" [commit_id]   # 带说明的标签
$ git show v1.0                # 查看标签信息

注意标签不是按时间顺序排列,而是按字母排序。标签只存储在本地、不会自动推送到远程,所以打错了可以安全删除(git tag -d v0.9)。要推送到远端:

bash 复制代码
$ git push origin v1.0     # 推送指定标签
$ git push origin --tags   # 一次推送全部标签

已经推到远程的标签要删除就麻烦一点:先在本地 git tag -d v1.0,再从远程删除------删除命令也是 push,但格式特殊:

bash 复制代码
$ git push origin :refs/tags/v1.0
 - [deleted]         v1.0

实际使用:

给当前分支打标签和查看:

给当前分支的commit打标签:

打附有说明的标签:

删除标签:

推送标签:

同时删除在本地和远端的标签:

五、多人协作模式一:同一分支上协作

准备工作:

  1. 把项目克隆到本机(可以在 Windows 上再克隆同一仓库模拟多人协作)
  2. 将用户添加进开发者列表

实际项目中绝对不允许直接在 master 分支上修改代码(为了保证主分支稳定),所以开发新功能时常常新建其他分支供迭代使用。

在 Gitee 上新建远程 dev 分支后,本地拉取就能看到:

bash 复制代码
$ git pull
 * [new branch]      dev        -> origin/dev
Already up to date.
$ git branch -r
  origin/HEAD -> origin/master
  origin/dev
  origin/master
$ git checkout -b dev origin/dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

这里有个容易忽略的点:git branch 只能查看本地分支,要看远程分支得加 -r,而前提是先 pull 一次拉取最新远端内容。

两名开发者A和B在 dev 上各自提交并 push origin dev。但B在推送时可能失败------因为B的最新提交和A已推送的提交可能有冲突。解决办法:先 git pull 把最新提交从 origin/dev 抓下来,在本地合并、解决冲突,再推送。

bash 复制代码
$ git pull
Updating 305f78a..72c5345
Fast-forward
 file.txt | 1 +                 # 看到小伙伴的代码了

整个流程可以总结成五步:

  • 先试着 git push origin branch-name 推送自己的修改;
  • 推送失败说明远程分支比你本地新,先 git pull 试图合并;
  • 合并有冲突就解决冲突,并在本地提交;
  • 没有冲突或解决掉冲突后再 push 就能成功;
  • 功能开发完毕,把分支 merge 进 master,最后删除分支。

六、多人协作模式二:一人一个 feature 分支

多需求并行时,不该让多人挤在一个分支上,而是一个需求或一个功能点就创建一个 feature 分支。

6.1 接手别人未完成的分支

如果开发者A需要临时接手B的工作,此时要做的操作是:

bash 复制代码
# 必须先拉取远端仓库内容
$ git pull
 * [new branch]      feature-2  -> origin/feature-2
# 确认远程已有 feature-2
$ git branch -a
  remotes/origin/feature-2
$ git checkout -b feature-2 origin/feature-2
Branch 'feature-2' set up to track remote branch 'feature-2' from 'origin'.

建立关联:checkout -b feature-2 origin/feature-2 把本地分支和远程分支关联起来,否则将来只用 git push 推送会失败

关联建立后本地代码与远端严格一致,接着开发并推送:

bash 复制代码
$ git commit -m"modify function2" && git push origin feature-2
   e1233f1..1079ae7  feature-2 -> feature-2

6.2 规范合并到 master

功能开发完毕后必须合并到 master ,步骤如下:

  1. 切回 master 拉取,保证本地 master 是最新内容
  2. 再切回自己的分支 merge master,让冲突在自己的分支上解决
  3. 解决冲突后,切换回 master 分支合并
  4. 推送本地 master 到远程(pull request表单
bash 复制代码
$ git checkout master && git pull          # 保证本地 master 最新
$ git checkout feature-1 && git merge master   # 在自己的分支吸收 master
$ git push origin feature-1                # 保证远程分支也是最新的
$ git checkout master && git merge feature-1   # 再合并回 master
$ git push origin master

Q:既然最终要合并到 master,为什么合并前还要 push origin feature-1?

A:两个原因------feature-1 已经 merge 进来新内容,为了保证远程分支最新;而实际开发中 master 的 merge 操作一般不是我们自己在本地做,而是由其他人员或平台在远程分支上操作,所以要保证远程分支是最新的。另外,如果 merge 出现冲突,不要忘记必须 commit 之后才能 push。

6.3 远程分支删除但本地分支还在的解决方法

远程分支删除后,git branch -a 依然能看到它们的残留。用 git remote show origin 可以查看 remote 地址、远程分支以及本地分支的对应关系,那些远端已不存在的分支会被标成 stale:

bash 复制代码
  Remote branches:
    master                        tracked
    refs/remotes/origin/dev       stale (use 'git remote prune' to remove)

按提示执行 git remote prune origin 即可清理:

bash 复制代码
$ git remote prune origin
 * [pruned] origin/dev

七、企业级开发模型

7.1 DevOps 的由来

一个软件从零到交付,大致包括规划、编码、构建、测试、发布、部署和维护几个阶段。最初程序简单,一个人能包办全部;随着软件规模与复杂度攀升,开始出现精细化分工。但传统 IT 组织下开发团队(Dev)追求变化、运维团队(Ops)追求稳定,双方诉求冲突:敏捷团队把持续交付作为目标,运维团队则为了线上稳定强调变更控制,部门墙由此建立,不利于 IT 价值最大化。

为了弥合两者鸿沟,DevOps 登上舞台。它是 Development 和 Operations 的组合词,是一种重视开发人员与 IT 运维人员之间沟通合作的文化、运动或惯例;通过自动化"软件交付"和"架构变更"的流程,让构建、测试、发布软件更加快捷、频繁和可靠。而一个软件的迭代说到底是代码的迭代,代码要管理就离不开 Git------这就是 Git 与 DevOps 的关系。

7.2 四类环境

  • 开发环境:程序员日常开发调试用的服务器,为方便调试一般打开全部错误报告和测试工具,是最基础的环境;
  • 测试环境:开发环境到生产环境的过渡环境,程序在它上面工作不正常,就肯定不能发布到生产机;
  • 预发布环境:为避免测试环境与线上环境差异导致缺陷漏测而设立,配置基本与生产环境一致,是产品质量最后一道防线;它不在线上集成服务器范围内,是单独的一些机器;
  • 生产环境:正式提供对外服务的线上环境,我们在移动端或 PC 端访问到的 App 都属于它。

主线可以概括成开发 → 测试 → 上线。规模稍大的公司环境不止这几个,正式上线前可能还有仿真/灰度环境,也可能有多套测试环境以满足不同版本的测试需要。一句话:一个项目的开始从设计开始,而一个项目的成功则从测试开始------测试体系的完善程度,是衡量软件企业整体水平的重要指标之一。

7.3 Git 分支设计规范

针对不同环境,一般会设计如下分支:

分支 名称 适用环境
master 主分支 生产环境
release 预发布分支 预发布 / 测试环境
develop 开发分支 开发环境
feature 需求开发分支 本地
hotfix 紧急修复分支 本地

各分支的职责边界:

  • master 是只读且唯一分支,用于部署到正式发布环境,一般由合并 release 得到;任何情况下不允许直接在上面改代码,所有推送都应该打 tag 做记录便于追溯,且该分支不可删除;
  • release 基于 develop 创建(前提是本次上线所有 feature 已合并进 develop),可部署到测试或预发布集群,命名以 release/ 开头,建议 release/version_publishtime;它主要用于提交测试人员做功能测试,提测以它的代码为基准;若在它上面测出问题,需要回归验证 develop;它属于临时分支,上线后可选删除;
  • develop 基于 master 创建,同样是只读且唯一分支,始终保持最新完成以及 bug 修复后的代码,可部署到开发环境对应集群;可根据需求大小决定由 feature 合并,还是直接在它上面开发(非常不建议);
  • feature 以 develop 为基础创建,命名以 feature/ 开头,建议 feature/user_createtime_feature;开发完成后需合回 develop,需求一旦上线便删除;
  • hotfix 是线上 bug 修复分支,线上出现紧急问题时基于 master 创建,命名以 hotfix/ 开头,建议 hotfix/user_createtime_hotfix;修复完成后合并到 master 和 develop 并推送远程,一旦修复上线便删除。

7.4 Git Flow 不是万金油

上面这套就是企业级常用的 Git Flow 模型,但它并不适用于所有团队、所有环境和文化。如果你采用持续交付,就会想要尽可能简化交付过程的东西;有人喜欢基于主干的开发模式、喜欢用特性标志,而站在测试角度看,这些反倒会吓一跳。

关键在于站在自己团队或项目的角度思考:这种分支模型能解决哪些问题?会带来哪些问题?它为哪种开发提供更好的支持?分支模型最终都是为了让人更容易协作开发,所以要考虑使用者的需求,而不是盲目听信所谓"成功的分支模型"。各家规范会有细微差异,但万变不离其宗,都是为了效率与稳定。

相关推荐
我先去打把游戏先1 小时前
Git-Rebase使用指南-从原理到实战
服务器·git·嵌入式硬件·物联网·ubuntu
kaixin_learn_qt_ing4 小时前
我有两个程序,它们在不同的项目中,但是它们复用了一部分头文件,它们在两个不同的git仓库中,有什么好的办法可以让它们复用吗?
git
greatofdream5 小时前
git rebase时如何解决merge节点冲突问题
大数据·git·elasticsearch
敲敲敲-敲代码20 小时前
git安装记录
git
我是人✓20 小时前
Git入门:从零开始掌握分布式版本控制
分布式·git
Aurora_eye21 小时前
Git 笔记
笔记·git
解道Jdon1 天前
FigCraft把Figma用Rust从零重写了一遍
ide·windows·git·svn·eclipse·github·visual studio
橙橙笔记1 天前
QT安卓软件的制作
android·git·qt·软件
我是人✓1 天前
Git远程协作与标签管理全攻略
git