开发模式是我们的标配:接到新需求,git checkout -b feature/xxx;开发完成,提交 Pull Request;代码合并,master 分支又茁壮成长了一点。然而,宁静的表面下,混乱正在悄然滋生。

一个寻常的周五下午,我准备发布一个稍大版本的迭代。在合并了十几个功能分支后,我的本地仓库变得“惨不忍睹”。执行 git branch,屏幕上滚过一长串已经合并过的、毫无用处的 feature/ 分支。它们就像数字世界的“幽灵”,任务早已完成,躯壳却依然盘踞在我的分支列表里。

我开始了机械的清理工作:

  1. git branch --merged 查看哪些分支可以被安全删除。
  2. 一个个 git branch -d feature/xxx-a
  3. git branch -d feature/xxx-b
  4. ……

这个过程枯燥、重复,而且容易出错。万一不小心删掉了还未合并的分支(虽然 -d 参数有保护,但 -D 可不跟你客气),那乐子就大了。

“一定有更好的办法!”——这个念头像一颗种子,在那个令人烦躁的下午,悄然埋下。我想要的很简单:一个工具,能自动列出所有已合并到主分支的本地分支,让我像在购物车里勾选商品一样,选择、然后一键删除。

那一刻,我还没意识到,这个小小的“偷懒”想法,即将开启我的第一个开源项目之旅。

第一章:从“脚本小子”到“工具匠人”的雏形

万事开头难。最初,我的想法极其朴素,就是把手动的命令自动化。我打开了最熟悉的编辑器,用 Python 写下了第一版“原型”。

它的核心逻辑简单粗暴:

import subprocess
import re

def get_merged_branches():
    # 切换到主分支,通常是 master 或 main
    # 这里为了简单,我们假设当前就在主分支
    
    # 执行 git branch --merged 命令
    result = subprocess.run(['git', 'branch', '--merged'], capture_output=True, text=True)
    
    if result.returncode != 0:
        print("Error: Git command failed.")
        return []
        
    branches = result.stdout.split('\n')
    
    # 清理和过滤
    merged_branches = []
    for branch in branches:
        # 去掉前面的 '*' 和空格
        clean_branch = branch.lstrip('* ').strip()
        # 排除主干分支和当前分支
        if clean_branch and clean_branch not in ['master', 'main', 'develop']:
            merged_branches.append(clean_branch)
            
    return merged_branches

if __name__ == "__main__":
    deletable_branches = get_merged_branches()
    if deletable_branches:
        print("The following branches have been merged and can be deleted:")
        for branch in deletable_branches:
            print(f"- {branch}")
    else:
        print("No local branches are available for deletion.")

这个脚本能跑!它成功地列出了那些可以被删除的分支。那一刻,我感觉自己像个“脚本小子”,用胶水代码粘合了几个命令,解决了一个实际问题。我把它命名为 clean_branch.py,放在我的个人工具文件夹里,心满意足地用了起来。

然而,满足感是短暂的。每次使用,我还是需要手动复制列表里的分支名,再执行 git branch -d。这并没有完全解决问题,只是把“重复的删除操作”变成了“重复的复制粘贴操作”。

“不行,要一步到位。” 我想。

于是,我开始研究如何让脚本更具交互性。我发现了 inquirer 这个库,它能轻松创建出漂亮的命令行交互界面。这正是我想要的!

[图片占位符 1:一张展示 inquirer 库生成的命令行复选框界面的截图,列出了一些示例分支名,如 feature/loginhotfix/typo-fix 等。]

引入交互性后,代码变得复杂了一些,但体验却好了无数倍。用户只需用上下键选择,按空格勾选,最后按回车确认。工具会自动在背后执行删除命令。

# (部分示例代码)
import inquirer

# ... (前面的 get_merged_branches 函数) ...

questions = [
    inquirer.Checkbox(
        'branches_to_delete',
        message="Select branches to delete (use space to select, enter to confirm)",
        choices=deletable_branches,
    ),
]

answers = inquirer.prompt(questions)

if answers and answers['branches_to_delete']:
    for branch in answers['branches_to_delete']:
        print(f"Deleting branch: {branch}...")
        subprocess.run(['git', 'branch', '-d', branch])
    print("\nCleanup complete!")

至此,这个小工具已经具备了“产品”的雏形。它不再是一个简单的脚本,而是一个能与用户友好交互的“匠人”之作。我每天都在用它,越用越顺手,也越发觉得——这么好用的东西,或许别人也需要?

第二章:拥抱开源,一次“社恐”的自我突破

“把它开源吧!”

这个念头一出现,就被另一个声音压了下去:“别了吧,你这代码写得这么烂,万一被大佬笑话怎么办?”

