Git分支协作与实战
在使用 Git 进行日常协作时,分支操作是最常打交道的功能之一。这篇笔记从“心法”到“招式”整理了一套更适合团队协作的实用指南。
Git 分支协作与实战
Main
任何开发与修复,都应该始于一个纯净的基准分支。操作前先确认当前所在分支,这是规避 Git 日常高频误操作的底线。
git branch 分支名
git checkout 分支名
git checkout -b bbs
git merge ask
git branch -d ask
# 分支用完就删,保持仓库清爽是一个好习惯。但在删除前,可以先多看一眼
git branch --merged
git branch --no-merged
git branch -d 分支名
git branch -D 分支名
git branch --no-merged 的作用可以简单理解为:看看哪些分支上还有没合并进主分支的新功能。git branch -D 分支名 则表示:我知道这个工作还没合并,但我决定不要它了。
在创建分支时,你可能会遇到下面这个熟悉的报错。你想创建并切换到一个叫 bbs 的分支,但系统发现这个分支已经存在了,所以会直接拒绝。
/demo# git checkout -b bbs
fatal: a branch named 'bbs' already exists
关于 checkout
checkout 的本质是“切换”。它的作用是切换到某个分支,并让工作区立刻刷新,显示出这个分支对应的内容。
刚接触 Git 时,不要把
checkout按字面意思理解成超市里的“结账”或酒店里的“退房”。在早期版本控制系统里,它更接近“把代码从档案室取出来,摊到你的工作台上开始干活”的动作。
当你执行 git checkout 分支名 时,Git 实际上会到 .git 里找到这个分支对应的文件状态,再把这些文件恢复到当前工作区。
创建并切换分支:
git switch -c bbs
这等价于旧版的:
git checkout -b bbs
撤销文件修改:
git checkout -- index.html
git restore index.html
关于 merge
不是所有事情都值得专门沟通,但只要一个改动会影响公共代码、别人模块或者接口边界,就应该早点、短点、准确地打个招呼。企业里真正贵的,不是沟通这几分钟,而是后面的返工、冲突和误解。
merge里常见的 Fast-forward 合并,可以理解为主分支没有新的分叉提交,所以 Git 不需要额外生成一次真正的合并,只是把主分支指针直接前移。- 团队开发很像装修房子。木工、电工、泥瓦工都在推进自己的工作,但如果彼此不提前沟通,就很容易返工。软件开发也是一样,很多冲突不是因为谁做错了,而是大家改到了互相影响的地方,却没有早点同步。
团队规范
- 公共代码通常会被很多人依赖,所以不能只顾自己写。只要涉及公共模块、通用配置、核心逻辑,最好提前同步,让别人知道风险,也避免多人同时改同一块地方。
- 在自己的分支开发一段时间后,主干代码往往已经发生变化。合并前先同步最新主干,可以更早发现冲突和差异,不要把问题拖到最后一刻集中爆发。提 PR 前先做一次同步,通常会顺很多。
- 接口先约定清楚,再开始联调。提前明确请求方式、传参格式、字段名和返回结构,能减少很多来回试错。
- 后端提到
dev之前先自测。比如先用 Postman 或 Swagger 验证接口是否能正常接参,返回是否符合约定。 git branch --merged是用来看哪些分支已经合并了。已经合并的分支一般可以删,没合并的分支要谨慎删除。- 常见分支思路是:
feature/*负责功能开发,dev负责开发集成,uat负责测试验收,production负责上线版本。 - 开发放
feature,联调放dev。feature是个人工作区,dev是团队协作区。
rebase = replace + base
rebase 解决的问题是:当主分支更新以后,怎样把你的分支“搬到最新起点”再接着放上去,这样合并时历史不会太乱。
rebase的重点不是“合并”,而是改基础点、重放提交。git rebase master可以理解为:把当前分支的基础点换到master的最新提交上。
从代码管理规范的角度看,在功能分支开发完成并准备发起 PR 之前,先在该分支执行一次 git rebase master 很有必要。这样可以让 PR 的提交历史更线性、更整洁,也能提前消化冲突,减少维护者的合并成本。开发者通常更熟悉自己的代码,因此这一步最好由开发者自己先完成。
从 master 切出子分支
│
├── master 不变
│ │
│ └── 直接合并,通常不会产生额外 merge commit
│
└── master 继续提交
│
├── 直接 merge
│ └── 可能产生 merge commit,历史变复杂
│
└── 先在子分支执行 git rebase master
│
├── 把子分支提交暂存
├── 把基础点移动到 master 最新提交
├── 重新应用子分支提交
└── 再合并,历史更线性、更整洁
合并实战
开发新功能时,通常先从 master 拉出一个新的功能分支,例如 feature1。开发完成后,先在当前分支上执行 git rebase master,把主分支最新代码同步过来;如果过程中有冲突,就先解决冲突并完成 rebase。之后将分支推送到远程,最后发起 PR,把 feature1 合并到 master。
合并结束之后,不要忘记删除 feature1 分支:
git push origin --delete feature1
关于 stash
stash 是 Git 给你配的保险箱;Shelf 更像是 IDEA 私自给你在办公桌底下挖的暗格,只有 IDEA 自己知道在哪。
git stash push -u -m "保存所有进度"
git config --global alias.stu '!f() { git stash push -u -m "${1:-保存所有进度}"; }; f'
git commit --amend --only -m "fix# xxxxxxx"
关于别名
git config --global alias.st status
git config --global alias.br branch
git config --global alias.co checkout
git config --global alias.cm "commit -m"
git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
关于 SSH
当你用 SSH 去访问一个仓库时,比如执行下面这条命令:
git clone git@github.com:xxx/yyy.git
会经历三步:先验证服务器身份,再验证用户身份,最后检查仓库权限。
- SSH 私钥解决的是“你能不能通过 SSH 证明自己身份”的问题。
known_hosts不是验证“我能不能访问仓库”,而是验证“我现在连到的是不是那台正确的服务器”。- 去 GitHub 拉代码时,不是先急着证明我是我,而是先确认眼前这个跟我对话的机器,真的就是 GitHub 那台机器。这个确认过程,就是拿服务器发来的公钥,去跟本地
known_hosts里记住的那份做对比。 - 如果仓库是公开的,使用 SSH 地址去
clone,而本机又没有配置好 SSH key,大概率仍然会因为 SSH 身份认证失败而报错。
GitHub Webhook 自动化部署指南
每次写完代码都要手动登录服务器拉取最新代码,会很烦。只要做一层 Webhook 配置,就能实现本地 git push 之后,服务器自动更新代码。
[你的本地电脑]
│ 1. 提交代码 (git push)
▼
[ GitHub 远程仓库 ]
│ 2. 触发 Webhook 钩子 (带着你设置的密钥)
│ 3. 发送 HTTP POST 请求
▼
[ 你的 Web 服务器 ]
│ 4. 接收请求 (webhook.php 脚本)
│ 5. 验证密钥通过
│ 6. 执行服务器终端命令 (shell_exec)
▼
[ 服务器终端执行 git pull ]
└─> 自动拉取 GitHub 上的最新代码,覆盖本地网站文件
- 编写接收脚本。在服务器网站根目录新建
webhook.php,核心作用是接收 GitHub 请求并执行shell_exec('git pull');。为了安全,记得在脚本里写死一个Secret密钥用于身份验证。 - 解除系统封印。出于安全限制,PHP 环境如宝塔面板,默认会禁用系统命令执行。你需要进入 PHP 设置里的“禁用函数”列表,将
shell_exec删掉以释放权限。其他环境可以按需跳过。 - 配置 GitHub。进入仓库的
Settings -> Webhooks,填入刚才webhook.php的公网访问地址,并设定与第一步完全相同的Secret密钥。
核心避坑:如果 GitHub 提示成功了,但代码没更新,绝大多数时候是 Linux 权限冲突。比如你第一次连上服务器时是用 root 账号手动 git clone 的,那么这些文件的所有者就是 root。而后续触发 Webhook 时,执行拉取动作的可能是 www 或 www-data。低权限用户无法覆盖 root 创建的文件,所以看起来像“执行成功了但没效果”。
解决方式通常是把网站目录所有权交给真正执行部署动作的 Web 用户,例如:
chown -R www /你的网站目录路径
这样后续就可以把部署动作交给自动化链路,而不是每次手动登录服务器拉代码。
