Git 分支管理策略:从日常开发到稳定发布

Git 分支管理策略:从日常开发到稳定发布

用AI写的, 用于参考

TLDR (Too Long; Didn't Read):

  • 核心原则:为每个新功能或 Bug 修复都创建一个新的、短生命周期的分支。 这能隔离工作、方便代码审查、保持主分支干净。完成后合并回主开发分支 (如 developmain),然后立即删除这个特性/Bug 分支
  • 长期分支 (如 main, develop, staging) 用于管理不同环境和发布阶段的代码状态。 main 通常是生产代码,develop 是集成开发代码,staging 是预生产测试代码。
  • 这两套分支策略是互补的:日常在短生命周期分支上开发,然后通过流程将代码合并到相应的长生命周期分支,最终部署到生产。
  • 关键操作
    git checkout -b feature/name 创建分支;
    git add ., git commit 提交;
    git merge --no-ff feature/name 合并分支 (推荐保留合并历史);
    git branch -d feature/namegit push origin --delete feature/name 删除分支。

核心原则:为每个任务创建独立分支(短生命周期分支)

无论项目大小,也无论开发者数量,为每一个独立的开发任务(如新功能、Bug 修复、代码重构、文档更新等)创建一个专门的分支都是最佳实践。

为什么这样做?

  1. 隔离性:每个任务在独立的环境中开发,不会与其他正在进行的工作互相干扰。如果一个特性开发遇到问题,不会影响主干代码。
  2. 清晰的提交历史:通过合并(尤其是 --no-ff)特性分支,可以将一组相关的提交聚合为一个逻辑单元,使得历史记录更易于理解和追溯。
  3. 代码审查 (Code Review):基于分支的 Pull Requests (PR) 或 Merge Requests (MR) 是进行代码审查的标准方式,方便团队成员针对特定改动进行讨论和反馈。
  4. 并行开发:团队成员可以同时在不同的特性分支上工作,互不阻塞。
  5. 易于放弃和回滚:如果某个特性不再需要或方向错误,可以直接删除该分支,对主干代码没有影响。
  6. 上下文切换:可以轻松地在不同任务之间切换,例如暂停一个特性开发去修复一个紧急 Bug。

典型工作流与命令

假设我们的主开发分支是 develop (或者 main 如果是更简单的流程)。

  1. 从基础分支创建新分支

    # 切换到基础分支并确保它是最新的
    git checkout develop
    git pull origin develop
    
    # 为新特性 "user-authentication" 创建并切换到新分支
    git checkout -b feature/user-authentication

    推荐命名规范feature/description, bugfix/issue-id-description, hotfix/description, chore/task-description

  2. 在新分支上开发和提交

    # ...进行代码修改...
    git add .
    git commit -m "Implement user login endpoint"
    
    # ...继续开发和提交...
    git commit -m "Add password hashing for user-authentication"
  3. 推送分支到远程 (可选但推荐)

    git push -u origin feature/user-authentication

    这有助于备份,并允许其他人看到你的进展或协作,也是创建 PR/MR 的前提。

  4. 创建 Pull Request / Merge Request
    在 Git 托管平台 (GitHub, GitLab, Bitbucket) 上,从 feature/user-authenticationdevelop 分支发起 PR/MR。

  5. 代码审查与合并
    PR/MR 通过审查后,通过平台界面合并,或者在本地合并:

    # 切换回目标分支并更新
    git checkout develop
    git pull origin develop
    
    # 合并特性分支 (推荐 --no-ff 保留合并信息)
    git merge --no-ff feature/user-authentication
    
    # 推送合并后的 develop 分支
    git push origin develop
  6. 删除已合并的分支 (非常重要!)

    # 删除本地分支
    git branch -d feature/user-authentication
    
    # 删除远程分支
    git push origin --delete feature/user-authentication

    许多 Git 平台在合并 PR/MR 后会自动提供删除源分支的选项。

环境驱动的长生命周期分支

除了为具体任务创建的短生命周期分支外,项目中通常还会维护一些长期存在的分支,它们代表了代码在不同环境或不同阶段的状态。

常见环境分支及其用途

  1. main (或 master)

    • 用途:通常代表生产环境就绪 (Production-Ready) 的代码。这个分支应该永远是稳定的、可部署的。
    • 合并来源:通常从 release 分支或经过充分测试的 develop (或 staging) 分支合并而来。
    • 重要性:直接部署到生产环境的代码来源,只接受经过严格测试和审查的代码。
  2. develop

    • 用途:作为主要开发集成 (Integration) 分支。所有已完成的 feature 分支通常会合并到这里。它代表了下一个版本中包含的所有新功能和修复。
    • 稳定性:相对 main 来说,develop 可能没那么稳定,但应该保持可编译和基本可运行状态,供日常测试和QA。
  3. staging (或 pre-production, uat)

    • 用途:用于用户验收测试 (UAT) 或预生产环境部署。这个分支的代码应该尽可能地接近生产环境。
    • 合并来源:通常从 develop 分支中某个稳定的点,或者从 release 分支合并而来。
    • 部署:此分支的代码会被部署到预生产服务器供测试人员或客户进行最终验证。
  4. production (可选)

    • 用途:有些团队会使用一个独立的 production 分支,严格映射当前生产环境部署的确切代码版本,通常通过标签指向。main 则可能代表“最新的稳定版”。在许多流程中,main 本身就扮演了生产分支的角色。
  5. 特殊用途的长/中生命周期分支

    • release/* (例如 release/v1.2.0):当 develop 分支达到一个准备发布的里程碑时,可以从 develop 创建一个 release 分支。此分支用于发布前的最后准备工作,如少量 Bug 修复、版本号更新、文档生成等。不允许再合入大的新功能。测试通过后,release 分支会合并到 main (打上版本标签) 和 develop (确保 develop 也包含这些修复)。
    • hotfix/* (例如 hotfix/critical-login-bug):当生产环境 (main) 出现紧急 Bug 需要立即修复时,从 main (或对应的生产标签) 创建 hotfix 分支。修复完成后,合并回 main (并打上新的紧急修复版本标签) 和 develop (以及活跃的 release 分支,如果存在)。

长生命周期分支的交互

简单的分支交互图 (概念 - Gitflow 模型)
(上图为经典 Gitflow 模型,实际可根据团队需求简化)

  • Feature 分支从 develop 拉出,完成后合并回 develop
  • develop 准备发布时,创建 release 分支。
  • release 分支完成后,合并到 maindevelop
  • hotfix 分支从 main 拉出,完成后合并回 maindevelop

结合两种模式:推荐的实践流程

对于大多数项目,推荐将以上两种模式结合起来:

  1. 日常开发:所有新功能和非紧急 Bug 修复都在从 develop (或 main,如果流程简化) 拉出的短生命周期分支 (feature/*, bugfix/*) 上进行。完成后通过 PR/MR 合并回 develop (或 main),然后删除这些短生命周期分支。

  2. 集成与测试develop 分支作为所有已完成特性的集成点,可以部署到开发或测试环境进行持续集成和测试。

  3. 发布准备

    • 简单流程:当 develop 分支被认为稳定并通过QA后,可以直接将其合并到 main,然后从 main 打标签并部署到 stagingproduction
    • Gitflow 流程:从 develop 创建 release/* 分支。在此分支上进行最终测试和微小修复。然后将 release/* 分支合并到 main (打标签) 和 develop
  4. 部署

    • main 分支的某个标签(或 release/* 分支)部署到 staging 环境进行 UAT。
    • UAT 通过后,将同一个标签/代码部署到 production 环境。
  5. 紧急修复 (Hotfix)

    • main (或生产环境对应的标签) 创建 hotfix/* 分支。
    • 修复、测试、合并回 main (打新标签),并同时合并回 develop (以及任何活跃的 release 分支)。
    • 部署新的 main 标签到生产环境。
    • 删除 hotfix/* 分支。

综合命令示例 (简化流程,以 main 为主干,develop 为集成)

1. 开始新功能 new-feature

git checkout develop
git pull origin develop
git checkout -b feature/new-feature develop
# ... 开发 ...
git add .
git commit -m "Add new-feature logic"
git push -u origin feature/new-feature
# ... 创建PR,审查,合并到 develop ...
# ... 在平台上或本地删除 feature/new-feature ...

2. 准备发布 (从 develop 到 main)

假设 develop 已经稳定,准备合并到 main

git checkout main
git pull origin main
git merge --no-ff develop -m "Merge develop for release v1.0.0" # 或通过PR
git tag v1.0.0
git push origin main
git push origin v1.0.0

3. 紧急修复生产 Bug critical-bug

从 main 拉取 hotfix 分支

git checkout main
git pull origin main # 确保基于最新的生产代码
git checkout -b hotfix/critical-bug main # 或者从某个生产标签 git checkout -b hotfix/critical-bug v1.0.0

# ... 修复 Bug ...
git add .
git commit -m "Fix critical-bug in production"
git push -u origin hotfix/critical-bug

# 创建PR,审查,合并 hotfix/critical-bug 到 main
git checkout main
git merge --no-ff hotfix/critical-bug
git tag v1.0.1 # 新的生产版本标签
git push origin main
git push origin v1.0.1

# 同时,将修复合并到 develop
git checkout develop
git pull origin develop
git merge --no-ff hotfix/critical-bug # 或者 cherry-pick 修复提交
git push origin develop

# 删除 hotfix 分支
git branch -d hotfix/critical-bug
git push origin --delete hotfix/critical-bug

关键 Git 命令汇总

查看分支:

git branch              # 列出本地分支
git branch -r           # 列出远程分支
git branch -a           # 列出所有分支
git branch --merged     # 列出已合并到当前分支的分支
git branch --no-merged  # 列出未合并到当前分支的分支

创建分支:

git branch <branch-name>                                # 创建分支
git checkout -b <branch-name> [start-point]             # 创建并切换到新分支,可选基于某个起点 (如另一分支或标签)

切换分支:

git checkout <branch-name>

合并分支:

git merge <branch-to-merge-into-current>          # 将指定分支合并到当前分支
git merge --no-ff <branch-to-merge>               # 创建一个合并提交,即使可以快进合并
git merge --squash <branch-to-merge>               # 将指定分支的多次提交合并为一个提交,然后需要手动 git commit

删除分支:

git branch -d <branch-name>                               # 删除已合并的本地分支 (安全)
git branch -D <branch-name>                               # 强制删除本地分支 (即使未合并)
git push origin --delete <remote-branch-name>              # 删除远程分支

同步与远程仓库:

git fetch origin                                          # 从远程仓库下载所有分支和提交,但不自动合并
git pull origin <branch-name>                               # 等同于 git fetch origin 后跟 git merge origin/<branch-name>
git push origin <branch-name>                               # 推送本地分支到远程
git push -u origin <branch-name>                              # 推送并设置上游跟踪分支

重命名分支:

git branch -m <old-name> <new-name>                      # 重命名本地分支
# (重命名远程分支较复杂:先重命名本地,推送新名称,删除旧远程名称)

总结与最佳实践

  • 始终为独立任务创建分支: 这是最核心的原则。
  • 保持分支短生命周期: 特性分支和 Bugfix 分支在合并后应立即删除。
  • 使用清晰的命名规范: 方便识别分支用途 (feature/, bugfix/, hotfix/, release/)。
  • 定期清理陈旧分支: git fetch --prune 清理本地不存在的远程跟踪分支,定期审查并删除远程不再需要的旧分支。
  • 善用 Pull/Merge Requests: 这是代码审查和讨论的绝佳工具。
  • 理解 --no-ff 合并: 它能保留分支的开发历史,使主干历史更易读。
  • 沟通: 团队应就分支策略达成一致并严格遵守。

选择合适的分支策略并坚持执行,将极大地提升开发效率、代码质量和团队协作的顺畅度。根据项目和团队的实际情况,可以对上述模型进行调整和简化。

适合中学生的电路仿真软件

Alex看我画电路图, 也来了兴致, 想试试他自己设计的电路是不是可以用.
竟然在网上找到了一个用javascript进行简单电路仿真的网页:
https://www.falstad.com/circuit/circuitjs.html
包含基础的元器件,已经常用的电路. 仿真的时候, 视觉效果非常直观, 推荐按
可以随便找个webserver进行部署.
Alex童鞋想做的跑马灯效果

2023总结

一直不觉得人为地设置一天, 和前一天有什么区别. 所以年终的总结和年初的计划, 我也一度不屑--只是另外一天而已, 并无不同.
随着年纪越来越长, 总觉得每天都是前一天的复制粘贴, 日复一日没有什么变化. 就突然觉得这样下去似乎没有了起点也没有终点. 所以这个人为设置的年的终点与起点, 借用来做一个阶段性的回顾还是有那么一点必要的.
过去的2023年是与自己和解的一年. 人到中年, 不惑就是知道自己是哪颗葱,哪些是自己想要的, 哪些是没必要去追求的. 不惑不代表事情都能做对, 甚至不代表知道哪些事情是对的, 只是更多的遵循自己的天性吧.
与自己和解也不代表着躺平. 这一年其实是相当不顺利的一年, 努力挣扎着,也勇于改变着, 勇于放弃着.
有些没有天分的事情, 就放弃吧, 一个内向的人做外向的事, 属于消耗, 不值得.
在自己喜欢的事情上, 不躺平, 要做的更有章法一些, 更有成果一些.这一年的年末, 开始试着做一些以前从来不曾接触过的电路设计, 在吃饭的时候还在想着电路改怎么设计, 是喜欢的事情带来的幸福感.
要把喜欢的事情做得更有成果一些 -- 至少简历上都好吹一些. 就像骑自行车一样, 骑行100公里其实也不算什么, 但是完成了环滇池的100公里骑行就是一个可以写进简历里的故事了.

那就流水账一下2023吧

  • 个人发展

    • 开始设计第一个电路图 (以前学校的作业不算)
    • 提升了焊接技术, 可以比较轻松地焊接喜导线和小原件了 (IC还不行)
    • 开始学习斯多葛
    • 对于电子生产制造和产品质量管理有了初步认知
    • 对于功能安全ISO26262概念有了初步了解
  • 旅行

    • 海南陵水, 分界洲岛, 五指山
    • 昆明, 环滇池骑行
    • 火车从成都穿越大巴山以及秦岭
    • 西安旅行: 兵马俑, 秦始皇陵, 自驾穿越秦岭, 定军山, 武侯墓, 佛坪大熊猫基地

穿越巴山秦岭

第一次做火车穿过秦岭, 心情还是有点激动的.

列车穿过一个又一个山洞. 向上仰望, 山崖顶上有一小片油菜花田, 漏出了一抹金黄. 路过一个山坳, 梯田间坐落着几个农家, 屋旁几树梨花雪白. 又穿过一个山洞, 火车行进在高高的旱桥上, 俯瞰下面的村庄, 几级山崖构成了天然的梯田, 每级的落差有两层楼高.

列车又钻过一个山洞, 开满桃花的山谷里装饰了几个热气球和一个舞台, 似乎是一个城市的桃花节正在准备.

柏树在山坡的高处成片生长, 和岭南的桉树, 以及长白的桦树完全不同.

列车穿过了接连几个长长长长的隧道, 来到了广元, 这还只是穿越大巴山脉和秦岭的前奏. 铁路在这里分成了北上宝鸡的宝成铁路, 和东北穿越大巴山到汉中的高速铁路, 还有一路直向西北, 经陇南去往兰州.

列车一头扎进了大巴山里, 隧道之间偶尔有一两秒的缝隙能够看到山里的景色. 出了大山, 就遇到了缓缓流过的汉江, 来到了夹在秦岭与大巴山之间的汉中平原. 两山之间地势平坦, 让人觉得格外值得珍惜. 这里的民居白墙红瓦, 相比四川平原, 檐窄坡缓, 防雨防潮的设计较少, 保暖效果更好, 是北方民居的风格.

列车再次钻进了秦岭, 走出来就到了西安. 回头看去, 巍峨的秦岭从平原上拔地而起.

西安真热, 2023-03-08就达到了25度, 应该是冷空气来之前的峰前升温吧?

西安基本上看不到口罩, 戴着口罩的我显得很突兀, 所以我也就摘了口罩.

红叶李开得及其旺盛, 一个妈妈推着婴儿车, 指着红叶李对宝宝说: 是不是很美啊?

理性人假设与价格预期

经济学里面有个理性人假设,假设大家都是理性的,选择的是自身利益最大化的方案。
投资里面,有价格预期的概念,一个资产,如果它的价格在之后某个时间点会确定涨到(或者跌到)某个价格点,那么它的现价就马上会涨到那个价格点(贴现后)。
可是在现实生活中不是这样。比方说,前几年上海的地铁建设很快速,每年都有新的地铁站建成。按照我的观察,地铁站建成前,房价涨幅不大;而当地铁站开始运营之后,房价会有一波迅速的,超过平均涨幅的上涨。我就非常好奇:这些买房子的人,早干啥去了?地铁通车之前的三个月,很容易知道这个地铁通车已经确定了,没有风险了啊。

Airtag的功能与限制

Airtag的主要功能部件:

  • Apple 自己的UWB (Ultra-Wide Band, 超宽带) 无线技术: 用于测量和iPhone手机之间的距离, 只在短距离内(十几米? 或者几十米?)有效. 用于定位, 开阔环境下定位精度估计有半米左右的误差半径
  • Nordic 52832蓝牙低功耗 + NFC芯片: 新的蓝牙鼠标和遥控器都用的都是蓝牙低功耗技术, 可以待机很久. 用于和iPhone手机通信. 可用距离未知, 如果使用了蓝牙长距离通信技术, 开阔环境下可以做到1公里以上. 实际如何没测过. 如果中间有遮挡, 距离会下降不少
  • Maxim Integrated MAX98357B AB类数字音频放大器 和扬声器 这个功能很有用, 粗略定位后可以用耳朵进行精准定位
  • CR2032纽扣电池

Airtag的定位原理

Airtag可以测量它与其它Apple UWB设备之间的相对位置. 手机再利自身的GNSS以及其它信息(附近的基站, 附近的蓝牙bacon, 附近的WiFi热点)获得的位置信息, 计算出来与airtag的距离.

Airtag的使用限制

从上面的这些功能可以看到, Airtag 的主要通讯方式是UWB和蓝牙, Apple可以依赖其强大的客户群, 与其它的Apple设备进行通信, 比如别人的iPhone , 别人的airtag等等. 但是如果在有效通讯范围内没有Apple的设备, 就无法进行定位于通信
所以, Airtag在城市环境中可以依赖附近的Apple设备定位, 并将定位信息传出去, 在人多的室内公共环境也是如此, 但并不适用于郊野等无Apple设备的环境.

Time tag on ping command

ping is a very useful and common command to test the link to another site.
It is frequently used to test whether the network is working or not.
But there is no time stamp in the output when using ping on windows, which made it hard to debug a short network break.
To solve this issue without install a 3rd party tool, powershell could be used:
Test-Connection -count 9999999 -delay 2 www.bing.com | select {Get-Date} , Address , IPv4Address ,ResponseTime
By select the objects of output, adding {Get-Date} info to the output, the timestamp could be output.

Update 2022-12-28
Another command also works
ping.exe -t www.bing.com|Foreach{"{0} - {1}" -f (Get-Date),$_}

Linux服务器的双网卡路由设置

PC1有两张网卡, 分别连接不同的网络, PC1上运行了服务, 服务必须根据来时的网络来进行路由. 所以路由策略是, 针对每个网卡单独配置一个路由表. 所有来自该网卡的数据, 根据该网卡的路由表发送.

#针对每个网卡创建一个路由表
echo 200 reth0 >> /etc/iproute2/rt_tables
echo 201 reth1 >> /etc/iproute2/rt_tables
#指定网卡对应的路由表
ip rule add from 192.168.50.99 table eth0
#指定网卡的路由
ip route add default via 192.168.50.1 dev enp2s0 table eth0
ip route add 192.168.50.0/24 via 0.0.0.0 dev enp2s0 table eth0
#指定网卡对应的路由表
ip rule add from 192.168.100.99 table wl0
#指定网卡的路由
ip route add default via 192.168.100.1 dev wlp1s0 table wl0
ip route add 192.168.100.0/24 via 0.0.0.0 dev wlp1s0 table wl0

[1]

"Linux network: reply on same interface as incoming"

Google reader 的数据备份

Google reader离关闭没有几天了. 到了把数据全部导出来的时候了.

网上找了两个工具:

GReader Archive - Google Reader 阅读历史存档下载工具

这个工具可以把订阅的每个feed的数据都作为一个rss文件保存到本地. 可以直接用opera或者其他Rss阅读器直接打开.作者在说明中也提到了不少技巧. 建议使用. 但似乎不能导出星标文件.

ConvertJSON for Google Reader 

这个工具可以把google takeout的数据解压成可阅读的文件. 作者也给出了详细的说明,包括如何用google takeout.

 

至于什么有道或者麦库的导出工具, 看描述觉得不靠谱, 干脆就没敢用. Feedly, 我就没成功浏览过……

 

为了保险起见, 我两个工具都用了. 下载下来的数据上传到skydrive.

 

需要指出的是,

  1. Google reader并不保存图片数据. 所以原来在msn space上的blog中的图片全部失效
  2. 只导出必要的数据, 否则对于google reader 的重度用户来说, 导出的数据量太大. 比方说我就只导出了朋友们的blog - 大部分都是 msn space 和 非常有价值的IT blog