这是很多新手程序员的共同心声:对自己的代码不自信,害怕暴露在公众的审视之下。我也不例外。我的代码里没有完善的单元测试,错误处理也很粗糙,甚至连函数命名都可能不符合最佳实践。把它放到 GitCode 这样的平台上,感觉就像是把一个满是补丁的习作交给了全世界的“阅卷老师”。

我犹豫了整整一个星期。期间,我反复打磨这个工具,增加了诸如“dry-run”模式(只看不删)、自定义主分支名称、彩色输出等功能。我像一个准备出嫁的女儿的母亲,希望能把她打扮得尽可能完美。

在这个过程中,我查阅了大量优秀的开源项目。我发现,它们并非一开始就完美无瑕。很多伟大的项目,最初的 commit 也很简单,甚至简陋。开源的本质,不是展示一个完美的作品,而是分享一个有用的想法,并邀请社区共同使其变得更好。

想通了这一点,我终于鼓起勇气,迈出了第一步。

1. 编写一份像样的 README.md

我意识到,README 是项目的“脸面”。它需要清晰地告诉别人:这是什么?它能解决什么问题?如何安装?如何使用?我花了一个下午,精心编写了 README.md,配上了动态的 GIF 演示图,力求让任何一个点进来的人都能在30秒内明白它的价值。

[图片占位符 2:一张展示项目 README 文件的截图,包含项目名称、徽章(build, license)、简介、安装步骤和使用示例(GIF动图)。]

2. 选择一个合适的许可证

许可证是开源项目的“身份证”。我不希望有任何商业使用的限制,同时也希望使用者能免除我的责任。在查阅了“阮一峰”老师的科普文章后,我选择了最宽松、最流行的 MIT License。它简单明了,几乎允许任何人以任何方式使用我的代码。

3. 整理项目结构并推送到 GitCode

我创建了一个规范的目录结构,包含了 src 源码目录、tests 测试目录(虽然当时是空的)、.gitignore 文件、LICENSEREADME.md

然后,我执行了那条至今仍记忆犹新的命令:
git remote add origin git@gitcode.com:my-username/my-git-cleaner.git
git push -u origin main

当终端显示推送成功时,我的心跳得飞快。兴奋、紧张、期待,五味杂陈。我的第一个开源项目,就这样诞生了。

第三章:回响与成长,远超预期的收获

项目发布后,我忐忑地把链接分享到了几个技术群里。起初,并没什么反响。我甚至一度认为,这不过是我的一场“自嗨”。

几天后,我收到了第一个 Star。

那是一个完全陌生的ID。我至今无法形容那一刻的激动。这意味着,在世界的某个角落,有另一个人,认可了我创造的价值。这个小小的星星,像一束光,照亮了我所有的不安。

紧接着,我收到了第一个 Issue。一位用户反馈说,在 Windows 的 PowerShell 环境下,颜色代码会显示为乱码。我立刻着手研究,发现是不同终端对 ANSI escape codes 的支持问题。最终,我通过引入 colorama 库,实现了跨平台的颜色支持,并发布了我的第一个补丁版本 v1.0.1

解决这个 Issue 的过程,让我学到了比项目本身更多的东西:如何与用户有效沟通、如何复现问题、如何编写更新日志(Changelog)。

最让我惊喜的是,一个月后,我收到了第一个 Pull Request!一位热心的开发者发现我的脚本在处理包含特殊字符的分支名时存在 Bug,他不仅指出了问题,还提交了修复代码。我认真地 Code Review,与他讨论了几轮,最终合并了他的贡献。

那一刻,我深刻地体会到了开源的魅力。它不再是我一个人的项目,它有了第二个、第三个贡献者。我们素未谋面,却通过代码连接在了一起,为了一个共同的目标而努力。

结语:这只是一个开始

从那个烦躁的下午,到一个拥有陌生用户、贡献者和几十个 Star 的开源项目,这段旅程不过短短数月。它带给我的,远不止一个趁手的工具。

  • 技术上,我从一个只会写业务逻辑的“码农”,变成了一个能独立开发、打包、发布完整工具的“开发者”。我学会了CLI交互设计、Python打包发布(PyPI)、版本管理和CI/CD(后来我加上了GitHub Actions)。
  • 心态上,我克服了“代码羞耻症”,建立了分享的自信。我明白了“完成比完美更重要”,也理解了开源社区的协作精神。
  • 视野上,我开始更主动地去了解和参与其他开源项目,从一个单纯的“使用者”向“贡献者”转变。

如果现在的你,也像当初的我一样,因为一个小小的痛点而萌生了一个想法,但又因为害怕和不自信而犹豫不决,那么我希望我的故事能给你一点点鼓励。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